Booking & Appointments
Add appointment scheduling and multi-day reservation functionality to your Larapen site. Manage services, providers, weekly schedules, and an interactive booking calendar: with optional payment integration.
Dual Booking Modes
Switch between time-slot appointments (e.g., consultations) and multi-day reservations (e.g., hotel stays) from a single setting.
Service & Provider Management
Create bookable services with pricing, duration, and capacity. Assign providers with individual weekly schedules and blocked dates.
Interactive Calendar
Admin calendar view with provider filtering. Front-end calendar with real-time slot availability powered by AJAX.
Payment Integration
Optional payment requirement before confirmation. Supports Stripe, PayPal, Paddle, and MoMo via the Payable interface.
Email Notifications
Configurable notifications for clients, admins, and providers on booking creation, status changes, and cancellations.
Multi-language Support
Service names, slugs, and descriptions are translatable. All UI strings use the translation system.
Use Cases
Appointment Mode
- Salon or Spa: Clients pick a service (haircut, massage), a date, and a time slot from available openings.
- Consulting Firm: Visitors book a 30-minute or 60-minute consultation with a specific advisor.
- Medical Practice: Patients schedule visits with practitioners. Break times and blocked dates keep the calendar accurate.
Reservation Mode
- Hotel / B&B: Guests select check-in and check-out dates with per-night pricing and capacity limits.
- Venue Rental: Event organizers reserve a venue for multiple days with minimum/maximum stay constraints.
- Equipment Rental: Customers reserve gear for a date range with overlapping reservation detection.
Requirements
- Larapen CMS v1.0.0 or later
- PHP 8.3+
- MySQL 8.0+
Installation
Step 1: Place the Add-on
Copy or symlink the booking folder into your Larapen "extensions/addons" directory:
Step 2: Activate the Add-on
Go to Admin → Add-ons → Installed Add-ons and activate Booking & Appointments.
Step 3: Run Migrations
This creates 6 tables: booking_services, booking_providers,
booking_provider_schedules, booking_provider_service (pivot),
booking_blocked_dates, and booking_appointments.
Step 4: Set Permissions
The add-on registers 13 permissions (see Permissions). Assign them to admin roles via Admin → Users → Roles & Permissions.
Step 5: Configure
Navigate to Admin → Booking → Settings and configure the booking type, scheduling options, and notification preferences. See Configuration.
Step 6: Create Services & Providers
- Go to Admin → Booking → Services and create at least one active service.
- Go to Admin → Booking → Providers and add providers (staff members). The system initializes a default Mon–Fri 9:00–17:00 schedule for each new provider.
- Assign services to providers and customize their weekly schedules.
Configuration
All settings are managed in Admin → Booking → Settings
(stored in the settings table, group booking).
Defaults are defined in config/booking.php.
| Setting | Description | Default |
|---|---|---|
booking_type |
Booking mode: appointment (time-slot) or reservation (date-range). |
appointment |
booking_enabled |
Show or hide the booking page on the front-end. | true |
booking_multi_provider |
Allow clients to choose a specific provider when booking. When disabled, the system auto-assigns a provider. | false |
booking_pending_blocks_slot |
When enabled, pending appointments also block the time slot. When disabled, only confirmed/completed appointments block slots. | true |
booking_advance_days |
How many days in advance clients can book. | 60 |
booking_min_advance_hours |
Minimum hours before an appointment can be booked (prevents last-minute bookings). | 2 |
booking_slot_interval |
Override the time slot interval in minutes. Leave empty to use the service’s duration. | (null: uses service duration) |
booking_notification_email |
Email address to receive admin booking notifications. | (empty) |
booking_require_payment |
Require payment before booking confirmation. Only applies to services with a price > 0. | false |
booking_cancellation_policy |
Free-text cancellation policy displayed on the booking page. | (empty) |
booking_captcha_enabled |
Enable CAPTCHA challenge on the booking form. | false |
Notification Settings
| Setting | Description | Default |
|---|---|---|
booking_notify_admin_on_new_booking |
Send email to the notification address on new bookings. | true |
booking_notify_client_on_booking |
Send confirmation email to the client on booking submission. | true |
booking_notify_client_on_status_change |
Notify client when booking status changes (confirmed, cancelled, completed). | true |
booking_notify_provider_on_new_booking |
Notify the assigned provider when they receive a new booking. | true |
booking_notify_provider_on_cancellation |
Notify the assigned provider when a booking is cancelled. | true |
Booking Types
The add-on supports two fundamentally different booking paradigms. The active mode is controlled by the
booking_type setting and affects the front-end form, admin views, slot generation logic,
and availability calculations.
Appointment Mode
Single-date, time-slot based bookings. Examples: haircut at 3pm, consultation at 10am, medical visit at 14:30.
- Services define a duration in minutes (e.g., 30, 60, 90).
- The system generates available time slots from the provider’s weekly schedule, excluding breaks and existing bookings.
- Clients pick a date from the calendar, then select a specific time slot.
- Capacity-aware:
max_capacityallows multiple simultaneous bookings per slot (e.g., a gym class with 10 spots). - The
start_timeandend_timefields are used;check_in_date/check_out_dateare null.
Reservation Mode
Multi-day, date-range based bookings. Examples: hotel room for 3 nights, venue rental for a weekend.
- Services define price per night, min/max nights, and max guests.
- Clients select check-in and check-out dates from a calendar that shows day-level availability.
- Total price is calculated as:
price_per_night × number of nights. - Overlap detection prevents double-booking: the system counts reservations occupying each date and compares against
max_capacity. - The
check_in_date,check_out_date, andnum_guestsfields are used;start_time/end_timeare null.
Admin: Services
The Services page (Booking → Services) manages your catalog of bookable services.
Services List
A sortable, paginated table showing:
- Name (translatable)
- Duration (formatted, e.g., “1h 30min”): appointment mode
- Price (formatted with currency): or Price per Night in reservation mode
- Max Capacity
- Appointments count
- Status (active/inactive badge)
- Position (display order)
Per-row actions: Edit, Delete.
Creating & Editing Services
Common Fields (Both Modes)
- Name (translatable, required for default locale)
- Slug (translatable, auto-generated if empty)
- Description (translatable)
- Price (numeric, optional)
- Currency (required, from active currencies)
- Max Capacity (integer, 1–100)
- Active toggle
- Position (display order)
Appointment Mode Fields
- Duration (minutes): required, 5–480 minutes. Determines slot length.
Reservation Mode Fields
- Duration (minutes): optional (not used for slot generation in reservation mode).
- Price per Night: used to calculate total price.
- Min Nights / Max Nights: stay length constraints.
- Max Guests: per-reservation guest limit.
Admin: Providers
Providers represent the staff members, rooms, or resources that deliver your services. Managed at Booking → Providers.
Providers List
A paginated table showing:
- Avatar (or initials fallback)
- Name, Email, Phone
- Assigned services count
- Appointments count
- Status (active/inactive)
Per-row actions: Edit, Delete.
Creating a Provider
- Name, Email, Phone
- Bio: text description
- Avatar: image upload (stored in
booking/providers/on the public disk) - Linked User Account: optional FK to the
userstable - Assigned Services: multi-select from active services
- Active toggle, Position
When a provider is created, a default weekly schedule is automatically initialized: Monday–Friday 09:00–17:00 with a 12:00–13:00 break. Saturday and Sunday are off.
Weekly Schedule
The schedule editor (Providers → {provider} → Edit → Schedule tab) allows configuring each day of the week:
- Available toggle (on/off)
- Start Time and End Time
- Break Start and Break End (optional lunch/rest period)
Slots that overlap the break window are automatically excluded from availability.
Blocked Dates
Individual dates when a provider is unavailable (holidays, sick days, vacations). Managed from the provider edit page.
- Date (required)
- Reason (optional: e.g., “National Holiday”, “Vacation”)
Blocked dates can also be global (no provider assigned) to block all providers on that date.
Admin: Appointments
The Appointments page (Booking → Appointments) shows all bookings in a paginated table.
Appointments List
Filterable by status. Columns include:
- Client name & email
- Service name
- Provider name
- Date & time (or check-in/check-out for reservations)
- Status badge (pending/confirmed/cancelled/completed)
- Payment status (if payment is enabled)
Stats cards at the top show totals, pending count, today’s appointments, this week, and upcoming. In reservation mode, additional stats show check-ins today, check-outs today, and active stays.
Appointment Detail & Status Management
The detail page (Appointments → {appointment}) shows:
- Client information: name, email, phone, IP address, booked-at timestamp
- Booking information: service, provider, date/time (or date range), total price
- Payment details (if applicable): status, method, reference, paid-at timestamp
- Status management: buttons to change status with transitions:
- Pending → Confirmed, Cancelled
- Confirmed → Completed, Cancelled, Revert to Pending
- Completed → Revert to Confirmed
- Cancelled → Reopen (back to Pending)
- Cancellation reason: shown when cancelling; cleared when reopening
- Admin notes: internal notes not visible to the client
- Client notes: notes submitted by the client during booking
booking_notify_client_on_status_change is enabled).
Cancellations also notify the assigned provider.
Calendar View
The Calendar page (Booking → Appointments → Calendar) provides a visual month view:
- Events are loaded via AJAX (
GET admin/booking/appointments/calendar-events). - Filter by provider using the dropdown.
- Appointments show as time-based events; reservations show as multi-day spans.
- Color-coded by status (warning=pending, success=confirmed, danger=cancelled, info=completed).
- Click an event to navigate to the appointment detail page.
Admin: Settings
The settings page (Booking → Settings) is organized into sections:
General Settings
- Booking Type: radio selector for Appointment or Reservation mode with descriptions.
- Enable Booking: toggle to show/hide the front-end booking page.
- Multi-Provider Mode: let clients choose their provider.
Scheduling
- Pending Appointments Block Slots: toggle.
- Slot Interval: override the default (service duration).
- Advance Booking (days): how far ahead clients can book.
- Minimum Advance (hours): prevents last-minute bookings.
- Cancellation Policy: free-text displayed on the booking page.
Payment
- Require Payment: toggle. Only applies to services with price > 0.
- A warning is shown if no payment gateway add-on is active.
Notifications
- Notification Email: admin email address for booking alerts.
- Five toggle switches controlling which emails are sent (see Notifications).
CAPTCHA
- Enable CAPTCHA on Booking Form: toggle. Requires a CAPTCHA provider to be configured in core settings.
Front-end: Booking Page
The booking page is available at /{locale}/booking and provides a step-by-step wizard.
Routes
| Method | URL | Route Name | Description |
|---|---|---|---|
| GET | /{locale}/booking |
booking.index.localized |
Booking wizard page |
| POST | /{locale}/booking |
booking.store.localized |
Submit a booking |
| GET | /{locale}/booking/confirmation/{appointment} |
booking.confirmation.localized |
Confirmation page |
| GET | /{locale}/booking/my-bookings |
booking.my-bookings.localized |
User’s booking history (auth required) |
Non-localized variants (without {locale}) are also registered.
Appointment Wizard Steps
- Select Service: card grid of active services showing name, description, duration, and price.
- Choose Provider: shown only if multi-provider mode is enabled. Includes an “Any Available Provider” option.
- Pick Date & Time: interactive calendar showing available/unavailable days. Selecting a date loads time slots via AJAX.
- Your Details: name, email, phone (optional), notes (optional). Pre-filled for authenticated users.
- Confirm: summary card with all selections. Submit button triggers the booking.
Reservation Wizard Steps
- Select Service: card grid showing name, description, price per night, and stay range constraints.
- Select Dates: interactive calendar for picking check-in and check-out dates. Real-time availability check via AJAX.
- Guests: number of guests input (if max_guests is configured).
- Your Details: client information form.
- Confirm: summary with date range, nights, total price. Submit button triggers the reservation.
Slot & Availability API
Three JSON endpoints power the front-end calendar and slot selection:
/{locale}/booking/slots
Description
Returns available time slots for a specific service, provider, and date. Used in appointment mode.
Query Parameters
service_id |
Required | Service ID |
provider_id |
Optional | Provider ID (null = any available) |
date |
Required | Date (YYYY-MM-DD) |
Response
/{locale}/booking/availability
Description
Returns day-level availability for an entire month. Used to render the calendar with available/unavailable indicators.
Query Parameters
year |
Required | Year (2024–2030) |
month |
Required | Month (1–12) |
service_id |
Optional | Service ID |
provider_id |
Optional | Provider ID |
Response
Values: past, available, unavailable.
/{locale}/booking/check-availability
Description
Checks if a specific date range is available for reservation. Used in reservation mode.
Query Parameters
service_id |
Required | Service ID |
provider_id |
Optional | Provider ID |
check_in_date |
Required | Check-in date (YYYY-MM-DD) |
check_out_date |
Required | Check-out date (YYYY-MM-DD, must be after check_in_date) |
Response
Slot Generation Logic
For appointment mode, time slots are generated as follows:
- Load the provider’s schedule for the requested day of week.
- Check for blocked dates (provider-specific and global).
- Generate slots from
start_timetoend_timeat intervals ofslot_interval(or service duration). - Exclude slots that overlap the break window.
- Exclude slots before the minimum advance cutoff time.
- For each candidate slot, count existing blocking appointments (confirmed + completed, and pending if
pending_blocks_slotis enabled). - Include the slot only if the overlap count is below
max_capacity.
When no provider is specified, the system aggregates slots across all active providers for the service. If no providers exist at all, a built-in default schedule (Mon–Fri 09:00–17:00, break 12:00–13:00) is used as a fallback.
Confirmation Page
After a successful booking (or successful payment), the user is redirected to
/{locale}/booking/confirmation/{appointment}.
- Shows a success message with appointment/reservation details.
- Displays service name, provider, date/time (or date range for reservations), and total price.
- Status note explaining the booking is pending confirmation.
- Links to “Back to Home” and “Book Another”.
Payment Checkout
When booking_require_payment is enabled and the appointment has a total price > 0,
the booking flow redirects to a checkout page instead of the confirmation page.
Routes
| Method | URL | Route Name | Description |
|---|---|---|---|
| GET | /{locale}/booking/checkout/{appointment} |
booking.checkout.localized |
Payment checkout page |
| POST | /{locale}/booking/checkout/{appointment} |
booking.checkout.process.localized |
Process payment |
Payable Interface
The Appointment model implements the App\Contracts\Payable interface, providing:
getPayableAmount(): returns total pricegetPayableCurrency(): from the service’s currency or site defaultgetPayableDescription(): e.g., “Appointment: Haircut on Mar 15, 2026”getPayableCustomerEmail(),getPayableCustomerName()markAsPaid(): sets status to Confirmed, payment_status to “paid”markPaymentFailed(): sets payment_status to “failed”getPaymentSuccessUrl(): redirects to confirmation pagegetPaymentCancelUrl(): redirects back to checkout page
Supported Payment Gateways
The checkout page works with any active payment gateway add-on:
- Stripe: client-side Payment Intents with Stripe.js
- PayPal: redirect-based checkout
- Paddle: inline overlay or redirect
- MoMo: mobile money with phone number input
booking_require_payment is enabled,
a warning is shown on the admin settings page. The checkout page will show no payment options.
My Bookings
Authenticated users can view their booking history at /{locale}/booking/my-bookings.
- Matches bookings by
user_idorclient_email(covers bookings made before registration). - Paginated list (15 per page) sorted by date descending.
- Each entry shows: service name, provider, date/time (or date range), status badge, and payment status.
This page is also accessible from the user account menu via the “My Bookings” link
(registered in addon.json under provides.user_menu).
Notifications
The add-on uses Laravel’s Notification system with on-demand mail recipients.
All notifications are sent via the BookingManager service and silently catch any sending errors.
| Notification Class | Recipient | Trigger | Setting |
|---|---|---|---|
BookingConfirmationNotification |
Client | Booking created (after payment, if required) | notify_client_on_booking |
NewBookingAdminNotification |
Admin (notification_email) | Booking created | notify_admin_on_new_booking |
NewBookingProviderNotification |
Assigned provider | Booking created | notify_provider_on_new_booking |
BookingStatusChangeNotification |
Client | Status changed to confirmed/cancelled/completed | notify_client_on_status_change |
BookingCancellationProviderNotification |
Assigned provider | Booking cancelled | notify_provider_on_cancellation |
Each notification adapts its subject line and content based on the booking_type
(appointment vs. reservation), including appropriate date/time or date-range details.
AppointmentObserver, which watches the status field for changes.
This ensures notifications fire regardless of how the status is updated (admin panel, API, etc.).
Updating
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
Step 3: Rebuild Assets
Required if the update includes new or modified theme SCSS/JS files.
Step 4: Clear Caches
Troubleshooting
Calendar shows no available days
- Ensure at least one active service exists.
- If providers are configured, verify at least one provider has an active schedule for the relevant day of the week.
- Check that
booking_advance_daysis set to a value greater than 0. - Verify no global blocked dates cover the entire visible period.
- If no providers exist, the system uses a default Mon–Fri schedule: weekends will show as unavailable.
No time slots appear for a selected date
- The selected date may be within the
booking_min_advance_hourscutoff (e.g., today before the minimum advance window). - All slots may be booked. Check if
booking_pending_blocks_slotis enabled: pending appointments count toward capacity. - The provider’s schedule may be set to unavailable for that day of the week.
- A blocked date may exist for the provider or globally.
- The service duration may exceed the available time window (e.g., 3-hour service but only 2 hours available after the break).
Booking page returns 404
- Ensure
booking_enabledis set totruein settings. The controller throws a 404 when booking is disabled. - Verify the add-on is activated in Admin → Add-ons.
Reservation mode: “dates not available” despite empty calendar
- Check blocked dates across the entire date range (any single blocked date invalidates the range).
- Verify min/max nights constraints on the service. A 2-night stay on a service with
min_nights = 3will fail. - Ensure
max_capacityis set correctly. A capacity of 1 means only one reservation can occupy any given date.
Client not receiving confirmation emails
- Check that
booking_notify_client_on_bookingis enabled in settings. - Verify your mail configuration works (SMTP, Mailgun, etc.) in Admin → Settings → Mail.
- Notification failures are silently caught: check
storage/logs/laravel.logfor any errors.
Payment checkout shows no payment methods
- Ensure at least one payment gateway add-on (Stripe, PayPal, Paddle, or MoMo) is installed and activated.
- Verify the gateway is properly configured with API keys in its own settings.
- The
PaymentService::getAvailableGateways()only returns gateways that are fully configured.
Multi-provider mode not showing provider selector
- Ensure
booking_multi_provideris enabled in settings. - Verify at least one active provider exists and is assigned to the selected service.
Status change notifications not sending
- The
AppointmentObserverhandles status change notifications. Ensure it is registered inBookingServiceProvider::boot(). - Only transitions to Confirmed, Cancelled, or Completed trigger client notifications.
- Provider cancellation notifications require the provider to have an email address set.