import type { InitializePaymentInput, PaymentService } from "./core.ts"; import type { PaymentIntent } from "./types.ts"; export type PaymentApiAction = | "list" | "read" | "initialize" | "sync" | "cancel" | "capture" | "refund" | "methods" | "reconcile"; export interface PaymentApiContext { req: Request; params?: Record; user?: { id?: string }; session?: { userId?: string; subject?: string }; } export interface PaymentApiConfig { enabled?: boolean; /** Resolve the authenticated subject on the server. Request-body subject values are ignored. */ subject?(context: PaymentApiContext): string | undefined | Promise; /** Additional authorization. Administrative actions deny by default. */ authorize?( context: PaymentApiContext, action: PaymentApiAction, payment?: PaymentIntent, ): boolean | Promise; /** Replace client input with authoritative catalog/order data before creating an intent. */ initialize?( context: PaymentApiContext, body: Record, subject: string, ): Omit | Promise>; } const HTTP = Symbol.for("@wrnexus/payment:http:v1"); type PaymentHttpGlobal = typeof globalThis & { [HTTP]?: PaymentApiConfig }; export function configurePaymentApi(config: PaymentApiConfig = {}) { (globalThis as PaymentHttpGlobal)[HTTP] = config; } export function getPaymentApiConfig(): PaymentApiConfig { return (globalThis as PaymentHttpGlobal)[HTTP] ?? {}; } export async function paymentApiSubject(context: PaymentApiContext) { const config = getPaymentApiConfig(); const resolved = await config.subject?.(context); return resolved?.trim() || context.user?.id?.trim() || context.session?.userId?.trim() || ""; } export async function paymentApiAllowed( context: PaymentApiContext, action: PaymentApiAction, payment?: PaymentIntent, ) { const config = getPaymentApiConfig(); if (config.enabled === false) return false; if (config.authorize) return Boolean(await config.authorize(context, action, payment)); const subject = await paymentApiSubject(context); if (!subject) return false; if (["capture", "refund", "reconcile"].includes(action)) return false; return !payment || payment.subject === subject; } export function publicPayment(payment: PaymentIntent, status: string) { return { id: payment.id, amount: payment.amount, currency: payment.currency, gateway: payment.gateway, status, createdAt: payment.createdAt, checkoutUrl: payment.checkoutUrl, client: payment.client, metadata: payment.metadata, }; } export async function ensureOwnedPayment( service: PaymentService, context: PaymentApiContext, id: string, action: PaymentApiAction, ) { const payment = await service.getPayment(id); if (!payment) return { response: Response.json({ error: "payment not found" }, { status: 404 }) }; if (!(await paymentApiAllowed(context, action, payment))) return { response: Response.json({ error: "payment access denied" }, { status: 403 }) }; return { payment }; }