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

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

666pay API. Один ключ — все операции.

REST поверх HTTPS, тела и ответы в JSON. Счёт, вебхук об оплате, выплата и обмен монет — всё делается ключом в заголовке, без SDK и без звонков менеджеру.

база https://pay.666.exchange/v1 ключ Authorization: Bearer sk_… суммы строками: "49.00"

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

Три шага от пустого магазина до оплаченного счёта. Всё это работает в песочнице — без реальных денег.

I

Получите ключ

Панель → Мерчанты → создайте магазин, задайте адрес вебхуков и секрет. Ключ sk_test_… работает сразу, боевой sk_live_… — после модерации. Про ключи подробнее.

II

Создайте счёт

В ответе — адрес, точная сумма в монете и готовая страница оплаты pay_url.

curl
curl -X POST https://pay.666.exchange/v1/invoices \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{ "fiat_amount": "49.00", "currency": "USDT", "network": "TRON", "order_id": "10482" }'
III

Обработайте вебхук

Когда деньги пришли и подтвердились, на ваш адрес прилетит invoice.paid. Сверьте подпись X-666pay-Signature — HMAC-SHA256 от сырого тела вашим секретом — и отгружайте заказ. Как проверять подпись.

тело вебхука
{
  "id": "evt_5Kq8vN",
  "event": "invoice.paid",
  "invoice": "inv_8fK2xQ",
  "order_id": "10482",
  "status": "PAID",
  "test": true
}

Разделы

Ключи API

У 666pay три вида ключей. Ключ приёма живёт у конкретного мерчанта и управляет только его счетами; ключ выплат один на весь аккаунт и двигает деньги с баланса.

Ключ Где взять Что умеет
sk_test_… Сразу после создания мерчанта Тестовые счета без денег и без блокчейна
sk_live_… После модерации мерчанта Настоящие счета: адрес, наблюдение за сетью, зачисление на баланс
pk_live_… Настройки → API → Выплатной API-ключ Выплаты, балансы, история и конвертация по всему аккаунту

Ключ мерчанта

  1. В панели откройте Мерчанты и нажмите Создать мерчанта: название, сайт или бот.
  2. В карточке мерчанта укажите URL для вебхуков и придумайте секрет — им подписываются уведомления.
  3. Скопируйте sk_test_… и начинайте интеграцию в песочнице.
  4. Когда всё готово, отправьте мерчанта на проверку — после неё появится sk_live_….

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

Песочница

Счета, созданные ключом sk_test_…, нужны для отладки и денег не касаются:

  • адреса нет и сеть не просматривается — в ответе "test": true и "address": null;
  • на странице оплаты (pay_url) есть кнопки, которые разыгрывают paid или underpaid;
  • вебхук уходит настоящий и подписанный, только с "test": true — обработчик проверяется так же, как в бою;
  • баланс мерчанта не меняется.

Ключ выплат

Выпускается в панели: Настройки → API → Выплатной API-ключ. Подтверждение — тем же кодом из приложения, что и вывод средств. Ключ показывается один раз; при перевыпуске старый отзывается немедленно.

С этим ключом можно вывести деньги, поэтому ограничьте его заранее: включите Список доверенных адресов — выплаты уйдут только на них — и Доверенные IP для API — запросы примутся только с ваших серверов.

Запросы и ответы

BASE https://pay.666.exchange

Только HTTPS. Запросы с телом шлются в JSON (UTF-8), ответы тоже приходят в JSON. Суммы передаются строками, чтобы не терять знаки после запятой.

Как представиться

Достаточно ключа в заголовке — тело запроса подписывать не нужно: канал шифрует TLS, а секрет здесь сам ключ.

Заголовок Значение
Authorization Bearer sk_live_…, Bearer sk_test_… или Bearer pk_live_…. Тот же ключ принимается и в X-Api-Key — если ваш HTTP-клиент не умеет Bearer.
Content-Type application/json — у каждого запроса с телом.
curl
curl https://pay.666.exchange/v1/invoices \
  -H "Authorization: Bearer sk_test_7Rk2Wq0Lm9ZbTn4Xc1Ve8Hy3" \
  -H "Content-Type: application/json" \
  -d '{"amount":"12.50","currency":"USDT","network":"TRON","order_id":"A-7731"}'

