微信支付平台证书下载后,下载器会用获得的平台证书对返回的消息进行验签。下载器同时开启了 Guzzle 的 debug => true 参数,方便查询请求/响应消息的基础调试信息。
composer exec CertificateDownloader.php -- -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
先获取微信平台的公钥证书,商户的公钥/秘钥,v3接口秘钥可以在微信商户平台下载,保存到服务器
/**
* [createAuthorization 获取接口授权header头信息]
* @param [type] $url [请求地址]
* @param array $data [请求参数]
* @param string $method [请求方式]
* @return [type] [description]
*/
//生成v3 Authorization
protected static function createAuthorization($url, $data = [], $method = 'POST') {
$config = self::$config;
//商户号
$mchid = $config['mchid'];
// 证书序列号
$serial_no = $config['serial_no'];
// 解析url地址
$url_parts = parse_url($url);
//生成签名
$body = [
'method' => $method,
'url' => ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : '')),
'time' => time(), // 当前时间戳
'nonce' => self::get_rand_str(32, 0, 1), // 随机32位字符串
'data' => ((strtolower($method) == 'post' || strtolower($method) == 'patch') ? json_encode($data,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : ''), // POST请求时 需要 转JSON字符串
];
$sign = self::makeSign($body);
//Authorization 类型
$schema = 'WECHATPAY2-SHA256-RSA2048';
//生成token
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mchid, $body['nonce'], $body['time'], $serial_no, $sign);
$header = [
'Content-Type:application/json',
'Accept:application/json',
//'User-Agent:*/*',
'Authorization: ' . $schema . ' ' . $token
];
return $header;
}
/**
* [makeSign 生成签名]
* @param [type] $data [加密数据]
* @return [type] [description]
*/
public static function makeSign($data) {
$config = self::$config;
if(!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
throw new \RuntimeException('当前PHP环境不支持SHA256withRSA');
}
// 拼接生成签名所需的字符串
$message = '';
foreach($data as $value) {
$message .= $value . "\n";
}
// 获取商户私钥
$private_key = self::getPrivateKey($config['cert_key']);
// 生成签名
openssl_sign($message, $sign, $private_key, 'sha256WithRSAEncryption');
return base64_encode($sign);
}
//获取私钥
public static function getPrivateKey($filepath) {
return openssl_pkey_get_private(file_get_contents($filepath));
}
以上介绍的是微信支付加签的代码。
下面在介绍一下微信支付回调的解密的方法,微信回调的时候,会返回associated_data, nonceStr, ciphertext三个报文,下面我们来看看支付解密的代码:
/**
* [decryptToString 证书和回调报文解密]
* @param [type] $associatedData [附加数据包(可能为空)]
* @param [type] $nonceStr [加密使用的随机串初始化向量]
* @param [type] $ciphertext [Base64编码后的密文]
* @return [type] [description]
*/
public static function decryptToString($associatedData, $nonceStr, $ciphertext) {
$config = self::$config;
$privateKey = $config['api_cert_key'];
$ciphertext = \base64_decode($ciphertext);
if(strlen($ciphertext) <= self::KEY_LENGTH_BYTE) {
return false;
}
// ext-sodium (default installed on >= PHP 7.2)
if(function_exists('\sodium_crypto_aead_aes256gcm_is_available') &&
\sodium_crypto_aead_aes256gcm_is_available()) {
return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $privateKey);
}
// ext-libsodium (need install libsodium-php 1.x via pecl)
if(function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
\Sodium\crypto_aead_aes256gcm_is_available()) {
return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $privateKey);
}
// openssl (PHP >= 7.1 support AEAD)
if(PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
$ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
$authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
return \openssl_decrypt($ctext, 'aes-256-gcm', $privateKey, \OPENSSL_RAW_DATA, $nonceStr,
$authTag, $associatedData);
}
throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
}
微信回调响应的数据如下:
{"id":"7a91137f-898f-5c29-a564-5309206c5423","create_time":"2022-10-09T10:21:18+08:00","resource_type":"encrypt-resource","event_type":"TRANSACTION.SUCCESS","summary":"\u652f\u4ed8\u6210","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"rssdf1IXDjcdD5JJysq4VnjxTC0sFIac4HQBXQvVeBc64pCLpOmNQgtreP+9xiJwZ9wwfQjpxbduT+xHirFtndbD1DOD3mHqfbhLdMhrFAe0QD+3w9xhNH0YpXFL5tX2+l8zcooZVz3xv\/dJoRxINO8P4CAhBHZap7VYf9RoxrCfqpl2Ayr5wk02d9t1Qn27JE7MYAPXj3uHLL6yy0zY+MfvHCgX5efBwKDQ13N0f8F+90keDvtviqprx2koktzkrSBkycUBSLdJkEaZcuKBf0p601ozujedFqR0peEiNcFS7JPTur0qVkU2nX\/D5GKBBkwPrqKL6gbzckfxu9fKXnNpOrLpSB\/o4jh\/JmRcBN1lIJqUiiZs83qAzPp8tHwZD9+kSpkFcyTmDi4tuibtSD5bc0PNSN3XZIP9acqm07RJbZQ5DInVV3gl17DVQpL72BMDKDjOwEbcMRAx\/4b0UuHgpgELMjJq4FEaBFukIXJOqGkt\/8rshiGQznunUn3l2ulLkohJfF1BOXcwD","associated_data":"transaction","nonce":"p7u0zWPEaNKa"}}
解密之后得到:
{"OrderNo":"SHOP8888888888888888","transaction_id":"4200006668888888888888888","trade_type":"MWEB","trade_state":"SUCCESS","success_time":"2022-05-10T09:18:51+08:00","appid":"wx8888888888c","mchid":"888888888888","payer_total":8888}
以上就是微信v3接口的加密和解密