Документация Вебхуки и обратные вызовы

Вебхуки и обратные вызовы

Что такое вебхуки?

В реальном мире API не всегда отвечают мгновенно. Когда вы совершаете платёж, платёжная система сразу возвращает «принято», но фактический результат (успех или неудача) приходит позже через вебхук – HTTP-запрос от сервера к ВАШЕМУ приложению.

Аналогия из жизни: Заказ доставки еды. Приложение сразу подтверждает «Заказ оформлен!» (ответ API). Но через 30 минут курьер звонит в дверь (вебхук), сообщая, что еда доставлена. Обратные вызовы Mockarty позволяют имитировать обе части этого процесса.

Mockarty может автоматически отправлять HTTP-запросы, сообщения в Kafka или RabbitMQ при каждом срабатывании мока. Это позволяет имитировать асинхронные системы уведомлений, тестировать приёмники вебхуков и выстраивать цепочки взаимодействия между сервисами — без написания кода.

Совет: Примеры кода доступны для cURL, CLI и SDK-клиентов (Go, Python, Java). Смотрите Руководство по SDK для установки и настройки. Смотрите Руководство по CLI для командной утилиты.

URL-адреса в примерах: Во всех примерах используется localhost:5770 как адрес Mockarty по умолчанию. Если ваш экземпляр работает на удалённом сервере, замените localhost:5770 на реальный адрес (например, https://mockarty.company.com или http://192.168.1.50:5770). Подробнее — в разделе Полезные функции и советы.

Приложение POST /api/payments 1. запрос Mockarty Мок найден ответ + триггер webhooks[] 2. ответ 3. вызов HTTP Webhook-приёмник Kafka Сообщение в топик RabbitMQ Очередь / Exchange on_success on_error always

Режимы триггера

$.req.* | $.fake.* | $.gS.* Шаблонизатор в body/headers retryCount + retryDelay Авто-повтор при ошибке sync / async Блокирующий или фоновый

Содержание

  1. Практическая ценность
  2. Типы обратных вызовов
  3. Режимы срабатывания
  4. Конфигурация HTTP-вебхука
  5. Конфигурация обратного вызова Kafka
  6. Конфигурация обратного вызова RabbitMQ
  7. Шаблонные переменные в обратных вызовах
  8. Политика повторных попыток
  9. Синхронное и асинхронное выполнение
  10. Вебхуки пространства имён
  11. Административные вебхуки
  12. Примеры
  13. Мониторинг и отладка

Практическая ценность

Реальные API редко работают изолированно. Платёжный шлюз уведомляет ваше приложение через вебхук. Сервис заказов публикует событие в Kafka. Сервис доставки отправляет сообщение в RabbitMQ. Обратные вызовы Mockarty позволяют воспроизвести эти паттерны в тестовом окружении:

  • Имитация асинхронных уведомлений — мок платёжного эндпоинта Stripe отправляет вебхук payment_intent.succeeded в ваше приложение.
  • Тестирование приёмников вебхуков — проверка корректной обработки повторных доставок, дублей и ошибочных данных.
  • Цепочки сервисов — мок A вызывает мок B через вебхук, создавая многошаговые интеграционные тестовые сценарии без реальной инфраструктуры.

Типы обратных вызовов

Каждый обратный вызов в массиве webhooks имеет поле type:

Тип Описание По умолчанию
http HTTP-запрос на внешний URL Да (если type не указан)
kafka Публикация сообщения в топик Kafka Нет
rabbitmq Публикация сообщения в exchange/очередь RabbitMQ Нет

Режимы срабатывания

Поле trigger определяет, когда обратный вызов срабатывает, в зависимости от кода ответа мока:

Триггер Срабатывает когда Коды ответа
on_success Ответ успешный 200–299
on_error Ответ содержит ошибку 400+
always При каждом совпавшем запросе Любой (по умолчанию, если не указан)
{
  "trigger": "on_success"
}

Если trigger не указан или пуст, обратный вызов срабатывает при каждом совпавшем запросе (always).


Конфигурация HTTP-вебхука

HTTP — тип обратного вызова по умолчанию. Он отправляет HTTP-запрос на указанный URL после ответа мока.

Поля

Поле Тип Обязательное По умолчанию Описание
type string Нет "http" Тип обратного вызова
url string Да Целевой URL
method string Нет "POST" HTTP-метод: POST, PUT, PATCH, DELETE, GET
headers object Нет {} Пользовательские HTTP-заголовки
body any Нет null Тело запроса (поддерживает шаблоны)
timeout int Нет 30 Таймаут в секундах
retryCount int Нет 0 Количество повторных попыток при ошибке
retryDelay int Нет 1 Задержка между повторными попытками в секундах
async bool Нет false Асинхронное выполнение (неблокирующее)
trigger string Нет "always" Когда срабатывать: on_success, on_error, always

Минимальный пример

cURL

curl -X POST http://localhost:5770/api/v1/mocks \
  -H "Content-Type: application/json" \
  -d '{
    "id": "payment-with-webhook",
    "http": {
      "route": "/api/payments",
      "httpMethod": "POST"
    },
    "response": {
      "statusCode": 200,
      "payload": {
        "id": "$.fake.UUID",
        "status": "succeeded"
      }
    },
    "webhooks": [
      {
        "url": "http://my-app:3000/webhooks/stripe",
        "method": "POST",
        "headers": {
          "X-Stripe-Signature": "t=123456,v1=abc123"
        },
        "body": {
          "type": "payment_intent.succeeded",
          "data": {
            "amount": 2500,
            "currency": "usd"
          }
        },
        "trigger": "on_success"
      }
    ]
  }'

