88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
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<string, TransactionPaidInternalEvent[]>();
|
|
const transactionPaidIndex = new Map<string, string>();
|
|
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;
|
|
}
|