PAYATHON 2026

支付宝退款接口出现 javax.net.ssl.SSLException 异常如何解决?

支付小周

结论

javax.net.ssl.SSLException: Connection reset 表示 HTTPS 连接在建立或传输时,被对端或中间网络设备强制断开。此时,请求通常还没有进入支付宝的退款业务处理阶段,所以这一般不是退款参数错误,也不是支付宝返回的业务错误码。

排查时,应重点检查网络连通性、TLS 协议、JDK 证书、代理或防火墙、支付宝网关地址,以及 SDK 和 JDK 版本。

常见原因

1. 网络链路被中断

服务器与支付宝网关之间可能经过防火墙、代理、NAT 网关或其他安全设备。如果这些设备拦截 HTTPS、触发连接超时或主动重置连接,Java 就会收到 Connection reset。

如果异常偶尔出现,还需检查网络抖动、连接池复用失效、代理空闲连接超时等问题。

2. TLS 协议或加密套件不兼容

较旧的 JDK 可能默认使用已经不适用的 TLS 协议,也可能缺少服务端支持的加密套件,导致 TLS 握手失败。

具体要求应以当前支付宝开放平台文档和所用 SDK 的兼容性说明为准。仅凭这条异常,无法断定必须使用某个固定的 TLS 版本。

3. JDK 信任库异常

如果 JDK 的 cacerts 文件过旧、损坏或被替换,Java 可能无法正确验证 HTTPS 证书。证书问题更常见的异常是 SSLHandshakeException,但某些代理或安全设备也会直接重置连接。

4. 网关地址或网络配置错误

生产环境和沙箱环境使用不同的网关。地址配置错误、域名解析异常、直接使用 IP 地址,或者通过不兼容的代理访问,都可能引发 SSL 连接异常。

网关应使用支付宝文档提供的完整 HTTPS 地址,不要用解析得到的 IP 替换域名。

5. SDK、HTTP 客户端或连接池版本过旧

旧版支付宝 Java SDK、旧 HTTP 组件或不合理的连接池配置,可能在新 JDK、代理环境或长连接复用场景中出现兼容性问题。

排查步骤

1. 获取完整的异常堆栈

不要只记录最外层的 AlipayApiException。应保留完整堆栈,并检查其中是否包含以下异常:

  • SSLHandshakeException
  • SocketTimeoutException
  • ConnectException
  • PKIX path building failed
  • handshake_failure
  • protocol_version
  • Broken pipe

这些信息有助于判断问题来自证书、协议、超时还是网络重置。

try {
    AlipayTradeRefundResponse response = alipayClient.execute(request);
    System.out.println(response.getBody());
} catch (AlipayApiException e) {
    e.printStackTrace();

    Throwable cause = e;
    while (cause != null) {
        System.err.println(
            cause.getClass().getName() + ": " + cause.getMessage()
        );
        cause = cause.getCause();
    }
}

生产环境中的异常应写入受控日志,同时避免记录私钥、应用密钥、完整用户信息等敏感内容。

2. 从应用所在服务器测试 HTTPS

测试必须在实际运行 Java 应用的服务器或容器内进行,不能只在开发电脑上执行:

curl -Iv https://openapi.alipay.com/gateway.do

如果使用沙箱,请替换为当前支付宝开放平台文档中提供的沙箱网关。

还可以通过以下命令检查 TLS 握手:

openssl s_client -connect openapi.alipay.com:443 \
  -servername openapi.alipay.com

可以根据测试结果缩小排查范围:

  • curl 和 openssl 都失败时,优先检查网络、防火墙、代理、DNS 和出口策略。
  • 命令正常,但 Java 请求失败时,重点检查 JDK、信任库、TLS 配置和 SDK。
  • 只有部分服务器失败时,对比异常节点与正常节点的 JDK、DNS、代理和安全策略。
  • 仅偶尔失败时,检查网络抖动、连接池、超时设置,以及中间设备的空闲连接回收策略。

3. 检查 DNS 和系统时间

先确认域名解析是否正常:

nslookup openapi.alipay.com

同时检查服务器的时间和时区。系统时间偏差过大,可能影响证书有效期校验。

不要在配置中固定支付宝网关的 IP 地址。服务端地址可能随负载均衡或调度系统发生变化。

4. 检查 JDK 和证书库

确认应用实际使用的 Java 版本:

java -version

如果应用运行在容器、应用服务器或进程管理器中,还需确认进程实际使用的 java 路径,以免检查了另一套 JDK。

检查信任库中是否有有效的根证书:

keytool -list \
  -keystore "$JAVA_HOME/lib/security/cacerts" \
  -storepass changeit

不同 JDK 发行版的信任库位置可能不同。如果使用了自定义 trustStore,还应检查以下启动参数:

