🆕 #1885 【微信支付】电商收付通增加资金账单下载的接口

This commit is contained in:
f00lish 2020-11-24 09:05:07 +08:00 committed by GitHub
parent 57ab245093
commit b4da5c9fe4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 285 additions and 24 deletions

View File

@ -0,0 +1,79 @@
package com.github.binarywang.wxpay.bean.ecommerce;
import com.google.gson.annotations.SerializedName;
import lombok.*;
/**
* 资金账单请求
* @author: f00lish
* @date: 2020/09/28
*/
@Data
@Builder
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class FundBillRequest {
/**
* <pre>
* 字段名账单日期
* 变量名bill_date
* 是否必填
* 类型string10
* 描述
* 格式YYYY-MM-DD
* 仅支持三个月内的账单下载申请
* 示例值2019-06-11
* </pre>
*/
@SerializedName(value = "bill_date")
private String billDate;
/**
* <pre>
* 字段名资金账户类型
* 变量名account_type
* 是否必填
* 类型string32
* 描述
* 枚举值
* ALL所有账户
* 示例值ALL
* </pre>
*/
@SerializedName(value = "account_type")
private String accountType;
/**
* <pre>
* 字段名压缩类型
* 变量名tar_type
* 是否必填
* 类型string32
* 描述
* 不填则以不压缩的方式返回数据流
* 枚举值
* GZIP返回格式为.gzip的压缩包账单
* 示例值GZIP
* </pre>
*/
@SerializedName(value = "tar_type")
private String tarType;
/**
* <pre>
* 字段名加密算法
* 变量名algorithm
* 是否必填
* 类型string32
* 描述
* 枚举值
* AEAD_AES_256_GCMAEAD_AES_256_GCM加密算法
* 示例值AEAD_AES_256_GCM
* </pre>
*/
@SerializedName(value = "algorithm")
private String algorithm;
}

View File

@ -0,0 +1,139 @@
package com.github.binarywang.wxpay.bean.ecommerce;
import com.google.gson.annotations.SerializedName;
import lombok.*;
/**
* 资金账单结果
* @author: f00lish
* @date: 2020/09/28
*/
@Data
@Builder
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class FundBillResult {
/**
* <pre>
* 字段名下载信息总数
* 变量名download_bill_count
* 是否必填
* 类型int
* 描述
* 下载信息总数
* 示例值1
* </pre>
*/
@SerializedName(value = "download_bill_count")
private int downloadBillCount;
/**
* <pre>
* 字段名下载信息明细
* 变量名download_bill_list
* 是否必填
* 类型array
* 描述
* 下载信息明细
* </pre>
*/
@SerializedName(value = "download_bill_list")
private FundBill[] downloadBillList;
@Data
public static class FundBill {
/**
* <pre>
* 字段名账单文件序号
* 变量名bill_sequence
* 是否必填
* 类型int
* 描述
* 商户将多个文件按账单文件序号的顺序合并为完整的资金账单文件起始值为1
* 示例值1
* </pre>
*/
@SerializedName(value = "bill_sequence")
private String billSequence;
/**
* <pre>
* 字段名哈希类型
* 变量名hash_type
* 是否必填
* 类型string32
* 描述
* 枚举值
* SHA1SHA1值
* 示例值SHA1
* </pre>
*/
@SerializedName(value = "hash_type")
private String hashType;
/**
* <pre>
* 字段名哈希值
* 变量名hash_value
* 是否必填
* 类型string1024
* 描述
* 原始账单gzip需要解压缩的摘要值用于校验文件的完整性
* 示例值79bb0f45fc4c42234a918000b2668d689e2bde04
* </pre>
*/
@SerializedName(value = "hash_value")
private String hashValue;
/**
* <pre>
* 字段名账单下载地址
* 变量名download_url
* 是否必填
* 类型string2048
* 描述
* 供下一步请求账单文件的下载地址该地址30s内有效
* 示例值https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx
* </pre>
*/
@SerializedName(value = "download_url")
private String downloadUrl;
/**
* <pre>
* 字段名加密密钥
* 变量名encrypt_key
* 是否必填
* 类型string512
* 描述
* 加密账单文件使用的加密密钥密钥用商户证书的公钥进行加密然后进行Base64编码
* 示例值YpkbxSne+mDwyXq//xYPmtr9eQ5LsH7zLMZSs+GSEcY4wjhlsfioS4n9X6q1ZBL0wM1v5qd7KhWuj0rFJ4N1FidP7Q8KDy25QDTt46wiKnsPKSCAXWRFNw1D2JmJBqZsc9y5g0DupONWKYB2GfRigRDEBVszj67uOIILPdxOKX1w3N4jvu0U9IFanJa7ldm70KVvYrMWVgQFDPbgjh1gVDbuTAjmPN88AobLdkiegnBUS2woDZW+PfhPo13kweOiR3h1gXIKRlnKnN3Jkkwpna/AFFijXrFphO3voSuiV0CfptfzTtcae4X3DYG3RSroKqmpa+5tuy2aU2VJUSIuFQ==
* </pre>
*/
@SerializedName(value = "encrypt_key")
private String encryptKey;
/**
* <pre>
* 字段名随机字符串
* 变量名nonce
* 是否必填
* 类型string16
* 描述
* 加密账单文件使用的随机字符串
* 示例值a8607ef79034c49c
* </pre>
*/
@SerializedName(value = "nonce")
private String nonce;
}
}

