O
Event commercecompleted

Project dossier

Occasio

Event booking and management platform with payments, QR ticketing, and organizer workflows.

What it solves

Overview

Occasio helps organizers publish events, sell tickets, manage attendees, process payments, and verify QR tickets from an operational dashboard. Interview focus: explain the organizer/attendee split, Prisma relational model, registration-to-order-to-ticket lifecycle, Razorpay and PhonePe verification, QR payload safety, waitlists, discount codes, ticket tiers, polls, team members, certificate generation, uploads, queues, and why payment webhooks are the authority.

Target audience

College festival committees and student organizers.Conference and workshop organizers.Small event teams that need booking and entry validation.

System design

Architecture

The product uses a React and Vite frontend with a Node backend. The backend owns event data, bookings, uploads, payments, and webhooks, while the frontend gives organizers and attendees separate flows. The source schema models users, events, forms, registrations, orders, tickets, waitlists, discounts, reviews, polls, team members, ticket tiers, speakers, and reminders. The backend also includes payment services, ticket services, queue/reminder services, QR utilities, upload middleware, Cloudinary/R2/S3 helpers, and scanner routes.

Architecture diagram

Diagram loads when visible

Attendee experience

Event discovery, booking flow, payment initiation, ticket retrieval, and QR access.

ReactViteTailwind

Organizer console

Event creation, capacity management, media uploads, and attendee reporting.

ReactClient-side routing

Backend API

RESTful endpoints for events, users, bookings, uploads, payments, and ticket verification.

Node.jsExpress

Commerce and verification

Payment gateway integration, webhook reconciliation, and QR-based entry validation.

RazorpayPhonePeQR codes

Registration lifecycle layer

A registration starts as pending form data, creates an order, moves through payment status, then receives a ticket and check-in state.

PrismaRegistrationOrderTicket

Organizer operations layer

Admins manage event teams, polls, reviews, ticket tiers, reminders, speakers, discounts, and certificate templates from operational screens.

TeamMemberPollCertificateDesigner

Asset and document layer

Poster, ticket PDF, and certificate assets are separated from event metadata and routed through upload/storage utilities.

CloudinaryR2S3upload middleware

Implementation surface

Tech stack

ReactFrontend

Attendee and organizer user interfaces.

ViteFrontend

Fast local development and SPA bundling.

Node.jsBackend

Backend runtime for REST APIs and payment callbacks.

ExpressBackend

Routing, middleware, and controller organization.

Razorpay / PhonePeLibrary

Payment initiation, status checks, and webhook confirmation.

Upload middlewareBackend

Event poster and media upload handling.

PrismaDatabase

Typed ORM for PostgreSQL models including events, registrations, orders, tickets, discounts, waitlists, and polls.

PostgreSQL / NeonDatabase

Relational source of truth for users, events, orders, tickets, and organizer operations.

Redis queueDevops

Background reminder, notification, and asynchronous job path for operational workflows.

Cloudinary / R2 / S3Devops

Media and document storage options for posters, tickets, certificates, and downloadable assets.

Operational flow

How it works

Organizers create events and attendees book tickets. Payments are reconciled through webhooks, then QR tickets become valid for entry scanning.

1

Create event

An organizer enters event details, capacity, ticket rules, and media assets.

2

Publish listing

The event appears in the attendee-facing catalog with date, venue, price, and availability.

3

Book and pay

The attendee chooses quantity, starts payment, and the backend creates a pending booking.

4

Reconcile webhook

The payment provider calls the backend, which verifies the payload and marks the booking paid.

5

Issue and scan ticket

The system generates a QR ticket that can be verified at check-in against booking state.

6

Apply discount and ticket-tier rules

Before order creation, the backend computes pricing from ticket tiers, discount code constraints, max uses, and registration quantity.

This keeps pricing authority on the server instead of trusting the browser's displayed total.

7

Generate provider metadata

Payment requests include order, registration, and event identifiers in provider notes or metadata so callbacks can be reconciled safely.

Razorpay notes and PhonePe merchant transaction IDs are essential when users leave and return from hosted payment pages.

