[TOC]
## 前言
完成微信h5支付的你,继续公众号的支付也许更简单哦。
## 场景
微信浏览器中的应用支付必须依赖于公众号支付,下面就公众号支付中的一些技术点进行详细的解析。
## 准备工作
### 基本配置申请
参考资料:[微信公众号开通支付功能--百度经验教程](https://jingyan.baidu.com/article/636f38bb48c6fcd6b9461071.html)
### 基本信息
* 服务号,服务号绑定的管理员号
* 开通支付账号,并记住支付账号,与支付账号绑定的微信号
* 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参考文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN)
#### 二 根据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://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3)
## 其他
- [微信支付java指南](https://mp.weixin.qq.com/s/VQJivfo3_rUMoqgAsj-_uQ)
- 前端入门
- 前端入职须知
- 入职准备
- 前端ide
- vsc快速上手指南
- 上手指南一
- 常用插件推荐
- 微信开发者
- sublime的使用
- hbuilder入门
- ws
- 前端面试
- 概要
- bat面试题库
- 题库一
- 面试大纲
- 题库二
- 面试大纲
- 前端基础面试题
- js基础面试题
- vue&&react面试题
- 数据结构&&算法面试题
- 题库三
- 001
- 题库四
- 中小公司leader
- 常规题库
- 前端规范
- 001
- css
- 001
- 002
- es6(js)
- 001
- 002
- 003
- 004
- node
- 001
- vue
- 001
- react
- 001
- 预处理器
- 001
- gulp
- 001
- webpack
- 001
- 设计模式
- 001
- web常识
- 001
- koa
- 001
- 小程序
- 001
- 数据结构与算法
- 001
- 推荐文章
- 面试指南
- web性能
- 面试分享
- 001
- ps
- ps入门阶段
- 图片类型以及区别
- 基本概念以及常用工具
- ps操作技巧
- 几个问题
- ps互动教程软件(app)
- 资源导航
- ps站点资源导航
- ui站点导航
- html
- h5专题
- audio/video
- Geolocation
- Websockets
- Web storage
- Communication
- Web Workers
- requestAnimationFrame
- async&&defer
- fileApi
- h5调用底层能力
- input新解
- canvas实战篇
- 教程
- js
- javascript入门
- js代码审查工具
- js性能优化
- 浏览器dom对象
- js优质资源
- indexDB入门
- jquery
- jq基本语法
- jq插件与原生插件
- Jq使用建议
- ajax后退解决方案
- jq常见问题
- js常用技术
- js控制运动-move.js
- 常用正则归纳
- js实用技术
- 鼠标行为分析
- document.referrer
- 你可能不知道的调试技巧
- 表格文件的读取与下载
- 异步编程那些事
- 数据结构
- 编程环境和模型
- 列表
- 栈
- 队列
- 链表
- 字典
- 散列
- 集合
- 二叉树和二叉查找树
- 参考
- js编程
- js模块机制
- 算法
- 基本算法
- 递归
- 图和图算法
- 图定义
- 系统建模
- 图类
- 搜索图
- 查找最短路径
- 拓扑排序
- 图实践
- 排序算法
- 测试平台
- 冒泡排序
- 选择排序
- 插入排序
- 基本排序的比较
- 希尔排序
- 归并排序
- 快速排序
- 实践
- 二分排序
- 检索算法
- 顺序查找
- 二分查找
- 查找文本数据
- 检索实践
- 高级算法
- 动态规划
- 贪心算法
- 高级算法实践
- 代码重构
- 简化函数参数
- 001
- 002
- 基础巩固
- 001
- es2015实战
- 初识es-module
- 异步编程
- es6工厂函数
- filter|map|reduce
- js实战篇
- 前端图像处理
- touch事件知多少
- 手势与实践
- print表格分页
- 精彩文章推荐
- 001
- 插件库
- 插件大全
- 功能性插件
- pdfjs
- wdatepicker
- qrcoder
- barcode插件
- photoviewer
- hammer.js
- echarts
- 视频控件
- 发送浏览器通知
- 触屏签名插件
- 图片相关插件推荐
- 待分类插件(pc)
- 待分类插件(手机端)
- 交互组件
- layerjs
- web
- web兼容
- pc端兼容bug汇总
- ie兼容bug汇总
- ie8测试专题
- web常用技术点
- web兼容汇总001
- ie6专题
- css兼容
- web安全
- web安全初级
- app/h5组件
- app教程
- 前端教程
- rubikx的教程
- 与app交互逻辑
- h5唤起app通识
- webview专题
- webview总纲
- js与oc交互协议
- js与安卓交互协议
- 兼容问题汇总
- jsBridge专题
- errorBook.js
- 常用工具
- chrome-devtool使用
- chraels
- 开发注意事项
- web常识
- markdown教程
- 自定义风格思路
- 经验与问题总结
- 总结1
- 前端应该注意哪些seo
- 懒加载和预加载
- https
- 前端重构
- web优化
- 移动端web优化
- http缓存
- web端优化
- 图片专题
- svg专题
- 深入浅出svg
- 地图使用
- 注意事项
- 需求提交
- 常规交互需求提交
- 缓存
- 干货文章
- 浏览器缓存
- 内存
- web性能指南
- 读书笔记
- ui框架
- 概论
- easyui
- bootstrap
- 入门推荐
- modal插件使用
- 按钮组件
- 正确使用栅格布局
- 下拉框插件使用
- 表单使用与验证
- tab切换项插件
- 分页控件
- 进度条控件
- 文件上传控件
- 面板控件
- 常见特效与插件
- weui
- sui-pc
- sui-mobile
- layerUI
- frozen-UI
- rubik-u那些事
- 基本内容
- 小程序
- 小程序入门
- 入门
- 实践踩坑
- 001
- 基本语法
- 开发大纲
- 注意事项
- 微信专题
- 基本入门
- 准备工作
- 定制菜单
- 图文消息与图文推送
- h5支付
- 公众号支付
- node完成微信支付
- 进阶使用
- 微信分享
- weui使用
- 基本使用
- 支付宝专题
- 支付宝h5支付
- app支付接入
- 服务窗支付
- java
- java入门
- eclipse基本使用
- 语言特点
- java代码规范
- 编译调试
- java基本语句
- springMVC
- javaweb
- vm模板引擎
- freemarker
- 常用常识
- 常用常识2
- 部署项目
- web --xml文件解析
- java生成pdf文件
- java读取、写文件案例
- 图片加水印
- 图片加水印2
- java-cookie
- 验证码文件
- sql-mapper语法
- maven教程
- mySql教程
- jeecms
- flash
- flash入门
- flash准备工作
- 运行与编译
- 浏览器中flash设置教程
- flash检测