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

Получение событий вебхуков

LTESocks отправляет события на настроенный endpoint вебхука HTTP-запросом POST с JSON-телом. Вебхуки позволяют реагировать на изменения порта и подключения без постоянного опроса API.

Создайте новый вебхук в пользовательском кабинете LTESocks. Укажите конечный публичный HTTPS endpoint для приема событий, затем сохраните signing secret в серверном хранилище секретов. Не передавайте секрет в браузерный код, логи или клиентские приложения.

Если кабинет позволяет, отправьте тестовое событие; иначе проверьте следующую реальную доставку до использования интеграции. Для отслеживания завершения смены IP подпишитесь на port.ip_changed и следуйте разделу Сброс и готовность порта.

Каждая доставка имеет следующий вид:

POST /your-webhook-path HTTP/1.1
Content-Type: application/json
X-Signature: BASE64_HMAC_SHA256

Подпись рассчитывается по точному телу запроса. Bearer-токен с вебхуком не передается.

Все события используют версионированную оболочку:

{
"id": "66c5f99e2f66247a9284620f",
"version": "v1",
"type": "port.ip_changed",
"occurred_at": "2026-08-26T09:15:37.123Z",
"data": {
"port": {
"id": "66bca5147010246361dbe256",
"service_id": "service-123",
"number": 1042,
"status": "active"
},
"modem": {
"id": "66bca4d67010246361dbe213",
"name": "modem-kyiv-01"
},
"old_ip": "198.51.100.10",
"new_ip": "203.0.113.24"
}
}
ПолеТипЗначение
idstringУникальный идентификатор события. Используйте его как ключ идемпотентности.
versionstringВерсия контракта. Текущее значение — v1.
typestringТип события.
occurred_atstringВремя события в UTC в формате RFC 3339.
data.portobjectПорт, связанный с событием. Присутствует всегда.
data.modemobjectДанные модема для событий, связанных с модемом.
data.old_ipstringПредыдущий IP-адрес для port.ip_changed.
data.new_ipstringНовый IP-адрес для port.ip_changed.

Объект port содержит id, service_id, number и status. Объект modem содержит id и name; событие отключения модема также содержит last_seen_at.

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

Отправляется, когда срок действия порта истек:

{
"id": "66c5f99e2f66247a92846210",
"version": "v1",
"type": "port.expired",
"occurred_at": "2026-08-26T09:20:00Z",
"data": {
"port": {
"id": "66bca5147010246361dbe256",
"service_id": "service-123",
"number": 1042,
"status": "disabled"
}
}
}

Отправляется при смене публичного IP-адреса модема, обслуживающего порт. В событии присутствуют modem, old_ip и new_ip:

{
"id": "66c5f99e2f66247a92846211",
"version": "v1",
"type": "port.ip_changed",
"occurred_at": "2026-08-26T09:21:00Z",
"data": {
"port": {
"id": "66bca5147010246361dbe256",
"service_id": "service-123",
"number": 1042,
"status": "active"
},
"modem": {
"id": "66bca4d67010246361dbe213",
"name": "modem-kyiv-01"
},
"old_ip": "198.51.100.10",
"new_ip": "203.0.113.24"
}
}

Отправляется при отключении модема, обслуживающего порт. last_seen_at содержит время последней зафиксированной активности модема:

{
"id": "66c5f99e2f66247a92846212",
"version": "v1",
"type": "port.modem_disconnected",
"occurred_at": "2026-08-26T09:22:00Z",
"data": {
"port": {
"id": "66bca5147010246361dbe256",
"service_id": "service-123",
"number": 1042,
"status": "disconnected"
},
"modem": {
"id": "66bca4d67010246361dbe213",
"name": "modem-kyiv-01",
"last_seen_at": "2026-08-26T09:21:45Z"
}
}
}

LTESocks вычисляет подпись по формуле:

Base64(HMAC-SHA256(webhook_secret, raw_request_body))

Результат передается в X-Signature. Храните секрет whsec_... только на сервере и сравнивайте подписи за постоянное время.

import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWebhook(rawBody, receivedSignature, secret) {
const expected = createHmac('sha256', secret)
.update(rawBody)
.digest('base64');
const received = Buffer.from(receivedSignature ?? '', 'base64');
const calculated = Buffer.from(expected, 'base64');
return (
received.length === calculated.length &&
timingSafeEqual(received, calculated)
);
}
<?php
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$expected = base64_encode(hash_hmac('sha256', $rawBody, $webhookSecret, true));
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit;
}

Официальные библиотеки сравнивают подпись за постоянное время и декодируют JSON только после успешной проверки:

import { WebhookVerifier } from '@ltesocks/sdk';
const event = await WebhookVerifier.decodeVerifiedJson(
rawRequestBody,
request.headers.get('x-signature') ?? '',
process.env.LTESOCKS_WEBHOOK_SECRET,
);
  1. Сохраните исходное тело и проверьте X-Signature.
  2. Отклоните неподдерживаемое значение version.
  3. Сохраните id до выполнения побочных эффектов.
  4. Если id уже обработан, верните 2xx без повторения действия.
  5. Поставьте событие в очередь для асинхронной обработки.
  6. Быстро верните ответ 2xx.

LTESocks считает любой ответ 2xx успешным. Тело ответа игнорируется. Перенаправления не выполняются.

Доставка может повторяться после ошибок DNS или сети, тайм-аутов, ответов HTTP 408, HTTP 429 и 5xx. Перенаправления, ошибки проверки TLS, нарушения политики endpoint и остальные ответы 4xx не повторяются.

Политика доставки по умолчанию:

НастройкаЗначение
Тайм-аут чтения5 секунд
Тайм-аут записи и подключения5 секунд
Повторы после первого запроса3
Пауза между попытками10 секунд

При настройках по умолчанию одно событие может быть доставлено до четырех раз. Во всех повторах используются одинаковые тело, id события и подпись, поэтому обработчик должен быть идемпотентным.

  • Используйте URL с протоколом http или https; рекомендуется HTTPS с TLS 1.2 или новее.
  • Имя хоста должно разрешаться только в публичные IP-адреса.
  • Localhost, приватные, link-local, multicast и зарезервированные адреса отклоняются.
  • Учетные данные в URL и фрагменты запрещены.
  • Не полагайтесь на перенаправления: сразу укажите конечный URL.