最近客户突然提出该需求,说要添加微信支付功能,于是奋不顾身的跳进了微信支付的大坑中。虽说有高人指点,但是微信支付的文档不是一般的烂,还有一大堆的“不成文的规定”。
前言
在一个不算PM的PM的折磨下,靠着自己顽强的生命力,终于把项目一期快啃下来了。
最坑爹的是在开发中途,客户突然提出该需求,说要添加微信支付功能。
一听到这个需求问题我就炸毛了,但是,没办法,有钱就是大爷。
在不得不的情况下,奋不顾身的跳进了微信支付的大坑中。
虽说有高人指点,但是微信支付的文档不是一般的烂,还有一大堆的“不成文的规定”。
正文
我们开发项目时使用的框架 Yii2
,所以之后我也会吐槽一下不懂 autoload
机制的蛋疼。
这个项目是纯 Web 项目,由 PC
& Mobile
组成,其中 移动端
使用的是 JS API 支付方式, PC站
使用的是 NATIVE 的支付方式。
JS API & 移动端
流程大致如此:
跳转到微信接口
-> 获取 User Code
-> 获取 access_token & Open ID
-> POST 统一支付订单
-> 调起微信支付 JS
-> 同步\异步消息回调
跳转到微信接口,获取 User Code
由于项目使用的是 Yii2
,所有例子都从真实代码中摘取出来,下不做说明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| /** * 调起微信支付的第一跳,获取用户code * 获得code之后跳到`actionWechatPay`这个action */ public function actionWechatCall(){ /* 验权代码 */
$url_base = 'https://open.weixin.qq.com/connect/oauth2/authorize?'; $url_appid = Config::AppID ; $url_redirect = Yii::$app->urlManager->createUrl([ 'user/wechat-pay', 'response_type'=>'code', 'scope'=>'snsapi_userinfo', 'state'=>$order_sn, '#'=>'wechat_redirect', ]); $url = $url_base.'appid='.$url_appid.'&redirect_uri=http://example.com'.$url_redirect; $this->redirect($url); }
|
下面的URI就是我们要跳转到的地方,然后微信会按照回调地址跳转回来并带上 Code 。
1
| https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx8888888888888888&redirect_uri=http://www.davex.pw/wxpay/js_api_call.php&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
|
提示:这里的回调URI可以带参数
获取 access_token & Open ID & POST 统一支付订单
这部分使用了微信开发给的SDK,可以在官网上找到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
public function actionWechatPay(){ $state = Yii::$app->request->get('state'); $code = Yii::$app->request->get('code'); $wechat = new WeChat(); $apiurl = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='; $appid = Config::AppID ; $secret = Config::Secret ; $url = $apiurl.$appid.'&secret='.$secret.'&code='.$code.'&grant_type=authorization_code'; $ret_json = $wechat->curl_get_contents($url); $ret = json_decode($ret_json); $access_token = $ret->access_token; $openid = $ret->openid;
$time = time();
$unifiedOrder = new WxPayUnifiedOrder(); $unifiedOrder->SetAppid(WxPayConfig::APPID); $unifiedOrder->SetMch_id(WxPayConfig::MCHID); $unifiedOrder->SetDevice_info('WEB'); $unifiedOrder->SetNonce_str(<!-- 随机字符串 -->); $unifiedOrder->SetOpenid($openid); $unifiedOrder->SetBody('Dave WeChat Pay Test Case'); $unifiedOrder->SetDetail('Dave WeChat Pay Test Case'); $unifiedOrder->SetOut_trade_no(<!-- 商品订单编号 -->); $unifiedOrder->SetFee_type('CNY'); $unifiedOrder->SetTotal_fee(<!-- 费用 单位:分 -->); $unifiedOrder->SetSpbill_create_ip(<!-- 用户IP -->); $unifiedOrder->SetTime_start(date('YmdHis',$time)); $unifiedOrder->SetTime_expire(date('YmdHis',$time + 30*60)); $unifiedOrder->SetNotify_url(<!-- 用户回调URL -->); $unifiedOrder->SetTrade_type('JSAPI');
$result = WxPayApi::unifiedOrder($unifiedOrder); if($result['return_code']=='SUCCESS'){ if($result['result_code']=='SUCCESS'){ } }
$tools = new JsApiPay(); $jsApiParameters = $tools->GetJsApiParameters($result); return $this->render('pay',[ 'jsApiParameters' => $jsApiParameters, ]);
}
|
请严重注意:接受微信支付异步通知回调地址不准带参数,只允许 http://www.davex.pw/wechat/mobile_pay.php
这就是微信所谓的不成文规定,我们当初因为这个问题花费了一个小时,最后才知道的。
上面那一长串的代码,实质上就是生成一个 XML
文档。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <xml> <openid><![CDATA[]]></openid> <body><![CDATA[DaveX]]></body> <out_trade_no><![CDATA[]]></out_trade_no> <total_fee>1</total_fee> <notify_url><![CDATA[http://www.davex.pw/wechat/mobile_pay.php]]></notify_url> <trade_type><![CDATA[JSAPI]]></trade_type> <appid><![CDATA[]]></appid> <mch_id>1008686</mch_id> <spbill_create_ip><![CDATA[127.0.0.1]]></spbill_create_ip> <nonce_str><![CDATA[]]></nonce_str> <sign><![CDATA[]]></sign> </xml>
|
然后这个 XML
会被 POST 到 https://api.mch.weixin.qq.com/pay/unifiedorder
然后会返回如下模样的数据:
1 2 3 4 5 6 7 8 9 10 11
| <xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[wx8888888888888888]]></appid> <mch_id><![CDATA[10012345]]></mch_id> <nonce_str><![CDATA[Be8YX7gjCdtCT7cr]]></nonce_str> <sign><![CDATA[885B6D84635AE6C020EF753A00C8EEDB]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[wx201410272009395522657a690389285100]]></prepay_id> <trade_type><![CDATA[JSAPI]]></trade_type> </xml>
|
其中有一个非常重要的数据 prepay_id
1
| wx201410272009395522657a690389285100
|
调起微信支付 JS
1
| $jsApiParameters = $tools->GetJsApiParameters($result);
|
上面这部分代码会生成如下json数据
1 2 3 4 5 6 7 8
| { "appId": "wx8888888888888888", "timeStamp": "1414411784", "nonceStr": "gbwr71b5no6q6ne18c8up1u7l7he2y75", "package": "prepay_id=wx201410272009395522657a690389285100", "signType": "MD5", "paySign": "9C6747193720F851EB876299D59F6C7D" }
|
在微信浏览器中调用如下 JS,完整 HTML 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>微信支付</title> <script type="text/javascript"> //调用微信JS api 支付 function jsApiCall() { WeixinJSBridge.invoke( 'getBrandWCPayRequest', <?php echo $jsApiParameters; ?>, function(res){ WeixinJSBridge.log(res.err_msg); alert(res.err_code+'|'+res.err_desc+'|'+res.err_msg); if(res.err_msg == "get_brand_wcpay_request:ok" ) { // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 // get_brand_wcpay_request:ok 支付成功后的操作 } } ); }
function callpay() { if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', jsApiCall, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', jsApiCall); document.attachEvent('onWeixinJSBridgeReady', jsApiCall); } }else{ jsApiCall(); } } </script> </head> <body> <input onclick="callpay()" type="button" class="ticket_payment_buton" value="确认付款" /> </body> </html>
|
点击 确认付款
就能看到这个神奇的魔法噜。
同步\异步消息回调
假设 http://www.davex.pw/wechat/mobile_pay.php
是我们的回调地址,支付成功后会有如下信息返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <xml> <appid><![CDATA[wx8888888888888888]]></appid> <bank_type><![CDATA[CFT]]></bank_type> <fee_type><![CDATA[CNY]]></fee_type> <is_subscribe><![CDATA[Y]]></is_subscribe> <mch_id><![CDATA[10012345]]></mch_id> <nonce_str><![CDATA[60uf9sh6nmppr9azveb2bn7arhy79izk]]></nonce_str> <openid><![CDATA[ou9dHt0L8qFLI1foP-kj5x1mDWsM]]></openid> <out_trade_no><![CDATA[wx88888888888888881414411779]]></out_trade_no> <result_code><![CDATA[SUCCESS]]></result_code> <return_code><![CDATA[SUCCESS]]></return_code> <sign><![CDATA[0C1D7F2534F1473247550A5A138F0CEB]]></sign> <sub_mch_id><![CDATA[10012345]]></sub_mch_id> <time_end><![CDATA[20141027200958]]></time_end> <total_fee>1</total_fee> <trade_type><![CDATA[JSAPI]]></trade_type> <transaction_id><![CDATA[1002750185201410270005514026]]></transaction_id> </xml>
|
NATIVE & PC
参考
微信支付开发(1) JS API支付
微信支付开发(4) 动态链接Native支付