feat 同步商业版代码

This commit is contained in:
bootx
2025-04-27 21:46:54 +08:00
parent 36a6901f8e
commit 3ec34a191c
1067 changed files with 19660 additions and 26251 deletions

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dromara.daxpay</groupId>
<artifactId>daxpay-open-channel</artifactId>
<version>3.0.0.beta5</version>
</parent>
<artifactId>daxpay-open-channel-union</artifactId>
<description>云闪付支付通道实现</description>
<dependencies>
</dependencies>
</project>

View File

@@ -0,0 +1,17 @@
package org.dromara.daxpay.channel.union;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.ComponentScan;
/**
* 云闪付支付实现
* @author xxm
* @since 2024/7/16
*/
@ConfigurationPropertiesScan
@MapperScan(annotationClass = Mapper.class)
@ComponentScan
public class UnionPayChannelApp {
}

View File

@@ -0,0 +1,16 @@
package org.dromara.daxpay.channel.union.code;
/**
* 云闪付常量编码
* @author xxm
* @since 2024/9/6
*/
public interface UnionPayCode {
/**
* 签名类型
*/
interface SignType{
String RSA2 = "RSA2";
}
}

View File

@@ -0,0 +1,37 @@
package org.dromara.daxpay.channel.union.controller;
import cn.bootx.platform.core.annotation.IgnoreAuth;
import org.dromara.daxpay.service.service.assist.PaymentAssistService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 银联回调通知
* @author xxm
* @since 2024/9/6
*/
@Validated
@IgnoreAuth
@Tag(name = "银联回调通知")
@RestController
@RequestMapping("/unipay/callback/{mchNo}/{AppId}")
@RequiredArgsConstructor
public class UnionPayCallbackController {
private final PaymentAssistService paymentAssistService;
@Operation(summary = "银联回调")
@PostMapping("/union")
public String UnionPayNotify(@PathVariable("mchNo") String mchNo, @PathVariable("AppId") String appId, HttpServletRequest request) {
paymentAssistService.initMchAndApp(mchNo, appId);
return "";
}
}

View File

@@ -0,0 +1,47 @@
package org.dromara.daxpay.channel.union.controller;
import cn.bootx.platform.core.annotation.OperateLog;
import cn.bootx.platform.core.annotation.RequestGroup;
import cn.bootx.platform.core.annotation.RequestPath;
import cn.bootx.platform.core.rest.Res;
import cn.bootx.platform.core.rest.result.Result;
import org.dromara.daxpay.channel.union.param.config.UnionPayConfigParam;
import org.dromara.daxpay.channel.union.result.UnionPayConfigResult;
import org.dromara.daxpay.channel.union.service.config.UnionPayConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 云闪付支付配置
* @author xxm
* @since 2024/9/12
*/
@Validated
@Tag(name = "云闪付支付配置")
@RestController
@RequestGroup(groupCode = "UnionPayConfig", groupName = "云闪付配置", moduleCode = "UnionPay", moduleName = "(DaxPay通道)云闪付")
@RequestMapping("/union/pay/config")
@RequiredArgsConstructor
public class UnionPayConfigController {
private final UnionPayConfigService configService;
@RequestPath("获取配置")
@Operation(summary = "获取配置")
@GetMapping("/findById")
public Result<UnionPayConfigResult> findById(@NotNull(message = "ID不可为空") Long id) {
return Res.ok(configService.findById(id));
}
@RequestPath("新增或更新")
@Operation(summary = "新增或更新")
@PostMapping("/saveOrUpdate")
@OperateLog(title = "新增或更新云闪付支付配置", businessType = OperateLog.BusinessType.UPDATE, saveParam = true)
public Result<Void> saveOrUpdate(@RequestBody UnionPayConfigParam param) {
configService.saveOrUpdate(param);
return Res.ok();
}
}

View File

