Выплаты крипты по API: автоматизация для обменника
Обновлено: 4 сентября 2026
Приём платежей прощает ошибки: худшее — деньги придут, а вы этого не заметите. Выплата не прощает ничего: неверный адрес, двойная отправка или угнанный ключ — это деньги, которые ушли навсегда. Разбираем, как автоматизировать вывод средств так, чтобы спать спокойно: идемпотентность, лимиты и честная работа с ошибками.
Почему выплата — самая опасная операция
Криптоперевод необратим: нет чарджбэков, звонка в банк и «отменить платёж». Из этого следуют три требования к любой автоматизации выплат:
- Повтор запроса не должен отправлять деньги дважды. Сеть моргнула, ваш бэкенд не дождался ответа и повторил запрос — классический сценарий двойной выплаты.
- Компрометация ключа не должна опустошать кошелёк. Ключ с правом выплат — самая ценная строка в вашем .env; нужен план на случай его утечки до того, как она случится.
- Ошибка должна быть кодом, а не текстом. Автоматика ветвится по машинному коду ошибки; парсить человекочитаемые сообщения — мина: формулировки меняются.
Идемпотентность: один ключ — одна отправка
Правильный API выплат принимает от вас ключ идемпотентности — ваш собственный идентификатор вывода. Сколько бы раз вы ни повторили запрос с тем же ключом, отправка произойдёт один раз:
- первый запрос →
202: выплата принята в очередь; - повтор с тем же
idempotencyKey→200: вернётся та же выплата, второй отправки не будет.
Поэтому ретрай при обрыве связи безопасен по построению. Правило на вашей стороне одно:
ключ идемпотентности — это id вывода в вашей системе (например,
withdraw-91422), а не случайная строка, сгенерированная на каждый HTTP-запрос.
Случайный ключ превращает защиту в бутафорию: каждый ретрай выглядит новой выплатой.
Запрос выплаты
Выплата — один POST. Получатель получает ровно amount:
комиссия сети берётся сверху из баланса проекта, добавлять её к сумме не нужно.
curl -X POST https://coinrail.net/v1/payouts \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"coin": "BTC",
"toAddress": "bc1q...",
"amount": "0.0009",
"feeRate": 8,
"idempotencyKey": "withdraw-91422"
}'
Нюансы по монетам:
- BTC и LTC: комиссию сети (
feeRate, сат/vByte) выбираете вы — шлюз её не навязывает. Все адреса проекта — один HD-кошелёк: входы для транзакции собираются автоматически, крупные первыми, сдача возвращается на внутренний адрес. Заниженный feeRate отвергается до отправки — узел такую транзакцию не принял бы. - USDT (TRC20): комиссию сети платит шлюз, поэтому feeRate нет вовсе.
За выплату удерживается фикс — 2.5 USDT в типовом случае (комиссия закрыта арендованной
энергией) или 4 USDT при сжигании TRX; обе ставки видны заранее в
POST /v1/payouts/estimate. Почему ставок две — в гайде про энергию TRON.
Прежде чем слать деньги, дешевле спросить: estimate отвечает, пройдёт ли
выплата, какой будет комиссия и плата — без движения средств.
Статусы и ошибки: чему верить
Жизненный цикл: QUEUED → BROADCASTING → SENT
(есть txHash) либо FAILED — средства не ушли. Финал приходит
вебхуком; поллинг не нужен.
У неудачи всегда есть машинный failureCode — контракт, по которому ветвится
ваша автоматика. Несколько кодов, на которые стоит запрограммировать реакцию заранее:
SEND_LIMIT_EXCEEDED— упёрлись в лимит на выплату или суточный. Не ошибка, а сработавшая страховка: см. следующий раздел.RECIPIENT_BLACKLISTED— адрес получателя заблокирован эмитентом USDT. Перевод бы откатился, а комиссия сгорела — шлюз проверяет заранее. Верните вывод клиенту на уточнение реквизитов.GAS_UNAVAILABLE— шлюз не смог оплатить комиссию сети TRON. Деньги не двигались, повтор безопасен.BROADCAST_STUCK— единственный код, требующий ручной сверки: отправка зависла и снята по таймауту, транзакция могла уйти. Не ретрайте вслепую — сверьте по txHash.
Поле failureReason — текст для человека и логов. Показывайте оператору,
но не парсьте: его формулировки — не контракт.
Лимиты — страховка от угона ключа
Представьте худшее: ключ с правом выплат утёк. Что остановит вывод всего кошелька? Только лимиты, настроенные до инцидента:
- Лимит на одну выплату и на сутки — по каждой монете отдельно. В кабинете их можно только ужесточить, ослабить выше глобальных нельзя. Настройте под свой реальный оборот: лимит «с запасом в десять раз» — это не страховка, а её имитация.
- Скоуп ключа. Ключ с
payouts:writeдержите только на бэкенде казны. Фронтенду, боту поддержки и аналитике достаточноread— утечка такого ключа неприятна, но деньги не двигает. - IP-allowlist — включается в кабинете под PIN: даже украденный ключ бесполезен с чужого адреса.
- Не храните лишнего. Кошелёк шлюза — операционная касса, а не сейф: накопленное выводите на собственное холодное хранилище. Это главная защита, которая работает даже при полной компрометации.
Частые вопросы
Что будет, если я отправлю один запрос выплаты дважды?
Нужно ли прибавлять комиссию сети к сумме выплаты?
Сколько стоит выплата?
Выплата ушла в FAILED — деньги в безопасности?
Как ограничить ущерб, если API-ключ украдут?
Дальше: полный справочник выплат — в документации API; как устроен приём — в гайде про USDT TRC20; кто держит ключи от кошелька — в гайде про кастодиальность.