Booking Flow & Settings
Complete guide to the Salonnz booking system, covering customer booking flow, admin configuration, and technical implementation.
Overview
The booking system enables customers to book salon appointments through web and mobile apps with configurable settings that control the user experience.
System Components
Customer Booking Flow
The booking process consists of multiple steps, with some steps appearing conditionally based on admin settings.
Flow Diagram
Step-by-Step Flow
Step 1: Location Selection
Page: /[slug]/booking/page.tsx
Purpose: Select salon location (if multiple locations exist)
API Call:
GET /booking/get-location-list
Response:
{
"status": true,
"data": [
{
"id": 1,
"name": "Downtown Salon",
"address": "123 Main St",
"phone": "+1 234 567 8900"
}
]
}
Redux Action:
dispatch(setSelectedLocation(location))
Logic:
- If only 1 location: Auto-select and navigate to service selection
- If multiple locations: Show selection UI
Step 2: Service Selection
Page: /[slug]/booking/select-services/page.tsx
Purpose: Browse categories and select salon services
API Call:
POST /booking/get-service-by-location
Body: { location_id: number }
Response:
{
"status": true,
"data": [
{
"id": 1,
"name": "Haircuts",
"services": [
{
"id": 10,
"name": "Men's Haircut",
"price": "35",
"duration": 30,
"payment_type": 0,
"enable_online_booking": 1
}
]
}
]
}
Features:
- Category filtering
- Service add-ons (optional extras)
- Pricing options (if service has variants)
- Service details popup
Redux Actions:
dispatch(addService(service)) // Add to cart
dispatch(removeService(serviceId)) // Remove from cart
dispatch(addAddon(addon)) // Add optional addon
Step 3: Staff Selection (Conditional)
Page: /[slug]/booking/select-staff/page.tsx
Visibility: Only shown if staff_selection === 1 in booking settings
API Call:
POST /booking/get-service-staff
Body: {
service_id: number,
location_id: number
}
Response:
{
"status": true,
"data": [
{
"id": 5,
"name": "Sarah Johnson",
"rating": 4.8,
"image": "https://...",
"specialization": "Hair Styling"
}
]
}
Redux Action:
dispatch(setSelectedStaff(staff))
Step 4: Date & Time Selection
Page: /[slug]/booking/select-time/page.tsx
Purpose: Select appointment date and available time slot
Date Selection:
- Calendar picker showing next 30 days
- Disabled dates for salon closed days
API Call:
POST /booking/get-slot
Body: {
start_date: "2024/12/06",
end_date: "2024/12/06",
location_id: 1,
service_pricing_options: [
{
service_id: 10,
pricing_option_id: null,
staff_id: 5
}
]
}
Response:
{
"status": true,
"data": [
{
"time": "09:00 AM",
"available": true
},
{
"time": "09:30 AM",
"available": true
},
{
"time": "10:00 AM",
"available": false
}
]
}
Slot Calculation Logic (Backend):
- Get business hours for location
- Get staff working hours
- Get existing appointments
- Get block times
- Calculate available slots based on:
- Booking interval (from settings)
- Service duration
- Staff availability
- Existing bookings
Redux Action:
dispatch(setDateAndTime({ date: "2024/12/06", time: "09:00 AM" }))
Step 5: Authentication (Conditional)
Page: Login popup appears based on settings
Conditional Logic:
if (!isLoggedIn) {
if (guest_checkout === 0) {
// Must login or register
showLoginModal();
} else {
// Can continue as guest or login
showLoginOptions();
}
}
Login Options (based on settings):
- Email/Password login
- Google OAuth (if
google_login_enable === 1) - Facebook OAuth (if
facebook_login_enable === 1) - Guest checkout (if
guest_checkout === 1) - Sign up (if
enable_signup === 1)
API Calls:
POST /customer/login
POST /customer/register
POST /auth/google
POST /auth/facebook
Step 6: Review & Confirm
Page: /[slug]/booking/review-confirm/page.tsx
Purpose: Review booking details, add payment, and confirm
Displayed Information:
- Selected services with prices
- Selected staff (if applicable)
- Date and time
- Location details
- Optional add-ons
- Total price calculation
Pricing Breakdown:
{
subtotal: servicesTotal,
tax: (servicesTotal * location.service_tax) / 100,
deposit: depositAmount, // If payment_type = 1
total: subtotal + tax
}
Cancellation Policy (Conditional):
- Shown if
cancellation_key === 1 - Checkbox must be checked to proceed
- Loads policy from
/business-contact-details
Step 7: Payment (Conditional)
Visibility: Based on service payment_type
Payment Types:
| Type | Value | Description | When Shown |
|---|---|---|---|
| Pay at Salon | 0 | No payment required | Default |
| Deposit | 1 | Partial payment upfront | deposit_enable === 1 AND service.min_amount > 0 |
| Save Card | 2 | Save card for future | save_card_enable === 1 |
| Pay Later | 3 | Book now, pay after service | pay_later_enable === 1 |
Stripe Integration:
For deposit payments:
// 1. Create payment intent
const { token } = await getToken({
amount: depositAmount
});
// 2. Confirm payment with Stripe
const { paymentIntent } = await stripe.confirmPayment({
elements,
confirmParams: { return_url: confirmationUrl }
});
// 3. Update appointment status
await updatePaymentStatus({
appointment_id: apptId,
payment_intent: paymentIntent.id
});
Redux Actions:
dispatch(setPaymentMethod(1)) // Set payment type
dispatch(setDepositAmount(amount)) // Set deposit amount
dispatch(setPaymentToken(token)) // Store Stripe token
Step 8: Confirmation
Page: /[slug]/booking/confirmed/page.tsx
Purpose: Show booking confirmation
Displayed Information:
- Booking ID
- Appointment details
- QR code for check-in
- Add to calendar button
- Email confirmation sent
API Response (from saveBooking):
{
"status": true,
"message": "Appointment created successfully",
"data": {
"id": 1234,
"appointment_number": "APT-1234",
"status": 1,
"date": "2024/12/06",
"time": "09:00 AM"
}
}
Admin Configuration
Booking Settings Panel
Location: Admin Panel → Settings → Front-End Booking
File: Frontend-Admin-Panel/pages/setting/front-end-booking/index.vue
Available Settings
1. Guest Checkout
Field: guest_checkout
Type: Toggle (0/1)
Description: Allow customers to book without creating an account
Impact on User Flow:
- Enabled (1): Shows "Continue as Guest" button
- Disabled (0): Forces login/registration
Use Case: Enable for walk-in customers who want quick bookings
2. Booking Interval
Field: booking_interval
Type: Select (5-240 minutes)
Description: Time slot intervals for appointments
Options: 5, 10, 15, 20, 30, 45, 60, 90, 120, 180, 240 minutes
Impact: Determines available time slots
Example:
- 15-minute intervals: 9:00, 9:15, 9:30, 9:45...
- 30-minute intervals: 9:00, 9:30, 10:00...
Backend Logic (BookingController.php):
$interval = $frontendSettings->booking_interval;
while ($currentTime < $endTime) {
$slots[] = $currentTime->format('h:i A');
$currentTime->addMinutes($interval);
}
3. Enable Sign Up
Field: enable_signup
Type: Toggle (0/1)
Description: Allow new user registration
Impact on User Flow:
- Enabled (1): Shows "Sign Up" option in login modal
- Disabled (0): Only login available
4. Staff Selection
Field: staff_selection
Type: Toggle (0/1)
Description: Let customers choose their preferred staff member
Impact on User Flow:
- Enabled (1): Adds staff selection step to booking flow
- Disabled (0): Staff auto-assigned by system
Use Case: Enable when customers have preferred stylists
5. Dynamic Slot Calculation
Field: enable_dynamic_slot
Type: Toggle (0/1)
Description: Calculate time slots based on real-time staff availability
Impact:
- Enabled (1): Slots calculated per staff member's schedule
- Disabled (0): Slots based only on business hours
Backend Logic:
if ($enable_dynamic_slot) {
// Check each staff member's:
// - Working hours
// - Existing appointments
// - Break times
// - Block times
}
6. Google Login
Field: google_login_enable
Type: Toggle (0/1)
Description: Enable Google OAuth authentication
Requirements:
- Google Client ID
- Google Client Secret
Impact: Shows "Continue with Google" button
7. Facebook Login
Field: facebook_login_enable
Type: Toggle (0/1)
Description: Enable Facebook OAuth authentication
Requirements:
- Facebook App ID
- Facebook App Secret
Impact: Shows "Continue with Facebook" button
8. Deposit Enable
Field: deposit_enable
Type: Toggle (0/1)
Description: Enable deposit payments for services
Impact:
- Enabled (1): Shows deposit payment option for services where
payment_type = 1 - Disabled (0): No deposit payments accepted
Service Configuration: Set min_amount on service for deposit amount
Settings API
Endpoint: POST /front_end_booking_settings/update
Request Body:
{
"guest_checkout": 1,
"booking_interval": 15,
"enable_signup": 1,
"staff_selection": 1,
"enable_dynamic_slot": 1,
"google_login_enable": 0,
"facebook_login_enable": 0,
"deposit_enable": 1
}
Backend Process (FrontEndBookingSettingController.php):
- Validate all fields
- Update
frontend_settingstable - Clear cache:
clearCacheByPattern('customers_booking_get_front_settings') - Return success response
Technical Implementation
Frontend Redux Store
File: Frontend-Userapp-Web/store/bookingSlice.ts
State Structure:
interface BookingState {
selectedLocation: Location | null;
currency: string | null;
services: Service[];
addOns: Addon[];
date: string;
time: string;
bookingSettings: BookingSettings;
customer: Customer;
depositAmount: number;
paymentMethod: number;
paymentType: number;
cancellationPolicy: boolean;
}
Key Actions:
setBookingSettings(settings)- Store fetched settingssetSelectedLocation(location)- Store selected locationaddService(service)- Add service to cartremoveService(serviceId)- Remove servicesetDateAndTime({ date, time })- Store appointment timesetDepositAmount(amount)- Store depositsetCancellationPolicy(accepted)- Track policy agreement
Backend API Endpoints
Get Frontend Settings
GET /booking/get-front-settings
Controller: BookingController.php::getFrontendSettings()
Caching: Cached with key customers_booking_get_front_settings
Response:
{
"status": true,
"data": {
"guest_checkout": 1,
"booking_interval": 15,
"staff_selection": 1,
"STRIPE_KEY": "pk_test_..."
},
"shop": {
"currency": "USD",
"name": "My Salon"
},
"calendar": {
"timeZone": "America/New_York"
}
}
Get Available Time Slots
POST /booking/get-slot
Controller: BookingController.php::getSlots()
Request:
{
"start_date": "2024/12/06",
"end_date": "2024/12/06",
"location_id": 1,
"service_pricing_options": [
{
"service_id": 10,
"pricing_option_id": null,
"staff_id": 5
}
]
}
Validation:
start_date: Required, formatY/m/dend_date: Required, formatY/m/dlocation_id: Requiredservice_pricing_options: Required array
Response:
{
"status": true,
"data": [
{ "time": "09:00 AM", "available": true },
{ "time": "09:15 AM", "available": true },
{ "time": "09:30 AM", "available": false }
]
}
Slot Calculation Algorithm:
- Get business hours for date
- Get staff working hours (if staff selected)
- Get booking interval from settings
- Generate all possible slots
- Check existing appointments
- Remove conflicting slots
- Remove block times
- Remove break times
- Return available slots
Save Booking
POST /booking/save-booking
Controller: BookingController.php::saveBooking()
Request:
{
"location_id": 1,
"customer_id": 123,
"customer_type": 0,
"date": "2024/12/06",
"time": "09:00 AM",
"note": "Please use low-ammonia products",
"services": [
{
"service_id": 10,
"pricing_option_id": null,
"duration": 30,
"price": 35,
"staff_id": 5
}
],
"addons": [
{
"add_on_id": 3,
"add_on_price": 10,
"add_on_duration": 15
}
],
"deposit_amount": "0"
}
Validation Rules:
location_id: Required, exists in locations tablecustomer_id: Nullable (for guest checkout)customer_type: 0 (Registered), 1 (Guest), 2 (Walk-in)date: Required, formatY/m/dservices: Required array, minimum 1 service
Response:
{
"status": true,
"message": "Appointment created successfully",
"data": {
"id": 1234,
"appointment_number": "APT-1234",
"status": 1
}
}
Backend Process:
- Validate request data
- Create
appointmentsrecord - Create
appointment_servicesrecords - Create
appointment_addonsrecords (if any) - Send confirmation email
- Send notification to staff
- Update cache
- Return appointment details
Payment Integration
Stripe Setup
Required Fields in frontend_settings:
STRIPE_KEY: Publishable key (pk_...)STRIPE_SECRET: Secret key (sk_...)
Payment Flow for Deposits
Step 1: Create Payment Intent
// Frontend: Frontend-Userapp-Web/api/bookingApi.ts
const { token } = await getToken({amount: depositAmount});
Backend: PaymentController.php::create()
$paymentIntent = $stripe->paymentIntents->create([
'amount' => $request['amount'] * 100, // Convert to cents
'currency' => 'usd',
'automatic_payment_methods' => ['enabled' => true]
]);
return response()->json([
'status' => true,
'token' => $paymentIntent->client_secret
]);
Step 2: Confirm Payment
// Frontend: CheckoutForm.jsx
const { paymentIntent } = await stripe.confirmPayment({
elements,
confirmParams: { return_url: confirmationUrl },
redirect: 'if_required'
});
Step 3: Update Status
await updatePaymentStatus({
appointment_id: apptId,
payment_intent: paymentIntent.id
});
Backend: PaymentController.php::updatePaymentStatus()
$output = $stripe->paymentIntents->retrieve($request->payment_intent);
if ($output->status == 'succeeded') {
$appointment->update(['status' => 1]);
AppointmentPayment::create([
'appointment_id' => $appointment->id,
'amount' => $output->amount / 100,
'txn_id' => $output->client_secret,
'status' => '1'
]);
}
Database Schema
frontend_settings Table
CREATE TABLE frontend_settings (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
guest_checkout INT DEFAULT 0,
booking_interval INT DEFAULT 15,
enable_signup INT DEFAULT 1,
staff_selection INT DEFAULT 0,
enable_dynamic_slot INT DEFAULT 0,
google_login_enable INT DEFAULT 0,
facebook_login_enable INT DEFAULT 0,
deposit_enable INT DEFAULT 0,
save_card_enable INT DEFAULT 0,
pay_later_enable INT DEFAULT 0,
STRIPE_KEY VARCHAR(255),
STRIPE_SECRET VARCHAR(255),
created_at TIMESTAMP,
updated_at TIMESTAMP
);
appointments Table
CREATE TABLE appointments (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
appointment_number VARCHAR(50),
location_id BIGINT,
customer_id BIGINT NULL,
customer_type INT DEFAULT 0,
date DATE,
time VARCHAR(20),
status INT DEFAULT 0,
note TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
FOREIGN KEY (location_id) REFERENCES locations(id),
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
appointment_services Table
CREATE TABLE appointment_services (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
appointment_id BIGINT,
service_id BIGINT,
pricing_option_id BIGINT NULL,
staff_id BIGINT NULL,
duration INT,
price DECIMAL(10,2),
created_at TIMESTAMP,
FOREIGN KEY (appointment_id) REFERENCES appointments(id) ON DELETE CASCADE
);
Best Practices
For Salon Administrators
-
Booking Interval: Set based on your average service duration
- Quick services (15-30 min): 15-minute intervals
- Standard services (30-60 min): 30-minute intervals
- Long services (1+ hour): 60-minute intervals
-
Staff Selection: Enable if you have specialized staff
- Hair color specialists
- Senior stylists
- Nail technicians with specialties
-
Guest Checkout: Enable for walk-in friendly salons
- Reduces booking friction
- Good for first-time customers
- Can convert to registered users later
-
Deposit Payments: Use for high-value services
- Reduces no-shows
- Protects against last-minute cancellations
- Set deposit at 25-50% of service cost
For Developers
- Cache Management: Always clear cache when settings change
- Validation: Validate date formats (
Y/m/d) on both frontend and backend - Error Handling: Provide clear error messages for slot unavailability
- Payment Security: Never store credit card details; use Stripe tokens
- Timezone Handling: Store appointments in salon's timezone
Troubleshooting
Common Issues
Issue: Time slots not showing
Causes:
- No staff assigned to service
- Staff has no working hours defined
- Block times covering all slots
- Service duration longer than business hours
Solution:
# Check backend logs
tail -f storage/logs/laravel.log
# Verify API response
GET /booking/get-slot
Issue: Booking settings not updating
Cause: Cache not cleared
Solution:
// Backend
clearCacheByPattern('customers_booking_get_front_settings');
Issue: Payment not processing
Causes:
- Invalid Stripe keys
- Currency mismatch
- Amount in wrong format (should be cents)
Solution: Check Stripe dashboard for error details
API Reference Summary
| Endpoint | Method | Purpose |
|---|---|---|
/booking/get-front-settings | GET | Fetch booking configuration |
/booking/get-location-list | GET | Get available locations |
/booking/get-service-by-location | POST | Get services for location |
/booking/get-service-staff | POST | Get staff for service |
/booking/get-slot | POST | Get available time slots |
/booking/save-booking | POST | Create appointment |
/payment/create-order | POST | Create Stripe payment intent |
/payment/update-status | POST | Update payment status |
Next Steps
- Admin Panel Overview - Configure your salon
- Payment Integration - Set up Stripe
- API Reference - Detailed API documentation