From d7869040fe7b10ab88600537d78132cc6a8198d5 Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Sun, 14 Jun 2026 10:55:33 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=B8=85=E7=90=86=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E4=B8=AD=E7=9A=84'=E5=BC=80=E6=BA=90=E7=89=88'?= =?UTF-8?q?=E8=AF=B4=E6=98=8E=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mch/AlipayChannelMerchantController.java | 9 ++++ .../config/AlipayChannelMerchantManager.java | 8 ++++ .../AlipayIsvChannelMerchantCreateParam.java | 38 +++++++++++++++++ .../mch/AlipayChannelMerchantService.java | 41 +++++++++++++++++++ .../basic/PayRouteBasicConfigService.java | 5 +-- .../route/basic/PayRouteBasicMatcher.java | 3 +- .../runtime/PayRouteProductResolver.java | 3 +- .../PayRouteStrategyCapabilitySupport.java | 4 +- .../i18n/en-US/error/channel/alipay.json | 1 + .../i18n/zh-CN/error/channel/alipay.json | 1 + 10 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/param/mch/AlipayIsvChannelMerchantCreateParam.java diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayChannelMerchantController.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayChannelMerchantController.java index 9954d4384..0a2954649 100644 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayChannelMerchantController.java +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayChannelMerchantController.java @@ -4,6 +4,7 @@ import org.dromara.daxpay.platform.core.annotation.PermCode; import org.dromara.daxpay.platform.core.rest.Res; import org.dromara.daxpay.platform.core.rest.result.Result; import org.dromara.daxpay.channel.alipay.param.mch.AlipayDirectChannelMerchantCreateParam; +import org.dromara.daxpay.channel.alipay.param.mch.AlipayIsvChannelMerchantCreateParam; import org.dromara.daxpay.channel.alipay.result.config.AlipayChannelMerchantResult; import org.dromara.daxpay.channel.alipay.service.mch.AlipayChannelMerchantService; import io.swagger.v3.oas.annotations.Operation; @@ -35,6 +36,14 @@ public class AlipayChannelMerchantController { return Res.ok(alipayChannelMerchantService.findByChannelMchNo(channelMchNo)); } + @PermCode(code = "add", nameCn = "商户通道商户新增", nameEn = "Merchant Channel Merchant Add") + @Operation(summary = "创建支付宝服务商通道商户") + @PostMapping("/create") + public Result create(@RequestBody @Validated AlipayIsvChannelMerchantCreateParam param) { + alipayChannelMerchantService.create(param); + return Res.ok(); + } + @PermCode(code = "add", nameCn = "商户通道商户新增", nameEn = "Merchant Channel Merchant Add") @Operation(summary = "创建支付宝直连通道商户") @PostMapping("/create-direct") diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantManager.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantManager.java index 607168a1b..e981d39ff 100644 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantManager.java +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantManager.java @@ -6,4 +6,12 @@ import org.springframework.stereotype.Repository; @Repository public class AlipayChannelMerchantManager extends BaseManager { + + /// 校验同一支付宝服务商应用下是否已绑定该子商户号 + public boolean existsByIsvAppIdAndAlipayUserId(String isvAppId, String alipayUserId) { + return lambdaQuery() + .eq(AlipayChannelMerchant::getIsvAppId, isvAppId) + .eq(AlipayChannelMerchant::getAlipayUserId, alipayUserId) + .exists(); + } } diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/param/mch/AlipayIsvChannelMerchantCreateParam.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/param/mch/AlipayIsvChannelMerchantCreateParam.java new file mode 100644 index 000000000..c5f1dac2a --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/param/mch/AlipayIsvChannelMerchantCreateParam.java @@ -0,0 +1,38 @@ +package org.dromara.daxpay.channel.alipay.param.mch; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +@Schema(title = "支付宝服务商通道商户创建参数") +public class AlipayIsvChannelMerchantCreateParam { + + @Schema(description = "商户号") + @NotBlank(message = "{validation.field.mchNo.notBlank}") + private String mchNo; + + @Schema(description = "通道商户名称") + @NotBlank(message = "{validation.field.channelMerchantName.notBlank}") + private String channelMerchantName; + + @Schema(description = "所属支付产品") + @NotBlank(message = "{validation.field.product.notBlank}") + private String product; + + /// 支付宝服务商应用ID + @Schema(description = "支付宝服务商应用ID") + @NotBlank(message = "{validation.field.isvAppId.notBlank}") + private String isvAppId; + + @Schema(description = "支付宝商家用户ID(2088开头)") + @NotBlank(message = "{validation.field.alipayUserId.notBlank}") + private String alipayUserId; + + /// 应用授权令牌,服务商代商户调用接口的凭据 + @Schema(description = "应用授权令牌") + @NotBlank(message = "{validation.field.appAuthToken.notBlank}") + private String appAuthToken; +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayChannelMerchantService.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayChannelMerchantService.java index 95c17fb60..9b8803840 100644 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayChannelMerchantService.java +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayChannelMerchantService.java @@ -1,12 +1,16 @@ package org.dromara.daxpay.channel.alipay.service.mch; +import org.dromara.daxpay.channel.alipay.dao.app.AlipayIsvAppManager; import org.dromara.daxpay.channel.alipay.dao.config.AlipayChannelMerchantManager; import org.dromara.daxpay.channel.alipay.entity.config.AlipayChannelMerchant; import org.dromara.daxpay.channel.alipay.param.mch.AlipayDirectChannelMerchantCreateParam; +import org.dromara.daxpay.channel.alipay.param.mch.AlipayIsvChannelMerchantCreateParam; import org.dromara.daxpay.channel.alipay.result.config.AlipayChannelMerchantResult; import org.dromara.daxpay.payment.channel.dao.mch.ChannelMerchantManager; import org.dromara.daxpay.payment.channel.entity.mch.ChannelMerchant; +import org.dromara.daxpay.platform.core.code.CommonErrorCode; import org.dromara.daxpay.platform.core.enums.channel.ChannelMerchantSourceEnum; +import org.dromara.daxpay.platform.core.exception.BizInfoException; import org.dromara.daxpay.platform.core.exception.DataNotExistException; import cn.hutool.core.util.IdUtil; import lombok.RequiredArgsConstructor; @@ -21,7 +25,43 @@ public class AlipayChannelMerchantService { private final ChannelMerchantManager channelMerchantManager; private final AlipayChannelMerchantManager alipayChannelMerchantManager; + private final AlipayIsvAppManager alipayIsvAppManager; + /// 创建支付宝服务商通道商户 + @Transactional(rollbackFor = Exception.class) + public void create(AlipayIsvChannelMerchantCreateParam param) { + var isvApp = alipayIsvAppManager.findByAliAppId(param.getIsvAppId()) + .orElseThrow(() -> new DataNotExistException("error.channel.alipay.appNotFound")); + // 校验同一应用下子商户号不重复 + if (alipayChannelMerchantManager.existsByIsvAppIdAndAlipayUserId( + isvApp.getAliAppId(), param.getAlipayUserId())) { + throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.alipay.subMerchantDuplicate"); + } + // 生成通道商户号:前缀 AISV + 雪花ID(无分隔符,符合 TradeNoGenerateUtil 约定) + String channelMchNo = "AISV" + IdUtil.getSnowflakeNextId(); + // 写通用通道商户主表 + ChannelMerchant channelMerchant = new ChannelMerchant(); + channelMerchant.setId(IdUtil.getSnowflakeNextId()); + channelMerchant.setMchNo(param.getMchNo()); + channelMerchant.setChannelMchNo(channelMchNo); + channelMerchant.setChannelMerchantName(param.getChannelMerchantName()); + channelMerchant.setProduct(param.getProduct()); + channelMerchant.setSource(ChannelMerchantSourceEnum.MANUAL.getCode()); + channelMerchant.setEnable(true); + channelMerchantManager.save(channelMerchant); + // 写支付宝扩展表(含服务商专属字段) + AlipayChannelMerchant alipayChannelMerchant = new AlipayChannelMerchant(); + alipayChannelMerchant.setId(IdUtil.getSnowflakeNextId()); + alipayChannelMerchant.setMchNo(param.getMchNo()); + alipayChannelMerchant.setChannelMchNo(channelMchNo); + alipayChannelMerchant.setProduct(param.getProduct()); + alipayChannelMerchant.setIsvAppId(isvApp.getAliAppId()); + alipayChannelMerchant.setAlipayUserId(param.getAlipayUserId()); + alipayChannelMerchant.setAppAuthToken(param.getAppAuthToken()); + alipayChannelMerchantManager.save(alipayChannelMerchant); + } + + /// 创建支付宝直连通道商户 @Transactional(rollbackFor = Exception.class) public Long createDirect(AlipayDirectChannelMerchantCreateParam param) { String channelMchNo = param.getAlipayUserId(); @@ -55,3 +95,4 @@ public class AlipayChannelMerchantService { .orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist")); } } + diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/basic/PayRouteBasicConfigService.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/basic/PayRouteBasicConfigService.java index b1b0066e4..fc46225cc 100644 --- a/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/basic/PayRouteBasicConfigService.java +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/basic/PayRouteBasicConfigService.java @@ -29,7 +29,6 @@ import java.util.stream.Collectors; /// # 通道路由基础模式配置 /// /// 按支付渠道配置默认支付产品,保存前校验产品策略能力。 -/// 开源版:所有产品默认可用,不再检查商户启用状态。 @Service @RequiredArgsConstructor public class PayRouteBasicConfigService { @@ -68,7 +67,7 @@ public class PayRouteBasicConfigService { } } - /// 校验产品存在且支持指定支付渠道(开源版:不再检查商户启用状态) + /// 校验产品存在且支持指定支付渠道 public void validateBasicProduct(String product, PayProviderEnum provider) { ProductEnum productEnum = ProductEnum.findByCode(product); if (productEnum == null) { @@ -103,7 +102,7 @@ public class PayRouteBasicConfigService { .setProducts(products); } - /// 从产品枚举中筛出支持指定支付渠道的产品编码(开源版:所有产品默认可用) + /// 从产品枚举中筛出支持指定支付渠道的产品编码 private List productsForProvider(PayProviderEnum provider) { return Arrays.stream(ProductEnum.values()) .map(ProductEnum::getCode) diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/basic/PayRouteBasicMatcher.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/basic/PayRouteBasicMatcher.java index 5318e9a72..4d23fac02 100644 --- a/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/basic/PayRouteBasicMatcher.java +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/basic/PayRouteBasicMatcher.java @@ -21,7 +21,6 @@ import java.util.Objects; /// # 基础模式通道路由匹配器 /// /// 按已配置的支付渠道与支付产品,结合产品策略解析通道与支付方式。 -/// 开源版:所有产品默认可用,不再检查商户启用状态。 @Component @RequiredArgsConstructor public class PayRouteBasicMatcher { @@ -73,7 +72,7 @@ public class PayRouteBasicMatcher { .orElse(null); } - /// 校验产品策略支持该支付渠道(开源版:不再检查商户启用状态) + /// 校验产品策略支持该支付渠道 private void assertProductConfigured(String product, PayProviderEnum provider) { if (!PaymentStrategyFactory.productSupportsProvider(product, provider)) { throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/runtime/PayRouteProductResolver.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/runtime/PayRouteProductResolver.java index 6acee6c81..5874dc840 100644 --- a/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/runtime/PayRouteProductResolver.java +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/runtime/PayRouteProductResolver.java @@ -14,14 +14,13 @@ import java.util.Objects; /// # 通道路由产品解析器 /// -/// 开源版:所有产品默认可用,直接从产品枚举中按通道+支付方式解析产品编码。 @Service @RequiredArgsConstructor public class PayRouteProductResolver { private final PayRouteStrategyCapabilitySupport payRouteStrategyCapabilitySupport; - /// 根据通道及支付方式解析产品编码(开源版:所有产品默认可用) + /// 根据通道及支付方式解析产品编码 public String resolve(String mchNo, String channel, String method) { return Arrays.stream(ProductEnum.values()) .filter(pe -> Objects.equals(pe.getChannel(), channel)) diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/support/PayRouteStrategyCapabilitySupport.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/support/PayRouteStrategyCapabilitySupport.java index 0a01ca4d8..4877fce18 100644 --- a/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/support/PayRouteStrategyCapabilitySupport.java +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/org/dromara/daxpay/payment/merchant/service/route/support/PayRouteStrategyCapabilitySupport.java @@ -33,7 +33,6 @@ import java.util.Objects; /// # 通道路由:策略方式→能力与 DB 求交 /// /// 统一场景模式产品/能力候选及保存校验,数据源为 {@link AbsProductStrategy#methodCapabilityMapping()}。 -/// 开源版:所有产品默认可用,不再检查商户启用状态。 @Service @RequiredArgsConstructor public class PayRouteStrategyCapabilitySupport { @@ -81,7 +80,7 @@ public class PayRouteStrategyCapabilitySupport { return provider + "|" + method + "|" + product; } - /// 目录项下可用支付产品候选(开源版:所有产品默认可用) + /// 目录项下可用支付产品候选 public List listSceneProductCandidates(String provider, String method) { if (!payProviderMethodService.contains(provider, method)) { return List.of(); @@ -102,7 +101,6 @@ public class PayRouteStrategyCapabilitySupport { } /// 指定产品+目录项下支付能力候选(策略 Map ∩ DB ∩ 能力主数据启用) - /// 开源版:不再检查商户产品启用状态 public List listSceneCapabilityCandidates(String provider, String method, String product) { if (!payProviderMethodService.contains(provider, method)) { return List.of(); diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/channel/alipay.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/channel/alipay.json index 27e5bc89a..4f5946d73 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/channel/alipay.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/channel/alipay.json @@ -36,5 +36,6 @@ "mchProductConfigNotExist": "Alipay merchant product payment configuration not found", "appIdDuplicate": "Application ID already exists under the same service provider", "appNotFound": "Alipay ISV application not found", + "subMerchantDuplicate": "This sub-merchant already exists under the Alipay application", "userIdTypeInvalid": "Invalid user ID type" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/channel/alipay.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/channel/alipay.json index 5c3c26cfa..d82a6eeeb 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/channel/alipay.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/channel/alipay.json @@ -36,5 +36,6 @@ "mchProductConfigNotExist": "支付宝产品支付配置不存在", "appIdDuplicate": "同一服务商下应用ID已存在", "appNotFound": "支付宝服务商应用不存在", + "subMerchantDuplicate": "该支付宝应用下已存在此子商户号", "userIdTypeInvalid": "用户标识类型不合法" }