支付寶微信拉取賬單到本地


背景

因為網絡、合作方、或者業務邏輯問題總會漏掉一些支付記錄,這時候要找到這些訂單號並查出原因。於是乎每日對賬需求就出來了,不僅僅對每日金額,還需要把支付寶微信的所有記錄拉取下來,再用sql對比訂單號。在網頁上顯示出來。

支付寶開放平台

文檔中心 - 》 全部API -》 賬務API -》查詢對賬單下載地址

https://docs.open.alipay.com/api_15/alipay.data.dataservice.bill.downloadurl.query

支付寶接口返回壓縮包,壓縮包中一個是詳細記錄,另一個是匯總。

微信支付商戶平台

最下層服務支持 - 》 開發文檔 -》 JSAPI支付 - 》 API列表 - 》 下載對賬單

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6

微信接口直接返回記錄,也可以返回壓縮格式的文件流。

Java代碼

支付寶

package com.gmtx.system.checkzwtask;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.annotation.Resource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.gmtx.system.dao.CommonDao;
import com.gmtx.system.entitys.ZfbEntity2;
import com.gmtx.system.tools.JsonUtil;
import com.gmtx.system.tools.PropsUtil;
import com.gmtx.system.tools.ZipUtil;

/**
 * 支付寶對賬單查詢下載
 */
@Component("ZfbBillCheck")
public class ZfbBillCheck{

    @Resource(name="commonDao")
    private CommonDao dao=null;

    public CommonDao getDao() {
        return dao;
    }
    
    private String url = "https://openapi.alipay.com/gateway.do";
    private String method = "alipay.data.dataservice.bill.downloadurl.query";
    private String app_id = "**********";
    private String charset = "gbk";
    // 加密驗簽RSA2或者RSA
    private String sign_type = "RSA2";
    // yyyy-MM-dd HH:mm:ss
    private String timestamp;
    // 版本
    private String version = "1.0";
    private String biz_content = "";
    
    // 對賬時間。用來創建下載的壓縮包名稱
    private String reconciliationTime;
    private String time;

    public ZfbCx(){}
    private boolean bl=true;
    
    private static final Properties properties = PropsUtil.loadProps("config.properties");
    
    String zfbZipDirPath = PropsUtil.getString(properties, "zfbzipdownloadDir"); 
    
    BillZfb zfb = null;
    
    /**
     * 判斷是否有昨天的對賬數據
     * @param rDate
     * @return 存在返回true
     */
    private boolean isExistDate(Date rDate) {
        SimpleDateFormat querySdf=new SimpleDateFormat("yyyy-MM-dd"); //設置時間格式
        String queryTime = querySdf.format(rDate);    //拉取時間
        String sql = "SELECT count(*) FROM bill_zfb_total WHERE DATE = ?";
        Integer result = (Integer) dao.queryOneColumnForSigetonRow(sql, new Object[]{queryTime}, Integer.class);
        if("1".equals(result+""))
            return true;
        else
            return false;
    }
    
