Инвойсы¶
Инвойс — это запрос на оплату, привязанный к одному заказу. При его создании за инвойсом закрепляется депозитный адрес в выбранной вами сети, и всё, что придёт на этот адрес, засчитывается именно ему и никакому другому.
Создание инвойса¶
curl -X POST https://api.pay.nullswap.com/api/v1/invoices \
-H "x-api-key: sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"expectedAmount": "25500000",
"expiresInSeconds": 3600,
"externalId": "order-10241"
}'
| Поле | Тип | Обяз. | Примечания |
|---|---|---|---|
chainId |
UUID | да | Сеть, в которой будет платить покупатель. Берётся из GET /api/v1/chains |
tokenId |
UUID | да | Должен относиться к сети chainId, иначе запрос вернёт 404 TOKEN_NOT_FOUND, и принимать платежи (srcStatus: ENABLED), иначе — 422 TOKEN_IS_DISABLED |
expectedAmount |
десятичная строка | да | Строго положительная, в базовых единицах токена |
expiresInSeconds |
целое число | да | 60–604800 (от 1 минуты до 7 дней) |
externalId |
строка | null | нет | Ваш собственный номер заказа. Уникален в пределах мерчанта |
acceptsAnyToken |
булево | нет | По умолчанию false. См. Альтернативные токены |
autoSweep |
булево | нет | По умолчанию true. См. Перевод средств на баланс |
Поля merchantId здесь нет: мерчанта уже определяет ваш API-ключ.
В ответе приходит сам инвойс:
{
"id": "b2f4a6c8-0d1e-4f23-9a45-6b7c8d9e0f11",
"merchantId": "9f1d2c3b-4a5e-4f60-8b71-2c3d4e5f6a7b",
"depositWalletId": "7e2a9c14-8b3d-4c56-a789-0f1e2d3c4b5a",
"depositAddress": "TQ5s...9Xk2",
"depositTag": null,
"depositXAddress": null,
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"expectedAmount": "25500000",
"acceptsAnyToken": false,
"autoSweep": true,
"paidAmount": "0",
"pendingAmount": "0",
"externalId": "order-10241",
"status": "WAITING",
"amlStatus": null,
"createdAt": "2026-01-01T12:00:00.000Z",
"expiresAt": "2026-01-01T13:00:00.000Z",
"updatedAt": "2026-01-01T12:00:00.000Z"
}
Покажите покупателю depositAddress и точную сумму expectedAmount, а после наступления expiresAt
уберите их с экрана. На RIPPLE вместе с ними покажите depositTag с пометкой «обязательно»: там
один адрес инвойс не определяет. См. Тег назначения на RIPPLE. На всех
остальных сетях depositTag и depositXAddress равны null.
Всегда отправляйте externalId
Именно он не даёт одному заказу получить два депозитных адреса, если запрос на создание отвалился
по таймауту и ваш клиент его повторил. Повторный запрос вернёт
409 INVOICE_EXTERNAL_ID_ALREADY_EXISTS вместо второго инвойса — чтобы восстановиться,
найдите исходный через GET /api/v1/invoices?externalId=order-10241&limit=1. Так же его можно
найти и позже, не сохраняя у себя наш идентификатор.
Ошибки¶
Проверки идут именно в таком порядке, так что вы получите ошибку по первому нарушенному правилу:
| Статус | message |
Причина |
|---|---|---|
403 |
MERCHANT_BLOCKED |
Мерчант заблокирован. Проверяется по вашему ключу, ещё до чтения тела |
400 |
INVALID_REQUEST_BODY |
Поля нет или у него неверный JSON-тип, например autoSweep — не булево |
422 |
INVALID_INVOICE_EXPIRATION |
expiresInSeconds вне диапазона 60–604800 |
422 |
INVALID_INVOICE_AMOUNT |
expectedAmount — не строка с положительным целым числом базовых единиц |
409 |
INVOICE_EXTERNAL_ID_ALREADY_EXISTS |
Этот externalId вы уже использовали |
404 |
CHAIN_NOT_FOUND |
Неизвестный chainId |
404 |
TOKEN_NOT_FOUND |
Неизвестный tokenId или токен относится к другой сети |
422 |
TOKEN_IS_DISABLED |
Токен сейчас не принимает платежи: его srcStatus равен DISABLED. См. токены |
502 |
INTERNAL_ERROR |
Не удалось выделить депозитный адрес. Запрос можно повторить |
Тег назначения на RIPPLE¶
На RIPPLE, то есть в XRP Ledger, депозитный адрес принадлежит не только вам. Все инвойсы RIPPLE
оплачиваются на один и тот же depositAddress, а к своему инвойсу платёж привязывается по тегу
назначения (destination tag), который он несёт, и больше ни по чему. Этот тег инвойс называет в
двух дополнительных полях:
| Поле | Тип | Примечания |
|---|---|---|
depositTag |
целое число | null | Тег назначения, с которым должен прийти платёж, 0–4294967295. Теги не последовательны: показывайте полученный, никогда не вычисляйте его сами |
depositXAddress |
строка | null | depositAddress и depositTag, упакованные в один X-адрес (X…), — для кошельков, которые принимают адрес без тега. Платёж на него — то же самое, что платёж на пару |
{
"id": "b2f4a6c8-0d1e-4f23-9a45-6b7c8d9e0f11",
"depositWalletId": "7e2a9c14-8b3d-4c56-a789-0f1e2d3c4b5a",
"depositAddress": "rHsMGQEkVNJmpGWs8XUBoTBiAAbwxZN5v3",
"depositTag": 959725,
"depositXAddress": "XVMFQQBMhdouRqhPMuawgBMN1AVFToG5NjwwoxoAYuiUyDF",
"expectedAmount": "25000000",
"status": "WAITING"
}
Покажите покупателю адрес, тег — подписанный как обязательный, а не как необязательная заметка, — и
точную сумму. Можно показать и depositXAddress как альтернативу паре; именно его и стоит
закладывать в QR-код: кошелёк, который его отсканирует, получит адрес и тег вместе. Оба поля есть и
в data каждого вебхука invoice.*.
Без нужного тега платёж до инвойса не дойдёт
Платёж на depositAddress без тега назначения сеть отклоняет, и кошелёк покупателя показывает
его как неудавшийся. Платёж с тегом, отличным от depositTag, этому инвойсу не засчитывается:
тег, который мы не выдавали, удерживается и автоматически ни одному инвойсу не засчитывается —
обратитесь в поддержку с хешем транзакции. В формах вывода на биржах поле тега часто подписано
как «необязательное»; предупредите покупателя, что это не так.
Засчитывается то, что платёж доставил на адрес, а не сумма, которая в нём указана: в XRP Ledger платёж можно отправить как частичный (partial payment), и считается только то, что дошло. Дальше недоплата и переплата работают как в любой другой сети — см. Недоплата и переплата.
Чтение инвойсов¶
| Метод | Путь | Назначение |
|---|---|---|
GET |
/api/v1/invoices/{invoiceId} |
Один инвойс |
GET |
/api/v1/invoices |
Список с фильтрами и пагинацией |
GET |
/api/v1/invoices/{invoiceId}/payments |
Отдельные ончейн-депозиты — см. Платежи по инвойсу |
GET /api/v1/invoices фильтрует по id, chainId, tokenId, status и externalId — каждый из
них принимает несколько значений — плюс offset, limit, order и sortBy (CREATED_AT,
EXPIRES_AT, ID).
Опрос — это запасной вариант, а не интеграция
Подпишитесь на вебхуки, а опрос держите как задачу сверки, которую запускают после простоя вашего эндпоинта. Ни long-polling, ни потокового эндпоинта здесь нет.
Жизненный цикл статусов¶
stateDiagram-v2
direction LR
[*] --> WAITING
WAITING --> PAYMENT_DETECTED
WAITING --> PARTIALLY_PAID
WAITING --> PAID
WAITING --> EXPIRED
PAYMENT_DETECTED --> WAITING
PAYMENT_DETECTED --> PARTIALLY_PAID
PAYMENT_DETECTED --> PAID
PAYMENT_DETECTED --> EXPIRED
PARTIALLY_PAID --> WAITING
PARTIALLY_PAID --> PAYMENT_DETECTED
PARTIALLY_PAID --> PAID
PARTIALLY_PAID --> EXPIRED
PAID --> WAITING
PAID --> PAYMENT_DETECTED
PAID --> PARTIALLY_PAID
WAITING --> SWAPPING
PAYMENT_DETECTED --> SWAPPING
PARTIALLY_PAID --> SWAPPING
PAID --> SWAPPING
SWAPPING --> COMPLETED
SWAPPING --> FAILED
FAILED --> SWAPPING
FAILED --> COMPLETED
COMPLETED --> [*]
EXPIRED --> [*]
| Статус | Что означает |
|---|---|
WAITING |
Создан, в сети пока ничего не видно |
PAYMENT_DETECTED |
Транзакция видна, но ещё не подтверждена и не прошла скрининг |
PARTIALLY_PAID |
Пришли прошедшие проверку деньги, но paidAmount всё ещё меньше expectedAmount |
PAID |
paidAmount >= expectedAmount — именно по нему нужно исполнять заказ |
SWAPPING |
Свип депозитного адреса начался, и его деньги идут на ваш баланс |
COMPLETED |
Ни один свип депозитного адреса не идёт и не провалился: деньги на вашем балансе |
FAILED |
Свип депозитного адреса сдался. Деньги всё ещё на адресе, и их заберёт следующий свип |
EXPIRED |
expiresAt наступил без достаточной оплаты |
Четыре детали на диаграмме легко не заметить:
- Статус может откатываться назад, в том числе из
PAID. Транзакция, вытесненная реоргом, забирает свою сумму с собой: инвойс возвращается изPARTIALLY_PAIDобратно вWAITING, а инвойс вPAID— вWAITING,PAYMENT_DETECTEDилиPARTIALLY_PAID, смотря по оставшимся суммам. Поэтому обработчик должен считать тело каждого вебхука снимком текущего состояния, а не приращением к предыдущему. - Вперёд из
PAIDведёт единственный путь — вSWAPPING. Пока инвойс оплачен, он не истечёт, а деньги, пришедшие в это время, запишутся как неожиданный платёж и наpaidAmountне повлияют. Вывести инвойс изPAIDможет только откат платежа; после начала свипа откат платежа статус назад уже не двигает. SWAPPING,COMPLETEDиFAILEDследуют за свипами, а не за оплатой. Автоматический свип переводит инвойс изPAIDвSWAPPING, когда свип принят; частично оплаченный инвойс продолжает ждать остаток. Ваш собственный вызов эндпоинта свипа переводит инвойс вSWAPPING, как только вызов отправил деньги в путь, — изWAITING,PAYMENT_DETECTEDиPARTIALLY_PAIDтак же, как изPAID. В обоих случаях инвойс становитсяCOMPLETED, когда ни один свип адреса не идёт и не провалился; оставшуюся на нём пыль никто не ждёт.FAILED— не конец. Когда свип инвойса вSWAPPINGсдаётся после всех повторов, инвойс становитсяFAILED, и вы получаетеinvoice.failed. Деньги всё ещё на депозитном адресе: следующий свип — автоматический или запрошенный вами — возвращает инвойс вSWAPPINGили сразу вCOMPLETED, когда зачисляется.
invoice.paid может быть отозван
Реорганизация сети может откатить платёж уже после отправки invoice.paid. Тогда инвойс выходит
из PAID, вы получаете invoice.payment_failed (и invoice.partially_paid, если он попадает в
PARTIALLY_PAID), а при повторной оплате invoice.paid придёт снова. Если то, что вы отдаёте по
invoice.paid, нельзя вернуть, перед выдачей убедитесь, что инвойс всё ещё PAID.
COMPLETED не значит «оплачен»
Запрошенный вами свип переводит инвойс в SWAPPING, сколько бы по нему ни заплатили, поэтому у
инвойса в COMPLETED paidAmount может быть меньше expectedAmount или равен "0", если
свипнут был только депозит в другом токене. После свипа статус уже не говорит, оплачен ли
заказ: решайте это по paidAmount до того, как вызывать свип.
COMPLETED и EXPIRED — терминальные состояния. Суммы после этого ещё могут уточняться,
поэтому запоздавшая транзакция по истёкшему инвойсу будет записана, а не потеряна, — но статус уже не
изменится.
Две суммы¶
pendingAmount и paidAmount меняются независимо друг от друга и означают разное.
| Учитывает деньги, которые… | Сравнивается с expectedAmount? |
|
|---|---|---|
pendingAmount |
видны в сети, но ещё не подтверждены и не прошли скрининг | нет |
paidAmount |
подтверждены и прошли скрининг | да |
На обе суммы влияют только платежи в той сети и в том токене, которые указаны в инвойсе. Депозиты в другом токене — никогда, что бы вы потом с ними ни решили делать. Статус выводится уже из этих двух величин:
paidAmount >= expectedAmount -> PAID
paidAmount > 0 -> PARTIALLY_PAID
pendingAmount > 0 -> PAYMENT_DETECTED
иначе -> WAITING
pendingAmount — это не деньги, которые у вас есть
Они пока только видны в сети. Вашими они становятся, когда переходят в paidAmount.
Инвойс никогда не достигнет PAID за счёт средств, не прошедших скрининг: помеченные деньги
вычитаются из pendingAmount и никогда не добавляются в paidAmount.
Недоплата и переплата¶
Неверную сумму инвойс не отклоняет — он записывает ровно то, что пришло.
Недоплата. Инвойс остаётся в PARTIALLY_PAID. Покупатель может доплатить до expiresAt,
несколько депозитов складываются. Если срок вышел, а суммы так и не хватило, инвойс закрывается с
уже записанным paidAmount: деньги не потеряны, но инвойс закрыт, и что делать дальше — решать вам.
Переплата. Инвойс переходит в PAID с paidAmount > expectedAmount. Верхней границы нет,
статуса OVERPAID нет, автоматического возврата тоже. Хотите вернуть разницу — сравнивайте эти две
суммы сами.
Проверяйте сумму, а не только статус
PAID значит «пришло не меньше ожидаемого». Если от точной суммы зависит выдача заказа, читайте
paidAmount.
Истечение срока¶
Инвойс истекает по наступлении expiresAt — но не тогда, когда деньги ещё в пути. Срок не сработает,
если у инвойса есть хотя бы один депозит, ждущий подтверждения, или хотя бы один подтверждённый
депозит в альтернативном токене. Поэтому покупатель, заплативший незадолго до дедлайна, не потеряет
платёж из-за того, что подтверждение пришло уже после него.
Подтверждённый депозит в альтернативном токене отменяет истечение навсегда
Правило не смотрит, разрешили ли вы платёж. Инвойс, получивший подтверждённый платёж
ALTERNATIVE, остаётся в своём статусе и после expiresAt и никогда не отправит
invoice.expired.
А вот инвойс в статусе PARTIALLY_PAID истечь может. Частичная оплата останавливает часы только на
то время, пока что-то ещё подтверждается.
Альтернативные токены¶
Поставьте acceptsAnyToken: true — и депозит в другом токене совместимой сети будет записан как
платёж ALTERNATIVE, а не признан ошибкой. В paidAmount он всё равно не попадёт: expectedAmount
выражен в собственном токене инвойса, так что сколько такой депозит стоит и оплачен ли заказ —
решаете вы.
Оставите false (значение по умолчанию) — тот же депозит станет платежом UNEXPECTED. Деньги
записываются и там, и там; флаг меняет только их классификацию и то, спросят ли у вас решение по ним.
См. Платежи по инвойсу.
Перевод средств на баланс¶
Деньги, оплаченные по инвойсу, приходят на его депозитный адрес. На ваш баланс — и в число того, что можно вывести, — они попадают, только когда их свипнут с этого адреса. Свип забирает всю чистую сумму одного токена на адресе разом, так что несколько платежей в одном токене переезжают вместе.
Кто запускает свип, решает флаг autoSweep у инвойса:
autoSweep |
Что происходит с подтверждённым чистым платежом |
|---|---|
true (по умолчанию) |
Свипается на ваш баланс автоматически, как только подтвердился и прошёл скрининг |
false |
Остаётся на депозитном адресе, пока вы не свипнете его сами |
Свипается каждый платёж, который подтвердился и прошёл скрининг, какого бы он ни был kind, —
EXPECTED, ALTERNATIVE и UNEXPECTED одинаково, и независимо от того, разрешили вы его или нет.
Деньги, не прошедшие скрининг, не свипаются никогда и до баланса не доходят: они остаются на
депозитном адресе.
Свипы повторяются без какого-либо участия с вашей стороны. Свип, который не удалось запустить, запускается снова, пока его не примут, а провалившийся поднимается заново ограниченное число раз, так что временный сбой задерживает деньги, а не оставляет их на адресе. Если свип так и не прошёл, вызовите эндпоинт свипа сами.
Автоматический свип переводит инвойс из PAID в SWAPPING, когда его приняли, и в COMPLETED,
когда деньги на вашем балансе. Инвойс, ещё не оплаченный
полностью, сохраняет свой статус, пока свипаются его первые платежи, и двигается дальше только после
того, как станет PAID. Свип, который сдался, переводит инвойс из SWAPPING в FAILED, пока
деньги не унесёт следующий свип; см. жизненный цикл статусов.
Ставьте autoSweep: false, когда хотите сами выбирать момент, — например, чтобы платёж, который вы,
возможно, разрешите как REFUNDED, не попадал на баланс, пока вы не решили. Свип — единственный
способ увести деньги с депозитного адреса: NullSwap
не отправляет возвраты, поэтому при false средства
остаются ровно там, где лежат, пока вы их не свипнете.
Пыль остаётся на адресе
Сумма, которая стоит не больше сетевой комиссии за её перемещение, не свипается: свип обошёлся бы дороже, чем она стоит. Она остаётся на депозитном адресе и уходит вместе со следующим свипом того же токена с этого адреса. На адрес инвойса следующий платёж приходит редко, поэтому пыль там обычно так и остаётся.
Перемещение денег стоит сетевой комиссии, и платите её в любом случае вы. В нативной монете сети свип оплачивает свою сетевую комиссию из перемещаемой суммы, поэтому доходит немного меньше, чем было заплачено. Для токена в сети с моделью счетов сетевую комиссию в нативной монете платим мы, а её стоимость в токене по текущему курсу удерживаем из зачисления — вместе со стоимостью approve, которые нужны конвертирующему свипу, и нативной монеты, отправленной на адрес для оплаты перемещения. В UTXO-сетях и в Monero сверх того, что платит сам свип, ничего не удерживается. Из того, что дошло, удерживаются комиссия мерчанта и эта сетевая комиссия, а остальное зачисляется на ваш баланс.
Сетевой комиссии нет среди полей свипа
Свип сообщает amount и fee, но не сетевую комиссию, удержанную рядом с fee, поэтому на
свипе токена amount − fee завышает зачисление. Сколько на самом деле дошло до баланса,
показывает credited в
списке удержанных комиссий.
Свип депозитного адреса¶
curl -X POST https://api.pay.nullswap.com/api/v1/wallets/7e2a9c14-8b3d-4c56-a789-0f1e2d3c4b5a/sweep \
-H "x-api-key: sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908"
}'
walletId в пути — это depositWalletId инвойса или id статического кошелька.
Тело необязательно:
| Поле | Тип | Обяз. | Примечания |
|---|---|---|---|
chainId |
UUID | нет | Свипнуть только токены этой сети |
tokenId |
UUID | нет | Свипнуть только этот токен |
Без тела свипаются все токены, у которых на адресе есть чистая сумма. Ответ — 202 со свипом на
каждый токен:
{
"data": [
{
"id": "0b1c2d3e-4f50-4617-8a29-3b4c5d6e7f80",
"walletId": "7e2a9c14-8b3d-4c56-a789-0f1e2d3c4b5a",
"invoiceId": "b2f4a6c8-0d1e-4f23-9a45-6b7c8d9e0f11",
"staticWalletId": null,
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"trigger": "MANUAL",
"status": "SWEEPING",
"amount": "25500000",
"feeEstimate": null,
"transferRequestId": "4d5e6f70-8192-4a3b-9c4d-5e6f708192a3",
"error": null,
"createdAt": "2026-01-01T12:10:00.000Z",
"updatedAt": "2026-01-01T12:10:01.000Z",
"feePercentage": "150",
"fee": null,
"feeUsd": null,
"settledAt": null,
"creditedTokenId": null,
"dstTokenId": null,
"amountIn": null,
"amountOut": null,
"conversionSkipReason": null
}
]
}
| Поле | Значение |
|---|---|
invoiceId, staticWalletId |
Какой адрес свипнут. Заполнено ровно одно из двух |
trigger |
AUTO — запущен автоматически; MANUAL — запущен этим эндпоинтом |
amount |
Базовые единицы tokenId, которые перемещаются, а после SWEPT — то, что дошло, в creditedTokenId. null, пока свип не принят |
feeEstimate |
Сетевая комиссия в базовых единицах нативной монеты сети. Заполняется только для пыли |
transferRequestId |
Наш идентификатор перевода, стоящего за свипом. Назовите его поддержке |
error |
Почему свип провалился, если провалился, — кодом: SWEEP_SOURCE_NOT_SUPPORTED (этот адрес нельзя свипнуть), SWEEP_FUNDS_UNAVAILABLE (денег для свипа на адресе уже нет) или SWEEP_FAILED (всё остальное). Незнакомый код считайте SWEEP_FAILED |
feePercentage |
Ставка комиссии мерчанта для этого свипа в базисных пунктах, зафиксированная при его открытии |
fee |
Комиссия мерчанта, удержанная из amount, в базовых единицах creditedTokenId. null до SWEPT |
feeUsd |
fee в USD, умноженная на 1018. null, пока не оценена |
settledAt |
Когда свип перешёл в SWEPT. До этого null |
creditedTokenId |
Токен, в котором выражены amount и fee после SWEPT: tokenId или dstTokenId, если свип был сконвертирован. До этого null |
dstTokenId |
Целевой токен вашего правила автоконвертации, скопированный при открытии свипа. null, если правила нет |
amountIn |
На сконвертированном свипе — проданные базовые единицы tokenId |
amountOut |
На сконвертированном свипе — полученные базовые единицы dstTokenId |
conversionSkipReason |
Почему свип, подпадающий под правило, прошёл без конвертации. null, если он сконвертирован или правила для него нет |
Правила автоконвертации задаются в кабинете, в разделе Конвертация → Автоконвертация. Свип,
подпадающий под такое правило, по пути на ваш баланс конвертируется в целевой токен правила, если
это возможно. Если нет, он зачисляется в собственном токене, а conversionSkipReason объясняет
почему:
conversionSkipReason |
Почему свип не сконвертирован |
|---|---|
NO_VENUE |
Токен негде обменять |
NO_QUOTE |
Для токена сейчас нет пригодной цены |
BELOW_MINIMUM |
Сумма слишком мала для конвертации |
ABOVE_LIQUIDITY_LIMIT |
Сумма больше, чем рынок принимает в пределах допустимого влияния на цену |
GAS_TOO_HIGH |
Сетевая комиссия обмена заняла бы слишком большую долю суммы |
SLIPPAGE |
Актуальная котировка оказалась ниже порога, допускаемого вашим проскальзыванием |
PRICE_DEVIATION |
Актуальная котировка слишком далека от рыночной цены |
SWAP_FAILED |
Обмен провалился в сети, и свип был повторён без конвертации |
TARGET_NOT_HEDGE |
В целевой токен правила больше нельзя конвертировать |
MERCHANT_BLOCKED |
Мерчант был заблокирован в момент открытия свипа |
status |
Значение |
|---|---|
PENDING |
Записан, но ещё не принят к переводу. Повторяется автоматически |
SWEEPING |
Принят, деньги идут на ваш баланс |
SWEPT |
Зачислено на ваш баланс: amount − fee, за вычетом сетевой комиссии на свипе токена. Терминальный |
FAILED |
Перевод провалился или был отклонён, читайте error. Повторяется автоматически ограниченное число раз, если дело не в самом адресе |
DUST |
Слишком мало, чтобы перемещать. Этот эндпоинт его не возвращает — в таком случае ответ 422 |
SUPERSEDED |
Свип FAILED или DUST, деньги которого переместил более поздний свип того же адреса и токена. Зачислил их тот свип, по этому не зачислено ничего, поэтому его amount никогда не учитывайте. Терминальный. Этот эндпоинт его не возвращает: в ответе только свипы, которые он запустил или застал активными |
Свип может прийти со статусом FAILED уже в самом ответе 202, если его отклонили в момент
запуска. 202 значит, что вызов принят, а не что каждый свип в нём идёт: проверяйте status у
каждого.
На один адрес и токен одновременно идёт не больше одного активного (PENDING или SWEEPING)
свипа. Если такой уже идёт — автоматический или от вашего предыдущего вызова, — он возвращается как
есть, а новый свип запускается только для токенов, у которых его нет. Это не ошибка:
trigger: "AUTO" в ответе на ваш вызов значит, что деньги уже были в пути. Поэтому повторять вызов
безопасно.
Прочитать свип в пути отдельным запросом нельзя
Нет эндпоинта, возвращающего свип по id, и нет вебхука о нём, если только под свип не подпадает
правило автоконвертации — тогда его sweepId несёт conversion.completed или
conversion.skipped. Когда ответ сказал SWEEPING, следите за
балансом. Повторный вызов этого эндпоинта возвращает
свип, пока он активен; когда он завершился, следующий вызов уже не найдёт, что перемещать.
Зачисленный свип появляется под тем же id в
списке удержанных комиссий; проваленный
не появляется нигде.
Если на адресе нет ничего чистого сверх пыли, вызов отвечает 422 NOTHING_TO_SWEEP и добавляет
объект details с оценкой, чтобы было видно, насколько сумме не хватает:
{
"statusCode": 422,
"message": "NOTHING_TO_SWEEP",
"timestamp": "2026-01-01T12:10:00.000Z",
"details": {
"amount": "150000",
"feeEstimate": "2800000",
"dust": [
{
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"amount": "150000",
"feeEstimate": "2800000"
}
]
}
}
amount — в базовых единицах токена, feeEstimate — в базовых единицах нативной монеты сети; пара
верхнего уровня повторяет первый элемент dust. Если чистой суммы нет совсем, dust пуст, amount
равен "0", а feeEstimate — null.
| Статус | message |
Причина |
|---|---|---|
400 |
INVALID_PATH_PARAMETER |
walletId — не UUID |
400 |
INVALID_REQUEST_BODY |
chainId или tokenId передан, но это не UUID |
403 |
MERCHANT_BLOCKED |
Мерчант заблокирован. Автоматические свипы уже полученных денег продолжаются; отклоняется только этот вызов |
404 |
WALLET_NOT_FOUND |
Такого адреса нет или он принадлежит другому мерчанту |
409 |
WALLET_HAS_NO_OWNER |
Адрес не закреплён ни за одним мерчантом, зачислять некуда. Обратитесь в поддержку; повтор не поможет |
422 |
NOTHING_TO_SWEEP |
На адресе нет ничего чистого сверх пыли. В details — оценка |
422 |
SWEEP_AMOUNT_TOO_SMALL |
Здесь не ожидается: на пыль отвечает NOTHING_TO_SWEEP. Если всё же встретится, обрабатывайте так же |
502 |
INTERNAL_ERROR |
Не удалось прочитать, что лежит на адресе. Ничего не запущено; можно повторить |
502 |
INTERNAL_ERROR |
Не удалось найти токены, лежащие на адресе. Ничего не запущено; можно повторить |
В отличие от остального API, адрес другого мерчанта даёт 404, а не 403: ответ не подтверждает,
что такой id существует.