Что такое Telegram Mini App и как сделать свой

14 минут чтения

Telegram Mini App это веб-страница, которую Telegram открывает внутри себя. Не отдельное приложение из магазина, не сайт в браузере: тот же HTML, CSS и JavaScript, только окно им выдаёт мессенджер, а вместе с окном имя того, кто его открыл. Человек нажимает кнопку в чате и сразу оказывается в приложении, не устанавливая ничего и не вводя пароль.

Дальше по делу: из чего оно собирается, где ломается и что нужно, чтобы запустить своё сегодня вечером. В конце лежит рабочий шаблон на Python, бот с панелью управления прямо внутри Telegram. Скачивается и запускается за десять минут.

Чем mini app отличается от обычного бота

Бот это диалог. Сообщения, кнопки под ними, иногда меню команд. Всё, что человек делает, он делает по очереди: выбрал, ответил, подождал, снова выбрал.

Mini app это экран. Карточки товаров с картинками, корзина, форма, таблица, календарь, что угодно из веба. Человек видит всё сразу и трогает то, что хочет, а не то, что ему предложили следующим сообщением.

Бот против mini app

Бот
Mini app

Интерфейс

Бот

Сообщения и кнопки под ними

Mini app

Полноценная веб-страница

Порядок действий

Бот

По очереди, шаг за шагом

Mini app

Всё на экране, в любом порядке

Что видно

Бот

Текст и картинки

Mini app

Вёрстка, анимация, таблицы, формы

Где выполняется код

Бот

Только на сервере

Mini app

В браузере Telegram плюс сервер

Трудоёмкость

Бот

Ниже

Mini app

Примерно вдвое выше

Когда достаточно

Бот

Заявка, уведомление, короткий опрос

Mini app

Каталог, корзина, кабинет, отчёт

Правило выбора простое. Если весь сценарий укладывается в три-четыре вопроса подряд, берите бота: mini app там будет из пушки по воробьям. Под этот случай у нас лежит отдельный шаблон разговорного бота, там ни фронтенда, ни панели нет вовсе. Если человеку нужно сравнить варианты, вернуться назад, поменять решение и увидеть итог, берите mini app, диалог такое поддерживает плохо.

Про удвоение я не преувеличиваю. Задача выглядит простой, потом хочется добавить визуал, и по моим проектам время уходит вдвое, иногда больше. Mini app ровно этот случай: к серверной логике добавляется целый фронтенд со своей вёрсткой, состояниями и проверками.

Чем mini app отличается от обычного сайта

Технически почти ничем. Это тот же фронтенд, который вы бы выложили на любой хостинг. Отличия начинаются там, где заканчивается вёрстка.

Он знает, кто пришёл. Обычный сайт видит анонима, и чтобы узнать человека, нужна регистрация: почта, пароль, письмо со ссылкой. Mini app получает идентификатор пользователя от самого Telegram, в момент открытия. Ни формы, ни подтверждения, ни забытого пароля.

Своего адреса у него для человека нет. Технический URL, конечно, есть, но пользователь его не видит и не вводит. Он попадает внутрь через бота или по ссылке вида t.me/имя_бота/имя_приложения.

И его нельзя найти поиском. Обычный сайт можно найти в Google или Яндексе. Mini app искать негде: он живёт внутри мессенджера, поисковики туда не ходят. Трафик приходит из чатов, каналов и рекомендаций. Сайт можно построить так, чтобы клиенты приходили сами, а к mini app приходят только те, кому вы дали ссылку.

Освещённый киоск с поднятой шторкой, встроенный в кафельную стену тёмного коридора
Mini app стоит внутри чужого пространства: своей витрины и своей двери у него нет, а всё нужное умещается в проёме.

Из чего состоит mini app

Четыре части, и ни одну нельзя выкинуть.

Бот. Он нужен обязательно, даже если делать в чате нечего. Приложение привязано к боту, кнопка запуска живёт в боте, и токен бота участвует в проверке подписи.

