微信公众号扫码支付笔记

前提

正常情况下, 我们是可以使用微信的 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']; # 用来换取openid到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();
// 单号规则, 15-17 服务器ID 17-19 计算节点ID
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 等信息调用下单接口.

微信公众号扫码支付笔记

https://doc.exti.cc/2022/03/02/woa-scan-pay-md/

作者

bywayboy

发布于

2022-03-02

更新于

2022-03-04

许可协议