Telegram Mini App with Vue 3: WebApp JS Integration and Idempotent Data Submission

Telegram Mini App on Vue 3 opens up opportunities to create a competitive interface right inside Telegram, bypassing the classic chat. Unlike a regular bot, a Mini App works as a web application with its own viewport, allowing you to use all the functionality of JavaScript and even perform local operations.

Connecting WebApp JS via Official SDK

To start integration, you need to connect Telegram WebApp SDK. In Vue 3, this is implemented via a reactive component that initializes the WebApp at page load or after receiving callback_data.

// Vue 3 компонент для инициализации Telegram Mini App
import { reactive, onMounted } from 'vue';
import * as WebAppSDK from 'telemetry-web-apps-js';

export default {
setup() {
const containerEl = reactive({
initialState: {},
isInitialized: false
});

async function initializeWebApp(botToken) {
const client = WebAppSDK();
try {
await client.initialize({
url: `https://api.telegram.org/bot${encodeURIComponent(botToken)}`,
version: '8.1',
config: {
actionType: 'set_viewport',
viewport: {
width: 360,
height: 640
}
}
});
console.log('Mini App успешно инициализирован');
containerEl.isInitialized = true;
} catch (error) {
console.error('Failed to initialize WebApp:', error);
}
}

onMounted(() => {
// Инициализируем при загрузке компонента
initializeWebApp(process.env.BOT_TOKEN);
});

return { containerEl, isInitialized };
}
};

Deep Linking with t.me/Bot?start=

When processing an incoming request from Telegram, it is important to properly parse t.me/Bot?start=payload. The payload must comply with a maximum limit of 64 bytes. In our case, a unique lead_id generated on the bot side is used to avoid conflicts during parallel execution of multiple application instances.

Secure Data Transfer via sendData

After initializing the WebApp, the bot can send data to users via a sendData operation. This mechanism involves transferring a JSON object from the bot to the application. It is critically important to validate the received data on the server before saving.

// PHP валидация initData на сервере (PHP/Yii2)
<?php
function validateTelegramInitData($initDataString, $botToken) {
// Декодируем base64-строку из initData
$decoded = base64_decode($initDataString, true);

// Получаем хэш подписи (HMAC-SHA256)
$expectedSignature = hash_hmac('sha256', $decoded, $botToken);

// Вычисляем ожидаемую подпись от бота
$computedSignature = hash('sha256', $botToken . $decoded);

// Проверяем корректность сигнатуры
if ($computedSignature !== $expectedSignature) {
return ['error' => 'Invalid signature'];
}

// Проверяем наличие auth_date и проверку свежести (30 минут)
if (!isset($decoded['auth_date'])) {
return ['error' => 'Missing auth_date'];
}

$date = pdtoTimestamp($decoded['auth_date']);
if (time() - $date > 300) { // 5 минут window
return ['error' => 'Session expired'];
}

return ['ok' => true];
}

// Пример вызова перед отправкой данных
$result = validateTelegramInitData($_REQUEST['init_data'], $BOT_TOKEN);
if (!in_array($result['ok'], [true])) {
http_response_code(400);
exit(json_encode(['error' => $result['error']], JSON_THROW_ON_ERROR));
}

// После успешной валидации отправляем данные в mini app
$transaction = ['type' => 'create', 'data' => $userRegistered];
$response = file_get_contents($WP_INSTALLED_PLUGIN_DIR . '/telegram_send.php')
->json_decode(file_get_contents("https://api.telegram.org/bot${BOT_TOKEN}/sendMessage", True));

echo $response->get('messageId');

// PHP конфиг для отправки (Yii2)
<?php
/**
* Консольная команда для обслуживания Telegram-бот а
*/
class ManagerController extends Controller {
public function index() {
// Set webhook
yii
base::setWebhook(
$url => "https://yourdomain.com/bot/webhook",
secret_token => "_TELEGRAM_SECRET_TOKEN"
);

// Получить ID обработанного сообщения
$updateId = $this->getLastUpdateId();

// Удалить вебхук, если он больше не нужен
if ($updateId) {
yii
base::deleteWebhook($updateId);
}

// Получить текущего владельца обновления
$leadId = $this->getLeadFromUpdateId($updateId);

echo sprintf(
"Server status\n- Last update ID: %s\n- Lead ID: %s\n",
$updateId ?? 'N/A',
$leadId ?: 'N/A'
);
}

protected function getLastUpdateId() {
// В реальном проекте используем session или DB
return $_SESSION['last_update_id'] ?? null;
}

protected function getLeadFromUpdateId($updateId) {
// Логика извлечения lead_id из update
return 'lead_' . substr($updateId, 20, 5);
}
}
<?php

Idempotency and Spoofing Protection

Every request from a Telegram Mini App is signed using HMAC with the bot token. This guarantees that data cannot be altered by an attacker between sending and processing. Additionally, idempotency is achieved by saving last_update_id in the database — when the same payload is resent, the system simply overwrites the result instead of duplicating it.

Best Practice Recommendations

  • Always decode initData using base64_decode with the true flag for security.
  • Store the secret_token (webhook secret) in a secure environment variable, never hardcode it in the code.
  • Validate auth_date — reject data associated with old sessions (>5 minutes).
  • Use cURL for HTTP requests to the Telegram API with checks for the HTTP code and json_last_error.

By following the steps described, you will be able to build a stable Telegram Mini App on Vue 3 with reliable data sending and protection against unauthorized actions.

New articles on Telegram

We explain what to automate in your business and how it works in practice. No spam.