@beignet/core
Version:
Core framework primitives for Beignet
230 lines • 7.93 kB
JavaScript
/**
* @beignet/core/payments
*
* Provider-neutral payments primitives for Beignet applications.
*/
import { createProvider, createProviderInstrumentation, } from "../providers/index.js";
/**
* Error thrown by payment helpers and provider adapters.
*/
export class PaymentProviderError extends Error {
/**
* Provider name when known.
*/
provider;
/**
* Operation that failed.
*/
operation;
/**
* Provider error code when known.
*/
code;
/**
* Original provider error when available.
*/
cause;
constructor(args) {
super(args.message);
this.name = "PaymentProviderError";
this.provider = args.provider;
this.operation = args.operation;
this.code = args.code;
this.cause = args.cause;
}
}
function defaultMemoryId(prefix) {
return `${prefix}_${crypto.randomUUID()}`;
}
function cloneMetadata(metadata) {
return metadata ? { ...metadata } : undefined;
}
function cloneCheckoutInput(input) {
return {
...input,
lineItems: input.lineItems.map((item) => ({ ...item })),
metadata: cloneMetadata(input.metadata),
};
}
function cloneRefundInput(input) {
return {
...input,
metadata: cloneMetadata(input.metadata),
};
}
/**
* Create an in-memory payments port for tests, local development, and
* examples.
*
* The memory adapter does not contact a payment provider or validate webhook
* signatures. Queue webhook events explicitly with `queueWebhookEvent(...)`.
*/
export function createMemoryPayments(options = {}) {
const checkoutSessions = [];
const billingPortalSessions = [];
const refunds = [];
const webhookEvents = [];
const queuedWebhookEvents = [];
const now = options.now ?? (() => new Date());
const id = options.id ?? defaultMemoryId;
return {
get checkoutSessions() {
return checkoutSessions;
},
get billingPortalSessions() {
return billingPortalSessions;
},
get refunds() {
return refunds;
},
get webhookEvents() {
return webhookEvents;
},
async createCheckoutSession(input) {
const session = {
id: id("checkout"),
provider: "memory",
mode: input.mode,
url: `https://payments.example.test/checkout/${id("url")}`,
customerId: input.customerId,
metadata: cloneMetadata(input.metadata),
input: cloneCheckoutInput(input),
createdAt: now(),
};
checkoutSessions.push(session);
await options.onCheckoutSessionCreated?.(session);
return session;
},
async createBillingPortalSession(input) {
const session = {
id: id("portal"),
provider: "memory",
url: `https://payments.example.test/portal/${id("url")}`,
customerId: input.customerId,
input: { ...input },
createdAt: now(),
};
billingPortalSessions.push(session);
await options.onBillingPortalSessionCreated?.(session);
return session;
},
async createRefund(input) {
const refund = {
id: id("refund"),
provider: "memory",
paymentId: input.paymentId,
amount: input.amount,
status: "succeeded",
metadata: cloneMetadata(input.metadata),
input: cloneRefundInput(input),
createdAt: now(),
};
refunds.push(refund);
await options.onRefundCreated?.(refund);
return refund;
},
async verifyWebhook() {
const event = queuedWebhookEvents.shift();
if (!event) {
throw new PaymentProviderError({
provider: "memory",
operation: "payments.verifyWebhook",
message: "No memory payment webhook event is queued. Call queueWebhookEvent(...) before verifyWebhook(...).",
});
}
webhookEvents.push(event);
await options.onWebhookVerified?.(event);
return event;
},
queueWebhookEvent(event) {
queuedWebhookEvents.push(event);
},
clear() {
checkoutSessions.length = 0;
billingPortalSessions.length = 0;
refunds.length = 0;
webhookEvents.length = 0;
queuedWebhookEvents.length = 0;
},
};
}
/**
* Create a provider that contributes an in-memory payments port.
*/
export function createMemoryPaymentsProvider(options = {}) {
const { name = "memory-payments", onCheckoutSessionCreated, onBillingPortalSessionCreated, onRefundCreated, onWebhookVerified, ...paymentsOptions } = options;
return createProvider({
name,
metadata: {
ports: ["payments"],
watchers: ["payments"],
},
setup({ ports }) {
const instrumentation = createProviderInstrumentation(ports, {
providerName: name,
watcher: "payments",
});
const payments = createMemoryPayments({
...paymentsOptions,
async onCheckoutSessionCreated(session) {
instrumentation.custom({
name: "payments.checkout.created",
label: "Checkout session created",
summary: session.id,
details: {
id: session.id,
mode: session.mode,
customerId: session.customerId,
},
});
await onCheckoutSessionCreated?.(session);
},
async onBillingPortalSessionCreated(session) {
instrumentation.custom({
name: "payments.portal.created",
label: "Billing portal session created",
summary: session.id,
details: {
id: session.id,
customerId: session.customerId,
},
});
await onBillingPortalSessionCreated?.(session);
},
async onRefundCreated(refund) {
instrumentation.custom({
name: "payments.refund.created",
label: "Refund created",
summary: refund.id,
details: {
id: refund.id,
paymentId: refund.paymentId,
amount: refund.amount,
status: refund.status,
},
});
await onRefundCreated?.(refund);
},
async onWebhookVerified(event) {
instrumentation.custom({
name: "payments.webhook.verified",
label: "Payment webhook verified",
summary: event.type,
details: {
id: event.id,
eventType: event.type,
livemode: event.livemode,
},
});
await onWebhookVerified?.(event);
},
});
return {
ports: {
payments,
},
};
},
});
}
//# sourceMappingURL=index.js.map