小程序微信退款是有点简单,类似支付要生成预支付订单,然后等待wx回调,不过请求微信后台时,退款请求要用到微信证书,直接从wx获得就行;有疑问在下面留言噢
/**
* weixin申请退款
* @param out_trade_no 商户订单号 (由商户自定义生成)
* @param out_refund_no 商户退款单号
* @param total_amount 订单付款总金额,单位为分,参数值不能带小数)
* @param refund_fee 退款金额
* notify_url 商户退款单号 String(64)
* @return
*/
public ResultBean weiXinRefundCreate(String out_trade_no,String out_refund_no, int total_amount, int refund_fee) {
/**
* 1:生产预创建退款订单
*/
ResultBean res=ResultBean.builder().build();
String entity = genRefundProductArgs(out_trade_no,out_refund_no,total_amount,refund_fee); //TODO 生成环境支付金额换成 paymentAmt int 整数,不能带小数
log.info("请求http创建退款订单参数|"+entity);
//建立连接并发送数据
try {
String result = WxHttpClientUtils.sendPostWxRefundByCertApi(WeixinConfig.wx_pay_refund, entity,WeixinConfig.MCH_ID);//商户server调用统一下单接口请求订单 的 接口链接
/**
* 2:解析返回的xml
* 微信退款SUCCESS退款申请接收成功,结果通过退款查询接口查询
*/
Map<String, String> resultMap = WeixinSignUtils.decodeXml(result);
log.info("微信退款SUCCESS退款申请接收参数|"+JSON.toJSONString(resultMap));
//退款返回标志码
String return_code = resultMap.get("return_code").toString();
String result_code = resultMap.get("result_code").toString();
if("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)){
String outRefunNo=resultMap.get("out_refund_no");
log.info("发起微信退款成功outRefunNo:"+outRefunNo);
AtRefunds record=AtRefunds.builder().refundId(resultMap.get("refund_id")).outRefundNo(outRefunNo).refundId(resultMap.get("refund_id")).build();
AtRefundsExample example=new AtRefundsExample();
example.createCriteria().andOutRefundNoEqualTo(outRefunNo).andOutTradeSeqEqualTo(resultMap.get("out_trade_no"));
atRefundsMapper.updateByExampleSelective(record,example);
//TODO 是否更新减去用户消费金额扩展
res.setMsg("发起微信退款成功");
res.setRet(ResponseCodeEnum.SUCCESS.getCode());
}else if(return_code.equals("SUCCESS") && result_code.equals("FAIL")){
log.info("FAIL提交业务失败!");
res.setMsg("FAIL提交业务失败!");
res.setRet(ResponseCodeEnum.FAIL.getCode());
}else{
log.info("微信原路返款失败!!");
res.setMsg("微信原路返款失败!");
res.setRet(ResponseCodeEnum.FAIL.getCode());
}
}catch (Exception e){
//错误码https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4
log.error("微信返款失败 Exception.");
e.printStackTrace();
}
return res;
}
sendPostWxRefundByCertApi:
/**
* 微信退款(证书)请求微信服务器,获取预退款订单
* @param url 请求url
* @param mch_id 用于加密的商户id
* @return String 退款结果
*/
public static String sendPostWxRefundByCertApi(String url, String xmlBody,String mch_id) throws Exception{
ClassPathResource classPathResource = new ClassPathResource("apiclient_cert.p12");//证书路径(此处我用的相对路径,你也可以考虑安全性,放在项目外)
InputStream instream = classPathResource.getInputStream();
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try {
keyStore.load(instream, mch_id.toCharArray());
} catch (Exception e) {
logger.error("微信退款(证书)请求 证书加载失败");
e.printStackTrace();
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mch_id.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
String result = "";
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(xmlBody));
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, "UTF-8");
System.out.println(response.getStatusLine());
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
//去除空格
return result.replaceAll(" ", "");
}
回调
/**
* 微信小程序径山花海 退款回调通知
* 特别说明:退款结果对重要的数据进行了加密,商户需要用商户秘钥进行解密后才能获得结果通知的内容
*/
@PostMapping("/wxRefundCallback")
public String weixin_refundCallback(HttpServletRequest request){
String weixinparam = null;
log.info("微信退款回调开始啦!!!!");
try {
InputStream inStream = request.getInputStream();
int _buffer_size = 1024;
if (inStream != null) {
ByteArrayOutputStream 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();
//将流转换成字符串
weixinparam = new String(outStream.toByteArray(), "UTF-8");
}
} catch (IOException e) {
log.error("微信退款回调返回数据样例格式化fail");
e.printStackTrace();
}
if (weixinparam == null) return resWXfail();
Map<String, String> resMap = WeixinSignUtils.decodeXml(weixinparam);
log.info("wx退款回调xml解析|" + JSON.toJSONString(resMap));
/**
* 2:通信成功,解密参数
*/
if ("SUCCESS".equals(resMap.get("return_code"))){
// String appid = resMap.get("appid");
String req_info = resMap.get("req_info");
Map<String, String> reqMap =null;
try {
//2.1解密参数,业务处理
String reqXml = AESUtil.wx_refund_decryptData(req_info,WeixinConfig.API_KEY);
reqMap = WeixinSignUtils.decodeXml(reqXml);
} catch (Exception e) {
log.error("解密参数Exception ,req_info:"+req_info);
e.printStackTrace();
}
if (reqMap == null) {
return resWXfail();
}
int flag =otherPayCallbackService.weixin_refundCallback(reqMap);
/**
* 返回成功给微信
*/
if (flag == 1) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
sb.append("<return_code><![CDATA[SUCCESS]]></return_code>");
sb.append("</xml>");
log.debug("返回SUCCESS给微信");
return sb.toString();
}else{
log.error("返回failure给微信");
}
} else {
log.error("wx退款回调xml解析error|"+resMap.get("return_msg"));
}
return resWXfail();
}
开发中参考了https://blog.csdn.net/weixin_43820012/article/details/99092320