import type { BankAccountOwnerType, BankAccountResponseDto, CreateBankAccountDto, WithdrawalResponseDto } from '@/api/generated/models' import { createServiceError, errorResult, handleServiceError, type ServiceCallOptions, type ServiceResult, shouldBubbleErrorToParent, successResult, } from '@/services/errorHandler' import { unwrapApiData } from '@/services/apiResponse' import { getFinancial } from '@/api/generated/financial/financial' import { parseRemittanceList, unwrapApiPayload } from '@/helpers/listResponse' import { ANALYTICS_EVENTS, trackAnalyticsEventOnce } from '@/lib/analytics' import { texts } from '@/texts' // docs/workflows/fa/settlement-withdrawal.md — Path A (self-service wallet // withdrawal). Distinct from admin settlement (Path B), which never // touches `wallets.balance` — out of scope here. export type { BankAccountOwnerType } export type BankAccount = BankAccountResponseDto & { verificationStatus?: 'pending_review' | 'approved' | 'rejected' rejectionReason?: string | null nationalCode?: string | null } export type CreateBankAccountPayload = CreateBankAccountDto & { nationalCode?: string } export type WithdrawalRequest = WithdrawalResponseDto const financialApi = getFinancial() export const LIST_BANK_ACCOUNTS = async (options?: ServiceCallOptions): Promise> => { try { const res = await financialApi.bankAccountsControllerListMine() const payload = unwrapApiPayload(res.data) const items = Array.isArray(payload) ? payload : Array.isArray((payload as { items?: unknown[] }).items) ? (payload as { items: unknown[] }).items : [] return successResult(items as BankAccount[]) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } } export const CREATE_BANK_ACCOUNT = async ( payload: CreateBankAccountPayload, options?: ServiceCallOptions ): Promise> => { try { const res = await financialApi.bankAccountsControllerCreate(payload) const body = res.data if (!body.success) { throw createServiceError(body.message || texts.wallet.bankAccountCreateFailed) } const result = unwrapApiData(body) if (!result?.id) { throw createServiceError(texts.common.invalidServerResponse) } return successResult(result) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } } export const LIST_WITHDRAWAL_REQUESTS = async (options?: ServiceCallOptions): Promise> => { try { const res = await financialApi.withdrawalRequestsControllerListMine({ page: 1, pageSize: 30, sort: '-createdAt' }) const parsed = parseRemittanceList(res.data, 'items') return successResult({ items: parsed.items }) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } } // amount is in Toman (platform-wide convention — see payment.md's note on // the matching CreatePaymentDto Swagger typo; CreateWithdrawalRequestDto // has the same "Rials" typo in its Swagger annotation). export const CREATE_WITHDRAWAL_REQUEST = async ( bankAccountId: string, amount: number, options?: ServiceCallOptions ): Promise> => { try { const res = await financialApi.withdrawalRequestsControllerCreate({ bankAccountId, amount, }) const payload = res.data if (!payload.success) { throw createServiceError(payload.message || texts.wallet.withdrawalCreateFailed) } const result = unwrapApiData(payload) if (!result?.id) { throw createServiceError(texts.common.invalidServerResponse) } trackAnalyticsEventOnce(ANALYTICS_EVENTS.WITHDRAWAL_REQUESTED, result.id, { withdrawal_id: result.id, value: amount * 10, currency: 'IRR', withdrawal_status: result.status, }) return successResult(result) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } }