一.公众号支付介绍
公众号支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付。应用场景有:
◆ 用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付
◆ 用户的好友在朋友圈、聊天窗口等分享商家页面连接,用户点击链接打开商家页面,完成支付
◆ 将商户页面转换成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付
二.支付账户
商户在微信公众平台(申请扫码支付、公众号支付)或开放平台(申请APP支付)按照相应提示,申请相应微信支付模式。微信支付工作人员审核资料无误后开通相应的微信支付权限。微信支付申请审核通过后,商户在申请资料填写的邮箱中收取到由微信支付小助手发送的邮件,此邮件包含开发时需要使用的支付账户信息,如图2.1所示。
2.1中的参数说明如2.2图所示:
我写微信公众号支付时,使用了mch_id、APPID、key三个参数。
三.接口规则
1.协议规则
传输方式 :为保证交易安全性,采用HTTPS传输
提交方式 :采用POST方法提交
数据格式 :提交和返回数据都为XML格式,根节点名为xml
字符编码 :统一采用UTF-8字符编码
签名算法 :MD5,后续会兼容SHA1、SHA256、HMAC等。
签名要求 :请求和接收数据均需要校验签名
证书要求 :调用申请退款、撤销订单接口需要商户证书
判断逻辑 :先判断协议字段返回,再判断业务返回,最后判断交易状态
2.参数规定
1、交易金额(重要)
交易金额默认为人民币交易,接口中参数支付金额单位为【分】,参数值不能带小数。对账单中的交易金额单位为【元】。
外币交易的支付金额精确到币种的最小单位,参数值不能带小数点。
2、交易类型
JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口
3、货币类型
货币类型的取值列表:
CNY:人民币
4、时间
标准北京时间,时区为东八区;如果商户的系统时间为非标准北京时间。参数值必须根据商户系统所在时区先换算成标准北京时间,例如商户所在地为0时区的伦敦,当地时间为2014年11月11日0时0分0秒,换算成北京时间为2014年11月11日8时0分0秒。
标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。
5、时间戳(重要)
标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。
6、商户订单号
商户支付的订单号由商户自定义生成,仅支持使用字母、数字、中划线-、下划线_、竖线|、星号*这些英文半角字符的组合,请勿使用汉字或全角等特殊字符。微信支付要求商户订单号保持唯一性(建议根据当前系统时间加随机序列来生成订单号)。重新发起一笔支付要使用原订单号,避免重复支付;已支付过或已调用关单、撤销(请见后文的API列表)的订单号不能重新发起支付。
等等。。。
3.安全规范-算法(重要) (微信支付接口签名校验工具)
1、签名算法
第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
◆ 参数名ASCII码从小到大排序(字典序);
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
◆ key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
举例如下:
第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:
第二步:拼接API密钥:
最终得到最终发送的数据:
2、生成随机数算法
微信支付API接口协议中包含字段nonce_str,主要保证签名不可预测。我们推荐生成随机数算法如下:调用随机数函数生成,将得到的值转换为字符串。
四.实践(重要)
1.统一下单
◆ 两个必传参数 :goodsId(商品id) openid(用户标识)
◆ 生成订单号封装成对象,并插入到订单表中
◆ 请求下单时微信必须传递的参数
◆ 请求数据先插入表wxpay_request以便查询
◆ 正式下单
WXPay wxPay =new WXPay(wxPayConfig);
Map resp = wxPay.unifiedOrder(order);
上两行是微信公众号支付的核心(重要)
其中WXPayConfig这个接口需要我们自己来实现
上图中的5个抽象方法是需要我们自己创建MyConfig类来实现的
其中的getCertStream()可直接返回null即可,但是getWXPayDomain()此方法实现不能返回null,否则会报如下错误:
getWXPayDomain()方法返回的是一个IWXPayDomain接口,我们创建一个WXPayDomain类来实现这个接口,其中IWXPayDomain中的信息如下图:
其中report(String s, long l, Exception e)实现但是不做处理,getDomain方法返回
new DomainInfo(WXPayConstants.DOMAIN_API, true);即可
此时,万事具备,只欠下单操作了:
WXPay wxPay =new WXPay(wxPayConfig);
Map resp = wxPay.unifiedOrder(order);
◆ 在这个我说明一下在这里栽的坑。下单的时候,微信那边是要穿spbill_create_ip这个参数的,
这个参数微信给的解释是:
APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
我在本地测试时,获取本地IP一直报错,后来不得已,将这个参数写死,就可以了,最后写完整个功能之后将写死的放开,在测试环境上毫无问题,自豪感爆棚。。。哈哈。。
◆ 下单成功之后,返回结果如下图所示:
赶紧将微信返回的数据保存到数据库wxpay_response
如果微信返回的return_code为SUCCESS,说明下单成功,如果这里没有成功就不要往后写了,赶紧回过头看看。。返回成功继续下面的操作。。
◆ 签名
按照微信介绍的来,记住timeStamp时间戳要以分为单位传递
String.valueOf(System.currentTimeMillis() /1000);
signType:MD5
nonceStr:WXPayUtil.generateNonceStr()
String sign = WXPayUtil.generateSignature(param, key); (重要)
上面一句话生成签名,大功告成。。
◆ 微信内H5调起支付
将上一步生成的数据,以接口的方式返回给前端,前端调起微信支付即可。。
2.支付回调
◆ 我们在下单的时候已经将notify_url通知地址传递给了微信端,微信会回调过来
StringwxpayResult(@Context HttpServletRequest request, @Context HttpServletResponse response);
通过这样的方式接口微信端的回调即可。。
Map notifyMap = WXPayUtil.xmlToMap(resultXml);
将微信返回的数据解析成Map类型数据来做处理
1>通过微信返回的out_trade_no(订单号)查询我们内部订单是否存在
2>状态是否时未支付,如果以支付直接返回即可
3>(重要)签名认证
WXPay.isPayResultNotifySignatureValid(Map reqData)
4>将微信返回的数据保存到wxpay_result表中
5>微信所有的处理就完了,下面就是你们自己的业务逻辑处理了。。。
很高心和大家一起学习,当然我也是有大佬帮助过的,后期我会更新一些工作中碰到的问题,希望和大家一起进步。。
所有的委屈,都是因为自己能力不够!!
- 2018.08.23 20:44