SpringBoot 微信退款功能的示例代碼
一:微信支付證書(shū)配置
二:證書(shū)讀取以及讀取后的使用
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)建時(shí)間:2020-06-15 16:19 * @Copyright Copyright by * @Direction 類(lèi)說(shuō)明 */ public class WeChatConfigUtil { private static byte[] certData; /**** * @throws Exception */ static { try { //從微信商戶(hù)平臺(tái)下載的安全證書(shū)存放的目錄 //String certPath = "D:\\config\\apiclient_cert.p12"; //File file = new File(certPath); //InputStream certStream = new FileInputStream(file); //使用springboot配置文件內(nèi)讀取的方式 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(); } } /** * 開(kāi)始退款操作 * * @param mchId 商戶(hù)ID * @param url 請(qǐng)求URL * @param data 退款參數(shù) * @return * @throws Exception */ public static String doRefund(String mchId, String url, String data) throws Exception { /** * 注意PKCS12證書(shū) 是從微信商戶(hù)平臺(tái)-》賬戶(hù)設(shè)置-》 API安全 中下載的 */ KeyStore keyStore = KeyStore.getInstance("PKCS12"); //這里自行實(shí)現(xiàn)我是使用數(shù)據(jù)庫(kù)配置將證書(shū)上傳到了服務(wù)器可以使用 FileInputStream讀取本地文件 //ByteArrayInputStream inputStream = FileUtil.getInputStream("https://############################.p12"); ByteArrayInputStream inputStream = new ByteArrayInputStream(WeChatConfigUtil.certData); try { //這里寫(xiě)密碼..默認(rèn)是你的MCHID keyStore.load(inputStream, mchId.toCharArray()); } finally { inputStream.close(); } SSLContext sslcontext = SSLContexts.custom() //這里也是寫(xiě)密碼的 .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ā)起訂單退款操作
/** * 封裝查詢(xún)請(qǐng)求數(shù)據(jù) * @param tradeRefund 退款訂單請(qǐng)求信息 * @param path 數(shù)據(jù)訪(fǎng)問(wèn)PATH * @return */ private static SortedMap<String, Object> refundData( TradeRefund tradeRefund , String path ) throws Exception { //構(gòu)建參數(shù) Map<String, String> dataMap = new HashMap<>(); dataMap.put("appid","wx#################"); dataMap.put("mch_id","137#############"); //自行實(shí)現(xiàn)該隨機(jī)串 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ù)轉(zhuǎn)xml String requestXML = getRequestXml( dataMap ); logger.info( "訂單退款請(qǐng)求參數(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:將請(qǐng)求參數(shù)轉(zhuǎn)換為xml格式的string * @param parameters * 請(qǐng)求參數(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 * 請(qǐng)求參數(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; } /*** * 簡(jiǎn)化版本 * @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 對(duì)象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); // 使用指定的字節(jié)更新摘要 mdInst.update(btInput); // 獲得密文 byte[] md = mdInst.digest(); // 把密文轉(zhuǎn)換成十六進(jìn)制的字符串形式 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:將請(qǐng)求參數(shù)轉(zhuǎn)換為xml格式的string * @param parameters * 請(qǐng)求參數(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(); }
四:請(qǐng)求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
需注意這兩個(gè)參數(shù)我使用的是out_trade_no
總結(jié)
到此這篇關(guān)于SpringBoot 微信退款功能的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot 微信退款內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot整合微信支付sdk過(guò)程解析
- springboot 微信授權(quán)網(wǎng)頁(yè)登錄操作流程
- Springboot單體架構(gòu)http請(qǐng)求轉(zhuǎn)換https請(qǐng)求來(lái)支持微信小程序調(diào)用接口
- 基于springboot微信公眾號(hào)開(kāi)發(fā)(微信自動(dòng)回復(fù))
- SpringBoot JS-SDK自定義微信分享的實(shí)現(xiàn)
- 微信小程序 springboot后臺(tái)如何獲取用戶(hù)的openid
- SpringBoot中獲取微信用戶(hù)信息的方法
- Spring Boot獲取微信用戶(hù)信息的超簡(jiǎn)單方法
- SpringBoot微信消息接口配置詳解
- activemq整合springboot使用方法(個(gè)人微信小程序用)
- Springboot網(wǎng)站第三方登錄 微信登錄
- Spring Boot項(xiàng)目中集成微信支付v3
相關(guān)文章
SpringMVC后端Controller頁(yè)面跳轉(zhuǎn)的三種方式匯總
這篇文章主要介紹了SpringMVC后端Controller頁(yè)面跳轉(zhuǎn)的三種方式匯總,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10java 線(xiàn)程池的實(shí)現(xiàn)原理、優(yōu)點(diǎn)與風(fēng)險(xiǎn)、以及4種線(xiàn)程池實(shí)現(xiàn)
這篇文章主要介紹了java 線(xiàn)程池的實(shí)現(xiàn)原理、優(yōu)點(diǎn)與風(fēng)險(xiǎn)、以及4種線(xiàn)程池實(shí)現(xiàn)包括了:配置線(xiàn)程池大小配置,線(xiàn)程池的實(shí)現(xiàn)原理等,需要的朋友可以參考下2023-02-02Spring Cloud微服務(wù)架構(gòu)的構(gòu)建:分布式配置中心(加密解密功能)
這篇文章主要給大家介紹了關(guān)于Spring Cloud微服務(wù)架構(gòu)的構(gòu)建:分布式配置中心(加密解密)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2018-05-05SpringBoot事務(wù)使用及回滾實(shí)現(xiàn)代碼詳解
這篇文章主要介紹了SpringBoot事務(wù)使用及回滾實(shí)現(xiàn)代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08net.sf.json.JSONObject 為null 的判斷方法
下面小編就為大家?guī)?lái)一篇net.sf.json.JSONObject 為null 的判斷方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02String split方法實(shí)現(xiàn)過(guò)程圖解
這篇文章主要介紹了String split方法實(shí)現(xiàn)過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Mybatis基于注解形式的sql語(yǔ)句生成實(shí)例代碼
這篇文章主要介紹了 Mybatis基于注解形式的sql語(yǔ)句生成實(shí)例代碼,需要的朋友可以參考下2017-09-09SpringBoot自動(dòng)配置特點(diǎn)與原理詳細(xì)分析
這篇文章主要介紹了SpringBoot自動(dòng)配置原理分析,SpringBoot是我們經(jīng)常使用的框架,那么你能不能針對(duì)SpringBoot實(shí)現(xiàn)自動(dòng)配置做一個(gè)詳細(xì)的介紹。如果可以的話(huà),能不能畫(huà)一下實(shí)現(xiàn)自動(dòng)配置的流程圖。牽扯到哪些關(guān)鍵類(lèi),以及哪些關(guān)鍵點(diǎn)2022-08-08