2026 · Case study · Phill Morgan
Back Chat Music Quotation Platform
A quotation and booking platform for the live music industry, with dynamic pricing, digital contracting, and full QuickBooks invoicing.
Available for portfolio review on request.
Stack
- Laravel 12
- Next.js 15
- React 19
- TypeScript
- Tailwind CSS
- shadcn/ui
- Sanctum
- QuickBooks API
- Google Maps API
- DomPDF
- Zod
- Docker
Role & Scope
Freelance build. Solo product design, full-stack development, QuickBooks integration, deployment, ongoing maintenance.
Overview
Back Chat is a quotation and booking platform for the live music industry. Operators generate multi-band quotes for events, from weddings and corporate functions to festivals, and run the whole booking end-to-end: enquiry, quote, contract, invoice, payment.
The stack is a Laravel 12 API and a Next.js 15 frontend, deployed independently and talking over a Sanctum-authenticated REST API.
Key decisions
Laravel API + Next.js frontend over a Laravel monolith. A Laravel monolith with Blade or Livewire would have been faster to ship and easier to deploy: one stack, one build, one server. I split them. The reason was the customer-facing quote view: it’s highly interactive (live recalculation as customers toggle bands and add-ons), and pushing that interactivity through Livewire over the network felt wrong for the experience the product was selling. A Next.js frontend running against a clean REST API gave instant client-side recalc, sharp SPA-feel navigation for admins, and a frontend codebase that could later serve a mobile app without duplication. The trade was the extra deployment surface and the Sanctum SPA auth setup, neither of which added meaningful complexity.
An explicit state machine for the quote lifecycle over boolean flags. The quote moves through four named states: Draft → Ready → Accepted → Signed. Each transition is a named event, each one fires the correct notifications, and each one unlocks or locks specific actions. The tempting shortcut was a ready boolean, an accepted boolean, a signed boolean, and a lot of conditional logic scattered across controllers. The state-machine approach meant transitions are centralised, auditable, and reversible only through explicit event replay, which matters because the workflow is commercial and legal (PDF contracts, postcode verification, QuickBooks invoices). Easier to reason about, easier to extend, and the pattern records its own audit trail.
QuickBooks native webhooks over polling. The QuickBooks integration is two-way: invoices created in the app must appear in QBO; status changes in QBO (invoice paid, voided, sent) must reflect back in the app. Polling QBO’s API on a schedule would have been simpler to implement but would have meant latency between QBO state changes and the app reflecting them, plus needless API calls. Webhooks from QBO direct to a dedicated endpoint mean near-instant status updates with minimal load. The trade is webhook reliability: a nightly reconciliation walks recent invoices to catch anything a webhook might have missed.
Dynamic Pricing Engine
The pricing engine calculates quotes across six intersecting variables:
- Distance brackets: tiered pricing based on travel from the band’s base, with distances calculated via the Google Maps Distance Matrix API against geocoded postcodes
- Date brackets: seasonal and peak-date adjustments for bank holidays, wedding season, and New Year’s Eve
- Time-slot pricing: start-time rates, with per-band availability rules
- Add-on services: photography, extended sets, lighting rigs, DJ add-ons, each priced per band
- Multi-band discounts: applied automatically when multiple bands are booked on the same event
- Timed discounts: promotional reductions with countdown timers, lockable at a fixed amount
Admins can override any calculation with manual pricing. Every override is logged with a timestamp and reason, giving full flexibility without losing the audit trail.
Multi-Band Booking Workflow
A quote can carry several bands on a single event. Each band has its own pricing matrix, time slots, and add-on list. Customers receiving the quote toggle bands on and off, change time slots, and adjust add-ons, with the total recalculating live in the browser.
The quote lifecycle moves through four states: Draft → Ready → Accepted → Signed. Each transition fires the right notifications and unlocks the next set of actions.
Contract and Signature Management
PDF contracts are uploaded, versioned, and tracked. Signing runs in a fixed order: admin signs first, then customer. Before the customer can sign, their postcode is verified against the quote, adding an identity check on top of the link itself.
Contracts can only be swapped before the customer has signed. Once a customer signs, the contract is frozen.
Invoicing and Payment Integration
Payments split into a 20% deposit and an 80% balance. Both invoices are created in QuickBooks Online automatically through the QBO SDK, including customer record creation, VAT/tax codes, and webhook-driven status updates from QuickBooks back into the platform.
Customers verify and download their invoices from the public quote view, authenticated by matching their postcode.
Public Quote Experience
Every quote lives at a unique UUID URL. The public view gives the customer an interactive breakdown: bands, add-ons, time slots, running total. Analytics sessions track each customer’s interactions so operators can see which quotes are being read and which are going cold.
Technical Architecture
- API: Laravel 12, service-oriented, with dedicated services for pricing, routing, geocoding, contract signing, QuickBooks sync, invoice generation, and notification dispatch
- Frontend: Next.js 15 with React 19, shadcn/ui on Radix primitives, React Hook Form with Zod validation, Axios for API calls
- Data layer: Eloquent with heavy pivot use, custom accessors for computed business logic, eager-loaded relationships, query scopes for reusable filters
- Security: Sanctum SPA auth, CSRF protection, rate limiting, input sanitisation throughout
- Infrastructure: Dockerised dev environment, queue-driven background work, per-user notification preferences