com.google.inject
guice
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
index 8e9eea95f..7cafd4ab7 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
@@ -36,7 +36,17 @@ public class WxPayConstants {
}
/**
- * 订单类型
+ * 压缩账单的类型
+ */
+ public static class TarType {
+ /**
+ * 固定值:GZIP,返回格式为.gzip的压缩包账单
+ */
+ public static final String GZIP = "GZIP";
+ }
+
+ /**
+ * 账单类型
*/
public static class BillType {
/**
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java
index 0eccb7272..d30ac9b57 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java
@@ -11,21 +11,47 @@ import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
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.constant.WxPayConstants;
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.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;
+import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
+import jodd.io.ZipUtil;
+import jodd.util.Base64;
import org.apache.commons.lang3.StringUtils;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.net.ssl.SSLContext;
import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.*;
+import java.util.zip.ZipException;
import static com.github.binarywang.wxpay.constant.WxPayConstants.QUERY_COMMENT_DATE_FORMAT;
+import static com.github.binarywang.wxpay.constant.WxPayConstants.TarType;
/**
*
@@ -61,7 +87,17 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService {
}
/**
- * 发送post请求
+ * 发送post请求,得到响应字节数组
+ *
+ * @param url 请求地址
+ * @param requestStr 请求信息
+ * @param useKey 是否使用证书
+ * @return 返回请求结果字节数组
+ */
+ protected abstract byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException;
+
+ /**
+ * 发送post请求,得到响应字符串
*
* @param url 请求地址
* @param requestStr 请求信息
@@ -410,6 +446,10 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService {
@Override
public WxPayBillResult downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxPayException {
+ if (!BillType.ALL.equals(billType)) {
+ throw new WxPayException("目前仅支持ALL类型的对账单下载");
+ }
+
WxPayDownloadBillRequest request = new WxPayDownloadBillRequest();
request.setBillType(billType);
request.setBillDate(billDate);
@@ -419,15 +459,52 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService {
request.checkAndSign(this.getConfig(), false);
String url = this.getPayBaseUrl() + "/pay/downloadbill";
- String responseContent = this.post(url, request.toXML(), false);
- if (responseContent.startsWith("<")) {
- throw WxPayException.from(WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class));
+
+ String responseContent;
+ if (TarType.GZIP.equals(tarType)) {
+ responseContent = this.handleGzipBill(url, request.toXML());
+ } else {
+ responseContent = this.post(url, request.toXML(), false);
+ if (responseContent.startsWith("<")) {
+ throw WxPayException.from(WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class));
+ }
}
- return this.handleBillInformation(responseContent);
+ return this.handleBill(billType, responseContent);
}
- private WxPayBillResult handleBillInformation(String responseContent) {
+ private WxPayBillResult handleBill(String billType, String responseContent) {
+ if (!BillType.ALL.equals(billType)) {
+ return null;
+ }
+
+ return this.handleAllBill(responseContent);
+ }
+
+ private String handleGzipBill(String url, String requestStr) throws WxPayException {
+ try {
+ byte[] responseBytes = this.postForBytes(url, requestStr, false);
+ Path tempDirectory = Files.createTempDirectory("bill");
+ Path path = Paths.get(tempDirectory.toString(), System.currentTimeMillis() + ".gzip");
+ Files.write(path, responseBytes);
+ try {
+ List allLines = 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(WxPayBaseResult.fromXML(new String(responseBytes, StandardCharsets.UTF_8),
+ WxPayCommonResult.class));
+ } else {
+ this.log.error("解压zip文件出错", e);
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private WxPayBillResult handleAllBill(String responseContent) {
WxPayBillResult wxPayBillResult = new WxPayBillResult();
String listStr = "";
@@ -444,11 +521,16 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService {
* 参考以上格式进行取值
*/
List wxPayBillBaseResultLst = new LinkedList<>();
- String newStr = listStr.replaceAll(",", " "); // 去空格
- String[] tempStr = newStr.split("`"); // 数据分组
- String[] t = tempStr[0].split(" ");// 分组标题
- int j = tempStr.length / t.length; // 计算循环次数
- int k = 1; // 纪录数组下标
+ // 去空格
+ 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++) {
WxPayBillBaseResult wxPayBillBaseResult = new WxPayBillBaseResult();
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
index 653c1a8d6..dc0a1a06e 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
@@ -2,6 +2,7 @@ package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.WxPayApiData;
import com.github.binarywang.wxpay.exception.WxPayException;
+import jodd.util.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
@@ -19,11 +20,12 @@ import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
+import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
/**
*
- * 微信支付请求实现类,apache httpclient实现
+ * 微信支付请求实现类,apache httpclient实现.
* Created by Binary Wang on 2016/7/28.
*
*
@@ -31,41 +33,35 @@ import java.nio.charset.StandardCharsets;
*/
public class WxPayServiceApacheHttpImpl extends WxPayServiceAbstractImpl {
+ @Override
+ protected byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException {
+ try {
+ HttpClientBuilder httpClientBuilder = createHttpClientBuilder(useKey);
+ HttpPost httpPost = this.createHttpPost(url, requestStr);
+ try (CloseableHttpClient httpclient = httpClientBuilder.build()) {
+ try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
+ final byte[] bytes = EntityUtils.toByteArray(response.getEntity());
+ final String responseData = Base64.encodeToString(bytes);
+ this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseData);
+ wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
+ return bytes;
+ }
+ } finally {
+ httpPost.releaseConnection();
+ }
+ } catch (Exception e) {
+ this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
+ wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage()));
+ throw new WxPayException(e.getMessage(), e);
+ }
+ }
+
@Override
protected String post(String url, String requestStr, boolean useKey) throws WxPayException {
try {
- HttpClientBuilder httpClientBuilder = HttpClients.custom();
- if (useKey) {
- SSLContext sslContext = this.getConfig().getSslContext();
- if (null == sslContext) {
- sslContext = this.getConfig().initSSLContext();
- }
-
- SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
- new String[]{"TLSv1"}, null, new DefaultHostnameVerifier());
- httpClientBuilder.setSSLSocketFactory(sslsf);
- }
-
- HttpPost httpPost = new HttpPost(url);
-
- httpPost.setConfig(RequestConfig.custom()
- .setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout())
- .setConnectTimeout(this.getConfig().getHttpConnectionTimeout())
- .setSocketTimeout(this.getConfig().getHttpTimeout())
- .build());
-
- if (StringUtils.isNotBlank(this.getConfig().getHttpProxyHost())
- && this.getConfig().getHttpProxyPort() > 0) {
- // 使用代理服务器 需要用户认证的代理服务器
- CredentialsProvider provider = new BasicCredentialsProvider();
- provider.setCredentials(
- new AuthScope(this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort()),
- new UsernamePasswordCredentials(this.getConfig().getHttpProxyUsername(), this.getConfig().getHttpProxyPassword()));
- httpClientBuilder.setDefaultCredentialsProvider(provider);
- }
-
+ HttpClientBuilder httpClientBuilder = this.createHttpClientBuilder(useKey);
+ HttpPost httpPost = this.createHttpPost(url, requestStr);
try (CloseableHttpClient httpclient = httpClientBuilder.build()) {
- httpPost.setEntity(new StringEntity(new String(requestStr.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)));
try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString);
@@ -82,4 +78,56 @@ public class WxPayServiceApacheHttpImpl extends WxPayServiceAbstractImpl {
}
}
+ private StringEntity createEntry(String requestStr) {
+ try {
+ return new StringEntity(new String(requestStr.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
+ } catch (UnsupportedEncodingException e) {
+ //cannot happen
+ this.log.error(e.getMessage(),e);
+ return null;
+ }
+ }
+
+ private HttpClientBuilder createHttpClientBuilder(boolean useKey) throws WxPayException {
+ HttpClientBuilder httpClientBuilder = HttpClients.custom();
+ if (useKey) {
+ this.setKey(httpClientBuilder);
+ }
+
+ if (StringUtils.isNotBlank(this.getConfig().getHttpProxyHost())
+ && this.getConfig().getHttpProxyPort() > 0) {
+ // 使用代理服务器 需要用户认证的代理服务器
+ CredentialsProvider provider = new BasicCredentialsProvider();
+ provider.setCredentials(
+ new AuthScope(this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort()),
+ new UsernamePasswordCredentials(this.getConfig().getHttpProxyUsername(), this.getConfig().getHttpProxyPassword()));
+ httpClientBuilder.setDefaultCredentialsProvider(provider);
+ }
+ return httpClientBuilder;
+ }
+
+ private HttpPost createHttpPost(String url, String requestStr) {
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.setEntity(this.createEntry(requestStr));
+
+ httpPost.setConfig(RequestConfig.custom()
+ .setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout())
+ .setConnectTimeout(this.getConfig().getHttpConnectionTimeout())
+ .setSocketTimeout(this.getConfig().getHttpTimeout())
+ .build());
+
+ return httpPost;
+ }
+
+ private void setKey(HttpClientBuilder httpClientBuilder) throws WxPayException {
+ SSLContext sslContext = this.getConfig().getSslContext();
+ if (null == sslContext) {
+ sslContext = this.getConfig().initSSLContext();
+ }
+
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
+ new String[]{"TLSv1"}, null, new DefaultHostnameVerifier());
+ httpClientBuilder.setSSLSocketFactory(sslsf);
+ }
+
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
index b342c5cda..3ac29a926 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
@@ -9,48 +9,40 @@ import jodd.http.ProxyInfo;
import jodd.http.ProxyInfo.ProxyType;
import jodd.http.net.SSLSocketHttpConnectionProvider;
import jodd.http.net.SocketHttpConnectionProvider;
+import jodd.util.Base64;
import org.apache.commons.lang3.StringUtils;
import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
/**
- * 微信支付请求实现类,jodd-http实现
+ * 微信支付请求实现类,jodd-http实现.
* Created by Binary Wang on 2016/7/28.
*
* @author Binary Wang
*/
public class WxPayServiceJoddHttpImpl extends WxPayServiceAbstractImpl {
+ @Override
+ protected byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException {
+ try {
+ HttpRequest request = this.buildHttpRequest(url, requestStr, useKey);
+ byte[] responseBytes = request.send().bodyBytes();
+ final String responseString = Base64.encodeToString(responseBytes);
+ this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseString);
+ wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
+ return responseBytes;
+ } catch (Exception e) {
+ this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
+ wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage()));
+ throw new WxPayException(e.getMessage(), e);
+ }
+ }
+
@Override
protected String post(String url, String requestStr, boolean useKey) throws WxPayException {
try {
- HttpRequest request = HttpRequest
- .post(url)
- .timeout(this.getConfig().getHttpTimeout())
- .connectionTimeout(this.getConfig().getHttpConnectionTimeout())
- .bodyText(requestStr);
-
- if (useKey) {
- SSLContext sslContext = this.getConfig().getSslContext();
- if (null == sslContext) {
- sslContext = this.getConfig().initSSLContext();
- }
- final SSLSocketHttpConnectionProvider provider = new SSLSocketHttpConnectionProvider(sslContext);
- request.withConnectionProvider(provider);
- }
-
- if (StringUtils.isNotBlank(this.getConfig().getHttpProxyHost()) && this.getConfig().getHttpProxyPort() > 0) {
- ProxyInfo httpProxy = new ProxyInfo(ProxyType.HTTP, this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort(),
- this.getConfig().getHttpProxyUsername(), this.getConfig().getHttpProxyPassword());
- HttpConnectionProvider provider = request.connectionProvider();
- if (null == provider) {
- provider = new SocketHttpConnectionProvider();
- }
- provider.useProxy(httpProxy);
- request.withConnectionProvider(provider);
- }
-
+ HttpRequest request = this.buildHttpRequest(url, requestStr, useKey);
String responseString = this.getResponseString(request.send());
this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString);
@@ -63,6 +55,35 @@ public class WxPayServiceJoddHttpImpl extends WxPayServiceAbstractImpl {
}
}
+ private HttpRequest buildHttpRequest(String url, String requestStr, boolean useKey) throws WxPayException {
+ HttpRequest request = HttpRequest
+ .post(url)
+ .timeout(this.getConfig().getHttpTimeout())
+ .connectionTimeout(this.getConfig().getHttpConnectionTimeout())
+ .bodyText(requestStr);
+
+ if (useKey) {
+ SSLContext sslContext = this.getConfig().getSslContext();
+ if (null == sslContext) {
+ sslContext = this.getConfig().initSSLContext();
+ }
+ final SSLSocketHttpConnectionProvider provider = new SSLSocketHttpConnectionProvider(sslContext);
+ request.withConnectionProvider(provider);
+ }
+
+ if (StringUtils.isNotBlank(this.getConfig().getHttpProxyHost()) && this.getConfig().getHttpProxyPort() > 0) {
+ ProxyInfo httpProxy = new ProxyInfo(ProxyType.HTTP, this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort(),
+ this.getConfig().getHttpProxyUsername(), this.getConfig().getHttpProxyPassword());
+ HttpConnectionProvider provider = request.connectionProvider();
+ if (null == provider) {
+ provider = new SocketHttpConnectionProvider();
+ }
+ provider.useProxy(httpProxy);
+ request.withConnectionProvider(provider);
+ }
+ return request;
+ }
+
private String getResponseString(HttpResponse response) throws WxPayException {
try {
this.log.debug("【微信服务器响应头信息】:\n{}", response.toString(false));
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java
index 9c293103b..819cd2d2e 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java
@@ -26,6 +26,8 @@ import java.nio.file.Path;
import java.util.Calendar;
import java.util.Date;
+import static com.github.binarywang.wxpay.constant.WxPayConstants.*;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.testng.Assert.*;
/**
@@ -139,7 +141,13 @@ public class WxPayServiceAbstractImplTest {
@DataProvider
public Object[][] billingData() {
return new Object[][]{
-// {"20170831", BillType.ALL, null, "deviceInfo"},
+ {"20170831", BillType.ALL, TarType.GZIP, "deviceInfo"},
+ {"20170831", BillType.RECHARGE_REFUND, TarType.GZIP, "deviceInfo"},
+ {"20170831", BillType.REFUND, TarType.GZIP, "deviceInfo"},
+ {"20170831", BillType.SUCCESS, TarType.GZIP, "deviceInfo"},
+ {"20170831", BillType.ALL, null, "deviceInfo"},
+ {"20170831", BillType.RECHARGE_REFUND, null, "deviceInfo"},
+ {"20170831", BillType.REFUND, null, "deviceInfo"},
{"20170831", BillType.SUCCESS, null, "deviceInfo"}
};
}
@@ -148,11 +156,11 @@ public class WxPayServiceAbstractImplTest {
public void testDownloadBill(String billDate, String billType,
String tarType, String deviceInfo) throws Exception {
WxPayBillResult billResult = this.payService.downloadBill(billDate, billType, tarType, deviceInfo);
- assertNotNull(billResult);
+ assertThat(billResult).isNotNull();
this.logger.info(billResult.toString());
}
- @Test
+ @Test(expectedExceptions = WxPayException.class)
public void testDownloadBill_withNoParams() throws Exception {
//必填字段为空时,抛出异常
this.payService.downloadBill("", "", "", null);
@@ -247,7 +255,7 @@ public class WxPayServiceAbstractImplTest {
.openid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP4")
.amount(1)
.spbillCreateIp("10.10.10.10")
- .checkName(WxPayConstants.CheckNameOption.NO_CHECK)
+ .checkName(CheckNameOption.NO_CHECK)
.description("描述信息")
.build();