Files
template-10559/java/payment/service/WxPayConfigService.java
赵忠林 5749fab9e8 feat(payment): 初始化支付模块核心代码
- 添加支付常量类PaymentConstants,定义支付状态、微信、支付宝、银联等相关常量
- 创建微信支付类型常量类WechatPayType,支持JSAPI、NATIVE、H5、APP支付方式
- 新增支付控制器PaymentController,提供创建支付、查询状态、退款等统一接口
- 实现支付回调控制器PaymentNotifyController,处理微信、支付宝、银联异步通知
- 添加支付请求数据传输对象PaymentRequest,支持多种支付方式参数校验
- 定义支付响应、状态更新请求等相关DTO类- 集成Swagger注解,完善接口文档说明- 添加参数校验和异常处理机制,确保支付流程安全可靠
2025-11-03 12:31:47 +08:00

339 lines
13 KiB
Java
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.

package com.gxwebsoft.payment.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.service.CertificateService;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.utils.WxNativeUtil;
import com.gxwebsoft.common.system.entity.Payment;
import com.gxwebsoft.common.system.param.PaymentParam;
import com.gxwebsoft.common.system.service.PaymentService;
import com.gxwebsoft.payment.exception.PaymentException;
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.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* 微信支付配置服务
* 负责管理微信支付的配置信息和证书
*
* @author 科技小王子
* @since 2025-01-26
*/
@Slf4j
@Service
public class WxPayConfigService {
@Resource
private RedisUtil redisUtil;
@Resource
private CertificateService certificateService;
@Resource
private CertificateProperties certificateProperties;
@Resource
private PaymentService paymentService;
@Value("${spring.profiles.active:dev}")
private String activeProfile;
/**
* 获取支付配置信息Payment对象- 公开方法
*
* @param tenantId 租户ID
* @return 支付配置信息
* @throws PaymentException 配置获取失败时抛出
*/
public Payment getPaymentConfigForStrategy(Integer tenantId) throws PaymentException {
if (tenantId == null) {
throw PaymentException.paramError("租户ID不能为空");
}
return getPaymentConfig(tenantId);
}
/**
* 获取微信支付配置
*
* @param tenantId 租户ID
* @return 微信支付配置
* @throws PaymentException 配置获取失败时抛出
*/
public Config getWxPayConfig(Integer tenantId) throws PaymentException {
if (tenantId == null) {
throw PaymentException.paramError("租户ID不能为空");
}
// 先从缓存获取已构建的配置
Config cachedConfig = WxNativeUtil.getConfig(tenantId);
if (cachedConfig != null) {
log.debug("从缓存获取微信支付配置成功租户ID: {}", tenantId);
return cachedConfig;
}
// 构建新的配置
Config newConfig = buildWxPayConfig(tenantId);
// 缓存配置
WxNativeUtil.addConfig(tenantId, newConfig);
log.info("微信支付配置创建并缓存成功租户ID: {}", tenantId);
return newConfig;
}
/**
* 构建微信支付配置
*/
private Config buildWxPayConfig(Integer tenantId) throws PaymentException {
try {
// 获取支付配置信息
Payment payment = getPaymentConfig(tenantId);
// 获取证书文件路径
String certificatePath = getCertificatePath(tenantId, payment);
// 创建微信支付配置对象
return createWxPayConfig(payment, certificatePath);
} catch (Exception e) {
if (e instanceof PaymentException) {
throw e;
}
throw PaymentException.systemError("构建微信支付配置失败: " + e.getMessage(), e);
}
}
/**
* 获取支付配置信息
* 优先从缓存获取,缓存没有则查询数据库,最后兜底到开发环境测试配置
*/
private Payment getPaymentConfig(Integer tenantId) throws PaymentException {
String cacheKey = "Payment:wxPay:" + tenantId;
Payment payment = redisUtil.get(cacheKey, Payment.class);
System.out.println("payment = " + payment);
if (payment != null) {
log.debug("从缓存获取支付配置成功租户ID: {}", tenantId);
// return payment;
}
// 缓存中没有,尝试从数据库查询
try {
final PaymentParam paymentParam = new PaymentParam();
paymentParam.setType(102);
paymentParam.setTenantId(tenantId);
log.debug("查询数据库支付配置,参数: type=102, tenantId={}", tenantId);
payment = paymentService.getByType(paymentParam);
log.debug("数据库查询结果: {}", payment != null ? "找到配置" : "未找到配置");
if (payment != null) {
log.info("从数据库获取支付配置成功租户ID: {},将缓存配置", tenantId);
// 将查询到的配置缓存起来缓存1天
redisUtil.set(cacheKey, payment, 1L, TimeUnit.DAYS);
return payment;
} else {
log.warn("数据库中未找到支付配置租户ID: {}, type: 102", tenantId);
}
} catch (Exception e) {
log.error("从数据库查询支付配置失败租户ID: {},错误: {}", tenantId, e.getMessage(), e);
// 抛出更详细的异常信息
throw PaymentException.systemError("查询支付配置失败租户ID: " + tenantId + ",错误: " + e.getMessage(), e);
}
// 数据库也没有配置
if (!"dev".equals(activeProfile)) {
throw PaymentException.systemError("微信支付配置未找到租户ID: " + tenantId + ",请检查数据库配置", null);
}
log.debug("开发环境模式将使用测试配置租户ID: {}", tenantId);
// 开发环境返回测试Payment配置
return createDevTestPayment(tenantId);
}
/**
* 获取证书文件路径
*/
private String getCertificatePath(Integer tenantId, Payment payment) throws PaymentException {
if ("dev".equals(activeProfile)) {
return getDevCertificatePath(tenantId);
} else {
return getProdCertificatePath(payment);
}
}
/**
* 获取开发环境证书路径
*/
private String getDevCertificatePath(Integer tenantId) throws PaymentException {
try {
// 根据租户ID构建证书路径
String certPath = "dev/wechat/" + tenantId + "/apiclient_key.pem";
ClassPathResource resource = new ClassPathResource(certPath);
if (!resource.exists()) {
throw PaymentException.systemError("开发环境微信支付证书文件不存在: " + certPath, null);
}
String absolutePath = resource.getFile().getAbsolutePath();
log.debug("开发环境证书路径: {}", absolutePath);
return absolutePath;
} catch (IOException e) {
throw PaymentException.systemError("获取开发环境证书路径失败: " + e.getMessage(), e);
}
}
/**
* 获取生产环境证书路径
*/
private String getProdCertificatePath(Payment payment) throws PaymentException {
if (payment == null || payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty()) {
throw PaymentException.systemError("生产环境支付配置或证书密钥文件为空", null);
}
try {
// 使用微信支付证书路径
String certificatePath = certificateService.getWechatPayCertPath(payment.getApiclientKey());
if (certificatePath == null) {
throw PaymentException.systemError("证书文件路径获取失败,证书文件: " + payment.getApiclientKey(), null);
}
log.debug("生产环境证书路径: {}", certificatePath);
return certificatePath;
} catch (Exception e) {
throw PaymentException.systemError("获取生产环境证书路径失败: " + e.getMessage(), e);
}
}
/**
* 创建微信支付配置对象
*/
private Config createWxPayConfig(Payment payment, String certificatePath) throws PaymentException {
try {
if ("dev".equals(activeProfile) && payment == null) {
// 开发环境测试配置
return createDevTestConfig(certificatePath);
} else if (payment != null) {
// 正常配置
return createNormalConfig(payment, certificatePath);
} else {
throw PaymentException.systemError("无法创建微信支付配置:配置信息不完整", null);
}
} catch (Exception e) {
if (e instanceof PaymentException) {
throw e;
}
throw PaymentException.systemError("创建微信支付配置对象失败: " + e.getMessage(), e);
}
}
/**
* 创建开发环境测试Payment配置
*/
private Payment createDevTestPayment(Integer tenantId) {
Payment testPayment = new Payment();
testPayment.setTenantId(tenantId);
testPayment.setType(102); // Native支付
testPayment.setAppId("wxa67c676fc445590e"); // 开发环境测试AppID
testPayment.setMchId("1246610101"); // 开发环境测试商户号
testPayment.setMerchantSerialNumber("48749613B40AA8F1D768583FC352358E13EB5AF0");
testPayment.setApiKey(certificateProperties.getWechatPay().getDev().getApiV3Key());
testPayment.setNotifyUrl("http://frps-10550.s209.websoft.top/api/payment/notify");
testPayment.setName("微信Native支付-开发环境");
testPayment.setStatus(true); // 启用
log.info("创建开发环境测试Payment配置租户ID: {}, AppID: {}, 商户号: {}",
tenantId, testPayment.getAppId(), testPayment.getMchId());
return testPayment;
}
/**
* 创建开发环境测试配置
*/
private Config createDevTestConfig(String certificatePath) throws PaymentException {
String testMerchantId = "1246610101";
String testMerchantSerialNumber = "48749613B40AA8F1D768583FC352358E13EB5AF0";
String testApiV3Key = certificateProperties.getWechatPay().getDev().getApiV3Key();
if (testApiV3Key == null || testApiV3Key.trim().isEmpty()) {
throw PaymentException.systemError("开发环境APIv3密钥未配置", null);
}
log.info("使用开发环境测试配置");
log.debug("测试商户号: {}", testMerchantId);
log.debug("测试序列号: {}", testMerchantSerialNumber);
return new RSAAutoCertificateConfig.Builder()
.merchantId(testMerchantId)
.privateKeyFromPath(certificatePath)
.merchantSerialNumber(testMerchantSerialNumber)
.apiV3Key(testApiV3Key)
.build();
}
/**
* 创建正常配置
*/
private Config createNormalConfig(Payment payment, String certificatePath) throws PaymentException {
// 验证配置完整性
validatePaymentConfig(payment);
log.info("使用数据库支付配置");
log.debug("商户号: {}", payment.getMchId());
return new RSAAutoCertificateConfig.Builder()
.merchantId(payment.getMchId())
.privateKeyFromPath(certificatePath)
.merchantSerialNumber(payment.getMerchantSerialNumber())
.apiV3Key(payment.getApiKey())
.build();
}
/**
* 验证支付配置完整性
*/
private void validatePaymentConfig(Payment payment) throws PaymentException {
if (payment == null) {
throw PaymentException.systemError("支付配置为空", null);
}
if (payment.getMchId() == null || payment.getMchId().trim().isEmpty()) {
throw PaymentException.systemError("商户号(mchId)未配置", null);
}
if (payment.getMerchantSerialNumber() == null || payment.getMerchantSerialNumber().trim().isEmpty()) {
throw PaymentException.systemError("商户证书序列号(merchantSerialNumber)未配置", null);
}
if (payment.getApiKey() == null || payment.getApiKey().trim().isEmpty()) {
throw PaymentException.systemError("APIv3密钥(apiKey)未配置", null);
}
if (payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty()) {
throw PaymentException.systemError("证书文件名(apiclientKey)未配置", null);
}
log.debug("支付配置验证通过租户ID: {}, 商户号: {}", payment.getTenantId(), payment.getMchId());
}
/**
* 清除指定租户的配置缓存
*
* @param tenantId 租户ID
*/
public void clearConfigCache(Integer tenantId) {
WxNativeUtil.addConfig(tenantId, null);
log.info("清除微信支付配置缓存租户ID: {}", tenantId);
}
}