Фронтенд. Обычная страница: HTML, CSS, JavaScript. Плюс один скрипт от Telegram, который даёт данные о пользователе, тему клиента и системные кнопки.

Сервер. Отдаёт эту страницу и обслуживает её запросы. Именно он проверяет подпись, считает деньги и хранит заказы.

Адрес по https. Тип данных, которым задаётся приложение, в документации Bot API описан прямо: «An HTTPS URL of a Web App to be opened». Без сертификата Telegram приложение не откроет, http работает только в тестовой среде.

Бот и сервер при этом могут жить в одном процессе. В шаблоне так и сделано: до нескольких заказов в секунду разделять их незачем. Общего состояния в памяти у них нет, всё через базу, поэтому разъехаться по разным машинам они смогут потом без переписывания.

Что нужно, чтобы запустить первое приложение

Бот, адрес по https, Python. Всё бесплатное, всё за один вечер.

К @BotFather за токеном, это полминуты: команда /newbot, он возвращает строку вида 123456789:AAG. Дальше туннель. Пока приложение живёт на вашем ноутбуке, постоянного адреса у него нет, а туннель поднимает публичный https-адрес и заворачивает его на локальный порт. Адрес меняется при каждом перезапуске, и для отладки это нормально.

Из пакетов понадобятся два: aiogram для бота и aiohttp для веб-части. Остальное уже в стандартной библиотеке, включая базу данных. Третий пакет нужен, только если включаете ответы нейросетью.

Про версию Python стоит знать заранее. Свежий aiogram требует 3.10 или новее, и на 3.9 установка пройдёт, но поставится не то, что вы ждёте: на нашей проверке подтянулась версия 3.22 вместо актуальной 3.31. Работать будет, обновляться перестанет.

Магазина приложений, модерации и ожидания ревью в этой схеме нет вообще. Первый работающий экран внутри Telegram появляется в тот же вечер.

Как выглядит бот на Python с нуля

Вот весь бот, который открывает mini app. Один обработчик и одна кнопка, библиотека aiogram 3.

bot.py
import asyncio

from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.types import (
    InlineKeyboardButton,
    InlineKeyboardMarkup,
    Message,
    WebAppInfo,
)

dp = Dispatcher()


@dp.message(CommandStart())
async def start(message: Message) -> None:
    button = InlineKeyboardButton(
        text="Открыть меню",
        web_app=WebAppInfo(url="https://ваш-адрес"),
    )
    await message.answer(
        "Меню открывается прямо здесь, без перехода в браузер.",
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[[button]]),
    )


asyncio.run(dp.start_polling(Bot(token="ТОКЕН")))

Ключевая строка тут одна: web_app=WebAppInfo(url=...). Обычная кнопка отправляет текст, эта открывает приложение и передаёт ему данные о пользователе.

Дальше на стороне страницы вы эти данные забираете.

app.js
const tg = window.Telegram.WebApp;
tg.ready();     // сообщаем клиенту, что интерфейс готов
tg.expand();    // разворачиваем на всю высоту

// Строка с данными о пользователе и подписью Telegram.
// Её нужно отправить на сервер и проверить ТАМ.
const initData = tg.initData;

Почему кнопка запуска решает, будет ли авторизация

В примере выше именно инлайн-кнопка, а не кнопка обычной клавиатуры. Разница выглядит косметической, а стоит всей авторизации.

Документация Telegram про данные, которые приходят в приложение, говорит прямо: они пустые, если приложение запущено с кнопки клавиатуры или из инлайн-режима. То есть приложение откроется, картинка будет на месте, а подписанных данных о пользователе в нём не окажется вообще.

Откуда открыли приложение

Что это
Подпись пользователя

Инлайн-кнопка

Что это

Кнопка под сообщением бота

Подпись пользователя

Есть

Кнопка меню

