装备材料
- 需要支付的主体(小程序,app,公众号),去各自平台认证。APP去微信开放平台认证
- 微信商户号(注意需要同一个公司认证)
- 绑定关系(在商户号绑定,并且各自平台进行同意)
- 商户号,下载证书,设置密码 参考微信手册
使用SDK
//使用官方推荐的
composer require wechatpay/wechatpay
SDK 注意事项
//这里的证书需要使用 file_get_contents(),里面写具体路径
$merchantPrivateKeyFilePath = file_get_contents('file:///path/to/merchant/apiclient_key.pem');
// 微信支付平台证书 需要提前下载,并且也需要使用, file_get_contents()
$platformCertificateFilePath = file_get_contents('file:///path/to/wechatpay/cert.pem');
下载微信平台证书
我这里使用composer 下载证书,需要注意的是,thinkphp 不能直接使用,建议新建文件夹,从头一步一步来。不能使用thinkphp 的 vendor
其他文章链接
- 安装composer包
composer require wechatpay/wechatpay
- 查询当前可用命令,提示 Available binaries: - CertificateDownloader.php 为正确,不然就升级composer 后重新安装
composer exec -l
- 重试执行 命令,会打印出使用说明的
composer exec CertificateDownloader.php
- 执行命令,会把证书下载到刚建了文件夹里面
composer exec CertificateDownloader.php -- -m 你的商户号 -s 40字节你的商户证书序列号 -f 你的apiclient_key.pem文件路径 -k 你的APIv3密钥 -o .
执行下单(注意手册上面的不是使用推荐方法,不能使用手册方法)
这里需要根据自己的下单接口来编写,具体参考文档
比如:JSAPI 下单,就把chain 改为 v3/pay/transactions/jsapi
然后得到返回值,进行下一步,签名,签名后返回到前台进行下单
$merchantId = config('wx_mini')['mchid'];
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = file_get_contents('/xxxxxxxxxxx');
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = config('wx_mini')['zhengshu_no'];
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = file_get_contents(xxxxxxxxxxx);
$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/jsapi')
->post(['json' => [
'mchid' => config('wx_mini')['mchid'],
"out_trade_no"=> $ji,
"appid"=> config('wx_mini')['appid'],
"description"=> "向商城付款",
"notify_url"=> config('wx_mini')['notify_url'],
"amount"=> [
//"total"=> (int)($data['money']*100),
"total"=>1,
"currency"=> "CNY"
],
"payer"=> [
"openid"=> $user['openid_wx']
]
]]);
if( $resp->getStatusCode()==200){
$wo=json_decode($resp->getBody(),true); //prepay_id
$params = [
'appId' => config('wx_mini')['appid'],
'timeStamp' => (string)Formatter::timestamp(),
'nonceStr' => Formatter::nonce(),
'package' => 'prepay_id='.$wo["prepay_id"],
];
$params += ['paySign' => Rsa::sign(
Formatter::joinedByLineFeed(...array_values($params)),
$merchantPrivateKeyInstance
), 'signType' => 'RSA'];
// Db::name('user_gu')->where('id')
// echo json_encode($params);
return ['code' => 1, 'message' => '微信支付成功','data'=>$params];
}
} catch (\Exception $e) {
return ['code' => 0, 'message' => '微信支付错误'];
// 进行错误处理
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;
}
前台执行支付行为这里小程序举例
我是用的是uniapp
uni.requestPayment({
provider: 'wxpay',
timeStamp: config.timeStamp,
nonceStr: config.nonceStr,
package: config.package,
signType: config.signType,
paySign: config.paySign,
success: function (res) {
console.log('success:' + JSON.stringify(res));
uni.common.back('支付成功')
},
fail: function (err) {
console.log('fail:' + JSON.stringify(err));
uni.common.msg('支付失败')
}
});
支付回调
先写好支付的回调地址,会接受到两个数据,一个是header 的数据一个是,body里面数据,使用file_get_contents(“php://input”); 接受数据
$info = Request::header();
$inWechatpaySignature = $info['wechatpay-signature'];
$inWechatpayTimestamp = $info['wechatpay-timestamp'];
$inWechatpaySerial = $info['wechatpay-serial'];
$inWechatpayNonce = $info['wechatpay-nonce'];
if (!$inWechatpaySignature or !$inWechatpayTimestamp or !$inWechatpaySerial or !$inWechatpayNonce) {
header("Location:/404.html");
exit;
}
$inBody = file_get_contents("php://input");// 请根据实际情况获取,例如: file_get_contents('php://input');
$apiv3Key = config('wx_mini')['v3_secret'];// 在商户平台上设置的APIv3密钥
// 根据通知的平台证书序列号,查询本地平台证书文件,这里是自己生成的证书
$platformPublicKeyInstance = Rsa::from(file_get_contents(env('root_path').'zhengshu/wechatpay.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);// 打印解密后的结果
$result=$inBodyResourceArray;
if ($result['trade_state'] == 'SUCCESS') {
$out_trade_no = $result['out_trade_no'];
$pay_money= $result['amount']['payer_total']/100;
//更加订单号和支付金额执行自己逻辑
// //分钱没有写呢
// if ($fen) {
// $this->fenqian($out_trade_no);
// }
return \json([
"code"=> "SUCCESS",
"message"=> "成功"
]);
}
}
使用uniapp 进行 微信app内支付
和上面一样,区别是两个一个是预下单接口,一个是发起支付接口
预下单
$merchantId = config('wx_mini')['mchid'];
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = file_get_contents('xxxxxxx/zhengshu/wx/apiclient_key.pem');
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = config('wx_mini')['zhengshu_no'];
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = file_get_contents('/xxxxxx/zhengshu/wx/cert.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/app')
->post(['json' => [
"appid"=> config('wx_mini')['kf_appid'],//这里使用开放平台的appid
'mchid' => config('wx_mini')['mchid'],
"description"=> "向商家付款",
"out_trade_no"=> $ji,
"notify_url"=> config('wx_mini')['notify_url'],
"amount"=> [
"total"=> (int)($data['money']*100),
//"total"=>1,
"currency"=> "CNY"
],
// "payer"=> [
// "openid"=> $user['openid_wx']
// ]
]]);
if( $resp->getStatusCode()==200){
$wo=json_decode($resp->getBody(),true); //prepay_id
$params = [
'appId' => config('wx_mini')['kf_appid'],
'timeStamp' => (string)Formatter::timestamp(),
'nonceStr' => Formatter::nonce(),
'prepayId'=>$wo["prepay_id"],
];
$params += ['paySign' => Rsa::sign(
Formatter::joinedByLineFeed(...array_values($params)),
$merchantPrivateKeyInstance
), 'signType' => 'RSA'];
$params['partnerId']=config('wx_mini')['mchid'];
$params['package'] = 'Sign=WXPay';
return ['code' => 1, 'message' => '微信支付成功','data'=>$params];
}
} catch (\Exception $e) {
return ['code' => 0, 'message' => '微信支付错误'];
// 进行错误处理
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;
}
uniapp 内支付
uni.requestPayment({
"provider": "wxpay",
"orderInfo": {
'appid':config.appId,
'noncestr':config.nonceStr,
'package':config.package,
'partnerid':config.partnerId,
'timestamp':config.timeStamp,
'prepayid':config.prepayId,
'sign':config.paySign,
},
success: function (res) {
console.log('success:' + JSON.stringify(res));
uni.common.back('支付成功')
},
fail: function (err) {
console.log('fail:' + JSON.stringify(err));
uni.common.msg('支付失败')
}
})
公众号(支付是一样的,主要是多了获取openid 过程)
前端
onLoad((e) => {
let aaa=window.location.search
if(!aaa){
let url=location.href
url=encodeURIComponent(url)
let state='lizhili'
setTimeout(()=>{
window.location.href='https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxxx&redirect_uri='+url+'&response_type=code&scope=snsapi_base&state='+state+'#wechat_redirect'
},10)
}else{
const queryString = aaa.split('?')[1];
if (!queryString) {
uni.common.msg('参数错误')
}
const params = queryString.split('&');
const result = {};
params.forEach(param => {
const [key, value] = param.split('=');
result[key] = decodeURIComponent(value);
});
if(!result?.code){
uni.common.msg('参数错误')
}
//获取授权
uni.common.httpPost('Weix/getuser', {code:result.code}).then((res) => {
//获取到openid
});
}
});
后端获取openid
public function getuser(Request $request)
{
$code =$request->post('code', '');
$appid='xxxxxxxx';
$secret='xxxxxxxxxxx';
$res=Curl::curl("https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$code&grant_type=authorization_code");
s($res);
}