一、准备
1、配置ngrok
将ngrok映射到本地8170端口,并启动
2、添加工具类
在common_util中添加工具类StreamUtils.java
public class StreamUtils {
private static int _buffer_size = 1024;
/**
* InputStream流转换成String字符串
* @param inStream InputStream流
* @param encoding 编码格式
* @return String字符串
*/
public static String inputStream2String(InputStream inStream, String encoding){
String result = null;
ByteArrayOutputStream outStream = null;
try {
if(inStream != null){
outStream = new ByteArrayOutputStream();
byte[] tempBytes = new byte[_buffer_size];
int count = -1;
while((count = inStream.read(tempBytes, 0, _buffer_size)) != -1){
outStream.write(tempBytes, 0, count);
}
tempBytes = null;
outStream.flush();
result = new String(outStream.toByteArray(), encoding);
outStream.close();
}
} catch (Exception e) {
result = null;
} finally {
try {
if(inStream != null) {
inStream.close();
inStream = null;
}
if(outStream != null) {
outStream.close();
outStream = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}
二、支付回调
1、回调方法
该链接是通过【统一下单API】中提交的参数notify_url设置,如果链接无法访问,商户将无法接收到微信通知。
参考文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8
ApiWeixinPayController
@Autowired
private WeixinPayProperties weixinPayProperties;
@Autowired
private OrderService orderService;
/**
* 支付回调:注意这里是【post】方式
*/
@PostMapping("callback/notify")
public String wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("callback/notify 被调用");
// 获得通知结果
ServletInputStream inputStream = request.getInputStream();
String notifyXml = StreamUtils.inputStream2String(inputStream, "utf-8");
System.out.println("xmlString = " + notifyXml);
// 定义响应对象
HashMap<String, String> returnMap = new HashMap<>();
// 签名验证:防止伪造回调
if (WXPayUtil.isSignatureValid(notifyXml, weixinPayProperties.getPartnerKey())) {
// 解析返回结果
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyXml);
//判断支付是否成功
if("SUCCESS".equals(notifyMap.get("result_code"))){
// 校验订单金额是否一致
String totalFee = notifyMap.get("total_fee");
String outTradeNo = notifyMap.get("out_trade_no");
Order order = orderService.getOrderByOrderNo(outTradeNo);
if(order != null && order.getTotalFee().intValue() == Integer.parseInt(totalFee)){
// 判断订单状态:保证接口调用的幂等性,如果订单状态已更新直接返回成功响应
// 幂等性:无论调用多少次结果都是一样的
if(order.getStatus() == 1){
returnMap.put("return_code", "SUCCESS");
returnMap.put("return_msg", "OK");
String returnXml = WXPayUtil.mapToXml(returnMap);
response.setContentType("text/xml");
log.warn("通知已处理");
return returnXml;
}else{
// 更新订单支付状态,并返回成功响应
orderService.updateOrderStatus(notifyMap);
returnMap.put("return_code", "SUCCESS");
returnMap.put("return_msg", "OK");
String returnXml = WXPayUtil.mapToXml(returnMap);
response.setContentType("text/xml");
log.info("支付成功,通知已处理");
return returnXml;
}
}
}
}
// 校验失败,返回失败应答
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "");
String returnXml = WXPayUtil.mapToXml(returnMap);
response.setContentType("text/xml");
log.warn("校验失败");
return returnXml;
}
2、更新订单状态
更新订单支付状态并记录支付日志,将微信返回的支付结果全部记录进数据库的json字段中
接口:OrderService
void updateOrderStatus(Map<String, String> map);
实现:OrderServiceImpl
@Autowired
private PayLogMapper payLogMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void updateOrderStatus(Map<String, String> map) {
//更新订单状态
String orderNo = map.get("out_trade_no");
Order order = this.getOrderByOrderNo(orderNo);
order.setStatus(1);//支付成功
baseMapper.updateById(order);
//记录支付日志
PayLog payLog = new PayLog();
payLog.setOrderNo(orderNo);
payLog.setPayTime(new Date());
payLog.setPayType(1);//支付类型
payLog.setTotalFee(Long.parseLong(map.get("total_fee")));//总金额(分)
payLog.setTradeState(map.get("result_code"));//支付状态
payLog.setTransactionId(map.get("transaction_id"));
payLog.setAttr(new Gson().toJson(map));
payLogMapper.insert(payLog);
}