Вебхуки: от события до результата | Creative Agency Here

Вебхуки: от события до результата

WebhooksAPIИнтеграции

Контур целиком

ШагЧто происходитЧто может пойти не так
01 · ДоставкаВнешний сервис отправляет HTTPS POSTDNS, TLS, сеть или неверный URL
02 · ПроверкаПриёмник проверяет подпись и допустимость событияПоддельный или изменённый запрос
03 · РегистрацияСохраняются ID, тип, время и технический статусДубликат или потеря контекста
04 · ПодтверждениеОтправителю быстро возвращается 2xxТаймаут запускает повторную доставку
05 · ОбработкаБизнес-логика выполняется отдельноОшибка внешнего API или данных
06 · НаблюдениеМетрики и журнал показывают результатСбой остаётся незамеченным

Проектируйте вебхук с предположением, что одно событие может прийти несколько раз, позже обычного и не в том порядке, в котором происходили действия.

Сначала определите контракт

До настройки URL зафиксируйте:

  • источник и владелец интеграции;
  • минимальный набор событий;
  • идентификатор доставки или события;
  • способ проверки подлинности;
  • максимальный размер тела запроса;
  • допустимое время ответа;
  • правила повторов и ручной доставки;
  • данные, которые нельзя писать в лог;
  • ожидаемое действие в HereCRM;
  • способ отключения интеграции.

Подписывайтесь только на нужные типы событий. Это уменьшает нагрузку, объём персональных данных и количество неожиданных веток логики.

Публичный endpoint

Адрес должен быть отдельным HTTPS-маршрутом без секретов в URL.

POST https://hooks.example.com/v1/github
POST https://hooks.example.com/v1/telegram

Не используйте такой вариант:

https://hooks.example.com/webhook?secret=my-secret

Query-параметры попадают в журналы прокси, историю диагностики и системы аналитики. Секрет передаётся или проверяется способом, который определяет конкретный поставщик: HMAC-подпись, специальный заголовок, mTLS или OAuth.

Endpoint должен принимать только ожидаемый HTTP-метод, ограничивать размер запроса и возвращать нейтральную ошибку без внутреннего стека.

Проверяйте сырое тело запроса

Многие поставщики вычисляют HMAC по исходным байтам payload. Если сначала разобрать JSON и собрать его заново, пробелы и порядок сериализации изменятся — подпись перестанет совпадать.

Пример проверки X-Hub-Signature-256 для GitHub:

import hashlib
import hmac


