feat(payment): configure named gateway providers

This commit is contained in:
Clintchiz committed 2026-08-28 09:49:53 +05:30
1 parent c578bc0f92
commit 95c291db64
5 files changed
+162 -5

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
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";
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<string, ConfiguredPaymentGateway | false>;
environment?: "development" | "test" | "production";
}
function namedGateway(name: string, gateway: PaymentGateway): PaymentGateway {
return Object.freeze({ ...gateway, id: name });
}
export function paymentOptionsFromConfig(
config: PaymentConfig,
store: PaymentStore,
): PaymentOptions {
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");
const adapters: PaymentGateway[] = [];
const webhookSecrets: Record<string, string> = {};
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;
}
if (!adapters.some((adapter) => adapter.id === config.default))
throw new Error(
`WRN-PAYMENT-CONFIG: default gateway '${config.default}' is not enabled or configured`,
);
return {
store,
adapters,
default: config.default,
webhookSecrets,
environment: config.environment,
};
}
export function configurePaymentFromConfig(
config: PaymentConfig,
store: PaymentStore,
): PaymentService {
return configurePayment(paymentOptionsFromConfig(config, store));
}
+1
View File
@@ -3,5 +3,6 @@ export * from "./store.ts";
export * from "./database.ts";
export * from "./core.ts";
export * from "./adapters.ts";
export * from "./config.ts";
export * from "./contract.ts";
export { paymentPlugin } from "./plugin.ts";
+12 -5
View File
@@ -1,6 +1,7 @@
import { basename, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { definePlugin, type PluginContext } from "@wrnexus/plugin";
import type { PaymentConfig } from "./config.ts";
const root = dirname(dirname(fileURLToPath(import.meta.url)));
const extension = basename(dirname(fileURLToPath(import.meta.url))) === "dist" ? ".js" : ".ts";
@@ -39,17 +40,23 @@ export function paymentPlugin() {
: [];
},
configure(config, context) {
const value = config.payment as
{ default?: string; gateways?: Record<string, unknown>; sandbox?: boolean } | undefined;
const value = config.payment as PaymentConfig | undefined;
context.metadata.set(key, Boolean(value));
if (!value) return;
if (!value.default) throw new Error("WRN-PAYMENT-CONFIG: payment.default is required");
if (!value.gateways?.[value.default] && value.default !== "sandbox")
const legacySandbox = value.default === "sandbox" && !value.gateways;
if (!value.gateways?.[value.default] && !legacySandbox)
throw new Error(
`WRN-PAYMENT-CONFIG: default gateway '${value.default}' has no configuration`,
);
if (process.env.NODE_ENV === "production" && value.sandbox)
throw new Error("WRN-PAYMENT-CONFIG: sandbox cannot be enabled in production");
if (
process.env.NODE_ENV === "production" &&
(legacySandbox ||
Object.values(value.gateways ?? {}).some(
(gateway) => gateway && gateway.provider === "sandbox",
))
)
throw new Error("WRN-PAYMENT-CONFIG: sandbox gateways cannot be enabled in production");
},
migrations(context: PluginContext) {
return context.metadata.get(key)