Webhook-система с логированием и повторной отправкой: настройка под ключ

Интеграция с партнёром работает, но часть событий теряется без следа — разбираться приходится вслепую. Мы разрабатываем webhook-системы с полным логированием каждой попытки и автоматической повторной отправкой. Наша команда реализует проект под ключ — от аудита до поддержки, обеспечивая прозрачность и надёжность доставки данных.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Webhook-система с логированием и повторной отправкой: настройка под ключ
Средний
~2-3 дня
Часто задаваемые вопросы

Вы запустили интеграцию с CRM-партнёра. Первые события уходят, но через час клиент жалуется, что не получил половину уведомлений. Логи молчат — получатель отвечал 200, но данные не обработал. Без детализации каждой попытки выяснить причину — гадание. Мы разрабатываем Webhook-системы с полным логированием и повторной отправкой: не просто «послать и забыть», а контроль каждого события. 5+ лет в интеграциях B2B, 100+ проектов — гарантируем прозрачность и надёжность.

Webhook — это исходящий HTTP-запрос, который вы не контролируете до конца. Получатель может вернуть 200, не обработав данные. Может упасть через 9 секунд после получения. Может пропустить событие без следа. Без детального логирования всех попыток и возможности ручной повторной отправки отладить проблемы с интеграцией практически невозможно.

Почему логирование каждой попытки — основа надёжной webhook-системы?

Допустим, событие упало на третьей попытке после таймаута. Если не хранить историю, вы увидите только финальный статус. А так — полная картина: первая попытка упала с 500, вторая с таймаутом через 8 секунд, третья — 502. Это сразу указывает на проблемы на стороне получателя. Системы без логирования попыток заставляют разработчиков тратить часы на воспроизведение. Наш подход сокращает время отладки в среднем в 5 раз по сравнению с традиционным мониторингом.

Какие данные мы логируем и как это реализовано?

Минимальный набор данных на каждую попытку доставки:

Поле Описание
delivery_id UUID доставки — связывает все попытки
attempt_number Номер попытки (1, 2, 3...)
started_at Время начала попытки
duration_ms Сколько заняло — важно для детектирования таймаутов
request_headers Заголовки запроса (без секрета в чистом виде)
request_body Тело запроса (payload события)
response_code HTTP-статус ответа
response_headers Заголовки ответа
response_body Первые 2 КБ тела ответа — для дебага
error Текст ошибки при ConnectionException / Timeout

Храним попытки отдельно от доставок — одна доставка может иметь 8 попыток. Это позволяет видеть полную историю и понимать, на каком шаге всё пошло не так.

CREATE TABLE webhook_attempts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    delivery_id UUID NOT NULL REFERENCES webhook_deliveries(id) ON DELETE CASCADE,
    attempt_number INTEGER NOT NULL,
    started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    duration_ms INTEGER,
    request_body JSONB,
    request_headers JSONB,
    response_code INTEGER,
    response_headers JSONB,
    response_body TEXT, -- обрезается до 2000 символов
    error_message TEXT,
    success BOOLEAN NOT NULL DEFAULT false
);
CREATE INDEX idx_attempts_delivery ON webhook_attempts(delivery_id);
CREATE INDEX idx_attempts_started ON webhook_attempts(started_at DESC);

Реализация логирования:

class WebhookAttemptLogger {
    public function log( WebhookDelivery $delivery, int $attempt, WebhookAttemptData $data ): WebhookAttempt {
        return WebhookAttempt::create([
            'delivery_id' => $delivery->id,
            'attempt_number' => $attempt,
            'started_at' => $data->startedAt,
            'duration_ms' => $data->durationMs,
            'request_body' => $delivery->payload,
            'request_headers' => $data->requestHeaders,
            'response_code' => $data->responseCode,
            'response_headers' => $data->responseHeaders,
            'response_body' => $data->responseBody ? mb_substr($data->responseBody, 0, 2000) : null,
            'error_message' => $data->errorMessage,
            'success' => $data->success,
        ]);
    }
}

