微信支付
微信内 H5 支付(公众号支付)
走统一引入 JSSDK
的新方式实现支付逻辑。
JSSDK
文档 地址
- 网页授权(获取
access_token
和 openid
) 文档 地址
- 统一下单 文档 地址
- 微信内
H5
调起支付 文档 地址
明确两种 access_token
- 网页授权
access_token
:基于 Oauth 2.0
,需要 code
换取,用于维持登录状态。
- 普通
access_token
:后台可直接生成,换取 jsapi_ticket
进而加密生成 signature
作为 wx.config
参数。
整个流程
支付统一下单接口需要 openid
,所以必须要授权获取。因为此处只需要 openid
,所以 snsapi_base
为 scope
走静默授权。(为了方便起见,此处每次进入都获取 code
走静默授权。)
进入页面判断链接上是否有 code
,没有则请求接口得到 url
并重定向:
1 2 3 4
| { url: 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx1232123123&redirect_uri=带参数(包括锚点)的编码后的重定向的回调地址&response_type=code&scope=snsapi_base&connect_redirect=1&state=xxxx#wechat_redirect'; }
|
微信内部多次重定向之后,最后带上 code
和 state
301 重定向回设置的回调地址:
1
| 301: https://www.flqin.com/test.html?id=135price=1322&code=ASDJIAJD13D823D&state=13212313#/
|
前端判断到链接有 code
,将 code
及一些其他页面需要的参数一起传给后端,后端拿到 code
换取网页授权 access_token
和 openid
,此处后端可将 access_token/refresh_token
存入 cookie
或者通过其他方式 jwt
维持登录状态,就无需重复获取 code
。
openid
为统一下单 jsapi
接口必传参数,用于得到 prepay_id
参数值,即为 package
参数。最后接口统一返回 wx.confg
和 wx.chooseWXPay
所需参数,前端调用 JSSDK
即可。
wx.chooseWXPay
的成功、完成回调仅代表支付完成,
代码演示:
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
| async queryWxCode() { if (!code) { const { data } = await api.wxToPay({url:'当前页面完整链接'}).catch((e) => e); data && (window.location.href = data); return; } return api.wxPay({code,url:'当前页面包含参数的url'}); }, async registerWx() { const { data } = await this.queryWxCode().catch((e) => {}); this.wxSdkInfo = data; wx.config({ debug: false, appId: data.appId, timestamp: data.timeStamp, nonceStr: data.nonceStr, signature: data.signature, jsApiList: ['chooseWXPay', 'hideMenuItems'] }); wx.ready(() => { this.hideWxMenus(); this.createWxpay(); }); }, hideWxMenus() { const menuList = ['menuItem:share:appMessage', 'menuItem:share:timeline', 'menuItem:copyUrl']; wx.hideMenuItems({ menuList }); }, createWxpay() { const data = this.wxSdkInfo; wx.chooseWXPay({ timestamp: data.timeStamp, nonceStr: data.nonceStr, package: `prepay_id=${data.prepayId}`, signType: data.signType, paySign: data.paySign, success: this.queryPayment, fail: this.showFail, cancel: this.showRefail, complete({ errMsg }) { const SUCCESS = /:ok/gi.test(errMsg); const CANCEL = /:cancel/gi.test(errMsg); if (SUCCESS) this.queryPayment(); else if (CANCEL) this.showRefail(); else this.showFail(); } }); },
|
支付宝支付
支付宝内 H5 支付
必须接入支付宝 JSAPI
。
H5
开发文档 地址
Alipay JSSDK
地址
- 支付文档 地址
注意点:
- 不涉及读取用户优惠券之类的都走普通无单号支付,即无需授权。
- 授权过程与微信大致一致。
代码演示:
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
| async registerAlipay() { const alipayInit = async () => { await this.queryAliOrder().catch((e) => e); this.createAlipay(); }; if (window.AlipayJSBridge) alipayInit(); else document.addEventListener('AlipayJSBridgeReady', alipayInit, false); }, async queryAliOrder() { const {data: orderStr } = await api.aliPayH5({orderId:1}).catch((e) => e); }, async queryAliCode() { const { href, search } = window.location; const query = search && search.replace(/[?\/]/g, ''); const { auth_code: code } = qs.parse(query); if (!code) { const { data } = await api.aliToPay(href).catch((e) => e); data && (window.location.href = data); return; } const {data: tradeNO } = await api.aliPayH5({orderId:1,code}).catch((e) => e); }, async createAlipay() { AlipayJSBridge.call('tradePay', { 'orderStr/tradeNO' }, ({ resultCode }) => { const SUCCESS_CODES = ['9000', '8000', '6004']; const UNKNOW_CODES = ['7001', '6001', '6002']; if (~SUCCESS_CODES.indexOf(resultCode)) _this.queryPayment(); else if (~UNKNOW_CODES.indexOf(resultCode)) _this.showRefail(); else _this.showFail(); }); },
|
第三方浏览器中 支付宝支付
无需授权,直接将 url
和订单号传给后端,后端返回一个 form
表单添加到页面即可唤起支付宝 APP
,支付完成后,根据后端配置支付宝会自动回跳到支付结果页并携带一堆参数。此时需要查询公共结果接口来确定支付是否成功。
注意点:
- 通过链接上是否有参数
alipay.trade.wap.pay.return
来判断是否是支付回调回来的页面。
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| registerH5() { const { origin, pathname, search } = window.location; const { method } = qs.parse(search.replace(/[?\/]/g, '')); if (method === 'alipay.trade.wap.pay.return') { this.queryPayment(); return; } const { data: form } = await api.aliPayH5(orderId,url).catch((e) => e); if (!form) return; const div = document.createElement('div'); div.innerHTML = form; document.body.appendChild(div); document.forms[0].submit(); }
|
公共查询结果 queryPayment
需要轮询支付结果。
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| async queryPayment() { const TIMES_REACH = 0; const TIMES_MAX = 5; const SUCCESS_CODE = '1'; const WAITING_CODE = '0'; if (this.qTimes === TIMES_REACH) { this.qTimes = TIMES_MAX; return; } const { data } = await api.payStatus(orderId).catch((e) => e); if (data == SUCCESS_CODE) { this.showSuccess(); this.qTimes = TIMES_MAX; } else if (data == WAITING_CODE) { console.log('查询支付结果中...'); clearTimeout(this.timer); this.timer = setTimeout(async () => { await this.queryPayment(); this.qTimes -= 1; }, 1000); } else { this.showFail(); } },
|
支付总结
- 微信支付宝均需要配置合法域名,可精确到文件夹路径。
- 开发可用微信开发工具,结合 alert 调试,直接把代码发到已配置好域名的测试环境测试最佳。
- 注意参数的大小写及传递的内容,支付主要复杂度都集中在后端,前端需要配合后端传参。
分享
微信- > 微信(微信联系人,微信朋友圈,QQ 联系人,QQ 空间)
必须引入 JSSDK
,否则分享出来无法设置分享描述及分享图片。
JSSDK
文档:地址
- 获取普通
access_token
文档:地址
需要给后端传入带有参数的当前页面 url
(无需编码),让后端返回 wx.config
所需参数即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const { data } = await api.getWxConfig({ url: '当前页面包含参数的url' }); wx.config({ debug: true, appId: data.appId, timestamp: data.timeStamp, nonceStr: data.nonceStr, signature: data.signature, jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone'], }); wx.ready(() => { const shareList = ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone']; shareList.forEach((item) => { wx[item]({ title: this.shareInfo.title, desc: this.shareInfo.description, link: window.location.href, imgUrl: this.shareInfo.image, success: function () { }, }); }); });
|
注意点:
- 首先需要在微信后台设置接口安全域名:
www.xxxx.com
,无需到具体路径及参数。
- 前端仅需传给后台当前页面带参数的
url
即可,甚至接口可以自己取请求页面带参数的 url
,前端无需传。
- 注意
timeStamp
的大小写。
- 注意
jsApiList
选择即将废弃的分享接口,新分享接口反而不好用。
- 分享无需授权,因:参数
nonceStr <- jsapi_ticket <- access_token <- appid,secret
。此 access_token
为普通 access_token
,appid
和 secret
均在微信后台获取。区别于支付等网页授权 access_token
。
QQ(TIM) -> QQ(QQ 联系人,QQ 空间,微信联系人,微信朋友圈)
需要引入 api
:
1
| <script src="//open.mobile.qq.com/sdk/qqapi.js"></script>
|
js
执行:
1 2 3 4 5 6 7
| const share = { title: '分享标题,最大45字节', desc: '分享内容,最大60字节', image_url: '图片URL,最小需要200 * 200', share_url: '分享链接与页面URL同', }; mqq.data.setShareInfo(share, callback);
|
另 mqq.ui.showShareMenu();
可直接唤起 QQ
分享面板。
支付宝 -> 支付宝(朋友动态,联系人)
直接按如下设置 meta
即可:
1 2 3 4
| <meta name="Alipay:title" content="分享标题" /> <meta name="Alipay:imgUrl" content="分享图片url" /> <meta name="Alipay:desc" content="分享描述" /> <meta name="Alipay:link" content="分享链接" />
|
微博,头条,知乎等其他平台浏览器 -> 微信,QQ
微博可注册轻应用使用 JS-SDK 完成分享设置。收益不大就没做了。
知乎实测 IOS 可以取写死的描述及动态标题,图片无法设置,安卓只能设置标题。
对于其他平台分享,统一兜底处理方式:
分享标题及描述设置 head
元素:
1 2 3 4
| <head> <title>分享标题</title> <meta name="description" content="分享描述" /> </head>
|
分享图及描述设置(一般默认取页面第一张大于 300px
图及第一段描述):
1 2 3 4 5 6
| <body> <div style="display: none;"> <p>分享描述</p> <img src="图片地址url" /> </div> </body>
|
使用 ogp
:
1 2 3 4 5 6 7
| <head> <meta property="og:type" content="website"> <meta property="og:title" content="分享标题"> <meta property="og:description" content="分享描述"> <meta property="og:img" content="完整的分享图片链接"> <meta property="og:url" content="完整的分享页面地址"> </head>
|