Что это

Слева от поля ввода

Подпись пользователя

Есть

Прямая ссылка

Что это

t.me/бот/приложение

Подпись пользователя

Есть

Кнопка клавиатуры

Что это

Обычная клавиатура под чатом

Подпись пользователя

Нет

Инлайн-режим

Что это

Вызов бота из любого чата

Подпись пользователя

Нет

Кнопка клавиатуры чаще попадается в примерах: она проще выглядит в коде. Всё кажется рабочим ровно до момента, когда на сервере появляется проверка подписи и начинает отклонять каждый запрос. Дальше человек два часа ищет ошибку в алгоритме, которого в этом месте нет.

Вывод короткий: инлайн-кнопка или кнопка меню слева от поля ввода. Остальные способы годятся, если приложению вообще не нужно знать, кто пришёл.

Две сургучные печати на столе под лампой, над правой стоит ювелирная лупа
Подделка держится ровно до того момента, пока на неё не посмотрели через увеличительное стекло. Проверка подписи и есть эта лупа.

Почему подпись Telegram надо проверять на сервере

Telegram кладёт в страницу строку initData. Внутри лежит идентификатор пользователя, его имя, время открытия и подпись.

Соблазн понятный: взять оттуда user.id, отправить на сервер и работать. Проблема в том, что страница выполняется на устройстве пользователя, и всё, что в ней лежит, редактируется из консоли браузера. Подставить чужой идентификатор занимает одну строку и десять секунд. Дальше человек оформляет заказы от чужого имени, видит чужую историю и меняет чужие данные.

От этого и защищает подпись. Telegram считает её по вашему токену бота, который знаете только вы и Telegram. Ваш сервер пересчитывает подпись и сравнивает. Совпало, данные настоящие. Не совпало, запрос отклоняется.

auth.py
import hashlib
import hmac
from urllib.parse import parse_qsl

def check(init_data: str, bot_token: str) -> dict:
    pairs = dict(parse_qsl(init_data))
    received = pairs.pop("hash")

    # Пары сортируются по ключу и склеиваются через перевод строки.
    check_string = "\n".join(f"{k}={pairs[k]}" for k in sorted(pairs))

    # Внимание на порядок: ключ - строка "WebAppData", сообщение - токен.
    secret = hmac.new(b"WebAppData", bot_token.encode(), hashlib.sha256).digest()
    expected = hmac.new(secret, check_string.encode(), hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, received):
        raise ValueError("подпись не совпала")
    return pairs

Где ошибаются чаще всего

Промахиваются почти всегда в одних и тех же трёх местах.

Порядок аргументов в предпоследней строке. Ключ это литерал WebAppData, сообщение токен бота. Поменяете местами: подпись всё равно посчитается, самодельные тесты пройдут, а настоящий Telegram получит отказ. Ошибка неприятна именно тем, что выглядит как работающий код.

Проверка возраста. В данных есть поле auth_date со временем открытия. Без него строка, однажды подсмотренная, работает вечно. Официального срока годности у неё нет: документация предлагает дополнительно проверить это поле и не называет ни одного числа. Все «initData живёт 24 часа» из туториалов это выбор их авторов, а не требование Telegram. Предел выбираете вы; в шаблоне стоят сутки, и это компромисс с приложением, которое провисело открытым в фоне.

Кодирование значений. Подпись считается по раскодированным значениям, а в самой строке они закодированы. Перепутаете, и получите приложение, которое падает у пользователя с кириллицей в имени и прекрасно работает у вас. Я на это наступил при сборке шаблона: строка с непреобразованными символами не уходит даже в HTTP-заголовок, браузер отказывается её отправлять.

Правило, которое экономит деньги

Идентификатор проверяется подписью. Цена берётся из каталога на сервере. Количество ограничивается сверху и снизу. Ни одно из трёх нельзя доверить браузеру.

Что ещё нельзя брать из браузера

