Home Our Work VenueHarbour

Hospitality · Restaurant Technology

VenueHarbour
Restaurant Platform

A complete restaurant operating system built from scratch for La Mar restaurant in Marbella — covering guest bookings, real-time floor plans, kitchen orders, table-side payments, staff attendance with facial recognition, and loyalty programmes. Designed to licence to other venues as a SaaS product.

Discuss a project → Our custom software service

69

Database Tables

~100

Vue 3 Components

60+

Service Classes

15+

Sprint Cycles

The Challenge

One restaurant. One platform. Built from the ground up.

La Mar, a restaurant and bar in Marbella, ran on disconnected phone bookings, paper rotas, and separate payment systems. They needed a unified platform where guests book online with Stripe card holds, kitchen staff work from a live display, floor managers see real-time table states, and the whole operation connects from booking through to fiscal compliance.

Nothing off-the-shelf could handle the combination of Spanish Verifactu tax law, multilingual guest flows, real-time Pusher-powered floor plans, and the ambition to licence the system to other venues under the VenueHarbour brand. Every component had to be engineered from scratch — purpose-built for the way La Mar actually operates.

What Was Delivered

  • Online booking widget with Stripe SCA/3DS card holds and bilingual confirmation email + PDF
  • Real-time interactive floor plan (Pusher — table colours shift as KDS tickets complete)
  • Kitchen Display System: 4-state lifecycle with compare-and-swap concurrency protection
  • Waiter tablet PWA (offline-first, Dexie.js IndexedDB)
  • Bar POS with quick-sale keypad and Verifactu fiscal SHA-256 hash chain (Spain AEAT)
  • Azure Face API staff kiosk — clock-in by facial recognition (GDPR-safe: only person_id stored)
  • Apple Wallet + Google Wallet subscription passes — TOTP QR codes regenerate every 60 seconds (replay-attack protected via lm_pass_totp_log)
  • Loyalty programme with SELECT FOR UPDATE anti-double-spend and immutable append-only ledger
  • Guest CRM: booking history, dietary flags, VIP status, NPS surveys (4-star+ → auto Google Reviews prompt)
  • Group reporting across all venues: covers, revenue, NPS, voucher, loyalty metrics
  • VenueHarbour SaaS self-signup with Stripe Connect (white-label for other venues)
  • Public multilingual website (EN/ES/FR/DE/NL) with language-aware transactional emails
  • Check & Bill Management — split-by-seat, split-by-item, and equal-split modes; card payments via Stripe and cash payments; house tabs for bar accounts; reopen closed cash checks; automatic VAT breakdown by rate on every receipt; integrates directly with the KDS and waiter app
  • Guest QR Table Ordering — guests scan a QR code at the table, receive a 1-hour session token, browse the full menu with modifiers, order and pay entirely self-service; no waiter required; handled via lm_qr_order_service.php with rate-limited session tokens
  • Waitlist — FIFO queue for fully-booked periods; when a booking cancels the next guest is notified by email and SMS with a time-limited acceptance window; if unclaimed the slot passes to the next guest automatically; race condition handled via lm_slot_holds UNIQUE KEY
  • Event Bookings — separate booking flow for special events (set menus, dinners, ticketed evenings); capacity-limited per event; prepayment captured immediately, not held; bilingual EN/ES event descriptions; configurable refund policy and buffer time per event
  • Gift Vouchers — guests purchase vouchers online; unique codes generated per voucher; partial redemption tracked across multiple bookings (a €200 voucher can be spent across visits); full redemption audit trail with fraud prevention
  • Cancellation Policy Engine — per-venue configurable free-cancel window, late-cancel charge percentage, and no-show charge percentage; Stripe hold captured automatically when any condition triggers via Lamar_Cancellation_Service
  • Promo Codes & Win-back Campaigns — staff create promo codes with value, percentage, or free-cover discounts (lm_promo_codes table, Lamar_Promo_Code_Service); win-back email sequences target lapsed guests via Lamar_Winback_Service and the existing notification queue
  • React Native Staff App (Android) — a separate native Android app for venue staff built with React Native, Expo, and WatermelonDB; provides offline-first access to bookings, orders, checks, and the KDS from a mobile device; delta sync keeps local data current without full downloads
  • Staff Shift Scheduling — managers build and publish weekly rotas on a CSS-grid calendar; staff confirm shifts from their own devices; late arrivals flagged automatically based on a configurable grace period; payroll data exports as CSV
  • Booking Channel Attribution — every booking records its source (website, Google, Instagram, Facebook, TripAdvisor, walk-in, phone); group and venue reports break down covers and revenue by channel
  • Delivery Zones — polygon-based delivery zone management drawn on a Google Maps canvas; delivery orders geocoded and validated against zone polygons; per-zone minimum order value and delivery fee configured by venue managers
  • Staff Messaging — org-scoped cross-venue chat; each staff member subscribes to a private Pusher channel (private-user.{id}); messages delivered in real time to sender and recipient; accessible from the staff dashboard

