Architecting Twaicy: How We Built a Zero-Commission Restaurant Commerce Monolith with NestJS, WhatsApp Bots, and UPI Webhooks

The restaurant industry in India operates under brutal economics: margins typically hover between 12% and 18%, yet delivery aggregators take 25% to 35% on every digital order. For cloud kitchens and local dhabas, this model is unsustainable.
We built Twaicy (twaicy.com) to break this paradigm: an Enterprise Zero-Commission Restaurant Commerce Operating System that enables restaurants to run direct digital ordering, counter UPI settlements, and automated WhatsApp storefronts with 0% commission.
Here is the engineering story of how we architected Twaicy as a unified monolith on Bun, Next.js 16, and NestJS 11.
1. The Unified Monolith Gateway
Rather than splitting the project into separate frontend and backend repositories that require complex cross-origin cookies, API gateways, and distributed deployments, Twaicy is structured as a Unified Monolith:
twicy/
├── src/
│ ├── app/ # Next.js 16 App Router (Storefront, Merchant KDS, Admin)
│ ├── features/ # Modular domain UI (Cart, FoodFlex, Dynamic Website Builder)
│ ├── services/ # Shared client utilities & TwaicySoundEngine audio chime
│ └── server/ # NestJS 11 Core Enterprise Backend
│ ├── modules/ # Auth, Store, Product, Order, Payment, WhatsApp, Loyalty
│ └── main.ts # NestJS bootstrap & internal HTTP listener (port 3001)
├── prisma/ # Authoritative PostgreSQL schema & migrations
└── e2e/ # Playwright end-to-end regression suites
Next.js Internal Reverse Proxy
All external browser and mobile traffic hits Port 3000 (https://twaicy.com). Next.js seamlessly rewrites and proxies /v1/*, /socket.io/*, and /uploads/* internally to NestJS on port 3001.
This grants three massive engineering advantages:
- Zero CORS Issues: All cookies, sessions, and WebSocket handshakes originate from the exact same origin.
- Atomic Deployments: A single Docker container hosts both the SSR frontend and the high-throughput NestJS API.
- Single Build Toolchain: Running under Bun v1.2+, compile and startup times drop to sub-second durations.
2. 0% Fee Payment Architecture: Dual-Mode Engine
Payment processing is where most direct-ordering tools stumble. Twaicy solves this with a Dual-Mode 0% Commission Payment Architecture:
┌──────────────────────┐
│ CUSTOMER CHECKOUT │
└──────────┬───────────┘
│
┌────────────────┴────────────────┐
│ │
▼ ▼
[Mode A: PhonePe PG] [Mode B: Counter UPI QR]
Instant UPI Redirect Zero Setup Soundbox Standee
│ │
Webhook SHA-256 HMAC Callback 12-Digit Bank UTR Input
│ │
└────────────────┬────────────────┘
│
▼
┌──────────────────────────────┐
│ ORDER CONFIRMED & KITCHEN │
│ AUDIO CHIME DISPATCHED │
└──────────────────────────────┘
Cryptographic Webhook Verification (PhonePe PG)
To guarantee that rogue actors cannot trigger fake payment confirmations, all incoming webhook payloads are validated against the PhonePe X-VERIFY header using constant-time cryptographic comparison:
// payment-webhook.service.ts
import crypto from 'crypto';
import { Injectable, UnauthorizedException } from '@nestjs/common';
@Injectable()
export class PaymentWebhookService {
verifyPhonePeChecksum(
payloadBase64: string,
checksumHeader: string,
saltKey: string,
saltIndex: string
): boolean {
const stringToHash = `${payloadBase64}/v3/transaction/callback${saltKey}`;
const calculatedHash = crypto.createHash('sha256').update(stringToHash).digest('hex');
const expectedChecksum = `${calculatedHash}###${saltIndex}`;
const isValid = crypto.timingSafeEqual(
Buffer.from(checksumHeader),
Buffer.from(expectedChecksum)
);
if (!isValid) {
throw new UnauthorizedException('PhonePe signature mismatch');
}
return true;
}
}
3. Autonomous WhatsApp Ordering Engine (Baileys)
WhatsApp is India's default consumer operating system. Rather than forcing users onto a browser, Twaicy embeds an autonomous conversational ordering bot powered by the Baileys persistent multi-device library:
- Persistent Authentication: Merchants scan a QR code once in their dashboard. The session is stored in an encrypted PostgreSQL key-value store.
- State Machine Dialogues:
- Customer texts
"Hi"or sends a location pin. - The bot responds with the merchant's live catalog, category carousels, and dietary filters.
- Items added in chat maintain an active Redis cart session.
- Customer texts
- Instant UPI Checkout: Once the customer types
"Done", the bot synthesizes an instant UPI intent link or requests the delivery address for Cash-on-Delivery.
4. Multi-Tenant IDOR Shield & Concurrency Safety
In a multi-tenant platform managing hundreds of restaurants, data leakage between merchants is a catastrophic vulnerability. Twaicy enforces the following invariants:
Mandatory Tenant Scoping in Prisma
// orders.service.ts
export async function getStoreOrder(storeId: string, orderId: string) {
// BANNED: findUnique({ where: { id: orderId } })
// ENFORCED: Strict compound key check with storeId
const order = await prisma.order.findFirst({
where: {
id: orderId,
storeId: storeId, // Guaranteed zero cross-tenant IDOR leakage
},
include: { items: true },
});
if (!order) {
throw new NotFoundException('Order not found for this store');
}
return order;
}
Brute-Force Rate Limiting
All merchant auth endpoints are guarded by Redis sliding-window limiters: 5 consecutive invalid password attempts trigger an immediate 15-minute HTTP 429 lockout.
5. Live Kitchen Sound Chime Engine (Socket.IO)
Visual alerts fail in steamy, noisy kitchen environments. Twaicy pairs WebSocket state broadcasting with a browser-native Web Audio synthesizer:
// twaicy-sound-engine.ts
export class TwaicySoundEngine {
private ctx: AudioContext | null = null;
public playNewOrderChime() {
if (!this.ctx) {
this.ctx = new (window.AudioContext || (window as any).webkitAudioContext)();
}
// Polyphonic dual-tone chime (587.33 Hz D5 -> 880.00 Hz A5)
const osc1 = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc1.type = 'sine';
osc1.frequency.setValueAtTime(587.33, this.ctx.currentTime);
osc1.frequency.exponentialRampToValueAtTime(880.00, this.ctx.currentTime + 0.35);
gain.gain.setValueAtTime(0.8, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.8);
osc1.connect(gain);
gain.connect(this.ctx.destination);
osc1.start();
osc1.stop(this.ctx.currentTime + 0.85);
}
}
Summary
Twaicy demonstrates that high-performance, enterprise-grade commerce platforms do not require fragile microservice sprawl. By embracing a unified Next.js + NestJS monolith running on Bun, pairing PhonePe 0% webhooks with Baileys WhatsApp automation, and applying strict multi-tenant IDOR isolation, Twaicy gives restaurant owners complete sovereignty over their commerce.
- Explore Live Platform: twaicy.com
- Built by: Sarthak Srivastava