前言
完成微信 h5 支付的你, 继续公众号的支付也许更简单哦
场景
微信浏览器中的应用支付必须依赖于公众号支付, 下面就公众号支付中的一些技术点进行详细的解析
准备工作
基本配置申请
参考资料: 微信公众号开通支付功能 -- 百度经验教程
基本信息
服务号, 服务号绑定的管理员号
开通支付账号, 并记住支付账号, 与支付账号绑定的微信号
appid, 秘钥
支付账号开通支付目录 (直接支付地址的上一级目录)
设置了页面授权域名, 并且是你的站点域名地址
基本接口权限, 尤其是 jssdk 部分权限, 保证尽可能都开通
业务流程图解以及时序图
与微信 h5 基本相同, 唯一不同的是这次微信返回的需要唤起微信 sdk 支付的参数列表
技术问题
获取 openid
网站应用微信登录是基于 OAuth2.0 协议标准构建的微信 OAuth2.0 授权登录系统获取 openid 分为两步, 获取 code, 然后根据 code 获取 openid, 建议这两部分请求由后端发起, 前端直接请求会涉及到跨域问题后端直接把这两个方法定义为工具方法, 使用方便, 便于其他场景的复用
一 : 请求获取 code, 如果不想浪费时间请直接复制粘贴使用
标准格式拼接代码:
- let encodeUrl=encodeURIComponent(`http://xxx/xhxwxpay?productId=${productId}&orderNo=${orderNo}`)
- let tempUrl=`https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx6f5de09c8ef178a7&redirect_uri=${encodeUrl}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`
请求参数说明
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识 |
redirect_uri | 是 | 请使用 urlEncode 对链接进行处理 |
response_type | 是 | 填 code |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写 snsapi_login 即可,这里用的 snsapi_userinfo |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止 csrf 攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加 session 进行校验 |
返回说明: 用户允许授权后, 将会重定向到 redirect_uri 的网址上, 并且带上 code 和 state 参数
redirect_uri ? code = CODE & state = STATE
若用户禁止授权, 则重定向后不会带上 code 参数, 仅会带上 state 参数
redirect_uri ? state = STATE
参考文档: 微信中 web 网站获取 code 参考文档
二 根据 code 获取 openid
一个用户针对一个公众号 openid 是固定的, 所以获取到一样的不用怀疑, 涉及到部分敏感的公众号的秘钥等, 建议是后端处理发起请求, 这样也可以避免前端跨域的问题
- // 网关或者后端的设置 (以 koa 框架的为例)
- *post_getOpenId(){
- let reqData = this.request.body;
- let param = {
- appid:'wxxxx',
- secret:'affsdcsdvdsvfv6',
- code:reqData.code,
- grant_type:'authorization_code'
- }
- let result = yield this.api.getOpenId(param);
- this.body = result;
- }
- // 获取 openid 传入对象的形式, 改造通用的 api 方法
- getOpenId: function* (apiParam, json = true) {
- // 获取 token 的地址
- let apiUrl='https://api.weixin.qq.com/sns/oauth2/access_token'
- let response = yield request.get(apiUrl, { qs: apiParam, json: json });
- return responseHandle(response, apiUrl, apiParam);
- }.bind(this),
- // 前端的写法, 好处是避免暴露公众号的相关信息
- this.$api.post("order/getOpenId", { code: this.code }).then(res => {
- // 正确获取 openid 的情况下 请求后台参数得到对应的返回参数, 目前只需要 openid
- if (res.openid) {
- this.openId = res.openid;
- // 准备条件足够的话 可以唤起支付
- this.topay()
- }else {
- // 请求失败或者没有对应的字段
- }
请求参数说明, 通过 code 获取 access_token
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
secret | 是 | 应用密钥 AppSecret,在微信开放平台提交应用审核通过后获得 |
code | 是 | 填写第一步获取的 code 参数 |
grant_type | 是 | 填 authorization_code |
返回结果说明
- // 正确的返回
- {
- "access_token":"ACCESS_TOKEN",
- "expires_in":7200,
- "refresh_token":"REFRESH_TOKEN",
- "openid":"OPENID",
- "scope":"SCOPE",
- "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
- }
- // 错误的返回
- {"errcode":40029,"errmsg":"invalid code"}
刷新 fresh-token 有效期 如果 token 失效了, 可以用 refresh_token 重新获取一个
微信支付 sdk
方式一 : 官网的方式
invoke 方法 , 简单有效, 直接根据接口返回参数唤起以下代码实例是 vue 环境下的, 其他环境请自行匹配, 仅供参考
- // 准备好微信 sdk 部分
- jsSdk() {
- // 判断微信的 WeixinJSBridge
- if (typeof WeixinJSBridge == "undefined") {
- if (document.addEventListener) {
- document.addEventListener('WeixinJSBridgeReady', this.onBridgeReady, false);
- } else if (document.attachEvent) {
- document.attachEvent('WeixinJSBridgeReady', this.onBridgeReady);
- document.attachEvent('onWeixinJSBridgeReady', this.onBridgeReady);
- }
- } else {
- this.onBridgeReady();
- }
- },
- // 支付 sdk 准备完成
- onBridgeReady() {
- // 触发微信支付
- WeixinJSBridge.invoke('getBrandWCPayRequest', {
- appId: this.payOption.appId,
- // 公众号名称, 由商户传入
- timeStamp: this.payOption.timeStamp,
- // 时间戳, 自 1970 年以来的秒数
- nonceStr: this.payOption.nonceStr,
- // 随机串
- package: this.payOption.package,
- //prepay_id 用等式的格式
- signType: this.payOption.signType,
- // 微信签名方式:
- paySign: this.payOption.paySign,
- // 微信签名
- },
- function(res) {
- if (res.err_msg == "get_brand_wcpay_request:ok") {
- // 支付成功 返回成功页
- let tempUrl = "//paysucc"location.href = tempUr
- } else {
- // 取消支付或者其他情况 get_brand_wcpay_request:cancel get_brand_wcpay_request:fail
- let tempUrl = '//topay'location.href = tempUrl
- }
- });
- },
方式二 : 需要引入 js-weixin 的模块, 流程如下:
引入模块 --ready-- 获取 access-token-- 获取 ticket-- 生成签名 (wx.config 需要)-- 结合接口返回参数 -- 唤起 wxpay(比较麻烦, 不推荐使用) 参考文档:
参考文档
设置支付目录以及回调域名
来源: https://juejin.im/post/5a7957d25188257a7f1d8b04