Skip to main content
Счёт (invoice) — это готовая платёжная страница (hosted payment page), которую размещает ваш кошелёк. CMS создаёт счёт одним запросом, получает публичную ссылку и редиректит на неё клиента. Клиент видит сумму, адрес, QR-код, обратный отсчёт и статус оплаты в реальном времени — вам не нужно верстать платёжный экран самостоятельно. Под капотом счёт — это обычный депозит: адрес выдаётся тем же депозитным путём со всеми денежными проверками (уникальный адрес на заявку, подтверждения, идемпотентность, переплата/недоплата). Счёт лишь добавляет публичный неугадываемый токен и срок жизни ссылки.
Создание счёта (POST) подписывается HMAC, как все write-операции — см. Аутентификация. А вот публичные GET-эндпоинты страницы оплаты не требуют подписи: токен в URL и есть аутентификация (страницу открывает конечный клиент, а не ваш сервер). Ответы — всегда в конверте { "ok": true, "data": … }.

Эндпоинты

МетодПутьНазначениеАутентификация
POST/v1/public/invoicesсоздать счёт (ссылку оплаты)HMAC-подпись
GET/v1/public/invoices/{token}данные платёжной страницы по токенупубличный (токен)
GET/v1/public/invoices/{token}/statusоблегчённый статус для поллингапубличный (токен)

Как это работает

1

CMS создаёт счёт

POST /v1/public/invoices с суммой и активом. В ответ — token и payPath.
2

Редирект клиента на оплату

Соберите абсолютную ссылку https://<домен-вашего-кошелька>{payPath} и откройте её клиенту. Страница сама покажет адрес, QR, сумму и обратный отсчёт.
3

Клиент платит, платформа отслеживает

Приход средств отслеживается существующим мониторингом депозитов ровно до конца срока счёта.
4

Вы узнаёте об оплате

Либо вебхуком deposit.finalized на ваш callback_url (рекомендуется), либо поллингом статуса по токену. См. Отслеживание оплаты.

Создать счёт

POST /v1/public/invoices Создаёт счёт для вызывающего сайта и возвращает данные, нужные для редиректа клиента на оплату.

Тело запроса

assetCode
string
required
Код актива: USDT_TRC20, TRX, GRAM, USDT_TON, … (до 32 символов). Список доступных активов — GET /v1/public/assets (см. Валюты).
amount
string
required
Сумма к оплате — десятичная строка, строго больше нуля (например "49.90"). Никогда не число: деньги передаются строками, чтобы избежать потери точности. "0" и отрицательные значения отвергаются с INVALID_AMOUNT.
ttlMinutes
integer
default:"60"
Срок действия счёта в минутах. Диапазон 5…43200 (до 30 дней), по умолчанию 60. Срок задаёт окно мониторинга: платформа следит за адресом ровно до expiresAt. После истечения счёт переходит в expired, но публичная ссылка живёт ещё 1 день (grace), чтобы клиент увидел финальный статус — затем ссылка отключается (410 Gone).
orderId
string
Ваш order_id. Если не передан — платформа сгенерирует уникальный. Уникален в рамках сайта; повтор того же order_id идемпотентно вернёт тот же счёт (второй не создаётся). До 255 символов. См. Идемпотентность.
title
string
Заголовок/назначение платежа — показывается клиенту на странице оплаты (до 200 символов).
description
string
Описание — показывается клиенту на странице оплаты (до 1000 символов).
sweepDestinationWalletUuid
string
UUID системного кошелька-получателя свипа после оплаты (override маршрутизации). Если не указан — применяется дефолтная маршрутизация (обычно hot). Кошелёк должен быть active, той же сети, что и актив, и иметь роль hot, admin, payout или cold (роль energy запрещена).
cURL
curl -X POST https://wallet.your-exchange.com/v1/public/invoices \
  -H "X-Api-Id: pk_live_a1b2c3d4" \
  -H "X-Timestamp: 1782345600" \
  -H "X-Signature: 9b1f8c2d4e6a0f3b5c7d9e1a2b4c6d8e0f1a3b5c7d9e1f2a4b6c8d0e2f4a6b8c" \
  -H "X-Idempotency-Key: 3f5a8c1e-2d4b-4f6a-8c0e-1b3d5f7a9c2e" \
  -H "Content-Type: application/json" \
  -d '{
    "assetCode": "USDT_TRC20",
    "amount": "49.90",
    "ttlMinutes": 60,
    "orderId": "order-1024",
    "title": "Оплата заказа №1024"
  }'
