Ticketing Platform Commercial¶
B2C and B2B ticket sales platform managing match tickets, season passes, seat allocation, and event access for BelFoot FC.
Business Capabilities¶
- Sell match tickets online with real-time seat availability
- Manage season pass subscriptions and renewals
- Allocate B2B ticket blocks for corporate hospitality
- Generate e-tickets with QR codes for stadium access
- Track ticket sales performance and attendance statistics
Bounded Context¶
Data Landscape¶
| Entity | Description | Role | Software Systems |
|---|---|---|---|
| Ticket | Entry pass granting access to an event or seat | Owns | |
| Season Pass | Subscription covering all home matches for a season | Owns | |
| Seat | Specific seat location within the stadium | Owns | |
| Event | Organised gathering such as a match or concert | Owns | |
| Match | Scheduled football match at the stadium | Owns | |
| Customer Profile | Contact and purchase history of a customer | Uses | Salesforce CRM |
Fan Purchases a Match Ticket¶
Deployment - Acceptance - Ticketing Platform¶
Deployment - Development - Ticketing Platform¶
Deployment - Production - Ticketing Platform¶
Deployment - Test - Ticketing Platform¶
Inbound¶
| System | Description | Technology |
|---|---|---|
| Fan | Purchase tickets and manage season passes | |
| Fan Engagement Platform | Get ticket purchase history | JSON/HTTPS |
| Matchday Operations | Get event and ticket data | JSON/HTTPS |
| Product Development | Publish ticket products | JSON/HTTPS |
| Season Ticket Holder | View and manage season pass | |
| Ticketing Agent | Manage ticket allocations and sales |
Outbound¶
| System | Description | Technology |
|---|---|---|
| Integration Platform | Publish ticket purchase events | AMQP/TCP |
| Microsoft Entra ID | Authenticate | JSON/HTTPS |
| Salesforce CRM | Sync customer data on ticket purchase | JSON/HTTPS |
| Stripe | Process ticket payments | JSON/HTTPS |
Technical Architecture¶
Node.js ticketing service backed by PostgreSQL with a React portal front-end. Two deployable components (ticketing-api + ticketing-portal) plus a shared Azure Database for PostgreSQL instance. The API publishes purchase events to Service Bus for downstream ingestion and calls Stripe for payments and Salesforce CRM for customer history.
Authentication & Authorization¶
- Portal (
ticketing-portal, React): OAuth 2.0 PKCE flow against Microsoft Entra ID (tenantbelfoot.onmicrosoft.com). The portal acquires an access token via@azure/msal-browserand attaches it as a Bearer token on every API call. - API (
ticketing-api, Node.js/Express): Validates Entra ID JWTs usingpassport-azure-ad(JWKS, RS256). Two app roles are defined in the Entra app manifest: Fan— granted to all authenticated fans and season-ticket holders; allowsGET /tickets,POST /tickets,GET /season-pass.TicketingAgent— granted to agents in theTicketing AgentsAD group; allows allocation endpoints under/admin/allocations.- Authorization is enforced by the
requireRole(role)Express middleware at the route level. Requests without a valid token return 401; authenticated requests without the required role return 403. - CORS:
https://tickets.belfoot.comandhttps://tickets-acc.belfoot.comare the only allowed origins; credentials are disabled (token flows throughAuthorizationheader). - Stripe and Salesforce CRM outbound calls use service-principal client-credentials flow; secrets are pulled at startup from Azure Key Vault
kv-belfoot-ticketing.
Ticketing Portal¶
React single-page app (Vite, TypeScript). Deployed to Azure Static Web Apps with the API as its linked backend. Major routes:
/— match listing with real-time availability badges (polls/availabilityevery 30s)/match/:id/seats— interactive seat picker/checkout— Stripe Elements + purchase confirmation/season-pass— season-pass dashboard for subscribers/admin— agent allocation console (role-gated)
Ticketing Database¶
Azure Database for PostgreSQL (v15). Key tables:
| Table | Domain | Usage |
|---|---|---|
ticket |
Ticket | One row per issued ticket; includes qr_code, status, event_id, seat_id, owner_email |
season_pass |
Season Pass | Active + historical passes with holder_id, season_year, tier |
seat |
Seat | Stadium seat inventory; section, row, number, accessibility_flag |
event |
Event | Match or non-match events; kickoff_at, capacity, status |
allocation |
Ticket | B2B seat blocks; links partner_id to reserved seats |
purchase_audit |
Infrastructure | Append-only log of purchase intents and outcomes (for dispute resolution) |
Migrations are managed via node-pg-migrate under migrations/ in the ticketing-api repo.
Event Publishing¶
On successful purchase, refund, or transfer, the API publishes a JSON message to the ticket-events topic on the Integration Platform's Azure Service Bus. The message schema lives at docs/events/ticket-events.schema.json in the API repo. The Data Platform subscribes to this topic for lakehouse ingestion (see the Event-Driven Data Ingestion to Lakehouse dynamic view).
Payment Flow¶
Portal -> API POST /tickets { matchId, seatId, paymentMethodId }
API -> DB INSERT ticket (status=pending)
API -> Stripe POST /v1/payment_intents (confirm=true)
Stripe -> API { status: succeeded, paymentIntentId }
API -> DB UPDATE ticket SET status=confirmed
API -> CRM POST /Contact/{id}/Purchase (Salesforce)
API -> Bus PUBLISH ticket.purchased
API -> Portal { ticket, qrCode }
If Stripe fails, the pending row stays in ticket with status=failed for 24h before being hard-deleted by the ticket-cleanup cron job (runs daily at 02:00 UTC).
API Reference¶
Ticketing API¶
Node.js/Express service exposing ticket and season-pass operations. Runs on Azure App Service (S1 in acceptance, P1v3 in production) with horizontal scale-out enabled.
Ticket endpoints (/api/v1/tickets/)¶
| Endpoint | Purpose |
|---|---|
GET /api/v1/tickets/availability?matchId= |
List remaining seats for a match, grouped by section |
POST /api/v1/tickets |
Purchase a ticket: reserves seat, charges Stripe, persists ticket, publishes ticket.purchased event |
GET /api/v1/tickets/{ticketId} |
Retrieve a single ticket including QR code payload |
POST /api/v1/tickets/{ticketId}/transfer |
Transfer a ticket to another fan by email |
DELETE /api/v1/tickets/{ticketId} |
Cancel a ticket and issue a Stripe refund |
Season pass endpoints (/api/v1/season-pass/)¶
| Endpoint | Purpose |
|---|---|
GET /api/v1/season-pass/me |
Return the current user's season pass and renewal status |
POST /api/v1/season-pass/renew |
Renew a season pass for the next season |
GET /api/v1/season-pass/{passId}/matches |
List matches covered by the pass, with check-in status |
Agent endpoints (/api/v1/admin/allocations/) — role TicketingAgent¶
| Endpoint | Purpose |
|---|---|
POST /api/v1/admin/allocations |
Reserve a block of seats for a B2B partner |
GET /api/v1/admin/allocations?partnerId= |
List current allocations for a corporate partner |
DELETE /api/v1/admin/allocations/{allocationId} |
Release an allocation back to general availability |