前言
这一章开始代码分析 下单相关的接口,需要查询接口的可以进入下一章。
正言
前端发起下单请求
后台统一下单
控制层
/**
* 统一下单 应用场景
* H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付。
* 主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付。
* 参考博客:https://blog.csdn.net/qq_41490913/article/details/104957525?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161127802816780269838022%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=161127802816780269838022&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-104957525.first_rank_v2_pc_rank_v29&utm_term=SpringBoot%E6%95%B4%E5%90%88%E5%BE%AE%E4%BF%A1H5%E6%94%AF%E4%BB%98%20&spm=1018.2226.3001.4187
* 参考博客代码排了一些坑。
*/
@PostMapping("/order")
public AjaxResult order(@RequestBody ZlsPaymentlog zlsPaymentlog){
AjaxResult result = wXH5PayService.order(zlsPaymentlog);
return result;
}
业务层
@Override
@Transactional
public AjaxResult order(ZlsPaymentlog zlsPaymentlog) {
AjaxResult result = AjaxResult.success();
MyWXPayConfig config = new MyWXPayConfig();//配置类
Map<String, String> data = new HashMap<String, String>();
String outRradeNo = IdUtils.fastSimpleUUID();
data.put("device_info", "WEB"); //设备号 PC网页或公众号内支付可以传"WEB"
data.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
data.put("body", WXPayConstants.BODY); //商品描述
data.put("out_trade_no", outRradeNo); //商户订单号
data.put("total_fee", String.valueOf(zlsPaymentlog.getAmount().intValue())); //标价金额 单位是分
data.put("spbill_create_ip", IpUtils.getHostIp()); //用户的客户端IP
data.put("notify_url", defultUrl+"prod-api/wxpay/h5pay/orderNotify"); //通知地址
data.put("trade_type", "MWEB"); // 交易类型 JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付
logger.info("data="+ JSONUtils.toJSONString(data));
try {
WXPay wxpay = new WXPay(config);
//统一下单接口
Map<String, String> resp = wxpay.unifiedOrder(data);
logger.info("统一下单:resp="+JSONUtils.toJSONString(resp));
logger.info("需要请求的地址:"+resp.get("mweb_url"));
String returnCode = resp.get("return_code");
if(WXPayConstants.SUCCESS.equals(returnCode)){
//跳转URL //redirect_url则为支付后返回的指定页面
String url = resp.get("mweb_url");
//不添加redirect_url参数,则默认返回原路径。
//url = url+"&redirect_url="+defultUrl+"xswk/mobiles/searchPayInfo"
// TODO 自己的一些逻辑 例如日志 或者记录订单状态
result = AjaxResult.success("统一下单接口成功",url);
}else {
throw new RuntimeException(resp.get("err_code_des"));
}
} catch (Exception e) {
result = AjaxResult.error("统一下单接口失败",e.getMessage());
}
logger.info("统一下单接口result:"+JSONUtils.toJSONString(result));
return result;
}
后台支付结果通知
控制层
/**
* 支付结果通知 应用场景
* 当商户申请的退款有结果后(退款状态为:退款成功、退款关闭、退款异常),微信会把相关结果发送给商户,商户需要接收处理,并返回应答。
* 对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)。
* 注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
* 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
* 特别说明:退款结果对重要的数据进行了加密,商户需要用商户秘钥进行解密后才能获得结果通知的内容
* 注意点(坑):商户系统收到支付结果通知,需要在 5 秒内返回应答报文,否则微信支付认为通知失败,后续会重复发送通知。。
*/
@ResponseBody
@RequestMapping("/orderNotify")
public String orderNotify(HttpServletRequest request, HttpServletResponse response) {
String result = wXH5PayService.orderNotify(request,response);
return result;
}
业务层
@Override
@Transactional
public String orderNotify(HttpServletRequest request, HttpServletResponse response) {
logger.info("支付结果通知回调中");
//默认返回给微信的数据 标志成功, SUCCESS表示商户接收通知成功并校验成功
Map<String, String> map = new HashMap<String, String>(){{put("return_code", WXPayConstants.SUCCESS);put("return_msg", WXPayConstants.OK);}};
String result = WXPayUtil.mapToXml(map);
try {
InputStream inputStream = request.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
String notifyData = new String(outputStream.toByteArray(), "utf-8");// 支付结果通知的xml格式数据
//关闭流
outputStream.close();
inputStream.close();
logger.info("获取xml格式数据成功:\n" + notifyData);
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData); // 转换成map
logger.info("notifyMap = " + notifyMap);
String returnCode = notifyMap.get("return_code");
if(WXPayConstants.SUCCESS.equals(returnCode)&&WXPayConstants.SUCCESS.equals(notifyMap.get("result_code"))){
String outTradeNo = notifyMap.get("out_trade_no");//获取商户订单号
logger.info("outTradeNo = " + outTradeNo);
String transactionId = notifyMap.get("transaction_id");//微信支付订单号
logger.info("transactionId = " + transactionId);
// TODO 微信回调函数5秒没有返回给微信后台,则认为失败,为防止各种原因导致多次回调,需要加校验判断,防止多次扣费。
// TODO 自己的业务逻辑,例如 更改订单状态 记录日志
}
//以XML格式发送给微信后台,不然可能会发生多次回调。
response.setContentType("text/xml");
response.getWriter().write(result);
response.flushBuffer();
} catch (Exception e) {
//发生异常,微信后台不会自动退费.
logger.info("支付信息回调发生异常:"+e.getMessage());
map.put("return_code", WXPayConstants.FAIL);
map.put("return_msg", e.getMessage());
result = WXPayUtil.mapToXml(map);
}
logger.info("支付结果通知接口result:" + result);
return result;
}