1
0
mirror of synced 2026-02-10 04:57:49 +08:00

修复公钥模式下自动更新证书报错问题

Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-19 03:36:37 +00:00
parent 0b3ec258af
commit 0b3c45aa81
4 changed files with 132 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,107 @@
package com.github.binarywang.wxpay.v3.auth;
import com.github.binarywang.wxpay.config.WxPayHttpProxy;
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 {
/**
* 测试当证书下载失败时,构造函数不应该抛出异常
* 这是为了支持公钥模式下的场景,在公钥模式下商户可能没有平台证书
*/
@Test
public void testConstructorShouldNotThrowExceptionWhenCertDownloadFails() {
// 使用一个无效的配置,模拟证书下载失败的场景
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 = null;
try {
verifier = new AutoUpdateCertificatesVerifier(
credentials,
invalidApiV3Key.getBytes(StandardCharsets.UTF_8),
60,
payBaseUrl,
null
);
// 如果没有抛出异常,测试通过
assertNotNull(verifier);
} catch (Exception e) {
fail("构造函数不应该抛出异常,但抛出了: " + e.getMessage());
}
}
/**
* 测试当没有有效证书时verify 方法应该返回 false 而不是抛出异常
*/
@Test
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
);
// 应该抛出有意义的异常
X509Certificate certificate = verifier.getValidCertificate();
}
}