Portfolio Details
FitZone — Gym Management SaaS
A multi-tenant SaaS platform that runs a gym end to end: members, subscriptions, attendance, point of sale, shifts and cash drawers, equipment maintenance, and decision-ready analytics — for a single branch or a whole chain. Built as an API-first Laravel back end where every gym gets its own isolated database.
FitZone is a gym management SaaS built with Laravel 13 as a pure JSON API, serving two surfaces: a central admin panel for the platform owner and a tenant dashboard for each gym's staff. Every gym is provisioned with its own MySQL database, so data isolation is enforced by the database itself rather than by a shared tenant column.
The platform covers 13 integrated modules. Members get auto-generated codes, QR codes and Code128 barcodes, plus a public profile page they open by scanning their QR — with the phone masked and no financial data exposed. Subscriptions support the full lifecycle: freezing with automatic day compensation, renewals that extend from the old end date, upgrades with prorated credit, transfers between members, daily passes, and a preview of the amount and new dates before any change is committed. Every subscription keeps a pricing snapshot, so later price changes never rewrite history.
Attendance can be recorded by member code, card number, barcode, QR or phone, with clear denial reasons and expiry calculated in each branch's timezone. The POS module handles per-branch stock, barcode/SKU sales, stocktakes, a full stock-movement ledger and profit reports, while shift management reconciles every payment method — from both POS and subscriptions — in a single drawer.
On top of the operational reports sit 23 analytics reports such as churn risk, revenue forecast, plan performance, drawer accountability and branch comparison — each shipped with a written explanation of what it measures and how to read it. Access is governed by 40+ granular permissions, and the codebase is covered by 648 end-to-end tests running against real MySQL and Redis.
The core challenge was building a true database-per-tenant architecture: resolving the right gym on every request, provisioning a new database with its migrations, roles and default data on sign-up, and keeping migrations, permissions and caches in sync across every tenant.
The business rules were just as demanding. Subscription money flows — renewals, prorated upgrades, freezes, refunds and transfers — had to stay accurate and auditable, cash drawers had to reconcile POS and subscription payments together, and sensitive figures like purchase cost and profit margin had to be hidden from front-desk roles without relying on the UI.
I built a header-based tenancy layer that switches the database connection per request, with Artisan commands for provisioning, tenant migrations and RBAC syncing. Business logic lives in dedicated services and pipelines, every compound operation runs inside a database transaction, and pricing snapshots plus a full audit log keep every financial event traceable.
Sensitive fields are stripped at the API resource level based on permissions, a database constraint guarantees one open shift per branch, Redis tagged caching keeps dashboards fast, and scheduled jobs expire subscriptions daily with a lazy-expiry safety net on check-in. WhatsApp notifications are wired to real system events, and the whole platform is bilingual (Arabic and English).
Key Features
Multi-Tenant SaaS with a separate database per gym
Full subscription lifecycle: freeze, renew, prorated upgrade, transfer and refund
Attendance by QR, barcode, card number, member code or phone
POS with per-branch stock, stocktakes and profit reports
Shift and cash-drawer reconciliation across all payment methods
Equipment assets and preventive maintenance tracking
23 analytics reports, including churn risk and revenue forecast
40+ granular permissions with Sanctum single-session auth
Audit log and pricing snapshots for every financial event
WhatsApp notifications tied to system events
Arabic & English localization
648 end-to-end tests on real MySQL and Redis