小程序开发-服务端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

5.9 KiB

微信支付异步通知证书读取问题修复报告

问题描述

在微信支付异步通知处理中,证书读取存在路径配置问题,导致异步通知无法正确验证签名和解密。

问题分析

原始问题

  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

修复前:

String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);

修复后:

// 开发环境 - 构建包含租户号的私钥路径
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. 增强错误处理和日志记录

改进点:

  • 添加证书文件存在性检查
  • 增加详细的调试日志
  • 改进异常处理,提供更有用的错误信息
  • 添加成功/失败状态的明确标识

新增日志:

logger.info("开发环境异步通知证书路径: {}", privateKeyPath);
logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath);
logger.info("私钥文件加载成功: {}", privateKey);
logger.info("使用APIv3密钥: {}", apiV3Key != null ? "已配置" : "未配置");
logger.info("✅ 开发环境使用自动证书配置创建通知解析器成功");

3. 改进异常处理

修复前:

} catch (Exception $e) {
  System.out.println($e.getMessage());
}

修复后:

} 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 密钥格式和长度
  • 验证证书文件存在性
  • 生成详细的诊断报告

集成到异步通知:

// 验证微信支付配置
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

验证结果

证书文件验证

✅ 证书目录存在: /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-----

配置说明

当前配置

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. 测试覆盖: 增加更多的单元测试和集成测试

修复完成

异步通知证书读取问题已修复 错误处理和日志记录已改进 测试验证已通过 文档已更新