All case studies

Hospitality / Events

Multi-Tenant Event & Ticketing Platform

A multi-tenant event management platform with Stripe-backed payments, Apple Wallet and Google Wallet ticket passes, QR-code check-in, and operational analytics — supporting multiple organizations on a single deployed system.

Role
Lead architecture, full-stack engineering, AWS infrastructure
Duration
12+ months
Sector
Hospitality / Events

What was on the table

The operating partner needed a ticketing platform purpose-built for the kind of events they ran — multi-tenant from day one (multiple organizations, multiple branded experiences), capable of handling free events alongside paid ones, with proper digital ticketing on iPhone and Android.

Off-the-shelf platforms each missed a subset of requirements: Eventbrite-style flows wouldn’t support the multi-tenant branding model, simpler tools couldn’t handle Apple Wallet pass generation, and most platforms wouldn’t expose a check-in API rich enough to support the operations team’s existing process.

What we built

The platform is a Go service (Gin) backed by PostgreSQL 14, with a Vue 3 + TypeScript front-end (Pinia for state, Vue Router, Vue I18n). Authentication runs through Casdoor SSO with role-based access control — admins, organizers, and check-in staff each have appropriately scoped permissions, including domain-level administration across tenants.

Payment processing runs through Stripe, with webhook handlers verifying signatures and using idempotency keys to deduplicate. Refunds, chargebacks, and dispute handling are first-class workflows, not afterthoughts.

Each ticket generates an Apple Wallet PKPass and a Google Wallet pass at purchase time. Passes are signed, hosted on S3 behind CloudFront, and updated when event details change (venue, time, lineup) — so a ticket on someone’s phone reflects current truth.

Check-in is a mobile QR scanner application that works against the tenant’s event roster. The scanner tolerates offline pockets — scans queue locally and reconcile when connectivity returns.

Ticketing supports tiers, age restrictions, child-ticket parent linking, free events, optional phone/address collection, and configurable per-event data collection. Email delivery runs through Postmark with templated multi-language ticket emails.

How we got there

The hard part of multi-tenancy is not the data model — it is the operational model. We built tenant scoping into every query path from the first commit, with database-level constraints that make cross-tenant data leaks impossible at the storage layer rather than relying on application checks.

Apple Wallet pass generation is exacting — bad signing, bad timestamps, bad image dimensions all produce passes that fail silently on iPhone. We invested heavily in the test infrastructure: passes generated in CI are checked against a known-good fixture set so signing regressions cannot ship.

Stripe webhook handling is the easiest place to lose money. Every webhook call is logged, signature-verified, and idempotency-keyed before any business logic runs. Replay is safe. Dropped webhooks are reconciled on a scheduled job.

What changed for the business

The platform now handles tickets across multiple branded experiences from a single deployment. Customers receive a digital ticket that adds itself to Apple Wallet or Google Wallet on tap. Event staff scan and check in from any device. The operations team sees real-time analytics — registrations, revenue, check-in throughput — across all tenants and events.

The system has run continuously through high-traffic event launches without scaling intervention, on standard AWS ECS-on-Fargate sizing.

What this looks like as a service

If you run events at scale, need real digital ticketing (not just a PDF emailed at purchase), and want a system that does not assume every event looks like every other event — this is the kind of build we have already done end-to-end.

Have a system that needs this kind of work?

We take on a small number of engagements at a time. If your problem rhymes with what's above, get in touch.

Start a conversation