    /**
     * 讀取文件夾中包含include字符串的文件名
     * @param include 返回文件名包含的字符串
     * @param path 文件夾路徑
     * @return
     */
    private String getFolderContentFileName(String include,String path) {
        File file = new File(path);
        String fileName = "";
        try
        {

            File[] tempList = file.listFiles();
            for (int i = 0; i < tempList.length; i++) {
                if (tempList[i].isFile()) {

                    fileName = tempList[i].getName();
                    if (fileName.indexOf(include) > 0) {
                        break;
                    }
                }
                if (tempList[i].isDirectory()) {
                    return "";
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return fileName;
    }
    
    /**
     * 讀取詳細記錄Excel,csv並插入到庫中
     * @param path
     * @throws Exception
     */
    private void readDetailExcelAndInsertIntoTable(String path) throws Exception {
        transferFile(path,path);
        File csv = new File(path); // CSV文件路徑
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(csv));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        String line = "";
        String everyLine = "";
        List<Object[]> paramList = new ArrayList<Object[]>();
        String[] params = null;
        int i = 0;
        try {
            while ((line = br.readLine()) != null) // 讀取到的內容給line變量
            {
                i++;
                if (i >= 6) {  //第六行為數據行
                    everyLine = new String(line.getBytes(),"utf-8");
                    if (everyLine.indexOf("----------------------------------------") > 0) {// 讀取到結束行
                        break;
                    } else {
                        everyLine=everyLine+" ";
                        params = everyLine.split("\\,");   //轉義才能分割
                        
                        zfb = new BillZfb();
                        zfb.setTradeNo(params[0].replace("\t", ""));
                        zfb.setMerchant_orderNo(params[1].replace("\t", ""));
                        zfb.setBusinessType(params[2].replace("\t", ""));
                        zfb.setCommodityName(params[3].replace("\t", ""));
                        zfb.setCreateTime(params[4].replace("\t", ""));
                        zfb.setFinishTime(params[5].replace("\t", ""));
                        zfb.setJifenbaoAmount(params[6].replace("\t", ""));
                        zfb.setMerchantNo(params[7].replace("\t", ""));
                        zfb.setMerchantName(params[8].replace("\t", ""));
                        zfb.setOperater(params[9].replace("\t", ""));
                        zfb.setTerminalNo(params[10].replace("\t", ""));
                        zfb.setClientAccount(params[11].replace("\t", ""));
                        zfb.setOrderAmount(params[12].replace("\t", ""));
                        zfb.setRealAmount(params[13].replace("\t", ""));
                        zfb.setRedPaperAmount(params[14].replace("\t", ""));
                        zfb.setZfbdiscountAmount(params[15].replace("\t", ""));
                        zfb.setMerdiscountAmount(params[16].replace("\t", ""));
                        zfb.setJuanhexiaoAmount(params[17].replace("\t", ""));
                        zfb.setJuanName(params[18].replace("\t", ""));
                        zfb.setMerredPaperConsume(params[19].replace("\t", ""));
                        zfb.setCardConsume(params[20].replace("\t", ""));
                        zfb.setRefundNo(params[21].replace("\t", ""));
                        zfb.setServiceFee(params[22].replace("\t", ""));
                        zfb.setShareBenefit(params[23].replace("\t", ""));
                        zfb.setRemark(params[24].replace("\t", ""));
                        paramList.add(zfb.getArray());
                    }
                }
            }
            String sql ="INSERT INTO `bill_zfb` (`tradeNo`, `merchant_orderNo`, `businessType`, `commodityName`, `createTime`, `finishTime`, `merchantNo`, `merchantName`, `operater`, `terminalNo`, `clientAccount`, `orderAmount`, `realAmount`, `redPaperAmount`, `jifenbaoAmount`, `zfbdiscountAmount`, `merdiscountAmount`, `juanhexiaoAmount`, `juanName`, `merredPaperConsume`, `cardConsume`, `refundNo`, `serviceFee`, `shareBenefit`, `remark`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
            dao.executeBatch2(sql, paramList);
            
        } catch (IOException e) {
            LOGGER.error("支付寶賬單detail數據插入出現異常"+ e);
        }
    }
    
    /**
     * 讀取支付寶匯總csv並入庫
     * TODO
     * @param path
     * @throws Exception
     */
    private void readTotalExcelAndInsertIntoTable(String path,Date before) throws Exception {
        SimpleDateFormat querySdf=new SimpleDateFormat("yyyy-MM-dd"); //設置時間格式
        String queryTime = querySdf.format(before);    //拉取時間
        transferFile(path,path);
        File csv = new File(path); // CSV文件路徑
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(csv));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        String line = "";
        String everyLine = "";
        int i = 0;
        List<String> params = new ArrayList<String>();
        params.add(queryTime);
        try {
            while ((line = br.readLine()) != null) // 讀取到的內容給line變量
            {
                i++;
                if (i >= 6) {  //第六行為數據行
                    everyLine = line;
                    if (everyLine.indexOf("結束") > 0) {// 讀取到結束行
                        break;
                    } else if(i==6){  //第六行
                        everyLine=everyLine+" ";
                        String[] excelArr = everyLine.split(",");
                        for (int j = 2; j < excelArr.length; j++) {  //前兩個不為主數據
                            params.add(excelArr[j]);
                        }
                        String sqlString = "INSERT INTO `bill_zfb_total` (`date`, `totalOrderCount`, `refundOrderCount`, `orderAmount`, `realAmount`, `zfbdiscountAmount`, `merdiscountAmount`, `cardConsumeAmount`, `serviceFee`, `shareBenefit`, `realBenefit`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
                        dao.execute(sqlString, params.toArray());
                    }
                }
            }
            
        } catch (IOException e) {
            LOGGER.error("支付寶賬單total數據插入出現異常"+e);
            e.printStackTrace();
        }
    }
    
    /**
     * 從支付寶獲取數據
     * @throws Exception 
     * @throws UnsupportedEncodingException 
     */
    private String getDataFromZfb(Date dBefore) throws UnsupportedEncodingException, Exception {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); //設置時間格式
        SimpleDateFormat sdf2=new SimpleDateFormat("yyyyMMdd"); //設置時間格式
        time = sdf.format(dBefore);    //格式化前一天
        LOGGER.info("開始下載"+time+"號支付寶賬單");
        reconciliationTime = sdf2.format(dBefore);   //新建文件夾用
                    
        timestamp=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        biz_content = "{\"bill_date\":\"" + time + "\",\"bill_type\":\"trade\"}";
                
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("app_id", app_id);
        params.put("method", method);
        params.put("charset", charset);
        params.put("sign_type", sign_type);
        params.put("timestamp", timestamp + "");
        params.put("version", version);
        params.put("biz_content", biz_content);
        List<String> ks = new ArrayList<String>(params.keySet());
        Collections.sort(ks);
        String ss = "";
        String ss2 = "";
        for (int i = 0; i < ks.size(); i++) {
            String key = ks.get(i);
            Object value = params.get(key);
            value = value == null ? "" : value;
            try {
                ss += key + "=" + value.toString() + "&";
                ss2 += key + "=" + URLEncoder.encode(value.toString(), charset)
                        + "&";
            } catch (Exception ex) {
                LOGGER.error("下載賬單出現異常"+ ex);
                ex.printStackTrace();
            }
        }
        ss = ss.substring(0, ss.length() - 1);
        ss2 = ss2.substring(0, ss2.length() - 1);
            String sign = new RSA().sign(ss.getBytes(charset));
            String result = new HttpsUtil().sendPostRequest(url, ss2 + "&sign="
                    + URLEncoder.encode(sign, charset), charset);
            // result 是支付寶返回的json數據
            return result;
    }
    
