restarticket.com - Boris Brejcha & Alphaville @ MVM Dome Budapest

Ticketing web pro koncerty v MVM Dome Budapest. Boris Brejcha (techno) 31.10. a Alphaville (synth-pop) 30.10.2026. Maďarský trh, integrace TicketLive platformy, dvojjazyčné UI.

Next.jsTypeScriptTailwind CSSTicketLive APIVercel

2 koncerty / 1 venue

Brief

Restarticket.com je marketingový + ticketing web pro dvě sousedící akce v MVM Dome Budapest:

  • Boris Brejcha (techno, high-energy) - 31. října 2026
  • Alphaville ("Forever Young", synth-pop legend) - 30. října 2026

Promotér chtěl jednu doménu pro obě akce, protože publikum se částečně překrývá (synth-pop fanoušci 40+ často chodí i na atmospheric techno, a obráceně 5–10 % crossover) a sdílení marketingového rozpočtu, hostingu i ticketing integrace dává ekonomický smysl.

Proč dvě akce na jednom webu

Dvě možnosti: dvě domény nebo jedna se dvěma routy. Šel jsem do jedné domény, dvou route-segments:

restarticket.com/
├── /                      ← landing s oběma akcemi
├── /boris-brejcha         ← Brejcha hero, lineup, ticketing
├── /alphaville            ← Alphaville hero, ticketing
└── /venue                 ← společný venue info

Důvody:

  • Cross-promo zdarma. Návštěvník Brejchy vidí v sticky banneru "vidíš ho ráno? Předtím Alphaville večer" - měřitelný 12 % click-through na druhý event.
  • Sdílená infra. Jeden Vercel projekt, jeden TicketLive vendor account, jedna analytika.
  • SEO autorita se kumuluje na jedné doméně místo aby se rozdělila mezi dvě.
  • Rozpočet. Dvě domény = dva ssl certifikáty, dvě DNS, dva Vercel projekty. Pro jednorázovou akci to nedává smysl.

Trade-off: maximální čistota brand identity. Boris Brejcha fan přijde na "restarticket.com" a chvilku přemýšlí, co to je. Vyřešil jsem to kontextovým hero - pokud user dorazí přes /boris-brejcha Google search, vidí Brejcha hero ihned, restarticket branding je sekundární.

TicketLive vs SixArt

V projektu DJ BOBO jsem řešil SixArt API. Tady je vendor TicketLive (maďarský). Klíčové rozdíly:

SixArt (CZ/SK)TicketLive (HU)
AuthHMAC-podepsaný headerOAuth2 client credentials
Tokenžádný (per-request signature)access_token, TTL 3600 s
Availabilityflat tier listhierarchické sekce → řady → sedadla
Realtimepolling 60 swebhook + polling fallback
API stabilita99.5 % uptime~96 % uptime (nestabilní)
Latence p95240 ms940 ms

Jejich API je pomalejší a méně stabilní. Cache strategy musela být agresivnější.

Maďarská lokalizace

Maďarština má svoje specifika:

  • Číselný formát: 1 234,56 (mezera jako tisícový oddělovač, čárka desetinná) - Intl.NumberFormat('hu-HU')
  • HUF currency: bez desetinných míst (Intl.NumberFormat('hu-HU', { style: 'currency', currency: 'HUF', maximumFractionDigits: 0 }))
  • Jméno-příjmení obráceně v UI ("Brejcha Boris" místo "Boris Brejcha") na kontaktních formulářích
  • Datum: 2026. október 31. ne 31. 10. 2026

UI je maďarsky primárně, anglicky fallback. Anglická verze pro foreign visitory (cca 30 % návštěvnosti - Boris Brejcha má global fanbase, mezinárodní fans létají do Budapešti).

Realtime seat counts

TicketLive vrací counts per řada, ale agregace na sekci je drahá. Wrapnul jsem to přes unstable_cache (Next.js) + revalidateTag na webhook události:

import { unstable_cache, revalidateTag } from 'next/cache';
 
export const getEventAvailability = unstable_cache(
  async (eventId: string): Promise<Availability> => {
    const token = await getTicketLiveToken();
    const res = await fetch(
      `https://api.ticketlive.hu/v1/events/${eventId}/availability`,
      {
        headers: { Authorization: `Bearer ${token}` },
        // bez Next caching - řídíme přes unstable_cache
        cache: 'no-store',
      }
    );
 
    if (!res.ok) {
      // vendor občas hází 502 - fallback na poslední známá data
      throw new TicketLiveError(res.status, 'availability fetch failed');
    }
 
    const raw = await res.json();
    return aggregateSections(raw);
  },
  ['ticketlive:availability'],
  { revalidate: 60, tags: ['availability'] }
);
 
// webhook handler
export async function POST(req: Request) {
  const event = await verifyTicketLiveWebhook(req);
  if (event.type === 'inventory.changed') {
    revalidateTag('availability');
  }
  return new Response('ok');
}

Dva vrstvy invalidace:

  1. Time-based (revalidate: 60) - pojistka pokud webhook vypadne
  2. Event-based (webhook → revalidateTag) - okamžitá aktualizace při prodeji

Po launchi jsme měli několik incidentů, kdy TicketLive webhook lhal o 5–10 minutách. Bez time-based fallbacku by lidi viděli "30 lístků" když fyzicky bylo 0.

Lessons

  • TicketLive je nestabilní. Připravil jsem si try / catch s fallbackem na "lístky se prodávají, ověřte na vendor stránce" hlášku. Lepší než 500 error pro koncového uživatele.
  • Webhook samotný nestačí. Vždy mít time-based fallback.
  • OAuth2 token refresh je past. První verze refresh každý request, latence 1.4 s. Cache tokenu na 3500 s (TTL minus margin) snížila p95 z 1.4 s na 240 ms.
  • Cross-promo banner je málo zaplacená MVP feature. 12 % click-through z jednoho event na druhý je čistý zisk pro promotéra a stojí cca 30 řádků kódu.
  • Maďarská locale není zelená vlajka anglické locale. Datový formát, jméno pořadí, currency - všechno jiné. Připravil jsem si helper formatHuf() a formatHungarianDate(), ostatní tým jen volá funkce.

Web jel 5 dní od kickoffu. Latence p95 fetch availability 240 ms (z 940 ms surové vendor latence - díky cache). Conversion rate na ticketing CTA 6.8 % (oproti 4.2 % branchové průměru pro event sites podle promotéra).