PHPackages                             justinholtweb/craft-stub - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. justinholtweb/craft-stub

ActiveCraft-plugin

justinholtweb/craft-stub
========================

Booking &amp; appointments plugin for Craft CMS

5.0.0(yesterday)00proprietaryPHP ^8.2

Since Jul 19Compare

[ Source](https://github.com/justinholtweb/craft-stub)[ Packagist](https://packagist.org/packages/justinholtweb/craft-stub)[ RSS](/packages/justinholtweb-craft-stub/feed)WikiDiscussions Synced today

READMEChangelog (1)Dependencies (6)Versions (2)Used By (0)

Stub — Booking &amp; Appointments for Craft CMS
===============================================

[](#stub--booking--appointments-for-craft-cms)

Stub is a booking and appointment scheduling plugin for Craft CMS 5. Built for service businesses, salons, consultants, and anyone who needs to accept bookings online. Multi-provider scheduling, Stripe payments, and a drop-in frontend booking form — no Commerce dependency required.

Requirements
------------

[](#requirements)

- Craft CMS 5.5+
- PHP 8.2+
- `stripe/stripe-php` ^13.0 (included via Composer)

Installation
------------

[](#installation)

```
composer require justinholtweb/craft-stub
php craft plugin/install stub
```

After installation, the **Stub** section appears in the CP sidebar with subnav items for Dashboard, Calendar, Bookings, Services, Providers, and Customers.

Features
--------

[](#features)

### Services

[](#services)

Define bookable services with duration, pricing, buffer times (before/after), capacity per slot, and color coding. Services support soft delete and drag-to-reorder.

### Multi-Provider Scheduling

[](#multi-provider-scheduling)

Each provider (staff member) gets:

- **Weekly schedule** — Set working hours per day of week (in the provider's own timezone)
- **Breaks** — Recurring break periods (lunch, etc.) per day of week
- **Blocked dates** — Date ranges for vacations, holidays, or closures
- **Service assignments** — Choose which services each provider offers
- **Optional Craft user link** — Tie a provider to a Craft user account

### Bookings (Craft Element Type)

[](#bookings-craft-element-type)

Bookings are native Craft elements. This means:

- Full element index with column customization, sorting, and search
- Status filters: Pending, Confirmed, Completed, Cancelled, No Show
- Trash/restore support
- Status transitions with cancellation reason tracking
- Payment status tracking (Unpaid, Paid, Refunded)
- Reference numbers (e.g. `STB-20260215-A1B2`)

### Availability Engine

[](#availability-engine)

The slot generation algorithm respects all constraints:

- Provider's weekly schedule and timezone
- Breaks and blocked dates
- Service duration and buffer times (before + after)
- Existing bookings and service capacity
- Minimum notice period and max advance booking window

### Frontend Booking Form

[](#frontend-booking-form)

A vanilla JS step wizard (no React/Vue dependency) with 6 steps:

1. **Select Service** — Cards with name, duration, price
2. **Select Provider** — AJAX-loaded providers for the chosen service
3. **Pick Date &amp; Time** — Custom calendar grid + time slot list
4. **Customer Info** — Name, email, phone, notes with client-side validation
5. **Payment** — Stripe Payment Element (if service has a price)
6. **Confirmation** — Reference number and booking summary

Embed in any Twig template:

```
{{ craft.stub.bookingForm() }}
```

The customer's browser timezone is auto-detected via `Intl.DateTimeFormat`. All times display in the customer's local timezone on the frontend and the provider's timezone in the CP.

### Stripe Payments

[](#stripe-payments)

- Direct `stripe/stripe-php` integration (no Commerce required)
- Stripe Payment Element with SCA/3DS support
- Webhook handler with signature verification (`payment_intent.succeeded`, `payment_intent.payment_failed`)
- CSRF disabled on the webhook endpoint
- Free services auto-confirm without payment flow
- All Stripe keys support `$ENV_VAR` syntax for environment variable parsing

### Email Notifications

[](#email-notifications)

Three system messages registered in **Utilities → System Messages** (editable by admins):

KeyTriggerRecipient`stub_booking_confirmation`Booking confirmedCustomer`stub_admin_notification`New booking createdAdmin email`stub_booking_cancellation`Booking cancelledCustomer + AdminTemplate variables: `referenceNumber`, `customerName`, `serviceName`, `providerName`, `dateFormatted`, `timeFormatted`, `priceFormatted`, plus full model objects (`booking`, `service`, `provider`, `customer`).

### Calendar View

[](#calendar-view)

FullCalendar v6 integration in the CP with:

- Week, day, and month views
- Provider filter dropdown
- Color-coded events by service
- Click-to-navigate to booking detail

### Dashboard

[](#dashboard)

At-a-glance stats: today's bookings, this week's count, pending confirmations, monthly revenue. Plus a today's schedule list with direct links to each booking.

### Customer Management

[](#customer-management)

- Searchable customer list
- Customer detail view with booking history
- Auto-links to existing Craft users by email
- Find-or-create pattern for returning customers

### Permissions

[](#permissions)

Six granular permissions:

- `stub:viewBookings`, `stub:manageBookings`, `stub:deleteBookings`
- `stub:manageServices`, `stub:manageProviders`, `stub:manageCustomers`

Configuration
-------------

[](#configuration)

All settings are in **Settings → Stub** or via `config/stub.php`:

### General

[](#general)

- `pluginName` — Display name in CP sidebar (default: "Stub")
- `defaultCurrency` — 3-letter currency code (default: USD)
- `defaultTimezone` — IANA timezone for new providers (default: America/New\_York)
- `minimumNotice` — Minutes before a booking can be made (default: 60)
- `maxAdvanceBooking` — Days into the future bookings are allowed (default: 90)
- `slotInterval` — Minutes between slot start times (default: 15)

### Booking

[](#booking)

- `autoConfirmFreeBookings` — Skip pending status for $0 services (default: true)
- `requirePhone` — Make phone field required on booking form (default: false)
- `allowCustomerNotes` — Show notes textarea on booking form (default: true)
- `referencePrefix` — Prefix for reference numbers (default: STB)
- `bookingPageUrl` — URL where booking form is embedded (for email links)
- `termsUrl` — Link to terms &amp; conditions (shows checkbox if set)

### Stripe

[](#stripe)

- `paymentEnabled` — Enable payment processing (default: false)
- `stripePublishableKey` — Supports `$STRIPE_PUBLISHABLE_KEY` syntax
- `stripeSecretKey` — Supports `$STRIPE_SECRET_KEY` syntax
- `stripeWebhookSecret` — Supports `$STRIPE_WEBHOOK_SECRET` syntax

### Notifications

[](#notifications)

- `adminEmail` — Email for admin booking alerts
- `sendCustomerConfirmation` — Send confirmation email to customer (default: true)
- `sendAdminNotification` — Send alert to admin on new booking (default: true)
- `sendCancellationEmail` — Send cancellation emails (default: true)

### Appearance

[](#appearance)

- `primaryColor` — Hex color for booking form UI (default: #2563eb)
- `embedStripeJs` — Auto-load Stripe.js on booking form pages (default: true)

### Anti-Abuse

[](#anti-abuse)

- `enableHoneypot` — Add a hidden field to the booking form; submissions that fill it are silently dropped (default: true)
- `honeypotFieldName` — `name` attribute used for the honeypot input (default: stub\_hp)
- `bookingsPerHour` — Max booking submissions allowed from a single IP per hour; set to 0 to disable (default: 10)
- `paymentIntentsPerHour` — Max create-intent calls allowed from a single IP per hour; set to 0 to disable (default: 30)

The anonymous booking and payment endpoints are protected by three measures: a configurable **honeypot** field that traps naive bots, per-IP **rate limiting** on both submission and payment-intent creation, and a signed **payment token**. The payment token is an HMAC of the booking's identity (keyed by the site security key) returned from the submit response and required by `payment/create-intent`, so booking IDs can't be enumerated to trigger Stripe PaymentIntents.

Twig API
--------

[](#twig-api)

```
{# Render the full booking form #}
{{ craft.stub.bookingForm() }}

{# Query bookings #}
{% set upcoming = craft.stub.bookings()
    .bookingStatus('confirmed')
    .startDateTime('>= now')
    .orderBy('startDateTime asc')
    .all() %}

{# Get all services #}
{% set services = craft.stub.services() %}

{# Get providers (optionally filtered by service) #}
{% set providers = craft.stub.providers(serviceId) %}

{# Access settings #}
{% set settings = craft.stub.settings() %}
```

Stripe Webhook Setup
--------------------

[](#stripe-webhook-setup)

Set your webhook endpoint in the Stripe dashboard to:

```
https://your-site.com/actions/stub/webhook/handle

```

Listen for these events:

- `payment_intent.succeeded`
- `payment_intent.payment_failed`

Database Tables
---------------

[](#database-tables)

The plugin creates 9 tables (all prefixed `stub_`):

TablePurpose`stub_services`Bookable services with pricing and duration`stub_providers`Staff members with timezone and contact info`stub_provider_schedules`Weekly working hours per provider`stub_provider_breaks`Recurring break periods per day`stub_provider_blocked_dates`Date ranges when provider is unavailable`stub_provider_services`Junction table: which providers offer which services`stub_customers`Customer contact info with optional Craft user link`stub_bookings`Booking element content table (FK to elements)`stub_payments`Stripe payment records linked to bookingsLicense
-------

[](#license)

This plugin requires a license purchased through the [Craft Plugin Store](https://plugins.craftcms.com). See [LICENSE.md](LICENSE.md).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/035cb655c55af0e9e5b96754b80fd9703e195c32dbdfc49ae9a43ab9cf8db560?d=identicon)[justinholtweb](/maintainers/justinholtweb)

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/justinholtweb-craft-stub/health.svg)

```
[![Health](https://phpackages.com/badges/justinholtweb-craft-stub/health.svg)](https://phpackages.com/packages/justinholtweb-craft-stub)
```

###  Alternatives

[verbb/formie

The most user-friendly forms plugin for Craft.

102393.6k72](/packages/verbb-formie)[solspace/craft-freeform

The most flexible and user-friendly form building plugin!

54681.3k20](/packages/solspace-craft-freeform)[leantime/leantime

Open source project management system for non-project managers. Simple like Trello, powerful like Jira. Built with neurodiversity in mind.

10.2k4.0k](/packages/leantime-leantime)[spicyweb/craft-neo

A Matrix-like field type with block hierarchy

393813.5k10](/packages/spicyweb-craft-neo)[duncanmcclean/statamic-cargo

Comprehensive e-commerce addon for Statamic. Build bespoke e-commerce sites without the complexity.

3518.3k](/packages/duncanmcclean-statamic-cargo)[verbb/vizy

A flexible visual editor field for Craft.

4250.4k](/packages/verbb-vizy)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
