Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

微信收付通增加下载账单接口 #1789

Merged
merged 10 commits into from
Sep 29, 2020
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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 BillRequest {

/**
* <pre>
* 字段名:账单日期
* 变量名:bill_date
* 是否必填:是
* 类型:string(10)
* 描述:
* 格式YYYY-MM-DD
* 仅支持三个月内的账单下载申请。
* 示例值:2019-06-11
* </pre>
*/
@SerializedName(value = "bill_date")
private String billDate;

/**
* <pre>
* 字段名:二级商户号
* 变量名:sub_mchid
* 是否必填:否
* 类型:string(12)
* 描述:
* 1、若商户是直连商户:无需填写该字段。
* 2、若商户是服务商:
* ● 不填则默认返回服务商下的交易或退款数据。
* ● 如需下载某个子商户下的交易或退款数据,则该字段必填。
* 特殊规则:最小字符长度为8
* 注意:仅适用于电商平台 服务商
* 示例值:1900000001
* </pre>
*/
@SerializedName(value = "sub_mchid")
private String subMchid;

/**
* <pre>
* 字段名:账单类型
* 变量名:bill_type
* 是否必填:否
* 类型:string(32)
* 描述:
* 不填则默认是ALL
* 枚举值:
* ALL:返回当日所有订单信息(不含充值退款订单)
* SUCCESS:返回当日成功支付的订单(不含充值退款订单)
* REFUND:返回当日退款订单(不含充值退款订单)
* 示例值:ALL
* </pre>
*/
@SerializedName(value = "bill_type")
private String billType;

/**
* <pre>
* 字段名:压缩类型
* 变量名:tar_type
* 是否必填:否
* 类型:string(32)
* 描述:
* 不填则默认是数据流
* 枚举值:
* GZIP:返回格式为.gzip的压缩包账单
* 示例值:GZIP
* </pre>
*/
@SerializedName(value = "tar_type")
private String tarType;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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 BillResult {

/**
* <pre>
* 字段名:哈希类型
* 变量名:hash_type
* 是否必填:是
* 类型:string(32)
* 描述:
* 原始账单(gzip需要解压缩)的摘要值,用于校验文件的完整性。
* 示例值:SHA1
* </pre>
*/
@SerializedName(value = "hash_type")
private String hashType;

/**
* <pre>
* 字段名:哈希值
* 变量名:hash_value
* 是否必填:是
* 类型:string(1024)
* 描述:
* 原始账单(gzip需要解压缩)的摘要值,用于校验文件的完整性。
* 示例值:79bb0f45fc4c42234a918000b2668d689e2bde04
* </pre>
*/
@SerializedName(value = "hash_value")
private String hashValue;

/**
* <pre>
* 字段名:账单下载地址
* 变量名:download_url
* 是否必填:是
* 类型:string(32)
* 描述:
* 供下一步请求账单文件的下载地址,该地址30s内有效。
* 示例值:https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx
* </pre>
*/
@SerializedName(value = "download_url")
private String downloadUrl;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.github.binarywang.wxpay.bean.ecommerce.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
* 账单类型
* @author: f00lish
* @date: 2020/09/28
*/
@Getter
@AllArgsConstructor
public enum BillTypeEnum {

/**
* 交易账单
*/
TRADE_BILL("%s/v3/bill/tradebill?%s"),
/**
* 资金账单
*/
FUND_FLOW_BILL("%s/v3/bill/fundflowbill?%s");


/**
* url
*/
private final String url;

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
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.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;

import java.io.InputStream;

/**
* <pre>
* 电商收付通相关服务类.
Expand Down Expand Up @@ -360,4 +363,29 @@ public interface EcommerceService {
*/
SettlementResult querySettlement(String subMchid) throws WxPayException;

/**
* <pre>
* 请求账单API
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/bill.shtml
* </pre>
*
* @param billType 账单类型。
* @param request 二级商户号。
* @return 返回数据 return bill result
* @throws WxPayException the wx pay exception
*/
BillResult applyBill(BillTypeEnum billType, BillRequest request) throws WxPayException;

/**
* <pre>
* 下载账单API
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/bill.shtml
* </pre>
*
* @param url 微信返回的账单地址。
* @return 返回数据 return inputStream
* @throws WxPayException the wx pay exception
*/
InputStream downloadBill(String url) throws WxPayException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.apache.http.client.methods.HttpPost;

import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.util.Date;
import java.util.Map;
Expand Down Expand Up @@ -97,6 +98,15 @@ public interface WxPayService {
*/
String getV3(URI url) throws WxPayException;

/**
* 发送下载 V3请求,得到响应流.
*
* @param url 请求地址
* @return 返回请求响应流
* @throws WxPayException the wx pay exception
*/
InputStream downloadV3(URI url) throws WxPayException;

/**
* 获取企业付款服务类.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
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.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.EcommerceService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.AesUtils;
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
import com.google.common.base.CaseFormat;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
import org.apache.commons.beanutils.BeanMap;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

@RequiredArgsConstructor
public class EcommerceServiceImpl implements EcommerceService {
Expand Down Expand Up @@ -273,6 +280,18 @@ public SettlementResult querySettlement(String subMchid) throws WxPayException {
return GSON.fromJson(response, SettlementResult.class);
}

@Override
public BillResult applyBill(BillTypeEnum billType, BillRequest 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);
}

@Override
public InputStream downloadBill(String url) throws WxPayException {
return this.payService.downloadV3(URI.create(url));
}

/**
* 校验通知签名
* @param header 通知头信息
Expand All @@ -287,4 +306,24 @@ private boolean verifyNotifySign(SignatureHeader header, String data) {
return payService.getConfig().getVerifier().verify(header.getSerialNo(),
beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned());
}
}

/**
* 对象拼接到url
* @param o 转换对象
* @return 拼接好的string
*/
private String parseURLPair(Object o) {
Map<Object, Object> map = new BeanMap(o);
Set<Map.Entry<Object, Object>> set = map.entrySet();
Iterator<Map.Entry<Object, Object>> it = set.iterator();
StringBuilder sb = new StringBuilder();
while (it.hasNext()) {
Map.Entry<Object, Object> e = it.next();
if ( !"class".equals(e.getKey()) && e.getValue() != null)
sb.append(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, String.valueOf(e.getKey()))).append("=").append(e.getValue()).append("&");
}
return sb.deleteCharAt(sb.length() - 1).toString();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
Expand Down Expand Up @@ -207,6 +208,31 @@ public String getV3(URI url) throws WxPayException {
}
}

@Override
public InputStream downloadV3(URI url) throws WxPayException {
CloseableHttpClient httpClient = this.createApiV3HttpClient();
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Accept", ContentType.WILDCARD.getMimeType());
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
//v3已经改为通过状态码判断200 204 成功
int statusCode = response.getStatusLine().getStatusCode();
if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) {
this.log.info("\n【请求地址】:{}\n", url);
return response.getEntity().getContent();
} else {
//有错误提示信息返回
String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
JsonObject jsonObject = GsonParser.parse(responseString);
throw new WxPayException(jsonObject.get("message").getAsString());
}
} catch (Exception e) {
this.log.error("\n【请求地址】:{}\n【异常信息】:{}", url, e.getMessage());
throw new WxPayException(e.getMessage(), e);
} finally {
httpGet.releaseConnection();
}
}

private CloseableHttpClient createApiV3HttpClient() throws WxPayException {
CloseableHttpClient apiV3HttpClient = this.getConfig().getApiV3HttpClient();
if (null == apiV3HttpClient) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.apache.http.client.methods.HttpPost;

import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
Expand Down Expand Up @@ -80,6 +81,11 @@ public String getV3(URI url) throws WxPayException {
return null;
}

@Override
public InputStream downloadV3(URI url) throws WxPayException {
return null;
}

private HttpRequest buildHttpRequest(String url, String requestStr, boolean useKey) throws WxPayException {
HttpRequest request = HttpRequest
.post(url)
Expand Down
Loading