一、介绍
本文介绍了微信支付异步回调通知,退款异步回调通知的代码实例,如有不足的请提出,我会做出改正。
二、官方文档
支付异步回调通知:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8
退款异步回调通知:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_16&index=11
三、微信SDK安装方法
微信PC扫码支付(一)-maven本地仓库安装:微信支付sdk
四、回调通知所需工具类
WeixinPayUtil.java
public class WeixinPayUtil {
/**
* 密钥算法
*/
private static final String ALGORITHM = "AES";
/**
* 加解密算法/工作模式/填充方式
*/
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding";
private static Logger logger = LoggerFactory.getLogger(WeixinPayUtil.class);
/**
* 获取请求参数
* @param request
* @return
*/
public static String readData(HttpServletRequest request){
BufferedReader br = null;
try {
StringBuilder ret;
br = request.getReader();
String line = br.readLine();
if(line != null) {
ret = new StringBuilder();
ret.append(line);
}else{
return "";
}
while ((line = br.readLine()) != null) {
ret.append('\n').append(line);
}
return ret.toString();
}catch (IOException e) {
throw new RuntimeException(e);
}finally {
if(br != null) {
try {
br.close();
}catch (IOException e){
logger.error(e.getMessage(), e);
}
}
}
}
/**
* AES解密
* @param 微信返回的加密数据
* @return
* @throws Exception
*/
public static String decryptData(String data) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING,"BC");
WeixinConfig weixinConfig = new WeixinConfig();
SecretKeySpec key = new SecretKeySpec(MD5.encrypt(weixinConfig.getKey()).toLowerCase().getBytes(), ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64Utils.decode(data.getBytes())));
}
}
五、支付异步通知
注意:
- 在处理业务逻辑的时候,必须能够正确处理重复的通知。例如:通知之后修改完订单状态,下次在进行回调就不需要修改了。
- 一定要安装这个格式返回给微信,要不微信会一直回调你。
- 下文PayException为自定义异常类
- 下文WeixinConfig为微信需要固定参数类
public Object payCallBack(){
String fail="<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[FAIL]]></return_msg></xml>";
String success="<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
try {
String xmlMsg = WeixinPayUtil.readData(R.getRequest());
logger.info("微信回调通知参数:---"+xmlMsg);
if(StringUtils.isBlank(xmlMsg)){
return fail;
}
Map<String, String> paramMap = WXPayUtil.xmlToMap(xmlMsg);
//验证签名
WeixinConfig weixinConfig=new WeixinConfig();
WXPay wxPay=new WXPay(weixinConfig);
Boolean flag=wxPay.isPayResultNotifySignatureValid(paramMap);
logger.info("微信支付通知-验签:"+flag);
if(!flag){
logger.info("微信支付通知-验签失败");
return fail;
}
/**
* 业务逻辑:修改订单状态
*/
return success;
} catch (Exception e) {
logger.info("微信支付回调发生错误:",e);
return fail;
}
}
六、退款异步回调通知
注意:
- 在处理业务逻辑的时候,必须能够正确处理重复的通知。例如:通知之后修改完退款订单状态,下次在进行回调就不需要修改了。
- 一定要安装这个格式返回给微信,要不微信会一直回调你。
- 下面代码如报java.security.InvalidKeyException: Illegal key size or default parameters这个错请看这篇文章来进行解决https://blog.youkuaiyun.com/cl11992/article/details/86703694
- 下文PayException为自定义异常类
- 下文WeixinConfig为微信需要固定参数类
public Object refundCallBack() throws PayException {
String fail="<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[FAIL]]></return_msg></xml>";
String success="<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
try {
String xmlMsg = WeixinPayUtil.readData(R.getRequest());
if(StringUtils.isBlank(xmlMsg)){
return fail;
}
Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlMsg);
//状态码
String return_code = resultMap.get("return_code");
logger.info("微信退款通知-return_code:"+return_code);
if (!("SUCCESS").equals(return_code) || StringUtils.isBlank(return_code)) {
return fail;
}
String base64Result = resultMap.get("req_info").toString();
logger.info("微信退款-base64Result:"+base64Result);
Security.addProvider(new BouncyCastleProvider());
String result = WeixinPayUtil.decryptData(base64Result);
Map<String, String> map = WXPayUtil.xmlToMap(result);
if(map.isEmpty()){
return fail;
}
//状态码
String refund_status = map.get("refund_status");
logger.info("微信退款通知-refund_status:"+refund_status);
if (!("SUCCESS").equals(refund_status) || StringUtils.isBlank(refund_status)) {
return fail;
}
/**
* 业务逻辑:修改退款订单状态
*/
return success;
} catch (Exception e) {
logger.info("微信退款回调发生错误:",e);
return false;
}
}