微信下載對賬單


最近接手了一個棘手的工作:微信下載對賬單。

剛接手完全懵逼,怎么和微信對接啊。然后就是百度。。

終於找到了組織:

微信支付|開發文檔 :點擊跳轉

 

通過文檔我們可以看到,首先是:

1.下載對賬單開放接口鏈接:

https://api.mch.weixin.qq.com/pay/downloadbill

 關於應用場景:

商戶可以通過該接口下載歷史交易清單。比如掉單、系統錯誤等導致商戶側和微信側數據不一致,通過對賬單核對后可校正支付狀態。
注意:
1、微信側未成功下單的交易不會出現在對賬單中。支付成功后撤銷的交易會出現在對賬單中,跟原支付單訂單號一致;
2、微信在次日9點啟動生成前一天的對賬單,建議商戶10點后再獲取;
3、對賬單中涉及金額的字段單位為“元”。
4、對賬單接口只能下載三個月以內的賬單。

 

2.傳入的參數:

接着我們看下傳入參數:

其中,微信分配的appId 和 商戶號 是自己的。

還有就是需要 去商戶平台查自己商戶號以及key。這個key主要用在生成簽名中。

看一下查詢代碼:

 

        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        parameters.put("appid", ConfigUtil.APPID); // APPid
        parameters.put("mch_id", ConfigUtil.MCH_ID); // 商戶id
        // parameters.put("device_info", "");//微信支付分配的終端設備號,填寫此字段,只下載該設備號 的對賬單
        parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());
        // 下載對賬單的日期,格式:20140603,當前日期前一天。
        String billDate = DateUtil.date2Str(DateUtil.addDay(new Date(), -1), "yyyyMMdd");
        parameters.put("bill_date", billDate);//
        // bill_type:ALL返回當日所有訂單信息,默認值SUCCESS返回當日成功支付的訂單。REFUND,返回當日退款訂單
        parameters.put("bill_type", "ALL");
        String sign = PayCommonUtil.createSign("utf-8", parameters);
        parameters.put("sign", sign);
        String reuqestXml = PayCommonUtil.getRequestXml(parameters);
        String result = CommonUtil.httpsRequest(ConfigUtil.DOWNLOAD_BILL_URL, "POST", reuqestXml);

 

configUtil 工具類里面要配置APPID,MCH_ID,已及Key 。(注:所有的工具類我都放文章末尾鏈接里,自己下載即可。)我們需要在ConfigUtil工具類中配置一下:

     public final static String APPID = "";//服務號的應用號
     public final static String APP_SECRECT = "";//服務號的應用密碼
     public final static String TOKEN = "weixinCourse";//服務號的配置token
     public final static String MCH_ID = "";//商戶號
     public final static String API_KEY = "";//API密鑰
     public final static String SIGN_TYPE = "MD5";//簽名加密方式
     public final static String CERT_PATH = "D:/apiclient_cert.p12";//微信支付證書存放路徑地址

3.隨機數:

我們看一下隨機數的生成

public static String CreateNoncestr() {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String res = "";
        for (int i = 0; i < 16; i++) {
            Random rd = new Random();
            res += chars.charAt(rd.nextInt(chars.length() - 1));
        }
        return res;
    }

 

4.關於簽名:

關於簽名,有三點需要注意:

①格式是:utf-8;

②簽名類型:MD5以及HMAC-SHA256。默認為MD5,本例也是MD5。

簽名的生成:需要前面幾個值(APPID等)以及Key

public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v) 
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + ConfigUtil.API_KEY);
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

 

之后我們把map 變成xml格式數據:

例如這樣:

<xml>
  <appid>wx2421b1c4370ec43b</appid>
  <bill_date>20141110</bill_date>
  <bill_type>ALL</bill_type>
  <mch_id>10000100</mch_id>
  <nonce_str>21df7dc9cd8616b56919f20d9f679233</nonce_str>
  <sign>332F17B766FC787203EBE9D6E40457A1</sign>
</xml>

