A Booking Platform, Not a Brochure
Waterfall Adventure Center is a rafting and adventure center at the confluence of the Tara and Piva rivers, in Šćepan Polje in northern Montenegro. They offer single-day and multi-day rafting and canyoning tours, hiking, and riverside accommodation to guests from around the world.
They didn't need another website with beautiful photos and a contact form. They needed a platform that takes real card payments, calculates actual tour prices with all their variables, and works in multiple languages. And one the owner can run independently, without calling a developer at every step.
That's exactly what we built: a single Laravel + Vue application that combines a fast public website, a serious booking engine, and an admin panel with nearly 55 screens.
The Challenge
Adventure tourism has a few things a regular website simply can't handle:
- Real payments. Tours are paid online, with a deposit at booking and the balance later. That means real money, EU regulation, and 3-D Secure, where mistakes aren't an option.
- Complex pricing. Per-person or per-group rates, separate pricing for children, add-on activities, packages, accommodation, and local taxes per tour. All of it has to calculate correctly, in real time.
- Multiple languages. Guests come from a dozen countries, so the entire site, both the interface and the content, needs to work in their language.
- Owner independence. The owner updates tours, prices, seasons, and content daily. That can't depend on a developer.
What We Built
One Laravel application with two faces: a fast, server-rendered public website and a full-featured admin panel built as a single-page application (Inertia + Vue 3). The numbers that describe the scope:
8 languages · ~55 admin screens · real card payments (3-D Secure) · 36 data models · ~88,000 lines of code · CI/CD to production.
The project was built across around 202 commits over three months, with automated testing and deployment, and runs in production behind Cloudflare.
The Booking Engine, the Heart of the Platform
A booking is completed in two steps: details first, then secure payment. The guest selects a tour, date, number of adults and children, adds accommodation if they want, and sees the live price on the right, broken down line by line.

What makes this system genuinely capable:
- Real-time availability checks, tour capacity and available accommodation units for selected dates, with cancelled bookings excluded. A reservation never blocks itself when staff moves it to a different date.
- Transparent pricing, for example a single-day Tara rafting tour shows the per-person rate, a separate rate for children, and local taxes (Rafting Tax, Piva Nature Park Tax) as individual line items.
- Deposit and balance flow, a deposit (default 20%) is charged at booking, and the balance is collected later.
For more on why direct bookings are worth more than relying on OTAs, read our post on direct bookings vs Booking.com.
Card Payments, Done Right
This is the part where most websites stop. We took it all the way.
- Bankart gateway via Payment.js hosted fields: the site never touches raw card data, with full 3-D Secure challenge support.
- Amount integrity: the charged amount always comes from the database, never from the browser request. Users can't manipulate the price.
- Idempotent confirmation: a signed webhook (HMAC-verified) confirms 3-DS payments and is safe to replay. A duplicate or repeated call can't create a double charge or corrupt the booking state.
- Pay-by-link: staff generates a link with a locked amount (deposit, balance, or a custom figure) and sends it to the guest by email.
An Operations Console, Not Just an Admin Panel
The admin panel isn't a list of records. A single booking's page is a complete back-office workspace in one place.

Staff can see and do everything from there: booking status, line items and price recalculation, deposit and balance state, Bankart payment status, payment link generation, Google Sheets sync, notes, and a full activity history.
Beyond that, there are unified availability calendars for tours and accommodation, an operational dashboard with a same-day arrivals and departures view, manual booking creation, and reports (bookings and financials, with CSV export).
A Real Pricing Engine
Pricing in tourism isn't a single number. We built a system that matches how the center actually operates:

- per-person or per-group rates
- separate rates for children (percentage or fixed discount)
- add-on activities and packages (group and with meals)
- accommodation surcharges and local taxes per tour
- seasonal pricing without year binding, a "Nov–Mar" window repeats every year, with a clear resolution order: fixed date > season > full year > base price
A CMS the Owner Runs Independently
Almost everything visible on the public website can be changed from the admin, no developer needed. That includes Site Settings (brand, contact, social media, SEO, analytics, booking rules), a tour editor with 7 tabs, accommodation management, seasons and surcharges, hero slides, a blog with a rich editor, legal pages, and content for the retreat and About pages.
Access is controlled: staff accounts with roles (super-admin, admin, agent, reception), optional two-factor login, and a full activity log that records who changed what and when.
Eight Languages, All the Way Through
The site is configured for 8 languages: English (the source), Montenegrin, German, French, Italian, Russian, Czech, and Polish. URLs are language-prefixed, with hreflang tags and an in-site language switcher.

Translations work on two levels: static interface strings, and a separate translations table for database content (tour titles, descriptions, itineraries, legal pages). Every entity in the admin has a "Translations" tab with a completion indicator, so the owner knows exactly what still needs translating.
Under the Hood
Backend: PHP 8.4, Laravel 12, Fortify (login + 2FA), Inertia.js 2, Spatie packages (permission, medialibrary, activitylog, honeypot), Google API client for Sheets, MySQL and Redis for job queues and cache.
Admin: Vue 3.5 + TypeScript, Tailwind CSS v4, reka-ui / shadcn-vue components, TinyMCE, vue-advanced-cropper for image cropping, Vite 7.
Public site: Blade + Alpine.js, Swiper for galleries, Flatpickr and GLightbox. Fjalla One and Albert Sans typefaces.
Quality and delivery: Pest 4 test suite (68 files) gating automated deployment via GitHub Actions.
Three things we're especially proud of:
- Payments with integrity guarantees: hosted fields, 3-DS, amount from the database, and idempotent confirmation mean a repeated call can never create a double charge.
- Year-agnostic seasonal pricing: windows repeat every year, with a deterministic priority resolution order.
- Multi-crop images: art direction via
<picture>with separate desktop and mobile crops, in AVIF/WebP format, sharp and fast on every device.
For a deeper look at our approach to building systems like this, see Website Development and System Integrations.
Scope and Results
| Item | Value |
|---|---|
| Data models | 36 |
| Database migrations | 104 |
| Controllers | 56 |
| Admin screens (Vue) | ~55 |
| Public page templates | 27 |
| Supported languages | 8 |
| Lines of code | ~88,000 |
| Test files (Pest) | 68 |
| Git commits | ~202 |
The site is live behind Cloudflare, accepting real bookings and charging card deposits. The owner manages tours, pricing, seasons, content, and translations across all 8 languages on their own.
Visit the Website
If you need a booking system that takes real payments and that you can run yourself, contact us and we'll put together a plan that fits your business. Browse our other portfolio projects.