微信退款這個功能仍是挺複雜的,建議去看一下官方文檔,理解了以後再寫功能,官方文檔:微信支付官方文檔php
首先去微信支付官網去下載各個版本的demo支付工具,我用的Java版本,地址:微信支付官方-SDK算法
下載完以後大概是這樣api
下面把我本身實現的這兩類貼出來,直接就能用微信
MyWXPayDomainImpl app
/** * @ClassName : MyWXPayDomainImpl * @Author : Yanqiang * @Date : 2019/4/1 * @Description :本身實現微信的abstract interface WXPayDomain接口,實現請求配置 */ public class MyWXPayDomainImpl implements WXPayDomain { private MyWXPayDomainImpl(){} private static class WxpayDomainHolder{ private static WXPayDomain holder = new MyWXPayDomainImpl(); } public static WXPayDomain instance(){ return WxpayDomainHolder.holder; } public synchronized void report(final String domain, long elapsedTimeMillis, final Exception ex) { DomainStatics info = domainData.get(domain); if(info == null){ info = new DomainStatics(domain); domainData.put(domain, info); } if(ex == null){ //success if(info.succCount >= 2){ //continue succ, clear error count info.connectTimeoutCount = info.dnsErrorCount = info.otherErrorCount = 0; }else{ ++info.succCount; } }else if(ex instanceof ConnectTimeoutException){ info.succCount = info.dnsErrorCount = 0; ++info.connectTimeoutCount; }else if(ex instanceof UnknownHostException){ info.succCount = 0; ++info.dnsErrorCount; }else{ info.succCount = 0; ++info.otherErrorCount; } } public synchronized DomainInfo getDomain(final WXPayConfig config) { DomainStatics primaryDomain = domainData.get(WXPayConstants.DOMAIN_API); if(primaryDomain == null || primaryDomain.isGood()) { return new DomainInfo(WXPayConstants.DOMAIN_API, true); } long now = System.currentTimeMillis(); if(switchToAlternateDomainTime == 0){ //first switch switchToAlternateDomainTime = now; return new DomainInfo(WXPayConstants.DOMAIN_API2, false); }else if(now - switchToAlternateDomainTime < MIN_SWITCH_PRIMARY_MSEC){ DomainStatics alternateDomain = domainData.get(WXPayConstants.DOMAIN_API2); if(alternateDomain == null || alternateDomain.isGood() || alternateDomain.badCount() < primaryDomain.badCount()){ return new DomainInfo(WXPayConstants.DOMAIN_API2, false); }else{ return new DomainInfo(WXPayConstants.DOMAIN_API, true); } }else{ //force switch back switchToAlternateDomainTime = 0; primaryDomain.resetCount(); DomainStatics alternateDomain = domainData.get(WXPayConstants.DOMAIN_API2); if(alternateDomain != null) alternateDomain.resetCount(); return new DomainInfo(WXPayConstants.DOMAIN_API, true); } } static class DomainStatics { final String domain; int succCount = 0; int connectTimeoutCount = 0; int dnsErrorCount =0; int otherErrorCount = 0; DomainStatics(String domain) { this.domain = domain; } void resetCount(){ succCount = connectTimeoutCount = dnsErrorCount = otherErrorCount = 0; } boolean isGood(){ return connectTimeoutCount <= 2 && dnsErrorCount <= 2; } int badCount(){ return connectTimeoutCount + dnsErrorCount * 5 + otherErrorCount / 4; } } private final int MIN_SWITCH_PRIMARY_MSEC = 3 * 60 * 1000; //3 minutes private long switchToAlternateDomainTime = 0; private Map<String, DomainStatics> domainData = new HashMap<String, DomainStatics>(); }
/** * @ClassName : MyWXPayConfig * @Author : Yanqiang * @Date : 2019/4/1 * @Description :微信支付/退款配置類 繼承abstract WXPayConfig ,本身實現微信配置 */ public class MyWXPayConfig extends WXPayConfig { private byte[] certData; private static MyWXPayConfig INSTANCE; private int profiles;//0是測試;1是正式 public MyWXPayConfig(int profiles) throws Exception { this.profiles = profiles; String path; //不是沙箱環境要要下載證書,開出來 //0是測試;1是正式 if (profiles == 0){ path = "這個是證書地址,以.p12結尾的那個文件"; }else { path = "這個是證書地址,以.p12結尾的那個文件"; } File file = new ClassPathResource(path).getFile(); InputStream certStream = new FileInputStream(file); this.certData = new byte[(int) file.length()]; certStream.read(this.certData); certStream.close(); } @Override String getAppID() { return "這裏是Appid"; } @Override String getMchID() { //0是測試;1是正式 if (profiles == 0){ return "這個是Mch的碼"; }else { return "這個是Mch的碼"; } } @Override String getKey() { return "這個是項目私鑰,不要泄露"; } @Override InputStream getCertStream() { ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData); return certBis; } @Override WXPayDomain getWXPayDomain() { return MyWXPayDomainImpl.instance(); } }
支付之類的最大的坑就是沙箱環境,那麼什麼是沙箱環境,其實就是在你去請求正式環境的時候,去(轉換/加密....)一下,讓服務商知道你這個請求是測試環境過來的請求dom
其次,微信退款須要的參數格式爲XML格式,若是用微信支付官網下載的demo的話不須要本身轉xmlide
/** * @Author : Yanqiang * @Date : 2019/4/9 * @Description : * * 其實,真正須要本身去寫的代碼只有這幾行, * 可是,是什麼讓咱們寫這一塊的時候這麼費勁的,仍是你要去看懂官方文檔, * 說道這兒,不得不說,微信支付的接口文檔寫的真夠爛的,看一遍以爲很好理解, * 寫的時候發現處處都是坑,再去看文檔也沒有寫清楚,就讓人二丈和尚摸不着頭髮 * * PS: 下面是真坑了! * * 1·參數必須按照文檔上的,如出一轍,不能大小寫,也不能駝峯 * 2·必須按照文檔上的參數順序一致!!! 除去忽略的參數,必傳參數順序依次往前排列 * 3·若是你用微信官方SDK的話,sign:簽名 這個參數能夠不傳,這個是工具類幫你作了,本身寫的話要寫! * 4·sign 的生成是將 除sign以外的參數,其餘不爲空的參數參與簽名。詳細介紹:簽名生成算法 */
//構造請求參數 不須要此時設置sign簽名,wxPay.refund()接口 經過配置類自動配置 ConcurrentHashMap<String, String> requestMap = new ConcurrentHashMap(); requestMap.put("appid", shopsApiProperties.mpWeixinAppid); requestMap.put("mch_id", mch_id); requestMap.put("nonce_str", WXPayUtil.generateNonceStr()); requestMap.put("out_refund_no", afterSale.getSaleordernum()); requestMap.put("out_trade_no", afterSale.getOuttradeno()); requestMap.put("refund_fee", String.valueOf(refundFee * 100)); requestMap.put("total_fee", String.valueOf(actualPrice)); //MyWXPayConfig: 配置類 0是測試;1是正式; // "": 回調通知地址; //autoReport: 這個沒有用到 //useSandbox: 沙箱環境 (false爲正式/true爲沙箱環境) WXPay wxPay = new WXPay(new MyWXPayConfig(0), "", true, false); Map<String, String> refund = wxPay.refund(requestMap, 6*1000,8*1000);