CLI

# Создание мока с вебхуком
mockarty-cli mock create --id payment-with-webhook \
  --route /api/payments --method POST \
  --status 200 \
  --payload '{"id":"$.fake.UUID","status":"succeeded"}' \
  --webhook-url http://my-app:3000/webhooks/stripe \
  --webhook-trigger on_success \
  --webhook-body '{"type":"payment_intent.succeeded","data":{"amount":2500,"currency":"usd"}}'

Go

// Создание мока с вебхуком
err := client.Mocks().Create(ctx, &mockarty.Mock{
    ID: "payment-with-webhook",
    HTTP: &mockarty.HTTPConfig{
        Route:      "/api/payments",
        HTTPMethod: "POST",
    },
    Response: &mockarty.Response{
        StatusCode: 200,
        Payload: map[string]any{
            "id": "$.fake.UUID", "status": "succeeded",
        },
    },
    Webhooks: []mockarty.Webhook{{
        URL:     "http://my-app:3000/webhooks/stripe",
        Method:  "POST",
        Headers: map[string]string{"X-Stripe-Signature": "t=123456,v1=abc123"},
        Body: map[string]any{
            "type": "payment_intent.succeeded",
            "data": map[string]any{"amount": 2500, "currency": "usd"},
        },
        Trigger: "on_success",
    }},
})

Python

# Создание мока с вебхуком
client.mocks.create({
    "id": "payment-with-webhook",
    "http": {"route": "/api/payments", "httpMethod": "POST"},
    "response": {
        "statusCode": 200,
        "payload": {"id": "$.fake.UUID", "status": "succeeded"},
    },
    "webhooks": [{
        "url": "http://my-app:3000/webhooks/stripe",
        "method": "POST",
        "headers": {"X-Stripe-Signature": "t=123456,v1=abc123"},
        "body": {"type": "payment_intent.succeeded", "data": {"amount": 2500, "currency": "usd"}},
        "trigger": "on_success",
    }],
})

Java

// Создание мока с вебхуком
client.mocks().create(Mock.builder()
    .id("payment-with-webhook")
    .http(HttpConfig.builder().route("/api/payments").httpMethod("POST").build())
    .response(Response.builder().statusCode(200)
        .payload(Map.of("id", "$.fake.UUID", "status", "succeeded")).build())
    .webhooks(List.of(Webhook.builder()
        .url("http://my-app:3000/webhooks/stripe")
        .method("POST")
        .trigger("on_success")
        .body(Map.of("type", "payment_intent.succeeded",
            "data", Map.of("amount", 2500, "currency", "usd")))
        .build()))
    .build());

Конфигурация обратного вызова Kafka

Публикация сообщения в топик Kafka при срабатывании мока.

Поля

