微信支付-JSAPI
必要条件
一台可通过公网地址访问的服务器
服务器可在阿里云或腾讯云购买。
一个具有支付接口权限的公众号
公众号自然是在 ‘微信公众平台’ 申请。具有支付权限的公众号得注册为企业级公众号(或称为服务号),并且通过了企业认证。企业认证需要机关单位或企业门店的 ‘营业执照’ 。在公司的话可使用公司营业执照。如果是个人,除了借别人的就只能自己去办一个了。(我就是自己去申请办理了 “个体工商户营业执照”。办理这个营业执照需要房屋或经营场所的产权使用证明和身份证。房屋使用证明可以是购房合同,和租赁合同,或产权证和产权所有方手写的一张授权使用证明。办理地点就在经营场所所在当地的市场监督管理局,一般一周就能办理下来。办理下来后根据规定得去当地的税务局办理税务登记手续,然后就可以去正规的刻章店刻章了,这时候你这个个体户也就是小企业可以开张了。公众号有了营业执照和公章就足以办理企业认证了。当然如果你办公场地装修好了也有人上岗办公了那么这个时候可以去银行开通一个对公账户。微信支付可用的上,没有的话也没关系微信支付也可以填写私人银行账户。另外:办理税务登记后记得每个季度去办理税务申报,可在网上办理也可直接去税务局办理。逾期没办的税务局会打电话给你,有可能会影响企业信誉)
听说前段时间微信给个人公众号也开放的支付功能,我还没去看相关文档。
一个已经通过了备案的域名
域名和服务器一样可在阿里或腾讯购买。(不过我忘了域名备案需要些什么东西了)
具有一定的前后端开发能力以及服务器搭建能力
实现步骤
首要目标
在微信的小程序或公众号内的支付场景中,除了业务上的一些交互外往往最终付款的时候我们就是通过一个按钮 “结算” 或 “付款” 来唤起支付页面。然后输入密码,成功后就跳转到支付成功的页面。事实上也是如此。通过微信开放文档得知唤起支付页面就是下面这段 js 代码
1
2
3
4
5
6
7
8
9
10
11 //文档地址:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#58
wx.chooseWXPay({
timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: '', // 支付签名随机串,不长于 32 位
package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: '', // 支付签名
success: function (res) {
// 支付成功后的回调函数(跳转到支付成功页面等操作)
}
});
我们的目标就是要这段代码能够完美的得到执行。
通过阅读文档发现这段 js 需要的参数package prepay_id 得通过统一下单接口才能拿到。而调用统一下单接口又有一个关键参数 openId 而这个 openId 得通过网页授权才能拿到。
网页授权的前提又必须开启公众号的开发者模式。开启开发者模式可参看本博客文章 微信公众号配置服务器
第一步:网页授权
网页授权又分为如下四步
文档地址:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
引导用户进入授权页面同意授权,获取code
执行窗口跳转 js 弹出授权页面
1 | let url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect" |
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 公众号的唯一标识 |
redirect_uri | 是 | 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理 |
response_type | 是 | 返回类型,请填写code |
scope | 是 | 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 ) |
state | 否 | 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 |
#wechat_redirect | 是 | 无论直接打开还是做页面302重定向时候,必须带此参数 |
appid:公众号 -> 基本配置 -> 公众号开发信息 开发者ID(AppID)
redirect_uri:为前端一个页面的地址。用户同意授权后会重定向到这个地址并会在地址后面跟上 code 值。在这个地址页面中我们通过解析到地址的 code 值向后端发起请求获取 access_token 从而获取到 openid
1
2
3 // vue 可通过 route 组件快速获取到 code 值
let code = this.$route.query.code;
alert(code);
通过code换取网页授权access_token
获取code后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
1 | // java 使用 fegin 请求 http 接口 |
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 公众号的唯一标识 |
secret | 是 | 公众号的appsecret |
code | 是 | 填写第一步获取的code参数 |
grant_type | 是 | 填写为authorization_code |
secret:公众号 -> 基本配置 -> 公众号开发信息 开发者密码(AppSecret) ps: 没有的话根据提示设定下就可以了
接口成功返回如下:
1 | { |
参数 | 描述 |
---|---|
access_token | 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 |
expires_in | access_token接口调用凭证超时时间,单位(秒) |
refresh_token | 用户刷新access_token |
openid | 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID |
scope | 用户授权的作用域,使用逗号(,)分隔 |
如果需要,开发者可以刷新网页授权access_token,避免过期
由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。(这里建议 access_token 存放在 redis 中,利用 redis 过期回调机制来反复更新 token)
获取第二步的refresh_token后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 公众号的唯一标识 |
grant_type | 是 | 填写为refresh_token |
refresh_token | 是 | 填写通过access_token获取到的refresh_token参数 |
正确时返回的JSON数据包如下:
1 | { |
通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。
请求方法
http:GET(请使用https协议) https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
参数说明
参数 | 描述 |
---|---|
access_token | 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 |
openid | 用户的唯一标识 |
lang | 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 |
返回说明
正确时返回的JSON数据包如下:
1 | { |
参数 | 描述 |
---|---|
openid | 用户的唯一标识 |
nickname | 用户昵称 |
sex | 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 |
province | 用户个人资料填写的省份 |
city | 普通用户个人资料填写的城市 |
country | 国家,如中国为CN |
headimgurl | 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。 |
privilege | 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom) |
unionid | 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。 |
第二步: 微信 js-sdk 初始化配置
通过网页授权我们已经可以拿到 openid 了。这时我们是否可以执行 wx.chooseWXPay 这个 js 了呢?还不行!这里有三个步骤还要做
文档地址:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#4
步骤一:绑定域名
先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
这里根据提示操作下载证书配置即可。(这里的域名简单理解就是前端服务的域名)
备注:登录后可在“开发者中心”查看对应的接口权限。
步骤二:引入JS文件
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.6.0.js
如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (支持https)。
步骤三:通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
1 | wx.config({ |
wx.config 所需的参数需调后端接口返回。
(部分前端代码)
1 | let url = process.env.API_PROXY_ROOT+"/weixin/wx-config-signature"; |
(部分后端代码如下)
1 |
|
签名算法
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
即signature=sha1(string1)。 示例:
1 | noncestr=Wm3WZYTPz0wzccnW |
步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
1 | jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value |
步骤2. 对string1进行sha1签名,得到signature:
1 | 0f9de62fce790f9a083d5c99e95740ceb90c27ed |
jsapi_ticket
jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取(注意这里的 token 不是前面网页授权的 token 详情参看文档)。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
- 参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
- 用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
成功返回如下JSON:
1 | { |
获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
第三步:唤起支付
终于到了 wx.chooseWXPay 的部分了。不过别高兴的太早,前面也说了调用这段 js 代码需要的 prepay_id 等参数我们需要提前准备好来,自然也是请求后端接口获取到喽。获取 prepay_id 的过程称之为 统一下单
文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
统一下单
在官方文档中统一下单的参数还是不少的。有了参数后还需要生成签名。生成完签名后参数 map 还得转成 xml 格式。这里微信提供了 java 版 demo 可以帮我们省去不少麻烦。
demo下载 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
java版下载解压后如下:
根据个人喜好可以将 src 代码和 pom 依赖复制到项目使用。也可以用 maven 打个包后引入项目中使用。打包时可能会遇到一个错误,这是由于构建该项目的 maven 版本比较老导致的。百度下解决方法就是在 pom 的 build 标签里的内容用 pluginManagement 包裹下就好。另外注意打包前 需将源码 WXPayConfig 类中的 抽象方法都加上 public 修饰(不要问我为啥,你试过就知道)
在项目中使用的时候可参照 README.md 文件进行使用。算了还是贴下我的代码吧。他文件中说的不对 不是用 implements 而是用 extends 一些方法的实现也不全。
1 | public class MyConfig extends WXPayConfig { |
除去上边配置的参数和非必填项,统一下单的参数大概有这些(不包括签名)
1 | Map<String, String> data = new HashMap<String, String>(); |
调用统一下单:
1 | MyConfig config = new MyConfig(); |
成功后我们就能拿到 prepay_id 了
1 | if ("SUCCESS".equals(resp.get("return_code"))){ |
组织参数并返回给前端
1 | Map map = new HashMap<String,String>(); |
唤起支付
唤起支付所在的的页面是需要在配置的
相关文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3
一切就绪 前端 js 可通过按钮来触发这段代码
1 | wxpay:function () { |
如果一次性成功!那么恭喜你太幸运了。
关于调试
- 在整个开发配置中微信让我们配置了一些个域名或目录(如支付目录,js 安全域名等),这些其实都限制了我们在调试的时候必须使用域名。我的做法是把域名解析到自己宽带的IP(前提是网络拨号得到的IP,如果不是的话可打供应商电话让其配置),然后在路由器配置虚拟服务器和端口转发,转到自己电脑的ip和端口上。这样通过域名访问的公众号页面经过路由器由转到我们本地来了。
- 此外唤起支付的 js 在开发者工具上执行是没有效果的。会提示:chooseWXPay:没有此SDK或暂不支持此SDK模拟。是因为这个 js 只有在手机微信的浏览器执行才能被识别。微信开发者工具有个预览功能,点击会有二维码。在手机与电脑在同一网络环境下手机微信扫码该页面就能在微信里打开了。自此就能正常唤起支付了。
关于工具
- 首先前端代码运行后请放在微信开发者工具中调试。这个工具的使用需要登录微信,从而在获取网页授权的时候是比较方便的。工具地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/devtools.html
- 微信提供了两个签名工具来帮助我们验证我们的签名代码,这极大的缩短了我们爬坑的时间
- ticket 签名工具:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
- 支付接口(统一下单等地方)签名工具:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1
关于公众号的一些配置
公众号 -> 基本配置
- 开发者密码:一些配置项要用到
- ip 白名单:白名单中的 IP 才能调微信的接口
- 服务器配置:配置成功才能成为开发者。然后才可以在公众平台网站中申请微信认证,认证成功后,将获得更多接口权限
公众号-> 开发者工具 -> web 开发者工具
绑定过的开发者账号才能登录微信开发者工具。绑定的对象必须是关注了该公众号的用户。
公众号 -> 公众号设置
在这些域名下的前端服务才能调用微信 js 功能。
微信商户平台 -> 账户中心 -> API安全
API证书与API密钥调用支付接口需用到。
商户信息的商户 id 支付接口也会用到。
参考文档
微信开发文档:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
JSAPI 开发文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=1_1
博客:https://blog.csdn.net/u013391488/article/details/79314041