yansongda / pay

可能是我用过的最优雅的 Alipay/WeChat/Douyin/Unipay/江苏银行 的支付 SDK 扩展包了
http://pay.yansongda.cn
MIT License
5k stars 1.03k forks source link

fpm模式,微信平台公钥证书校验不通过 #947

Closed YanFengQiLi closed 5 months ago

YanFengQiLi commented 5 months ago

包版本号

v3.5.3

问题描述

Laravel 版本:9.52.16

php版本:8.1.27

微信平台公钥证书校验不通过,我确保了,微信公钥证书已经配置在了config文件中。 并且生成方式,就是利用示例代码完成的。

\Yansongda\Pay\Pay::config($config);

$params = [ '_config' => 'default' // 多租户配置时使用 ];

\Yansongda\Pay\get_wechat_public_certs($params, storage_path('pay/wechat_default'));

你的代码

我是一个多租户的配置 config.php 配置信息: return [ 'wechat' => [ 'default' => [ // 商户号 'mch_id' => '', // v3 商户秘钥 'mch_secret_key' => '', // 商户私钥证书路径 'mch_secret_cert' => '', // 商户公钥证书路径 'mch_public_cert_path' => '', // 公众号 appID 'mp_app_id' => '', // 微信平台公钥证书路径 'wechat_public_cert_path' => [ 'xxx' => storage_path( 'xxx' ), ], ], 'beijing' => [ // 商户号 'mch_id' => '', // v3 商户秘钥 'mch_secret_key' => '', // 商户私钥证书路径 'mch_secret_cert' => '', // 商户公钥证书路径 'mch_public_cert_path' => '', // 公众号 appID 'mp_app_id' => '', // 微信平台公钥证书路径 'wechat_public_cert_path' => [ 'xxxxx' => storage_path( '' ), ], ] ] ];

拉起支付代码: $config = $this->getPayConfigByRegion($region);

$result = Pay::wechat($config)->scan([ 'out_trade_no' => $info->payment_no, 'description' => $this->getProductService($product), 'amount' => [ 'total' => $this->getPayMoney($info->need_amount), ], 'notify_url' => env('APP_URL') . '/api/v1/wechat/qrNotify/' . $region, '_config' => $config['_config'] ]);

支付回调代码: // 动态获取配置 $config = $this->getPayConfigByRegion($region);

// 接受微信二维码回调 $result = Pay::wechat($config)->callback(null, [ '_config' => $config['_config'] ])->toArray();

报错详情

Yansongda\Pay\Exception\InvalidResponseException

"Exception Basic": { "class": "Yansongda\Pay\Exception\InvalidResponseException", "message": "", "code": 5003, "file": "xxx/vendor/yansongda/pay/src/Functions.php", "line": 214 }, "Exception Context": { " 205": "", " 206": " $result = 1 === openssl_verify(", " 207": " $content,", " 208": " base64_decode($sign),", " 209": " $public,", " 210": " 'sha256WithRSAEncryption'", " 211": " );", " 212": "", " 213": " if (!$result) {", "➤ 214": " throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, '', ['headers' => $message->getHeaders(), 'body' => $body]);", " 215": " }", " 216": "}", " 217": "", " 218": "function encrypt_wechat_contents(string $contents, string $publicKey): ?string", " 219": "{", " 220": " if (openssl_public_encrypt($contents, $encrypted, get_public_cert($publicKey), OPENSSL_PKCS1_OAEP_PADDING)) {", " 221": " return base64_encode($encrypted);", " 222": " }", " 223": "", " 224": " return null;" }

sdk 日志

nginx/apache 日志

涉及到 异步通知、同步通知 的问题,请贴出来

yansongda commented 5 months ago

try catch 看具体错误是啥

PS:最好先自己排查下,您这提供的信息应该谁也判断不了具体问题

YanFengQiLi commented 5 months ago

try catch 看具体错误是啥

PS:最好先自己排查下,您这提供的信息应该谁也判断不了具体问题

我这边本地排查过,我看了 verify_wechat_sign() 这个源码,我能确定 get_public_cert() 时能获取到微信公钥证书,现在的问题是,以下这段代码块中,openssl_verify 函数返回值为0导致的。我这边也追了,尽管报了这个错误,但是支付回调全部都是成功的!!! 而且服务器的日志,不是所有的支付回调都报这个错误。比如一天支付了N单,总是有一单会报这个错误。注:现在虽然是多租户的配置,由于业务原因,支付现在都走的 default 配置。

 $result = 1 === openssl_verify(
        $content,
        base64_decode($sign),
        $public,
        'sha256WithRSAEncryption'
    );
mengdodo commented 5 months ago

yansongda/pay V3.5 @yansongda 我也遇到这个问题了,V3的服务商模式支付出去没问题,回调也很好,就是莫名其妙过上1天或者两天又来上一条回调【"event_type":"TRANSACTION.SUCCESS","summary":"支付成功"】 ,但是verify_wechat_sign就报错。

{"exception":"[object] (Yansongda\\Pay\\Exception\\InvalidResponseException(code: 5003):  at /www/wwwroot/payment.ngtyw.com/vendor/yansongda/pay/src/Functions.php:214)
[stacktrace]
#0 /www/wwwroot/payment.ngtyw.com/vendor/yansongda/pay/src/Plugin/Wechat/CallbackPlugin.php(40): Yansongda\\Pay\\verify_wechat_sign()
#1 /www/wwwroot/payment.ngtyw.com/vendor/yansongda/supports/src/Pipeline.php(93): Yansongda\\Pay\\Plugin\\Wechat\\CallbackPlugin->assembly()
#2 /www/wwwroot/payment.ngtyw.com/vendor/yansongda/supports/src/Pipeline.php(57): Yansongda\\Supports\\Pipeline->Yansongda\\Supports\\{closure}()
#3 /www/wwwroot/payment.ngtyw.com/vendor/yansongda/pay/src/Provider/AbstractProvider.php(75): Yansongda\\Supports\\Pipeline->then()
#4 /www/wwwroot/payment.ngtyw.com/vendor/yansongda/pay/src/Provider/Wechat.php(122): Yansongda\\Pay\\Provider\\AbstractProvider->pay()
#5 /www/wwwroot/payment.ngtyw.com/app/Http/Controllers/Api/Pay/WechatPartnerController.php(187): Yansongda\\Pay\\Provider\\Wechat->callback()
yansongda commented 5 months ago

如果有成功的,有失败的,考虑:

1、有人攻击; 2、微信官方的流量探测:https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html#%E5%BA%94%E5%AF%B9%E7%AD%BE%E5%90%8D%E6%8E%A2%E6%B5%8B%E6%B5%81%E9%87%8F

正常 try catch 返回错误的响应即可,无需特殊处理

YanFengQiLi commented 5 months ago

@yansongda 好的,我去试试,我 try catch 包含回调代码块就可以把 $result = Pay::wechat($config)->callback(null, [ '_config' => $config['_config'] ])->toArray();

mengdodo commented 5 months ago

如果有成功的,有失败的,考虑:

1、有人攻击; 2、微信官方的流量探测:https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html#%E5%BA%94%E5%AF%B9%E7%AD%BE%E5%90%8D%E6%8E%A2%E6%B5%8B%E6%B5%81%E9%87%8F

正常 try catch 返回错误的响应即可,无需特殊处理 感谢,捕获到了header头,确实是探测的WECHATPAY/SIGNTEST前缀。

"wechatpay-signature-type":["WECHATPAY2-SHA256-RSA2048"],
"wechatpay-signature":["WECHATPAY/SIGNTEST/gnpWTGp/Aenlxh1ufF……