Перейти к основному содержанию

Вебхуки

У аккаунта один URL вебхука, и все события уходят на него. Задайте его в Dashboard → API или через Настройку вебхука.

События

Сегодня существует шесть событий.
order.updated отправляется только для заказов, созданных с API-ключом. Покупки на сайте и через Telegram-бота вебхук не порождают — при том что события Smart Mode тот же аккаунт получает как обычно.

Конверт

У каждой доставки три одинаковых ключа верхнего уровня. timestamp — момент, когда мы собрали событие, ISO-8601 в UTC.
Response
Заголовки:

order.updated

number
ID заказа.
string
energy или bandwidth.
string
processing, filled, partial, failed, reclaimed или reclaim_failed. Это поле — источник истины, см. предупреждение ниже.
string
Адрес, которому делегирован ресурс.
number
Заказанный объём — единицы энергии или пункты bandwidth.
number
То же, что volume. Присутствует, чтобы сравнивать ожидаемое с доставленным, не выясняя, какое поле что означает.
number
Сколько фактически делегировано на данный момент. Равно volume, когда заказ filled; на processing и partial — сумма уже выданных частей; 0 при отказе.
string
Цена заказа в TRX, десятичной строкой.
string | null
Транзакция, которой ресурс делегирован.
string | null
Транзакция, которой его забрали обратно по окончании аренды.
string | null
Когда ресурс запланировано отозвать, ISO-8601, либо null для заказов без таймера отзыва.
Response
Судите об исходе по status, а не по наличию delegate_txid. У неуспешного заказа идентификатор транзакции тоже может быть заполнен — мы оставляем его намеренно, чтобы отказ можно было расследовать. Получен ли ресурс, говорит только status.
Крупный заказ мы внутри делим на части, но это наша кухня: вы получаете один вебхук на заказ, а не по одному на часть.

subscription.transfer

number
Подписка Smart Mode, к которой относится перевод.
string
Отслеживаемый адрес, с которого ушёл перевод.
string | null
Метка, которую вы дали подписке.
string
Транзакция перевода в сети.
number
Сколько переводов покрыла эта делегация.
number
Сколько энергии под них делегировано.
string
Сколько списано за энергию, в TRX.
string | null
Сколько списано за bandwidth сверху, либо null, если не списывалось.
string
Баланс аккаунта после списания.
string
Когда перевод обработан. Обратите внимание: это поле внутри data и отличается от timestamp самого конверта.
Response

События Transfer Package

Четыре события package.* уходят аккаунту, которому принадлежит пакет. У пакета, купленного по инвойсу без входа в аккаунт и без API-ключа, владельца нет, и событий по нему не будет. Не будет их и по отменённым инвойсам, отклонённым покупкам и покупкам на ручной проверке; пакет с проверки пришлёт package.activated, когда будет активирован. Каждое событие package.* несёт в data эти поля:
number
ID пакета — id в Списке пакетов.
string
Адрес, который обслуживает пакет.
string
Статус пакета после события: active или completed у package.transfer, completed у package.completed, active у package.activated, expired у package.expired.
number
Сколько переводов было в пакете при покупке.
number
Сколько переводов осталось в пакете после изменения, о котором сообщает событие, — абсолютное число, а не разница.
События могут прийти не по порядку. Каждое доставляется отдельно, с повторами примерно до 35 секунд, поэтому package.completed, вызванное переводом, может прийти раньше package.transfer этого перевода, а package.transfer — раньше package.activated. Для каждого package_id храните наименьший увиденный remaining: он никогда не растёт. Статусы completed и expired окончательные.
remaining может уменьшиться и без события. Пакет на несколько переводов списывает один перевод в сутки как суточную подписку — первый при активации, поэтому пакет на 500 переводов начинается с remaining: 499, — и об этом списании событие не приходит. Два перевода с одного адреса в одном блоке могут прислать одинаковый remaining. Считайте remaining подсказкой, а источником правды — Список пакетов.
Чтобы распознать повтор, используйте (package_id, tx_hash) для package.transfer и (event, package_id) для остальных: повтор приходит с тем же телом, тем же timestamp и той же подписью.

package.transfer

Перевод с адреса оплачен из пакета. Отправляется по одному разу на каждый пакет, с которого списан перевод, — почти всегда одно событие на перевод; перевод, забравший последний перевод одного пакета и продолжившийся на следующем, даёт по событию на каждый.
string
Транзакция перевода в сети.
number
Сколько переводов списано с этого пакета за эту транзакцию. Перевод на адрес, где нет USDT, требует двойной энергии и считается за 2 — их можно разделить между двумя пакетами.
Здесь status равен completed, если этот перевод исчерпал пакет.
Response

package.completed

Пакет перестал обслуживать переводы.
string
transfer — пакет исчерпан переводом; subscription — его исчерпало суточное списание; manual — пакет закрыла поддержка. После manual значение remaining может быть больше нуля, но эти переводы использовать уже нельзя.
string | null
При reason: "transfer" — перевод, закрывший пакет; иначе null.
Пакет без остатка — например, обычный пакет на один перевод, у которого первое суточное списание забрало единственный перевод, — закрывается следующим переводом без собственного package.transfer: придёт только package.completed с tx_hash этого перевода.
Response

package.activated

Пакет стал активным, и с этого момента его переводы получают энергию. Отправляется при каждой активации — в том числе при покупке с баланса, хотя её ответ уже содержит active, и при оплате инвойса, которая активирует пакет вскоре после того, как платёж виден в сети.
string
Как оплачен пакет: ровно balance, TRX или USDT.
Response

package.expired

Инвойс на пакет не оплачен за отведённые 30 минут. Ничего не списано; чтобы получить новый инвойс, купите заново. Дополнительных полей нет; remaining равен quota_total и для использования недоступен.
Response

Доставка

  • До 3 попыток, с паузами 1 секунда и затем 4 секунды между ними. После успеха и после последней попытки пауз нет.
  • Таймаут 10 секунд на попытку.
  • Мы ждём любой 2xx. Всё остальное считается неудачной попыткой.
  • Редиректы не следуются. 301/302 — это неудачная попытка, а не переход.
  • Пропущенные события не досылаются. Если все три попытки не удались, событие потеряно; сверяйтесь через Получение заказа или Список пакетов, а не ждите повтора.
Ваш URL обязан резолвиться в публичный адрес. Loopback, приватные и link-local диапазоны отвергаются, как и хост, который вообще не резолвится — в обоих случаях ничего не отправляется и строка в журнале не пишется.

Проверка подписи

Каждый запрос несёт X-Webhook-Signature: sha256=<hex> — HMAC-SHA256 по сырому телу запроса с вашим секретом вебхука. Работоспособность вашей проверки решают две детали:
  1. Отрежьте префикс sha256= перед сравнением. В заголовке не голый hex-дайджест.
  2. Хешируйте полученные байты, а не пересобранный объект. Разбор JSON и повторная сериализация меняют порядок ключей и пробелы — подпись не сойдётся никогда.
Секрет подписи показан в Dashboard → API рядом с URL вебхука. По API-ключу он не отдаётся — чтобы его прочитать, нужно войти на сайте.
Проверяйте подпись до того, как действовать по содержимому, и сравнивайте за постоянное время (hmac.compare_digest / crypto.timingSafeEqual). Сравнение через == утекает по таймингу.