119 lines
4.2 KiB
TypeScript
119 lines
4.2 KiB
TypeScript
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<string, ConfiguredPaymentGateway | false>;
|
|
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<string, unknown>) {
|
|
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<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;
|
|
}
|
|
return {
|
|
store,
|
|
adapters,
|
|
default: config.default,
|
|
webhookSecrets,
|
|
environment: config.environment,
|
|
};
|
|
}
|
|
|
|
export function configurePaymentFromConfig(
|
|
config: PaymentConfig,
|
|
store: PaymentStore,
|
|
): PaymentService {
|
|
return configurePayment(paymentOptionsFromConfig(config, store));
|
|
}
|