Поле Тип Обязательное По умолчанию Описание
type string Да Должен быть "kafka"
kafkaBrokers string Да Адреса брокеров через запятую (например, "kafka1:9092,kafka2:9092")
kafkaTopic string Да Имя целевого топика
kafkaKey string Нет "" Ключ сообщения (поддерживает шаблоны)
kafkaUseSASL bool Нет false Включить аутентификацию SASL/PLAIN
kafkaUsername string Нет "" Имя пользователя SASL (обязательно, если kafkaUseSASL = true)
kafkaPassword string Нет "" Пароль SASL
kafkaUseTLS bool Нет false Включить TLS (минимум TLS 1.2)
headers object Нет {} Заголовки сообщения Kafka (пары ключ-значение)
body any Нет null Значение сообщения (поддерживает шаблоны)
timeout int Нет 30 Таймаут записи в секундах
retryCount int Нет 0 Количество повторных попыток
retryDelay int Нет 1 Задержка между повторными попытками в секундах
trigger string Нет "always" Когда срабатывать

Пример

curl -X POST http://localhost:5770/api/v1/mocks \
  -H "Content-Type: application/json" \
  -d '{
    "id": "order-created-kafka",
    "http": {
      "route": "/api/orders",
      "httpMethod": "POST"
    },
    "response": {
      "statusCode": 201,
      "payload": {
        "orderId": "$.fake.UUID",
        "status": "created"
      }
    },
    "webhooks": [
      {
        "type": "kafka",
        "kafkaBrokers": "localhost:9092",
        "kafkaTopic": "orders.created",
        "kafkaKey": "$.req.customerId",
        "body": {
          "event": "order.created",
          "orderId": "$.fake.UUID",
          "timestamp": "$.fake.UnixTime"
        },
        "trigger": "on_success"
      }
    ]
  }'

Kafka с SASL/TLS

{
  "type": "kafka",
  "kafkaBrokers": "kafka-prod.example.com:9093",
  "kafkaTopic": "events",
  "kafkaUseSASL": true,
  "kafkaUsername": "my-service",
  "kafkaPassword": "secret",
  "kafkaUseTLS": true,
  "body": {"event": "test"},
  "trigger": "always"
}

Примечание: Заголовок Content-Type автоматически исключается из заголовков сообщения Kafka. Все остальные записи в headers передаются как заголовки сообщения Kafka.


Конфигурация обратного вызова RabbitMQ

Публикация сообщения в exchange или очередь RabbitMQ при срабатывании мока.

Поля

Поле Тип Обязательное По умолчанию Описание
type string Да Должен быть "rabbitmq"
rabbitURL string Да URI подключения AMQP (например, "amqp://guest:guest@localhost:5672/")
rabbitExchange string Нет "" Имя exchange (пусто = exchange по умолчанию)
rabbitRoutingKey string Нет "" Ключ маршрутизации
rabbitQueue string Нет "" Имя очереди (используется как ключ маршрутизации, если exchange и routingKey пусты)
rabbitMandatory bool Нет false Флаг mandatory для публикации
headers object Нет {} Заголовки AMQP-сообщения
body any Нет null Тело сообщения (поддерживает шаблоны)
timeout int Нет 30 Таймаут подключения в секундах
retryCount int Нет 0 Количество повторных попыток
retryDelay int Нет 1 Задержка между повторными попытками в секундах
trigger string Нет "always" Когда срабатывать

Пример — прямая очередь

curl -X POST http://localhost:5770/api/v1/mocks \
  -H "Content-Type: application/json" \
  -d '{
    "id": "notification-rabbitmq",
    "http": {
      "route": "/api/notifications/send",
      "httpMethod": "POST"
    },
    "response": {
      "statusCode": 200,
      "payload": {"status": "queued"}
    },
    "webhooks": [
      {
        "type": "rabbitmq",
        "rabbitURL": "amqp://guest:guest@localhost:5672/",
        "rabbitQueue": "notifications",
        "body": {
          "type": "email",
          "to": "$.req.email",
          "subject": "Your order has been placed"
        },
        "trigger": "on_success"
      }
    ]
  }'

Пример — exchange с ключом маршрутизации

{
  "type": "rabbitmq",
  "rabbitURL": "amqp://guest:guest@localhost:5672/",
  "rabbitExchange": "events",
  "rabbitRoutingKey": "order.created",
  "body": {
    "orderId": "$.fake.UUID"
  }
}

