背景
因為網絡、合作方、或者業務邏輯問題總會漏掉一些支付記錄,這時候要找到這些訂單號並查出原因。於是乎每日對賬需求就出來了,不僅僅對每日金額,還需要把支付寶微信的所有記錄拉取下來,再用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然后再讀取就沒這么多事了。