Балансы и выводы¶
Деньги, свипнутые с инвойса или статического кошелька, попадают на ваш баланс за вычетом комиссии мерчанта и сетевой комиссии за их перевод. Вывод отправляет их оттуда на адрес, который укажете вы.
Балансы¶
{
"items": [
{
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"currency": "USDT",
"decimals": 6,
"amount": "125000000",
"balanceId": "b2c3d4e5-f6a7-4890-8b1c-2d3e4f5a6b7c"
}
]
}
Обратите внимание на конверт: { "items": [...] }, а не { "data": [...], "pagination": {...} },
как в остальных списках. Ни пагинации, ни фильтров здесь нет — вы получаете все свои строки сразу.
Одна строка — это один токен. Токена, в котором вам ни разу не платили, в ответе обычно просто нет,
но строка может показывать и "0" — например, после того как вы вывели всё, — поэтому отсутствие
строки и "0" обрабатывайте одинаково. Баланс в токене, которого больше нет в
/api/v1/tokens, не показывается.
| Поле | Значение |
|---|---|
tokenId, chainId |
Токен и сеть, в которой уходит его вывод |
currency |
Символ валюты токена |
decimals |
Масштаб amount |
amount |
Сколько у вас есть, в базовых единицах. Вывести можно всё |
balanceId |
Наш идентификатор этого баланса. Назовите его поддержке |
Весь amount можно вывести. На баланс свипаются только деньги, которые подтвердились и прошли
скрининг; средства, не прошедшие скрининг, до баланса не доходят и остаются на депозитном или
статическом адресе, куда пришли. Смотрите их по платежам: через amlStatus у
платежей по инвойсу и settlement: WITHHELD у
платежей статических кошельков.
Оплачено — ещё не значит на балансе
Платёж зачисляется сюда, только когда его свипнули.
Между invoice.paid и зачислением есть свип в сети, а у инвойса, созданного с
autoSweep: false, зачисления не будет вовсе, пока вы не свипнете его сами.
amount — строка с целым числом базовых единиц; чтобы показать её человеку, разделите на
10^decimals. И, как всегда, складывайте суммы только внутри одного tokenId — см.
соглашения.
| Статус | message |
Причина |
|---|---|---|
502 |
INTERNAL_ERROR |
Не удалось прочитать баланс. Можно повторить |
502 |
INTERNAL_ERROR |
Не удалось найти токены. Можно повторить |
Комиссия мерчанта¶
С каждого мерчанта мы берём комиссию с его платежей: ставку в базисных пунктах, которую показывает
поле feePercentage в GET /api/v1/merchant. Её устанавливаем мы, и API-ключ
никогда не может её изменить. Комиссия удерживается, когда свип зачисляется на ваш баланс: из того,
что пришло, комиссия остаётся у нас, а остальное зачисляется на баланс.
amount — то, что доставил свип, в базовых единицах токена, в котором он зачислен. Комиссия
округляется вниз до целой базовой единицы, так что остаток от округления всегда остаётся у вас. При
"0" комиссия мерчанта не удерживается.
Сетевая комиссия — это стоимость перевода денег при свипе
токена, оценённая в зачисленном токене и удерживаемая вместе с комиссией мерчанта. Она равна нулю
при свипе нативной монеты сети, в UTXO-сетях и в Monero, где свип сам оплачивает себя из того, что
переводит. Отдельного поля у неё нет: это разница между amount − fee и credited.
Каждый свип фиксирует ставку в момент, когда он открыт. Изменение ставки касается свипов, открытых после него, и никогда — свипа, который уже в пути.
Свип в SWEPT зачисляет не свой amount
Баланс вырастает на credited, а не на amount, а при свипе токена — и не на amount − fee.
Сверяйте свип с балансом по credited из списка ниже и никогда — по собственным цифрам свипа.
Комиссия за вывод — отдельный платёж, который берётся при выводе. Ваши балансы уже за вычетом всех комиссий мерчанта и сетевых комиссий свипов.
Список удержанных комиссий¶
curl "https://api.pay.nullswap.com/api/v1/merchant/fees?settledFrom=2026-01-01T00:00:00.000Z&settledTo=2026-02-01T00:00:00.000Z&limit=50" \
-H "x-api-key: sk_live_..."
| Параметр | Тип | Обязателен | Примечания |
|---|---|---|---|
chainId |
uuid | нет | Повторяемый. Только свипы в этих сетях |
tokenId |
uuid | нет | Повторяемый. Только свипы этих токенов |
settledFrom |
ISO-8601 | нет | Только свипы, зачисленные в этот момент или позже. Включительно |
settledTo |
ISO-8601 | нет | Только свипы, зачисленные раньше этого момента. Не включительно |
offset, limit |
integer | нет | См. пагинацию. Передавайте limit |
order |
ASC | DESC |
нет | По времени зачисления. По умолчанию DESC, сначала новые |
{
"data": [
{
"id": "0b1c2d3e-4f50-4617-8a29-3b4c5d6e7f80",
"merchantId": "9f1d2c3b-4a5e-4f60-8b71-2c3d4e5f6a7b",
"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",
"transferRequestId": "4d5e6f70-8192-4a3b-9c4d-5e6f708192a3",
"amount": "25500000",
"credited": "25117500",
"fee": "382500",
"feePercentage": "150",
"feeUsd": "382576500000000000",
"settledAt": "2026-01-01T12:14:00.000Z",
"creditedTokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"dstTokenId": null,
"amountIn": null,
"amountOut": null,
"conversionSkipReason": null
}
],
"pagination": { "offset": 0, "limit": 50, "total": 1, "order": "DESC" }
}
Одна строка — один зачисленный свип. В списке каждый свип, зачисленный на ваш баланс с тех пор, как
появилась комиссия мерчанта, в том числе свип по нулевой ставке с fee: "0"; свипов, которые ещё в
пути, в нём нет.
| Поле | Значение |
|---|---|
id |
Идентификатор свипа: id свипа из ответа POST /api/v1/wallets/{walletId}/sweep и sweepId платежа статического кошелька |
walletId |
Свипнутый адрес: depositWalletId инвойса или id статического кошелька |
invoiceId, staticWalletId |
Какой это был адрес. Заполнено ровно одно |
transferRequestId |
Наш идентификатор перевода. Назовите его поддержке |
tokenId |
Токен, который свипнули с адреса |
creditedTokenId |
Токен, в котором зачислено на баланс: tokenId либо целевой токен, если свип сконвертировало правило автоконвертации |
amount |
Что пришло, в базовых единицах creditedTokenId |
credited |
Что зачислено на баланс, в базовых единицах creditedTokenId: amount − fee, а при свипе токена — ещё за вычетом сетевой комиссии |
fee |
Удержанная комиссия мерчанта, в базовых единицах creditedTokenId |
feePercentage |
Применённая ставка в базисных пунктах, зафиксированная при открытии свипа |
feeUsd |
fee в USD на момент зачисления, умноженная на 1018. null, пока не оценена |
settledAt |
Когда свип зачислен на баланс |
dstTokenId, amountIn, amountOut, conversionSkipReason |
Автоконвертация свипа, как у самого свипа. null, если правила для него не было |
feeUsd нужна только для отчётности. Комиссия, у токена которой в момент зачисления не было цены,
читается как null и оценивается позже — по цене того, более позднего момента. Для точных цифр
складывайте fee внутри одного tokenId — см. соглашения.
| Статус | message |
Причина |
|---|---|---|
400 |
INVALID_QUERY_PARAMETER |
chainId или tokenId — не UUID |
400 |
INVALID_DATE_FILTER |
settledFrom или settledTo не удалось разобрать |
401 |
INVALID_API_KEY |
x-api-key отсутствует, искажён, неизвестен или отозван |
403 |
CALLER_IP_NOT_ALLOWED |
Ключ верный, но адреса отправителя нет в его белом списке |
403 |
MERCHANT_BLOCKED |
Мерчант заблокирован |
Создание вывода¶
curl -X POST https://api.pay.nullswap.com/api/v1/merchant/withdrawals \
-H "x-api-key: sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"to": "0xfedcba0987654321fedcba0987654321fedcba09",
"amount": "50000000",
"externalId": "payout-2026-01-14-001"
}'
| Поле | Тип | Обязательно | Примечания |
|---|---|---|---|
chainId |
uuid | да | |
tokenId |
uuid | да | Должен быть токеном в этой сети |
to |
string | да | Адрес назначения в нативном формате сети. На RIPPLE — классический адрес r… или X-адрес, который читается как адрес и тег, упакованные в нём |
toTag |
integer | null | нет | Только RIPPLE: тег назначения получателя, 0–4294967295. Отклоняется на любой другой сети, а также если расходится с тегом X-адреса в to. См. Вывод на RIPPLE |
amount |
string | да | Базовые единицы. "50000000" — это 50 USDT при 6 decimals |
externalId |
string | null | нет, но настоятельно рекомендуется | Ваша ссылка, от 1 до 255 символов. Ключ идемпотентности — см. ниже. Без него повтор отправит деньги дважды |
subtractFeeFromAmount |
boolean | нет | По умолчанию false. Взять комиссию из amount, а не сверху. См. комиссию |
maxFee |
string | null | нет | Наибольшая комиссия, на которую вы согласны, в базовых единицах tokenId. Если выше — отказ 422 FEE_EXCEEDS_MAX_FEE |
{
"transferRequestId": "9c0d1e2f-3a4b-45c6-8d78-9e0f1a2b3c4d",
"status": "PENDING",
"hash": null,
"amount": "50000000",
"fee": "1350000"
}
201 значит принято к обработке, а не отправлено. До сети дело ещё не дошло, hash равен
null, результат придёт асинхронно. А вот списание уже произошло: сумма и fee снимаются с
вашего баланса в момент приёма вывода, а вывод, который баланс не покрывает, сразу отклоняется с
422 INSUFFICIENT_MERCHANT_BALANCE.
Адрес назначения за вас никто не проверяет
Мы не проверяем ни того, что to вообще похож на адрес из chainId, ни того, что этот адрес
принадлежит вам. Вывод на опечатанный или чужой адрес — совершенно нормальный, правильно
составленный запрос, и мы попробуем его выполнить. Проверяйте адрес у себя и предпочитайте
адресную книгу свободному вводу.
Две проверки to всё же проходят до какого-либо списания:
- Скрининг. Адрес назначения проверяется при создании вывода. Адрес, не прошедший скрининг,
отклоняется с
422 RECIPIENT_ADDRESS_BLOCKED;SKIPPEDвывод пропускает. Адреса Monero не проверяются. - Ваш белый список выводов, если вы его включили. Владелец мерчанта может ограничить выплаты
списком доверенных адресов в кабинете, в разделе Настройки → Белые списки. Когда ограничение
включено,
to, которого нет в списке для этой сети, отклоняется с422 WITHDRAWAL_ADDRESS_NOT_WHITELISTED— в том числе при вызовах API, — а при пустом списке отклоняется любая выплата. Список ведётся по сетям: адрес, добавленный для одной EVM-сети, в другой доверенным не считается. НаRIPPLEзапись списка — это адрес вместе с тегом назначения, и выплата совпадает только с записью ровно с еёtoTag; запись без тега совпадает только с выплатой без тега.
Вывод на RIPPLE¶
На RIPPLE, то есть в XRP Ledger, большинство бирж и кастодианов принимают деньги на один счёт,
общий для всех их клиентов, и различают клиентов по тегу назначения (destination tag). Передайте тег
получателя в toTag или передайте в to X-адрес, который вам дали. Выплата на такой счёт без тега
дойдёт до биржи, но ни на чей баланс там не попадёт.
Ещё две проверки до какого-либо списания читают счёт получателя из леджера:
- Обязательный тег. Счёт, который не принимает платежи без тега назначения, при выплате без
toTagдаёт отказ422 DESTINATION_TAG_REQUIRED. - Счёт, которого ещё нет. Счёт в XRP Ledger создаётся первым платежом на него, и только если этот
платёж не меньше базового резерва сети — сейчас это 1 XRP. Выплата на несуществующий счёт меньше
этой суммы отклоняется с
422 DESTINATION_NOT_ACTIVATED, аdetails.minAmountсообщает, сколько нужно, в drops:
{
"statusCode": 422,
"message": "DESTINATION_NOT_ACTIVATED",
"timestamp": "2026-01-14T09:00:00.000Z",
"details": {
"minAmount": "1000000"
}
}
Берите цифру из details.minAmount, а не зашивайте её в код: резерв устанавливает сеть, и она может
его изменить. Ни один из этих отказов ничего не списывает, и тот же externalId можно отправить
снова, когда запрос исправлен.
Комиссия¶
Вывод стоит столько, сколько сетевая комиссия за его отправку, и платите вы её в выводимом
токене — по текущему курсу этого токена и с небольшим запасом на изменение цены между оценкой и
отправкой. fee в ответе — то, что с вас взяли, amount — то, что отправят получателю, оба в
базовых единицах tokenId. Других сборов нет, и держать газ не нужно: чтобы вывести токен, баланс в
нативной монете сети не требуется.
subtractFeeFromAmount |
Получатель получит | Спишется с баланса |
|---|---|---|
false (по умолчанию) |
amount |
amount + fee |
true |
amount − fee |
amount |
true — это способ вывести всё: отправьте весь баланс в amount, и комиссия возьмётся из него.
С false вывод всего баланса будет отклонён — на комиссию не хватит.
Комиссия пересчитывается в момент вывода, поэтому может отличаться от любой показанной вам оценки.
Передайте maxFee, чтобы ограничить её: вывод, комиссия которого оказалась выше, отклоняется с
422 FEE_EXCEEDS_MAX_FEE, и ничего не списывается.
Вывести можно только токен с известным курсом
Комиссия пересчитывается по курсу токена. Токен, курса которого у нас нет, вывести нельзя:
запрос вернёт 422 TOKEN_PRICE_UNAVAILABLE. То же относится к оценке комиссии.
Оценка комиссии¶
curl "https://api.pay.nullswap.com/api/v1/merchant/withdrawals/fee-quote?chainId=1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10&tokenId=3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908" \
-H "x-api-key: sk_live_..."
| Параметр | Тип | Обязательно | Примечания |
|---|---|---|---|
chainId |
uuid | да | |
tokenId |
uuid | да | Токен, который вы собираетесь выводить |
{
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"nativeTokenId": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
"nativeFee": "2800000",
"fee": "1350000"
}
fee — сколько взяли бы за вывод tokenId прямо сейчас, в его базовых единицах, с учётом запаса.
nativeFee — сетевая стоимость, из которой он пересчитан, в базовых единицах нативной монеты сети
nativeTokenId.
Это только оценка. Ничего не резервируется и курс не фиксируется: если комиссия должна остаться в
показанных пределах, передайте показанный fee как maxFee при выводе.
| Статус | message |
Причина |
|---|---|---|
400 |
INVALID_QUERY_PARAMETER |
chainId или tokenId не передан либо не UUID |
404 |
CHAIN_NOT_FOUND |
Неизвестный chainId |
404 |
TOKEN_NOT_FOUND |
Неизвестный tokenId либо токен не из этой сети |
422 |
TOKEN_PRICE_UNAVAILABLE |
Нет курса, чтобы пересчитать комиссию в этот токен |
502 |
INTERNAL_ERROR |
Не удалось оценить комиссию. Можно повторить |
Здесь externalId действительно ключ идемпотентности¶
Это единственная операция записи в API, где повторный externalId безопасен, а не ошибочен.
Отправьте один и тот же дважды с теми же параметрами — повтор после таймаута сокета, задублившаяся
задача — и второй вызов вернёт уже созданный вывод, а не сделает второй. Уникальность считается в
пределах вашего мерчанта.
externalId необязателен, но не передавайте его, только если никогда не повторяете запросы. Без
него каждый вызов — новый вывод: два одинаковых запроса — это две выплаты, а повтор после таймаута,
ответа на который вы так и не увидели, отправит деньги дважды.
Тот же externalId с любым изменённым параметром — chainId, tokenId, to, toTag, amount,
subtractFeeFromAmount или maxFee — отклоняется с 409 WITHDRAWAL_EXTERNAL_ID_CONFLICT, и
ничего не отправляется. Это значит, что ваша система использовала одну ссылку для двух разных
выплат; исправьте ссылку, а не повторяйте запрос.
Сравните с инвойсами, где повторный externalId даёт 409. Асимметрия сделана
намеренно: дублирующийся инвойс поправим, а дублирующийся вывод отправит деньги дважды.
Всегда передавайте его и делайте воспроизводимым
Стройте его из чего-то устойчивого в вашей собственной системе — из id строки выплаты, а не из
метки времени или случайного значения. externalId, который вы не сможете воспроизвести после
сбоя, ни от чего не защищает.
Повтор возвращает существующий вывод, чем бы он ни закончился, в том числе FAILED. Чтобы
повторить проваленный вывод, отправьте новый externalId. Вывод, отклонённый с 422, не
записывается, поэтому тот же externalId можно использовать снова, когда причина устранена. После
5xx externalId остаётся занятым: повтор с ним вернёт то, чем закончилась первая попытка.
Ошибки¶
| Статус | message |
Причина |
|---|---|---|
400 |
INVALID_REQUEST_BODY |
Поле отсутствует или имеет неверный формат: externalId передан, но пуст, состоит из пробелов, не является строкой или длиннее 255 символов, chainId или tokenId — не UUID, to пустой, amount — не строка с положительным целым числом, maxFee передан, но это не строка с неотрицательным целым числом, subtractFeeFromAmount — не булево значение, toTag — не целое число 0–4294967295, toTag передан на сети, отличной от RIPPLE, to — X-адрес другой сети или его тег расходится с toTag |
403 |
MERCHANT_BLOCKED |
Мерчант заблокирован |
404 |
CHAIN_NOT_FOUND |
Неизвестный chainId |
404 |
TOKEN_NOT_FOUND |
Неизвестный tokenId либо токен не из этой сети |
409 |
WITHDRAWAL_EXTERNAL_ID_CONFLICT |
Этот externalId уже использован для вывода с другими параметрами |
422 |
WITHDRAWAL_ADDRESS_NOT_WHITELISTED |
Ваш белый список выводов включён, а to в нём для этой сети нет |
422 |
INSUFFICIENT_MERCHANT_BALANCE |
Баланс не покрывает то, что с него спишется, — amount, плюс fee, если не задан subtractFeeFromAmount |
422 |
TOKEN_PRICE_UNAVAILABLE |
У токена нет курса, чтобы взять в нём комиссию |
422 |
FEE_EXCEEDS_MAX_FEE |
Комиссия оказалась выше вашего maxFee |
422 |
AMOUNT_DOES_NOT_COVER_FEE |
Задан subtractFeeFromAmount, а комиссия не меньше amount |
422 |
RECIPIENT_ADDRESS_BLOCKED |
Адрес назначения не прошёл скрининг. Ничего не списано |
422 |
DESTINATION_TAG_REQUIRED |
RIPPLE: счёт получателя не принимает платежи без тега назначения, а toTag не передан |
422 |
DESTINATION_NOT_ACTIVATED |
RIPPLE: счёта получателя ещё нет, а выплата меньше суммы, нужной для его создания. details.minAmount — эта сумма, в drops |
502 |
INTERNAL_ERROR |
Вывод не удалось оформить, либо неизвестно, оформлен ли он. Повторите с тем же externalId; если вы его не передавали, сначала проверьте свои выводы |
201 — это ещё не конец
Принятый вывод всё ещё может провалиться по пути в сеть — вы увидите status: "FAILED" и
причину в error. Считайте ответом терминальный статус, а не ответ на POST.
Отслеживание вывода¶
curl "https://api.pay.nullswap.com/api/v1/merchant/withdrawals?limit=50" \
-H "x-api-key: sk_live_..."
Только offset, limit и order: фильтров по статусу, токену или externalId здесь нет. По
умолчанию сортировка createdAt DESC.
{
"id": "9c0d1e2f-3a4b-45c6-8d78-9e0f1a2b3c4d",
"chainId": "1c9a7f36-5f2b-4a83-9d51-0e6b7c8d9a10",
"tokenId": "3b7e1a48-2c6d-4e9f-8a01-5d4c3b2a1908",
"toAddress": "0xfedcba0987654321fedcba0987654321fedcba09",
"toTag": null,
"amount": "50000000",
"fee": "1350000",
"decimals": 6,
"status": "CONFIRMED",
"hash": "0x9a8b7c6d5e4f30211a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70819",
"error": null,
"createdAt": "2026-01-14T09:00:00.000Z",
"updatedAt": "2026-01-14T09:04:00.000Z"
}
id — это тот самый transferRequestId, что вернулся вам при создании: то же значение под другим
именем. amount — то, что отправлено получателю (уже за вычетом комиссии, если вы задали
subtractFeeFromAmount), а fee — то, что с вас взяли сверх этого. toTag — тег назначения, с
которым выплата ушла на RIPPLE; null, если тега не было, и на всех остальных сетях. toAddress
всегда классический адрес, даже если вы отправили X-адрес. fee равен null у выплат,
сделанных до того, как комиссию за вывод стали брать в выводимом токене.
status |
Значение |
|---|---|
PENDING |
В очереди. В сети ничего нет, hash равен null |
BROADCASTING |
Прямо сейчас подписывается и отправляется. hash обычно ещё null |
BROADCAST |
Подписан и опубликован, hash заполнен. Подтверждения ещё нет |
CONFIRMED |
Подтверждён на нужную сети глубину. Терминальный и окончательный |
FAILED |
Не отправлен либо отправлен и откачен. Терминальный, причина — в error |
stateDiagram-v2
[*] --> PENDING: POST принят
PENDING --> BROADCASTING: отправляется
BROADCASTING --> BROADCAST: опубликован
BROADCASTING --> FAILED: не удалось отправить
PENDING --> FAILED: не удалось отправить
BROADCAST --> CONFIRMED: подтверждён в сети
BROADCAST --> FAILED: откачен
CONFIRMED --> [*]
FAILED --> [*]
Выводы не отправляют вебхуков
Выводов не касается ни одно событие из каталога. Опрашивайте этот
эндпоинт, пока каждая интересующая вас запись не станет CONFIRMED или FAILED.
Две детали, которые нужно учесть в опрашивающем коде:
- У записи
FAILEDhashможет бытьnull— и навсегда. У вывода, провалившегося ещё до публикации, хеша не было никогда. Проверяйте поле, прежде чем показывать ссылку на обозреватель. decimalsтоже может бытьnull. Это значит, что токен с тех пор делистнули. Возьмите закешированное значение из/api/v1/tokens, но не подставляйте по умолчанию 18.
Вывод, провалившийся до попадания в сеть, возвращает на баланс и сумму, и fee. Вывод, который
был отправлен и откачен, возвращает только сумму: сетевая комиссия уже потрачена.
Два случая автоматически не урегулируются, и снаружи ни один из них не выглядит иначе:
- Вывод
FAILED, который, возможно, дошёл до сети. Когда отправка обрывается по таймауту так, что неизвестно, ушла ли транзакция, запись получаетFAILED, но ничего не возвращается: выплата ещё может дойти. По записи не понять, какой этоFAILED: сверьте баланс и, если деньги не вернулись, обратитесь в поддержку сidвывода. - Вывод
BROADCAST, который сеть отбросила. Он остаётсяBROADCAST, и ничего не возвращается. Если вывод остаётсяBROADCASTнамного дольше времени подтверждения в этой сети, обратитесь в поддержку с егоidиhash.
error заполняется только при FAILED, кодом: WITHDRAWAL_REVERTED — транзакция попала в сеть и
откатилась, сумма вернулась на баланс, сетевая комиссия потрачена; WITHDRAWAL_FAILED — всё остальное,
ничего не отправлено. Незнакомый код считайте WITHDRAWAL_FAILED: коды могут добавляться.