    /**
     * 下載zip並解壓
     * @return
     * @throws UnsupportedEncodingException
     * @throws Exception
     */
    private String downloadZIPAndUnzip(Date before) throws UnsupportedEncodingException, Exception{
        //獲取支付寶賬單數據數據
        String result = getDataFromZfb(before);
        
            JsonUtil<ZfbEntity2> dataJson = new JsonUtil<ZfbEntity2>();
            ZfbEntity2 temp = dataJson.jsonToObject(result, ZfbEntity2.class);  //將支付寶返回的json數據解析實體
            
            if (temp.getAlipay_data_dataservice_bill_downloadurl_query_response() != null
                    && temp.getAlipay_data_dataservice_bill_downloadurl_query_response().getCode().equals("10000")) {// 支付寶有返回下載路徑,並且返回的代碼是成功
                String downUrl = temp.getAlipay_data_dataservice_bill_downloadurl_query_response().getBill_download_url();
                
                HttpsUtil.downloadCreateDir(downUrl, zfbZipDirPath + reconciliationTime+ ".zip"); // 根據支付寶返回的下載路徑,下載到本地
                ZipUtil.unZip(zfbZipDirPath + reconciliationTime+ ".zip");  //解壓zip文件
            }
        return null;
    }
    
    /**
     * 把GBK文件轉為UTF-8
     * 兩個參數值可以為同一個路徑
     * @param srcFileName 源文件
     * @param destFileName 目標文件
     * @throws IOException
     */
    private static void transferFile(String srcFileName, String destFileName) throws IOException {
        String line_separator = System.getProperty("line.separator"); 
        FileInputStream fis = new FileInputStream(srcFileName);
        StringBuffer content = new StringBuffer();
        DataInputStream in = new DataInputStream(fis);
        BufferedReader d = new BufferedReader(new InputStreamReader(in, "GBK"));  //源文件的編碼方式
        String line = null;
        while ((line = d.readLine()) != null) {
            content.append(line + line_separator);
        }
        d.close();
        in.close();
        fis.close();
            
        Writer ow = new OutputStreamWriter(new FileOutputStream(destFileName), "utf-8");  //需要轉換的編碼方式
        ow.write(content.toString());
        ow.close();
    }

