Перейти к содержимому

Вебхук от сервиса не доходит - разбор причин

Внешний сервис уверяет, что уведомления отправлены, а на сайте их нет. Разбираем причины по убыванию частоты, начиная с журнала веб-сервера.

С чего начать

Ищем запросы сервиса в журнале веб-сервера:

Окно терминала
grep -F '/local/hooks/' /var/log/nginx/access.log | tail -20 | awk '{print $9, $7, $1}'
grep -cF '/local/hooks/' /var/log/nginx/access.log
# код 200 - запрос принят; 301, 403 и 404 объясняют «уведомления не приходят»

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

Повторяем запрос сервиса своими руками:

Окно терминала
curl -si -X POST 'https://example.com/local/hooks/partner.php' \
-H 'Content-Type: application/json' -d '{"event":"test","id":1}' | head -20
# повтор с тем же телом и заголовками воспроизводит проблему за секунду

Смотрим, что вообще пришло на вход:

$raw = file_get_contents('php://input');
\Bitrix\Main\Diag\Debug::writeToFile([
'raw' => mb_substr($raw, 0, 500),
'type' => $_SERVER['CONTENT_TYPE'] ?? '',
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
], date('H:i:s'), 'hook.log');
// журнал входа пишут всегда: без него разбор превращается в переписку с сервисом

Тело запроса читают из потока, а не из массива формы. Уведомления в формате JSON в привычный массив не попадают вовсе, и обработчик видит пустые данные.

Проверяем, не режет ли запрос защита:

printf("проактивный фильтр: %s\n", \COption::GetOptionString('security', 'filter_action'));
// временное отключение фильтра для одного адреса проверяет гипотезу за минуту

Причины

  1. Адрес отвечает не тем кодом: переадресация или отказ примерно 30% случаев

    ПризнакВ журнале видны запросы с кодом переадресации или отказа вместо успешного.

    ПроверкаПовторяем запрос с консоли и смотрим первую строку ответа вместе с заголовками.

    Что делатьДаём сервису точный адрес с нужным протоколом и косой чертой, снимая переадресацию.

  2. Запрос режет проактивная защита сайта примерно 20% случаев

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

    ПроверкаСмотрим журнал проактивной защиты за время отправки уведомления.

    Что делатьДобавляем адрес приёма в исключения защиты и оставляем свою проверку подписи.

  3. Подпись уведомления не совпала примерно 20% случаев

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

    ПроверкаСчитаем подпись руками по телу запроса и сверяем её со значением из заголовка.

    Что делатьСверяем секрет с личным кабинетом сервиса и порядок полей в строке подписи.

  4. Тело запроса не разбирается примерно 15% случаев

    ПризнакОбработчик получает пустые данные, хотя запрос пришёл с успешным кодом ответа.

    ПроверкаПечатаем сырое тело запроса и заголовок с типом содержимого в журнал.

    Что делатьЧитаем тело из потока и разбираем его по типу содержимого, а не из массива формы.

  5. Сервис отключил доставку после серии ошибок примерно 15% случаев

    ПризнакУведомления перестали приходить разом, а в личном кабинете сервиса точка помечена как нерабочая.

    ПроверкаОткрываем в кабинете сервиса историю доставки и смотрим коды последних попыток.

    Что делатьЧиним приём, включаем доставку заново и просим сервис повторить пропущенные события.

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

Почему сервис считает, что уведомление отправлено?

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

Как отличить проблему сети от проблемы кода?

По журналу веб-сервера: запроса в нём нет - проблема снаружи, есть - внутри. Это первое действие любого такого разбора.

Можно ли отключить защиту для точки приёма?

Да, для конкретного адреса, оставив собственную проверку подписи. Отключать защиту на весь сайт ради одного адреса нельзя.

Что отвечать сервису при своей ошибке?

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

Как поймать редкое уведомление?

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

Смежное

Первоисточник