From 16e3078104793a6698a4fe94d52c1e534a1ffc45 Mon Sep 17 00:00:00 2001 From: Billy <641833868@qq.com> Date: Mon, 27 Oct 2025 15:42:14 +0800 Subject: [PATCH] up --- DEPLOY_README.md | 95 ++++ SERVER_FIX_GUIDE.md | 195 ++++++++ .../shopro/library/ccblife/CcbEncryption.php | 447 ------------------ deploy_fix.sh | 88 ++++ server_diagnose.sh | 112 +++++ 5 files changed, 490 insertions(+), 447 deletions(-) create mode 100644 DEPLOY_README.md create mode 100644 SERVER_FIX_GUIDE.md delete mode 100644 addons/shopro/library/ccblife/CcbEncryption.php create mode 100755 deploy_fix.sh create mode 100755 server_diagnose.sh diff --git a/DEPLOY_README.md b/DEPLOY_README.md new file mode 100644 index 0000000..1bd6001 --- /dev/null +++ b/DEPLOY_README.md @@ -0,0 +1,95 @@ +# 快速部署说明 + +## 方式1: 一键自动部署 (推荐) + +```bash +bash deploy_fix.sh +``` + +这个脚本会自动完成: +1. ✅ 检查本地代码 +2. ✅ 上传诊断脚本 +3. ✅ 运行服务器诊断 +4. ✅ 上传修复文件 +5. ✅ 重启PHP-FPM +6. ✅ 清除OPcache +7. ✅ 验证部署 + +**注意:** 需要输入服务器密码 `atBSGSu0$!T` + +--- + +## 方式2: 手动步骤 + +### 1. 上传诊断脚本 +```bash +scp server_diagnose.sh root@47.96.77.165:/www/wwwroot/fengketrade/ +``` + +### 2. SSH登录服务器 +```bash +ssh root@47.96.77.165 +# 密码: atBSGSu0$!T +``` + +### 3. 运行诊断 +```bash +cd /www/wwwroot/fengketrade +bash server_diagnose.sh +``` + +### 4. 上传修复文件(在本地新终端执行) +```bash +scp addons/shopro/library/ccblife/CcbPaymentService.php \ + root@47.96.77.165:/www/wwwroot/fengketrade/addons/shopro/library/ccblife/ +``` + +### 5. 重启PHP(在服务器上) +```bash +systemctl restart php-fpm +php -r "opcache_reset();" +``` + +--- + +## 验证部署 + +### 方法1: 监控日志 +```bash +ssh root@47.96.77.165 \ + 'tail -f /www/wwwroot/fengketrade/runtime/log/$(date +%Y%m)/$(date +%d).log' \ + | grep '最终支付串' +``` + +然后触发一次支付,观察输出: +- ✅ **正确**: `PROINFO=%u6F2B` (% 后直接是 u) +- ❌ **错误**: `PROINFO=%25u6F2B` (% 被编码成 %25) + +### 方法2: 检查关键代码行 +```bash +ssh root@47.96.77.165 \ + 'sed -n "279p" /www/wwwroot/fengketrade/addons/shopro/library/ccblife/CcbPaymentService.php' +``` + +应该输出: `$finalParts[] = $key . '=' . $value;` +不应包含: `urlencode` + +--- + +## 详细文档 + +查看 `SERVER_FIX_GUIDE.md` 获取完整的操作指南和故障排查。 + +--- + +## 已修复的问题 + +1. ✅ **URL编码问题**: 移除了最终支付串的URL编码 +2. ✅ **ENCPUB格式**: 转换为URL-safe BASE64 (+ → -, / → _) +3. ✅ **十六进制商户公钥**: 支持.env中的十六进制格式 + +--- + +## 预期结果 + +修复后,建行支付应该能成功拉起收银台,不再报"支付签名生成不成功"错误。 diff --git a/SERVER_FIX_GUIDE.md b/SERVER_FIX_GUIDE.md new file mode 100644 index 0000000..be573e8 --- /dev/null +++ b/SERVER_FIX_GUIDE.md @@ -0,0 +1,195 @@ +# 服务器修复指南 + +## 服务器信息 +- **地址**: 47.96.77.165 +- **用户**: root +- **密码**: atBSGSu0$!T +- **项目路径**: /www/wwwroot/fengketrade + +## 步骤1: 上传诊断脚本到服务器 + +在本地执行: +```bash +scp server_diagnose.sh root@47.96.77.165:/www/wwwroot/fengketrade/ +``` + +## 步骤2: 登录服务器 + +```bash +ssh root@47.96.77.165 +# 密码: atBSGSu0$!T +``` + +## 步骤3: 运行诊断脚本 + +```bash +cd /www/wwwroot/fengketrade +bash server_diagnose.sh +``` + +## 步骤4: 根据诊断结果修复 + +### 如果使用Git管理代码: + +```bash +cd /www/wwwroot/fengketrade + +# 查看当前状态 +git status + +# 拉取最新代码 +git pull + +# 或者如果有冲突,强制更新 +git fetch --all +git reset --hard origin/main # 或 origin/master +``` + +### 如果需要手动替换文件: + +```bash +# 在本地执行(上传修复后的文件) +scp addons/shopro/library/ccblife/CcbPaymentService.php \ + root@47.96.77.165:/www/wwwroot/fengketrade/addons/shopro/library/ccblife/ +``` + +## 步骤5: 重启PHP服务 + +```bash +# 方法1: systemctl +systemctl restart php-fpm + +# 方法2: service +service php-fpm restart + +# 方法3: 如果是宝塔面板 +bt restart php-fpm + +# 查看PHP进程 +ps aux | grep php-fpm +``` + +## 步骤6: 清除OPcache缓存 + +```bash +# 直接清除 +php -r "opcache_reset(); echo 'OPcache cleared\n';" + +# 或者创建清除脚本 +cat > /www/wwwroot/fengketrade/public/clear_cache.php << 'EOF' + +EOF + +# 通过浏览器访问: http://fengketrade.com/clear_cache.php +# 清除后记得删除这个文件: +# rm /www/wwwroot/fengketrade/public/clear_cache.php +``` + +## 步骤7: 实时监控日志验证 + +```bash +cd /www/wwwroot/fengketrade + +# 监控支付日志 +tail -f runtime/log/$(date +%Y%m)/$(date +%d).log | grep -E "建行支付.*最终支付串|MAC签名字符串" +``` + +## 步骤8: 触发测试支付 + +保持步骤7的日志监控,然后在手机或浏览器发起一次支付。 + +观察日志输出: + +**✅ 正确的日志:** +``` +[建行支付] MAC签名字符串: ...PROINFO=%u6F2B... +[建行支付] 最终支付串: ...PROINFO=%u6F2B... +``` + +**❌ 错误的日志:** +``` +[建行支付] MAC签名字符串: ...PROINFO=%u6F2B... +[建行支付] 最终支付串: ...PROINFO=%25u6F2B... ← %被编码成了%25 +``` + +## 关键检查点 + +修复后的代码第279行应该是: +```php +$finalParts[] = $key . '=' . $value; +``` + +**不应该**是: +```php +$finalParts[] = $key . '=' . urlencode($value); // ❌ 错误 +``` + +## 快速验证命令 + +```bash +# 检查第279行 +sed -n '279p' /www/wwwroot/fengketrade/addons/shopro/library/ccblife/CcbPaymentService.php + +# 应该输出: $finalParts[] = $key . '=' . $value; +# 如果包含 urlencode,说明代码还未更新 +``` + +## 常见问题 + +### Q1: git pull失败 +```bash +# 查看冲突 +git status + +# 放弃本地修改,强制更新 +git reset --hard +git pull +``` + +### Q2: PHP-FPM重启失败 +```bash +# 查看错误日志 +tail -50 /var/log/php-fpm/error.log + +# 检查PHP-FPM状态 +systemctl status php-fpm + +# 检查配置语法 +php-fpm -t +``` + +### Q3: 修复后仍然报签名错误 +```bash +# 1. 确认代码已更新 +md5sum /www/wwwroot/fengketrade/addons/shopro/library/ccblife/CcbPaymentService.php + +# 2. 确认PHP进程已重启 +ps aux | grep php-fpm | grep -v grep + +# 3. 检查文件权限 +ls -lh /www/wwwroot/fengketrade/addons/shopro/library/ccblife/CcbPaymentService.php + +# 4. 查看完整日志 +tail -100 /www/wwwroot/fengketrade/runtime/log/$(date +%Y%m)/$(date +%d).log +``` + +## 退出服务器 + +```bash +exit +``` + +## 注意事项 + +1. **备份**: 修改前建议备份当前文件 +2. **权限**: 确保PHP有权限读取修改后的文件 +3. **缓存**: 务必重启PHP-FPM和清除OPcache +4. **验证**: 修改后一定要查看日志验证是否生效 diff --git a/addons/shopro/library/ccblife/CcbEncryption.php b/addons/shopro/library/ccblife/CcbEncryption.php deleted file mode 100644 index c3efbd9..0000000 --- a/addons/shopro/library/ccblife/CcbEncryption.php +++ /dev/null @@ -1,447 +0,0 @@ -config = $config; - - // 加载密钥 - $this->loadKeys(); - } - - /** - * 加载密钥 - * - * @throws Exception - */ - private function loadKeys() - { - $this->privateKey = $this->config['private_key'] ?? ''; - $this->publicKey = $this->config['public_key'] ?? ''; - - if (empty($this->privateKey)) { - throw new Exception('服务方私钥未配置'); - } - - if (empty($this->publicKey)) { - throw new Exception('服务方公钥未配置'); - } - } - - /** - * RSA加密 (使用建行平台公钥加密) - * - * @param string $data 原始数据 - * @return string BASE64编码的加密数据 - * @throws Exception - */ - public function rsaEncrypt($data) - { - // 格式化公钥 - $publicKey = $this->formatKey($this->publicKey); - - // 获取公钥资源 - $pubKeyId = openssl_pkey_get_public($publicKey); - if (!$pubKeyId) { - throw new Exception('建行平台公钥格式错误: ' . openssl_error_string()); - } - - // ⚠️ 动态获取RSA密钥大小,而非写死117字节 - $keyDetails = openssl_pkey_get_details($pubKeyId); - if (!$keyDetails || !isset($keyDetails['bits'])) { - throw new Exception('无法获取RSA密钥详情'); - } - - $keySize = $keyDetails['bits'] / 8; // 密钥字节数: 1024位=128字节, 2048位=256字节 - $chunkSize = $keySize - 11; // PKCS1填充需要预留11字节 - - // RSA加密 (分段加密) - $encrypted = ''; - $dataLen = strlen($data); - - for ($i = 0; $i < $dataLen; $i += $chunkSize) { - $chunk = substr($data, $i, $chunkSize); - $encryptedChunk = ''; - - if (!openssl_public_encrypt($chunk, $encryptedChunk, $pubKeyId, OPENSSL_PKCS1_PADDING)) { - throw new Exception('RSA加密失败: ' . openssl_error_string()); - } - - $encrypted .= $encryptedChunk; - } - - // PHP 8+ 资源自动释放 - if (PHP_VERSION_ID < 80000) { - openssl_free_key($pubKeyId); - } - - // BASE64编码并去除换行符 - return str_replace(["\r", "\n"], '', base64_encode($encrypted)); - } - - /** - * RSA解密 (使用服务方私钥解密) - * - * @param string $data BASE64编码的加密数据 - * @return string 解密后的原始数据 - * @throws Exception - */ - public function rsaDecrypt($data) - { - // 格式化私钥 - $privateKey = $this->formatKey($this->privateKey, 'PRIVATE'); - - // 获取私钥资源 - $privKeyId = openssl_pkey_get_private($privateKey); - if (!$privKeyId) { - throw new Exception('服务方私钥格式错误: ' . openssl_error_string()); - } - - // ⚠️ 动态获取RSA密钥大小 - $keyDetails = openssl_pkey_get_details($privKeyId); - if (!$keyDetails || !isset($keyDetails['bits'])) { - throw new Exception('无法获取RSA密钥详情'); - } - - $keySize = $keyDetails['bits'] / 8; // 密钥字节数: 1024位=128字节, 2048位=256字节 - - // BASE64解码 - $encrypted = base64_decode($data); - - // RSA解密 (分段解密,每段密文长度等于密钥字节数) - $decrypted = ''; - $encryptedLen = strlen($encrypted); - - for ($i = 0; $i < $encryptedLen; $i += $keySize) { - $chunk = substr($encrypted, $i, $keySize); - $decryptedChunk = ''; - - if (!openssl_private_decrypt($chunk, $decryptedChunk, $privKeyId, OPENSSL_PKCS1_PADDING)) { - throw new Exception('RSA解密失败: ' . openssl_error_string()); - } - - $decrypted .= $decryptedChunk; - } - - // PHP 8+ 资源自动释放 - if (PHP_VERSION_ID < 80000) { - openssl_free_key($privKeyId); - } - - return $decrypted; - } - - /** - * 生成MD5签名 - * - * @param string $data 原始数据(JSON字符串) - * @return string 32位大写MD5签名 (与Java保持一致) - */ - public function generateSign($data) - { - // 签名规则: MD5(原始数据 + 服务方私钥) - $signString = $data . $this->privateKey; - return strtoupper(md5($signString)); // 转为大写,与Java一致 - } - - /** - * 验证MD5签名 - * - * @param string $data 原始数据 - * @param string $sign 签名 - * @return bool 验证结果 - */ - public function verifySign($data, $sign) - { - $expectedSign = $this->generateSign($data); - return $expectedSign === $sign; - } - - /** - * 构造完整加密报文 - * - * @param string $txCode 交易代码 (如: A3341TP01) - * @param array $bodyData 业务数据 - * @return array 加密后的完整报文 ['cnt' => '...', 'mac' => '...', 'svcid' => '...'] - * @throws Exception - */ - public function buildEncryptedMessage($txCode, $bodyData) - { - // 1. 构造原始报文 - $txSeq = $this->generateTransSeq(); - $txTime = date('YmdHis'); - - $message = [ - 'CLD_HEADER' => [ - 'CLD_TX_CHNL' => $this->config['service_id'], - 'CLD_TX_TIME' => $txTime, - 'CLD_TX_CODE' => $txCode, - 'CLD_TX_SEQ' => $txSeq, - ], - 'CLD_BODY' => $bodyData, - ]; - - // 2. 转换为JSON (不转义中文和斜杠) - $jsonData = json_encode($message, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); - if ($jsonData === false) { - throw new Exception('JSON编码失败: ' . json_last_error_msg()); - } - - // 3. RSA加密 - $encryptedData = $this->rsaEncrypt($jsonData); - - // 4. 生成MD5签名 - $sign = $this->generateSign($jsonData); - - // 5. 组装最终报文 - return [ - 'cnt' => $encryptedData, - 'mac' => $sign, - 'svcid' => $this->config['service_id'], - ]; - } - - /** - * 解析响应报文 - * - * @param string $response 响应JSON字符串 - * @return array 解析后的业务数据 - * @throws Exception - */ - public function parseResponse($response) - { - // 1. 解析JSON - $result = json_decode($response, true); - if (json_last_error() !== JSON_ERROR_NONE) { - throw new Exception('响应不是有效的JSON: ' . json_last_error_msg()); - } - - // 2. 检查是否包含加密字段 - if (!isset($result['cnt']) || !isset($result['mac'])) { - // 可能是错误响应,直接返回 - return $result; - } - - // 3. 验证签名 (如果启用) - if ($this->config['security']['verify_sign'] ?? true) { - // 解密后验证签名 - $decryptedData = $this->rsaDecrypt($result['cnt']); - - if (!$this->verifySign($decryptedData, $result['mac'])) { - throw new Exception('响应签名验证失败'); - } - - // 解析业务数据 - $businessData = json_decode($decryptedData, true); - if (json_last_error() !== JSON_ERROR_NONE) { - throw new Exception('业务数据解析失败: ' . json_last_error_msg()); - } - - return $businessData; - } else { - // 不验证签名,直接解密 - $decryptedData = $this->rsaDecrypt($result['cnt']); - return json_decode($decryptedData, true); - } - } - - /** - * 生成唯一交易流水号 - * - * 格式: YmdHis + 微秒 + 4位随机数 - * 示例: 202501161200001234567890 - * - * @return string 24位交易流水号 - */ - public function generateTransSeq() - { - $date = date('YmdHis'); // 14位 - $microtime = substr(microtime(), 2, 6); // 6位微秒 - $random = str_pad(mt_rand(0, 9999), 4, '0', STR_PAD_LEFT); // 4位随机数 - - return $date . $microtime . $random; // 24位 - } - - /** - * 生成支付流水号 - * - * 格式: PAY + YmdHis + 8位随机数 - * 示例: PAY2025011612000012345678 - * - * @return string 支付流水号 - */ - public function generatePayFlowId() - { - $prefix = 'PAY'; - $date = date('YmdHis'); // 14位 - $random = str_pad(mt_rand(0, 99999999), 8, '0', STR_PAD_LEFT); // 8位随机数 - - return $prefix . $date . $random; // 3 + 14 + 8 = 25位 - } - - /** - * 格式化密钥 (添加PEM头尾) - * - * 密钥格式说明: - * - 公钥: X.509格式 (BEGIN PUBLIC KEY) - * - 私钥: PKCS#8格式 (BEGIN PRIVATE KEY) - 与Java保持一致 - * - * @param string $key 密钥内容 (BASE64字符串,不含头尾) - * @param string $type 类型: PUBLIC 或 PRIVATE - * @return string 格式化后的PEM密钥 - */ - private function formatKey($key, $type = 'PUBLIC') - { - // 如果已经包含头尾,直接返回 - if (strpos($key, '-----BEGIN') !== false) { - return $key; - } - - // 添加头尾 (注意: 私钥使用PKCS#8格式,与Java的PKCS8EncodedKeySpec一致) - if ($type === 'PUBLIC') { - $header = "-----BEGIN PUBLIC KEY-----\n"; - $footer = "\n-----END PUBLIC KEY-----"; - } else { - // 使用PKCS#8格式 (不是RSA PRIVATE KEY) - $header = "-----BEGIN PRIVATE KEY-----\n"; - $footer = "\n-----END PRIVATE KEY-----"; - } - - // 每64个字符换行 - $key = chunk_split($key, 64, "\n"); - - return $header . $key . $footer; - } - - /** - * 生成支付串签名 - * - * 用于生成建行支付串的MAC签名 - * - * @param array $params 支付参数数组 - * @return string 32位大写MD5签名 (与Java保持一致) - */ - public function generatePaymentSign($params) - { - // 1. 按参数名ASCII排序 - ksort($params); - - // 2. 拼接成字符串: key1=value1&key2=value2&... - $signString = http_build_query($params); - - // 3. 追加平台公钥 - $signString .= '&PLATFORMPUB=' . $this->publicKey; - - // 4. 生成MD5签名 (转为大写,与Java一致) - return strtoupper(md5($signString . $this->privateKey)); - } - - /** - * 加密商户公钥 (用于支付串的ENCPUB字段) - * - * ⚠️ 已废弃:建行要求只加密公钥后30位,请使用 encryptMerchantPublicKeyLast30() - * - * @return string BASE64编码的加密公钥 - * @throws Exception - * @deprecated 使用 encryptMerchantPublicKeyLast30() 替代 - */ - public function encryptMerchantPublicKey() - { - if (empty($this->publicKey)) { - throw new Exception('服务方公钥未配置'); - } - - // 使用建行平台公钥加密商户公钥 - return $this->rsaEncrypt($this->publicKey); - } - - /** - * 加密商户公钥后30位 (用于支付串的ENCPUB字段) - * - * 根据建行文档v2.2规范: - * "使用服务方公钥对商户公钥后30位进行RSA加密并base64后的密文" - * - * @return string BASE64编码的加密密文 - * @throws Exception - */ - public function encryptMerchantPublicKeyLast30() - { - if (empty($this->publicKey)) { - throw new Exception('服务方公钥未配置'); - } - - // 取商户公钥的后30位 - $publicKeyContent = $this->publicKey; - - // 如果是PEM格式,去除头尾和换行符 - $publicKeyContent = str_replace([ - '-----BEGIN PUBLIC KEY-----', - '-----END PUBLIC KEY-----', - "\r", "\n", " " - ], '', $publicKeyContent); - - // 取后30位 - $last30Chars = substr($publicKeyContent, -30); - - if (strlen($last30Chars) < 30) { - throw new Exception('商户公钥长度不足30位'); - } - - // 使用建行平台公钥加密这30位字符 - return $this->rsaEncrypt($last30Chars); - } -} diff --git a/deploy_fix.sh b/deploy_fix.sh new file mode 100755 index 0000000..d2c4a87 --- /dev/null +++ b/deploy_fix.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# 一键部署修复脚本 +# 用法: bash deploy_fix.sh + +set -e # 遇到错误立即退出 + +SERVER="root@47.96.77.165" +SERVER_DIR="/www/wwwroot/fengketrade" +FILE_TO_UPLOAD="addons/shopro/library/ccblife/CcbPaymentService.php" + +echo "====================================" +echo "建行支付修复 - 一键部署" +echo "====================================" +echo "" + +# 检查本地文件是否存在 +if [ ! -f "$FILE_TO_UPLOAD" ]; then + echo "❌ 本地文件不存在: $FILE_TO_UPLOAD" + exit 1 +fi + +echo "【步骤1】检查本地代码..." +echo "-----------------------------------" +if grep -q "urlencode" <(sed -n '279p' "$FILE_TO_UPLOAD"); then + echo "❌ 本地代码第279行仍包含urlencode,请先修复本地代码" + exit 1 +else + echo "✅ 本地代码已修复" +fi +echo "" + +echo "【步骤2】上传诊断脚本到服务器..." +echo "-----------------------------------" +scp server_diagnose.sh $SERVER:$SERVER_DIR/ || { + echo "❌ 上传诊断脚本失败" + exit 1 +} +echo "✅ 诊断脚本上传成功" +echo "" + +echo "【步骤3】运行服务器端诊断..." +echo "-----------------------------------" +ssh $SERVER "cd $SERVER_DIR && bash server_diagnose.sh" || { + echo "❌ 服务器诊断失败" + exit 1 +} +echo "" + +echo "【步骤4】上传修复后的文件..." +echo "-----------------------------------" +echo "正在上传: $FILE_TO_UPLOAD" +scp $FILE_TO_UPLOAD $SERVER:$SERVER_DIR/$FILE_TO_UPLOAD || { + echo "❌ 文件上传失败" + exit 1 +} +echo "✅ 文件上传成功" +echo "" + +echo "【步骤5】重启PHP-FPM..." +echo "-----------------------------------" +ssh $SERVER "systemctl restart php-fpm || service php-fpm restart" || { + echo "⚠️ PHP-FPM重启可能失败,请手动检查" +} +echo "✅ PHP-FPM重启完成" +echo "" + +echo "【步骤6】清除OPcache..." +echo "-----------------------------------" +ssh $SERVER "php -r 'if(function_exists(\"opcache_reset\")){opcache_reset();echo \"OPcache cleared\n\";}'" || { + echo "⚠️ OPcache清除可能失败" +} +echo "" + +echo "【步骤7】验证部署..." +echo "-----------------------------------" +echo "检查服务器上的代码..." +ssh $SERVER "sed -n '279p' $SERVER_DIR/$FILE_TO_UPLOAD" +echo "" + +echo "====================================" +echo "✅ 部署完成!" +echo "====================================" +echo "" +echo "下一步:" +echo "1. 触发一次支付测试" +echo "2. 监控日志: ssh $SERVER 'tail -f $SERVER_DIR/runtime/log/\$(date +%Y%m)/\$(date +%d).log | grep 最终支付串'" +echo "3. 检查是否还有 %25u (错误) 还是 %u (正确)" +echo "" diff --git a/server_diagnose.sh b/server_diagnose.sh new file mode 100755 index 0000000..ff5615a --- /dev/null +++ b/server_diagnose.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# 服务器端建行支付诊断脚本 +# 在服务器上运行: bash server_diagnose.sh + +echo "====================================" +echo "建行支付服务器端诊断" +echo "====================================" +echo "" + +PROJECT_DIR="/www/wwwroot/fengketrade" +FILE="$PROJECT_DIR/addons/shopro/library/ccblife/CcbPaymentService.php" + +if [ ! -f "$FILE" ]; then + echo "❌ 文件不存在: $FILE" + exit 1 +fi + +echo "【1. 文件信息】" +echo "-----------------------------------" +ls -lh "$FILE" +echo "" + +echo "【2. 最后修改时间】" +echo "-----------------------------------" +stat "$FILE" | grep "Modify" +echo "" + +echo "【3. 检查第279行(最终支付串)】" +echo "-----------------------------------" +echo "应该是: \$finalParts[] = \$key . '=' . \$value;" +echo "实际是:" +sed -n '279p' "$FILE" +echo "" + +if grep -q "urlencode" <(sed -n '279p' "$FILE"); then + echo "❌ 错误: 第279行包含urlencode,需要修复!" +else + echo "✅ 正确: 第279行无URL编码" +fi +echo "" + +echo "【4. 检查第286行(非MAC参数)】" +echo "-----------------------------------" +sed -n '286p' "$FILE" +if grep -q "urlencode" <(sed -n '286p' "$FILE"); then + echo "❌ 错误: 第286行包含urlencode" +else + echo "✅ 正确: 第286行无URL编码" +fi +echo "" + +echo "【5. 检查ENCPUB的URL-safe处理】" +echo "-----------------------------------" +if grep -q "str_replace(\['+', '/'\], \['-', '_'\]" "$FILE"; then + echo "✅ 找到URL-safe BASE64转换" + grep -n "str_replace(\['+', '/'\]" "$FILE" | head -1 +else + echo "❌ 未找到URL-safe BASE64转换" +fi +echo "" + +echo "【6. PHP版本和OPcache状态】" +echo "-----------------------------------" +php -v | head -1 +php -r "echo 'OPcache enabled: ' . (function_exists('opcache_reset') ? 'Yes' : 'No') . \"\n\";" +echo "" + +echo "【7. 最近的支付日志】" +echo "-----------------------------------" +LOGFILE="$PROJECT_DIR/runtime/log/$(date +%Y%m)/$(date +%d).log" +if [ -f "$LOGFILE" ]; then + echo "日志文件: $LOGFILE" + echo "最近的支付串:" + tail -50 "$LOGFILE" | grep "最终支付串" | tail -1 + echo "" + echo "检查是否有URL编码问题:" + if tail -50 "$LOGFILE" | grep "最终支付串" | grep -q "%25u"; then + echo "❌ 发现URL编码问题: %被编码成%25" + else + echo "✅ 未发现%25,可能已修复或未测试" + fi +else + echo "日志文件不存在: $LOGFILE" +fi +echo "" + +echo "【8. 建议操作】" +echo "-----------------------------------" +echo "如果发现代码有问题,执行以下步骤:" +echo "" +echo "1. 检查git状态:" +echo " cd $PROJECT_DIR" +echo " git status" +echo "" +echo "2. 拉取最新代码:" +echo " git pull" +echo "" +echo "3. 重启PHP-FPM:" +echo " systemctl restart php-fpm" +echo " # 或者" +echo " service php-fpm restart" +echo "" +echo "4. 清除OPcache:" +echo " php -r 'opcache_reset();'" +echo "" +echo "5. 再次测试支付,查看日志:" +echo " tail -f $PROJECT_DIR/runtime/log/$(date +%Y%m)/$(date +%d).log | grep '最终支付串'" +echo "" + +echo "====================================" +echo "诊断完成" +echo "===================================="