Примечание: Если rabbitExchange и rabbitRoutingKey пусты, но задан rabbitQueue, имя очереди используется как ключ маршрутизации с exchange по умолчанию. Content-Type AMQP-сообщения по умолчанию application/json, если не переопределён в headers.


Шаблонные переменные в обратных вызовах

Поля body и headers обратных вызовов поддерживают тот же шаблонизатор, что и ответы моков. Доступные возможности:

JsonPath — извлечение данных из исходного запроса (полное руководство)

{
  "body": {
    "userId": "$.req.userId",
    "email": "$.req.email",
    "authToken": "$.reqHeader.Authorization[0]"
  }
}

Faker — генерация динамических данных (полный справочник)

{
  "body": {
    "webhookId": "$.fake.UUID",
    "timestamp": "$.fake.UnixTime",
    "ip": "$.fake.IPv4"
  }
}

Значения из хранилищ — доступ к Global/Chain/Mock Store (документация)

{
  "body": {
    "sessionId": "$.gS.currentSession",
    "orderId": "$.cS.orderId",
    "tempValue": "$.mS.computed"
  }
}

Строковое тело с шаблонами

Вместо объекта можно использовать строковое тело. Если оно содержит выражения $., они будут обработаны:

{
  "body": "Order $.req.orderId has been processed at $.fake.Date"
}

Данные ответа

Доступ к полям собственного ответа мока:

{
  "body": {
    "mockResponseStatus": "$.response.status"
  }
}

Примечание: $.response ссылается на объект payload ответа мока. Например, если ваш мок возвращает {"status": "ok", "data": {"id": 1}}, то $.response.status разрешится в "ok", а $.response.data.id — в 1.


Политика повторных попыток

Все типы обратных вызовов поддерживают автоматические повторные попытки при ошибке.

Поле По умолчанию Описание
retryCount 0 Количество дополнительных попыток после первой ошибки (0 = без повторов)
retryDelay 1 Задержка между попытками в секундах

Повторные попытки используют фиксированную задержку (не экспоненциальный откат). Если контекст отменяется во время задержки повторной попытки, обратный вызов немедленно прекращается.

{
  "url": "http://unreliable-service.example.com/webhook",
  "retryCount": 3,
  "retryDelay": 2,
  "timeout": 10
}

В этом примере выполняется до 4 запросов (1 начальный + 3 повтора) с задержкой 2 секунды между попытками и таймаутом 10 секунд на каждый запрос.


Синхронное и асинхронное выполнение

Режим async Поведение
Асинхронный true (по умолчанию) Обратный вызов выполняется в фоновой горутине. Ответ мока возвращается немедленно.
Синхронный false Обратный вызов выполняется до возврата ответа мока вызывающей стороне. Полезно, когда вызывающей стороне важно знать, что обратный вызов завершён.
{
  "url": "http://my-app.com/webhook",
  "async": true,
  "trigger": "on_success"
}

Внимание: Синхронные обратные вызовы ("async": false) с большими значениями timeout или retryCount задерживают ответ мока клиенту.


Сетевая безопасность и защита от SSRF

По умолчанию Mockarty блокирует исходящие webhook-вызовы к private, loopback, link-local и cloud-metadata IP-адресам. Это защита от SSRF — без неё вредоносная конфигурация мока могла бы заставить сервер Mockarty сканировать собственную внутреннюю сеть.

Блокируемые диапазоны (по умолчанию):

  • 127.0.0.0/8 (loopback)
  • 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (private IPv4)
  • 169.254.0.0/16 (link-local / cloud metadata, например 169.254.169.254)
  • IPv6-эквиваленты (::1, fc00::/7, fe80::/10)

Переменные окружения (задаются на resolver; а также на административной ноде, если она сама обслуживает моки):

Переменная По умолчанию Описание
ALLOW_PROXY_TO_PRIVATE_IPS false Когда false, защита от SSRF активна и webhook-вызовы к перечисленным диапазонам отклоняются. Устанавливайте true ТОЛЬКО в изолированных dev/CI-окружениях, где целевые сервисы легитимно находятся на private IP
WEBHOOK_POOL_SIZE 10 Максимальное число одновременных исходящих webhook-воркеров на resolver / на административную ноду. Это ограничение пропускной способности на уровне ноды, а не на уровне мока

