1
0
mirror of synced 2026-03-24 22:03:02 +08:00

🎨 #3854 【微信支付】修复公钥模式下平台证书自动更新导致的初始化失败问题

This commit is contained in:
Copilot
2026-03-03 13:20:26 +08:00
committed by GitHub
parent 3980017478
commit 3cd05c88f9
4 changed files with 116 additions and 4 deletions

View File

@@ -420,7 +420,13 @@ public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
return wxPayConfig.getPublicKeyId();
}
return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
try {
return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
} catch (Exception e) {
log.warn("Failed to get certificate serial number: {}", e.getMessage());
// 返回空字符串而不是抛出异常让请求继续进行由微信服务器判断是否需要Wechatpay-Serial
return "";
}
}
private void logRequestAndResponse(String url, String requestStr, String responseStr) {

View File

@@ -398,7 +398,13 @@ public class WxPayServiceHttpComponentsImpl extends BaseWxPayServiceImpl {
return wxPayConfig.getPublicKeyId();
}
return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
try {
return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
} catch (Exception e) {
log.warn("Failed to get certificate serial number: {}", e.getMessage());
// 返回空字符串而不是抛出异常让请求继续进行由微信服务器判断是否需要Wechatpay-Serial
return "";
}
}
private void logRequestAndResponse(String url, String requestStr, String responseStr) {

View File

@@ -109,18 +109,24 @@ public class AutoUpdateCertificatesVerifier implements Verifier {
this.minutesInterval = minutesInterval;
this.payBaseUrl = payBaseUrl;
this.wxPayHttpProxy = wxPayHttpProxy;
//构造时更新证书
//构造时尝试更新证书,但失败时不抛出异常,避免影响公钥模式的使用
try {
autoUpdateCert();
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());
// 设置 instant 为 null后续每次使用时都会尝试下载证书直到成功
instant = null;
}
}
@Override
public boolean verify(String serialNumber, byte[] message, String signature) {
checkAndAutoUpdateCert();
if (verifier == null) {
log.warn("No valid certificate available for verification");
return false;
}
return verifier.verify(serialNumber, message, signature);
}
@@ -220,6 +226,9 @@ public class AutoUpdateCertificatesVerifier implements Verifier {
@Override
public X509Certificate getValidCertificate() {
checkAndAutoUpdateCert();
if (verifier == null) {
throw new WxRuntimeException("No valid certificate available, please check your configuration or use fullPublicKeyModel mode");
}
return verifier.getValidCertificate();
}

View File

@@ -0,0 +1,91 @@
package com.github.binarywang.wxpay.v3.auth;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import static org.testng.Assert.*;
/**
* 测试公钥模式下 AutoUpdateCertificatesVerifier 的健壮性
*
* @author copilot
*/
public class AutoUpdateCertificatesVerifierPublicKeyModeTest {
private String invalidMchId;
private String invalidApiV3Key;
private String invalidCertSerialNo;
private String payBaseUrl;
private WxPayCredentials credentials;
@BeforeMethod
public void setUp() {
// 使用无效的配置,模拟证书下载失败的场景
invalidMchId = "invalid_mch_id";
invalidApiV3Key = "invalid_api_v3_key_must_be_32_b";
invalidCertSerialNo = "invalid_serial_no";
payBaseUrl = "https://api.mch.weixin.qq.com";
credentials = new WxPayCredentials(
invalidMchId,
new PrivateKeySigner(invalidCertSerialNo, null)
);
}
/**
* 测试当证书下载失败时,构造函数不应该抛出异常
* 这是为了支持公钥模式下的场景,在公钥模式下商户可能没有平台证书
*/
@Test
public void testConstructorShouldNotThrowExceptionWhenCertDownloadFails() {
// 构造函数应该不抛出异常,即使证书下载失败
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
credentials,
invalidApiV3Key.getBytes(StandardCharsets.UTF_8),
60,
payBaseUrl,
null
);
// 如果没有抛出异常,测试通过
assertNotNull(verifier);
}
/**
* 测试当没有有效证书时verify 方法应该返回 false 而不是抛出异常
*/
@Test
public void testVerifyShouldReturnFalseWhenNoCertificateAvailable() {
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() {
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
credentials,
invalidApiV3Key.getBytes(StandardCharsets.UTF_8),
60,
payBaseUrl,
null
);
// 应该抛出有意义的异常
X509Certificate certificate = verifier.getValidCertificate();
}
}