A Aurum

Документация

Всё взаимодействие — обычный HTTP с JSON. Базовый адрес: https://api.aurum.example/api/v1

Быстрый старт

  1. Зарегистрируйтесь и создайте проект в личном кабинете.
  2. Выпустите API-ключ — он показывается один раз.
  3. Выставьте счёт запросом ниже.
  4. Отправьте покупателя по checkout_url.
  5. Получите вебхук, когда перевод подтвердится.

Аутентификация

Интеграционные запросы подписываются ключом проекта. Передавайте его в заголовке X-API-Key либо как Authorization: Bearer — принимаются оба.

X-API-Key: aur_live_kNyJo-33JxgnwLUjDirM8M1XY489mwWFieAcLIGpxrM
Ключ даёт право выставлять счета от вашего имени. Держите его на сервере: в коде страницы, в мобильном приложении и в репозитории ему не место. Утёкший ключ отзывается в кабинете за секунду, и старый сразу перестаёт работать.

Суммы и валюты

Все суммы передаются строками и в минимальных единицах. Это не придирка: у токенов бывает 18 знаков после запятой, и такое число не помещается в number ни в JavaScript, ни в JSON без потери точности.

ВалютаАлиасЗнаков1 единица
TRXTRX6"1000000"
USDT (TRC-20)USDT_TRON6"1000000"

25 USDT записываются как "25000000".

Выставить счёт

POST /integration/invoices

curl https://api.aurum.example/api/v1/integration/invoices \
  -H "X-API-Key: aur_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "A-1042",
    "description": "Подписка Pro, 1 месяц",
    "amount": "25000000",
    "currency_alias": "USDT_TRON"
  }'
ПолеОписание
order_idВаш номер заказа. Он же ключ идемпотентности: повтор с тем же значением вернёт уже выставленный счёт и код 200 вместо 201.
amountСумма в минимальных единицах, строкой.
currency_aliasUSDT_TRON или TRX.
descriptionНеобязательно. Покупатель увидит его на странице оплаты.
Повторяйте запрос при таймауте сети смело: пока order_id тот же, второго счёта не появится. Без этого двойной клик по кнопке «оплатить» означал бы, что покупатель может заплатить дважды.

В ответе придёт checkout_url — страница оплаты, — и address, если адрес уже готов. Изредка адрес приходит с задержкой в пару секунд; тогда статус будет PENDING_ADDRESS, и адрес появится при следующем запросе счёта.

Получить счёт

GET /integration/invoices/{uuid}

Счёт без интеграции

Интеграция нужна не всем. Если заказов немного или вы только пробуете, счёт можно выставить руками в личном кабинете — вкладка Счета, кнопка «Выставить счёт». Получится ровно то же самое: та же ссылка на оплату, тот же адрес, те же уведомления. Тот же order_id и та же идемпотентность.

Цена в валюте магазина

Если вы продаёте за доллары, передавайте цену в долларах — мы пересчитаем по курсу и зафиксируем его в счёте. Покупатель, открывший ссылку через час, увидит ту же сумму.

{
  "order_id": "A-1043",
  "fiat_code": "USD",
  "fiat_amount": "25.00",
  "currency_alias": "TRX"
}

Указывайте либо amount, либо fiat_amount. Оба сразу — ошибка: две суммы в одном запросе означают, что магазин сам не знает, сколько хочет получить.

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

СтатусЧто означает
PENDING_ADDRESSГотовим адрес, обычно пара секунд.
AWAITING_PAYMENTЖдём перевод.
UNDERPAIDПришло меньше запрошенного. В outstanding — сколько осталось.
PAIDОплачен полностью.
EXPIREDВремя вышло. Перевод, пришедший позже, всё равно будет засчитан.
CANCELLEDСчёт отменён в кабинете.

Вебхуки

Укажите адрес уведомлений в настройках проекта — только https. Мы отправим POST с телом:

{
  "event": "invoice.paid",
  "invoice_uuid": "8702702b-bd52-45f7-…",
  "order_id": "A-1042",
  "currency_alias": "USDT_TRON",
  "amount": "25000000",
  "received": "25000000",
  "status": "PAID",
  "paid_at": "2026-08-27T02:41:12Z"
}

События: invoice.paid и invoice.underpaid.

Ответьте любым кодом 2xx. Всё остальное считается неудачей, и мы повторим — пятнадцать раз с растущей паузой, суммарно почти сутки. Магазин, упавший ночью и починенный утром, всё равно получит уведомление.

Уведомление — сигнал, а не источник истины. Прежде чем отгружать товар, запросите счёт через API и убедитесь, что он действительно PAID.

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

Каждый вебхук приходит с заголовком Aurum-Signature:

Aurum-Signature: t=1787797272,v1=5f3a…c81b

Подпись — HMAC-SHA256 от строки "{t}.{тело запроса}" с секретом проекта. Секрет виден в кабинете рядом с адресом вебхука.

// Node.js
import crypto from 'node:crypto'

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(',').map((p) => p.split('=')),
  )

  // Отметка времени защищает от повторной отправки перехваченного уведомления.
  const age = Math.abs(Date.now() / 1000 - Number(parts.t))
  if (age > 300) return false

  const expected = crypto
    .createHmac('sha256', secret)
    .update(parts.t + '.' + rawBody)
    .digest('hex')

  // timingSafeEqual, а не ===: обычное сравнение подбирается по времени ответа.
  return crypto.timingSafeEqual(
    Buffer.from(expected), Buffer.from(parts.v1),
  )
}
Считайте подпись от сырого тела запроса, а не от результата JSON.parse и обратной сериализации: порядок ключей и пробелы изменятся, и подпись не сойдётся.

