import { paypalGateway, razorpayGateway, sandboxGateway, stripeGateway, type PayPalConfig, type RazorpayConfig, type SandboxOptions, type StripeConfig, } from "./adapters.ts"; import { configurePayment, type PaymentOptions, type PaymentService } from "./core.ts"; import type { PaymentGateway, PaymentStore } from "./types.ts"; import type { PaymentApiConfig } from "./http.ts"; export type ConfiguredPaymentGateway = | ({ provider: "sandbox" } & SandboxOptions) | ({ provider: "stripe" } & StripeConfig) | ({ provider: "razorpay" } & RazorpayConfig) | ({ provider: "paypal" } & PayPalConfig); export interface PaymentConfig { /** Name of the gateway used when an operation does not select one explicitly. */ default: string; /** Any number of named gateway accounts. Names are persisted with payment intents. */ gateways: Record; environment?: "development" | "test" | "production"; api?: PaymentApiConfig | false; /** Defaults to the package database store. Override for custom persistence or tests. */ store?: PaymentStore; } function required(name: string, fields: Record) { for (const [field, value] of Object.entries(fields)) if (typeof value !== "string" || !value.trim()) throw new Error(`WRN-PAYMENT-CONFIG: gateway '${name}' requires ${field}`); } export function validatePaymentConfig(config: PaymentConfig) { if (!config?.default?.trim()) throw new Error("WRN-PAYMENT-CONFIG: payment.default is required"); if (!config.gateways || typeof config.gateways !== "object") throw new Error("WRN-PAYMENT-CONFIG: payment.gateways is required"); for (const [name, value] of Object.entries(config.gateways)) { if (value === false) continue; if (!name.trim()) throw new Error("WRN-PAYMENT-CONFIG: gateway name is required"); if (value.provider === "stripe") required(name, { secretKey: value.secretKey, publishableKey: value.publishableKey, webhookSecret: value.webhookSecret, }); if (value.provider === "razorpay") required(name, { keyId: value.keyId, keySecret: value.keySecret }); if (value.provider === "razorpay" && config.environment === "production") required(name, { webhookSecret: value.webhookSecret }); if (value.provider === "paypal") required(name, { clientId: value.clientId, clientSecret: value.clientSecret, webhookId: value.webhookId, }); } if (!config.gateways[config.default]) throw new Error( `WRN-PAYMENT-CONFIG: default gateway '${config.default}' is not enabled or configured`, ); } function namedGateway(name: string, gateway: PaymentGateway): PaymentGateway { return Object.freeze({ ...gateway, id: name, provider: gateway.provider ?? gateway.id }); } export function paymentOptionsFromConfig( config: PaymentConfig, store: PaymentStore, ): PaymentOptions { validatePaymentConfig(config); const adapters: PaymentGateway[] = []; const webhookSecrets: Record = {}; for (const [name, value] of Object.entries(config.gateways)) { if (value === false) continue; if (!name.trim()) throw new Error("WRN-PAYMENT-CONFIG: gateway name is required"); let adapter: PaymentGateway; switch (value.provider) { case "sandbox": adapter = sandboxGateway(value); break; case "stripe": adapter = stripeGateway(value); break; case "razorpay": adapter = razorpayGateway(value); break; case "paypal": adapter = paypalGateway(value); break; default: throw new Error(`WRN-PAYMENT-CONFIG: unsupported provider '${(value as any).provider}'`); } adapters.push(namedGateway(name, adapter)); if ("webhookSecret" in value && value.webhookSecret) webhookSecrets[name] = value.webhookSecret; if (value.provider === "paypal" && value.webhookId) webhookSecrets[name] = value.webhookId; } return { store, adapters, default: config.default, webhookSecrets, environment: config.environment, }; } export function configurePaymentFromConfig( config: PaymentConfig, store: PaymentStore, ): PaymentService { return configurePayment(paymentOptionsFromConfig(config, store)); }