Files
mp-java/docs/WECHAT_NOTIFICATION_CERTIFICATE_FIX.md

181 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 微信支付异步通知证书读取问题修复报告
## 问题描述
在微信支付异步通知处理中,证书读取存在路径配置问题,导致异步通知无法正确验证签名和解密。
## 问题分析
### 原始问题
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. **测试覆盖**: 增加更多的单元测试和集成测试
## 修复完成
✅ 异步通知证书读取问题已修复
✅ 错误处理和日志记录已改进
✅ 测试验证已通过
✅ 文档已更新