今天来讲讲微信退款的功能吧
微信支付和支付宝支付的代码在我第一篇文章有讲 点我
首先准备的东西就是证书:
代码部分 :导入jar包
<!--微信退款-->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.8</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.11</version>
<scope>test</scope>
</dependency>
package com.ja.mp.communal.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.util.ResourceUtils;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
@Slf4j
public class ClientCustomSSL {
public static String doRefund(String url, String data, String mchId) throws Exception {
//指定读取证书格式为PKCS12(注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的)
KeyStore keyStore = KeyStore.getInstance("PKCS12");
String path = ResourceUtils.getURL("pay/apiclient_cert.p12").getPath();
// 指定证书路径
log.info("证书路径:"+path);
// String path = "D:/test/pay/apiclient_cert.p12";
//读取本机存放的PKCS12证书文件
FileInputStream instream = new FileInputStream(new File(path));
//比如安装在D:/pkcs12/apiclient_cert.p12情况下,就可以写成如下语句
//E:/test/pay
//FileInputStream instream = new FileInputStream(new File("D:/pay/apiclient_cert.p12"));
try {
//指定PKCS12的密码(商户ID)
keyStore.load(instream, mchId.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,mchId.toCharArray()).build();
//指定TLS版本
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
//设置httpclient的SSLSocketFactory
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
HttpPost httpost = new HttpPost(url); // 设置响应头信息
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
return jsonStr;
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
}
上面这个是公共模板,把证书文件放在对应的路径即可
业务代码
public Map<String, String> refund(String out_trade_no) throws Exception{
String xmlStr = "";
String resultXml = "";
Map<String, String> resultMap = new HashMap<String, String>();
// 公众账号ID:登陆微信公众号后台-开发-基本配置
String appid = configuration.getAppId();
// 微信支付商户号: mch_id-登陆微信支付后台,即可看到
String mch_id = configuration.getMchId();
// 随机字符串,长度要求在32位以内,调用工具类中的随机数生成方法
String nonce_str = WXPayUtil.generateNonceStr();
// 商户退款单号,同一单号多次请求,只退款一次
String out_refund_no = UUIDGenerator.getInstance().get();
//根据订单号查询信息
Optional<OrderDetail> orderDetail = orderMapper.selectUnique(out_trade_no);
String price = orderDetail.get().getOrderTotalPrice().toString();
Double total_price = Double.valueOf(price);
// 订单总金额
String total_fee = Integer.toString((int) (total_price * 100));
// 退款总金额
String refund_fee = Integer.toString((int) (total_price * 100));
// 退款原因,会在下发给用户的退款消息中体现(可不传入)
String refund_desc = "退款";
// API密钥(设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置)
String key = configuration.getKey();
// 将获得的信息存入Map集合中
Map<String, String> map = new HashMap<String, String>();
map.put("appid", appid);
map.put("mch_id", mch_id);
map.put("nonce_str", nonce_str);
map.put("out_trade_no", out_trade_no);
map.put("out_refund_no", out_refund_no);
map.put("total_fee", total_fee);
map.put("refund_fee", refund_fee);
map.put("refund_desc", refund_desc);
try {
// 调用工具类,将Map集合转化为带签名sign的XML格式字符串
xmlStr = WXPayUtil.generateSignedXml(map, key);
System.out.println(xmlStr);
// 调用微信退款接口地址(固定写法)
String url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
// 调用双向证书,返回xml格式状态码
resultXml = ClientCustomSSL.doRefund(url, xmlStr, mch_id);
System.out.println();
System.out.println(resultXml);
System.out.println();
// 将返回结果转换成Map集合
resultMap = WXPayUtil.xmlToMap(resultXml);
System.out.println(resultMap);
} catch (Exception e) {
log.debug("调用退款接口失败");
e.printStackTrace();
throw e;
}
// 微信端返回字符串为成功时,退款成功,更新数据
if (resultMap.get("return_code").equals(WXPayConstants.SUCCESS) && resultMap.get("result_code").equals(WXPayConstants.SUCCESS)) {
// 退款成功时,在此处更改订单的状态,并更新数据库对应信息
log.debug("退款成功");
//将退款成功的数据保存到数据库
} else {
// 退款失败时,在此处设置相应信息,更新相应记录
log.debug("退款失败");
}
//执行数据库更新动作
// 处理人
// 保存退款处理时间
return resultMap;
}
好了,微信退款即可完成, 注意的是一下几点 ,
1、 证书一定要放对,如果不知道证书或者弄丢了,可以去更换证书,一年只能更换3次,
2、 金额是以分为单位的,要 x100 ,
3、 一定一定要仔细的吧商户id ,appid等基础的配置给填写正确
4、 退款金额不能大于支付金额
4、 传入订单号 (这里可以是自己生成的订单号,也可以是微信支付的时候的订单号)
好了,来看看效果吧