改造支付证书管理模块

This commit is contained in:
2025-07-26 20:47:43 +08:00
parent 9d61f9dc38
commit 2d2b913eb3
23 changed files with 2305 additions and 21 deletions

View File

@@ -0,0 +1,197 @@
package com.gxwebsoft.common.core.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 证书配置属性类
* 支持开发环境从classpath加载证书生产环境从Docker挂载卷加载证书
*
* @author 科技小王子
* @since 2024-07-26
*/
@Data
@Component
@ConfigurationProperties(prefix = "certificate")
public class CertificateProperties {
/**
* 证书加载模式
* CLASSPATH: 从classpath加载开发环境
* FILESYSTEM: 从文件系统加载(生产环境)
* VOLUME: 从Docker挂载卷加载容器环境
*/
private LoadMode loadMode = LoadMode.CLASSPATH;
/**
* Docker挂载卷证书根路径
*/
private String certRootPath = "/app/certs";
/**
* 开发环境证书路径前缀
*/
private String devCertPath = "certs/dev";
/**
* 微信支付证书配置
*/
private WechatPayConfig wechatPay = new WechatPayConfig();
/**
* 支付宝证书配置
*/
private AlipayConfig alipay = new AlipayConfig();
/**
* 证书加载模式枚举
*/
public enum LoadMode {
CLASSPATH, // 从classpath加载
FILESYSTEM, // 从文件系统加载
VOLUME // 从Docker挂载卷加载
}
/**
* 微信支付证书配置
*/
@Data
public static class WechatPayConfig {
/**
* 开发环境配置
*/
private DevConfig dev = new DevConfig();
/**
* 生产环境基础路径
*/
private String prodBasePath = "/file";
/**
* 微信支付证书目录名
*/
private String certDir = "wechat";
@Data
public static class DevConfig {
/**
* APIv3密钥
*/
private String apiV3Key;
/**
* 商户私钥证书文件名
*/
private String privateKeyFile = "apiclient_key.pem";
/**
* 商户证书文件名
*/
private String apiclientCertFile = "apiclient_cert.pem";
/**
* 微信支付平台证书文件名
*/
private String wechatpayCertFile = "wechatpay_cert.pem";
}
}
/**
* 支付宝证书配置
*/
@Data
public static class AlipayConfig {
/**
* 支付宝证书目录名
*/
private String certDir = "alipay";
/**
* 应用私钥文件名
*/
private String appPrivateKeyFile = "app_private_key.pem";
/**
* 应用公钥证书文件名
*/
private String appCertPublicKeyFile = "appCertPublicKey.crt";
/**
* 支付宝公钥证书文件名
*/
private String alipayCertPublicKeyFile = "alipayCertPublicKey.crt";
/**
* 支付宝根证书文件名
*/
private String alipayRootCertFile = "alipayRootCert.crt";
}
/**
* 获取证书文件的完整路径
*
* @param certType 证书类型wechat/alipay
* @param fileName 文件名
* @return 完整路径
*/
public String getCertificatePath(String certType, String fileName) {
switch (loadMode) {
case CLASSPATH:
return devCertPath + "/" + certType + "/" + fileName;
case FILESYSTEM:
return System.getProperty("user.dir") + "/certs/" + certType + "/" + fileName;
case VOLUME:
return certRootPath + "/" + certType + "/" + fileName;
default:
throw new IllegalArgumentException("不支持的证书加载模式: " + loadMode);
}
}
/**
* 获取微信支付证书路径
*
* @param fileName 文件名
* @return 完整路径
*/
public String getWechatPayCertPath(String fileName) {
return getCertificatePath(wechatPay.getCertDir(), fileName);
}
/**
* 获取支付宝证书路径
*
* @param fileName 文件名
* @return 完整路径
*/
public String getAlipayCertPath(String fileName) {
return getCertificatePath(alipay.getCertDir(), fileName);
}
/**
* 检查证书加载模式是否为classpath模式
*
* @return true if classpath mode
*/
public boolean isClasspathMode() {
return LoadMode.CLASSPATH.equals(loadMode);
}
/**
* 检查证书加载模式是否为文件系统模式
*
* @return true if filesystem mode
*/
public boolean isFilesystemMode() {
return LoadMode.FILESYSTEM.equals(loadMode);
}
/**
* 检查证书加载模式是否为挂载卷模式
*
* @return true if volume mode
*/
public boolean isVolumeMode() {
return LoadMode.VOLUME.equals(loadMode);
}
}

View File

