php对接微信支付平台证书下载接口,单文件(支持php5.6)。

浏览数(1270)

php对接微信支付平台证书下载接口,单文件(支持php5.6)。

0

微信支付平台证书是用于微信支付V3异步回调验签,这个平台证书是V3的接口独有,每隔一段时间就要更新一次,网上大多数都是java或者是臃肿sdk集成,里面有很多用不到的模块,且不支持php7+以下的版本,因为我平时会帮客户二开一些系统,但是那些系统有些没有这么高的版本,所以就写了这个,现在分享出来。


以下是微信支付V3接口的证书验签流程(图片来自于微信支付官方文档):

fbc4545b28e37ecdae9d0ca8e38ddd6f.jpg

以下是代码:

<?php

/**
 * LCSAY TENCENT WECHAT PAY V3 微信支付平台证书下载接口(支持php5.6)
 * WECHAT AND QQ : 516519782
 */

// 微信支付平台证书下载接口
$platformCertUrl = 'https://api.mch.weixin.qq.com/v3/certificates';

// 商户号
$mchId = '';
// 证书路径
$certPath = '';
// 证书密码
$certPassword = '';
// API 密钥
$apiKey = '';
$serialNumber = getCertificateSerialNumber($certPath, $certPassword);      // 商户证书序列号
$privateKey = getcertpkey($certPath, $certPassword);   // 商户私钥

// 构建请求头
$headers = [
    'Accept: application/json',
    'Content-Type: application/json',
	'User-Agent:*/*',
    'Authorization: WECHATPAY2-SHA256-RSA2048 ' . generateAccessToken(),
];

// 发送请求获取平台证书
//封装curl请求
function curl($platformCertUrl,$headers)
{
	$ch = curl_init($platformCertUrl);
	curl_setopt($ch, CURLOPT_URL, $platformCertUrl);	// 设置请求 URL
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
	curl_setopt($ch, CURLOPT_HEADER, false);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);	//设置请求头
	return curl_exec($ch);
}

$result = curl($platformCertUrl,$headers);

// 解析返回的证书列表
$certificates = json_decode($result, true);
$certificatesdata = $certificates['data'];
//print_r($certificatesdata);

// 遍历证书列表
foreach ($certificatesdata as $certificate) {
    $serialNumber = $certificate['serial_no'];
    $ciphertext = $certificate['encrypt_certificate'];

    // 解密平台证书
    $platformPublicKey = decryptCiphertext($ciphertext,$apiKey);

    // TODO: 处理平台证书,可以保存到本地或者使用其它方式进行后续操作
    echo "平台证书序列号: " . $serialNumber . "\n";
    echo "平台证书公钥: " . $platformPublicKey . "\n";
	//如果证书存在 就保存证书 并以序列号命名
	if(isset($serialNumber) && isset($platformPublicKey)){
		$file = './apiclient_platform_cert.pem';
		$fp = fopen($file, 'w');	//w是先清空内容 再写入
		fwrite($fp, $platformPublicKey);
		fclose($fp);
	}
}

//获取私钥证书密钥
function getcertpkey($certPath, $certPassword)
{
	$certContent = file_get_contents($certPath);
	if (openssl_pkcs12_read($certContent, $certData, $certPassword)){
    	$certificate = $certData['pkey'];
	}else{
    	echo "Error: Unable to read the cert store.\n";
    	exit;
	}
    return $certificate;
}

// 获取证书序列号
function getCertificateSerialNumber($certPath, $certPassword)
{
    $certContent = file_get_contents($certPath);
	if (openssl_pkcs12_read($certContent, $certData, $certPassword)){
    	$certificate = $certData['cert'];
	}else{
    	echo "Error: Unable to read the cert store.\n";
    	exit;
	}
    $x509 = openssl_x509_read($certificate);
    $certInfo = openssl_x509_parse($x509);
	///////////////////php获取证书编号没有serialNumberHex只有serialNumber处理方法(php5.6)///////////////////////
	///////////////////将 serialNumber 转换成 serialNumberHex///////////////////////
	if(isset($certInfo['serialNumberHex'])){
		$serialNumberHex = $certInfo['serialNumberHex'];
	}else{
		$serial = $certInfo['serialNumber'];
		$base = bcpow("2", "32");
		$counter = 100;
		$res = "";
		$val = $serial;
		while($counter > 0 && $val > 0) {
			$counter = $counter - 1;
			$tmpres = dechex(bcmod($val, $base)) . "";
			/* adjust for 0's */
			for ($i = 8-strlen($tmpres); $i > 0; $i = $i-1) {
				$tmpres = "0$tmpres";
			}
			$res = $tmpres .$res;
			$val = bcdiv($val, $base);
		}
		if ($counter <= 0) {
			echo 'Occured failed.';
			exit;
		}
		$serialNumberHex = strtoupper($res);
	}
	return $serialNumberHex;
}

// 生成访问令牌
function generateAccessToken()
{
    global $mchId, $serialNumber, $privateKey, $platformCertUrl;

    $timestamp = time();
    $nonce = md5(mt_rand());
    $message = "GET\n" . parse_url($platformCertUrl, PHP_URL_PATH) . "\n" . $timestamp . "\n" . $nonce . "\n\n";
    $signature = '';
    openssl_sign($message, $signature, $privateKey, OPENSSL_ALGO_SHA256);
    $signature = base64_encode($signature);
    return "mchid=\"$mchId\",nonce_str=\"$nonce\",timestamp=\"$timestamp\",serial_no=\"$serialNumber\",signature=\"$signature\"";
}

// 解密平台证书
function decryptCiphertext($ciphertext,$apiKey)
{
	global $privateKey;
    // 将 ciphertext 参数进行 Base64 解码
    $ciphertextcip = base64_decode($ciphertext['ciphertext']);
	$associatedData = $ciphertext['associated_data'];
    $nonce = $ciphertext['nonce'];

    // 解密数据
	$AUTH_TAG_LENGTH_BYTE = 16;
    if (strlen($ciphertextcip) <= $AUTH_TAG_LENGTH_BYTE) {
        return false;
    }
    $ctext = substr($ciphertextcip, 0, -$AUTH_TAG_LENGTH_BYTE);
    $authTag = substr($ciphertextcip, -$AUTH_TAG_LENGTH_BYTE);
	return openssl_decrypt($ctext, 'aes-256-gcm', $apiKey, OPENSSL_RAW_DATA, $nonce, $authTag, $associatedData);
}
?>


注:本文由www.lcsay.com发表,如需转载或已侵权,请联系我。

✎﹏𝓁𝒸𝓈𝒶𝓎﹍﹍·

评论0