diff --git a/src/api/finance-api.ts b/src/api/finance-api.ts index c87be53..7c54f68 100644 --- a/src/api/finance-api.ts +++ b/src/api/finance-api.ts @@ -81,6 +81,8 @@ function normalizeRate(dto: FinanceRateConfigDto) { function normalizePayChannel(dto: FinancePayChannelDto) { return { code: dto.code, + currencyCodes: Array.isArray(dto.currency_codes) ? dto.currency_codes : [], + depositBanks: (dto.deposit_banks ?? []).map(normalizeWithdrawBank), name: dto.name, sort: Number.isFinite(dto.sort) ? dto.sort : 0, status: typeof dto.status === 'number' ? dto.status : 1, @@ -133,12 +135,22 @@ function normalizeDepositFields(dto: FinanceDepositFieldsDto | undefined = {}) { } } -function normalizeDepositConfig(dto: FinanceDepositConfigDto | undefined) { +function normalizeDepositConfig( + dto: FinanceDepositConfigDto | undefined, + fallbackPayChannels: FinancePayChannelDto[] = [], +) { + const payChannels = + dto?.pay_channels && dto.pay_channels.length > 0 + ? dto.pay_channels + : fallbackPayChannels + return { banks: (dto?.banks ?? []).map(normalizeWithdrawBank), + defaultChannelCode: dto?.default_channel_code ?? '', defaultChannelByCurrency: dto?.default_channel_by_currency ?? {}, fields: normalizeDepositFields(dto?.fields), methods: (dto?.methods ?? []).map(normalizeDepositMethod), + payChannels: payChannels.map(normalizePayChannel), } } @@ -176,7 +188,7 @@ function normalizeDepositWithdrawConfig( return { currencies: (dto.currencies ?? []).map(normalizeCurrency), defaultDepositChannelCode: dto.default_deposit_channel_code ?? '', - deposit: normalizeDepositConfig(dto.deposit), + deposit: normalizeDepositConfig(dto.deposit, dto.pay_channels ?? []), payChannels: (dto.pay_channels ?? []).map(normalizePayChannel), platformCoinLabel: dto.platform_coin_label, rates: (dto.rates ?? []).map(normalizeRate), @@ -204,6 +216,10 @@ function normalizeDepositTierItem( typeof dto.pay_channel_code === 'string' && dto.pay_channel_code.length > 0 ? dto.pay_channel_code : null + const channelCode = + typeof dto.channel_code === 'string' && dto.channel_code.length > 0 + ? dto.channel_code + : (payChannelCode ?? null) const sort = toFiniteNumber(dto.sort ?? dto.pay_amount ?? dto.amount) const status = toFiniteNumber(dto.status ?? 1) const channels = (dto.channels ?? []) @@ -217,6 +233,7 @@ function normalizeDepositTierItem( return { amount, bonusAmount, + channelCode, channels, coins, currency, diff --git a/src/constants/game.ts b/src/constants/game.ts index 46abab6..6045463 100644 --- a/src/constants/game.ts +++ b/src/constants/game.ts @@ -288,6 +288,7 @@ export const DEFAULT_WITHDRAW_CONFIG: DepositWithdrawConfig = { defaultDepositChannelCode: '', deposit: { banks: [], + defaultChannelCode: '', defaultChannelByCurrency: {}, fields: { depositBankAccountParam: 'deposit_bank_account', @@ -300,6 +301,7 @@ export const DEFAULT_WITHDRAW_CONFIG: DepositWithdrawConfig = { requirePaymentType: false, }, methods: [], + payChannels: [], }, payChannels: [], platformCoinLabel: '钻石', diff --git a/src/features/game/components/desktop/desktop-topup.tsx b/src/features/game/components/desktop/desktop-topup.tsx index 0f9dae6..ecd9022 100644 --- a/src/features/game/components/desktop/desktop-topup.tsx +++ b/src/features/game/components/desktop/desktop-topup.tsx @@ -1,90 +1,72 @@ import { useMutation } from '@tanstack/react-query' +import { CircleDollarSign, CreditCard, Landmark } from 'lucide-react' import { useRef } from 'react' import { useTranslation } from 'react-i18next' import { createDeposit } from '@/api' import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator' -import { DEFAULT_WITHDRAW_CONFIG } from '@/constants' -import { useDepositTierList } from '@/hooks/use-deposit-tier-list' -import { useDepositWithdrawConfig } from '@/hooks/use-deposit-withdraw-config' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { useTopupVm } from '@/hooks/use-topup-vm' import { notify } from '@/lib/notify' import { cn } from '@/lib/utils' -import type { DepositTierItem } from '@/type' +import type { DepositCreateRequestDto, DepositTierItem } from '@/type' const PANEL_CLASS = - 'rounded-md border border-[rgba(110,229,243,0.24)] bg-[linear-gradient(180deg,rgba(7,30,43,0.9),rgba(3,15,26,0.94))] shadow-[inset_0_0_calc(var(--design-unit)*14)_rgba(88,225,238,0.08),0_0_calc(var(--design-unit)*10)_rgba(32,163,186,0.12)]' + 'rounded-md border border-[rgba(126,234,244,0.22)] bg-[linear-gradient(180deg,rgba(4,18,29,0.98),rgba(1,8,16,0.99))] shadow-[inset_0_1px_0_rgba(255,255,255,0.06),0_0_calc(var(--design-unit)*16)_rgba(44,210,230,0.1)]' + +const FILTER_BAR_CLASS = + 'rounded-[calc(var(--design-unit)*8)] border border-[rgba(126,234,244,0.16)] bg-[linear-gradient(180deg,rgba(9,35,48,0.82),rgba(5,20,32,0.9))] p-design-10 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]' + +const FIELD_LABEL_CLASS = + 'mb-design-5 flex items-center gap-design-5 text-design-11 font-semibold uppercase tracking-[0.08em] text-[#8FE7EF]' + +const SELECT_TRIGGER_CLASS = + 'h-design-42 w-full rounded-[calc(var(--design-unit)*6)] border-[rgba(126,234,244,0.18)] bg-[rgba(2,15,26,0.78)] px-design-10 text-design-12 font-semibold text-[#E9FEFF] shadow-none hover:border-[rgba(170,247,255,0.48)] focus-visible:border-[#7CE3E8] focus-visible:ring-[#7CE3E8]/25 data-placeholder:text-[#6FAAB2]' function formatNumber(value: number) { return new Intl.NumberFormat('en-US').format(value) } +function getMethodIcon(gatewayCode: string) { + if (gatewayCode === 'ONLINE_DEBIT') { + return Landmark + } + + if (gatewayCode === 'E_WALLET') { + return CreditCard + } + + return CircleDollarSign +} + function DesktopTopup() { const { t } = useTranslation() - const depositWithdrawConfigQuery = useDepositWithdrawConfig() - const tierListQuery = useDepositTierList() - const depositWithdrawConfig = - depositWithdrawConfigQuery.data ?? DEFAULT_WITHDRAW_CONFIG - const isLoading = - tierListQuery.isLoading || depositWithdrawConfigQuery.isLoading - const isError = tierListQuery.isError || depositWithdrawConfigQuery.isError - const tiers = tierListQuery.data ?? [] + const vm = useTopupVm() const createDepositInFlightRef = useRef(false) const pendingPayWindowRef = useRef(null) const createDepositMutation = useMutation({ - mutationFn: ({ - channelCode, - depositBank, - depositBankAccount, - depositFromAddress, - paymentType, - tierId, - }: { - channelCode: string - depositBank?: string - depositBankAccount?: string - depositFromAddress?: string - paymentType: string - tierId: string - }) => - createDeposit({ - channel_code: channelCode, - deposit_bank: depositBank, - deposit_bank_account: depositBankAccount, - deposit_from_address: depositFromAddress, - idempotency_key: String(Date.now()), - payment_type: paymentType, - tier_id: tierId, - }), + mutationFn: (payload: DepositCreateRequestDto) => createDeposit(payload), }) + const getPaymentMethodLabel = (method: (typeof vm.methods)[number]) => + method.name.trim() || + t(`gameDesktop.topup.flow.paymentMethods.${method.gatewayCode}`, { + defaultValue: t('gameDesktop.topup.flow.methodTitle'), + }) + const handleCreateDeposit = async (tier: DepositTierItem) => { if (createDepositInFlightRef.current || createDepositMutation.isPending) { return } - const channelCode = - tier.payChannelCode ?? - tier.channels[0]?.code ?? - (tier.currency - ? depositWithdrawConfig.deposit.defaultChannelByCurrency[tier.currency] - : undefined) ?? - depositWithdrawConfig.defaultDepositChannelCode - const selectedPaymentMethod = - depositWithdrawConfig.deposit.methods.find( - (method) => method.currencyCode === tier.currency, - ) ?? depositWithdrawConfig.deposit.methods[0] - const paymentType = selectedPaymentMethod?.code ?? '' - const depositBank = selectedPaymentMethod?.requiresBank - ? depositWithdrawConfig.deposit.banks.find( - (bank) => bank.currencyCode === tier.currency, - )?.code - : undefined + const payload = vm.getCreateDepositPayload(tier) - if (!channelCode) { - notify.error(t('commonUi.toast.configError')) - return - } - - if (!paymentType) { + if (!payload) { notify.error(t('commonUi.toast.configError')) return } @@ -102,12 +84,7 @@ function DesktopTopup() { pendingPayWindowRef.current = payWindow try { - const result = await createDepositMutation.mutateAsync({ - channelCode, - depositBank, - paymentType, - tierId: tier.id, - }) + const result = await createDepositMutation.mutateAsync(payload) const payUrl = result.pay_url.trim() if (!payUrl) { @@ -139,87 +116,247 @@ function DesktopTopup() {
-
-
- {t('gameDesktop.topup.tier.title')} -
-
- - {isLoading ? ( + {vm.isLoading ? ( - ) : isError ? ( -
+ ) : vm.isError ? ( +
{t('gameDesktop.topup.tier.failed')}
- ) : tiers.length === 0 ? ( -
- {t('gameDesktop.topup.tier.empty')} -
) : ( -
- {tiers.map((tier) => ( - - ))} +
+ {vm.filteredTiers.length} +
+
+ + {vm.filteredTiers.length === 0 ? ( +
+ {t('gameDesktop.topup.tier.empty')} +
+ ) : ( +
+ {vm.filteredTiers.map((tier) => ( + + ))} +
+ )} +
)} diff --git a/src/features/game/components/mobile/mobile-topup.tsx b/src/features/game/components/mobile/mobile-topup.tsx index f4e3859..270a11b 100644 --- a/src/features/game/components/mobile/mobile-topup.tsx +++ b/src/features/game/components/mobile/mobile-topup.tsx @@ -1,90 +1,72 @@ import { useMutation } from '@tanstack/react-query' +import { CircleDollarSign, CreditCard, Landmark } from 'lucide-react' import { useRef } from 'react' import { useTranslation } from 'react-i18next' import { createDeposit } from '@/api' import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator' -import { DEFAULT_WITHDRAW_CONFIG } from '@/constants' -import { useDepositTierList } from '@/hooks/use-deposit-tier-list' -import { useDepositWithdrawConfig } from '@/hooks/use-deposit-withdraw-config' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { useTopupVm } from '@/hooks/use-topup-vm' import { notify } from '@/lib/notify' import { cn } from '@/lib/utils' -import type { DepositTierItem } from '@/type' +import type { DepositCreateRequestDto, DepositTierItem } from '@/type' const PANEL_CLASS = - 'rounded-md border border-[rgba(110,229,243,0.24)] bg-[linear-gradient(180deg,rgba(7,30,43,0.9),rgba(3,15,26,0.94))] shadow-[inset_0_0_calc(var(--design-unit)*12)_rgba(88,225,238,0.08)]' + 'rounded-md border border-[rgba(126,234,244,0.22)] bg-[linear-gradient(180deg,rgba(4,18,29,0.98),rgba(1,8,16,0.99))] shadow-[inset_0_1px_0_rgba(255,255,255,0.06),0_0_calc(var(--design-unit)*12)_rgba(44,210,230,0.08)]' + +const FILTER_BAR_CLASS = + 'rounded-[calc(var(--design-unit)*7)] border border-[rgba(126,234,244,0.16)] bg-[linear-gradient(180deg,rgba(9,35,48,0.82),rgba(5,20,32,0.9))] p-design-7 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]' + +const FIELD_LABEL_CLASS = + 'mb-design-3 flex items-center gap-design-4 text-design-9 font-semibold uppercase tracking-[0.07em] text-[#8FE7EF]' + +const SELECT_TRIGGER_CLASS = + 'h-design-34 w-full rounded-[calc(var(--design-unit)*5)] border-[rgba(126,234,244,0.18)] bg-[rgba(2,15,26,0.78)] px-design-8 text-design-10 font-semibold text-[#E9FEFF] shadow-none focus-visible:border-[#7CE3E8] focus-visible:ring-[#7CE3E8]/25 data-placeholder:text-[#6FAAB2]' function formatNumber(value: number) { return new Intl.NumberFormat('en-US').format(value) } +function getMethodIcon(gatewayCode: string) { + if (gatewayCode === 'ONLINE_DEBIT') { + return Landmark + } + + if (gatewayCode === 'E_WALLET') { + return CreditCard + } + + return CircleDollarSign +} + function MobileTopup() { const { t } = useTranslation() - const depositWithdrawConfigQuery = useDepositWithdrawConfig() - const tierListQuery = useDepositTierList() - const depositWithdrawConfig = - depositWithdrawConfigQuery.data ?? DEFAULT_WITHDRAW_CONFIG - const isLoading = - tierListQuery.isLoading || depositWithdrawConfigQuery.isLoading - const isError = tierListQuery.isError || depositWithdrawConfigQuery.isError - const tiers = tierListQuery.data ?? [] + const vm = useTopupVm() const createDepositInFlightRef = useRef(false) const pendingPayWindowRef = useRef(null) const createDepositMutation = useMutation({ - mutationFn: ({ - channelCode, - depositBank, - depositBankAccount, - depositFromAddress, - paymentType, - tierId, - }: { - channelCode: string - depositBank?: string - depositBankAccount?: string - depositFromAddress?: string - paymentType: string - tierId: string - }) => - createDeposit({ - channel_code: channelCode, - deposit_bank: depositBank, - deposit_bank_account: depositBankAccount, - deposit_from_address: depositFromAddress, - idempotency_key: String(Date.now()), - payment_type: paymentType, - tier_id: tierId, - }), + mutationFn: (payload: DepositCreateRequestDto) => createDeposit(payload), }) + const getPaymentMethodLabel = (method: (typeof vm.methods)[number]) => + method.name.trim() || + t(`gameDesktop.topup.flow.paymentMethods.${method.gatewayCode}`, { + defaultValue: t('gameDesktop.topup.flow.methodTitle'), + }) + const handleCreateDeposit = async (tier: DepositTierItem) => { if (createDepositInFlightRef.current || createDepositMutation.isPending) { return } - const channelCode = - tier.payChannelCode ?? - tier.channels[0]?.code ?? - (tier.currency - ? depositWithdrawConfig.deposit.defaultChannelByCurrency[tier.currency] - : undefined) ?? - depositWithdrawConfig.defaultDepositChannelCode - const selectedPaymentMethod = - depositWithdrawConfig.deposit.methods.find( - (method) => method.currencyCode === tier.currency, - ) ?? depositWithdrawConfig.deposit.methods[0] - const paymentType = selectedPaymentMethod?.code ?? '' - const depositBank = selectedPaymentMethod?.requiresBank - ? depositWithdrawConfig.deposit.banks.find( - (bank) => bank.currencyCode === tier.currency, - )?.code - : undefined + const payload = vm.getCreateDepositPayload(tier) - if (!channelCode) { - notify.error(t('commonUi.toast.configError')) - return - } - - if (!paymentType) { + if (!payload) { notify.error(t('commonUi.toast.configError')) return } @@ -102,12 +84,7 @@ function MobileTopup() { pendingPayWindowRef.current = payWindow try { - const result = await createDepositMutation.mutateAsync({ - channelCode, - depositBank, - paymentType, - tierId: tier.id, - }) + const result = await createDepositMutation.mutateAsync(payload) const payUrl = result.pay_url.trim() if (!payUrl) { @@ -139,87 +116,247 @@ function MobileTopup() {
-
-
- {t('gameDesktop.topup.tier.title')} -
-
- - {isLoading ? ( + {vm.isLoading ? ( - ) : isError ? ( + ) : vm.isError ? (
{t('gameDesktop.topup.tier.failed')}
- ) : tiers.length === 0 ? ( -
- {t('gameDesktop.topup.tier.empty')} -
) : ( -
- {tiers.map((tier) => ( - - ))} +
+ {vm.filteredTiers.length} +
+
+ + {vm.filteredTiers.length === 0 ? ( +
+ {t('gameDesktop.topup.tier.empty')} +
+ ) : ( +
+ {vm.filteredTiers.map((tier) => ( + + ))} +
+ )} +
)} diff --git a/src/hooks/use-topup-vm.ts b/src/hooks/use-topup-vm.ts index 6a885ed..25ba81a 100644 --- a/src/hooks/use-topup-vm.ts +++ b/src/hooks/use-topup-vm.ts @@ -1,3 +1,192 @@ -export function useTopupVm() { - return {} +import { useCallback, useEffect, useMemo, useState } from 'react' +import { DEFAULT_WITHDRAW_CONFIG } from '@/constants' +import { useDepositTierList } from '@/hooks/use-deposit-tier-list' +import { useDepositWithdrawConfig } from '@/hooks/use-deposit-withdraw-config' +import type { + DepositCreateRequestDto, + DepositTierItem, + DepositWithdrawConfig, + FinancePayChannel, +} from '@/type' + +function getTierChannelCode(tier: DepositTierItem) { + return tier.channelCode ?? tier.channels[0]?.code ?? tier.payChannelCode ?? '' +} + +function getInitialChannelCode(config: DepositWithdrawConfig) { + return ( + config.deposit.payChannels[0]?.code ?? + config.deposit.defaultChannelCode ?? + config.defaultDepositChannelCode ?? + '' + ) +} + +function getChannelCurrencyCodes(channel: FinancePayChannel | null) { + return channel?.currencyCodes ?? [] +} + +export function useTopupVm() { + const depositWithdrawConfigQuery = useDepositWithdrawConfig() + const tierListQuery = useDepositTierList() + const config = depositWithdrawConfigQuery.data ?? DEFAULT_WITHDRAW_CONFIG + const payChannels = useMemo( + () => + [...config.deposit.payChannels] + .filter((channel) => channel.status === 1) + .sort((left, right) => left.sort - right.sort), + [config.deposit.payChannels], + ) + const [selectedPayChannelCode, setSelectedPayChannelCode] = useState(() => + getInitialChannelCode(config), + ) + const selectedPayChannel = useMemo( + () => + payChannels.find((channel) => channel.code === selectedPayChannelCode) ?? + payChannels[0] ?? + null, + [payChannels, selectedPayChannelCode], + ) + const currencyCodes = useMemo( + () => getChannelCurrencyCodes(selectedPayChannel), + [selectedPayChannel], + ) + const [selectedCurrencyCode, setSelectedCurrencyCode] = useState( + () => currencyCodes[0] ?? '', + ) + const methods = useMemo( + () => + config.deposit.methods.filter( + (method) => method.currencyCode === selectedCurrencyCode, + ), + [config.deposit.methods, selectedCurrencyCode], + ) + const [selectedPaymentMethodCode, setSelectedPaymentMethodCode] = useState( + () => methods[0]?.code ?? '', + ) + const selectedPaymentMethod = useMemo( + () => + methods.find((method) => method.code === selectedPaymentMethodCode) ?? + methods[0] ?? + null, + [methods, selectedPaymentMethodCode], + ) + const banks = useMemo( + () => + (selectedPayChannel?.depositBanks ?? []).filter( + (bank) => bank.currencyCode === selectedCurrencyCode, + ), + [selectedCurrencyCode, selectedPayChannel?.depositBanks], + ) + const [selectedBankCode, setSelectedBankCode] = useState( + () => banks[0]?.code ?? '', + ) + const selectedBank = useMemo( + () => + banks.find((bank) => bank.code === selectedBankCode) ?? banks[0] ?? null, + [banks, selectedBankCode], + ) + const tiers = tierListQuery.data ?? [] + const filteredTiers = useMemo( + () => + tiers.filter((tier) => { + const tierChannelCode = getTierChannelCode(tier) + const matchesChannel = + selectedPayChannel === null || + tierChannelCode === selectedPayChannel.code + const matchesCurrency = + !selectedCurrencyCode || tier.currency === selectedCurrencyCode + + return matchesChannel && matchesCurrency + }), + [selectedCurrencyCode, selectedPayChannel, tiers], + ) + + useEffect(() => { + const nextPayChannelCode = getInitialChannelCode(config) + + if ( + selectedPayChannelCode && + payChannels.some((channel) => channel.code === selectedPayChannelCode) + ) { + return + } + + setSelectedPayChannelCode(nextPayChannelCode) + }, [config, payChannels, selectedPayChannelCode]) + + useEffect(() => { + const nextCurrencyCode = currencyCodes[0] ?? '' + + if (selectedCurrencyCode && currencyCodes.includes(selectedCurrencyCode)) { + return + } + + setSelectedCurrencyCode(nextCurrencyCode) + }, [currencyCodes, selectedCurrencyCode]) + + useEffect(() => { + const nextPaymentMethodCode = methods[0]?.code ?? '' + + if ( + selectedPaymentMethodCode && + methods.some((method) => method.code === selectedPaymentMethodCode) + ) { + return + } + + setSelectedPaymentMethodCode(nextPaymentMethodCode) + }, [methods, selectedPaymentMethodCode]) + + useEffect(() => { + const nextBankCode = banks[0]?.code ?? '' + + if ( + selectedBankCode && + banks.some((bank) => bank.code === selectedBankCode) + ) { + return + } + + setSelectedBankCode(nextBankCode) + }, [banks, selectedBankCode]) + + const getCreateDepositPayload = useCallback( + (tier: DepositTierItem): DepositCreateRequestDto | null => { + if (!selectedPayChannel || !selectedPaymentMethod) { + return null + } + + return { + channel_code: selectedPayChannel.code, + deposit_bank: selectedBank?.code, + idempotency_key: String(Date.now()), + payment_type: selectedPaymentMethod.gatewayCode, + tier_id: tier.id, + } + }, + [selectedBank?.code, selectedPayChannel, selectedPaymentMethod], + ) + + return { + banks, + config, + filteredTiers, + getCreateDepositPayload, + isError: tierListQuery.isError || depositWithdrawConfigQuery.isError, + isLoading: tierListQuery.isLoading || depositWithdrawConfigQuery.isLoading, + methods, + payChannels, + selectedBank, + selectedBankCode, + selectedCurrencyCode, + selectedPayChannel, + selectedPayChannelCode, + selectedPaymentMethod, + selectedPaymentMethodCode, + setSelectedBankCode, + setSelectedCurrencyCode, + setSelectedPayChannelCode, + setSelectedPaymentMethodCode, + } } diff --git a/src/hooks/use-withdraw-vm.ts b/src/hooks/use-withdraw-vm.ts index 59781ab..b322728 100644 --- a/src/hooks/use-withdraw-vm.ts +++ b/src/hooks/use-withdraw-vm.ts @@ -246,11 +246,7 @@ export function useWithdrawVm() { exchangeRateLabel: t('gameDesktop.withdraw.preview.exchangeRate', { currency: selectedCurrency.code, }), - exchangeRateValue: t('gameDesktop.withdraw.preview.exchangeRateValue', { - coins: formatNumber(locale, selectedRate), - currency: selectedCurrency.code, - platformCoinLabel: config.platformCoinLabel, - }), + exchangeRateValue: `1:${formatNumber(locale, selectedRate)}`, convertibleLabel: t('gameDesktop.withdraw.preview.convertible', { currency: selectedCurrency.code, }), @@ -261,7 +257,6 @@ export function useWithdrawVm() { }), [ amount, - config.platformCoinLabel, locale, selectedCurrency.code, selectedCurrency.label, diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 3e33263..cb28a86 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -581,6 +581,19 @@ export default { channelLabel: TEXT_PAYMENT_CHANNEL, rateHint: 'Exchange rates are for reference only. The final amount follows the top-up-time rate.', + flow: { + channelTitle: 'Select Payment Channel', + currencyTitle: 'Currency', + methodTitle: 'Top-up Method', + bankTitle: 'Bank', + bankPlaceholder: 'Select a bank', + noBank: 'No banks available', + paymentMethods: { + QR: 'QR Code', + ONLINE_DEBIT: 'Online Banking', + E_WALLET: 'E-wallet', + }, + }, tier: { bonus: 'Bonus', coins: 'Credited Coins', diff --git a/src/locales/id-ID.ts b/src/locales/id-ID.ts index 6116299..c5c6573 100644 --- a/src/locales/id-ID.ts +++ b/src/locales/id-ID.ts @@ -581,6 +581,19 @@ export default { channelLabel: TEXT_PAYMENT_CHANNEL, rateHint: 'Kurs hanya sebagai referensi. Jumlah akhir mengikuti kurs saat isi ulang.', + flow: { + channelTitle: 'Pilih Saluran Pembayaran', + currencyTitle: 'Mata Uang', + methodTitle: 'Metode Isi Ulang', + bankTitle: 'Bank', + bankPlaceholder: 'Pilih bank', + noBank: 'Tidak ada bank tersedia', + paymentMethods: { + QR: 'Kode QR', + ONLINE_DEBIT: 'Perbankan Online', + E_WALLET: 'Dompet Elektronik', + }, + }, tier: { bonus: 'Bonus', coins: 'Koin Masuk', diff --git a/src/locales/ms-MY.ts b/src/locales/ms-MY.ts index 07d9f1b..c297084 100644 --- a/src/locales/ms-MY.ts +++ b/src/locales/ms-MY.ts @@ -586,6 +586,19 @@ export default { channelLabel: TEXT_PAYMENT_CHANNEL, rateHint: 'Kadar pertukaran hanya untuk rujukan. Jumlah akhir tertakluk kepada kadar semasa tambah nilai.', + flow: { + channelTitle: 'Pilih Saluran Pembayaran', + currencyTitle: 'Mata Wang', + methodTitle: 'Kaedah Tambah Nilai', + bankTitle: 'Bank', + bankPlaceholder: 'Pilih bank', + noBank: 'Tiada bank tersedia', + paymentMethods: { + QR: 'Kod QR', + ONLINE_DEBIT: 'Perbankan Dalam Talian', + E_WALLET: 'Dompet Elektronik', + }, + }, tier: { bonus: 'Bonus', coins: 'Syiling Diterima', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index e17326e..6fe1a3a 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -565,6 +565,19 @@ export default { currencyLabel: TEXT_CURRENCY_TYPE, channelLabel: TEXT_PAYMENT_CHANNEL, rateHint: '汇率为参考价格,实际以充值时为准。', + flow: { + channelTitle: '请选择支付渠道', + currencyTitle: '选择币种', + methodTitle: '充值方式', + bankTitle: '选择银行', + bankPlaceholder: '请选择银行', + noBank: '暂无可选银行', + paymentMethods: { + QR: '扫码', + ONLINE_DEBIT: '网银', + E_WALLET: '电子钱包', + }, + }, tier: { bonus: '赠送', coins: '到账钻石', diff --git a/src/type/api.type.ts b/src/type/api.type.ts index 68cff1f..1b10d2c 100644 --- a/src/type/api.type.ts +++ b/src/type/api.type.ts @@ -26,6 +26,8 @@ export interface FinanceRateConfigDto { export interface FinancePayChannelDto { code: string + currency_codes?: string[] + deposit_banks?: FinanceWithdrawBankDto[] name: string sort: number status?: number @@ -68,8 +70,10 @@ export interface FinanceDepositFieldsDto { export interface FinanceDepositConfigDto { banks?: FinanceWithdrawBankDto[] default_channel_by_currency?: Record + default_channel_code?: string fields?: FinanceDepositFieldsDto methods?: FinanceDepositMethodDto[] + pay_channels?: FinancePayChannelDto[] } export interface FinanceWithdrawFieldsDto { @@ -110,6 +114,7 @@ export interface DepositTierItemDto { amount?: number | string bonus_amount?: number | string bonus_coins?: number | string + channel_code?: string channels?: Array<{ code?: string; name?: string; sort?: number | string }> coins?: number | string currency?: string @@ -143,6 +148,8 @@ export interface FinanceRateConfig { export interface FinancePayChannel { code: string + currencyCodes: string[] + depositBanks: FinanceWithdrawBank[] name: string sort: number status: number @@ -182,9 +189,11 @@ export interface FinanceDepositFields { export interface FinanceDepositConfig { banks: FinanceWithdrawBank[] + defaultChannelCode: string defaultChannelByCurrency: Record fields: FinanceDepositFields methods: FinanceDepositMethod[] + payChannels: FinancePayChannel[] } export interface FinanceWithdrawFields { @@ -224,6 +233,7 @@ export interface DepositWithdrawConfig { export interface DepositTierItem { amount: number bonusAmount: number + channelCode: string | null channels: Array<{ code: string; name: string; sort: number }> coins: number currency: string | null