代碼:

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)) {
            if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)) {
                sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
            }else {
                sb.append("<"+k+">"+v+"</"+k+">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

 

之后把xml傳給微信對賬單接口而后接口會返回值:

5.返回值解析:

關於返回值,這里我詳解下:最重要一點是:成功和失敗返回數據流類型不一樣:成功返回文本格式,失敗返回xml格式數據

①返回失敗的情況 :返回xml ,其中無數據也會返回xml 。並且格式是下圖。

下面是錯誤碼

 

②成功:數據以文本方式返回。

拿微信給的成功返回數據舉例:

交易時間,公眾賬號ID,商戶號,子商戶號,設備號,微信訂單號,商戶訂單號,用戶標識,交易類型,交易狀態,付款銀行,貨幣種類,總金額,代金券或立減優惠金額,微信退款單號,商戶退款單號,退款金額,代金券或立減優惠退款金額,退款類型,退款狀態,商品名稱,商戶數據包,手續費,費率
`2014-11-1016:33:45,`wx2421b1c4370ec43b,`10000100,`0,`1000,`1001690740201411100005734289,`1415640626,`085e9858e3ba5186aafcbaed1,`MICROPAY,`SUCCESS,`CFT,`CNY,`0.01,`0.0,`0,`0,`0,`0,`,`,`被掃支付測試,`訂單額外描述,`0,`0.60%
`2014-11-1016:46:14,`wx2421b1c4370ec43b,`10000100,`0,`1000,`1002780740201411100005729794,`1415635270,`085e9858e90ca40c0b5aee463,`MICROPAY,`SUCCESS,`CFT,`CNY,`0.01,`0.0,`0,`0,`0,`0,`,`,`被掃支付測試,`訂單額外描述,`0,`0.60%
總交易單數,總交易額,總退款金額,總代金券或立減優惠退款金額,手續費總金額
`2,`0.02,`0.0,`0.0,`0

 

我們對成功的數據進行處理:

①把第一行表頭去掉

代碼為:

String tradeMsg = result.substring(result.indexOf("`"));

數據為:

`2014-11-1016:33:45,`wx2421b1c4370ec43b,`10000100,`0,`1000,`1001690740201411100005734289,`1415640626,`085e9858e3ba5186aafcbaed1,`MICROPAY,`SUCCESS,`CFT,`CNY,`0.01,`0.0,`0,`0,`0,`0,`,`,`被掃支付測試,`訂單額外描述,`0,`0.60%
`2014-11-1016:46:14,`wx2421b1c4370ec43b,`10000100,`0,`1000,`1002780740201411100005729794,`1415635270,`085e9858e90ca40c0b5aee463,`MICROPAY,`SUCCESS,`CFT,`CNY,`0.01,`0.0,`0,`0,`0,`0,`,`,`被掃支付測試,`訂單額外描述,`0,`0.60%
總交易單數,總交易額,總退款金額,總代金券或立減優惠退款金額,手續費總金額
`2,`0.02,`0.0,`0.0,`0

 

②去掉匯總數據,並且去掉"`"這個符號。

代碼為:

String tradeInfo = tradeMsg.substring(0, tradeMsg.indexOf("總")).replace("`", "");// 去掉匯總數據,並且去掉'`'

數據為:

2014-11-1016:33:45,wx2421b1c4370ec43b,10000100,0,1000,1001690740201411100005734289,1415640626,085e9858e3ba5186aafcbaed1,MICROPAY,SUCCESS,CFT,CNY,0.01,0.0,0,0,0,0,,,被掃支付測試,訂單額外描述,0,0.60%
2014-11-1016:46:14,wx2421b1c4370ec43b,10000100,0,1000,1002780740201411100005729794,1415635270,085e9858e90ca40c0b5aee463,MICROPAY,SUCCESS,CFT,CNY,0.01,0.0,0,0,0,0,,,被掃支付測試,訂單額外描述,0,0.60%

可以看到和我們想要的數據已經大致一樣了。

 

③用spilt方法拿出每一天數據放進數組里。之后再用spilt方法把數據放進二維數組里。

String[] tradeArray = tradeInfo.split("%");  // 根據%來區分

 

 for (String tradeDetailInfo : tradeArray) {
            String[] tradeDetailArray = tradeDetailInfo.split(",");
 }

 

④最后保存下來就可以了。

            PtWxTradeDetail entity = null;
entity = new PtWxTradeDetail(); entity.setId(null); // 自動生成id entity.setTransDate(DateUtil.str2Date(tradeDetailArray[0], format));// 交易時間 entity.setCommonId(tradeDetailArray[1]);// 公眾賬號ID entity.setBusinessNo(tradeDetailArray[2]);// 商戶號 entity.setChildBusinessNo(tradeDetailArray[3]);// 子商戶號 entity.setEquipmentNo(tradeDetailArray[4]);// 設備號 entity.setWxOrderNo(tradeDetailArray[5]);// 微信訂單號 entity.setBusinessOrderNo(tradeDetailArray[6]);// 商戶訂單號 entity.setUserIdentity(tradeDetailArray[7]);// 用戶標識 entity.setTransType(tradeDetailArray[8]);// 交易類型 entity.setTransStatus(tradeDetailArray[9]);// 交易狀態 entity.setPaymentBank(tradeDetailArray[10]);// 付款銀行 entity.setCurrency(tradeDetailArray[11]);// 貨幣種類 entity.setTotalAmount(tradeDetailArray[12]);// 總金額 entity.setRedEnvelopesAmount(tradeDetailArray[13]);// 企業紅包金額 entity.setWxRefundNo(tradeDetailArray[14]);// 微信退款單號 entity.setBusinessRefundNo(tradeDetailArray[15]);// 商戶退款單號 entity.setRefundAmount(tradeDetailArray[16]);// 退款金額 entity.setRedEnvelopesRefundAmount(tradeDetailArray[17]);// 企業紅包退款金額 entity.setRefundType(tradeDetailArray[18]);// 退款類型 entity.setRefundStatus(tradeDetailArray[19]);// 退款狀態 entity.setBusinessName(tradeDetailArray[20]);// 商品名稱 entity.setBusinessData(tradeDetailArray[21]);// 商戶數據包 entity.setFee(tradeDetailArray[22]);// 手續費 entity.setRate(tradeDetailArray[23] + "%");// 費率 entity.setCreateDate(new Date()); wxTradeDetailDao.insert(entity);

 

最后,微信支付工具類下載鏈接(百度網盤):點擊下載

 

后記

   寫完這篇博文給我最大的感悟就是:一定要仔細,仔細,再仔細的看官方文檔。你需要的,文檔都有,除了手把手教你以外你需要的肯定都有。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM