有道无术,术尚可求,有术无道,止于术。
申请交易账单
微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址。文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况。
首先在WechatPayNativeApiEnum
枚举类添加申请交易账单接口地址:
TRADE_BILLS("https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=%s&account_type=%s&tar_type=%s", "申请交易账单"),
微信服务类添加申请交易账单逻辑处理方法:
/**
* 申请交易账单API
*
* @param billDate 账单日期 格式yyyy-MM-dd 仅支持三个月内的账单下载申请。 示例值:2019-06-11
* @param billType 账单类型 不填则默认是ALL
* 枚举值:
* ALL:返回当日所有订单信息(不含充值退款订单)
* SUCCESS:返回当日成功支付的订单(不含充值退款订单)
* REFUND:返回当日退款订单(不含充值退款订单)
* @param tarType 压缩类型 不填则默认是数据流 枚举值: GZIP:返回格式为.gzip的压缩包账单 示例值:GZIP
*/
String getTransactionBill(String billDate, String billType, String tarType) throws Exception;
@Override
public String getTransactionBill(String billDate, String billType, String tarType) throws Exception {
// 1. 创建请求对象
HttpGet httpGet = new HttpGet(String.format(WechatPayNativeApiEnum.TRADE_BILLS.getAddress(), billDate, billType, tarType));
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
String bodyAsString = EntityUtils.toString(response.getEntity());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200 || statusCode == 204) {
log.info("申请账单成功, 返回结果:" + bodyAsString);
// 2. 解析获取账单下载地址
Map<String, String> resultMap = objectMapper.readValue(bodyAsString, Map.class);
return resultMap.get("download_url");
} else {
throw new RuntimeException("申请账单异常, 响应码:" + statusCode);
}
}
添加访问接口:
@Operation(summary = "申请交易账单")
@PostMapping("/applyTransactionBill")
public String applyTransactionBill() throws Exception {
return wechatPayService.getTransactionBill("2023-02-03","BASIC","GZIP");
}
访问接口,成功返回下载地址:
申请资金账单
微信支付按天提供微信支付账户的资金流水账单文件,商户可以通过该接口获取账单文件的下载地址。文件内包含该账户资金操作相关的业务单号、收支金额、记账时间等信息,供商户进行核对。
首先在WechatPayNativeApiEnum
枚举类添加申请资金账单接口地址:
FUND_FLOW_BILLS("https://api.mch.weixin.qq.com/v3/bill/fundflowbill?bill_date=%s&bill_type=%s&tar_type=%s", "申请资金账单");
微信服务类添加申请资金账单逻辑处理方法:
/**
* 申请资金账单API
* @param billDate 账单日期 格式yyyy-MM-dd 仅支持三个月内的账单下载申请。 示例值:2019-06-11
* @param accountType 资金账户类型 不填则默认是BASIC 枚举值:BASIC:基本账户 OPERATION:运营账户 FEES:手续费账户
* @param tarType 压缩类型 不填则默认是数据流 枚举值: GZIP:返回格式为.gzip的压缩包账单 示例值:GZIP
*/
String getCapitalBill(String billDate, String accountType, String tarType) throws Exception;
@Override
public String getCapitalBill(String billDate, String accountType, String tarType) throws Exception {
// 1. 创建请求对象
HttpGet httpGet = new HttpGet(String.format(WechatPayNativeApiEnum.FUND_FLOW_BILLS.getAddress(), billDate, accountType, tarType));
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
String bodyAsString = EntityUtils.toString(response.getEntity());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200 || statusCode == 204) {
log.info("申请账单成功, 返回结果:" + bodyAsString);
// 2. 解析获取账单下载地址
Map<String, String> resultMap = objectMapper.readValue(bodyAsString, Map.class);
return resultMap.get("download_url");
} else {
throw new RuntimeException("申请账单异常, 响应码:" + statusCode);
}
}
添加访问接口:
@Operation(summary = "申请资金账单")
@PostMapping("/applyCapitalBill")
public String applyCapitalBill() throws Exception {
return wechatPayService.getCapitalBill("2023-02-03","BASIC","GZIP");
}
访问接口,成功返回下载地址:
下载账单
下载账单API为通用接口,交易/资金账单都可以通过该接口获取到对应的账单。
微信服务类添加下载账单逻辑处理方法:
/**
* 下载账单文件
*
* @param downloadUrl 下载链接
*/
void downloadBill(String downloadUrl) throws Exception;
@Override
public void downloadBill(String downloadUrl) throws Exception {
// 1. 创建请求对象
HttpGet httpGet = new HttpGet(downloadUrl);
httpGet.addHeader("Accept", "application/json");
// 设置一个空的应答签名验证器~ PS:下载账单时,微信没给应答签名,如果还验签会报错
PrivateKey merchantPrivateKey = MchPrivateKeyUtils.loadPrivateKeyByPath(wechatPayProperties.getPrivateKeyPath());
CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(wechatPayProperties.getMerchantId(),
wechatPayProperties.getMerchantSerialNumber(),
merchantPrivateKey)
.withValidator(response -> true)
.build();
// 2. 执行
CloseableHttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200 || statusCode == 204) {
// 3. 本地存放
byte[] bytes = EntityUtils.toByteArray(response.getEntity());
String path = "D:\\Users\\Downloads\\";
fileToBytes(bytes,path,"aaa.zip");
log.info("下载账单成功");
} else {
throw new RuntimeException("下载账单异常, 响应码 : " + statusCode);
}
}
/**
* 将Byte数组转换成文件
* @param bytes byte数组
* @param filePath 文件路径 如 D:\\Users\\Downloads\\
* @param fileName 文件名
*/
public static void fileToBytes(byte[] bytes, String filePath, String fileName) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
try {
file = new File(filePath + fileName);
if (!file.getParentFile().exists()){
//文件夹不存在 生成
file.getParentFile().mkdirs();
}
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
添加访问接口:
@Operation(summary = "下载账单")
@GetMapping("/downloadBill")
public R<?> downloadBill(String downloadUrl) throws Exception {
wechatPayService.downloadBill(downloadUrl);
return R.success() ;
}
先访问申请账号接口,然后访问下载账单接口,查看账单文件,实际开发时,可以转为更方便查看的文件,比如 Execl
。