Когда что-то не так

Ответ с кодом 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. invalid_request, unsupported_asset, amount_too_small
429 Израсходован бюджет запросов — см. Лимиты. Сколько ждать, скажет Retry-After (в секундах). too_many_requests, rate_limit_banned
503 Адрес для оплаты сейчас выдать нельзя: модуль подписи недоступен. Запрос можно повторить. signer_down

Лимиты

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

Правила

  1. Запас пополняется плавно — примерно на 8.33 единицы в секунду, пока не дойдёт до полного (500). Весь запас можно потратить разом; дальше он набирается снова.
  2. Остаток виден в каждом ответе: X-RateLimit-Limit — размер запаса, X-RateLimit-Cost — сколько стоил этот вызов, X-RateLimit-Remaining — сколько осталось.
  3. Не хватило — ответ 429 с кодом too_many_requests и заголовком Retry-After. На это время ключ блокируется целиком.
  4. Повторные нарушения удлиняют паузу. Первая блокировка — 10 секунд, каждая следующая вдвое дольше, но не больше 1 часа. Пока она действует, отвечает 429 с кодом rate_limit_banned любой метод, даже самый дешёвый. Поработайте без нарушений — и отсчёт начнётся заново с 10 секунд. Отсюда правило: после 429 ждите ровно Retry-After, а не повторяйте запрос сразу.
  5. Счёт ведётся отдельно для каждого ключа — sk_ и pk_ друг другу запас не тратят.

Числа ниже — значения по умолчанию, их можно поменять для вашего аккаунта. В коде полагайтесь на заголовки X-RateLimit-*, а не на эту таблицу.

Цена методов

Метод Вес
POST /v1/invoices50
POST /v1/payouts, POST /v1/conversions, POST /v1/exchange25
POST /v1/buffer/scan20
GET /v1/conversions/quote, GET /v1/exchange/estimate, GET /v1/exchange-rate/{cur}/list, POST /v1/invoices/{id}/cancel5
Списки: GET /v1/invoices, /v1/payouts, /v1/conversions, /v1/exchange; а также /v1/exchange/currencies, /v1/balances, /v1/transactions, /v1/buffer/addresses2
Один объект: GET /v1/invoices/{id}, /v1/payouts/{id}, /v1/conversions/{id}, /v1/exchange/{id}; а также /v1/payouts/fee и /v1/currencies1

Как прикинуть: 500 единиц в минуту — это десять новых счетов (по 50), или пятьсот проверок статуса (по 1), или любое их сочетание на ту же сумму.

Сценарий: магазин

Задача: покупатель выбирает на кассе оплату криптовалютой, платит, и заказ сам становится оплаченным. Понадобится: ключ мерчанта sk_live_… и URL для вебхуков.

Шаги

  1. Счёт на заказ. Когда покупатель нажал «Оплатить криптой», сервер магазина создаёт счёт — POST /v1/invoices: сумма в долларах (fiat_amount) или сразу в монете, номер заказа в order_id, монета и сеть в currency и network.
  2. Реквизиты покупателю. Из ответа берёте address, amount — точную сумму в монете — и expires_at. Нарисуйте их у себя вместе с QR или просто отправьте покупателя на готовую страницу pay_url.
  3. Оплата пришла. Когда перевод получен и подтверждён сетью, на ваш URL приходит invoice.paid. Проверьте подпись, найдите заказ по order_id и отметьте его оплаченным.
  4. Деньги. Оплаты собираются на балансе мерчанта. Выводите их выплатой или включите авто-конвертацию — тогда всё будет копиться в одной монете, например в 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, а об оплате ему сообщает сам бот.

