'use client'; import { useState, useEffect } from 'react'; import { PAYMENT_TYPE_META } from '@/lib/pay-utils'; export interface MethodLimitInfo { available: boolean; remaining: number | null; /** 单笔限额,0 = 使用全局 maxAmount */ singleMax?: number; /** 手续费率百分比,0 = 无手续费 */ feeRate?: number; } interface PaymentFormProps { userId: number; userName?: string; userBalance?: number; enabledPaymentTypes: string[]; methodLimits?: Record; minAmount: number; maxAmount: number; onSubmit: (amount: number, paymentType: string) => Promise; loading?: boolean; dark?: boolean; } const QUICK_AMOUNTS = [10, 20, 50, 100, 200, 500, 1000, 2000]; const AMOUNT_TEXT_PATTERN = /^\d*(\.\d{0,2})?$/; function hasValidCentPrecision(num: number): boolean { return Math.abs(Math.round(num * 100) - num * 100) < 1e-8; } export default function PaymentForm({ userId, userName, userBalance, enabledPaymentTypes, methodLimits, minAmount, maxAmount, onSubmit, loading, dark = false, }: PaymentFormProps) { const [amount, setAmount] = useState(''); const [paymentType, setPaymentType] = useState(enabledPaymentTypes[0] || 'alipay'); const [customAmount, setCustomAmount] = useState(''); // Reset paymentType when enabledPaymentTypes changes (e.g. after config loads) useEffect(() => { if (!enabledPaymentTypes.includes(paymentType)) { setPaymentType(enabledPaymentTypes[0] || 'stripe'); } }, [enabledPaymentTypes, paymentType]); const handleQuickAmount = (val: number) => { setAmount(val); setCustomAmount(String(val)); }; const handleCustomAmountChange = (val: string) => { if (!AMOUNT_TEXT_PATTERN.test(val)) { return; } setCustomAmount(val); if (val === '') { setAmount(''); return; } const num = parseFloat(val); if (!isNaN(num) && num > 0 && hasValidCentPrecision(num)) { setAmount(num); } else { setAmount(''); } }; const selectedAmount = amount || 0; const isMethodAvailable = !methodLimits || (methodLimits[paymentType]?.available !== false); const methodSingleMax = methodLimits?.[paymentType]?.singleMax; const effectiveMax = (methodSingleMax !== undefined && methodSingleMax > 0) ? methodSingleMax : maxAmount; const feeRate = methodLimits?.[paymentType]?.feeRate ?? 0; const feeAmount = feeRate > 0 && selectedAmount > 0 ? Math.ceil(selectedAmount * feeRate / 100 * 100) / 100 : 0; const payAmount = feeRate > 0 && selectedAmount > 0 ? Math.round((selectedAmount + feeAmount) * 100) / 100 : selectedAmount; const isValid = selectedAmount >= minAmount && selectedAmount <= effectiveMax && hasValidCentPrecision(selectedAmount) && isMethodAvailable; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!isValid || loading) return; await onSubmit(selectedAmount, paymentType); }; const renderPaymentIcon = (type: string) => { if (type === 'alipay') { return ( ); } if (type === 'wxpay') { return ( ); } if (type === 'stripe') { return ( ); } return null; }; return (
{/* User Info */}
充值账户
{userName || `用户 #${userId}`}
{userBalance !== undefined && (
当前余额: {userBalance.toFixed(2)}
)}
{/* Quick Amount Selection */}
{QUICK_AMOUNTS.filter((val) => val >= minAmount && val <= effectiveMax).map((val) => ( ))}
{/* Custom Amount */}
¥ handleCustomAmountChange(e.target.value)} placeholder={`${minAmount} - ${effectiveMax}`} className={[ 'w-full rounded-lg border py-3 pl-8 pr-4 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500', dark ? 'border-slate-700 bg-slate-900 text-slate-100' : 'border-gray-300 bg-white text-gray-900', ].join(' ')} />
{customAmount !== '' && !isValid && (() => { const num = parseFloat(customAmount); let msg = '金额需在范围内,且最多支持 2 位小数(精确到分)'; if (!isNaN(num)) { if (num < minAmount) msg = `单笔最低充值 ¥${minAmount}`; else if (num > effectiveMax) msg = `单笔最高充值 ¥${effectiveMax}`; } return (
{msg}
); })()} {/* Payment Type — only show when multiple types available */} {enabledPaymentTypes.length > 1 && (
{enabledPaymentTypes.map((type) => { const meta = PAYMENT_TYPE_META[type]; const isSelected = paymentType === type; const limitInfo = methodLimits?.[type]; const isUnavailable = limitInfo !== undefined && !limitInfo.available; return ( ); })}
{/* 当前选中渠道额度不足时的提示 */} {(() => { const limitInfo = methodLimits?.[paymentType]; if (!limitInfo || limitInfo.available) return null; return (

所选支付方式今日额度已满,请切换到其他支付方式

); })()}
)} {/* Fee Detail */} {feeRate > 0 && selectedAmount > 0 && (
充值金额 ¥{selectedAmount.toFixed(2)}
手续费({feeRate}%) ¥{feeAmount.toFixed(2)}
实付金额 ¥{payAmount.toFixed(2)}
)} {/* Submit */}
); }