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

Три основных способа:

  1. Inline-кнопка — в сообщении бота (reply_markup.inline_keyboard).
  2. Reply-кнопка — в клавиатуре под полем ввода (KeyboardButton.web_app).
  3. Прямая ссылка — 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

  1. Пользователь нажимает кнопку → Telegram открывает WebView.
  2. Приложение загружается, вызывает Telegram.WebApp.ready() — сплеш-скрин скрывается.
  3. Приложение читает initData, отправляет на свой бэкенд для верификации (см. следующую страницу).
  4. Пользователь взаимодействует с UI.
  5. Приложение вызывает Telegram.WebApp.sendData(JSON.stringify({...})) и close().
  6. Бот получает 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 на бэкенде, иначе кто угодно может подделать запрос.

Дальше: Проверка initData