Back to projects

Next.js 16, Prisma, PostgreSQL, Stripe, BullMQ, Docker

Koncertügynökség

Full-stack · In development

A production-grade ticketing platform for a concert agency's MVM Dome events. The MVM Dome has no public ticket API — the organizer runs its own inventory, gate export, and invoicing. This system is the single source of truth for sector, seat, and standing inventory: browse, hold, pay, invoice, transfer, and gate export in one stack. The buyer picks seats on a two-level SVG arena map or an EAA/WCAG-equivalent list view, holds the cart for ten minutes, pays via Stripe Checkout, and receives a named ticket with QR and PDF in their account while számlázz.hu issues the e-invoice automatically. Race-safe inventory uses a unique seat-hold index, SELECT FOR UPDATE on GA zones, and a partial unique index on sold tickets — no double-selling under concurrent on-sale demand.

Buyer → event browse (hu/en/de/sr)
     → SVG seat map or accessible list picker
     → 10-minute cart hold → Stripe Checkout
     → named QR/PDF ticket + számlázz.hu e-invoice

Super admin:
  event/pricing/layout CRUD · occupancy heat map · row closures
  KPI dashboard · refunds/chargebacks · audit log · gate CSV export

Stack:
  Next.js App Router · Prisma/PostgreSQL 17 · Redis/BullMQ worker
  Docker Compose VPS · web replicas for on-sale spikes · GitHub Actions CD

Deep dive

Why a custom platform

Arena concerts at the MVM Dome need seat-level control the venue's public box office does not expose. The organizer owns the full lifecycle: inventory, pricing tiers, row open/close, named tickets, Hungarian e-invoicing, chargebacks, and a CSV export shaped for the venue's gate system. One database is the authority — not a third-party ticketing API with opaque holds.

Race-safe inventory

On-sale spikes are the hard part. Seat holds get a DB-level unique index; GA standing zones use SELECT FOR UPDATE; sold tickets sit behind a partial unique index so two checkouts cannot claim the same seat. Holds expire after ten minutes and release back to inventory automatically.

Customer flow in four languages

The storefront ships HU, EN, DE, and SR via next-intl. Accounts require e-mail verification; billing address and per-ticket holder names are collected before checkout. Paid ticket transfer lets buyers reassign a named ticket without bypassing the gate export. The accessible list picker mirrors the SVG map for EAA/WCAG-equivalent seat selection.

Super-admin operations

Operators manage events, pricing, sector layout, and row closures from a super-admin surface: occupancy heat map, KPI dashboard, refund and chargeback handling, append-only audit log, and beléptetési CSV export for the venue gate system. Admin auth runs through Better Auth with e-mail plus 2FA.

Production architecture

Next.js 16 App Router and React 19 on the frontend; server actions and Route Handlers with Zod on the backend; Prisma 7 over PostgreSQL 17. Stripe Checkout webhooks drive a state machine; számlázz.hu Számla Agent handles e-invoice and storno asynchronously via BullMQ workers on Redis. QR tickets and PDFs use pdf-lib; charts use Recharts. Docker Compose on a VPS with Caddy TLS, GHCR images, web replicas for on-sale load, and GitHub Actions CI/CD.

Highlights

End-to-end MVM Dome ticketing: two-level SVG seat map (~19k seats) plus GA standing, 10-minute holds, Stripe Checkout, számlázz.hu e-invoice, named QR/PDF tickets
Race-safe inventory: unique seat-hold index, SELECT FOR UPDATE on GA zones, partial unique index on sold tickets — no double-selling under demand
Super-admin ops: event/pricing/layout CRUD, occupancy heat map, row closures, KPI dashboard, refunds, audit log, gate-system CSV export
Customer flow in HU/EN/DE/SR: verified accounts, billing address, per-ticket holders, paid transfer, EAA/WCAG-equivalent list seat picker
Production stack: Next.js App Router, Prisma/PostgreSQL, Redis/BullMQ workers, Docker Compose VPS with web replicas, GitHub Actions CD
In active development — not yet publicly deployed