import { apiService } from '@/services/api'; import { DEFAULT_MERCHANT, MERCHANT_IDS, type TMerchant } from '@/config/api'; import type { TCashierFlowState, IApiCurrency, IPaymentRequest, IFormMetadataField, } from '../types'; import type { IPaymentMethod } from '@/features/payment-methods/types'; import { getCashierConfig } from '@/config/cashierConfig'; type StateChangeListener = (state: TCashierFlowState) => void; class CashierService { private state: TCashierFlowState = 'loading'; private methods: IPaymentMethod[] = []; private currencies: IApiCurrency[] = []; private selectedMethod: IPaymentMethod | null = null; private formMetadata: IFormMetadataField[] = []; private error: Error | null = null; private paymentUrl: string | null = null; private listeners: Set = new Set(); getState(): TCashierFlowState { return this.state; } getMethods(): IPaymentMethod[] { return this.methods; } getCurrencies(): IApiCurrency[] { return this.currencies; } getError(): Error | null { return this.error; } getPaymentUrl(): string | null { return this.paymentUrl; } getSelectedMethod(): IPaymentMethod | null { return this.selectedMethod; } getFormMetadata(): IFormMetadataField[] { return this.formMetadata; } async setSelectedMethod( method: IPaymentMethod | null, merchant: TMerchant = DEFAULT_MERCHANT ): Promise { this.selectedMethod = method; this.formMetadata = []; if (method) { try { const config = getCashierConfig(); console.log('config', config); const paymentType = config.paymentType || 'deposit'; // Fetch form metadata and currencies for the selected method in parallel const [formMetadataData, currenciesData] = await Promise.all([ apiService.getFormMetadata(method.code, paymentType, merchant), apiService.getCurrencies(merchant, method.code), ]); this.formMetadata = formMetadataData; this.currencies = currenciesData; } catch (err) { // If form metadata or currencies fetch fails, we still allow method selection // Error can be handled by the UI if needed console.error('Failed to fetch form metadata or currencies:', err); } } else { // Reset currencies when method is deselected // Optionally reload all currencies or keep empty this.currencies = []; } this.notifyListeners(); } private setState(newState: TCashierFlowState): void { if (this.state !== newState) { this.state = newState; this.notifyListeners(); } } subscribe(listener: StateChangeListener): () => void { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; } private notifyListeners(): void { this.listeners.forEach(listener => listener(this.state)); } async loadData(merchant: TMerchant = DEFAULT_MERCHANT): Promise { try { this.setState('loading'); this.error = null; // Only fetch methods, currencies will be fetched when a method is selected const methodsData = await apiService.getMethods(merchant); this.methods = methodsData; this.currencies = []; // Currencies will be loaded when method is selected this.setState('ready'); } catch (err) { this.error = err instanceof Error ? err : new Error('Failed to load cashier data'); this.setState('error'); throw this.error; } } async initiatePayment( paymentRequest: Omit, merchant: TMerchant = DEFAULT_MERCHANT ): Promise { try { this.setState('submitting'); this.error = null; const fullRequest = { ...paymentRequest, merchant_id: MERCHANT_IDS[merchant], } as IPaymentRequest; const response = await apiService.initiatePayment(fullRequest, merchant); this.paymentUrl = response.redirect_url; this.setState('redirecting'); } catch (err) { this.error = err instanceof Error ? err : new Error('Failed to initiate payment'); this.setState('error'); throw this.error; } } reset(): void { this.state = 'loading'; this.selectedMethod = null; this.formMetadata = []; this.currencies = []; this.error = null; this.paymentUrl = null; this.notifyListeners(); } goBack(): void { this.selectedMethod = null; this.formMetadata = []; this.currencies = []; this.error = null; // Reset to ready state if we have methods available, otherwise keep current state if (this.methods.length > 0) { this.setState('ready'); } else { this.notifyListeners(); } } } export const cashierService = new CashierService();