Case study · UK · Event ticketing
Ontick: a custom Laravel ticketing platform, Stripe instalment payments, and two mobile apps.
Off Eventbrite, off platform commissions, on to a fully owned ticketing stack: multi-organizer, with Stripe-orchestrated instalment payments, QR tickets released only on full settlement, a venue-side check-in app, and a customer app. £2M+ in sales generated to date, with the engagement ongoing.
Client
Event ticketingOntick, event ticketing platform
Surfaces shipped
- Web platform, organizer plus customer
- Check-in mobile app for venue staff
- Customer mobile app on iOS and Android
Numbers from the work
Sales generated through the platform since launch
Super admin plus per-organizer events, by default
Automated, idempotent, recovery built in
Surfaces shipped: web plus check-in app plus customer app
The platform
A fully owned Laravel ticketing stack, public to back-office.

How the engagement started
An Eventbrite margin problem worth solving in code.
Ontick came to us running their events on Eventbrite. The experience was fine for buyers, the operational tooling was fine for staff, but the commission Eventbrite took on every ticket was the largest fixed cost on the business. At the volume Ontick was already running, the percentage they were paying made a custom platform a clearly positive ROI decision rather than a vanity project.
The brief was specific: a custom booking platform with a rich public front-end and a full back-end for the team, on a stack the client could own end-to-end. One feature was non-negotiable from day one: tickets had to be purchasable in instalments, with the QR codes only released after the final payment landed. That single requirement set the architectural agenda for everything that followed.
TL;DR
- Migrated off Eventbrite onto a fully owned Laravel platform, eliminating the per-ticket commission that was the original reason for the engagement.
- Multi-organizer architecture by default: a super admin creates organizers; each organizer creates events with multiple ticket types, per-tier inventory, and per-tier sale windows.
- Stripe-orchestrated instalment payments with strict guarantees: QR tickets are not released until the full balance settles; every instalment is idempotent against duplicate charges; failed payments recover automatically.
- Two React Native mobile apps on top of the platform: a venue-side check-in app tuned for speed (thousands queuing) and a customer app with push notifications for upsell, reminders and recovery.
- £2M+ in sales processed through the platform to date; the engagement is ongoing.
Stage 1 · Custom Laravel platform
Multi-organizer from day one, not retrofitted.
The platform is built end-to-end on Laravel: the public booking surface, the organizer back-end, the super-admin back-end, and the REST API the mobile apps later consumed. One framework, one team, one deploy.
The multi-organizer model was the most consequential architectural choice. A super admin can create any number of organizer accounts; each organizer manages their own events, ticket inventory, sale windows and payouts. Events themselves support multiple ticket tiers with independent pricing, capacity, and on-sale or off-sale dates. The data model was designed for this from day one rather than retrofitted, which is usually where multi-tenant ticketing platforms run into compounding problems later, as we cover in our companion multi-tenant architecture cost study.
Stage 2 · Stripe-orchestrated instalments
Instalments look simple from the outside. They are not, in production.
Each charge is a network call that can fail, succeed slowly, succeed twice, or end up in an ambiguous state. Each ticket can be in any of those states at any point in its payment lifecycle. The engineering job is to keep the system in a known-good state at every step. The shape we shipped:
Stripe handles the actual capture
We store payment methods via Stripe's tokenised flow and orchestrate the schedule from our side, calling Stripe's API when each instalment is due.
QR tickets are held back until full settlement
A booking can be paid down over months; the customer gets confirmation and a schedule at booking time, but the actual ticket QR is only minted and emailed once the final instalment posts. This is the single most important business rule and it is enforced in code, not in policy.
Idempotency on every payment touchpoint
Every charge uses a Stripe idempotency key derived from the booking id and instalment index. The webhook handler insert-on-conflicts against an event-id-primary-key table before doing any work, so the same Stripe event arriving twice produces exactly one state change.
Automated failed-payment recovery
A failed charge enters a retry schedule with backoff, customer notifications at sensible intervals, and a soft-delete path for bookings that never recover. The operations team see what is happening but do not have to touch it for routine failures.
Duplicate-charge protection
Beyond idempotency keys, we layered defensive checks: booking-level locks during a payment attempt, a guard on the webhook side that refuses to mark a booking paid past its total, and a daily reconciliation cron that compares our ledger to the Stripe balance transactions.
The idempotency and reconciliation discipline is the same pattern we wrote up in our companion Stripe webhooks reference architecture post.

