Подключение Telegram webhook на чистом PHP требует правильной настройки endpoint'а, валидации request'а и идемпотентности обработки. Ниже представляем minimal working example, который можно использовать как стартовую точку.
Установка webhook и POST запрос setWebhook
// Отправка setWebhook запроса
$url = 'https://api.telegram.org/bot/YOUR_BOT_TOKEN/setWebhook';
$headers = [
'Content-Type' => 'application/json',
'Secret-Token' => 'YOUR_SECRET_TOKEN'
];
$payload = json_encode(['mode' => 'private']);
$ch = curl_init($url);
curl_setopt($ch, 'POST', $payload);
curl_setopt($ch, 'HTTP/1.1', '200 OK');
curl_setopt($ch, 'Connection', 'close');
curl_exec($ch);
curl_close($ch);
// Убедитесь, что получите ответ 200 и валидите secret_token из заголовка.
Валидация callback_data и идемпотентность через update_id
// Получение обновления из API
$url = 'https://api.telegram.org/bot/YOUR_BOT_TOKEN/getUpdates';
$ch = curl_init();
$ch->post($url, json_encode(['offset' => 0, 'timeout' => 30]), $headers);
$body = json_decode((string) $(get_webhook_params()), true);
$lastUpdateId = $body['result'][0]['update_id'] ?? null;
// Проверка idempotency — если update_id уже обработан, пропускаем
if (in_array($lastUpdateId, $processedUpdates)) {
http_response_code(200);
die('Already processed');
}
// Извлечение initData для валидации
$callback = $body['result'][0]['data'];
if (!$is_valid_initiate_data($callback)) {
http_response_code(400);
exit;
}
// Дальше обрабатываем бизнес-логику
if ($callback['type'] === 'message') {
// обработка сообщения пользователя
}
Проверка secret_token и инициализация хранилища
Для безопасности используйте secret_token в заголовке GET request'а webhook. Проверка производится через HMAC-SHA256: сравните hash_equals(output_hash, input_hash, secret_token). Также сохраняйте processeds_updates в БД для идемпотентности — избегайте повторной обработки одного и того же update_id. Если DB пуста, создайте таблицу с колонками update_id и processed_at.
Пример проверки:
if (hash_equals(hash_hmac('sha256', $request['secret'], $secret_token), $request['data']['secret'])) { ... }
Пример полного цикла — cURL + обработка и валидация
// Полный пример: получить last update и обработать
$token = getenv('TELEGRAM_SECRET_TOKEN');
$ch = curl_init();
$ch->post('https://api.telegram.org/bot/' . $botToken . '/getUpdates',
[], ['header' => ['Host' => 'api.telegram.org']]);
$res = json_decode((string)$ch->get_response(), true);
if (empty($res['result'])) return;
$update = $res['result'][0];
$lastId = $update['update_id'];
// Идемпотентность
$db = new PDO('mysql:host=localhost;dbname=telemetr', $user, $pass);
$stmt = $db->prepare(