/** * InMemoryPaymentsClient — deterministic in-memory PaymentsClient for dev. * * Holds a few FAKE payment records in memory. CRITICAL: the seed records carry * realistic-looking sensitive values (bank account/routing numbers, card * number) so the redaction egress path has something to mask before a response * leaves the server. These are fabricated test values, not real account data. * * This is for build-plan §4 "in-memory dev clients (tools return real data * locally)". */ import type { Payment, PaymentsClient, PaymentsQueryOptions } from './client.js'; const SEED_PAYMENTS: Payment[] = [ { paymentId: 'PAY-9001', vendor: 'Harbor Electric Co.', vendorContact: 'ap@harborelectric.example', amount: 4820.75, currency: 'USD', invoiceNumber: 'INV-4821', checkNumber: '2087', paymentDate: '2026-06-21T00:00:00Z', status: 'cleared', bankAccountNumber: '123456789012', bankRoutingNumber: '021000021', cardNumber: '4111111111111111', memo: 'Dock lighting circuit replacement — WO-1001', }, { paymentId: 'PAY-9002', vendor: 'Coastal Supply & Hardware', amount: 1842.5, currency: 'USD', invoiceNumber: 'INV-3310', checkNumber: '2088', paymentDate: '2026-06-22T00:00:00Z', status: 'pending', bankAccountNumber: '987654321098', bankRoutingNumber: '011401533', cardNumber: '5500000000000004', memo: 'PO-2001 hardware order', }, { paymentId: 'PAY-9003', vendor: 'Tidewater Plumbing LLC', amount: 640.0, currency: 'USD', invoiceNumber: 'INV-7755', checkNumber: '2089', paymentDate: '2026-06-23T00:00:00Z', status: 'cleared', bankAccountNumber: '456789012345', bankRoutingNumber: '026009593', cardNumber: '340000000000009', memo: 'Bayfront warehouse fixture repair', }, ]; /** In-memory PaymentsClient seeded with sensitive-bearing fake records. */ export class InMemoryPaymentsClient implements PaymentsClient { private readonly payments: Payment[] = SEED_PAYMENTS.map((p) => ({ ...p })); private applyLimit(rows: Payment[], opts?: PaymentsQueryOptions): Payment[] { return opts?.limit !== undefined ? rows.slice(0, opts.limit) : rows; } async getByVendor(vendor: string, opts?: PaymentsQueryOptions): Promise { const needle = vendor.toLowerCase(); const matches = this.payments.filter((p) => p.vendor.toLowerCase().includes(needle)); return this.applyLimit(matches, opts); } async getByInvoice(invoiceNumber: string, opts?: PaymentsQueryOptions): Promise { const matches = this.payments.filter((p) => p.invoiceNumber === invoiceNumber); return this.applyLimit(matches, opts); } async getByCheck(checkNumber: string, opts?: PaymentsQueryOptions): Promise { const matches = this.payments.filter((p) => p.checkNumber === checkNumber); return this.applyLimit(matches, opts); } }