    /**
     * 檢查庫中是否已經存在某天賬單,如果不存在則插入
     * @param dBefore 日期
     */
    public void checkAndInsert(Date dBefore){
        //判斷數據庫中是否已經有對賬數據
        boolean isExist = isExistDate(dBefore);
        if(!isExist)
        {
            try {
                downloadZIPAndUnzip(dBefore);
            } catch (Exception e) {
                LOGGER.error("支付寶賬單壓縮包解壓失敗"+e);
            }
            //詳細記錄表入庫
            String fileName = getFolderContentFileName("_業務明細.csv",zfbZipDirPath + reconciliationTime);
            try {
                readDetailExcelAndInsertIntoTable(zfbZipDirPath + reconciliationTime+File.separator+fileName);
            } catch (Exception e) {
                LOGGER.error("支付寶詳細賬單入庫失敗"+e);
            }
            //匯總記錄入庫
            String fileName_total = getFolderContentFileName("_業務明細(匯總).csv",zfbZipDirPath + reconciliationTime);
            try {
                readTotalExcelAndInsertIntoTable(zfbZipDirPath + reconciliationTime+File.separator+fileName_total,dBefore);
            } catch (Exception e) {
                LOGGER.error("支付寶匯總賬單入庫失敗"+e);
            }
        }
    }

    @Scheduled(cron="0 0 23 * * *")
    public void execute() throws UnsupportedEncodingException, Exception {
        Thread.sleep(5*1000);
        //得到日歷
        Calendar calendar = Calendar.getInstance();

        //設置為前一天
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        //得到前一天的時間
        Date dBefore = calendar.getTime();
        checkAndInsert(dBefore);

        /*為了防止服務器宕機等特殊情況的發生,每天晚上除了拉取昨天的賬單,還會拉取前天和大前天的賬單*/
        //設置為前兩天
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        //判斷數據庫中是否已經有對賬數據
        checkAndInsert(calendar.getTime());

        //設置為前三天
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        //判斷數據庫中是否已經有對賬數據
        checkAndInsert(calendar.getTime());
        LOGGER.info("支付寶賬單核對結束");
        Thread.sleep(500*1000);
    }

    
    /**
     * 手動拉取數據-遍歷月份的每一天
     */
    //@Scheduled(cron="* * * * * *")
    public void dateInMonthIterator() throws UnsupportedEncodingException, Exception{
        Thread.sleep(5000);
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); //設置時間格式
        
        Calendar calendar = Calendar.getInstance(); //得到日歷
        calendar.set(2019, 11-1, 1);  //設置年月日,月份從0開始
        int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);   //獲取當月最大天數
        
        for (int i = 0; i < maxDay; i++) {
            Date dBefore = calendar.getTime();
            String time = sdf.format(dBefore);
            boolean isExist = isExistDate(dBefore);
            if(!isExist)
            {
                downloadZIPAndUnzip(dBefore);
                //詳細記錄表入庫
                String fileName = getFolderContentFileName("_業務明細.csv",zfbZipDirPath + reconciliationTime);
                readDetailExcelAndInsertIntoTable(zfbZipDirPath + reconciliationTime+File.separator+fileName);
                //匯總記錄入庫
                String fileName_total = getFolderContentFileName("_業務明細(匯總).csv",zfbZipDirPath + reconciliationTime);
                readTotalExcelAndInsertIntoTable(zfbZipDirPath + reconciliationTime+File.separator+fileName_total,dBefore);
            }
            
            calendar.add(Calendar.DAY_OF_MONTH, 1);  //天數加一操作
        }
        Thread.sleep(5*1000);
    }
}

微信

package com.gmtx.system.checkzwtask;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.gmtx.system.dao.CommonDao;
import com.gmtx.system.tools.MD5Util;

@Component("wxBillCheck")
public class WxBillCheck{

    @Resource(name="commonDao")
    private CommonDao dao=null;

    public CommonDao getDao() {
        return dao;
    }
    
    //公眾號
    private String appid="***";
    //微信支付分配的商戶號
    private String mch_id="***";
    //隨機字符串,不長於32位
    private String nonce_str;
    //簽名
    private String sign;
    //簽名類型,目前支持HMAC-SHA256和MD5,默認為MD5
    private String sign_type="MD5";

