mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-12 23:45:39 +08:00
feat: 抖音支付通道新增 entity/param/result/service/dao/controller 全套代码
- 新增抖音支付产品策略、直连支付策略 - Alipay 直连应用调整 - 新增抖音 SQL 建表脚本
This commit is contained in:
159
_config/sql/douyin-tables.sql
Normal file
159
_config/sql/douyin-tables.sql
Normal file
@@ -0,0 +1,159 @@
|
||||
-- =====================================================================
|
||||
-- 抖音支付直连通道相关表
|
||||
-- 对应实体:
|
||||
-- org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectChannelMerchant
|
||||
-- org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectKeyConfig
|
||||
-- org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectApp
|
||||
-- org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectAppAuthConfig
|
||||
-- 执行方式: 手动执行(业务表 DDL 不入库, 由开发者按需在数据库执行)
|
||||
-- 注意: 加密字段(DataEncryptTypeHandler)在数据库层均为普通 text, 加解密在应用层完成
|
||||
-- =====================================================================
|
||||
|
||||
-- =========================
|
||||
-- 1. 抖音直连通道商户绑定 douyin_direct_channel_merchant
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."douyin_direct_channel_merchant";
|
||||
|
||||
CREATE TABLE "public"."douyin_direct_channel_merchant" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"product" varchar(32) NOT NULL,
|
||||
"dy_mch_id" varchar(64),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."douyin_direct_channel_merchant" IS '抖音直连通道商户绑定';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."channel_mch_no" IS '通道商户号(系统生成雪花号)';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."product" IS '所属支付产品';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."dy_mch_id" IS '抖音商户号(MCHID)';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_channel_merchant"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."douyin_direct_channel_merchant"
|
||||
ADD CONSTRAINT "pk_douyin_direct_channel_merchant" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 2. 抖音直连密钥配置 douyin_direct_key_config
|
||||
-- 商户维度的密钥和证书配置, 按通道商户号关联, 敏感字段加密存储
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."douyin_direct_key_config";
|
||||
|
||||
CREATE TABLE "public"."douyin_direct_key_config" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"merchant_private_key" text,
|
||||
"merchant_serial_number" varchar(64),
|
||||
"encrypt_key" text,
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."douyin_direct_key_config" IS '抖音直连密钥配置';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."channel_mch_no" IS '通道商户号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."merchant_private_key" IS '商户私钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."merchant_serial_number" IS '商家公钥证书序列号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."encrypt_key" IS '接口加密密钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_key_config"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."douyin_direct_key_config"
|
||||
ADD CONSTRAINT "pk_douyin_direct_key_config" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 3. 抖音直连商户应用 douyin_direct_app
|
||||
-- 每个应用关联一个通道商户号, 拥有独立的抖音应用ID
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."douyin_direct_app";
|
||||
|
||||
CREATE TABLE "public"."douyin_direct_app" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"app_name" varchar(64),
|
||||
"douyin_app_id" varchar(64),
|
||||
"app_type" varchar(32),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."douyin_direct_app" IS '抖音直连商户应用';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."channel_mch_no" IS '通道商户号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."app_name" IS '应用名称';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."douyin_app_id" IS '抖音应用AppId(APPID)';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."app_type" IS '应用类型: mini_program-小程序, mobile_app-移动应用, web_app-网站应用';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."douyin_direct_app"
|
||||
ADD CONSTRAINT "pk_douyin_direct_app" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 4. 抖音直连商户应用授权认证配置 douyin_direct_app_auth_config
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."douyin_direct_app_auth_config";
|
||||
|
||||
CREATE TABLE "public"."douyin_direct_app_auth_config" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"douyin_direct_app_id" int8,
|
||||
"auth_callback_url" varchar(512),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."douyin_direct_app_auth_config" IS '抖音直连商户应用授权认证配置';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."channel_mch_no" IS '通道商户号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."douyin_direct_app_id" IS '关联应用ID(指向 douyin_direct_app.id)';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."auth_callback_url" IS '授权回调地址';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."douyin_direct_app_auth_config"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."douyin_direct_app_auth_config"
|
||||
ADD CONSTRAINT "pk_douyin_direct_app_auth_config" PRIMARY KEY ("id");
|
||||
@@ -12,7 +12,7 @@
|
||||
Target Server Version : 160009 (160009)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 13/06/2026 17:02:31
|
||||
Date: 15/06/2026 22:06:18
|
||||
*/
|
||||
|
||||
|
||||
@@ -139,92 +139,14 @@ INSERT INTO "public"."iam_perm_menu" VALUES (4040113, 40401, 'payment:merchant:c
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (4040114, 40401, 'payment:merchant:channelMerchant:alipayApp', 'admin', 'AlipayMchAppManage', '支付宝通道商户应用', 'Alipay Channel Merchant App', 'menu.payment.merchant.channelMerchant.alipayApp', NULL, 't', 'f', '/payment/channel/alipay/manage/mch/app/AlipayMchAppManage', '/payment/merchant/channel-merchant/alipay-app-manage', NULL, 12, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-11 22:28:11.274785+00', '2026-06-11 22:28:11.274785+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (404, NULL, 'payment:merchant', 'admin', 'PaymentMerchant', '商户管理', 'Merchant Management', 'menu.payment.merchant', 'lucide:store', 'f', 'f', NULL, '/payment/merchant', NULL, 4, 't', 't', 'f', 0, NULL, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-04-05 16:00:00+00', '2026-06-13 00:00:00+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (30403, 304, 'system:credential:config', 'admin', 'PlatformCredentialConfig', '对接配置', 'Credential Config', 'menu.system.config.credential', 'lucide:key-round', 'f', 'f', '/system/config/credential/PlatformCredentialConfig', '/system/config/credential', NULL, 3, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-13 00:00:00+00', '2026-06-13 00:00:00+00');
|
||||
|
||||
-- 支付配置目录
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (405, 4, 'payment:config', 'admin', 'PaymentConfig', '支付配置', 'Payment Config', 'menu.payment.config', 'lucide:settings-2', 'f', 'f', NULL, '/payment/config', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00')
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
menu_code = EXCLUDED.menu_code,
|
||||
name = EXCLUDED.name,
|
||||
title_cn = EXCLUDED.title_cn,
|
||||
title_en = EXCLUDED.title_en,
|
||||
i18n_key = EXCLUDED.i18n_key,
|
||||
icon = EXCLUDED.icon,
|
||||
path = EXCLUDED.path,
|
||||
sort_no = EXCLUDED.sort_no,
|
||||
keep_alive = EXCLUDED.keep_alive,
|
||||
menu_type = EXCLUDED.menu_type,
|
||||
last_modified_time = EXCLUDED.last_modified_time;
|
||||
|
||||
-- 支付配置:支付产品管理(卡片式)
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40105, 405, 'payment:config:productConfig', 'admin', 'ProductConfig', '支付产品管理', 'Payment Product Management', 'menu.payment.config.productConfig', 'lucide:layout-grid', 'f', 'f', '/payment/config/product/ProductConfig', '/payment/config/product', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00')
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
pid = EXCLUDED.pid,
|
||||
menu_code = EXCLUDED.menu_code,
|
||||
name = EXCLUDED.name,
|
||||
title_cn = EXCLUDED.title_cn,
|
||||
title_en = EXCLUDED.title_en,
|
||||
i18n_key = EXCLUDED.i18n_key,
|
||||
icon = EXCLUDED.icon,
|
||||
component = EXCLUDED.component,
|
||||
path = EXCLUDED.path,
|
||||
sort_no = EXCLUDED.sort_no,
|
||||
keep_alive = EXCLUDED.keep_alive,
|
||||
menu_type = EXCLUDED.menu_type,
|
||||
last_modified_time = EXCLUDED.last_modified_time;
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (405, 4, 'payment:config', 'admin', 'PaymentConfig', '支付配置', 'Payment Config', 'menu.payment.config', 'lucide:settings-2', 'f', 'f', NULL, '/payment/config', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40105, 405, 'payment:config:productConfig', 'admin', 'ProductConfig', '支付产品管理', 'Payment Product Management', 'menu.payment.config.productConfig', 'lucide:layout-grid', 'f', 'f', '/payment/config/product/ProductConfig', '/payment/config/product', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-13 00:00:00+00', '2026-06-13 09:14:10.179605+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40501, 405, 'payment:config:productDetail', 'admin', 'ProductDetailDispatch', '支付产品详情', 'Payment Product Detail', 'menu.payment.config.productDetail', NULL, 't', 'f', '/payment/config/product/detail/ProductDetailDispatch', '/payment/product-detail', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40502, 405, 'payment:alipay:isvApp', 'admin', 'AlipayIsvAppManage', '支付宝服务商应用', 'Alipay ISV Application', 'menu.payment.alipay.isvApp', NULL, 't', 'f', '/payment/channel/alipay/manage/app/AlipayIsvAppManage', '/payment/config/product/app-manage', NULL, 3, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40503, 405, 'payment:wechat:isvApp', 'admin', 'WechatIsvAppManage', '微信服务商应用', 'WeChat ISV Application', 'menu.payment.wechat.isvApp', NULL, 't', 'f', '/payment/channel/wechat/manage/app/WechatIsvAppManage', '/payment/config/product/wechat-app-manage', NULL, 4, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (4040115, 40401, 'payment:merchant:channelMerchant:douyinApp', 'admin', 'DouyinMchAppManage', '抖音通道商户应用', 'Douyin Channel Merchant App', 'menu.payment.merchant.channelMerchant.douyinApp', NULL, 't', 'f', '/payment/channel/douyin/manage/app/DouyinMchAppManage', '/payment/merchant/channel-merchant/douyin-app-manage', NULL, 13, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-15 16:00:00+00', '2026-06-15 16:00:00+00');
|
||||
|
||||
-- ----------------------------
|
||||
-- Primary Key structure for table iam_perm_menu
|
||||
-- ----------------------------
|
||||
ALTER TABLE "public"."iam_perm_menu" ADD CONSTRAINT "iam_perm_menu_pkey" PRIMARY KEY ("id");
|
||||
|
||||
-- ----------------------------
|
||||
-- 支付宝服务商相关菜单
|
||||
-- ----------------------------
|
||||
-- 支付产品详情分发页(隐藏,供支付产品管理卡片跳转进入对应通道总览页)
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40501, 405, 'payment:config:productDetail', 'admin', 'ProductDetailDispatch', '支付产品详情', 'Payment Product Detail', 'menu.payment.config.productDetail', NULL, 't', 'f', '/payment/config/product/detail/ProductDetailDispatch', '/payment/product-detail', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00')
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
pid = EXCLUDED.pid,
|
||||
menu_code = EXCLUDED.menu_code,
|
||||
name = EXCLUDED.name,
|
||||
title_cn = EXCLUDED.title_cn,
|
||||
title_en = EXCLUDED.title_en,
|
||||
i18n_key = EXCLUDED.i18n_key,
|
||||
component = EXCLUDED.component,
|
||||
path = EXCLUDED.path,
|
||||
sort_no = EXCLUDED.sort_no,
|
||||
menu_type = EXCLUDED.menu_type,
|
||||
last_modified_time = EXCLUDED.last_modified_time;
|
||||
|
||||
-- 支付宝服务商应用管理(隐藏,从支付宝服务商总览页卡片进入)
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40502, 405, 'payment:alipay:isvApp', 'admin', 'AlipayIsvAppManage', '支付宝服务商应用', 'Alipay ISV Application', 'menu.payment.alipay.isvApp', NULL, 't', 'f', '/payment/channel/alipay/manage/app/AlipayIsvAppManage', '/payment/config/product/app-manage', NULL, 3, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00')
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
pid = EXCLUDED.pid,
|
||||
menu_code = EXCLUDED.menu_code,
|
||||
name = EXCLUDED.name,
|
||||
title_cn = EXCLUDED.title_cn,
|
||||
title_en = EXCLUDED.title_en,
|
||||
i18n_key = EXCLUDED.i18n_key,
|
||||
component = EXCLUDED.component,
|
||||
path = EXCLUDED.path,
|
||||
sort_no = EXCLUDED.sort_no,
|
||||
menu_type = EXCLUDED.menu_type,
|
||||
last_modified_time = EXCLUDED.last_modified_time;
|
||||
|
||||
-- ----------------------------
|
||||
-- 微信服务商相关菜单
|
||||
-- ----------------------------
|
||||
-- 微信服务商应用管理(隐藏,从微信服务商总览页卡片进入)
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40503, 405, 'payment:wechat:isvApp', 'admin', 'WechatIsvAppManage', '微信服务商应用', 'WeChat ISV Application', 'menu.payment.wechat.isvApp', NULL, 't', 'f', '/payment/channel/wechat/manage/app/WechatIsvAppManage', '/payment/config/product/wechat-app-manage', NULL, 4, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00')
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
pid = EXCLUDED.pid,
|
||||
menu_code = EXCLUDED.menu_code,
|
||||
name = EXCLUDED.name,
|
||||
title_cn = EXCLUDED.title_cn,
|
||||
title_en = EXCLUDED.title_en,
|
||||
i18n_key = EXCLUDED.i18n_key,
|
||||
component = EXCLUDED.component,
|
||||
path = EXCLUDED.path,
|
||||
sort_no = EXCLUDED.sort_no,
|
||||
menu_type = EXCLUDED.menu_type,
|
||||
last_modified_time = EXCLUDED.last_modified_time;
|
||||
|
||||
|
||||
@@ -1,323 +1,3 @@
|
||||
-- =====================================================================
|
||||
-- 微信通道相关表 (DROP + CREATE 重建)
|
||||
-- 对应实体:
|
||||
-- org.dromara.daxpay.channel.wechat.entity.isv.WechatIsvApp
|
||||
-- org.dromara.daxpay.channel.wechat.entity.isv.WechatIsvAppAuthConfig
|
||||
-- org.dromara.daxpay.channel.wechat.entity.isv.WechatIsvKeyConfig
|
||||
-- org.dromara.daxpay.channel.wechat.entity.isv.WechatIsvChannelMerchant
|
||||
-- org.dromara.daxpay.channel.wechat.entity.direct.WechatDirectApp
|
||||
-- org.dromara.daxpay.channel.wechat.entity.direct.WechatDirectKeyConfig
|
||||
-- org.dromara.daxpay.channel.wechat.entity.direct.WechatDirectAppAuthConfig
|
||||
-- org.dromara.daxpay.channel.wechat.entity.direct.WechatDirectChannelMerchant
|
||||
-- 执行方式: 手动执行(业务表 DDL 不入库, 由开发者按需在数据库执行)
|
||||
-- 注意: 加密字段(DataEncryptTypeHandler)在数据库层均为普通 text, 加解密在应用层完成
|
||||
-- =====================================================================
|
||||
|
||||
-- =========================
|
||||
-- 1. 微信服务商应用 wechat_isv_app
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."wechat_isv_app";
|
||||
|
||||
CREATE TABLE "public"."wechat_isv_app" (
|
||||
"id" int8 NOT NULL,
|
||||
"app_name" varchar(64),
|
||||
"app_type" varchar(32),
|
||||
"wx_app_id" varchar(64),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."wechat_isv_app" IS '微信服务商应用';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."app_name" IS '应用名称';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."app_type" IS '应用类型';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."wx_app_id" IS '微信应用AppId';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."wechat_isv_app"
|
||||
ADD CONSTRAINT "pk_wechat_isv_app" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 2. 微信服务商应用授权配置 wechat_isv_app_auth_config
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."wechat_isv_app_auth_config";
|
||||
|
||||
CREATE TABLE "public"."wechat_isv_app_auth_config" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32),
|
||||
"channel_mch_no" varchar(64),
|
||||
"wechat_isv_app_id" int8,
|
||||
"app_secret" text,
|
||||
"auth_callback_url" varchar(255),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."wechat_isv_app_auth_config" IS '微信服务商应用授权配置';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."channel_mch_no" IS '通道商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."wechat_isv_app_id" IS '关联服务商应用ID(指向 wechat_isv_app.id)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."app_secret" IS '应用密钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."auth_callback_url" IS '授权回调地址(仅公众号)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_app_auth_config"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."wechat_isv_app_auth_config"
|
||||
ADD CONSTRAINT "pk_wechat_isv_app_auth_config" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 3. 微信服务商密钥配置 wechat_isv_key_config
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."wechat_isv_key_config";
|
||||
|
||||
CREATE TABLE "public"."wechat_isv_key_config" (
|
||||
"id" int8 NOT NULL,
|
||||
"product" varchar(32),
|
||||
"wx_mch_id" varchar(32),
|
||||
"api_key_v3" text,
|
||||
"public_key" text,
|
||||
"public_key_id" varchar(128),
|
||||
"private_key" text,
|
||||
"private_cert" text,
|
||||
"cert_serial_no" varchar(128),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."wechat_isv_key_config" IS '微信服务商密钥配置';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."product" IS '产品编码';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."wx_mch_id" IS '微信服务商商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."api_key_v3" IS 'APIv3密钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."public_key" IS '支付公钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."public_key_id" IS '支付公钥ID';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."private_key" IS '商户私钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."private_cert" IS '商户证书(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."cert_serial_no" IS '证书序列号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_key_config"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."wechat_isv_key_config"
|
||||
ADD CONSTRAINT "pk_wechat_isv_key_config" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 4. 微信服务商通道商户绑定 mch_wechat_isv_channel_merchant
|
||||
-- 微信特约商户关联到服务商本身, 不挂靠具体服务商应用
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."mch_wechat_isv_channel_merchant";
|
||||
|
||||
CREATE TABLE "public"."mch_wechat_isv_channel_merchant" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"product" varchar(32) NOT NULL,
|
||||
"sub_mch_id" varchar(32),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."mch_wechat_isv_channel_merchant" IS '微信服务商通道商户绑定';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."channel_mch_no" IS '通道商户号(WISV+雪花)';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."product" IS '所属支付产品';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."sub_mch_id" IS '微信特约商户号/二级商户号(V3服务商支付 sub_mchid)';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_isv_channel_merchant"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."mch_wechat_isv_channel_merchant"
|
||||
ADD CONSTRAINT "pk_mch_wechat_isv_channel_merchant" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 5. 微信直连通道商户绑定 mch_wechat_direct_channel_merchant
|
||||
-- 一个微信商户号(wx_mch_id)对应一个channelMchNo, 商户的多个应用共享此绑定
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."mch_wechat_direct_channel_merchant";
|
||||
|
||||
CREATE TABLE "public"."mch_wechat_direct_channel_merchant" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"product" varchar(32) NOT NULL,
|
||||
"wx_mch_id" varchar(32),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."mch_wechat_direct_channel_merchant" IS '微信直连通道商户绑定';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."channel_mch_no" IS '通道商户号(系统生成雪花号)';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."product" IS '所属支付产品';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."wx_mch_id" IS '微信直连商户号';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."mch_wechat_direct_channel_merchant"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."mch_wechat_direct_channel_merchant"
|
||||
ADD CONSTRAINT "pk_mch_wechat_direct_channel_merchant" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 6. 微信直连商户应用 wechat_direct_app
|
||||
-- 每个应用关联一个通道商户号, 拥有独立的微信应用ID
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."wechat_direct_app";
|
||||
|
||||
CREATE TABLE "public"."wechat_direct_app" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"app_name" varchar(64),
|
||||
"wx_app_id" varchar(64),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."wechat_direct_app" IS '微信直连商户应用';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."channel_mch_no" IS '通道商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."app_name" IS '应用名称';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."wx_app_id" IS '微信应用AppId';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."wechat_direct_app"
|
||||
ADD CONSTRAINT "pk_wechat_direct_app" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 7. 微信直连密钥配置 wechat_direct_key_config
|
||||
-- 直连商户维度的APIv3密钥和证书, 按通道商户号关联, 与具体应用无关, 敏感字段加密存储
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."wechat_direct_key_config";
|
||||
|
||||
CREATE TABLE "public"."wechat_direct_key_config" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"api_key_v3" text,
|
||||
"public_key" text,
|
||||
"public_key_id" varchar(128),
|
||||
"private_key" text,
|
||||
"private_cert" text,
|
||||
"cert_serial_no" varchar(128),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."wechat_direct_key_config" IS '微信直连密钥配置';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."channel_mch_no" IS '通道商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."api_key_v3" IS 'APIv3密钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."public_key" IS '支付公钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."public_key_id" IS '支付公钥ID';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."private_key" IS '商户私钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."private_cert" IS '商户证书(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."cert_serial_no" IS '证书序列号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_key_config"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."wechat_direct_key_config"
|
||||
ADD CONSTRAINT "pk_wechat_direct_key_config" PRIMARY KEY ("id");
|
||||
|
||||
|
||||
-- =========================
|
||||
-- 8. 微信直连商户应用授权配置 wechat_direct_app_auth_config
|
||||
-- =========================
|
||||
DROP TABLE IF EXISTS "public"."wechat_direct_app_auth_config";
|
||||
|
||||
CREATE TABLE "public"."wechat_direct_app_auth_config" (
|
||||
"id" int8 NOT NULL,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"wechat_direct_app_id" int8,
|
||||
"app_secret" text,
|
||||
"auth_callback_url" varchar(255),
|
||||
"creator" int8,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" int8,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" int4 DEFAULT 0 NOT NULL,
|
||||
"deleted" bool DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE "public"."wechat_direct_app_auth_config" IS '微信直连商户应用授权配置';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."channel_mch_no" IS '通道商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."wechat_direct_app_id" IS '关联应用ID(指向 wechat_direct_app.id)';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."app_secret" IS '应用密钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."auth_callback_url" IS '授权回调地址';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."creator" IS '创建人';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."last_modifier" IS '最后修改人';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."last_modified_time" IS '最后修改时间';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_direct_app_auth_config"."deleted" IS '逻辑删除标志';
|
||||
|
||||
ALTER TABLE "public"."wechat_direct_app_auth_config"
|
||||
ADD CONSTRAINT "pk_wechat_direct_app_auth_config" PRIMARY KEY ("id");
|
||||
-- 支付宝直连商户应用增加应用类型字段
|
||||
ALTER TABLE "public"."alipay_direct_app"
|
||||
ADD COLUMN "app_type" varchar(32);
|
||||
|
||||
@@ -31,6 +31,9 @@ public class AlipayDirectApp extends MchBaseEntity implements ToResult<AlipayDir
|
||||
/// 支付宝应用ID
|
||||
private String aliAppId;
|
||||
|
||||
/** 应用类型: mini_program-小程序 mobile_app-移动应用 web_app-网站应用 */
|
||||
private String appType;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public AlipayDirectAppResult toResult() {
|
||||
|
||||
@@ -35,4 +35,8 @@ public class AlipayDirectAppParam {
|
||||
@NotBlank(message = "{validation.field.aliAppId.notBlank}")
|
||||
@Schema(description = "支付宝应用ID")
|
||||
private String aliAppId;
|
||||
|
||||
@NotBlank(message = "{validation.field.appType.notBlank}")
|
||||
@Schema(description = "应用类型: mini_program-小程序 mobile_app-移动应用 web_app-网站应用")
|
||||
private String appType;
|
||||
}
|
||||
|
||||
@@ -24,4 +24,7 @@ public class AlipayDirectAppResult extends MchBaseResult {
|
||||
|
||||
@Schema(description = "支付宝应用ID")
|
||||
private String aliAppId;
|
||||
|
||||
@Schema(description = "应用类型: mini_program-小程序 mobile_app-移动应用 web_app-网站应用")
|
||||
private String appType;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.dromara.daxpay.channel.douyin.code;
|
||||
|
||||
/// # 抖音支付常量
|
||||
///
|
||||
/// @author xxm
|
||||
/// @since 2026/6/4
|
||||
public interface DouyinPayCode {
|
||||
|
||||
/** AES 加密 */
|
||||
String ENCRYPT_TYPE_AES = "AES";
|
||||
|
||||
/** 扫码支付 */
|
||||
String TRADE_TYPE_NATIVE = "NATIVE";
|
||||
|
||||
/** JSAPI支付 */
|
||||
String TRADE_TYPE_JSAPI = "JSAPI";
|
||||
|
||||
/** H5支付 */
|
||||
String TRADE_TYPE_MWEB = "MWEB";
|
||||
|
||||
/** APP支付 */
|
||||
String TRADE_TYPE_APP = "APP";
|
||||
|
||||
/** 支付成功 */
|
||||
String TRADE_STATE_SUCCESS = "SUCCESS";
|
||||
|
||||
/** 转入退款 */
|
||||
String TRADE_STATE_REFUND = "REFUND";
|
||||
|
||||
/** 未支付 */
|
||||
String TRADE_STATE_NOTPAY = "NOTPAY";
|
||||
|
||||
/** 用户支付中 */
|
||||
String TRADE_STATE_USERPAYING = "USERPAYING";
|
||||
|
||||
/** 已关闭 */
|
||||
String TRADE_STATE_CLOSED = "CLOSED";
|
||||
|
||||
/** 支付失败 */
|
||||
String TRADE_STATE_PAYERROR = "PAYERROR";
|
||||
|
||||
/** 回调签名序列号 */
|
||||
String HEADER_SERIAL = "Douyinpay-Serial";
|
||||
|
||||
/** 回调签名 */
|
||||
String HEADER_SIGNATURE = "Douyinpay-Signature";
|
||||
|
||||
/** 回调时间戳 */
|
||||
String HEADER_TIMESTAMP = "Douyinpay-Timestamp";
|
||||
|
||||
/** 回调随机串 */
|
||||
String HEADER_NONCE = "Douyinpay-Nonce";
|
||||
|
||||
/** 回调成功应答 */
|
||||
String NOTIFY_SUCCESS = "{\"code\":\"SUCCESS\",\"message\":\"成功\"}";
|
||||
|
||||
/** 回调失败应答 */
|
||||
String NOTIFY_FAIL = "{\"code\":\"FAIL\",\"message\":\"失败\"}";
|
||||
|
||||
/** 货币种类 */
|
||||
String CURRENCY_CNY = "CNY";
|
||||
|
||||
/** 退款成功 */
|
||||
String REFUND_STATUS_SUCCESS = "SUCCESS";
|
||||
|
||||
/** 退款关闭 */
|
||||
String REFUND_STATUS_CLOSED = "CLOSED";
|
||||
|
||||
/** 退款处理中 */
|
||||
String REFUND_STATUS_PROCESSING = "PROCESSING";
|
||||
|
||||
/** 退款异常 */
|
||||
String REFUND_STATUS_ABNORMAL = "ABNORMAL";
|
||||
|
||||
/** 退款成功通知 */
|
||||
String EVENT_TYPE_REFUND_SUCCESS = "REFUND.SUCCESS";
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package org.dromara.daxpay.channel.douyin.controller.direct;
|
||||
|
||||
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.platform.core.util.ValidationUtil;
|
||||
import org.dromara.daxpay.platform.core.validation.ValidationGroup;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectAppParam;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectAppAuthConfigParam;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectAppResult;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectAppAuthConfigResult;
|
||||
import org.dromara.daxpay.channel.douyin.service.direct.DouyinDirectAppService;
|
||||
import org.dromara.daxpay.channel.douyin.service.direct.DouyinDirectAppAuthConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 抖音直连商户应用管理
|
||||
///
|
||||
/// 提供直连商户应用及其授权认证配置的 REST API,支持按商户号和通道商户号查询列表。
|
||||
///
|
||||
@PermCode(menuCode = "payment:douyin:mch-app")
|
||||
@Validated
|
||||
@Tag(name = "抖音直连商户应用管理")
|
||||
@RestController
|
||||
@RequestMapping("/admin/douyin/mch-app")
|
||||
@RequiredArgsConstructor
|
||||
public class DouyinDirectAppController {
|
||||
|
||||
private final DouyinDirectAppService douyinDirectAppService;
|
||||
private final DouyinDirectAppAuthConfigService douyinDirectAppAuthConfigService;
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "根据商户号和通道商户号查询应用列表")
|
||||
@GetMapping("/list-by-channel-mch-no")
|
||||
public Result<List<DouyinDirectAppResult>> listByChannelMchNo(
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}") String mchNo,
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) {
|
||||
return Res.ok(douyinDirectAppService.listByMchNoAndChannelMchNo(mchNo, channelMchNo));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "查询应用详情")
|
||||
@GetMapping("/find-by-id")
|
||||
public Result<DouyinDirectAppResult> findById(
|
||||
@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
return Res.ok(douyinDirectAppService.findById(id));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "同一通道商户下抖音应用ID是否已存在")
|
||||
@GetMapping("/exists-douyin-app-id-by-channel")
|
||||
public Result<Boolean> existsDouyinAppIdByChannel(
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}") String mchNo,
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo,
|
||||
@NotBlank(message = "{validation.field.douyinAppId.notBlank}") String douyinAppId) {
|
||||
return Res.ok(douyinDirectAppService.existsDouyinAppIdByChannel(mchNo, channelMchNo, douyinAppId, null));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "同一通道商户下抖音应用ID是否已存在(排除自身)")
|
||||
@GetMapping("/exists-douyin-app-id-by-channel-not-id")
|
||||
public Result<Boolean> existsDouyinAppIdByChannelNotId(
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}") String mchNo,
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo,
|
||||
@NotBlank(message = "{validation.field.douyinAppId.notBlank}") String douyinAppId,
|
||||
@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
return Res.ok(douyinDirectAppService.existsDouyinAppIdByChannel(mchNo, channelMchNo, douyinAppId, id));
|
||||
}
|
||||
|
||||
@PermCode(code = "add", nameCn = "通道商户新增", nameEn = "Channel Merchant Add")
|
||||
@Operation(summary = "新增直连商户应用")
|
||||
@PostMapping("/add")
|
||||
public Result<Void> add(@RequestBody @Validated(ValidationGroup.add.class) DouyinDirectAppParam param) {
|
||||
ValidationUtil.validateParam(param, ValidationGroup.add.class);
|
||||
douyinDirectAppService.add(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "edit", nameCn = "通道商户编辑", nameEn = "Channel Merchant Edit")
|
||||
@Operation(summary = "修改直连商户应用")
|
||||
@PostMapping("/update")
|
||||
public Result<Void> update(@RequestBody @Validated(ValidationGroup.edit.class) DouyinDirectAppParam param) {
|
||||
ValidationUtil.validateParam(param, ValidationGroup.edit.class);
|
||||
douyinDirectAppService.update(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "edit", nameCn = "通道商户编辑", nameEn = "Channel Merchant Edit")
|
||||
@Operation(summary = "删除直连商户应用")
|
||||
@PostMapping("/delete")
|
||||
public Result<Void> delete(@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
douyinDirectAppService.delete(id);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "查询应用授权认证配置")
|
||||
@GetMapping("/find-auth-config-by-app-id")
|
||||
public Result<DouyinDirectAppAuthConfigResult> findAuthConfigByAppId(
|
||||
@NotNull(message = "{validation.field.douyinDirectAppId.notNull}") Long douyinDirectAppId) {
|
||||
var config = douyinDirectAppAuthConfigService.findByDouyinDirectAppId(douyinDirectAppId);
|
||||
return Res.ok(config.toResult());
|
||||
}
|
||||
|
||||
@PermCode(code = "edit", nameCn = "通道商户编辑", nameEn = "Channel Merchant Edit")
|
||||
@Operation(summary = "保存应用授权认证配置")
|
||||
@PostMapping("/save-auth-config")
|
||||
public Result<Void> saveAuthConfig(@RequestBody @Validated DouyinDirectAppAuthConfigParam param) {
|
||||
douyinDirectAppAuthConfigService.save(param);
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.dromara.daxpay.channel.douyin.controller.direct;
|
||||
|
||||
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.douyin.param.direct.DouyinDirectChannelMerchantCreateParam;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectKeyConfigParam;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectChannelMerchantResult;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectKeyConfigResult;
|
||||
import org.dromara.daxpay.channel.douyin.service.direct.DouyinDirectChannelMerchantService;
|
||||
import org.dromara.daxpay.channel.douyin.service.direct.DouyinDirectKeyConfigService;
|
||||
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/douyin/direct-channel-merchant")
|
||||
@RequiredArgsConstructor
|
||||
public class DouyinDirectChannelMerchantController {
|
||||
|
||||
private final DouyinDirectChannelMerchantService douyinDirectChannelMerchantService;
|
||||
private final DouyinDirectKeyConfigService douyinDirectKeyConfigService;
|
||||
|
||||
@PermCode(code = "view", nameCn = "商户通道商户查看", nameEn = "Merchant Channel Merchant View")
|
||||
@Operation(summary = "根据通道商户号查询抖音直连通道商户配置")
|
||||
@GetMapping("/find-by-channel-mch-no")
|
||||
public Result<DouyinDirectChannelMerchantResult> findByChannelMchNo(
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) {
|
||||
return Res.ok(douyinDirectChannelMerchantService.findByChannelMchNo(channelMchNo));
|
||||
}
|
||||
|
||||
@PermCode(code = "add", nameCn = "商户通道商户新增", nameEn = "Merchant Channel Merchant Add")
|
||||
@Operation(summary = "创建抖音直连通道商户")
|
||||
@PostMapping("/create")
|
||||
public Result<Void> create(@RequestBody @Validated DouyinDirectChannelMerchantCreateParam param) {
|
||||
douyinDirectChannelMerchantService.create(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "商户通道商户查看", nameEn = "Merchant Channel Merchant View")
|
||||
@Operation(summary = "根据通道商户号查询密钥配置")
|
||||
@GetMapping("/find-key-config")
|
||||
public Result<DouyinDirectKeyConfigResult> findKeyConfig(
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) {
|
||||
var config = douyinDirectKeyConfigService.findByChannelMchNo(channelMchNo);
|
||||
var result = config.toResult();
|
||||
result.setPrivateKeyConfigured(config.getMerchantPrivateKey() != null);
|
||||
result.setEncryptKeyConfigured(config.getEncryptKey() != null);
|
||||
return Res.ok(result);
|
||||
}
|
||||
|
||||
@PermCode(code = "edit", nameCn = "商户通道商户编辑", nameEn = "Merchant Channel Merchant Edit")
|
||||
@Operation(summary = "保存密钥配置")
|
||||
@PostMapping("/save-key-config")
|
||||
public Result<Void> saveKeyConfig(@RequestBody @Validated DouyinDirectKeyConfigParam param) {
|
||||
douyinDirectKeyConfigService.save(param);
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.dromara.daxpay.channel.douyin.convert.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectAppAuthConfig;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectAppAuthConfigParam;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectAppAuthConfigResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 抖音直连商户应用授权认证配置转换
|
||||
///
|
||||
@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
public interface DouyinDirectAppAuthConfigConvert {
|
||||
DouyinDirectAppAuthConfigConvert CONVERT = Mappers.getMapper(DouyinDirectAppAuthConfigConvert.class);
|
||||
|
||||
DouyinDirectAppAuthConfigResult toResult(DouyinDirectAppAuthConfig entity);
|
||||
|
||||
void copy(DouyinDirectAppAuthConfigParam param, @MappingTarget DouyinDirectAppAuthConfig entity);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.dromara.daxpay.channel.douyin.convert.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectApp;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectAppParam;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectAppResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 抖音直连商户应用转换
|
||||
///
|
||||
@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
public interface DouyinDirectAppConvert {
|
||||
DouyinDirectAppConvert CONVERT = Mappers.getMapper(DouyinDirectAppConvert.class);
|
||||
|
||||
DouyinDirectAppResult toResult(DouyinDirectApp entity);
|
||||
|
||||
DouyinDirectApp toEntity(DouyinDirectAppParam param);
|
||||
|
||||
void copy(DouyinDirectAppParam param, @MappingTarget DouyinDirectApp entity);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.dromara.daxpay.channel.douyin.convert.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectChannelMerchant;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectChannelMerchantResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 抖音直连通道商户转换
|
||||
///
|
||||
@Mapper
|
||||
public interface DouyinDirectChannelMerchantConvert {
|
||||
DouyinDirectChannelMerchantConvert CONVERT = Mappers.getMapper(DouyinDirectChannelMerchantConvert.class);
|
||||
|
||||
DouyinDirectChannelMerchantResult toResult(DouyinDirectChannelMerchant entity);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.dromara.daxpay.channel.douyin.convert.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectKeyConfig;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectKeyConfigParam;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectKeyConfigResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 抖音直连密钥配置转换
|
||||
///
|
||||
@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
public interface DouyinDirectKeyConfigConvert {
|
||||
DouyinDirectKeyConfigConvert CONVERT = Mappers.getMapper(DouyinDirectKeyConfigConvert.class);
|
||||
|
||||
DouyinDirectKeyConfigResult toResult(DouyinDirectKeyConfig entity);
|
||||
|
||||
void copy(DouyinDirectKeyConfigParam param, @MappingTarget DouyinDirectKeyConfig entity);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.daxpay.channel.douyin.dao.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectAppAuthConfig;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 抖音直连商户应用授权认证配置
|
||||
///
|
||||
/// 直连商户应用授权认证配置数据访问管理器,提供按应用ID查询和删除授权认证配置的方法。
|
||||
///
|
||||
@Repository
|
||||
public class DouyinDirectAppAuthConfigManager extends BaseManager<DouyinDirectAppAuthConfigMapper, DouyinDirectAppAuthConfig> {
|
||||
|
||||
/// 根据应用ID查询授权认证配置
|
||||
public Optional<DouyinDirectAppAuthConfig> findByDouyinDirectAppId(Long douyinDirectAppId) {
|
||||
return lambdaQuery()
|
||||
.eq(DouyinDirectAppAuthConfig::getDouyinDirectAppId, douyinDirectAppId)
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
/// 根据应用ID删除授权认证配置
|
||||
public void deleteByDouyinDirectAppId(Long douyinDirectAppId) {
|
||||
lambdaUpdate()
|
||||
.eq(DouyinDirectAppAuthConfig::getDouyinDirectAppId, douyinDirectAppId)
|
||||
.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.channel.douyin.dao.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectAppAuthConfig;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 抖音直连商户应用授权认证配置
|
||||
///
|
||||
@Mapper
|
||||
public interface DouyinDirectAppAuthConfigMapper extends MPJBaseMapper<DouyinDirectAppAuthConfig> {
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.dromara.daxpay.channel.douyin.dao.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectApp;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 抖音直连商户应用
|
||||
///
|
||||
/// 直连商户应用数据访问管理器,提供按商户号和通道商户号查询列表、同一通道下应用ID唯一性校验等方法。
|
||||
///
|
||||
@Repository
|
||||
public class DouyinDirectAppManager extends BaseManager<DouyinDirectAppMapper, DouyinDirectApp> {
|
||||
|
||||
/// 根据商户号和通道商户号查询应用列表(按创建时间升序)
|
||||
public List<DouyinDirectApp> listByMchNoAndChannelMchNo(String mchNo, String channelMchNo) {
|
||||
return lambdaQuery()
|
||||
.eq(DouyinDirectApp::getMchNo, mchNo)
|
||||
.eq(DouyinDirectApp::getChannelMchNo, channelMchNo)
|
||||
.orderByAsc(DouyinDirectApp::getCreateTime)
|
||||
.orderByAsc(DouyinDirectApp::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 校验同一通道商户下douyinAppId是否已存在(排除自身)
|
||||
public boolean existsByChannelMchNoAndDouyinAppId(String mchNo, String channelMchNo, String douyinAppId, Long excludeId) {
|
||||
return lambdaQuery()
|
||||
.eq(DouyinDirectApp::getMchNo, mchNo)
|
||||
.eq(DouyinDirectApp::getChannelMchNo, channelMchNo)
|
||||
.eq(DouyinDirectApp::getDouyinAppId, douyinAppId)
|
||||
.ne(excludeId != null, DouyinDirectApp::getId, excludeId)
|
||||
.exists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.channel.douyin.dao.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectApp;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 抖音直连商户应用
|
||||
///
|
||||
@Mapper
|
||||
public interface DouyinDirectAppMapper extends MPJBaseMapper<DouyinDirectApp> {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.daxpay.channel.douyin.dao.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectChannelMerchant;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 抖音直连通道商户绑定
|
||||
///
|
||||
@Repository
|
||||
public class DouyinDirectChannelMerchantManager extends BaseManager<DouyinDirectChannelMerchantMapper, DouyinDirectChannelMerchant> {
|
||||
|
||||
/// 校验同一商户下抖音直连商户号不重复
|
||||
public boolean existsByMchNoAndDyMchId(String mchNo, String dyMchId) {
|
||||
return lambdaQuery()
|
||||
.eq(DouyinDirectChannelMerchant::getMchNo, mchNo)
|
||||
.eq(DouyinDirectChannelMerchant::getDyMchId, dyMchId)
|
||||
.exists();
|
||||
}
|
||||
|
||||
/// 根据通道商户号查询
|
||||
public Optional<DouyinDirectChannelMerchant> findByChannelMchNo(String channelMchNo) {
|
||||
return lambdaQuery()
|
||||
.eq(DouyinDirectChannelMerchant::getChannelMchNo, channelMchNo)
|
||||
.oneOpt();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.dromara.daxpay.channel.douyin.dao.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectChannelMerchant;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 抖音直连通道商户绑定
|
||||
///
|
||||
/// 抖音直连通道商户 MyBatis-Plus Mapper,继承 MPJBaseMapper 支持多表联查。
|
||||
///
|
||||
@Mapper
|
||||
public interface DouyinDirectChannelMerchantMapper extends MPJBaseMapper<DouyinDirectChannelMerchant> {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.daxpay.channel.douyin.dao.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectKeyConfig;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 抖音直连密钥配置
|
||||
///
|
||||
/// 直连密钥配置数据访问管理器,提供按通道商户号查询和删除密钥配置的方法。
|
||||
///
|
||||
@Repository
|
||||
public class DouyinDirectKeyConfigManager extends BaseManager<DouyinDirectKeyConfigMapper, DouyinDirectKeyConfig> {
|
||||
|
||||
/// 根据通道商户号查询密钥配置
|
||||
public Optional<DouyinDirectKeyConfig> findByChannelMchNo(String channelMchNo) {
|
||||
return lambdaQuery()
|
||||
.eq(DouyinDirectKeyConfig::getChannelMchNo, channelMchNo)
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
/// 根据通道商户号删除密钥配置
|
||||
public void deleteByChannelMchNo(String channelMchNo) {
|
||||
lambdaUpdate()
|
||||
.eq(DouyinDirectKeyConfig::getChannelMchNo, channelMchNo)
|
||||
.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.channel.douyin.dao.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectKeyConfig;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 抖音直连密钥配置
|
||||
///
|
||||
@Mapper
|
||||
public interface DouyinDirectKeyConfigMapper extends MPJBaseMapper<DouyinDirectKeyConfig> {
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.dromara.daxpay.channel.douyin.entity.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.convert.direct.DouyinDirectAppConvert;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectAppResult;
|
||||
import org.dromara.daxpay.payment.common.entity.merchant.MchBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 抖音直连商户应用
|
||||
///
|
||||
/// 表示抖音直连模式下商户注册的应用实体,每个应用关联一个通道商户号,拥有独立的抖音应用ID。
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "douyin_direct_app", autoResultMap = true)
|
||||
public class DouyinDirectApp extends MchBaseEntity implements ToResult<DouyinDirectAppResult> {
|
||||
|
||||
/// 通道商户号
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String channelMchNo;
|
||||
|
||||
/// 应用名称
|
||||
private String appName;
|
||||
|
||||
/// 抖音应用AppId(APPID)
|
||||
private String douyinAppId;
|
||||
|
||||
/** 应用类型: mini_program-小程序 mobile_app-移动应用 web_app-网站应用 */
|
||||
private String appType;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public DouyinDirectAppResult toResult() {
|
||||
return DouyinDirectAppConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.daxpay.channel.douyin.entity.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.convert.direct.DouyinDirectAppAuthConfigConvert;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectAppAuthConfigResult;
|
||||
import org.dromara.daxpay.payment.common.entity.merchant.MchBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
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 = "douyin_direct_app_auth_config", autoResultMap = true)
|
||||
public class DouyinDirectAppAuthConfig extends MchBaseEntity implements ToResult<DouyinDirectAppAuthConfigResult> {
|
||||
|
||||
/// 通道商户号
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String channelMchNo;
|
||||
|
||||
/// 关联应用ID
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private Long douyinDirectAppId;
|
||||
|
||||
/// 授权回调地址
|
||||
private String authCallbackUrl;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public DouyinDirectAppAuthConfigResult toResult() {
|
||||
return DouyinDirectAppAuthConfigConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.dromara.daxpay.channel.douyin.entity.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.convert.direct.DouyinDirectChannelMerchantConvert;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectChannelMerchantResult;
|
||||
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;
|
||||
|
||||
/// # 抖音直连通道商户绑定
|
||||
///
|
||||
/// 一个抖音商户号(dyMchId)对应一个 channelMchNo, 商户的多个应用共享此绑定。
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "douyin_direct_channel_merchant", autoResultMap = true)
|
||||
public class DouyinDirectChannelMerchant extends MchBaseEntity implements ToResult<DouyinDirectChannelMerchantResult> {
|
||||
|
||||
/// 通道商户号(系统生成雪花号)
|
||||
private String channelMchNo;
|
||||
|
||||
/// 所属支付产品
|
||||
/// @see org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum
|
||||
private String product;
|
||||
|
||||
/// 抖音商户号(MCHID)
|
||||
private String dyMchId;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public DouyinDirectChannelMerchantResult toResult() {
|
||||
return DouyinDirectChannelMerchantConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.dromara.daxpay.channel.douyin.entity.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.convert.direct.DouyinDirectKeyConfigConvert;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectKeyConfigResult;
|
||||
import org.dromara.daxpay.payment.common.entity.merchant.MchBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.handler.encrypt.DataEncryptTypeHandler;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
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 = "douyin_direct_key_config", autoResultMap = true)
|
||||
public class DouyinDirectKeyConfig extends MchBaseEntity implements ToResult<DouyinDirectKeyConfigResult> {
|
||||
|
||||
/// 通道商户号
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String channelMchNo;
|
||||
|
||||
/// 商户私钥(加密存储)
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String merchantPrivateKey;
|
||||
|
||||
/// 商家公钥证书序列号
|
||||
private String merchantSerialNumber;
|
||||
|
||||
/// 接口加密密钥(加密存储)
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String encryptKey;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public DouyinDirectKeyConfigResult toResult() {
|
||||
return DouyinDirectKeyConfigConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.dromara.daxpay.channel.douyin.param.direct;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 抖音直连商户应用授权认证配置保存参数
|
||||
///
|
||||
/// 保存/更新抖音直连商户应用授权认证配置时接收的请求参数,含商户号、通道商户号和授权回调地址。
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "抖音直连商户应用授权认证配置保存参数")
|
||||
public class DouyinDirectAppAuthConfigParam {
|
||||
|
||||
@NotNull(message = "{validation.field.douyinDirectAppId.notNull}")
|
||||
@Schema(description = "关联应用ID")
|
||||
private Long douyinDirectAppId;
|
||||
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}")
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}")
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Size(max = 512, message = "{validation.field.authCallbackUrl.size}")
|
||||
@Schema(description = "授权回调地址")
|
||||
private String authCallbackUrl;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.dromara.daxpay.channel.douyin.param.direct;
|
||||
|
||||
import org.dromara.daxpay.platform.core.validation.ValidationGroup;
|
||||
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;
|
||||
|
||||
/// # 抖音直连商户应用保存参数
|
||||
///
|
||||
/// 保存/更新抖音直连商户应用时接收的请求参数,含商户号、通道商户号、应用名称和抖音应用AppId。
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "抖音直连商户应用保存参数")
|
||||
public class DouyinDirectAppParam {
|
||||
|
||||
@NotNull(message = "{validation.field.id.notNull}", groups = ValidationGroup.edit.class)
|
||||
@Schema(description = "主键,新增时不传")
|
||||
private Long id;
|
||||
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}")
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}")
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@NotBlank(message = "{validation.field.appName.notBlank}")
|
||||
@Schema(description = "应用名称")
|
||||
private String appName;
|
||||
|
||||
@NotBlank(message = "{validation.field.douyinAppId.notBlank}")
|
||||
@Schema(description = "抖音应用AppId(APPID)")
|
||||
private String douyinAppId;
|
||||
|
||||
@NotBlank(message = "{validation.field.appType.notBlank}", groups = ValidationGroup.add.class)
|
||||
@Schema(description = "应用类型: mini_program-小程序 mobile_app-移动应用 web_app-网站应用")
|
||||
private String appType;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.dromara.daxpay.channel.douyin.param.direct;
|
||||
|
||||
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 DouyinDirectChannelMerchantCreateParam {
|
||||
|
||||
/// 商户号
|
||||
@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;
|
||||
|
||||
/// 抖音商户号(MCHID)
|
||||
@Schema(description = "抖音商户号(MCHID)")
|
||||
@NotBlank(message = "{validation.field.dyMchId.notBlank}")
|
||||
private String dyMchId;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.dromara.daxpay.channel.douyin.param.direct;
|
||||
|
||||
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 DouyinDirectKeyConfigParam {
|
||||
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}")
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}")
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
@Schema(description = "商户私钥")
|
||||
private String merchantPrivateKey;
|
||||
|
||||
@Schema(description = "商家公钥证书序列号")
|
||||
private String merchantSerialNumber;
|
||||
|
||||
@Schema(description = "接口加密密钥")
|
||||
private String encryptKey;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.daxpay.channel.douyin.result.direct;
|
||||
|
||||
import org.dromara.daxpay.payment.common.result.MchBaseResult;
|
||||
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 DouyinDirectAppAuthConfigResult extends MchBaseResult {
|
||||
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "关联应用ID")
|
||||
private Long douyinDirectAppId;
|
||||
|
||||
@Schema(description = "授权回调地址")
|
||||
private String authCallbackUrl;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.daxpay.channel.douyin.result.direct;
|
||||
|
||||
import org.dromara.daxpay.payment.common.result.MchBaseResult;
|
||||
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 DouyinDirectAppResult extends MchBaseResult {
|
||||
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "应用名称")
|
||||
private String appName;
|
||||
|
||||
@Schema(description = "抖音应用AppId(APPID)")
|
||||
private String douyinAppId;
|
||||
|
||||
@Schema(description = "应用类型")
|
||||
private String appType;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.daxpay.channel.douyin.result.direct;
|
||||
|
||||
import org.dromara.daxpay.payment.common.result.MchBaseResult;
|
||||
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 DouyinDirectChannelMerchantResult extends MchBaseResult {
|
||||
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "所属支付产品")
|
||||
private String product;
|
||||
|
||||
@Schema(description = "抖音商户号(MCHID)")
|
||||
private String dyMchId;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.dromara.daxpay.channel.douyin.result.direct;
|
||||
|
||||
import org.dromara.daxpay.payment.common.result.MchBaseResult;
|
||||
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 DouyinDirectKeyConfigResult extends MchBaseResult {
|
||||
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "商户私钥(已脱敏)")
|
||||
private String merchantPrivateKey;
|
||||
|
||||
@Schema(description = "商家公钥证书序列号")
|
||||
private String merchantSerialNumber;
|
||||
|
||||
@Schema(description = "接口加密密钥(已脱敏)")
|
||||
private String encryptKey;
|
||||
|
||||
@Schema(description = "私钥是否已配置")
|
||||
private boolean privateKeyConfigured;
|
||||
|
||||
@Schema(description = "加密密钥是否已配置")
|
||||
private boolean encryptKeyConfigured;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.dromara.daxpay.channel.douyin.service.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.convert.direct.DouyinDirectAppAuthConfigConvert;
|
||||
import org.dromara.daxpay.channel.douyin.dao.direct.DouyinDirectAppManager;
|
||||
import org.dromara.daxpay.channel.douyin.dao.direct.DouyinDirectAppAuthConfigManager;
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectAppAuthConfig;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectAppAuthConfigParam;
|
||||
import org.dromara.daxpay.platform.core.code.CommonErrorCode;
|
||||
import org.dromara.daxpay.platform.core.exception.BizInfoException;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/// # 抖音直连商户应用授权认证配置
|
||||
///
|
||||
/// 管理直连商户应用的授权认证配置,查询时不存在则创建默认记录,保存时校验应用归属关系。
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DouyinDirectAppAuthConfigService {
|
||||
|
||||
private final DouyinDirectAppAuthConfigManager douyinDirectAppAuthConfigManager;
|
||||
private final DouyinDirectAppManager douyinDirectAppManager;
|
||||
|
||||
/// 根据应用ID查询授权认证配置, 不存在则创建默认记录
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public DouyinDirectAppAuthConfig findByDouyinDirectAppId(Long douyinDirectAppId) {
|
||||
var existing = douyinDirectAppAuthConfigManager.findByDouyinDirectAppId(douyinDirectAppId);
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
var app = douyinDirectAppManager.findById(douyinDirectAppId)
|
||||
// 抖音: 直连商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.douyin.mchAppNotFound"));
|
||||
var config = new DouyinDirectAppAuthConfig()
|
||||
.setChannelMchNo(app.getChannelMchNo())
|
||||
.setDouyinDirectAppId(douyinDirectAppId);
|
||||
config.setMchNo(app.getMchNo());
|
||||
douyinDirectAppAuthConfigManager.save(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
/// 保存应用授权认证配置(更新)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(DouyinDirectAppAuthConfigParam param) {
|
||||
var app = douyinDirectAppManager.findById(param.getDouyinDirectAppId())
|
||||
// 抖音: 直连商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.douyin.mchAppNotFound"));
|
||||
if (!app.getMchNo().equals(param.getMchNo()) || !app.getChannelMchNo().equals(param.getChannelMchNo())) {
|
||||
// 抖音: 直连商户应用不存在或商户号归属不匹配
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.douyin.mchAppNotFound");
|
||||
}
|
||||
var config = this.findByDouyinDirectAppId(param.getDouyinDirectAppId());
|
||||
config.setAuthCallbackUrl(param.getAuthCallbackUrl());
|
||||
DouyinDirectAppAuthConfigConvert.CONVERT.copy(param, config);
|
||||
douyinDirectAppAuthConfigManager.updateById(config);
|
||||
}
|
||||
|
||||
/// 删除应用授权认证配置
|
||||
public void deleteByDouyinDirectAppId(Long douyinDirectAppId) {
|
||||
douyinDirectAppAuthConfigManager.deleteByDouyinDirectAppId(douyinDirectAppId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.dromara.daxpay.channel.douyin.service.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.convert.direct.DouyinDirectAppConvert;
|
||||
import org.dromara.daxpay.channel.douyin.dao.direct.DouyinDirectAppManager;
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectApp;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectAppParam;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectAppResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.util.MpUtil;
|
||||
import org.dromara.daxpay.platform.core.code.CommonErrorCode;
|
||||
import org.dromara.daxpay.platform.core.exception.BizInfoException;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 抖音直连商户应用管理
|
||||
///
|
||||
/// 提供直连商户应用的增删改查功能,包含同一通道商户下应用ID的唯一性校验和操作范围校验(防止跨商户操作)。
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DouyinDirectAppService {
|
||||
|
||||
private final DouyinDirectAppManager douyinDirectAppManager;
|
||||
private final DouyinDirectAppAuthConfigService douyinDirectAppAuthConfigService;
|
||||
|
||||
/// 根据商户号和通道商户号查询应用列表
|
||||
public List<DouyinDirectAppResult> listByMchNoAndChannelMchNo(String mchNo, String channelMchNo) {
|
||||
var list = douyinDirectAppManager.listByMchNoAndChannelMchNo(mchNo, channelMchNo);
|
||||
return MpUtil.toListResult(list);
|
||||
}
|
||||
|
||||
/// 根据ID查询应用详情
|
||||
public DouyinDirectAppResult findById(Long id) {
|
||||
return douyinDirectAppManager.findById(id)
|
||||
.map(DouyinDirectApp::toResult)
|
||||
// 抖音: 直连商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.douyin.mchAppNotFound"));
|
||||
}
|
||||
|
||||
/// 判断同一通道商户下抖音应用ID是否已存在
|
||||
public boolean existsDouyinAppIdByChannel(String mchNo, String channelMchNo, String douyinAppId, Long excludeId) {
|
||||
if (StrUtil.hasBlank(mchNo, channelMchNo, douyinAppId)) {
|
||||
return false;
|
||||
}
|
||||
return douyinDirectAppManager.existsByChannelMchNoAndDouyinAppId(mchNo, channelMchNo, douyinAppId, excludeId);
|
||||
}
|
||||
|
||||
/// 新增抖音直连商户应用
|
||||
public void add(DouyinDirectAppParam param) {
|
||||
this.assertDouyinAppIdUnique(param.getMchNo(), param.getChannelMchNo(), param.getDouyinAppId(), null);
|
||||
var entity = DouyinDirectAppConvert.CONVERT.toEntity(param);
|
||||
douyinDirectAppManager.save(entity);
|
||||
}
|
||||
|
||||
/// 更新抖音直连商户应用
|
||||
public void update(DouyinDirectAppParam param) {
|
||||
var entity = douyinDirectAppManager.findById(param.getId())
|
||||
// 抖音: 直连商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.douyin.mchAppNotFound"));
|
||||
this.assertScopeMatch(entity, param.getMchNo(), param.getChannelMchNo());
|
||||
this.assertDouyinAppIdUnique(entity.getMchNo(), entity.getChannelMchNo(), param.getDouyinAppId(), param.getId());
|
||||
DouyinDirectAppConvert.CONVERT.copy(param, entity);
|
||||
douyinDirectAppManager.updateById(entity);
|
||||
}
|
||||
|
||||
/// 删除应用(级联删除授权认证配置)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delete(Long id) {
|
||||
douyinDirectAppManager.findById(id)
|
||||
// 抖音: 直连商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.douyin.mchAppNotFound"));
|
||||
douyinDirectAppAuthConfigService.deleteByDouyinDirectAppId(id);
|
||||
douyinDirectAppManager.deleteById(id);
|
||||
}
|
||||
|
||||
/// 校验同一通道商户下应用ID唯一
|
||||
private void assertDouyinAppIdUnique(String mchNo, String channelMchNo, String douyinAppId, Long excludeId) {
|
||||
if (this.existsDouyinAppIdByChannel(mchNo, channelMchNo, douyinAppId, excludeId)) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.douyin.appIdDuplicate");
|
||||
}
|
||||
}
|
||||
|
||||
/// 校验操作范围与记录归属一致
|
||||
private void assertScopeMatch(DouyinDirectApp entity, String mchNo, String channelMchNo) {
|
||||
if (!entity.getMchNo().equals(mchNo) || !entity.getChannelMchNo().equals(channelMchNo)) {
|
||||
// 抖音: 直连商户应用不存在或商户号归属不匹配
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.douyin.mchAppNotFound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.dromara.daxpay.channel.douyin.service.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.dao.direct.DouyinDirectChannelMerchantManager;
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectChannelMerchant;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectChannelMerchantCreateParam;
|
||||
import org.dromara.daxpay.channel.douyin.result.direct.DouyinDirectChannelMerchantResult;
|
||||
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;
|
||||
|
||||
/// # 抖音直连通道商户管理
|
||||
///
|
||||
/// 一个抖音商户号(dyMchId)对应一个 channelMchNo, 商户的多个应用共享此绑定。
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DouyinDirectChannelMerchantService {
|
||||
|
||||
private final ChannelMerchantManager channelMerchantManager;
|
||||
private final DouyinDirectChannelMerchantManager douyinDirectChannelMerchantManager;
|
||||
|
||||
/// 创建抖音直连通道商户
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void create(DouyinDirectChannelMerchantCreateParam param) {
|
||||
// 校验同一商户下抖音商户号不重复
|
||||
if (douyinDirectChannelMerchantManager.existsByMchNoAndDyMchId(
|
||||
param.getMchNo(), param.getDyMchId())) {
|
||||
// 抖音: 同一商户下该抖音商户已存在
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.douyin.directMchDuplicate");
|
||||
}
|
||||
// 生成通道商户号(雪花号)
|
||||
String channelMchNo = String.valueOf(IdUtil.getSnowflakeNextId());
|
||||
// 写通用通道商户主表
|
||||
ChannelMerchant channelMerchant = new ChannelMerchant();
|
||||
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);
|
||||
// 写直连绑定表(dyMchId 作为业务字段)
|
||||
DouyinDirectChannelMerchant entity = new DouyinDirectChannelMerchant();
|
||||
entity.setMchNo(param.getMchNo());
|
||||
entity.setChannelMchNo(channelMchNo);
|
||||
entity.setProduct(param.getProduct());
|
||||
entity.setDyMchId(param.getDyMchId());
|
||||
douyinDirectChannelMerchantManager.save(entity);
|
||||
}
|
||||
|
||||
/// 根据通道商户号查询直连通道商户配置
|
||||
public DouyinDirectChannelMerchantResult findByChannelMchNo(String channelMchNo) {
|
||||
return douyinDirectChannelMerchantManager.lambdaQuery()
|
||||
.eq(DouyinDirectChannelMerchant::getChannelMchNo, channelMchNo)
|
||||
.oneOpt()
|
||||
.map(DouyinDirectChannelMerchant::toResult)
|
||||
// 通道: 通道商户配置不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.dromara.daxpay.channel.douyin.service.direct;
|
||||
|
||||
import org.dromara.daxpay.channel.douyin.convert.direct.DouyinDirectKeyConfigConvert;
|
||||
import org.dromara.daxpay.channel.douyin.dao.direct.DouyinDirectChannelMerchantManager;
|
||||
import org.dromara.daxpay.channel.douyin.dao.direct.DouyinDirectKeyConfigManager;
|
||||
import org.dromara.daxpay.channel.douyin.entity.direct.DouyinDirectKeyConfig;
|
||||
import org.dromara.daxpay.channel.douyin.param.direct.DouyinDirectKeyConfigParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/// # 抖音直连密钥配置
|
||||
///
|
||||
/// 管理直连商户维度的密钥配置,查询时不存在则创建默认记录,保存时合并敏感字段。
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DouyinDirectKeyConfigService {
|
||||
|
||||
private final DouyinDirectKeyConfigManager douyinDirectKeyConfigManager;
|
||||
private final DouyinDirectChannelMerchantManager douyinDirectChannelMerchantManager;
|
||||
|
||||
/// 根据通道商户号查询密钥配置, 不存在则创建默认记录
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public DouyinDirectKeyConfig findByChannelMchNo(String channelMchNo) {
|
||||
var existing = douyinDirectKeyConfigManager.findByChannelMchNo(channelMchNo);
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
var config = new DouyinDirectKeyConfig()
|
||||
.setChannelMchNo(channelMchNo);
|
||||
// 查询通道商户记录填充商户号
|
||||
douyinDirectChannelMerchantManager.findByChannelMchNo(channelMchNo)
|
||||
.ifPresent(mch -> config.setMchNo(mch.getMchNo()));
|
||||
douyinDirectKeyConfigManager.save(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
/// 保存密钥配置(更新)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(DouyinDirectKeyConfigParam param) {
|
||||
var config = this.findByChannelMchNo(param.getChannelMchNo());
|
||||
config.setMchNo(param.getMchNo());
|
||||
DouyinDirectKeyConfigConvert.CONVERT.copy(param, config);
|
||||
douyinDirectKeyConfigManager.updateById(config);
|
||||
}
|
||||
|
||||
/// 根据通道商户号删除密钥配置
|
||||
public void deleteByChannelMchNo(String channelMchNo) {
|
||||
douyinDirectKeyConfigManager.deleteByChannelMchNo(channelMchNo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.daxpay.channel.douyin.strategy;
|
||||
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum;
|
||||
import org.dromara.daxpay.payment.pay.bo.trade.PayResultBo;
|
||||
import org.dromara.daxpay.payment.pay.strategy.AbsPayStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// # 抖音支付直连支付策略
|
||||
///
|
||||
/// 抖音支付直连模式的支付策略(虚类,暂未实现具体支付逻辑)。
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DouyinDirectPayStrategy extends AbsPayStrategy {
|
||||
|
||||
@Override
|
||||
public ProductEnum getProduct() {
|
||||
return ProductEnum.DOUYIN_PAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayResultBo doPayHandler() {
|
||||
throw new UnsupportedOperationException("抖音支付直连暂未实现");
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,11 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.dromara.daxpay.platform.core.enums.pay.channel.PayProviderEnum.DOUYIN;
|
||||
|
||||
/// # 抖音支付直连产品策略
|
||||
///
|
||||
@Slf4j
|
||||
@@ -22,11 +23,22 @@ import java.util.Map;
|
||||
@RequiredArgsConstructor
|
||||
public class DouyinPayProductStrategy extends AbsProductStrategy {
|
||||
|
||||
private static final Map<PayMethodEnum, List<PayCapabilityEnum>> METHOD_CAP_MAP = Map.of(
|
||||
PayMethodEnum.DOUYIN_QR, List.of(PayCapabilityEnum.DOUYIN_QR),
|
||||
PayMethodEnum.DOUYIN_JSAPI, List.of(PayCapabilityEnum.DOUYIN_JSAPI),
|
||||
PayMethodEnum.DOUYIN_H5, List.of(PayCapabilityEnum.DOUYIN_H5),
|
||||
PayMethodEnum.DOUYIN_APP, List.of(PayCapabilityEnum.DOUYIN_APP));
|
||||
|
||||
@Override
|
||||
public ProductEnum getProduct() {
|
||||
return ProductEnum.DOUYIN_PAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllocatable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSandbox() {
|
||||
return false;
|
||||
@@ -44,11 +56,11 @@ public class DouyinPayProductStrategy extends AbsProductStrategy {
|
||||
|
||||
@Override
|
||||
public List<PayProviderEnum> supportedPayProviders() {
|
||||
return Collections.emptyList();
|
||||
return List.of(DOUYIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<PayMethodEnum, List<PayCapabilityEnum>> methodCapabilityMapping() {
|
||||
return Collections.emptyMap();
|
||||
return METHOD_CAP_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user