Гарантии доставки вебхуков: что обещают провайдеры на деле
Что провайдеры вебхуков гарантируют на деле: доставку хотя бы один раз, повторы, отсутствие порядка и дубликаты. Как построить получатель, который это выдержит.
Проверил(а) Maurice Sobiera

Гарантии доставки вебхуков описывают, как часто и в каком порядке провайдер доставляет события на ваш endpoint. Большинство провайдеров обещают в лучшем случае доставку хотя бы один раз. Ваш endpoint получает большинство событий, иногда дважды, иногда с опозданием и иногда не по порядку. Надёжные интеграции строят защиту на стороне получателя.
В этом руководстве каждая гарантия объясняется простыми словами, показано, что обещает реальная документация провайдеров, и приведён чек-лист для любой интеграции вебхуков, включая события крипто-платежей.
TL;DR
- Потолок реалистичен: доставка хотя бы один раз. Провайдеры повторяют отправку, пока вы не вернёте ответ 2xx, поэтому дубликаты нормальны, и их нужно обрабатывать.
- Порядок не гарантируется. Событие, которое ранее истекло по таймауту, может прийти после более позднего, поэтому сортируйте по собственной временной метке события или полю последовательности.
- Сначала подтверждайте, потом обрабатывайте. Проверьте подпись, надёжно сохраните событие, верните 2xx, а тяжёлую работу выполняйте асинхронно.
- Выполняйте дедупликацию по ID события. Записывайте его с уникальным индексом до любого побочного эффекта, например до зачисления баланса.
- Считайте вебхуки уведомлениями, а не учётной книгой. По расписанию сверяйте критичные записи с API, чтобы поймать то, что пропустили повторы.
- Chaingateway не повторяет неудачные уведомления автоматически. Он показывает их список и отправляет повторно по запросу.
Что означают три гарантии доставки?
Гарантии доставки пришли из распределённых систем и хорошо ложатся на вебхуки.
| Гарантия | Что делает отправитель | Что может пойти не так | Что строите вы |
|---|---|---|---|
| Не более одного раза (at-most-once) | Отправляет событие один раз и никогда не повторяет | Событие теряется, если ваш endpoint недоступен | Сверка с API |
| Хотя бы один раз (at-least-once) | Повторяет, пока вы не подтвердите получение или пока не закончатся попытки | Дубликаты, опоздавшие события, перестановка порядка | Идемпотентный обработчик, дедупликация |
| Ровно один раз (exactly-once) | Доставляла бы один раз без потерь и дубликатов | Недостижимо через ненадёжную сетевую границу | At-least-once плюс дедупликация |
При at-most-once неудачная попытка означает потерянное событие. Для отправителя это просто, но для всего, что связано с деньгами, небезопасно.
При at-least-once отправитель продолжает попытки, пока не получит ответ об успехе или не исчерпает бюджет повторов. Потери становятся редкими, но есть подвох. Допустим, ваш обработчик зачисляет средства на счёт, а затем падает до того, как вернуть 200. Отправитель не видел подтверждения и отправляет событие снова. Для отправителя это правильное поведение. Для вас это двойное зачисление, если вы его не предотвратите.
Exactly-once хотят все, но по HTTP этого не обещает никто. Отправитель не может знать, обработал ли ваш сервер запрос, когда соединение оборвалось. Практическая замена: доставка хотя бы один раз в сочетании с получателем, который игнорирует повторы. Эта комбинация даёт ровно один раз эффекты, а именно это и важно.
Что провайдеры обещают на самом деле?
Прочитайте раздел о доставке в документации любого серьёзного провайдера вебхуков, и вы увидите один и тот же шаблон.
Adobe I/O Events пишет, что каждое событие доставляется хотя бы один раз на регистрацию, что дубликаты возможны и что порядок событий не гарантируется. Column, американский банковский API, заявляет, что события вебхуков не гарантированно приходят в порядке создания. Ранее истёкшее по таймауту событие может прийти после более позднего, потому что каждый повтор планируется независимо.
Эта последняя деталь объясняет большинство ошибок с порядком. Событие A падает один раз и ставится в повтор через минуту. Событие B, созданное позже, проходит сразу. Ваша система видит сначала B, потом A. На стороне провайдера ничего не сломано. Гарантия просто никогда не касалась порядка.
Порядок: опирайтесь на время события, а не на время доставки
Рекомендация Wise для вебхуков: восстанавливать хронологию по временным меткам событий или, где они есть, по монотонно растущим ID последовательности, а не по моменту отправки уведомления. В их документации отмечено, что время отправки может отличаться от времени, когда событие произошло, особенно при повторах.
На практике это выглядит так:
- Храните бизнес-временную метку события или номер последовательности вместе с payload.
- Когда приходит событие, сравните его с последним состоянием, которое вы держите для этого объекта.
- Если входящее событие старше уже применённого, запишите его, но не позволяйте ему перезаписать более новое состояние.
Для статуса платежа, который переходит от pending к confirmed, это не даёт опоздавшему событию «pending» откатить подтверждённый платёж.
Как работают повторы вебхуков и когда они прекращаются?
Политики повторов различаются, но форма одна и та же: экспоненциальная задержка, предел и жёсткая остановка. Исключение — Chaingateway, который не повторяет автоматически.
| Провайдер | Поведение повторов (по его документации) |
|---|---|
| Column | До 25 повторов в течение трёх дней, экспоненциальная задержка, первый повтор через минуту после неудачной доставки |
| Probo | До 12 попыток, экспоненциальная задержка с полным джиттером от базы 30 секунд, предел четыре часа |
| Stripe | Экспоненциальная задержка в live-режиме до трёх дней |
| Chaingateway | Автоматических повторов нет. Неудачные уведомления выводятся через GET /api/v2/{chain}/webhooks/notifications/failed и отправляются повторно через POST /api/v2/{chain}/webhooks/notifications/{id}/retry |
Источники: документация Column, Probo и Stripe, прочитано 5 ноября 2026 года, а также спецификация OpenAPI Chaingateway.
В документации Probo есть полезные детали. Повторяются сетевые ошибки, таймауты и ответы 408, 425, 429 или 5xx, а заголовок Retry-After соблюдается до предела. Когда попытки исчерпаны, доставка помечается как неудачная и в очередь больше не ставится.
Из этого следуют два вывода. Во-первых, коды ошибок вашего endpoint — это сигналы: 5xx велит отправителю повторить попытку, поэтому возвращайте его только тогда, когда действительно хотите повтора. Во-вторых, окно повторов конечно. Если ваш сервис лежит дольше этого окна, события выпадают из очереди повторов, и для их восстановления нужен другой способ.
Почему стоит отвечать быстро и обрабатывать позже?
Доставка считается успешной, когда вы возвращаете статус 2xx. Column считает событие доставленным, если получает 2xx в течение 10 секунд, и рекомендует возвращать его сразу после получения, до существенной обработки. Документация NestJS считает успехом 2xx, а всё остальное, включая редиректы, таймауты и отказанные соединения, неудачей.
Поэтому задача обработчика узкая:
- Прочитать исходное тело запроса.
- Проверить подпись.
- Проверить тип и ID события.
- Записать событие в надёжное хранилище (строку базы данных или сообщение очереди).
- Вернуть 200, 202 или 204.
Всё остальное, например обновление балансов, отправка писем или вызовы других сервисов, выполняется воркером, который читает из этого хранилища. Если делать медленную работу прямо в обработчике, медленный запрос к базе превращается в таймаут, таймаут — в повтор, а повтор — в дубликат. Подход «приём, очередь, fan-out» описан в нашей статье вебхуки или WebSockets.
Как сделать дубликаты безвредными?
Поскольку доставка хотя бы один раз означает повторы, ваш обработчик должен быть идемпотентным. Стандартный метод прост.
Рекомендация Probo: проверить подпись, убедиться в ожидаемой организации и типе события, затем атомарно записать ID события с уникальным индексом до любых побочных эффектов. Последующий запрос с уже записанным ID игнорируется. Stripe даёт тот же совет: записывайте обработанные ID событий и пропускайте события, которые уже обработаны. Многие провайдеры упрощают задачу, добавляя в каждый payload уникальный ID. Например, Adobe кладёт UUID в payload и повторяет его в заголовке x-adobe-event-id.
Минимальный шаблон:
- Вставьте ID события в таблицу с уникальным ограничением.
- Если вставка не удалась, потому что ID уже существует, верните 200 и остановитесь.
- Если вставка прошла, поставьте работу в очередь.
Порядок важен. Сначала записать, потом действовать. Если действовать первым и упасть до записи, повтор повторит действие. Если записать первым и упасть до действия, воркер при перезапуске должен подхватить незавершённые записи, чтобы событие не потерялось молча.
Для движения денег добавьте второй слой: сделайте идемпотентной и саму бизнес-операцию. Привязывайте зачисление депозита к хешу транзакции и индексу лога или к ID платежа, чтобы даже ошибка в таблице событий не могла зачислить один платёж дважды.
Как проверять каждый запрос?
Endpoint вебхука — это публичный URL, который запускает действия в вашей системе. Отправить на него может кто угодно, если вы не проверяете отправителя.
Stripe требует проверку с использованием сырого тела запроса, заголовка подписи и секрета endpoint. Probo советует использовать нетронутое тело и проверять подпись до разбора JSON, а затем проверять свежесть временной метки. Разбор и повторная сериализация тела до проверки — классическая причина того, что подписи никогда не совпадают.
Надёжная процедура проверки для провайдеров, которые подписывают тело:
- Вычислите HMAC по точным байтам, которые получили, с вашим секретом.
- Сравните его со значением заголовка сравнением за постоянное время.
- Отклоняйте запросы, чья временная метка выходит за короткое окно допуска, чтобы ограничить повторное воспроизведение перехваченных запросов.
- И только потом разбирайте JSON и смотрите на тип события.
Chaingateway работает иначе. Он вычисляет Base64 HMAC-SHA256 от txid из payload и отправляет его в заголовке X-Signature. Документация по подписи показывает проверку, а наша статья о функции безопасности вебхуков объясняет настройку.
Как восстановить события, которые пропустили повторы?
Даже идеальный получатель теряет события в некоторых сценариях: простой дольше окна повторов, неверно настроенный файрвол, деплой, который несколько часов возвращал 500. Варианты восстановления у провайдеров различаются. Probo не предусматривает ручного повторного воспроизведения и не ставит неудачные доставки в очередь заново: он ожидает, что вы настроите оповещения о сбоях и будете сверяться с его API. Stripe отмечает, что операторы могут вручную обработать накопившуюся очередь, чтобы ускорить восстановление. Column сообщает, что пропущенные или лишние события можно получить через их event API, когда сбои требуют сверки.