Шаги

  1. Кнопка «Купить». Бот создаёт счёт (POST /v1/invoices) и пишет в order_id всё, что нужно, чтобы потом узнать покупку, — например tg:<user_id>:<тариф>. Пользователю уходит адрес, сумма и QR или ссылка pay_url.
  2. Ждём оплату. Удобнее всего — вебхук invoice.paid. Если вебхуков нет, повесьте на кнопку «Я оплатил» запрос GET /v1/invoices/{id}.
  3. Выдаём. Из 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: с ними украденный ключ ничего не выведет.

Шаги

  1. Хватит ли денег. Перед пачкой выплат запросите GET /v1/balances: нужна сама монета и нативная монета сети на комиссию.
  2. Отправка. На каждого получателя — POST /v1/payouts: монета, сеть, адрес, сумма. Ответ приходит сразу со статусом queued. Свой номер платежа положите в comment.
  3. Результат. Передайте в запросе merchant — и события payout.sent или payout.failed придут на вебхук этого мерчанта. Без вебхука — опрашивайте GET /v1/payouts/{id}. При failed сумма возвращается на баланс.
  4. Сверка. Время от времени выгружайте GET /v1/transactions: там в одном списке все движения — выплаты, комиссии, возвраты.
bash
curl -s https://pay.666.exchange/v1/payouts \
  -H "Authorization: Bearer pk_live_…" -H "Content-Type: application/json" \
  -d '{ "asset":"USDT", "network":"TRON",
        "address":"T…адрес получателя",
        "amount":"30.00",
        "comment":"withdrawal W-55120",
        "merchant":"mch_…" }'   # события payout.* придут на вебхук этого мерчанта
# ответ: { "id":"pay_…", "status":"queued", "fee":"…" }

Чтобы обрыв связи не обернулся двойной выплатой, передавайте заголовок 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 списывают монету с вашего баланса, так что к моменту вызова она уже должна там быть. Есть два способа её туда получить.

Через счёт клиенту — без своего капитала

  1. Выставляете клиенту счёт POST /v1/invoices в монете A; оплата попадает в пул.
  2. На вебхук invoice.paid отвечаете вызовом POST /v1/exchange с source:"pool" и адресом клиента в recipient.
  3. Обмен и выплата B идут сами; следите через GET /v1/exchange/{id} или вебхук.

Из буфера — быстрее, но нужны свои деньги

  1. Заранее пополняете буфер: адреса — в GET /v1/buffer/addresses. Перевод зачислится сам; POST /v1/buffer/scan лишь ускоряет проверку (см. Буфер).
  2. Монету A от клиента принимаете любым способом, хоть вне 666pay.
  3. Вызываете 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. Монету и сеть можно задать сразу или оставить выбор плательщику. Деньги ложатся на баланс в той монете, в которой пришли; если удобнее копить всё в стейблкоине, включите у мерчанта авто-конвертацию.

Путь счёта

  1. Создание. POST /v1/invoices с монетой (currency), сетью (network), суммой и order_id. Можно и без монеты — только с fiat_amount: тогда монету и сеть плательщик выберет на странице оплаты.
  2. Ожидание. Ответ содержит адрес, pay_url и expires_at; статус — PENDING. У счёта без монеты адреса пока нет — он появится, когда плательщик выберет монету, а до того есть только pay_url.
  3. Перевод замечен. Транзакция появилась в сети — статус PROCESSING. С этой минуты срок счёта уже не истечёт.
  4. Подтверждения. Сеть набирает нужное их число, и сумма зачисляется на баланс мерчанта.
  5. Оплачен. Статус 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. Вернуть разницу плательщику можно только самостоятельно.

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

POST https://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. Плательщик выбирает монету и сеть из включённых у мерчанта, и только в этот момент фиксируются точная сумма и адрес. Монету и сеть передают вместе или не передают вовсе.

Примеры

curl -X POST https://pay.666.exchange/v1/invoices \
  -H "Authorization: Bearer $PAY666_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "B-2041",
    "amount": "19.00",
    "currency": "USDC",
    "network": "SOL"
  }'