Правило для продакшена. В продакшене оставляйте ALLOW_PROXY_TO_PRIVATE_IPS=false. Если нужно вызвать внутренний сервис, опубликуйте его через reverse proxy с публичным DNS-именем или явный allow-list, а не снимайте SSRF-защиту глобально.

О примере Stripe-Signature ниже. Пример в разделе «Примеры» использует литеральную строку v1=test_signature. Mockarty НЕ вычисляет реальные HMAC-подписи — если вам нужна валидная подпись, сгенерируйте её в клиентском коде до вызова мока и передайте через тело запроса или через значение в Store. Шаблонизация выполняет только подстановку строк.


Вебхуки пространства имён

Вебхуки пространства имён срабатывают при CRUD-операциях с моками внутри пространства имён. Они настраиваются отдельно от обратных вызовов конкретных моков.

Поддерживаемые операции

Операция Срабатывает когда
mock.create Мок создан в пространстве имён
mock.update Мок обновлён
mock.delete Мок удалён
mock.restore Мок восстановлен из мягкого удаления
user.add_to_namespace Пользователь добавлен в пространство имён

API-эндпоинты

# Список вебхуков пространства имён
GET /api/v1/namespaces/{namespace}/webhooks

# Создание/обновление вебхука пространства имён
POST /api/v1/namespaces/{namespace}/webhooks

# Удаление вебхука пространства имён
DELETE /api/v1/namespaces/{namespace}/webhooks/{id}

Пример — уведомление в Slack при изменении моков

curl -X POST http://localhost:5770/api/v1/namespaces/production/webhooks \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "operation": "mock.create",
    "url": "https://hooks.slack.com/services/T.../B.../xxx",
    "method": "POST",
    "body": {
      "text": "New mock created in production namespace"
    },
    "enabled": true,
    "retryCount": 2,
    "retryDelay": 3
  }'

Административные вебхуки

Административные вебхуки срабатывают при системных операциях. Только администраторы могут их настраивать.

Поддерживаемые операции

Операция Срабатывает когда
namespace.create Создано новое пространство имён
namespace.delete Пространство имён удалено
namespace.update Пространство имён обновлено
namespace.user.add Пользователь добавлен в пространство имён
user.create Создан новый пользователь
user.delete Пользователь удалён
user.update Пользователь обновлён
user.password.set Пароль пользователя изменён
job.run Запущена фоновая задача
backup.create Создана резервная копия
backup.restore Восстановлена резервная копия
license.create Создана лицензия
license.update Лицензия обновлена
license.delete Лицензия удалена

API-эндпоинты

# Список административных вебхуков
GET /api/v1/admin/webhooks

# Создание/обновление административного вебхука
POST /api/v1/admin/webhooks

# Удаление административного вебхука
DELETE /api/v1/admin/webhooks/{id}

Пример — аудит-лог при создании пользователя

curl -X POST http://localhost:5770/api/v1/admin/webhooks \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <admin-token>" \
  -d '{
    "operation": "user.create",
    "url": "https://audit.internal.com/events",
    "method": "POST",
    "body": {
      "event": "user.created",
      "source": "mockarty"
    },
    "enabled": true
  }'

Примеры

1. Имитация вебхука Stripe при платёжном моке

Когда ваше приложение вызывает POST /api/payments, Mockarty возвращает успешный ответ и отправляет вебхук на Stripe-слушатель вашего приложения:

curl -X POST http://localhost:5770/api/v1/mocks \
  -H "Content-Type: application/json" \
  -d '{
    "id": "stripe-payment",
    "http": {
      "route": "/api/payments/intents",
      "httpMethod": "POST"
    },
    "response": {
      "statusCode": 200,
      "payload": {
        "id": "pi_$.fake.UUID",
        "status": "succeeded",
        "amount": "$.req.amount"
      }
    },
    "webhooks": [
      {
        "url": "http://my-app:3000/webhooks/stripe",
        "method": "POST",
        "headers": {
          "Stripe-Signature": "t=$.fake.UnixTime,v1=test_signature",
          "Content-Type": "application/json"
        },
        "body": {
          "id": "evt_$.fake.UUID",
          "type": "payment_intent.succeeded",
          "data": {
            "object": {
              "id": "pi_$.fake.UUID",
              "amount": "$.req.amount",
              "currency": "$.req.currency",
              "status": "succeeded"
            }
          }
        },
        "trigger": "on_success",
        "retryCount": 3,
        "retryDelay": 5,
        "timeout": 15
      }
    ]
  }'

