//退款的核心就是验证证书
public R refuse(HttpServletRequest request,String orderId){
try {
String app_id = null;
String mch_id = null;
String mch_key = null;
BladeCompany bladeCompany = bladeCompanyClient.getByTenantId();
if (null == bladeCompany){
return R.fail("商户未设置商户号信息");
}
app_id = bladeCompany.getAppId();
mch_id = bladeCompany.getMchId();
mch_key = bladeCompany.getMchKey();
OrderTransaction orderTransaction = orderTransactionService.
getOne(Wrappers.<OrderTransaction>lambdaQuery()
.eq(OrderTransaction::getId,orderId)
.eq(OrderTransaction::getPayStatus,OrderTransaction.PayStatus.PAID)
.eq(OrderTransaction::getIsRefund,0));
if (null == orderTransaction){
return R.fail("订单已退款");
}
//获取请求ip地址
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getRemoteAddr();
}
if(ip.indexOf(",")!=-1){
String[] ips = ip.split(",");
ip = ips[0].trim();
}
String nonceStr = WXPayUtil.generateNonceStr();
//拼接统一下单地址参数
Map<String, String> paraMap = new HashMap<String, String>();
paraMap.put("appid", app_id);
paraMap.put("mch_id", mch_id);
paraMap.put("nonce_str", nonceStr);
paraMap.put("transaction_id", orderTransaction.getPayId());//微信订单id
paraMap.put("out_refund_no", orderTransaction.getOrderCode());//订单号
//把金额转换为分
BigDecimal amount = orderTransaction.getAmount();
amount = amount.multiply(new BigDecimal(100));
Integer totalFee = amount.intValue();
paraMap.put("total_fee",String.valueOf(totalFee));
paraMap.put("refund_fee", String.valueOf(totalFee));
String sign = WXPayUtil.generateSignature(paraMap, mch_key);
paraMap.put("sign",sign);
String xml = WXPayUtil.mapToXml(paraMap);
//读取证书
String path = "/mnt/cert/" + bladeCompany.getMchId() + "/apiclient_cert.p12";
String refuseUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";
RefundRequest refundRequest = new RefundRequest();
//证书验证
String xmlStr = refundRequest.httpsRequest(refuseUrl,xml,path,mch_id);
if (xmlStr.indexOf("SUCCESS") != -1) {
Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
//微信退款id
String refundId = map.get("refund_id");
//微信退款金额 单位:分
BigDecimal refundFee = new BigDecimal(map.get("refund_fee"));
refundFee = refundFee.divide(new BigDecimal(100));
refundFee = refundFee.setScale(2,BigDecimal.ROUND_HALF_DOWN);
return orderTransactionService.refuse(orderId,refundId,refundFee);
}
return R.fail("退款失败");
}catch (Exception e){
log.error(e.getMessage());
return R.fail("退款失败");
}
}
package com.ovu.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
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 javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.*;
import java.security.cert.CertificateException;
@Slf4j
public class RefundRequest {
//连接超时时间,默认10秒
private int socketTimeout = 10000;
//传输超时时间,默认30秒
private int connectTimeout = 30000;
//请求器的配置
private RequestConfig requestConfig;
//HTTP请求器
private CloseableHttpClient httpClient;
/**
* 加载证书
* @param path
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
private void initCert(String path,String mchId) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
//拼接证书的路径
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//加载本地的证书进行https加密传输
FileInputStream instream = new FileInputStream(new File(path));
try {
keyStore.load(instream, mchId.toCharArray()); //加载证书密码,默认为商户ID
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, mchId.toCharArray()) //加载证书密码,默认为商户ID
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[]{"TLSv1"},
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
//根据默认超时限制初始化requestConfig
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
}
/**
* 通过Https往API post xml数据
* @param url API地址
* @param xmlObj 要提交的XML数据对象
* @param path 当前目录,用于加载证书
* @param mchId 商户id
* @return
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public String httpsRequest(String url, String xmlObj, String path,String mchId) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
//加载证书
initCert(path,mchId);
String result = null;
HttpPost httpPost = new HttpPost(url);
//得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
//设置请求器的配置
httpPost.setConfig(requestConfig);
try {
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, "UTF-8");
} catch (ConnectionPoolTimeoutException e) {
log.trace("http get throw ConnectionPoolTimeoutException(wait time out)");
} catch (ConnectTimeoutException e) {
log.trace("http get throw ConnectTimeoutException");
} catch (SocketTimeoutException e) {
log.trace("http get throw SocketTimeoutException");
} catch (Exception e) {
log.trace("http get throw Exception");
} finally {
httpPost.abort();
}
return result;
}
}