商城接入微信支付
微信支付产品
如上图所示,接入微信支付的方法有很多,我们可以按需接入。本次我们选择Native支付进行开发,也就是最常见的购物车下单后,商家给出一个二维码,用户微信扫码付款的流程。
微信支付业务流程
Native支付方法开发指引:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5&index=3
本次研究Native支付方法,其他方法大同小异。下图是微信支付官方给出来的微信支付业务流程:
业务流程说明:
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【[统一下单API](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1)】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【[查询订单API](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2)】(查单实现可参考:[支付回调和查单实现指引](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=23_9&index=1))。
(12)商户确认订单已支付后给用户发货。
对支付流程有一定了解之后,接下来我们进行详细说明。
支付状态状态机
支付状态转变如下:
如上图可以了解到,订单生成后,支付状态可以有上面几个变化:
- 订单未支付,支付成功后转为支付成功状态(SUCCESS);
- 订单未支付,支付成功后转为支付成功状态(SUCCESS),但用户发起退款,转为已退款状态(REFUND);
- 订单未支付,用户主动关闭订单或超时关闭订单,转为已关单状态(CLOSED);
- 订单未支付,由于网络波动等原因导致出现支付错误,转为支付错误状态(PAYERROR)。
微信支付二维码生成
在支付页面上生成支付二维码,并显示订单号和金额。
用户拿出手机,打开微信扫描页面上的二维码,然后在微信中完成支付。
类似这样在日常生活中经常遇到的情况,应该怎么调用微信支付接口生成微信二维码呢?
构建参数发送给统一下单的url
我们通过HttpClient工具类实现对远程支付接口的调用。
接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
参数地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
具体参数参见“统一下单”API, 构建参数发送给统一下单的url ,返回的信息中有支付url,根据url生成二维码,显示的订单号和金额也在返回的信息中。
本函数用于生成微信支付二维码,传入商户系统的订单号和订单金额,返回一个含支付二维码url的Map。
public Map createNative(String out_trade_no, String total_fee){
try {
//1、封装参数
Map param = new HashMap();
param.put("appid", appid); //微信支付分配的公众账号ID(企业号corpid即为此appid)
param.put("mch_id", mch_id); //微信支付分配的商户号
param.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串,长度要求在32位以内。这里调用微信支付自带的函数生成
param.put("body", "腾讯充值中心-QQ会员充值"); //商品简单描述
param.put("out_trade_no",out_trade_no); //商户系统内部订单号
param.put("total_fee", total_fee); //订单总金额,单位为分
param.put("spbill_create_ip", "127.0.0.1"); //支持IPV4和IPV6两种格式的IP地址。用户的客户端IP
param.put("notify_url", notifyurl); //回调地址,异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
param.put("trade_type", "NATIVE"); //交易类型,NATIVE -Native支付
//2、将参数转成xml字符,并携带签名
String paramXml = WXPayUtil.generateSignedXml(param, sign);//这个sign为通过签名算法计算得出的签名值,默认为MD5,具体生成规则看参数地址
///3、执行请求
HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
httpClient.setHttps(true);
httpClient.setXmlParam(paramXml);
httpClient.post();
//4、获取参数
String content = httpClient.getContent();
Map<String, String> stringMap = WXPayUtil.xmlToMap(content);
//5、获取部分页面所需参数,stringMap的code_url参数在return_code 和result_code都为SUCCESS的时候有返回
Map<String,String> dataMap = new HashMap<String,String>();
dataMap.put("code_url",stringMap.get("code_url"));//二维码链接
dataMap.put("out_trade_no",out_trade_no);//商户内部订单号
dataMap.put("total_fee",total_fee);//订单总金额
return dataMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
注意:还可以传入不同参数,按需按照官方规定传入。注意返回结果,具体可以参考官方。
检测支付状态
用户在扫描微信支付二维码并支付后,会出现两种不同情况:
- 一种是支付成功,商家得到信息,准备发货
- 另一种是支付失败,微信支付订单提示出错
那么我们要怎么查询微信支付订单,完成下一步的业务逻辑呢?
查询订单
官方地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
接口地址:https://api.mch.weixin.qq.com/pay/orderquery
该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。
需要调用查询接口的情况:
- 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知(查单实现可参考:支付回调和查单实现指引);
- 调用支付接口后,返回系统错误或未知交易状态情况;
- 调用付款码支付API,返回USERPAYING的状态;
- 调用关单或撤销接口API之前,需确认支付状态;
构建参数
同样,我们也是通过发送参数来进行查询。
public Map queryPayStatus(String out_trade_no) {
try {
//1.封装参数
Map param = new HashMap();
param.put("appid",appid); //应用ID
param.put("mch_id",mch_id); //商户号
param.put("out_trade_no",out_trade_no); //商户订单编号
param.put("nonce_str",WXPayUtil.generateNonceStr()); //随机字符
//2、将参数转成xml字符,并携带签名
String paramXml = WXPayUtil.generateSignedXml(param,partnerkey);
//3、发送请求
HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
httpClient.setHttps(true);
httpClient.setXmlParam(paramXml);
httpClient.post();
//4、获取返回值,并将返回值转成Map
String content = httpClient.getContent();
return WXPayUtil.xmlToMap(content);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
根据返回结果,就可以进行下一步业务逻辑了。