咸鱼

咸鱼是以盐腌渍后,晒干的鱼

0%

微信支付开发日志

支付类型很多,这里是指在公众号上调用 JSAPI支付

支付产品.png

开通支付

这部分比较繁琐,但都是一些资料认证,在公众号平台的入口进入申请提交就可以了。

商户平台

如果成功开通了 微信支付 ,那么你就可以登录 商户平台 了,进行关联公众号,成功关联之后,就能实现在当前公众号中使用微信支付收款。

产品中心 > APPID授权管理 > 账号关联(AppID绑定) > 已关联账号 ,可以看到 是否已经关联公众号(服务号) 。

准备开发所需参数

  1. 商户平台 > 产品中心 > 开发配置 处,可以找到商户号
  2. 商户平台 > 账户中心 > API安全 > API密钥 安装安全证书之后,就可以设置API密钥,密钥为32为字符,自己定义。
  3. 商户平台 > 账户中心 > API安全 > API证书,申请证书,下载的是一个zip压缩包,有p12和pem证书。
  4. 公众平台 > 开发 > 基本配置 > AppId
  5. 公众平台 > 开发 > 基本配置 > AppSecret

公众号设置

公众号设置 > 功能设置 ,在 业务域名,JS接口安全域名,网页授权域名 三个地方设置前端的域名,如 wx.mydomain.com

公众平台-后端开发

第一步先把公众平台的接口调通,这相对简单一些,在保证公众平台没问题的情况下,再进行微信支付的接口开发。

1. 签名接口

首先,后端要提供一个签名接口,用于前端签名当前的URL,否则无法调用 JSSDK 的接口。

签名算法可以看 微信文档 ,大概流程:前端将URL发到后端,后端签名成功返回 timestamp、nonceStr、signature 三个参数的结果。

2. 获取用户的OpenId

OpenId 是用户的标识,在支付的时候要用到。本身这个接口非常简单,前端也可以直接向微信服务器请求,但为了不暴露公众号的 AppSecret 参数和跨域问题,这个请求放在后端发起。

微信文档:第二步

1
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

前端传入code,后端发起GET请求,响应结果直接返给前端。

公众平台-前端开发

前端开发在 《微信web开发者工具》(以下称:模拟器)下进行,下载地址:下载 。需要在 公众号平台 > 开发者工具 >web开发者工具 绑定开发者的微信号。

坑:npm上没有微信官方发布的包, weixin-jsapi 是个人发布v1.1.0 版本的包。

网上很多文章用 weixin-js-sdk 这个包也是个人发布的,目前最新的1.4.0版,但是只能通过require使用,Vue中并不能用,有问题。

1. 微信网页授权

微信文档:第一步 拿到 code 直接向后端发起请求获取 OpenId ,正确的话,有以下数据包返回,我们就用到openid ,缓存起来备用 。

1
2
3
4
5
6
7
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
2. 通过config接口注入权限验证配置

所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用。

1
2
3
4
5
6
7
8
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});

上面这些参数,需要向后端的 签名接口 发起请求获取,appId 可以写在前端代码中,也可以由后端响应返回。

1
jsApiList: ['getNetworkType','chooseWXPay'] 

这里是两个需要使用的JS接口,getNetworkType 用于测试,chooseWXPay 就是微信支付的接口。

执行 wx.config ,如果模拟器弹出 ok 的话,表示签名没问题了。

由于微信支付在模拟器无法调试,所以有必要选用一个其他接口(如 getNetworkType)来调试。

3. 检查能否调用JSSDK
1
2
3
4
5
wx.getNetworkType({
success: function (res) {
var networkType = res.networkType; // 返回网络类型2g,3g,4g,wifi
}
});

如果模拟器弹出当前的网络类型,就表示成功了。

沙箱sandbox测试开发

微信支付沙箱开发时, 官方开发文档只是简单说明, 并没有给出相关示例。

沙箱开发要修改两个地方:

  1. 路径
  2. 秘钥Key

其中,Key要通过Post请求获取,但文档请求参数怎么传说的不清楚(其实是xml格式), 文档地址

沙箱测试要关注公众号 WXPayAssist ,里面很多相关的资料,沙箱测试付款也是要在这个公众号下进行。

获取Key的请求如下:

其中,sign 和支付的签名算法一样,要用到正式的KEY来签名。

https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey

微信接口-沙箱KEY

微信支付-后端开发

微信支付的后端开发,即要调用 微信文档:统一下单 接口,获取 预支付交易会话标识 prepay_id ,生成 支付签名 响应给前端。支付签名 由:appId, timeStamp, nonceStr, package, signType 参与签名,其中

1
String packages = "prepay_id=" + prepay_id;

