Skip to content

修复公钥模式下平台证书自动更新导致的初始化失败问题#3854

Merged
binarywang merged 6 commits intodevelopfrom
copilot/fix-platform-certificate-issue
Mar 3, 2026
Merged

修复公钥模式下平台证书自动更新导致的初始化失败问题#3854
binarywang merged 6 commits intodevelopfrom
copilot/fix-platform-certificate-issue

Conversation

Copy link
Contributor

Copilot AI commented Jan 19, 2026

修复计划

  • 分析问题:用户在配置公钥模式并设置 strictlyNeedWechatPaySerial=true 时,AutoUpdateCertificatesVerifier 在构造函数中尝试下载平台证书导致 404 错误
  • 修改 AutoUpdateCertificatesVerifier 构造函数:延迟证书下载,构造时失败不抛出致命异常
  • 修改 verify 方法:当没有有效证书时返回 false 而不是抛出异常
  • 修改 getValidCertificate 方法:抛出更友好的异常提示
  • 修改 getWechatPaySerial 方法:增加异常处理,避免获取证书序列号失败时中断请求
  • 编写单元测试:验证公钥模式下的证书更新行为
  • 重构测试代码:将重复的测试数据提取到 @BeforeMethod 中,提高代码可维护性
  • 运行代码风格检查:通过
  • 运行构建验证:通过

问题说明

用户在使用 4.7.7.B 版本时,配置了公钥模式(publicKeyId + publicKeyPath)并设置 strictlyNeedWechatPaySerial=true,导致以下问题:

  1. 在订单查询和回调时报错:"无可用的平台证书,请在商户平台-API安全申请使用微信支付公钥"
  2. 错误日志显示:Auto update cert failed, statusCode = 404, body = {"code":"RESOURCE_NOT_EXISTS","message":"无可用的平台证书"}
  3. 虽然请求实际上是成功的,但每次都会有这个错误提示

根本原因

当用户未设置 fullPublicKeyModel=true 时(默认为 false),SDK 会尝试同时构建平台证书验证器和公钥验证器。在构建 AutoUpdateCertificatesVerifier 时,构造函数会立即尝试下载平台证书。对于只使用公钥模式的新商户号,没有平台证书,导致下载失败并抛出异常,中断了整个初始化过程。

解决方案

1. 修改 AutoUpdateCertificatesVerifier 构造函数

  • 构造时如果证书下载失败,只记录警告日志,不抛出异常
  • 设置 instant = null,后续每次使用时都会尝试下载证书直到成功

2. 增强 verify 方法

  • 当 verifier 为 null 时,返回 false 而不是抛出异常
  • 记录警告日志,便于问题排查

3. 增强 getValidCertificate 方法

  • 当 verifier 为 null 时,抛出更友好的异常提示
  • 建议用户检查配置或使用完全公钥模式

4. 修改 getWechatPaySerial 方法(两个实现类)

  • 增加 try-catch 异常处理
  • 获取证书序列号失败时,返回空字符串而不是中断请求
  • 记录警告日志

5. 重构单元测试

  • 将重复的测试数据初始化代码提取到 @BeforeMethod
  • 提高测试代码的可维护性和可读性

影响范围

  • 向后兼容:完全兼容,不影响现有用户
  • 公钥模式:修复了公钥模式下的初始化问题
  • 混合模式:支持平台证书和公钥同时存在的场景
  • 完全公钥模式:不受影响,继续正常工作

测试验证

  1. ✅ Checkstyle 代码风格检查通过
  2. ✅ Maven 编译构建通过
  3. ✅ Maven 打包构建通过
  4. ✅ 新增单元测试验证公钥模式下的健壮性
  5. ✅ 代码审查反馈已应用

使用建议

对于只使用公钥的场景,建议设置 fullPublicKeyModel=true,这样可以完全避免尝试下载平台证书,提升性能。

wxPayConfig.setFullPublicKeyModel(true);
Original prompt