Виджет оплаты

Оплата во всплывающем окне поверх вашего сайта — покупатель не уходит с оформления заказа. Подключается одним скриптом.

<script src="https://api.aurum.example/widget.js"></script>
<script>
  Aurum.open(checkoutURL, {
    onPaid:  function (invoice) { location.href = '/thanks' },
    onClose: function () {}
  })
</script>

Ссылку checkoutURL отдаёт ваш сервер — это checkout_url из ответа на выставление счёта. Если вставить свой скрипт некуда, хватит разметки:

<a data-aurum="https://api.aurum.example/pay/<invoice_uuid>">Оплатить криптой</a>
Не подтверждайте заказ по onPaid. Это сообщение из браузера покупателя, и подделать его может кто угодно — достаточно открыть консоль. Отгружайте по вебхуку с подписью; onPaid нужен только чтобы показать человеку, что можно идти дальше.

Виджет не выставляет счета и не знает вашего ключа. Ключ в браузере — это ключ у всех, кто открыл страницу: им можно выставлять счета от вашего имени и читать чужие платежи.

SDK для Node

Тонкий клиент для сервера магазина: выставление счетов и проверка подписи уведомлений. Зависимостей нет, нужен Node 18+.

const { Aurum, verifyWebhook } = require('@aurum/node')

const aurum = new Aurum({ apiKey: process.env.AURUM_API_KEY, baseURL: 'https://api.aurum.example' })

const invoice = await aurum.createInvoice({
  order_id: 'A-1042',
  currency_alias: 'USDT_TRON',
  amount: '12500000',
})

Проверка подписи требует сырого тела запроса: подпись считается по байтам, и тело, прошедшее через JSON.parse и обратно, отличается пробелами и порядком полей.

app.post('/webhooks/aurum', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifyWebhook({ secret, header: req.get('Aurum-Signature'), body: req.body })) {
    return res.sendStatus(400)
  }
  // ...
})

Баланс

GET /balances — по токену кабинета.

Баланс ведётся отдельно по каждой валюте и не сводится в одну: курса на момент зачисления у нас нет, а пересчёт задним числом показал бы сумму, которой вы никогда не видели.

GET /balances/{currency_alias}/history отдаёт движения: каждое зачисление, каждое удержание комиссии, каждое списание под вывод.

Вывод

POST /withdrawals

{
  "currency_alias": "USDT_TRON",
  "amount": "25000000",
  "address_to": "TN7hUEFWqqSiFH1K27tDqwPAdRRa7YwbAi"
}

Сумма списывается с баланса сразу, в ответ приходит 202. Дальше заявка проходит статусы PENDINGSENTCOMPLETED. Если сеть отвергнет перевод, статус станет FAILED, а деньги вернутся на баланс обратной проводкой.

Перевод в блокчейне необратим. Проверьте адрес: ошибка в одном символе означает деньги, ушедшие в никуда, и вернуть их не сможем ни мы, ни кто-либо ещё.

Возвраты

Вернуть деньги покупателю можно из кабинета или запросом. По умолчанию возвращается переплата — то, что пришло сверх суммы счёта:

POST /invoices/{invoice_uuid}/refunds
{}                                  // вся переплата, на кошелёк плательщика

Можно указать сумму и адрес явно — например, когда заказ отменён целиком или оплата пришла с нескольких кошельков:

{
  "amount": "2500000",
  "address_to": "TEqqQkYGtae6ic6BXPYvfCXLfrqji5MY5z"
}

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

Кто платит комиссию

Настройка проекта. По умолчанию комиссия платформы удерживается из пришедшего — вы получаете меньше суммы заказа. Второй вариант: сумма к оплате увеличивается так, чтобы после удержания вам досталась ровно цена заказа.

PATCH /projects/{project_uuid}
{ "fee_paid_by": "CUSTOMER" }   // или MERCHANT

Это не «сумма плюс процент»: комиссия берётся от итога платежа, поэтому счёт на 100 USDT при ставке 1% превращается в 101.010102 — ровно столько, чтобы вам осталось 100. В ответе счёта обе величины видны: amount — сколько платит покупатель, merchant_amount — сколько получите вы.

Настройка действует на новые счета. Уже выставленный счёт хранит снимок правил: сумма, названная покупателю, не меняется задним числом.

Адреса ключа

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

PUT /projects/{project_uuid}/keys/{key_uuid}/allowed-ips
{ "allowed_ips": ["203.0.113.10", "198.51.100.0/24"] }

Пустой список означает «откуда угодно». Запрос с чужого адреса получает 403 ip_not_allowed — отдельный код, а не общий отказ, чтобы не путать его с отозванным ключом. Список меняется без перевыпуска ключа.

Ошибки

Все ошибки приходят в одном виде:

{
  "error": "bad_request",
  "message": "amount must be a decimal integer in minor units"
}
КодHTTPКогда
bad_request400Запрос не прошёл проверку.
unauthorized401Нет ключа, он неверен или отозван.
not_found404Объекта нет либо он не ваш.
conflict409Например, недостаточно средств.
too_many_requests429Слишком часто. Смотрите Retry-After.
service_unavailable503Наша сторона временно не готова — повторите.

error стабилен и предназначен для кода, message — для человека и может меняться. Не разбирайте message программно.

Тестовая сеть

Пока система работает в сети TRON Nile. Тестовые TRX выдаёт кран Nile, стоят они ноль, и вести себя всё будет ровно так же, как в боевой сети.

Транзакции удобно смотреть в обозревателе Nile.