2. Отправка сообщения в Kafka при создании заказа

Мок API заказов, который публикует событие в Kafka для нижестоящих потребителей:

curl -X POST http://localhost:5770/api/v1/mocks \
  -H "Content-Type: application/json" \
  -d '{
    "id": "order-with-kafka-event",
    "http": {
      "route": "/api/orders",
      "httpMethod": "POST"
    },
    "response": {
      "statusCode": 201,
      "payload": {
        "orderId": "$.fake.UUID",
        "status": "created"
      }
    },
    "webhooks": [
      {
        "type": "kafka",
        "kafkaBrokers": "kafka:9092",
        "kafkaTopic": "orders.events",
        "kafkaKey": "$.req.customerId",
        "headers": {
          "event-type": "order.created",
          "source": "order-service"
        },
        "body": {
          "orderId": "$.fake.UUID",
          "customerId": "$.req.customerId",
          "items": "$.req.items",
          "total": "$.req.total",
          "createdAt": "$.fake.Date"
        },
        "trigger": "on_success"
      }
    ]
  }'

3. Цепочка: мок -> вебхук -> другой мок

Двухшаговый сценарий, где платёжный мок запускает мок уведомлений:

Шаг 1 — создание мока-приёмника уведомлений:

curl -X POST http://localhost:5770/api/v1/mocks \
  -H "Content-Type: application/json" \
  -d '{
    "id": "notification-receiver",
    "http": {
      "route": "/internal/notifications",
      "httpMethod": "POST"
    },
    "response": {
      "statusCode": 200,
      "payload": {"notified": true}
    },
    "extract": {
      "gStore": {
        "lastNotifiedOrder": "$.req.orderId"
      }
    }
  }'

Шаг 2 — создание платёжного мока с цепочкой:

curl -X POST http://localhost:5770/api/v1/mocks \
  -H "Content-Type: application/json" \
  -d '{
    "id": "payment-with-chain",
    "http": {
      "route": "/api/pay",
      "httpMethod": "POST"
    },
    "response": {
      "statusCode": 200,
      "payload": {
        "paid": true,
        "orderId": "$.req.orderId"
      }
    },
    "webhooks": [
      {
        "url": "http://localhost:5770/stubs/sandbox/internal/notifications",
        "method": "POST",
        "body": {
          "orderId": "$.req.orderId",
          "event": "payment.completed",
          "amount": "$.req.amount"
        },
        "async": false,
        "trigger": "on_success"
      }
    ]
  }'

Теперь при вызове POST /api/pay будет возвращён успешный ответ и выполнен вызов POST /stubs/sandbox/internal/notifications на том же экземпляре Mockarty с сохранением ID заказа в глобальное хранилище.

4. Вебхук при ошибке — уведомление о сбое

Обратный вызов срабатывает только при возврате ошибки моком:

curl -X POST http://localhost:5770/api/v1/mocks \
  -H "Content-Type: application/json" \
  -d '{
    "id": "flaky-service",
    "http": {
      "route": "/api/fragile",
      "httpMethod": "GET"
    },
    "response": {
      "statusCode": 503,
      "payload": {"error": "service unavailable"}
    },
    "webhooks": [
      {
        "url": "http://alerting-system:8080/alerts",
        "body": {
          "alert": "fragile-service-mock returned error",
          "statusCode": 503
        },
        "trigger": "on_error"
      }
    ]
  }'

5. Несколько обратных вызовов на одном моке

Один мок может иметь несколько обратных вызовов разных типов:

{
  "webhooks": [
    {
      "type": "http",
      "url": "http://audit.internal/log",
      "body": {"event": "order.created"},
      "trigger": "always"
    },
    {
      "type": "kafka",
      "kafkaBrokers": "kafka:9092",
      "kafkaTopic": "orders.events",
      "body": {"event": "order.created"},
      "trigger": "on_success"
    },
    {
      "type": "rabbitmq",
      "rabbitURL": "amqp://guest:guest@rabbitmq:5672/",
      "rabbitQueue": "notifications",
      "body": {"notify": true},
      "trigger": "on_success"
    }
  ]
}

