ЛОДЖИК ТЕЛЕКОМ
Коммуникации4 августа 2026 г.5 мин

Webhook статусов SMS: подпись, повторы и идемпотентность

Как принимать статусы SMS без потерь и дублей: проверка подписи, идемпотентность, повторы, порядок событий, очередь ошибок и сверка с провайдером.

Защищённый поток статусов SMS проходит через проверку подписи и очередь повторов
Содержание

Надёжный webhook статусов SMS должен считать повторную доставку нормальным событием, а не исключением. Приёмник сначала проверяет подлинность запроса, сохраняет событие с уникальным идентификатором и быстро отвечает 2xx; бизнес-обработка выполняется асинхронно. Такая схема защищает систему от потерь при кратковременных сбоях, дублей после повторной отправки и неправильного порядка DLR.

Почему одного HTTP-обработчика недостаточно

Delivery Receipt проходит через несколько систем. Любая из них может повторить запрос после тайм-аута, даже если ваша сторона уже успела записать результат. События submitted, delivered и failed также могут прийти не по порядку. Если обработчик сразу меняет заказ, начисляет бонус или отправляет резервное уведомление, один повтор способен запустить бизнес-действие дважды.

Поэтому webhook — это граница интеграции, а не место для всей логики. Его задача ограничена четырьмя операциями:

  1. принять запрос и проверить формат;
  2. подтвердить, что запрос пришёл от ожидаемой стороны;
  3. надёжно сохранить исходное событие;
  4. вернуть ответ до истечения тайм-аута отправителя.

Какие поля нужны событию

Согласуйте контракт до запуска. Практический минимум:

  • идентификатор сообщения на стороне клиента;
  • идентификатор платформы или маршрута;
  • уникальный идентификатор события;
  • исходный и нормализованный статус;
  • время события у источника и время получения;
  • код и описание ошибки, если они есть;
  • версия схемы;
  • идентификатор попытки доставки webhook;
  • подпись и временная метка запроса.

Номер телефона и текст SMS обычно не нужны обработчику статусов. Если без номера не обойтись, ограничьте хранение и доступ, а в журналах используйте маскирование. Связать DLR с заказом или авторизацией лучше через непрозрачный correlation ID.

Проверка подписи и защита от повтора

Разрешение IP-адресов полезно как дополнительный слой, но не подтверждает целостность тела запроса. Основой должна быть криптографическая подпись: например, HMAC от необработанного тела, временной метки и идентификатора события. Проверяйте подпись до разбора бизнес-полей и сравнивайте значения константным по времени способом.

Временная метка ограничивает окно повторного воспроизведения. Запросы со слишком старым временем отклоняются, а идентификатор события сохраняется в таблице дедупликации. Секрет подписи должен храниться отдельно от кода и поддерживать ротацию с коротким периодом одновременного действия старого и нового ключа.

Не записывайте подписи, секреты и полные номера в обычный application log. Для расследования достаточно request ID, результата проверки, версии ключа и хеша полезной нагрузки.

Идемпотентность: один факт — одно изменение

Одинаковое событие может быть доставлено несколько раз. Создайте уникальное ограничение по provider + event_id или другому подтверждённо стабильному ключу. Вставка события и фиксация факта приёма должны происходить атомарно.

Если поставщик не выдаёт event ID, составной ключ можно построить из идентификатора сообщения, статуса, времени источника и кода ошибки. Такой вариант слабее: правила округления времени и нормализации должны быть зафиксированы, иначе разные события могут ошибочно слиться.

Идемпотентность нужна и на уровне бизнес-операции. Переход submitted → delivered допустим один раз, а повторный delivered не должен повторно закрывать заказ. Для побочных действий используйте outbox: в одной транзакции сохраните новое состояние и команду на последующую обработку.

Что делать с событиями не по порядку

Не полагайтесь на время получения. Храните журнал событий и отдельно вычисляемое текущее состояние сообщения. Правила переходов должны учитывать финальность статуса и временную метку источника.

Например, поздний submitted не должен заменить уже полученный delivered. Но более новый финальный статус может потребовать уточнения, если поставщик допускает корректировку результата. Такие правила нельзя угадывать: зафиксируйте таблицу переходов вместе с набором DLR, о котором подробно рассказано в материале про метрики доставки SMS.

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

Повторы, backoff и очередь ошибок

Webhook-провайдер обычно повторяет доставку при тайм-ауте или ответе не 2xx. Поэтому приёмник должен отвечать быстро — после надёжной записи, но до тяжёлой обработки. Если база или брокер недоступны и событие не сохранено, возвращайте ошибку: ложный 200 OK превращает временный сбой в безвозвратную потерю.

Внутренний обработчик использует ограниченное число повторов с экспоненциальной задержкой и jitter. Повторять без паузы опасно: сбой зависимости превращается в лавину запросов. После исчерпания попыток событие помещается в dead-letter queue с причиной, числом попыток и инструкцией по безопасному переигрыванию.

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

Сверка закрывает тихие потери

Даже хорошая доставка webhook не доказывает полноту истории. Нужен периодический процесс сверки:

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

Сверка особенно важна после аварии, ротации ключей или изменения контракта. Она дополняет, а не заменяет webhook.

Метрики и алерты

Контролируйте не только долю ответов 2xx, но и весь путь:

  • задержку от времени события до приёма и обработки;
  • число повторных доставок и долю дублей;
  • ошибки подписи и запросы вне допустимого окна;
  • размер очереди и возраст самого старого события;
  • количество записей в dead-letter queue;
  • сообщения без финального статуса;
  • неизвестные коды и запрещённые переходы состояния.

Разделяйте технические ошибки webhook и недоставку самой SMS. Первый показатель описывает вашу интеграцию, второй — маршрут связи. Общие принципы алертов и целевых показателей есть в руководстве по SLI, SLO и бюджету ошибок.

Чек-лист приёмки

Перед промышленным запуском воспроизведите:

  1. два одинаковых события подряд;
  2. delivered, пришедший раньше submitted;
  3. неверную подпись и просроченную временную метку;
  4. тайм-аут после успешной записи;
  5. недоступность базы и брокера;
  6. неизвестный статус и новую версию схемы;
  7. исчерпание повторов и переигрывание из DLQ;
  8. сверку сообщения, webhook которого был потерян.

Измеряйте время ответа под пиковой нагрузкой и проверяйте, что один медленный потребитель не блокирует весь входящий поток. Общую границу ответственности API полезно сверить с материалом про устойчивость внешних API.

Практический вывод

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

QuickTel предоставляет API и статусы для корпоративных SMS. Перед интеграцией согласуйте формат DLR, правила подписи, тайм-ауты, повторы и способ сверки, а затем проверьте контракт на собственном тестовом контуре.

SMSAPIУведомленияИнтеграции

Читайте также