最近在做微信支付,其中有个申请退款环节;
关于申请退款,这里其实个人觉得比较简单,
Map<String, String> params = new HashMap<String, String>();
params.put("appid", wechatInfo.getAppId());
params.put("mch_id",wechatInfo.getStr("mchId"));
//商户订单号和微信订单号二选一
if(null != order.getOrderId()){
//商户订单号
params.put("out_trade_no", order.getOrderId() + "");
}else if (null != order.getTransactionId()){
//微信订单号
params.put("transaction_id", order.getTransactionId());
}else{
throw new BaseRuntimeException("商户订单号和微信订单号都不存在");
}
//商户退款单号
params.put("out_refund_no", System.currentTimeMillis()+"");
//总金额
params.put("total_fee", order.getTotalFee() + "");
//退款金额
params.put("refund_fee", order.getCashFee() + "");
//操作员帐号, 默认为商户号
params.put("op_user_id", wechatInfo.getStr("mchId"));
//退款结果回调地址
params.put("notify_url", "https://weixin.qq.com/notify/refundNotify");
//随机字符串
params.put("nonce_str", System.currentTimeMillis() + "");
//这里根据当前的params和商户密钥key去生成一个签名,百度一下,有很多,这里就不细说了
//生成sign
String sign = "(这里是生成的签名)";
params.put("sign", sign);
// 申请退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
//微信申请退款接口
String wx_refund_url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
//然后把params拼接成xml,调用退款接口;
//返回一个xml字符串,转成Map<String,String>
//返回一个refundCallParams;
Map<String,String> refundCallParams ;
if("SUCCESS".equalsIgnoreCase((String) refundCallParams.get("return_code"))){
log.info("------申请退款成功,正在退款中----orderId:" + orderId);
//申请微信退款接口返回success,但是退款到账还需要时间;
//把订单状态改为退款中
order.setTradeState("REFUNDING");
order.update();
renderJsonSuccess();
}else{
log.error("------退款失败----orderId:" + orderId);
order.setTradeState("REFUND_ERROR");
order.update();
renderJsonError("退款失败:" + refundCallParams.get("return_msg"));
}
这里要注意了,申请退款返回成功,并不退款成功,只是申请退款成功而已;
这个时候我们申请退款的时候传的
notify_url //结果回调地址
这个就很重要的,微信会有退款产生结果的时候通知我们,
首先退款结果通知返回的是这样的XML:
<xml>
<return_code>SUCCESS</return_code>
<appid><![CDATA[wx2421b1c4370ec43b]]></appid>
<mch_id><![CDATA[10000100]]></mch_id>
<nonce_str><![CDATA[TeqClE3i0mvn3DrK]]></nonce_str>
<req_info><![CDATA[T87GAHG17TGAHG1TGHAHAHA1Y1CIOA9UGJH1GAHV871HAGAGQYQQPOOJMXNBCXBVNMNMAJAA]]></req_info>
</xml>
这里的req_info是加密过的,下面是微信官方给出的解密方式:
解密步骤如下:
(1)对加密串A做base64解码,得到加密串B
(2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
(3)用key*对加密串B做AES-256-ECB解密(PKCS7Padding)
-----------------------------------------------
我只想说,还好有百度,不然这解密方式,靠自己,不知道什么时候能解开密文信息;
下面贴上这个解密的类文件:
ps:其实解密这里大部分都是摘录人家的,小小的搬运一下,见谅呀;
package com.abc.utils;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* 解密微信退款中返回相关信息的字符串
* @author wxy
* @since 2018/04/25
*/
public class AESDecodeUtil {
public static final String ALGORITHM = "AES/ECB/PKCS7Padding";
/**
* @author wxy
* @param reqInfo 退款结果里的密文
* @param paternerKey 商户微信支付key
* @return
* @throws Exception
*/
public static String decode(String reqInfo, String paternerKey) throws Exception{
try {
byte[] decodeBase64 = Base64.decodeBase64(reqInfo);
String md5Key = MD5Util.GetMD5Code(paternerKey);
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKey keySpec = new SecretKeySpec(md5Key.getBytes(), "AES"); //生成加密解密需要的Key
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decoded = cipher.doFinal(decodeBase64);
String result = new String(decoded, "UTF-8");
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
这段解密的代码,我觉得最重要的是要引入对的包;
至于详细的解密方式 ,我没有去细究;
这里返回的是一个xml字符串,直接转成Map就可以直接取其中的参数了,
参考官方微信退款结果通知文档:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=10
喜欢的话,点个赞吧!