Telegram

Telegram Bot for Sunday

Telegram-Bot :

Project Overview

Overview

A pickup ordering system for Sunday520 Coffee, built entirely inside Telegram. Instead of asking customers to download a separate app or use a third-party ordering platform, the whole experience — browsing the menu, customizing drinks, and placing an order — happens through a Telegram bot and an embedded Mini App. Customers pay cash on pickup, so the current focus is on making the ordering flow fast, reliable, and abuse-resistant rather than handling payments.

The project is split into three connected pieces: a Telegram bot, a React frontend (the Mini App itself), and an Express backend that ties everything together.

What I Built

Telegram Bot (Node.js + Telegraf) Built the bot layer that acts as the entry point to the whole system. Commands like /start and /menu launch the Mini App using Telegraf’s Markup.button.webApp(), so customers go from a chat message straight into a full ordering interface without leaving Telegram. Configuration (bot token, Mini App URL, admin chat ID) is environment-driven so the same codebase can run against different bots or environments.

Mini App Frontend (Vite + React) Built a four-page ordering flow — Home, Product, Cart, and Checkout — using React Router’s HashRouter, which avoids routing issues with the way Telegram loads Mini Apps in its in-app browser. Integrated Telegram’s WebApp JavaScript SDK directly, and wrote a small helper module to wrap the common operations the app needed repeatedly: initializing the WebApp, reading the logged-in Telegram user, pulling Telegram’s initData for backend verification, closing the Mini App, and triggering native alerts and haptic feedback so the app feels like part of Telegram rather than a website inside it.

Customers can browse drinks by category, customize sugar and ice level per item, build a cart, and check out by entering their name, phone number, and pickup time — all without ever leaving the Telegram client.

Backend API (Express + MongoDB + Prisma) Built the order pipeline that the Mini App talks to. The checkout flow sends a single POST request to /orders containing the cart, customer details, payment method, and the user’s Telegram identity data. On the backend, that request is validated, assigned a waiting number, and turned into two automatic Telegram messages: a detailed order summary sent to the admin chat (customer info, items, sugar/ice level, total, and the customer’s Telegram username/ID), and a confirmation message sent directly back to the customer — so both sides know the order went through without anyone needing to refresh a page.

Data is modeled with Prisma on MongoDB, with separate models for Users, Categories, Menu Items, Orders, and Order Items, which keeps the menu (organized into categories like Special Drink, Coffee Drink, Matcha Drink, and others) easy to manage independently from order history.

Challenges & Solutions

Challenge: Preventing order spam from a single user. Because there’s no payment gate, nothing technically stops one customer from submitting the same order repeatedly. IP-based rate limiting alone wasn’t precise enough, since multiple Telegram users can share a network (or one user can switch networks). Solution: Implemented rate limiting keyed on the customer’s Telegram user ID rather than IP address — extracting it from req.body.telegram.user.id and falling back to IP only when that’s unavailable. Combined with app.set("trust proxy", 1) for accurate IP detection behind Railway’s proxy, this limits each Telegram user to 2 orders per minute regardless of network conditions.

Challenge: Keeping secrets out of the public frontend. The Mini App is a public, client-side React app hosted on Vercel — anything bundled into it is visible to anyone who opens dev tools. Solution: Drew a hard line between frontend-safe and backend-only configuration: the bot token and admin chat ID live only in the Railway backend’s environment variables, while Vercel only ever receives the public API base URL. .env files are excluded from version control on both sides, and the Prisma schema is committed without exposing connection strings.

Challenge: Making the Mini App feel native, not embedded. A generic web page opened inside Telegram feels off — wrong theming, no native interactions, awkward navigation. Solution: Used the Telegram Mini Apps SDK to sync the app’s behavior with the platform: reading user identity straight from window.Telegram.WebApp.initDataUnsafe.user, triggering Telegram’s native alert and haptic feedback APIs instead of browser defaults, and using HashRouter to keep navigation stable inside Telegram’s in-app browser.

What's Next

The bot currently has to run locally for /start to respond, so the next step is deploying it to Railway (likely merged into the existing backend service) for full uptime. Planned improvements also include tightening admin tooling and continuing to harden the order pipeline as real customer traffic comes in.

Architecture Snapshot

Telegram Bot (Telegraf)
│ Markup.button.webApp()

Mini App Frontend (Vite + React, hosted on Vercel)
Home → Product → Cart → Checkout
│ POST /orders (cart, customer info, Telegram initData)

Backend API (Express, hosted on Railway)
Validates order → assigns waiting number → MongoDB (via Prisma)

├──► Telegram message to Admin (order details)
└──► Telegram message to Customer (confirmation)