A full-stack, real-time restaurant ordering platform that eliminates traditional paper menus and manual order-taking. Customers scan a table-specific QR code, browse menus, place orders, and complete payments — entirely from their mobile browser, with no app installation required. Restaurant staff manage incoming orders through a live-updating dashboard.
Built as a production-ready prototype demonstrating modern full-stack architecture, real-time communication, role-based access control, and automated transactional workflows.
Each physical table is assigned a unique QR code that encodes its table ID. When a customer scans the code, the system initializes a session tied to that table — enabling seamless multi-restaurant ordering without requiring login or account creation. The table context persists across the entire order lifecycle (browsing → cart → payment → tracking).
The platform supports multiple restaurants operating within a shared food court environment. Each restaurant manages its own menu through an authenticated dashboard:
- Categorized menus — items are organized under restaurant-specific categories (Starters, Main Course, Beverages, Desserts)
- Full CRUD operations — restaurant owners can create, update, toggle availability, and delete menu items in real time
- Media uploads — menu item images are uploaded and served through a dedicated media API with file storage
Unlike client-only cart solutions, QR Dine persists cart state on the server, bound to the customer's table session. This ensures:
- Cart data survives page refreshes and browser closures
- Multiple devices at the same table share a unified cart
- Automatic cart cleanup after checkout
Orders follow a state machine (Pending → Preparing → Ready → Completed) with live status updates pushed through WebSocket channels:
- Restaurant channel (
ws/restaurant/{id}) — the dashboard receives instantNEW_ORDERevents when customers place orders, with no polling required - Order channel (
ws/order/{id}) — customers track their specific order's status transitions in real time on the tracking page
The WebSocket manager implements automatic dead-connection cleanup to prevent resource leaks.
The payment system implements a Stripe-like approval workflow:
- Customer initiates payment → server simulates processing delay
- Generates a unique transaction ID (
TXN{random}_{uuid}) - Creates/updates the
Paymentrecord and transitions order status fromPendingtoPreparing - Triggers the post-payment notification pipeline (receipt generation → email delivery)
The system is designed with a PAYMENT_MODE config toggle (dummy / live) to facilitate integration with real payment gateways without architectural changes.
After successful payment, the system executes a multi-step notification pipeline:
- PDF Receipt Generation — uses ReportLab to produce professional receipts with itemized charges, tax breakdown, transaction ID, and customer details
- Email Delivery — sends an HTML-formatted confirmation email with the PDF receipt attached via Gmail SMTP (TLS-secured)
- SMS Notification — architecture in place for SMS delivery via configurable providers
Each step is independently fault-tolerant — a failure in email delivery does not block receipt generation or order processing.
Three distinct user roles with JWT-based authentication:
| Role | Capabilities |
|---|---|
| Admin | Manage all restaurants, create restaurant accounts, view/impersonate any restaurant, configure system settings (tax %, platform fee, delivery fee) |
| Restaurant | Manage own menu items, view/update order statuses on live dashboard, upload media |
| Customer | Browse restaurants, build cart, place orders, make payments, track order status (no login required — identified by device ID) |
Admin impersonation enables the super admin to act as any restaurant user for debugging and support purposes.
Customers are identified by a unique device ID (generated client-side), enabling a frictionless experience without mandatory registration. Customers can optionally save their name, email, and phone number — this profile data is then used for:
- Personalizing email receipts
- Populating payment records
- Order history attribution
Tax percentage, platform fees, and delivery fees are stored as system-level settings in the database, configurable through the admin dashboard. These values are dynamically applied during order total calculation, enabling easy adjustments without code changes.
The launcher script (run.py) auto-detects the host machine's LAN IP address, starts both backend and frontend servers, and generates a terminal QR code pointing to the frontend URL. This enables instant access from any device on the same network — ideal for demos and food court deployments.
┌─────────────────────────────────────────────────────────────┐
│ CLIENT (Browser) │
│ React 19 · React Router · Tailwind CSS 4 · Axios · QR.js │
└────────────────────┬────────────────────┬───────────────────┘
│ REST API │ WebSocket
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ SERVER (FastAPI) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Auth │ │ Orders │ │ Payments │ │ WebSocket │ │
│ │ (JWT) │ │ (CRUD) │ │ (Stripe- │ │ Manager │ │
│ │ │ │ │ │ style) │ │ (Channels) │ │
│ └──────────┘ └──────────┘ └──────────┘ └───────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Cart │ │ Media │ │ Profile │ │ Restaurant │ │
│ │ (Server) │ │ (Upload) │ │ (Device) │ │ & Menu CRUD │ │
│ └──────────┘ └──────────┘ └──────────┘ └───────────────┘ │
│ │
│ Services: Email (SMTP) · Receipt (PDF) · Notifications │
│ Security: bcrypt hashing · JWT tokens · OAuth2 bearer │
│ ORM: SQLAlchemy · Migrations: Alembic │
└────────────────────┬────────────────────────────────────────┘
│
▼
┌──────────────┐
│ SQLite DB │
│ (7 tables) │
└──────────────┘
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 7, Tailwind CSS 4, React Router 7, Axios, Lucide Icons, qrcode.react |
| Backend | Python, FastAPI 0.100+, Uvicorn (ASGI), Pydantic v2 |
| Database | SQLite (dev), SQLAlchemy ORM, Alembic migrations |
| Auth | JWT (python-jose), bcrypt password hashing, OAuth2 Bearer |
| Real-Time | WebSockets (FastAPI native), channel-based pub/sub |
| Notifications | SMTP email (Gmail TLS), ReportLab PDF generation |
| DevOps | Single-command launcher, LAN auto-discovery, QR code terminal output |
qr-dine/
├── backend/ # FastAPI application
│ ├── app/
│ │ ├── api/ # Route handlers
│ │ │ ├── auth.py # Login, JWT issuance, admin impersonation
│ │ │ ├── restaurants.py # Restaurant & menu CRUD
│ │ │ ├── orders.py # Order placement, status updates, WebSocket broadcast
│ │ │ ├── payments.py # Payment processing, customer profile linking
│ │ │ ├── cart.py # Server-side cart (table-bound sessions)
│ │ │ ├── profile.py # Anonymous customer profiles (device-ID based)
│ │ │ ├── media.py # Image upload/serving
│ │ │ ├── tables.py # QR table management
│ │ │ ├── settings.py # System config (tax, fees)
│ │ │ └── websockets.py # Real-time order/restaurant channels
│ │ ├── core/
│ │ │ ├── config.py # Pydantic settings (env-driven)
│ │ │ ├── security.py # JWT + bcrypt auth utilities
│ │ │ └── websockets.py # ConnectionManager (pub/sub channels)
│ │ ├── models/ # SQLAlchemy ORM models
│ │ ├── schemas/ # Pydantic request/response schemas
│ │ └── services/
│ │ ├── email_service.py # SMTP email with HTML templates
│ │ ├── receipt_generator.py # PDF receipt generation (ReportLab)
│ │ ├── notification_service.py # Orchestrates receipt → email → SMS
│ │ ├── payment_service.py # Stripe-style payment simulation
│ │ └── sms_service.py # SMS notification (extensible)
│ ├── alembic/ # Database migration scripts
│ └── requirements.txt
├── frontend/ # React SPA
│ └── src/
│ ├── api/ # Axios HTTP client configuration
│ ├── components/
│ │ ├── layout/ # Sidebar, BottomNavigation (responsive)
│ │ ├── payment/ # PaymentForm, OrderSummary, ApproveButton
│ │ └── ui/ # CartDrawer, SearchBar, CarouselBanner, etc.
│ ├── context/ # React contexts (Cart, Auth, Table session)
│ ├── pages/ # Route-level page components
│ └── services/ # API service layer
├── database.dbml # Database schema documentation
└── run.py # One-command launcher (both servers + QR code)
- Python 3.10+
- Node.js 18+
# Backend
cd backend
python -m venv venv
venv\Scripts\activate # Windows (use source venv/bin/activate on macOS/Linux)
pip install -r requirements.txt
cp .env.example .env # Configure your settings
alembic upgrade head # Apply database migrations
python seed_data.py # Seed demo restaurants & menu items
# Frontend
cd ../frontend
npm install# From project root — starts both servers + displays QR code
python run.pyBackend runs on http://localhost:8000 (API docs at /docs), frontend on http://localhost:5173.
| Decision | Rationale |
|---|---|
| Server-side cart over client-side localStorage | Enables multi-device shared cart per table; survives browser closures |
| Device-ID based profiles over mandatory auth | Eliminates friction for customers — no signup required to order |
| WebSocket channels per entity over polling | Sub-second order updates without wasting bandwidth on empty polls |
| Modular notification pipeline | Each step (receipt → email → SMS) fails independently; extensible to new channels |
Pydantic Settings with .env |
Twelve-factor app config; no secrets in code; easy environment switching |
| Alembic migrations over auto-create | Reproducible schema changes; safe for multi-developer workflows |
| Module | Endpoints | Description |
|---|---|---|
| Auth | POST /api/auth/login, /impersonate/{id} |
JWT login, admin impersonation |
| Restaurants | GET/POST/DELETE /api/restaurants, GET /{id}/menu |
Multi-restaurant CRUD with menus |
| Orders | POST /api/orders, PUT /{id}/status, GET /{id}/info |
Order lifecycle with WebSocket broadcast |
| Payments | POST /api/payments/process |
Simulated payment with notification trigger |
| Cart | POST /api/cart/add, GET /{table_id}, DELETE /clear |
Server-persisted table-bound cart |
| Profile | GET/POST /api/profile |
Device-ID customer profiles |
| WebSocket | ws/restaurant/{id}, ws/order/{id} |
Real-time order and status channels |
| Settings | GET/PUT /api/settings |
Tax, platform fee, delivery fee config |
| Media | POST /api/media/upload, GET /{filename} |
Image upload and serving |
Full interactive API documentation available at http://localhost:8000/docs (Swagger UI).
See backend/.env.example for the complete list of required configuration variables.
This project was developed as a college capstone project demonstrating full-stack web development, real-time systems, and software engineering best practices.