SpringBoot 微信退款功能的示例代码

网友投稿 202 2022-12-04


SpringBoot 微信退款功能的示例代码

一:微信支付证书配置

二:证书读取以及读取后的使用

package com.zhx.guides.assistant.config.wechatpay;

import org.apache.commons.io.IOUtils;

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.core.io.ClassPathResource;

import javax.net.ssl.SSLContext;

import java.io.ByteArrayInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.security.KeyStore;

/**

* @Class WeChatConfigUtil

* @Version 1.0

* @Date 创建时间:2020-06-15 16:19

* @Copyright Copyright by

* @Direction 类说明

*/

public class WeChatConfigUtil {

private static byte[] certData;

/****

* @throws Exception

*/

static {

try {

//从微信商户平台下载的安全证书存放的目录

//String certPath = "D:\\config\\apiclient_cert.p12";

//File file = new File(certPath);

//InputStream certStream = new FileInputStream(file);

//使用springboot配置文件内读取的方式

ClassPathResource classPathResource = new ClassPathResource("\\user_key\\apiclient_cert.p12");

InputStream certStream = classPathResource.getInputStream();

WeChatConfigUtil.certData = IOUtils.toByteArray(certStream);

certStream.read(WeChatConfigUtil.certData);

certStream.close();

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* 开始退款操作

*

* @param mchId 商户ID

* @param url 请求URL

* @param data 退款参数

* @return

* @throws Exception

*/

public static String doRefund(String mchId, String url, String data) throws Exception {

/**

* 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的

*/

KeyStore keyStore = KeyStore.getInstance("PKCS12");

//这里自行实现我是使用数据库配置将证书上传到了服务器可以使用 FileInputStream读取本地文件

//ByteArrayInputStream inputStream = FileUtil.getInputStream("https://############################.p12");

ByteArrayInputStream inputStream = new ByteArrayInputStream(WeChatConfigUtil.certData);

try {

//这里写密码..默认是你的MCHID

keyStore.load(inputStream, mchId.toCharArray());

} finally {

inputStream.close();

}

SSLContext sslcontext = SSLContexts.custom()

//这里也是写密码的

.loadKeyMaterial(keyStore, mchId.toCharArray())

.build();

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(

http://sslcontext,

SSLConnectionSocketFactory.getDefaultHostnameVerifier());

CloseableHttpClient httpclient = HttpClients.custom()

.setSSLSocketFactory(sslsf)

.build();

try {

HttpPost httpost = new HttpPost(url);

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();

}

}

}

三:发起订单退款操作

/**

* 封装查询请求数据

* @param tradeRefund 退款订单请求信息

* @param path 数据访问PATH

* @return

*/

private static SortedMap refundData( TradeRefund tradeRefund , String path ) throws Exception {

//构建参数

Map dataMap = new HashMap<>();

dataMap.put("appid","wx#################");

dataMap.put("mch_id","137#############");

//自行实现该随机串

dataMap.put("nonce_str",Core.MD5("12344"));

dataMap.put("out_trade_no","P190808170038402889c5318502");

dataMap.put("out_refund_no","P190808170038402889c5318502");

dataMap.put("total_fee","1");

dataMap.put("refund_fee","1");

dataMap.put("refund_desc","退款");

//生成签名

String sign = PayToolUtil.createSign("UTF-8", dataMap , WeichatPayConfigure.API_KEY );

//WXPayUtil.generateSignature(dataMap, "rv4###################");

dataMap.put("sign", sign);

//map数据转xml

String requestXML = getRequestXml( dataMap );

logger.info( "订单退款请求参数:\n" + requestXML );

//发起退款

String responseXml = WeChatConfigUtil.doRefund( WeichatPayConfigure.MCH_ID , "https://api.mch.weixin.qq.com/secapi/pay/refund", requestXML );

}

/**

* @author

* @date 2016-4-22

* @Description:将请求参数转换为xml格式的string

* @param parameters

* 请求参数

* @return

*/

public static String getRequestXml(SortedMap parameters) {

StringBuffer sb = new StringBuffer();

sb.append("");

Set es = parameters.entrySet();

Iterator it = es.iterator();

while (it.hasNext()) {

Map.Entry entry = (Map.Entry) it.next();

String k = (String) entry.getKey();

String v = (String) entry.getValue();

if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {

sb.append("<" + k + ">" + "<![CDATA[" + v + "]]>" + k + ">");

} else {

sb.append("<" + k + ">" + v + "" + k + ">");

}

}

sb.append("");

return sb.toString();

}

/**

* @author

* @date 2016-4-22

* @Description:sign签名

* @param characterEncoding

* 编码格式

* @param packageParams

* 请求参数

* @return

*/

public static String createSign(String characterEncoding, SortedMap packageParams, String API_KEY) {

StringBuffer sb = new StringBuffer();

Set es = packageParams.entrySet();

Iterator it = es.iterator();

while (it.hasNext()) {

Map.Entry entry = (Map.Entry) it.next();

String k = (String) entry.getKey();

String v = (String) entry.getValue();

if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {

sb.append(k + "=" + v + "&");

}

}

sb.append("key=" + API_KEY);

String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();

return sign;

}

package com.zhx.guides.assistant.interfaces.pay.wechatpay.util;

import java.security.MessageDigest;

public class MD5Util {

private static String byteArrayToHexString(byte b[]) {

StringBuffer resultSb = new StringBuffer();

for (int i = 0; i < b.length; i++)

resultSb.append(byteToHexString(b[i]));

return resultSb.toString();

}

private static String byteToHexString(byte b) {

int n = b;

if (n < 0)

n += 256;

int d1 = n / 16;

int d2 = n % 16;

return hexDigits[d1] + hexDigits[d2];

}

public static String MD5Encode(String origin, String charsetname) {

String resultString = null;

try {

resultString = new String(origin);

MessageDigest md = MessageDigest.getInstance("MD5");

if (charsetname == null || "".equals(charsetname))

resultString = byteArrayToHexString(md.digest(resultString

.getBytes()));

else

resultString = byteArrayToHexString(md.digest(resultString

.getBytes(charsetname)));

} catch (Exception exception) {

}

return resultString;

}

/***

* 简化版本

* @param s

* @return

*/

public final static String MD5(String s) {

char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

try {

byte[] btInput = s.getBytes("utf-8");

// 获得MD5摘要算法的 MessageDigest 对象

MessageDigest mdInst = MessageDigest.getInstance("MD5");

// 使用指定的字节更新摘要

mdInst.update(btInput);

// 获得密文

byte[] md = mdInst.digest();

// 把密文转换成十六进制的字符串形式

int j = md.length;

char str[] = new char[j * 2];

int k = 0;

for (int i = 0; i < j; i++) {

byte byte0 = md[i];

str[k++] = hexDigits[byte0 >>> 4 & 0xf];

str[k++] = hexDigits[byte0 & 0xf];

}

return new String(str);

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",

"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

}

/**

* @author

* @date 2016-4-22

* @Description:将请求参数转换为xml格式的string

* @param parameters

* 请求参数

* @return

*/

public static String getRequestXml(SortedMap parameters) {

StringBuffer sb = new StringBuffer();

sb.append("");

Set es = parameters.entrySet();

Iterator it = es.iterator();

while (it.hasNext()) {

Map.Entry entry = (Map.Entry) it.next();

String k = (String) entry.getKey();

String v = (String) entry.getValue();

if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {

sb.append("<" + k + ">" + "<![CDATA[" + v + "]]>" + k + ">");

} else {

sb.append("<" + k + ">" + v + "" + k + ">");

}

}

sb.append("");

return sb.toString();

}

四:请求XML示例

wx2421b1c4370ec43b

10000100

6cefdb308e1e2e8aabd48cf79e546a02

1415701182

1415757673

1

1

FE56DD4AA85C0EECA82C35595A69E153

五:官方信息

官方地址:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4

需注意这两个参数我使用的是out_trade_no

总结


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:一文秒懂java到底是值传递还是引用传递
下一篇:老生常谈之Java中堆和栈的概念和区别
相关文章

 发表评论

暂时没有评论,来抢沙发吧~