This section details on the original issue you should resolve

<issue_title>【4.7.7.B版本】无可用的平台证书,请在商户平台-API安全申请使用微信支付公钥。</issue_title>
<issue_description>使用版本
<binarywang.weixin.java.version>4.7.7.B</binarywang.weixin.java.version>

Image 配置公钥时 设置所有请求携带标识参数 设置为true了 Image

订单状态查询时 会有这个报错 但是实际上请求都成功了

Image 回调也有这个问题

Comments on the Issue (you are @copilot in this section)

Custom agent used: 全部用中文
需要用中文,包括PR标题和分析总结过程


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix platform certificate issue for version 4.7.7.B 修复公钥模式下平台证书自动更新导致的初始化失败问题 Jan 19, 2026
Copilot AI requested a review from binarywang January 19, 2026 03:40
@binarywang binarywang marked this pull request as ready for review January 19, 2026 03:44
Copilot AI review requested due to automatic review settings January 19, 2026 03:44
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

此 PR 修复了在公钥模式下(使用 publicKeyId + publicKeyPath)配置并启用 strictlyNeedWechatPaySerial=true 时,SDK 初始化失败并在每次请求时都报错"无可用的平台证书"的问题。根本原因是 AutoUpdateCertificatesVerifier 构造函数立即尝试下载平台证书,对于仅使用公钥的新商户号,下载失败导致初始化异常。

主要改动:

  • 增强了 AutoUpdateCertificatesVerifier 的容错性,允许证书下载失败而不影响初始化
  • 改进了 WxPayService 实现类中的异常处理,使证书获取失败时请求仍可继续
  • 新增单元测试验证公钥模式下的健壮性

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
AutoUpdateCertificatesVerifier.java 构造函数增加异常容错,verify() 和 getValidCertificate() 方法增加空值检查和有意义的错误提示
WxPayServiceHttpComponentsImpl.java getWechatPaySerial() 方法捕获异常并返回空字符串,允许请求继续
WxPayServiceApacheHttpImpl.java getWechatPaySerial() 方法捕获异常并返回空字符串,允许请求继续
AutoUpdateCertificatesVerifierPublicKeyModeTest.java 新增单元测试,验证公钥模式下构造函数、verify() 和 getValidCertificate() 的行为

Comment on lines +56 to +102
public void testVerifyShouldReturnFalseWhenNoCertificateAvailable() {
String invalidMchId = "invalid_mch_id";
String invalidApiV3Key = "invalid_api_v3_key_must_be_32_b";
String invalidCertSerialNo = "invalid_serial_no";
String payBaseUrl = "https://api.mch.weixin.qq.com";

WxPayCredentials credentials = new WxPayCredentials(
invalidMchId,
new PrivateKeySigner(invalidCertSerialNo, null)
);

AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
credentials,
invalidApiV3Key.getBytes(StandardCharsets.UTF_8),
60,
payBaseUrl,
null
);

// verify 方法应该返回 false,而不是抛出异常
boolean result = verifier.verify("test_serial", "test_message".getBytes(), "test_signature");
assertFalse(result, "当没有有效证书时,verify 应该返回 false");
}

/**
* 测试当没有有效证书时,getValidCertificate 方法应该抛出有意义的异常
*/
@Test(expectedExceptions = me.chanjar.weixin.common.error.WxRuntimeException.class,
expectedExceptionsMessageRegExp = ".*No valid certificate available.*")
public void testGetValidCertificateShouldThrowMeaningfulException() {
String invalidMchId = "invalid_mch_id";
String invalidApiV3Key = "invalid_api_v3_key_must_be_32_b";
String invalidCertSerialNo = "invalid_serial_no";
String payBaseUrl = "https://api.mch.weixin.qq.com";

WxPayCredentials credentials = new WxPayCredentials(
invalidMchId,
new PrivateKeySigner(invalidCertSerialNo, null)
);

AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
credentials,
invalidApiV3Key.getBytes(StandardCharsets.UTF_8),
60,
payBaseUrl,
null
);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