class SendWebhookJob implements ShouldQueue {
    public function handle( WebhookAttemptLogger $logger ): void {
        $startedAt = now();
        $requestHeaders = $this->buildHeaders();
        try {
            $response = Http::timeout(15)
                ->withHeaders($requestHeaders)
                ->post($this->delivery->subscription->endpoint_url, $this->delivery->payload);
            $durationMs = (int)(microtime(true) * 1000 - $startedAt->timestamp * 1000);
            $logger->log($this->delivery, $this->delivery->attempt_count, new WebhookAttemptData(
                startedAt: $startedAt,
                durationMs: $durationMs,
                requestHeaders: $requestHeaders,
                responseCode: $response->status(),
                responseHeaders: $response->headers(),
                responseBody: $response->body(),
                success: $response->successful(),
            ));
            if ($response->successful()) {
                $this->delivery->markDelivered();
            } else {
                $this->delivery->scheduleRetry();
            }
        } catch (\Throwable $e) {
            $durationMs = (int)(microtime(true) * 1000 - $startedAt->timestamp * 1000);
            $logger->log($this->delivery, $this->delivery->attempt_count, new WebhookAttemptData(
                startedAt: $startedAt,
                durationMs: $durationMs,
                requestHeaders: $requestHeaders,
                errorMessage: get_class($e) . ': ' . $e->getMessage(),
                success: false,
            ));
            $this->delivery->scheduleRetry();
        }
    }
}

Как организована ручная повторная отправка?

Администратор или разработчик должны уметь переотправить любое событие без изменения кода. Это критично при отладке интеграций и восстановлении после сбоев.

class WebhookDeliveryController extends Controller {
    // Повторить конкретную доставку
    public function resend(WebhookDelivery $delivery): JsonResponse {
        abort_if(
            $delivery->status === 'delivered',
            422,
            'Delivery already succeeded'
        );

        $delivery->update([
            'status' => 'pending',
            'attempt_count' => 0,
            'next_attempt_at' => now(),
        ]);

        SendWebhookJob::dispatch($delivery);

        return response()->json(['queued' => true]);
    }

    // Повторить все упавшие доставки подписки
    public function resendFailed(WebhookSubscription $subscription): JsonResponse {
        $count = WebhookDelivery::where('subscription_id', $subscription->id)
            ->where('status', 'failed')
            ->count();

        WebhookDelivery::where('subscription_id', $subscription->id)
            ->where('status', 'failed')
            ->update([
                'status' => 'pending',
                'attempt_count' => 0,
                'next_attempt_at' => now(),
            ]);

        WebhookDelivery::where('subscription_id', $subscription->id)
            ->where('status', 'pending')
            ->each(fn($d) => SendWebhookJob::dispatch($d));

        return response()->json(['requeued' => $count]);
    }

    // История попыток для конкретной доставки
    public function attempts(WebhookDelivery $delivery): JsonResponse {
        return response()->json(
            $delivery->attempts()
                ->orderBy('attempt_number')
                ->get(['attempt_number', 'started_at', 'duration_ms', 'response_code', 'response_body', 'error_message', 'success'])
        );
    }
}

Пошаговый план внедрения webhook-системы с логированием

Этап Описание Срок
Анализ текущих интеграций Определяем список событий и получателей 1 день
Проектирование схемы Таблицы webhook_subscriptions, webhook_deliveries, webhook_attempts 1 день
Реализация логирования Класс WebhookAttemptLogger и поправки в SendWebhookJob 2 дня
Настройка retry policy Интервалы, максимальное количество попыток (рекомендуем 5-8) 1 день
Создание дашборда Фильтры по статусу, типу события, дате. Агрегаты: количество за 24 часа, P95 времени доставки 2 дня
Документация API для ручной переотправки Swagger/OpenAPI 1 день
Тестирование Симуляция сбоев с помощью заглушек 1 день

Стратегия ротации логов: успешные попытки — 30 дней с телом, затем только метаданные. Неуспешные — 90 дней для аудита. Тело ответа с ошибкой — максимум 2 КБ, бинарные данные не сохраняются.

Почему наша система экономит до 80% времени на отладку?

Обычный лог-файл не даёт контекста: вы видите ошибку, но не знаете, что было до неё. Наша система хранит полную хронологию каждой доставки, связывая все попытки. Это сокращает время расследования с часов до минут. Встроенные агрегаты (среднее число попыток, P95 доставки) позволяют заранее выявлять проблемные интеграции. Сравните: без логирования — ручной поиск по логам сервера, догадки, перезапуск интеграций. С нашей системой — открыл дашборд, отфильтровал по статусу, увидел историю каждой попытки. Нажал «переотправить» — и сбойные события ушли заново. Мы реализуем это на Laravel очередях с PostgreSQL. Результат — экономия до 80% времени на отладку.

Что входит в работу и сроки

  • Разработка модуля логирования попыток и повторной отправки.
  • Настройка очередей и retry policy.
  • Создание дашборда с фильтрацией и агрегатами.
  • Документация API и схемы данных.
  • Обучение команды работе с системой.
  • Техническая поддержка в течение месяца.

Сроки: система логирования попыток и ручная повторная отправка — от 3 до 5 дней. С дашбордом, агрегатами, фильтрацией и retention policy — от 1 до 1.5 недель.

Закажите систему под ключ — свяжитесь с нами для точной оценки вашего проекта. Получите консультацию по внедрению.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060