    private  final String URL="https://api.mch.weixin.qq.com/pay/downloadbill";

    private String charset="UTF-8";
    
    private String time;
    
    private String queryTime;
    
    private String key = "***";

    public WxSm(){
    }

    /**
     * 獲取微信賬單記錄
     * @param dBefore 賬單日期
     * @return
     */
    private String getDataFromWx(Date dBefore) {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd"); //設置時間格式
        time = sdf.format(dBefore);    //微信賬單日期
                                
        SimpleDateFormat sdf2=new SimpleDateFormat("yyyyMMddhhmmss");
        nonce_str = sdf2.format(dBefore);  //隨機字符串
                
        Map<String,Object> params=new HashMap<String,Object>();
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        params.put("nonce_str", nonce_str);
        params.put("sign_type", sign_type);
        params.put("bill_date", time);        //下載對賬單的日期,格式:20140603
        /*
         * ALL,返回當日所有訂單信息,默認值
            SUCCESS,返回當日成功支付的訂單
            REFUND,返回當日退款訂單
            RECHARGE_REFUND,返回當日充值退款訂單(相比其他對賬單多一欄“返還手續費”)
         * */
        params.put("bill_type", "ALL");
    
        List<String> ks=new ArrayList<String>(params.keySet());
        Collections.sort(ks);
        String str="";
        for (int i = 0; i < ks.size(); i++) {
            String key = ks.get(i);
            Object value = params.get(key);
            if(null!=value&&!value.toString().trim().equals("")){
                str+=key+"="+value+"&";
            }
        }
        str = str + "key="+key;

        sign= new MD5Util().md5ByWx(str,charset).toUpperCase();
        String xml="<xml><appid>"+appid+"</appid><mch_id>"+mch_id+"</mch_id><nonce_str>"+nonce_str+"</nonce_str>" +
                "<sign_type>"+sign_type+"</sign_type><bill_date>"+time+"</bill_date>" +
                "<bill_type>ALL</bill_type><sign>"+sign+"</sign></xml>";
        
        String result=HttpsUtil.sendPostRequest(URL,xml,charset);
        return result;
    }
    
    /**
     * 將微信返回的記錄插入到庫里面
     * TODO
     */
    public void insertIntoTable(Date dBefore){
        SimpleDateFormat querySdf=new SimpleDateFormat("yyyy-MM-dd"); //設置時間格式
        queryTime = querySdf.format(dBefore);    //拉取時間
        LOGGER.info("開始拉取"+queryTime+"號微信賬單");
        String result_wx = getDataFromWx(dBefore);   //拉取微信賬單
        Reader strReader = new StringReader(result_wx);
        BufferedReader reader = new BufferedReader(strReader);
        boolean result = false;
        
        /*//如果有某日的記錄刪除bill_wx_total和bill_wx那天的記錄
        String sql_del ="DELETE FROM bill_wx WHERE tradetime between ? and ? ";  // 刪除微信賬單記錄
        String sqltotal_del ="DELETE FROM bill_wx_total WHERE DATE = ?";  // 刪除匯總
        dao.execute(sql_del, new Object[]{queryTime+" 00:00:00",queryTime+" 23:59:59"});
        dao.execute(sqltotal_del, new Object[]{queryTime});*/
        
        String sql_check ="SELECT COUNT(*) FROM bill_wx_total WHERE DATE = ?";  //查看某天庫里是否已經存在
        Integer r = (Integer)dao.queryOneColumnForSigetonRow(sql_check, new Object[]{queryTime}, Integer.class);
        
        if ("1".equals(r+"")) {  //如果庫里存在直接退出
            return;
        }
        
        try {
            List<Object[]> dataparamList = new ArrayList<Object[]>();  //微信所有記錄參數
            
            String line = reader.readLine();  //讀取第一行(行頭)
            while (line != null) {
                if (line.contains("`")&&line.lastIndexOf("`")>200) {   //數據區域
                    String[] strArr=line.replace("`","").split(",");
                    dataparamList.add(strArr);
                }else if (line.contains("`")&&line.lastIndexOf("`")>10) {   //最后一行統計數據
                    String[] strArr=line.replace("`","").split(",");
                    String sql_total = "INSERT INTO `bill_wx_total` (`date`,totalOrderCount, `payableTotalAmount`, `totalRefundAmount`, `totalDiscountRefundAmount`, `serviceAmount`, `orderTotalAmount`, `applyRefundtotalAmount`) VALUES ('"+queryTime+"',?, ?, ?, ?, ?, ?, ?);";
                    result = dao.execute(sql_total, strArr);
                }
                line = reader.readLine();
            }
            //批量插入微信訂單數據
            String sql ="INSERT INTO `bill_wx` (`tradetime`, `pubaccountID`, `merchantNo`, `submerchantNo`, `devNo`, `wxorderNo`, `merchant_orderNo`, `userID`, `tradeType`, `tradeStatus`, `payBank`, `currencyType`, `amount_payable`, `amount_discount`, `wx_refundNo`, `merchant_refundNo`, `amount_refund`, `amount_discountRefund`, `refundType`, `refundStatus`, `commodityName`, `merchantPacket`, `servicefee`, `servicerate`,orderAmount,amount_applyrefund,servicerate_remark) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '');";
            dao.executeBatch2(sql, dataparamList);
            
        } catch (IOException e) {
            LOGGER.error("微信賬單拉取失敗"+e);
        } catch (Exception e) {
            LOGGER.error("微信賬單拉取失敗"+e);
        }
        LOGGER.info("微信賬單"+queryTime+"拉取成功");
        //return result;
    }

