The problem: phone, paper and memory
A salon’s real bottleneck isn’t marketing — it’s operations. Every phone booking is a chance to double-book a specialist. Every multi-session package — a laser course sold as “eight sessions plus two,” say — needs someone to remember, visit by visit, how many are left. Every month-end, income and expenses get reconstructed from receipts and guesswork. And every reminder sent by hand is one a busy team eventually forgets. None of this needs an enterprise platform; it needs software shaped around exactly how this business works — and built so the whole team, not just one person, can drive it.
The shape of the system
Two halves share one database. The public side is a fast, mobile-first booking page. The private side is a full admin panel behind login, covering roughly ten working areas: a dashboard, appointments, clients and client detail, session packages, services and pricing, the team, roles and permissions, finance, business analytics, and settings. It is deliberately built as plain PHP on MySQL with no framework — so it drops onto any ordinary shared host, starts instantly, and never carries a dependency the salon has to maintain or pay for. A one-time installer creates the schema and seeds the catalogue; from then on the salon owns everything.
A booking flow that can’t double-book
Clients book themselves in four steps — treatment → specialist → date & time → contact — with available times fetched live and shown behind skeleton loaders while they compute. The availability engine is the heart of it: for a given service and day it reads the service’s duration, the salon’s working hours and closed days, and every existing appointment for each eligible specialist, then returns only the slots that are genuinely free. “No preference” resolves to a concrete specialist at submission time.
The important part is that none of this trusts the browser. When a booking is submitted, the server re-validates the slot against the live schedule — if someone grabbed it in the meantime, the request is rejected cleanly rather than written as a clash. The client is matched or created by a normalised phone number, a light per-session rate limit blunts abuse, and every online request lands as pending until a staff member confirms it, so no stranger can lock a specialist’s calendar without a human check.
Session packages, the salon’s way
Salons sell courses far more than single visits, so the package ledger mirrors that directly. On a client’s profile you add a package — a name, a session count, a price — and after each visit tap Use session; an animated progress ring shows what remains, and a one-tap undo reverses the inevitable mis-click. Each draw-down is its own ledger row, not just a decremented counter, so the history stays auditable. It sounds minor, but it is exactly the feature that decides whether staff live in the system every day or quietly slip back to a notebook.
Money, tracked properly
The finance module (manager-only by default) records payments by method — cash, card, transfer — and expenses by category from a curated list (products, rent, salaries, marketing, equipment and more, plus a free-text “other”). Because charges come from both completed visits and packages, every client profile carries a live balance: paid, owed, or in credit. The month view rolls the lot into income, spending, net result and an expense-by-category breakdown — the numbers an owner actually needs, generated instead of reconstructed.
On top of that sits a staff-commission layer: a payment can be split across team members by a configurable rule (a percentage or a fixed amount), stored per payment so month-end pay is defensible line by line, with a back-fill routine that keeps older records consistent when the rule changes. Client selection throughout uses a live name-or-phone autocomplete, and the form refuses to save against a client who wasn’t picked from the list — a small guard that prevents a whole class of mis-keyed records.
The business, by the numbers
A dedicated analytics screen turns the same data into decisions. It reports the appointment funnel (pending, confirmed, completed, cancelled), new versus returning clients as a retention signal, average ticket, revenue split by payment method, and per-specialist commission totals — each compared against the previous period with an up/down delta. Two trends round it out: bookings over recent months, and the busiest day of the week, computed straight from the calendar so staffing follows demand rather than hunches.
Reminders that send themselves
Nobody at a local salon checks email for a reminder — they check WhatsApp. Every appointment carries a one-tap WhatsApp button that opens a pre-filled message (an editable template with name, service, date and time placeholders) straight to the client. For salons that want it hands-off, the system also speaks the WhatsApp Cloud API directly: an hourly cron job messages tomorrow’s confirmed appointments using an approved template and flags each one as reminded so it never double-sends. The cron endpoint is gated by a secret key checked in constant time, and the one-tap buttons stay as a manual fallback — automation that degrades gracefully instead of failing silently.
Granular roles, enforced on the server
Access is governed by a capability catalogue grouped by area, with a separate view and manage permission for each — so a staff member can, for example, be allowed to see finance without being able to record or delete a payment. An admin is always full-access (locked on); staff and supervisor roles are edited independently from a dedicated Roles page and stored as their own permission maps. The defaults are safe: a new staff account sees appointments, clients and services (read-only) while finance, settings and team are hidden entirely, and a switched-off area disappears from the menu completely.
Crucially, every restriction is enforced server-side, not by hiding a button — the page itself refuses the action — and no staff account, even one granted team management, can promote itself or anyone else to admin. Privilege escalation is closed by design, not by convention.
Built like a product, not a script
Security is layered rather than sprinkled: parameterised PDO prepared statements with emulation disabled across every query, CSRF tokens on all state-changing forms verified with a constant-time compare, httponly and SameSite session cookies, and a hardened .htaccess that blocks direct access to internal includes. The whole interface is genuinely bilingual — a key-based translation layer with English fallback covers every screen, message and validation string, and even localises weekday and month names, switchable per user without reloading state. And the brand isn’t bolted on: the lavender-sprig motif, the official logo and the green-and-gold palette are hand-coded as SVG right down to the favicon, with every animation respecting the visitor’s reduced-motion preference. Product design, scheduling logic, accounting, permissions, i18n and deployment — end to end by one developer, shipped and refined across five production versions as the salon grew.
Stack & scope
Custom PHP 8 + MySQL on standard shared hosting, no framework dependency; a from-scratch availability and scheduling engine; a booking API, a session-package ledger, a full accounting and commission module, a business-analytics screen, WhatsApp Cloud API automation, a granular server-side permission system, and a bilingual EN/TR interface — roughly 8,000 lines of application code over twelve database tables, delivered as five iterative versions in production.
Building an operations system for your own business — bookings, clients, packages or accounting? Let’s talk.
Frequently asked questions
How does the system prevent double-booking?
An availability engine builds each day’s open slots from the service duration, the salon’s working hours and closed days, and every specialist’s existing appointments — so only genuinely free times are offered. On submission the server re-validates the chosen slot against the live schedule and rejects it if it was just taken, and online requests stay pending until a staff member confirms them. Nothing trusts the browser.
How are multi-session packages handled?
Each client profile has a package ledger: add a course (name, session count, price), then tap “Use session” after each visit. An animated progress ring shows what’s left and a one-tap undo reverses a mistake. Every draw-down is a separate ledger row rather than a bare counter, so the visit history stays auditable.
How do the automatic reminders work?
The system integrates the WhatsApp Cloud API directly. An hourly cron job sends an approved template message to every confirmed appointment scheduled for the next day and marks each one reminded so it never sends twice. The cron endpoint is protected by a secret key compared in constant time, and one-tap manual WhatsApp buttons remain as a fallback.
How is staff access controlled, and are there monthly fees?
Permissions use a capability catalogue with separate view and manage rights per area, enforced server-side rather than by hiding buttons; staff default to a safe read-mostly set with finance, settings and team hidden, and no one but an admin can grant admin. There are no recurring fees: it’s custom PHP + MySQL on the salon’s own shared hosting, so they own the code and the data outright.