# payment-intent-store ## Purpose Persistence contract for the intent-id → owner map every payment plugin needs to enforce per-user scoping on lookups, lists, and mutations. Without this map, a sequential or guessable intent id (which is what most provider APIs return) would let any authenticated user enumerate or even mutate other users' intents — the textbook insecure-direct-object-reference defect. The interface ships as an injectable dependency so operators can wire a SurrealDB, Postgres, Redis, or DynamoDB backing store without changing any code in the plugin. The default in-process `MemoryPaymentIntentStore` (in the sibling module) is provided for tests, ephemeral dev environments, and single-process deployments. ## Features - Three-method contract: `put`, `get`, `listByOwner` — minimal surface so adapters stay small. - `OwnedIntent` carries `id`, `ownerId`, optional `tenantId`, and `createdAt` for sorting / GDPR retention. - Asynchronous everywhere so storage adapters can plug in network IO without monkey-patching the surface. ## Usage: Basic ```ts import { type PaymentIntentStore, MemoryPaymentIntentStore, } from "@suaveplan/api-plugin-payment/store/payment-intent-store"; const store: PaymentIntentStore = new MemoryPaymentIntentStore(); await store.put({ id: "pi_abc", ownerId: "u1", createdAt: Date.now() }); const owned = await store.listByOwner("u1"); ``` ## Usage: Advanced ```ts class SurrealPaymentIntentStore implements PaymentIntentStore { public constructor(private readonly db: SurrealAdapter) {} public async put(record: OwnedIntent): Promise { await this.db.create("payment_intent", record); } public async get(id: string): Promise { const rows = await this.db.select(`payment_intent:${id}`); return rows[0]; } public async listByOwner(ownerId: string): Promise { return this.db.query( "SELECT * FROM payment_intent WHERE ownerId = $o", { o: ownerId } ); } } ``` ## API Reference - `OwnedIntent` — `{ id: string; ownerId: string; tenantId?: string; createdAt: number }`. - `PaymentIntentStore` — `put(record): Promise`, `get(id): Promise`, `listByOwner(ownerId): Promise`. ## Implementation Notes The interface intentionally does NOT include `delete` — payment intents are a regulated audit surface and removal is the operator's responsibility (GDPR erasure, retention policy enforcement) outside this plugin's scope. The `createdAt` field is `number` (Unix ms) rather than `Date` so the structure stays JSON-safe for stores that round-trip through external serialisers.