Мониторинг и отладка

Логи приложения

Вся активность обратных вызовов логируется через структурированное логирование (Zap). Ключевые сообщения в логах:

Уровень Сообщение Значение
INFO Callback executed successfully Обратный вызов завершён без ошибок
WARN Callback attempt failed Одна попытка завершилась ошибкой (возможен повтор)
ERROR Callback failed after all retries Все попытки исчерпаны
ERROR Failed to prepare webhook body Ошибка рендеринга шаблона
ERROR Panic in webhook executeCallback Непредвиденная паника (включает стек-трейс)

Записи в логах содержат type обратного вызова (http/kafka/rabbitmq) и номер попытки.

Рекомендации

  1. Используйте синхронный режим для отладки — установите "async": false, чтобы обратные вызовы завершались до отправки ответа. Это делает порядок событий в логах предсказуемым.

  2. Начинайте с retryCount: 0 — добавляйте повторные попытки только после подтверждения доступности целевого сервиса.

  3. Сначала проверьте доступность — перед созданием мока убедитесь, что URL вебхука / брокер Kafka / подключение RabbitMQ доступны с хоста Mockarty:

    # Проверка HTTP-цели
    curl -v http://my-app:3000/webhooks/stripe
    
    # Проверка подключения к Kafka
    kafkacat -b kafka:9092 -L
    
    # Проверка подключения к RabbitMQ
    curl -u guest:guest http://rabbitmq:15672/api/overview
    
  4. Проверяйте шаблоны в теле обратного вызова — если тело использует выражения $.req.*, убедитесь, что исходный запрос содержит ожидаемые поля. Необработанные шаблоны отправляются как обычные строки.

  5. Настройка таймаутов — таймаут по умолчанию 30 секунд достаточно щедрый. Для локальных сервисов обычно достаточно 5–10 секунд. Для внешних сервисов за файрволами увеличивайте по необходимости.

  6. Отмена контекста — если клиент отключается до завершения асинхронного обратного вызова, обратный вызов может быть отменён во время задержки повторной попытки. Для критичных обратных вызовов используйте короткое значение retryDelay.


Частые ошибки

Наиболее распространённые проблемы при настройке обратных вызовов:

  1. Использование localhost в URL вебхука, когда Mockarty в Docker. Внутри Docker localhost указывает на сам контейнер. Используйте имя Docker-сервиса (например, http://my-app:3000/webhook) или http://host.docker.internal:3000/webhook для доступа к хост-машине.

  2. Не учли, что синхронный режим задерживает ответ. При "async": false ответ мока не возвращается вызывающей стороне до завершения обратного вызова. Если цель обратного вызова медленная или недоступная, ваш тест зависнет. По умолчанию "async": true (фоновое выполнение), поэтому устанавливайте false только когда обратный вызов должен завершиться до отправки ответа.

  3. Слишком большой retryCount без учёта таймаута. При retryCount: 5 и timeout: 30 неудачный обратный вызов будет блокировать до 150 секунд (5 x 30с). Начните с retryCount: 1 и timeout: 5 для локальных сервисов.

  4. Ожидание, что $.req.* ссылается на тело вебхука. Выражения $.req.* ссылаются на исходный запрос, который вызвал мок, а не на запрос обратного вызова. Если вам нужны данные из ответа обратного вызова, это пока не поддерживается.

  5. Путаница между вебхуками мока и пространства имён. Вебхуки уровня мока (в массиве webhooks мока) срабатывают при совпадении конкретного мока. Вебхуки пространства имён срабатывают при создании/обновлении/удалении моков через API. Они служат разным целям.

  6. Kafka: неправильный адрес брокера. Поле kafkaBrokers должно содержать адрес, видимый с хоста Mockarty. В Docker Compose используйте имя сервиса Kafka (например, kafka:9092), а не localhost:9092.


Смотрите также

  • Справочник API – полная документация REST API, включая эндпоинты вебхуков
  • Справочник Faker – все доступные функции Faker для динамических данных в обратных вызовах
  • Руководство по JsonPath – выражения JsonPath для извлечения данных запроса в обратных вызовах
  • Хранилища – системы хранилищ Global, Chain и Mock, используемые в шаблонах обратных вызовов
  • Рекордер – запись трафика и генерация моков с конфигурациями вебхуков