Tech Stack

Every tool chosen for a specific reason — not defaults.

Backend

PHP 8.2 WordPress MU-plugins

Frontend

Vue 3 Vite Pinia Tailwind CSS

Mobile

React Native Expo WatermelonDB

A separate Android staff app gives venue staff offline-first access to bookings, orders, checks, and the KDS from their mobile device. Built with React Native, Expo, and WatermelonDB — delta sync keeps local data current without a full download on every shift.

Database

MySQL 8.0 Dexie.js (IndexedDB) WatermelonDB

Real-time

Pusher

Payments

Stripe (holds + SCA/3DS + Connect)

Biometric

Azure Face API

Passes

Apple Wallet (.pkpass) Google Wallet JWT TOTP (RFC 6238)

Email

SendGrid

Hosting

Google Cloud VM Cloudflare CDN

PDF / QR

mPDF endroid/qr-code

Key Features

Major systems built.

Facial Recognition Staff Clock-In

Staff tap their face at a tablet kiosk to clock in and out. Powered by Azure Face API with a 0.85 confidence threshold. Critically, only a person_id is ever stored — no raw biometric image or facial embedding touches the database. This is the hard architectural guarantee: biometric data stays inside Azure, and our system only ever sees an opaque identifier it cannot reverse-engineer.

A PIN fallback with bcrypt hashing and a 5-attempt lockout ensures access is never blocked if lighting or camera angle causes a failed match. Fully GDPR-compliant by design — not by policy — because there is simply nothing biometric to breach.

Azure Face API 0.85 confidence threshold GDPR-safe bcrypt PIN fallback

Apple & Google Wallet Passes with Rotating QR Codes

Subscription passes are issued as native .pkpass files (Apple Wallet) and Google Wallet JWT tokens. The QR code inside regenerates every 60 seconds using TOTP (RFC 6238 — the same standard as two-factor authentication apps like Google Authenticator).

A screenshot of someone else's pass is worthless 60 seconds later. Replay attacks are blocked by the lm_pass_totp_log table which records every scan with a timestamp — if the same TOTP window is presented twice, it is rejected regardless of how fresh the code appears.

Apple Wallet (.pkpass) Google Wallet JWT TOTP RFC 6238 60s rotation replay-attack proof

Real-Time Floor Plan

Managers build their table layout with a drag-and-drop builder. Staff see a live view where table tile colours shift from red → green as KDS tickets are completed — giving a visual pulse of how busy each table is and where they are in their meal. Powered by Pusher private channels, updating within milliseconds of a kitchen action.

Kitchen Display System

Orders from the waiter app appear instantly on the KDS. A 4-state lifecycle (new → preparing → ready → completed) with compare-and-swap concurrency protection ensures two kitchen staff can't accidentally bump the same ticket simultaneously. An offline queue backed by Dexie.js handles network disruptions without losing a single order.

