应用场景:
商户根据商品信息,生成商品二维码,用户通过微信扫一扫功能扫描该二维码,完成支付。
支付模式:
现在微信扫码支付支持两种模式。
模式一需要商户必须先在公众平台后台设置支付回调URL。URL实现的功能:接收用户扫码后微信支付系统回调的productid和openid。
模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。
我使用的模式二,也就是微信的统一下单接口。该模式的业务流程时序图如下:
业务流程说明:
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(12)商户确认订单已支付后给用户发货。
整个支付流程精简下来主要就是2点:
①调用接口获取支付二维码并显示出来
②微信回调接口,通知支付结果
针对这2点我写了两个方法,一个是获取二维码,一个是提供给微信的支付结果回调接口。
开发过程中需要用到微信提供的jar包,maven地址如下:
<dependency>
<groupId>com.github.wxpaygroupId>
<artifactId>wxpay-sdkartifactId>
<version>0.0.3version>
dependency>
/**
* 该类为配置类,主要是关于公众号和商户的一些配置信息抽离出来
* @author
*
* 2017年11月17日
*/
public class MyConfig implements WXPayConfig{
//公众账号ID
public String getAppID() {
return "wx11bd61834b0d57ef";
}
public int getHttpConnectTimeoutMs() {
return 8000;
}
public int getHttpReadTimeoutMs() {
return 10000;
}
//商户秘钥
public String getKey() {
return "1234567890";
}
//商户号
public String getMchID() {
return "1234567890";
}
}
/**
* 获得微信支付二维码
* @param req
* @param resp
* @throws IOException
*/
@RequestMapping(value = "/getWxPayCode")
public void getWxPayCode(HttpServletRequest req, HttpServletResponse resp)
throws IOException{
MyConfig config = new MyConfig();
WXPay wxpay = new WXPay(config);
String out_trade_no = DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");
Map<String, String> data = new HashMap<String, String>();
data.put("body", "填写商品名称"); //商品描述
data.put("out_trade_no", out_trade_no); //商户订单号,不可重复
data.put("device_info", ""); //设备号
data.put("fee_type", "CNY"); //标价币种(默认人民币)
data.put("total_fee", "1"); //标价金额,单位:分
data.put("spbill_create_ip", "127.0.0.1"); //终端IP
data.put("notify_url", "https://www.baidu.com/getWxPayNotify.action"); //通知地址,必须是外网能访问的地址
data.put("trade_type", "NATIVE"); // 此处指定为扫码支付
data.put("product_id", "12"); //商品ID
Map<String, String> respnoe = null;
try {
respnoe = wxpay.unifiedOrder(data);
String codeUrl = respnoe.get("code_url");
System.out.println("返回的二维码url:" + codeUrl);
} catch (Exception e) {
e.printStackTrace();
}
resp.setContentType("text/html;charset=UTF-8");
OutputStream os = resp.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(respnoe.toString());
oos.flush();
oos.close();
os.close();
}
这样就完成了微信支付的二维码请求。在进行请求的时候,微信是以xml格式的数据进行的请求,不过微信已经在提供的jar包里进行了封装,所以我们就不必再进行数据封装了。
商户后台将该二维码地址返回给手机端,手机端通过zxing工具包,将该地址转换成二维码供用户扫码支付。当用户扫码支付完成后,微信后台会回调我们提供给微信后台的接口,就是刚才我们设置的notify_url参数,这里一定要记得是外网能够访问到的地址!
下面是处理微信回调的方法:
/**
* 获得微信支付通知回调结果
* @param req
* @param resp
* @throws Exception
* @throws IOException
*/
@RequestMapping(value = "/getWxPayNotify")
public void getWxPayNotify(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String notifyData = "";
try {
InputStream is = req.getInputStream();
StringBuffer sb = new StringBuffer();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
while ((s = in.readLine()) != null){
sb.append(s);
}
in.close();
is.close();
notifyData = sb.toString();
MyConfig config = new MyConfig();
WXPay wxpay = new WXPay(config);
Map notifyMap = WXPayUtil.xmlToMap(notifyData); // 转换成map
if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
logger.info("支付成功");
// 签名正确
// 进行处理。
// 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功
}
else {
// 签名错误,如果数据里没有sign字段,也认为是签名错误
logger.error("支付失败");
}
logger.info("微信支付返回的通知为:" + notifyMap);
resp.setContentType("text/html;charset=UTF-8");
OutputStream os = resp.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(notifyData);
oos.flush();
oos.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
具体的处理方法这里我就不写了,微信回调的时候需要验证签名是否正确,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。另外当微信回调我们的接口的时候,我们需要给微信后台返回应答,已经接收到回调,不然微信将会重新发起通知(通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
另外当客户端一定时间内没有接收到微信后台的回调时,也可以主动查询订单状态,跟统一下单差不多,只是把下单改为查询
Map<String, String> resp = wxpay.orderQuery(data);
微信扫码支付暂时写到这里,后续如果有什么变化,再添加。附上微信支付的开发文档,里面会有各个参数的说明, 上面的代码没说的太详细。
统一下单:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
支付结果通知:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7
如需转载,请注明出处!