import { API_SUCCESS_CODE, DEFAULT_LIST_PAGE_SIZE, FINANCE_API_ENDPOINTS, } from '@/constants' import { api } from '@/lib/api/api-client' import { ApiError } from '@/lib/api/api-error' import type { ApiResponse, DepositCreateRequestDto, DepositCreateResponseDto, DepositTierItem, DepositTierItemDto, DepositWithdrawConfig, DepositWithdrawConfigDto, FinanceCurrencyConfigDto, FinanceDepositConfigDto, FinanceDepositFieldsDto, FinanceDepositMethodDto, FinanceOrderItemDto, FinanceOrderList, FinanceOrderListDto, FinancePayChannelDto, FinanceRateConfigDto, FinanceWithdrawBankDto, FinanceWithdrawConfigDto, WalletRecordItemDto, WalletRecordList, WalletRecordListDto, WalletRecordType, WithdrawCreateRequestDto, WithdrawCreateResponseDto, } from '@/type' function unwrapFinanceEnvelope( response: ApiResponse, fallbackMessage = 'Finance request failed', ) { if (response.code === API_SUCCESS_CODE) { return response.data } const responseMessage = typeof response.message === 'string' && response.message.length > 0 ? response.message : typeof response.msg === 'string' && response.msg.length > 0 ? response.msg : fallbackMessage throw new ApiError({ data: response, message: responseMessage, }) } function toFiniteNumber(value: string | number | null | undefined) { const numericValue = Number(value) return Number.isFinite(numericValue) ? numericValue : 0 } function normalizeCurrency(dto: FinanceCurrencyConfigDto) { return { code: dto.code, depositCoinsPerFiat: dto.deposit_coins_per_fiat, depositCoinsPerFiatValue: toFiniteNumber(dto.deposit_coins_per_fiat), label: dto.label, withdrawCoinsPerFiat: dto.withdraw_coins_per_fiat, withdrawCoinsPerFiatValue: toFiniteNumber(dto.withdraw_coins_per_fiat), } } function normalizeRate(dto: FinanceRateConfigDto) { return { currency: dto.currency, diamondsPerFiatUnit: dto.diamonds_per_fiat_unit, diamondsPerFiatUnitValue: toFiniteNumber(dto.diamonds_per_fiat_unit), } } 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, tierIds: Array.isArray(dto.tier_ids) ? dto.tier_ids : [], } } function normalizeWithdrawBank(dto: FinanceWithdrawBankDto, index: number) { const code = dto.code ?? dto.id ?? dto.name ?? `bank-${index + 1}` const label = dto.label ?? dto.name ?? code return { code, currencyCode: dto.currency_code ?? null, gatewayCode: dto.gateway_code ?? null, label, sort: typeof dto.sort === 'number' && Number.isFinite(dto.sort) ? dto.sort : index, status: typeof dto.status === 'number' ? dto.status : 1, } } function normalizeDepositMethod(dto: FinanceDepositMethodDto) { return { code: dto.code, currencyCode: dto.currency_code, gatewayCode: dto.gateway_code, name: dto.name, requiresBank: dto.requires_bank, requiresBankAccount: dto.requires_bank_account, requiresDepositorName: dto.requires_depositor_name, requiresFromAddress: dto.requires_from_address, } } function normalizeDepositFields(dto: FinanceDepositFieldsDto | undefined = {}) { return { depositBankAccountParam: dto.deposit_bank_account_param ?? 'deposit_bank_account', depositBankParam: dto.deposit_bank_param ?? 'deposit_bank', depositFromAddressParam: dto.deposit_from_address_param ?? 'deposit_from_address', depositNameServerSide: dto.deposit_name_server_side ?? false, paymentTypeParam: dto.payment_type_param ?? 'payment_type', requireChannelCode: dto.require_channel_code ?? true, requireIdempotencyKey: dto.require_idempotency_key ?? true, requirePaymentType: dto.require_payment_type ?? false, } } 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), } } function normalizeWithdrawFields(dto: FinanceWithdrawConfigDto['fields'] = {}) { return { receiveTypeBankOnly: dto.receive_type_bank_only ?? true, requireBankBranch: dto.require_bank_branch ?? false, requireBankCode: dto.require_bank_code ?? true, requireChannelCode: dto.require_channel_code ?? true, requireReceiveAccount: dto.require_receive_account ?? true, requireReceiverEmail: dto.require_receiver_email ?? true, requireReceiverMobile: dto.require_receiver_mobile ?? true, requireReceiverName: dto.require_receiver_name ?? true, } } function normalizeWithdrawConfig(dto: FinanceWithdrawConfigDto) { return { banks: (dto.banks ?? []).map(normalizeWithdrawBank), feeNote: dto.fee_note, fields: normalizeWithdrawFields(dto.fields), minBank: dto.min_bank, minEwallet: dto.min_ewallet, payChannels: (dto.pay_channels ?? []).map(normalizePayChannel), processingNote: dto.processing_note, rateHint: dto.rate_hint, rateMode: dto.rate_mode, reviewThresholdCoin: dto.review_threshold_coin ?? '0', } } function normalizeDepositWithdrawConfig( dto: DepositWithdrawConfigDto, ): DepositWithdrawConfig { return { currencies: (dto.currencies ?? []).map(normalizeCurrency), defaultDepositChannelCode: dto.default_deposit_channel_code ?? '', deposit: normalizeDepositConfig(dto.deposit, dto.pay_channels ?? []), payChannels: (dto.pay_channels ?? []).map(normalizePayChannel), platformCoinLabel: dto.platform_coin_label, rates: (dto.rates ?? []).map(normalizeRate), withdraw: normalizeWithdrawConfig(dto.withdraw), } } function normalizeDepositTierItem( dto: DepositTierItemDto, index: number, ): DepositTierItem { const id = String(dto.id ?? dto.tier_id ?? `tier-${index + 1}`) const amount = toFiniteNumber(dto.amount) const bonusAmount = toFiniteNumber(dto.bonus_amount ?? dto.bonus_coins) const payAmount = toFiniteNumber(dto.pay_amount ?? dto.amount) const totalAmount = toFiniteNumber( dto.total_amount ?? dto.coins ?? amount + bonusAmount, ) const coins = totalAmount const currency = typeof dto.currency === 'string' && dto.currency.length > 0 ? dto.currency : null const payChannelCode = 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 ?? []) .map((channel, channelIndex) => ({ code: channel.code ?? `channel-${channelIndex + 1}`, name: channel.name ?? channel.code ?? '--', sort: toFiniteNumber(channel.sort ?? channelIndex), })) .sort((left, right) => left.sort - right.sort) return { amount, bonusAmount, channelCode, channels, coins, currency, desc: dto.desc ?? '', id, name: typeof dto.name === 'string' && dto.name.length > 0 ? dto.name : `${amount}`, payAmount, payChannelCode, sort, status, tierKey: typeof dto.tier_key === 'string' && dto.tier_key.length > 0 ? dto.tier_key : null, totalAmount, title: typeof dto.title === 'string' && dto.title.length > 0 ? dto.title : typeof dto.name === 'string' && dto.name.length > 0 ? dto.name : `${amount}`, } } function normalizeFinanceOrderItem(dto: FinanceOrderItemDto) { return { amount: String(dto.amount ?? ''), bonusAmount: String(dto.bonus_amount ?? ''), orderNo: dto.order_no, status: String(dto.status ?? ''), } } function normalizeFinanceOrderList(dto: FinanceOrderListDto): FinanceOrderList { return { list: (dto.list ?? []).map(normalizeFinanceOrderItem), pagination: { page: dto.pagination?.page ?? 1, page_size: dto.pagination?.page_size ?? DEFAULT_LIST_PAGE_SIZE, total: dto.pagination?.total ?? 0, }, } } function stringifyNullableValue(value: unknown) { return value === null || value === undefined ? '' : String(value) } function normalizeWalletRecordItem(dto: WalletRecordItemDto, index: number) { const createdAt = dto.created_at ?? dto.create_time ?? dto.time ?? null const id = dto.id ?? dto.record_id ?? dto.wallet_record_id ?? dto.order_no ?? `${createdAt ?? 'wallet-record'}-${index + 1}` return { amount: stringifyNullableValue( dto.amount ?? dto.change_amount ?? dto.coin ?? '', ), balanceAfter: stringifyNullableValue( dto.balance_after ?? dto.after_balance ?? dto.balance ?? '', ), balanceBefore: stringifyNullableValue( dto.balance_before ?? dto.before_balance ?? '', ), createdAt, id: String(id), remark: stringifyNullableValue(dto.remark ?? dto.description ?? dto.memo), type: stringifyNullableValue( dto.type ?? dto.change_type ?? dto.biz_type ?? dto.scene, ), } } function normalizeWalletRecordList(dto: WalletRecordListDto): WalletRecordList { return { list: (dto.list ?? []).map(normalizeWalletRecordItem), pagination: { page: dto.pagination?.page ?? dto.page ?? 1, page_size: dto.pagination?.page_size ?? dto.page_size ?? DEFAULT_LIST_PAGE_SIZE, total: dto.pagination?.total ?? dto.total ?? 0, }, } } export async function getDepositWithdrawConfig() { const response = await api.post( FINANCE_API_ENDPOINTS.depositWithdrawConfig, ) const dto = unwrapFinanceEnvelope( response as ApiResponse, 'Failed to load deposit and withdrawal config', ) return normalizeDepositWithdrawConfig(dto) } export async function getDepositTierList() { const response = await api.post< | DepositTierItemDto[] | { items?: DepositTierItemDto[]; list?: DepositTierItemDto[] }, undefined >(FINANCE_API_ENDPOINTS.depositTierList) const dto = unwrapFinanceEnvelope( response as ApiResponse< | DepositTierItemDto[] | { items?: DepositTierItemDto[]; list?: DepositTierItemDto[] } >, 'Failed to load deposit tier list', ) const tierItems = Array.isArray(dto) ? dto : (dto?.list ?? dto?.items ?? []) return tierItems .map(normalizeDepositTierItem) .filter((item) => item.status === 1) .sort((left, right) => left.sort - right.sort) } export async function createDeposit(payload: DepositCreateRequestDto) { const response = await api.post< DepositCreateResponseDto, DepositCreateRequestDto >(FINANCE_API_ENDPOINTS.depositCreate, { json: payload, }) const dto = unwrapFinanceEnvelope( response as ApiResponse, 'Failed to create deposit', ) return dto } export async function getDepositOrderList(params?: { page?: number pageSize?: number }) { const response = await api.get( FINANCE_API_ENDPOINTS.depositList, { searchParams: { page: String(params?.page ?? 1), page_size: String(params?.pageSize ?? DEFAULT_LIST_PAGE_SIZE), }, }, ) const dto = unwrapFinanceEnvelope( response as ApiResponse, 'Failed to load deposit order list', ) return normalizeFinanceOrderList(dto) } export async function getWithdrawOrderList(params?: { page?: number pageSize?: number }) { const response = await api.get( FINANCE_API_ENDPOINTS.withdrawList, { searchParams: { page: String(params?.page ?? 1), page_size: String(params?.pageSize ?? DEFAULT_LIST_PAGE_SIZE), }, }, ) const dto = unwrapFinanceEnvelope( response as ApiResponse, 'Failed to load withdraw order list', ) return normalizeFinanceOrderList(dto) } export async function getWalletRecordList(params?: { page?: number pageSize?: number type?: WalletRecordType }) { const response = await api.get( FINANCE_API_ENDPOINTS.walletRecordList, { searchParams: { page: String(params?.page ?? 1), page_size: String(params?.pageSize ?? DEFAULT_LIST_PAGE_SIZE), type: params?.type ?? 'payout', }, }, ) const dto = unwrapFinanceEnvelope( response as ApiResponse, 'Failed to load wallet record list', ) return normalizeWalletRecordList(dto) } export async function createWithdraw(payload: WithdrawCreateRequestDto) { const response = await api.post< WithdrawCreateResponseDto, WithdrawCreateRequestDto >(FINANCE_API_ENDPOINTS.withdrawCreate, { json: payload, }) const dto = unwrapFinanceEnvelope( response as ApiResponse, 'Failed to create withdraw', ) return dto }