mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-10-21 19:17:39 +08:00
🆕 #1789 微信支付电商收付通增加下载账单的接口
This commit is contained in:
@@ -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;
|
||||
|
||||
}
|
@@ -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;
|
||||
|
||||
}
|
@@ -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;
|
||||
|
||||
}
|
@@ -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>
|
||||
* 电商收付通相关服务类.
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
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;
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 获取企业付款服务类.
|
||||
*
|
||||
|
@@ -1,6 +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.SpAccountTypeEnum;
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
@@ -8,15 +9,21 @@ 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 {
|
||||
@@ -273,6 +280,18 @@ public class EcommerceServiceImpl implements EcommerceService {
|
||||
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 通知头信息
|
||||
@@ -287,4 +306,24 @@ public class EcommerceServiceImpl implements EcommerceService {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ import org.apache.http.impl.client.HttpClients;
|
||||
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;
|
||||
@@ -207,6 +208,31 @@ public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
|
@@ -13,6 +13,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
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;
|
||||
@@ -80,6 +81,11 @@ public class WxPayServiceJoddHttpImpl extends BaseWxPayServiceImpl {
|
||||
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)
|
||||
|
@@ -1,15 +1,16 @@
|
||||
package com.github.binarywang.wxpay.v3.auth;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.github.binarywang.wxpay.v3.Validator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
public class WxPayValidator implements Validator {
|
||||
private Verifier verifier;
|
||||
@@ -20,6 +21,9 @@ public class WxPayValidator implements Validator {
|
||||
|
||||
@Override
|
||||
public final boolean validate(CloseableHttpResponse response) throws IOException {
|
||||
if (!ContentType.APPLICATION_JSON.getMimeType().equals(ContentType.parse(String.valueOf(response.getFirstHeader("Content-Type").getValue())).getMimeType())) {
|
||||
return true;
|
||||
}
|
||||
Header serialNo = response.getFirstHeader("Wechatpay-Serial");
|
||||
Header sign = response.getFirstHeader("Wechatpay-Signature");
|
||||
Header timestamp = response.getFirstHeader("Wechatpay-TimeStamp");
|
||||
|
Reference in New Issue
Block a user