Общий принцип дизайна, прямо сформулированный в документации разработчиков ThriveDesk: считайте вебхуки уведомлениями, а не единственным журналом событий. Сохраняйте payload надёжно, обрабатывайте асинхронно и периодически сверяйте критичные записи с REST API.
Задача сверки может быть небольшой:
- По расписанию запрашивайте из API провайдера важные объекты (платежи, кошельки, заказы) за последнее окно.
- Сравнивайте их статусы с вашими записями.
- Применяйте расхождения через тот же идемпотентный путь кода, которым пользуется обработчик вебхуков.
- Поднимайте тревогу, если задача часто находит пробелы: это указывает на проблему с endpoint.
Поскольку следующее событие часто несёт полное текущее состояние объекта, пропущенное промежуточное событие всё равно может быть перезаписано следующим. Сверка нужна для случаев, когда следующего события нет.
Как эти гарантии работают для событий крипто-платежей?
Платежи в сети повышают ставки: пропущенное или удвоенное событие означает пропущенное или удвоенное зачисление. Несколько привычек помогают независимо от провайдера:
- Привязывайте зачисления к идентификаторам в блокчейне. Используйте хеш транзакции и индекс перевода внутри неё, а не только ID доставки вебхука.
- Моделируйте статус как последовательность. Pending, confirmed и final — состояния с порядком. Никогда не позволяйте старому статусу перезаписать новый.
- Сопоставляйте с собственными данными. Перед зачислением найдите принимающий адрес или референс в своей базе; игнорируйте события по адресам, которые вам не принадлежат.
- Сверяйте балансы. Плановое сравнение вашего учёта с балансами кошельков из API ловит всё, что выпало из потока событий.
Наш разбор автоматической обработки депозитов через вебхуки показывает эти шаги в платёжном флоу, а руководство по вебхукам Polygon описывает альтернативу опросу на конкретной сети.
Наш взгляд
Мы строим вебхуки для разработчиков, которые двигают деньги, поэтому думаем о гарантиях доставки каждый день. Наш вывод: ни один честный провайдер не может обещать доставку ровно один раз, и правильный ответ — чистый контракт. События — это уведомления, подписанные запросы доказывают, откуда они пришли, а API всегда остаётся источником истины.
Поэтому мы сочетаем уведомления о событиях в реальном времени с единым REST API для нескольких сетей. Вебхуки сообщают, что что-то произошло. API позволяет это подтвердить, получить декодированные данные транзакции и сверить при необходимости. Наши вебхуки подписаны HMAC, чтобы ваш получатель мог их проверить, а payload содержит декодированные отправителя, получателя, контракт токена и сумму, поэтому обработчик тратит время на бизнес-логику, а не на разбор сырых логов.
Мы также считаем, что командам не стоит запускать собственные ноды или жонглировать endpoint-ами конкретных сетей, чтобы этого добиться. Одна интеграция, одна схема подписи и один способ запрашивать состояние в Ethereum, Bitcoin, Tron, Solana, Polygon, BNB Chain и Arbitrum означают меньше мест, где может спрятаться граничный случай доставки.
Bitblade
Стройте на подписанных мультичейн-вебхуках
Если вы добавляете крипто-платежи в приложение, Chaingateway Webhooks доставляют уведомления о событиях блокчейна с подписями HMAC. За ними стоит один Blockchain API для кошельков, переводов, декодированных транзакций и цен газа. Тарифы на странице цен начинаются с 49 € в месяц за Plus; Pro стоит 99 €, Premium 249 €, Enterprise 499 € в месяц, при годовой оплате от 490 €.

