Payment processing with Stripe and Square.
A provider-agnostic payment interface with adapters for Stripe and Square. Handles checkout sessions, webhook verification, refunds, and idempotent payment processing.
Provider Interface
PaymentProvider defines the common contract:
- Create single and multi-event checkout sessions
- Verify webhook signatures
- Retrieve session details and process refunds
Error subclass for user-facing payment validation errors (e.g. invalid phone number). These propagate through safeAsync so the message can be shown to the user.
Build checkout metadata from a CheckoutIntent (converts items to compact form).
Build checkout session metadata from booking data (items already compact).
Cache a provider API client keyed on its config. Reuses the cached client while the config is unchanged and recreates it when the config changes; returns null when the provider is unconfigured.
Create a withClient helper that runs an operation with a lazily-resolved client. Returns null if the client is not available or the operation fails.
Enforce metadata value length limits for a payment provider.
Extract a human-readable message from an unknown caught value
Normalize validated session metadata into the canonical SessionMetadata shape.
Build a provider fee line-item array from the configured booking fee.
Returns [] when the fee is zero, else a single item shaped by build.
Validate that session metadata contains required fields (name + items).
Type guard: check if a string is a valid PaymentStatus
Safely execute async operation, returning null on error. Re-throws PaymentUserError so user-facing messages propagate.
Convert single-event answerIds to the per-event format used in metadata
Convert registration line items to compact booking items
Convert a provider-specific checkout result to a CheckoutSessionResult. Returns null if session ID or URL is missing.
Wrap a checkout operation, converting PaymentUserError to { error } result and swallowing unexpected errors as null. Used by both provider adapters.
Payment provider interface.
-
checkoutCompletedEventType: string
The webhook event type name that indicates a completed checkout
-
createCheckoutSession(): Promise<CheckoutSessionResult>intent: CheckoutIntent,baseUrl: string
Create a checkout session for one or more events. Returns a session ID and hosted checkout URL, or null on failure.
-
isPaymentRefunded(paymentReference: string): Promise<boolean>
Check if a payment has been refunded via the provider API. Used to refresh refund status from the edit attendee page.
-
refundPayment(paymentReference: string): Promise<boolean>
Refund a completed payment.
-
requiresWebhookSignature: boolean
Whether incoming webhooks carry a verifiable signature. Providers that sign their webhooks (Stripe, Square) set this true so the endpoint rejects unsigned requests. Providers whose webhooks are unsigned (SumUp) set this false and instead establish authenticity by re-fetching from the API.
-
resolveWebhookSession(event: WebhookEvent): Promise<ValidatedPaymentSession | "skip" | null>
Resolve a validated session from a webhook event. Each provider knows how to extract/fetch session data from its own event structure, so the webhook handler stays provider-agnostic.
-
retrieveSession(sessionId: string): Promise<ValidatedPaymentSession | null>
Retrieve and validate a completed checkout session by ID. Returns the validated session or null if not found / invalid.
-
setupWebhookEndpoint(): Promise<WebhookSetupResult>secretKey: string,webhookUrl: string,existingEndpointId?: string | null
Set up a webhook endpoint for this provider. Some providers (e.g. Stripe) support programmatic creation.
-
type: PaymentProviderType
Provider identifier
-
verifyWebhookSignature(): Promise<WebhookVerifyResult>payload: string,signature: string,webhookUrl: string,payloadBytes: Uint8Array
Verify a webhook request's signature and parse the event payload.
& { date: string | null; items: BookingItem[]; eventAnswerIds?: Record<string, number[]>; siteTokenIndex?: string; }
Processed booking intent extracted from payment session metadata
Compact booking item stored in session metadata (serialized/deserialized as JSON)
| { type: "checkout"; checkoutUrl: string; }
| { type: "sold_out"; }
| { type: "checkout_failed"; error?: string; }
| { type: "creation_failed"; reason: "capacity_exceeded" | "encryption_error"; }
Booking result — callers map this to their response format
& { date: string | null; items: CheckoutItem[]; eventAnswerIds?: Record<string, number[]>; siteToken?: string; }
Registration intent for checkout (one or more events)
| { error: string; }
| null
Result of creating a checkout session.
Shared shape for a provider credential check in connection-test results.
Supported payment provider identifiers
Supported payment provider identifiers
Valid payment status values. "failed" is a terminal non-payment (declined or expired checkout) — distinct from "unpaid", which may still complete.
A validated payment session returned after checkout completion
-
amountTotal: number
Total amount charged in smallest currency unit (cents), from the payment provider
- id: string
- metadata: SessionMetadata
- paymentReference: string
- paymentStatus: PaymentStatus
| { success: false; error: string; }
Result of webhook endpoint setup
| { valid: false; error: string; }
Result of webhook signature verification
Stubbable API for internal calls (testable via spyOn, like stripeApi/squareApi)
Square metadata constraint: each value max 255 characters
Stripe metadata constraint: each value max 500 characters
Usage
import * as mod from "docs/payments.ts";