Розробка Telegram Mini Apps (TWA) на сучасному стеку Vue 3 вимагає глибокого розуміння специфіки взаємодії клієнтського WebView з клієнтом Telegram та серверним бекендом. На відміну від стандартних SPA, міні-додатки функціонують усередині ізольованого контейнера, де необхідно коректно обробляти зміни viewport, підлаштовуватися під системну колірну схему користувача та захищати дані, що передаються, від підробки.
У цій статті ми розберемо побудову клієнтської частини на Vue 3 із застосуванням Composition API, управління стійкістю інтерфейсу, передачу даних боту та сувору валідацію автентичності сесії на стороні PHP.
1. Ініціалізація Telegram WebApp SDK та Composition API у Vue 3
Підключити Telegram WebApp JS SDK можна через скрипт https://telegram.org/js/telegram-web-app.js в index.html або встановивши npm-пакет. Для забезпечення реактивності у Vue 3 доцільно винести роботу з window.Telegram.WebApp в окремий Composable-модуль useTelegram.
Створимо реактивний хук, який ініціалізує WebApp, відстежує стан розгортання екрана (isExpanded) та надає інтерфейс для виклику нативних методів.
import { ref, onMounted, onUnmounted } from 'vue';
export function useTelegram() {
const tg = window.Telegram?.WebApp;
const user = ref(tg?.initDataUnsafe?.user || null);
const isExpanded = ref(tg?.isExpanded || false);
const themeParams = ref(tg?.themeParams || {});
const handleViewportChanged = () => {
isExpanded.value = tg?.isExpanded || false;
};
onMounted(() => {
if (!tg) {
console.warn('Telegram WebApp SDK не найден');
return;
}
tg.ready();
tg.expand();
tg.onEvent('viewportChanged', handleViewportChanged);
});
onUnmounted(() => {
if (tg) {
tg.offEvent('viewportChanged', handleViewportChanged);
}
});
const sendDataToBot = (data) => {
if (!tg) return;
tg.sendData(JSON.stringify(data));
};
return {
tg,
user,
isExpanded,
themeParams,
sendDataToBot
};
}2. Управління Viewport та захист від стрибаючого інтерфейсу
Однією з головних проблем при розробці Mini Apps на мобільних пристроях є нестабільність висоти області перегляду (viewport). Виклик клавіатури або свайп користувача може призводити до згортання додатка або появи порожніх областей.
Щоб запобігти випадковому закриттю додатка свайпом вниз і зафіксувати інтерфейс, необхідно при монтуванні головного компонента викликати метод expand(), а також застосувати CSS-змінні для динамічної висоти екрана.
import { onMounted } from 'vue';
import { useTelegram } from './useTelegram';
export default {
name: 'App',
setup() {
const { tg, themeParams } = useTelegram();
const applyThemeVariables = () => {
if (!themeParams.value) return;
const root = document.documentElement;
const tp = themeParams.value;
if (tp.bg_color) root.style.setProperty('--tg-theme-bg-color', tp.bg_color);
if (tp.text_color) root.style.setProperty('--tg-theme-text-color', tp.text_color);
if (tp.button_color) root.style.setProperty('--tg-theme-button-color', tp.button_color);
if (tp.button_text_color) root.style.setProperty('--tg-theme-button-text-color', tp.button_text_color);
if (tp.secondary_bg_color) root.style.setProperty('--tg-theme-secondary-bg-color', tp.secondary_bg_color);
};
onMounted(() => {
if (tg) {
tg.enableClosingConfirmation();
applyThemeVariables();
}
});
return {};
}
};3. Передача даних у чат з ботом через sendData
Метод Telegram.WebApp.sendData() дозволяє миттєво відправити стрічкове корисне навантаження в діалог з ботом і автоматично закрити Mini App. Важливе технічне обмеження: метод sendData() працює тільки якщо вебдодаток було відкрито через звичайну кнопку клавіатури (KeyboardButton типу web_app). Якщо додаток відкрито з Inline-кнопки або системного меню, sendData() викликає помилку SDK.
Приклад відправки структури замовлення з компонента Vue 3:
import { ref } from 'vue';
import { useTelegram } from './useTelegram';
export default {
name: 'OrderForm',
setup() {
const { sendDataToBot } = useTelegram();
const serviceId = ref(102);
const comment = ref('');
const submitForm = () => {
const payload = {
action: 'create_order',
lead_id: Date.now().toString(36),
service_id: serviceId.value,
comment: comment.value
};
sendDataToBot(payload);
};
return { serviceId, comment, submitForm };
}
};4. Валідація initData на бекенді (PHP HMAC-SHA256)
Об'єкт initDataUnsafe на клієнті може бути легко підроблений користувачем через DevTools. Для безпечної автентифікації бекенд повинен отримувати сирий рядок initData (переданий через HTTP-заголовок Authorization або в тілі POST-запиту) і валідувати його з використанням токена бота.
Нижче наведено робочий PHP-скрипт перевірки криптографічного підпису initData із застосуванням бінарного HMAC-SHA256 та функції hash_equals для захисту від атак за часом (timing attacks):
<?php
function validateTelegramInitData(string $initData, string $botToken): ?array
{
parse_str($initData, $data);
if (!isset($data['hash'])) {
return null;
}
$checkHash = $data['hash'];
unset($data['hash']);
$dataCheckArray = [];
foreach ($data as $key => $value) {
$dataCheckArray[] = $key . '=' . $value;
}
sort($dataCheckArray);
$dataCheckString = implode("
", $dataCheckArray);
// 1. Создаем секретный ключ из токена бота
$secretKey = hash_hmac('sha256', $botToken, 'WebAppData', true);
// 2. Вычисляем контрольный хэш от строки данных
$calculatedHash = hash_hmac('sha256', $dataCheckString, $secretKey);
// 3. Timing-safe сравнение хэшей
if (!hash_equals($calculatedHash, $checkHash)) {
return null;
}
// 4. Проверка устаревания данных (например, не старше 24 часов)
if (isset($data['auth_date']) && (time() - (int)$data['auth_date'] > 86400)) {
return null;
}
return $data;
}
// Пример обработки входящего запроса
header('Content-Type: application/json');
$headers = getallheaders();
$initData = $headers['X-Telegram-Init-Data'] ?? $_POST['init_data'] ?? '';
$botToken = getenv('TELEGRAM_BOT_TOKEN') ?: '';
if (empty($initData) || empty($botToken)) {
http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'Missing required parameters']);
exit;
}
$validated = validateTelegramInitData($initData, $botToken);
if ($validated === null) {
http_response_code(403);
echo json_encode(['ok' => false, 'error' => 'Invalid initData signature or data expired']);
exit;
}
$userData = json_decode($validated['user'] ?? '{}', true);
echo json_encode([
'ok' => true,
'user_id' => $userData['id'] ?? null,
'username' => $userData['username'] ?? null
]);5. Відправка відповідей через Bot API на PHP при роботі через Inline Mini Apps
Якщо Mini App запущено з Inline-кнопки, відправка sendData() не спрацює. У цьому випадку фронтенд відправляє AJAX-запит на ваш PHP-бекенд, а бекенд здійснює прямий виклик методу Telegram Bot API sendMessage через cURL.
<?php
function sendTelegramMessage(int $chatId, string $text): bool
{
$botToken = getenv('TELEGRAM_BOT_TOKEN');
if (!$botToken) {
error_log('TELEGRAM_BOT_TOKEN is not defined');
return false;
}
$url = "https://api.telegram.org/bot{$botToken}/sendMessage";
$payload = [
'chat_id' => $chatId,
'text' => $text,
'parse_mode' => 'HTML'
];
$ch = curl_init($url);
curl_setopt_array($ch, [
'CURLOPT_POST' => true,
'CURLOPT_RETURNTRANSFER' => true,
'CURLOPT_HTTPHEADER' => ['Content-Type: application/json'],
'CURLOPT_POSTFIELDS' => json_encode($payload),
'CURLOPT_TIMEOUT' => 5,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError || $httpCode !== 200) {
error_log("Telegram API Error: HTTP {$httpCode}, cURL: {$curlError}");
return false;
}
$result = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE || !($result['ok'] ?? false)) {
error_log("Telegram API non-ok response: " . $response);
return false;
}
return true;
}6. Типові помилки та чекліст перед деплоєм
- Зав'язка на sendData всюди: Пам'ятайте, що
sendDataне працює для Mini Apps, відкритих за прямим посиланням (t.me/bot/app) або з InlineKeyboardMarkup. Завжди закладайте fallback на свій REST API. - Відсутність валідації auth_date: Обов'язково перевіряйте поле
auth_dateзinitDataна сервері, інакше старий рядокinitDataможе бути повторно використаний третіми особами (Replay-атака). - Пряме використання window.Telegram: При серверному рендерингу (SSR / Nuxt) або при локальній розробці в браузері об'єкт
window.Telegram.WebAppможе бути недоступним. Завжди перевіряйте його існування перед викликом методів.
Розробляти відмовостійкі вебхуки та архітектуру Mini Apps допомагає команда BotCreator.