APIv3回调通知
- 从请求头部
Headers
,拿到Wechatpay-Signature
、Wechatpay-Nonce
、Wechatpay-Timestamp
、Wechatpay-Serial
及Request-ID
,商户侧Web
解决方案可能有差异,请求头可能大小写不敏感,请根据自身应用来定; - 获取请求
body
体的JSON
纯文本; - 检查通知消息头标记的
Wechatpay-Timestamp
偏移量是否在5分钟之内; - 调用
SDK
内置方法,构造验签名串然后经Rsa::verfify
验签; - 消息体需要解密的,调用
SDK
内置方法解密; - 如遇到问题,请拿
Request-ID
点击这里,联系官方在线技术支持;
样例代码如下:
use WeChatPay\Util\PemUtil;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Formatter;
$inWechatpaySignature = '';// 请根据实际情况获取
$inWechatpayTimestamp = '';// 请根据实际情况获取
$inWechatpaySerial = '';// 请根据实际情况获取
$inWechatpayNonce = '';// 请根据实际情况获取
$inBody = '';// 请根据实际情况获取,例如: file_get_contents('php://input');
$apiv3Key = '';// 在商户平台上设置的APIv3密钥
// 根据通知的平台证书序列号,查询本地平台证书文件,
// 假定为 `/path/to/wechatpay/inWechatpaySerial.pem`
$certInstance = PemUtil::loadCertificate('/path/to/wechatpay/inWechatpaySerial.pem');
// 检查通知时间偏移量,允许5分钟之内的偏移
$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
$verifiedStatus = Rsa::verify(
// 构造验签名串
Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature,
$certInstance
);
if ($timeOffsetStatus && $verifiedStatus) {
$inBodyArray = (array)json_decode($inBody, true);
['resource' => [
'ciphertext' => $ciphertext,
'nonce' => $nonce,
'associated_data' => $aad
]] = $inBodyArray;
$inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
$inBodyResourceArray = (array)json_decode($inBodyResource, true);
// print_r($inBodyResourceArray);// 打印解密后的结果
}
APIv2回调通知
- 从请求头
Headers
获取Request-ID
,商户侧Web
解决方案可能有差异,请求头的Request-ID
可能大小写不敏感,请根据自身应用来定; - 获取请求
body
体的XML
纯文本; - 调用
SDK
内置方法,根据签名算法做本地数据签名计算,然后与通知文本的sign
做Hash::equals
对比验签; - 消息体需要解密的,调用
SDK
内置方法解密; - 如遇到问题,请拿
Request-ID
点击这里,联系官方在线技术支持;
样例代码如下:
use WeChatPay\Transformer;
use WeChatPay\Crypto\Hash;
use WeChatPay\Crypto\AesEcb;
use WeChatPay\Formatter;
$inBody = '';// 请根据实际情况获取,例如: file_get_contents('php://input');
$apiv2Key = '';// 在商户平台上设置的APIv2密钥
$inBodyArray = Transformer::toArray($inBody);
// 部分通知体无`sign_type`,部分`sign_type`默认为`MD5`,部分`sign_type`默认为`HMAC-SHA256`
// 部分通知无`sign`字典
// 请根据官方开发文档确定
['sign_type' => $signType, 'sign' => $sign] = $inBodyArray;
$calculated = Hash::sign(
$signType ?? Hash::ALGO_MD5,// 如没获取到`sign_type`,假定默认为`MD5`
Formatter::queryStringLike(Formatter::ksort($inBodyArray)),
$apiv2Key
);
$signatureStatus = Hash::equals($calculated, $sign);
if ($signatureStatus) {
// 如需要解密的
['req_info' => $reqInfo] = $inBodyArray;
$inBodyReqInfoXml = AesEcb::decrypt($reqInfo, Hash::md5($apiv2Key));
$inBodyReqInfoArray = Transformer::toArray($inBodyReqInfoXml);
// print_r($inBodyReqInfoArray);// 打印解密后的结果
}
$inBody = '';// 请根据实际情况获取,例如: file_get_contents('php://input');
这$inBody 如果获取的是 file_get_contents('php://input') 那么 下面提交的时候 还需要
$inBody = "$inWechatpayTimestamp\n$inWechatpayNonce\n$inBody\n";
研究了我一天。妹的! 说又不说清楚 还留点坑给别人挖。
$apiv3Key = ''; 这个代码没用。
<?php
Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody)
点个赞
大哥 你这两个参数没用到啊
$inWechatpaySerial = '';
$inWechatpayNonce = '';
确定这样能行吗? 我试了不行哦
应该要调用 ClientJsonTrait::verifier() 才行吧 我看代码 只有这里有验签的。
$inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
V3回调 这个解密不出来,是什么原因呢?
$verifiedStatus = Rsa::verify(
// 构造验签名串
Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature,
$certInstance
);
这里一种报Argument 2 passed to WeChatPay\Crypto\Rsa::verify() must be of the type string, null given
// 根据通知的平台证书序列号,查询本地平台证书文件,
这inWechatpaySerial.pem没明白在那里生成,是指那个平台证书序列号?那个本地平台?
php CertificateDownloader.php -k 1234567 -m 15111507000 -f 20211028_cert\apiclient_key.pem -s 28CD7B3DA9D8658E19FEF06ADC88C56400A954
$inWechatpaySignature = '';// 请根据实际情况获取
$inWechatpayTimestamp = '';// 请根据实际情况获取
$inWechatpaySerial = '';// 请根据实际情况获取
$inWechatpayNonce = '';// 请根据实际情况获取、
这四个参数是啥,要填啥