这里的签名算法直接调用微信的 JavaSDK 里面的方法,所以不管它了,要注意的是签名的类型最好统一一种,比如全部用MD5或者HMAC-SHA256

1. 统一下单

调用微信的统一下单接口,所需要的参数如下:

1
2
3
4
5
6
7
8
data.put("body", "腾讯充值中心-QQ会员充值");//商家名称-销售商品类目
data.put("out_trade_no", "2016090910595900000012");//商户订单号,商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一
data.put("openid", "xxx");//用户标识,此参数为微信用户在商户对应appid下的唯一标识
data.put("total_fee", "1");//标价金额,订单总金额,单位为分
data.put("spbill_create_ip", '192.168.0.0');//用户IP,
data.put("notify_url", "http://www.example.com/wxpay/notify");//通知地址,异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
data.put("fee_type", "CNY");
data.put("trade_type", "JSAPI"); // 此处指定为JSAPI支付(或小程序支付)

微信响应结果是XML数据,转为JSON结果如下:

1
{"nonce_str":"xxx","appid":"xxx","sign":"xxx","trade_type":"JSAPI","return_msg":"OK","result_code":"SUCCESS","mch_id":"xxx","return_code":"SUCCESS","prepay_id":"xxx"}

解析数据包,提取 prepay_id

打包参与支付签名 的数据 (注意 timeStamp 自动一定要大写 S )

1
2
3
4
5
6
7
String packages = "prepay_id=" + prepay_id;
Map<String, String> wxPayMap = new HashMap<>();
wxPayMap.put("appId", "xxx");
wxPayMap.put("timeStamp", String.valueOf(System.currentTimeMillis()/1000));
wxPayMap.put("nonceStr", UUID.randomUUID().toString().substring(16).replace("-",""));
wxPayMap.put("package", packages);
wxPayMap.put("signType", "HMAC-SHA256");

支付签名 结果 sign参与签名 的数据 打包响应给前端

1
2
3
Map<String,String> result = new HashMap<>();
result.put("paySign", sign);
result.putAll(wxPayMap);

例如:

1
{"timeStamp":"1555476146","packageStr":"prepay_id=xxx","paySign":"xx","appId":"xxx","signType":"HMAC-SHA256","nonceStr":"xxx"}
2. 接收通知

notify_url 填的是后端的接口地址,新建一个接口用于异步接收微信支付结果。

微信服务器将以 的方式给这个地址发送xml格式的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<xml>
<appid><![CDATA[xxx]]></appid>
<bank_type><![CDATA[CFT]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[Y]]></is_subscribe>
<mch_id><![CDATA[xxx]]></mch_id>
<nonce_str><![CDATA[xxx]]></nonce_str>
<openid><![CDATA[xxx]]></openid>
<out_trade_no><![CDATA[xxx]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[xxx]]></sign>
<time_end><![CDATA[xxx]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[JSAPI]]></trade_type>
<transaction_id><![CDATA[xxx]]></transaction_id>
</xml>

接收到消息之后,要回复响应数据:

1
2
3
4
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>

接收数据示例代码:

要从HttpServletRequest 的 getInputStream 方法获取流,从流读取byte ,不过我们用SpringMVC可以很简洁的接收:

1
2
3
4
5
6
7
8
9
10
public String notifyPay(@RequestBody byte[] body){
String result = null;
try {
result = new String(body, "UTF-8");
logger.info("result:\n"+result);

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}

微信支付-前端

剩下最后一步了:唤起微信支付,这个操作要在手机微信上进行,真实的环境。

将付款的页面URL放在 商户平台 > 产品中心 >支付配置 > JSAPI支付授权目录 中,

  1. 仅有公众号支付和扫码支付需配置支付域名,APP支付、刷卡支付无需配置域名

  2. 所有使用JS API方式发起支付请求的链接地址,都必须在当前页面所配置的支付授权目录之下。下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid

  3. 当公众平台接到扫码支付请求时,会回调当前页面所配置的支付回调链接传递订单信息

授权目录须以左斜杠 “/” 结尾 ,例如:

支付的地址是:http://wx.mydomain.com/#/car/pay ;

授权目录设置为:http://wx.mydomain.com/#/car/

支付授权目录配置.png

1
2
3
4
5
6
7
8
9
10
wx.chooseWXPay({
timestamp: '', // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: '', // 支付签名随机串,不长于 32 位
package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: '', // 支付签名
success: function (res) {
// 支付成功后的回调函数
}
});

chooseWXPay 的参数都在后端的统一下单 接口响应里面,解析JSON数据即可。要注意的是,这里有一个坑 timestamp ,后端生成 支付签名 的是 timeStampS是大写的,但前端 chooseWXPaytimestamp依然是全部小写的。

如果没问题,即可以看到手机的 微信支付输入密码 弹框了。