Dev White-Label Engagementcontracting · confidential client
A booking-fee funnel with a full job lifecycle — built where no plugin existed.
A contractor needed customers to qualify, pay a booking fee, and enter a managed pipeline. No plugin treated a booking as a first-class entity, so we built one.
WooCommerce can take a payment. It cannot own a booking or run a job lifecycle.
The client needed customers to move through a guided qualifier, pay a booking fee, and enter a structured work pipeline — from initial intake all the way through site visit, quote, scheduling, and completion. That's a lifecycle problem, not a payment problem. Off-the-shelf plugins don't see the distinction.
No booking entity
WooCommerce orders are payment records, not job records. There was no place to hang a job lifecycle, a qualifier answer set, or a status that meant anything to the business.
Static service pages
Every service needed its own qualifier flow — different questions, different options, different logic. That content had to be editable in the admin without a code deploy every time something changed.
Payment tightly coupled
If the payment processor ever changed, every piece of booking logic would break. The business needed payment to be one swappable component inside a larger system — not the system itself.
Six layers. One plugin.
Each layer owns one responsibility. None of them know more than they need to.
Service CPT
Each service is a native WordPress custom post type. Fee, lead time, site-visit requirements, and the entire qualifier flow are configured per-service in the admin — no code deploy to add a new service or change a price.
Qualifier flow builder
A dashboard-configurable step builder defines any number of qualifier questions with multiple-choice options per service. Code owns the funnel behavior; the client owns every word of its content. Flow is stored in post meta, never hardcoded.
Booking entity + status machine
The booking is a first-class custom database table record — not a WooCommerce order. It owns a 10-state lifecycle from intake to cleared, with validated status transitions, action hooks on every change, and full audit trail.
Payment adapter interface
An interface contract means WooCommerce is one implementation, not a dependency. The booking controller never calls WooCommerce directly — only the adapter. Switching processors is an adapter swap with zero booking-layer changes.
Dual contact record
Contact data lives in two layers: the original funnel-session capture (never overwritten — the source-of-intent record) and the final WooCommerce billing details (editable at checkout). Both link to one booking for the CRM layer.
Notification system
Lifecycle-driven transactional emails for every event. Every subject, body, and recipient is filterable via WordPress hooks — no code deploy to change messaging. Qualifier answers are mapped to human-readable labels in admin emails.
From the moment a customer submits to the moment the job is cleared.
The funnel creates a pending_payment booking and the payment layer advances it to fee_paid. The full vocabulary is defined now — every downstream phase of the job is already modeled so later additions never need to redefine it. The next layer — CRM and post-booking management — picks up exactly where this lifecycle leaves off.
From first click to confirmed booking.
Server-side fee enforcement
The booking controller is the trust boundary. Fee is always recomputed from the service config — the browser has no ability to set or manipulate the price.
Admin-configurable per-service flows
Three launch services seeded with real qualifier flows (lead remediation, painting, concrete). Each fully editable in the WP admin — new services, new questions, new options with no code changes.
Homepage funnel router
A [home_funnel] shortcode surfaces up to 4 featured services with their own taglines, page URLs, and display order — managed entirely from the admin dashboard.
Full admin bookings dashboard
Bookings table in WP admin — searchable, filterable by status, showing both the original funnel capture and the confirmed billing layer side-by-side.
Security-first at the trust boundary.
Nonce-verified REST endpoint
Every booking submission requires a valid wp_rest nonce as a CSRF guard. No anonymous request without a verified nonce reaches the booking logic.
Server-side answer validation
Qualifier answers are validated against the service's step schema on the server. Invalid or missing answers return a clean error — the booking is never created with unvalidated data.
Pluggable payment interface
An interface contract enforces what a payment adapter must do. A null adapter supports dev/staging environments. Switching processors never touches booking logic.
Preserve-by-default uninstall
Deactivation only flushes rewrite rules. Deleting the plugin preserves all booking data and settings. An optional purge block is present but disabled — for operators who ever want true full removal.
Full input sanitization
Every field sanitized at the controller before the booking record is created — sanitize_text_field(), sanitize_email(), sanitize_title(). No raw user input touches the database.
Action hook architecture
booking_created, booking_status_changed, booking_fee — all filterable. The business can customize behavior without ever modifying core plugin code.
Need a booking system that owns the full job lifecycle?
We build what the plugins don't. Tell us the problem.