目录
一、安装微信SDK
二、准备请求资料
三、引入和定义
四、Native下单
五、Native调起支付
六、Native异步通知
composer require wechatpay/wechatpay
appid:开放平台
mchid:商户平台
商户API证书:商户平台-账户中心-api安全(申请)
商户API秘钥:商户平台-账户中心-api安全(申请)
商户证书序列号:开放平台-API管理
apiv3_key:商户平台-api安全
微信支付平台证书:(在项目根目录执行命令,保证sdk已安装 证书保存文件夹已经创建 outputFilePath 保存地址是项目地址中的文件夹)
php vendor/bin/CertificateDownloader.php -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
${apiV3key} 替换成apiV3key秘钥
${mchId} 替换成商户号
${mchPrivateKeyFilePath} 替换成商户私钥路径
${mchSerialNo} 替换成商户证书序列号
${outputFilePath} 替换成微信支付证书要保存的路径
use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;
private const APPID = ''; //appid
private const MCHID = ''; //商户号
private const CERT_NUM = ''; //商户证书序列号
private const APIV3_KEY = ''; //apiv3秘钥
返回一个code_url 后端生成二维码显示给前端
//Native下单
public function paymentCode($out_trade_no, $description, $notify_url, $total)
{
// 微信配置
$merchantId = self::MCHID; // 商户号
$appid = self::APPID; // appid
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = 'file://' . ROOT_PATH . '\app\...私钥路径..\apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = self::CERT_NUM;
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = 'file://' . ROOT_PATH . '\app\...支付证书路径...\wechatpay_123456.pem';
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
// 从「微信支付平台证书」中获取「证书序列号」
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
// 构造一个 APIv3 客户端实例
$instance = Builder::factory([
'mchid' => $merchantId,
'serial' => $merchantCertificateSerial,
'privateKey' => $merchantPrivateKeyInstance,
'certs' => [
$platformCertificateSerial => $platformPublicKeyInstance,
],
]);
try {
$resp = $instance
->chain('v3/pay/transactions/native')
->post(['json' => [
'mchid' => $merchantId,
'out_trade_no' => $out_trade_no,
'appid' => $appid,
'description' => $description,
'notify_url' => $notify_url,
'amount' => [
'total' => $total * 100,
'currency' => 'CNY'
],
]]);
echo $resp->getStatusCode(), PHP_EOL;
echo $resp->getBody(), PHP_EOL; //返回的code_url
} catch (\Exception $e) {
// 进行错误处理
echo $e->getMessage(), PHP_EOL;
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$r = $e->getResponse();
echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
}
echo $e->getTraceAsString(), PHP_EOL;
}
}
用户扫码即调起
// 异步通知
public function wxPayNotify()
{
$headers = $this->request->header();
$inWechatpaySignature = $headers['wechatpay-signature']; // 应答heade中对应数据:应答签名
$inWechatpayTimestamp = $headers['wechatpay-timestamp']; // 应答heade中对应数据:应答时间戳
$inWechatpaySerial = $headers['wechatpay-serial']; // 应答heade中对应数据:应答平台证书序列号
$inWechatpayNonce = $headers['wechatpay-nonce']; // 应答heade中对应数据:应答随机字符串
$inBody = file_get_contents('php://input'); // 应答主体:请根据实际情况获取,例如: file_get_contents('php://input');
$apiv3Key = self::APIV3_KEY; // 在商户平台上设置的APIv3密钥
// 根据通知的平台证书序列号,查询本地平台证书文件,
$platformPublicKeyInstance = Rsa::from('file://' . ROOT_PATH . '\app\..路径..\wxpay_cert\wechatpay_1234563.pem', Rsa::KEY_TYPE_PUBLIC);
// 检查通知时间偏移量,允许5分钟之内的偏移
$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
$verifiedStatus = Rsa::verify(
// 构造验签名串
Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature,
$platformPublicKeyInstance
);
if ($timeOffsetStatus && $verifiedStatus) {
// 转换通知的JSON文本消息为PHP Array数组
$inBodyArray = (array)json_decode($inBody, true);
// 使用PHP7的数据解构语法,从Array中解构并赋值变量
['resource' => [
'ciphertext' => $ciphertext,
'nonce' => $nonce,
'associated_data' => $aad
]] = $inBodyArray;
// 加密文本消息解密
$inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
// 把解密后的文本转换为PHP Array数组
$inBodyResourceArray = (array)json_decode($inBodyResource, true);
// print_r($inBodyResourceArray); // 打印解密后的结果
$out_trade_no = $inBodyResourceArray['out_trade_no'];
$trade_state = $inBodyResourceArray['trade_state'];
$total = $inBodyResourceArray['amount']['total'];
if (Db::name('记录表')->where('out_trade_no', $out_trade_no)->find()) {
http_response_code(200);
$data = [
'code' => 'SUCCESS',
'message' => '成功'
];
return json_encode($data);
} else {
//订单处理业务
http_response_code(200);
$data = [
'code' => 'SUCCESS',
'message' => '成功'
];
return json_encode($data);
}
} else {
// 调用微信查询订单API
}
}
}