目录
- 一、准备工作
- 二、代码编写
- 1.将大部分常用的数据封装在WechatConstant中
- 2.controller
- 3.service
一、准备工作
要实现退款,需要准备很多东西。
小程序ID(APPID)
商户号(mch_id)
随机字符串(nonce_str)
微信订单号(transaction_id)
商户退款单号(out_refund_no)
订单金额(total_fee)
退款金额(refund_fee)
签名(sign)通过商户证书密钥生成
商户证书:是从微信商户平台-》账户设置-》API安全 中下载的
Tip:其中的微信的订单号、订单金额都在订单里,有提到支付成功后如何实现回调,如何存支付订单。
二、代码编写
1.将大部分常用的数据封装在WechatConstant中
Tip:这个WechatConstant在下边的代码中会用到,而且以后修改信息可直接在这里修改,别处的代码就不用动了
public class WechatConstant {
public final static String MCH_ID = "****************"; //商户号
public final static String MCH_KEY = "***************"; //商户密钥
public final static String APPID = "*****************"; //appid
public final static String SECRET = "****************"; //小程序密钥
public final static String KEY = "*******************"; //商户证书密钥
}
2.controller
Tip:这个controller主要是构建参数的,将那些APPID、商户号等东西放进map里,在后边的service中要转成xml格式
@ApiOperation("退款")
@RequestMapping(value = "/refund", method = {RequestMethod.GET})
public Res refund(HttpServletRequest request,
@RequestParam("openid") String openid,
@RequestParam("activityid")int activityid,
@RequestParam("memberid") int memberid) throws Exception {
//先将订单信息查出来 这里的订单就是上边说的支付订单,我的是通过openid和一个activityid查出来的,自己可根据业务逻辑修改。
QueryWrapper<Wxpaynotifyvo> wxPayNotifyVOQueryWrapper = new QueryWrapper<>();
wxPayNotifyVOQueryWrapper.eq("openid", openid);
wxPayNotifyVOQueryWrapper.eq("activityid",activityid);
Wxpaynotifyvo wxPayNotifyVO = this.wxPayService.getBaseMapper().selectOne(wxPayNotifyVOQueryWrapper);
//构建参数
Map<String, String> dataMap = new HashMap<>();
//小程序ID
dataMap.put("appid", WechatConstant.APPID);
//商户号
dataMap.put("mch_id", WechatConstant.MCH_ID);
//随机字符串
dataMap.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));
//微信订单号
dataMap.put("transaction_id", wxPayNotifyVO.getTransactionid());
//商户退款单号
Date now = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//可以方便地修改日期格式
String outRefundNo = "NO" + dateFormat.format(now);
dataMap.put("out_refund_no", outRefundNo);
//订单金额
dataMap.put("total_fee", wxPayNotifyVO.getTotalfee());
//退款金额
dataMap.put("refund_fee", wxPayNotifyVO.getTotalfee());
//商户证书密钥,生成签名
String sign = WXPayUtil.generateSignature(dataMap, WechatConstant.KEY);
dataMap.put("sign", sign);
//这个是将报名表里的数据删除
int i = applymanService.deleteApplyman(openid, activityid, memberid);
//这个不是
this.wxPayService.refound(dataMap);
Res res = new Res();
res.setCode(0);
res.setMsg("退款成功!");
return res;
}
3.service
Tip:在这里真正实现退款,里边要用到一个商户证书,上边提到过,需要在微信商户平台下载的,大概就长这个样子:
/**
* 退款
*
* @param dataMap
*/
@Override
public void refound(Map<String, String> dataMap) {
try {
//将传来的map转xml
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
for (String key : dataMap.keySet()) {
String value = "<![CDATA[" + dataMap.get(key) + "]]>";
sb.append("<" + key + ">" + value + "</" + key + ">");
System.out.println();
}
sb.append("</xml>");
String xmlString = sb.toString();
doRefoud(dataMap.get("mch_id"), "https://api.mch.weixin.qq.com/secapi/pay/refund", xmlString);
} catch (Exception e) {
e.printStackTrace();
}
//退款之后将wxpaynotifyvo中的数据删除,防止下次报名活动时与上次数据重复
QueryWrapper<Wxpaynotifyvo> wxpaynotifyvoQueryWrapper = new QueryWrapper<>();
wxpaynotifyvoQueryWrapper.eq("transactionid",dataMap.get("transaction_id"));
int delete = this.baseMapper.delete(wxpaynotifyvoQueryWrapper);
}
private static String doRefoud(String mch_id, String url, String xmlString) throws Exception {
//证书 是从微信商户平台-》账户设置-》 API安全 中下载的
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//我想的是把证书传到服务器上,没写出来
//FileInputStream inputStream = new FileInputStream(new File("src\\main\\resources\\apiclient_cert.p12"));
//据说这样写打包后也可以找到,就放在resources里了
ClassPathResource classPathResource = new ClassPathResource("apiclient_cert.p12");
InputStream inputStream = classPathResource.getInputStream();
try {
//这里写密码,默认是MCHID
keyStore.load(inputStream, WechatConstant.MCH_ID.toCharArray());
} finally {
inputStream.close();
} //这里写密码
SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, WechatConstant.MCH_ID.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
//请求路径
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(xmlString,"UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
//接受到返回信息
String jsonStr = EntityUtils.toString(response.getEntity(),"UTF-8");
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpClient.close();
}
return null;
}
Tip:退款之前先要实现支付,因为支付订单里边的参数是必须的,本来支付退款应该写在一起,但是东西太多所以分开了。这篇小程序退款是和前边写的微信支付是一个项目里的,可以结合着一起看比较完整。
欢迎批评纠正 ~ _ ~