Сочетайте поток вебхуков с описанной выше схемой сверки, и вы получите быстрые уведомления со страховочной сеткой.
Источники
- Adobe I/O Events FAQ
- Column: события и вебхуки
- Wise Platform: порядок событий
- Документация Probo: доставка вебхуков и восстановление
- Документация Stripe: вебхуки
- NestJS: вебхуки
- Документация разработчиков ThriveDesk: повторы, сбои и воспроизведение
Рекомендуем
Часто задаваемые вопросы
Не через ненадёжное HTTP-соединение. Отправитель не может определить, обработал ли ваш сервер запрос, если ответ потерян. Практический подход: доставка хотя бы один раз плюс дедупликация по ID события, которая даёт в вашей системе эффект ровно одного раза.
Возвращайте код 2xx, например 200, 202 или 204, как только проверили событие и надёжно его сохранили. Провайдеры вроде Column считают доставку успешной только при 2xx в течение 10 секунд. Возвращайте 5xx, только если хотите, чтобы отправитель повторил попытку.
Повторы планируются независимо для каждого события. Если событие A истекло по таймауту, а событие B прошло успешно, B приходит первым, а A появляется после своего повтора. Такие провайдеры, как Adobe и Column, прямо говорят, что порядок не гарантируется, поэтому сортируйте по временной метке события или полю последовательности.
Большинство провайдеров повторяют с экспоненциальной задержкой в ограниченном окне, по документации, которую мы изучили, до трёх дней. После этого событие может не попасть в очередь повторно. Chaingateway не повторяет автоматически: он показывает неудачные уведомления и отправляет их повторно по вашему запросу. В обоих случаях запустите плановую сверку с API провайдера, чтобы восстановить всё пропущенное.
Да. Ваш endpoint доступен публично, и проверка подписи по сырому телу и вашему секрету — это способ понять, что запрос пришёл от провайдера. Добавьте допуск по временной метке, чтобы снизить риск повторного воспроизведения.
Готовы собрать это сами? Получите свой API-ключ — 7-дневный пробный период, без карты — или посмотрите API блокчейна для полного справочника по эндпоинтам.