1.demo
package com.util;
import cn.hutool.core.util.StrUtil;
import com.lib.util.MD5Util;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.BiFunction;
public class WxPayUtil {
private final static Logger logger = LoggerFactory.getLogger(WxPayUtil.class);
/**
* 微信支付回调通用模板
* BiFunction: 参数一:支付金额 参数二:订单号 参数三: 返回信息
*/
public static String payCallback(HttpServletRequest request, HttpServletResponse response, BiFunction<Double, String, String> function) throws Exception {
response.setContentType("text/html;charset=UTF-8");
ServletInputStream inputStream = request.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferReader = new BufferedReader(inputStreamReader);
String line = null;
String result = "";
while ((line = bufferReader.readLine()) != null) {
result += line;
}
logger.info("微信支付回调返回:" + result);
Document doc = DocumentHelper.parseText(result);
Element et = doc.getRootElement();
List<Element> elementList = et.elements();
HashMap<String, String> params = new HashMap<String, String>();
for (Element e : elementList) {
params.put(e.getName(), e.getTextTrim());
}
String return_code = params.get("return_code");
String appid = params.get("appid");
String appidConfig = WechatConfig.appid;
String signWxpay = signWxpay(params);
boolean isReturnCode = "SUCCESS".equals(return_code) && appid.equals(appidConfig);
if (!isReturnCode) {
logger.info("return_code is fail");
return responseFailedWxpay("return_code is fail");
}
String sign = params.get("sign");
boolean isSign = StrUtil.isNotBlank(sign) && sign.equals(signWxpay);
if (!isSign) {
logger.warn("签名校验有误");
return responseFailedWxpay("签名校验有误");
}
String result_code = params.get("result_code");
boolean isResultCode = "SUCCESS".equals(result_code);
if (!isResultCode) {
logger.info("result_code is fail");
return responseFailedWxpay("result_code is fail");
}
String transaction_id = params.get("transaction_id");
String out_trade_no = params.get("out_trade_no");
double cash_fee = Double.parseDouble(params.get("cash_fee")) / 100.0f;
logger.info("transaction_id:" + transaction_id + ",out_trade_no:" + out_trade_no + "," + cash_fee);
// 处理业务
String apply = function.apply(cash_fee, out_trade_no);
return apply;
}
/**
* 微信支付通知参数签名
*
* @param params
* @return String
*/
public static String signWxpay(HashMap<String, String> params) {
Object[] keys = params.keySet().toArray();
Arrays.sort(keys);
String k, v;
String str = "";
for (int i = 0; i < keys.length; i++) {
k = (String) keys[i];
if (k.equals("sign")) {
continue;
}
if (params.get(k) == null) {
continue;
}
v = (String) params.get(k);
if (v.equals("0") || v.equals("")) {
continue;
}
str += k + "=" + v + "&";
}
String appSecret = WechatConfig.key;
str += "key=" + appSecret;
String md5Str = MD5Util.MD5(str).toUpperCase();
return md5Str;
}
public static String responseFailedWxpay(String msg) {
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("xml");
root.addElement("return_code").addText("FAIL");
root.addElement("return_msg").addText(msg + "");
String result = doc.asXML();
System.out.println(result);
return result;
}
public static String responseSuccessWxpay() {
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("xml");
root.addElement("return_code").addText("SUCCESS");
root.addElement("return_msg").addText("OK");
String result = doc.asXML();
return result;
}
}
/**
* 微信支付回调
*/
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public synchronized String buyCardNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
String s = WxPayUtil.payCallback(request, response, (payMoney, orderId) -> {
// 查询待支付订单状态
QueryWrapper<OrderMembership> orderMembershipQueryWrapper = new QueryWrapper<>();
orderMembershipQueryWrapper.lambda().eq(OrderMembership::getDeflag, Dictionary.DEFLAG_BY_NOT_DELETE)
.eq(OrderMembership::getStatus, Dictionary.ORDER_COURSE_STATUS_BY_WAIT_PAY)
.eq(OrderMembership::getOrderSn, orderId);
orderMembershipQueryWrapper.last("LIMIT 1");
OrderMembership orderMembership = orderMembershipService.getOne(orderMembershipQueryWrapper);
if (orderMembership == null) {
return WxPayUtil.responseSuccessWxpay();
}
// 需对非开发环境做金额校验
boolean isErrorPayMoney = !"dev".equals(env) && orderMembership.getPayedMoney() != payMoney;
if (isErrorPayMoney) {
return WxPayUtil.responseFailedWxpay("cash_fee Error");
}
try {
homePageService.buyCardNotifyUrl(orderMembership);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
e.printStackTrace();
return WxPayUtil.responseFailedWxpay("Internal Error");
}
return WxPayUtil.responseSuccessWxpay();
});
return s;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return WxPayUtil.responseFailedWxpay("Internal Error");
}
}
2.微信支付回调postman测试数据
<?xml version="1.0" encoding="utf-8"?>
<xml>
<appid><![CDATA[appid]]></appid>
<bank_type><![CDATA[CMB_CREDIT]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[mch_id]]></mch_id>
<nonce_str><![CDATA[8cfa9ff035f9409dae4806f677891a31]]></nonce_str>
<openid><![CDATA[openid]]></openid>
<out_trade_no><![CDATA[out_trade_no]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[2C3F3B207FBA0A21A819424A82A25058]]></sign>
<time_end><![CDATA[20200806200255]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[JSAPI]]></trade_type>
<transaction_id><![CDATA[4200000713202008064770449128]]></transaction_id>
</xml>
3.文章参考链接
a.https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.addTemplate.html