// system architecture · v2.0

Zynto Stack

Complete architecture map — every service, every data flow, every integration powering the canteen ordering platform.

Node.js · Express Firebase MongoDB Supabase Flutter HTML/CSS/JS Admin Cashfree Payments Cloudinary Betterstack Logs Socket.io
01Client layer
📱
Flutter App
Mobile · Android
Native Android app for students and vendors. Handles Google Sign-In, real-time order tracking, menu browsing, and FCM push notifications.
Student viewVendor viewFCMGoogle auth
🌐
Flutter Web
Web · Flutter
Web version of the Flutter app compiled to HTML/JS. Same codebase, same real-time updates via Firebase RTDB listeners.
Same codebaseReal-time
🖥️
Admin Panel
Web · HTML / CSS / JS
Standalone web panel built in vanilla HTML, CSS, and JS. Manages canteens, vendors, payments, analytics, and support tickets.
Canteen controlAnalyticsTicketsBlock/unblock
🔐
Firebase Auth
Auth · Firebase
Google Sign-In via Firebase. The ID token is sent with every API request as a Bearer token and verified server-side with cached validation (55 min TTL).
Google OAuthID TokenToken cache
02Backend core — Node.js / Express
Express Server
Runtime · Node.js
Core HTTP server with compression, CORS, rate limiting (100 req/15 min global, 5 orders/min strict), and verifyToken middleware on all protected routes.
Rate limitingCORSCompressionAuth middleware
🔌
Socket.io
Realtime · WebSocket
Broadcast layer for instant UI updates. Emits menuUpdated, canteenUpdated, and canteenCreated events to all connected clients.
menuUpdatedcanteenUpdatedcanteenCreated
🗄️
In-memory Cache
Performance · Node Map
Three Map-based caches: userCache, canteenCache (5 min TTL), menuCache. Token cache with 55 min TTL and max 10,000 entries.
userCachecanteenCachemenuCachetokenCache
03Data layer — databases & storage
🍃
MongoDB
Database · Primary store
Main persistent store. Collections: users, canteens, menuitems, orders. Indexed on email, vendorId, order_id.
userscanteensmenuitemsorders
🔥
Firebase RTDB
Realtime · Live state
Live order tracking nodes. Paths: currentOrders/vendor/, currentOrders/student/, paymentStatus/, users/{uid}/fcmToken.
currentOrderspaymentStatusfcmToken
🟢
Supabase
Database · Postgres
Dedicated support ticket system. Table: tickets with uid, subject, message, status, admin_reply, replied_at columns.
tickets tableopen / closedadmin reply
☁️
Cloudinary
Storage · Images
Menu item image uploads. Vendors upload to vendors/{uid}/ folder. Served via Cloudinary CDN as secure HTTPS URLs.
Menu imagesCDNVendor folders
📋
Betterstack
Observability · Logs
Structured server logs for every event: auth failures, order creation, webhook results, admin actions. Log files per date.
AUTH logsORDER logsWEBHOOK logs
04Payment & notification layer
💳
Cashfree PG
Payments · Gateway
Payment gateway for INR transactions. Backend creates order via POST /pg/orders with 2% gateway fee absorbed. Returns payment_session_id to client.
INR only2% feesession_idWebhooks
🪝
Cashfree Webhook
Events · Payment status
Handles PAYMENT_SUCCESS and PAYMENT_FAILED events. On success: marks order PAID, generates 4-digit PIN per canteen, pushes to RTDB, fires FCM. Idempotent.
PAYMENT_SUCCESSPAYMENT_FAILED4-digit PINIdempotent
🔔
Firebase FCM
Push · Notifications
Push notifications for key events. Vendor gets New Order alert. Student gets Payment Success, Order Packed, and Order Delivered.
New orderPayment okPackedDelivered
05Order lifecycle — end to end
01
Student browses
Flutter app · Menu loaded from MongoDB + cached
02
Create order
Express validates items + prices from DB
03
Cashfree PG
Payment session created · Client pays
04
Webhook fires
PAID status · 4-digit PIN · RTDB updated
05
Vendor notified
FCM push + live order appears via RTDB
06
Packs order
Vendor marks Packed · Student gets FCM
07
PIN verify
Student shows PIN · Vendor scans · Delivered
08
Logged
All steps written to Betterstack logs
06Admin capabilities
🏪
Canteen Management
Admin · Control
Create canteens, assign vendors by email, force open/close, block/unblock. Socket.io broadcasts changes instantly to all connected clients.
CreateBlockForce close
📊
Payment Analytics
Admin · Reports
Daily revenue charts, per-canteen breakdown, today's totals, failed/pending orders, and undelivered (PAID but not yet delivered) order tracking.
RevenueBy dateBy canteen
🎫
Support Tickets
Admin · Supabase
View, search, filter, and reply to student tickets stored in Supabase. Admin can close tickets after resolution. Full pagination support.
SupabaseReplyClose
🗑️
Cache Control
Admin · Ops
One-click clear of all in-memory caches (user, canteen, menu). Forces fresh DB reads on next request. Essential after manual DB edits.
Clear all cachesManual refresh
07  // endpoint flowcharts — every route, every node, animated
POST /getrole  — identify user, auto-create if new, return role + name
Flutter / AdminBearer token in header
App sends Firebase ID token
after Google Sign-In
verifyTokencheck tokenCache first
55 min TTL · max 10,000 entries
Falls back to Firebase Admin SDK
Re-caches decoded token
userCache hit?Map.get(uid)
Avoids MongoDB read
Returns cached role/name instantly
MongoDB usersfindOne by email
Looks up by email (indexed)
Returns uid, role, name, phone
User exists?
If null → new user flow
Default role = student
zyntoaditya@ → admin
insertOne userscreate new user doc
uid, email, name, role, createdAt
Set userCachecache for future calls
BetterstackNEW_USER_REGISTERED
200 OKrole · uid · name · email
POST /getUserInfo  — resolve uid → display name (vendor lookup)
Client{uid} in body
verifyToken
userCache.get(uid)cache hit → name only
Returns cached name without DB read
MongoDB usersfindOne {uid} → {name}
200 {name}
404 not found
POST /getCanteens  &&  POST /getMenuItems  — 5-min cached reads with role split
Clientrole in body
verifyToken+ role check
canteenCache / menuCachehit → return instantly (5 min TTL)
Key = "all" or "vendor:{uid}"
Menu key = canteen_id
Auto-deleted via setTimeout
Vendor role?
Vendors see only their canteen
Students see all canteens
MongoDB canteensfindOne by vendorId
MongoDB canteensfind({}) all
Set cacheexpire 5 min
200 canteens[]
POST /createOrder  — 5/min rate-limited · server-side price validation · Cashfree session
Client{orders:[{canteen_id, items[]}]}
orderLimiter5 req/min · per IP
Stricter than global 100/15min
Prevents order spam / abuse
verifyToken
MongoDB menuitemsfindOne per canteen_id
Fetches real prices server-side
Client prices are IGNORED
Prevents price tampering
Compute totalsgrandTotal + 2% fee
chargeableAmount = grandTotal/0.98
gatewayFee = charged - grand
Cashfree APIPOST /pg/orders
order_id, amount, currency
customer_id, name, email, phone
Returns payment_session_id
MongoDB ordersinsertOne status=PENDING
Enriched items with real prices
grand_total, gateway_fee, chargeable_amount
BetterstackORDER_CREATED
200 OKorder_id · payment_session_id
429 Too manyrate limit hit
POST /webhook  — Cashfree callback · idempotent · PIN generation · RTDB + FCM fanout
Cashfreewebhook POST
Event type?
PAYMENT_SUCCESS_WEBHOOK
or PAYMENT_FAILED_WEBHOOK
findOneAndUpdatestatus≠PAID → set PAID
Atomic + idempotent
Returns null if already PAID
Already PAID?
Null = duplicate webhook
Return 200 immediately
200 Already processedidempotent skip
Generate 4-digit PINMath.floor(1000+rand*9000)
One unique PIN per canteen
Stored in MongoDB orders
Also inside RTDB orderData
RTDB vendor nodecurrentOrders/vendor/{cid}/{oid}
Vendor's Flutter app sees
new order via RTDB listener
RTDB student nodecurrentOrders/student/{uid}/…
RTDB paymentStatuspaymentStatus/{uid}/{oid}
shown:false triggers popup
in student Flutter app
FCM push × 2vendor: New Order 🛎️
student: Payment Success ✓
Fire and forget
errors caught and ignored
MongoDB updateOnedeliveryPin + pinUsed=false
findOneAndUpdatestatus≠FAILED → set FAILED
RTDB paymentStatusstatus: FAILED
BetterstackPAYMENT_SUCCESS/FAILED
POST /updateOrderStatus  — vendor advances order status · FCM on Packed & Delivered
Vendor app{order_id, canteen_id, status}
Auth + uid checkuid must == canteen_id
checkAuthorized() — only canteen
owner can update their orders
status == Delivered?
Remove RTDB vendorcurrentOrders/vendor/…
MongoDB updateorders.$.status=DELIVERED
All canteens delivered?
Every canteen sub-order
must be DELIVERED
Partial vs full completion
Remove student nodefull order done
Set student partialone canteen done
MongoDB set DELIVERED+ delivered_at:new Date()
RTDB set statusvendor + student nodes
MongoDB updateorders.$.status = status
FCM to studentDelivered🎉 or Packed🍱
BetterstackORDER_STATUS_UPDATED
POST /verifyDelivery  — PIN scan delivery verification · replay-safe
Vendor QR scan{order_id, canteen_id, pin}
Auth + uid check
Find orderMongoDB by order_id
PIN already used?
deliveryPinUsed flag
prevents replay attacks
400 PIN usedor 404 not found
PIN matches?
400 Invalid PIN
Mark PIN useddeliveryPinUsed: true
Remove vendor RTDBupdate student node
FCM: Delivered 🎉to student
200 DELIVERED
BetterstackDELIVERY_VERIFIED
POST /resolveShortOrderId  — last-6 short ID → full order_id for QR scan lookup
Vendor app{short_id, canteen_id}
Auth + uid check
MongoDB orders$regex short_id$ + canteen_id
status PAID or DELIVERED
Regex suffix match on order_id
Finds orders ending in short_id
200 full order_id
404 not found
POST /addMenuItem · /editMenuItem · /toggleMenuItem · /deleteMenuItem  — vendor menu CRUD
Vendor appvendorId in body
uid == vendorId?checkAuthorized()
Only the canteen owner
can edit their own menu
403 Unauthorized
Socket.io emitmenuUpdated broadcast
All clients get real-time update
action: add/edit/toggle/delete
MongoDB menuitems$push / $set / $pull
add → $push menuItems
edit → $set menuItems.$
toggle → $set isAvailable
delete → $pull by id
Update menuCachein-memory sync
Cache patched in-place
Stays fresh after write
Cloudinary upload/vendor/upload-menu-image
Separate route for images
Multipart → Cloudinary SDK
Returns secure_url CDN link
BetterstackMENU_ITEM_ADDED/EDITED…
200 {success:true}
POST /toggleCanteen  — open/close canteen with admin-block guard
Vendor app{vendorId, isOpen}
Auth checkuid == vendorId
Opening? Check isBlocked
Only checked when isOpen=true
If blocked by admin → 403
MongoDB canteensfindOne {isBlocked}
403 Account blockedblocked:true in response
Socket.io emitcanteenUpdated
MongoDB canteens$set {isOpen}
Invalidate caches"all" + "vendor:{uid}"
BetterstackCANTEEN_TOGGLED
POST /raiseticket  &&  POST /getmytickets  — student support via Supabase Postgres
Student app{subject, message}
verifyTokenget uid/email/name
Supabase tickets INSERTuid, email, name, subject,
message, status=open
Stored in Supabase Postgres
Admin can view/reply/close
Supabase tickets SELECTWHERE uid=me
ORDER BY created_at DESC
201 ticket{}
200 tickets[]
POST /getOrderHistory · /getTodayVendorTotal · /getVendorOrdersByDate  — analytics with IST dates
Client{date:"YYYY-MM-DD"}
verifyTokenuid from token
Compute IST boundaries+05:30 offset → UTC range
new Date(date+"T00:00:00.000+05:30")
Correct for India timezone
MongoDB ordersfind DELIVERED + date range
Student: filter by user_id
Vendor: aggregate by canteen_id
Admin: all orders
Aggregate pipeline$unwind + $match + $group
totalAmount + totalOrders
Used by getTodayVendorTotal
and getVendorOrdersByDate
200 orders[]+ totals
POST /admin/*  — admin-only guard · full canteen control · analytics · ticket management · cache wipe
Admin panelHTML/CSS/JS
verifyToken+ requireAdmin guard
email==zyntoaditya@gmail.com
OR role==admin in MongoDB
403 if neither matches
403 Admin required
/lookupUser + /createCanteenfind user → promote → insert
1. findOne by email
2. set role=vendor in users
3. insertOne canteen
4. setCustomUserClaims vendor
5. insertOne empty menuitems
/blockCanteen + /closeCanteenisBlocked · force isOpen
Block: isBlocked=true, isOpen=false
Close: set isOpen directly
Both broadcast via Socket.io
/payments + /todayTotalaggregate by date range
MongoDB pipeline: $unwind orders
$match by date + status=DELIVERED
$group by canteen → revenue + count
/failedOrders + /undeliveredOrdersPENDING + PAID orders
/gettickets · /replyticket · /closeticketSupabase ticket CRUD
MongoDB users+canteens+menuitems write
Firebase Admin SDKsetCustomUserClaims
Sets role=vendor claim
on Firebase Auth user
Socket.io broadcastcanteenUpdated/Created
MongoDB aggregateorders pipeline
Supabase ticketsquery / update / close
Invalidate cachescanteenCache + userCache
+ menuCache (/clearCache)
Admin can wipe all in-memory
caches via /admin/clearCache
BetterstackADMIN_CREATE/BLOCK/
CLOSE/CLEAR_CACHE
// service directory
Flutter (App + Web)
Student & vendor interface. Google login, real-time order tracking, menu ordering, PIN-based delivery verification.
Firebase (Auth + RTDB + FCM)
Google auth tokens, live order state in RTDB nodes, push notifications via FCM with high-priority Android channel.
MongoDB (zyntoDB)
Primary persistence — users, canteens, menu items, full order documents with delivery PINs and status history.
Supabase (Postgres)
Support ticket system — raise, view, reply, close. Queried via Supabase JS client with row-level filtering.
Cashfree
INR payment gateway. Order creation, session token, webhook callbacks for success/failure with idempotent processing.
Cloudinary
Menu image CDN. Vendors upload via multipart form; server uploads to Cloudinary and returns a secure URL.
Socket.io
WebSocket broadcast for instant UI sync — menu edits, canteen open/close, new canteen creation pushed to all sessions.
Betterstack
Structured log ingestion. Every auth event, order lifecycle step, webhook, and admin action written with severity levels.