相信,写过微信支付接口的程序员,都会骂一句,什么垃圾文档。惠州网站建设今天给个完整的解决案例。哎,绕来绕去,把你绕坑里。我也是不知道掉了多少坑才写出这个避坑文档。目的是想让自己记住thinkphp6在接入微信支付v3时候jsapi的时候,不要在掉一次坑。因为,官网文档的说明内容真的让人无语。都严重怀疑,他不想让人成功接入他们支付一样。
下面说下我们怎么掉坑和出坑的。
1,打开 https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_1.shtml 阅读里面的相关设置信息,配置好你的商户号i信息那些。这个配置就不多说了,根据流程操作就好了。
2,有了相关信息之后,一般,我们这里要求,A要有证书文件,这里文件有三个,可以看下截图。
这三个文件中,我们这里测试和使用的时候,目前就只用到 apiclient_key.pem,其他文件,我这里暂时没用到,可能技术不够吧。但是目前我就只用到这个文件,其他三个文件是放在一起的。放到你的站点中。这个路径要你的程序能访问到。
B,获取微信商户ID,公众号的ID,公众号的密钥,微信支付api v3的密钥。
C,平台证书,这个也是巨坑之一,不认证看文档,你都不知道要用来干啥。
3,开发之前,我们先疏通下微信支付的流程。
先上官方的流程图。
你晕不晕,我不清楚,反正我是挺晕的。说了半天,没告诉你,什么时候要用上面提到的哪个文件。用哪个接口去申请流程。
这个时候,我们想到的是他们提供的接口。好家伙,不提还好。一提,把几火。
先看他们的demo吧。下面是demo下载地址。
https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay6_0.shtml
我们是php的,所以选择这个 wechatpay-php就可以了,后面的哪个东西,你不要管。wechatpay-guzzle-middleware这个不知道是哪个专家非得跟他放一起,还不写清楚是干啥的。其实这wechatpay-guzzle-middleware就是一个将php扩展。用来做网页请求的。这里,你就把他当成html里面的ajax这样的东西就好了。放在一起,只能让人掉坑里。
然后,我们看看代码。继续掉坑。后面,我们在整理出自己的流程图和使用哪个接口。
下载demo的时候,他提供composer的方法,我们就用这个方法。其他方法,可能会缺少依赖。
composer require wechatpay/wechatpay
这个时候,https://github.com/wechatpay-apiv3/wechatpay-php ,我们要从这个文档里面拿到的代码,基本就是可以用的。其他百度回来的代码,就各种问题。不过这里的代码不是直接用的,要各种修改。这里拿最原本的代码来说吧。直接上代码。
首先,我们新建一个叫 wxPay.php的文件,然后,第一段代码加入进去。我们上代码吧。
因为我们当初是放在index这个文件下面的,所以就没改成wxPay了,将就着用吧。然后下面将他们的代码加入进来。
public function wxpay(){
// 商户号
$merchantId = '190000****';
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = 'file:///path/to/wechatpay/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,
],
]);
// 发送请求
$resp = $instance->chain('v3/certificates')->get(
['debug' => true] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
);
echo $resp->getBody(), PHP_EOL;
}
用这个作为 第一个输入的地方,因为 ['debug' => true] ,所以,有错误的话是可以直接显示出来的。
上面的参数按照提示补充进去就可以了。
这里有个坑,上面的两个证书,直接用我们的下载到的商户证书就可以了,不要用平台证书。
还有个就是 file:///这个地方是3个/,为什么呢?file://算是php的读取格式吧,跟ftp://一个道理。然后,第三个/ 是文件路径。
然后,根据他们的文档,下面的是native的支付,其实,是什么支付,主要就是看传什么参数,这里,我们调整下,改成jsapi。
try {
$resp = $instance
->chain('v3/pay/transactions/jsapi')
->post(['json' => [
'mchid' => '1900006XXX',
'out_trade_no' => 'native12177525012014070332333',
'appid' => 'wxdace645e0bc2cXXX',
'description' => 'Image形象店-深圳腾大-QQ公仔',
'notify_url' => 'https://weixin.qq.com/',
'amount' => [
'total' => 1,
'currency' => 'CNY'
],
]]);
echo$resp->getStatusCode(), PHP_EOL;
echo$resp->getBody(), PHP_EOL;
} 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;
}
到这里,我们就可以获得$resp,这个东西有什么用呢?这个东西就是下面用来给微信支付接口发送数据的东东,这里暂时叫他一个实例后的对象。然后,我们继续往下看。
到这里,我们的预支付就完成了,返回的$resp->getBody()里面就带有prepay_id,这个参数就是唤醒微信支付的参数。好了,到这里,哪个官方的说明文档又开始犯二了。到这里,他就不告诉你下面要怎么办。如果你以前有过微信开发经验的话,可能知道要怎么办。但是,像我这种刚接触微信支付的新手来说,就懵懵懂懂了。不知道怎么搞了。
这个时候,我们回去看文档。https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml,这个地方,有一个叫唤醒jsapi支付的。好吧,我们接着弄。那具体怎么弄呢?
文档,我们就不说了,大家自己看吧,这里,我们拿出这个代码来给大家展示下。
function onBridgeReady() {
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId": "wx2421b1c4370ec43b", //公众号ID,由商户传入
"timeStamp": "1395712654", //时间戳,自1970年以来的秒数
"nonceStr": "e61463f8efa94090b1f366cccfbbb444", //随机串
"package": "prepay_id=up_wx21201855730335ac86f8c43d1889123400",
"signType": "RSA", //微信签名方式:
"paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg=="//微信签名
},
function(res) {
if (res.err_msg =="get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
});
}
if (typeof WeixinJSBridge=="undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} elseif (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
是不是有点蒙,刚开的时候,我还以为是个php函数,后来在微信支付文档里面看到说WeixinJSBridge 是微信内置浏览器的对象,其他浏览器没有。所以嘛,简单来说,这个就是客户端的东西。既然是js调用的,我们就在获得预支付结果的pid哪里,将这个唤醒微信支付的js输出到微信就好了。好了,到这里,神奇的事情发生了。微信支付,成功唤醒了。
到这里,是不是很觉得云里雾里呢?是的,没看到代码之前,都是蒙的。文档的最后,我们会把完整的tp6写的微信支付v3的代码,显示出来。
到这里,微信支付v3的tp6开发,支付部分就完成了。但是回调还没写。回调又是怎么样的呢?
先留个悬念。我们这里先解决下微信支付思路的问题。
目前我们这里之说,jsapi的微信支付问题。其他的可能参数不同,流程可能有些差异。
A,使用微信登录获得openid,这个一般微信登录的时候,都会有返回,这个是用来验证微信登录是否成功的重要参数,也是微信支付的时候,判断是哪个账号支付的。
B,使用商户号,商户证书,openid这写参数,生成$resp对象,这个就是一个用php来模拟浏览器访问微信接口的东西,返回出来的一个对象。这个对象可以用来预支付。
C,预支付之后,我们要用预支付返回的prepay_id,和商户证书,和其他参数生成一个签名,这个签名成功之后,我们在调用WeixinJSBridge这个,唤醒微信支付。
好吧,这个就是微信支付从下单到唤醒的流程。
下面,我们给出这个部分的代码。
获得openid的代码
<?php
$url="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx5f7e1393xxxxxx&redirect_uri=http%3A%2F%2Fwww.xxxx.cn%2Fapi.php%2Fajax%2Fhuidiaourl&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
header("Location:".$url);
//这里的重点是 回调的url redirect_uri=http%3A%2F%2Fwww.xxxx.cn%2Fapi.php%2Fajax%2Fhuidiaourl
function huidiaourl(){
$post=Request::param();
// dump($post);
if(is_array($post)){
$url2="https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx5f7e1393932628df&secret=821bf3340ad1eace4c585b6cb5e754a5&code=".$post["code"]."&grant_type=authorization_code";
$str=get_urls($url2);
$arr=json_decode($str,true);
$arr['openid']; //这个$arr数组就含有openid,需要的时候,大家可以自己打印出来看看
}
}
?>
获得openid之后,下面给出完整的调用代码。
<?php
publicfunctionwxpay(){
$configdb=Db::name("config");
$configdata=$configdb->where("name in ('web_keywords','web_name','web_logo','ico_log','web_basehost','web_description','web_attr_15','web_attr_16','web_attr_17','web_attr_18','web_attr_19','web_attr_20','web_attr_21')")->order("id desc")->column('*','id');
$data_users=Db::name("users")->where("open_id",Session::get("wxcode"))->find();
$post=Request::post();//接收本站提交的参数
$get=Request::get();//接收微信回调过来的信息
$yicun=false;
$openid=Session::get("openid");
if(!$openid){
echo"openid错误...";
exit();
}
$payorder=Db::name("payorder")->where([["openid","=",$openid],["paystatus","=",0]])->order("id desc")->find();
if($payorder){
if(time()-$payorder['addtime']<=60 ){
$out_trade_no=$payorder["out_trade_no"];
$yicun=true;
}else{
$out_trade_no='dahonghu'.time().rand(1000,9999);
}
}else{
$out_trade_no='dahonghu'.time().rand(1000,9999);
}
// 设置参数
// 商户号
$merchantId = '16422XXXXX';
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = 'file:///apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = '48185fb88454XXXXXXXXXXXXXX';
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = 'file:///wechatpay.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,
],
]);
// 发送请求
$resp = $instance->chain('v3/certificates')->get(
['debug' => false] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
);
try {
$zhenjia=intval(round($post["tprice"]*100));
$resp = $instance
->chain('v3/pay/transactions/jsapi')
->post(['json' => [
'mchid' => '1642XXXXXXX',
'out_trade_no' => $out_trade_no,
'appid' => 'wx5f7XXXXXX',
'description' => 'vip年卡',
'notify_url' => 'http://www.dahXXXXX.cn/index/wx3_notify',
'amount' => [
'total' => $zhenjia,
'currency' => 'CNY'
],
'payer' =>[
"openid"=>$openid
]
]]);
$inarr=[
"out_trade_no"=>$out_trade_no,
"description"=>'vip年卡',
"openid"=>$openid,
"addtime"=>time(),
"days"=>365,
"jine"=>floatval($zhenjia/100),
"paytype"=>"wxpay"
];
if($yicun){
$input_payorder=Db::name("payorder")->where("out_trade_no",$out_trade_no)->save($inarr);
}else{
$input_payorder=Db::name("payorder")->save($inarr);
}
$prepay_id_arr=json_decode($resp->getBody(),true);
$prepay_id="prepay_id=".$prepay_id_arr["prepay_id"];
$sn=self::signname($prepay_id);
$sn_obj=json_decode($sn);
echo'
';
} 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;
}
}
这里还要给一个加密的函数。
publicfunctionsignname($package){
$merchantPrivateKeyFilePath = 'file:///zs/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath);
$params = [
'appId' => 'wx5f7e13xxxxxx',
'timeStamp' => (string)Formatter::timestamp(),
'nonceStr' => Formatter::nonce(),
'package' => $package,
];
$params += ['paySign' => Rsa::sign(
Formatter::joinedByLineFeed(...array_values($params)),
$merchantPrivateKeyInstance
), 'signType' => 'RSA'];
returnjson_encode($params);
}
到这里,微信从下单到预支付到拉起微信支付都完成了。
以上案例由惠州网站建设原创发布,如需要使用相关代码,请备注来源。