    /**
     * 定時器執行
     * TODO
     * @throws InterruptedException
     */
    @Scheduled(cron="0 0 23 * * *")
    public void execute() throws InterruptedException {
            Thread.sleep(6000);

            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DAY_OF_MONTH, -1);
            //得到前第一天的時間
            Date dBefore = calendar.getTime();
            insertIntoTable(dBefore);

            /*為了防止服務器宕機等特殊情況的發生,每天晚上除了拉取昨天的賬單,還會拉取前天和大前天的賬單*/
            calendar.add(Calendar.DAY_OF_MONTH, -1);
            //得到前第兩天的時間
            dBefore = calendar.getTime();
            insertIntoTable(dBefore);

            calendar.add(Calendar.DAY_OF_MONTH, -1);
            //得到前第三天的時間
            dBefore = calendar.getTime();
            insertIntoTable(dBefore);
            LOGGER.info("微信賬單核對結束");
    }
    
    /**
     * 手動拉取數據-遍歷月份的每一天
     */
    //@Scheduled(cron="* * * * * *")
    public void dateInMonthIterator() throws InterruptedException {
        Thread.sleep(6000);
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); //設置時間格式
        
        Calendar calendar = Calendar.getInstance(); //得到日歷
        calendar.set(2019, 11-1, 1);  //設置年月日,月份從0開始
        int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);   //獲取當月最大天數
        
        for (int i = 0; i < maxDay; i++) {
            Date dBefore = calendar.getTime();
            
            insertIntoTable(dBefore);
            
            calendar.add(Calendar.DAY_OF_MONTH, 1);  //天數加一操作
        }
        Thread.sleep(5*1000);
    }
}

日志配置

偶然發現有兩天的支付寶微信沒拉取下來,添加日志,並添加核對前兩天是否拉取賬單成功,如果前兩天失敗,則重新拉取。

log4j.logger.com.gmtx.system.checkzwtask=INFO,zwFile

###指定包名下的輸出到指定文件
log4j.appender.zwFile=org.apache.log4j.FileAppender
log4j.appender.zwFile.layout=org.apache.log4j.PatternLayout
log4j.appender.zwFile.File = D:/checkzwtask.log  
log4j.appender.zwFile.encoding=UTF-8
log4j.appender.zwFile.Append=true 
log4j.appender.zwFile.layout.ConversionPattern=[ %p ] %-d{yyyy-MM-dd HH:mm:ss} [ %t:%L ] %37c %3x - %m%n

Mysql數據庫

建四張表sql語句

-- --------------------------------------------------------
-- 主機:                           222.222.221.197
-- Server version:               5.5.40 - MySQL Community Server (GPL)
-- Server OS:                    Win64
-- HeidiSQL 版本:                  10.1.0.5464
-- --------------------------------------------------------

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;

