In Progress

Kolpa Adventures Booking

Online rafting booking platform for a Slovenian outfitter, with Stripe payments and FURS-compliant fiscal receipts.

Next.jsNext.js
TypeScriptTypeScript
Tailwind CSSTailwind CSS
Kolpa Adventures Booking

Overview

Kolpa Adventures Booking is an online reservation platform for a Slovenian river-rafting outfitter. Guests pick a date and time slot, pay online with Stripe, and receive a confirmation email together with a FURS-verified fiscal receipt as required by Slovenian tax law. Staff manage bookings, schedules, pricing, and promo codes from a dedicated admin panel.

Key Features

  • Bilingual booking flow (SL/EN) with URL-prefixed locales and a 6-step guided checkout.
  • Slot-based capacity with race-safe seat allocation under concurrent checkouts.
  • Stripe payments via embedded Elements with 3DS/SCA handled automatically.
  • Slovenian fiscal receipts issued through Minimax and verified by FURS, attached as a PDF to the confirmation email.
  • Admin panel for bookings, walk-in entry, calendar, schedule overrides, promo codes, versioned pricing, and GDPR marketing exports.

Technical Implementation

  • Framework: Next.js (App Router) with Server Actions, React Server Components, and TypeScript in strict mode.
  • Styling: Tailwind CSS with shadcn/ui primitives and custom brand tokens.
  • i18n: next-intl with URL-prefixed Slovenian and English locales.
  • Database & Auth: Supabase Postgres with Row Level Security and magic-link auth for staff.
  • Payments: Stripe Payment Intents with an idempotent webhook for paid-state transitions.
  • Fiscalization: Minimax REST API (FURS ZOI/EOR signing) with an inline retry path and a daily cron worker for failures.
  • Email: Resend with React Email templates in both languages.
  • Hosting: Vercel.

Challenges & Solutions

The hardest part was guaranteeing that two guests racing for the last seat in a slot can't both succeed. Solved with a Postgres advisory lock keyed on activity + date + hour, taken inside the capacity-recheck function so the check and the booking commit are atomic across instances.

Fiscal issuance is also fragile by nature — Minimax or FURS can be momentarily unavailable. The system retries inline three times with exponential backoff; if that still fails, the confirmation email is sent without the PDF and a background cron retries daily until the receipt issues, after which a deferred email delivers the PDF.