三个测试方法中都重复了相同的测试数据初始化代码(invalidMchId、invalidApiV3Key、invalidCertSerialNo、payBaseUrl、credentials)。建议考虑将这些通用的测试数据提取到 @BeforeMethod 或私有辅助方法中,以减少代码重复并提高可维护性。

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +32
WxPayCredentials credentials = new WxPayCredentials(
invalidMchId,
new PrivateKeySigner(invalidCertSerialNo, null)
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

测试中直接将 null 传递给 PrivateKeySigner 构造函数的第二个参数(应该是 PrivateKey 类型)。这可能在实际调用时导致空指针异常,虽然这个测试的目的是验证构造函数不抛异常,但从测试设计角度来说,应该提供一个虽然无效但至少类型正确的对象,而不是 null。建议考虑生成一个模拟的 PrivateKey 对象或者在注释中说明为何这里使用 null 是安全的。

Copilot uses AI. Check for mistakes.
@augmentcode
Copy link

augmentcode bot commented Jan 19, 2026

🤖 Augment PR Summary

Summary: 本 PR 修复微信支付 V3 在公钥模式下因平台证书自动更新失败而导致 SDK 初始化/请求阶段报“无可用的平台证书”的问题。

Changes:

  • AutoUpdateCertificatesVerifier:构造期证书更新失败时仅告警并允许后续重试;无证书时 verify() 返回 falsegetValidCertificate() 抛出更明确的提示异常
  • WxPayServiceApacheHttpImpl/WxPayServiceHttpComponentsImpl:获取 Wechatpay-Serial 失败时不再中断请求,改为告警并返回空串
  • 新增 AutoUpdateCertificatesVerifierPublicKeyModeTest 覆盖公钥模式下的健壮性

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

instant = Instant.now();
} catch (IOException | GeneralSecurityException e) {
throw new WxRuntimeException(e);
log.warn("Auto update cert failed during initialization, will retry later, exception = {}", e.getMessage());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的容错只捕获了 IOException | GeneralSecurityException,但 autoUpdateCert() 在 HTTP 非 200(以及签名等运行时失败)时会直接抛 WxRuntimeException,仍可能导致初始化阶段异常退出(与 PR 目标的 404 场景不一致)。建议确认是否需要把该运行时异常也纳入容错范围。

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

String invalidMchId = "invalid_mch_id";
String invalidApiV3Key = "invalid_api_v3_key_must_be_32_b";
String invalidCertSerialNo = "invalid_serial_no";
String payBaseUrl = "https://api.mch.weixin.qq.com";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

该测试使用真实的 https://api.mch.weixin.qq.com 作为 payBaseUrl,会引入外部网络依赖(CI 环境下可能被禁网/超时/不稳定),从而导致单测偶发失败或变慢。建议确认项目单测规范是否允许这种外部依赖。

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

@binarywang binarywang added this to the 4.8.2 milestone Jan 25, 2026
binarywang and others added 3 commits March 3, 2026 12:38
…uth/AutoUpdateCertificatesVerifier.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…uth/AutoUpdateCertificatesVerifierPublicKeyModeTest.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…uth/AutoUpdateCertificatesVerifierPublicKeyModeTest.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@binarywang
Copy link
Owner

@copilot apply changes based on the comments in this thread

Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 3, 2026

@copilot apply changes based on the comments in this thread

已完成重构,将三个测试方法中重复的测试数据初始化代码提取到 @BeforeMethod 中。这样可以减少代码重复,提高可维护性。修改已在 commit 43c69b1 中完成。

@binarywang binarywang merged commit 3cd05c8 into develop Mar 3, 2026
1 check passed
@binarywang binarywang deleted the copilot/fix-platform-certificate-issue branch March 3, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

【4.7.7.B版本】无可用的平台证书,请在商户平台-API安全申请使用微信支付公钥。

3 participants