前提
正常情况下, 我们是可以使用微信的 NATIVE 支付来做. 但如果我们有公众号, 我们希望用户扫码付款的时候,能够进入到公众号网页中来完成. 这对于增加用户粘性是有一定好处的.
基本步骤
1. 生成一个二维码
扫码支付, 第一步肯定是生成一个二维码, 这个二维码可以是自己定义的一个链接.
例如: 我这里生成的是 https://woa.exti.cc/sq?p=J202203101230110000000000
在这里 p 就是生成的交易单号. 这个交易单号作为主键 保存有交易数据.
2. 扫码后换取CODE
我们需要用户微信打开这个链接的时候, 自动跳转到 open.weixin.qq.com 去获取code, 微信会跳转回redirect_uri指定的路径, 这个时候新的url里面多了一个code.为了追求性能, 这一步我们就直接在nginx里面做了.
1 2 3 4 5 6 7
| location /sq { if ($request_uri ~* "^/sq\?p\=([\d\w]+)$" ) { set $query_arg1 $1; return 302 "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&response_type=code&scope=snsapi_base&state=$query_arg1&redirect_uri=https%3A%2F%2Fwoa.exti.cc%2fqrpay&connect_redirect=1#wechat_redirect"; } return 204; }
|
3. 获取openid
当最终跳转到付款页面的真实地址的时候, 我们使用 appid secret 去获取openid, 并将必要的数据渲染到模板页面中, 再由模板页面去调用公众号支付接口, 完成支付过程.
1 2 3 4 5 6 7 8 9 10 11 12
| $state = $r->get['state']; $code = $r->get['code']; $woa = Config::get('woa'); $result = http_get("https://api.weixin.qq.com/sns/oauth2/access_token?appid={$woa['appid']}&secret={$woa['secret']}&code={$code}&grant_type=authorization_code"); if($result['code'] === 200){ $body = json_decode($result['body'], true); return view('qrpay/index.php',[ 'title'=>'APP支付页面', 'openid'=>$body['openid'], 'out_trade_no'=>$state, ]); }
|
下面是模板页的内容
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
| <!doctype html> <html> <head> <title>万家APP</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"/> <title><?php echo $title ?></title> <style> </style> </head>
<body> </body> <script lang="javascript"> var buyer_id = "<?php echo $openid ?>"; var trade_no = "<?php echo $out_trade_no ?>"; function onBridgeReady() { let s = parseInt(trade_no.substr(15, 2)),n = 8400 + parseInt(trade_no.substr(17,2));
let xhr = new XMLHttpRequest(); xhr.open('POST', 'https://fee-' + s + '.exti.cc/n-' + n +'/notify/qrpay'); xhr.onreadystatechange = function(){ if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { let data = JSON.parse(xhr.responseText); if(data.success){ WeixinJSBridge.invoke('getBrandWCPayRequest', data.package, function(res) { WeixinJSBridge.call('closewindow'); }); }else{ alert(data.message); } } } xhr.setRequestHeader('Content-Type', 'application/json') xhr.send(JSON.stringify({trade_no, buyer_id})); }
if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); } else if (document.attachEvent) { document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } } else { onBridgeReady(); } </script> </html>
|
4. JSAPI 支付
使用 交易单号获取交易信息, 结合openid、appid 等信息调用下单接口.