微信官方验证签名文档https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-yan-zheng
//获取平台证书序列,解密后是平台公钥 回调验证签名要用到
public
function get_cert()
{
$url = 'https://api.mch.weixin.qq.com/v3/certificates';
$url_parts = parse_url($url);
$http_method = 'GET';
$timestamp = time();
// $nonce = md5(time() . $out_trade_no);
$nonce = $this->set_nonce();//生成随机字符串
$body = '';
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$message = $http_method . "\n" .
$canonical_url . "\n" .
$timestamp . "\n" .
$nonce . "\n" .
$body . "\n";
$sign = $this->wechat_sign($message);//签名 和统一下单 调起支付签名一样
$rs = $this->wechat_req($nonce, $timestamp, $sign, $url, $http_method, '');
//保存
// $path = APPPATH . '/Wechat/wxp_cert.pem';//平台证书序列
// file_put_contents($path, json_encode($rs));
return $rs;
}
在这里插入代码片
//回调首先是要接收头部信息,回调方法里最好保存回调信息用来调试
$response_body回调主体接收 :
$response_body= file_get_contents(‘php://input’);
$header的接收要注意,如果服务器不是Apche不要用getallheaders
//百度Nginx getallheaders 就可以找到替代方法
我用的CI框架$header = $this->input->request_headers();
TP应该是$this->request->header();具体看官方文档
在这里插入代码片
public
function verify_wechatnotify($header, $response_body)
{
//$header 回调头部信息 //$response_body回调主体
//获取平台证书
$plat = $this->get_cert();
// $path = 'XX/Wechat/wxp_cert.pem';//平台证书
// $plat_cert = file_get_contents($path);
// $plat = json_decode($plat_cert, true);
//serial_no平台证书序列号 //跟商户证书序列号不一样 不要搞混了
$serial_no = $plat['data'][0]['serial_no'];
//验证回调头部信息中的平台证书序列号跟平台证书序列号是否一致
if ($header['Wechatpay-Serial'] != $serial_no) {
return $data=['code'=>'0','msg'=> '平台证书序列号不一致,签名验证不通过'];
}
//解密平台证书序列
$associatedData = $plat['data'][0]['encrypt_certificate']['associated_data'];
$nonceStr = $plat['data'][0]['encrypt_certificate']['nonce'];
$ciphertext = $plat['data'][0]['encrypt_certificate']['ciphertext'];
//官方解密文档 https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi
$plat_cert_decrtpt = $this->decryptToString($associatedData, $nonceStr, $ciphertext);//平台证书解密后即生成公钥 //这个解密方法看[官方文档](https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi)https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi
//$pub_path = 'XX/Wechat/wxp_pub.pem';
// file_put_contents($pub_path, $plat_cert_decrtpt);//平台公钥 //保存平台公钥 回调验签要用
//$plat_cert_decrtpt = file_get_contents($pub_path);
$pub_key = openssl_pkey_get_public($plat_cert_decrtpt);//平台公钥
$Signature = $header['Wechatpay-Signature'];
$message = $header['Wechatpay-Timestamp'] . "\n" . $header['Wechatpay-Nonce'] . "\n" . $response_body . "\n";
$sign = base64_decode($Signature);
$ok = openssl_verify($message, $sign, $pub_key, OPENSSL_ALGO_SHA256);//回调验签
if ($ok == 1) {
// return $this->error('1', '回调签名验证成功');
return 1;
} else {
return false;
}
}
注意:商户证书跟平台证书是不一样的,我一开始一直拿商户证书来验签,浪费了时间。后面的获取平台证书序列了之后,一直不知道怎么生成平台公钥。其实解密之后就是公钥了