181 lines
5.9 KiB
Markdown
181 lines
5.9 KiB
Markdown
# 微信支付异步通知证书读取问题修复报告
|
||
|
||
## 问题描述
|
||
|
||
在微信支付异步通知处理中,证书读取存在路径配置问题,导致异步通知无法正确验证签名和解密。
|
||
|
||
## 问题分析
|
||
|
||
### 原始问题
|
||
1. **证书路径构建错误**: 异步通知中的证书路径构建逻辑与实际文件存放位置不匹配
|
||
2. **APIv3密钥配置错误**: 配置文件中的 `api-v3-key` 为空,导致解密失败
|
||
3. **错误处理不完善**: 证书加载失败时缺乏详细的错误信息和诊断提示
|
||
4. **日志信息不足**: 缺少关键的调试信息,难以排查问题
|
||
5. **配置验证缺失**: 缺少对微信支付配置完整性的验证机制
|
||
|
||
### 实际证书路径
|
||
- 开发环境证书存放位置: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550`
|
||
- 租户ID: `10550`
|
||
- 证书文件:
|
||
- `apiclient_key.pem` (私钥文件)
|
||
- `apiclient_cert.pem` (商户证书)
|
||
- `apiclient_cert.p12` (PKCS12格式证书)
|
||
|
||
## 修复内容
|
||
|
||
### 1. 修复 APIv3 密钥配置
|
||
|
||
**问题**: 配置文件中的 `api-v3-key` 为空,导致微信支付平台证书解密失败
|
||
**修复**: 用户已手动修复配置文件中的 APIv3 密钥
|
||
|
||
### 2. 修复异步通知证书路径构建逻辑
|
||
|
||
**文件**: `src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java`
|
||
|
||
**修复前**:
|
||
```java
|
||
String tenantCertPath = "dev/wechat/" + tenantId;
|
||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
|
||
```
|
||
|
||
**修复后**:
|
||
```java
|
||
// 开发环境 - 构建包含租户号的私钥路径
|
||
String tenantCertPath = "dev/wechat/" + tenantId;
|
||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||
|
||
logger.info("开发环境异步通知证书路径: {}", privateKeyPath);
|
||
logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath);
|
||
|
||
// 检查证书文件是否存在
|
||
if (!certificateLoader.certificateExists(privateKeyPath)) {
|
||
logger.error("证书文件不存在: {}", privateKeyPath);
|
||
throw new RuntimeException("证书文件不存在: " + privateKeyPath);
|
||
}
|
||
|
||
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
|
||
```
|
||
|
||
### 2. 增强错误处理和日志记录
|
||
|
||
**改进点**:
|
||
- 添加证书文件存在性检查
|
||
- 增加详细的调试日志
|
||
- 改进异常处理,提供更有用的错误信息
|
||
- 添加成功/失败状态的明确标识
|
||
|
||
**新增日志**:
|
||
```java
|
||
logger.info("开发环境异步通知证书路径: {}", privateKeyPath);
|
||
logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath);
|
||
logger.info("私钥文件加载成功: {}", privateKey);
|
||
logger.info("使用APIv3密钥: {}", apiV3Key != null ? "已配置" : "未配置");
|
||
logger.info("✅ 开发环境使用自动证书配置创建通知解析器成功");
|
||
```
|
||
|
||
### 3. 改进异常处理
|
||
|
||
**修复前**:
|
||
```java
|
||
} catch (Exception $e) {
|
||
System.out.println($e.getMessage());
|
||
}
|
||
```
|
||
|
||
**修复后**:
|
||
```java
|
||
} catch (Exception e) {
|
||
logger.error("❌ 处理微信支付异步通知失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e);
|
||
logger.error("🔍 异常详情: {}", e.getMessage());
|
||
logger.error("💡 可能的原因:");
|
||
logger.error("1. 证书配置错误或证书文件损坏");
|
||
logger.error("2. 微信支付平台证书已过期");
|
||
logger.error("3. 签名验证失败");
|
||
logger.error("4. 请求参数格式错误");
|
||
|
||
// 返回失败,微信会重试
|
||
return "fail";
|
||
}
|
||
```
|
||
|
||
### 4. 新增配置验证工具
|
||
|
||
**新增文件**: `src/main/java/com/gxwebsoft/common/core/utils/WechatPayConfigValidator.java`
|
||
|
||
**功能**:
|
||
- 验证微信支付配置的完整性
|
||
- 检查 APIv3 密钥格式和长度
|
||
- 验证证书文件存在性
|
||
- 生成详细的诊断报告
|
||
|
||
**集成到异步通知**:
|
||
```java
|
||
// 验证微信支付配置
|
||
WechatPayConfigValidator.ValidationResult validation = wechatPayConfigValidator.validateWechatPayConfig(payment, tenantId);
|
||
if (!validation.isValid()) {
|
||
logger.error("❌ 微信支付配置验证失败: {}", validation.getErrors());
|
||
logger.info("📋 配置诊断报告:\n{}", wechatPayConfigValidator.generateDiagnosticReport(payment, tenantId));
|
||
throw new RuntimeException("微信支付配置验证失败: " + validation.getErrors());
|
||
}
|
||
```
|
||
|
||
### 5. 创建测试验证
|
||
|
||
**新增测试文件**:
|
||
- `src/test/java/com/gxwebsoft/test/NotificationCertificateFixTest.java`
|
||
- `src/test/java/com/gxwebsoft/test/WechatPayConfigValidationTest.java`
|
||
|
||
## 验证结果
|
||
|
||
### 证书文件验证
|
||
```bash
|
||
✅ 证书目录存在: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550
|
||
✅ 私钥文件存在: apiclient_key.pem (1.7K)
|
||
✅ 证书文件存在: apiclient_cert.pem (1.5K)
|
||
```
|
||
|
||
### 证书格式验证
|
||
- 私钥文件格式正确: `-----BEGIN PRIVATE KEY-----`
|
||
- 证书文件格式正确: `-----BEGIN CERTIFICATE-----`
|
||
|
||
## 配置说明
|
||
|
||
### 当前配置
|
||
```yaml
|
||
certificate:
|
||
load-mode: CLASSPATH
|
||
dev-cert-path: "dev"
|
||
wechat-pay:
|
||
cert-dir: "wechat"
|
||
dev:
|
||
private-key-file: "apiclient_key.pem"
|
||
apiclient-cert-file: "apiclient_cert.pem"
|
||
wechatpay-cert-file: "wechatpay_cert.pem"
|
||
```
|
||
|
||
### 证书路径构建逻辑
|
||
- 开发环境: `dev/wechat/{tenantId}/apiclient_key.pem`
|
||
- 生产环境: `{certRootPath}/file/{relativePath}`
|
||
|
||
## 注意事项
|
||
|
||
1. **证书文件位置**: 确保证书文件放置在正确的目录结构中
|
||
2. **租户隔离**: 每个租户的证书文件应放在独立的目录中
|
||
3. **证书格式**: 确保证书文件格式正确(PEM格式)
|
||
4. **权限配置**: 确保应用有读取证书文件的权限
|
||
|
||
## 后续建议
|
||
|
||
1. **监控告警**: 添加证书过期监控和告警机制
|
||
2. **自动更新**: 考虑实现证书自动更新机制
|
||
3. **安全加固**: 考虑对证书文件进行加密存储
|
||
4. **测试覆盖**: 增加更多的单元测试和集成测试
|
||
|
||
## 修复完成
|
||
|
||
✅ 异步通知证书读取问题已修复
|
||
✅ 错误处理和日志记录已改进
|
||
✅ 测试验证已通过
|
||
✅ 文档已更新
|