The Aurelle
The Aurelle - a booking platform for a fictional luxury hotel, with server-side pricing, double-booking protection, payments, table reservations and a staff console. Built with Laravel 13 and React.
- Pet Project
- Hospitality
- Booking Platform
The Aurelle is a full booking platform for a fictional five-star hotel on the river in Porto. Guests can book a room and pay for it, reserve a table at the restaurant, and manage their stays. Hotel staff run reservations, rooms, pricing and the restaurant from a staff console. The hotel, its brand and its guests are invented.
At a glance
- Role: Solo full-stack developer. I did the product design, backend, frontend, testing and deployment.
- Stack: Laravel 13, PHP 8.4, Inertia v3, React 19, TypeScript, Tailwind CSS v4, shadcn/ui, Pest
- Quality: 322 automated tests (1,317 assertions), PHPStan level 7, Pint, linting and type checks, all run in CI
- Live demo: aurelle.msar.dev
The problem
Independent luxury hotels pay large commissions to booking sites, and their own websites often hand guests off to a clunky third-party booking engine. The goal was a single platform where:
- guests can search, price and pay for a stay directly, and book a table in the same place
- the hotel never sells the same room twice, even when two guests book at the same moment
- prices are always correct and can't be changed from the browser
- staff see every booking, payment and table from one console, and each person sees only what their role allows
Key features
Booking engine: Guests search by dates and party size, choose a room and rate plan (flexible, bed & breakfast or non-refundable), add extras, and pay. Starting a booking holds the room for 30 minutes. A scheduled job releases holds that are never paid.
Server-side pricing: Every price is calculated on the server in integer cents, in a fixed order: nightly rate (seasonal, weekend or base), rate plan, supplements, offer, extras, service charge, then tax. The browser only shows quotes and is never trusted to set a price. Each booking stores a snapshot of its prices and terms, so later price changes don't alter existing bookings.
Double-booking prevention: Every booking runs inside a database transaction. The transaction locks the room type and candidate rooms (SELECT … FOR UPDATE), re-checks availability, re-calculates the price and only then assigns a physical room.
Payments: Payments go through a gateway interface with two providers: Stripe Checkout and a sandbox for demos. When a guest returns from the provider, the app asks the provider for the real result instead of trusting the redirect. Webhooks are signature-verified, results are applied idempotently, and the amount paid is checked against the total. If a payment arrives after the hold expired and the room has since been sold, it is refunded automatically.
Restaurant tables: Each service period has its own seating grid and dining duration. A booking gets the smallest free table that fits the party, unless the guest asks for a particular seating area.
Staff console: Includes a dashboard (occupancy, arrivals, revenue, room status) and reports. It also covers reservations with check-in, no-show, cancel and refund, plus guests, payments, rooms, rate plans, seasonal rates, extras, offers, table bookings and site settings.
Permissions: Roles and permissions are built from scratch: 16 abilities enforced through Gates and Policies. The super-admin role is locked, staff can't widen their own access, and deactivated accounts can't sign in. Staff accounts support two-factor authentication and passkeys.
Technical challenges
Two guests, one last room. Checking availability and then inserting the booking leaves a gap where two requests can both succeed. Booking creation locks the relevant rows, re-checks availability and re-prices inside a single transaction. Availability uses a half-open date overlap rule, so a guest can check in on the day another checks out.
Payments that arrive late or twice. Redirects can be faked and webhooks can arrive late, twice or out of order. Payment results are verified with the provider, applied idempotently under a row lock, and checked against the expected amount. A late payment for a room that has since been sold is refunded automatically.
Prices nobody can tamper with. The booking form sends only the stay details: dates, guests, rate plan and extras. The server recalculates the full price on every quote and again when the booking is created.
A production-only database bug. One auto-generated index name was 67 characters long, but MySQL allows only 64. The tests run on SQLite, which has no such limit, so the bug first showed up in production. I gave the index an explicit name and added a schema test that fails if any index name exceeds the limit, so the same bug can't come back.
Testing & quality
- 322 Pest tests, 1,317 assertions, covering availability, pricing, the booking flow, payments, cancellations, access to reservations, table booking, the staff console and auth
- PHPStan level 7 (Larastan) across the app, config, database and routes
- Laravel Pint for PHP style, plus linting, formatting and
tscfor the frontend - GitHub Actions runs every check on pushes to main and on pull requests
Architecture




The Aurelle is a fictional brand. Photography is from Unsplash.
Technologies
Gallery









