From eb987323e1235de746769582e97cae7a499f9e67 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Thu, 24 Jul 2025 15:35:44 +0800 Subject: [PATCH 1/4] =?UTF-8?q?:art:=20=E4=BF=AE=E5=A4=8D=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E5=8F=98=E9=87=8F=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java | 4 ++-- .../wxpay/bean/notify/WxPayRefundNotifyResultTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java index ae86b8c854..8615a2e461 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java @@ -273,7 +273,7 @@ public String toString() { * */ @XStreamAlias("refund_recv_accout") - private String refundRecvAccout; + private String refundRecvAccount; /** *
@@ -324,7 +324,7 @@ public void loadXML(Document d) {
       settlementRefundFee = readXmlInteger(d, "settlement_refund_fee");
       refundStatus = readXmlString(d, "refund_status");
       successTime = readXmlString(d, "success_time");
-      refundRecvAccout = readXmlString(d, "refund_recv_accout");
+      refundRecvAccount = readXmlString(d, "refund_recv_accout");
       refundAccount = readXmlString(d, "refund_account");
       refundRequestSource = readXmlString(d, "refund_request_source");
     }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
index 963afb2618..e7a22ee6cd 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
@@ -119,7 +119,7 @@ public void testFromXMLFastMode() throws WxPayException {
       refundNotifyResult.loadReqInfo(xmlDecryptedReqInfo);
       assertEquals(refundNotifyResult.getReqInfo().getRefundFee().intValue(), 15);
       assertEquals(refundNotifyResult.getReqInfo().getRefundStatus(), "SUCCESS");
-      assertEquals(refundNotifyResult.getReqInfo().getRefundRecvAccout(), "用户零钱");
+      assertEquals(refundNotifyResult.getReqInfo().getRefundRecvAccount(), "用户零钱");
       System.out.println(refundNotifyResult);
     } finally {
       XmlConfig.fastMode = false;

From 88a6888d2f1943e372e935333b265cf96094cce0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 24 Jul 2025 07:42:16 +0000
Subject: [PATCH 2/4] Initial plan


From c120cdd92683ebdaba9743fc2910aa54a5ec20ce Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 24 Jul 2025 07:53:42 +0000
Subject: [PATCH 3/4] Implement HTTP connection pooling for
 WxPayServiceApacheHttpImpl

Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
---
 .../binarywang/wxpay/config/WxPayConfig.java  | 56 +++++++++++++++++
 .../impl/WxPayServiceApacheHttpImpl.java      | 55 +++++++++++++++--
 ...rviceApacheHttpImplConnectionPoolTest.java | 61 +++++++++++++++++++
 3 files changed, 166 insertions(+), 6 deletions(-)
 create mode 100644 weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImplConnectionPoolTest.java

diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
index 96b6f1dd8f..ffcfd3be8e 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
@@ -15,6 +15,8 @@
 import org.apache.commons.lang3.RegExUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 import org.apache.http.ssl.SSLContexts;
 
 import javax.net.ssl.SSLContext;
@@ -185,11 +187,27 @@ public class WxPayConfig {
 
 
   private CloseableHttpClient apiV3HttpClient;
+  
+  /**
+   * 用于普通支付接口的可复用HttpClient,使用连接池
+   */
+  private CloseableHttpClient httpClient;
+  
   /**
    * 支持扩展httpClientBuilder
    */
   private HttpClientBuilderCustomizer httpClientBuilderCustomizer;
   private HttpClientBuilderCustomizer apiV3HttpClientBuilderCustomizer;
+  
+  /**
+   * HTTP连接池最大连接数,默认20
+   */
+  private int maxConnTotal = 20;
+  
+  /**
+   * HTTP连接池每个路由的最大连接数,默认10
+   */
+  private int maxConnPerRoute = 10;
   /**
    * 私钥信息
    */
@@ -498,4 +516,42 @@ private Object[] p12ToPem() {
     return null;
 
   }
+
+  /**
+   * 初始化使用连接池的HttpClient
+   * 
+   * @return CloseableHttpClient
+   * @throws WxPayException 初始化异常
+   */
+  public CloseableHttpClient initHttpClient() throws WxPayException {
+    if (this.httpClient != null) {
+      return this.httpClient;
+    }
+    
+    // 创建连接池管理器
+    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+    connectionManager.setMaxTotal(this.maxConnTotal);
+    connectionManager.setDefaultMaxPerRoute(this.maxConnPerRoute);
+    
+    // 创建HttpClient构建器
+    org.apache.http.impl.client.HttpClientBuilder httpClientBuilder = HttpClients.custom()
+        .setConnectionManager(connectionManager);
+    
+    // 提供自定义httpClientBuilder的能力
+    Optional.ofNullable(httpClientBuilderCustomizer).ifPresent(e -> {
+      e.customize(httpClientBuilder);
+    });
+    
+    this.httpClient = httpClientBuilder.build();
+    return this.httpClient;
+  }
+
+  /**
+   * 获取用于普通支付接口的HttpClient
+   * 
+   * @return CloseableHttpClient
+   */
+  public CloseableHttpClient getHttpClient() {
+    return httpClient;
+  }
 }
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 130a47a49f..011b99c1ca 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
@@ -52,9 +52,19 @@ public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
   @Override
   public 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()) {
+      CloseableHttpClient httpClient = this.createHttpClient(useKey);
+      
+      // 对于使用连接池的客户端,不需要关闭,对于新创建的SSL客户端,需要关闭
+      if (useKey) {
+        try (CloseableHttpClient clientToClose = httpClient) {
+          final byte[] bytes = clientToClose.execute(httpPost, ByteArrayResponseHandler.INSTANCE);
+          final String responseData = Base64.getEncoder().encodeToString(bytes);
+          this.logRequestAndResponse(url, requestStr, responseData);
+          wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
+          return bytes;
+        }
+      } else {
         final byte[] bytes = httpClient.execute(httpPost, ByteArrayResponseHandler.INSTANCE);
         final String responseData = Base64.getEncoder().encodeToString(bytes);
         this.logRequestAndResponse(url, requestStr, responseData);
@@ -71,9 +81,24 @@ public byte[] postForBytes(String url, String requestStr, boolean useKey) throws
   @Override
   public String post(String url, String requestStr, boolean useKey) throws WxPayException {
     try {
-      HttpClientBuilder httpClientBuilder = this.createHttpClientBuilder(useKey);
       HttpPost httpPost = this.createHttpPost(url, requestStr);
-      try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
+      CloseableHttpClient httpClient = this.createHttpClient(useKey);
+      
+      // 对于使用连接池的客户端,不需要关闭,对于新创建的SSL客户端,需要关闭
+      if (useKey) {
+        try (CloseableHttpClient clientToClose = httpClient) {
+          try (CloseableHttpResponse response = clientToClose.execute(httpPost)) {
+            String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
+            this.logRequestAndResponse(url, requestStr, responseString);
+            if (this.getConfig().isIfSaveApiData()) {
+              wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
+            }
+            return responseString;
+          } finally {
+            httpPost.releaseConnection();
+          }
+        }
+      } else {
         try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
           String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
           this.logRequestAndResponse(url, requestStr, responseString);
@@ -81,9 +106,9 @@ public String post(String url, String requestStr, boolean useKey) throws WxPayEx
             wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
           }
           return responseString;
+        } finally {
+          httpPost.releaseConnection();
         }
-      } finally {
-        httpPost.releaseConnection();
       }
     } catch (Exception e) {
       this.logError(url, requestStr, e);
@@ -281,6 +306,24 @@ private CloseableHttpClient createApiV3HttpClient() throws WxPayException {
     return apiV3HttpClient;
   }
 
+  CloseableHttpClient createHttpClient(boolean useKey) throws WxPayException {
+    CloseableHttpClient httpClient = this.getConfig().getHttpClient();
+    if (null == httpClient) {
+      // 初始化带连接池的HttpClient
+      this.getConfig().initHttpClient();
+      httpClient = this.getConfig().getHttpClient();
+    }
+    
+    // 对于需要SSL的情况,当前连接池客户端还不支持动态SSL配置
+    // 暂时保持原有行为,为SSL请求创建新客户端
+    if (useKey) {
+      HttpClientBuilder httpClientBuilder = this.createHttpClientBuilder(useKey);
+      return httpClientBuilder.build();
+    }
+    
+    return httpClient;
+  }
+
   private static StringEntity createEntry(String requestStr) {
     return new StringEntity(requestStr, ContentType.create(APPLICATION_JSON, StandardCharsets.UTF_8));
     //return new StringEntity(new String(requestStr.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImplConnectionPoolTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImplConnectionPoolTest.java
new file mode 100644
index 0000000000..373013a6c3
--- /dev/null
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImplConnectionPoolTest.java
@@ -0,0 +1,61 @@
+package com.github.binarywang.wxpay.service.impl;
+
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * 测试WxPayServiceApacheHttpImpl的连接池功能
+ */
+public class WxPayServiceApacheHttpImplConnectionPoolTest {
+
+  @Test
+  public void testHttpClientConnectionPool() throws Exception {
+    WxPayConfig config = new WxPayConfig();
+    config.setAppId("test-app-id");
+    config.setMchId("test-mch-id");
+    config.setMchKey("test-mch-key");
+    
+    // 测试初始化连接池
+    CloseableHttpClient httpClient1 = config.initHttpClient();
+    Assert.assertNotNull(httpClient1, "HttpClient should not be null");
+    
+    // 再次获取,应该返回同一个实例
+    CloseableHttpClient httpClient2 = config.getHttpClient();
+    Assert.assertSame(httpClient1, httpClient2, "Should return the same HttpClient instance");
+    
+    // 验证连接池配置
+    WxPayServiceApacheHttpImpl service = new WxPayServiceApacheHttpImpl();
+    service.setConfig(config);
+    
+    // 测试不使用SSL的情况下应该使用连接池
+    CloseableHttpClient clientForNonSSL = service.createHttpClient(false);
+    Assert.assertSame(httpClient1, clientForNonSSL, "Should use pooled client for non-SSL requests");
+    
+    // 测试使用SSL的情况下应该创建新的客户端(暂时保持原有行为)
+    CloseableHttpClient clientForSSL = service.createHttpClient(true);
+    Assert.assertNotSame(httpClient1, clientForSSL, "Should create new client for SSL requests");
+    
+    // 清理SSL客户端
+    clientForSSL.close();
+  }
+  
+  @Test
+  public void testConnectionPoolConfiguration() throws Exception {
+    WxPayConfig config = new WxPayConfig();
+    config.setAppId("test-app-id");
+    config.setMchId("test-mch-id");
+    config.setMchKey("test-mch-key");
+    config.setMaxConnTotal(50);
+    config.setMaxConnPerRoute(20);
+    
+    CloseableHttpClient httpClient = config.initHttpClient();
+    Assert.assertNotNull(httpClient, "HttpClient should not be null");
+    
+    // 验证配置值是否正确设置
+    Assert.assertEquals(config.getMaxConnTotal(), 50, "Max total connections should be 50");
+    Assert.assertEquals(config.getMaxConnPerRoute(), 20, "Max connections per route should be 20");
+  }
+}
\ No newline at end of file

From cffbb84ef6363b845db87e4f03233e615acdad86 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 24 Jul 2025 08:02:40 +0000
Subject: [PATCH 4/4] Enhance connection pooling with SSL support and
 comprehensive documentation

Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
---
 weixin-java-pay/CONNECTION_POOL.md            | 67 +++++++++++++++
 .../binarywang/wxpay/config/WxPayConfig.java  | 81 ++++++++++++++++++
 .../impl/WxPayServiceApacheHttpImpl.java      | 83 +++++++------------
 .../impl/ConnectionPoolUsageExampleTest.java  | 61 ++++++++++++++
 ...rviceApacheHttpImplConnectionPoolTest.java | 37 +++++++--
 5 files changed, 270 insertions(+), 59 deletions(-)
 create mode 100644 weixin-java-pay/CONNECTION_POOL.md
 create mode 100644 weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ConnectionPoolUsageExampleTest.java

diff --git a/weixin-java-pay/CONNECTION_POOL.md b/weixin-java-pay/CONNECTION_POOL.md
new file mode 100644
index 0000000000..2b2569adcb
--- /dev/null
+++ b/weixin-java-pay/CONNECTION_POOL.md
@@ -0,0 +1,67 @@
+# HTTP连接池功能说明
+
+## 概述
+
+`WxPayServiceApacheHttpImpl` 现在支持HTTP连接池功能,可以显著提高高并发场景下的性能表现。
+
+## 主要改进
+
+1. **连接复用**: 不再为每个请求创建新的HttpClient实例,而是复用连接池中的连接
+2. **性能提升**: 减少连接建立和销毁的开销,提高吞吐量
+3. **资源优化**: 合理控制并发连接数,避免资源浪费
+4. **SSL支持**: 同时支持普通HTTP和SSL连接的连接池
+
+## 配置说明
+
+### 默认配置
+```java
+WxPayConfig config = new WxPayConfig();
+// 默认配置:
+// maxConnTotal = 20        (最大连接数)
+// maxConnPerRoute = 10     (每个路由最大连接数)
+```
+
+### 自定义配置
+```java
+WxPayConfig config = new WxPayConfig();
+config.setMaxConnTotal(50);      // 设置最大连接数
+config.setMaxConnPerRoute(20);   // 设置每个路由最大连接数
+```
+
+## 使用方式
+
+连接池功能是自动启用的,无需额外配置:
+
+```java
+// 1. 配置微信支付
+WxPayConfig config = new WxPayConfig();
+config.setAppId("your-app-id");
+config.setMchId("your-mch-id");
+config.setMchKey("your-mch-key");
+
+// 2. 创建支付服务(连接池自动启用)
+WxPayServiceApacheHttpImpl payService = new WxPayServiceApacheHttpImpl();
+payService.setConfig(config);
+
+// 3. 正常使用,所有HTTP请求都会使用连接池
+WxPayUnifiedOrderResult result = payService.unifiedOrder(request);
+```
+
+## 向后兼容性
+
+- 此功能完全向后兼容,现有代码无需修改
+- 如果不设置连接池参数,将使用默认配置
+- 支持原有的HttpClientBuilderCustomizer自定义功能
+
+## 注意事项
+
+1. 连接池中的HttpClient实例会被复用,不要手动关闭
+2. SSL连接和普通连接使用不同的连接池
+3. 连接池参数建议根据实际并发量调整
+4. 代理配置仍然正常工作
+
+## 性能建议
+
+- 对于高并发应用,建议适当增加`maxConnTotal`和`maxConnPerRoute`
+- 监控连接池使用情况,避免连接数不足导致的阻塞
+- 在容器环境中,注意连接池配置与容器资源限制的平衡
\ No newline at end of file
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
index ffcfd3be8e..01f9cd534f 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
@@ -14,9 +14,16 @@
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.RegExUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.impl.client.BasicCredentialsProvider;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
 import org.apache.http.ssl.SSLContexts;
 
 import javax.net.ssl.SSLContext;
@@ -193,6 +200,11 @@ public class WxPayConfig {
    */
   private CloseableHttpClient httpClient;
   
+  /**
+   * 用于需要SSL证书的支付接口的可复用HttpClient,使用连接池
+   */
+  private CloseableHttpClient sslHttpClient;
+  
   /**
    * 支持扩展httpClientBuilder
    */
@@ -536,6 +548,9 @@ public CloseableHttpClient initHttpClient() throws WxPayException {
     // 创建HttpClient构建器
     org.apache.http.impl.client.HttpClientBuilder httpClientBuilder = HttpClients.custom()
         .setConnectionManager(connectionManager);
+        
+    // 配置代理
+    configureProxy(httpClientBuilder);
     
     // 提供自定义httpClientBuilder的能力
     Optional.ofNullable(httpClientBuilderCustomizer).ifPresent(e -> {
@@ -546,6 +561,63 @@ public CloseableHttpClient initHttpClient() throws WxPayException {
     return this.httpClient;
   }
 
+  /**
+   * 初始化使用连接池且支持SSL的HttpClient
+   * 
+   * @return CloseableHttpClient
+   * @throws WxPayException 初始化异常
+   */
+  public CloseableHttpClient initSslHttpClient() throws WxPayException {
+    if (this.sslHttpClient != null) {
+      return this.sslHttpClient;
+    }
+    
+    // 初始化SSL上下文
+    SSLContext sslContext = this.getSslContext();
+    if (null == sslContext) {
+      sslContext = this.initSSLContext();
+    }
+    
+    // 创建支持SSL的连接池管理器
+    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+    connectionManager.setMaxTotal(this.maxConnTotal);
+    connectionManager.setDefaultMaxPerRoute(this.maxConnPerRoute);
+    
+    // 创建HttpClient构建器,配置SSL
+    org.apache.http.impl.client.HttpClientBuilder httpClientBuilder = HttpClients.custom()
+        .setConnectionManager(connectionManager)
+        .setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier()));
+        
+    // 配置代理
+    configureProxy(httpClientBuilder);
+    
+    // 提供自定义httpClientBuilder的能力
+    Optional.ofNullable(httpClientBuilderCustomizer).ifPresent(e -> {
+      e.customize(httpClientBuilder);
+    });
+    
+    this.sslHttpClient = httpClientBuilder.build();
+    return this.sslHttpClient;
+  }
+
+  /**
+   * 配置HTTP代理
+   */
+  private void configureProxy(org.apache.http.impl.client.HttpClientBuilder httpClientBuilder) {
+    if (StringUtils.isNotBlank(this.getHttpProxyHost()) && this.getHttpProxyPort() > 0) {
+      if (StringUtils.isEmpty(this.getHttpProxyUsername())) {
+        this.setHttpProxyUsername("whatever");
+      }
+
+      // 使用代理服务器 需要用户认证的代理服务器
+      CredentialsProvider provider = new BasicCredentialsProvider();
+      provider.setCredentials(new AuthScope(this.getHttpProxyHost(), this.getHttpProxyPort()),
+        new UsernamePasswordCredentials(this.getHttpProxyUsername(), this.getHttpProxyPassword()));
+      httpClientBuilder.setDefaultCredentialsProvider(provider)
+        .setProxy(new HttpHost(this.getHttpProxyHost(), this.getHttpProxyPort()));
+    }
+  }
+
   /**
    * 获取用于普通支付接口的HttpClient
    * 
@@ -554,4 +626,13 @@ public CloseableHttpClient initHttpClient() throws WxPayException {
   public CloseableHttpClient getHttpClient() {
     return httpClient;
   }
+
+  /**
+   * 获取用于SSL支付接口的HttpClient
+   * 
+   * @return CloseableHttpClient
+   */
+  public CloseableHttpClient getSslHttpClient() {
+    return sslHttpClient;
+  }
 }
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 011b99c1ca..977a2856fe 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
@@ -55,22 +55,12 @@ public byte[] postForBytes(String url, String requestStr, boolean useKey) throws
       HttpPost httpPost = this.createHttpPost(url, requestStr);
       CloseableHttpClient httpClient = this.createHttpClient(useKey);
       
-      // 对于使用连接池的客户端,不需要关闭,对于新创建的SSL客户端,需要关闭
-      if (useKey) {
-        try (CloseableHttpClient clientToClose = httpClient) {
-          final byte[] bytes = clientToClose.execute(httpPost, ByteArrayResponseHandler.INSTANCE);
-          final String responseData = Base64.getEncoder().encodeToString(bytes);
-          this.logRequestAndResponse(url, requestStr, responseData);
-          wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
-          return bytes;
-        }
-      } else {
-        final byte[] bytes = httpClient.execute(httpPost, ByteArrayResponseHandler.INSTANCE);
-        final String responseData = Base64.getEncoder().encodeToString(bytes);
-        this.logRequestAndResponse(url, requestStr, responseData);
-        wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
-        return bytes;
-      }
+      // 使用连接池的客户端,不需要手动关闭
+      final byte[] bytes = httpClient.execute(httpPost, ByteArrayResponseHandler.INSTANCE);
+      final String responseData = Base64.getEncoder().encodeToString(bytes);
+      this.logRequestAndResponse(url, requestStr, responseData);
+      wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
+      return bytes;
     } catch (Exception e) {
       this.logError(url, requestStr, e);
       wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage()));
@@ -84,31 +74,16 @@ public String post(String url, String requestStr, boolean useKey) throws WxPayEx
       HttpPost httpPost = this.createHttpPost(url, requestStr);
       CloseableHttpClient httpClient = this.createHttpClient(useKey);
       
-      // 对于使用连接池的客户端,不需要关闭,对于新创建的SSL客户端,需要关闭
-      if (useKey) {
-        try (CloseableHttpClient clientToClose = httpClient) {
-          try (CloseableHttpResponse response = clientToClose.execute(httpPost)) {
-            String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
-            this.logRequestAndResponse(url, requestStr, responseString);
-            if (this.getConfig().isIfSaveApiData()) {
-              wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
-            }
-            return responseString;
-          } finally {
-            httpPost.releaseConnection();
-          }
-        }
-      } else {
-        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
-          String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
-          this.logRequestAndResponse(url, requestStr, responseString);
-          if (this.getConfig().isIfSaveApiData()) {
-            wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
-          }
-          return responseString;
-        } finally {
-          httpPost.releaseConnection();
+      // 使用连接池的客户端,不需要手动关闭
+      try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
+        String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
+        this.logRequestAndResponse(url, requestStr, responseString);
+        if (this.getConfig().isIfSaveApiData()) {
+          wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
         }
+        return responseString;
+      } finally {
+        httpPost.releaseConnection();
       }
     } catch (Exception e) {
       this.logError(url, requestStr, e);
@@ -307,21 +282,23 @@ private CloseableHttpClient createApiV3HttpClient() throws WxPayException {
   }
 
   CloseableHttpClient createHttpClient(boolean useKey) throws WxPayException {
-    CloseableHttpClient httpClient = this.getConfig().getHttpClient();
-    if (null == httpClient) {
-      // 初始化带连接池的HttpClient
-      this.getConfig().initHttpClient();
-      httpClient = this.getConfig().getHttpClient();
-    }
-    
-    // 对于需要SSL的情况,当前连接池客户端还不支持动态SSL配置
-    // 暂时保持原有行为,为SSL请求创建新客户端
     if (useKey) {
-      HttpClientBuilder httpClientBuilder = this.createHttpClientBuilder(useKey);
-      return httpClientBuilder.build();
+      // 使用SSL连接池客户端
+      CloseableHttpClient sslHttpClient = this.getConfig().getSslHttpClient();
+      if (null == sslHttpClient) {
+        this.getConfig().initSslHttpClient();
+        sslHttpClient = this.getConfig().getSslHttpClient();
+      }
+      return sslHttpClient;
+    } else {
+      // 使用普通连接池客户端
+      CloseableHttpClient httpClient = this.getConfig().getHttpClient();
+      if (null == httpClient) {
+        this.getConfig().initHttpClient();
+        httpClient = this.getConfig().getHttpClient();
+      }
+      return httpClient;
     }
-    
-    return httpClient;
   }
 
   private static StringEntity createEntry(String requestStr) {
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ConnectionPoolUsageExampleTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ConnectionPoolUsageExampleTest.java
new file mode 100644
index 0000000000..143743ffcf
--- /dev/null
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ConnectionPoolUsageExampleTest.java
@@ -0,0 +1,61 @@
+package com.github.binarywang.wxpay.service.impl;
+
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * 演示连接池功能的示例测试
+ */
+public class ConnectionPoolUsageExampleTest {
+
+  @Test
+  public void demonstrateConnectionPoolUsage() throws Exception {
+    // 1. 创建配置并设置连接池参数
+    WxPayConfig config = new WxPayConfig();
+    config.setAppId("wx123456789");
+    config.setMchId("1234567890");
+    config.setMchKey("32位商户密钥32位商户密钥32位商户密钥");
+    
+    // 设置连接池参数(可选,有默认值)
+    config.setMaxConnTotal(50);  // 最大连接数,默认20
+    config.setMaxConnPerRoute(20); // 每个路由最大连接数,默认10
+    
+    // 2. 初始化连接池
+    CloseableHttpClient pooledClient = config.initHttpClient();
+    Assert.assertNotNull(pooledClient);
+    
+    // 3. 创建支付服务实例
+    WxPayServiceApacheHttpImpl payService = new WxPayServiceApacheHttpImpl();
+    payService.setConfig(config);
+    
+    // 4. 现在所有的HTTP请求都会使用连接池
+    // 对于非SSL请求,会复用同一个HttpClient实例
+    CloseableHttpClient client1 = payService.createHttpClient(false);
+    CloseableHttpClient client2 = payService.createHttpClient(false);
+    Assert.assertSame(client1, client2, "非SSL请求应该复用同一个客户端实例");
+    
+    // 对于SSL请求,也会复用同一个SSL HttpClient实例(需要配置证书后)
+    System.out.println("连接池配置成功!");
+    System.out.println("最大连接数:" + config.getMaxConnTotal());
+    System.out.println("每路由最大连接数:" + config.getMaxConnPerRoute());
+  }
+  
+  @Test
+  public void demonstrateDefaultConfiguration() throws Exception {
+    // 使用默认配置的示例
+    WxPayConfig config = new WxPayConfig();
+    config.setAppId("wx123456789");
+    config.setMchId("1234567890");
+    config.setMchKey("32位商户密钥32位商户密钥32位商户密钥");
+    
+    // 不设置连接池参数,使用默认值
+    CloseableHttpClient client = config.initHttpClient();
+    Assert.assertNotNull(client);
+    
+    // 验证默认配置
+    Assert.assertEquals(config.getMaxConnTotal(), 20, "默认最大连接数应该是20");
+    Assert.assertEquals(config.getMaxConnPerRoute(), 10, "默认每路由最大连接数应该是10");
+  }
+}
\ No newline at end of file
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImplConnectionPoolTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImplConnectionPoolTest.java
index 373013a6c3..393d601a69 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImplConnectionPoolTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImplConnectionPoolTest.java
@@ -1,6 +1,7 @@
 package com.github.binarywang.wxpay.service.impl;
 
 import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 import org.testng.Assert;
@@ -33,13 +34,37 @@ public void testHttpClientConnectionPool() throws Exception {
     // 测试不使用SSL的情况下应该使用连接池
     CloseableHttpClient clientForNonSSL = service.createHttpClient(false);
     Assert.assertSame(httpClient1, clientForNonSSL, "Should use pooled client for non-SSL requests");
+  }
+  
+  @Test
+  public void testSslHttpClientConnectionPool() throws Exception {
+    WxPayConfig config = new WxPayConfig();
+    config.setAppId("test-app-id");
+    config.setMchId("test-mch-id");
+    config.setMchKey("test-mch-key");
     
-    // 测试使用SSL的情况下应该创建新的客户端(暂时保持原有行为)
-    CloseableHttpClient clientForSSL = service.createHttpClient(true);
-    Assert.assertNotSame(httpClient1, clientForSSL, "Should create new client for SSL requests");
-    
-    // 清理SSL客户端
-    clientForSSL.close();
+    // 为了测试SSL客户端,我们需要设置一些基本的SSL配置
+    // 注意:在实际使用中需要提供真实的证书
+    try {
+      CloseableHttpClient sslClient1 = config.initSslHttpClient();
+      Assert.assertNotNull(sslClient1, "SSL HttpClient should not be null");
+      
+      CloseableHttpClient sslClient2 = config.getSslHttpClient();
+      Assert.assertSame(sslClient1, sslClient2, "Should return the same SSL HttpClient instance");
+      
+      WxPayServiceApacheHttpImpl service = new WxPayServiceApacheHttpImpl();
+      service.setConfig(config);
+      
+      // 测试使用SSL的情况下应该使用SSL连接池
+      CloseableHttpClient clientForSSL = service.createHttpClient(true);
+      Assert.assertSame(sslClient1, clientForSSL, "Should use pooled SSL client for SSL requests");
+      
+    } catch (WxPayException e) {
+      // SSL初始化失败是预期的,因为我们没有提供真实的证书
+      // 这里主要是测试代码路径是否正确
+      Assert.assertTrue(e.getMessage().contains("证书") || e.getMessage().contains("商户号"), 
+          "Should fail with certificate or merchant ID related error");
+    }
   }
   
   @Test