Stage 3 · Check-in app for venue staff
One design constraint: speed. Each check-in lands in under a second.
Once the platform was generating real ticket volume, the next operational pain point surfaced quickly: door staff were checking in attendees from a browser-based view that was not built for the queue tempo of a real event. We shipped a dedicated check-in app built in React Native consuming the same Laravel REST API. The decisions that earn that latency:
Offline-first scan path
The app caches the full attendee list for the event on session start. Scanning resolves locally; the network round-trip happens in the background to mark the attendee server-side. The door operator never waits on the API.
One-tap operation
Open camera, scan, green tick, scan the next. No confirmation dialogs, no navigation. Edge cases (already-checked-in, wrong event, comp ticket) surface inline without breaking the flow.
Bluetooth-scanner support
For staff who prefer dedicated hardware over the camera.
Role-scoped login
A staff member can only check in attendees for events they are assigned to. Permissions live on the API side, not the app.
Stage 4 · The customer app
The booking experience in the buyer's pocket, with messaging as the unlock.
Most recently we shipped a customer-facing React Native app that gives the buyer the entire booking experience at their fingertips: browse, book, schedule instalments, access tickets. The bigger commercial unlock is the messaging layer. Push notifications power three flows that previously sat in email and underperformed:
Upsell
Targeted offers for upgrade tickets, add-ons or partner events, segmented by the customer's booking history.
Failed-payment recovery
A push lands within minutes of an instalment declining, with a one-tap path back into the app to retry. Conversion on this channel is materially higher than the equivalent email recovery sequence.
Upcoming payment schedule reminders
A heads-up before the next instalment is due, with the option to swap the payment method without leaving the app.
The push infrastructure itself is the same pattern we wrote up in our companion push notifications on Expo plus FCM plus APNs post, with the same focus on real delivery measurement rather than ticket-success rates.

Outcomes
Where the engagement stands today.
- £2M+ in sales processed through the Ontick platform since launch.
- Zero platform commissions: every pound of ticket revenue stays with the organizers, minus Stripe's payment-processing fees.
- Multi-organizer architecture running in production, with new organizers onboarded by the super-admin team without engineering involvement.
- Instalment-payment engine running cleanly, including the duplicate-protection, idempotency, and automated-recovery guarantees, none of which have generated a real-world incident to date.
- Two production mobile apps: the check-in app used at every event, the customer app live on iOS and Android.
- The engagement is ongoing: we run the platform day-to-day, ship new features alongside the client's product team, and own the post-launch observability and recovery flows.
Stack summary
One Laravel core, two React Native apps, Stripe underneath.
- Web platform
- Laravel (PHP): public booking, organizer back-end, super admin, REST API
- Frontend (web)
- Blade plus Vue.js components, hand-audited bundle
- Database
- MySQL with multi-organizer schema, booking plus instalment ledger
- Payments
- Stripe: tokenised payment methods, idempotency-keyed charges, webhook-driven reconciliation
- Scheduled jobs
- Laravel queues plus scheduler: instalment runs, failed-payment retries, daily Stripe reconciliation
- Check-in app
- React Native (iOS plus Android), offline-first scan cache, Bluetooth-scanner support
- Customer app
- React Native (iOS plus Android), Expo plus FCM plus APNs for push, deep links
- Observability
- Structured request logging, Stripe-event ledger, push-delivery instrumentation
Where it shipped
A live, ongoing engagement.
Live engagement
The Ontick platform is live, processing instalment bookings across multiple organizers, with the check-in app running at every event door and the customer app live on the iOS and Android stores. £2M+ in sales generated to date, with the per-ticket commission that drove the original brief now sitting at zero.
The engagement is ongoing: day-to-day platform support, new feature work, post-launch observability and payment reconciliation are all owned by us.
Building something in this shape? The three engagements that map directly to the Ontick rebuild: SaaS web app development, React Native app development, and maintenance and support.
Got an Eventbrite-style margin problem?