Track 2 · Operate

Meeting Room Bookings

Self-serve booking for seven rooms. Stripe PaymentIntent + one Google Calendar event per day, with double-gated availability and auto-refund.

Self-serve booking for seven rooms. Stripe PaymentIntent + one Google Calendar event per day. No customer confirmation email yet — see incident 1.

At a glance

Worker labour-temple-bookings.labourtemple.workers.dev
Deploy cd cloudflare-worker-bookings && wrangler deploy
Logs wrangler tail --format pretty
Room config rooms.js (pricing ¢, capacity, min hours, discount eligibility, BOOKABLE list)
Operating hours 8 AM – 5 PM Pacific, Mon–Fri (hardcoded)
Inter-booking buffer 15 min (hardcoded)
Booking window 24 h – 60 d out (hardcoded)
Multi-day max 10 weekdays per booking
Calendar auth Google service account JWT (no attendee invites possible)
Webhook Stripe payment_intent.succeeded/webhook

The two gates (memorize this)

Multi-day: one event per day. Any single day fails → delete all already-created events for that booking + auto-refund full amount.

A Stripe refund with no calendar event is not a bug — it's the safety net. Always check Worker logs first.

The five surfaces

  1. Stripe → Payments / Customers / Events (payment_intent.succeeded) / Logs
  2. Google Calendar → room calendars (one event per booking day)
  3. Worker logswrangler tail
  4. Browser → Console + Network on the room page
  5. rooms.js → authoritative for pricing, capacity, min hours, BOOKABLE

When to escalate


Incident playbooks

1. "I booked but got no confirmation email" — expected today

Track 2 does not yet send a customer confirmation email. The customer has only the on-screen redirect to /meeting-booking-confirmation + the Stripe receipt.

Fix. Send a manual confirmation from admin@labourtemple.com with date, room, time, cancellation policy. Do not tell them to "check spam" — there's no email to find.

Priority. A MailerSend confirmation for Track 2 is a documented follow-up (see roadmap.md). Meanwhile, keep a manual-confirmation template in the team inbox.

2. Stripe payment succeeded, no calendar event

Check.

  • Worker logs filtered by PaymentIntent ID. Look for: "Gate 2 conflict" / "availability check failed" / "Google Calendar event creation failed" — in each case, an auto-refund should have fired.
  • Stripe → Developers → Events → find the payment_intent.succeeded → check delivery. Failed/retrying = Worker had a transient error.

Fix.

  • Auto-refund already fired → email customer: slot was taken at the moment of payment, card refunded, offer help finding an alternative.
  • Webhook didn't reach us → Stripe → Developers → Events → "Resend" the event; Worker will process.
  • Last resort → manually create the calendar event from PaymentIntent metadata (booking_dates, booking_time, customer info).
3. Calendar event exists, customer has no confirmation

Same as #1 — we don't send an email yet. Send manually.

4. Multi-day booking — some days on calendar, others missing

Check. Worker logs filtered by PaymentIntent ID.

  • "Multi-day partial failure — cleaning up" → atomic cleanup fired. Confirm all events deleted and refund issued.
  • Orphan events and no refund → cleanup itself failed. Worst case.

Fix.

  • Cleanup + refund both succeeded → email customer and offer rebook.
  • Cleanup failed → delete orphans manually + refund manually in Stripe + email customer.

Escalate to Chris same-day if cleanup failed — indicates a code path that needs hardening.

5. Double-booking on the calendar

Check. Was one event added manually? Our Worker can't see manual additions — no race, just a human mistake.

If both were Worker-created, check log timestamps; both gates should have prevented this.

Fix. Contact one customer (usually the later or lower-commercial-value one) with goodwill gesture + rebooking offer. Refund if they can't rebook. Report to Chris for RCA.

Prevention. Do not add manual Google Calendar events to any bookable room's calendar. Use a separate "internal" calendar for non-paid events.

6. Calendar shows no availability when it should

Check. Is the date within 24 h – 60 d? Is it a weekend (disabled)? Is it a Canadian holiday (currently not handled — customer can book, but space may be closed)? Check Google Calendar directly for capacity.

Fix. Suggest alternatives. If the day is staff-blocked by a manual calendar event, that's expected.

7. Customer wants to cancel

No self-service cancellation.

Fix. Confirm booking details → agree refund amount per policy → refund in Stripe (note reason if partial) → delete Calendar event → email confirmation.

Policy to publish. Typical: full refund 48+ h out, 50% 24–48 h, none under 24 h. Post near the booking form. Handle case-by-case until published.

8. Chargeback / dispute

Fix. Respond via Stripe Dashboard with: booking metadata, calendar event (showing the slot was reserved), published cancellation policy, any emails. Stripe + card network decide.

Dispute fee is $15–25 per case regardless of outcome. Losing also costs the original amount. Respond before Stripe's deadline.

9. Sonic Studio booking missing extras

Check. Did the customer actually see the Sonic extras on the form? Extras are triggered by the Sonic slug — if slug or JS is wrong, extras don't render.

Fix. Email the customer to gather podcast/show name, group size, set choice. Update the Calendar event description.

10. CORS / "Failed to fetch" on submit

Happens when a new origin (staging, preview) hits the Worker without being in ALLOWED_ORIGINS.

Fix. Add the origin to ALLOWED_ORIGINS in worker.js and redeploy.