Reservations Feature

Enable the opt-in reservation workflow with concurrency-safe holds, Stripe checkout, email, and calendars.

Synced with starter commit 7d452a6.

Reservations are disabled by default. Set NEXT_PUBLIC_FEATURE_RESERVATIONS_ENABLED=true only after defining the service catalog, IANA timezone, business hours, pricing/deposit policy, Stripe webhook, and email policy in src/config/reservations.ts. Demo auto-seeding is also off; never depend on it in production.

The public flow uses GET /api/reservations/services, POST /api/reservations/availability, and idempotent POST /api/reservations; members view GET /api/reservations/mine. UI routes are /:locale/reserve and /:locale/account/reservations.

A checkout request takes a database lock, creates one 35-minute hold and Stripe Checkout Session, and replays safely for the same Idempotency-Key. PostgreSQL advisory locking plus an exclusion constraint prevent overlapping held or confirmed slots under concurrency. Only a verified Stripe webhook confirms payment; session return pages do not. Expiration releases only the hold linked to that Stripe session.

Confirmation queues email with ICS and exposes a Google Calendar link. Before launch, test simultaneous booking, duplicate request/webhook, abandoned checkout, delayed payment, timezone/DST boundaries, deposit/full-payment modes, email retries, and organization scoping.

Reservations Feature · Sushi SaaS