bot-me / src /bots /services /PurchaseTrackingService.ts
Mohammed Foud
allh
80d4bc1
import { createLogger } from '../../utils/logger';
import { supabase } from '../../db/supabase';
import { camelToSnake, snakeToCamel } from '../../utils';
const logger = createLogger('PurchaseTrackingService');
export enum PurchaseState {
PENDING = 'pending',
SUCCESS = 'success',
FAILED = 'failed',
CANCELED = 'canceled',
TIMEOUT = 'timeout'
}
export interface PurchaseRecord {
id?: number;
userId: number;
telegramId: number;
service: string;
countryId: string;
operator: string;
phoneNumber?: string;
orderId?: string;
cost: number;
state: PurchaseState;
verificationCode?: string;
verificationMessage?: string;
createdAt?: string;
updatedAt?: string;
}
export class PurchaseTrackingService {
private static instance: PurchaseTrackingService;
private constructor() {}
public static getInstance(): PurchaseTrackingService {
if (!PurchaseTrackingService.instance) {
PurchaseTrackingService.instance = new PurchaseTrackingService();
}
return PurchaseTrackingService.instance;
}
/**
* Create a new purchase record
*/
async createPurchase(purchaseData: Omit<PurchaseRecord, 'id' | 'createdAt' | 'updatedAt'>): Promise<PurchaseRecord> {
try {
// Convert camelCase to snake_case for database
const snakeData = camelToSnake(purchaseData);
const { data, error } = await supabase
.from('purchases')
.insert(snakeData)
.select();
if (error) {
logger.error(`Error creating purchase record: ${error.message}`);
throw new Error(`Failed to create purchase record: ${error.message}`);
}
// Convert snake_case back to camelCase
return snakeToCamel(data[0]) as PurchaseRecord;
} catch (error: any) {
logger.error(`Error in createPurchase: ${error.message}`);
throw error;
}
}
/**
* Update purchase state and related information
*/
async updatePurchaseState(
orderId: string,
state: PurchaseState,
additionalData: Partial<PurchaseRecord> = {}
): Promise<PurchaseRecord> {
try {
const updateData = camelToSnake({
state,
updatedAt: new Date().toISOString(),
...additionalData
});
const { data, error } = await supabase
.from('purchases')
.update(updateData)
.eq('order_id', orderId)
.select();
if (error) {
logger.error(`Error updating purchase state: ${error.message}`);
throw new Error(`Failed to update purchase state: ${error.message}`);
}
return data && data.length > 0 ? snakeToCamel(data[0]) as PurchaseRecord : {} as PurchaseRecord;
} catch (error: any) {
logger.error(`Error in updatePurchaseState: ${error.message}`);
throw error;
}
}
/**
* Get purchase by order ID
*/
async getPurchaseByOrderId(orderId: string): Promise<PurchaseRecord | null> {
try {
const { data, error } = await supabase
.from('purchases')
.select('*')
.eq('order_id', orderId)
.limit(1);
if (error) {
logger.error(`Error fetching purchase: ${error.message}`);
throw new Error(`Failed to fetch purchase: ${error.message}`);
}
return data && data.length > 0 ? snakeToCamel(data[0]) as PurchaseRecord : null;
} catch (error: any) {
logger.error(`Error in getPurchaseByOrderId: ${error.message}`);
throw error;
}
}
/**
* Get user's purchase history
*/
async getUserPurchases(telegramId: number, limit: number = 10, offset: number = 0): Promise<PurchaseRecord[]> {
try {
const { data, error } = await supabase
.from('purchases')
.select('*')
.eq('telegram_id', telegramId)
.order('created_at', { ascending: false })
.range(offset, offset + limit - 1);
if (error) {
logger.error(`Error fetching user purchases: ${error.message}`);
throw new Error(`Failed to fetch user purchases: ${error.message}`);
}
return data ? data.map(record => snakeToCamel(record) as PurchaseRecord) : [];
} catch (error: any) {
logger.error(`Error in getUserPurchases: ${error.message}`);
throw error;
}
}
/**
* Get user's purchase history filtered by state
*/
async getUserPurchasesByState(telegramId: number, state: PurchaseState, limit: number = 10, offset: number = 0): Promise<PurchaseRecord[]> {
try {
const { data, error } = await supabase
.from('purchases')
.select('*')
.eq('telegram_id', telegramId)
.eq('state', state)
.order('created_at', { ascending: false })
.range(offset, offset + limit - 1);
if (error) {
logger.error(`Error fetching user purchases by state: ${error.message}`);
throw new Error(`Failed to fetch user purchases by state: ${error.message}`);
}
return data ? data.map(record => snakeToCamel(record) as PurchaseRecord) : [];
} catch (error: any) {
logger.error(`Error in getUserPurchasesByState: ${error.message}`);
throw error;
}
}
}