在基于 riverslei/payment 实现支付宝退款时,报错:request get failed, msg[Invalid Arguments], sub_msg[验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配
1、在基于 riverslei/payment 实现支付宝退款时,报错:request get failed, msg[Invalid Arguments], sub_msg[验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配。如图1
2、你提供的是一个 PKCS#1 格式的 RSA 私钥(以 MIIEvQ… 开头,未带头尾标识),需要通过 OpenSSL 命令将它转换为 PKCS#8 格式的 RSA 私钥,支付宝才能识别。将私钥内容复制至文件 pkcs1.pem,然后执行如下命令。 如图2
$ openssl pkcs8 -topk8 -inform PEM -in pkcs1.pem -out pkcs8.pem -outform PEM -nocrypt wangqiang@DESKTOP-F6AO3M2 MINGW64 /C/wwwroot/apply-server/src (feature/alipay)
3、最后将 pkcs8.pem 中的内容保存至 ali_rsa_private_key。如图3
4、提示有所变化,提示:sign error, sign type is [RSA2]. msg: [您使用的私钥格式错误,请检查RSA私钥配置]。如图4
5、编辑文件 vendor/riverslei/payment/src/Helpers/Rsa2Encrypt.php,
/** * RSA2签名, 此处秘钥是私有秘钥 * @param string $data 签名的数组 * @throws \Exception * @return string * @author Leo */ public function encrypt($data) { if ($this->key === false) { return ''; } \Yii::info( [ 'data' => $data, 'key' => $this->key ] ); $res = openssl_get_privatekey($this->key); if (empty($res)) { throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置1'); } openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256); openssl_free_key($res); //base64编码 $sign = base64_encode($sign); return $sign; }
6、查看 $this->key,竟然有 2 个 —–BEGIN RSA PRIVATE KEY—–、—–BEGIN PRIVATE KEY—– 。如图5
7、将 pkcs8.pem 中的内容保存至 ali_rsa_private_key 时,先清理掉 —–BEGIN RSA PRIVATE KEY—–。如图6
$aliConfig = [ 'use_sandbox' => false, // 是否使用沙盒模式 'app_id' => $payee->ali_app_id, 'sign_type' => $payee->ali_sign_type, // RSA RSA2 // 支付宝公钥字符串 'ali_public_key' => $payee->ali_public_key, // 自己生成的密钥字符串 'rsa_private_key' => $payee->ali_rsa_private_key, 'fee_type' => 'CNY', // 货币类型 当前仅支持该字段 ];
8、再次提示:request get failed, msg[Invalid Arguments], sub_msg[验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配,网关生成的验签字符串为
9、查看文件 vendor/riverslei/payment/src/Helpers/StrUtil.php ,发现会自动将 PKCS#1 格式的 RSA 私钥(以 MIIEvQ… 开头,未带头尾标识),转换为 旧版 PKCS#1 格式的 RSA 私钥。最后发现仍然报错:sub_msg[验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配,网关生成的验签字符串为
/** * 获取rsa密钥内容 * @param string $key 传入的密钥信息, 可能是文件或者字符串 * @param string $type * * @return string */ public static function getRsaKeyValue($key, $type = 'private') { if (is_file($key)) {// 是文件 $keyStr = @file_get_contents($key); } else { $keyStr = $key; } if (empty($keyStr)) { return null; } $keyStr = str_replace(PHP_EOL, '', $keyStr); // 为了解决用户传入的密钥格式,这里进行统一处理 if ($type === 'private') { $beginStr = '-----BEGIN RSA PRIVATE KEY-----' . PHP_EOL; $endStr = PHP_EOL . '-----END RSA PRIVATE KEY-----'; } else { $beginStr = '-----BEGIN PUBLIC KEY-----' . PHP_EOL; $endStr = PHP_EOL . '-----END PUBLIC KEY-----'; } $rsaKey = $beginStr . wordwrap($keyStr, 64, "\n", true) . $endStr; return $rsaKey; }
10、编辑 vendor/riverslei/payment/src/Supports/HttpRequest.php,验签成功
/** * @param string $method * @param string $url * @param array $options * @return array|ResponseInterface|string|mixed */ private function sendRequest(string $method, string $url, array $options = []) { \Yii::info( [ '$method' => $method, '$url' => $url, '$options' => $options ] ); // return $this->unwrapResponse($this->getHttpClient($this->getBaseOptions())->{$method}($url, $options)); return $this->unwrapResponse($this->getHttpClient($this->getBaseOptions())->{$method}($url, [ 'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'], 'body' => http_build_query($options['query'], '', '&', PHP_QUERY_RFC3986), ])); }
11、但是,由于响应中有乱码,进而导致 json_decode 报错。如图7
$ret = $this->get($this->gatewayUrl, $params); if (!mb_check_encoding($ret, 'UTF-8')) { $ret = mb_convert_encoding($ret, 'UTF-8', 'GBK'); }
12、报错:验签出错,请确认charset参数放在了URL查询字符串中且各参数值使用charset参数指示的字符集编码 。如图8
13、仔细对比 请求前后的签名字符串,发现 return_url= 丢失,决定在配置时删除 return_url。原因在于
/** * 获取基础数据 * @param string $method * @param array $bizContent * @return array */ private function getBaseData(string $method, array $bizContent) { $requestData = [ 'app_id' => self::$config->get('app_id', ''), 'method' => $method, 'format' => 'JSON', 'return_url' => self::$config->get('return_url', ''), 'charset' => 'utf-8', 'sign_type' => self::$config->get('sign_type', ''), 'timestamp' => date('Y-m-d H:i:s'), 'version' => '1.0', 'notify_url' => self::$config->get('notify_url', ''), // 'app_auth_token' => '', // 暂时不用 'biz_content' => json_encode($bizContent, JSON_UNESCAPED_UNICODE), ]; return ArrayUtil::arraySort($requestData); }
echo html_entity_decode('app_id=2021005146654430&biz_content={"out_trade_no":"1831248803313247","refund_amount":"0.01","refund_currency":"CNY","refund_reason":"Refund for Conference Registration Fee (Ticket)","out_request_no":"1831248803313311"}&charset=utf-8&format=JSON&method=alipay.trade.refund&notify_url=https://5347-43-249-50-182.ngrok-free.app/ali-payment/notify-ticket-order/1831248803313247&sign_type=RSA2&timestamp=2025-05-10 16:39:56&version=1.0'); // 请求前的签名字符串 // app_id=2021005146654430&biz_content={"out_trade_no":"1831248803313247","refund_amount":"0.01","refund_currency":"CNY","refund_reason":"Refund for Conference Registration Fee (Ticket)","out_request_no":"1831248803313311"}&charset=utf-8&format=JSON&method=alipay.trade.refund¬ify_url=https://5347-43-249-50-182.ngrok-free.app/ali-payment/notify-ticket-order/1831248803313247&return_url=&sign_type=RSA2×tamp=2025-05-10 16:39:56&version=1.0 // 请求后报错,支付宝使用的签名字符串 // app_id=2021005146654430&biz_content={"out_trade_no":"1831248803313247","refund_amount":"0.01","refund_currency":"CNY","refund_reason":"Refund for Conference Registration Fee (Ticket)","out_request_no":"1831248803313311"}&charset=utf-8&format=JSON&method=alipay.trade.refund¬ify_url=https://5347-43-249-50-182.ngrok-free.app/ali-payment/notify-ticket-order/1831248803313247&sign_type=RSA2×tamp=2025-05-10 16:39:56&version=1.0
14、支付宝返回的验签字符串中确实不会包含空值字段,例如 return_url=(空值)这一项,而你在参与签名时包含了它,导致签名字符串不一致,从而验签失败。最终决定调整配置
$aliConfig = [ 'use_sandbox' => false, // 是否使用沙盒模式 'app_id' => $payee->ali_app_id, 'sign_type' => $payee->ali_sign_type, // RSA RSA2 // 支付宝公钥字符串 'ali_public_key' => $payee->ali_public_key, // 自己生成的密钥字符串 'rsa_private_key' => $payee->ali_rsa_private_key, 'limit_pay' => [ // 'balance', // 余额 // 'moneyFund', // 余额宝 // 'debitCardExpress', // 借记卡快捷 // 'creditCard', // 信用卡 // 'creditCardExpress', // 信用卡快捷 // 'creditCardCartoon', // 信用卡卡通 // 'credit_group', // 信用支付类型(包含信用卡卡通、信用卡快捷、花呗、花呗分期) ], // 用户不可用指定渠道支付当有多个渠道时用“,”分隔 // 与业务相关参数 'notify_url' => 'notify_url', 'return_url' => 'return_url', 'fee_type' => 'CNY', // 货币类型 当前仅支持该字段 ];
15、不再报错。打印退款的响应
$result = $client->refund($refundData); Yii::info( [ 'result' => $result, 'data' => $refundData, ], 'refund ali' ); [ 'result' => [ 'code' => '10000', 'msg' => 'Success', 'buyer_logon_id' => 'shu***@163.com', 'fund_change' => 'Y', 'gmt_refund_pay' => '2025-05-10 17:01:59', 'out_trade_no' => '1831248803313320', 'refund_detail_item_list' => [ [ 'amount' => '0.01', 'fund_channel' => 'ALIPAYACCOUNT', ], ], 'refund_fee' => '0.01', 'send_back_fee' => '0.01', 'trade_no' => '2025051022001491671421159148', 'buyer_open_id' => '067IqA-DLOIYEoiWWh1KNCyoaiY-qUzekwwoGX_3VQKI9s7', ], 'data' => [ 'trade_no' => '1831248803313320', 'refund_fee' => '0.01', 'reason' => 'Refund for Conference Registration Fee (Ticket)', 'refund_no' => '1831248803313321', ], ]
近期评论