Skip to content

Commit 0b3c45a

Browse files
Copilotbinarywang
andcommitted
修复公钥模式下自动更新证书报错问题
Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
1 parent 0b3ec25 commit 0b3c45a

File tree

4 files changed

+132
-4
lines changed

4 files changed

+132
-4
lines changed

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,13 @@ private String getWechatPaySerial(WxPayConfig wxPayConfig) {
420420
return wxPayConfig.getPublicKeyId();
421421
}
422422

423-
return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
423+
try {
424+
return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
425+
} catch (Exception e) {
426+
log.warn("Failed to get certificate serial number: {}", e.getMessage());
427+
// 返回空字符串而不是抛出异常,让请求继续进行,由微信服务器判断是否需要Wechatpay-Serial
428+
return "";
429+
}
424430
}
425431

426432
private void logRequestAndResponse(String url, String requestStr, String responseStr) {

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceHttpComponentsImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,13 @@ private String getWechatPaySerial(WxPayConfig wxPayConfig) {
398398
return wxPayConfig.getPublicKeyId();
399399
}
400400

401-
return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
401+
try {
402+
return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
403+
} catch (Exception e) {
404+
log.warn("Failed to get certificate serial number: {}", e.getMessage());
405+
// 返回空字符串而不是抛出异常,让请求继续进行,由微信服务器判断是否需要Wechatpay-Serial
406+
return "";
407+
}
402408
}
403409

404410
private void logRequestAndResponse(String url, String requestStr, String responseStr) {

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/AutoUpdateCertificatesVerifier.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,24 @@ public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key,
109109
this.minutesInterval = minutesInterval;
110110
this.payBaseUrl = payBaseUrl;
111111
this.wxPayHttpProxy = wxPayHttpProxy;
112-
//构造时更新证书
112+
//构造时尝试更新证书,但失败时不抛出异常,避免影响公钥模式的使用
113113
try {
114114
autoUpdateCert();
115115
instant = Instant.now();
116116
} catch (IOException | GeneralSecurityException e) {
117-
throw new WxRuntimeException(e);
117+
log.warn("Auto update cert failed during initialization, will retry later, exception = {}", e.getMessage());
118+
// 设置instant为null,在第一次使用时会触发重新下载
119+
instant = null;
118120
}
119121
}
120122

121123
@Override
122124
public boolean verify(String serialNumber, byte[] message, String signature) {
123125
checkAndAutoUpdateCert();
126+
if (verifier == null) {
127+
log.warn("No valid certificate available for verification");
128+
return false;
129+
}
124130
return verifier.verify(serialNumber, message, signature);
125131
}
126132

@@ -220,6 +226,9 @@ private List<X509Certificate> deserializeToCerts(byte[] apiV3Key, String body) t
220226
@Override
221227
public X509Certificate getValidCertificate() {
222228
checkAndAutoUpdateCert();
229+
if (verifier == null) {
230+
throw new WxRuntimeException("No valid certificate available, please check your configuration or use fullPublicKeyModel mode");
231+
}
223232
return verifier.getValidCertificate();
224233
}
225234

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.github.binarywang.wxpay.v3.auth;
2+
3+
import com.github.binarywang.wxpay.config.WxPayHttpProxy;
4+
import org.testng.annotations.Test;
5+
6+
import java.nio.charset.StandardCharsets;
7+
import java.security.cert.X509Certificate;
8+
9+
import static org.testng.Assert.*;
10+
11+
/**
12+
* 测试公钥模式下 AutoUpdateCertificatesVerifier 的健壮性
13+
*
14+
* @author copilot
15+
*/
16+
public class AutoUpdateCertificatesVerifierPublicKeyModeTest {
17+
18+
/**
19+
* 测试当证书下载失败时,构造函数不应该抛出异常
20+
* 这是为了支持公钥模式下的场景,在公钥模式下商户可能没有平台证书
21+
*/
22+
@Test
23+
public void testConstructorShouldNotThrowExceptionWhenCertDownloadFails() {
24+
// 使用一个无效的配置,模拟证书下载失败的场景
25+
String invalidMchId = "invalid_mch_id";
26+
String invalidApiV3Key = "invalid_api_v3_key_must_be_32_b";
27+
String invalidCertSerialNo = "invalid_serial_no";
28+
String payBaseUrl = "https://api.mch.weixin.qq.com";
29+
30+
WxPayCredentials credentials = new WxPayCredentials(
31+
invalidMchId,
32+
new PrivateKeySigner(invalidCertSerialNo, null)
33+
);
34+
35+
// 构造函数应该不抛出异常,即使证书下载失败
36+
AutoUpdateCertificatesVerifier verifier = null;
37+
try {
38+
verifier = new AutoUpdateCertificatesVerifier(
39+
credentials,
40+
invalidApiV3Key.getBytes(StandardCharsets.UTF_8),
41+
60,
42+
payBaseUrl,
43+
null
44+
);
45+
// 如果没有抛出异常,测试通过
46+
assertNotNull(verifier);
47+
} catch (Exception e) {
48+
fail("构造函数不应该抛出异常,但抛出了: " + e.getMessage());
49+
}
50+
}
51+
52+
/**
53+
* 测试当没有有效证书时,verify 方法应该返回 false 而不是抛出异常
54+
*/
55+
@Test
56+
public void testVerifyShouldReturnFalseWhenNoCertificateAvailable() {
57+
String invalidMchId = "invalid_mch_id";
58+
String invalidApiV3Key = "invalid_api_v3_key_must_be_32_b";
59+
String invalidCertSerialNo = "invalid_serial_no";
60+
String payBaseUrl = "https://api.mch.weixin.qq.com";
61+
62+
WxPayCredentials credentials = new WxPayCredentials(
63+
invalidMchId,
64+
new PrivateKeySigner(invalidCertSerialNo, null)
65+
);
66+
67+
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
68+
credentials,
69+
invalidApiV3Key.getBytes(StandardCharsets.UTF_8),
70+
60,
71+
payBaseUrl,
72+
null
73+
);
74+
75+
// verify 方法应该返回 false,而不是抛出异常
76+
boolean result = verifier.verify("test_serial", "test_message".getBytes(), "test_signature");
77+
assertFalse(result, "当没有有效证书时,verify 应该返回 false");
78+
}
79+
80+
/**
81+
* 测试当没有有效证书时,getValidCertificate 方法应该抛出有意义的异常
82+
*/
83+
@Test(expectedExceptions = me.chanjar.weixin.common.error.WxRuntimeException.class,
84+
expectedExceptionsMessageRegExp = ".*No valid certificate available.*")
85+
public void testGetValidCertificateShouldThrowMeaningfulException() {
86+
String invalidMchId = "invalid_mch_id";
87+
String invalidApiV3Key = "invalid_api_v3_key_must_be_32_b";
88+
String invalidCertSerialNo = "invalid_serial_no";
89+
String payBaseUrl = "https://api.mch.weixin.qq.com";
90+
91+
WxPayCredentials credentials = new WxPayCredentials(
92+
invalidMchId,
93+
new PrivateKeySigner(invalidCertSerialNo, null)
94+
);
95+
96+
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
97+
credentials,
98+
invalidApiV3Key.getBytes(StandardCharsets.UTF_8),
99+
60,
100+
payBaseUrl,
101+
null
102+
);
103+
104+
// 应该抛出有意义的异常
105+
X509Certificate certificate = verifier.getValidCertificate();
106+
}
107+
}

0 commit comments

Comments
 (0)