first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
# 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<void> {
await this.db.create("payment_intent", record);
}
public async get(id: string): Promise<OwnedIntent | undefined> {
const rows = await this.db.select<OwnedIntent>(`payment_intent:${id}`);
return rows[0];
}
public async listByOwner(ownerId: string): Promise<readonly OwnedIntent[]> {
return this.db.query<OwnedIntent[]>(
"SELECT * FROM payment_intent WHERE ownerId = $o",
{ o: ownerId }
);
}
}
```
## API Reference
- `OwnedIntent` — `{ id: string; ownerId: string; tenantId?: string; createdAt: number }`.
- `PaymentIntentStore` — `put(record): Promise<void>`, `get(id): Promise<OwnedIntent | undefined>`, `listByOwner(ownerId): Promise<readonly OwnedIntent[]>`.
## 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.