Webhook не приходит: как проверить адрес, доставку и обработчик
Если webhook не приходит, проблема может находиться в нескольких местах: событие ещё не произошло, отправитель выбрал другой адрес, запрос остановил защитный шлюз, обработчик не сохранил уведомление или фоновая задача не завершилась. Для владельца сайта все эти случаи выглядят одинаково — заказ или другой объект не обновился. Но исправления потребуются разные.
Проверка webhook должна восстановить цепочку доказательств от события у отправителя до результата внутри сайта. Ниже — порядок диагностики для разработчика и руководителя проекта с примерами ЮKassa. Правила подтверждения и повторов относятся к конкретному провайдеру; переносить их на другую интеграцию автоматически нельзя. Все идентификаторы, времена и журналы в примерах учебные.
Разделите отсутствие доставки и отсутствие результата
Начните с вопроса, какой именно результат не появился. Отправитель может считать уведомление доставленным, потому что получил успешный HTTP-ответ. Ваш сервер при этом мог только принять запрос, а изменение заказа выполняется позже. Между получением уведомления и видимым результатом есть несколько самостоятельных границ.
Для расследования полезно различать пять состояний: событие возникло, попытка отправки состоялась, запрос дошёл до обработчика, сообщение надёжно сохранено, необходимое действие завершено. Для каждого состояния найдите отдельное подтверждение. Запись «получено» в журнале не заменяет запись в хранилище, а запись в хранилище не подтверждает завершение обработки.
Составьте карточку одного проблемного случая: провайдер, среда, аккаунт или магазин, тип события, идентификатор объекта и интервал времени с часовым поясом. Не начинайте с массового повторения операций. Сначала установите последнюю границу, которую сообщение точно прошло.
Если доказательства отсутствуют, пометьте состояние как неизвестное. Формулировка «нет строки в приложении» точнее, чем «провайдер ничего не отправлял»: запрос мог завершиться ещё на прокси, а журнал приложения мог быть неполным.
Проверьте выбранное событие, магазин и среду
Убедитесь, что подписка соответствует ожидаемому изменению. Настройка одного события не означает получение всех обновлений объекта. В инструкции ЮKassa по HTTP-уведомлениям отдельно перечислены URL и выбранные события, например payment.succeeded и payment.waiting_for_capture. Это разные состояния, и путать их в проверке нельзя.
Сопоставьте настройки с тем магазином и окружением, где создан проверяемый объект. Типичная диагностическая гипотеза — тестовый сценарий смотрят в рабочих журналах или после смены интеграции остался прежний адрес. Подтверждать её нужно значениями конфигурации, а не внешним видом страницы оплаты.
Проверьте, когда подписка была включена относительно события. Не предполагайте, что новая настройка автоматически доставит всю прошлую историю. Если важен уже произошедший случай, уточните доступные способы повторной доставки или сверки у конкретного отправителя.
Зафиксируйте текущую конфигурацию до изменений. Смена нескольких событий и адреса одновременно может восстановить работу, но оставить причину неизвестной. Для дальнейшего сопровождения полезно понимать, что именно было неверно и какой тест это подтвердил.
Отдельно подтвердите сам факт события у отправителя. Возврат пользователя на страницу сайта или нажатие кнопки не являются доказательством нужного перехода объекта. Для выбранного тестового случая найдите его состояние и время изменения в доступных данных провайдера. Если событие пока не произошло, проверка обработчика не объяснит отсутствие уведомления: сначала нужно восстановить правильные предпосылки теста.
Убедитесь, что отправитель обращается к правильному URL
Сверьте адрес посимвольно: протокол, домен, порт, путь и завершающий слеш. После переноса сайта или обновления модуля привычный маршрут может вести в другой обработчик. Проверьте, что адрес доступен из интернета и разрешается в нужную инфраструктуру, а не существует только внутри корпоративной сети.
В документации входящих уведомлений ЮKassa для URL указаны HTTPS, TCP-порт 443 или 8443 и TLS версии 1.2 либо выше. Это требования данного подключения. При диагностике другого провайдера сверяйте его собственные условия соединения и проверки сертификата.
Исследуйте фактический путь через CDN, балансировщик и обратный прокси. Проблема может касаться только одного доменного имени, маршрута или узла. Доступность главной страницы с вашего компьютера ещё не подтверждает, что внешний отправитель успешно устанавливает нужное соединение.
Проверьте перенаправления. Указывайте конечный адрес обработчика и не рассчитывайте, что отправитель пройдёт цепочку входа, смены домена или исправления слеша так же, как браузер. Правила обработки перенаправлений также определяются интеграцией.
После изменения инфраструктуры проверьте, не принимает ли сообщения прежняя площадка. Учебный пример: старый сервер продолжает отвечать 200, но пишет в уже неиспользуемую базу. Отправитель видит подтверждение, а рабочий сайт не видит запись. Сопоставление адреса назначения, выбранного узла и окружения обработчика помогает найти такую ошибку без повторного запуска исходной операции.
Отделите открытие страницы от приёма POST
Открыть URL в браузере — полезная предварительная проверка адреса, но обычно это GET. Уведомления ЮKassa поступают POST-запросом. Сервер может отдавать GET с кодом 200 и одновременно запрещать POST на том же пути. Такая проверка доступности не испытывает реальный обработчик уведомления.
Проверьте регистрацию маршрута, допустимый метод, обработку JSON и ограничения размера тела. Уточните, не требует ли общий middleware браузерную сессию или CSRF-токен, которых сервер отправителя не передаёт. Исключение для webhook проектируют адресно и компенсируют предусмотренной проверкой источника; отключать защиту всех маршрутов для этого не нужно.
Посмотрите, какой компонент сформировал отказ. Код 403 от защитного шлюза и 403 от приложения требуют разных действий. Аналогично 404 может означать неверный путь на прокси, а не отсутствие контроллера в коде. Сопоставляйте журналы по времени и техническим идентификаторам.
Для проверки используйте документированный тестовый механизм провайдера или изолированный стенд с учебным сообщением. Произвольный POST помогает испытать маршрут, но не подтверждает подлинную доставку от провайдера. Проверку канала и проверку настоящего уведомления оформляйте как разные результаты.
Найдите попытку доставки и ответ на неё
Запросите сведения со стороны отправителя: время попытки, адрес назначения, HTTP-результат, ошибку соединения и наличие последующих попыток. Если интерфейс предоставляет такой журнал, начните с него. Если сведений недостаточно, передайте поддержке идентификатор объекта, событие, среду и временной интервал.
Например, документация Stripe описывает вкладку Event deliveries с состояниями доставки и кодами ответа. Это возможность Stripe, а не обещание одинакового интерфейса у любого провайдера. Наличие истории API-запросов в кабинете другого сервиса само по себе не означает наличие истории исходящих webhook.
| Наблюдение | Следующая проверка |
|---|---|
| Нет подтверждённой попытки отправки | Событие, подписка, аккаунт, среда и доступность сведений |
| Ошибка соединения или TLS | Адрес, сеть и требования к защищённому соединению |
| HTTP-отказ до обработчика | Маршрут, прокси, защитный шлюз и ограничения метода |
| Подтверждение доставки, но нет сохранённого сообщения | Кто выдал ответ и где проходит граница сохранения |
| Сообщение сохранено, результата нет | Диспетчеризацию, очередь, воркер и применение изменения |
Ошибка timeout означает, что отправитель не получил ожидаемый ответ вовремя. Она не доказывает, что ваш сервер не принял запрос. Обработчик мог сохранить сообщение и закончить работу, пока ответ задержался или потерялся. Поэтому перед восстановлением проверяйте обе стороны.
Разделяйте время соединения, время до ответа обработчика и срок завершения фоновой работы. У каждого интервала своя причина задержки и свой критерий. Например, быстрый HTTP-ответ при растущем возрасте сохранённых сообщений показывает проблему после приёма. Медленное подтверждение при быстром применении события требует изучения пути ответа, а не немедленного увеличения числа воркеров.
Проверьте уведомление способом, предусмотренным провайдером
В инструкции ЮKassa проверка уведомления описана через актуальный статус объекта и IP-адрес источника. Сверку объекта выполняйте серверным запросом к API с правильными учётными данными; список допустимых адресов берите из действующей документации. Совпадение поля status в пришедшем JSON само по себе не является независимой проверкой.
Сопоставляйте объект с ожидаемым магазином и внутренней операцией. Не изменяйте произвольный заказ только потому, что в уведомлении указан его номер. Если объект успел перейти в другое состояние, разберите актуальность данных, а не объявляйте любое отличие признаком подделки.
За прокси определите, где надёжно устанавливается адрес источника. Доверие к произвольному входящему заголовку с IP позволяет ошибиться в проверке. Команда должна знать, какие посредники разрешено считать доверенными и где отбрасываются подставленные значения.
У Stripe применяется другой документированный механизм: подпись Stripe-Signature проверяется с секретом endpoint и исходным телом запроса. Эту схему нельзя приписывать ЮKassa по аналогии. Если интеграция использует подпись, изменение тела промежуточным обработчиком проверяйте по требованиям именно этой схемы.
Возвращайте подтверждение после надёжного сохранения
Для ЮKassa подтверждение получения — именно HTTP 200; другие коды не считаются валидным подтверждением. Тело и заголовки ответа не используются для этого подтверждения. У Stripe документация говорит об успешном ответе 2xx. Поэтому совет «верните любой успешный код» без имени провайдера недостаточно точен.
Смысл подтверждения в вашей архитектуре должен быть понятным: система приняла ответственность за дальнейшую обработку. Перед ответом сохраните допустимое сообщение в устойчивом хранилище или получите предусмотренное подтверждение надёжной постановки в очередь. Запись только в памяти процесса исчезнет при его перезапуске.
Проверьте опасную последовательность: сервер сначала ответил 200, затем попытался сохранить уведомление и получил ошибку. Отправитель уже вправе считать доставку завершённой, а внутри сайта события нет. Нужно изменить границу подтверждения, чтобы принятые сообщения оставались доступными для последующей обработки.
Если часть проверки требует внешнего API и выполняется позже, сохраните явное состояние «ожидает проверки». Подтверждение приёма такого сообщения не должно запускать выдачу товара или другое действие до успешной верификации. Ответственность за повтор проверки уже находится внутри вашей системы.
Подтверждение сохранения должно соответствовать выбранному хранилищу. Уточните, завершилась ли транзакция, получил ли издатель нужный ответ очереди и что произойдёт при немедленном перезапуске процесса. Сообщение «запись начата» в логе недостаточно. На стенде проверьте именно восстановление принятого уведомления после такого перезапуска, сохранив его состояние и связь с исходным объектом.
Проверьте переход от сохранённого сообщения к обработке
Найдите запись принятого уведомления и её внутренний идентификатор. Затем проследите, как она становится задачей: кто публикует её в очередь, каким воркером она берётся и где отмечается завершение. Если публикация и запись выполняются отдельно, исследуйте промежуток между ними: процесс мог остановиться после сохранения, но до отправки задачи.
Для диагностики достаточно нескольких явных состояний: принято, проверяется, готово к обработке, выполняется, завершено, требует разбора. Названия выбирает команда. Важно, чтобы состояние «принято» не скрывало бесконечное ожидание, а ошибка обработки имела причину и следующего ответственного.
Проверяйте возраст самого старого необработанного сообщения вместе с количеством задач. Маленькая очередь тоже может содержать одно уведомление, которое ждёт неприемлемо долго. Предел ожидания определяют требования конкретного сценария, а не универсальное число секунд.
Если воркер отметил успех, подтвердите необходимое изменение в основной системе. Завершение функции ещё не доказывает запись результата, отправку связанной команды или обновление интерфейса. Последняя проверка зависит от задачи сайта, но её нужно сформулировать заранее.
Не смешивайте временную недоступность внешней проверки с окончательно неприемлемым сообщением. Для первого случая нужен управляемый повтор, для второго — понятная причина отказа и разбор. Если все ошибки просто возвращают задачу в начало очереди, одна проблемная запись может долго скрываться среди нормальных. Сохраняйте число попыток, последнюю ошибку и назначенное следующее действие.
Учитывайте повторную доставку и предел ожидания
ЮKassa продолжает доставлять неподтверждённое уведомление в течение 24 часов с момента события. Этот срок относится к доставке уведомления и не описывает время выполнения вашей фоновой задачи. За пределами окна автоматические повторы нельзя считать планом восстановления.
У другого провайдера могут отличаться сроки, расписание и возможности ручной отправки. Найдите эти правила до инцидента и запишите их рядом с настройками интеграции. Не выводите интервал следующей попытки из случайной пары запросов в журнале.
Повтор может прийти после успешно сохранённого сообщения, если подтверждение потерялось. Получатель должен узнавать уже принятую работу и не запускать повторный бизнес-эффект. Используйте идентификаторы и правила конкретного события; внутренний ID попытки доставки и логическая операция не всегда совпадают.
Если автоматическое окно закончилось, определите отсутствующие изменения через согласованную сверку и восстановите только нужную работу. Ручное создание нового платежа или заказа не является способом «вернуть webhook». При повторной обработке сохранённого сообщения фиксируйте причину, исполнителя и результат.
Соберите журнал, по которому видна вся цепочка
Записывайте провайдера, среду, тип события, безопасный идентификатор объекта, внутренний ID приёма, этап и время. Если отправитель предоставляет отдельный идентификатор события или попытки, сохраните его согласно своему контракту. Не выдумывайте обязательное поле, которого в формате провайдера нет.
OWASP рекомендует не записывать в журналы в открытом виде пароли, токены доступа и идентификаторы сессий. Для webhook также заранее определите, какие части тела действительно нужны для расследования и кто может их видеть. Полный объект не обязательно должен копироваться в каждый технический лог.
Учебная последовательность ниже показывает уведомление, которое доставлено и сохранено, но ждёт обработчика. Это внутренний журнал приложения, а не пример полного сообщения ЮKassa.
10:00:00.120 UTC receipt=r-17 stage=received object=demo-object-17
10:00:00.180 UTC receipt=r-17 stage=stored state=ready
10:00:00.190 UTC receipt=r-17 stage=response status=200
10:03:00.180 UTC receipt=r-17 state=ready age_seconds=180
При условии согласованных часов между сохранением и ответом прошло 0,010 секунды, а спустя три минуты сообщение всё ещё готово к обработке. Искать причину следует на участке запуска или работы потребителя. Эти данные не подтверждают конкретную неисправность воркера, но сужают проверяемую область.
Испытайте приём, потерю ответа и остановку воркера
Проверка webhook должна включать контролируемые неудачи на тестовом стенде. Успешная доставка одного сообщения подтверждает только один путь. Команде нужно знать, что произойдёт при сохранённом уведомлении и потерянном подтверждении, отказе хранилища и перезапуске обработчика.
- Отправьте предусмотренное тестовое событие и проследите его до результата.
- Смоделируйте невозможность надёжного сохранения и проверьте отсутствие ложного подтверждения.
- После сохранения прервите ответ и убедитесь, что повтор не создаёт второй эффект.
- Остановите воркер, подтвердите видимость отставания, затем восстановите обработку.
- Проверьте отклонение неподходящего источника и отсутствие изменений от непроверенного сообщения.
- Испытайте восстановление записи, которая сохранена, но не была передана исполнителю.
Для каждого опыта укажите ожидаемый HTTP-ответ, состояние записи и результат в приложении. Используйте тестовые объекты без реальных списаний, писем клиентам и отгрузок. Необходимые внешние действия заменяйте предусмотренным тестовым режимом интеграции.
Проверку изолированного обработчика дополните прохождением публичного маршрута: прокси и защитные правила могут менять поведение. При этом не ослабляйте рабочую проверку подлинности ради успешной демонстрации.
Когда можно считать, что webhook снова работает
Исправление принимают по цепочке доказательств: нужное событие возникло в нужной среде, попытка достигла правильного маршрута, уведомление проверено и сохранено, отправитель получил установленное подтверждение, а внутренняя обработка завершилась ожидаемым результатом.
В отчёте укажите найденную границу отказа, изменение конфигурации или кода, проверенный сценарий и оставшиеся неизвестные. Если доступна только часть журналов, так и напишите. Успешная новая доставка не доказывает, что все старые пропуски восстановлены: их следует сверить отдельно.
Назначьте владельца интеграции и сохраните минимальную инструкцию: события, адрес, способ проверки источника, код подтверждения, окно повторов, место поиска сообщений и порядок восстановления. Тогда следующее сообщение «вебхук не работает» превратится в понятную проверку нескольких границ.
Дополните её внешним наблюдением за HTTP-доступностью через SiteGuard. Оно поможет заметить недоступность выбранных адресов сайта, а доставку POST-уведомлений и результат обработки команда проверит по своей интеграции.