Интеграция веб-приложений непосредственно в интерфейс мессенджера Telegram Mini Apps (TMA) открывает огромные возможности для бизнеса. Однако при разработке на современном фронтенд-стеке, например Vue 3, разработчики часто сталкиваются с неочевидными проблемами: прыгающий viewport при открытии клавиатуры, некорректная обработка цветовых схем Telegram и уязвимости при валидации данных пользователя на бэкенде.
В этой статье мы разберем, как правильно подключить Telegram WebApp SDK в приложение на Vue 3 (с Composition API), как стабилизировать область просмотра (viewport), когда и как использовать метод sendData, и как реализовать надежную криптографическую проверку полученных данных на стороне PHP-сервера.
Подключение WebApp SDK во Vue 3 и управление темой оформления
Для интеграции Mini App с нативным клиентом Telegram необходимо подключить стандартный скрипт. Самый надежный способ — добавить CDN-скрипт в файл index.html вашего Vue-проекта. Это гарантирует, что глобальный объект window.Telegram.WebApp будет доступен сразу при инициализации приложения.
<!-- index.html -->
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<title>My Telegram Mini App</title>
</head>
<body>
<div id="app"></div>
</body>
</html>Внутри Vue 3 мы оборачиваем работу с SDK в компонент или composable-функцию. Важно правильно обработать цветовую схему Telegram, которая передается через объект themeParams. Это позволит интерфейсу приложения бесшовно мимикрировать под темную или светлую тему мессенджера пользователя.
<!-- App.vue -->
<template>
<div class="tma-wrapper" :style="themeVariables">
<header class="tma-header">
<p>Привет, {{ username }}!</p>
</header>
<main class="tma-content">
<slot></slot>
<button @click="sendPayload" class="tma-btn">Отправить заказ</button>
</main>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue';
const username = ref('Гость');
const themeParams = ref({});
onMounted(() => {
const tg = window.Telegram?.WebApp;
if (tg) {
tg.ready();
tg.expand(); // Разворачиваем приложение на максимум
username.value = tg.initDataUnsafe?.user?.first_name || 'Пользователь';
themeParams.value = tg.themeParams || {};
// Следим за изменениями темы на лету
tg.onEvent('themeChanged', () => {
themeParams.value = tg.themeParams;
});
}
});
const themeVariables = computed(() => {
const colors = themeParams.value;
return {
'--bg-color': colors.bg_color || '#ffffff',
'--text-color': colors.text_color || '#000000',
'--hint-color': colors.hint_color || '#999999',
'--link-color': colors.link_color || '#2481cc',
'--button-color': colors.button_color || '#2481cc',
'--button-text-color': colors.button_text_color || '#ffffff'
};
});
</script>Управление viewport и предотвращение сворачивания
Одной из главных проблем при разработке TMA является поведение области просмотра (viewport) на мобильных устройствах, особенно при фокусе на текстовых полях ввода. Когда появляется экранная клавиатура, размер окна уменьшается, что может приводить к нежелательному смещению элементов или попытке свернуть Mini App жестом swipe-down.
Чтобы зафиксировать высоту и предотвратить случайное закрытие приложения пользователем, необходимо использовать методы управления viewport:
Telegram.WebApp.expand()— принудительно разворачивает окно приложения на максимально доступную высоту.Telegram.WebApp.enableClosingConfirmation()— включает подтверждение закрытия. Если пользователь случайно потянет экран вниз, Telegram спросит, действительно ли он хочет закрыть приложение.- Отслеживание события
viewportChangedс параметромisStateStableпозволяет вовремя адаптировать интерфейс (например, скрывать фиксированные футеры при открытии клавиатуры).
Рекомендуется вызывать tg.enableClosingConfirmation() сразу при монтировании главного компонента приложения.
Отправка данных боту: sendData против HTTP API
Для отправки данных из Mini App обратно в чат с ботом существует метод Telegram.WebApp.sendData(data). Однако у него есть критическое ограничение, о котором часто забывают начинающие разработчики:
Важное ограничение: МетодsendDataработает только в том случае, если Mini App был запущен через обычную кнопку клавиатуры бота (клавиша типаKeyboardButtonс параметромweb_app). Если приложение открыто через Inline-кнопку в сообщении, через меню вложений (Attachment Menu) или по прямой ссылке, вызовsendDataне сработает.
Если ваше приложение запущено через Keyboard Button, вызов tg.sendData(JSON.stringify(payload)) мгновенно закроет Mini App и отправит текстовое сообщение от лица пользователя в чат с ботом. Бот получит это событие в объекте обновления Update в поле message.web_app_data.
В реальных проектах (интернет-магазины, CRM, запись на услуги) гораздо надежнее использовать стандартные HTTP-запросы (через Axios или Fetch) на ваш собственный бэкенд. Для авторизации запроса в заголовках передается строка window.Telegram.WebApp.initData. Бэкенд проверяет ее валидность и самостоятельно отправляет сообщения пользователю через Bot API.
Безопасная валидация initData на PHP
Никогда не доверяйте данным, полученным напрямую из фронтенда (включая initDataUnsafe). Злоумышленник может легко перехватить трафик или подменить значения полей в браузере. Для верификации параметров необходимо использовать строку initData и проверить подпись (хэш) с помощью секретного ключа, созданного на основе токена вашего бота.
Ниже представлен готовый, безопасный класс на чистом PHP для валидации initData с использованием криптографического алгоритма HMAC-SHA-256 и защитой от атак по времени (timing-safe comparison).
<?php
declare(strict_types=1);
class TelegramInitDataValidator
{
private string $botToken;
private int $expireSeconds;
public function __construct(string $botToken, int $expireSeconds = 86400)
{
$this.botToken = $botToken;
$this.expireSeconds = $expireSeconds;
}
/**
* Валидирует строку initData и возвращает массив параметров в случае успеха.
*
* @param string $initData Строка запроса из window.Telegram.WebApp.initData
* @return array|null Данные авторизации или null в случае невалидности
*/
public function validate(string $initData): ?array
{
parse_str($initData, $params);
if (!isset($params['hash']) || !isset($params['auth_date'])) {
return null;
}
// 1. Проверяем актуальность данных по времени (защита от повторного использования старых сессий)
if (time() - (int)$params['auth_date'] > $this.expireSeconds) {
return null;
}
$receivedHash = $params['hash'];
unset($params['hash']);
// 2. Сортируем параметры в алфавитном порядке
ksort($params);
// 3. Формируем строку проверки
$dataCheckArr = [];
foreach ($params as $key => $value) {
$dataCheckArr[] = "{$key}={$value}";
}
$dataCheckString = implode("\n", $dataCheckArr);
// 4. Генерируем секретный ключ на основе токена бота
$secretKey = hash_hmac('sha256', $this.botToken, 'WebAppData', true);
// 5. Вычисляем эталонный хэш
$calculatedHash = hash_hmac('sha256', $dataCheckString, $secretKey);
// 6. Сравниваем хэши безопасным методом hash_equals
if (!hash_equals($receivedHash, $calculatedHash)) {
return null;
}
// Декодируем вложенный JSON пользователя для удобства
if (isset($params['user'])) {
$params['user'] = json_decode($params['user'], true);
}
return $params;
}
}Пример использования класса в вашем PHP-контроллере:
<?php
require_once 'TelegramInitDataValidator.php';
$botToken = getenv('TELEGRAM_BOT_TOKEN') ?: 'YOUR_BOT_TOKEN';
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (preg_match('/^Bearer\s+(.+)$/i', $authHeader, $matches)) {
$initData = $matches[1];
} else {
http_response_code(401);
echo json_encode(['error' => 'Missing authorization header']);
exit;
}
$validator = new TelegramInitDataValidator($botToken);
$userData = $validator->validate($initData);
if ($userData === null) {
http_response_code(403);
echo json_encode(['error' => 'Invalid or expired Telegram initData']);
exit;
}
// Данные успешно проверены, можно доверять $userData['user']['id']
header('Content-Type: application/json');
echo json_encode([
'status' => 'success',
'user_id' => $userData['user']['id'] ?? null,
'username' => $userData['user']['username'] ?? null
]);Типичные ошибки при интеграции Vue и Telegram WebApp
При разработке Mini Apps разработчики чаще всего допускают следующие архитектурные ошибки:
- Использование initDataUnsafe для бизнес-логики. Запомните: этот объект предназначен исключительно для отрисовки интерфейса во фронтенде (например, показать аватарку или имя пользователя). Любые операции с балансом, заказами или правами доступа должны проходить проверку
initDataна бэкенде. - Игнорирование параметра auth_date. Без проверки времени генерации данных злоумышленник может перехватить валидную строку
initDataодин раз и использовать ее бесконечно для выполнения несанкционированных действий от лица пользователя. Ограничивайте время жизни сессии (рекомендуется от 1 до 24 часов). - Неправильное выравнивание под клавиатуру. Если вы используете во Vue жестко зафиксированные элементы (
position: fixed), обязательно тестируйте их поведение на реальных мобильных устройствах iOS и Android при открытой клавиатуре.
Если вам требуется профессиональная разработка сложных интеграций и Telegram Mini Apps, команда BotCreator поможет реализовать проект любой сложности на сайте botservice.biz.