Conversation
Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
There was a problem hiding this comment.
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() 的行为 |
| AutoUpdateCertificatesVerifier verifier = null; | ||
| try { | ||
| verifier = new AutoUpdateCertificatesVerifier( | ||
| credentials, | ||
| invalidApiV3Key.getBytes(StandardCharsets.UTF_8), | ||
| 60, | ||
| payBaseUrl, | ||
| null | ||
| ); | ||
| // 如果没有抛出异常,测试通过 | ||
| assertNotNull(verifier); | ||
| } catch (Exception e) { | ||
| fail("构造函数不应该抛出异常,但抛出了: " + e.getMessage()); | ||
| } |
There was a problem hiding this comment.
测试方法在 try-catch 块中调用 fail(),但在成功路径上只是断言 verifier 不为空。实际上,如果构造函数成功返回,测试已经通过了(因为没有抛出异常)。可以简化这个测试,移除 try-catch 块,直接创建对象并断言不为空即可。TestNG 会自动捕获并报告任何未预期的异常。
| AutoUpdateCertificatesVerifier verifier = null; | |
| try { | |
| verifier = new AutoUpdateCertificatesVerifier( | |
| credentials, | |
| invalidApiV3Key.getBytes(StandardCharsets.UTF_8), | |
| 60, | |
| payBaseUrl, | |
| null | |
| ); | |
| // 如果没有抛出异常,测试通过 | |
| assertNotNull(verifier); | |
| } catch (Exception e) { | |
| fail("构造函数不应该抛出异常,但抛出了: " + e.getMessage()); | |
| } | |
| AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( | |
| credentials, | |
| invalidApiV3Key.getBytes(StandardCharsets.UTF_8), | |
| 60, | |
| payBaseUrl, | |
| null | |
| ); | |
| // 如果没有抛出异常,测试通过 | |
| assertNotNull(verifier); |
| } catch (IOException | GeneralSecurityException e) { | ||
| throw new WxRuntimeException(e); | ||
| log.warn("Auto update cert failed during initialization, will retry later, exception = {}", e.getMessage()); | ||
| // 设置instant为null,在第一次使用时会触发重新下载 |
There was a problem hiding this comment.
注释说"在第一次使用时会触发重新下载",但实际行为是:只要 instant 为 null,每次调用 verify() 或 getValidCertificate() 时都会尝试重新下载证书,直到成功为止。建议修改注释为:"设置 instant 为 null,后续每次使用时都会尝试下载证书直到成功",以更准确地反映实际行为。
| // 设置instant为null,在第一次使用时会触发重新下载 | |
| // 设置 instant 为 null,后续每次使用时都会尝试下载证书直到成功 |
| 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 | ||
| ); |
There was a problem hiding this comment.
三个测试方法中都重复了相同的测试数据初始化代码(invalidMchId、invalidApiV3Key、invalidCertSerialNo、payBaseUrl、credentials)。建议考虑将这些通用的测试数据提取到 @BeforeMethod 或私有辅助方法中,以减少代码重复并提高可维护性。
| @@ -0,0 +1,107 @@ | |||
| package com.github.binarywang.wxpay.v3.auth; | |||
|
|
|||
| import com.github.binarywang.wxpay.config.WxPayHttpProxy; | |||
There was a problem hiding this comment.
测试代码中导入了未使用的类 WxPayHttpProxy。虽然这不会影响测试的功能,但会增加不必要的依赖引用。建议移除这个未使用的导入。
| import com.github.binarywang.wxpay.config.WxPayHttpProxy; |
| WxPayCredentials credentials = new WxPayCredentials( | ||
| invalidMchId, | ||
| new PrivateKeySigner(invalidCertSerialNo, null) |
There was a problem hiding this comment.
测试中直接将 null 传递给 PrivateKeySigner 构造函数的第二个参数(应该是 PrivateKey 类型)。这可能在实际调用时导致空指针异常,虽然这个测试的目的是验证构造函数不抛异常,但从测试设计角度来说,应该提供一个虽然无效但至少类型正确的对象,而不是 null。建议考虑生成一个模拟的 PrivateKey 对象或者在注释中说明为何这里使用 null 是安全的。
🤖 Augment PR SummarySummary: 本 PR 修复微信支付 V3 在公钥模式下因平台证书自动更新失败而导致 SDK 初始化/请求阶段报“无可用的平台证书”的问题。 Changes:
🤖 Was this summary useful? React with 👍 or 👎 |
| 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()); |
| 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"; |
问题描述
用户配置公钥模式(
publicKeyId+publicKeyPath)并设置strictlyNeedWechatPaySerial=true时,SDK 在初始化和每次请求时都会报错"无可用的平台证书"(HTTP 404),虽然请求实际成功但日志持续报错。根本原因:
AutoUpdateCertificatesVerifier构造函数立即尝试下载平台证书,对于只使用公钥的新商户号(无平台证书),下载失败导致初始化异常。主要修改
1. AutoUpdateCertificatesVerifier 容错增强
verify(): 无证书时返回false而非异常getValidCertificate(): 抛出明确的配置提示异常2. WxPayService 实现类异常处理
getWechatPaySerial(): 捕获证书获取异常,返回空串允许请求继续(WxPayServiceApacheHttpImpl和WxPayServiceHttpComponentsImpl)3. 单元测试
AutoUpdateCertificatesVerifierPublicKeyModeTest验证公钥模式健壮性影响范围
fullPublicKeyModel=true以跳过证书下载Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.