Why Bookmysal0n
What makes it better
than the alternatives
- 01
Multi-tenant architecture
Multi-tenant architecture
- 02
UAE-specific configuration
UAE-specific configuration
- 03
No ORM, hand-written SQL
No ORM, hand-written SQL
- 04
Clean Architecture with full test coverage
Clean Architecture with full test coverage
- 05
Blazor Interactive Server for admin, static SSR for auth
Blazor Interactive Server for admin, static SSR for auth
Online Booking & Customer Portal
- Public booking page per salon at /book/{slug}: service menu
- professional selection
- date strip with available slots
- customer sign-in by email or UAE mobile code
- Customer authentication: 6-digit codes by email or SMS
- Google and Apple sign-in with account linking
- 60-day sliding cookie
- My Bookings page: view upcoming and past appointments
- reschedule (checks availability in transaction)
- cancel with reason
- pay deposit online
- Customer wallet: view credit balance
- top-up online
- transaction history
- Booking settings per salon: advance booking window
- cancellation deadline
- deposit percentage
- blocked days
- service visibility
- professional assignment rules
Calendar & Appointments
- Team calendar with day
- 3-day
- week and month views; one column per team member; drag-and-drop rescheduling with conflict detection
- Appointment statuses: Booked
- Confirmed
- In Progress
- Completed
- No Show
- Cancelled; change history with timestamp and user
- No double booking: every save re-checks the slot inside a database transaction (AppointmentRepository.IsSlotAvailableAsync with UPDLOCK)
- Blocked time: mark unavailable periods per professional (lunch
- training
- personal time)
- Appointment details: client
- services
- professional
- duration
- notes
- internal notes (staff only)
- deposit paid
- total price
- Automatic reminders: scheduled by MessageScheduler
- sent 24 hours before via SMS or email
Point of Sale & Checkout
- Checkout cart: add services (from appointment or walk-in)
- products
- custom items; apply discounts (percentage or fixed amount)
- promo codes
- tips
- Split payments: cash
- card
- online (Stripe)
- gift card
- package session
- membership
- wallet credit; one sale can use multiple tenders
- VAT calculation: configurable rate (default 5% UAE)
- applied to services and products
- shown on receipt
- Invoice numbering: gap-free sequence per business (SaleRepository.NextInvoiceNumberAsync with UPDLOCK)
- Receipt generation: 80mm thermal or A4 PDF (ReceiptPdfRenderer with QuestPDF)
- email to client
- print from browser
- Cash register: opening float
- cash in/out with reason
- closing count with variance report (RegisterService)
- Refunds and voids: full or partial refunds with reason
- void unpaid sales
- refund history per sale
Client Management
- Client profiles: name
- mobile
- birthday
- gender
- tags
- source
- notes
- visit count
- total spend
- last visit date
- Client segments: new (first visit <30 days)
- loyal (5+ visits)
- lapsed (no visit >90 days)
- birthdays this month
- custom filters by tags/source/spend
- Client wallet: credit balance
- top-up at checkout or online
- transaction log with sale references
- Consultation forms: custom forms per business (FormService)
- sent by link
- client fills online
- stored with client record
- Consent forms: GDPR-style consent tracking
- signed date
- form version
- CSV import: bulk import clients with mapping (ClientService.ImportFromCsvAsync)
- Contact masking: staff without 'view_client_contacts' permission see masked email/phone (ClientService applies mask based on CurrentUser permissions)
Prepaid, Loyalty & Marketing
- Gift cards: define types with value and validity period
- sell at POS or online (e-gift with email delivery)
- redeem at checkout
- balance tracking
- Packages: bundles of sessions (e.g. 10 massages)
- sold at POS
- sessions deducted at checkout
- expiry date
- remaining sessions shown in cart
- Memberships: recurring access (monthly/annual)
- sold at POS
- benefits applied at checkout (discounts
- free services)
- renewal reminders
- Loyalty points: earn points on spend (configurable rate)
- redeem for discounts
- referral bonus points
- points ledger per client (LoyaltyPosting)
- Promo codes: percentage or fixed discount
- usage limit
- expiry date
- applied at checkout (CheckoutDiscounts validates and applies)
- Blast campaigns: email or SMS to segments
- scheduled send
- unsubscribe link
- delivery tracking (CampaignService
- OutboxProcessor)
- Review requests: automatic after appointment completion
- public review page at /review/{token}
- star rating and comment (ReviewService)
Team, Roster & Payroll
- Team members: linked to user accounts
- assigned to services
- working hours
- commission rate
- hourly rate
- active/inactive status
- Roles and permissions: 8 system roles (Owner
- Admin
- Manager
- Receptionist
- Stylist
- Therapist
- Technician
- Trainee) plus custom roles; 47 granular permissions across 12 areas
- Shift scheduling: weekly roster
- assign shifts per professional
- copy previous week
- conflict detection (ShiftService)
- Timesheets: clock in/out (manual entry by manager)
- break deduction
- overtime calculation
- approval workflow (TimesheetService)
- Payroll: pay runs per period
- base salary + commission on sales + hourly for extra hours
- deductions
- net pay
- PDF payslip (PayrollService)
- Commission tracking: percentage of service price or fixed amount per service
- calculated at sale completion
- included in pay run
Inventory & Products
- Product catalog: name
- SKU
- category
- brand
- cost price
- retail price
- stock level
- reorder point
- supplier
- Stock tracking: current quantity per branch
- stock movements (purchase
- sale
- adjustment
- transfer)
- movement history
- Transfers: move stock between branches
- pending/completed status
- transfer log (TransferService
- TransferRepository)
- Low stock alerts: products below reorder point shown on dashboard
- email notification (InventoryService.GetLowStockAsync)
- Stock adjustments: manual corrections with reason (damaged
- expired
- found)
- adjustment history
- Product sales: add to checkout cart
- deduct from stock on sale completion
- cost of goods sold tracked
Payments & Billing
- Stripe integration: AED payments
- payment intents for deposits and gift cards
- webhook handling for payment confirmation (StripeGateway
- OnlinePaymentRecorder)
- Payment links: generate link for unpaid sale
- send by SMS/email
- customer pays online
- sale marked paid on webhook (PaymentLinkService)
- Deposit handling: online deposit for bookings
- held in customer wallet
- applied at checkout
- auto-refund if cancelled >24h before (OnlineBookingService.DepositReturns)
- Multi-currency support: AED only in UAE edition (Region.Currency)
- but architecture supports other currencies (Money value object)
- Billing module: subscription plans (trial
- basic
- pro)
- usage tracking
- invoice generation
- payment collection (BillingService
- Phase 16 in PHASE-NOTES.md)
Reports & Analytics
- Dashboard: today's appointments
- sales summary
- top services
- top professionals
- low stock alerts
- upcoming birthdays (DashboardService)
- Daily sales summary: total sales
- payment method breakdown
- VAT collected
- tips
- refunds
- register variance (SalesReportService.GetDailySummaryAsync)
- Sales list: filterable by date range
- professional
- payment method
- status; export to Excel/CSV (SalesReportService.GetSalesAsync
- SpreadsheetWriter)
- Appointment reports: by status
- professional
- service
- date range; no-show rate
- cancellation reasons
- Client reports: new clients
- repeat rate
- average spend
- visit frequency
- segment sizes
- Staff reports: sales per professional
- commission earned
- hours worked
- appointments completed
- Inventory reports: stock value
- movement history
- low stock
- products sold
Multi-Location & Settings
- Multi-branch support: one business can have multiple branches
- each with own address
- phone
- working hours
- staff assignment
- inventory (Phase 15 in PHASE-NOTES.md)
- Business settings: name
- slug (for booking URL)
- logo
- address
- phone
- timezone (Asia/Dubai)
- currency (AED)
- VAT rate
- booking rules (SettingsService)
- Service catalog: define services with name
- duration
- price
- category
- assigned professionals
- online visibility (CatalogService)
- Working hours: set per branch and per professional
- used for availability calculation (Availability.GetSlotsAsync)
- Email templates: customizable for login codes
- appointment confirmations
- reminders
- receipts (EmailLayout.WrapAsync)
- SMS gateway: HTTP API integration for any UAE SMS provider (SmsSenders.HttpGateway)
- configurable URL/query/auth in appsettings.json
- Data protection: ASP.NET Core Data Protection keys stored in configurable folder (appsettings.json DataProtection:KeysPath)
- survives restarts
| Language | Version | Framework | Used for | Share of code |
|---|---|---|---|---|
| C# | — | — | Web, API, admin | 64.9% |
| C# (Razor) | — | — | Web pages | 23.4% |
| SQL | — | — | Database | 7.4% |
| CSS | — | — | Styling | 2.4% |
| HTML | — | — | Front-end | 1.4% |
| JavaScript | — | — | Front-end | 0.5% |
- Primary language
- C# 13 / .NET 10
- Framework
- ASP.NET Core 10 Blazor Web App (Interactive Server)
- Database
- SQL Server 2019+
- Authentication
- Cookie authentication with email/SMS OTP codes, Google OAuth, Apple Sign In
- Licence
- Single-site
- Screens
- 78
- Database tables
- 89
- Supported languages
- English only
- Browser support
- Modern browsers (Chrome, Firefox, Safari, Edge — ES6+)
| Minimum | Recommended | |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 4 GB RAM / 20 GB storage | 8 GB RAM / 50 GB storage |
| OS | Linux (Ubuntu 22.04+) or Windows Server 2019+, .NET 10 runtime, SQL Server 2019+ | Linux (Ubuntu 22.04+) or Windows Server 2019+, .NET 10 runtime, SQL Server 2019+ |
| Est. monthly hosting | $40 | $40 |
```bash cp .env.example .env # Edit .env: set MSSQL_SA_PASSWORD (16+ chars, mixed case/digits/symbols) # and CODE_HASH_KEY (openssl rand -base64 32) docker compose up -d --build # Open http://localhost:8080 # Login codes appear in: docker compose logs -f web ```
```bash # Install .NET 10 SDK wget https://dot.net/v1/dotnet-install.sh chmod +x dotnet-install.sh ./dotnet-install.sh --channel 10.0 # Install SQL Server 2019+ or use a managed instance # Set connection string in environment or appsettings.Production.json cd "NET Project" dotnet publish src/BookMySalon.Web/BookMySalon.Web.csproj -c Release -o /var/www/bookmysalon # Set secrets as environment variables: export Security__CodeHashKey="$(openssl rand -base64 32)" export Database__ConnectionString="Server=localhost;Database=BookMySalon;User Id=app;Password=***;TrustServerCertificate=True" export App__BaseUrl="https://yourdomain.com" # Run with systemd service or: cd /var/www/bookmysalon dotnet BookMySalon.Web.dll # Configure Nginx reverse proxy on port 443 ```
```bash az login az group create --name bookmysalon-rg --location uaenorth az sql server create --name bookmysalon-sql --resource-group bookmysalon-rg --location uaenorth --admin-user sqladmin --admin-password "<strong-password>" az sql db create --resource-group bookmysalon-rg --server bookmysalon-sql --name BookMySalon --service-objective S1 az webapp create --resource-group bookmysalon-rg --plan bookmysalon-plan --name bookmysalon-app --runtime "DOTNET:10" # Set app settings: az webapp config appsettings set --resource-group bookmysalon-rg --name bookmysalon-app --settings \ Database__ConnectionString="Server=tcp:bookmysalon-sql.database.windows.net,1433;Database=BookMySalon;User ID=sqladmin;Password=<password>;Encrypt=True" \ Security__CodeHashKey="$(openssl rand -base64 32)" \ App__BaseUrl="https://bookmysalon-app.azurewebsites.net" cd "NET Project" dotnet publish src/BookMySalon.Web/BookMySalon.Web.csproj -c Release -o ./publish cd publish zip -r ../app.zip . az webapp deployment source config-zip --resource-group bookmysalon-rg --name bookmysalon-app --src ../app.zip ```
```bash # Start SQL Server in Docker: docker run -d --name bookmysalon-sql \ -e ACCEPT_EULA=Y \ -e MSSQL_SA_PASSWORD="YourStrong!Passw0rd" \ -p 1433:1433 \ mcr.microsoft.com/mssql/server:2022-CU27-ubuntu-22.04 cd "NET Project" # Set user secrets (never commit these): dotnet user-secrets init --project src/BookMySalon.Web dotnet user-secrets set "Security:CodeHashKey" "$(openssl rand -base64 32)" --project src/BookMySalon.Web dotnet user-secrets set "Database:ConnectionString" "Server=localhost,1433;Database=BookMySalon;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True" --project src/BookMySalon.Web # Run: dotnet run --project src/BookMySalon.Web --launch-profile https # Open https://localhost:7180 # Login codes appear in the terminal ```
Quality report
Manually reviewedHow to read the quality report
What this is. Every project on Vibe96 goes through an automated audit of its source code before a reviewer in Dubai checks it by hand. The score is out of 100; 75 is the minimum to be listed, and any single failing check blocks a listing regardless of the score.
- Security
- No real credentials in the code, every data-changing route protected, admin areas role-gated, no SQL built from user input.
- Code structure
- A developer can navigate and extend it: clear module boundaries, no giant files, little copy-paste.
- Docs & completeness
- The setup guide, example config, schema and required files the builder committed to are actually in the archive.
- Dependency health
- Packages pinned, no known critical vulnerabilities, no abandoned libraries in production paths.
- Build & deploy
- A clean machine can restore, build and start it by following the shipped instructions.
Warnings are problems that didn't block the listing; they stay visible here until a later version clears them. The audit reads the code — it does not run it — so always try the live demo before you buy. Lines of code are counted from the same audited archive.
What to know before you buy
2 non-blocking findings shown publicly: no real secrets found; weak default sql password in docker-compose.yml needs attention; dapper sql is parameterized and scoped; minor schema gaps (missing indexes on fks); strong authentication and role checks; minor ownership gaps on read endpoints; moderate duplication and some oversized files; architecture is sound but refactoring needed. The seller has been notified; fixes ship in the next version.
Versions & updates
No updates yet. New versions appear here with a changelog.
Buyers receive updates free for 1 year.
What you get
- </>Full source codeThe complete audited source, delivered as a download from your account. No obfuscation.
- ▤DocumentationSetup guide, architecture notes, CLAUDE.md and AGENTS.md.
- ↻One year of updatesEvery new version the builder publishes, free for 12 months.
- ♧30 days of supportInstallation and defect support through Vibe96 tickets.