8

Update attendance state

Scanner routes mark checkedInAt, checkedOutAt, checkedInBy, and legacy scannedAt fields while rejecting revoked or already-used tickets.

The QR code is not the source of truth; it is a lookup payload for server-side validation.

9

Run post-event operations

Organizers can manage reviews, polls, certificates, reminders, and team access after the core ticket purchase flow.

Sequence diagram

Diagram loads when visible

Concept depth

Key concepts

Payments and ticket state are naturally event-driven because external gateways confirm payment asynchronously after a user leaves the app flow.

In Occasio: Occasio treats payment callbacks as events that transition bookings from pending to paid.

Confidence

Implementation evidence

Code highlights

Payment webhook reconciliation

The backend should verify provider callbacks before marking a booking as paid.

Code highlight loads when visible

The browser redirect is not trusted as payment proof.

Ticket issuance happens after server-side payment confirmation.

QR ticket verification

Ticket scanning checks booking state and prevents duplicate entry.

Code highlight loads when visible

The encoded code is only an identifier; the backend decides validity.

Duplicate scan prevention is part of the verification path.

Idempotent ticket issuance

A payment completion handler should detect already-paid orders before generating a ticket.

Code highlight loads when visible

The existing paid order is returned instead of issuing another ticket.

The order update and ticket creation happen in one transaction.

Server-side pricing authority

The backend computes the amount from ticket tiers and discount codes before creating a provider order.

Code highlight loads when visible

Client totals are display-only; the server recomputes final amount.

Discount use limits must be checked before provider order creation.

Contracts

API design

Base URL: http://localhost:5000/api

GET/events

Lists published events with dates, venue, capacity, and ticket status.

POST/bookings

Creates a pending booking and payment order for selected tickets.

{ "eventId": "evt_42", "quantity": 2 }
{ "bookingId": "book_91", "paymentOrderId": "order_77" }
POST/payments/webhook

Verifies gateway callback and updates booking payment state.

POST/tickets/verify

Validates a QR ticket and records check-in.

{ "ticketCode": "TICKET-9F42" }
POST/registrations

Stores form response, selected ticket tier, attendee identity, and pending registration state before payment.

{ "eventId": "evt_42", "tierId": "tier_student", "formResponse": { "college": "ABC" } }
{ "registrationId": "reg_81", "status": "PENDING" }
POST/orders/{orderId}/phonepe/status

Checks PhonePe transaction status using merchant transaction ID and X-VERIFY checksum.

POST/waitlist

Adds an attendee to an event waitlist when capacity or ticket availability is exhausted.

POST/certificates/generate

Generates participation or prize certificates from event certificate templates and registration data.

POST/polls/{pollId}/vote

Records attendee poll responses while respecting single or multiple choice poll configuration.

State model

Database design

Document or relational event booking store

Data relationship diagram

Diagram loads when visible

events

Event listing, schedule, venue, capacity, and organizer ownership.

idorganizer_idtitlevenuestarts_atcapacity

bookings

Attendee booking intent and status transitions from pending to paid.

idevent_idattendee_idquantitystatus

tickets

QR ticket codes and check-in timestamps linked to paid bookings.

idbooking_idticket_codechecked_in_at

registrations

Attendee form response and lifecycle state before and after payment.

idevent_iduser_emailform_responsestatuscreated_at

orders

Payment order linked to a registration, provider, amount, status, and raw provider metadata.

idregistration_idamount_centsproviderprovider_order_idstatuspayment_data

discount_codes

Event-scoped discount rules with type, amount, max uses, active window, and used count.

idevent_idcodetypeamountmax_usesused_countvalid_until

waitlists

Attendees waiting for capacity, keyed by event and email.

idevent_idemailnamephonecreated_at

polls

Event poll questions, options, response settings, and end time.

idevent_idquestionallow_multipleends_atis_active

Architecture decisions

Trade-offs

Frontend framework

React with Vite over Next.js

The app is an operational SPA where authenticated workflows matter more than SEO or server rendering.

Payment truth source

Gateway webhook over Frontend success redirect