// Node 18+: встроенный fetch
const r = await fetch('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
$body = json_encode(['order_id' => $cart->number, 'amount' => '19.00', 'currency' => 'USDC', 'network' => 'SOL']);
$ctx = stream_context_create(['http' => [
  'method'  => 'POST',
  'header'  => "Authorization: Bearer " . getenv('PAY666_KEY') . "\r\nContent-Type: application/json",
  'content' => $body,
]]);
$bill = json_decode(file_get_contents('https://pay.666.exchange/v1/invoices', false, $ctx), true);

Что вернётся

json
// 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
}

Статус и отмена счёта

GET https://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"
}

Отменить счёт

POST https://pay.666.exchange/v1/invoices/:id/cancel

Без тела. Работает, только пока счёт в PENDING; в ответ приходит счёт со статусом canceled.

Если перевод уже замечен в сети (PROCESSING) или счёт завершён, ответ — 409 с кодом not_pending: деньги в пути не остановить. Тот же 409 вернётся и при повторной отмене.

Все счета мерчанта

GET https://pay.666.exchange/v1/invoices

Счета того мерчанта, чей ключ в запросе, от новых к старым. Счета других мерчантов аккаунта в выдачу не попадают.

Фильтры и страницы

Query Что значит
statusОПЦ string — только счета в этом статусе: pending, processing, paid, underpaid или expired.
order_idОПЦ string — поиск по номеру вашего заказа. Номер у мерчанта уникален, поэтому в ответе будет ноль или один счёт.
limitОПЦ integer — размер страницы: 20, если не указано, и не больше 100.
cursorОПЦ string — откуда продолжать: значение next_cursor из предыдущего ответа.
curl
curl "https://pay.666.exchange/v1/invoices?status=underpaid&limit=50" \
  -H "Authorization: Bearer $PAY666_KEY"
json
// 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);
});
$body     = file_get_contents('php://input');
$given    = $_SERVER['HTTP_X_666PAY_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $body, getenv('PAY666_HOOK_SECRET'));

if (!hash_equals($expected, $given)) { http_response_code(401); exit; }

$evt = json_decode($body, true);
if ($evt['event'] === 'invoice.paid') { fulfil($evt['order_id'], $evt['amount'], $evt['id']); }
http_response_code(200);

Повторная доставка

Не получили от вас 2xx — пробуем снова, каждый раз выжидая дольше (но не более 6 часов между попытками). Всего попыток 12, это около суток; после последней доставка прекращается.

Одно событие может прийти дважды: например, ваш ответ потерялся по дороге, и мы повторили отправку. Запоминайте обработанные id событий (evt_…) — тогда второй invoice.paid не приведёт к повторной выдаче.

Отправить выплату

POST https://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. Добавлять комиссию не нужно — иначе он получит лишнее.
true Нативные SOL, GRAM, SUI, TRX, XMR amount − network_fee. Нужна точная сумма — отправляйте сумма + network_fee.

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

Итог выплаты вебхуком

Передайте при создании merchant — и на вебхук этого мерчанта (Настройки приёма → Вебхук) придёт payout.sent или payout.failed, подписанный так же, как события счетов. В теле: { payout, status, source, asset, network, address, amount, fee, tx, comment }. У выплат из буфера (source:"buffer") мерчант известен всегда, поэтому вебхук по ним приходит и без лишних полей.

Баланс

Сколько денег на счёте аккаунта прямо сейчас — их можно вывести или обменять. Ключ — pk_live_…, как у выплат: баланс общий для всего аккаунта.

GET https://pay.666.exchange/v1/balances
QueryТипЧто значит
sourcestringpool — баланс мерчантов (так по умолчанию) или buffer — буфер обменника; для буфера нужен ещё merchant.
merchantstringuid мерчанта вида mch_…; обязателен, если source=buffer.
networkstringПоказать только одну сеть: 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_…. Знак суммы показывает направление: + пришло, − ушло.

GET https://pay.666.exchange/v1/transactions
QueryТипЧто значит
assetstringОдна монета, например USDT.
networkstringОдна сеть: TRON, BSC…
kindstringВид записи: invoice_settled, payout, payout_fee, fee, refund, convert_out, convert_in, adjustment.
limitnumberОт 1 до 100, без указания — 30.
cursornumberПродолжение: 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. Узнать курс

GET https://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.
exacttrue — обмен на DEX в сети, результат совпадёт с quoted до последнего знака. false — оценка агрегатора: придёт не меньше min, обычно около quoted.

Сколько пришло на самом деле, всегда видно в to.arrived — в статусе и в вебхуке.

2. Запустить обмен

POST https://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"
}

