第一部分 拉起支付
第一步:
按官方文档先:
composer require wechatpay/wechatpay
下载wechatpay包
第二步,然后在代码中引入:
require_once ROOT_PATH.'/vendor/autoload.php';
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
ROOT_PATH 为根路径,可自行处理
/**
* order=订单详情
* appid=公众号appid、需与微信商户号绑定
* mch_id 商户号
*
*/
function create_pay_order_wechatH5($order, $appid, $mch_id) {
// 商户号
$merchantId = $mch_id;
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名,'file://'一定要写
$merchantPrivateKeyFilePath = 'file://'.ROOT_PATH.'/app/pay/wechatapp/cert/1625662730/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」可以在微信支付商户号后台获取
$merchantCertificateSerial = '41CD2B0A5814D14A07C7E4CFDD4C653******';
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名,'file://'一定要写,首次获取平台证书需要用执行命令获取:
//命令如下:vendor/bin/CertificateDownloader.php -k ${32位密码} -m ${商户号} -f ${商户API私钥} -s ${商户API证书的证书序列号} -o ${获取平台证书后的保存路径}
$platformCertificateFilePath = 'file://'.ROOT_PATH.'/app/pay/wechatapp/wechatpay_603C2803A9A56774EDA68F4BB8F77ECA2*****.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,
],
]);
// 构造一个 APIv3 客户端实例
try {
$resp = $instance
->v3->pay->transactions->h5
->post(['json' => [
'mchid' => $merchantId,
'out_trade_no' => $order["order_id"],
'appid' => $appid,
'description' => '端口标题',
'notify_url' => HOST."/app/pay/wechatapp/callback_wechatapp2.php",
'amount' => [
'total' => intval($order["price"] * 100),
'currency' => 'CNY'
],
"scene_info" => ['payer_client_ip'=>Fun::getClientIp(),'h5_info'=>['type'=>'Wap']],
]]);
// echo $resp->getStatusCode(), PHP_EOL;
// echo $resp->getBody(), PHP_EOL;
$body = $resp->getBody();
$body = json_decode($body, true);
} catch (\Exception $e) {
// 进行错误处理
// Tool::log(LOG_PATH."pay.txt",$e,"微信h5-直连");
// echo $e->getMessage(), PHP_EOL;
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
Tool::log(LOG_PATH."pay.txt",$e->getMessage(),"微信h5-直连");
// $r = $e->getResponse();
// echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
// echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
}
}
if(isset($body['h5_url'])){
$url = $body['h5_url'];
return $url;
}
}
注意:
一、'file://'不要忘记写了,
二、api证书和平台证书不要搞混了。
第三步
测试能否拉起支付。如果app打h5支付链接,可能会报参数格式错误的提示,需要让客户端设置一下referer。
相关错误问题可查阅:
开发指引-H5支付 | 微信支付商户平台文档中心
第二部分 支付回调通知
相对来说更简单了,我直接贴代码:
require_once "../../../configs/config.inc.php";
require_once ROOT_PATH."configs/init.inc.php";
require_once ROOT_PATH.'/vendor/autoload.php';
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
/**
* 微信支付回调
* @param $input 获取的body数据
* @param $header 获取的请求头数据
* @return bool
*/
function notify($input,$header)
{
Tool::log(LOG_PATH."pay.txt", 1,"微信支付回调");
$inWechatpaySignature = $header['wechatpay-signature'];// 请根据实际情况获取
$inWechatpayTimestamp = $header['wechatpay-timestamp'];// 请根据实际情况获取
$inWechatpaySerial = $header['wechatpay-serial'];// 请根据实际情况获取
$inWechatpayNonce = $header['wechatpay-nonce'];// 请根据实际情况获取
$inBody = $input;// 请根据实际情况获取,例如: file_get_contents('php://input');
$apiv3Key = '********_fd5hgasg5gda6gdf8gfd';// 在商户平台上设置的APIv3密钥
// 根据通知的平台证书序列号,查询本地平台证书文件,
// 假定为 `/path/to/wechatpay/inWechatpaySerial.pem`
$platformPublicKeyInstance = Rsa::from('file://'.ROOT_PATH.'/app/pay/wechatapp/wechatpay_603C2803A9A56774EDA68F4BB8F77********.pem', Rsa::KEY_TYPE_PUBLIC);
Tool::log(LOG_PATH."pay.txt", 2,"微信支付回调");
// 检查通知时间偏移量,允许5分钟之内的偏移
$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
$verifiedStatus = Rsa::verify(
// 构造验签名串
Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature,
$platformPublicKeyInstance
);
Tool::log(LOG_PATH."pay.txt", $verifiedStatus,"verifiedStatus");
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);
Tool::log(LOG_PATH."pay.txt", $inBodyResourceArray,"inBodyResourceArray");
if ($inBodyResourceArray['trade_state'] == 'SUCCESS') {
// 这里写业务逻辑
$order_id = (string)$inBodyResourceArray["out_trade_no"];
$trade_id = (string)$inBodyResourceArray["transaction_id"];
include(ROOT_PATH."app/pay/pay_callback_common.php");
$result = [
'code'=>'SUCCESS',
'message'=>'成功',
];
echo json_encode($result);exit;
}
}
}
// 获取header头的方法
function get_all_header()
{
// 忽略获取的header数据。这个函数后面会用到。主要是起过滤作用
$ignore = array('host','accept','content-length','content-type','connection','accept-encoding'); $headers = array(); //这里大家有兴趣的话,可以打印一下。会出来很多的header头信息。咱们想要的部分,都是‘http_'开头的。所以下面会进行过滤输出。/* var_dump($_SERVER);
foreach($_SERVER as $key=>$value){ if(substr($key, 0, 5)==='HTTP_'){ //这里取到的都是'http_'开头的数据。
//前去开头的前5位
$key = substr($key, 5); //把$key中的'_'下划线都替换为空字符串
$key = str_replace('_', ' ', $key); //再把$key中的空字符串替换成‘-’
$key = str_replace(' ', '-', $key); //把$key中的所有字符转换为小写
$key = strtolower($key); //这里主要是过滤上面写的$ignore数组中的数据
if(!in_array($key, $ignore)){ $headers[$key] = $value;
}
}
}//输出获取到的header
return $headers;
}
$headers = get_all_header();
$input = file_get_contents('php://input');
notify($input,$headers);
回调里只要平台证书引入对就会验签正确、还有api3密钥32位要填一下。
看官方文档太乱了,这一点那一点的,我就整理一下逻辑就好,代码可以直接,相关文件路径可能要调整一下。
相关链接:
https://github.com/wechatpay-apiv3/wechatpay-php#%E5%A6%82%E4%BD%95%E4%B8%8B%E8%BD%BD%E5%B9%B3%E5%8F%B0%E8%AF%81%E4%B9%A6
https://github.com/wechatpay-apiv3/wechatpay-php#%E5%A6%82%E4%BD%95%E4%B8%8B%E8%BD%BD%E5%B9%B3%E5%8F%B0%E8%AF%81%E4%B9%A6
微信支付-开发者文档