def verify_github_signature(body: bytes, signature: str | None, secret: bytes) -> bool:
    if not signature or not signature.startswith("sha256="):
        return False

    expected = "sha256=" + hmac.new(secret, body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Используйте собственный случайный секрет для каждого webhook, храните его вне кода и сравнивайте значения функцией постоянного времени. Не переносите название заголовка GitHub на другие сервисы — у каждого поставщика свой контракт.

Защита от повторов

Повторная доставка — нормальная часть работы webhook. Причиной может быть таймаут, временная ошибка сети или ручной redelivery.

До запуска бизнес-логики сохраните уникальный ключ:

source + endpoint + delivery_id

Если такой ключ уже успешно обработан, верните успешный ответ и не повторяйте действие. Для событий без ID сформируйте устойчивый ключ из документированных полей, но не используйте случайный UUID при каждом приёме.

Идемпотентность нужна на уровне результата:

  • одна оплата не должна дважды увеличить баланс;
  • одно сообщение не должно создать два лида;
  • одно изменение задачи не должно дважды запустить отчёт;
  • повторный статус не должен повторять необратимое действие.

Быстрый ответ и отдельная обработка

Приёмник выполняет только короткую синхронную часть:

  1. проверяет метод, размер и content type;
  2. получает сырое тело и обязательные заголовки;
  3. проверяет подпись;
  4. проверяет ID на повтор;
  5. надёжно регистрирует событие;
  6. возвращает 2xx.

Тяжёлая работа — запросы во внешние API, создание отчёта, обработка файлов — выполняется после подтверждения доставки. Конкретный механизм зависит от архитектуры проекта; публичная статья не фиксирует очередь или брокер, которого может не быть в контуре.

Статусы HTTP

КодКогда возвращатьЧто ожидает отправитель
200 или 204Событие принято либо уже безопасно обработаноДоставка завершена
400Тело не соответствует контрактуИсправление запроса, повтор обычно бесполезен
401 или 403Подпись отсутствует или невернаПроверка секрета и источника
404Endpoint отключён или неверенИсправление URL
409Конфликт действительно требует вмешательстваПоведение зависит от поставщика
429Приёмник временно ограничивает потокПовтор по правилам поставщика
500–503Временный внутренний сбой до надёжной регистрацииПовторная доставка

После того как событие надёжно зарегистрировано, ошибка фоновой бизнес-логики не должна превращать приём в бесконечные повторы со стороны поставщика. Обрабатывайте её во внутреннем контуре повторов.

Журнал без утечки данных

Для каждой доставки полезно хранить:

ПолеПример безопасного значения
Источникgithub
Delivery IDИдентификатор из заголовка поставщика
Тип событияissues.opened
ПолученоUTC-время приёма
Проверка подписиpassed или failed
HTTP-ответ204
Результат обработкиlead_created
Correlation IDВнутренний идентификатор цепочки
ПопыткиКоличество внутренних повторов

Не пишите в обычный лог полный payload, токены, подписи, cookies, персональные данные и содержимое сообщений. Если сырой запрос нужен для расследования, храните его ограниченное время в защищённом хранилище с контролем доступа.

Наблюдаемость

Минимальные метрики:

  • число доставок по источникам и типам;
  • доля отклонённых подписей;
  • количество дубликатов;
  • время синхронного ответа;
  • задержка до завершения обработки;
  • ошибки и число повторов;
  • возраст самого старого необработанного события.

Алерт нужен не на каждую единичную ошибку, а на потерю функции: рост серии 5xx, накопление необработанных событий, отсутствие ожидаемого трафика или повторяющийся отказ конкретного источника.

Диагностика по слоям

Событие не приходит

Проверьте активность подписки, выбранный тип события, точный URL, DNS и журнал доставок у поставщика. Не меняйте сервер вслепую, пока не видно, была ли попытка отправки.

TLS или соединение

Проверьте сертификат, цепочку доверия, имя домена и доступность с внешней сети. Не отключайте проверку TLS как постоянное решение.

Подпись не совпадает

Убедитесь, что используется сырой body, правильный секрет и актуальный заголовок. Проверьте, не изменяет ли proxy тело или кодировку до верификации.

Отправитель повторяет событие

Сравните время ответа с требованиями поставщика. Проверьте, что 2xx возвращается после надёжной регистрации, а не после всей бизнес-логики.

Ответ успешный, результата нет

Найдите событие по delivery ID и correlation ID, затем проверьте внутренний статус обработки, последнюю ошибку и возможность безопасного повтора.

Особенности источников

GitHub

Используйте случайный webhook secret, проверяйте X-Hub-Signature-256 по сырому телу и сохраняйте X-GitHub-Delivery для защиты от повторов. GitHub показывает журнал deliveries и позволяет выполнить redelivery.

Telegram Bot API

При setWebhook задайте secret_token; Telegram будет передавать его в заголовке X-Telegram-Bot-Api-Secret-Token. Ограничьте allowed_updates только нужными типами. Токен бота не должен появляться в документации, shell history и логах.

Платёжные системы

Не считайте возврат 2xx подтверждением оплаты для пользователя. Сначала проверьте подпись, ID события, сумму, валюту и состояние объекта по контракту провайдера. Все финансовые переходы должны быть идемпотентными и журналируемыми.

Таблицы и пользовательские скрипты

Не отправляйте всю строку «на всякий случай». Сформируйте минимальный payload, удалите персональные данные, вынесите секрет из исходного кода и учитывайте, что пользователь может изменить структуру листа.

Безопасный тест

  1. Создайте отдельный тестовый endpoint и отдельный секрет.
  2. Отправьте документированный пример события.
  3. Проверьте корректную подпись и отказ при изменённом body.
  4. Повторите один delivery ID и убедитесь, что действие не дублируется.
  5. Смоделируйте временную ошибку обработки.
  6. Проверьте внутренний повтор и итоговый статус.
  7. Убедитесь, что в логах нет секретов и полного payload.
  8. Только после этого подключайте production-события.

Сервисы просмотра webhook удобны для обезличенного тестового payload. Не отправляйте туда реальные клиентские данные, токены и события production.

Чек-лист выпуска

  • HTTPS и проверка сертификата включены.
  • Секрет не находится в URL или репозитории.
  • Подпись проверяется по сырому телу согласно документации источника.
  • Endpoint принимает только нужные события и ограничивает размер запроса.
  • Delivery ID защищает бизнес-действие от повторов.
  • Событие надёжно регистрируется до ответа 2xx.
  • Тяжёлая логика вынесена из синхронного ответа.
  • Логи не содержат payload и учётные данные.
  • Есть метрики, алерт и способ безопасного redelivery.
  • Владелец и процедура отключения интеграции известны.

Источники

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