@@ -0,0 +1,188 @@
package com.gxwebsoft.common.core.controller;
import com.gxwebsoft.common.core.service.CertificateHealthService;
import com.gxwebsoft.common.core.service.CertificateService;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.health.Health;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
/**
* 证书管理控制器
* 提供证书状态查询、健康检查等功能
*
* @author 科技小王子
* @since 2024-07-26
*/
@Slf4j
@Api(tags = "证书管理")
@RestController
@RequestMapping("/api/system/certificate")
public class CertificateController extends BaseController {
@Resource
private CertificateService certificateService;
@Resource
private CertificateHealthService certificateHealthService;
@ApiOperation("获取所有证书状态")
@GetMapping("/status")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<Map<String, Object>> getCertificateStatus() {
try {
Map<String, Object> status = certificateService.getAllCertificateStatus();
return success("获取证书状态成功", status);
} catch (Exception e) {
log.error("获取证书状态失败", e);
return fail("获取证书状态失败: " + e.getMessage());
}
}
@ApiOperation("证书健康检查")
@GetMapping("/health")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<Map<String, Object>> healthCheck() {
try {
Health health = certificateHealthService.health();
Map<String, Object> result = Map.of(
"status", health.getStatus().getCode(),
"details", health.getDetails()
);
return success("证书健康检查完成", result);
} catch (Exception e) {
log.error("证书健康检查失败", e);
return fail("证书健康检查失败: " + e.getMessage());
}
}
@ApiOperation("获取证书诊断信息")
@GetMapping("/diagnostic")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<Map<String, Object>> getDiagnosticInfo() {
try {
Map<String, Object> diagnostic = certificateHealthService.getDiagnosticInfo();
return success("获取证书诊断信息成功", diagnostic);
} catch (Exception e) {
log.error("获取证书诊断信息失败", e);
return fail("获取证书诊断信息失败: " + e.getMessage());
}
}
@ApiOperation("检查特定证书")
@GetMapping("/check/{certType}/{fileName}")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<Map<String, Object>> checkSpecificCertificate(
@ApiParam(value = "证书类型", example = "wechat") @PathVariable String certType,
@ApiParam(value = "文件名", example = "apiclient_key.pem") @PathVariable String fileName) {
try {
Map<String, Object> result = certificateHealthService.checkSpecificCertificate(certType, fileName);
return success("检查证书完成", result);
} catch (Exception e) {
log.error("检查证书失败: {}/{}", certType, fileName, e);
return fail("检查证书失败: " + e.getMessage());
}
}
@ApiOperation("验证证书文件")
@GetMapping("/validate/{certType}/{fileName}")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<CertificateService.CertificateInfo> validateCertificate(
@ApiParam(value = "证书类型", example = "wechat") @PathVariable String certType,
@ApiParam(value = "文件名", example = "apiclient_cert.pem") @PathVariable String fileName) {
try {
CertificateService.CertificateInfo certInfo =
certificateService.validateX509Certificate(certType, fileName);
if (certInfo != null) {
return success("证书验证成功", certInfo);
} else {
return fail("证书验证失败可能不是有效的X509证书");
}
} catch (Exception e) {
log.error("验证证书失败: {}/{}", certType, fileName, e);
return fail("验证证书失败: " + e.getMessage());
}
}
@ApiOperation("检查证书文件是否存在")
@GetMapping("/exists/{certType}/{fileName}")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<Boolean> checkCertificateExists(
@ApiParam(value = "证书类型", example = "alipay") @PathVariable String certType,
@ApiParam(value = "文件名", example = "appCertPublicKey.crt") @PathVariable String fileName) {
try {
boolean exists = certificateService.certificateExists(certType, fileName);
String message = exists ? "证书文件存在" : "证书文件不存在";
return success(message, exists);
} catch (Exception e) {
log.error("检查证书文件存在性失败: {}/{}", certType, fileName, e);
return fail("检查证书文件存在性失败: " + e.getMessage());
}
}
@ApiOperation("获取证书文件路径")
@GetMapping("/path/{certType}/{fileName}")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<String> getCertificatePath(
@ApiParam(value = "证书类型", example = "wechat") @PathVariable String certType,
@ApiParam(value = "文件名", example = "wechatpay_cert.pem") @PathVariable String fileName) {
try {
String path = certificateService.getCertificateFilePath(certType, fileName);
return success("获取证书路径成功", path);
} catch (Exception e) {
log.error("获取证书路径失败: {}/{}", certType, fileName, e);
return fail("获取证书路径失败: " + e.getMessage());
}
}
@ApiOperation("获取微信支付证书路径")
@GetMapping("/wechat-path/{fileName}")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<String> getWechatPayCertPath(
@ApiParam(value = "文件名", example = "apiclient_key.pem") @PathVariable String fileName) {
try {
String path = certificateService.getWechatPayCertPath(fileName);
return success("获取微信支付证书路径成功", path);
} catch (Exception e) {
log.error("获取微信支付证书路径失败: {}", fileName, e);
return fail("获取微信支付证书路径失败: " + e.getMessage());
}
}
@ApiOperation("获取支付宝证书路径")
@GetMapping("/alipay-path/{fileName}")
@PreAuthorize("hasAuthority('system:certificate:view')")
public ApiResult<String> getAlipayCertPath(
@ApiParam(value = "文件名", example = "appCertPublicKey.crt") @PathVariable String fileName) {
try {
String path = certificateService.getAlipayCertPath(fileName);
return success("获取支付宝证书路径成功", path);
} catch (Exception e) {
log.error("获取支付宝证书路径失败: {}", fileName, e);
return fail("获取支付宝证书路径失败: " + e.getMessage());
}
}
@ApiOperation("刷新证书缓存")
@PostMapping("/refresh")
@PreAuthorize("hasAuthority('system:certificate:manage')")
public ApiResult<String> refreshCertificateCache() {
try {
// 这里可以添加刷新证书缓存的逻辑
log.info("证书缓存刷新请求,操作用户: {}", getLoginUser().getUsername());
return success("证书缓存刷新成功");
} catch (Exception e) {
log.error("刷新证书缓存失败", e);
return fail("刷新证书缓存失败: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,227 @@
package com.gxwebsoft.common.core.service;
import com.gxwebsoft.common.core.config.CertificateProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* 证书健康检查服务
* 实现Spring Boot Actuator健康检查接口
*
* @author 科技小王子
* @since 2024-07-26
*/
@Slf4j
@Service
public class CertificateHealthService implements HealthIndicator {
private final CertificateService certificateService;
private final CertificateProperties certificateProperties;
public CertificateHealthService(CertificateService certificateService,
CertificateProperties certificateProperties) {
this.certificateService = certificateService;
this.certificateProperties = certificateProperties;
}
@Override
public Health health() {
try {
Map<String, Object> details = new HashMap<>();
boolean allHealthy = true;
// 检查微信支付证书
Map<String, Object> wechatHealth = checkWechatPayCertificates();
details.put("wechatPay", wechatHealth);
if (!(Boolean) wechatHealth.get("healthy")) {
allHealthy = false;
}
// 检查支付宝证书
Map<String, Object> alipayHealth = checkAlipayCertificates();
details.put("alipay", alipayHealth);
if (!(Boolean) alipayHealth.get("healthy")) {
allHealthy = false;
}
// 添加系统信息
details.put("loadMode", certificateProperties.getLoadMode());
details.put("certRootPath", certificateProperties.getCertRootPath());
if (allHealthy) {
return Health.up().withDetails(details).build();
} else {
return Health.down().withDetails(details).build();
}
} catch (Exception e) {
log.error("证书健康检查失败", e);
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
/**
* 检查微信支付证书健康状态
*/
private Map<String, Object> checkWechatPayCertificates() {
Map<String, Object> health = new HashMap<>();
boolean healthy = true;
Map<String, Object> certificates = new HashMap<>();
CertificateProperties.WechatPayConfig wechatConfig = certificateProperties.getWechatPay();
// 检查私钥证书
String privateKeyFile = wechatConfig.getDev().getPrivateKeyFile();
boolean privateKeyExists = certificateService.certificateExists("wechat", privateKeyFile);
certificates.put("privateKey", Map.of(
"file", privateKeyFile,
"exists", privateKeyExists,
"path", certificateService.getWechatPayCertPath(privateKeyFile)
));
if (!privateKeyExists) healthy = false;
// 检查商户证书
String apiclientCertFile = wechatConfig.getDev().getApiclientCertFile();
boolean apiclientCertExists = certificateService.certificateExists("wechat", apiclientCertFile);
certificates.put("apiclientCert", Map.of(
"file", apiclientCertFile,
"exists", apiclientCertExists,
"path", certificateService.getWechatPayCertPath(apiclientCertFile)
));
if (!apiclientCertExists) healthy = false;
// 检查微信支付平台证书
String wechatpayCertFile = wechatConfig.getDev().getWechatpayCertFile();
boolean wechatpayCertExists = certificateService.certificateExists("wechat", wechatpayCertFile);
certificates.put("wechatpayCert", Map.of(
"file", wechatpayCertFile,
"exists", wechatpayCertExists,
"path", certificateService.getWechatPayCertPath(wechatpayCertFile)
));
if (!wechatpayCertExists) healthy = false;
health.put("healthy", healthy);
health.put("certificates", certificates);
return health;
}
/**
* 检查支付宝证书健康状态
*/
private Map<String, Object> checkAlipayCertificates() {
Map<String, Object> health = new HashMap<>();
boolean healthy = true;
Map<String, Object> certificates = new HashMap<>();
CertificateProperties.AlipayConfig alipayConfig = certificateProperties.getAlipay();
// 检查应用私钥
String appPrivateKeyFile = alipayConfig.getAppPrivateKeyFile();
boolean appPrivateKeyExists = certificateService.certificateExists("alipay", appPrivateKeyFile);
certificates.put("appPrivateKey", Map.of(
"file", appPrivateKeyFile,
"exists", appPrivateKeyExists,
"path", certificateService.getAlipayCertPath(appPrivateKeyFile)
));
if (!appPrivateKeyExists) healthy = false;
// 检查应用公钥证书
String appCertPublicKeyFile = alipayConfig.getAppCertPublicKeyFile();
boolean appCertExists = certificateService.certificateExists("alipay", appCertPublicKeyFile);
certificates.put("appCertPublicKey", Map.of(
"file", appCertPublicKeyFile,
"exists", appCertExists,
"path", certificateService.getAlipayCertPath(appCertPublicKeyFile)
));
if (!appCertExists) healthy = false;
// 检查支付宝公钥证书
String alipayCertPublicKeyFile = alipayConfig.getAlipayCertPublicKeyFile();
boolean alipayCertExists = certificateService.certificateExists("alipay", alipayCertPublicKeyFile);
certificates.put("alipayCertPublicKey", Map.of(
"file", alipayCertPublicKeyFile,
"exists", alipayCertExists,
"path", certificateService.getAlipayCertPath(alipayCertPublicKeyFile)
));
if (!alipayCertExists) healthy = false;
// 检查支付宝根证书
String alipayRootCertFile = alipayConfig.getAlipayRootCertFile();
boolean rootCertExists = certificateService.certificateExists("alipay", alipayRootCertFile);
certificates.put("alipayRootCert", Map.of(
"file", alipayRootCertFile,
"exists", rootCertExists,
"path", certificateService.getAlipayCertPath(alipayRootCertFile)
));
if (!rootCertExists) healthy = false;
health.put("healthy", healthy);
health.put("certificates", certificates);
return health;
}
/**
* 获取详细的证书诊断信息
*/
public Map<String, Object> getDiagnosticInfo() {
Map<String, Object> diagnostic = new HashMap<>();
try {
// 基本系统信息
diagnostic.put("loadMode", certificateProperties.getLoadMode());
diagnostic.put("certRootPath", certificateProperties.getCertRootPath());
diagnostic.put("devCertPath", certificateProperties.getDevCertPath());
// 获取所有证书状态
diagnostic.put("certificateStatus", certificateService.getAllCertificateStatus());
// 健康检查结果
Health health = health();
diagnostic.put("healthStatus", health.getStatus().getCode());
diagnostic.put("healthDetails", health.getDetails());
} catch (Exception e) {
log.error("获取证书诊断信息失败", e);
diagnostic.put("error", e.getMessage());
}
return diagnostic;
}
/**
* 检查特定证书的详细信息
*/
public Map<String, Object> checkSpecificCertificate(String certType, String fileName) {
Map<String, Object> result = new HashMap<>();
try {
boolean exists = certificateService.certificateExists(certType, fileName);
String path = certificateService.getCertificateFilePath(certType, fileName);
result.put("certType", certType);
result.put("fileName", fileName);
result.put("exists", exists);
result.put("path", path);
if (exists && (fileName.endsWith(".crt") || fileName.endsWith(".pem"))) {
// 尝试验证证书
CertificateService.CertificateInfo certInfo =
certificateService.validateX509Certificate(certType, fileName);
result.put("certificateInfo", certInfo);
}
} catch (Exception e) {
log.error("检查证书失败: {}/{}", certType, fileName, e);
result.put("error", e.getMessage());
}
return result;
}
}

View File

@@ -0,0 +1,272 @@
package com.gxwebsoft.common.core.service;
import com.gxwebsoft.common.core.config.CertificateProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 证书管理服务
* 负责处理不同环境下的证书加载、验证和管理
*
* @author 科技小王子
* @since 2024-07-26
*/
@Slf4j
@Service
public class CertificateService {
private final CertificateProperties certificateProperties;
public CertificateService(CertificateProperties certificateProperties) {
this.certificateProperties = certificateProperties;
}
@PostConstruct
public void init() {
log.info("证书服务初始化,当前加载模式: {}", certificateProperties.getLoadMode());
log.info("证书根路径: {}", certificateProperties.getCertRootPath());
// 检查证书目录和文件
checkCertificateDirectories();
}
/**
* 获取证书文件的输入流
*
* @param certType 证书类型wechat/alipay
* @param fileName 文件名
* @return 输入流
* @throws IOException 文件读取异常
*/
public InputStream getCertificateInputStream(String certType, String fileName) throws IOException {
String certPath = certificateProperties.getCertificatePath(certType, fileName);
if (certificateProperties.isClasspathMode()) {
// 从classpath加载
Resource resource = new ClassPathResource(certPath);
if (!resource.exists()) {
throw new IOException("证书文件不存在: " + certPath);
}
log.debug("从classpath加载证书: {}", certPath);
return resource.getInputStream();
} else {
// 从文件系统加载
File file = new File(certPath);
if (!file.exists()) {
throw new IOException("证书文件不存在: " + certPath);
}
log.debug("从文件系统加载证书: {}", certPath);
return Files.newInputStream(file.toPath());
}
}
/**
* 获取证书文件路径
*
* @param certType 证书类型
* @param fileName 文件名
* @return 文件路径
*/
public String getCertificateFilePath(String certType, String fileName) {
return certificateProperties.getCertificatePath(certType, fileName);
}
/**
* 检查证书文件是否存在
*
* @param certType 证书类型
* @param fileName 文件名
* @return 是否存在
*/
public boolean certificateExists(String certType, String fileName) {
try {
String certPath = certificateProperties.getCertificatePath(certType, fileName);
if (certificateProperties.isClasspathMode()) {
Resource resource = new ClassPathResource(certPath);
return resource.exists();
} else {
File file = new File(certPath);
return file.exists() && file.isFile();
}
} catch (Exception e) {
log.error("检查证书文件存在性时出错: {}", e.getMessage());
return false;
}
}
/**
* 获取微信支付证书路径
*
* @param fileName 文件名
* @return 证书路径
*/
public String getWechatPayCertPath(String fileName) {
return certificateProperties.getWechatPayCertPath(fileName);
}
/**
* 获取支付宝证书路径
*
* @param fileName 文件名
* @return 证书路径
*/
public String getAlipayCertPath(String fileName) {
return certificateProperties.getAlipayCertPath(fileName);
}
/**
* 验证X509证书
*
* @param certType 证书类型
* @param fileName 文件名
* @return 证书信息
*/
public CertificateInfo validateX509Certificate(String certType, String fileName) {
try (InputStream inputStream = getCertificateInputStream(certType, fileName)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
CertificateInfo info = new CertificateInfo();
info.setSubject(cert.getSubjectDN().toString());
info.setIssuer(cert.getIssuerDN().toString());
info.setNotBefore(cert.getNotBefore());
info.setNotAfter(cert.getNotAfter());
info.setSerialNumber(cert.getSerialNumber().toString());
info.setValid(isValidDate(cert.getNotBefore(), cert.getNotAfter()));
return info;
} catch (Exception e) {
log.error("验证证书失败: {}/{}, 错误: {}", certType, fileName, e.getMessage());
return null;
}
}
/**
* 检查证书目录结构
*/
private void checkCertificateDirectories() {
String[] certTypes = {"wechat", "alipay"};
for (String certType : certTypes) {
if (!certificateProperties.isClasspathMode()) {
// 检查文件系统目录
String dirPath = certificateProperties.getCertificatePath(certType, "");
File dir = new File(dirPath);
if (!dir.exists()) {
log.warn("证书目录不存在: {}", dirPath);
} else {
log.info("证书目录存在: {}", dirPath);
}
}
}
}
/**
* 获取所有证书状态
*
* @return 证书状态映射
*/
public Map<String, Object> getAllCertificateStatus() {
Map<String, Object> status = new HashMap<>();
// 微信支付证书状态
Map<String, Object> wechatStatus = new HashMap<>();
CertificateProperties.WechatPayConfig wechatConfig = certificateProperties.getWechatPay();
wechatStatus.put("privateKey", getCertStatus("wechat", wechatConfig.getDev().getPrivateKeyFile()));
wechatStatus.put("apiclientCert", getCertStatus("wechat", wechatConfig.getDev().getApiclientCertFile()));
wechatStatus.put("wechatpayCert", getCertStatus("wechat", wechatConfig.getDev().getWechatpayCertFile()));
status.put("wechat", wechatStatus);
// 支付宝证书状态
Map<String, Object> alipayStatus = new HashMap<>();
CertificateProperties.AlipayConfig alipayConfig = certificateProperties.getAlipay();
alipayStatus.put("appPrivateKey", getCertStatus("alipay", alipayConfig.getAppPrivateKeyFile()));
alipayStatus.put("appCertPublicKey", getCertStatus("alipay", alipayConfig.getAppCertPublicKeyFile()));
alipayStatus.put("alipayCertPublicKey", getCertStatus("alipay", alipayConfig.getAlipayCertPublicKeyFile()));
alipayStatus.put("alipayRootCert", getCertStatus("alipay", alipayConfig.getAlipayRootCertFile()));
status.put("alipay", alipayStatus);
// 系统信息
Map<String, Object> systemInfo = new HashMap<>();
systemInfo.put("loadMode", certificateProperties.getLoadMode());
systemInfo.put("certRootPath", certificateProperties.getCertRootPath());
systemInfo.put("devCertPath", certificateProperties.getDevCertPath());
status.put("system", systemInfo);
return status;
}
/**
* 获取单个证书状态
*/
private Map<String, Object> getCertStatus(String certType, String fileName) {
Map<String, Object> status = new HashMap<>();
status.put("fileName", fileName);
status.put("exists", certificateExists(certType, fileName));
status.put("path", getCertificateFilePath(certType, fileName));
// 如果是.crt或.pem文件尝试验证证书
if (fileName.endsWith(".crt") || fileName.endsWith(".pem")) {
CertificateInfo certInfo = validateX509Certificate(certType, fileName);
status.put("certificateInfo", certInfo);
}
return status;
}
/**
* 检查日期是否有效
*/
private boolean isValidDate(Date notBefore, Date notAfter) {
Date now = new Date();
return now.after(notBefore) && now.before(notAfter);
}
/**
* 证书信息类
*/
public static class CertificateInfo {
private String subject;
private String issuer;
private Date notBefore;
private Date notAfter;
private String serialNumber;
private boolean valid;
// Getters and Setters
public String getSubject() { return subject; }
public void setSubject(String subject) { this.subject = subject; }
public String getIssuer() { return issuer; }
public void setIssuer(String issuer) { this.issuer = issuer; }
public Date getNotBefore() { return notBefore; }
public void setNotBefore(Date notBefore) { this.notBefore = notBefore; }
public Date getNotAfter() { return notAfter; }
public void setNotAfter(Date notAfter) { this.notAfter = notAfter; }
public String getSerialNumber() { return serialNumber; }
public void setSerialNumber(String serialNumber) { this.serialNumber = serialNumber; }
public boolean isValid() { return valid; }
public void setValid(boolean valid) { this.valid = valid; }
}
}

View File

@@ -7,7 +7,11 @@ import com.alipay.api.AlipayConstants;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.service.CertificateService;
import com.gxwebsoft.common.core.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
@@ -15,9 +19,11 @@ import javax.annotation.Resource;
/**
* 支付宝工具类
* 支持新的证书管理系统
* @author leng
*
*/
@Slf4j
@Component
public class AlipayConfigUtil {
private final StringRedisTemplate stringRedisTemplate;
@@ -30,8 +36,15 @@ public class AlipayConfigUtil {
public String alipayCertPublicKey;
public String alipayRootCert;
@Value("${spring.profiles.active}")
private String active;
@Resource
private ConfigProperties pathConfig;
@Resource
private CertificateService certificateService;
@Resource
private CertificateProperties certificateProperties;
public AlipayConfigUtil(StringRedisTemplate stringRedisTemplate){
this.stringRedisTemplate = stringRedisTemplate;
@@ -66,21 +79,59 @@ public class AlipayConfigUtil {
* 获取支付宝秘钥
*/
public JSONObject payment(Integer tenantId) {
System.out.println("tenantId = " + tenantId);
log.debug("获取支付宝配置租户ID: {}", tenantId);
String key = "cache".concat(tenantId.toString()).concat(":setting:payment");
System.out.println("key = " + key);
log.debug("Redis缓存key: {}", key);
String cache = stringRedisTemplate.opsForValue().get(key);
if (cache == null) {
throw new BusinessException("支付方式未配置");
}
// 解析json数据
JSONObject payment = JSON.parseObject(cache.getBytes());
this.config = payment;
this.appId = payment.getString("alipayAppId");
this.privateKey = payment.getString("privateKey");
this.appCertPublicKey = pathConfig.getUploadPath() + "file" + payment.getString("appCertPublicKey");
this.alipayCertPublicKey = pathConfig.getUploadPath() + "file" + payment.getString("alipayCertPublicKey");
this.alipayRootCert = pathConfig.getUploadPath() + "file" + payment.getString("alipayRootCert");
try {
if (active.equals("dev")) {
// 开发环境:使用证书服务获取证书路径
CertificateProperties.AlipayConfig alipayConfig = certificateProperties.getAlipay();
this.appCertPublicKey = certificateService.getAlipayCertPath(alipayConfig.getAppCertPublicKeyFile());
this.alipayCertPublicKey = certificateService.getAlipayCertPath(alipayConfig.getAlipayCertPublicKeyFile());
this.alipayRootCert = certificateService.getAlipayCertPath(alipayConfig.getAlipayRootCertFile());
log.info("开发环境支付宝证书路径:");
log.info("应用证书: {}", this.appCertPublicKey);
log.info("支付宝证书: {}", this.alipayCertPublicKey);
log.info("根证书: {}", this.alipayRootCert);
// 检查证书文件是否存在
if (!certificateService.certificateExists("alipay", alipayConfig.getAppCertPublicKeyFile())) {
throw new RuntimeException("支付宝应用证书文件不存在");
}
if (!certificateService.certificateExists("alipay", alipayConfig.getAlipayCertPublicKeyFile())) {
throw new RuntimeException("支付宝公钥证书文件不存在");
}
if (!certificateService.certificateExists("alipay", alipayConfig.getAlipayRootCertFile())) {
throw new RuntimeException("支付宝根证书文件不存在");
}
} else {
// 生产环境:使用上传的证书文件
this.appCertPublicKey = pathConfig.getUploadPath() + "file" + payment.getString("appCertPublicKey");
this.alipayCertPublicKey = pathConfig.getUploadPath() + "file" + payment.getString("alipayCertPublicKey");
this.alipayRootCert = pathConfig.getUploadPath() + "file" + payment.getString("alipayRootCert");
log.info("生产环境支付宝证书路径:");
log.info("应用证书: {}", this.appCertPublicKey);
log.info("支付宝证书: {}", this.alipayCertPublicKey);
log.info("根证书: {}", this.alipayRootCert);
}
} catch (Exception e) {
log.error("配置支付宝证书路径失败: {}", e.getMessage(), e);
throw new RuntimeException("支付宝证书配置失败: " + e.getMessage());
}
return payment;
}

View File

@@ -2,6 +2,8 @@ package com.gxwebsoft.common.system.controller;
import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.service.CertificateService;
import com.gxwebsoft.common.core.utils.CommonUtil;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.utils.RequestUtil;
@@ -22,6 +24,7 @@ import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@@ -31,19 +34,21 @@ import java.util.Map;
import java.util.Set;
@Slf4j
@Api(tags = "微信Native支付接口")
@RestController
@RequestMapping("/api/system/wx-native-pay")
public class WxNativePayController extends BaseController {
@Value("${spring.profiles.active}")
String active;
// 保留用于兼容性的静态配置(开发环境备用)
public static String merchantId = "1246610101";
public static String privateKeyPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/apiclient_key.pem";
public static String merchantSerialNumber = "48749613B40AA8F1D768583FC352358E13EB5AF0";
public static String apiV3Key = "zGufUcqa7ovgxRL0kF5OlPr482EZwtn9";
@Resource
private RedisUtil redisUtil;
private Config wxPayConfig;
@Resource
private ConfigProperties config;
@Resource
@@ -52,6 +57,10 @@ public class WxNativePayController extends BaseController {
private RequestUtil requestUtil;
@Resource
private OrderService orderService;
@Resource
private CertificateService certificateService;
@Resource
private CertificateProperties certificateProperties;
@ApiOperation("生成付款码")
@@ -106,23 +115,46 @@ public class WxNativePayController extends BaseController {
if (build != null) {
return build;
}
String key = "Payment:wxPay:".concat(tenantId.toString());
final Payment payment = redisUtil.get(key, Payment.class);
System.out.println("payment = " + payment);
String apiclientKey = config.getUploadPath().concat("/file").concat(payment.getApiclientKey());
// 开发环境配置
if(active.equals("dev")){
apiclientKey = privateKeyPath;
log.debug("获取支付配置: {}", payment);
String apiclientKey;
try {
if (active.equals("dev")) {
// 开发环境:使用证书服务获取证书路径
apiclientKey = certificateService.getWechatPayCertPath(
certificateProperties.getWechatPay().getDev().getPrivateKeyFile()
);
log.info("开发环境使用证书路径: {}", apiclientKey);
// 检查证书文件是否存在
if (!certificateService.certificateExists("wechat",
certificateProperties.getWechatPay().getDev().getPrivateKeyFile())) {
throw new RuntimeException("微信支付私钥证书文件不存在");
}
} else {
// 生产环境:使用上传的证书文件
apiclientKey = config.getUploadPath().concat("/file").concat(payment.getApiclientKey());
log.info("生产环境使用证书路径: {}", apiclientKey);
}
Config wxConfig = new RSAAutoCertificateConfig.Builder()
.merchantId(payment.getMchId())
.privateKeyFromPath(apiclientKey)
.merchantSerialNumber(payment.getMerchantSerialNumber())
.apiV3Key(payment.getApiKey())
.build();
log.info("微信支付配置创建成功");
WxNativeUtil.addConfig(tenantId, wxConfig);
return wxConfig;
} catch (Exception e) {
log.error("创建微信支付配置失败: {}", e.getMessage(), e);
throw new RuntimeException("微信支付配置失败: " + e.getMessage());
}
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(payment.getMchId())
.privateKeyFromPath(apiclientKey)
.merchantSerialNumber(payment.getMerchantSerialNumber())
.apiV3Key(payment.getApiKey())
.build();
System.out.println("config = " + config);
WxNativeUtil.addConfig(tenantId, config);
return config;
}
@ApiModelProperty("异步通知")

View File

@@ -41,3 +41,9 @@ config:
bucketName: oss-gxwebsoft
bucketDomain: https://oss.wsdns.cn
aliyunDomain: https://oss-gxwebsoft.oss-cn-shenzhen.aliyuncs.com
# 生产环境证书配置
certificate:
# 生产环境使用挂载卷模式
load-mode: VOLUME
cert-root-path: /app/certs

View File

@@ -102,3 +102,30 @@ config:
aliyunDomain: https://oss-gxwebsoft.oss-cn-shenzhen.aliyuncs.com
# 证书配置
certificate:
# 证书加载模式: CLASSPATH, FILESYSTEM, VOLUME
load-mode: CLASSPATH
# Docker挂载卷证书路径
cert-root-path: /app/certs
# 开发环境证书路径前缀
dev-cert-path: certs/dev
# 微信支付证书配置
wechat-pay:
dev:
api-v3-key: "zGufUcqa7ovgxRL0kF5OlPr482EZwtn9"
private-key-file: "apiclient_key.pem"
apiclient-cert-file: "apiclient_cert.pem"
wechatpay-cert-file: "wechatpay_cert.pem"
prod-base-path: "/file"
cert-dir: "wechat"
# 支付宝证书配置
alipay:
cert-dir: "alipay"
app-private-key-file: "app_private_key.pem"
app-cert-public-key-file: "appCertPublicKey.crt"
alipay-cert-public-key-file: "alipayCertPublicKey.crt"
alipay-root-cert-file: "alipayRootCert.crt"

View File

@@ -0,0 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIDqDCCApCgAwIBAgIQICISFUe9Mqj+pWqb2fs9jDANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjIxMjE1MDExMjA0WhcNMjMxMjE1MDExMjA0WjB6
MQswCQYDVQQGEwJDTjEVMBMGA1UECgwM5rKZ566x546v5aKDMQ8wDQYDVQQLDAZBbGlwYXkxQzBB
BgNVBAMMOuaUr+S7mOWunSjkuK3lm70p572R57uc5oqA5pyv5pyJ6ZmQ5YWs5Y+4LTIwODgxMDIx
NzUyMDcyOTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQwLlj0xdcIy4rQLNg73Nl
zjSqKV7cjE1aieYpEQ/0aNZxVBqm0wxSiXgDR+NjyuD2VadER6caFDhB9bO2lLroI9xHjc1Griwc
4XAN9JA2kh/3irzQxKCBzrW0/6gpEIIVFSK+reCAmD0vtMTLOIChUhl4OSDYWLpUzFmICa9tRNGD
hPUSr08JFn2DhXW3eUnOzJEkf4qxn3HIDlKwBb50CJhjkuCIWxKBxRe1oQy2zEd6tezp00xvW4DQ
OqkSHEW641sUbA7ntckdOF7X5FQGKqKqwrFxTSblixk/mkBxAkIj7dM+k9AqFtrhnzn01sP21MYx
PPOhddGk3indwe9DAgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEA
xJXsNQ5rDQBwn8BXYuSaj1Hkw8W3wKRr55Y2fDoQIx2kek9kI53PRvIVAdxlrLxZ6z+lTFrkThJ/
rsH84ffkDvfSTca3QCB6c01jveQ+qGvGQSx/HPu92DMT/hJ0V8LstLlq6Q1r8hTvcjHOPyE9l3vF
I0Ozbe2F3TCOFFjtEjHHOw9bo+tB8gtiY/bfidPbTtCClTTyPRTE8MuzQqDABhGl3khL4aue9h8g
x0i0yAn15VBf9ruqlTrTnhuI5ak7AOwdxjKaMwVbTCy838rQjt4xKMD80h2go/6MLRGidnbeiTU2
Uq3PVgEJo2kxE8ZSD7x4JtskZD07YCSA5DZtuw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDszCCApugAwIBAgIQIBkIGbgVxq210KxLJ+YA/TANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UE
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxJTAjBgNVBAsMHENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IHRlc3QxNjA0BgNVBAMMLUFudCBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgUjEgdGVzdDAeFw0xOTA4MTkxMTE2MDBaFw0yNDA4MDExMTE2MDBaMIGRMQswCQYDVQQGEwJD
TjEbMBkGA1UECgwSQW50IEZpbmFuY2lhbCB0ZXN0MSUwIwYDVQQLDBxDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eSB0ZXN0MT4wPAYDVQQDDDVBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IENsYXNzIDIgUjEgdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMh4FKYO
ZyRQHD6eFbPKZeSAnrfjfU7xmS9Yoozuu+iuqZlb6Z0SPLUqqTZAFZejOcmr07ln/pwZxluqplxC
5+B48End4nclDMlT5HPrDr3W0frs6Xsa2ZNcyil/iKNB5MbGll8LRAxntsKvZZj6vUTMb705gYgm
VUMILwi/ZxKTQqBtkT/kQQ5y6nOZsj7XI5rYdz6qqOROrpvS/d7iypdHOMIM9Iz9DlL1mrCykbBi
t25y+gTeXmuisHUwqaRpwtCGK4BayCqxRGbNipe6W73EK9lBrrzNtTr9NaysesT/v+l25JHCL9tG
wpNr1oWFzk4IHVOg0ORiQ6SUgxZUTYcCAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
DQEBCwUAA4IBAQBWThEoIaQoBX2YeRY/I8gu6TYnFXtyuCljANnXnM38ft+ikhE5mMNgKmJYLHvT
yWWWgwHoSAWEuml7EGbE/2AK2h3k0MdfiWLzdmpPCRG/RJHk6UB1pMHPilI+c0MVu16OPpKbg5Vf
LTv7dsAB40AzKsvyYw88/Ezi1osTXo6QQwda7uefvudirtb8FcQM9R66cJxl3kt1FXbpYwheIm/p
j1mq64swCoIYu4NrsUYtn6CV542DTQMI5QdXkn+PzUUly8F6kDp+KpMNd0avfWNL5+O++z+F5Szy
1CPta1D7EQ/eYmMP+mOQ35oifWIoFCpN6qQVBS/Hob1J/UUyg7BW
-----END CERTIFICATE-----

View File

@@ -0,0 +1,88 @@
-----BEGIN CERTIFICATE-----
MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG
EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw
MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO
UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT
V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti
W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ
MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b
53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI
pDoiVhsLwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj
YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0
MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV
BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j
aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk
rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2
xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp
dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6
vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl
YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1
Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H
DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98
SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG
PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe
9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC
AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90
tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy
nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf
tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq
JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3
IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW
05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41
T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI
kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop
PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N
1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y
jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02
77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi
kT9qhqn+lw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG
EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0
WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE
CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp
YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU
WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt
rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ
4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2
zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg
wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH
Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF
BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM
E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg
MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq
MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp
bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv
b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV
nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5
4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg
wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw
WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN
z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g
KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA
uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF
MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp
emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3
U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I
UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn
DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU
1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX
Yf4Zr0fJsGuv
-----END CERTIFICATE-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDjzCCAnegAwIBAgIQICISFPMNDPadFdV3VF7atzANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjIxMjE0MTEzNjE3WhcNMjMxMjE5MTEzNjE3WjBh
MQswCQYDVQQGEwJDTjEVMBMGA1UECgwM5rKZ566x546v5aKDMQ8wDQYDVQQLDAZBbGlwYXkxKjAo
BgNVBAMMITIwODgxMDIxNzUyMDcyOTItMjAxNjA5MTEwMDQ4OTQ4NjCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAJFjmRNQkJ1d2kWZ4bn8WvIUWKu2+wMDQ5nOMaIGtKo+bx3o1RaAWYct
XJL82GkdUr+JpiBy7W1iFl0quZJIo2n9tyxsTGTswq1mtYJVKonHELxN1L2Xz9PjU8wlzQwxb2Rm
JlW2/SpUGaRiZxzYJHhHXbqvPH8D/xG+x6Hwq9zEF/ZIDMCLi5wiXhK7KFXDOBFYdOw0zwCmGIeY
73htk56kay1HoTjwACvZzkw8ff5FRA76/5/7ZEj6R6Hga/LMmYJXfntPPYW/wuMiBI7rU5f4s6El
S1A2uwK4+kbepg9klOYR2Lg30SNz4hj4k8KNtoeWnzrTlWoZj3SfDErJuuMCAwEAAaMSMBAwDgYD
VR0PAQH/BAQDAgTwMA0GCSqGSIb3DQEBCwUAA4IBAQC9YAgw5uwHUgY73t8eABW8LzrhLoUafN/j
WG6QataRgaTHbNCuCz5yWTMmD7hZGmb8NuZzaLOPD+/0yM5nz+w/nc+Emc6hzTCrBVtFX80nnM3j
lIDBRGJRS2JlyrwL80DxoVCbY7JLkSRpGhc9RYLrNfPjpxhxchJ/8V1JU21rL5GKSdaR2YJDvANi
Bth321Q0G6djxfLPjx3zXp8VTGDdhRZjblJ7EddK4kaQ3RKTm4+UivUYMMQ+esD8NnoHTGvDXRCi
rqd+EtAZZ84yqW7YKKTjsh9a3tLBFwFMc2A2WM3s6fXtrFAiffsXwcyqaKTXibVTFE9t2sTUUaPF
IoJu
-----END CERTIFICATE-----

View File

@@ -0,0 +1,8 @@
-----BEGIN PRIVATE KEY-----
# 这是支付宝应用私钥文件的占位符
# 请将实际的支付宝应用私钥内容替换到这里
# 获取方式:
# 1. 登录支付宝开放平台
# 2. 进入应用详情页
# 3. 在"开发设置"中下载应用私钥
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEKzCCAxOgAwIBAgIUSHSWE7QKqPHXaFg/w1I1jhPrWvAwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjQwNTExMDk0MzIzWhcNMjkwNTEwMDk0MzIzWjCBhDETMBEGA1UEAwwK
MTI0NjYxMDEwMTEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL
DCfljZflroHluILnvZHlrr/kv6Hmga/np5HmioDmnInpmZDlhazlj7gxCzAJBgNV
BAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAJSGQstwTNKfEUWGGNRdzAG691PKkpa78IV7SNAaAWdBUohvSGQB
Hxg2JcTjnNifqxWAVj302u0+OEPETQ+teTLeLgRfGp4b8WBKdibn9RzZD964xGhM
NkcMEwUxdqfBK28kGaKYW0zBifkzS1LDGuEVmUo9jE7pAuzDz5mJwcd1fZs4NsjD
7O60QLw4SZCXINW6IYVc41Ln+RlY2XPkm/keBydjrfvMI7Z+DqW/TEWOWshNycYr
3hqVeipz2FnUwK4ruGxEOqTXhYtn0QtvYaMcrfcXJ1U+zuwtZf+kh3RI/Lk+y2rJ
kfnuxZZ+P5K2oG+hcBapYS3q15kmf9RpMH0CAwEAAaOBuTCBtjAJBgNVHRMEAjAA
MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2
Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD
MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC
MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQCK
sgR2Wgb9wyyLX7ltlGXDqT44aMc3n5KI02LXv0mBD1aR4m5TFjlMzJIW2DIe01LF
yxVsUsoGIpjnAkmQOdNPL3tnCfl3bWqdNDDH9B711llNe5y1i4IYOcObhX08dEQd
vBnzuZ7/kH/t2h8q7rd7hqpQ5ZtU2xEY6ZlnohGyzNgVsDkLJI4b9iKRqOxRPVhs
GGbGKrv3JAYiFouSeH/m04xMWARFKhPoWduIeSWEJZmszWfkUBvPXo26+0YOKBVN
5gSkjioeXEX2T4/9K1SHx/iTzWvgN9MjlIJNujbg3Vz4PFU6aw2b8eK3Y0juto96
2uoUN1fLIqxNOz2E4iSJ
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCUhkLLcEzSnxFF
hhjUXcwBuvdTypKWu/CFe0jQGgFnQVKIb0hkAR8YNiXE45zYn6sVgFY99NrtPjhD
xE0PrXky3i4EXxqeG/FgSnYm5/Uc2Q/euMRoTDZHDBMFMXanwStvJBmimFtMwYn5
M0tSwxrhFZlKPYxO6QLsw8+ZicHHdX2bODbIw+zutEC8OEmQlyDVuiGFXONS5/kZ
WNlz5Jv5HgcnY637zCO2fg6lv0xFjlrITcnGK94alXoqc9hZ1MCuK7hsRDqk14WL
Z9ELb2GjHK33FydVPs7sLWX/pId0SPy5PstqyZH57sWWfj+StqBvoXAWqWEt6teZ
Jn/UaTB9AgMBAAECggEAb1Nvj5OeUaUfShBoXg3sU0O0DR9i3w8CCttMyYcklCO3
XEKlbSgWCYzUpI7DSu/rSdOHUStOSdOAUvM5m824cbNtpKMwjWB+fWFyzFjDNhtR
NO0jctXlPT3Ep/jaaoV1K/pQKLqwfIj5BUw4YlGRvTL2Ulpt59vp8FQZMIm8MOcw
rNwYcUbMPaNKk4q3GF0LGvzW8k+S6wAWcAbx1KINRsLE0127o+shjGIlBiZgMJGZ
nTMz4xdvVbojsMhdM8aEhq6GtmSHgBFKjETQPXiOjRDCGOM5yC/9R/9WsMGJmJ4m
6Ec/RM4k9TZlnMZFsOZYO8S/kM+xgQUcAD8uGT1UgQKBgQDDGVZiqsDjudFcRkG/
5pJN9PAC/Dk0Wzt6uRPZIhyFo2tDC/uL210Z5QR4hhB2nUSK8ANfAnepTotNzPHO
DC/sO2NzLuZz5EZTLeg9ij9BZDK+0/6AiBT2XdBKR/uGZAffjFCDh+ujm44lbrRK
7MUb9LtvDjPru1WVR0WhpFIwXQKBgQDC4xTQv6x3cPSW2SEglLVrl9CA68yO1g4T
MphCav64Cl9UDk1ov5C2SCvshFbWlIBv2g7tqb/bUk8nj42GuZWBu1spkUt2y7HS
eO89BmnaRNkVtWT8GtSMYYrYYAd23IGiOHPQqMnw/6HXkpjonpBa9c9CfEPwNtdq
84pgqed+oQKBgC6rV/PAPuX6pC87iyzZffPx/JvqM9DnZgIEVdAiDcqV/emK60BY
WBwCoaAnCbcmBahqo5PNpkw0wrP4q3sLhUcwKaj69huQ5pWtLJnUAS+mRVFKqt2a
L9GDPXkXYP6T3SJHkVb1Y5O+eTFRGwW1P61hTJjTP+5K4L0V0H1LLnHtAoGAEDBU
1lJVvUZAyxcWTWKM/3cI9uyffW4ClU2qoDnLFvalnJHjlEP1fW7ZVzhXDlQfpyrx
+oQTT+CyepLOKtbXuIMbu4Q6RI//IYCyPtt9h4gYkFkVHmwMI+0mX3r6o8EFc7hE
xpx+yeoyQ3oGAazKSQQKR3eTHS0xD81TPVxfwoECgYEAvBi3fPvIQ08pxk6kxj+S
bypHo06JHT1Fi8pmKtKCGLduK85dCeBZqHmsorWC/qg4RgCFWFFKfrFTGTxC4nf8
MRQHmKxq+SAh4SvFgRDA0lyaUWmw7H/JpolbBDIGnXhoDI0CmQU3s2xsQdJnNPIL
azgaJXtOu+wr1MPR7Ij5OTU=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEFDCCAvygAwIBAgIUSjIxWE6Ttq53ggB00H6t6sy34iMwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMwOTE5MTUxNTQ4WhcNMjgwOTE3MTUxNTQ4WjBuMRgwFgYDVQQDDA9U
ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl
bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo
ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5AOeoNuRReVPfOodE
TTE/wq96OK/7MI1lkwgtRlOIqwFGbGPxPx+wiLweWOWAeGrd0h+YuktIZezLhMhB
8pkJBzxz4U+zoQ9n3yY4CgDUBeAO8eNHhEQTTOTFUIvIlRxyr0EVuTHNP7pIkxA6
gUvajWjJFbvU393vDHA9RflWrBNw1qVjkPPUx6axizD3wS8f77bwuspEWGTza/pS
g96HWUwFh9OSlQNPOjPG4km2YRQwGhoRMlLeZsUB+a0PtV0HI/B0TbY5u2EmPt0R
uNZLmcwImtiANYmySww6gp5uo4+im0P/kHKynPrW1SkS+8M9JP5T7Xck3bnHNv4S
9UOPAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB
kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv
aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4
RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0
MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAHd42YyvxvvjYEIkCURHweCSQpWQrdsnP
AU2rcVzOx0kDxjq7+W2HkIkYeAZfE8pyBs5c39tgK+qospiDsKa9WYeBNyIRcCvN
q9ObLqSV4Cy/8lQMNh4Q37cdc3X+pAlTr7MtKka8ZcXYvbMBqus0dfJZayZvW7Ak
nnaXXJ4k7urgHOGYsZlZ+HC+DC/sYoN3DXzvg3XPlL0SNEQH0cWrRbaQnpOKVMk5
TptbeNHKJfUxVW5jh4GtBFqvLeiOruY+gFYg7UkCKWo9ZIYe7/mEvJeHYh3acTTl
DOl3L0fR5KR7vqFMwZEUZxVOs6K2ANSCr57OQPBV++MG4DPr3yOhCQ==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,147 @@
package com.gxwebsoft.common.core.service;
import com.gxwebsoft.common.core.config.CertificateProperties;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import javax.annotation.Resource;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* 证书服务测试类
*
* @author 科技小王子
* @since 2024-07-26
*/
@SpringBootTest
@ActiveProfiles("dev")
public class CertificateServiceTest {
@Resource
private CertificateService certificateService;
@Resource
private CertificateProperties certificateProperties;
@Resource
private CertificateHealthService certificateHealthService;
@Test
public void testCertificateServiceInitialization() {
assertNotNull(certificateService);
assertNotNull(certificateProperties);
assertEquals(CertificateProperties.LoadMode.CLASSPATH, certificateProperties.getLoadMode());
}
@Test
public void testWechatCertificateExists() {
// 测试微信支付证书文件是否存在
assertTrue(certificateService.certificateExists("wechat", "apiclient_key.pem"));
assertTrue(certificateService.certificateExists("wechat", "apiclient_cert.pem"));
assertTrue(certificateService.certificateExists("wechat", "wechatpay_cert.pem"));
}
@Test
public void testAlipayCertificateExists() {
// 测试支付宝证书文件是否存在
assertTrue(certificateService.certificateExists("alipay", "appCertPublicKey.crt"));
assertTrue(certificateService.certificateExists("alipay", "alipayCertPublicKey.crt"));
assertTrue(certificateService.certificateExists("alipay", "alipayRootCert.crt"));
assertTrue(certificateService.certificateExists("alipay", "app_private_key.pem"));
}
@Test
public void testGetCertificatePaths() {
// 测试获取证书路径
String wechatKeyPath = certificateService.getWechatPayCertPath("apiclient_key.pem");
assertNotNull(wechatKeyPath);
assertTrue(wechatKeyPath.contains("wechat"));
assertTrue(wechatKeyPath.contains("apiclient_key.pem"));
String alipayAppCertPath = certificateService.getAlipayCertPath("appCertPublicKey.crt");
assertNotNull(alipayAppCertPath);
assertTrue(alipayAppCertPath.contains("alipay"));
assertTrue(alipayAppCertPath.contains("appCertPublicKey.crt"));
}
@Test
public void testGetAllCertificateStatus() {
// 测试获取所有证书状态
Map<String, Object> status = certificateService.getAllCertificateStatus();
assertNotNull(status);
assertTrue(status.containsKey("wechat"));
assertTrue(status.containsKey("alipay"));
assertTrue(status.containsKey("system"));
@SuppressWarnings("unchecked")
Map<String, Object> wechatStatus = (Map<String, Object>) status.get("wechat");
assertTrue(wechatStatus.containsKey("privateKey"));
assertTrue(wechatStatus.containsKey("apiclientCert"));
assertTrue(wechatStatus.containsKey("wechatpayCert"));
@SuppressWarnings("unchecked")
Map<String, Object> alipayStatus = (Map<String, Object>) status.get("alipay");
assertTrue(alipayStatus.containsKey("appPrivateKey"));
assertTrue(alipayStatus.containsKey("appCertPublicKey"));
assertTrue(alipayStatus.containsKey("alipayCertPublicKey"));
assertTrue(alipayStatus.containsKey("alipayRootCert"));
}
@Test
public void testCertificateHealthCheck() {
// 测试证书健康检查
assertNotNull(certificateHealthService);
Map<String, Object> diagnostic = certificateHealthService.getDiagnosticInfo();
assertNotNull(diagnostic);
assertTrue(diagnostic.containsKey("loadMode"));
assertTrue(diagnostic.containsKey("certificateStatus"));
assertTrue(diagnostic.containsKey("healthStatus"));
}
@Test
public void testValidateX509Certificate() {
// 测试X509证书验证对于.crt和.pem文件
CertificateService.CertificateInfo wechatCertInfo =
certificateService.validateX509Certificate("wechat", "apiclient_cert.pem");
if (wechatCertInfo != null) {
assertNotNull(wechatCertInfo.getSubject());
assertNotNull(wechatCertInfo.getIssuer());
assertNotNull(wechatCertInfo.getSerialNumber());
}
CertificateService.CertificateInfo alipayCertInfo =
certificateService.validateX509Certificate("alipay", "appCertPublicKey.crt");
if (alipayCertInfo != null) {
assertNotNull(alipayCertInfo.getSubject());
assertNotNull(alipayCertInfo.getIssuer());
assertNotNull(alipayCertInfo.getSerialNumber());
}
}
@Test
public void testNonExistentCertificate() {
// 测试不存在的证书文件
assertFalse(certificateService.certificateExists("wechat", "non_existent.pem"));
assertFalse(certificateService.certificateExists("alipay", "non_existent.crt"));
}
@Test
public void testCertificateConfiguration() {
// 测试证书配置
CertificateProperties.WechatPayConfig wechatConfig = certificateProperties.getWechatPay();
assertNotNull(wechatConfig);
assertEquals("wechat", wechatConfig.getCertDir());
assertEquals("apiclient_key.pem", wechatConfig.getDev().getPrivateKeyFile());
CertificateProperties.AlipayConfig alipayConfig = certificateProperties.getAlipay();
assertNotNull(alipayConfig);
assertEquals("alipay", alipayConfig.getCertDir());
assertEquals("app_private_key.pem", alipayConfig.getAppPrivateKeyFile());
}
}