import type { PaymentEvent, PaymentIntent, PaymentMethod, PaymentRefund, PaymentStore, } from "./types.ts"; export function memoryPaymentStore(): PaymentStore { const intents = new Map(); const keys = new Map(); const eventRows = new Map(); const refundRows = new Map(); const methodRows = new Map(); let lock = Promise.resolve(); const exclusive = async (run: () => T | Promise): Promise => { const previous = lock; let release!: () => void; lock = new Promise((resolve) => (release = resolve)); await previous; try { return await run(); } finally { release(); } }; return { async findIntentByKey(key) { const id = keys.get(key); return id ? structuredClone(intents.get(id)!) : null; }, async putIntent(intent) { intents.set(intent.id, structuredClone(intent)); keys.set(intent.idempotencyKey, intent.id); }, async getIntent(id) { const row = intents.get(id); return row ? structuredClone(row) : null; }, async listIntents(filter = {}) { return [...intents.values()] .filter( (row) => (!filter.subject || row.subject === filter.subject) && (!filter.gateway || row.gateway === filter.gateway), ) .map((row) => structuredClone(row)); }, async appendEvent(event) { if (eventRows.has(event.id)) return false; eventRows.set(event.id, structuredClone(event)); return true; }, async events(intentId) { return [...eventRows.values()] .filter((row) => row.intentId === intentId) .sort((a, b) => a.at - b.at) .map((row) => structuredClone(row)); }, async reserveRefund(refund, captured) { return exclusive(() => { if ([...refundRows.values()].some((row) => row.idempotencyKey === refund.idempotencyKey)) return false; const used = [...refundRows.values()] .filter((row) => row.intentId === refund.intentId && row.status !== "failed") .reduce((sum, row) => sum + row.amount, 0); if (used + refund.amount > captured) return false; refundRows.set(refund.id, structuredClone(refund)); return true; }); }, async updateRefund(id, status, gatewayRef) { const row = refundRows.get(id); if (row) refundRows.set(id, { ...row, status, gatewayRef }); }, async refunds(intentId) { return [...refundRows.values()] .filter((row) => row.intentId === intentId) .map((row) => structuredClone(row)); }, async methods(subject) { return [...methodRows.values()] .filter((row) => row.subject === subject) .map((row) => structuredClone(row)); }, async getMethod(id) { const row = methodRows.get(id); return row ? structuredClone(row) : null; }, async putMethod(method) { methodRows.set(method.id, structuredClone(method)); }, async removeMethod(id) { methodRows.delete(id); }, async defaultMethod(subject, id) { for (const [key, row] of methodRows) if (row.subject === subject) methodRows.set(key, { ...row, default: key === id }); }, }; }