微信扫码支付总结-PC端-Java(模式二)
前言
最近做的项目有对接微信支付的需求,于是开始了一个人的摸索。本文的前提是公司已经申请了商户号和appid,设置了商户号对应的key,即appId,mchId,key三个参数。以下为开发步骤:
1 阅读微信官方开发文档
微信官方文档链接:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1
这里选择流程更为简单的模式二,主要是看模式二的时序图:
作为开发者,此模式跟微信支付系统直接相关的是步骤2,步骤10,步骤11
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
2 导入相关依赖
2.1 添加pom.xml依赖
由于采用maven开发,故在pom.xml文件加上如下依赖:
<!-- wxpay -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
也可以在官方开发文档网站下载sdk和demo
2.2 maven依赖的使用
只需新建一个Java配置类,实现WXPayConfig接口,配置appId,mchId,key三个主要参数
public class WxPayConfig implements WXPayConfig {
@Override
public String getAppID() {
return "xxxxxxxxxxxxxxxxxx";
}
@Override
public String getMchID() {
return "xxxxxxxxxx";
}
@Override
public String getKey() {
return "xxxxxxxxxxxxxxxxxxxxx";
}
/*此证书是退款才需要配置*/
@Override
public InputStream getCertStream() {
return this.getClass().getResourceAsStream("/cert/apiclient_cert.p12");
}
@Override
public int getHttpConnectTimeoutMs() {
return 6000;
}
@Override
public int getHttpReadTimeoutMs() {
return 8000;
}
}
3 调用统一下单接口
3.1 接口地址
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
// new一个新建的配置类
WxPayConfig wxPayConfig = new WxPayConfig();
// WXPay即为maven依赖的jar包封装好的调用官方接口的
WXPay wxPay = new WXPay(wxPayConfig);
// 调用统一下单接口
Map<String, String> returnMap = wxPay.unifiedOrder(dataMap);
3.2 此处应注意参数传递notify_url这个参数(在第4节会细说)
3.3 接口的返回值
根据官方文档得知下单接口的返回值,可以判断returnMap的属性return_code和result_code都为SUCCESS时,返回二维码链接code_url
if(WXPayConstants.SUCCESS.equals(returnMap.get("return_code")) &&
WXPayConstants.SUCCESS.equals(returnMap.get("result_code"))){
return returnMap.get("code_url");
}
3.4 二维码插件
由于项目是前后端分离,这里使用前端插件生成qrcode,具体使用请参考http://code.ciaoca.com/javascript/qrcode/
<script type="text/javascript" src="qrcode.js"></script>
<script type="text/javascript">
// 设置参数方式
var qrcode = new QRCode('qrcode', {
// 只需替换这一串内容即可生成二维码
text: 'weixin://wxpay/bizpayurl?pr=xxxxxx',
width: 256,
height: 256,
colorDark : '#000000',
colorLight : '#ffffff',
correctLevel : QRCode.CorrectLevel.H
});
</script>
ps:此处也可使用zxing的工具类直接在后端生成二维码图片返回给前端显示,暂不详细展开。
4 设置支付结果通知的notify_url
开发者在调用统一下单接口时,需要传递notify_url给微信支付系统。使得用户扫码付款成功后,微信会主动调用开发者设置的接口,对支付结果进行通知,开发者可对支付结果进行判断,并结合业务需求,实现项目业务。
4.1 使用内网穿透工具ngrok实现内网穿透
若是在本地测试开发,则需要使用内网穿透工具,方便测试
这样微信访问马赛克的链接,就相当于访问了本地的8080端口的程序接口
4.2 配置notify_url
在项目配置文件application.properties文件或者自定义文件中配置notify_url
wechat.notify_url=http://xxxx.ngrok.xxxxx.cn/test/payNotify
4.3 新增payNotify接口,以便微信回调
controller层:提供微信回调接口
@PostMapping(value = "/payNotify")
public void payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("UTF-8");
String notify = payService.payNotify(request);
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
response.getWriter().println(notify);
}
service层:接收处理参数,并返回指定内容给微信支付系统
// 返回微信格式
private static final String RESULT_SUCCESS = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
private static final String RESULT_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[参数为空]]></return_msg></xml>";
// 异步接收到微信的支付结果
String requestBody = getRequestBody(request);
// 使用maven依赖工具类对结果进行转化
Map<String, String> map = WXPayUtil.xmlToMap(requestBody);
LOGGER.debug("微信支付结果通知:{}",requestBody);
// 返回成功
if(!WXPayConstants.SUCCESS.equals(map.get("return_code"))){
LOGGER.error("支付失败");
return RESULT_FAIL;
}
// 签名
String sign = map.get("sign");
// 业务结果
String result_code = map.get("result_code");
// 商户订单号
String out_trade_no = map.get("out_trade_no");
// 订单金额
String total_fee = map.get("total_fee");
// 微信支付订单号
String transaction_id = map.get("transaction_id");
// 1,验证签名是否正确
WxPayConfig config = new WxPayConfig();
String signature = WXPayUtil.generateSignature(map, config.getKey());
LOGGER.info("重新生成的签名:{}",signature);
if(!signature.equals(sign)){
LOGGER.info("签名验证失败");
return RESULT_FAIL;
}
LOGGER.info("验签成功");
// 验证支付金额和订单金额是否一致,此处省略代码
// ...
// 验证通过,则返回成功结果
return RESULT_SUCCESS;
获取request输入流,解析参数
/**
* 接收微信传过来的结果
* @param request req
* @return String格式
* @throws IOException
*/
private String getRequestBody(HttpServletRequest request) throws IOException {
ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder inputLine = new StringBuilder();
String line;
try {
while ((line = reader.readLine())!=null){
inputLine.append(line);
}
} catch (Exception e){
LOGGER.debug(e.getMessage());
e.printStackTrace();
} finally {
reader.close();
inputStream.close();
}
return inputLine.toString();
}
4.4 注意事项
微信回调此处有坑,微信回调是定时重试地调用,若因为网络原因或者其他因素,可能导致即使是返回成功了,也会不断调用回调接口,废话不多说,上代码:
// 1获取订单号,查询该订单在商户系统中的订单状态
Map<String, Object> order = orderInfoMapper.queryOrder(out_trade_no);
// 2判断订单是否支付成功,成功则直接返回
Integer statu = Integer.parseInt(order.get("statu") + "");
// 1为已支付
if(1 == statu){
LOGGER.info("该订单已支付,订单号为:{}",out_trade_no);
return RESULT_SUCCESS;
}
5 查询订单状态接口调用
接口链接:https://api.mch.weixin.qq.com/pay/orderquery
参照api文档,传递参数直接调用:
/**
* 查询订单
* @param dataMap 数据参数
* @return 返回结果集合
* @throws Exception 异常
*/
public static Map<String,String> queryOrder(Map<String,String> dataMap) throws Exception {
WxPayConfig wxPayConfig = new WxPayConfig();
WXPay wxPay = new WXPay(wxPayConfig);
Map<String, String> orderQuery = wxPay.orderQuery(dataMap);
return orderQuery;
}
6 退款接口
退款接口需下载证书,并配置证书路径,未完待续...