【记录】记一次支付功能的开发

今天国庆赶车回家 ,陪女朋友赶车(女朋友的票要早两个小时), 无聊本想着看会剧, 但是车站坑爹啊, 网络太卡了, 想下载个腾讯视频都下载不了, 既然没网看不了剧, 发呆太可惜了, 想着最近很久没补博客了, 趁这个时间补一点好了, 这星期做的这个支付的几个关键点记录一下。

提到支付当然微信和支付宝都得做, 还有公司内部的支付方式, 当然公司内部的支付就不提了, 这个每个公司的情况都不一样, 这里关键记录一下微信和支付宝的

微信中打开支付

在微信自身中打开我们的支付页面, 此时我们的网页可以被赋能可以获得jsBridge , 这是微信暴露给webview的方法, 类似于Cordova 之类的 web容器式混合应用, 通过jsBridge 唤起微信原生的支付

获取微信code

在微信原生中支付,需要获取一串code, 类似于登录操作, 需要把code交给后台, 后台去问微信索要openID , 用来支付

那么如果获取到这串code:
我们需要在路径上判断一下query, 如果没有query 那就跳转到获取code的地址, 并携带上当前的地址,因为跳转到该地址后会携带着code 跳转回来,

跳转去获取code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取路径上携带的code
data() {
return {
code: this.$route.query.code ? this.$route.query.code : '',
}
}

// 检测微信环境
if (verify('wechat')) {
// 如果code 不存在就跳去获取
if (!this.code) {
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wechatAppId + "&redirect_uri=" + encodeURIComponent(window.location.href) + "&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect"
}
}

同时这里还有需要注意的地方,微信获取的code只能使用一次支付, 也就是说, 当用户唤起一次支付以后, 但并没有真的支付, 而是取消支付了, 那他下次就无法再唤起支付, 因为code使用过了, 后台的订单会创建失败,

解决方法就是在获取一次code!

如何再获取一次code呢? 刷新页面呗

这里我用了比较偷懒的办法, 我们不是判断 this.code 是否存在才去获取code吗, 那我们把旧的code干掉就可以了

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
getCodeAgain() {
let _url = window.location.href
// soot 是废弃的意思,, 随便起的名字, 可以随意换,
_url = _url.replace('code', 'soot')
window.location.href = _url
},
```

如此替换之后访问页面时就获取不到code, 自然会再去跳转获取新code , 嘿嘿, 这是比较偷懒的方法, 最好还是去正则匹配一下路径参数, 然后把code相关内容删掉, 避免url过长

***

关键的调用js 支付方法, 有比较坑爹的是, 使用 `res.err_msg` 来判断用户是否支付成功, 微信居然无法保证其可靠性!

顺便一提, params 参数是后台给的(我们公司的情况), 这种支付相关的还是交给后台保存吧


```js
WeixinJSBridge.invoke('getBrandWCPayRequest', params, function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 支付成功
// 微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
success && success('ok');
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
// 支付取消
cancel && cancel('cancel');
} else {
// 支付失败或错误
fail && fail('fail');
}
});

进行封装后的代码, 个人觉得还是封装的挺烂的, 但姑且可以用

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
// 微信支付
/**
* @msg 设置微信js 桥的 支付相关配置
* @param {type}
* @param { function } success 成功回调
* @param { function } cancel 用户去取消回调
* @param { function } fail 支付失败或错误回调
* @param { function } localError 本地出现错误回调 - 会在 wxPay 方法中被拦截
* @param { function } params 支付的相关配置 - 由后台返回
* @return:
*/
function setWxBridgeConfig({ success, cancel, fail, ...params }) {
WeixinJSBridge.invoke('getBrandWCPayRequest', params, function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 支付成功
// 微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
success && success('ok');
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
// 支付取消
cancel && cancel('cancel');
} else {
// 支付失败或错误
fail && fail('fail');
}
});
}

/**
* @msg 尝试调起微信支付
* @param {type}
* @param { function } success 成功回调
* @param { function } cancel 用户去取消回调
* @param { function } fail 支付失败或错误回调
* @param { function } localError 本地出现错误回调 - 会在 wxPay 方法中被拦截
* @param { function } params 支付的相关配置 - 由后台返回
* @return:
*/
export default function wxPay({ localError, ...params }) {
try {
let JSBridgeInterval = null;
// 如果 WeixinJSBridge 对象存在则进入配置
if (!(typeof WeixinJSBridge == "undefined")) {
setWxBridgeConfig(params);
} else {
// 等待sdk 加载完成, 如果sdk未加载完成就调用支付时 WeixinJSBridge 对象 不存在
JSBridgeInterval = setInterval(() => {
if (!(typeof WeixinJSBridge == "undefined")) {
clearInterval(JSBridgeInterval);
setWxBridgeConfig(params);
}
}, 100);
}
} catch (error) {
localError && localError()
}
}

浏览器中打开微信支付

浏览器中唤醒微信支付, 这就比较简单了, 当我们创建订单后只需要将网页跳转至后台返回的微信页面

1
2
// res 指得是后台接口的返回, mweb_url这也是我们公司的字段, 和微信没有关系,只是demo
window.location.href = res.data.mweb_url

支付宝支付(浏览器中)

同样的, 支付宝也有jsBridge 这种方式, 但我们没有选择, 因为我们的主要面向是微信用户, 支付宝我们选择一种比较通用的方式就可以, 也就是浏览器中的支付方式

这种方式需要在创建订单后后台返回一个html 字符串, 这是一段form表单, 我们接收到这串字符串以后,使用下面这段打开唤醒支付宝, alipayHtml就是后台返回的表单字符串

1
2
3
4
5
let div = document.createElement('div');
// alipayHtml 是后台返回的字符串
div.innerHTML = alipayHtml;
document.body.appendChild(div);
document.forms[0].submit();

如此就可以调用支付宝支付了

你的支持将鼓励我继续创作