Tổng Kho eSIM
Исходящие вебхуки · событий: 8

Вебхуки

Система отправляет события на URL, зарегистрированные через POST /v1/webhooks (обновление — PUT /v1/webhooks/{id}; при создании обработчик должен ответить 2xx на CHECK_HEALTH). Вебхуки подписываются отдельным секретом, независимым от секрета API-ключа.

Вебхуки — удобный канал уведомлений. Источник достоверного состояния — REST: даже если обработчик пропустит событие, GET /v1/orders?updated_since=… позволяет восстановить актуальное состояние. Учитывайте это при проектировании.

webhook-events.ru.json

Каталог событий

event_typeКогда возникаетОсновные поля данных
order.completedЗаказ оформлен, eSIM готовы
order_idexternal_refitems[] (order_item_id, iccid)
order.failedЗаказ не завершён (ошибка выдачи и т. п.)
order_idexternal_refreason
esim.deliveredВыдана eSIM с данными установки

Связь с заказом по `order_id` и `external_ref`. `lpa` расшифровывается непосредственно перед POST и не хранится открытым текстом в доставках. URL изображения QR не передаётся: создайте QR из `lpa` или получите `qr_code_base64` через `GET /v1/esims/{id}`.

order_item_idorder_idexternal_reficcidlpa
esim.activatedeSIM установлена и активирована (smdp enabled/in_use)

Используйте `order_id` и `external_ref` (ваша ссылка, переданная при покупке) для связи eSIM с вашим заказом.

order_item_idorder_idexternal_reficcidactivated_at
esim.usage_thresholdРасход достиг порога 75% / 90%

Свяжите eSIM с заказом через `order_id` и `external_ref`. Передаётся только наибольший новый пройденный порог: рост 0→95% вызывает только 90. `threshold` означает, что превышен как минимум этот уровень.

order_item_idorder_idexternal_refthreshold (75/90)used_bytesdata_bytes
esim.expiringЗа 3 дня / 1 день до истечения срока

Используйте `order_id` и `external_ref` (ваша ссылка, переданная при покупке) для связи eSIM с вашим заказом.

order_item_idorder_idexternal_refdays_remaining (3/1)expires_at
wallet.low_balanceБаланс кошелька достиг порога предупреждения 25% / 10%

`threshold` — процент (25/10), передаётся только при известном значении; иначе поле отсутствует (сравнивайте `balance` самостоятельно). Сумма VND никогда не передаётся в `threshold`.

threshold (25/10)balancecurrency
catalog.updatedИзменение каталога: тариф добавлен, изменён, скрыт, изменена базовая цена или закончился запас

Цены не передаются, поскольку они зависят от организации. Получите ваши цены через `GET /v1/catalog/packages`. Изменения за короткое окно (по умолчанию 10 минут) объединяются в один вебхук `changes[]`; каждый тариф появляется один раз. Синхронизация сотен тарифов не создаёт сотни вебхуков. Действует в тестовом и рабочем режимах.

changes[] (package_code, change: price|availability|package, occurred_at)change_count
json
{
  "event_id": "018f3c00-0000-7000-8000-0000000000c1",
  "event_type": "ping",
  "occurred_at": "2026-07-11T07:29:00Z",
  "is_testmode": true,
  "data": {
    "message": "TongKho eSIM webhook health check"
  }
}

Формат вебхука и заголовки подписи

Все вебхуки используют единый формат. Подпись `HMAC-SHA256(webhook_secret, timestamp\nraw_body)` в `X-TKE-Webhook-Signature` (hex в нижнем регистре). В течение 24 часов смены секрета также передаётся `X-TKE-Webhook-Signature-Old`. Доставка как минимум один раз; удаляйте дубли по `event_id`.

json
{
  "event_id": "018f3c00-0000-7000-8000-0000000000b0",
  "event_type": "order.completed",
  "occurred_at": "2026-07-11T07:30:00Z",
  "is_testmode": false,
  "data": {
    "order_id": "018f2b00-0000-7000-8000-00000000009a",
    "external_ref": "partner-cart-9931",
    "items": [
      {
        "order_item_id": "018f2b00-0000-7000-8000-000000000001",
        "iccid": "8984012345678901234"
      }
    ]
  }
}
ПолеОписание
dataСодержимое зависит от типа события (см. ниже).
event_idПостоянный UUID, уникальный для события (защита от дублей).
event_typeТип события.
is_testmodetrue, если событие создано тестовым ключом.
occurred_atISO-8601 UTC.