@@ -0,0 +1,37 @@
package org.dromara.daxpay.channel.union.controller;
import cn.bootx.platform.core.annotation.IgnoreAuth;
import org.dromara.daxpay.service.service.assist.PaymentAssistService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
/**
* 银联
* @author xxm
* @since 2024/9/6
*/
@Validated
@IgnoreAuth
@Tag(name = "同步通知跳转控制器")
@RestController
@RequestMapping("/unipay/return/{mchNo}/{AppId}")
@RequiredArgsConstructor
public class UnionPayRedirectUrlController {
private final PaymentAssistService paymentAssistService;
@Operation(summary = "银联同步跳转通知")
@GetMapping("/union")
public ModelAndView alipay(@PathVariable("mchNo") String mchNo, @PathVariable("AppId") String appId, HttpServletRequest request){
paymentAssistService.initMchAndApp(mchNo, appId);
String redirect = "";
return new ModelAndView("redirect:"+redirect);
}
}

View File

@@ -0,0 +1,26 @@
package org.dromara.daxpay.channel.union.convert;
import org.dromara.daxpay.channel.union.result.UnionPayConfigResult;
import org.dromara.daxpay.channel.union.entity.config.UnionPayConfig;
import org.dromara.daxpay.channel.union.param.config.UnionPayConfigParam;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author xxm
* @since 2024/9/6
*/
@Mapper
public interface UnionPayConfigConvert {
UnionPayConfigConvert CONVERT = Mappers.getMapper(UnionPayConfigConvert.class);
UnionPayConfigResult toResult(UnionPayConfig unionPayConfig);
UnionPayConfig toEntity(UnionPayConfigParam param);
UnionPayConfig copy(UnionPayConfig unionPayConfig);
}

View File

@@ -0,0 +1,122 @@
package org.dromara.daxpay.channel.union.entity.config;
import cn.bootx.platform.common.mybatisplus.function.ToResult;
import cn.bootx.platform.core.annotation.BigField;
import cn.bootx.platform.core.util.JsonUtil;
import org.dromara.daxpay.channel.union.code.UnionPayCode;
import org.dromara.daxpay.channel.union.convert.UnionPayConfigConvert;
import org.dromara.daxpay.channel.union.result.UnionPayConfigResult;
import org.dromara.daxpay.core.enums.ChannelEnum;
import org.dromara.daxpay.service.entity.config.ChannelConfig;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Objects;
/**
* 云闪付支付配置
*
* @author xxm
* @since 2022/3/11
*/
@Data
@Accessors(chain = true)
public class UnionPayConfig implements ToResult<UnionPayConfigResult> {
/** 主键 */
private Long id;
/** 云闪付商户号 */
private String unionMachId;
/** 是否启用, 只影响支付和退款操作 */
private Boolean enable;
/**
* 商户收款账号
*/
private String seller;
/**
* 签名类型
* @see UnionPayCode.SignType
*/
private String signType;
/**
* 是否为证书签名
*/
private boolean certSign;
/**
* 应用私钥证书 字符串
*/
@BigField
private String keyPrivateCert;
/**
* 私钥证书对应的密码
*/
private String keyPrivateCertPwd;
/**
* 中级证书
*/
@BigField
private String acpMiddleCert;
/**
* 根证书
*/
private String acpRootCert;
/** 是否沙箱环境 */
private boolean sandbox;
/** 商户号 */
private String mchNo;
/** 商户AppId */
private String appId;
public Boolean getEnable() {
return Objects.equals(true,enable);
}
/**
* 转换为通道配置
*/
public ChannelConfig toChannelConfig() {
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.setId(this.getId());
channelConfig.setOutMchNo(this.getUnionMachId());
channelConfig.setAppId(this.getAppId());
channelConfig.setMchNo(this.getMchNo());
channelConfig.setEnable(this.getEnable());
channelConfig.setChannel(ChannelEnum.UNION_PAY.getCode());
UnionPayConfig copy = UnionPayConfigConvert.CONVERT.copy(this);
// 清空不需要序列化的字段
copy.setId(null).setAppId(null).setEnable(null).setUnionMachId(null).setAppId(null).setMchNo(null);
String jsonStr = JsonUtil.toJsonStr(copy);
channelConfig.setExt(jsonStr);
return channelConfig;
}
/**
* 从通道配置转换为支付宝配置
*/
public static UnionPayConfig convertConfig(ChannelConfig channelConfig) {
UnionPayConfig config = JsonUtil.toBean(channelConfig.getExt(), UnionPayConfig.class);
config.setId(channelConfig.getId())
.setUnionMachId(channelConfig.getOutMchNo())
.setEnable(channelConfig.isEnable());
return config;
}
/**
* 转换
*/
@Override
public UnionPayConfigResult toResult() {
return UnionPayConfigConvert.CONVERT.toResult(this);
}
}