Node.js
import crypto from "node:crypto";

const baseUrl   = "https://wallet.your-exchange.com";
const apiId     = "pk_live_a1b2c3d4";
const apiSecret = process.env.WALLET_API_SECRET; // никогда не уходит по сети

const body = JSON.stringify({
  assetCode: "USDT_TRC20",
  amount: "49.90",
  ttlMinutes: 60,
  orderId: "order-1024",
  title: "Оплата заказа №1024",
});

const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto
  .createHmac("sha256", apiSecret)
  .update(`${timestamp}.${body}`)
  .digest("hex");

const res = await fetch(`${baseUrl}/v1/public/invoices`, {
  method: "POST",
  headers: {
    "X-Api-Id": apiId,
    "X-Timestamp": timestamp,
    "X-Signature": signature,
    "X-Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body,
});

const { ok, data, error } = await res.json();
if (!ok) throw new Error(`${error.code}: ${error.message}`);

// Абсолютная ссылка для редиректа клиента:
const payUrl = `${baseUrl}${data.payPath}`;
console.log(payUrl); // https://wallet.your-exchange.com/invoice/1c06b6b4...
{
  "ok": true,
  "data": {
    "uuid": "8f3a1c2e-5b6d-4e7f-9a0b-1c2d3e4f5a6b",
    "orderId": "order-1024",
    "token": "1c06b6b4f6dbb5aaf795b27757a3d2c9d1e0f2a3b4c5d6e7f8091a2b3c4d5e6f",
    "payPath": "/invoice/1c06b6b4f6dbb5aaf795b27757a3d2c9d1e0f2a3b4c5d6e7f8091a2b3c4d5e6f",
    "assetCode": "USDT_TRC20",
    "network": "TRON",
    "address": "TKh9...c4dZ",
    "memo": null,
    "amount": "49.90",
    "expiresAt": "2026-06-25T14:00:00.000Z",
    "status": "pending"
  }
}

Поля ответа

uuid
string
Внутренний UUID счёта (для корреляции).
orderId
string
Ваш order_id (или сгенерированный, если не передавали).
token
string
Публичный токен ссылки (64 hex-символа, неугадываемый). Используется в публичных GET-эндпоинтах и в payPath.
payPath
string
Относительный путь страницы оплаты. Абсолютная ссылка: https://<домен-вашего-кошелька>{payPath}.
assetCode
string
Код актива счёта.
network
string
Сеть актива (TRON, TON, …).
address
string
Адрес для оплаты (тот же, что покажет страница).
memo
string | null
Memo для memo-сетей (TON). Для остальных сетей — null.
amount
string
Сумма к оплате (десятичная строка).
expiresAt
string
Срок действия счёта (ISO-8601). После него счёт expired.
status
string
Состояние счёта на момент создания — обычно pending. Полный набор — Состояния счёта.
Для memo-сетей (TON) memo обязателен при оплате. Hosted-страница показывает его отдельно с предупреждением, но если вы строите свой UI поверх API — обязательно выводите memo рядом с адресом: перевод без memo не будет привязан к счёту.

Отслеживание оплаты

Есть два способа узнать об оплате. Их можно комбинировать.
1

Вебхук (рекомендуется)

Поскольку счёт — это депозит, при финализации оплаты платформа отправит на callback_url вашего сайта событие deposit.finalized с вашим order_id. Отдельной подписки не нужно — достаточно настроенного callback_url. См. Webhooks.
2

Поллинг по токену

Опрашивайте GET /v1/public/invoices/{token}/status (облегчённый ответ) каждые ~3 секунды для live-обновления экрана оплаты. Полное состояние страницы — GET /v1/public/invoices/{token}. Оба эндпоинта публичные (подпись не нужна, аутентификация по токену).

Получить данные платёжной страницы

GET /v1/public/invoices/{token} Возвращает всё, что нужно для отрисовки страницы оплаты: адрес, актив, сумму, USD-эквивалент (best-effort), срок (для обратного отсчёта), текущее состояние, подтверждения, ссылки на explorer и брендинг оператора.
curl https://wallet.your-exchange.com/v1/public/invoices/1c06b6b4f6dbb5aaf795b27757a3d2c9d1e0f2a3b4c5d6e7f8091a2b3c4d5e6f
{
  "ok": true,
  "data": {
    "token": "1c06b6b4f6dbb5aaf795b27757a3d2c9d1e0f2a3b4c5d6e7f8091a2b3c4d5e6f",
    "title": "Оплата заказа №1024",
    "description": null,
    "assetCode": "USDT_TRC20",
    "assetSymbol": "USDT",
    "assetName": "Tether USD (TRC-20)",
    "network": "TRON",
    "address": "TKh9...c4dZ",
    "memo": null,
    "expectedAmount": "49.90",
    "amountUsd": "49.90",
    "decimals": 6,
    "expiresAt": "2026-06-25T14:00:00.000Z",
    "createdAt": "2026-06-25T13:00:00.000Z",
    "state": "pending",
    "paymentStatus": "check",
    "receivedAmount": null,
    "confirmations": null,
    "requiredConfirmations": 19,
    "txhash": null,
    "explorerTxUrl": null,
    "explorerAddressUrl": "https://tronscan.org/#/address/TKh9...c4dZ",
    "paidAt": null,
    "branding": {
      "displayName": "WalletCore",
      "primaryColor": "#3b82f6"
    }
  }
}

Поля ответа

token
string
Публичный токен счёта.
title
string | null
Заголовок/назначение платежа.
description
string | null
Описание счёта.
assetCode
string
Код актива (USDT_TRC20).
assetSymbol
string
Короткий символ для UI (USDT).
assetName
string
Человекочитаемое имя актива.
network
string
Сеть актива.
address
string
Адрес для оплаты (показывается + QR).
memo
string | null
Memo для memo-сетей (обязателен при оплате), иначе null.
expectedAmount
string
Сумма к оплате.
amountUsd
string | null
USD-эквивалент суммы (best-effort, по oracle). null, если цены нет.
decimals
number
Число знаков после запятой для актива.
expiresAt
string
Срок оплаты (для обратного отсчёта).
createdAt
string
Когда счёт создан.
state
string
Состояние оплаты для UI. См. Состояния счёта.
paymentStatus
string
Сырой статус депозита (для отладки/детализации). По умолчанию check.
receivedAmount
string | null
Фактически полученная сумма (null, пока нет tx).
confirmations
number | null
Текущее число подтверждений (null, пока нет tx).
requiredConfirmations
number
Сколько подтверждений нужно для финализации.
txhash
string | null
Хеш входящей транзакции.
explorerTxUrl
string | null
Ссылка на tx в explorer.
explorerAddressUrl
string | null
Ссылка на адрес в explorer.
paidAt
string | null
Когда оплата финализирована (null, пока не оплачено).
branding
object
Брендинг оператора для white-label страницы.

Опрашивать статус оплаты

GET /v1/public/invoices/{token}/status Облегчённый ответ для частого поллинга страницы (раз в ~3 секунды). Содержит только поля, нужные для live-обновления, без брендинга и метаданных актива.
curl https://wallet.your-exchange.com/v1/public/invoices/1c06b6b4f6dbb5aaf795b27757a3d2c9d1e0f2a3b4c5d6e7f8091a2b3c4d5e6f/status
{
  "ok": true,
  "data": {
    "state": "detected",
    "paymentStatus": "process",
    "receivedAmount": "49.90",
    "confirmations": 7,
    "requiredConfirmations": 19,
    "txhash": "a1b2c3d4e5f6...90ab",
    "explorerTxUrl": "https://tronscan.org/#/transaction/a1b2c3d4e5f6...90ab",
    "paidAt": null,
    "expiresAt": "2026-06-25T14:00:00.000Z"
  }
}

Поля ответа

state
string
Состояние оплаты для UI. См. Состояния счёта.
paymentStatus
string
Сырой статус депозита.
receivedAmount
string | null
Фактически полученная сумма.
confirmations
number | null
Текущее число подтверждений.
requiredConfirmations
number
Сколько нужно для финализации.
txhash
string | null
Хеш входящей транзакции.
explorerTxUrl
string | null
Ссылка на tx в explorer.
paidAt
string | null
Когда оплата финализирована.
expiresAt
string
Срок оплаты (для обратного отсчёта).
Прогресс подтверждений считайте как confirmations / requiredConfirmations (например 7 / 19). Сравнивайте receivedAmount с expectedAmount, чтобы отличить точную оплату (paid) от переплаты (overpaid) и недоплаты (underpaid).

Состояния счёта

Поле state (в обоих GET-ответах) и status (при создании) — это упрощённое для UI представление статуса депозита. Возможные значения:
stateЧто значитФинальное
pendingадрес выдан, оплаты ещё нет, срок не истёкнет
detectedвходящая tx замечена, идут подтверждениянет
paidоплачено точно (сумма совпала)да
overpaidпереплата (получено больше ожидаемого)да
underpaidнедоплата (получено меньше ожидаемого)да
refundedоплата возвращена отправителюда
expiredсрок истёк, оплаты не было (или счёт отменён)да
Счёт делегирует приём средств депозиту, поэтому переплата/недоплата обрабатываются ровно как у депозита. Логика подтверждений и финализации — та же; см. Статусы депозита. Поле paymentStatus отдаёт сырой статус депозита, если нужна более точная детализация.

Краевые случаи и ошибки

СитуацияПоведение
Сумма "0" или отрицательная422 INVALID_AMOUNT («Amount must be greater than zero»)
ttlMinutes вне диапазона 5…43200422 VALIDATION_FAILED
Неизвестный assetCode404 NOT_FOUND / INVALID_ASSET
Повтор того же orderIdидемпотентно возвращается тот же счёт (второй не создаётся)
sweepDestinationWalletUuid не найден / неактивен422 VALIDATION_FAILED
Кошелёк свипа в другой сети, чем актив422 VALIDATION_FAILED
Роль кошелька свипа не из hot/admin/payout/cold422 VALIDATION_FAILED
Запрос токена несуществующего счёта404 NOT_FOUND
Ссылка отключена оператором410 Gone
Прошёл срок счёта + 1 день (grace)410 Gone — страница больше недоступна
Полный формат ошибки и справочник кодов — Ошибки.

Частые вопросы

Ничем по сути приёма средств — счёт это депозит плюс публичная hosted-страница оплаты и срок жизни ссылки. Если вам не нужна готовая страница (вы рисуете платёжный экран сами), используйте Депозиты напрямую. Если хотите редиректить клиента на готовую страницу — используйте счета.
Нет. POST на создание счёта подписывается HMAC (это делает ваш сервер). А GET /{token} и GET /{token}/status — публичные: их вызывает браузер клиента, и аутентификацией служит сам неугадываемый токен в URL. Не встраивайте api_secret в клиентский код.
Из ответа на создание возьмите payPath и соберите абсолютную ссылку https://<домен-вашего-кошелька>{payPath}. Откройте её клиенту (редирект или новая вкладка). Альтернативно постройте ссылку из token: …/invoice/{token}.
Платформа мониторит адрес до expiresAt (задаётся ttlMinutes, по умолчанию 60 минут). После истечения счёт переходит в expired, но публичная ссылка остаётся доступной ещё 1 день, чтобы клиент увидел финальный статус. Затем ссылка отключается и возвращает 410 Gone. Оператор также может отключить ссылку вручную из админки в любой момент.
Вебхук deposit.finalized — основной и надёжный способ обновить вашу заявку на бэкенде (см. Webhooks). Поллинг GET /{token}/status — для живого обновления экрана у клиента в браузере (раз в ~3 секунды). Обычно используют оба: вебхук на сервере, поллинг на странице.
Обрабатываются как у депозита: точная сумма → paid, больше → overpaid, меньше → underpaid. Сравнивайте receivedAmount и expectedAmount. Окно мониторинга равно сроку счёта — после expiresAt адрес больше не отслеживается.
Да. Передайте sweepDestinationWalletUuid при создании — после оплаты свип уйдёт на этот системный кошелёк (override маршрутизации). Кошелёк должен быть active, той же сети, что и актив, и иметь роль hot, admin, payout или cold. Без этого поля применяется дефолтная маршрутизация.