-Djavax.net.ssl.trustStore=/path/to/truststore
-Djavax.net.ssl.trustStorePassword=******

不要通过关闭证书校验或信任所有证书来处理此类问题。这会破坏 HTTPS 的身份验证能力,并带来中间人攻击风险。

5. 检查代理、防火墙和出口白名单

如果服务器通过代理访问公网,应逐项核对:

  • JVM 是否设置了 https.proxyHost 和 https.proxyPort
  • 代理是否支持 HTTPS CONNECT
  • 防火墙是否允许访问支付宝网关的 443 端口
  • 安全设备是否对 HTTPS 进行了流量解密或证书替换
  • 容器、宿主机和云网络的出口规则是否一致
  • 是否仍在使用按域名或 IP 配置的过期白名单

如果企业代理会解密 HTTPS 流量,Java 还需要信任该代理签发证书所使用的企业 CA。是否允许这样配置,应由所在组织的安全策略决定。

6. 检查 SDK 和网关配置

建议使用与当前 JDK 兼容的支付宝官方 Java SDK,并检查:

  • 网关地址是否正确
  • 是否混用了生产环境和沙箱环境
  • 请求是否通过 HTTPS 发送
  • SDK 版本是否过旧
  • 项目中是否同时存在多个相互冲突的 HTTP、TLS 或支付宝 SDK 依赖

升级前应阅读对应版本的变更说明,并先在测试环境验证,不要直接替换生产环境中的依赖。

7. 临时开启 TLS 调试日志

如果仍无法确定原因,可以在测试环境中开启 Java SSL 调试:

java -Djavax.net.debug=ssl,handshake ...

如果应用由容器或启动脚本管理,可以将该参数加入 JVM 启动选项。查看日志时,重点关注:

  • 客户端实际发送的 TLS 版本
  • 握手在哪个阶段中断
  • 证书链是否完整
  • 是否出现 fatal alert
  • 连接是否在收到服务端证书前就被重置

这类日志内容很多,也可能包含服务器证书、域名和网络信息,不适合长期在生产环境中开启。

TLS 配置示例

如果已经确认旧环境存在 TLS 协议协商问题,可以在充分测试后显式启用 TLS 1.2:

public class Application {
    public static void main(String[] args) {
        System.setProperty("https.protocols", "TLSv1.2");

        // 初始化 AlipayClient 并发起请求
    }
}

也可以使用 JVM 参数:

-Dhttps.protocols=TLSv1.2

这种配置并不能解决所有 Connection reset。如果问题出在防火墙、代理、证书库或网络中断,强制指定 TLS 版本不会生效。较新的 JDK 通常可以自动协商合适的 TLS 版本,因此应优先使用仍受支持并及时更新的 JDK。

是否可以重试

Connection reset 是通信层异常。仅凭这条异常,客户端无法确认支付宝是否已经收到并处理请求。退款涉及资金操作,不能无限重试。

建议按以下方式处理:

  1. 使用稳定且唯一的退款请求号,例如 out_request_no。
  2. 发生超时或连接中断后,先查询退款结果或交易状态。
  3. 确认请求未被处理后,再使用相同的业务标识进行有限次数重试。
  4. 使用退避策略,避免立即进行高频重试。
  5. 记录订单号、退款请求号、请求时间和支付宝返回内容,方便后续对账。

下面是退避逻辑示例:

int maxAttempts = 3;

for (int attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
        AlipayTradeRefundResponse response = alipayClient.execute(request);

        if (response.isSuccess()) {
            break;
        }

        // 已收到明确的业务响应,应按业务错误码处理,
        // 不应一律作为网络异常重试。
        System.err.println(response.getBody());
        break;
    } catch (AlipayApiException e) {
        if (attempt == maxAttempts) {
            throw e;
        }

        // 实际重试前应查询退款结果,避免重复资金操作。
        try {
            Thread.sleep(1000L * attempt);
        } catch (InterruptedException interrupted) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException("Retry interrupted", interrupted);
        }
    }
}

这个示例只演示有限重试和退避结构。正式实现必须结合退款结果查询、幂等设计和业务状态机,不能捕获所有 AlipayApiException 后直接重试。

注意事项

  • 不要关闭 HTTPS 证书校验,也不要采用信任所有证书的实现。
  • 不要用固定 IP 替换支付宝域名。
  • 不要因为通信异常就创建新的退款请求号,以免重复退款。
  • 不要只检查开发机的网络,应在每个实际应用节点上验证。
  • 如果所有本地检查都正常,但问题仍持续出现,应整理完整异常堆栈、发生时间、出口 IP、网关环境和请求标识,并通过支付宝开放平台的官方支持渠道进一步核查。提交材料时不要包含应用私钥或用户敏感数据。

备注:内容仅供参考。