feat(payment): enhance payment method configuration with icons and improve UI interactions

This commit is contained in:
CaIon
2026-06-17 20:47:24 +08:00
parent a2f3ac02e4
commit 2154fce094
17 changed files with 1437 additions and 904 deletions
+14 -5
View File
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useState, useEffect } from 'react'
import { useState, useEffect, useCallback } from 'react'
import { getTopupInfo } from '../api'
import {
generatePresetAmounts,
@@ -70,6 +70,7 @@ function parsePaymentMethods(
name: typeof item.name === 'string' ? item.name : '',
type,
color: typeof item.color === 'string' ? item.color : undefined,
icon: typeof item.icon === 'string' ? item.icon : undefined,
min_topup:
type === 'stripe' && normalizedMinTopup <= 0
? stripeMinTopup
@@ -166,7 +167,7 @@ export function useTopupInfo() {
const [presetAmounts, setPresetAmounts] = useState<PresetAmount[]>([])
const [loading, setLoading] = useState(true)
const fetchTopupInfo = async () => {
const fetchTopupInfo = useCallback(async () => {
try {
setLoading(true)
@@ -211,11 +212,19 @@ export function useTopupInfo() {
} finally {
setLoading(false)
}
}
}, [])
useEffect(() => {
fetchTopupInfo()
}, [])
let cancelled = false
queueMicrotask(() => {
if (!cancelled) void fetchTopupInfo()
})
return () => {
cancelled = true
}
}, [fetchTopupInfo])
return {
topupInfo,
+16 -4
View File
@@ -20,6 +20,7 @@ import { type ReactNode } from 'react'
import i18next from 'i18next'
import { CreditCard, Landmark } from 'lucide-react'
import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si'
import { ReactIconByName } from '@/components/react-icon-by-name'
import { PAYMENT_TYPES, PAYMENT_ICON_COLORS } from '../constants'
// ============================================================================
@@ -57,16 +58,18 @@ function normalizeHttpIconUrl(raw: string | undefined | null): string | null {
/**
* Get payment method icon component
*
* When iconUrl is provided, render an <img/> with that URL so custom
* gateway logos can be configured per-method.
* When icon is provided, render a safe http(s) image URL or resolve it as a
* react-icons component name. Invalid configured icons intentionally render
* nothing instead of falling back to the payment type.
*/
export function getPaymentIcon(
paymentType: string | undefined,
className: string = 'h-4 w-4',
iconUrl?: string,
icon?: string,
altName?: string
): ReactNode {
const safeIconUrl = normalizeHttpIconUrl(iconUrl)
const iconValue = icon?.trim()
const safeIconUrl = normalizeHttpIconUrl(iconValue)
if (safeIconUrl) {
return (
<img
@@ -80,6 +83,15 @@ export function getPaymentIcon(
/>
)
}
if (iconValue) {
return (
<ReactIconByName
name={iconValue}
className={className}
title={altName || paymentType || iconValue}
/>
)
}
if (!paymentType) {
return <CreditCard className={className} />
+2 -2
View File
@@ -94,11 +94,11 @@ export interface PaymentMethod {
name: string
/** Payment method type identifier */
type: string
/** Optional color for UI display */
/** Legacy optional color for UI display */
color?: string
/** Minimum topup amount for this payment method */
min_topup?: number
/** Optional icon URL provided by backend (preferred over built-in icons) */
/** Optional react-icons component name or safe icon URL */
icon?: string
}