CREATE TABLE IF NOT EXISTS `bill_wx` (
  `tradetime` datetime NOT NULL COMMENT '交易時間',
  `pubaccountID` char(20) DEFAULT NULL COMMENT '公眾賬號ID',
  `merchantNo` varchar(50) DEFAULT NULL COMMENT '商戶號',
  `submerchantNo` varchar(20) DEFAULT NULL COMMENT '特約商戶號',
  `devNo` varchar(20) DEFAULT NULL COMMENT '設備號',
  `wxorderNo` char(30) COMMENT '微信訂單號',
  `merchant_orderNo` varchar(50) NOT NULL COMMENT '商戶訂單號',
  `userID` char(30) DEFAULT NULL COMMENT '用戶標識',
  `tradeType` varchar(20) DEFAULT NULL COMMENT '交易類型',
  `tradeStatus` varchar(20) DEFAULT NULL COMMENT '交易狀態',
  `payBank` varchar(20) DEFAULT NULL COMMENT '付款銀行',
  `currencyType` char(5) DEFAULT NULL COMMENT '貨幣種類',
  `amount_payable` decimal(10,2) DEFAULT NULL COMMENT '應結訂單金額',
  `amount_discount` decimal(10,2) COMMENT '代金券金額',
  `wx_refundNo` varchar(50) DEFAULT NULL COMMENT '微信退款單號',
  `merchant_refundNo` varchar(50) DEFAULT NULL COMMENT '商戶退款單號',
  `amount_refund` decimal(10,2) DEFAULT NULL COMMENT '退款金額',
  `amount_discountRefund` decimal(10,2) DEFAULT NULL COMMENT '充值券退款金額',
  `refundType` varchar(20) DEFAULT NULL COMMENT '退款類型',
  `refundStatus` varchar(20) DEFAULT NULL COMMENT '退款狀態',
  `commodityName` varchar(30) DEFAULT NULL COMMENT '商品名稱',
  `merchantPacket` varchar(50) DEFAULT NULL COMMENT '商戶數據包',
  `servicefee` decimal(10,6) DEFAULT NULL COMMENT '手續費',
  `servicerate` varchar(10) DEFAULT NULL COMMENT '費率',
  `orderAmount` varchar(10) DEFAULT NULL COMMENT '訂單金額',
  `amount_applyrefund` varchar(10) DEFAULT NULL COMMENT '申請退款金額',
  `servicerate_remark` varchar(10) DEFAULT NULL COMMENT '費率備注',
  PRIMARY KEY (`merchant_orderNo`,`tradetime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='每日微信賬單拉取';

CREATE TABLE IF NOT EXISTS `bill_wx_total` (
  `date` date NOT NULL COMMENT '日期',
  `totalOrderCount` int(11) DEFAULT NULL,
  `payableTotalAmount` decimal(10,2) DEFAULT NULL COMMENT '應結訂單總金額',
  `totalRefundAmount` decimal(10,2) DEFAULT NULL COMMENT '退款總金額',
  `totalDiscountRefundAmount` decimal(10,2) DEFAULT NULL COMMENT '充值券退款總金額',
  `serviceAmount` decimal(10,2) DEFAULT NULL COMMENT '手續費總金額',
  `orderTotalAmount` decimal(10,2) DEFAULT NULL COMMENT '訂單總金額',
  `applyRefundtotalAmount` decimal(10,2) DEFAULT NULL COMMENT '申請退款總金額',
  `sysDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入庫時間',
  PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信日賬單總金額';

CREATE TABLE IF NOT EXISTS `bill_zfb` (
  `tradeNo` char(30) DEFAULT NULL COMMENT '支付寶交易號',
  `merchant_orderNo` char(35) NOT NULL COMMENT '商戶訂單號',
  `businessType` char(10) DEFAULT NULL COMMENT '業務類型',
  `commodityName` varchar(50) DEFAULT NULL COMMENT '商品名稱',
  `createTime` timestamp NULL DEFAULT NULL COMMENT '創建時間',
  `finishTime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '完成時間',
  `merchantNo` varchar(50) DEFAULT NULL COMMENT '門店編號',
  `merchantName` varchar(50) DEFAULT NULL COMMENT '門店名稱',
  `operater` varchar(50) DEFAULT NULL COMMENT '操作員',
  `terminalNo` varchar(50) DEFAULT NULL COMMENT '終端號',
  `clientAccount` varchar(50) DEFAULT NULL COMMENT '對方賬戶',
  `orderAmount` decimal(10,2) DEFAULT NULL COMMENT '訂單金額(元)',
  `realAmount` decimal(10,2) DEFAULT NULL COMMENT '商家實收(元)',
  `redPaperAmount` decimal(10,2) DEFAULT NULL COMMENT '支付寶紅包(元)',
  `jifenbaoAmount` decimal(10,2) NOT NULL COMMENT '集分寶(元)',
  `zfbdiscountAmount` decimal(10,2) DEFAULT NULL COMMENT '支付寶優惠(元)',
  `merdiscountAmount` decimal(10,2) DEFAULT NULL COMMENT '商家優惠(元)',
  `juanhexiaoAmount` decimal(10,2) DEFAULT NULL COMMENT '券核銷金額(元)',
  `juanName` varchar(50) DEFAULT NULL COMMENT '券名稱',
  `merredPaperConsume` decimal(10,2) DEFAULT NULL COMMENT '商家紅包消費金額(元)',
  `cardConsume` decimal(10,2) DEFAULT NULL COMMENT '卡消費金額(元)',
  `refundNo` varchar(50) DEFAULT NULL COMMENT '退款批次號/請求號',
  `serviceFee` decimal(10,2) DEFAULT NULL COMMENT '服務費(元)',
  `shareBenefit` decimal(10,2) DEFAULT NULL COMMENT '分潤(元)',
  `remark` varchar(50) DEFAULT NULL COMMENT '備注',
  PRIMARY KEY (`merchant_orderNo`,`finishTime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='支付寶每日賬單拉取';

CREATE TABLE IF NOT EXISTS `bill_zfb_total` (
  `date` date NOT NULL,
  `totalOrderCount` int(11) DEFAULT NULL COMMENT '交易訂單總筆數',
  `refundOrderCount` int(11) DEFAULT NULL COMMENT '退款訂單總筆數',
  `orderAmount` decimal(10,2) DEFAULT NULL COMMENT '訂單金額(元)',
  `realAmount` decimal(10,2) DEFAULT NULL COMMENT '商家實收(元)',
  `zfbdiscountAmount` decimal(10,2) DEFAULT NULL COMMENT '支付寶優惠(元)',
  `merdiscountAmount` decimal(10,2) DEFAULT NULL COMMENT '商家優惠(元)',
  `cardConsumeAmount` decimal(10,2) DEFAULT NULL COMMENT '卡消費金額(元)',
  `serviceFee` decimal(10,2) DEFAULT NULL COMMENT '服務費(元)',
  `shareBenefit` decimal(10,2) DEFAULT NULL COMMENT '分潤(元)',
  `realBenefit` decimal(10,2) DEFAULT NULL COMMENT '實收凈額(元)',
  `sysDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入庫時間',
  PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='支付寶日賬單總金額';

/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

支付寶微信退款訂單為單獨的一條記錄和過去的交易記錄互不影響。

微信每日匯總有訂單金額和退款金額分開的兩個字段。

支付寶的訂單金額(實收金額)為 交易訂單金額 - 退款訂單金額(退款訂單金額為負)即所有訂單的訂單金額之和

部署在線上時遇到的問題

中文亂碼

下載出來的支付寶Linux環境中讀取為亂碼(Windows下沒問題),要將每行讀出來的記錄轉為gbk,但出現了一些問題,被轉為?。后采用文件編碼轉碼解決。

everyLine = new String(line.getBytes(),"gbk");

標點轉義

分割符問題,Linux環境下逗號分割無效(Windows下沒問題),要用轉義字符,任何標點符號都要用轉義字符

params = everyLine.split("\\,"); //轉義

參數個數問題

readLine后支付寶正常參數為25個,轉碼為gbk之后發現有些居然為24個(依然是Linux中有問題Windows沒問題),是因為有的,變成了?,這尼瑪。用其他的編碼逗號就沒有問題,轉成GBK就有幾率出現?。

機智的我先用亂碼分割然后在用gbk轉碼然后出現了另一個亂碼...

最后解決的方法是先把文件轉成UTF-8然后再讀取就沒這么多事了。

 


免責聲明!

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



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