diff --git a/apps/daxpay-admin/src/api/payment/develop/developTrade.api.ts b/apps/daxpay-admin/src/api/payment/develop/developTrade.api.ts index 7c4c72bf8..4fb802f00 100644 --- a/apps/daxpay-admin/src/api/payment/develop/developTrade.api.ts +++ b/apps/daxpay-admin/src/api/payment/develop/developTrade.api.ts @@ -1,4 +1,5 @@ -import type { Result } from '#/types/web'; +import type { LabelValue, Result } from '#/types/web'; +import type { PayProviderMethod } from '#/api/payment/masterdata/provider.api'; import { defHttp } from '#/api/request'; @@ -19,6 +20,33 @@ export const DevelopTradeApi = { pay(data: DevelopParam): Promise> { return defHttp.post({ url: '/admin/develop/trade/pay', data }); }, + + /** + * 已启用渠道支付方式目录(供调试页支付方式下拉) + */ + methodDirectory(): Promise> { + return defHttp.get({ url: '/admin/develop/trade/method-directory' }); + }, + + /** + * 传值模式: 按商户号筛选通道商户候选 + */ + channelMchCandidates(mchNo: string): Promise> { + return defHttp.get({ + url: '/admin/develop/trade/channel-mch-candidates', + params: { mchNo }, + }); + }, + + /** + * 传值模式: 按通道商户筛选支付能力候选 + */ + capabilityCandidates(channelMchNo: string): Promise> { + return defHttp.get({ + url: '/admin/develop/trade/capability-candidates', + params: { channelMchNo }, + }); + }, }; /** 开发调试参数包装 */ @@ -47,8 +75,10 @@ export interface PayParam { amount: number; /** 支付产品编码, 为空时由路由自动选择 */ product?: string; - /** 支付方式编码 */ - method: string; + /** 支付方式编码(路由模式必填, 传值模式由后端从能力反推) */ + method?: string; + /** 支付能力编码(传值模式输入, 路由模式由后端回填) */ + capability?: string; /** 用户标识 OpenId(微信 jsapi/mini 场景) */ openId?: string; /** 付款码(被扫支付) */ diff --git a/apps/daxpay-admin/src/locales/langs/en-US/payment/develop.json b/apps/daxpay-admin/src/locales/langs/en-US/payment/develop.json index 19bea6a60..b2b8df9c9 100644 --- a/apps/daxpay-admin/src/locales/langs/en-US/payment/develop.json +++ b/apps/daxpay-admin/src/locales/langs/en-US/payment/develop.json @@ -62,6 +62,13 @@ "order": "Order Info", "preview": "Request Preview" }, + "mode": { + "label": "Pay Mode", + "route": "Route Mode", + "direct": "Direct Mode", + "routeDesc": "Dynamically matched by merchant/method + app via routing engine", + "directDesc": "Directly decided by channel merchant + capability" + }, "preview": { "title": "Request JSON", "signBtn": "Sign Preview", @@ -74,6 +81,7 @@ "channelMchNo": "Channel Merchant", "product": "Payment Product", "method": "Payment Method", + "capability": "Capability", "openId": "OpenID", "authCode": "Auth Code", "notifyUrl": "Async Notify URL", @@ -84,7 +92,8 @@ "description": "Description" }, "placeholder": { - "method": "qr_code / jsapi / barcode / ...", + "method": "Select payment method", + "capability": "Select capability", "openId": "Required for WeChat jsapi/mini", "authCode": "Required for barcode pay", "notifyUrl": "https://...", diff --git a/apps/daxpay-admin/src/locales/langs/zh-CN/payment/develop.json b/apps/daxpay-admin/src/locales/langs/zh-CN/payment/develop.json index d56031e45..d40df291c 100644 --- a/apps/daxpay-admin/src/locales/langs/zh-CN/payment/develop.json +++ b/apps/daxpay-admin/src/locales/langs/zh-CN/payment/develop.json @@ -62,6 +62,13 @@ "order": "订单信息", "preview": "请求预览" }, + "mode": { + "label": "支付模式", + "route": "路由模式", + "direct": "传值模式", + "routeDesc": "由商户/支付方式+应用经路由引擎动态匹配", + "directDesc": "由通道商户+支付能力直接决定支付实现" + }, "preview": { "title": "请求参数 JSON", "signBtn": "生成签名预览", @@ -74,6 +81,7 @@ "channelMchNo": "通道商户", "product": "支付产品", "method": "支付方式", + "capability": "支付能力", "openId": "OpenID", "authCode": "付款码", "notifyUrl": "异步通知地址", @@ -84,7 +92,8 @@ "description": "支付描述" }, "placeholder": { - "method": "qr_code / jsapi / barcode / ...", + "method": "请选择支付方式", + "capability": "请选择支付能力", "openId": "微信 jsapi/mini 场景必填", "authCode": "付款码支付必填", "notifyUrl": "https://...", diff --git a/apps/daxpay-admin/src/views/payment/develop/trade/DevelopTrade.vue b/apps/daxpay-admin/src/views/payment/develop/trade/DevelopTrade.vue index 48a40ae4d..f04bf0090 100644 --- a/apps/daxpay-admin/src/views/payment/develop/trade/DevelopTrade.vue +++ b/apps/daxpay-admin/src/views/payment/develop/trade/DevelopTrade.vue @@ -8,9 +8,7 @@ import { IconifyIcon } from '@vben-core/icons'; - import { ChannelMerchantApi } from '#/api/payment/channel/channel-merchant.api'; import { DevelopTradeApi } from '#/api/payment/develop/developTrade.api'; - import { PayProductApi } from '#/api/payment/masterdata/product.api'; import { MchAppInfoApi } from '#/api/payment/merchant/mch-app-info.api'; import { MerchantApi } from '#/api/payment/merchant/merchant.api'; import { QrCode } from '#/components/qrcode'; @@ -23,6 +21,9 @@ // 私钥在 localStorage 中的键名 const PRIVATE_KEY_STORAGE_KEY = 'daxpay_dev_private_key'; + // 支付模式: route=路由模式(商户+方式+应用动态匹配) direct=传值模式(通道商户+能力直接决定) + const routeMode = ref<'route' | 'direct'>('route'); + // ===== 表单数据 ===== const form = reactive({ mchNo: '', @@ -32,7 +33,7 @@ title: '', amount: 0.01, method: '', - product: '', + capability: '', description: '', }); @@ -45,8 +46,9 @@ // ===== 下拉选项 ===== const mchNoOptions = ref([]); const mchAppOptions = ref([]); + const methodOptions = ref([]); const channelMchNoOptions = ref([]); - const productOptions = ref([]); + const capabilityOptions = ref([]); // ===== 调试结果 ===== const resultVisible = ref(false); @@ -75,22 +77,28 @@ MerchantApi.dropdown().then(({ data }) => { mchNoOptions.value = data ?? []; }); - PayProductApi.dropdown().then(({ data }) => { - productOptions.value = data ?? []; + DevelopTradeApi.methodDirectory().then(({ data }) => { + methodOptions.value = + data?.map((item) => ({ + label: item.methodLabel || item.method, + value: item.method, + })) ?? []; }); if (!form.bizOrderNo) { genBizOrderNo(); } } - /** 商户变更时刷新应用与通道商户下拉 */ + /** 商户变更: 刷新应用列表, 传值模式下重载通道商户候选 */ function merchantChange() { form.appId = ''; form.channelMchNo = ''; + form.capability = ''; mchAppOptions.value = []; channelMchNoOptions.value = []; + capabilityOptions.value = []; if (!form.mchNo) return; - // 应用列表 + // 应用列表(路由模式使用) MchAppInfoApi.page({ mchNo: form.mchNo, size: 100 }).then(({ data }) => { mchAppOptions.value = data?.records?.map((item) => ({ @@ -98,15 +106,43 @@ value: item.appId ?? '', })) ?? []; }); - // 通道商户列表 - ChannelMerchantApi.findAllByMchNo(form.mchNo).then(({ data }) => { - channelMchNoOptions.value = - data?.map((item) => ({ - label: item.channelMerchantName - ? `${item.channelMerchantName} (${item.channelMchNo})` - : (item.channelMchNo ?? ''), - value: item.channelMchNo ?? '', - })) ?? []; + // 传值模式: 商户变更即加载通道商户候选(不依赖支付方式) + if (routeMode.value === 'direct') { + loadChannelMchCandidates(form.mchNo); + } + } + + /** 通道商户变更: 重置能力并重载能力候选 */ + function channelMchNoChange() { + form.capability = ''; + capabilityOptions.value = []; + if (form.channelMchNo) { + loadCapabilityCandidates(form.channelMchNo); + } + } + + /** 模式切换: 清空通道相关字段, 传值模式重载通道商户候选 */ + function modeChange() { + form.channelMchNo = ''; + form.capability = ''; + channelMchNoOptions.value = []; + capabilityOptions.value = []; + if (routeMode.value === 'direct' && form.mchNo) { + loadChannelMchCandidates(form.mchNo); + } + } + + /** 加载通道商户候选(商户全部启用通道商户) */ + function loadChannelMchCandidates(mchNo: string) { + DevelopTradeApi.channelMchCandidates(mchNo).then(({ data }) => { + channelMchNoOptions.value = data ?? []; + }); + } + + /** 加载支付能力候选(按通道商户产品) */ + function loadCapabilityCandidates(channelMchNo: string) { + DevelopTradeApi.capabilityCandidates(channelMchNo).then(({ data }) => { + capabilityOptions.value = data ?? []; }); } @@ -150,10 +186,24 @@ } // ===== 实时请求预览 ===== - /** 当前表单的 JSON 预览(剔除空值) */ + /** 按当前模式组装提交参数(各模式只透传自身字段, product/method 由后端派生) */ + function buildPayload(): PayParam { + const payload: PayParam = { ...form }; + if (routeMode.value === 'route') { + // 路由模式: 不传通道商户/能力, 由路由引擎决定 + payload.channelMchNo = ''; + payload.capability = ''; + } else { + // 传值模式: method 由后端从(通道商户, 能力)反推, 不透传 + payload.method = ''; + } + return payload; + } + + /** 当前请求 JSON 预览(剔除空值) */ const requestPreview = computed(() => { const cleaned: Record = {}; - for (const [k, v] of Object.entries(form)) { + for (const [k, v] of Object.entries(buildPayload())) { if (v !== '' && v != null) { cleaned[k] = v; } @@ -170,7 +220,7 @@ signPreviewLoading.value = true; try { const { data } = await DevelopTradeApi.sign({ - param: form, + param: buildPayload(), privateKey: privateKey.value, }); signPreview.signStr = data?.signStr ?? ''; @@ -202,7 +252,7 @@ loading.value = true; try { const { data } = await DevelopTradeApi.pay({ - param: form, + param: buildPayload(), privateKey: privateKey.value, }); resultData.value = data ?? {}; @@ -223,7 +273,7 @@ title: '', amount: 0.01, method: '', - product: '', + capability: '', description: '', openId: undefined, authCode: undefined, @@ -231,8 +281,10 @@ returnUrl: undefined, expiredTime: undefined, }); + routeMode.value = 'route'; mchAppOptions.value = []; channelMchNoOptions.value = []; + capabilityOptions.value = []; signPreview.signStr = ''; signPreview.sign = ''; genBizOrderNo(); @@ -322,6 +374,21 @@ + + + + {{ $t('payment.develop.trade.mode.route') }} + {{ $t('payment.develop.trade.mode.direct') }} + +
+ {{ + routeMode === 'route' + ? $t('payment.develop.trade.mode.routeDesc') + : $t('payment.develop.trade.mode.directDesc') + }} +
+
+ @@ -336,7 +403,21 @@ /> - + + + + + + + + - - + + + - - + + + - - - - -