import { usePaymentStore } from '#lib/stores/payment' import cloneDeep from 'lodash/cloneDeep' import { useUserBankStore } from '#lib/stores' import type { BankListItem, EWalletResponseItem, FastpayInfo, FastpayItem, BankInfoRequest, PhoneCardProvider, PaymentBankScheduleItem, PaywinResponse, PriorityBankMethod, PhoneCardItem } from '#lib/types' import { EWALLET_OPTIONS, PRIORITY_BANKS } from '#lib/constants' import { DepositMethodEnum } from '#lib/enums' import { useCrypto, useNadal } from '#lib/composables' /** * Import types from Nadal. */ import type { PaymentBankListResponseData, PaymentBankScheduleResponseData, } from '@kira-dancer/nadal' import { formatNumberWithCommas } from '#lib/utils' import { useRuntimeConfig } from 'nuxt/app' import { ref } from 'vue' import { useApiFetch } from '#lib/composables/service/use-api-fetch' import { DEPOSIT_API, DEPOSIT_WITHDRAW_API } from '#lib/configs' /** * Provides methods for managing deposit-related data, including banks, e-wallets, cryptocurrencies, and phone card providers. * @returns {Object} An object containing methods and state related to deposit data management. * @namespace */ export function useDepositData() { const store = usePaymentStore() const { payment } = useNadal() const isShowMenuLabel = ref(false) const bankStore = useUserBankStore() const { request } = useApiFetch() const { fetchCryptoList } = useCrypto() const { token } = useUser() /** * Fetches the list of phone card providers and updates the store. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchPhoneCardProviders = async () => { let isDisabledAll = false if (store?.phoneCardProviders?.length) { return } try { const { data } = await request>( DEPOSIT_WITHDRAW_API.PHONE_CARD_LIST, ) if (data && data?.cardlist) { const { cardlist, status = 0 } = data isDisabledAll = status === 0 const telecoms: PhoneCardProvider[] = Object.keys(cardlist).map((key) => { const cardProvider = cardlist[key] return { rate: cardProvider.rate, status: isDisabledAll ? 0 : cardProvider.status, providerId: key, providerName: (status || cardProvider.status) ? key : `${key} - Đang bảo trì`, values: cardProvider.value?.map((price: number) => ({ value: price.toString(), label: `${formatNumberWithCommas(price)} VNĐ`, })), isDisabled: isDisabledAll || !cardProvider.status, } }) store.setPhoneCardProviders(telecoms) } const isMaintainedPhoneCardWithdraw = store.phoneCardProviders.every( (item: PhoneCardProvider) => item.status === 0, ) store.setMaintainedPhoneCardWithdraw(isDisabledAll || isMaintainedPhoneCardWithdraw) } catch (ex) { console.error(' ~ fetchPhoneCardProviders ~ ex:', ex) // } } /** * Fetches the list of e-wallets and updates the store. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchEWallets = async (): Promise => { try { const { data } = await request( DEPOSIT_API.DEPOSIT_E_WALLET_LIST, ) if (data) { const listData = data.map((item: EWalletResponseItem) => ({ ...item, account_no: item.account_no.replace(/[^0-9]/g, ''), })) const result = EWALLET_OPTIONS.map((item) => { const items = listData.filter( (eWallet: EWalletResponseItem) => eWallet.bank_code.toLocaleLowerCase() === item.value.toLocaleLowerCase(), ) return { ...item, items, isMaintainance: !items.length, } }) store.setEWallet(result) } } catch (ex) { console.error(' ~ fetchEWallets ~ ex:', ex) } } /** * Fetches the e-wallet code and updates the store. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchEWalletCode = async (): Promise => { try { const { data } = await request( DEPOSIT_API.DEPOSIT_E_WALLET_CODE, ) if (data) { store.setEWalletCode(data) } } catch (ex) { console.error(' ~ fetchEWalletCode ~ ex:', ex) } } /** * Fetches the list of withdraw banks and updates the store. * * PaymentBankListResponseData type is imported from the Nadal module. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchWithdrawBanks = async (): Promise => { try { const { data } = await request( DEPOSIT_WITHDRAW_API.WITHDRAW_BANK_LIST, ) store.setWithdrawBanks(data) } catch (ex) { console.error(' ~ fetchWithdrawBanks ~ ex:', ex) } } /** * Fetches the Fastpay code and updates the store. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchFastpayCode = async (): Promise => { try { const data = await payment.payCode() store.setFastpayCode(data ?? '') } catch (ex) { console.error(' ~ fetchFastpayCode ~ ex:', ex) } } /** * Fetches the list of Fastpay items and updates the store. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchFastpays = async (): Promise => { try { const data = await payment.codePayList() if (data?.length) { const fastPays = data .filter((item) => item.status) .map((bank) => ({ ...bank, bankName: `${bank.name}${bank.isMaintained ? ' (Bảo trì)' : ''}`, })) store.setDepositFastPays(fastPays) } } catch (ex) { console.error(' ~ fetchFastpays ~ ex:', ex) } } /** * Fetches the list of Paywin banks and updates the store. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchPaywinBanks = async (): Promise => { try { const data = await payment.paywinList() if (data?.length) { const listBanks: PaywinResponse[] = data .map((bank: PaywinResponse) => ({ ...bank, bank_name_text: `${bank.bank_name} ${ bank.isMaintained ? ' - Hệ thống bảo trì' : '' }`, isDisabled: bank.isMaintained, })) .sort( (a: { isMaintained: boolean }, b: { isMaintained: boolean }) => { if (a.isMaintained && !b.isMaintained) { return 1 } if ( (a.isMaintained && b.isMaintained) || (!a.isMaintained && !b.isMaintained) ) { return 0 } return -1 }, ) store.setPaywinBanks(listBanks) } } catch (ex) { console.error(' ~ fetchPaywinBanks ~ ex:', ex) } } /** * Fetches the list of banks and updates the bank store. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchBankList = async (): Promise => { try { const data = await payment.bankList() if (data?.length) { const listBanks: BankListItem[] = data let banks = cloneDeep(listBanks) banks = banks.map((bank) => ({ ...bank, name: bank.bank_name, bankName: `${bank.bank_name}${bank.isMaintained ? ' (Bảo trì)' : ''}`, })) bankStore.setBanks(banks) } } catch (ex) { console.error(' ~ fetchBankList ~ ex:', ex) } } /** * Retrieves detailed information for a specific bank. * @async * @param {string} bankCode - The code of the bank to retrieve information for. * @param {number} [isAuto=0] - Optional flag to indicate if the request is automatic. * @returns {Promise} A promise that resolves to the bank information or undefined if not found. */ const getBankInfo = async ( bankCode: string, isAuto: number = 0, ): Promise => { return await payment.bankAccount({ bank_code: bankCode, is_auto: isAuto, }) } /** * Fetches the bank schedules and updates the store. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchStatementBanks = async () => { try { const data = await payment.bankSchedule< void, PaymentBankScheduleResponseData[] >() store.setBankSchedules(data) } catch (ex) { console.error(' ~ fetchStatementBanks ~ ex:', ex) } } /** * Checks if a bank has a schedule or maintenance time. * @param {string} bankCode - The code of the bank to check. * @param {boolean} [isCodePay=false] - Optional flag to indicate if CodePay is being used. * @param {boolean} [isPayWin=false] - Optional flag to indicate if PayWin is being used. * @returns {boolean} True if the bank has a schedule or maintenance time, false otherwise. */ const getScheduleBanks = ( bankCode: string, isPayWin: boolean = false, ): boolean => { const bankSchedules = store.bankSchedules if (bankSchedules?.length) { const newBankSchedules = bankSchedules.find( (bank: { bank_code: string }) => bank.bank_code?.toLowerCase() === bankCode?.toLowerCase(), ) let timeSchedule = null let isInvalid: boolean = false if (newBankSchedules?.timeSchedule?.length) { // handle maintain timeSchedule = newBankSchedules.timeSchedule.find( (timeSchedule: PaymentBankScheduleItem) => timeSchedule.note === 'baotri' && timeSchedule.status, ) // handle statement if (!timeSchedule && !isPayWin) { timeSchedule = newBankSchedules.timeSchedule.find( (timeSchedule: PaymentBankScheduleItem) => timeSchedule.note === 'saoke' && timeSchedule.status, ) } if (timeSchedule) { isInvalid = timeSchedule.status ?? false } } if (isInvalid) { return true } } return false } /** * Checks if a deposit method is under maintenance. * @param {string | undefined} type - The deposit method type to check. * @returns {boolean} True if the deposit method is under maintenance, false otherwise. */ const isMaintenance = (type: string | undefined): boolean => { if (!isShowMenuLabel.value) { return false } switch (type) { case DepositMethodEnum.FastPay: return !store.depositFastPays || !store.fastpayCode case DepositMethodEnum.EWallet: return !store.eWallets.some((i) => i.items.length) || !store.eWalletCode case DepositMethodEnum.Crypto: if (!store.cryptos.length) { return true } if (typeof store.cryptos[0]?.allowDeposit === 'boolean') { return !store.cryptos[0].allowDeposit } return false case DepositMethodEnum.Paywin: return !store.paywinBanks.length case DepositMethodEnum.PhoneCard: return store.isMaintainedPhoneCardWithdraw case DepositMethodEnum.ViettelPay: return ( store.eWallets.find( (item) => item.value === DepositMethodEnum.ViettelPay, )?.isMaintainance || !store.eWalletCode ) case DepositMethodEnum.Momo: return ( store.eWallets.find((item) => item.value === DepositMethodEnum.Momo) ?.isMaintainance || !store.eWalletCode ) default: return false } } /** * Fetches all deposit-related data and updates the store. * @async * @returns {Promise} A promise that resolves when all data has been fetched and stored. */ const fetchAllDepositData = async (): Promise => { const config = useRuntimeConfig() const isDisablePaywin = config.public.DEPOSIT_PAYWIN_DISABLE === '1' const fetchAllDepositData = [ fetchWithdrawBanks(), fetchPhoneCardProviders(), fetchFastpayCode(), fetchFastpays(), fetchStatementBanks(), fetchBankList(), fetchEWallets(), fetchEWalletCode(), fetchCryptoList(), ] if (!isDisablePaywin) { fetchAllDepositData.push(fetchPaywinBanks()) } await Promise.all(fetchAllDepositData) } /** * Fetches e-wallet and crypto data if not already fetched. * @async * @returns {Promise} A promise that resolves when the data has been fetched and stored. */ const fetchDepositEWalletsData = async (): Promise => { if (!token.value) { return } if (store?.isFetchData) { isShowMenuLabel.value = true return } try { const fetchAllDepositData = [ fetchEWallets(), fetchEWalletCode(), fetchCryptoList(), fetchPhoneCardProviders(), ] const result = await Promise.all(fetchAllDepositData) isShowMenuLabel.value = !!result.length store.setIsFetchData(true) } catch (ex) { console.error(' ~ fetchDepositEWalletsData ~ ex:', ex) store.setIsFetchData(false) } } /** * Checks if a bank has a schedule. * @param {string} bankCode - The code of the bank to check. * @returns {boolean} True if the bank has a schedule, false otherwise. */ const isScheduleBank = (bankCode: string): boolean => { const bankSchedules = store.bankSchedules if (!bankCode || !bankSchedules?.length) { return false } let isScheduleBank = false const statementBank = bankSchedules.find( (e) => e.bank_code.toLowerCase() === bankCode.toLowerCase(), ) if (statementBank?.timeSchedule) { const timeSchedule = statementBank.timeSchedule.find( (e) => (e.note === 'baotri' || e.note === 'saoke') && e.status, ) isScheduleBank = Boolean(timeSchedule) } return isScheduleBank } /** * Finds the first available priority bank from the list. * @param {PriorityBankMethod[]} [banks=[]] - The list of banks to search. * @returns {PriorityBankMethod | null} The available priority bank or null if none found. */ const findAvailablePriorityBank = ( banks: PriorityBankMethod[] = [], ): PriorityBankMethod | null => { for (const priorityBank of PRIORITY_BANKS) { const bankCode = priorityBank.bank_code const bank = banks.find( (b) => b.bank_code.toLowerCase() === bankCode.toLowerCase(), ) if (bank && !bank.isMaintained && !bank.isScheduleBank) { return bank } } return null } /** * Formats and sorts the list of banks. * @param {PriorityBankMethod[]} banks - The list of banks to format and sort. * @returns {PriorityBankMethod[]} The formatted and sorted list of banks. */ const formatBankList = ( banks: PriorityBankMethod[], ): PriorityBankMethod[] => { return banks .map((bank: PriorityBankMethod) => { const isScheduled = isScheduleBank(bank.bank_code) const bankNameSuffix = isScheduled && !bank.isMaintained ? ' (Sao kê)' : '' return { ...bank, isScheduleBank: isScheduled, bankName: `${bank.bankName as string}${bankNameSuffix}`, } }) .sort((a, b) => { if (a.isScheduleBank && !b.isScheduleBank) { return 1 } if ( Boolean(a.isScheduleBank && b.isScheduleBank) || Boolean(!a.isScheduleBank && !b.isScheduleBank) ) { return 0 } return -1 }) .sort((a, b) => { if (a.isMaintained && !b.isMaintained) { return 1 } if ( Boolean(a.isMaintained && b.isMaintained) || Boolean(!a.isMaintained && !b.isMaintained) ) { return 0 } return -1 }) } return { fetchBankList, fetchEWallets, fetchEWalletCode, fetchWithdrawBanks, fetchPhoneCardProviders, fetchAllDepositData, fetchFastpays, getBankInfo, getScheduleBanks, isMaintenance, formatBankList, findAvailablePriorityBank, fetchDepositEWalletsData, isShowMenuLabel, } }