View File

@@ -0,0 +1,88 @@
package org.dromara.daxpay.channel.union.param.config;
import org.dromara.daxpay.channel.union.code.UnionPayCode;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 云闪付支付配置参数
* @author xxm
* @since 2024/3/9
*/
@Data
@Accessors(chain = true)
@Schema(title = "云闪付支付配置参数")
public class UnionPayConfigParam {
/** 主键 */
@Schema(description = "主键")
private Long id;
/** 银联商户号 */
@Schema(description = "银联商户号")
private String unionMachId;
/** 是否启用, 只影响支付和退款操作 */
@Schema(description = "是否启用")
private Boolean enable;
@Schema(description = "支付限额")
private Integer limitAmount;
/**
* 商户收款账号
*/
@Schema(description = "商户收款账号")
private String seller;
/**
* 签名类型
* @see UnionPayCode.SignType
*/
@Schema(description = "签名类型")
public String signType;
/**
* 是否为证书签名
*/
@Schema(description = "是否为证书签名")
private boolean certSign;
/**
* 应用私钥证书
*/
@Schema(description = "应用私钥证书")
private String keyPrivateCert;
/**
* 私钥证书对应的密码
*/
@Schema(description = "私钥证书对应的密码")
private String keyPrivateCertPwd;
/**
* 中级证书
*/
@Schema(description = "中级证书")
private String acpMiddleCert;
/**
* 根证书
*/
@Schema(description = "根证书")
private String acpRootCert;
/** 是否沙箱环境 */
@Schema(description = "是否沙箱环境")
private boolean sandbox;
/** 商户号 */
@NotBlank(message = "商户号不可为空")
@Schema(description = "商户号")
private String mchNo;
/** 商户AppId */
@Schema(description = "商户AppId")
@NotBlank(message = "商户AppId不可为空")
private String appId;
}

View File

@@ -0,0 +1,15 @@
package org.dromara.daxpay.channel.union.param.pay;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 云闪付支付参数
* @author xxm
* @since 2024/3/6
*/
@Data
@Schema(title = "云闪付支付参数")
public class UnionPayParam {
}

View File

@@ -0,0 +1,75 @@
package org.dromara.daxpay.channel.union.result;
import cn.bootx.platform.common.jackson.sensitive.SensitiveInfo;
import org.dromara.daxpay.service.common.result.MchResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @author xxm
* @since 2022/3/11
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "云闪付配置")
public class UnionPayConfigResult extends MchResult {
/** 商户号 */
@Schema(description = "商户号")
private String unionMachId;
/** 是否启用, 只影响支付和退款操作 */
@Schema(description = "是否启用")
private Boolean enable;
/**
* 商户收款账号
*/
@Schema(description = "商户收款账号")
private String seller;
/**
* 签名类型
*/
@Schema(description = "签名类型")
public String signType;
/**
* 是否为证书签名
*/
@Schema(description = "是否为证书签名")
private boolean certSign;
/**
* 应用私钥证书 字符串
*/
@SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15)
@Schema(description = "应用私钥证书")
private String keyPrivateCert;
/**
* 私钥证书对应的密码
*/
@SensitiveInfo(value = SensitiveInfo.SensitiveType.PASSWORD)
@Schema(description = "私钥证书对应的密码")
private String keyPrivateCertPwd;
/**
* 中级证书
*/
@SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15)
@Schema(description = "中级证书")
private String acpMiddleCert;
/**
* 根证书
*/
@SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15)
@Schema(description = "根证书")
private String acpRootCert;
/** 是否沙箱环境 */
@Schema(description = "是否沙箱环境")
private boolean sandbox;
}

View File