Синхронизация каталога: catalog.updated

Возникает при добавлении, изменении или скрытии тарифов, смене базовой цены или исчерпании запаса. Данные содержат объединённый массив changes[] : сотни изменений тарифов означают одну доставку на каждый URL.

json
{
  "event_id": "018f3c00-0000-7000-8000-0000000000c1",
  "event_type": "catalog.updated",
  "occurred_at": "2026-07-11T08:10:00Z",
  "is_testmode": false,
  "data": {
    "changes": [
      {
        "package_code": "THAY-BANG-MA-GOI-CUA-BAN",
        "change": "price",
        "occurred_at": "2026-07-11T08:02:11Z"
      },
      {
        "package_code": "VN_10GB_30D",
        "change": "availability",
        "occurred_at": "2026-07-11T08:04:52Z"
      },
      {
        "package_code": "JP_3GB_7D",
        "change": "package",
        "occurred_at": "2026-07-11T08:09:30Z"
      }
    ],
    "change_count": 3
  }
}
Отсутствие цены в данных предусмотрено

Цены продажи зависят от организации . Единая цена была бы неверной и раскрывала бы конфиденциальные данные. Это событие служит сигналом : вызовите GET /v1/catalog/packages?market=… для обновления цен.

change принимает три значения: price (изменение базовой цены), availability (наличие или отсутствие запаса), package (добавлен / обновлён / скрыт). Неожиданное значение также считайте сигналом синхронизации.

Подпись, повторы, задержки и защита от дублей по event_id работают как у обычных событий в тестовом и рабочем режимах с разделением по is_testmode.

Проверка подписи вебхука

Каждый исходящий вебхук содержит X-TKE-Webhook-Signature, вычисленный HMAC-SHA256 с webhook_secret по исходным байтам тела запроса.

text
signing_string = timestamp + "\n" + raw_body   # timestamp = header X-TKE-Webhook-Timestamp
signature      = HMAC_SHA256(webhook_secret, signing_string)   → hex lowercase

# Проверяйте свежесть времени (±300 с) для защиты от повторного воспроизведения.
# Подписывайте точные полученные байты. НЕ сериализуйте JSON повторно.

Интервалы повторной доставки

1 phút→5 phút→30 phút→2 giờ→6 giờ

Если обработчик вернул не 2xx или не ответил за 10 секунд, система повторяет доставку с растущей задержкой в течение 24 giờ: 1 phút → 5 phút → 30 phút → 2 giờ → 6 giờ.

4 важных правила работы с вебхуками

1. Асинхронная обработка и быстрый 200 OK
Сразу помещайте данные в очередь и возвращайте HTTP 200 OK менее чем за секунду. Не выполняйте тяжёлую работу синхронно: тайм-аут запроса — 10 секунд.
2. Идемпотентность и защита от дублей
Доставка гарантируется как минимум один раз. Используйте event_id или data.order_id / data.order_item_id для удаления дублей и идемпотентной обработки.
3. Отдельные URL рабочего и тестового режимов
У каждого API-ключа свои настройки вебхуков. Разделяйте URL обработчиков рабочего и тестового ключей, чтобы тестовые данные не попадали в рабочую базу.
4. Периодическая сверка запросами
При длительных сбоях сети вебхуки могут быть окончательно потеряны. Периодически вызывайте GET /v1/orders?updated_since=… для поиска пропущенных изменений.

Особенности полей и поведения

esim.delivered.lpa расшифровывается перед отправкой POST и не хранится открытым текстом. URL изображения QR не передаётся: создайте QR из LPA или получите через GET /v1/esims/{id}.

esim.usage_threshold: передаёт только наибольший пройденный порог: при росте 0→95% приходит только threshold=90.

wallet.low_balance.threshold является процентом (25/10), а не суммой VND.

catalog.updated не содержит цен и подробного запаса, только код тарифа и тип изменения.