The redirect can be interrupted or forged. The provider webhook gives server-side confirmation.

Ticket format

QR code identifier over Printable free-form receipt

A QR identifier can be scanned quickly and verified against backend state during entry.

Data model

Explicit Registration, Order, and Ticket tables over One booking table with many nullable fields

The lifecycle has distinct responsibilities: user form data, payment reconciliation, and entry validation. Separate tables make state transitions and debugging clearer.

Payment providers

Razorpay and PhonePe support over Single gateway dependency

Indian event flows often need multiple payment options. Supporting both creates integration complexity but improves operational flexibility.

Asset storage

External object storage helpers over Storing files in the database

Posters, ticket PDFs, and certificates are large binary assets; databases should keep metadata and URLs while object storage handles file delivery.

Scanner validation

Server-verified QR payload over QR code as standalone proof

A QR payload can be copied. The scanner must check payment status, event, revocation, validity window, and scan history on the backend.

Lessons learned

Challenges and solutions

Problem

Payment state can become inconsistent if the user closes the browser after payment.

Solution: Use webhooks as the authority and let the booking page poll or refresh state.

Lesson: Commerce flows should treat the server-side payment event as the source of truth.

Problem

Event posters and uploaded assets need consistent handling across routes.

Solution: Centralize upload middleware and keep event metadata separate from file storage concerns.

Lesson: Media upload is a backend boundary, not just a form field.

Problem

Payment providers can retry callbacks or return the user through different redirect paths.

Solution: Use provider metadata to map callbacks to orders and make order-paid transitions idempotent.

Lesson: Payment code must be designed around retries, not just the happy path.

Problem

Capacity can be oversold if two attendees reserve the last ticket at the same time.

Solution: Compute availability server-side and update registration/order state in a transaction or through a locking strategy.

Lesson: Inventory correctness is a backend concurrency problem.

Problem

Certificate and ticket templates can become tightly coupled to one event design.

Solution: Store certificate mappings and ticket styling as JSON configuration attached to the event.

Lesson: Template systems should keep presentation configuration out of business logic.

Runbook

Requirements and future work

Requirements

  • Node.js and npm for backend and frontend development.
  • Payment gateway credentials for Razorpay or PhonePe flows.
  • File upload storage configuration for event media.
  • Organizer and attendee role handling.
  • DATABASE_URL must point to the PostgreSQL/Neon database used by Prisma.
  • Razorpay and PhonePe credentials are required for live payment flows; sandbox credentials are only for testing.
  • Ticket scanning requires backend access so QR payloads can be validated against order and ticket state.
  • Object storage credentials are needed for production posters, certificates, and downloadable ticket assets.
  • Queue or reminder configuration is needed before relying on automated reminders or background notifications.

Future improvements

  • Add live organizer analytics for sales, capacity, and check-ins.
  • Support seat maps and tiered ticket inventory.
  • Add automated refund and cancellation workflows.
  • Add transaction-level capacity locking or reservation expiry for high-demand events.
  • Add offline scanner mode with signed short-lived manifests and later reconciliation.
  • Add organizer audit logs for event edits, check-ins, refund actions, and certificate generation.
  • Add webhook replay tooling so failed payment callbacks can be inspected and retried safely.

Active recall

Interview Q&A

ArchitectureMedium

Why should webhooks decide payment success?

02:00
ConceptsEasy

What does QR verification need beyond decoding the QR code?

02:00
TradeoffsHard

What would you add for high-volume events?

02:00
ArchitectureMedium

Why separate Registration, Order, and Ticket instead of using one booking table?

02:00
CodeHard

How do you make payment callbacks idempotent?

02:00
ConceptsMedium

What should the scanner validate before allowing entry?

02:00
TradeoffsMedium

What is risky about trusting the frontend price?

02:00
ArchitectureHard

How would you prevent overselling capacity?

02:00
TradeoffsMedium

Why store ticketStyle and certificate configs as JSON?

02:00
ConceptsHard

What does PhonePe checksum verification protect against?

02:00
ArchitectureMedium

What background jobs belong in an event platform?

02:00