From 78cfc9f0246637f0296c10d9da29683fde212015 Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Sun, 14 Jun 2026 13:01:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E4=BB=98=E5=AE=9D=E9=80=9A?= =?UTF-8?q?=E9=81=93=E5=95=86=E6=88=B7=E5=BA=94=E7=94=A8=E7=AE=A1=E7=90=86?= =?UTF-8?q?=EF=BC=88ISV/=E7=9B=B4=E8=BF=9E=EF=BC=89=E6=8B=86=E5=88=86?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _config/sql/alipay_channel_merchant.sql | 75 ++++++++++++++ ...AlipayDirectChannelMerchantController.java | 45 +++++++++ ...> AlipayIsvChannelMerchantController.java} | 28 ++---- .../convert/AlipayChannelMerchantConvert.java | 14 --- .../AlipayDirectChannelMerchantConvert.java | 14 +++ .../AlipayIsvChannelMerchantConvert.java | 17 ++++ .../config/AlipayChannelMerchantManager.java | 17 ---- .../config/AlipayChannelMerchantMapper.java | 9 -- .../AlipayDirectChannelMerchantManager.java | 17 ++++ .../AlipayDirectChannelMerchantMapper.java | 11 +++ .../AlipayIsvChannelMerchantManager.java | 17 ++++ .../AlipayIsvChannelMerchantMapper.java | 11 +++ .../entity/config/AlipayChannelMerchant.java | 40 -------- .../config/AlipayDirectChannelMerchant.java | 37 +++++++ .../config/AlipayIsvChannelMerchant.java | 44 +++++++++ .../AlipayIsvChannelMerchantCreateParam.java | 15 +-- ...=> AlipayDirectChannelMerchantResult.java} | 14 +-- .../AlipayIsvChannelMerchantResult.java | 32 ++++++ .../mch/AlipayChannelMerchantService.java | 98 ------------------- .../AlipayDirectChannelMerchantService.java | 69 +++++++++++++ .../mch/AlipayIsvChannelMerchantService.java | 77 +++++++++++++++ .../i18n/en-US/error/channel/alipay.json | 1 + .../main/resources/i18n/en-US/validation.json | 3 +- .../i18n/zh-CN/error/channel/alipay.json | 1 + .../main/resources/i18n/zh-CN/validation.json | 3 +- 25 files changed, 494 insertions(+), 215 deletions(-) create mode 100644 _config/sql/alipay_channel_merchant.sql create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayDirectChannelMerchantController.java rename daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/{AlipayChannelMerchantController.java => AlipayIsvChannelMerchantController.java} (58%) delete mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayChannelMerchantConvert.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayDirectChannelMerchantConvert.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayIsvChannelMerchantConvert.java delete mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantManager.java delete mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantMapper.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayDirectChannelMerchantManager.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayDirectChannelMerchantMapper.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayIsvChannelMerchantManager.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayIsvChannelMerchantMapper.java delete mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayChannelMerchant.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayDirectChannelMerchant.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayIsvChannelMerchant.java rename daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/{AlipayChannelMerchantResult.java => AlipayDirectChannelMerchantResult.java} (61%) create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayIsvChannelMerchantResult.java delete mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayChannelMerchantService.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayDirectChannelMerchantService.java create mode 100644 daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayIsvChannelMerchantService.java diff --git a/_config/sql/alipay_channel_merchant.sql b/_config/sql/alipay_channel_merchant.sql new file mode 100644 index 000000000..8fa0b86a5 --- /dev/null +++ b/_config/sql/alipay_channel_merchant.sql @@ -0,0 +1,75 @@ +-- ---------------------------- +-- 支付宝服务商通道商户绑定表 +-- 一条记录代表"子商户挂靠在某个服务商应用下"的授权关系 +-- ---------------------------- +DROP TABLE IF EXISTS mch_alipay_isv_channel_merchant; +CREATE TABLE mch_alipay_isv_channel_merchant ( + id int8 NOT NULL, + mch_no varchar(32) COLLATE pg_catalog."default", + channel_mch_no varchar(64) COLLATE pg_catalog."default", + product varchar(32) COLLATE pg_catalog."default", + app_id int8, + alipay_user_id varchar(32) COLLATE pg_catalog."default", + app_auth_token varchar(128) COLLATE pg_catalog."default", + creator int8, + create_time timestamptz(6), + last_modifier int8, + last_modified_time timestamptz(6), + version int4 DEFAULT 0, + deleted bool NOT NULL DEFAULT false +); + +COMMENT ON TABLE mch_alipay_isv_channel_merchant IS '支付宝服务商通道商户绑定表'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.id IS '主键'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.mch_no IS '商户号'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.channel_mch_no IS '通道商户号(AISV+雪花)'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.product IS '所属支付产品'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.app_id IS '关联服务商应用ID(系统主键, 指向alipay_isv_app.id)'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.alipay_user_id IS '子商户支付宝识别码(2088开头)'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.app_auth_token IS '应用授权令牌(会过期/刷新)'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.creator IS '创建者ID'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.create_time IS '创建时间'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.last_modifier IS '最后修改ID'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.last_modified_time IS '最后修改时间'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.version IS '版本号'; +COMMENT ON COLUMN mch_alipay_isv_channel_merchant.deleted IS '删除标志'; + +ALTER TABLE mch_alipay_isv_channel_merchant ADD CONSTRAINT pk_mch_alipay_isv_channel_merchant PRIMARY KEY (id); +-- 同一服务商应用下子商户不重复 +ALTER TABLE mch_alipay_isv_channel_merchant ADD CONSTRAINT uk_isv_app_alipay_user UNIQUE (app_id, alipay_user_id); + +-- ---------------------------- +-- 支付宝直连通道商户绑定表 +-- 一个商户PID对应一个channelMchNo, 商户的多个应用共享此绑定 +-- ---------------------------- +DROP TABLE IF EXISTS mch_alipay_direct_channel_merchant; +CREATE TABLE mch_alipay_direct_channel_merchant ( + id int8 NOT NULL, + mch_no varchar(32) COLLATE pg_catalog."default", + channel_mch_no varchar(64) COLLATE pg_catalog."default", + product varchar(32) COLLATE pg_catalog."default", + alipay_user_id varchar(32) COLLATE pg_catalog."default", + creator int8, + create_time timestamptz(6), + last_modifier int8, + last_modified_time timestamptz(6), + version int4 DEFAULT 0, + deleted bool NOT NULL DEFAULT false +); + +COMMENT ON TABLE mch_alipay_direct_channel_merchant IS '支付宝直连通道商户绑定表'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.id IS '主键'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.mch_no IS '商户号'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.channel_mch_no IS '通道商户号(系统生成雪花号)'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.product IS '所属支付产品'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.alipay_user_id IS '支付宝商家唯一识别码(2088开头)'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.creator IS '创建者ID'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.create_time IS '创建时间'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.last_modifier IS '最后修改ID'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.last_modified_time IS '最后修改时间'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.version IS '版本号'; +COMMENT ON COLUMN mch_alipay_direct_channel_merchant.deleted IS '删除标志'; + +ALTER TABLE mch_alipay_direct_channel_merchant ADD CONSTRAINT pk_mch_alipay_direct_channel_merchant PRIMARY KEY (id); +-- 直连一PID一channelMchNo: 同一商户下同一alipayUserId唯一 +ALTER TABLE mch_alipay_direct_channel_merchant ADD CONSTRAINT uk_direct_mch_alipay_user UNIQUE (mch_no, alipay_user_id); diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayDirectChannelMerchantController.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayDirectChannelMerchantController.java new file mode 100644 index 000000000..b06d96a56 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayDirectChannelMerchantController.java @@ -0,0 +1,45 @@ +package org.dromara.daxpay.channel.alipay.controller.mch; + +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.result.config.AlipayDirectChannelMerchantResult; +import org.dromara.daxpay.channel.alipay.service.mch.AlipayDirectChannelMerchantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@PermCode(menuCode = "payment:merchant:channelMerchant") +@Validated +@Tag(name = "支付宝直连通道商户管理") +@RestController +@RequestMapping("/admin/alipay/direct-channel-merchant") +@RequiredArgsConstructor +public class AlipayDirectChannelMerchantController { + + private final AlipayDirectChannelMerchantService alipayDirectChannelMerchantService; + + @PermCode(code = "view", nameCn = "商户通道商户查看", nameEn = "Merchant Channel Merchant View") + @Operation(summary = "根据通道商户号查询支付宝直连通道商户配置") + @GetMapping("/find-by-channel-mch-no") + public Result findByChannelMchNo( + @NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) { + return Res.ok(alipayDirectChannelMerchantService.findByChannelMchNo(channelMchNo)); + } + + @PermCode(code = "add", nameCn = "商户通道商户新增", nameEn = "Merchant Channel Merchant Add") + @Operation(summary = "创建支付宝直连通道商户") + @PostMapping("/create") + public Result create(@RequestBody @Validated AlipayDirectChannelMerchantCreateParam param) { + alipayDirectChannelMerchantService.create(param); + return Res.ok(); + } +} 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/AlipayIsvChannelMerchantController.java similarity index 58% rename from daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayChannelMerchantController.java rename to daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/controller/mch/AlipayIsvChannelMerchantController.java index 0a2954649..5dc339e81 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/AlipayIsvChannelMerchantController.java @@ -3,10 +3,9 @@ package org.dromara.daxpay.channel.alipay.controller.mch; 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 org.dromara.daxpay.channel.alipay.result.config.AlipayIsvChannelMerchantResult; +import org.dromara.daxpay.channel.alipay.service.mch.AlipayIsvChannelMerchantService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.NotBlank; @@ -20,34 +19,27 @@ import org.springframework.web.bind.annotation.RestController; @PermCode(menuCode = "payment:merchant:channelMerchant") @Validated -@Tag(name = "支付宝通道商户管理") +@Tag(name = "支付宝服务商通道商户管理") @RestController -@RequestMapping("/admin/alipay/channel-merchant") +@RequestMapping("/admin/alipay/isv-channel-merchant") @RequiredArgsConstructor -public class AlipayChannelMerchantController { +public class AlipayIsvChannelMerchantController { - private final AlipayChannelMerchantService alipayChannelMerchantService; + private final AlipayIsvChannelMerchantService alipayIsvChannelMerchantService; @PermCode(code = "view", nameCn = "商户通道商户查看", nameEn = "Merchant Channel Merchant View") - @Operation(summary = "根据通道商户号查询支付宝通道商户配置") + @Operation(summary = "根据通道商户号查询支付宝服务商通道商户配置") @GetMapping("/find-by-channel-mch-no") - public Result findByChannelMchNo( + public Result findByChannelMchNo( @NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) { - return Res.ok(alipayChannelMerchantService.findByChannelMchNo(channelMchNo)); + return Res.ok(alipayIsvChannelMerchantService.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); + alipayIsvChannelMerchantService.create(param); return Res.ok(); } - - @PermCode(code = "add", nameCn = "商户通道商户新增", nameEn = "Merchant Channel Merchant Add") - @Operation(summary = "创建支付宝直连通道商户") - @PostMapping("/create-direct") - public Result createDirect(@RequestBody @Validated AlipayDirectChannelMerchantCreateParam param) { - return Res.ok(alipayChannelMerchantService.createDirect(param)); - } } diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayChannelMerchantConvert.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayChannelMerchantConvert.java deleted file mode 100644 index bdacf9e2a..000000000 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayChannelMerchantConvert.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.dromara.daxpay.channel.alipay.convert; - -import org.dromara.daxpay.channel.alipay.entity.config.AlipayChannelMerchant; -import org.dromara.daxpay.channel.alipay.result.config.AlipayChannelMerchantResult; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -@Mapper -public interface AlipayChannelMerchantConvert { - - AlipayChannelMerchantConvert CONVERT = Mappers.getMapper(AlipayChannelMerchantConvert.class); - - AlipayChannelMerchantResult toResult(AlipayChannelMerchant entity); -} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayDirectChannelMerchantConvert.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayDirectChannelMerchantConvert.java new file mode 100644 index 000000000..c313853a3 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayDirectChannelMerchantConvert.java @@ -0,0 +1,14 @@ +package org.dromara.daxpay.channel.alipay.convert; + +import org.dromara.daxpay.channel.alipay.entity.config.AlipayDirectChannelMerchant; +import org.dromara.daxpay.channel.alipay.result.config.AlipayDirectChannelMerchantResult; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface AlipayDirectChannelMerchantConvert { + + AlipayDirectChannelMerchantConvert CONVERT = Mappers.getMapper(AlipayDirectChannelMerchantConvert.class); + + AlipayDirectChannelMerchantResult toResult(AlipayDirectChannelMerchant entity); +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayIsvChannelMerchantConvert.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayIsvChannelMerchantConvert.java new file mode 100644 index 000000000..21b6252ce --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/convert/AlipayIsvChannelMerchantConvert.java @@ -0,0 +1,17 @@ +package org.dromara.daxpay.channel.alipay.convert; + +import org.dromara.daxpay.channel.alipay.entity.config.AlipayIsvChannelMerchant; +import org.dromara.daxpay.channel.alipay.result.config.AlipayIsvChannelMerchantResult; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface AlipayIsvChannelMerchantConvert { + + AlipayIsvChannelMerchantConvert CONVERT = Mappers.getMapper(AlipayIsvChannelMerchantConvert.class); + + /// 实体 appId(Long) 映射到结果 isvAppId(Long), 避免与父类 MchTradeBaseResult.appId(String) 冲突 + @Mapping(source = "appId", target = "isvAppId") + AlipayIsvChannelMerchantResult toResult(AlipayIsvChannelMerchant entity); +} 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 deleted file mode 100644 index e981d39ff..000000000 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantManager.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.dromara.daxpay.channel.alipay.dao.config; - -import org.dromara.daxpay.channel.alipay.entity.config.AlipayChannelMerchant; -import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager; -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/dao/config/AlipayChannelMerchantMapper.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantMapper.java deleted file mode 100644 index 8cf8d9c5e..000000000 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayChannelMerchantMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.dromara.daxpay.channel.alipay.dao.config; - -import org.dromara.daxpay.channel.alipay.entity.config.AlipayChannelMerchant; -import com.github.yulichang.base.MPJBaseMapper; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface AlipayChannelMerchantMapper extends MPJBaseMapper { -} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayDirectChannelMerchantManager.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayDirectChannelMerchantManager.java new file mode 100644 index 000000000..a8a425904 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayDirectChannelMerchantManager.java @@ -0,0 +1,17 @@ +package org.dromara.daxpay.channel.alipay.dao.config; + +import org.dromara.daxpay.channel.alipay.entity.config.AlipayDirectChannelMerchant; +import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager; +import org.springframework.stereotype.Repository; + +@Repository +public class AlipayDirectChannelMerchantManager extends BaseManager { + + /// 校验同一商户下是否已绑定该支付宝商户PID + public boolean existsByMchNoAndAlipayUserId(String mchNo, String alipayUserId) { + return lambdaQuery() + .eq(AlipayDirectChannelMerchant::getMchNo, mchNo) + .eq(AlipayDirectChannelMerchant::getAlipayUserId, alipayUserId) + .exists(); + } +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayDirectChannelMerchantMapper.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayDirectChannelMerchantMapper.java new file mode 100644 index 000000000..57830def6 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayDirectChannelMerchantMapper.java @@ -0,0 +1,11 @@ +package org.dromara.daxpay.channel.alipay.dao.config; + +import org.dromara.daxpay.channel.alipay.entity.config.AlipayDirectChannelMerchant; +import com.github.yulichang.base.MPJBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/// # 支付宝直连通道商户绑定 +/// +@Mapper +public interface AlipayDirectChannelMerchantMapper extends MPJBaseMapper { +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayIsvChannelMerchantManager.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayIsvChannelMerchantManager.java new file mode 100644 index 000000000..b78121dd3 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayIsvChannelMerchantManager.java @@ -0,0 +1,17 @@ +package org.dromara.daxpay.channel.alipay.dao.config; + +import org.dromara.daxpay.channel.alipay.entity.config.AlipayIsvChannelMerchant; +import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager; +import org.springframework.stereotype.Repository; + +@Repository +public class AlipayIsvChannelMerchantManager extends BaseManager { + + /// 校验同一服务商应用下是否已绑定该子商户 + public boolean existsByAppIdAndAlipayUserId(Long appId, String alipayUserId) { + return lambdaQuery() + .eq(AlipayIsvChannelMerchant::getAppId, appId) + .eq(AlipayIsvChannelMerchant::getAlipayUserId, alipayUserId) + .exists(); + } +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayIsvChannelMerchantMapper.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayIsvChannelMerchantMapper.java new file mode 100644 index 000000000..fd7e74216 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/dao/config/AlipayIsvChannelMerchantMapper.java @@ -0,0 +1,11 @@ +package org.dromara.daxpay.channel.alipay.dao.config; + +import org.dromara.daxpay.channel.alipay.entity.config.AlipayIsvChannelMerchant; +import com.github.yulichang.base.MPJBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/// # 支付宝服务商通道商户绑定 +/// +@Mapper +public interface AlipayIsvChannelMerchantMapper extends MPJBaseMapper { +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayChannelMerchant.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayChannelMerchant.java deleted file mode 100644 index 48e93a3d6..000000000 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayChannelMerchant.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.dromara.daxpay.channel.alipay.entity.config; - -import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult; -import org.dromara.daxpay.channel.alipay.convert.AlipayChannelMerchantConvert; -import org.dromara.daxpay.channel.alipay.result.config.AlipayChannelMerchantResult; -import org.dromara.daxpay.payment.common.entity.merchant.MchBaseEntity; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; - -/// # 支付宝通道商户配置 -/// -@EqualsAndHashCode(callSuper = true) -@Data -@Accessors(chain = true) -@TableName(value = "mch_alipay_channel_merchant", autoResultMap = true) -public class AlipayChannelMerchant extends MchBaseEntity implements ToResult { - - /// 通道商户号 - private String channelMchNo; - - /// 所属支付产品 - private String product; - - /// 支付宝服务商应用ID - private String isvAppId; - - /// 支付宝商家用户唯一识别码(2088开头) - private String alipayUserId; - - /// 应用授权令牌,服务商代商户调用接口的凭据 - private String appAuthToken; - - /// 转换 - @Override - public AlipayChannelMerchantResult toResult() { - return AlipayChannelMerchantConvert.CONVERT.toResult(this); - } -} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayDirectChannelMerchant.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayDirectChannelMerchant.java new file mode 100644 index 000000000..0199c1108 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayDirectChannelMerchant.java @@ -0,0 +1,37 @@ +package org.dromara.daxpay.channel.alipay.entity.config; + +import org.dromara.daxpay.channel.alipay.convert.AlipayDirectChannelMerchantConvert; +import org.dromara.daxpay.channel.alipay.result.config.AlipayDirectChannelMerchantResult; +import org.dromara.daxpay.payment.common.entity.merchant.MchBaseEntity; +import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/// # 支付宝直连通道商户绑定 +/// +/// 一个商户PID对应一个channelMchNo, 商户的多个应用共享此绑定。 +/// +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@TableName(value = "mch_alipay_direct_channel_merchant", autoResultMap = true) +public class AlipayDirectChannelMerchant extends MchBaseEntity implements ToResult { + + /// 通道商户号(系统生成雪花号) + private String channelMchNo; + + /// 所属支付产品 + /// @see org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum + private String product; + + /// 支付宝商家唯一识别码(2088开头的16位数字) + private String alipayUserId; + + /// 转换 + @Override + public AlipayDirectChannelMerchantResult toResult() { + return AlipayDirectChannelMerchantConvert.CONVERT.toResult(this); + } +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayIsvChannelMerchant.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayIsvChannelMerchant.java new file mode 100644 index 000000000..4241fa132 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/entity/config/AlipayIsvChannelMerchant.java @@ -0,0 +1,44 @@ +package org.dromara.daxpay.channel.alipay.entity.config; + +import org.dromara.daxpay.channel.alipay.convert.AlipayIsvChannelMerchantConvert; +import org.dromara.daxpay.channel.alipay.result.config.AlipayIsvChannelMerchantResult; +import org.dromara.daxpay.payment.common.entity.merchant.MchBaseEntity; +import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/// # 支付宝服务商通道商户绑定 +/// +/// 一条记录代表"子商户挂靠在某个服务商应用下"的授权关系。 +/// 同一子商户挂不同应用 = 不同行(不同 channelMchNo)。 +/// +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@TableName(value = "mch_alipay_isv_channel_merchant", autoResultMap = true) +public class AlipayIsvChannelMerchant extends MchBaseEntity implements ToResult { + + /// 通道商户号(AISV+雪花) + private String channelMchNo; + + /// 所属支付产品 + /// @see org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum + private String product; + + /// 关联服务商应用ID(系统主键, 指向 alipay_isv_app.id) + private Long appId; + + /// 子商户支付宝识别码(2088开头的16位数字) + private String alipayUserId; + + /// 应用授权令牌(服务商代子商户调用接口的凭据, 会过期/刷新) + private String appAuthToken; + + /// 转换 + @Override + public AlipayIsvChannelMerchantResult toResult() { + return AlipayIsvChannelMerchantConvert.CONVERT.toResult(this); + } +} 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 index 98748d2f8..993e9e7c0 100644 --- 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 @@ -2,6 +2,7 @@ package org.dromara.daxpay.channel.alipay.param.mch; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.experimental.Accessors; @@ -25,17 +26,17 @@ public class AlipayIsvChannelMerchantCreateParam { @NotBlank(message = "{validation.field.product.notBlank}") private String product; - /// 支付宝服务商应用ID - @Schema(description = "支付宝服务商应用ID") - @NotBlank(message = "{validation.field.isvAppId.notBlank}") - private String isvAppId; + /// 服务商应用ID(系统主键, 指向 alipay_isv_app.id) + @Schema(description = "服务商应用ID") + @NotNull(message = "{validation.field.appId.notNull}") + private Long appId; - /// 支付宝商家用户ID(2088开头) - @Schema(description = "支付宝商家用户ID(2088开头)") + /// 子商户支付宝用户ID(2088开头) + @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/result/config/AlipayChannelMerchantResult.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayDirectChannelMerchantResult.java similarity index 61% rename from daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayChannelMerchantResult.java rename to daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayDirectChannelMerchantResult.java index 9d631c7a3..c49f6d96d 100644 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayChannelMerchantResult.java +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayDirectChannelMerchantResult.java @@ -6,13 +6,13 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; -/// # 支付宝通道商户配置结果 +/// # 支付宝直连通道商户绑定结果 /// @EqualsAndHashCode(callSuper = true) @Data @Accessors(chain = true) -@Schema(title = "支付宝通道商户配置结果") -public class AlipayChannelMerchantResult extends MchTradeBaseResult { +@Schema(title = "支付宝直连通道商户绑定结果") +public class AlipayDirectChannelMerchantResult extends MchTradeBaseResult { @Schema(description = "通道商户号") private String channelMchNo; @@ -20,12 +20,6 @@ public class AlipayChannelMerchantResult extends MchTradeBaseResult { @Schema(description = "所属支付产品") private String product; - @Schema(description = "支付宝服务商应用ID(aliAppId)") - private String isvAppId; - - @Schema(description = "支付宝商家唯一识别码(2088开头的16位数字)") + @Schema(description = "支付宝商家唯一识别码(2088开头)") private String alipayUserId; - - @Schema(description = "应用授权令牌") - private String appAuthToken; } diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayIsvChannelMerchantResult.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayIsvChannelMerchantResult.java new file mode 100644 index 000000000..a655f03d4 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/result/config/AlipayIsvChannelMerchantResult.java @@ -0,0 +1,32 @@ +package org.dromara.daxpay.channel.alipay.result.config; + +import org.dromara.daxpay.payment.common.result.MchTradeBaseResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/// # 支付宝服务商通道商户绑定结果 +/// +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@Schema(title = "支付宝服务商通道商户绑定结果") +public class AlipayIsvChannelMerchantResult extends MchTradeBaseResult { + + @Schema(description = "通道商户号") + private String channelMchNo; + + @Schema(description = "所属支付产品") + private String product; + + /// 注意: 此处用 isvAppId(Long) 而非 appId, 避免与父类 MchTradeBaseResult.appId(String) 字段冲突 + @Schema(description = "关联服务商应用ID(系统主键)") + private Long isvAppId; + + @Schema(description = "子商户支付宝识别码(2088开头)") + private String alipayUserId; + + @Schema(description = "应用授权令牌") + 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 deleted file mode 100644 index 9b8803840..000000000 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayChannelMerchantService.java +++ /dev/null @@ -1,98 +0,0 @@ -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; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Slf4j -@Service -@RequiredArgsConstructor -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(); - - ChannelMerchant channelMerchant = new ChannelMerchant(); - long channelMerchantId = IdUtil.getSnowflakeNextId(); - channelMerchant.setId(channelMerchantId); - 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.setAlipayUserId(param.getAlipayUserId()); - alipayChannelMerchantManager.save(alipayChannelMerchant); - return channelMerchantId; - } - - public AlipayChannelMerchantResult findByChannelMchNo(String channelMchNo) { - return alipayChannelMerchantManager.lambdaQuery() - .eq(AlipayChannelMerchant::getChannelMchNo, channelMchNo) - .oneOpt() - .map(AlipayChannelMerchant::toResult) - .orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist")); - } -} - diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayDirectChannelMerchantService.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayDirectChannelMerchantService.java new file mode 100644 index 000000000..dd9fe00b5 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayDirectChannelMerchantService.java @@ -0,0 +1,69 @@ +package org.dromara.daxpay.channel.alipay.service.mch; + +import org.dromara.daxpay.channel.alipay.dao.config.AlipayDirectChannelMerchantManager; +import org.dromara.daxpay.channel.alipay.entity.config.AlipayDirectChannelMerchant; +import org.dromara.daxpay.channel.alipay.param.mch.AlipayDirectChannelMerchantCreateParam; +import org.dromara.daxpay.channel.alipay.result.config.AlipayDirectChannelMerchantResult; +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; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/// # 支付宝直连通道商户管理 +/// +/// 一个商户PID对应一个channelMchNo, 商户的多个应用共享此绑定。 +/// +@Slf4j +@Service +@RequiredArgsConstructor +public class AlipayDirectChannelMerchantService { + + private final ChannelMerchantManager channelMerchantManager; + private final AlipayDirectChannelMerchantManager alipayDirectChannelMerchantManager; + + /// 创建支付宝直连通道商户 + @Transactional(rollbackFor = Exception.class) + public void create(AlipayDirectChannelMerchantCreateParam param) { + // 校验同一商户下 PID 不重复 + if (alipayDirectChannelMerchantManager.existsByMchNoAndAlipayUserId( + param.getMchNo(), param.getAlipayUserId())) { + throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.alipay.directMchDuplicate"); + } + // 生成通道商户号(雪花号, 不再使用 alipayUserId) + String channelMchNo = String.valueOf(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); + // 写直连绑定表(alipayUserId 作为业务字段, 不参与关联) + AlipayDirectChannelMerchant entity = new AlipayDirectChannelMerchant(); + entity.setId(IdUtil.getSnowflakeNextId()); + entity.setMchNo(param.getMchNo()); + entity.setChannelMchNo(channelMchNo); + entity.setProduct(param.getProduct()); + entity.setAlipayUserId(param.getAlipayUserId()); + alipayDirectChannelMerchantManager.save(entity); + } + + /// 根据通道商户号查询直连通道商户配置 + public AlipayDirectChannelMerchantResult findByChannelMchNo(String channelMchNo) { + return alipayDirectChannelMerchantManager.lambdaQuery() + .eq(AlipayDirectChannelMerchant::getChannelMchNo, channelMchNo) + .oneOpt() + .map(AlipayDirectChannelMerchant::toResult) + .orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist")); + } +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayIsvChannelMerchantService.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayIsvChannelMerchantService.java new file mode 100644 index 000000000..8b04c651e --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/org/dromara/daxpay/channel/alipay/service/mch/AlipayIsvChannelMerchantService.java @@ -0,0 +1,77 @@ +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.AlipayIsvChannelMerchantManager; +import org.dromara.daxpay.channel.alipay.entity.app.AlipayIsvApp; +import org.dromara.daxpay.channel.alipay.entity.config.AlipayIsvChannelMerchant; +import org.dromara.daxpay.channel.alipay.param.mch.AlipayIsvChannelMerchantCreateParam; +import org.dromara.daxpay.channel.alipay.result.config.AlipayIsvChannelMerchantResult; +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; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/// # 支付宝服务商通道商户管理 +/// +/// 一条记录代表"子商户挂靠在某个服务商应用下"的授权关系。 +/// +@Slf4j +@Service +@RequiredArgsConstructor +public class AlipayIsvChannelMerchantService { + + private final ChannelMerchantManager channelMerchantManager; + private final AlipayIsvChannelMerchantManager alipayIsvChannelMerchantManager; + private final AlipayIsvAppManager alipayIsvAppManager; + + /// 创建支付宝服务商通道商户 + @Transactional(rollbackFor = Exception.class) + public void create(AlipayIsvChannelMerchantCreateParam param) { + // 校验服务商应用存在(用 appId 系统主键查询) + AlipayIsvApp isvApp = alipayIsvAppManager.findById(param.getAppId()) + .orElseThrow(() -> new DataNotExistException("error.channel.alipay.appNotFound")); + // 校验同一应用下子商户号不重复 + if (alipayIsvChannelMerchantManager.existsByAppIdAndAlipayUserId( + param.getAppId(), 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); + // 写服务商绑定表(含挂靠关系专属字段) + AlipayIsvChannelMerchant entity = new AlipayIsvChannelMerchant(); + entity.setId(IdUtil.getSnowflakeNextId()); + entity.setMchNo(param.getMchNo()); + entity.setChannelMchNo(channelMchNo); + entity.setProduct(param.getProduct()); + entity.setAppId(isvApp.getId()); + entity.setAlipayUserId(param.getAlipayUserId()); + entity.setAppAuthToken(param.getAppAuthToken()); + alipayIsvChannelMerchantManager.save(entity); + } + + /// 根据通道商户号查询服务商通道商户配置 + public AlipayIsvChannelMerchantResult findByChannelMchNo(String channelMchNo) { + return alipayIsvChannelMerchantManager.lambdaQuery() + .eq(AlipayIsvChannelMerchant::getChannelMchNo, channelMchNo) + .oneOpt() + .map(AlipayIsvChannelMerchant::toResult) + .orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist")); + } +} 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 4f5946d73..b88242f61 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 @@ -37,5 +37,6 @@ "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", + "directMchDuplicate": "The Alipay merchant already exists under this merchant", "userIdTypeInvalid": "Invalid user ID type" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json index 46756dd83..3ebab4cf8 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json @@ -60,7 +60,8 @@ }, "appId": { "notBlank": "Merchant app ID cannot be blank", - "size": "Merchant app ID cannot exceed 32 characters" + "size": "Merchant app ID cannot exceed 32 characters", + "notNull": "Application ID cannot be null" }, "subMchId": { "notBlank": "WeChat sub-merchant ID cannot be blank" 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 d82a6eeeb..729c39f76 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 @@ -37,5 +37,6 @@ "appIdDuplicate": "同一服务商下应用ID已存在", "appNotFound": "支付宝服务商应用不存在", "subMerchantDuplicate": "该支付宝应用下已存在此子商户号", + "directMchDuplicate": "同一商户下该支付宝商户已存在", "userIdTypeInvalid": "用户标识类型不合法" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json index cf479ec69..42994513f 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json @@ -60,7 +60,8 @@ }, "appId": { "notBlank": "商户AppId不可为空", - "size": "应用号不可超过32位" + "size": "应用号不可超过32位", + "notNull": "应用ID不可为空" }, "subMchId": { "notBlank": "微信子商户号不可为空"