initialize(); use app\common\model\qyt\Enterprise as EnterpriseModel; use app\common\service\QytConfig; $mobile = $argv[1] ?? ''; $enterpriseId = (int) ($argv[2] ?? 0); if (!preg_match('/^1[3-9]\d{9}$/', $mobile)) { exit("用法:php deploy/h5_debug.php <司机手机号> [企业ID]\n"); } $enterprise = EnterpriseModel::resolve($enterpriseId); if (!$enterprise) { exit("未找到可用企业,请先在后台「设置 → 企业管理」建企业并设为默认\n"); } $conf = QytConfig::forEnterprise($enterprise); $base = rtrim((string) $conf['h5_base_url'], '/'); $appId = (string) ($conf['h5_app_id'] ?: $conf['app_id']); // ---------- 按 7.15 规则签名(ksort -> http_build_query -> urldecode -> 大写MD5 -> SHA1withRSA -> base64) ---------- $params = [ 'appId' => $appId, 'mobile' => $mobile, 'timestamp' => date('YmdHis'), ]; !empty($conf['h5_with_creditcode']) && $params['creditcode'] = (string) $conf['business_license']; $forSign = $params; ksort($forSign); $raw = urldecode(http_build_query($forSign)); $md5 = strtoupper(md5($raw)); $pem = trim((string) $conf['private_key']); if (strpos($pem, '-----BEGIN') === false) { $pem = "-----BEGIN PRIVATE KEY-----\n" . chunk_split($pem, 64, "\n") . "-----END PRIVATE KEY-----\n"; } $pkey = openssl_pkey_get_private($pem); if ($pkey === false) { exit("私钥无效:" . openssl_error_string() . "\n"); } $sig = ''; openssl_sign($md5, $sig, $pkey, OPENSSL_ALGO_SHA1); $sign = base64_encode($sig); // ---------- 输出 ---------- $build = function (array $p, string $sign, string $tail) { $pairs = []; foreach ($p as $k => $v) { $pairs[] = $k . '=' . $v; } $pairs[] = 'sign=' . $sign; return '/?' . implode('&', $pairs) . $tail; }; echo "企业:{$enterprise['company_name']}({$enterprise['business_license']},状态 {$enterprise['status']})\n"; echo "通道:{$conf['passageway_name']} appId:{$appId}\n"; echo "待签串:{$raw}\n"; echo "大写MD5:{$md5}\n"; echo "签名含 + 的个数:" . substr_count($sign, '+') . ",含 / 的个数:" . substr_count($sign, '/') . "\n"; echo str_repeat('=', 70) . "\n\n"; $variants = [ 'A. 原样(当前线上行为)' => [$sign, '#/'], 'B. 只把 + 编码为 %2B' => [str_replace('+', '%2B', $sign), '#/'], 'C. 把 + 和 / 都编码' => [str_replace(['+', '/'], ['%2B', '%2F'], $sign), '#/'], 'D. 全部编码(改之前的老行为)' => [rawurlencode($sign), '#/'], 'E. 原样,但去掉末尾的 #/' => [$sign, ''], ]; $i = 1; foreach ($variants as $label => [$s, $tail]) { echo "【{$i}】{$label}\n"; echo $base . $build($params, $s, $tail) . "\n\n"; $i++; } echo str_repeat('-', 70) . "\n"; echo "把上面 5 条分别发到微信里点开,记录结果:\n"; echo " 只有 A 失败、B 成功 -> 问题是 +,对方页面会解码,我们编码 + 即可\n"; echo " A、B 都失败、C 成功 -> + 和 / 都要编码\n"; echo " A 成功、其余失败 -> 不是编码问题,重点查 E(末尾 #/)与微信追加参数\n"; echo " 只有 E 成功 -> 末尾的 #/ 被算进了 sign\n"; echo " 全都失败 -> 不是 URL 层面的问题,查公钥是否配对、企业是否已审核通过\n";