172 lines
4.7 KiB
TypeScript
172 lines
4.7 KiB
TypeScript
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<StateChangeListener> = 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<void> {
|
|
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<void> {
|
|
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<IPaymentRequest, 'merchant_id'>,
|
|
merchant: TMerchant = DEFAULT_MERCHANT
|
|
): Promise<void> {
|
|
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();
|