View File

@ -4,7 +4,7 @@ import com.google.gson.annotations.SerializedName;
import lombok.*;
/**
* 账单请求
* 交易账单请求
* @author: f00lish
* @date: 2020/09/28
*/
@ -13,7 +13,7 @@ import lombok.*;
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class BillRequest {
public class TradeBillRequest {
/**
* <pre>

View File

@ -4,7 +4,7 @@ import com.google.gson.annotations.SerializedName;
import lombok.*;
/**
* 账单结果
* 交易账单结果
* @author: f00lish
* @date: 2020/09/28
*/
@ -13,7 +13,7 @@ import lombok.*;
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class BillResult {
public class TradeBillResult {
/**
* <pre>

View File

@ -10,17 +10,16 @@ import lombok.Getter;
*/
@Getter
@AllArgsConstructor
public enum BillTypeEnum {
public enum FundBillTypeEnum {
/**
* 交易账单
*/
TRADE_BILL("%s/v3/bill/tradebill?%s"),
/**
* 资金账单
*/
FUND_FLOW_BILL("%s/v3/bill/fundflowbill?%s");
FUND_FLOW_BILL("%s/v3/bill/fundflowbill?%s"),
/**
* 二级商户资金账单
*/
SUB_FUND_FLOW_BILL("%s/v3/ecommerce/bill/fundflowbill?%s");
/**
* url

View File

@ -1,7 +1,7 @@
package com.github.binarywang.wxpay.service;
import com.github.binarywang.wxpay.bean.ecommerce.*;
import com.github.binarywang.wxpay.bean.ecommerce.enums.BillTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.FundBillTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
@ -394,12 +394,24 @@ public interface EcommerceService {
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/bill.shtml
* </pre>
*
* @param billType 账单类型
* @param request 二级商户号
* @return 返回数据 return bill result
* @param request 请求信息
* @return 返回数据 return trade bill result
* @throws WxPayException the wx pay exception
*/
BillResult applyBill(BillTypeEnum billType, BillRequest request) throws WxPayException;
TradeBillResult applyBill(TradeBillRequest request) throws WxPayException;
/**
* <pre>
* 申请资金账单API
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/bill/chapter3_2.shtml
* </pre>
*
* @param billType 账单类型
* @param request 请求信息
* @return 返回数据 return fund bill result
* @throws WxPayException the wx pay exception
*/
FundBillResult applyFundBill(FundBillTypeEnum billType, FundBillRequest request) throws WxPayException;
/**
* <pre>

View File

@ -1,7 +1,7 @@
package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.ecommerce.*;
import com.github.binarywang.wxpay.bean.ecommerce.enums.BillTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.FundBillTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
@ -295,10 +295,17 @@ public class EcommerceServiceImpl implements EcommerceService {
}
@Override
public BillResult applyBill(BillTypeEnum billType, BillRequest request) throws WxPayException {
public TradeBillResult applyBill(TradeBillRequest request) throws WxPayException {
String url = String.format("%s/v3/bill/tradebill?%s", this.payService.getPayBaseUrl(), this.parseURLPair(request));
String response = this.payService.getV3(URI.create(url));
return GSON.fromJson(response, TradeBillResult.class);
}
@Override
public FundBillResult applyFundBill(FundBillTypeEnum billType, FundBillRequest request) throws WxPayException {
String url = String.format(billType.getUrl(), this.payService.getPayBaseUrl(), this.parseURLPair(request));
String response = this.payService.getV3(URI.create(url));
return GSON.fromJson(response, BillResult.class);
return GSON.fromJson(response, FundBillResult.class);
}
@Override

View File

@ -4,6 +4,11 @@ import com.google.common.base.CharMatcher;
import com.google.common.io.BaseEncoding;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
@ -13,11 +18,6 @@ import java.util.Base64;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AesUtils {
@ -32,6 +32,31 @@ public class AesUtils {
this.aesKey = key;
}
public static byte[] decryptToByte(byte[] nonce, byte[] cipherData, byte[] key)
throws GeneralSecurityException {
return decryptToByte(null, nonce, cipherData, key);
}
public static byte[] decryptToByte(byte[] associatedData, byte[] nonce, byte[] cipherData, byte[] key)
throws GeneralSecurityException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, spec);
if (associatedData != null) {
cipher.updateAAD(associatedData);
}
return cipher.doFinal(cipherData);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
throws GeneralSecurityException, IOException {
try {