From 453c7aefd957d10c995c703cde79c03c6a360e84 Mon Sep 17 00:00:00 2001 From: cattie <2237829695@qq.com> Date: Mon, 9 Feb 2026 12:04:00 +0800 Subject: [PATCH] fix: merchant distribution encounter 404 using web portal --- .../common/merchant/merchant-distribute.tsx | 16 +++++++++++--- .../common/merchant/merchant-info.tsx | 2 +- frontend/lib/services/core/base.service.ts | 4 +++- .../lib/services/merchant/merchant.service.ts | 20 ++++++++++++----- frontend/lib/services/merchant/types.ts | 2 +- frontend/lib/utils.ts | 22 +++++++++++++++++++ 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/frontend/components/common/merchant/merchant-distribute.tsx b/frontend/components/common/merchant/merchant-distribute.tsx index 79612c5b..568c561d 100644 --- a/frontend/components/common/merchant/merchant-distribute.tsx +++ b/frontend/components/common/merchant/merchant-distribute.tsx @@ -19,18 +19,20 @@ import { Label } from "@/components/ui/label" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { Spinner } from "@/components/ui/spinner" -import { MerchantService } from "@/lib/services" +import { MerchantService, type MerchantAPIKey } from "@/lib/services" interface DistributeDialogProps { /** 自定义触发器 */ trigger?: React.ReactNode + /** 商户凭证 */ + apiKey?: Pick } /** * 商户分发对话框 * 商户向用户分发积分 */ -export function DistributeDialog({ trigger }: DistributeDialogProps) { +export function DistributeDialog({ trigger, apiKey }: DistributeDialogProps) { const [open, setOpen] = useState(false) const [loading, setLoading] = useState(false) @@ -88,11 +90,19 @@ export function DistributeDialog({ trigger }: DistributeDialogProps) { try { setLoading(true) + if (!apiKey?.client_id || !apiKey?.client_secret) { + toast.error('缺少应用凭证', { description: '请先创建应用并确认凭证可用' }) + return + } + const result = await MerchantService.distribute({ - user_id: userId.trim(), + user_id: Number(userId.trim()), username: username.trim(), amount: amountNum, remark: remark.trim() || undefined, + }, { + client_id: apiKey.client_id, + client_secret: apiKey.client_secret, }) toast.success('分发成功', { diff --git a/frontend/components/common/merchant/merchant-info.tsx b/frontend/components/common/merchant/merchant-info.tsx index 7d9a5c2a..66608dea 100644 --- a/frontend/components/common/merchant/merchant-info.tsx +++ b/frontend/components/common/merchant/merchant-info.tsx @@ -221,7 +221,7 @@ export function MerchantInfo({ apiKey, onUpdate, onDelete, updateAPIKey }: Merch } /> - + diff --git a/frontend/lib/services/core/base.service.ts b/frontend/lib/services/core/base.service.ts index b1ea363d..efa86654 100644 --- a/frontend/lib/services/core/base.service.ts +++ b/frontend/lib/services/core/base.service.ts @@ -170,6 +170,7 @@ export class BaseService { * @template T - 响应数据类型 * @param url - 完整 URL * @param data - 请求数据 + * @param config - 额外的请求配置 * @returns 响应数据(不经过 response.data.data 解包) * * @remarks @@ -178,8 +179,9 @@ export class BaseService { protected static async rawPost( url: string, data?: unknown, + config?: InternalAxiosRequestConfig, ): Promise { - const response = await apiClient.post(url, data); + const response = await apiClient.post(url, data, config); return response.data; } } diff --git a/frontend/lib/services/merchant/merchant.service.ts b/frontend/lib/services/merchant/merchant.service.ts index 622d85b2..e59b0ea9 100644 --- a/frontend/lib/services/merchant/merchant.service.ts +++ b/frontend/lib/services/merchant/merchant.service.ts @@ -1,4 +1,6 @@ +import { AxiosHeaders, type InternalAxiosRequestConfig } from 'axios'; import { BaseService } from '../core/base.service'; +import { encodeBase64 } from '../../utils'; import type { MerchantAPIKey, CreateAPIKeyRequest, @@ -456,7 +458,7 @@ export class MerchantService extends BaseService { * @example * ```typescript * const result = await MerchantService.distribute({ - * user_id: '123', + * user_id: 123, * username: 'alice', * amount: 100, * out_trade_no: 'DIST20251231001', @@ -466,16 +468,24 @@ export class MerchantService extends BaseService { * ``` * * @remarks - * - 使用 POST 请求调用 `/pay/distribute` 接口 + * - 使用 POST 请求调用 `/pay/distribute` 接口(前端通过 `/lpay/distribute` 代理) * - 需要通过 Basic Auth 提供商户凭证 * - 不能分发给商户自己 * - 商户余额必须充足 * - 分发会扣除分发费率(根据商户的支付等级) */ static async distribute( - request: MerchantDistributeRequest + request: MerchantDistributeRequest, + auth?: { client_id: string; client_secret: string } ): Promise { - return this.rawPost('/pay/distribute', request); + const config: InternalAxiosRequestConfig | undefined = auth + ? { + headers: new AxiosHeaders({ + Authorization: `Basic ${encodeBase64(`${auth.client_id}:${auth.client_secret}`)}`, + }), + } + : undefined; + + return this.rawPost('/lpay/distribute', request, config); } } - diff --git a/frontend/lib/services/merchant/types.ts b/frontend/lib/services/merchant/types.ts index e86add80..322c37a4 100644 --- a/frontend/lib/services/merchant/types.ts +++ b/frontend/lib/services/merchant/types.ts @@ -334,7 +334,7 @@ export interface RefundMerchantOrderResponse { */ export interface MerchantDistributeRequest { /** 接收用户 ID (必填) */ - user_id: string; + user_id: number; /** 接收用户名,用于验证 (必填) */ username: string; /** 分发金额 (必填) */ diff --git a/frontend/lib/utils.ts b/frontend/lib/utils.ts index 16ba90f0..9752780c 100644 --- a/frontend/lib/utils.ts +++ b/frontend/lib/utils.ts @@ -37,6 +37,28 @@ export function formatLocalDate(date: Date): string { return `${ year }-${ month }-${ day }T${ hours }:${ minutes }:${ seconds }+08:00` } +type Base64Buffer = { + from: (input: string, encoding: 'utf-8') => { toString: (encoding: 'base64') => string } +} + +/** + * Base64 编码 + * @param value 待编码字符串 + * @returns Base64 编码后的字符串 + */ +export function encodeBase64(value: string): string { + if (typeof globalThis.btoa === 'function') { + return globalThis.btoa(value) + } + + const bufferConstructor = (globalThis as typeof globalThis & { Buffer?: Base64Buffer }).Buffer + if (bufferConstructor) { + return bufferConstructor.from(value, 'utf-8').toString('base64') + } + + throw new Error('当前环境不支持 Base64 编码') +} + /** * 生成交易缓存的唯一键 * @param params 交易查询参数