Add time-slot appointment scheduling to your Larapen site. Manage services, providers, weekly schedules, and an interactive booking calendar, with optional payment integration.
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.
Time Slot Generation
Automatic slot generation from provider schedules. Respects breaks, blocked dates, capacity limits, and minimum advance time.
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 appointment creation, status changes, and cancellations.
Multi-language Support
Service names, slugs, and descriptions are translatable. All UI strings use the translation system.
Use Cases
- 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.
- Fitness Classes: Members reserve spots in group sessions with capacity-limited time slots.
- Tutoring & Coaching: Students book individual or group sessions with tutors, each with their own availability schedule.
Requirements
- Larapen CMS v1.0.0 or later
- PHP 8.3+
- MySQL 8.0+
Installation
Step 1: Upload the Add-on
In the admin panel, go to Admin → Extensions → Add-ons and click the Upload Add-on button. Select the add-on’s ZIP file: the system extracts it automatically and the add-on appears in the installed add-ons list.
Step 2: Activate the Add-on
Find Appointment Scheduling in the list and click Activate. Its migrations, seeders (if any) and permissions are set up automatically.
Step 3: Configure
Navigate to Admin → Appointments → Settings to configure services, availability rules, booking windows, and notifications. See Configuration.
Purchase Code (License Key)
Appointments is sold as a separate product, so it has its own purchase code (license key), distinct from the purchase code of the main application and from the one of every other add-on. You are asked for it when you activate Appointments in Admin panel → Add-ons.
Our products are sold on three platforms. The way you receive a purchase code depends on where you bought the product.
| Platform / Marketplace | How you get the purchase code | Where to find it again |
|---|---|---|
| bedigit.com Store In-site purchase (Shop) |
Generated automatically when the order is paid, then sent by email, either in its own license email, or inside the order confirmation email. | My Account → My Licenses on bedigit.com |
| Gumroad | Created as soon as Gumroad notifies us of the sale, then sent in a separate email, in addition to the Gumroad receipt. | The license email, your Gumroad Library, and My Account → My Licenses on bedigit.com |
| Envato Market CodeCanyon |
Issued by Envato, not by us, and never sent by email: you download it yourself from your Envato account. | Envato account → Downloads → License certificate & purchase code |
1. bedigit.com Store (in-site purchase)
- As soon as the order’s payment status becomes Paid, a license key is generated automatically for every licensed item in the order (one key per purchased unit: buying 3 units gives 3 distinct keys).
- It is emailed to the address used on the order, either in a dedicated license email or inside the order confirmation email. Check your inbox and your spam / junk folder.
- The key stays available in your account under My Account → My Licenses. Keys are masked in the list; open the license detail page to reveal and copy the full key, see the domains it is activated on, and deactivate a domain to free an activation slot.
- The matching invoice is under My Account → My Orders.
2. Gumroad
- A Gumroad purchase produces two separate emails: the Gumroad receipt (sent by Gumroad, giving access to the files) and a license key email (sent by bedigit.com) that contains your purchase code.
- The license key email is generated as soon as Gumroad notifies us of the sale, so it normally arrives within seconds of the payment. Here too, check your inbox and your spam / junk folder.
- When the Gumroad product uses Gumroad’s own license-key feature, the same key also appears in your Gumroad receipt and under Library → your purchase on gumroad.com.
- Use the same email address on bedigit.com as on Gumroad: your keys are then linked to your account automatically and listed under My Account → My Licenses, even if you register after the purchase. You can also add a Gumroad key manually from My Account → My Gumroad Licenses.
3. Envato Market (CodeCanyon)
- Envato purchase codes are issued and delivered by Envato Market, never emailed by us, so there is nothing to look for in your spam folder: you retrieve the code from your Envato account.
- Log in to your Envato / CodeCanyon account, open the Downloads page, find the item, and choose License certificate & purchase code from the Download dropdown. The code is written in that certificate.
- An Envato purchase code looks like
12345678-90ab-cdef-1234-567890abcdef(8-4-4-4-12 characters). It never changes, and renewing item support does not issue a new one. - Official Envato article: Where Is My Purchase Code?
Configuration
All settings are managed in Admin → Appointments → Settings
(stored in the settings table, group appointment).
Defaults are defined in config/appointment.php.
| Setting | Description | Default |
|---|---|---|
appointment_enabled |
Show or hide the appointment page on the front-end. | true |
appointment_multi_provider |
Allow clients to choose a specific provider when booking. When disabled, the system auto-assigns a provider. | false |
appointment_provider_selection |
Show the provider selection step in the booking wizard (requires multi-provider mode). | true |
appointment_pending_blocks_slot |
When enabled, pending appointments also block the time slot. When disabled, only confirmed/completed appointments block slots. | true |
appointment_advance_days |
How many days in advance clients can book. | 60 |
appointment_min_advance_hours |
Minimum hours before an appointment can be booked (prevents last-minute bookings). | 2 |
appointment_slot_interval |
Override the time slot interval in minutes. Leave empty to use the service’s duration. | (null: uses service duration) |
appointment_services_per_row |
Number of service cards per row on the front-end booking page. | 3 |
appointment_providers_per_row |
Number of provider cards per row on the front-end booking page. | 4 |
appointment_notification_email |
Email address to receive admin appointment notifications. | (empty) |
appointment_require_payment |
Require payment before appointment confirmation. Only applies to services with a price > 0. | false |
appointment_cancellation_policy |
Free-text cancellation policy displayed on the booking page (translatable). | (empty) |
appointment_captcha_enabled |
Enable CAPTCHA challenge on the booking form. | false |
Provider Label Settings
| Setting | Description | Default |
|---|---|---|
appointment_provider_step_label |
Custom heading for the provider selection step (translatable). Leave empty to use the default translation. | (empty: uses default) |
appointment_provider_any_label |
Label for the “Any Available Provider” option (translatable). Leave empty to use the default. | (empty: uses default) |
Notification Settings
| Setting | Description | Default |
|---|---|---|
appointment_notify_admin_on_new_appointment |
Send email to the notification address on new appointments. | true |
appointment_notify_client_on_appointment |
Send confirmation email to the client on appointment submission. | true |
appointment_notify_client_on_status_change |
Notify client when appointment status changes (confirmed, cancelled, completed). | true |
appointment_notify_provider_on_new_appointment |
Notify the assigned provider when they receive a new appointment. | true |
appointment_notify_provider_on_cancellation |
Notify the assigned provider when an appointment is cancelled. | true |
Admin: Services
The Services page (Appointments → Services) manages your catalog of bookable services.
Services List
A sortable, paginated table showing:
- Name (translatable)
- Duration (formatted, e.g., “1h 30min”)
- Price (formatted with currency)
- Max Capacity
- Appointments count
- Status (active/inactive badge)
- Position (display order)
Per-row actions: Edit, Delete.
Creating & Editing Services
- Name (translatable, required for default locale)
- Slug (translatable, auto-generated if empty)
- Description (translatable)
- Duration (minutes): required, 5–480 minutes. Determines slot length.
- Price (numeric, optional)
- Currency (required, from active currencies)
- Max Capacity (integer, 1–100)
- Active toggle
- Position (display order)
Admin: Providers
Providers represent the staff members or resources that deliver your services. Managed at Appointments → 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
appointment/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 (Appointments → Appointments) shows all appointments in a paginated table.
Appointments List
Filterable by status and provider. Columns include:
- Client name & email
- Service name
- Provider name
- Date & time
- Status badge (pending/confirmed/cancelled/completed)
- Payment status (if payment is enabled)
Stats cards at the top show: total appointments, pending count, confirmed count, today’s appointments, and this week’s appointments.
Appointment Detail & Status Management
The detail page (Appointments → {appointment}) shows:
- Client information: name, email, phone, IP address, booked-at timestamp
- Appointment information: service, provider, date, start time, end time, 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
appointment_notify_client_on_status_change is enabled).
Cancellations also notify the assigned provider.
Calendar View
The Calendar page (Appointments → Appointments → Calendar) provides a visual month view:
- Events are loaded via AJAX (
GET admin/appointment/appointments/calendar-events). - Filter by provider using the dropdown.
- Appointments show as time-based events on the calendar.
- 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 (Appointments → Settings) is organized into sections:
General Settings
- Enable Appointments: toggle to show/hide the front-end appointment page.
- Multi-Provider Mode: let clients choose their provider.
- Provider Selection: show the provider selection step in the wizard (requires multi-provider mode).
Provider Labels
- Provider Step Label: custom heading for the provider selection step (translatable).
- Any Provider Label: label for the “Any Available Provider” option (translatable).
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.
- Services Per Row: number of service cards per row on the front-end.
- Providers Per Row: number of provider cards per row on the front-end.
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 appointment alerts.
- Five toggle switches controlling which emails are sent (see Notifications).
Cancellation Policy
- Cancellation Policy: free-text displayed on the booking page (translatable).
CAPTCHA
- Enable CAPTCHA on Booking Form: toggle. Requires a CAPTCHA provider to be configured in core settings.
Front-end: Booking Page
The appointment booking page is available at /{locale}/appointment and provides a step-by-step wizard.
Routes
| Method | URL | Route Name | Description |
|---|---|---|---|
| GET | /{locale}/appointment |
appointment.index.localized |
Appointment wizard page |
| POST | /{locale}/appointment |
appointment.store.localized |
Submit an appointment |
| GET | /{locale}/appointment/confirmation/{appointment} |
appointment.confirmation.localized |
Confirmation page |
| GET | /{locale}/appointment/my-appointments |
appointment.my-appointments.localized |
User’s appointment history (auth required) |
Non-localized variants (without {locale}) are also registered.
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 appointment.
Slot & Availability API
Two JSON endpoints power the front-end calendar and slot selection:
/{locale}/appointment/slots
Description
Returns available time slots for a specific service, provider, and date.
Query Parameters
service_id |
Required | Service ID |
provider_id |
Optional | Provider ID (null = any available) |
date |
Required | Date (YYYY-MM-DD) |
Response
/{locale}/appointment/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.
Slot Generation Logic
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 appointment booking (or successful payment), the user is redirected to
/{locale}/appointment/confirmation/{appointment}.
- Shows a success message with appointment details.
- Displays service name, provider, date, time slot, and total price.
- Status note explaining the appointment is pending confirmation.
- Links to “Back to Home” and “Book Another”.
Payment Checkout
When appointment_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}/appointment/checkout/{appointment} |
appointment.checkout.localized |
Payment checkout page |
| POST | /{locale}/appointment/checkout/{appointment} |
appointment.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
appointment_require_payment is enabled,
a warning is shown on the admin settings page. The checkout page will show no payment options.
My Appointments
Authenticated users can view their appointment history at /{locale}/appointment/my-appointments.
- Matches appointments by
user_idorclient_email(covers appointments made before registration). - Paginated list (15 per page) sorted by date descending.
- Each entry shows: service name, provider, date, time slot, status badge, and payment status.
This page is also accessible from the user account menu via the “My Appointments” 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 AppointmentManager service and silently catch any sending errors.
| Notification Class | Recipient | Trigger | Setting |
|---|---|---|---|
AppointmentConfirmationNotification |
Client | Appointment created (after payment, if required) | notify_client_on_appointment |
NewAppointmentAdminNotification |
Admin (notification_email) | Appointment created | notify_admin_on_new_appointment |
NewAppointmentProviderNotification |
Assigned provider | Appointment created | notify_provider_on_new_appointment |
AppointmentStatusChangeNotification |
Client | Status changed to confirmed/cancelled/completed | notify_client_on_status_change |
AppointmentCancellationProviderNotification |
Assigned provider | Appointment cancelled | notify_provider_on_cancellation |
AppointmentObserver, which watches the status field for changes.
This ensures notifications fire regardless of how the status is updated (admin panel, API, etc.).
Updating
There are two ways to update this add-on: via the admin panel (recommended) or manually replacing files.
Method 1: Admin Panel Upload (Recommended)
- Download the latest
.zipfile of this add-on. - Go to Admin panel → Add-ons and click the Upload button.
- Select or drag the
.zipfile into the upload area. - A confirmation prompt will show the current and new version numbers. Click Replace to proceed.
- Go to Admin panel → System Update (
/admin/update) to apply any pending database migrations.
Method 2: Manual File Replacement
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
php artisan migrate
Pending migrations run once, so the command is safe to repeat.
Step 3: Rebuild Assets
Required if the update includes new or modified theme SCSS/JS files.
Step 4: Clear Caches
php artisan config:clear
php artisan route:clear
php artisan view:clear
Uninstallation
Switching an add-on off without losing anything is a deactivation: go to Admin panel → Add-ons, find Appointments and click Deactivate.
- Its routes, views, admin menu entries and permissions stop being registered, and its front-end pages stop answering.
- Its database tables and all the data they hold are kept, and its files stay under
extensions/addons/appointment/. Nothing is deleted. - The purchase code recorded at activation is kept too, so activating the add-on again does not ask for it.
- Deactivation is refused while another active add-on depends on this one: deactivate that add-on first.
Click Activate on the same card to switch it back on. Pending migrations are re-run, assets are republished, and the add-on picks up exactly where it left off.
Removing
Removing is permanent and destroys the add-on's data. The Remove button only appears on a deactivated add-on, so removal is always two steps:
- Deactivate Appointments (see Uninstallation).
- Click Remove on its card and confirm the prompt.
The admin panel then, in one pass:
- runs the add-on's uninstall hook, if it ships one, while its code is still on disk;
- revokes the permissions declared in its
addon.json; - rolls back its migrations (this drops its database tables and every row they hold) and purges its entries from the
migrationstable, so a later reinstall migrates from scratch; - deletes its published assets:
public/addons/appointment/,public/vendor/appointment/andstorage/app/public/addons/appointment/; - deletes the add-on directory
extensions/addons/appointment/; - deletes its row in the
addonstable (the recorded purchase code goes with it) and clears the application cache.
Removal is refused, with an explanatory message and before anything is destroyed, when the add-on is still active, when another active add-on depends on it, or when the web server (PHP) user cannot delete extensions/addons/appointment/. In that last case, give that user write permission on the directory and on its parent, then try again.
Deleting the folder over FTP or SSH is not equivalent: the add-on's tables, its entries in the migrations table and its addons row are all left behind, and its card stays in the list. Use Remove in the admin panel instead.
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
appointment_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
appointment_min_advance_hourscutoff (e.g., today before the minimum advance window). - All slots may be booked. Check if
appointment_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).
Appointment page returns 404
- Ensure
appointment_enabledis set totruein settings. The controller throws a 404 when appointments are disabled. - Verify the add-on is activated in Admin → Add-ons.
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.
Client not receiving confirmation emails
- Check that
appointment_notify_client_on_appointmentis 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.
Multi-provider mode not showing provider selector
- Ensure
appointment_multi_provideris enabled in settings. - Verify
appointment_provider_selectionis also enabled. - Verify at least one active provider exists and is assigned to the selected service.
CAPTCHA not displaying on booking form
- Ensure
appointment_captcha_enabledis set totruein settings. - Verify a CAPTCHA provider (reCAPTCHA, hCaptcha, etc.) is configured in Admin → Settings → Security.
- Check that the CAPTCHA driver is set and API keys are valid.
Status change notifications not sending
- The
AppointmentObserverhandles status change notifications. Ensure it is registered inAppointmentServiceProvider::boot(). - Only transitions to Confirmed, Cancelled, or Completed trigger client notifications.
- Provider cancellation notifications require the provider to have an email address set.
Appointments v1.0.0: Part of the Larapen CMS platform.
© BeDigit. All rights reserved.