@@ -0,0 +1,117 @@
package org.dromara.daxpay.channel.union.service.config;
import org.dromara.daxpay.channel.union.result.UnionPayConfigResult;
import org.dromara.daxpay.channel.union.convert.UnionPayConfigConvert;
import org.dromara.daxpay.channel.union.entity.config.UnionPayConfig;
import org.dromara.daxpay.channel.union.param.config.UnionPayConfigParam;
import org.dromara.daxpay.core.enums.ChannelEnum;
import org.dromara.daxpay.core.exception.ConfigNotEnableException;
import org.dromara.daxpay.core.exception.DataErrorException;
import org.dromara.daxpay.service.common.cache.ChannelConfigCacheService;
import org.dromara.daxpay.core.context.MchAppLocal;
import org.dromara.daxpay.service.common.local.MchContextLocal;
import org.dromara.daxpay.service.common.local.PaymentContextLocal;
import org.dromara.daxpay.service.dao.config.ChannelConfigManager;
import org.dromara.daxpay.service.entity.config.ChannelConfig;
import org.dromara.daxpay.service.service.config.PlatformConfigService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 银联支付
* @author xxm
* @since 2024/9/6
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UnionPayConfigService {
private final ChannelConfigManager channelConfigManager;
private final ChannelConfigCacheService channelConfigCacheService;
private final PlatformConfigService platformConfigService;
/**
* 查询
*/
public UnionPayConfigResult findById(Long id) {
return channelConfigManager.findById(id)
.map(UnionPayConfig::convertConfig)
.map(UnionPayConfig::toResult)
.orElseThrow(() -> new ConfigNotEnableException("支付宝配置不存在"));
}
/**
* 新增或更新
*/
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdate(UnionPayConfigParam param){
if (param.getId() == null){
this.save(param);
} else {
this.update(param);
}
}
/**
* 添加
*/
public void save(UnionPayConfigParam param) {
UnionPayConfig entity = UnionPayConfigConvert.CONVERT.toEntity(param);
ChannelConfig channelConfig = entity.toChannelConfig();
// 如果运营端使用, 商户号写入上下文中
MchContextLocal.setMchNo(channelConfig.getMchNo());
// 判断商户和应用下是否存在该配置
if (channelConfigManager.existsByAppIdAndChannel(channelConfig.getAppId(), channelConfig.getChannel())){
throw new DataErrorException("该应用下已存在云闪付配置, 请勿重新添加");
}
channelConfigManager.save(channelConfig);
}
/**
* 更新
*/
public void update(UnionPayConfigParam param){
ChannelConfig channelConfig = channelConfigManager.findById(param.getId())
.orElseThrow(() -> new ConfigNotEnableException("支付宝配置不存在"));
// 通道配置 --转换--> 支付宝配置 ----> 从更新参数赋值 --转换--> 通道配置 ----> 保存更新
UnionPayConfig unionPayConfig = UnionPayConfig.convertConfig(channelConfig);
BeanUtil.copyProperties(param, unionPayConfig, CopyOptions.create().ignoreNullValue());
ChannelConfig channelConfigParam = unionPayConfig.toChannelConfig();
// 手动清空一下默认的数据版本号
channelConfigParam.setVersion(null);
BeanUtil.copyProperties(channelConfigParam, channelConfig, CopyOptions.create().ignoreNullValue());
channelConfigManager.updateById(channelConfig);
}
/**
* 获取支付宝支付配置
*/
public UnionPayConfig getUnionPayConfig(){
MchAppLocal mchAppInfo = PaymentContextLocal.get().getMchAppInfo();
ChannelConfig channelConfig = channelConfigCacheService.getMchChannelConfig(mchAppInfo.getAppId(), ChannelEnum.ALIPAY.getCode());
return UnionPayConfig.convertConfig(channelConfig);
}
/**
* 获取支步通知地址
*/
public String getNotifyUrl() {
var mchAppInfo = PaymentContextLocal.get().getMchAppInfo();
return StrUtil.format("{}/unipay/callback/{}/{}/union",mchAppInfo.getGatewayServiceUrl(), mchAppInfo.getMchNo(),mchAppInfo.getAppId());
}
/**
* 获取同步通知地址
*/
public String getReturnUrl() {
MchAppLocal mchAppInfo = PaymentContextLocal.get().getMchAppInfo();
return StrUtil.format("{}/unipay/return/{}/{}/union",mchAppInfo.getGatewayServiceUrl(), mchAppInfo.getMchNo(),mchAppInfo.getAppId());
}
}