# Architecture ```text Visitor │ ▼ your-booking-domain.example │ ├── static React UI ───────────────┐ │ │ one Worker deployment └── /api/* Cloudflare Worker ──────┘ │ ├── D1 │ settings │ bookings │ OAuth token (AES-GCM encrypted) │ OAuth state │ short-lived slot locks │ basic rate limits │ Stripe payment/refund state │ reschedule proposals (hashed bearer tokens) │ append-only admin activity events │ ├── Google Calendar API │ FreeBusy │ Event creation │ Google Meet creation │ attendee invitation │ └── Stripe API Checkout Sessions PaymentIntent refunds signed payment/refund webhooks ``` ## Booking transaction 1. Calculate candidate times inside weekly availability. 2. Subtract Google FreeBusy ranges and recent D1 reservations. 3. Visitor chooses a start and duration. 4. Server recomputes availability rather than trusting the browser. 5. D1 reserves each time segment touched by the booking and buffers. 6. Google FreeBusy is checked once more. 7. If payment is disabled, the normal Google event is created and the booking is confirmed immediately. 8. If payment is enabled, Slot creates a **private owner-only Google Calendar hold** with no attendee invitation. A Google Meet conference may be attached to the hold internally, but it is not exposed to the visitor. 9. Stripe Checkout opens for the calculated duration × per-minute rate. The Checkout Session expires after 30 minutes. 10. A signed Stripe webhook is the primary fulfillment signal. Only after `payment_status=paid` does Slot convert the private hold into the attendee event, send Google Calendar updates, expose the Meet link, and mark the booking confirmed. 11. Cancelled or expired Checkout sessions release the private hold and D1 locks. A return-page status check provides an idempotent fallback if the webhook and browser redirect arrive in the opposite order. ## Why D1 exists Google Calendar owns calendar truth. D1 only holds application state Google does not provide: UI settings, booking metadata, encrypted OAuth credentials, concurrency locks, payment/refund state, and the admin activity history. ## Booking management A direct admin reschedule acquires short-lived D1 locks for the destination, rechecks Google FreeBusy, PATCHes the existing Google Calendar event with `sendUpdates=all`, and then moves the D1 booking record. The booking ID and Stripe payment remain the same. A suggested new time does **not** move or release the original booking. Slot stores the proposed start/end plus a SHA-256 hash of a random bearer token, then PATCHes the Google event description with a secure acceptance URL using `sendUpdates=all`. When the attendee accepts, Slot re-runs normal availability checks before moving the existing event. For a paid cancellation, Slot creates an idempotent full refund using the stored Stripe PaymentIntent before finalizing cancellation. Refund webhook events keep the D1 refund status current. If the configured Stripe mode does not match the payment's Checkout Session mode, Slot refuses the paid cancellation rather than cancelling without a safe refund path. Every booking also has a separate high-entropy public booking ID (`SLT-...`). It is shown to the attendee on confirmation and written into the Google Calendar event description. The public cancellation endpoint accepts only this reference, rate-limits attempts, requires a future confirmed booking, and then delegates to the same cancellation/refund function used by Admin. The internal UUID remains the relational key and is not used as the guest-facing cancellation reference. ## Activity history Slot writes lifecycle events to `activity_events` after successful booking, payment, reschedule, suggestion, refund, cancellation, and payment-hold transitions. Idempotency keys suppress duplicate webhook events. Activity writes are deliberately non-blocking so analytics can never make a booking or refund fail. The Admin Activity endpoint computes aggregate booking metrics from the booking table and reads the latest activity events for the chronological feed. Monetary totals stay grouped by currency. ## Optional paid durations Stripe is optional. When Stripe payments are enabled, `paymentFreeDurations` can mark selected configured meeting lengths as free. Booking creation decides whether Checkout is required from the selected duration. Free bookings take the normal immediate-confirmation path; paid bookings use the Stripe hold/Checkout/fulfillment path. ## Public booking management Confirmed bookings expose a high-entropy public `SLT-...` reference. The public Manage booking flow uses that bearer reference to load limited booking metadata and lets the attendee cancel or reschedule an upcoming confirmed booking. Rescheduling preserves duration and existing payment state, rechecks live availability, and updates the existing Google Calendar event. Public management endpoints are rate-limited.