Checkout Sessions
Session creation, loading, and persistence in Upstash Redis via @prood/checkout-host.
Checkout sessions are managed by @prood/checkout-host and persisted in Upstash Redis. Each session wraps a @prood/checkout CheckoutSession state machine snapshot.
Session lifecycle
Drag to pan · Scroll to zoom
API routes
Create session
POST /api/sessions
Header: x-checkout-secret: {CHECKOUT_API_SECRET}Request body:
{
"orderId": "ord_abc123",
"amount": 9999,
"currency": "EUR",
"tenantId": "org_demo",
"returnUrl": "http://localhost:3000/order-confirmation?order=ord_abc123",
"customerInfo": {
"email": "customer@example.com",
"firstName": "Maria",
"lastName": "Silva"
},
"fulfillment": "shipping",
"provider": "stripe"
}Response:
{
"id": "cs_abc123",
"checkoutUrl": "http://localhost:3004/c/cs_abc123",
"expiresAt": "2026-05-29T11:00:00.000Z"
}Only callers with the correct x-checkout-secret can create sessions. This prevents unauthorized session creation.
Load session
GET /api/sessions/{id}Returns the current session snapshot (state, customer info, payment session, amount, etc.).
Submit payment
POST /api/sessions/{id}/payRequest body (Stripe):
{
"sourceToken": "pm_xxx"
}Request body (Easypay/Ifthenpay):
{
"method": "multibanco"
}Advances the CheckoutSession state machine and returns the payment session (may include redirectUrl for 3DS).
Confirm payment
POST /api/sessions/{id}/confirmCalled after 3DS redirect or async confirmation:
{
"chargeId": "pi_xxx"
}Redis persistence
@prood/checkout-host stores session snapshots in Upstash Redis:
| Key pattern | TTL | Content |
|---|---|---|
checkout:session:{id} | Configurable (default 30 min) | Full CheckoutSession snapshot JSON |
Hydration
On each request to /c/{id}:
loadSession(id)reads snapshot from RedisloadAndHydrate(id, providerFactory)reconstructs CheckoutSession from snapshot- Payment provider rebuilt with tenant's credentials
- State machine continues from saved state
Expiry
Sessions with expiresIn set a TTL on the Redis key. After expiry:
- All state-mutating methods throw
- The
expiredevent fires - Customer sees an expiry message on the payment page
Payment links
POST /api/payment-links
Header: x-checkout-secret: {CHECKOUT_API_SECRET}Creates a shareable payment link (POS, invoice, etc.) without an existing order:
{
"amount": 4500,
"currency": "EUR",
"tenantId": "org_demo",
"description": "Invoice #1234",
"expiresIn": 86400000
}Returns a URL like http://localhost:3004/c/cs_link_abc.
Implementation
// @prood/checkout-host
import { createCheckoutSession, loadSession, saveSession } from '@prood/checkout-host'
// Create
const session = await createCheckoutSession({
orderId,
amount,
currency,
tenantId,
returnUrl,
provider: getPaymentProvider(providerId, tenantConfig),
})
// Load
const snapshot = await loadSession(sessionId)
const session = await loadAndHydrate(sessionId, (id) =>
getPaymentProvider(id, tenantConfig)
)