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 創(chuàng)建時間: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 退款參數(shù)
* @return
* @throws Exception
*/
public static String doRefund(String mchId, String url, String data) throws Exception {
/**
* 注意PKCS12證書 是從微信商戶平臺-》賬戶設置-》 API安全 中下載的
*/
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//這里自行實現(xiàn)我是使用數(shù)據(jù)庫配置將證書上傳到了服務器可以使用 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(
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();
}
}
}
三:發(fā)起訂單退款操作
/**
* 封裝查詢請求數(shù)據(jù)
* @param tradeRefund 退款訂單請求信息
* @param path 數(shù)據(jù)訪問PATH
* @return
*/
private static SortedMap<String, Object> refundData( TradeRefund tradeRefund , String path ) throws Exception {
//構建參數(shù)
Map<String, String> dataMap = new HashMap<>();
dataMap.put("appid","wx#################");
dataMap.put("mch_id","137#############");
//自行實現(xiàn)該隨機串
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數(shù)據(jù)轉xml
String requestXML = getRequestXml( dataMap );
logger.info( "訂單退款請求參數(shù):\n" + requestXML );
//發(fā)起退款
String responseXml = WeChatConfigUtil.doRefund( WeichatPayConfigure.MCH_ID , "https://api.mch.weixin.qq.com/secapi/pay/refund", requestXML );
}
/**
* @author
* @date 2016-4-22
* @Description:將請求參數(shù)轉換為xml格式的string
* @param parameters
* 請求參數(shù)
* @return
*/
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
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("</xml>");
return sb.toString();
}
/**
* @author
* @date 2016-4-22
* @Description:sign簽名
* @param characterEncoding
* 編碼格式
* @param packageParams
* 請求參數(shù)
* @return
*/
public static String createSign(String characterEncoding, SortedMap<Object, Object> 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");
// 使用指定的字節(jié)更新摘要
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:將請求參數(shù)轉換為xml格式的string
* @param parameters
* 請求參數(shù)
* @return
*/
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
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("</xml>");
return sb.toString();
}
四:請求XML示例
<xml> <appid>wx2421b1c4370ec43b</appid> <mch_id>10000100</mch_id> <nonce_str>6cefdb308e1e2e8aabd48cf79e546a02</nonce_str> <out_refund_no>1415701182</out_refund_no> <out_trade_no>1415757673</out_trade_no> <refund_fee>1</refund_fee> <total_fee>1</total_fee> <transaction_id></transaction_id> <sign>FE56DD4AA85C0EECA82C35595A69E153</sign> </xml>
五:官方信息
官方地址:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4
需注意這兩個參數(shù)我使用的是out_trade_no

總結
到此這篇關于SpringBoot 微信退款功能的示例代碼的文章就介紹到這了,更多相關SpringBoot 微信退款內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- springboot整合微信支付sdk過程解析
- springboot 微信授權網(wǎng)頁登錄操作流程
- Springboot單體架構http請求轉換https請求來支持微信小程序調用接口
- 基于springboot微信公眾號開發(fā)(微信自動回復)
- SpringBoot JS-SDK自定義微信分享的實現(xiàn)
- 微信小程序 springboot后臺如何獲取用戶的openid
- SpringBoot中獲取微信用戶信息的方法
- Spring Boot獲取微信用戶信息的超簡單方法
- SpringBoot微信消息接口配置詳解
- activemq整合springboot使用方法(個人微信小程序用)
- Springboot網(wǎng)站第三方登錄 微信登錄
- Spring Boot項目中集成微信支付v3
相關文章
MyBatis異常-Property ''configLocation'' not specified, using d
今天小編就為大家分享一篇關于MyBatis異常-Property 'configLocation' not specified, using default MyBatis Configuration,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03
thymeleaf實現(xiàn)前后端數(shù)據(jù)交換的示例詳解
Thymeleaf?是一款用于渲染?XML/XHTML/HTML5?內容的模板引擎,當通過?Web?應用程序訪問時,Thymeleaf?會動態(tài)地替換掉靜態(tài)內容,使頁面動態(tài)顯示,這篇文章主要介紹了thymeleaf實現(xiàn)前后端數(shù)據(jù)交換,需要的朋友可以參考下2022-07-07
Java中ArrayIndexOutOfBoundsException 異常報錯的解決方案
本文主要介紹了Java中ArrayIndexOutOfBoundsException 異常報錯的解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-06-06