Подпись закрывает вопрос «кто это». Дальше начинается вопрос «что ему можно», и его браузер решать тоже не должен.

Витрина с корзиной. Логика та же, что с идентификатором: браузер и так знает цены, он их показывает, пусть он и считает итог, а сервер запишет готовое число. Но браузер находится в руках покупателя. Открыть консоль и отправить заказ с ценой в одну копейку занимает столько же времени, сколько подставить чужой идентификатор. Правильно так: фронт присылает идентификатор товара и количество, всё остальное сервер берёт из своего каталога. То же с количеством: отрицательное даёт отрицательную сумму, а миллион штук это заказ, который никто не выполнит, но который займёт место в базе.

Панель управления. Спрятать вкладку в интерфейсе недостаточно: запросы к серверу уходят напрямую, минуя кнопки. Проверка «этот человек владелец» должна стоять в каждом обработчике, а не в разметке. В шаблоне панель управления ботом устроена именно так, и посторонний, знающий адрес, видит вежливый отказ вместо чужой базы клиентов.

Правило одной строкой: всё, что пришло из браузера, это пожелание. Фактом его делает сервер.

Где всё это живёт и сколько стоит

Туннель хорош для отладки и не годится дальше: адрес меняется, канал зависит от вашего ноутбука. Для постоянной работы нужен сервер с доменом и сертификатом. Самая дешёвая рабочая конфигурация складывается из обычного VPS за несколько долларов, домена и бесплатного сертификата. Про то, где вообще держать фронтенд и когда свой сервер не нужен, есть разбор вариантов хостинга.

Отдельная база на старте не нужна: sqlite лежит файлом рядом с кодом, копируется обычным cp и спокойно тянет сотни заказов в день. Момент переезда узнаётся по симптомам: несколько процессов на запись, заказы чаще одного в секунду, отчёты, которые считаются минутами. Куда переезжать и по каким признакам выбирать, разобрано в материале про выбор базы данных.

Свежая деталь про домен. С 20 июля 2026 Telegram запрещает вызывать свои методы с домена, отличного от домена самого приложения. Раньше можно было подтянуть внутрь чужой виджет или увести человека на соседний сайт, и системные кнопки продолжали работать. Теперь вызовы оттуда просто перестают отвечать. Отключить защиту можно там же, у BotFather, но тогда ответственность за чужие ссылки внутри приложения переходит к вам.

Что делать с деньгами внутри приложения

Тут развилка, и зависит она от того, что вы продаёте.

Цифровые товары и услуги внутри Telegram проводятся только через встроенную валюту платформы, Stars. Формулировка в документации жёсткая: платежи за цифровые товары должны идти исключительно в Stars, и наличие собственного сайта с эквайрингом этого не отменяет. Дело в правилах магазинов приложений, через которые Telegram распространяется. Криптовалюту вместо Stars принимать тоже нельзя, это отдельным пунктом.

Физические товары наоборот: они проводятся обычными платёжными инструментами, через подключённого провайдера. Кофе, доставка, запись к мастеру, всё это оплачивается картой, и Telegram комиссию не берёт.

Практический вывод для витрины кофейни или магазина: смотрите в сторону обычной оплаты картой. Для доступа к платному контенту, подписки внутри приложения или игровой валюты в сторону Stars. И если бот принимает платежи, у него должны быть команда /terms, работающий /paysupport и внятно объявленный способ связи с поддержкой: команда /support или что-то столь же очевидное.

Что можно взять и попробовать прямо сейчас

Шаблон я собрал на нём же, статья на нём и проверялась. Это рабочий инструмент для переписки с клиентами, а демо на пять минут мы и сами насмотрелись.

Бот принимает сообщения, отвечает по правилам и копит базу: кто написал, когда, сколько сообщений, вся история диалога. Если правило не подошло и включена галочка, отвечает нейросеть с вашим промптом. Панель управления открывается внутри Telegram: клиенты, правила, промпт и рассылки с отложенным стартом.

Две вещи, которые превращают это из демонстрации в рабочий инструмент. Когда бот не смог ответить, владельцу сразу приходит сообщение: кто написал и что спросил. И ответить можно там же, из панели: открыл карточку клиента, прочитал переписку, написал ответ, он ушёл от имени бота.

Что в шаблоне уже сделано

База клиентов и переписка

Кто написал, когда пришёл, сколько сообщений. К контакту заметка, статус «не беспокоить» и вся история диалога.

Автоответы по правилам

Содержит, начинается с, точно совпадает. Первое подошедшее отвечает мгновенно и всегда одинаково.

Ответы нейросетью

Промпт задаётся в панели, историю переписки бот берёт из своей базы. Без ключа ветка выключена.

Рассылки с отложенным стартом

Очередь лежит в базе и переживает перезапуск. Заблокировавшие бота помечаются и больше не тревожатся.

Панель только для владельца

Вход по подписанному идентификатору. Проверка живёт в каждом обработчике, разметка тут ни при чём.

Ответ клиенту из панели

Прочитал переписку и ответил в том же экране. Сообщение уходит от имени бота и остаётся в истории.

82 теста

Подделка подписи, чужой в панели, порядок правил и модели, продолжение рассылки после падения.

Панель в работе: промпт для нейросети, правило автоответа на «сколько стоит» и рассылка, поставленная на будущую дату.

И вещь, ради которой шаблон стоит открывать первой: режим предпросмотра. Панель поднимается в обычном браузере с поддельным владельцем, без бота и без туннеля. Интерфейс можно править и обновлять страницу, а Telegram подключать потом, когда экран уже нравится.

Правила, промпт и рассылки настраиваются в самой панели, код для этого трогать не нужно. Менять придётся только то, что захотите добавить сверх.

Скачать шаблон

Бот с панелью управления на Python: aiogram 3, aiohttp, sqlite, правила, рассылки и режим предпросмотра. Бесплатно, нужен только вход.

Забрать шаблон

Частые вопросы

Да, обойтись без него нельзя. Приложение привязывается к боту, кнопка запуска живёт в боте, и токен бота участвует в проверке подписи пользователя. Сам бот при этом может не уметь ничего, кроме команды /start с кнопкой.
Только если приложению не нужно ничего сохранять и не нужно знать, кто пришёл. Как только появляются заказы, корзина или личные данные, нужен сервер: подпись Telegram проверяется именно там, и цену считать в браузере нельзя.
Да. Тип, которым задаётся приложение в Bot API, прямо описан как адрес по https. Исключение одно: тестовая среда Telegram, где допустимы ссылки без сертификата. Для отладки на своей машине проще поднять туннель, чем возиться с тестовой средой.
Выбора по фронтенду нет: он выполняется в браузере Telegram, значит JavaScript. Сервер пишите на чём привыкли. Python с aiogram удобен тем, что бот и веб-часть живут в одном процессе и в одном языке.
Панель на готовом шаблоне обойдётся в вечер работы и несколько долларов в месяц за сервер. Каталог с оплатой, личный кабинет и интеграция с учётной системой считаются уже десятками часов: точная оценка зависит от того, сколько состояний у интерфейса и куда уходят данные после заказа. Часовая ставка и типовые вилки по объёму лежат на странице услуги «Telegram mini apps».
Нет, поисковые роботы внутрь мессенджера не заходят. Mini app это канал для тех, кто уже в Telegram, а за поисковым трафиком нужен обычный сайт.
Бот это переписка: он задаёт вопросы по очереди и ждёт ответа. Mini app это экран приложения: всё видно сразу, и человек сам решает, в каком порядке действовать. Если выбирать не из чего, бот справится и обойдётся дешевле.

Новые посты на почту

Без спама. Отписка в один клик в любом письме.