Опрос статуса — самый простой способ получить код: отправил запрос, посмотрел ответ, если пусто — повторил через несколько секунд. Простота обманчива: каждый лишний запрос — нагрузка на лимит API, а задержка всё равно не фиксирована, потому что код может появиться сразу после проверки и пролежать неотправленным до следующего цикла. Вебхук устроен иначе: сервер сам сообщает о событии в момент, когда оно случилось. Разберём, что даёт переход на вебхук на практике, что нужно подготовить на своей стороне, как отличить настоящий вызов от подделки и когда без поллинга всё же не обойтись.

Пуш против опроса: что даёт вебхук

При поллинге клиент дёргает эндпоинт статуса раз в несколько секунд, и до момента получения кода почти все ответы означают «ничего нового». При десятке параллельных активаций это уже сотни лишних вызовов в минуту, которые упираются в лимит API, но не устраняют саму задержку между тем, как код появился на сервере, и тем, как об этом узнало приложение. Что такое сам код — разобрано в статье что такое OTP-код.

Вебхук переворачивает схему: сервер отправителя сам инициирует запрос на адрес вебхука, как только событие готово. Задержка сокращается до времени доставки одного HTTP-запроса, а число вызовов к API падает до одного на каждую активацию вместо десятков опросов в ожидании результата.

Что нужно подготовить на своей стороне

Первое требование — публично доступный адрес вебхука. Приёмник обязан отвечать снаружи по HTTPS: доменом или белым IP, без адреса локальной сети и без прокси между отправителем и сервером. Если адрес не резолвится из интернета, событию некуда доставиться — отправитель зафиксирует ошибку соединения, а не тишину на своей стороне. Причины, по которым приёмник оказывается недоступен, разобраны в статье про разрыв обратных вызовов.

Второе требование — скорость ответа. Обработчик вебхука обязан принять тело запроса и сразу вернуть код 2xx, не дожидаясь бизнес-логики — разбора текста, поиска кода регуляркой, записи в базу. Эта работа уходит в очередь и выполняется отдельным процессом. Если тянуть обработку синхронно до ответа, просадка базы или стороннего сервиса обернётся таймаутом, и отправитель посчитает доставку неудачной, хотя событие фактически дошло.

Подпись запроса: как проверить, что вебхук настоящий

Публичный адрес вебхука виден любому, кто его найдёт или подсмотрит в перехваченном трафике. Без проверки подлинности эндпоинт примет любое тело с произвольным содержимым — достаточно отправить POST-запрос с выдуманным «кодом», и обработчик проглотит его как настоящее событие.

Подпись закрывает эту дыру: отправитель хеширует тело запроса секретным ключом и кладёт результат в заголовок, приёмник пересчитывает хеш тем же ключом и сверяет значения. Совпадение подтверждает и подлинность источника вебхука, и то, что тело не изменилось по дороге. Ключ хранится только на сервере и не попадает в клиентский код; при подозрении на утечку его ротируют в кабинете, синхронно обновив проверку на своей стороне.

Повторная доставка вебхука — это норма, а не сбой

Отправитель не может быть до конца уверен, что код 2xx означает «событие сохранено», а не «сервер ответил, но обработка упала секундой позже». Поэтому большинство систем повторяют доставку вебхука по расписанию: через несколько секунд, потом через минуту, и так несколько раз, пока не получат подтверждение или не исчерпают лимит попыток. Итог — один и тот же код с одним и тем же идентификатором события может прийти дважды и даже трижды.

Обработчик обязан быть идемпотентным: перед записью проверять, не сохранён ли уже код с этим идентификатором активации или события, и на повторе просто отвечать 200, ничего не создавая заново. Ретраи со стороны отправителя близки к тому, как правильно ретраить собственные запросы к чужому API — общие принципы разобраны в статье ошибки API: как обрабатывать и ретраить. Если приёмник был недоступен всё окно повторов, событие не потеряется только при одном условии — у отправителя есть очередь недоставленных событий, и пропущенное можно забрать вручную отдельным запросом статуса.

Когда поллинг всё же уместен

Если у задачи нет постоянного публичного адреса — тестовый стенд без домена, разовый скрипт на машине за NAT, короткий пилот, который отключат через день, — заводить вебхук ради него бессмысленно. Поллинг с интервалом в несколько секунд закрывает разовую или редкую задачу без инфраструктуры приёмника и подписи.

Второй случай — по-настоящему единичный запрос: один код на одну активацию, без потока событий, где выигрыш от вебхука не окупает настройку публичного адреса и очереди обработки. При регулярном или параллельном приёме кодов соотношение меняется на обратное: поллинг начинает съедать лимит API быстрее, чем экономит время на интеграции.

Частые вопросы

Можно ли использовать один и тот же адрес вебхука для нескольких сервисов?

Технически да, если обработчик различает события по полю с идентификатором сервиса или активации в теле запроса. Практичнее развести источники по отдельным путям на одном домене — так проще читать логи и обновлять подпись для одного источника, не трогая остальные.

Что делать, если провайдер не поддерживает повтор доставки?

Заведите параллельный поллинг статуса как страховку на случай, если приёмник был недоступен в момент единственной попытки push. Опрашивайте редко — раз в несколько минут: этого достаточно, чтобы подобрать пропущенное событие, не превращая опрос в основной канал.

Как проверить, что подпись работает правильно, до выхода в прод?

Отправьте тестовое событие с намеренно повреждённым телом или неверным заголовком подписи и убедитесь, что обработчик отклоняет его до записи в базу. Затем повторите с корректной подписью и тем же идентификатором события дважды подряд — второй вызов должен вернуть 200, ничего не создав повторно.

Формат события, заголовок подписи, структура тела запроса и параметры повторных попыток для приёма кодов по вебхуку описаны в документации API.