Eisstock Soest – Buchungssystem für den Weihnachtsmarkt
Stundenweise buchbare Eisstockbahnen und beheizte Gondeln auf dem Soester Weihnachtsmarkt – mit Stripe-Anzahlung, Multi-Slot-Buchungen und einem Admin-Bereich für die Standcrew.
Idee
Auf dem Soester Weihnachtsmarkt gibt es zwei Eisstockbahnen und eine Reihe beheizter Gondeln, in denen sich Gruppen für ein, zwei Stunden in der Vorweihnachtszeit treffen können. Bisher lief die Reservierung über Mails und ein Excel auf dem Stand-Tablet. Das funktioniert genau so lange, bis ein Donnerstagabend voll ist und niemand mehr weiß, ob die Anfrage von Frau Schmidt jetzt bestätigt war oder nicht.
Daraus ist eisstock-soest.de geworden: ein schlankes Buchungssystem, das aus dem Telefon-Tetris einen normalen Online-Vorgang macht.
Was Besucher:innen tun
- Termin und Uhrzeit auswählen, beliebig viele Stunden hintereinander oder verteilt über den Abend
- Pro Slot Bahnen (0–2) und Gondeln (0–2) wählen
- Optional Speisen/Getränke vorab einplanen
- 50 % Anzahlung via Stripe Checkout, Rest am Stand
- Bestätigungsmail mit Buchungsreferenz – an dieser Referenz zieht später die Standcrew die Reservierung in der Übersicht hoch
Wer alles auf 0 stellt (Bahnen, Gondeln, Essen) und nur ein Datum reserviert, wird trotzdem nicht durch den Bezahlflow geschickt – sondern direkt bestätigt.
Architektur-Entscheidungen, die was geändert haben
- Eine Buchung kann mehrere Time-Slots umfassen. Statt jeder Stunde ihre eigene Reservierung zu geben, hängen alle Slots einer Buchung an derselben
booking_reference. Die Bestätigungsmail fasst aufeinanderfolgende Slots automatisch zu Bereichen zusammen („2 Bahnen am 15.12. von 16:00–18:00 Uhr"), statt fünf separate Zeilen zu zeigen. - Optimistisch in der UI, pessimistisch beim Submit. Während der Auswahl zeigt Livewire die noch verfügbare Kapazität live an, ohne jeden Klick zu serialisieren. Beim eigentlichen Submit greifen
lockForUpdate()auf den Time-Slots: erst dann wird die Kapazität nochmal hart geprüft und die Reservierung geschrieben. Das vermeidet Doppelbuchungen, ohne die UX langsam zu machen. - Doppelte Zeitzone, klar getrennt. Datenbank in UTC, Anzeige in
Europe/Berlin. Eine Carbon-MacroinApplicationTimezone()ist die einzige Stelle, an der konvertiert wird – nirgendwo im Code rechnet jemand selbst mit Stunden. - Idempotente Confirmation-Mail. Stripe-Webhooks treffen schon mal doppelt ein. Das
booking_confirmation_sent_at-Timestamp am Reservierungssatz ist die einzige Quelle der Wahrheit für „Mail ist raus" – nochmal versendet wird nichts.
Stack
Laravel 13, Livewire 4 mit Flux-Komponenten, Tailwind 4. Auth über Fortify inkl. 2FA für die Standcrew, Mailgun für die Zustellung, Stripe Checkout für die Anzahlung. Pest für die Tests, SQLite in der Entwicklung, MySQL im Betrieb.
Der Admin-Bereich hat drei Bildschirme: Time-Slots in Bulk anlegen (Datum von–bis × Uhrzeit von–bis), Reservierungen filtern nach Status/Bezahlung/Zeitraum und ein einfaches User-Management. Mehr braucht ein saisonaler Stand nicht.
Status
Live seit Frühjahr 2026, eingerichtet und für die Saison 2026 vorbereitet. Nächste Iteration: Storno-Handling über einen sicheren Link in der Bestätigungsmail und ein optionaler „Sammelrechnung am Saisonende"-Modus für den Veranstalter.