最近做小程序,涉及到微信支付,看了看微信小程序开发文档,尽管之前做过微信支付,还是有点懵逼,不过好在之前研究过,不然真的是无从下手。对比了一下发现,其实小程序中做支付比公众号支付要省事很多,因为不需要支付授权目录,也不需要授权域名,但是支付流程却比公众号多了一步,就是统一下单是预支付,然后需要对预支付的结果再次签名之后,才调起支付。


小程序即成微信支付demo 小程序 微信支付_数据


前期准备:


1.开通了微信支付,并且小程序绑定了微信支付;


2.准备好小程序的appid,微信支付的商户号,支付秘钥。



商户系统和微信支付系统主要交互:


1、小程序内调用登录接口,获取到用户的openid


此步骤在小程序内完成,也很简单,方法见:【

小程序登录API


2、调用商户服务器支付统一下单接口,进行预支付


[php]  view plain  copy

1. /**
2.  * 预支付请求接口(POST)
3.  * @param string $openid    openid
4.  * @param string $body      商品简单描述
5.  * @param string $order_sn  订单编号
6.  * @param string $total_fee 金额
7.  * @return  json的数据
8.  */  
9. public function prepay(){  
10. $config = $this->config;  
11.       
12. $openid = I('post.openid');  
13. $body = I('post.body');  
14. $order_sn = I('post.order_sn');  
15. $total_fee = I('post.total_fee');  
16.       
17. //统一下单参数构造  
18. $unifiedorder = array(  
19. 'appid'         => $config['appid'],  
20. 'mch_id'        => $config['pay_mchid'],  
21. 'nonce_str'     => self::getNonceStr(),  
22. 'body'          => $body,  
23. 'out_trade_no'  => $order_sn,  
24. 'total_fee'     => $total_fee * 100,  
25. 'spbill_create_ip'  => get_client_ip(),  
26. 'notify_url'    => 'https://'.$_SERVER['HTTP_HOST'].'/Api/Wxpay/notify',  
27. 'trade_type'    => 'JSAPI',  
28. 'openid'        => $openid  
29.     );  
30. $unifiedorder['sign'] = self::makeSign($unifiedorder);  
31. //请求数据  
32. $xmldata = self::array2xml($unifiedorder);  
33. $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';  
34. $res = self::curl_post_ssl($url, $xmldata);  
35. if(!$res){  
36. "Can't connect the server");  
37.     }  
38. // 这句file_put_contents是用来查看服务器返回的结果 测试完可以删除了  
39. //file_put_contents(APP_ROOT.'/Statics/log1.txt',$res,FILE_APPEND);  
40.       
41. $content = self::xml2array($res);  
42. if(strval($content['result_code']) == 'FAIL'){  
43. strval($content['err_code_des']));  
44.     }  
45. if(strval($content['return_code']) == 'FAIL'){  
46. strval($content['return_msg']));  
47.     }  
48. array('data'=>$content));  
49. //$this->ajaxReturn($content);  
50. }

3、调用商户服务器再次签名接口,返回支付数据


[php]  view plain  copy


1. /**
2.  * 进行支付接口(POST)
3.  * @param string $prepay_id 预支付ID(调用prepay()方法之后的返回数据中获取)
4.  * @return  json的数据
5.  */  
6. public function pay(){  
7. $config = $this->config;  
8. $prepay_id = I('post.prepay_id');  
9.       
10. $data = array(  
11. 'appId'     => $config['appid'],  
12. 'timeStamp' => time(),  
13. 'nonceStr'  => self::getNonceStr(),  
14. 'package'   => 'prepay_id='.$prepay_id,  
15. 'signType'  => 'MD5'  
16.     );  
17.       
18. $data['paySign'] = self::makeSign($data);  
19.       
20. $this->ajaxReturn($data);  
21. }

4、小程序内完成支付,商户服务器接收支付回调通知

小程序端代码:


[javascript]  view plain  copy

