Webhook для Telegram-бота на Yii2: actionWebhook,

Webhook — это основной способ получать обновления Telegram-бота в продакшене: быстрее long polling, не держит постоянное соединение и нормально живёт за балансировщиком. В Yii2 приём апдейтов делается в одном action, но именно в нём чаще всего ломаются три вещи: CSRF-валидация, повторная обработка одного и того же update и таймауты на тяжёлой логике. Разберём рабочую связку.

1. Регистрация webhook и переменные окружения

Сначала зафиксируем токен и секрет в params.php или в ENV. Никаких литералов в коде:

Секрет генерируйте один раз и храните рядом с токеном. Установка webhook:

allowed_updates ограничивает поток: меньше типов — меньше шума. После смены URL или секрета setWebhook вызывается заново, иначе Telegram продолжит слать апдейты на старый адрес.

2. Роутинг и отключение CSRF для bot-роута

Webhook-эндпоинт должен быть POST и не должен проходить через стандартный CSRF-фильтр Yii2, иначе Yii просто не пустит запрос, потому что у Telegram нет CSRF-токена. Правильный путь — отключить CSRF точечно для конкретного action, а не глобально:

Глобально enableCsrfValidation = false ставить не нужно — это выключит защиту форм всего сайта.

3. Проверка X-Telegram-Bot-Api-Secret-Token

Telegram присылает заголовок X-Telegram-Bot-Api-Secret-Token с каждым апдейтом, если вы задали secret_token в setWebhook. Сравнивайте строго и константно по времени, иначе кто угодно сможет дёргать ваш URL:

hash_equals — обязательно: обычный === теоретически уязвим к атакам по времени на коротких секретах.

4. Идемпотентность по update_id

Telegram обещает, что update_id уникален и монотонно растёт, и при ретрае доставит один и тот же update_id. Без защиты от дубля вы будете дважды списывать деньги, отправлять два одинаковых сообщения и плодить лиды. Делается таблица processed_updates:

Хранить «один глобальный курсор» не нужно — PK по update_id решает задачу идемпотентности надёжнее. Периодически чистите старые записи, например раз в сутки джобой удаляйте всё старше 7 дней.

5. Очередь: Yii Queue для тяжёлой обработки

Telegram ждёт ответа на webhook не дольше ~60 секунд, и всё это время держит соединение. Если вы в action пишете в БД, отправляете HTTP в CRM, считаете аналитику и отвечаете пользователю — легко словить таймаут и ретрай. Решение: в action только подтвердить приём, а обработку положить в очередь.

Запись в processed_updates и пуш в очередь в одной транзакции — это защита от потери: если очередь упала вместе с коммитом, воркер подхватит запись; если коммит не прошёл — апдейт придёт снова от Telegram.

6. Клиент к Bot API: cURL, проверка ok:false

Отдельный разговор — как из Yii2 корректно дёргать api.telegram.org. file_get_contents с потоком http не даёт нормально контролировать таймаут и HTTP-код, а у Telegram иногда возвращается 5xx без тела. Шаблон клиента:

Не забывайте про лимиты: не более ~30 сообщений в секунду на один бот суммарно и не более 1 сообщения в секунду в один и тот же чат. На массовые рассылки ставьте задержки и обрабатывайте 429 с retry_after.

7. Чек-лист перед боевым запуском

  • CSRF выключен только для action webhook, остальные формы защищены.
  • Секрет задан в setWebhook и сравнивается через hash_equals.
  • В action сначала фиксируется update_id, потом кладётся джоб в очередь, потом возвращается 200 OK.
  • Ответ Telegram уходит ≤ 1 секунды, вся тяжёлая логика — в воркере Yii Queue.
  • Используется cURL, проверяется HTTP-код и ok:false в JSON.
  • Токены и секрет — только в params / ENV, не в репозитории.

Такая связка переживает ретраи Telegram, длинные CRM-вызовы и случайные дубли, а главное — не превращает action в тонкое место всего приложения.

Если нужен готовый каркас webhook-контроллера на Yii2 с очередью и админкой для бота — посмотрите BotCreator.

Новые статьи — в Telegram

Разбираем, что автоматизировать в бизнесе и как это работает на практике. Без спама.