Отладка вебхука на ноутбуке обычно требует туннеля: адрес меняется при каждом перезапуске, провайдера приходится перенастраивать, а половина событий срабатывает только в продакшене. Постоянный catch-all URL в облаке решает это иначе: провайдер всегда обращается по одному адресу, а вы открываете перехваченный запрос когда удобно.
Почему catch-all URL удобнее туннеля
- Постоянный адрес — провайдер настраивается один раз, вместе с ретраями и тестовыми событиями.
- Никаких портов на вашей машине — нечего открывать и не нужно держать ноутбук включённым.
- Полная история — можно сравнить последние десять попыток одного и того же события, а не только пойманную вживую.
- Можно поделиться — read-only ссылка даст коллеге посмотреть payload без доступа к вашему аккаунту.
Шаг 1. Создайте URL вебхука
Создайте вебхук и скопируйте выданный URL. Принимается любой метод и любой content type: сохраняются метод, полный URL, query-строка, все заголовки, тело, IP клиента, user-agent и время.
Шаг 2. Направьте на него провайдера
Добавьте URL как endpoint в панели провайдера и вызовите событие:
- Stripe — Developers → Webhooks → Add endpoint; используйте Send test webhook или Resend на реальном событии.
- GitHub — Repository → Settings → Webhooks; кнопка Redeliver повторяет любую прошлую доставку.
- Telegram / Slack / свой сервис — вызовите сами:
curl -X POST https://your-sandbox-host/w/YOUR_TOKEN \
-H "Content-Type: application/json" \
-H "X-Custom-Signature: test" \
-d '{"event":"payment.succeeded","id":42}'
Шаг 3. Посмотрите, что реально пришло
Откройте детали запроса: query-строка таблицей, заголовки и тело в отформатированном виде, если это JSON. Именно здесь становится видно большинство проблем интеграции:
- провайдер прислал
application/x-www-form-urlencoded, а парсер ждёт JSON; - заголовок подписи отсутствует, потому что endpoint в тестовом режиме;
- полезная нагрузка вложена на уровень глубже, чем сказано в документации;
- событие — это повтор уже обработанного события.
Учтите, что подпись считается по сырому телу, ровно по тем байтам, что пришли. Храните и сравнивайте raw body, а не пересобранный JSON.
Шаг 4. Воспроизведите запрос локально
Скопируйте тело и заголовки из перехвата и отправьте тот же запрос на свой dev-сервер. Поскольку виден весь исходный запрос, падающий локальный обработчик воспроизводится без догадок и без ожидания провайдера.
Подсказки
- Если провайдер делает ретраи, группируйте запросы по id события (Stripe передаёт его в заголовке) — так видно дубликаты.
- Дайте URL коллеге только на чтение: создайте ссылку и отзовите её, когда отладка закончится.
- Для автоматизации ссылка умеет отдавать JSON — см. Проверка писем с помощью ИИ-ассистента.