1. wx.requestPayment({  
2. 'timeStamp': '',  
3. 'nonceStr': '',  
4. 'package': '',  
5. 'signType': 'MD5',  
6. 'paySign': '',  
7. 'success':function(res){  
8.    },  
9. 'fail':function(res){  
10.    }  
11. })

服务器回调通知:


[php]  view plain  copy

1. //微信支付回调验证  
2. public function notify(){  
3. $xml = $GLOBALS['HTTP_RAW_POST_DATA'];  
4.       
5. // 这句file_put_contents是用来查看服务器返回的XML数据 测试完可以删除了  
6. //file_put_contents(APP_ROOT.'/Statics/log2.txt',$res,FILE_APPEND);  
7.       
8. //将服务器返回的XML数据转化为数组  
9. $data = self::xml2array($xml);  
10. // 保存微信服务器返回的签名sign  
11. $data_sign = $data['sign'];  
12. // sign不参与签名算法  
13. $data['sign']);  
14. $sign = self::makeSign($data);  
15.       
16. // 判断签名是否正确  判断支付状态  
17. if ( ($sign===$data_sign) && ($data['return_code']=='SUCCESS') && ($data['result_code']=='SUCCESS') ) {  
18. $result = $data;  
19. //获取服务器返回的数据  
20. $order_sn = $data['out_trade_no'];          //订单单号  
21. $openid = $data['openid'];                  //付款人openID  
22. $total_fee = $data['total_fee'];            //付款金额  
23. $transaction_id = $data['transaction_id'];  //微信支付流水号  
24.           
25. //更新数据库  
26. $this->updateDB($order_sn,$openid,$total_fee,$transaction_id);  
27.           
28. else{  
29. $result = false;  
30.     }  
31. // 返回状态给微信服务器  
32. if ($result) {  
33. $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';  
34. else{  
35. $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';  
36.     }  
37. echo $str;  
38. return $result;  
39. }

我将完整的接口代码封装成了一个类文件,可以直接引入项目更改一下配置参数就可以使用的

,源码下载:



小程序端完整代码如下:


[javascript]  view plain  copy


1. /**
2.  * 支付函数
3.  * @param  {[type]} _payInfo [description]
4.  * @return {[type]}          [description]
5.  */  
6. pay:function(_payInfo,success,fail){  
7. var payInfo = {  
8. '',  
9.         total_fee:0,  
10. ''  
11.     }  
12.     Object.assign(payInfo, _payInfo);  
13. if(payInfo.body.length==0){  
14.         wx.showToast({  
15. '支付信息描述错误'  
16.         })  
17. return false;  
18.     }  
19. if(payInfo.total_fee==0){  
20.         wx.showToast({  
21. '支付金额不能0'  
22.         })  
23. return false;   
24.     }  
25. if(payInfo.order_sn.length==0){  
26.         wx.showToast({  
27. '订单号不能为空'  
28.         })  
29. return false;   
30.     }  
31. var This = this;  
32. function(openid){  
33.         payInfo.openid=openid;  
34.         This.request({  
35. 'api/pay/prepay',  
36.             data:payInfo,  
37. function(res){  
38. var data = res.data;  
39.                 console.log(data);  
40. if(!data.status){  
41.                     wx.showToast({  
42. 'errmsg']  
43.                     })  
44. return false;  
45.                 }  
46.                 This.request({  
47. 'api/pay/pay',  
48.                     data:{prepay_id:data.data.data.prepay_id},  
49. function(_payResult){  
50. var payResult = _payResult.data;  
51.                         console.log(payResult);  
52.                         wx.requestPayment({  
53. 'timeStamp': payResult.timeStamp.toString(),  
54. 'nonceStr': payResult.nonceStr,  
55. 'package': payResult.package,  
56. 'signType': payResult.signType,  
57. 'paySign': payResult.paySign,  
58. 'success': function (succ) {  
59.                                 success&&success(succ);  
60.                             },  
61. 'fail': function (err) {  
62.                                 fail&&fail(err);  
63.                             },  
64. 'complete': function (comp) {   
65.   
66.                             }  
67.                         })   
68.                     }  
69.                 })  
70.             }  
71.         })  
72.     })  
73. }