Stripe SCA/3DS Card Holds

Bookings capture a card hold, not an immediate charge. The hold is released on cancellation or captured at arrival. Every EU transaction enforces 3D Secure. Idempotency keys (lm_pi_{booking_id}_{action}) prevent duplicate charges even if the Stripe webhook fires twice. Holds approaching the 7-day limit are automatically re-authorised via WP Cron.

Verifactu Fiscal Compliance

Every sale writes an immutable record to lm_fiscal_records with a SHA-256 hash chained to the previous entry — satisfying Spain's AEAT Verifactu requirements. S0 mode (hash-only) is live in production. S1 SOAP transmission to AEAT is code-complete, pending the mTLS certificate from the tax authority.

Guest CRM + NPS Surveys

Full guest profiles: complete booking history, dietary and allergy flags, VIP status, internal staff notes, tags, and loyalty balance. Post-visit NPS emails fire automatically 24 hours after arrival. A 4-star or above response triggers an automatic prompt to the Google Reviews link — converting satisfied guests into public reviews without any manual follow-up.

Loyalty with Anti-Double-Spend

An immutable, append-only loyalty ledger records every earn and redeem event — rows are never updated, only inserted. SELECT FOR UPDATE in every redemption transaction prevents concurrent requests from creating negative balances — the database-level equivalent of a mutex. Earn rates are configurable per venue, decoupled from the booking flow via WordPress action hooks.

System Architecture

How the layers connect — from guest browser to MySQL VM.

Guest browser → Cloudflare (CDN + DDoS + SSL)
  → Google Cloud VM (Nginx + PHP 8.2)
    → WordPress MU-plugin loader (lamar-loader.php)
      → 60+ Service Classes → $wpdb->prepare()
        → MySQL VM (dedicated, 10.154.0.11)

// Vue 3 front-end layer
Vue 3 AJAX → admin-ajax.php
  → check_ajax_referer()
  → Service class
  → MySQL

// Real-time layer
Pusher private channels
  → Floor plan · KDS · Checks · Messaging

// Payment layer
Stripe → SCA/3DS holds → Webhooks
  → Booking lifecycle state machine

// Biometric layer
Azure Face API → person_id only
  → lm_staff_faces → clock_events

// Wallet pass layer
Apple Wallet (.pkpass)
Google Wallet JWT
  → TOTP (60s rotating QR)
  → lm_pass_totp_log (replay prevention)

Engineering Decisions

No raw biometric data stored

Azure Face API person_id only — GDPR compliance by architecture, not policy. There is nothing biometric to breach.

Pusher fire-and-forget via wp_remote_post

Non-blocking — Pusher events are dispatched without waiting for a response, so real-time updates carry zero latency overhead on the request that triggered them.

SELECT FOR UPDATE on all loyalty redemptions

Database-level mutex — not an application lock. Concurrent redemption requests queue at the row level; no race condition can create a negative loyalty balance.

TOTP passes use RFC 6238

The same standard as Google Authenticator and Authy — a proven, time-tested algorithm for time-based one-time passwords with robust replay protection.

Stripe idempotency keys on every mutation

Safe webhook retry by design. If Stripe fires a webhook twice due to a timeout, the second call is rejected at the API level — the charge never happens twice.

PHP MU-plugins as application framework

WordPress is the infrastructure layer — database, auth, cron, admin. The 54 service classes are the application. No plugin activation required; the system loads unconditionally on boot.

Scale at a Glance

The numbers behind the platform.

A genuinely large-scale PHP application — not a theme with some plugins on top.

69

Database Tables

2,400+

Translation Keys

90+

DB Migrations

5

Guest Languages (EN/ES/FR/DE/NL)

Ready to build something like this?

Have a complex operations problem
that needs a bespoke solution?

We build production-grade systems — not prototypes. Tell us what you need to automate, connect or replace, and we'll tell you exactly how we'd build it.

Start a conversation → Our custom software service