export type TransactionPaidPayload = { transaction_id: string; merchant_id: string; outlet_id: string; terminal_id: string; device_id: string | null | undefined; amount: number; currency: string; partner_reference: string; paid_at?: string; }; type TransactionPaidInternalEvent = { id: string; event_type: "transaction.paid"; transaction_id: string; payload_json: TransactionPaidPayload; created_at: string; }; export type TransactionPaidEvent = TransactionPaidInternalEvent; import { randomUUID } from "node:crypto"; const transactionPaidEvents = new Map(); const transactionPaidIndex = new Map(); const transactionPaidSubscribers = new Set<(event: TransactionPaidEvent) => void>(); function nowIso() { return new Date().toISOString(); } function cloneInternalEvent(event: TransactionPaidInternalEvent): TransactionPaidInternalEvent { return { ...event, payload_json: { ...event.payload_json } }; } export function subscribeTransactionPaid(handler: (event: TransactionPaidEvent) => void) { transactionPaidSubscribers.add(handler); return () => transactionPaidSubscribers.delete(handler); } export function emitTransactionPaid(payload: TransactionPaidPayload): TransactionPaidEvent { const existingId = transactionPaidIndex.get(payload.transaction_id); if (existingId) { const events = transactionPaidEvents.get(payload.transaction_id) || []; const existing = events.find((event) => event.id === existingId); if (existing) { return cloneInternalEvent(existing); } } const event: TransactionPaidInternalEvent = { id: randomUUID(), event_type: "transaction.paid", transaction_id: payload.transaction_id, payload_json: payload, created_at: nowIso() }; const bucket = transactionPaidEvents.get(payload.transaction_id) || []; bucket.push(event); transactionPaidEvents.set(payload.transaction_id, bucket); transactionPaidIndex.set(payload.transaction_id, event.id); const frozen = cloneInternalEvent(event); for (const listener of Array.from(transactionPaidSubscribers)) { listener(frozen); } return frozen; } export function getTransactionPaidEvents(transactionId?: string): TransactionPaidEvent[] { if (transactionId) { return (transactionPaidEvents.get(transactionId) || []).map(cloneInternalEvent); } return Array.from(transactionPaidEvents.values()).flatMap((bucket) => bucket.map(cloneInternalEvent)); } export function getTransactionPaidEventByTransactionId(transactionId: string): TransactionPaidEvent | null { const events = transactionPaidEvents.get(transactionId) || []; return events.length > 0 ? cloneInternalEvent(events[events.length - 1]) : null; }