REST поверх HTTPS, тела и ответы в JSON. Счёт, вебхук об оплате, выплата и обмен монет —
всё делается ключом в заголовке, без SDK и без звонков менеджеру.
база https://pay.666.exchange/v1ключ Authorization: Bearer sk_…суммы строками: "49.00"
Быстрый старт
Три шага от пустого магазина до оплаченного счёта. Всё это работает в песочнице — без реальных денег.
I
Получите ключ
Панель → Мерчанты → создайте магазин, задайте адрес вебхуков и секрет. Ключ
sk_test_… работает сразу, боевой sk_live_… — после модерации.
Про ключи подробнее.
II
Создайте счёт
В ответе — адрес, точная сумма в монете и готовая страница оплаты pay_url.
Когда деньги пришли и подтвердились, на ваш адрес прилетит invoice.paid. Сверьте
подпись X-666pay-Signature — HMAC-SHA256 от сырого тела вашим секретом — и отгружайте заказ.
Как проверять подпись.
У 666pay три вида ключей. Ключ приёма живёт у конкретного мерчанта и управляет только его счетами;
ключ выплат один на весь аккаунт и двигает деньги с баланса.
Ключ
Где взять
Что умеет
sk_test_…
Сразу после создания мерчанта
Тестовые счета без денег и без блокчейна
sk_live_…
После модерации мерчанта
Настоящие счета: адрес, наблюдение за сетью, зачисление на баланс
pk_live_…
Настройки → API → Выплатной API-ключ
Выплаты, балансы, история и конвертация по всему аккаунту
Ключ мерчанта
В панели откройте Мерчанты и нажмите Создать мерчанта:
название, сайт или бот.
В карточке мерчанта укажите URL для вебхуков и придумайте секрет — им подписываются уведомления.
Скопируйте sk_test_… и начинайте интеграцию в песочнице.
Когда всё готово, отправьте мерчанта на проверку — после неё появится sk_live_….
Полный ключ виден только в момент выпуска. Держите его в переменных окружения сервера — в коде
сайта, приложения или бота он окажется у любого, кто их откроет. Выпуск нового ключа сразу
отключает прежний.
Песочница
Счета, созданные ключом sk_test_…, нужны для отладки и денег не касаются:
адреса нет и сеть не просматривается — в ответе "test": true и "address": null;
на странице оплаты (pay_url) есть кнопки, которые разыгрывают paid или
underpaid;
вебхук уходит настоящий и подписанный, только с "test": true — обработчик проверяется
так же, как в бою;
баланс мерчанта не меняется.
Ключ выплат
Выпускается в панели: Настройки → API → Выплатной API-ключ. Подтверждение — тем же кодом из
приложения, что и вывод средств. Ключ показывается один раз; при перевыпуске старый отзывается
немедленно.
С этим ключом можно вывести деньги, поэтому ограничьте его заранее: включите
Список доверенных адресов — выплаты уйдут только на них — и Доверенные IP для API —
запросы примутся только с ваших серверов.
Запросы и ответы
BASEhttps://pay.666.exchange
Только HTTPS. Запросы с телом шлются в JSON (UTF-8), ответы тоже приходят в JSON. Суммы передаются
строками, чтобы не терять знаки после запятой.
Как представиться
Достаточно ключа в заголовке — тело запроса подписывать не нужно: канал шифрует TLS, а секрет здесь
сам ключ.
Заголовок
Значение
Authorization
Bearer sk_live_…, Bearer sk_test_… или Bearer pk_live_….
Тот же ключ принимается и в X-Api-Key — если ваш HTTP-клиент не умеет Bearer.
Ответ с кодом 4xx или 5xx всегда несёт объект error: в code — строка, на
которую можно опираться в коде, в message — пояснение для человека, а у ошибок в
параметрах ещё и field с именем поля.
json
// 422 — сумма ниже минимума для этой сети{"error":{"code":"amount_too_small","message":"Сумма меньше минимальной для сети","field":"amount"}}
HTTP
Причина
Коды
400
Тело — не JSON или JSON с ошибкой.
—
401
Ключа нет в запросе, он неверный или уже отозван.
no_key, bad_key
403
Боевой ключ у мерчанта без пройденной проверки — или запрос с адреса, которого нет среди
доверенных IP.
not_live, ip_not_allowed
404
Такого счёта нет — либо он чужой: счета других мерчантов для ключа не существуют.
not_found
422
Параметр неверный или пропущен обязательный; имя поля — в field.
Израсходован бюджет запросов — см. Лимиты. Сколько
ждать, скажет Retry-After (в секундах).
too_many_requests, rate_limit_banned
503
Адрес для оплаты сейчас выдать нельзя: модуль подписи недоступен. Запрос можно повторить.
signer_down
Лимиты
Ограничение считается не в штуках запросов, а в весе. Каждому ключу отпущено
500 единиц веса в минуту, у каждого метода — своя цена. Создать счёт или выплату дорого,
спросить статус — почти бесплатно, поэтому опрос статуса упирается в лимит последним.
Правила
Запас пополняется плавно — примерно на 8.33 единицы в секунду, пока не дойдёт до
полного (500). Весь запас можно потратить разом; дальше он набирается снова.
Остаток виден в каждом ответе: X-RateLimit-Limit — размер запаса,
X-RateLimit-Cost — сколько стоил этот вызов, X-RateLimit-Remaining — сколько
осталось.
Не хватило — ответ 429 с кодом too_many_requests и заголовком
Retry-After. На это время ключ блокируется целиком.
Повторные нарушения удлиняют паузу. Первая блокировка — 10 секунд, каждая
следующая вдвое дольше, но не больше 1 часа. Пока она действует, отвечает
429 с кодом rate_limit_banned любой метод, даже самый дешёвый. Поработайте
без нарушений — и отсчёт начнётся заново с 10 секунд. Отсюда правило: после
429 ждите ровно Retry-After, а не повторяйте запрос сразу.
Счёт ведётся отдельно для каждого ключа — sk_ и pk_ друг другу запас не
тратят.
Числа ниже — значения по умолчанию, их можно поменять для вашего аккаунта. В коде полагайтесь на
заголовки X-RateLimit-*, а не на эту таблицу.
Цена методов
Метод
Вес
POST /v1/invoices
50
POST /v1/payouts, POST /v1/conversions, POST /v1/exchange
25
POST /v1/buffer/scan
20
GET /v1/conversions/quote, GET /v1/exchange/estimate, GET /v1/exchange-rate/{cur}/list, POST /v1/invoices/{id}/cancel
5
Списки: GET /v1/invoices, /v1/payouts, /v1/conversions, /v1/exchange; а также /v1/exchange/currencies, /v1/balances, /v1/transactions, /v1/buffer/addresses
2
Один объект: GET /v1/invoices/{id}, /v1/payouts/{id}, /v1/conversions/{id}, /v1/exchange/{id}; а также /v1/payouts/fee и /v1/currencies
1
Как прикинуть: 500 единиц в минуту — это десять новых счетов (по 50), или пятьсот проверок
статуса (по 1), или любое их сочетание на ту же сумму.
Сценарий: магазин
Задача: покупатель выбирает на кассе оплату криптовалютой, платит, и заказ сам становится
оплаченным. Понадобится: ключ мерчанта sk_live_… и URL для вебхуков.
Шаги
Счёт на заказ. Когда покупатель нажал «Оплатить криптой», сервер магазина создаёт счёт —
POST /v1/invoices: сумма в долларах
(fiat_amount) или сразу в монете, номер заказа в order_id, монета и сеть в
currency и network.
Реквизиты покупателю. Из ответа берёте address, amount — точную сумму
в монете — и expires_at. Нарисуйте их у себя вместе с QR или просто отправьте покупателя
на готовую страницу pay_url.
Оплата пришла. Когда перевод получен и подтверждён сетью, на ваш URL приходит
invoice.paid. Проверьте подпись, найдите заказ по order_id и отметьте его
оплаченным.
Деньги. Оплаты собираются на балансе мерчанта. Выводите их
выплатой или включите авто-конвертацию —
тогда всё будет копиться в одной монете, например в USDT.
bash
# счёт на $39.90 за заказ A-7731, оплата в USDT (TRON)
curl -s https://pay.666.exchange/v1/invoices \
-H "Authorization: Bearer sk_live_…" -H "Content-Type: application/json" \
-d '{ "fiat_amount":"39.90", "fiat_currency":"USD",
"currency":"USDT", "network":"TRON",
"order_id":"A-7731" }'
# ответ: { "id":"inv_…", "address":"T…", "amount":"39.94", "pay_url":"https://…", "expires_at":"…" }# потом на ваш URL придёт:# { "id":"evt_…", "event":"invoice.paid", "invoice":"inv_…", "order_id":"A-7731",# "status":"PAID", "amount":"39.94", "txid":"…", "test":false, "at":"…" }# сверьте X-666pay-Signature (HMAC-SHA256 сырого тела вашим секретом) и закрывайте заказ.
Сам факт вызова ничего не доказывает: сверяйте подпись и сумму в теле — подробно в разделе
Вебхуки. И отмечайте заказ оплаченным
один раз: повторный вебхук с тем же order_id не должен отгрузить товар второй раз.
Сценарий: Telegram-бот
Задача: продавать подписку или товар прямо в чате. Всё как у магазина, только номер заказа
указывает на пользователя Telegram, а об оплате ему сообщает сам бот.
Шаги
Кнопка «Купить». Бот создаёт счёт (POST /v1/invoices) и пишет в
order_id всё, что нужно, чтобы потом узнать покупку, — например
tg:<user_id>:<тариф>. Пользователю уходит адрес, сумма и QR или ссылка
pay_url.
Ждём оплату. Удобнее всего — вебхук invoice.paid. Если вебхуков нет, повесьте на
кнопку «Я оплатил» запрос GET /v1/invoices/{id}.
Выдаём. Из order_id пришедшего события достаньте user_id и тариф,
включите доступ и напишите пользователю.
json
// тело POST /v1/invoices: месяц подписки за $7 в TON
{ "fiat_amount":"7", "fiat_currency":"USD", "currency":"TON", "network":"TON",
"order_id":"tg:51733096:month", "ttl_minutes":20 }
// пришёл invoice.paid с order_id "tg:51733096:month"// → включить подписку пользователю 51733096 и написать ему в чат
Для бота лучше короткий срок счёта — ttl_minutes на 15–30 минут: просроченный счёт проще
выпустить заново кнопкой, чем держать открытым. У одного пользователя может быть несколько открытых
счетов одновременно, поэтому покупку определяйте по order_id, а не по пользователю.
Сценарий: выплаты пользователям
Задача: регулярно отправлять деньги своим пользователям — выигрыши, партнёрские, вывод с
биржевого счёта. Понадобится: ключ выплат pk_live_…. До первой выплаты включите
Список доверенных адресов и Доверенные IP для API: с ними украденный ключ ничего не
выведет.
Шаги
Хватит ли денег. Перед пачкой выплат запросите
GET /v1/balances: нужна сама монета и нативная монета
сети на комиссию.
Отправка. На каждого получателя — POST /v1/payouts: монета, сеть, адрес, сумма.
Ответ приходит сразу со статусом queued. Свой номер платежа положите в
comment.
Результат. Передайте в запросе merchant — и события payout.sent или
payout.failed придут на вебхук этого мерчанта. Без вебхука — опрашивайте
GET /v1/payouts/{id}. При failed сумма возвращается на баланс.
Сверка. Время от времени выгружайте
GET /v1/transactions: там в одном списке все
движения — выплаты, комиссии, возвраты.
Чтобы обрыв связи не обернулся двойной выплатой, передавайте заголовок Idempotency-Key
со своим номером платежа: повтор с тем же ключом и тем же телом вернёт уже созданную выплату, а не
отправит вторую. Без заголовка повторять вслепую нельзя — сначала найдите выплату по
comment в списке.
Сценарий: обменник
Задача: клиент отдаёт монету A, а на его адрес приходит монета B. Путь целиком: показать курс,
получить монету A, обменять, отправить B клиенту. Понадобится: ключ pk_live_….
Курс для клиента
Если клиент вводит, сколько отдаёт, передавайте amount; если сколько хочет
получить — amount_out. Клиенту показывайте min: меньше этого обмен не
выдаст. Все поля котировки — в разделе
Конвертация.
bash
# отдаёт 0.4 ETH — сколько USDT (TRON) получит
GET /v1/conversions/quote?from_asset=ETH&from_network=ETH&to_asset=USDT&to_network=TRON&amount=0.4
# хочет получить ровно 1000 USDT (TRON) — сколько ETH взять
GET /v1/conversions/quote?from_asset=ETH&from_network=ETH&to_asset=USDT&to_network=TRON&amount_out=1000
Обмен и отправка клиенту
/v1/exchange и /v1/conversions списывают монету с вашего баланса, так что
к моменту вызова она уже должна там быть. Есть два способа её туда получить.
Через счёт клиенту — без своего капитала
Выставляете клиенту счёт POST /v1/invoices в монете A; оплата попадает в пул.
На вебхук invoice.paid отвечаете вызовом
POST /v1/exchange с
source:"pool" и адресом клиента в recipient.
Обмен и выплата B идут сами; следите через GET /v1/exchange/{id} или вебхук.
Из буфера — быстрее, но нужны свои деньги
Заранее пополняете буфер: адреса — в GET /v1/buffer/addresses. Перевод зачислится сам;
POST /v1/buffer/scan лишь ускоряет проверку (см.
Буфер).
Монету A от клиента принимаете любым способом, хоть вне 666pay.
Вызываете POST /v1/exchange с source:"buffer", merchant:"mch_…" и
адресом клиента в recipient — обмен из буфера и выплата сразу.
bash
# обмен из буфера и выплата клиенту одним запросом
curl -s https://pay.666.exchange/v1/exchange \
-H "Authorization: Bearer pk_live_…" -H "Content-Type: application/json" \
-d '{ "from_asset":"USDT", "from_network":"TRON",
"to_asset":"SOL", "to_network":"SOL",
"amount":"150.00",
"recipient":"…адрес клиента в Solana",
"source":"buffer", "merchant":"mch_…" }'
# ответ: { "id":"cnv_…", "status":"queued", "payout":null }# позже GET /v1/exchange/cnv_… → status:done, to.arrived, payout:{ status:sent, tx }
Комиссия сети при отправке клиенту в quoted и min не входит — это
результат самого обмена. Дальше зависит от монеты, которую получает клиент:
· сети EVM (например, USDC на BSC или Arbitrum, BNB) — газ оплачивается отдельно из нативной
монеты буфера, клиенту приходит ровно arrived;
· BTC, LTC, SOL, SUI, TON — комиссия вычитается из самой монеты, клиенту приходит
arrived минус комиссия сети. Там, где она заметна (SUI около 0.005, TON около 0.01),
итог может оказаться меньше min.
Закладывайте эту комиссию в цену для клиента. Сколько ушло на самом деле, показывает
payout.amount в статусе обмена.
Счёт (invoice) — просьба заплатить определённую сумму, связанная с вашим order_id. Монету
и сеть можно задать сразу или оставить выбор плательщику. Деньги ложатся на баланс в той монете, в
которой пришли; если удобнее копить всё в стейблкоине, включите у мерчанта авто-конвертацию.
Путь счёта
Создание.POST /v1/invoices с монетой (currency), сетью
(network), суммой и order_id. Можно и без монеты — только с
fiat_amount: тогда монету и сеть плательщик выберет на странице оплаты.
Ожидание. Ответ содержит адрес, pay_url и expires_at; статус —
PENDING. У счёта без монеты адреса пока нет — он появится,
когда плательщик выберет монету, а до того есть только pay_url.
Перевод замечен. Транзакция появилась в сети — статус
PROCESSING. С этой минуты срок счёта уже не истечёт.
Подтверждения. Сеть набирает нужное их число, и сумма зачисляется на баланс мерчанта.
Оплачен. Статус PAID, на ваш URL уходит
invoice.paid.
С включённой у мерчанта AML-проверкой между подтверждениями и invoice.paid
есть ещё одна остановка: платёж проверяется на происхождение средств. Пока проверка не пройдена,
invoice.paid не будет, а задержанный платёж пришлёт invoice.aml_hold. См.
Вебхуки. Без AML этой остановки нет.
Как показать счёт плательщику
Способ
Что делать
Наша страница
Отправьте плательщика по pay_url (вида https://pay.666.exchange/i/8fK2xQ).
На ней уже есть QR, адрес, точная сумма, таймер и живой статус.
Своя вёрстка
Возьмите из ответа address и amount и покажите их у себя. За статусом
следите вебхуком или запросом GET /v1/invoices/:id.
Сумма в монете фиксируется при создании и держится до expires_at. Показывайте её
целиком, со всеми знаками: по точной сумме перевод и узнаётся в сети.
Пришло не столько, сколько просили
Меньше. Статус UNDERPAID. Фактически пришедшее видно в
paid_amount (рядом с запрошенным amount) и в поле amount вебхука.
Дальше решаете вы: принять как частичную оплату, попросить доплатить или вернуть.
Больше. Статус PAID, на баланс зачисляется вся пришедшая
сумма — paid_amount окажется больше amount. Вернуть разницу плательщику можно
только самостоятельно.
Выставить счёт
POSThttps://pay.666.exchange/v1/invoices
Ключ — мерчанта (sk_live_… или sk_test_…). Повтор с тем же order_id
не создаёт второй счёт, а возвращает уже существующий, поэтому запрос безопасно повторять.
Тело запроса
Поле
Что значит
order_idОБЯЗ
string — номер заказа в вашей системе. В пределах мерчанта не
повторяется: второй запрос с ним вернёт прежний счёт.
amountОБЯЗ*
string — сумма в монете счёта, строкой: "19.00"
(у числа с плавающей точкой копейки теряются). Знаки сверх точности монеты отбрасываются.
Нужно одно из двух: amount или fiat_amount.
fiat_amountОПЦ*
string — сумма в долларах. Количество монеты посчитаем по текущему
курсу и закрепим за счётом; в ответе будут amount (в монете),
fiat_amount и rate. Одно из двух с amount.
fiat_currencyОПЦ
string — валюта fiat_amount. Сейчас поддерживается
одна — USD, она же по умолчанию.
currencyОПЦ**
string — монета оплаты: USDT, USDC,
BTC, ETH, SOL и остальные из
справочника; счёт номинирован в ней.
Не указывайте вместе с network — и монету выберет плательщик (тогда нужен
fiat_amount).
networkОПЦ**
string — сеть монеты: TRON, BSC,
ETH, TON, BTC… Сочетание монеты и сети должно быть в
справочнике, иначе вернётся 422.
Передаётся только в паре с currency.
ttl_minutesОПЦ
integer — сколько минут ждать оплату: от 5 до 1440, без указания —
30.
descriptionОПЦ
string — подпись для плательщика на странице оплаты, до 160 знаков.
Счёт без монеты — это fiat_amount без currency и
network. Плательщик выбирает монету и сеть из включённых у мерчанта, и только в этот
момент фиксируются точная сумма и адрес. Монету и сеть передают вместе или не передают вовсе.
// Node 18+: встроенный fetchconst r =awaitfetch('https://pay.666.exchange/v1/invoices', {method:'POST',headers: {Authorization:'Bearer '+ process.env.PAY666_KEY,'Content-Type':'application/json'},body: JSON.stringify({order_id: cart.number,amount:'19.00',currency:'USDC',network:'SOL'}),});const bill =await r.json();// дальше — редирект на bill.pay_url или свой экран с bill.address и bill.amount
// 201 — счёт создан (повтор с тем же order_id вернёт его же){"id":"inv_3Tw9Ld","order_id":"B-2041","status":"pending","test":false,"currency":"USDC","network":"SOL","amount":"19",// три поля ниже заполнены, только если счёт выставлен в долларах (fiat_amount)"fiat_amount":null,"fiat_currency":null,"rate":null,"address":"7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU","pay_url":"https://pay.666.exchange/i/3Tw9Ld","confirmations_required":32,"confirmed":0,"tx_id":null,"paid_amount":null,"settled_amount":null,"expires_at":"2026-11-02T14:30:00.000Z","created_at":"2026-11-02T14:00:00.000Z","paid_at":null}
Статус и отмена счёта
GEThttps://pay.666.exchange/v1/invoices/:id
Где :id — идентификатор inv_…, полученный при создании. Знаете только свой
order_id? Повторите POST /v1/invoices с ним: вернётся тот же счёт со свежим
статусом.
О том, что счёт оплачен, сообщит вебхук.
Этот метод — для сверки, например если вебхук не дошёл; крутить его в цикле в ожидании оплаты не нужно.
json
// 200 — поля те же, что при создании, плюс данные оплаты{"id":"inv_3Tw9Ld","order_id":"B-2041","status":"paid","currency":"USDC","network":"SOL","amount":"19","address":"7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU","confirmations_required":32,"confirmed":32,"tx_id":"5Vf1Qm…","paid_amount":"19","settled_amount":"19","paid_at":"2026-11-02T14:06:41.000Z"}
Без тела. Работает, только пока счёт в PENDING; в ответ приходит счёт со статусом
canceled.
Если перевод уже замечен в сети (PROCESSING) или счёт завершён, ответ — 409 с
кодом not_pending: деньги в пути не остановить. Тот же 409 вернётся и при
повторной отмене.
Все счета мерчанта
GEThttps://pay.666.exchange/v1/invoices
Счета того мерчанта, чей ключ в запросе, от новых к старым. Счета других мерчантов аккаунта в выдачу
не попадают.
Фильтры и страницы
Query
Что значит
statusОПЦ
string — только счета в этом статусе: pending,
processing, paid, underpaid или expired.
order_idОПЦ
string — поиск по номеру вашего заказа. Номер у мерчанта уникален,
поэтому в ответе будет ноль или один счёт.
limitОПЦ
integer — размер страницы: 20, если не указано, и не больше 100.
cursorОПЦ
string — откуда продолжать: значение next_cursor из
предыдущего ответа.
// 200{"data": [{"id":"inv_Q0w9Er","order_id":"B-2033","status":"underpaid",/* остальное — как в статусе счёта */},{"id":"inv_Lp4sYc","order_id":"B-2019","status":"underpaid",/* … */}],"has_more":true,"next_cursor":"inv_Lp4sYc"}
Элементы data устроены так же, как ответ
статуса счёта.
Как обойти все страницы
Страницы листаются курсором, а не номером. Пока в ответе has_more: true, запрашивайте
следующую с cursor, равным next_cursor. Последняя страница узнаётся по
has_more: false и next_cursor: null. Счета, появившиеся во время обхода, ничего
не сдвигают — строки не пропадут и не повторятся.
Вебхуки счетов
Когда счёт доходит до итогового состояния, мы отправляем POST с JSON на URL, указанный в
настройках мерчанта. Ответьте любым кодом 2xx — всё остальное считается неудачей, и
доставка будет повторена.
Какие события приходят
event
Что произошло
Что делать
invoice.paid
Оплата подтверждена и зачислена.
Выдавать товар. Других поводов для выдачи нет.
invoice.underpaid
Оплата подтверждена, но меньше суммы счёта. Дальше счёт не меняется.
Решить: частичная оплата, доплата или возврат.
invoice.aml_hold
Только с AML-проверкой: деньги пришли, но задержаны до решения.
Не выдавать, ждать следующего события.
invoice.aml_cleared
Задержанный платёж одобрен вручную.
Ничего — следом придёт invoice.paid.
invoice.aml_rejected
Задержанный платёж отклонён, деньги возвращены отправителю.
Заказ не выполнять: invoice.paid по нему не будет.
С AML-проверкой порядок другой. Платёж сначала проверяется, и только потом может прийти
invoice.paid. Пока идёт проверка, событий нет. Платёж с низким риском проходит сам — сразу
invoice.paid. Задержанный даёт invoice.aml_hold, а затем одно из двух:
invoice.aml_cleared и следом invoice.paid — или invoice.aml_rejected,
и на этом всё. Без AML-проверки invoice.paid приходит сразу после подтверждений сети.
О промежуточном processing и об истечении срока (expired) вебхуков нет:
эти состояния смотрите запросом статуса.
Как выглядит запрос
http
POST /payments/666pay-callback
X-666pay-Event: invoice.paid
X-666pay-Signature: 3c1e7d0b52a94f6e8d2b77c0a9e41f5b…
{"id":"evt_R2mX7a",// событие; повтор доставки придёт с тем же id"event":"invoice.paid","invoice":"inv_3Tw9Ld",// счёт — его можно запросить GET /v1/invoices/:id"order_id":"B-2041","status":"PAID","amount":"19",// фактически пришло, в монете счёта"test":false,// true — событие из песочницы"txid":"5Vf1Qm…","at":"2026-11-02T14:06:41.000Z"}
Подпись
URL вебхука может вызвать кто угодно. Без проверки подписи любой сможет сообщить вашему магазину,
что заказ «оплачен».
X-666pay-Signature — это HMAC-SHA256 в виде hex (без префикса), посчитанный от тела
запроса как есть, байт в байт, с секретом вебхуков мерчанта в качестве ключа. Считайте подпись до
разбора JSON: если сначала разобрать, а потом сериализовать обратно, байты будут другими и подпись не
совпадёт.
import{ createHmac, timingSafeEqual }from'node:crypto';// тело нужно сырым: express.raw, а не express.json
app.post('/payments/666pay-callback', express.raw({type:'application/json'}),(req, res)=>{const expected =createHmac('sha256', process.env.PAY666_HOOK_SECRET).update(req.body).digest();const given = Buffer.from(String(req.get('x-666pay-signature') ||''),'hex');// сравнение за постоянное время: по времени ответа подпись не подобратьif(given.length !== expected.length || !timingSafeEqual(given, expected))return res.sendStatus(401);const evt = JSON.parse(req.body);if(evt.event ==='invoice.paid')fulfil(evt.order_id, evt.amount, evt.id);
res.sendStatus(200);});
Не получили от вас 2xx — пробуем снова, каждый раз выжидая дольше (но не более 6 часов
между попытками). Всего попыток 12, это около суток; после последней доставка прекращается.
Одно событие может прийти дважды: например, ваш ответ потерялся по дороге, и мы повторили отправку.
Запоминайте обработанные id событий (evt_…) — тогда второй
invoice.paid не приведёт к повторной выдаче.
Отправить выплату
POSThttps://pay.666.exchange/v1/payouts
Только с ключом выплат pk_live_…; ключ мерчанта sk_live_… получит
401.
Пока не включён Список доверенных адресов (Настройки → Безопасность), деньги можно
отправить на любой правильный адрес. С ним — только на адреса из списка, прочие получат
422 not_whitelisted. Включите его до первой выплаты: это главная защита на случай утечки
ключа.
Тело запроса
Поле
Что значит
assetОБЯЗ
string — монета: USDT, USDC, BTC,
ETH, SOL, TRX, XMR и любая другая из
справочника.
networkОБЯЗ
string — сеть. EVM: BSC, ETH,
ARBITRUM, OP, BASE, POLYGON, AVAX;
а также TRON, BTC, LTC, DOGE, BCH,
ZEC, SOL, SUI, TON, XMR.
addressОБЯЗ
string — куда отправить. Адрес должен подходить к сети, а при
включённом списке доверенных адресов — ещё и быть в нём.
amountОБЯЗ
string — сколько списать с баланса. Комиссия сети берётся из этой
суммы, получатель увидит amount − fee. Подробнее — ниже, в «Комиссии».
commentОПЦ
string — ваша пометка для учёта; в блокчейн не записывается.
sourceОПЦ
string — откуда платить: pool (по умолчанию) или
buffer, см. Буфер.
merchantОПЦ
string — uid мерчанта. Для source=buffer — чей буфер
тратить; в любом случае — на чей вебхук прислать итог выплаты.
json
// 201 — выплата принята в очередь{"id":"pay_2Hn6wQ9tV4mZ","status":"queued","asset":"USDT","network":"TRON","amount":"30.00","fee":"1.1","address":"TQ9xr4LmA8kz2Vb7pPq1Hs6Df3GjWt5Nc","tx":null,"comment":"withdrawal W-55120","created_at":"2026-11-02T15:12:00.000Z"}
status
Значит
queued
Ждёт отправки. Поле tx пока null.
sent
Транзакция подтверждена сетью, хеш — в tx.
failed
Отправить не удалось, сумма вернулась на баланс.
Без вебхука следите за выплатой запросом GET /v1/payouts/{id}, пока не появится хеш и
статус sent.
Комиссия заранее
GET /v1/payouts/fee?asset=…&network=… отдаёт текущую оценку комиссии сети. Она важна,
когда получатель должен получить точную сумму: в части сетей комиссия списывается из перевода.
json
// GET /v1/payouts/fee?asset=SOL&network=SOL{"asset":"SOL","network":"SOL","subtracted_from_amount":true,"network_fee":"0.000005"}
subtracted_from_amount
Сети
Получатель увидит
false
BTC, LTC, DOGE, BCH, ZEC; все токены; нативные монеты EVM — газ у них оплачивается отдельно
Ровно amount. Добавлять комиссию не нужно — иначе он получит лишнее.
И в том и в другом случае network_fee — это запас, который должен лежать на балансе
поверх суммы выплаты: комиссию платит отправитель, даже когда из перевода она не вычитается.
Итог выплаты вебхуком
Передайте при создании merchant — и на вебхук этого мерчанта (Настройки приёма → Вебхук)
придёт payout.sent или payout.failed, подписанный так же, как события счетов.
В теле: { payout, status, source, asset, network, address, amount, fee, tx, comment }. У выплат
из буфера (source:"buffer") мерчант известен всегда, поэтому вебхук по ним приходит и без
лишних полей.
Баланс
Сколько денег на счёте аккаунта прямо сейчас — их можно вывести или обменять. Ключ — pk_live_…,
как у выплат: баланс общий для всего аккаунта.
GEThttps://pay.666.exchange/v1/balances
Query
Тип
Что значит
source
string
pool — баланс мерчантов (так по умолчанию)
или buffer — буфер обменника; для буфера нужен ещё merchant.
merchant
string
uid мерчанта вида mch_…; обязателен,
если source=buffer.
network
string
Показать только одну сеть: TRON,
BSC и т.п.
json
// GET /v1/balances?network=TRON{"source":"pool","balances": [{"asset":"USDT","network":"TRON","amount":"1250.23","usd":1250.23},{"asset":"TRX","network":"TRON","amount":"41.5","usd":13.94}],"total_usd":"1264.17"}
Суммы — строки, чтобы ничего не округлилось по дороге. usd — оценка по текущему курсу,
null, если курса нет.
Нулевые остатки не показываются; порядок — от самого дорогого в долларах.
Одна монета в разных сетях — разные строки: USDT/TRON и USDT/BSC не
складываются.
Что такое буфер и зачем он обменнику — в разделе Буфер.
Выписка
Каждое изменение баланса аккаунта — одной лентой, от свежих к старым: зачисление по оплаченному счёту,
списание на выплату, обе стороны обмена, комиссии сети. Ключ — pk_live_…. Знак суммы
показывает направление: + пришло, − ушло.
GEThttps://pay.666.exchange/v1/transactions
Query
Тип
Что значит
asset
string
Одна монета, например USDT.
network
string
Одна сеть: TRON, BSC…
kind
string
Вид записи: invoice_settled,
payout, payout_fee, fee, refund,
convert_out, convert_in, adjustment.
limit
number
От 1 до 100, без указания — 30.
cursor
number
Продолжение: next_cursor прошлого ответа.
json
// GET /v1/transactions?asset=USDT&limit=2{"transactions": [{"id":912,"kind":"payout","asset":"USDT","network":"TRON","amount":"-30","ref":{"type":"payout","id":"pay_2Hn6wQ9tV4mZ"} },{"id":907,"kind":"invoice_settled","asset":"USDT","network":"TRON","amount":"39.94","ref":{"type":"invoice","id":"inv_…"} }],"next_cursor":906}
ref указывает, из-за чего появилась запись — счёт, выплата или обмен, — так движение легко
сопоставить с объектом, который у вас уже есть. Чтобы листать дальше, передавайте
next_cursor в cursor; когда он станет null, записи кончились.
Конвертация
Обмен одной монеты баланса на другую по рыночному маршруту — через те же DEX и мосты, что работают в
конвертере панели. Для обменника это середина пути: монета A пришла по счёту (/v1/invoices),
здесь превращается в B, а B уходит клиенту выплатой (/v1/payouts). Если хотите обмен и
выплату одним вызовом — см. Обмен с выплатой.
Курс не фиксируется: на баланс приходит то, что реально дал обмен. Если вы обещаете клиенту
фиксированный курс, запас и маржу закладывайте сами. Комиссии за конвертацию у нас нет — только
расходы сети на исполнение.
1. Узнать курс
GEThttps://pay.666.exchange/v1/conversions/quote
Ничего не списывает и ничего не запускает — только считает. Можно спрашивать сколько угодно (в
пределах лимитов).
json
// GET /v1/conversions/quote?from_asset=USDT&from_network=TRON&to_asset=ETH&to_network=ETH&amount=300{"from": {"asset":"USDT","network":"TRON","amount":"300"},"to": {"asset":"ETH","network":"ETH","quoted":"0.0961","min":"0.0951"},"provider":"oneclick","exact":false,"quoted_at":"2026-11-02T16:01:12Z"}
Поле
Как понимать
to.quoted
Ожидаемый результат.
to.min
Нижняя граница с учётом проскальзывания. Клиенту обещайте её, а не
quoted.
exact
true — обмен на DEX в сети, результат совпадёт с
quoted до последнего знака. false — оценка агрегатора: придёт не меньше
min, обычно около quoted.
Сколько пришло на самом деле, всегда видно в to.arrived — в статусе и в вебхуке.
2. Запустить обмен
POSThttps://pay.666.exchange/v1/conversions
Это операция с деньгами аккаунта, поэтому ключ — pk_live_…; ключ мерчанта
sk_live_… получит 401.
Поле
Что значит
from_assetОБЯЗ
string — что отдаём,
например USDT.
from_networkОБЯЗ
string — сеть этой
монеты, например TRON. Пара должна быть в
справочнике.
to_assetОБЯЗ
string — что получаем,
например ETH.
to_networkОБЯЗ
string — сеть
получаемой монеты.
amountОБЯЗ
string — сколько
списать с баланса. Лишние знаки отбрасываются вниз до точности монеты.
via_asset, via_networkОПЦ
string —
промежуточная монета для маршрута в несколько шагов; берутся из route.via котировки,
передаются вдвоём. См. ниже.
providerОПЦ
string — заставить
использовать конкретный маршрут; без него выбирается лучший из исполнимых.
merchantОПЦ
string — uid
мерчанта. Тогда итог (conversion.done или conversion.failed) придёт на его
вебхук. Без него вебхука не будет — спрашивайте статус.
json
// 201 — исходная сумма уже списана, обмен в очереди{"id":"cnv_K8RW2NQX4LTP","status":"queued","from": {"asset":"USDT","network":"TRON","amount":"300"},"to": {"asset":"ETH","network":"ETH","quoted":null,"min":null,"arrived":null},"provider":"oneclick","source":"pool","src_tx":null,"dst_tx":null,"in_usd":"300.00","out_usd":null,"error":null,"created_at":"2026-11-02T16:02:40.000Z"}
Не у каждой пары есть прямой путь: из USDT/BSC, например, сразу в NOT не
попасть. Тогда котировка сама строит маршрут через ликвидную монету и возвращает блок
route: сколько шагов (hops) и через что (via). Особенно это
выручает, когда резерв лежит в одной монете — без промежуточного шага из неё достижимо далеко не всё.
json
// GET /v1/conversions/quote?from_asset=USDT&from_network=BSC&to_asset=NOT&to_network=TON&amount=5{"to": {"asset":"NOT","network":"TON","quoted":"13748.8","min":"13611.3"},"provider":"oneclick+stonfi","exact":false,"route": {"hops":2,"via": {"asset":"USDT","network":"TON"} }}
Сколько шагов. Два или три. Третий нужен, когда редкую монету сначала надо обменять на стейбл
в её же сети, — так становятся возможными пары вроде USDe/TON → PAXG/ETH. У такого ответа
route.hops: 3, вторая промежуточная монета — в route.via2, а в
route.legs три записи. Маршрут подбирается под пару и сумму и между запросами может
меняться, так что смотрите на route.hops, а не рассчитывайте на фиксированное число.
Как исполнить. Передайте в создание via_asset и via_network из
route.via — оба вместе. Это работает в POST /v1/conversions и в
POST /v1/exchange; оценка /v1/exchange/estimate возвращает тот же
route.via. Указывать нужно только первую промежуточную монету: остальные шаги подбираются
автоматически под фактически полученную сумму, route.via2 — для справки.
Что происходит. Цепочка связанных обменов: первый меняет A на промежуточную монету, следующий
берёт то, что реально пришло, и так до конечной монеты. Вы видите одну запись A → C. Сколько
шагов вышло на деле, показывает via.hops в GET /v1/exchange/:id — маршрут может
стать длиннее уже после создания.
Если шаг сорвался. Деньги не возвращаются в A, а остаются в последней промежуточной
монете, до которой дошли. Это штатно: доведите остаток обычной конвертацией.
Прямая доставка.delivery: direct совместима с такими маршрутами: последний обмен
сразу отдаёт монету на адрес получателя, без отдельного вывода. Требование EVM здесь касается
промежуточной монеты, а не исходной, поэтому даже источник вне EVM (например, GRAM) доставляется
напрямую через EVM-посредника. Если посредник не EVM (скажем, USDT/TON), запрос будет
отклонён с понятной ошибкой — используйте two_leg.
Обратный расчёт
Когда заказ сформулирован как «клиент должен получить ровно N», передавайте amount_out
вместо amount — и сервер сам подберёт, сколько нужно отдать. Подбор сделан так, чтобы
гарантированный min был не меньше цели. Из двух полей amount и
amount_out передаётся одно.
json
// клиент должен получить 0.5 SOL, платит USDT (TRON) — сколько USDT брать?// GET /v1/conversions/quote?from_asset=USDT&from_network=TRON&to_asset=SOL&to_network=SOL&amount_out=0.5{"mode":"exact_out","from": {"asset":"USDT","network":"TRON","amount_in":"81.402117","estimated":true},"to": {"asset":"SOL","network":"SOL","target":"0.5","quoted":"0.5061","min":"0.5011"},"provider":"oneclick","exact":false}
С клиента берите from.amount_in плюс свою маржу и на эту сумму запускайте
/v1/exchange. Флаг
estimated: true значит, что вход подобран расчётом (обмен идёт от входа, а не от выхода), но
условие to.min ≥ target гарантирует клиенту не меньше заказанного.
Статус конвертации
GEThttps://pay.666.exchange/v1/conversions/:id
Ключ — pk_live_…. Все конвертации списком: GET /v1/conversions с фильтрами
status и limit.
status
Что происходит
queued
Сумма списана, обмен ждёт очереди.
sent
Обмен отправлен в сеть.
done
Готово: на баланс пришло to.arrived.
failed
Не вышло; исходная сумма вернулась на баланс, причина — в
error.
json
// 200 — обмен завершён{"id":"cnv_K8RW2NQX4LTP","status":"done","from": {"asset":"USDT","network":"TRON","amount":"300"},"to": {"asset":"ETH","network":"ETH","quoted":"0.0961","arrived":"0.09634","min":"0.0951"},"provider":"oneclick","source":"pool","src_tx":"7b1f0e…a42c","dst_tx":"0x9e4d…31b7","in_usd":"300.00","out_usd":"299.12","error":null,"created_at":"2026-11-02T16:02:40.000Z"}
На баланс зачисляется то, что реально пришло, — to.arrived, а не quoted.
Вебхук конвертаций
Отдельный адрес не настраивается: события конвертаций идут на тот же вебхук мерчанта, что и
события счетов, с тем же секретом. Найти его: карточка мерчанта → Настройки приёма → вкладка «Вебхук».
Событие приходит, только если конвертация привязана к мерчанту. Авто-конвертация берёт мерчанта из
счёта сама; при ручном POST /v1/conversions передайте merchant. В конце
приходит одно из двух: conversion.done или conversion.failed.
json
// тело POST на ваш URL; подпись — в X-666pay-Signature{"id":"evt_W4cN8pY2tB","event":"conversion.done","conversion":"cnv_K8RW2NQX4LTP","status":"DONE","from": {"asset":"USDT","network":"TRON","amount":"300"},"to": {"asset":"ETH","network":"ETH","arrived":"0.09634","min":"0.0951"},"src_tx":"7b1f0e…a42c","dst_tx":"0x9e4d…31b7","in_usd":"300.00","out_usd":"299.12","error":null,"at":"2026-11-02T16:04:57.000Z"}
Всё как у вебхуков счетов: подпись
X-666pay-Signature — HMAC-SHA256 от тела как есть с вашим секретом; повторы отсеивайте по
id события; отвечайте 2xx, иначе доставка повторится с нарастающей паузой.
Обмен с выплатой
Один запрос вместо двух: конвертирует A в B и отправляет полученное клиенту. Когда обмен завершён,
выплата уходит сама — из того же источника, откуда списали A (пул → обычная выплата, буфер → вывод из
буфера). Ключ — pk_live_….
POSThttps://pay.666.exchange/v1/exchange
Поле
Что значит
from_asset, from_networkОБЯЗ
string —
что списываем с баланса.
to_asset, to_networkОБЯЗ
string —
что получает клиент.
amountОБЯЗ
string — сколько A
обменять.
recipientОБЯЗ
string — адрес клиента
в сети to_network.
recipient_tagОПЦ
string — тег или
memo получателя, если сеть его требует (TON, XMR и т.п.).
payout_amountОПЦ
string — отправить
клиенту ровно столько, а не всё, что пришло. Больше пришедшего не уйдёт никогда; излишек обмена
остаётся у вас. Без поля клиент получает весь результат.
sourceОПЦ
string —
pool (по умолчанию) или buffer.
merchantОПЦ
string — uid мерчанта;
обязателен для source:"buffer".
deliveryОПЦ
string —
auto (по умолчанию), direct или two_leg. При
direct обмен сам доставляет монету на recipient одной операцией, без
отдельного вывода и его комиссии; доступно для источника в сети EVM (или через EVM-посредника).
auto выбирает direct, когда можно. С payout_amountdirect не сочетается.
via_asset, via_networkОПЦ
string —
промежуточная монета для пары без прямого пути, из route.via оценки
/v1/exchange/estimate. Без них запрос ищет только прямой путь и, если его нет,
сразу получает отказ — деньги клиента не застрянут в промежуточной монете.
providerОПЦ
string — принудительно
выбрать маршрут.
Повторять запрос после обрыва связи безопасно с заголовком Idempotency-Key: тот же ключ и то
же тело вернут уже созданный обмен, а не создадут второй. Заголовок понимают все методы, которые двигают
деньги, — POST /v1/payouts, /v1/conversions и /v1/exchange.
json
// POST /v1/exchange Idempotency-Key: ord-58812{"from_asset":"USDT","from_network":"TRON","to_asset":"TON","to_network":"TON","amount":"60","recipient":"UQ…адрес клиента","recipient_tag":"58812","source":"buffer","merchant":"mch_…"}// ответ: обмен создан, выплата появится после него{"id":"cnv_…","status":"queued","recipient":"UQ…","payout":null}
Как следить
GET /v1/exchange/{id} показывает обе части сразу: статус обмена, to.arrived и
объект payout — null, пока обмен не done, затем
{ id, status, amount, tx }. Итог выплаты придёт и вебхуком: payout.sent или
payout.failed.
json
// GET /v1/exchange/cnv_… — всё завершено{"id":"cnv_…","status":"done","to": {"asset":"TON","network":"TON","arrived":"11.82"},"recipient":"UQ…","payout": {"id":"pay_…","status":"sent","amount":"11.81","tx":"…"}}
Сколько дойдёт до клиента. Для монет в сетях EVM газ платится отдельно, из нативной монеты
буфера, и клиент получает весь arrived. У BTC, LTC, SOL, SUI, TON комиссия сети вычитается
из самой монеты — до клиента дойдёт arrived минус она, а на SUI и TON это бывает меньше
min котировки. Закладывайте комиссию в цену; точная отправленная сумма — в
payout.amount.
Буфер обменника
Буфер — свой кошелёк мерчанта, отдельный от денег, которые платят ему клиенты. Обменник сам кладёт
туда оборотные средства в удобной монете и из них расплачивается с клиентами — обменом или прямой
выплатой. Оплаты по счетам в буфер не попадают: всё, что в нём лежит, принадлежит мерчанту.
За пополнение и вывод мы ничего не берём (по умолчанию 0%). Газ сети платится из нативной монеты
самого буфера, поэтому держите в нём натив тех сетей, где работаете: BNB для BSC, ETH для Ethereum и
так далее. Без него обмен или выплата не пройдут.
Адреса — там же или через API (ключ pk_live_…):
GET /v1/buffer/addresses?merchant=mch_…. Адреса постоянные, на каждый можно отправлять
сколько угодно раз.
Перевод — просто отправьте монету. Шлюз сам проходит по сетям буфера и зачисляет пришедшее
после подтверждений, обычно за несколько минут; сети, где недавно было движение, проверяются чаще.
Ускорить — по желанию: POST /v1/buffer/scan проверяет все сети сразу и зачисляет
найденное за вычетом fee_bps. Повторный вызов ничего не задвоит: каждый перевод
засчитывается один раз.
// GET /v1/buffer/addresses?merchant=mch_… — куда отправлять{"fee_bps":0,"addresses": [{"network":"TRON","network_label":"TRON (TRC-20)","address":"T…"},{"network":"SOL","network_label":"Solana","address":"Ds3i…"}] }// POST /v1/buffer/scan { "merchant": "mch_…" } — проверить прямо сейчас (обычно не нужно){"balances": [ {"asset":"USDC","network":"SOL","amount":"500","usd":500} ] }
Сколько в буфере сейчас: GET /v1/balances?source=buffer&merchant=mch_…. Что с этими
деньгами можно делать: вывести или
обменять.
Вывод из буфера
Отдельного метода нет: это обычная выплата,
у которой источник — буфер. Добавьте в тело source: "buffer" и uid мерчанта.
POSThttps://pay.666.exchange/v1/payouts
json
// тело запроса, ключ pk_live_…{"source":"buffer","merchant":"mch_…","asset":"USDC","network":"SOL","address":"…адрес в Solana","amount":"250"}
Статус — как у любой выплаты: GET /v1/payouts/{id} и вебхуки
payout.*.
Сети — все, что есть у буфера: EVM, TRON, BTC, LTC,
DOGE, BCH, ZEC, SOL, SUI, TON
(нативные монеты и токены) и XMR.
Газ — всегда из буфера. Токены (EVM, SPL, Move, jetton) платят его нативной монетой буфера в
той же сети; её нет — выплата отклоняется. У нативных монет и UTXO-сетей комиссия вычитается из суммы:
получатель увидит amount − комиссия.
Из панели — кнопка ↗ у баланса на вкладке «Буфер»; окно заранее показывает комиссию.
Обмен внутри буфера
Та же конвертация, с теми же
маршрутами, только монета берётся из буфера и результат возвращается туда же. В тело добавьте
source: "buffer" и uid мерчанта.
json
// POST /v1/conversions, ключ pk_live_… — перегнать резерв из Solana в TRON{"source":"buffer","merchant":"mch_…","from_asset":"USDC","from_network":"SOL","to_asset":"USDT","to_network":"TRON","amount":"200"}
Статус и вебхук — те же, что у обычной конвертации.
Газ — из нативной монеты буфера в сети источника. Это касается и обмена токенов из сетей вне
EVM — USDT в Solana, TON или TRON, USDC в Sui: газ берётся из SOL, GRAM или TRX буфера. Если нативной
монеты нет, будет отказ с объяснением.
Чего нельзя — обменять внутри самой сети TRON (TRX ↔ USDT-TRC20) из буфера пока не получится:
обменяйте через другую сеть или выведите.
Из панели — кнопка ⇄ у баланса на вкладке «Буфер».
Монеты и сети
GEThttps://pay.666.exchange/v1/currencies
Справочник того, что принимается и отправляется. code и network отсюда — это
ровно те значения, что идут в currency и network счёта. Список меняется, поэтому
читайте его через API (тем же ключом, что и счета), а не переписывайте таблицу ниже в код.
json
// 200 — по объекту на каждую пару «монета + сеть»{"currencies": [{"code":"USDT","network":"TRON","name":"Tether","label":"TRON (TRC-20)","decimals":6,"confirmations":20,"type":"token","swappable":true}]}
swappable — можно ли монету менять через
/v1/conversions и
/v1/exchange; считается по тому,
какие маршруты доступны прямо сейчас.
confirmations — сколько подтверждений сети ждём перед зачислением.
Ключу обменника, которому нужен только /v1/exchange, хватит короткого списка
GET /v1/exchange/currencies: code, network, decimals,
type, swappable.
Адрес действует только для своей пары «монета + сеть». Монета, отправленная по другой сети, не
зачислится, и достать её мы не сможем — ключей от неё у нас нет. Предупредите об этом плательщиков.
{currency} — код монеты: BTC, ETH, USDT… Ключ — как
для счетов (sk_live_…). В ответе на каждую валюту одна строка, где course —
сколько to дают за 1 from (строкой, 8 знаков после точки). Это то, что обычно
показывают в окне обмена до оформления заявки.
json
// GET /v1/exchange-rate/BTC/list{"base":"BTC","rates": [{"from":"BTC","to":"USD","course":"81240.00000000"},{"from":"BTC","to":"USDT","course":"81251.17400000"},{"from":"BTC","to":"SOL","course":"498.31207561"}],"source":"reference","updated_at":"2026-11-02T16:10:00Z"}
source: "reference" значит «рыночный ориентир»: маршрут, комиссии и проскальзывание тут не
учтены, и по этому курсу ничего не исполнится. Цену настоящего обмена даёт
/v1/conversions/quote — там же и
расчёт от суммы на выходе.
Все направления сразу, в привычном мониторингам формате BestChange, и курс в нём уже очищен от наших
комиссий — обменнику остаётся добавить свою наценку и отдать фид мониторингу. Строится из прайс-фида,
не дёргая провайдеров, поэтому охватывает тысячи пар и спокойно выдерживает частый опрос.
Ключ — в ?key= (мониторинг ходит по голой ссылке без заголовков) или как обычно, в
Authorization.
Ответ — application/xml, кэшируется на 30 секунд.
Коды монет — по правилам BestChange: монета и тег сети подряд (USDTTRC20,
USDTBEP20, USDCERC20, ETHARBTM, ETHBASE), а нативная
монета в своей сети — одним тикером (BTC, ETH, TRX,
BNB).
out — уже за вычетом нашей части (фиксированная сеть плюс проскальзывание); свою маржу
добавляете вы.
Лимиты — из настроек, по умолчанию от $10 до $50 000 в единицах from. Привязки к
глубине конкретного пула пока нет.
xml
<rates>
<item>
<from>USDTTRC20</from> <!-- что отдаёт клиент -->
<to>SOL</to> <!-- что получает -->
<in>1</in>
<out>0.00611720</out> <!-- за 1 from, уже без наших комиссий -->
<amount>812.4</amount> <!-- резерв монеты to -->
<minamount>10</minamount> <!-- лимиты — в единицах from -->
<maxamount>50000</maxamount>
</item>
<!-- …и так по всем направлениям -->
</rates>
Статусы счёта
Статусов шесть, из них в работе два — остальные итоговые и больше не меняются.
Статус
Значит
Итоговый
PENDING
Счёт выставлен, ждём перевод. Если он не придёт до expires_at, счёт истечёт.
нет
PROCESSING
Перевод уже в сети и копит подтверждения. Истечь счёт больше не может.
нет
PAID
Подтверждения набраны, деньги на балансе.
да
UNDERPAID
Подтверждено, но paid_amount меньше amount.
да
EXPIRED
Срок вышел, перевода не было.
да
CANCELED
Вы отменили счёт, пока он был в PENDING.
да
Куда может перейти счёт
PENDING ──┬──► PROCESSING ──┬──► PAID // деньги на балансе — только этим путём
│ └──► UNDERPAID // подтверждено, но меньше счёта
├──► EXPIRED // срок вышел без перевода
└──► CANCELED // отменили вы
Не выдавайте товар на PROCESSING: пока подтверждений мало, транзакцию ещё может отменить
перестройка цепочки блоков. Сигнал к выдаче — PAID.
Отдельного статуса для AML-проверки нет. С включённой проверкой счёт может уже стоять в
PAID, а платёж — ещё ждать решения и не быть зачисленным. Поэтому выдавайте товар по
вебхуку invoice.paid — он приходит только после проверки, — а не по одному лишь статусу. См.
Вебхуки.