import { postRemittance } from "@/lib/remittance-client"; export type RemittanceChannel = { id: string; code: string; requestUrl: string; icon: string; name: string; description: string; amountRange: string; processingTime: string; fee: string; groupName: string; groupOrder: number; channelType: number | null; bankValid: number; }; export type BankChannelOption = { value: string; label: string; currency: string; }; function toNumber(v: unknown): number | null { if (typeof v === "number" && Number.isFinite(v)) return v; if (typeof v === "string" && v.trim()) { const n = Number(v); if (Number.isFinite(n)) return n; } return null; } function pickString(record: Record, keys: string[]): string { for (const key of keys) { const value = record[key]; if (typeof value === "string" && value.trim()) return value.trim(); } return ""; } function pickAmountRange(record: Record): string { const min = toNumber( record.minAmount ?? record.amountMin ?? record.lowerAmount ?? record.min, ); const max = toNumber( record.maxAmount ?? record.amountMax ?? record.upperAmount ?? record.max, ); const unit = pickString(record, ["currency", "currencyCode", "coin", "unit"]).toUpperCase(); if (min !== null && max !== null) { return `$${min} - $${max}${unit ? ` ${unit}` : ""}`; } const rangeText = pickString(record, ["amountRange", "limitRange", "amountDesc"]); return rangeText || "-"; } function pickFee(record: Record): string { const feeRate = toNumber(record.feeRate ?? record.rate); if (feeRate !== null) { if (feeRate <= 1) return `${(feeRate * 100).toFixed(2).replace(/\.00$/, "")}%`; return `${feeRate.toFixed(2).replace(/\.00$/, "")}%`; } const fee = pickString(record, ["fee", "charge", "feeDesc"]); return fee || "0%"; } const CHANNEL_GROUP_NAME_MAP: Record = { 1: "国际转账支付", 2: "中国网银支付", 3: "数字货币", 4: "电子钱包", 5: "CWG 电子卡", }; const CHANNEL_GROUP_DISPLAY_ORDER: Record = { 3: 1, // 数字货币 2: 2, // 中国网银支付 1: 3, // 国际转账支付 4: 4, // 电子钱包 5: 5, // CWG 电子卡 }; function pickGroupId(record: Record): number | null { const id = toNumber( record.type ?? record.channelType ?? record.payTypeType ?? record.groupId ?? record.channelGroupId ?? record.channelClassify ?? record.channelCategory ?? record.categoryId ?? record.payTypeGroup ?? record.payTypeClassify, ); if (id === null) return null; if (id >= 1 && id <= 5) return id; return null; } function normalizeChannelList(raw: unknown): RemittanceChannel[] { if (!raw || typeof raw !== "object") return []; const container = raw as Record; const inner = container.data ?? container.list ?? container.rows ?? container.records ?? container.channels; const source = Array.isArray(inner) ? inner : []; const output: RemittanceChannel[] = []; for (const item of source) { if (!item || typeof item !== "object") continue; const row = item as Record; const id = String( row.id ?? row.channelId ?? row.payTypeId ?? row.code ?? row.channelCode ?? output.length + 1, ); const name = pickString(row, ["name", "channelName", "payType", "channelCode", "code"]) || "-"; const code = pickString(row, ["code", "channelCode", "payCode", "channelNo"]) || id; const requestUrl = pickString(row, ["requestUrl", "payUrl", "url"]) || "/xfgpay/pay"; const icon = pickString(row, ["icon", "logo", "img", "image", "iconUrl"]); const processingTime = pickString(row, ["processingTime", "processTime", "arrivalTime", "timeDesc"]) || "1 hours"; const groupId = pickGroupId(row); const fallbackGroupName = pickString(row, [ "groupName", "categoryName", "channelGroup", "payScene", "group", ]) || "支付通道"; const groupName = groupId ? CHANNEL_GROUP_NAME_MAP[groupId] : fallbackGroupName; const groupOrder = groupId ? (CHANNEL_GROUP_DISPLAY_ORDER[groupId] ?? 999) : 999; output.push({ id, code, requestUrl, icon, name, description: name || "-", amountRange: pickAmountRange(row), processingTime, fee: pickFee(row), groupName, groupOrder, channelType: toNumber(row.type ?? row.channelType ?? row.payTypeType), bankValid: toNumber(row.bankValid) ?? 0, }); } return output; } export async function fetchRemittanceChannels(): Promise { let raw: unknown; try { raw = await postRemittance("/remit/channel/list", {}); } catch { raw = await postRemittance("/remittance/channel/list", {}); } return normalizeChannelList(raw); } function normalizeBankChannelList(raw: unknown): BankChannelOption[] { if (!raw || typeof raw !== "object") return []; const container = raw as Record; const inner = container.data ?? container.list ?? container.rows ?? container.records ?? container.channels; const source = Array.isArray(inner) ? inner : []; const output: BankChannelOption[] = []; for (const item of source) { if (!item || typeof item !== "object") continue; const row = item as Record; const value = pickString(row, ["code", "name", "currency", "enName"]); if (!value) continue; const label = pickString(row, ["name", "enName", "currency", "code"]) || value; const currency = pickString(row, ["currency", "code", "name"]) || "USDT"; output.push({ value, label, currency }); } return output; } export async function fetchBankChannelOptions(channelCode?: string): Promise { const body = channelCode ? { channelCode } : {}; const raw = await postRemittance("/channel/bank/list", body); return normalizeBankChannelList(raw); } export async function submitXfgPayOrder(input: { requestUrl: string; amount: number; bankCode?: string; goodIds: string[]; payName: string; payPhone: string; }): Promise<{ raw: unknown; resultUrl: string | null }> { const normalizedRequestUrl = `/${input.requestUrl.replace(/^\/+|\/+$/g, "")}`; const amount = String(input.amount); const path = input.bankCode ? `${normalizedRequestUrl}/1/${encodeURIComponent(amount)}/${encodeURIComponent(input.bankCode)}/0` : `${normalizedRequestUrl}/1/${encodeURIComponent(amount)}/0`; const body = { goodIds: input.goodIds, payName: input.payName, payPhone: input.payPhone, }; const data = await postRemittance(path, body); return { raw: data, resultUrl: pickResultUrl(data) }; } function pickResultUrl(raw: unknown): string | null { if (!raw || typeof raw !== "object") return null; const o = raw as Record; const candidates: unknown[] = [ o.result, o.url, o.payUrl, o.redirectUrl, ]; if (o.data && typeof o.data === "object" && o.data !== null) { const d = o.data as Record; candidates.push(d.result, d.url, d.payUrl, d.redirectUrl); } for (const item of candidates) { if (typeof item === "string" && item.trim()) return item.trim(); } return null; }