Résumé
All work
← All work

Full-stack app · 2026

Refined Gatherings

An event booking site where guests book places for a group, pay with Razorpay and get a QR ticket.

My role
Sole developer
Team
Solo
When
Started August 2024, rebuilt August to October 2026

Overview

Refined Gatherings is an event booking site on the MERN stack, which I built alone. Guests book places for a group, pay with Razorpay and get a QR ticket that is checked at the door; admins manage events, bookings, refunds and promo codes. It began in 2024 as a one-page site, and I rebuilt it into a booking system in 2026.

The problem

Online bookings go wrong at the edges. Two guests can book the last place at the same moment, two payments can claim a promo code's last use, and a guest can pay and close the tab before the page hears back. A booking site has to give one right answer in each of those cases, and an admin needs to see and fix what happened.

Screens

The home page.
The events page: search, filters and places left on every event.
An event page: date, places left and the price per guest.

What I built

  • Bookings that cannot oversell: places are claimed in one atomic update, and an unpaid booking holds them for 30 minutes.
  • Razorpay payments: orders created on the server, checked against Razorpay's signature, and confirmed by a webhook if the guest closes the tab.
  • Tickets and check-in: a QR e-ticket for every booking, and a check-in page that admits each ticket once.
  • Waitlist, promo codes and reviews: one live waitlist entry per guest, and verified reviews after the event.
  • The admin dashboard: events, bookings, refunds, promo codes, users, inquiries and analytics.

Decisions and challenges

  • One atomic update instead of a check and then a write. Places, a promo code's uses, a check-in and a rate-limit window are each claimed in a single conditional update, and the tests race requests for the last one.
  • The webhook as well as the page. A guest who pays and closes the tab would never report the payment, so Razorpay's webhook confirms it too, and confirming twice changes nothing.

System architecture

The React app talks to an Express API on Vercel, in Mumbai beside MongoDB Atlas. Razorpay is called only from the server, and photos go straight from the browser to Cloudinary.

Results

  • Tested with 8 bookings sent at once for the last place: one gets it, and the other seven are told it is full.
  • The same kind of test covers a promo code's last use, which goes to one of two guests paying at once, and a payment reported by the page and the webhook together, which is confirmed once.
  • CI runs lint, formatting, the tests and the build on every push.

Built with

React 18, Vite and React Router · Node.js and Express · MongoDB and Mongoose · Razorpay · Cloudinary · Nodemailer · qrcode.react and Recharts · JWT, bcrypt and Google sign-in · Vitest and React Testing Library · GitHub Actions