diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java new file mode 100644 index 00000000..8ce06238 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java @@ -0,0 +1,90 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.github.binarywang.wxpay.constant.WxPayConstants.AccountType; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + *
+ * 微信支付下载资金账单请求参数类 + * Created by cwivan on 2018-08-02. + *+ * + * @author cwivan + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayDownloadFundFlowRequest extends BaseWxPayRequest { + private static final String[] ACCOUNT_TYPES = new String[]{AccountType.BASIC, AccountType.OPERATION, AccountType.FEES}; + private static final String SIGN_TYPE_HMAC_SHA256 = "HMAC-SHA256"; + private static final String TAR_TYPE_GZIP = "GZIP"; + + /** + *
+ * 对账单日期 + * bill_date + * 是 + * String(8) + * 20140603 + * 下载对账单的日期,格式:20140603 + *+ */ + @Required + @XStreamAlias("bill_date") + private String billDate; + + /** + *
+ * 资金账户类型 + * account_type + * 是 + * Basic + * String(8) + * --Basic,基本账户 + * --Operation,运营账户 + * --Fees,手续费账户 + *+ */ + @Required + @XStreamAlias("account_type") + private String accountType; + + /** + *
+ * 压缩账单 + * tar_type + * 否 + * String(8) + * GZIP + * 非必传参数,固定值:GZIP,返回格式为.gzip的压缩包账单。不传则默认为数据流形式。 + *+ */ + @XStreamAlias("tar_type") + private String tarType; + + @Override + protected void checkConstraints() throws WxPayException { + if (StringUtils.isNotBlank(this.getTarType()) && !TAR_TYPE_GZIP.equals(this.getTarType())) { + throw new WxPayException("tar_type值如果存在,只能为GZIP"); + } + + if (!ArrayUtils.contains(ACCOUNT_TYPES, this.getAccountType())) { + throw new WxPayException(String.format("account_type必须为%s其中之一,实际值:%s", + Arrays.toString(ACCOUNT_TYPES), this.getAccountType())); + } + /** + * 目前仅支持HMAC-SHA256 + */ + this.setSignType(SIGN_TYPE_HMAC_SHA256); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowBaseResult.java new file mode 100644 index 00000000..dc631314 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowBaseResult.java @@ -0,0 +1,71 @@ +package com.github.binarywang.wxpay.bean.result; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.io.Serializable; + +/** + * 记账时间:2018-02-01 04:21:23 微信支付业务单号:50000305742018020103387128253 资金流水单号:1900009231201802015884652186 业务名称:退款 + * 业务类型:退款 收支类型:支出 收支金额(元):0.02 账户结余(元):0.17 资金变更提交申请人:system 备注:缺货 业务凭证号:REF4200000068201801293084726067 + * + * @author cwivan + */ +@Data +@NoArgsConstructor +public class WxPayFundFlowBaseResult implements Serializable { + private static final long serialVersionUID = 4474557532904682718L; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); + } + + /** + * 记账时间 + */ + private String BillingTime; + /** + * 微信支付业务单号 + */ + private String bizTransactionId; + /** + * 资金流水单号 + */ + private String fundFlowId; + /** + * 业务名称 + */ + private String bizName; + /** + * 业务类型 + */ + private String bizType; + /** + * 收支类型 + */ + private String financialType; + /** + * 收支金额(元) + */ + private String financialFee; + /** + * 账户结余(元) + */ + private String AccountBalance; + /** + * 资金变更提交申请人 + */ + private String fundApplicant; + /** + * 备注 + */ + private String memo; + /** + * 业务凭证号 + */ + private String bizVoucherId; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowResult.java new file mode 100644 index 00000000..2cfd56e8 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowResult.java @@ -0,0 +1,60 @@ +package com.github.binarywang.wxpay.bean.result; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 下载资金账单接口响应结果对象类 + * Created by cwivan on 2018-08-02. + *+ * + * @author cwivan + * @date 2018-08-02 + */ +@Data +@NoArgsConstructor +public class WxPayFundFlowResult implements Serializable{ + private static final long serialVersionUID = 8371500036495349207L; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); + } + + /** + * 资金流水返回对象 + */ + private List
@@ -106,6 +105,24 @@ public class WxPayConstants { public static final String MICROPAY = "MICROPAY"; } + /** + * 账户类型 + */ + public static class AccountType{ + /** + * 基本账户 + */ + public static final String BASIC = "Basic"; + /** + * 运营账户 + */ + public static final String OPERATION = "Operation"; + /** + * Fees + */ + public static final String FEES = "Fees"; + } + /** * 签名类型. */ diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index 7e3dd196..e1dccd43 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -1,44 +1,19 @@ package com.github.binarywang.wxpay.service; -import java.io.File; -import java.util.Date; -import java.util.Map; - import com.github.binarywang.wxpay.bean.WxPayApiData; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponInfoQueryRequest; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponInfoQueryResult; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendRequest; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendResult; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponStockQueryRequest; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponStockQueryResult; +import com.github.binarywang.wxpay.bean.coupon.*; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxScanPayNotifyResult; -import com.github.binarywang.wxpay.bean.request.WxPayAuthcode2OpenidRequest; -import com.github.binarywang.wxpay.bean.request.WxPayDownloadBillRequest; -import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderCloseRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderReverseRequest; -import com.github.binarywang.wxpay.bean.request.WxPayRefundQueryRequest; -import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest; -import com.github.binarywang.wxpay.bean.request.WxPayReportRequest; -import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest; -import com.github.binarywang.wxpay.bean.request.WxPayShorturlRequest; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; -import com.github.binarywang.wxpay.bean.result.WxPayBillResult; -import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderCloseResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderReverseResult; -import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayRefundResult; -import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult; -import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult; +import com.github.binarywang.wxpay.bean.request.*; +import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.exception.WxPayException; +import java.io.File; +import java.util.Date; +import java.util.Map; + /** ** 微信支付相关接口. @@ -378,6 +353,42 @@ public interface WxPayService { */ WxPayBillResult downloadBill(WxPayDownloadBillRequest request) throws WxPayException; + /** + *+ * 下载资金账单. + * 商户可以通过该接口下载自2017年6月1日起 的历史资金流水账单。 + * 注意: + * 1、资金账单中的数据反映的是商户微信账户资金变动情况; + * 2、当日账单在次日上午9点开始生成,建议商户在上午10点以后获取; + * 3、资金账单中涉及金额的字段单位为“元”。 + * 接口链接:https://api.mch.weixin.qq.com/pay/downloadfundflow + * 详情请见: 下载对账单 + *+ * + * @param billDate 资金账单日期 bill_date 下载对账单的日期,格式:20140603 + * @param accountType 资金账户类型 account_type Basic,基本账户,Operation,运营账户,Fees,手续费账户 + * @param tarType 压缩账单 tar_type 非必传参数,固定值:GZIP,返回格式为.gzip的压缩包账单。不传则默认为数据流形式。 + * @return WxPayFundFlowResult对象 + */ + WxPayFundFlowResult downloadFundFlow(String billDate, String accountType, String tarType) throws WxPayException; + + /** + *+ * 下载资金账单. + * 商户可以通过该接口下载自2017年6月1日起 的历史资金流水账单。 + * 注意: + * 1、资金账单中的数据反映的是商户微信账户资金变动情况; + * 2、当日账单在次日上午9点开始生成,建议商户在上午10点以后获取; + * 3、资金账单中涉及金额的字段单位为“元”。 + * 接口链接:https://api.mch.weixin.qq.com/pay/downloadfundflow + * 详情请见: 下载对账单 + *+ * + * @param request 下载资金流水请求 + * @return WxPayFundFlowResult对象 + */ + WxPayFundFlowResult downloadFundFlow(WxPayDownloadFundFlowRequest request) throws WxPayException; + /** ** 提交刷卡支付. diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java index c627dcae..a113ca12 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -581,6 +581,125 @@ public abstract class BaseWxPayServiceImpl implements WxPayService { return wxPayBillResult; } + @Override + public WxPayFundFlowResult downloadFundFlow(String billDate, String accountType, String tarType) throws WxPayException { + + WxPayDownloadFundFlowRequest request = new WxPayDownloadFundFlowRequest(); + request.setBillDate(billDate); + request.setAccountType(accountType); + request.setTarType(tarType); + + return this.downloadFundFlow(request); + } + + @Override + public WxPayFundFlowResult downloadFundFlow(WxPayDownloadFundFlowRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/downloadfundflow"; + + String responseContent; + if (TarType.GZIP.equals(request.getTarType())) { + responseContent = this.handleGzipFundFlow(url, request.toXML()); + } else { + responseContent = this.post(url, request.toXML(), true); + if (responseContent.startsWith("<")) { + throw WxPayException.from(BaseWxPayResult.fromXML(responseContent, WxPayCommonResult.class)); + } + } + + return this.handleFundFlow(responseContent); + } + + private String handleGzipFundFlow(String url, String requestStr) throws WxPayException { + try { + byte[] responseBytes = this.postForBytes(url, requestStr, true); + Path tempDirectory = Files.createTempDirectory("fundFlow"); + Path path = Paths.get(tempDirectory.toString(), System.currentTimeMillis() + ".gzip"); + Files.write(path, responseBytes); + + try { + ListallLines = Files.readAllLines(ZipUtil.ungzip(path.toFile()).toPath(), StandardCharsets.UTF_8); + return Joiner.on("\n").join(allLines); + } catch (ZipException e) { + if (e.getMessage().contains("Not in GZIP format")) { + throw WxPayException.from(BaseWxPayResult.fromXML(new String(responseBytes, StandardCharsets.UTF_8), + WxPayCommonResult.class)); + } else { + this.log.error("解压zip文件出错", e); + throw new WxPayException("解压zip文件出错"); + } + } + } catch (WxPayException wxPayException) { + throw wxPayException; + } catch (Exception e){ + this.log.error("解析对账单文件时出错", e); + throw new WxPayException("解压zip文件出错"); + } + } + + private WxPayFundFlowResult handleFundFlow(String responseContent) { + WxPayFundFlowResult wxPayFundFlowResult = new WxPayFundFlowResult(); + + String listStr = ""; + String objStr = ""; + + if (StringUtils.isNotBlank(responseContent) && responseContent.contains("资金流水总笔数")) { + listStr = responseContent.substring(0, responseContent.indexOf("资金流水总笔数")); + objStr = responseContent.substring(responseContent.indexOf("资金流水总笔数")); + } + /* + * 记账时间:2018-02-01 04:21:23 微信支付业务单号:50000305742018020103387128253 资金流水单号:1900009231201802015884652186 业务名称:退款 + * 业务类型:退款 收支类型:支出 收支金额(元):0.02 账户结余(元):0.17 资金变更提交申请人:system 备注:缺货 业务凭证号:REF4200000068201801293084726067 + * 参考以上格式进行取值 + */ + List wxPayFundFlowBaseResultList = new LinkedList<>(); + // 去空格 + String newStr = listStr.replaceAll(",", " "); + // 数据分组 + String[] tempStr = newStr.split("`"); + // 分组标题 + String[] t = tempStr[0].split(" "); + // 计算循环次数 + int j = tempStr.length / t.length; + // 纪录数组下标 + int k = 1; + for (int i = 0; i < j; i++) { + WxPayFundFlowBaseResult wxPayFundFlowBaseResult = new WxPayFundFlowBaseResult(); + + wxPayFundFlowBaseResult.setBillingTime(tempStr[k].trim()); + wxPayFundFlowBaseResult.setBizTransactionId(tempStr[k + 1].trim()); + wxPayFundFlowBaseResult.setFundFlowId(tempStr[k + 2].trim()); + wxPayFundFlowBaseResult.setBizName(tempStr[k + 3].trim()); + wxPayFundFlowBaseResult.setBizType(tempStr[k + 4].trim()); + wxPayFundFlowBaseResult.setFinancialType(tempStr[k + 5].trim()); + wxPayFundFlowBaseResult.setFinancialFee(tempStr[k + 6].trim()); + wxPayFundFlowBaseResult.setAccountBalance(tempStr[k + 7].trim()); + wxPayFundFlowBaseResult.setFundApplicant(tempStr[k + 8].trim()); + wxPayFundFlowBaseResult.setMemo(tempStr[k + 9].trim()); + wxPayFundFlowBaseResult.setBizVoucherId(tempStr[k + 10].trim()); + + wxPayFundFlowBaseResultList.add(wxPayFundFlowBaseResult); + k += t.length; + } + wxPayFundFlowResult.setWxPayFundFlowBaseResultList(wxPayFundFlowBaseResultList); + + /* + * 资金流水总笔数,收入笔数,收入金额,支出笔数,支出金额 `20.0,`17.0,`0.35,`3.0,`0.18 + * 参考以上格式进行取值 + */ + String totalStr = objStr.replaceAll(",", " "); + String[] totalTempStr = totalStr.split("`"); + wxPayFundFlowResult.setTotalRecord(totalTempStr[1]); + wxPayFundFlowResult.setIncomeRecord(totalTempStr[2]); + wxPayFundFlowResult.setIncomeAmount(totalTempStr[3]); + wxPayFundFlowResult.setExpenditureRecord(totalTempStr[4]); + wxPayFundFlowResult.setExpenditureAmount(totalTempStr[5]); + + return wxPayFundFlowResult; + + } + @Override public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayException { request.checkAndSign(this.getConfig()); diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java index 15cfdd82..8e01429e 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java @@ -11,6 +11,7 @@ import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType; +import com.github.binarywang.wxpay.constant.WxPayConstants.AccountType; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.testbase.ApiTestModule; @@ -165,6 +166,31 @@ public class BaseWxPayServiceImplTest { this.payService.downloadBill("", "", "", null); } + @DataProvider + public Object[][] fundFlowData() { + return new Object[][]{ + {"20180819", AccountType.BASIC, TarType.GZIP}, + {"20180819", AccountType.OPERATION, TarType.GZIP}, + {"20180819", AccountType.FEES, TarType.GZIP}, + {"20180819", AccountType.BASIC, null}, + {"20180819", AccountType.OPERATION, null}, + {"20180819", AccountType.FEES, null} + }; + } + + @Test(dataProvider = "fundFlowData") + public void testDownloadFundFlow(String billDate, String accountType, String tarType) throws Exception { + WxPayFundFlowResult fundFlowResult = this.payService.downloadFundFlow(billDate, accountType, tarType); + assertThat(fundFlowResult).isNotNull(); + this.logger.info(fundFlowResult.toString()); + } + + @Test(expectedExceptions = WxPayException.class) + public void testDownloadFundFlow_withNoParams() throws Exception { + //必填字段为空时,抛出异常 + this.payService.downloadFundFlow("", "", null); + } + @Test public void testReport() throws Exception { WxPayReportRequest request = new WxPayReportRequest();