先看文章
微信支付介绍和接入指引(在线实战!!!!微信直连!!)_微信支付接入-CSDN博客
之前看的都是APIv3的版本
打开v2
pay.weixin.qq.com/wiki/doc/api/index.html
1、V2和V3的比较
2、引入依赖和工具
2.1、引入依赖
2.2、复制工具类
2.3、添加商户APIv2 key
然后
2.4、添加枚举
3、统一下单
v2:
v3:
注意,有些参数都不一样
3.1、创建WxPayV2Controller
3.2、WxPayService
/**
* v2统一下单
* @param productId
* @param remoteAddress
* @return
* @throws Exception
*/
@Override
public Map<String, Object> nativePayV2(Long productId, String remoteAddress) throws Exception {
log.info("生成订单");
//生成订单
OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId);
String codeUrl=orderInfo.getCodeUrl();
if(orderInfo!=null&&!StringUtils.isEmpty(codeUrl)){
log.info("订单已存在,二维码已保存");
//返回二维码
Map<String,Object> map = new HashMap<>();
map.put("codeUrl",codeUrl);
map.put("orderNo",orderInfo.getOrderNo());
return map;
}
log.info("调用统一下单API");
//调用统一下单API
HttpClientUtils client=new HttpClientUtils(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY_V2.getType()));
//请求body参数
Map<String,String> paramsMap = new HashMap();
paramsMap.put("appid",wxPayConfig.getAppid());//关联的公众号appid
paramsMap.put("mch_id",wxPayConfig.getMchId());//商户号
paramsMap.put("nonce_str", WXPayUtil.generateNonceStr());//生成随机字符串
paramsMap.put("body",orderInfo.getTitle());//商品描述
//paramsMap.put("description",orderInfo.getTitle());
paramsMap.put("out_trade_no",orderInfo.getOrderNo());
paramsMap.put("trade_type","NATIVE");
paramsMap.put("notify_url",wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY_V2.getType()));
//注意,这里必须使用字符串类型的参数(分)
String totalFee=orderInfo.getTotalFee()+"";
paramsMap.put("total_fee",totalFee);
paramsMap.put("spbill_create_ip",remoteAddress);
//将参数转化为xml字符串格式:生成带有签名的xml格式字符串
String xmlParams = WXPayUtil.generateSignedXml(paramsMap,wxPayConfig.getPartnerKey());
log.info("请求参数:"+xmlParams);
client.setXmlParam(xmlParams);//将参数放入请求对象的方法体
client.setHttps(true);//使用https请求
client.post();//发送请求
String resultXml=client.getContent();//得到响应结果
log.info("响应结果:"+resultXml);
//将xml响应结果转换成map对象
Map<String,String> resultMap = WXPayUtil.xmlToMap(resultXml);
//错误处理
if("FATL".equals(resultMap.get("return_code"))||"FAIL".equals(resultMap.get("result_code"))){
log.error("微信支付统一下单错误====》",resultXml);
throw new RuntimeException("微信支付统一下单错误");
}
//二维码
codeUrl = resultMap.get("code_url");
//保存二维码
String orderNo = orderInfo.getOrderNo();
orderInfoService.saveCodeUrl(orderNo, codeUrl);
//返回二维码
Map<String, Object> map = new HashMap<>();
map.put("codeUrl",codeUrl);
map.put("orderNo",orderInfo.getOrderNo());
return map;
}
4、支付回调
v2的回调与v3的回调最大的区别就是v2的回调参数没有经过加密,所以可以直接进行解析
/**
//处理通知参数
String body = HttpUtils.readData(request);
* 支付通知
* 微信支付通过支付通知接口将用户支付成功消息通知给商户
*/
@PostMapping("/native/notify")
public String wxNotify(HttpServletRequest request) throws Exception {
System.out.println("微信发送的回调");
Map<String, String> returnMap = new HashMap<>();//应答对象
//处理通知参数
String body = HttpUtils.readData(request);
//验签
if(!WXPayUtil.isSignatureValid(body, wxPayConfig.getPartnerKey())){
log.error("通知验签失败");
//失败应答
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "验签失败");
String returnXml=WXPayUtil.mapToXml(returnMap);
return returnXml;
}
//解析xml数据
Map<String, String> notifyMap = WXPayUtil.xmlToMap(body);
//判断通信和业务是否成功
if(!"SUCCESS".equals(notifyMap.get("return_code")) || !"SUCCESS".equals(notifyMap.get("result_code"))){
log.error("通知业务失败");
//失败应答
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "业务失败");
String returnXml=WXPayUtil.mapToXml(returnMap);
return returnXml;
}
//获取商户订单号
String orderNo = notifyMap.get("out_trade_no");
OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo);
//并校验返回的订单金额是否与商户侧的订单金额一致
//也是v2的要求
if(orderInfo != null && orderInfo.getTotalFee()!=Long.parseLong(notifyMap.get("total_fee"))){
log.error("通知金额与订单金额不一致");
//失败应答
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "金额校验失败");
String returnXml=WXPayUtil.mapToXml(returnMap);
return returnXml;
}
//处理订单
if(lock.tryLock()){
try {
//处理重复的通知
//接口调用的幂等性:无论接口被调用多少次,产生的结果是一致的。
String orderStatus = orderInfoService.getOrderStatus(orderNo);
if(OrderStatus.NOTPAY.getType().equals(orderStatus)){
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderNo,
OrderStatus.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfo(body);
}
} finally {
//要主动释放锁
lock.unlock();
}
}
returnMap.put("return_code", "SUCCESS");
returnMap.put("return_msg", "OK");
String returnXml = WXPayUtil.mapToXml(returnMap);
log.info("支付成功,已应答");
return returnXml;
}
记录日志的时候注意,可能要做一下数据转换:
可以用swagger测试
或者前端做一个小修改启动程序:
然后
修改
启动程序
支付并通知成功
注意关于记录日志到t_payment_info中:
因为v2通知请求参数和v3有点区别,所以记录的时候会报错,这里可以注释掉关于金额的字段
然后日志 也记录了,不过少了一个字段
到次v2的下单也完成了,不过因为只是介绍一下v2,只写了两个接口。。。。