mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-12 15:35:38 +08:00
feat(wechat): 微信服务商子商户应用功能 + 授权配置基类修复
- 新增微信服务商通道商户应用(WechatIsvMchApp)子商户自己的微信应用管理, 含应用/能力绑定/授权配置三表及配套 controller/service/dao/convert/param/result - 修复 WechatIsvAppAuthConfig 基类错误(MchBaseEntity→MpBaseEntity): 服务商应用为平台级全局配置, 不挂商户维度, 移除冗余的 mchNo/channelMchNo 字段, 修复 channel_mch_no / mch_no 列不存在的运行时异常 - 支付宝支付异步通知地址改用平台端点配置(backendBaseUrl)生成 - 微信直连凭证组装从通道商户绑定表取真实 wxMchId(channelMchNo 为系统雪花号) - 支付方式枚举与 i18n 文案统一精简(去掉冗余'支付'后缀, 统一大小写) - 校验消息 key 修正(wechatIsvAppId / wechatIsvMchAppId)
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
Target Server Version : 160014 (160014)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 03/07/2026 18:32:43
|
||||
Date: 05/07/2026 10:35:00
|
||||
*/
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ INSERT INTO "public"."iam_perm_menu" VALUES (4040116, 401, 'payment:platform:pro
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (309, 308, 'system:notify:notice', 'admin', 'SystemNotify', '通知公告', 'Notification', 'menu.system.notify.notice', 'lucide:megaphone', 'f', 'f', '/system/notify/notice/NoticeList', '/system/notify/notice', NULL, 20, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, '2026-06-24 16:00:00+00', '2026-06-24 16:00:00+00', NULL);
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (308, 3, 'system:notify', 'admin', 'SystemNotice', '通知中心', 'Notification Center', 'menu.system.notify', 'lucide:bell', 'f', 'f', NULL, '/system/notify', '/system/notify/notice', 20, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-01 07:16:30.927645+00', '2026-07-01 07:16:30.927645+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (603, 6, 'payment:refund', 'admin', 'RefundOrderList', '退款订单', 'Refund Orders', 'menu.trade.refundOrder', 'lucide:rotate-ccw', 'f', 'f', '/payment/order/RefundOrderList', '/trade/refund-order', NULL, 3, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-03 16:00:00+00', '2026-07-03 16:00:00+00');
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (4040119, 40401, 'channel:wechat:app', 'admin', 'WechatIsvMchAppManage', '微信服务商通道商户应用', 'WeChat ISV Channel Merchant App', 'menu.payment.merchant.channelMerchant.wechatIsvMchApp', NULL, 't', 'f', '/payment/channel/wechat/manage/mch/isv-app/WechatIsvMchAppManage', '/payment/merchant/channel-merchant/wechat-isv-mch-app-manage', NULL, 14, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-05 01:49:32.557374+00', '2026-07-05 01:49:32.557374+00');
|
||||
|
||||
-- ----------------------------
|
||||
-- Primary Key structure for table iam_perm_menu
|
||||
|
||||
@@ -3,3 +3,44 @@ INSERT INTO "public"."iam_perm_menu" VALUES (4040118, 40401, 'merchant:notify_co
|
||||
|
||||
-- 退款订单: 菜单(权限码 payment:refund)
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (603, 6, 'payment:refund', 'admin', 'RefundOrderList', '退款订单', 'Refund Orders', 'menu.trade.refundOrder', 'lucide:rotate-ccw', 'f', 'f', '/payment/order/RefundOrderList', '/trade/refund-order', NULL, 3, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-03 16:00:00+00', '2026-07-03 16:00:00+00');
|
||||
|
||||
-- 微信服务商通道商户应用(子商户应用): 菜单(权限码 channel:wechat:app)
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (4040119, 40401, 'channel:wechat:app', 'admin', 'WechatIsvMchAppManage', '微信服务商通道商户应用', 'WeChat ISV Channel Merchant App', 'menu.payment.merchant.channelMerchant.wechatIsvMchApp', NULL, 't', 'f', '/payment/channel/wechat/manage/mch/isv-app/WechatIsvMchAppManage', '/payment/merchant/channel-merchant/wechat-isv-mch-app-manage', NULL, 14, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-05 00:00:00+00', '2026-07-05 00:00:00+00');
|
||||
|
||||
-- ============================================================
|
||||
-- 菜单标题与 i18n_key 统一重命名
|
||||
-- ============================================================
|
||||
|
||||
-- 支付产品详情 → 产品配置详情
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='产品配置详情', title_en='Product Config Detail',
|
||||
i18n_key='menu.payment.config.detail' WHERE id=40501;
|
||||
-- 支付宝服务商应用: i18n_key 归入 config.*
|
||||
UPDATE "public"."iam_perm_menu" SET i18n_key='menu.payment.config.alipayApp' WHERE id=40502;
|
||||
-- 微信服务商应用: i18n_key 归入 config.*
|
||||
UPDATE "public"."iam_perm_menu" SET i18n_key='menu.payment.config.wechatApp' WHERE id=40503;
|
||||
|
||||
-- 商户信息(列表入口) → 商户列表
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='商户列表', title_en='Merchant List',
|
||||
i18n_key='menu.payment.merchant.list' WHERE id=40401;
|
||||
-- 商户管理(工作台) → 商户工作台
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='商户工作台', title_en='Merchant Workbench',
|
||||
i18n_key='menu.payment.merchant.workbench' WHERE id=4040101;
|
||||
-- 对接配置: i18n_key 去掉中间 manage 层
|
||||
UPDATE "public"."iam_perm_menu" SET i18n_key='menu.payment.merchant.credential' WHERE id=4040102;
|
||||
-- 商户信息(详情) → 商户资料
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='商户资料', title_en='Merchant Profile',
|
||||
i18n_key='menu.payment.merchant.profile' WHERE id=4040103;
|
||||
-- 通道路由: i18n_key 从 app.* 移出(实为商户级配置)
|
||||
UPDATE "public"."iam_perm_menu" SET i18n_key='menu.payment.merchant.payRoute' WHERE id=4040111;
|
||||
-- 通道商户详情 → 通道商户管理(静态通用名)
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='通道商户管理', title_en='Channel Merchant Management'
|
||||
WHERE id=4040112;
|
||||
-- 微信通道商户应用 → 微信应用
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='微信应用', title_en='WeChat App' WHERE id=4040113;
|
||||
-- 支付宝通道商户应用 → 支付宝应用
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='支付宝应用', title_en='Alipay App' WHERE id=4040114;
|
||||
-- 抖音通道商户应用 → 抖音应用
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='抖音应用', title_en='Douyin App' WHERE id=4040115;
|
||||
-- 微信服务商通道商户应用 → 微信服务商子商户应用
|
||||
UPDATE "public"."iam_perm_menu" SET title_cn='微信服务商子商户应用', title_en='WeChat ISV Sub-merchant App',
|
||||
i18n_key='menu.payment.merchant.channelMerchant.wechatIsvApp' WHERE id=4040119;
|
||||
|
||||
@@ -29,3 +29,101 @@ COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."deleted" IS '逻辑
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "uk_user_das_pref_user_client"
|
||||
ON "public"."iam_user_dashboard_preference" ("user_id", "client_code")
|
||||
WHERE "deleted" = false;
|
||||
|
||||
-- ============================================================
|
||||
-- 微信服务商子商户应用 / 能力绑定 / 授权配置
|
||||
-- ============================================================
|
||||
|
||||
-- 微信服务商通道商户应用(子商户自己的微信应用, channelMchNo 维度)
|
||||
CREATE TABLE IF NOT EXISTS "public"."wechat_isv_mch_app" (
|
||||
"id" bigint NOT NULL PRIMARY KEY,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"app_name" varchar(128) NOT NULL,
|
||||
"app_type" varchar(32) NOT NULL,
|
||||
"wx_app_id" varchar(64) NOT NULL,
|
||||
"creator" bigint,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" bigint,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" integer NOT NULL DEFAULT 0,
|
||||
"deleted" boolean NOT NULL DEFAULT false
|
||||
);
|
||||
COMMENT ON TABLE "public"."wechat_isv_mch_app" IS '微信服务商通道商户应用(子商户自己的微信应用)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."channel_mch_no" IS '通道商户号(服务商特约商户)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."app_name" IS '应用名称';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."app_type" IS '应用类型(official_account/mini_program/mobile_app)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."wx_app_id" IS '微信应用AppId(对应微信支付sub_appid)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."creator" IS '创建人ID';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."create_time" IS '创建时间(UTC)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."last_modifier" IS '最后修改人ID';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."last_modified_time" IS '最后修改时间(UTC)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app"."deleted" IS '逻辑删除标志';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "uk_wechat_isv_mch_app_channel_wxappid"
|
||||
ON "public"."wechat_isv_mch_app" ("channel_mch_no", "wx_app_id")
|
||||
WHERE "deleted" = false;
|
||||
|
||||
-- 微信服务商通道商户应用支付能力关联(channelMchNo 维度, 仅存子商户显式选自己应用的记录)
|
||||
CREATE TABLE IF NOT EXISTS "public"."wechat_isv_mch_app_capability" (
|
||||
"id" bigint NOT NULL PRIMARY KEY,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"capability" varchar(64) NOT NULL,
|
||||
"wechat_isv_mch_app_id" bigint NOT NULL,
|
||||
"creator" bigint,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" bigint,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" integer NOT NULL DEFAULT 0,
|
||||
"deleted" boolean NOT NULL DEFAULT false
|
||||
);
|
||||
COMMENT ON TABLE "public"."wechat_isv_mch_app_capability" IS '微信服务商通道商户应用支付能力关联';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."channel_mch_no" IS '通道商户号(服务商特约商户)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."capability" IS '支付能力编码';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."wechat_isv_mch_app_id" IS '关联服务商通道商户应用ID(指向wechat_isv_mch_app.id)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."creator" IS '创建人ID';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."create_time" IS '创建时间(UTC)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."last_modifier" IS '最后修改人ID';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."last_modified_time" IS '最后修改时间(UTC)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_capability"."deleted" IS '逻辑删除标志';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "uk_wechat_isv_mch_app_cap_channel_cap"
|
||||
ON "public"."wechat_isv_mch_app_capability" ("channel_mch_no", "capability")
|
||||
WHERE "deleted" = false;
|
||||
|
||||
-- 微信服务商通道商户应用授权配置(appSecret等, 一对一)
|
||||
CREATE TABLE IF NOT EXISTS "public"."wechat_isv_mch_app_auth_config" (
|
||||
"id" bigint NOT NULL PRIMARY KEY,
|
||||
"mch_no" varchar(32) NOT NULL,
|
||||
"channel_mch_no" varchar(64) NOT NULL,
|
||||
"wechat_isv_mch_app_id" bigint NOT NULL,
|
||||
"app_secret" varchar(512),
|
||||
"auth_callback_url" varchar(512),
|
||||
"creator" bigint,
|
||||
"create_time" timestamptz(6),
|
||||
"last_modifier" bigint,
|
||||
"last_modified_time" timestamptz(6),
|
||||
"version" integer NOT NULL DEFAULT 0,
|
||||
"deleted" boolean NOT NULL DEFAULT false
|
||||
);
|
||||
COMMENT ON TABLE "public"."wechat_isv_mch_app_auth_config" IS '微信服务商通道商户应用授权配置';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."id" IS '主键';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."mch_no" IS '商户号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."channel_mch_no" IS '通道商户号(服务商特约商户)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."wechat_isv_mch_app_id" IS '关联服务商通道商户应用ID';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."app_secret" IS '应用密钥(加密存储)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."auth_callback_url" IS '授权回调地址(仅公众号)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."creator" IS '创建人ID';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."create_time" IS '创建时间(UTC)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."last_modifier" IS '最后修改人ID';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."last_modified_time" IS '最后修改时间(UTC)';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."version" IS '乐观锁版本号';
|
||||
COMMENT ON COLUMN "public"."wechat_isv_mch_app_auth_config"."deleted" IS '逻辑删除标志';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "uk_wechat_isv_mch_app_auth_channel_app"
|
||||
ON "public"."wechat_isv_mch_app_auth_config" ("channel_mch_no", "wechat_isv_mch_app_id")
|
||||
WHERE "deleted" = false;
|
||||
|
||||
@@ -15,6 +15,7 @@ import cn.daxpay.open.platform.core.code.DaxPayErrorCode;
|
||||
import cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum;
|
||||
import cn.daxpay.open.platform.core.enums.unipay.PayBodyTypeEnum;
|
||||
import cn.daxpay.open.platform.core.exception.BizInfoException;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformUrlConfigService;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.stereotype.Service;
|
||||
public class AlipayPayService {
|
||||
|
||||
private final AlipayChannelClient alipayChannelClient;
|
||||
private final PlatformUrlConfigService platformUrlConfigService;
|
||||
|
||||
/// 执行支付宝支付
|
||||
///
|
||||
@@ -50,6 +52,8 @@ public class AlipayPayService {
|
||||
// 付款码(BARCODE) / 买家标识(JSAPI) 通道专属参数透传
|
||||
req.setAuthCode(payParam.getAuthCode());
|
||||
req.setOpenId(payParam.getOpenId());
|
||||
// 通道通知地址: 始终使用平台生成的回调地址(支付宝→平台), 不使用 payParam.notifyUrl(语义为平台→商户)
|
||||
req.setNotifyUrl(this.buildNotifyUrl(order));
|
||||
req.setCredential(credential);
|
||||
|
||||
// 调用子应用
|
||||
@@ -61,6 +65,21 @@ public class AlipayPayService {
|
||||
return toPayResult(result.getData());
|
||||
}
|
||||
|
||||
/// 生成支付宝支付异步通知地址(支付宝→平台)
|
||||
///
|
||||
/// 沿用商业版旧版路径约定: `{backendBaseUrl}/unipay/callback/{mchNo}/{appId}/alipay`
|
||||
/// backendBaseUrl 来自平台端点配置 [PlatformUrlConfig], 与社交登录回调地址同源
|
||||
/// 注: 支付宝支付/退款共用同一回调端点(旧版设计), 无 /pay 后缀, 与微信路径不同
|
||||
private String buildNotifyUrl(PayTrade order) {
|
||||
String base = platformUrlConfigService.getUrlConfig().getBackendBaseUrl();
|
||||
if (StrUtil.isBlank(base)) {
|
||||
// backendBaseUrl 未配置时抛清晰异常, 避免 null 透传到支付宝导致无异步回调
|
||||
throw new IllegalStateException("平台后端访问地址(backendBaseUrl)未配置, 无法生成支付宝回调地址");
|
||||
}
|
||||
return StrUtil.format("{}/unipay/callback/{}/{}/alipay",
|
||||
base, order.getMchNo(), order.getAppId());
|
||||
}
|
||||
|
||||
/// 平台支付方式([PayMethodEnum] code) -> 支付宝通道支付方式
|
||||
private static AlipayPayMethod mapMethod(String methodCode) {
|
||||
PayMethodEnum m = PayMethodEnum.findByCode(methodCode);
|
||||
|
||||
@@ -97,7 +97,7 @@ public class WechatIsvAppController {
|
||||
@Operation(summary = "查询应用授权认证配置")
|
||||
@GetMapping("/find-auth-config-by-app-id")
|
||||
public Result<WechatIsvAppAuthConfigResult> findAuthConfigByAppId(
|
||||
@NotNull(message = "{validation.field.id.notNull}") Long wechatIsvAppId) {
|
||||
@NotNull(message = "{validation.field.wechatIsvAppId.notNull}") Long wechatIsvAppId) {
|
||||
var config = wechatIsvAppAuthConfigService.findByWechatIsvAppId(wechatIsvAppId);
|
||||
return Res.ok(WechatIsvAppAuthConfigConvert.CONVERT.toResult(config));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package cn.daxpay.open.channel.wechat.controller.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppCapabilityBatchParam;
|
||||
import cn.daxpay.open.channel.wechat.result.WechatCapabilityOption;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppCapabilityResult;
|
||||
import cn.daxpay.open.channel.wechat.service.isv.WechatIsvMchAppCapabilityService;
|
||||
import cn.daxpay.open.platform.core.annotation.PermCode;
|
||||
import cn.daxpay.open.platform.core.rest.Res;
|
||||
import cn.daxpay.open.platform.core.rest.result.Result;
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 微信服务商通道商户应用支付能力关联管理
|
||||
///
|
||||
/// 提供通道商户(特约商户)维度下「支付能力 → 子商户应用」绑定关系的查询、批量保存及能力候选查询。
|
||||
///
|
||||
@PermCode(menuCode = "channel:wechat:app")
|
||||
@Validated
|
||||
@Tag(name = "微信服务商通道商户应用支付能力关联管理")
|
||||
@RestController
|
||||
@RequestMapping("/admin/wechat/isv-mch-app/capability")
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvMchAppCapabilityController {
|
||||
|
||||
private final WechatIsvMchAppCapabilityService wechatIsvMchAppCapabilityService;
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "查询通道商户的能力应用关联列表")
|
||||
@GetMapping("/list-by-channel-mch-no")
|
||||
public Result<List<WechatIsvMchAppCapabilityResult>> listByChannelMchNo(
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}") String mchNo,
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) {
|
||||
return Res.ok(wechatIsvMchAppCapabilityService.listByChannelMchNo(mchNo, channelMchNo));
|
||||
}
|
||||
|
||||
@PermCode(code = "manage", nameCn = "通道商户管理", nameEn = "Channel Merchant Manage")
|
||||
@Operation(summary = "全量保存能力应用关联")
|
||||
@PostMapping("/save-batch")
|
||||
public Result<Void> saveBatch(@RequestBody @Validated WechatIsvMchAppCapabilityBatchParam param) {
|
||||
wechatIsvMchAppCapabilityService.saveBatch(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "查询微信服务商支持的支付能力候选")
|
||||
@GetMapping("/list-supported-capabilities")
|
||||
public Result<List<WechatCapabilityOption>> listSupportedCapabilities() {
|
||||
return Res.ok(wechatIsvMchAppCapabilityService.listSupportedCapabilities());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package cn.daxpay.open.channel.wechat.controller.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppParam;
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppAuthConfigParam;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppAuthConfigResult;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppResult;
|
||||
import cn.daxpay.open.channel.wechat.service.isv.WechatIsvMchAppAuthConfigService;
|
||||
import cn.daxpay.open.channel.wechat.service.isv.WechatIsvMchAppService;
|
||||
import cn.daxpay.open.platform.core.annotation.PermCode;
|
||||
import cn.daxpay.open.platform.core.rest.Res;
|
||||
import cn.daxpay.open.platform.core.rest.result.Result;
|
||||
import cn.daxpay.open.platform.core.util.ValidationUtil;
|
||||
import cn.daxpay.open.platform.core.validation.ValidationGroup;
|
||||
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 = "channel:wechat:app")
|
||||
@Validated
|
||||
@Tag(name = "微信服务商通道商户应用管理")
|
||||
@RestController
|
||||
@RequestMapping("/admin/wechat/isv-mch-app")
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvMchAppController {
|
||||
|
||||
private final WechatIsvMchAppService wechatIsvMchAppService;
|
||||
private final WechatIsvMchAppAuthConfigService wechatIsvMchAppAuthConfigService;
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "根据商户号和通道商户号查询应用列表")
|
||||
@GetMapping("/list-by-channel-mch-no")
|
||||
public Result<List<WechatIsvMchAppResult>> listByChannelMchNo(
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}") String mchNo,
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) {
|
||||
return Res.ok(wechatIsvMchAppService.listByMchNoAndChannelMchNo(mchNo, channelMchNo));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "查询应用详情")
|
||||
@GetMapping("/find-by-id")
|
||||
public Result<WechatIsvMchAppResult> findById(
|
||||
@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
return Res.ok(wechatIsvMchAppService.findById(id));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "同一通道商户下微信应用ID是否已存在")
|
||||
@GetMapping("/exists-wx-app-id-by-channel")
|
||||
public Result<Boolean> existsWxAppIdByChannel(
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}") String mchNo,
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo,
|
||||
@NotBlank(message = "{validation.field.wxAppId.notBlank}") String wxAppId) {
|
||||
return Res.ok(wechatIsvMchAppService.existsWxAppIdByChannel(mchNo, channelMchNo, wxAppId, null));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "同一通道商户下微信应用ID是否已存在(排除自身)")
|
||||
@GetMapping("/exists-wx-app-id-by-channel-not-id")
|
||||
public Result<Boolean> existsWxAppIdByChannelNotId(
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}") String mchNo,
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo,
|
||||
@NotBlank(message = "{validation.field.wxAppId.notBlank}") String wxAppId,
|
||||
@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
return Res.ok(wechatIsvMchAppService.existsWxAppIdByChannel(mchNo, channelMchNo, wxAppId, id));
|
||||
}
|
||||
|
||||
@PermCode(code = "manage", nameCn = "通道商户管理", nameEn = "Channel Merchant Manage")
|
||||
@Operation(summary = "新增服务商通道商户应用")
|
||||
@PostMapping("/add")
|
||||
public Result<Void> add(@RequestBody @Validated(ValidationGroup.add.class) WechatIsvMchAppParam param) {
|
||||
ValidationUtil.validateParam(param, ValidationGroup.add.class);
|
||||
wechatIsvMchAppService.add(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "manage", nameCn = "通道商户管理", nameEn = "Channel Merchant Manage")
|
||||
@Operation(summary = "修改服务商通道商户应用")
|
||||
@PostMapping("/update")
|
||||
public Result<Void> update(@RequestBody @Validated(ValidationGroup.edit.class) WechatIsvMchAppParam param) {
|
||||
ValidationUtil.validateParam(param, ValidationGroup.edit.class);
|
||||
wechatIsvMchAppService.update(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "manage", nameCn = "通道商户管理", nameEn = "Channel Merchant Manage")
|
||||
@Operation(summary = "删除服务商通道商户应用")
|
||||
@PostMapping("/delete")
|
||||
public Result<Void> delete(@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
wechatIsvMchAppService.delete(id);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "通道商户查看", nameEn = "Channel Merchant View")
|
||||
@Operation(summary = "查询应用授权认证配置")
|
||||
@GetMapping("/find-auth-config-by-app-id")
|
||||
public Result<WechatIsvMchAppAuthConfigResult> findAuthConfigByAppId(
|
||||
@NotNull(message = "{validation.field.wechatIsvMchAppId.notNull}") Long wechatIsvMchAppId) {
|
||||
var config = wechatIsvMchAppAuthConfigService.findByWechatIsvMchAppId(wechatIsvMchAppId);
|
||||
return Res.ok(config.toResult());
|
||||
}
|
||||
|
||||
@PermCode(code = "manage", nameCn = "通道商户管理", nameEn = "Channel Merchant Manage")
|
||||
@Operation(summary = "保存应用授权认证配置")
|
||||
@PostMapping("/save-auth-config")
|
||||
public Result<Void> saveAuthConfig(@RequestBody @Validated WechatIsvMchAppAuthConfigParam param) {
|
||||
wechatIsvMchAppAuthConfigService.save(param);
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.daxpay.open.channel.wechat.convert.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppAuthConfig;
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppAuthConfigParam;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppAuthConfigResult;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 微信服务商通道商户应用授权认证配置转换
|
||||
///
|
||||
/// MapStruct转换器,负责微信服务商通道商户应用授权认证配置在实体、参数和返回结果之间的转换,更新时空值不覆盖。
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvMchAppAuthConfigConvert {
|
||||
|
||||
WechatIsvMchAppAuthConfigConvert CONVERT = Mappers.getMapper(WechatIsvMchAppAuthConfigConvert.class);
|
||||
|
||||
/// 转换为返回对象
|
||||
WechatIsvMchAppAuthConfigResult toResult(WechatIsvMchAppAuthConfig entity);
|
||||
|
||||
/// 转换为实体
|
||||
WechatIsvMchAppAuthConfig toEntity(WechatIsvMchAppAuthConfigParam param);
|
||||
|
||||
/// 更新源数据到实体(空值不覆盖)
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
void copy(WechatIsvMchAppAuthConfigParam param, @MappingTarget WechatIsvMchAppAuthConfig entity);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cn.daxpay.open.channel.wechat.convert.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppCapability;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppCapabilityResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 微信服务商通道商户应用支付能力关联转换
|
||||
///
|
||||
/// MapStruct转换器,负责关联记录在实体与返回结果之间的转换。
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvMchAppCapabilityConvert {
|
||||
|
||||
WechatIsvMchAppCapabilityConvert CONVERT = Mappers.getMapper(WechatIsvMchAppCapabilityConvert.class);
|
||||
|
||||
/// 转换为返回对象
|
||||
WechatIsvMchAppCapabilityResult toResult(WechatIsvMchAppCapability entity);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.daxpay.open.channel.wechat.convert.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchApp;
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppParam;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 微信服务商通道商户应用转换
|
||||
///
|
||||
/// MapStruct转换器,负责微信服务商通道商户应用在实体、参数和返回结果之间的相互转换。
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvMchAppConvert {
|
||||
|
||||
WechatIsvMchAppConvert CONVERT = Mappers.getMapper(WechatIsvMchAppConvert.class);
|
||||
|
||||
/// 转换为返回对象
|
||||
WechatIsvMchAppResult toResult(WechatIsvMchApp entity);
|
||||
|
||||
/// 转换为实体
|
||||
WechatIsvMchApp toEntity(WechatIsvMchAppParam param);
|
||||
|
||||
/// 更新源数据到实体(忽略空值)
|
||||
void copy(WechatIsvMchAppParam param, @MappingTarget WechatIsvMchApp entity);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.daxpay.open.channel.wechat.dao.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppAuthConfig;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商通道商户应用授权认证配置 Manager
|
||||
///
|
||||
/// 服务商通道商户应用授权认证配置数据访问管理器,提供按应用ID查询和删除授权认证配置的方法。
|
||||
///
|
||||
@Repository
|
||||
public class WechatIsvMchAppAuthConfigManager extends BaseManager<WechatIsvMchAppAuthConfigMapper, WechatIsvMchAppAuthConfig> {
|
||||
|
||||
/// 根据应用ID查询授权认证配置
|
||||
public Optional<WechatIsvMchAppAuthConfig> findByWechatIsvMchAppId(Long wechatIsvMchAppId) {
|
||||
return lambdaQuery()
|
||||
.eq(WechatIsvMchAppAuthConfig::getWechatIsvMchAppId, wechatIsvMchAppId)
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
/// 根据应用ID删除授权认证配置
|
||||
public void deleteByWechatIsvMchAppId(Long wechatIsvMchAppId) {
|
||||
lambdaUpdate()
|
||||
.eq(WechatIsvMchAppAuthConfig::getWechatIsvMchAppId, wechatIsvMchAppId)
|
||||
.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.daxpay.open.channel.wechat.dao.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppAuthConfig;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 微信服务商通道商户应用授权认证配置
|
||||
///
|
||||
/// 微信服务商通道商户应用授权认证配置 MyBatis-Plus Mapper,继承 MPJBaseMapper 支持多表联查。
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvMchAppAuthConfigMapper extends MPJBaseMapper<WechatIsvMchAppAuthConfig> {
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package cn.daxpay.open.channel.wechat.dao.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppCapability;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商通道商户应用支付能力关联 Manager
|
||||
///
|
||||
/// 关联记录数据访问管理器,提供按通道商户号查询/删除、按能力查应用、按应用清理等方法。
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvMchAppCapabilityManager extends BaseManager<WechatIsvMchAppCapabilityMapper, WechatIsvMchAppCapability> {
|
||||
|
||||
/// 根据通道商户号查询全部关联(按创建时间升序)
|
||||
public List<WechatIsvMchAppCapability> listByChannelMchNo(String channelMchNo) {
|
||||
return lambdaQuery()
|
||||
.eq(WechatIsvMchAppCapability::getChannelMchNo, channelMchNo)
|
||||
.orderByAsc(WechatIsvMchAppCapability::getCreateTime)
|
||||
.orderByAsc(WechatIsvMchAppCapability::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 根据通道商户号与支付能力查询单条关联(支付时调用)
|
||||
public Optional<WechatIsvMchAppCapability> findOne(String channelMchNo, String capability) {
|
||||
return lambdaQuery()
|
||||
.eq(WechatIsvMchAppCapability::getChannelMchNo, channelMchNo)
|
||||
.eq(WechatIsvMchAppCapability::getCapability, capability)
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
/// 根据通道商户号删除全部关联(批量保存时先清后插)
|
||||
public void deleteByChannelMchNo(String channelMchNo) {
|
||||
lambdaUpdate()
|
||||
.eq(WechatIsvMchAppCapability::getChannelMchNo, channelMchNo)
|
||||
.remove();
|
||||
}
|
||||
|
||||
/// 根据应用ID删除关联(应用被删除时级联清理,避免悬空引用)
|
||||
public void deleteByWechatIsvMchAppId(Long wechatIsvMchAppId) {
|
||||
lambdaUpdate()
|
||||
.eq(WechatIsvMchAppCapability::getWechatIsvMchAppId, wechatIsvMchAppId)
|
||||
.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.daxpay.open.channel.wechat.dao.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppCapability;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 微信服务商通道商户应用支付能力关联
|
||||
///
|
||||
/// 微信服务商通道商户应用支付能力关联 MyBatis-Plus Mapper,继承 MPJBaseMapper 支持多表联查。
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvMchAppCapabilityMapper extends MPJBaseMapper<WechatIsvMchAppCapability> {
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package cn.daxpay.open.channel.wechat.dao.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchApp;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商通道商户应用 Manager
|
||||
///
|
||||
/// 服务商通道商户应用(子商户应用)数据访问管理器,提供按商户号和通道商户号查询列表、
|
||||
/// 首个应用查询、应用类型推导查询及同一通道下应用AppId唯一性校验等方法。
|
||||
///
|
||||
@Repository
|
||||
public class WechatIsvMchAppManager extends BaseManager<WechatIsvMchAppMapper, WechatIsvMchApp> {
|
||||
|
||||
/// 根据商户号和通道商户号查询应用列表(按创建时间升序)
|
||||
public List<WechatIsvMchApp> listByMchNoAndChannelMchNo(String mchNo, String channelMchNo) {
|
||||
return lambdaQuery()
|
||||
.eq(WechatIsvMchApp::getMchNo, mchNo)
|
||||
.eq(WechatIsvMchApp::getChannelMchNo, channelMchNo)
|
||||
.orderByAsc(WechatIsvMchApp::getCreateTime)
|
||||
.orderByAsc(WechatIsvMchApp::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 按通道商户号查询首个应用(能力解析兜底时使用)
|
||||
public Optional<WechatIsvMchApp> findFirstByChannelMchNo(String channelMchNo) {
|
||||
return firstOpt(q -> q
|
||||
.eq(WechatIsvMchApp::getChannelMchNo, channelMchNo)
|
||||
.orderByAsc(WechatIsvMchApp::getCreateTime)
|
||||
.orderByAsc(WechatIsvMchApp::getId));
|
||||
}
|
||||
|
||||
/// 按通道商户号与应用类型查询首个应用(能力→应用类型推导时使用)
|
||||
public Optional<WechatIsvMchApp> findFirstByChannelMchNoAndAppType(String channelMchNo, String appType) {
|
||||
return firstOpt(q -> q
|
||||
.eq(WechatIsvMchApp::getChannelMchNo, channelMchNo)
|
||||
.eq(WechatIsvMchApp::getAppType, appType)
|
||||
.orderByAsc(WechatIsvMchApp::getCreateTime)
|
||||
.orderByAsc(WechatIsvMchApp::getId));
|
||||
}
|
||||
|
||||
/// 校验同一通道商户下wxAppId是否已存在(排除自身)
|
||||
public boolean existsByChannelMchNoAndWxAppId(String mchNo, String channelMchNo, String wxAppId, Long excludeId) {
|
||||
return lambdaQuery()
|
||||
.eq(WechatIsvMchApp::getMchNo, mchNo)
|
||||
.eq(WechatIsvMchApp::getChannelMchNo, channelMchNo)
|
||||
.eq(WechatIsvMchApp::getWxAppId, wxAppId)
|
||||
.ne(excludeId != null, WechatIsvMchApp::getId, excludeId)
|
||||
.exists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.daxpay.open.channel.wechat.dao.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchApp;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 微信服务商通道商户应用
|
||||
///
|
||||
/// 微信服务商通道商户应用 MyBatis-Plus Mapper,继承 MPJBaseMapper 支持多表联查。
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvMchAppMapper extends MPJBaseMapper<WechatIsvMchApp> {
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package cn.daxpay.open.channel.wechat.entity.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.convert.isv.WechatIsvAppAuthConfigConvert;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvAppAuthConfigResult;
|
||||
import cn.daxpay.open.payment.common.entity.merchant.MchBaseEntity;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.function.ToResult;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.handler.encrypt.DataEncryptTypeHandler;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
@@ -14,17 +14,14 @@ import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商应用授权认证配置
|
||||
///
|
||||
/// 继承商户基础实体,自动填充商户号用于多商户隔离。
|
||||
/// 服务商应用(WechatIsvApp)本身为平台级全局配置, 不挂商户维度,
|
||||
/// 其授权认证配置(appSecret/回调地址)同样为全局配置。
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "wechat_isv_app_auth_config", autoResultMap = true)
|
||||
public class WechatIsvAppAuthConfig extends MchBaseEntity implements ToResult<WechatIsvAppAuthConfigResult> {
|
||||
|
||||
/// 通道商户号
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String channelMchNo;
|
||||
public class WechatIsvAppAuthConfig extends MpBaseEntity implements ToResult<WechatIsvAppAuthConfigResult> {
|
||||
|
||||
/// 微信服务商应用ID
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.daxpay.open.channel.wechat.entity.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.convert.isv.WechatIsvMchAppConvert;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppResult;
|
||||
import cn.daxpay.open.payment.common.entity.merchant.MchBaseEntity;
|
||||
import cn.daxpay.open.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;
|
||||
|
||||
/// # 微信服务商通道商户应用
|
||||
///
|
||||
/// 表示微信服务商模式下特约商户(子商户)注册的微信应用实体,挂在通道商户号(channelMchNo)维度下。
|
||||
/// 每个应用对应一个微信AppId,支付时作为 sub_appid 使用。同一通道商户下微信应用AppId唯一。
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "wechat_isv_mch_app", autoResultMap = true)
|
||||
public class WechatIsvMchApp extends MchBaseEntity implements ToResult<WechatIsvMchAppResult> {
|
||||
|
||||
/// 通道商户号(服务商特约商户)
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String channelMchNo;
|
||||
|
||||
/// 应用名称
|
||||
private String appName;
|
||||
|
||||
/// 应用类型
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String appType;
|
||||
|
||||
/// 微信应用AppId(对应微信支付sub_appid)
|
||||
private String wxAppId;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public WechatIsvMchAppResult toResult() {
|
||||
return WechatIsvMchAppConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.daxpay.open.channel.wechat.entity.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.convert.isv.WechatIsvMchAppAuthConfigConvert;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppAuthConfigResult;
|
||||
import cn.daxpay.open.payment.common.entity.merchant.MchBaseEntity;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.function.ToResult;
|
||||
import cn.daxpay.open.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;
|
||||
|
||||
/// # 微信服务商通道商户应用授权认证配置
|
||||
///
|
||||
/// 配置服务商通道商户应用(子商户应用)的应用密钥和授权回调地址,用于微信OAuth授权流程中的身份验证与回调跳转。
|
||||
/// 一个应用对应一份授权配置(由唯一约束 channel_mch_no + wechat_isv_mch_app_id 保证)。
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "wechat_isv_mch_app_auth_config", autoResultMap = true)
|
||||
public class WechatIsvMchAppAuthConfig extends MchBaseEntity implements ToResult<WechatIsvMchAppAuthConfigResult> {
|
||||
|
||||
/// 通道商户号(服务商特约商户)
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String channelMchNo;
|
||||
|
||||
/// 关联服务商通道商户应用ID
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private Long wechatIsvMchAppId;
|
||||
|
||||
/// 应用密钥(加密存储)
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String appSecret;
|
||||
|
||||
/// 授权回调地址
|
||||
private String authCallbackUrl;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public WechatIsvMchAppAuthConfigResult toResult() {
|
||||
return WechatIsvMchAppAuthConfigConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.daxpay.open.channel.wechat.entity.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.convert.isv.WechatIsvMchAppCapabilityConvert;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppCapabilityResult;
|
||||
import cn.daxpay.open.payment.common.entity.merchant.MchBaseEntity;
|
||||
import cn.daxpay.open.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;
|
||||
|
||||
/// # 微信服务商通道商户应用支付能力关联
|
||||
///
|
||||
/// 建立通道商户维度下「支付能力 → 子商户应用」的绑定关系,供支付时选择正确的微信应用(sub_appid)。
|
||||
/// 本表只存子商户显式选择自己应用的记录,某能力未配置时自动回退到全局服务商应用配置。
|
||||
/// 同一通道商户下,一个支付能力只能绑定一个应用(由唯一约束 channel_mch_no + capability 保证)。
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "wechat_isv_mch_app_capability", autoResultMap = true)
|
||||
public class WechatIsvMchAppCapability extends MchBaseEntity implements ToResult<WechatIsvMchAppCapabilityResult> {
|
||||
|
||||
/// 通道商户号(服务商特约商户)
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String channelMchNo;
|
||||
|
||||
/// 支付能力编码
|
||||
/// @see cn.daxpay.open.platform.core.enums.pay.channel.PayCapabilityEnum
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String capability;
|
||||
|
||||
/// 关联微信服务商通道商户应用ID(指向 wechat_isv_mch_app.id)
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private Long wechatIsvMchAppId;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public WechatIsvMchAppCapabilityResult toResult() {
|
||||
return WechatIsvMchAppCapabilityConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import lombok.experimental.Accessors;
|
||||
@Schema(title = "微信服务商应用授权认证配置保存参数")
|
||||
public class WechatIsvAppAuthConfigParam {
|
||||
|
||||
@NotNull(message = "{validation.field.id.notNull}")
|
||||
@NotNull(message = "{validation.field.wechatIsvAppId.notNull}")
|
||||
@Schema(description = "微信服务商应用ID")
|
||||
private Long wechatIsvAppId;
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.daxpay.open.channel.wechat.param.isv;
|
||||
|
||||
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 WechatIsvMchAppAuthConfigParam {
|
||||
|
||||
@NotNull(message = "{validation.field.wechatIsvMchAppId.notNull}")
|
||||
@Schema(description = "关联应用ID")
|
||||
private Long wechatIsvMchAppId;
|
||||
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}")
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}")
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "应用密钥")
|
||||
private String appSecret;
|
||||
|
||||
@Size(max = 512, message = "{validation.field.authCallbackUrl.size}")
|
||||
@Schema(description = "授权回调地址(仅公众号,非必填)")
|
||||
private String authCallbackUrl;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.daxpay.open.channel.wechat.param.isv;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 微信服务商通道商户支付能力关联应用批量保存参数
|
||||
///
|
||||
/// 全量覆盖某通道商户下「支付能力 → 子商户应用」的绑定关系:先清除旧记录,再按 items 批量插入。
|
||||
/// items 为空表示清空该通道商户下所有绑定(该能力的应用将自动回退到全局服务商配置)。
|
||||
/// 仅存子商户显式选择自己应用的记录,选「服务商默认应用」的能力不在此参数中。
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商通道商户支付能力关联应用批量保存参数")
|
||||
public class WechatIsvMchAppCapabilityBatchParam {
|
||||
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}")
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}")
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "支付能力关联应用列表(仅含选子商户应用的项,选服务商默认的不传)")
|
||||
private List<WechatIsvMchAppCapabilityItem> items;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.daxpay.open.channel.wechat.param.isv;
|
||||
|
||||
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;
|
||||
|
||||
/// # 微信服务商通道商户支付能力关联应用单项
|
||||
///
|
||||
/// 用于批量保存时,声明单个支付能力绑定的服务商通道商户应用(子商户应用)。
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商通道商户支付能力关联应用单项")
|
||||
public class WechatIsvMchAppCapabilityItem {
|
||||
|
||||
@NotBlank(message = "{validation.field.capability.notBlank}")
|
||||
@Schema(description = "支付能力编码")
|
||||
private String capability;
|
||||
|
||||
@NotNull(message = "{validation.field.wechatIsvMchAppId.notNull}")
|
||||
@Schema(description = "关联微信服务商通道商户应用ID")
|
||||
private Long wechatIsvMchAppId;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package cn.daxpay.open.channel.wechat.param.isv;
|
||||
|
||||
import cn.daxpay.open.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 WechatIsvMchAppParam {
|
||||
|
||||
@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.appType.notBlank}")
|
||||
@Schema(description = "应用类型")
|
||||
private String appType;
|
||||
|
||||
@NotBlank(message = "{validation.field.wxAppId.notBlank}")
|
||||
@Schema(description = "微信应用AppId")
|
||||
private String wxAppId;
|
||||
}
|
||||
@@ -15,12 +15,6 @@ import lombok.experimental.Accessors;
|
||||
@Schema(title = "微信服务商应用授权认证配置")
|
||||
public class WechatIsvAppAuthConfigResult extends BaseResult {
|
||||
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "微信服务商应用ID")
|
||||
private Long wechatIsvAppId;
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.daxpay.open.channel.wechat.result.isv;
|
||||
|
||||
import cn.daxpay.open.platform.common.json.sensitive.SensitiveInfo;
|
||||
import cn.daxpay.open.platform.core.result.BaseResult;
|
||||
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 WechatIsvMchAppAuthConfigResult extends BaseResult {
|
||||
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "关联应用ID")
|
||||
private Long wechatIsvMchAppId;
|
||||
|
||||
// 应用密钥(序列化时脱敏)
|
||||
@SensitiveInfo
|
||||
@Schema(description = "应用密钥(已脱敏)")
|
||||
private String appSecret;
|
||||
|
||||
@Schema(description = "授权回调地址")
|
||||
private String authCallbackUrl;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.daxpay.open.channel.wechat.result.isv;
|
||||
|
||||
import cn.daxpay.open.payment.common.result.MchBaseResult;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商通道商户应用支付能力关联
|
||||
///
|
||||
/// 展示通道商户(特约商户)下各支付能力绑定的子商户应用信息,含应用名称/AppId/类型等冗余字段(由 Service 填充)。
|
||||
/// 注:「服务商默认应用」回退信息由前端另行调用全局 wechat_isv_app_capability 接口获取,不在本结果中冗余。
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商通道商户应用支付能力关联")
|
||||
public class WechatIsvMchAppCapabilityResult extends MchBaseResult {
|
||||
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "支付能力编码")
|
||||
private String capability;
|
||||
|
||||
@Schema(description = "关联微信服务商通道商户应用ID")
|
||||
private Long wechatIsvMchAppId;
|
||||
|
||||
@Schema(description = "应用名称(冗余展示)")
|
||||
private String appName;
|
||||
|
||||
@Schema(description = "微信应用AppId(冗余展示)")
|
||||
private String wxAppId;
|
||||
|
||||
@Schema(description = "应用类型(冗余展示): official_account/mini_program/mobile_app")
|
||||
private String appType;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.daxpay.open.channel.wechat.result.isv;
|
||||
|
||||
import cn.daxpay.open.payment.common.result.MchTradeBaseResult;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商通道商户应用
|
||||
///
|
||||
/// 微信服务商通道商户应用(子商户应用)的返回结果对象,含通道商户号、应用名称、应用类型和微信应用AppId等信息。
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商通道商户应用")
|
||||
public class WechatIsvMchAppResult extends MchTradeBaseResult {
|
||||
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Schema(description = "应用名称")
|
||||
private String appName;
|
||||
|
||||
@Schema(description = "应用类型")
|
||||
private String appType;
|
||||
|
||||
@Schema(description = "微信应用AppId")
|
||||
private String wxAppId;
|
||||
}
|
||||
@@ -2,7 +2,9 @@ package cn.daxpay.open.channel.wechat.service.direct;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.client.credential.WechatSdkCredential;
|
||||
import cn.daxpay.open.channel.wechat.dao.direct.WechatDirectAppManager;
|
||||
import cn.daxpay.open.channel.wechat.dao.direct.WechatDirectChannelMerchantManager;
|
||||
import cn.daxpay.open.channel.wechat.entity.direct.WechatDirectApp;
|
||||
import cn.daxpay.open.channel.wechat.entity.direct.WechatDirectChannelMerchant;
|
||||
import cn.daxpay.open.channel.wechat.entity.direct.WechatDirectKeyConfig;
|
||||
import cn.daxpay.open.payment.masterdata.constants.product.dao.PayProductConfigManager;
|
||||
import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
|
||||
@@ -28,6 +30,7 @@ import org.springframework.stereotype.Service;
|
||||
public class WechatDirectConfigAssembler {
|
||||
|
||||
private final WechatDirectAppManager wechatDirectAppManager;
|
||||
private final WechatDirectChannelMerchantManager wechatDirectChannelMerchantManager;
|
||||
private final WechatDirectKeyConfigService wechatDirectKeyConfigService;
|
||||
private final WechatDirectAppCapabilityService wechatDirectAppCapabilityService;
|
||||
private final PayProductConfigManager payProductConfigManager;
|
||||
@@ -35,16 +38,20 @@ public class WechatDirectConfigAssembler {
|
||||
/// 组装直连商户的通道调用凭证(下发给子应用)
|
||||
///
|
||||
/// @param mchNo 商户号(兜底定位应用)
|
||||
/// @param channelMchNo 通道商户号(微信商户号, 密钥查询与应用定位主键)
|
||||
/// @param channelMchNo 通道商户号(系统雪花号, 密钥查询与应用定位主键, 不等于微信商户号)
|
||||
/// @param capability 支付能力编码(用于选择匹配的应用)
|
||||
/// @return 微信 SDK 凭证, 字段对齐子应用 WechatSdkCredential
|
||||
public WechatSdkCredential buildConfig(String mchNo, String channelMchNo, String capability) {
|
||||
WechatDirectApp app = resolveApp(mchNo, channelMchNo, capability);
|
||||
WechatDirectKeyConfig keyConfig = wechatDirectKeyConfigService.findByChannelMchNo(channelMchNo);
|
||||
|
||||
// 从通道商户绑定表取真实微信商户号(channelMchNo 是系统雪花号, 不等于 wxMchId)
|
||||
WechatDirectChannelMerchant channelMerchant = wechatDirectChannelMerchantManager.findByChannelMchNo(channelMchNo)
|
||||
// 微信: 通道商户配置不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
|
||||
|
||||
WechatSdkCredential credential = new WechatSdkCredential();
|
||||
// 微信商户号 = 通道商户号
|
||||
credential.setWxMchId(app.getChannelMchNo());
|
||||
credential.setWxMchId(channelMerchant.getWxMchId());
|
||||
credential.setWxAppId(app.getWxAppId());
|
||||
// 密钥与证书
|
||||
credential.setApiKeyV3(keyConfig.getApiKeyV3());
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package cn.daxpay.open.channel.wechat.service.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.convert.isv.WechatIsvMchAppAuthConfigConvert;
|
||||
import cn.daxpay.open.channel.wechat.dao.isv.WechatIsvMchAppAuthConfigManager;
|
||||
import cn.daxpay.open.channel.wechat.dao.isv.WechatIsvMchAppManager;
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchApp;
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppAuthConfig;
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppAuthConfigParam;
|
||||
import cn.daxpay.open.platform.core.code.CommonErrorCode;
|
||||
import cn.daxpay.open.platform.core.exception.BizInfoException;
|
||||
import cn.daxpay.open.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 WechatIsvMchAppAuthConfigService {
|
||||
|
||||
private final WechatIsvMchAppAuthConfigManager wechatIsvMchAppAuthConfigManager;
|
||||
private final WechatIsvMchAppManager wechatIsvMchAppManager;
|
||||
|
||||
/// 根据应用ID查询授权认证配置, 不存在则创建默认记录
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public WechatIsvMchAppAuthConfig findByWechatIsvMchAppId(Long wechatIsvMchAppId) {
|
||||
var existing = wechatIsvMchAppAuthConfigManager.findByWechatIsvMchAppId(wechatIsvMchAppId);
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
var app = wechatIsvMchAppManager.findById(wechatIsvMchAppId)
|
||||
// 微信: 服务商通道商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.mchAppNotFound"));
|
||||
var config = new WechatIsvMchAppAuthConfig()
|
||||
.setChannelMchNo(app.getChannelMchNo())
|
||||
.setWechatIsvMchAppId(wechatIsvMchAppId);
|
||||
config.setMchNo(app.getMchNo());
|
||||
wechatIsvMchAppAuthConfigManager.save(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
/// 保存应用授权认证配置(更新)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(WechatIsvMchAppAuthConfigParam param) {
|
||||
var app = wechatIsvMchAppManager.findById(param.getWechatIsvMchAppId())
|
||||
// 微信: 服务商通道商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.mchAppNotFound"));
|
||||
if (!app.getMchNo().equals(param.getMchNo()) || !app.getChannelMchNo().equals(param.getChannelMchNo())) {
|
||||
// 微信: 服务商通道商户应用不存在或商户号归属不匹配
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.mchAppNotFound");
|
||||
}
|
||||
var config = this.findByWechatIsvMchAppId(param.getWechatIsvMchAppId());
|
||||
WechatIsvMchAppAuthConfigConvert.CONVERT.copy(param, config);
|
||||
wechatIsvMchAppAuthConfigManager.updateById(config);
|
||||
}
|
||||
|
||||
/// 删除应用授权认证配置
|
||||
public void deleteByWechatIsvMchAppId(Long wechatIsvMchAppId) {
|
||||
wechatIsvMchAppAuthConfigManager.deleteByWechatIsvMchAppId(wechatIsvMchAppId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package cn.daxpay.open.channel.wechat.service.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.code.WechatAppTypeCode;
|
||||
import cn.daxpay.open.channel.wechat.convert.isv.WechatIsvMchAppCapabilityConvert;
|
||||
import cn.daxpay.open.channel.wechat.dao.isv.WechatIsvMchAppCapabilityManager;
|
||||
import cn.daxpay.open.channel.wechat.dao.isv.WechatIsvMchAppManager;
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchApp;
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppCapability;
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppCapabilityBatchParam;
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppCapabilityItem;
|
||||
import cn.daxpay.open.channel.wechat.result.WechatCapabilityOption;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppCapabilityResult;
|
||||
import cn.daxpay.open.platform.common.i18n.util.I18nUtil;
|
||||
import cn.daxpay.open.platform.core.code.CommonErrorCode;
|
||||
import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
|
||||
import cn.daxpay.open.platform.core.exception.BizInfoException;
|
||||
import cn.daxpay.open.payment.core.strategy.PaymentStrategyFactory;
|
||||
import cn.daxpay.open.payment.core.strategy.product.AbsProductStrategy;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
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.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 微信服务商通道商户应用支付能力关联
|
||||
///
|
||||
/// 管理通道商户(特约商户)维度下「支付能力 → 子商户应用」的绑定关系。
|
||||
/// 本表只存子商户显式选择自己应用的记录;某能力未配置时,支付解析返回 empty,
|
||||
/// 由调用方(未来的微信服务商支付策略/配置组装器)回退到全局服务商应用配置
|
||||
/// ([cn.daxpay.open.channel.wechat.service.isv.WechatIsvAppCapabilityService])。
|
||||
///
|
||||
/// 支付时通过 [resolveApp] 解析当前能力对应的应用:显式配置 > appType自动推导 > 通道商户首个兜底,
|
||||
/// 三者均未命中返回 empty(语义为"使用全局服务商配置")。
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvMchAppCapabilityService {
|
||||
|
||||
private final WechatIsvMchAppCapabilityManager capabilityManager;
|
||||
private final WechatIsvMchAppManager wechatIsvMchAppManager;
|
||||
|
||||
/// 查询通道商户下的能力关联列表,并填充子商户应用展示信息(应用名称/AppId/类型)
|
||||
public List<WechatIsvMchAppCapabilityResult> listByChannelMchNo(String mchNo, String channelMchNo) {
|
||||
List<WechatIsvMchAppCapability> rels = capabilityManager.listByChannelMchNo(channelMchNo);
|
||||
if (rels.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
// 批量查询该通道商户下全部子商户应用,构建 id → app 映射用于填充展示字段
|
||||
Map<Long, WechatIsvMchApp> appMap = wechatIsvMchAppManager.listByMchNoAndChannelMchNo(mchNo, channelMchNo)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(WechatIsvMchApp::getId, Function.identity()));
|
||||
return rels.stream()
|
||||
.map(rel -> fillResult(rel, appMap.get(rel.getWechatIsvMchAppId())))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 全量保存能力关联(先清后插),校验应用归属与能力唯一
|
||||
///
|
||||
/// items 仅含子商户显式选择自己应用的项;选「服务商默认应用」的能力不在此参数中,
|
||||
/// 先清后插时会自然移除其历史记录,支付时自动回退到全局服务商配置。
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveBatch(WechatIsvMchAppCapabilityBatchParam param) {
|
||||
String mchNo = param.getMchNo();
|
||||
String channelMchNo = param.getChannelMchNo();
|
||||
List<WechatIsvMchAppCapabilityItem> items = param.getItems();
|
||||
// 先清空旧关联
|
||||
capabilityManager.deleteByChannelMchNo(channelMchNo);
|
||||
if (CollUtil.isEmpty(items)) {
|
||||
return;
|
||||
}
|
||||
// 查询该通道商户下全部子商户应用,校验应用归属
|
||||
List<WechatIsvMchApp> apps = wechatIsvMchAppManager.listByMchNoAndChannelMchNo(mchNo, channelMchNo);
|
||||
Map<Long, WechatIsvMchApp> appMap = apps.stream()
|
||||
.collect(Collectors.toMap(WechatIsvMchApp::getId, Function.identity()));
|
||||
HashSet<String> capabilitySet = new HashSet<>();
|
||||
for (WechatIsvMchAppCapabilityItem item : items) {
|
||||
// 能力不可重复
|
||||
if (!capabilitySet.add(item.getCapability())) {
|
||||
// 微信: 支付能力[{0}]重复配置
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
|
||||
"error.channel.wechat.capabilityDuplicate", item.getCapability());
|
||||
}
|
||||
// 应用归属校验
|
||||
if (!appMap.containsKey(item.getWechatIsvMchAppId())) {
|
||||
// 微信: 服务商通道商户应用不存在
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
|
||||
"error.channel.wechat.mchAppNotFound");
|
||||
}
|
||||
var entity = new WechatIsvMchAppCapability()
|
||||
.setChannelMchNo(channelMchNo)
|
||||
.setCapability(item.getCapability())
|
||||
.setWechatIsvMchAppId(item.getWechatIsvMchAppId());
|
||||
entity.setMchNo(mchNo);
|
||||
capabilityManager.save(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/// 应用被删除时级联清理关联,避免悬空引用
|
||||
public void deleteByWechatIsvMchAppId(Long wechatIsvMchAppId) {
|
||||
capabilityManager.deleteByWechatIsvMchAppId(wechatIsvMchAppId);
|
||||
}
|
||||
|
||||
/// 支付时解析当前支付能力对应的子商户应用:显式配置 > appType自动推导 > 通道商户首个兜底
|
||||
///
|
||||
/// 三者均未命中返回 empty,语义为"该能力未在子商户维度配置,请回退到全局服务商应用配置"。
|
||||
/// 调用方(未来的微信服务商配置组装器)应在此基础上回退 [WechatIsvAppCapabilityService.resolveApp]。
|
||||
///
|
||||
/// @param channelMchNo 通道商户号(服务商特约商户)
|
||||
/// @param capability 支付能力编码
|
||||
/// @return 命中的子商户应用;未命中返回 empty(应回退全局服务商配置)
|
||||
public Optional<WechatIsvMchApp> resolveApp(String channelMchNo, String capability) {
|
||||
if (StrUtil.hasBlank(channelMchNo, capability)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// 1. 显式配置优先
|
||||
var rel = capabilityManager.findOne(channelMchNo, capability);
|
||||
if (rel.isPresent()) {
|
||||
return wechatIsvMchAppManager.findById(rel.get().getWechatIsvMchAppId());
|
||||
}
|
||||
// 2. 未配置则按能力 → appType 自动推导
|
||||
String appType = WechatAppTypeCode.resolveAppType(capability);
|
||||
if (appType != null) {
|
||||
Optional<WechatIsvMchApp> byType = wechatIsvMchAppManager.findFirstByChannelMchNoAndAppType(channelMchNo, appType);
|
||||
if (byType.isPresent()) {
|
||||
return byType;
|
||||
}
|
||||
}
|
||||
// 3. 最终兜底:按通道商户号取首个子商户应用;仍未命中返回 empty(回退全局)
|
||||
return wechatIsvMchAppManager.findFirstByChannelMchNo(channelMchNo);
|
||||
}
|
||||
|
||||
/// 查询微信服务商产品支持的支付能力候选列表(含国际化名称)
|
||||
public List<WechatCapabilityOption> listSupportedCapabilities() {
|
||||
AbsProductStrategy strategy = PaymentStrategyFactory.createByProduct(
|
||||
ProductEnum.WECHAT_ISV.getCode(), AbsProductStrategy.class);
|
||||
return strategy.methodCapabilityMapping().values().stream()
|
||||
.flatMap(List::stream)
|
||||
.distinct()
|
||||
.map(cap -> new WechatCapabilityOption(cap.getCode(), I18nUtil.getEnumName(cap)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 填充关联结果的应用展示字段
|
||||
private WechatIsvMchAppCapabilityResult fillResult(WechatIsvMchAppCapability rel, WechatIsvMchApp app) {
|
||||
WechatIsvMchAppCapabilityResult result = WechatIsvMchAppCapabilityConvert.CONVERT.toResult(rel);
|
||||
if (app != null) {
|
||||
result.setAppName(app.getAppName())
|
||||
.setWxAppId(app.getWxAppId())
|
||||
.setAppType(app.getAppType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package cn.daxpay.open.channel.wechat.service.isv;
|
||||
|
||||
import cn.daxpay.open.channel.wechat.code.WechatIsvAppTypeEnum;
|
||||
import cn.daxpay.open.channel.wechat.convert.isv.WechatIsvMchAppConvert;
|
||||
import cn.daxpay.open.channel.wechat.dao.isv.WechatIsvMchAppManager;
|
||||
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchApp;
|
||||
import cn.daxpay.open.channel.wechat.param.isv.WechatIsvMchAppParam;
|
||||
import cn.daxpay.open.channel.wechat.result.isv.WechatIsvMchAppResult;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil;
|
||||
import cn.daxpay.open.platform.core.code.CommonErrorCode;
|
||||
import cn.daxpay.open.platform.core.exception.BizInfoException;
|
||||
import cn.daxpay.open.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.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/// # 微信服务商通道商户应用管理
|
||||
///
|
||||
/// 提供服务商通道商户应用(子商户应用)的增删改查功能,包含同一通道商户下应用AppId的唯一性校验、
|
||||
/// 应用类型校验、操作范围校验(防止跨商户操作),以及删除时级联清理授权配置与能力关联。
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvMchAppService {
|
||||
|
||||
private final WechatIsvMchAppManager wechatIsvMchAppManager;
|
||||
private final WechatIsvMchAppAuthConfigService wechatIsvMchAppAuthConfigService;
|
||||
private final WechatIsvMchAppCapabilityService wechatIsvMchAppCapabilityService;
|
||||
|
||||
/// 根据商户号和通道商户号查询应用列表
|
||||
public List<WechatIsvMchAppResult> listByMchNoAndChannelMchNo(String mchNo, String channelMchNo) {
|
||||
var list = wechatIsvMchAppManager.listByMchNoAndChannelMchNo(mchNo, channelMchNo);
|
||||
return MpUtil.toListResult(list);
|
||||
}
|
||||
|
||||
/// 根据ID查询应用详情
|
||||
public WechatIsvMchAppResult findById(Long id) {
|
||||
return wechatIsvMchAppManager.findById(id)
|
||||
.map(WechatIsvMchApp::toResult)
|
||||
// 微信: 服务商通道商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.mchAppNotFound"));
|
||||
}
|
||||
|
||||
/// 判断同一通道商户下微信应用AppId是否已存在
|
||||
public boolean existsWxAppIdByChannel(String mchNo, String channelMchNo, String wxAppId, Long excludeId) {
|
||||
if (StrUtil.hasBlank(mchNo, channelMchNo, wxAppId)) {
|
||||
return false;
|
||||
}
|
||||
return wechatIsvMchAppManager.existsByChannelMchNoAndWxAppId(mchNo, channelMchNo, wxAppId, excludeId);
|
||||
}
|
||||
|
||||
/// 新增服务商通道商户应用
|
||||
public void add(WechatIsvMchAppParam param) {
|
||||
this.assertWxAppIdUnique(param.getMchNo(), param.getChannelMchNo(), param.getWxAppId(), null);
|
||||
this.validateAppType(param.getAppType());
|
||||
var entity = WechatIsvMchAppConvert.CONVERT.toEntity(param);
|
||||
wechatIsvMchAppManager.save(entity);
|
||||
}
|
||||
|
||||
/// 更新服务商通道商户应用
|
||||
public void update(WechatIsvMchAppParam param) {
|
||||
var entity = wechatIsvMchAppManager.findById(param.getId())
|
||||
// 微信: 服务商通道商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.mchAppNotFound"));
|
||||
this.assertScopeMatch(entity, param.getMchNo(), param.getChannelMchNo());
|
||||
this.assertWxAppIdUnique(entity.getMchNo(), entity.getChannelMchNo(), param.getWxAppId(), param.getId());
|
||||
this.validateAppType(param.getAppType());
|
||||
WechatIsvMchAppConvert.CONVERT.copy(param, entity);
|
||||
wechatIsvMchAppManager.updateById(entity);
|
||||
}
|
||||
|
||||
/// 删除应用(级联删除授权配置与能力关联)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delete(Long id) {
|
||||
wechatIsvMchAppManager.findById(id)
|
||||
// 微信: 服务商通道商户应用不存在
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.mchAppNotFound"));
|
||||
wechatIsvMchAppAuthConfigService.deleteByWechatIsvMchAppId(id);
|
||||
wechatIsvMchAppCapabilityService.deleteByWechatIsvMchAppId(id);
|
||||
wechatIsvMchAppManager.deleteById(id);
|
||||
}
|
||||
|
||||
/// 校验同一通道商户下应用AppId唯一
|
||||
private void assertWxAppIdUnique(String mchNo, String channelMchNo, String wxAppId, Long excludeId) {
|
||||
if (this.existsWxAppIdByChannel(mchNo, channelMchNo, wxAppId, excludeId)) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.appIdDuplicate");
|
||||
}
|
||||
}
|
||||
|
||||
/// 校验操作范围与记录归属一致
|
||||
private void assertScopeMatch(WechatIsvMchApp entity, String mchNo, String channelMchNo) {
|
||||
if (!entity.getMchNo().equals(mchNo) || !entity.getChannelMchNo().equals(channelMchNo)) {
|
||||
// 微信: 服务商通道商户应用不存在或商户号归属不匹配
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.mchAppNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
/// 校验应用类型(复用服务商应用类型枚举)
|
||||
private void validateAppType(String appType) {
|
||||
boolean valid = Arrays.stream(WechatIsvAppTypeEnum.values())
|
||||
.anyMatch(item -> item.getCode().equals(appType));
|
||||
if (!valid) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.appTypeInvalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"aggregate_pay_qrcode": "Aggregate QR Code Payment",
|
||||
"aggregate_pay_barcode": "Aggregate Barcode Payment",
|
||||
"aggregate_pay_qrcode": "Aggregate QR Code",
|
||||
"aggregate_pay_barcode": "Aggregate Barcode",
|
||||
"wechat_cashier": "WeChat Mini Program Cashier",
|
||||
"wechat_qr": "WeChat Native",
|
||||
"wechat_jsapi": "WeChat JSAPI",
|
||||
"wechat_mini": "WeChat Mini Program",
|
||||
"wechat_h5": "WeChat H5",
|
||||
"wechat_app": "WeChat App",
|
||||
"wechat_barcode": "WeChat Barcode Payment",
|
||||
"wechat_barcode": "WeChat Barcode",
|
||||
"alipay_qr": "Alipay QR",
|
||||
"alipay_order_qr": "Alipay Order QR",
|
||||
"alipay_jsapi": "Alipay JSAPI",
|
||||
@@ -15,15 +15,15 @@
|
||||
"alipay_pc": "Alipay PC",
|
||||
"alipay_h5": "Alipay H5",
|
||||
"alipay_app": "Alipay App",
|
||||
"alipay_barcode": "Alipay Barcode Payment",
|
||||
"alipay_barcode": "Alipay Barcode",
|
||||
"union_qr": "UnionPay QR",
|
||||
"union_jsapi": "UnionPay JSAPI",
|
||||
"union_h5": "UnionPay H5",
|
||||
"union_pay_barcode": "UnionPay Barcode Payment",
|
||||
"douyin_qr": "Douyin QR Pay",
|
||||
"douyin_jsapi": "Douyin JSAPI Pay",
|
||||
"douyin_h5": "Douyin H5 Pay",
|
||||
"douyin_app": "Douyin APP Pay",
|
||||
"union_pay_barcode": "UnionPay Barcode",
|
||||
"douyin_qr": "Douyin QR",
|
||||
"douyin_jsapi": "Douyin JSAPI",
|
||||
"douyin_h5": "Douyin H5",
|
||||
"douyin_app": "Douyin App",
|
||||
"visa_card_gateway": "Visa Gateway",
|
||||
"visa_card_present": "Visa Card Present",
|
||||
"mastercard_card_gateway": "Mastercard Gateway",
|
||||
|
||||
@@ -103,6 +103,9 @@
|
||||
"alipayDirectAppId": {
|
||||
"notNull": "Alipay direct app id cannot be null"
|
||||
},
|
||||
"wechatIsvAppId": {
|
||||
"notNull": "WeChat ISV app id cannot be null"
|
||||
},
|
||||
"account": {
|
||||
"notBlank": "Account cannot be blank",
|
||||
"size": "Account length must be between 6 and 20 characters",
|
||||
@@ -508,6 +511,9 @@
|
||||
"wechatDirectAppId": {
|
||||
"notNull": "WeChat direct app ID cannot be blank"
|
||||
},
|
||||
"wechatIsvMchAppId": {
|
||||
"notNull": "WeChat ISV channel merchant app ID cannot be blank"
|
||||
},
|
||||
"appSecret": {
|
||||
"size": "App secret cannot exceed 512 characters"
|
||||
},
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
{
|
||||
"aggregate_pay_qrcode": "聚合扫码支付",
|
||||
"aggregate_pay_barcode": "聚合付款码支付",
|
||||
"aggregate_pay_qrcode": "聚合扫码",
|
||||
"aggregate_pay_barcode": "聚合付款码",
|
||||
"wechat_cashier": "微信小程序收银台",
|
||||
"wechat_qr": "Native 支付",
|
||||
"wechat_jsapi": "微信jsapi",
|
||||
"wechat_qr": "微信Native",
|
||||
"wechat_jsapi": "微信JSAPI",
|
||||
"wechat_mini": "微信小程序",
|
||||
"wechat_h5": "微信H5",
|
||||
"wechat_app": "微信应用支付",
|
||||
"wechat_barcode": "微信付款码支付",
|
||||
"wechat_app": "微信APP",
|
||||
"wechat_barcode": "微信付款码",
|
||||
"alipay_qr": "支付宝扫码",
|
||||
"alipay_order_qr": "支付宝订单码",
|
||||
"alipay_jsapi": "支付宝jsapi",
|
||||
"alipay_jsapi": "支付宝JSAPI",
|
||||
"alipay_mini": "支付宝小程序",
|
||||
"alipay_pc": "支付宝电脑支付",
|
||||
"alipay_pc": "支付宝PC",
|
||||
"alipay_h5": "支付宝H5",
|
||||
"alipay_app": "支付宝应用支付",
|
||||
"alipay_barcode": "支付宝付款码支付",
|
||||
"alipay_app": "支付宝APP",
|
||||
"alipay_barcode": "支付宝付款码",
|
||||
"union_qr": "银联扫码",
|
||||
"union_jsapi": "银联jsapi",
|
||||
"union_jsapi": "银联JSAPI",
|
||||
"union_h5": "银联H5",
|
||||
"union_pay_barcode": "银联付款码支付",
|
||||
"douyin_qr": "抖音扫码支付",
|
||||
"douyin_jsapi": "抖音JSAPI支付",
|
||||
"douyin_h5": "抖音H5支付",
|
||||
"douyin_app": "抖音APP支付",
|
||||
"visa_card_gateway": "Visa 网关支付",
|
||||
"visa_card_present": "Visa 刷卡支付",
|
||||
"mastercard_card_gateway": "万事达网关支付",
|
||||
"mastercard_card_present": "万事达刷卡支付",
|
||||
"other": "其他支付方式"
|
||||
"union_pay_barcode": "银联付款码",
|
||||
"douyin_qr": "抖音扫码",
|
||||
"douyin_jsapi": "抖音JSAPI",
|
||||
"douyin_h5": "抖音H5",
|
||||
"douyin_app": "抖音APP",
|
||||
"visa_card_gateway": "Visa网关",
|
||||
"visa_card_present": "Visa刷卡",
|
||||
"mastercard_card_gateway": "万事达网关",
|
||||
"mastercard_card_present": "万事达刷卡",
|
||||
"other": "其他"
|
||||
}
|
||||
|
||||
@@ -103,6 +103,9 @@
|
||||
"alipayDirectAppId": {
|
||||
"notNull": "支付宝直连应用ID不可为空"
|
||||
},
|
||||
"wechatIsvAppId": {
|
||||
"notNull": "微信服务商应用ID不可为空"
|
||||
},
|
||||
"account": {
|
||||
"notBlank": "登录账号不可为空",
|
||||
"size": "账号长度为6-20个字符",
|
||||
@@ -511,6 +514,9 @@
|
||||
"wechatDirectAppId": {
|
||||
"notNull": "微信直连应用ID不可为空"
|
||||
},
|
||||
"wechatIsvMchAppId": {
|
||||
"notNull": "微信服务商通道商户应用ID不可为空"
|
||||
},
|
||||
"appSecret": {
|
||||
"size": "应用密钥不可超过512位"
|
||||
},
|
||||
|
||||
@@ -17,23 +17,23 @@ import java.util.Objects;
|
||||
public enum PayMethodEnum implements I18nSupport {
|
||||
|
||||
/* 聚合支付 */
|
||||
/// 聚合扫码支付
|
||||
/// 聚合扫码
|
||||
AGGREGATE_PAY_QRCODE("aggregate_pay_qrcode", PayProviderEnum.AGGREGATE_PAY),
|
||||
/// 聚合付款码支付
|
||||
/// 聚合付款码
|
||||
AGGREGATE_PAY_BARCODE("aggregate_pay_barcode", PayProviderEnum.AGGREGATE_PAY),
|
||||
|
||||
/* 微信 */
|
||||
/// 微信小程序收银台
|
||||
WECHAT_CASHIER("wechat_cashier", PayProviderEnum.WECHAT),
|
||||
/// 微信扫码
|
||||
/// 微信Native
|
||||
WECHAT_QR("wechat_qr", PayProviderEnum.WECHAT),
|
||||
/// 微信jsapi
|
||||
/// 微信JSAPI
|
||||
WECHAT_JSAPI("wechat_jsapi", PayProviderEnum.WECHAT),
|
||||
/// 微信小程序
|
||||
WECHAT_MINI("wechat_mini", PayProviderEnum.WECHAT),
|
||||
/// 微信H5
|
||||
WECHAT_H5("wechat_h5", PayProviderEnum.WECHAT),
|
||||
/// 微信应用支付
|
||||
/// 微信APP
|
||||
WECHAT_APP("wechat_app", PayProviderEnum.WECHAT),
|
||||
/// 微信付款码
|
||||
WECHAT_BARCODE("wechat_barcode", PayProviderEnum.WECHAT),
|
||||
@@ -43,15 +43,15 @@ public enum PayMethodEnum implements I18nSupport {
|
||||
ALIPAY_QR("alipay_qr", PayProviderEnum.ALIPAY),
|
||||
/// 支付宝订单码
|
||||
ALIPAY_ORDER_QR("alipay_order_qr", PayProviderEnum.ALIPAY),
|
||||
/// 支付宝jsapi
|
||||
/// 支付宝JSAPI
|
||||
ALIPAY_JSAPI("alipay_jsapi", PayProviderEnum.ALIPAY),
|
||||
/// 支付宝小程序
|
||||
ALIPAY_MINI("alipay_mini", PayProviderEnum.ALIPAY),
|
||||
/// 支付宝电脑支付
|
||||
/// 支付宝PC
|
||||
ALIPAY_PC("alipay_pc", PayProviderEnum.ALIPAY),
|
||||
/// 支付宝H5
|
||||
ALIPAY_H5("alipay_h5", PayProviderEnum.ALIPAY),
|
||||
/// 支付宝应用支付
|
||||
/// 支付宝APP
|
||||
ALIPAY_APP("alipay_app", PayProviderEnum.ALIPAY),
|
||||
/// 支付宝付款码
|
||||
ALIPAY_BARCODE("alipay_barcode", PayProviderEnum.ALIPAY),
|
||||
@@ -59,7 +59,7 @@ public enum PayMethodEnum implements I18nSupport {
|
||||
/* 银联 */
|
||||
/// 银联扫码
|
||||
UNION_QR("union_qr", PayProviderEnum.UNION_PAY),
|
||||
/// 银联jsapi
|
||||
/// 银联JSAPI
|
||||
UNION_JSAPI("union_jsapi", PayProviderEnum.UNION_PAY),
|
||||
/// 银联H5
|
||||
UNION_H5("union_h5", PayProviderEnum.UNION_PAY),
|
||||
@@ -67,26 +67,26 @@ public enum PayMethodEnum implements I18nSupport {
|
||||
UNION_PAY_BARCODE("union_pay_barcode", PayProviderEnum.UNION_PAY),
|
||||
|
||||
/* 抖音 */
|
||||
/// 抖音扫码支付
|
||||
/// 抖音扫码
|
||||
DOUYIN_QR("douyin_qr", PayProviderEnum.DOUYIN),
|
||||
/// 抖音JSAPI支付
|
||||
/// 抖音JSAPI
|
||||
DOUYIN_JSAPI("douyin_jsapi", PayProviderEnum.DOUYIN),
|
||||
/// 抖音H5支付
|
||||
/// 抖音H5
|
||||
DOUYIN_H5("douyin_h5", PayProviderEnum.DOUYIN),
|
||||
/// 抖音APP支付
|
||||
/// 抖音APP
|
||||
DOUYIN_APP("douyin_app", PayProviderEnum.DOUYIN),
|
||||
|
||||
/* 卡组 */
|
||||
/// Visa 网关支付
|
||||
/// Visa网关
|
||||
VISA_CARD_GATEWAY("visa_card_gateway", PayProviderEnum.VISA),
|
||||
/// Visa 刷卡支付
|
||||
/// Visa刷卡
|
||||
VISA_CARD_PRESENT("visa_card_present", PayProviderEnum.VISA),
|
||||
/// 万事达网关支付
|
||||
/// 万事达网关
|
||||
MASTERCARD_CARD_GATEWAY("mastercard_card_gateway", PayProviderEnum.MASTERCARD),
|
||||
/// 万事达刷卡支付
|
||||
/// 万事达刷卡
|
||||
MASTERCARD_CARD_PRESENT("mastercard_card_present", PayProviderEnum.MASTERCARD),
|
||||
|
||||
/// 其他支付方式(无固定渠道归属)
|
||||
/// 其他(无固定渠道归属)
|
||||
OTHER("other", null),
|
||||
;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user