Inspiration
It's 12:47. I have thirteen minutes until class, and I'm seven people deep in a canteen line.
Peak lunch is a 10-to-20-minute war: twenty students shouting orders over each other, waving paper chits, fumbling for cash, all praying the kitchen heard them. I've missed the start of class more times than I can count. The professor doesn't care why you're late — I've lost attendance marks over a plate of food. On the worst days I left half my lunch on the tray and sprinted back anyway. A samosa is not worth a debarred semester.
So why, in a building full of phones, is there still zero coordination between hundreds of hungry students and a kitchen ten feet away?
I didn't read about this in a market report — I lived it, three times a week, in that line. And when I talked to the people behind the counter, I found their version was worse. They have exactly two options: stay on cash and paper and drown at every rush, or hand 15-30% of every order to Swiggy and Zomato — a commission a small campus canteen simply doesn't have the margin to pay. "Just build your own system" is a fantasy: a canteen run on a notebook and a cash box cannot hire developers or an agency.
So nothing changes. Year after year, the line just gets longer.
The fix isn't one more delivery app stacked on the same broken economics. It's giving every campus canteen its own system — instantly, no code, no agency, no commission. That became TRAY.
What it does
TRAY is Shopify for campus canteens.
Shopify didn't make merchants rent a stall inside Amazon — it gave each one a store they own and control. TRAY does exactly that for the canteen. One deployment runs every canteen on campus, and a non-technical owner logs in once to instantly get their own running system — no developers, no agency, no code:
- An isolated Admin Console — menu, pricing, live orders, revenue analytics, staff controls, audit logs.
- An isolated Kitchen View — live ticket queue, prep timers, OTP handover.
- Their own domain, if they want it. The canteen owns its system; it is not a tenant on someone else's marketplace.
Every canteen the college adds shows up with its own live menu inside one unified Student App — but each canteen's kitchen and admin data is strictly isolated by Postgres Row-Level Security. The worst-case failure mode is "zero rows," never a leaked one.
Four roles, cleanly separated by blast radius: Student (all campus canteens), Kitchen staff (their canteen), Canteen Admin (one canteen), Campus Admin (cross-canteen analytics).
The student flow replaces every broken step of that 12:47 line: choose a canteen → browse live menus with prep times → pay by single-use UPI QR → track live (Placed → Preparing → Ready in ~300ms) → collect with a 4-digit OTP. No chit. No cash. No shouting.
And the part that changes the math: 0% commission. Payments settle directly to each canteen's bank via Razorpay. Where an aggregator takes 15-30 of every 100 rupees, TRAY takes zero.
How we built it
Next.js 15 (App Router) + TypeScript (strict) + Tailwind v4, on Vercel Edge/CDN.
Supabase carries the weight: Postgres with Row-Level Security for tenant isolation, Auth on per-tenant scoped routes, and Realtime over WebSocket (~300ms) with a polling fallback and exponential-backoff reconnect for when the socket has a bad day.
Money is where we got paranoid. Razorpay UPI with HMAC-SHA256 webhook signature verification (constant-time), and an atomic safe_capture_payment() Postgres function built on SELECT … FOR UPDATE that validates the amount and transitions order state inside one locked transaction — backed by an idempotency ledger and a webhook dead-letter queue, because money deserves a second chance and a paper trail.
Tenant resolution runs through x-tenant-slug middleware. On ops: Upstash QStash for async jobs, Sentry for monitoring, strict CSP + HSTS, and a real /api/health that actually pings the database instead of lying about it.
27 SQL migrations. 191 commits. Shipped.
Challenges we ran into
Making tenant isolation provable, not probable. A forgotten WHERE clause in app code is a data leak waiting to happen. So isolation lives in Postgres RLS, where the failure mode is "zero rows" — not someone else's order on your screen.
"Two students, the last samosa." Payment race conditions and duplicate webhooks could double-sell a single item or capture a payment twice. We solved it with row locks + atomic capture + an idempotency ledger + a dead-letter queue. The network will betray you, usually at lunch — so we planned for it.
Keeping three portals in sync in ~300ms without hammering Postgres — solved with Supabase Realtime push, a polling fallback, and backoff.
Hiding all of this behind a no-code, self-serve experience — so the person who feels the complexity is us, never the canteen owner.
Accomplishments that we're proud of
A non-technical canteen owner stands up their entire operation themselves: one login → isolated Admin + Kitchen, their own domain, no code. If they needed a manual, we'd have failed — they don't.
Money you can trust: 0% commission, direct settlement, and TRAY never touches the funds — backed by HMAC webhook verification, atomic capture (no "two checkouts, one samosa"), idempotency, and a dead-letter queue.
Provable tenant isolation via RLS. Sub-300ms realtime across three portals. A 4-digit OTP that retires the paper token for good.
And we shipped it for real, as students: 191 commits, 27 migrations, CSP/HSTS, Sentry — live at https://trayy.vercel.app.
What we learned
WHERE you enforce a guarantee matters more than how loudly you promise it. Multi-tenancy belongs in Postgres RLS, not in app-layer WHERE clauses you have to remember every single time.
Respect concurrency. Atomic SELECT … FOR UPDATE capture, idempotency, and a dead-letter queue exist because the network will betray you — so design as if it already has.
Realtime fan-out is a different animal than request-response. We learned to push, not poll.
Building for non-technical users deletes your assumptions. If they need a manual, you failed.
And the biggest one: the real innovation isn't a screen — it's the business model. Zero commission + direct settlement + no-code multi-tenancy. Solve the economics, and the queue takes care of itself.
What's next for TRAY
- Waste & inventory: ingredient-level stock, auto-86 for sold-out items, daily waste reports and prep recommendations.
- AI demand forecasting tuned to weather (hot days → cold drinks; rain → chai and snacks), day of week, exam schedules, and historical demand — feeding prep quantities to cut waste before it happens.
- Personalization from student preferences (past orders, veg/diet), plus dish voting and polls so canteens cook what the campus actually wants.
- Pre-order + scheduled pickup, a campus wallet, loyalty, and meal-plan subscriptions.
- Master Control Centre: cross-canteen analytics for college administration.
- Beyond campus: office cafeterias, food courts, hostel messes — same multi-tenant engine.
- Mobile-first PWA with deeper accessibility.
The line was never about food. It was about coordination. TRAY is the coordination layer — and we're just getting started.
Built With
- next.js
- postgresql
- qstash
- razorpay
- react
- row-level-security
- sentry
- supabase
- supabase-realtime
- tailwindcss
- typescript
- upi
- upstash
- vercel
- websockets
Log in or sign up for Devpost to join the conversation.