/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { type ReactNode } from 'react' import i18next from 'i18next' import { CreditCard, Landmark } from 'lucide-react' import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si' import { PAYMENT_TYPES, PAYMENT_ICON_COLORS } from '../constants' // ============================================================================ // UI Helper Functions // ============================================================================ const HAS_LOCATION = typeof globalThis !== 'undefined' && 'location' in globalThis /** * Resolves a backend-provided image URL to http(s) only. Rejects javascript:, * data:, blob:, file:, and URLs with userinfo, which are unsafe in . */ function normalizeHttpIconUrl(raw: string | undefined | null): string | null { if (!raw) return null const s = raw.trim() if (!s) return null let url: URL try { url = HAS_LOCATION ? new URL(s, (globalThis as { location: Location }).location.href) : new URL(s) } catch { return null } if (url.protocol !== 'http:' && url.protocol !== 'https:') { return null } if (url.username || url.password) { return null } return url.toString() } /** * Get payment method icon component * * When iconUrl is provided, render an with that URL so custom * gateway logos can be configured per-method. */ export function getPaymentIcon( paymentType: string | undefined, className: string = 'h-4 w-4', iconUrl?: string, altName?: string ): ReactNode { const safeIconUrl = normalizeHttpIconUrl(iconUrl) if (safeIconUrl) { return ( {altName ) } if (!paymentType) { return } switch (paymentType) { case PAYMENT_TYPES.ALIPAY: return ( ) case PAYMENT_TYPES.WECHAT: return ( ) case PAYMENT_TYPES.STRIPE: return ( ) case PAYMENT_TYPES.CREEM: return ( ) case PAYMENT_TYPES.WAFFO: return ( ) case PAYMENT_TYPES.WAFFO_PANCAKE: // The W glyph fills only ~40% of its viewBox vertically (wide and // short letterform); scale(2) brings its rendered height in line // with Stripe's S and Creem's Landmark. return ( {i18next.t('Waffo')} {i18next.t('Waffo')} ) default: return } }