Ответ приходит сразу, со статусом queued. До done следите через GET /v1/conversions/{id} или вебхук.

Маршрут через промежуточную монету

Не у каждой пары есть прямой путь: из 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 гарантирует клиенту не меньше заказанного.

Статус конвертации

GET https://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_….

POST https://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_amount direct не сочетается.
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 и так далее. Без него обмен или выплата не пройдут.

Включить и пополнить

  1. Включение — в панели: карточка мерчанта → Настройки приёма → вкладка «Буфер».
  2. Адреса — там же или через API (ключ pk_live_…): GET /v1/buffer/addresses?merchant=mch_…. Адреса постоянные, на каждый можно отправлять сколько угодно раз.
  3. Перевод — просто отправьте монету. Шлюз сам проходит по сетям буфера и зачисляет пришедшее после подтверждений, обычно за несколько минут; сети, где недавно было движение, проверяются чаще.
  4. Ускорить — по желанию: POST /v1/buffer/scan проверяет все сети сразу и зачисляет найденное за вычетом fee_bps. Повторный вызов ничего не задвоит: каждый перевод засчитывается один раз.
GET https://pay.666.exchange/v1/buffer/addresses?merchant=mch_…
json
// 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 мерчанта.

POST https://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) из буфера пока не получится: обменяйте через другую сеть или выведите.
  • Из панели — кнопка ⇄ у баланса на вкладке «Буфер».

Монеты и сети

GET https://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.

Адрес действует только для своей пары «монета + сеть». Монета, отправленная по другой сети, не зачислится, и достать её мы не сможем — ключей от неё у нас нет. Предупредите об этом плательщиков.

Сейчас в справочнике

СетьПодтвержденийМонеты
TRON
TRON
20USDT, TRX
BSC
BNB Chain
15USDT, USDC, ETH, WBNB, DAI, BTC, SHIB, TUSD, USD1, CAKE, TWT, BNB
ETH
Ethereum
12USDT, USDC, WETH, DAI, SHIB, TUSD, USD1, USDS, USDP, PYUSD, ONDO, USDe, ENA, USDD, GHO, WBTC, AAVE, BAT, DUSK, LINK, MANA, PAXG, PEPE, POL, UNI, XAUT, ZRX, ETH
ARBITRUM
Arbitrum
12USDT, USDC, ARB, WETH, DAI, GHO, WBTC, ETH
BASE
Base
12USDC, WETH, DAI, WBTC, ETH
OP
Optimism
12USDT, USDC, OP, DAI, WBTC, ETH
POLYGON
Polygon
30USDT, USDC, WETH, DAI, WBTC, POL
AVAX
Avalanche C-Chain
12USDT, USDC, AVAX
SOL
Solana
32USDT, JUP, PONKE, TRUMP, WSOL, USDC, SOL
TON
TON
1USDT, NOT, USDe, GRAM
SUI
Sui
1USDC, SUI
BTC
Bitcoin
1BTC
LTC
Litecoin
4LTC
DOGE
Dogecoin
6DOGE
BCH
Bitcoin Cash
1BCH
ZEC
Zcash
3ZEC
XMR
Monero
10XMR

Курсы

Два источника курсов: справочный — для одной монеты ко всем остальным, и XML-фид всех направлений для мониторингов обменников.

Курс монеты

GET https://pay.666.exchange/v1/exchange-rate/{currency}/list

{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 — там же и расчёт от суммы на выходе.

XML-фид для мониторингов

GET https://pay.666.exchange/v1/rates.xml?key=sk_live_…

Все направления сразу, в привычном мониторингам формате 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 — он приходит только после проверки, — а не по одному лишь статусу. См. Вебхуки.