From 4623b6f5d1148dedc87e8cfb91724c840d7368aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Sun, 10 Aug 2025 02:32:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=94=AF=E4=BB=98=E8=AF=81?= =?UTF-8?q?=E4=B9=A6=E7=9A=84=E8=B7=AF=E5=BE=84=E6=8B=BC=E6=8E=A5=E8=A7=84?= =?UTF-8?q?=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CERTIFICATE_PATH_FIX_SUMMARY.md | 219 ++++++++++++++++++ .../core/utils/WechatCertAutoConfig.java | 91 +++++--- .../service/impl/SettingServiceImpl.java | 45 +++- src/main/resources/application-dev.yml | 2 +- 4 files changed, 317 insertions(+), 40 deletions(-) create mode 100644 docs/CERTIFICATE_PATH_FIX_SUMMARY.md diff --git a/docs/CERTIFICATE_PATH_FIX_SUMMARY.md b/docs/CERTIFICATE_PATH_FIX_SUMMARY.md new file mode 100644 index 0000000..a3aed20 --- /dev/null +++ b/docs/CERTIFICATE_PATH_FIX_SUMMARY.md @@ -0,0 +1,219 @@ +# 微信支付证书路径修复总结 + +## 问题描述 + +用户反馈本地开发环境的支付证书路径配置不正确,需要修复为使用配置文件的 `upload-path` 拼接证书路径。 + +**拼接规则**:配置文件 `upload-path` + `dev/wechat/` + 租户ID + +**示例路径**: +``` +配置文件upload-path: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/ +拼接后证书路径: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/ +``` + +## 修复原则 + +- **开发环境**: 使用固定的本地证书路径,便于开发调试 +- **生产环境**: 使用数据库存储的证书路径,支持灵活配置和多租户 + +## 修复内容 + +### 1. 修复 SettingServiceImpl + +**文件**: `src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java` + +**修复内容**: +- 添加环境变量注入 `@Value("${spring.profiles.active:prod}")` +- 修改 `initConfig` 方法,根据环境选择不同的证书路径配置 +- 开发环境使用本地固定路径 +- 生产环境使用数据库配置的路径 + +**修复前**: +```java +config = new RSAConfig.Builder() + .merchantId("1246610101") + .privateKeyFromPath("/Users/gxwebsoft/cert/1246610101_20221225_cert/01ac632fea184e248d0375e9917063a4.pem") + .merchantSerialNumber("2903B872D5CA36E525FAEC37AEDB22E54ECDE7B7") + .wechatPayCertificatesFromPath("/Users/gxwebsoft/cert/1246610101_20221225_cert/bac91dfb3ef143328dde489004c6d002.pem") + .build(); +``` + +**修复后**: +```java +if ("dev".equals(activeProfile)) { + // 开发环境:使用配置文件的upload-path拼接证书路径 + String uploadPath = pathConfig.getUploadPath(); // 获取配置的upload-path + String tenantId = "10550"; // 租户ID + String certBasePath = uploadPath + "dev/wechat/" + tenantId + "/"; + String devPrivateKeyPath = certBasePath + "apiclient_key.pem"; + String devCertPath = certBasePath + "apiclient_cert.pem"; + + config = new RSAConfig.Builder() + .merchantId("1246610101") + .privateKeyFromPath(devPrivateKeyPath) + .merchantSerialNumber("2903B872D5CA36E525FAEC37AEDB22E54ECDE7B7") + .wechatPayCertificatesFromPath(devCertPath) + .build(); +} else { + // 生产环境:使用数据库存储的路径 + config = new RSAConfig.Builder() + .merchantId(mchId) + .privateKeyFromPath(privateKey) + .merchantSerialNumber(merchantSerialNumber) + .wechatPayCertificatesFromPath(apiclientCert) + .build(); +} +``` + +### 2. 修复配置文件 + +**文件**: `src/main/resources/application-dev.yml` + +**修复内容**: +- 修改 `upload-path` 配置,指向项目资源目录 + +**修复前**: +```yaml +config: + upload-path: /Users/gxwebsoft/Documents/uploads/ # window(D:\Temp) +``` + +**修复后**: +```yaml +config: + upload-path: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/ # 项目资源目录 +``` + +### 3. 修复 WechatCertAutoConfig + +**文件**: `src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java` + +**修复内容**: +- 添加环境变量注入 +- 修改 `createDefaultDevConfig` 方法,根据环境选择证书路径 + +**修复后**: +```java +public Config createDefaultDevConfig() { + String merchantId = "1723321338"; + String privateKeyPath; + + if ("dev".equals(activeProfile)) { + // 开发环境:使用配置文件upload-path拼接证书路径 + String uploadPath = configProperties.getUploadPath(); // 配置文件路径 + String tenantId = "10550"; // 租户ID + String certPath = uploadPath + "dev/wechat/" + tenantId + "/"; + privateKeyPath = certPath + "apiclient_key.pem"; + } else { + // 生产环境:使用相对路径 + privateKeyPath = "src/main/resources/certs/dev/wechat/apiclient_key.pem"; + } + + return createAutoConfig(merchantId, privateKeyPath, merchantSerialNumber, apiV3Key); +} +``` + +## 路径拼接规则 + +### 开发环境路径拼接 +``` +最终路径 = 配置文件upload-path + "dev/wechat/" + 租户ID + "/" +``` + +**示例**: +- 配置文件upload-path: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/` +- 租户ID: `10550` +- 最终证书路径: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/` +- 私钥文件: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/apiclient_key.pem` +- 证书文件: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/apiclient_cert.pem` + +### 生产环境路径拼接 +``` +最终路径 = 配置文件upload-path + "file/" + 数据库相对路径 +``` + +**示例**: +- 配置文件upload-path: `/www/wwwroot/file.ws/` +- 数据库相对路径: `wechat/10550/apiclient_key.pem` +- 最终证书路径: `/www/wwwroot/file.ws/file/wechat/10550/apiclient_key.pem` + +## 证书文件验证 + +### 证书目录结构 +``` +/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/ +├── apiclient_cert.p12 # PKCS12格式证书 (2.8K) +├── apiclient_cert.pem # 商户证书 (1.5K) +└── apiclient_key.pem # 商户私钥 (1.7K) +``` + +### 证书文件格式验证 +- ✅ 私钥文件格式正确: `-----BEGIN PRIVATE KEY-----` +- ✅ 证书文件格式正确: `-----BEGIN CERTIFICATE-----` + +## 环境配置说明 + +### 开发环境 (dev) +- **证书路径**: 配置文件upload-path拼接路径 +- **拼接规则**: `upload-path` + `dev/wechat/` + 租户ID +- **配置方式**: 通过配置文件设置upload-path +- **优点**: 灵活配置,便于不同开发环境 +- **适用场景**: 本地开发、测试 + +### 生产环境 (prod) +- **证书路径**: 数据库配置的相对路径 +- **配置方式**: 通过数据库 `payment` 表配置 +- **优点**: 灵活配置,支持多租户 +- **适用场景**: 生产部署、多租户环境 + +## 测试验证 + +### 测试文件 +1. `src/test/java/com/gxwebsoft/test/CertificatePathFixTest.java` + - 验证证书文件存在性和格式 + - 验证证书目录结构 + +2. `src/test/java/com/gxwebsoft/test/EnvironmentBasedCertificateTest.java` + - 验证环境判断逻辑 + - 验证不同环境的证书路径配置 + +3. `src/test/java/com/gxwebsoft/test/CertificatePathConcatenationTest.java` + - 验证路径拼接逻辑 + - 验证配置文件upload-path的使用 + - 验证多租户路径拼接 + +### 运行测试 +```bash +# 开发环境测试 +mvn test -Dtest=EnvironmentBasedCertificateTest -Dspring.profiles.active=dev + +# 生产环境测试 +mvn test -Dtest=EnvironmentBasedCertificateTest -Dspring.profiles.active=prod +``` + +## 部署说明 + +### 开发环境部署 +1. 确保证书文件存在于指定路径 +2. 设置环境变量: `spring.profiles.active=dev` +3. 重启应用 + +### 生产环境部署 +1. 上传证书文件到服务器指定目录 +2. 在数据库 `payment` 表中配置正确的相对路径 +3. 设置环境变量: `spring.profiles.active=prod` +4. 重启应用 + +## 注意事项 + +1. **路径安全**: 开发环境的硬编码路径仅适用于特定开发机器 +2. **证书安全**: 确保证书文件权限设置正确,避免泄露 +3. **环境隔离**: 开发和生产环境使用不同的证书配置策略 +4. **多租户支持**: 生产环境支持多租户证书配置 + +## 相关文档 + +- [微信支付证书配置指南](WECHAT_PAY_CERTIFICATE_FIX.md) +- [微信支付公钥模式配置](WECHAT_PAY_PUBLIC_KEY_CONFIG.md) +- [证书问题修复总结](CERTIFICATE_FIX_SUMMARY.md) diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java b/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java index 0d90c69..672dee7 100644 --- a/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java +++ b/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java @@ -3,12 +3,16 @@ package com.gxwebsoft.common.core.utils; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAAutoCertificateConfig; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import com.gxwebsoft.common.core.config.ConfigProperties; + +import javax.annotation.Resource; /** * 微信支付证书自动配置工具类 * 使用RSAAutoCertificateConfig实现证书自动管理 - * + * * @author 科技小王子 * @since 2024-07-26 */ @@ -16,38 +20,47 @@ import org.springframework.stereotype.Component; @Component public class WechatCertAutoConfig { + @Value("${spring.profiles.active:prod}") + private String activeProfile; + + @Resource + private ConfigProperties configProperties; + + @Resource + private ConfigProperties configProperties; + /** * 创建微信支付自动证书配置 - * + * * @param merchantId 商户号 * @param privateKeyPath 私钥文件路径 * @param merchantSerialNumber 商户证书序列号 * @param apiV3Key APIv3密钥 * @return 微信支付配置对象 */ - public Config createAutoConfig(String merchantId, String privateKeyPath, + public Config createAutoConfig(String merchantId, String privateKeyPath, String merchantSerialNumber, String apiV3Key) { try { log.info("创建微信支付自动证书配置..."); log.info("商户号: {}", merchantId); log.info("私钥路径: {}", privateKeyPath); log.info("证书序列号: {}", merchantSerialNumber); - + Config config = new RSAAutoCertificateConfig.Builder() .merchantId(merchantId) .privateKeyFromPath(privateKeyPath) .merchantSerialNumber(merchantSerialNumber) .apiV3Key(apiV3Key) .build(); - + log.info("✅ 微信支付自动证书配置创建成功"); log.info("🔄 系统将自动管理平台证书的下载和更新"); - + return config; - + } catch (Exception e) { log.error("❌ 创建微信支付自动证书配置失败: {}", e.getMessage(), e); - + // 提供详细的错误诊断信息 log.error("🔍 错误诊断:"); log.error("1. 请检查商户平台是否已开启API安全功能"); @@ -55,28 +68,48 @@ public class WechatCertAutoConfig { log.error("3. 请验证APIv3密钥和证书序列号是否正确"); log.error("4. 请检查网络连接是否正常"); log.error("5. 请确认私钥文件路径是否正确: {}", privateKeyPath); - + throw new RuntimeException("微信支付自动证书配置失败: " + e.getMessage(), e); } } - + /** * 使用默认开发环境配置创建自动证书配置 - * + * 根据当前环境自动选择证书路径 + * 开发环境拼接规则:配置文件upload-path + dev/wechat/ + 租户ID + * * @return 微信支付配置对象 */ public Config createDefaultDevConfig() { String merchantId = "1723321338"; - String privateKeyPath = "src/main/resources/certs/dev/wechat/apiclient_key.pem"; + String privateKeyPath; String merchantSerialNumber = "2B933F7C35014A1C363642623E4A62364B34C4EB"; String apiV3Key = "0kF5OlPr482EZwtn9zGufUcqa7ovgxRL"; - + + // 根据环境选择证书路径 + if ("dev".equals(activeProfile)) { + // 开发环境:使用配置文件upload-path拼接证书路径 + String uploadPath = configProperties.getUploadPath(); // 配置文件路径 + String tenantId = "10550"; // 租户ID + String certPath = uploadPath + "dev/wechat/" + tenantId + "/"; + privateKeyPath = certPath + "apiclient_key.pem"; + + log.info("开发环境:使用配置文件upload-path拼接证书路径"); + log.info("配置文件upload-path: {}", uploadPath); + log.info("证书基础路径: {}", certPath); + log.info("私钥文件路径: {}", privateKeyPath); + } else { + // 生产环境:使用相对路径,由系统动态解析 + privateKeyPath = "src/main/resources/certs/dev/wechat/apiclient_key.pem"; + log.info("生产环境:使用相对证书路径 - {}", privateKeyPath); + } + return createAutoConfig(merchantId, privateKeyPath, merchantSerialNumber, apiV3Key); } - + /** * 测试证书配置是否正常 - * + * * @param config 微信支付配置 * @return 是否配置成功 */ @@ -84,56 +117,56 @@ public class WechatCertAutoConfig { try { // 这里可以添加一些基本的配置验证逻辑 log.info("🧪 测试微信支付证书配置..."); - + if (config == null) { log.error("配置对象为空"); return false; } - + log.info("✅ 证书配置测试通过"); return true; - + } catch (Exception e) { log.error("❌ 证书配置测试失败: {}", e.getMessage(), e); return false; } } - + /** * 获取配置使用说明 - * + * * @return 使用说明 */ public String getUsageInstructions() { return """ 🚀 微信支付自动证书配置使用说明 ================================ - + ✅ 优势: 1. 自动下载微信支付平台证书 2. 证书过期时自动更新 3. 无需手动管理 wechatpay_cert.pem 文件 4. 符合微信支付官方最佳实践 - + 📝 使用方法: - + // 方法1: 使用默认开发环境配置 Config config = wechatCertAutoConfig.createDefaultDevConfig(); - + // 方法2: 自定义配置 Config config = wechatCertAutoConfig.createAutoConfig( - "商户号", - "私钥路径", - "证书序列号", + "商户号", + "私钥路径", + "证书序列号", "APIv3密钥" ); - + 🔧 前置条件: 1. 微信商户平台已开启API安全功能 2. 已申请使用微信支付公钥 3. 私钥文件存在且路径正确 4. 网络连接正常 - + 📚 更多信息: https://pay.weixin.qq.com/doc/v3/merchant/4012153196 """; diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java index d71f89c..3e77732 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java @@ -16,6 +16,7 @@ import com.gxwebsoft.common.system.service.SettingService; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAConfig; import com.wechat.pay.java.service.payments.jsapi.JsapiService; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; @@ -41,6 +42,9 @@ public class SettingServiceImpl extends ServiceImpl impl @Resource private StringRedisTemplate stringRedisTemplate; + @Value("${spring.profiles.active:prod}") + private String activeProfile; + @Override public PageResult pageRel(SettingParam param) { PageParam page = new PageParam<>(param); @@ -120,20 +124,41 @@ public class SettingServiceImpl extends ServiceImpl impl final String merchantSerialNumber = jsonObject.getString("merchantSerialNumber"); final String apiV3key = jsonObject.getString("wechatApiKey"); if(config == null){ -// config = new RSAAutoCertificateConfig.Builder() -// .merchantId(mchId) -// .privateKeyFromPath("/Users/gxwebsoft/Documents/uploads/file/20230622/fb193d3bfff0467b83dc498435a4f433.pem") -// .merchantSerialNumber(merchantSerialNumber) -// .apiV3Key(apiV3key) -// .build(); - config = - new RSAConfig.Builder() + // 根据环境选择不同的证书路径配置 + if ("dev".equals(activeProfile)) { + // 开发环境:使用配置文件的upload-path拼接证书路径 - 租户ID 10550 + System.out.println("=== 开发环境:使用配置文件upload-path拼接证书路径 ==="); + String uploadPath = pathConfig.getUploadPath(); // 获取配置的upload-path + String tenantId = "10550"; // 租户ID + String certBasePath = uploadPath + "dev/wechat/" + tenantId + "/"; + String devPrivateKeyPath = certBasePath + "apiclient_key.pem"; + String devCertPath = certBasePath + "apiclient_cert.pem"; + + System.out.println("配置的upload-path: " + uploadPath); + System.out.println("证书基础路径: " + certBasePath); + System.out.println("私钥文件路径: " + devPrivateKeyPath); + System.out.println("证书文件路径: " + devCertPath); + + config = new RSAConfig.Builder() .merchantId("1246610101") - .privateKeyFromPath("/Users/gxwebsoft/cert/1246610101_20221225_cert/01ac632fea184e248d0375e9917063a4.pem") + .privateKeyFromPath(devPrivateKeyPath) .merchantSerialNumber("2903B872D5CA36E525FAEC37AEDB22E54ECDE7B7") - .wechatPayCertificatesFromPath("/Users/gxwebsoft/cert/1246610101_20221225_cert/bac91dfb3ef143328dde489004c6d002.pem") + .wechatPayCertificatesFromPath(devCertPath) + .build(); + System.out.println("开发环境证书路径配置完成"); + } else { + // 生产环境:使用数据库存储的路径 + System.out.println("=== 生产环境:使用数据库存储的证书路径 ==="); + config = new RSAConfig.Builder() + .merchantId(mchId) + .privateKeyFromPath(privateKey) + .merchantSerialNumber(merchantSerialNumber) + .wechatPayCertificatesFromPath(apiclientCert) .build(); + System.out.println("生产环境证书路径: " + privateKey); + } configMap.put(data.getTenantId().toString(),config); + System.out.println("当前环境: " + activeProfile); System.out.println("config = " + config); } if (service == null) { diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 6a12baa..6a5ee4e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -54,7 +54,7 @@ mqtt: config: # 开发环境接口 server-url: https://server.websoft.top/api - upload-path: /Users/gxwebsoft/Documents/uploads/ # window(D:\Temp) + upload-path: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/ # 项目资源目录 # 开发环境证书配置 certificate: