Mini Apps
Что такое Mini App
Что такое Telegram Mini App, как открыть его через inline- и reply-кнопки, как бот получает данные через web_app_data, и примеры на PHP с telegramApi().
Mini App — это веб-приложение, которое запускается внутри Telegram в нативном WebView. Пользователь не покидает мессенджер: интерфейс открывается поверх чата, имеет доступ к теме (светлая/тёмная), может запрашивать данные пользователя и отправлять результаты обратно боту.
Как это работает
Бот отправляет сообщение с InlineKeyboardButton, у которого задано поле web_app с URL вашего приложения. При нажатии Telegram открывает WebView, загружает страницу и передаёт стартовые данные через window.Telegram.WebApp.initData.
Внутри Mini App доступен JavaScript SDK (@twa-dev/sdk или CDN-скрипт), который даёт методы: ready(), expand(), close(), sendData(), работу с MainButton, BackButton, HapticFeedback и т.д.
Отличия от обычного сайта
- Запускается только из Telegram — нет прямого доступа по URL в браузере.
- Получает
initData— подписанную строку сuser,chat,query_id,auth_date,hash. Это единственный способ идентифицировать пользователя на бэкенде. - Имеет доступ к теме Telegram через CSS-переменные (
--tg-theme-bg-color,--tg-theme-text-colorи др.). - Может вернуть данные боту через
sendData()— бот получитweb_app_dataвMessage.
Открытие Mini App
Три основных способа:
- Inline-кнопка — в сообщении бота (
reply_markup.inline_keyboard). - Reply-кнопка — в клавиатуре под полем ввода (
KeyboardButton.web_app). - Прямая ссылка —
https://t.me/<bot_username>/<app_name>(настраивается в BotFather → Menu Button).
Пример: отправка кнопки через telegramApi()
В главе Первый запрос: getMe мы сделали функцию telegramApi() — здесь используем её. Она уже умеет кодировать JSON, проверять HTTP-код и поле ok.
<?php
$token = getenv('TELEGRAM_BOT_TOKEN');
if (!$token) {
throw new RuntimeException('TELEGRAM_BOT_TOKEN not set');
}
function telegramApi(string $method, array $params = []): array
{
$ch = curl_init("https://api.telegram.org/bot{$token}/{$method}");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($params),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$res = curl_exec($ch);
if ($res === false) {
throw new RuntimeException('cURL error: ' . curl_error($ch));
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new RuntimeException("HTTP {$httpCode}: {$res}");
}
$data = json_decode($res, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new RuntimeException('Invalid JSON: ' . json_last_error_msg());
}
if (!($data['ok'] ?? false)) {
throw new RuntimeException("API error: " . ($data['description'] ?? 'unknown'));
}
return $data['result'];
}
$chatId = 123456789; // замените на реальный chat_id
$url = 'https://example.com/miniapp';
$result = telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => 'Нажмите кнопку ниже, чтобы открыть Mini App:',
'reply_markup' => [
'inline_keyboard' => [[
['text' => 'Открыть Mini App', 'web_app' => ['url' => $url]]
]]
]
]);
// Посмотрим, что вернул API
print_r($result);
Пример ответа API (sendMessage)
{
"ok": true,
"result": {
"message_id": 42,
"from": {
"id": 987654321,
"is_bot": true,
"first_name": "MyBot",
"username": "mybot"
},
"chat": {
"id": 123456789,
"first_name": "Ivan",
"type": "private"
},
"date": 1700000000,
"text": "Нажмите кнопку ниже, чтобы открыть Mini App:",
"reply_markup": {
"inline_keyboard": [[
{
"text": "Открыть Mini App",
"web_app": {"url": "https://example.com/miniapp"}
}
]]
}
}
}
Поле result — это объект Message. Сохраните message_id, если планируете редактировать или удалять это сообщение позже (см. главу Редактирование и удаление). В терминале print_r($result) покажет структуру массива — удобно для отладки.
Пример: Reply-кнопка с web_app
Иногда удобнее прикрепить кнопку к клавиатуре под полем ввода — она не исчезает после нажатия.
<?php
$result = telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => 'Откройте приложение из меню:',
'reply_markup' => [
'keyboard' => [[
['text' => '🛒 Каталог', 'web_app' => ['url' => 'https://example.com/shop']]
]],
'resize_keyboard' => true,
'one_time_keyboard' => false
]
]);
var_dump($result);
var_dump покажет типы и длины строк — полезно, когда нужно убедиться, что message_id пришёл как int, а не string.
Жизненный цикл Mini App
- Пользователь нажимает кнопку → Telegram открывает WebView.
- Приложение загружается, вызывает
Telegram.WebApp.ready()— сплеш-скрин скрывается. - Приложение читает
initData, отправляет на свой бэкенд для верификации (см. следующую страницу). - Пользователь взаимодействует с UI.
- Приложение вызывает
Telegram.WebApp.sendData(JSON.stringify({...}))иclose(). - Бот получает
Messageс полемweb_app_data— обрабатывает результат.
Как бот получает данные от Mini App
Когда пользователь нажимает кнопку «Готово» в приложении и оно вызывает sendData(), Telegram доставляет боту обычное сообщение с полем web_app_data:
{
"update_id": 123456789,
"message": {
"message_id": 43,
"from": {"id": 123456789, "first_name": "Ivan"},
"chat": {"id": 123456789, "type": "private"},
"date": 1700000005,
"web_app_data": {
"button_text": "Открыть Mini App",
"data": "{\"action\":\"checkout\",\"items\":[1,2,3]}"
}
}
}
Поле web_app_data.data — это строка, которую передал sendData(). Обычно это JSON. В PHP-хендлере (см. главу getUpdates и update_id) разбирайте так:
<?php
// внутри processUpdate($update) из главы Long polling
if (isset($update['message']['web_app_data'])) {
$raw = $update['message']['web_app_data']['data'];
$payload = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
// невалидный JSON от приложения
telegramApi('sendMessage', [
'chat_id' => $update['message']['chat']['id'],
'text' => 'Ошибка: неверные данные от приложения'
]);
return;
}
// $payload теперь ассоциативный массив: ['action' => 'checkout', 'items' => [1,2,3]]
// ... ваша бизнес-логика ...
telegramApi('sendMessage', [
'chat_id' => $update['message']['chat']['id'],
'text' => 'Заказ принят: ' . json_encode($payload, JSON_UNESCAPED_UNICODE)
]);
}
Настройка Menu Button (кнопка в меню чата)
Чтобы Mini App открывалась по клику на имя бота в шапке чата, настройте Menu Button через BotFather или метод setChatMenuButton:
<?php
telegramApi('setChatMenuButton', [
'chat_id' => $chatId, // можно не указывать — тогда для всех чатов
'menu_button' => [
'type' => 'web_app',
'text' => 'Открыть приложение',
'web_app' => ['url' => 'https://example.com/miniapp']
]
]);
Ответ API:
{
"ok": true,
"result": true
}
Поле result здесь просто true — значит, настройка применена. Проверить текущую кнопку можно методом getChatMenuButton.
Mini App подходит для каталогов, форм, оплаты, игр, дашбордов — всего, где нужен богатый UI, которого не хватает нативных клавиатур. Главное — не забыть проверить initData на бэкенде, иначе кто угодно может подделать запрос.