Кошелёк: вывод USDT и адрес пополнения (Wallet)
Вывод баланса в криптовалюту USDT через сеть TRON и TRC-20 адрес для пополнения collateral-баланса мерчанта
Вывод средств в криптовалюту (Wallet)
Платформа Syncra V2 позволяет мерчантам конвертировать накопленный фиатный баланс и выводить его на свои внешние криптовалютные адреса в стейблкоинах Tether (USDT).
Вывод осуществляется через блокчейн-сеть TRON (TRC-20) — самый быстрый и
дешёвый метод. Адрес получателя всегда передаётся в поле tron_address.
Сети BEP-20 (BNB Smart Chain) и ERC-20 (Ethereum) находятся в
разработке и будут активированы в будущих обновлениях. Не передавайте
blockchain: "TRC20" — это не валидный идентификатор сети. Поле blockchain
в протоколе использует коды сетей (TRON, TON); для вывода USDT сейчас
поддерживается только TRON (поведение по умолчанию).
Создание заявки на вывод
Для инициации вывода криптовалюты со своего баланса отправьте POST-запрос:
POST /api/v1/p2p/merchant/wallet/withdrawПараметры запроса (JSON Body)
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
amount_usdt | int64 | Да | Сумма вывода в USDT, целое число (единицы USDT, не float). Например, 1000 = 1000 USDT. Внутренне конвертируется в центы (amount_usdt * 100) для хранения как int64 в минимальных единицах. |
tron_address | string | Да | TRC-20 (Tron) адрес получателя (base58check, 34 символа; невалидный адрес → 400). Не передавайте отдельное поле blockchain: "TRC20" — сеть вывода фиксируется как TRON. |
idempotency_key | string | Нет | Поле присутствует в контракте, но не проверяется и не требуется: идемпотентность вывода обеспечивает сама платформа — детерминированный ключ вывода строится из (мерчант, адрес, сумма, актуальная комиссия), поэтому повтор идентичного запроса сходится на той же on-chain транзакции и никогда не порождает второй перевод. Передавать можно для симметрии с PayIn/PayOut — значение игнорируется. |
Пример запроса
{
"amount_usdt": 1000,
"tron_address": "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a",
"idempotency_key": "f3b8a1c2-4d5e-4f6a-9b0c-1d2e3f4a5b6c"
}Пример ответа (200 OK, авто-одобрение)
Поля ответа — верхний уровень, без обёртки data. status в синхронном
ответе — COMPLETED (авто-путь: транзакция отправлена в блокчейн,
transaction_id несёт on-chain хэш) или PENDING_MANUAL (ручной путь:
заявка встала в очередь одобрения оператора). Это синхронный вердикт запроса;
доменный статус самой заявки (в листингах и вебхуках) — см.
Статусы заявки на вывод:
{
"status": "COMPLETED",
"transaction_id": "0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b...",
"amount_usdt": "1000",
"tron_address": "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a",
"created_at": "2026-07-12T17:22:00Z",
"fee_usdt_cents": "150",
"net_usdt_cents": "99985"
}Пример ответа (200 OK, ручное одобрение)
Сумма сверх эффективного лимита авто-вывода (или политика MANUAL_ONLY)
создаёт заявку в очереди оператора: status: "PENDING_MANUAL",
transaction_id пуст, комиссия указана как та, что спишет путь одобрения:
{
"status": "PENDING_MANUAL",
"transaction_id": "",
"amount_usdt": "50000",
"tron_address": "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a",
"created_at": "2026-07-12T17:23:00Z",
"fee_usdt_cents": "150",
"net_usdt_cents": "4999985"
}| Поле | Тип | Описание |
|---|---|---|
status | string | Синхронный вердикт: COMPLETED | PENDING_MANUAL. |
transaction_id | string | On-chain TX-хэш (только при COMPLETED; пуст на ручном пути). |
amount_usdt | int64 | Эхо суммы запроса (целые USDT, строкой). |
fee_usdt_cents | int64 | Комиссия вывода в USDT-центах: фактически списанная (авто-путь) или та, что спишет путь одобрения. |
net_usdt_cents | int64 | Чистая сумма зачисления получателю: amount_usdt*100 − fee_usdt_cents. |
Суммы в пределах эффективного лимита автоодобрения (пер-мерчант override или
база тенанта; см. расчёт комиссии)
обрабатываются мгновенно и возвращают COMPLETED. Суммы сверх лимита (или
политика MANUAL_ONLY) переходят в PENDING_MANUAL и требуют одобрения
администратора.
Статусы заявки на вывод
У вывода USDT две статусные поверхности — не смешивайте их:
1. Статус в синхронном ответе POST /wallet/withdraw
| Статус | Описание |
|---|---|
COMPLETED | Авто-одобрение: on-chain транзакция отправлена (transaction_id = TX-хэш), средства списаны, ledger-запись проведена. |
PENDING_MANUAL | Заявка в очереди ручного одобрения оператора (политика MANUAL_ONLY или сумма сверх эффективного лимита). transaction_id пуст. |
Терминальные отказы в этом поле не появляются — ошибки вывода отдаются как
RFC 9457 problem+json (например, 400 при невалидном TRON-адресе,
400 FAILED_PRECONDITION при недостатке баланса).
2. Доменный статус заявки (domain.WithdrawalStatus)
Живёт в строке заявки (p2p_pending_withdrawals), виден в
истории выводов и в вебхуках
(p2p.finance.withdrawal_completed несёт status: "APPROVED"):
| Статус | Описание | Переход |
|---|---|---|
PENDING | Заявка создана (ручной путь), средства зарезервированы, ожидает одобрения оператора. | → APPROVED или CANCELLED. |
APPROVED | Одобрена, транзакция отправлена в блокчейн, ledger списан. | Терминальный. |
CANCELLED | Отклонена оператором или отменена до broadcast; резерв возвращается на баланс. | Терминальный. |
V1 Compatibility Adapter транслирует APPROVED → COMPLETED
автоматически для legacy-интеграций. PROCESSING зарезервирован на будущее
(подтверждение блоков между APPROVED и финалом) и текущим кодом не
эмитируется. В листинге выводов дополнительно
различаются PENDING (ждёт одобрения) и COMPLETED (broadcast прошёл,
TX-хэш известен).
Расчёт комиссии вывода (before submit)
GET /api/v1/p2p/merchant/wallet/withdraw-fee?amount_usdt_cents=100000Read-only калькулятор комиссии до отправки заявки: резолвит ту же per-counterparty политику вывода, что и денежный путь (пер-мерчант override, иначе база тенанта), поэтому квота не может разойтись с фактически списанной комиссией. Деньги не двигаются.
Параметры (Query)
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
amount_usdt_cents | int64 | Нет | Сумма планируемого вывода в USDT-центах (внимание: не в целых USDT, как amount_usdt создания). Когда задана (> 0), ответ дополнительно несёт net_amount_usdt_cents. |
Пример ответа (200 OK)
{
"base_fee_usdt_cents": "150",
"override_applied": false,
"total_fee_usdt_cents": "150",
"net_amount_usdt_cents": "99850"
}| Поле | Тип | Описание |
|---|---|---|
base_fee_usdt_cents | int64 | База тенанта (p2p_system_config.withdrawal_fee_usdt_cents). |
override_applied | bool | true = пер-мерчант override заменил базу. |
total_fee_usdt_cents | int64 | Фактически применяемая комиссия: override при наличии, иначе база. |
net_amount_usdt_cents | int64 | amount − total_fee (0, если сумма не передана). Может быть ≤ 0, если комиссия покрывает всю сумму — такой вывод денежный путь отклонит (комиссия должна быть меньше суммы). |
История депозитов (TRC-20 incoming)
GET /api/v1/p2p/merchant/wallet/depositsВходящие TRC-20 депозиты мерчанта с custodial-статусами: ledger-кредит +
жизненный цикл sweep-воркера принимающего адреса. Keyset-пагинация AIP-158
(page_size 1–100, по умолчанию 20; page_token/next_page_token).
Пример ответа (200 OK)
{
"data": [
{
"block_timestamp": "2026-07-12T17:20:01Z",
"amount_usdt_cents": "100000",
"tx_hash": "0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b...",
"trc20_address": "TCBYf3t6D2VshYVQLh55GGBDWTZvjQRoHd",
"status": "SWEPT"
}
],
"next_page_token": ""
}| Поле | Тип | Описание |
|---|---|---|
block_timestamp | string(RFC3339) | Время on-chain блока + глубина финальности (19 подтверждений). |
amount_usdt_cents | int64 | Зачисленная сумма, USDT-центы. |
tx_hash | string | On-chain TRON-хэш. |
trc20_address | string | Адрес зачисления (реконструирован из истории адресов контрагента). |
status | string | CREDITED (средства остаются на адресе зачисления) / SWEEP_PENDING (sweep ещё не дошёл или в полёте) / SWEEP_FAILED (последний sweep не удался, retry) / SWEEP_STUCK (исход broadcast неизвестен, оператор) / SWEPT (sweep прошёл, средства на hot wallet). |
История выводов
GET /api/v1/p2p/merchant/wallet/withdrawalsИстория выводов: очередь ручных одобрений, смёрженная с исполненными ledger-списаниями, дедуплицированная по on-chain TX-хэшу. Та же пагинация AIP-158, что и у депозитов.
Пример ответа (200 OK)
{
"data": [
{
"created_at": "2026-07-12T17:22:00Z",
"amount_usdt_cents": "100000",
"fee_usdt_cents": "150",
"net_usdt_cents": "99850",
"tx_hash": "0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b...",
"status": "COMPLETED",
"tron_address": "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a"
}
],
"next_page_token": ""
}| Поле | Тип | Описание |
|---|---|---|
created_at | string(RFC3339) | Время заявки (ручная очередь) или ledger-списания (авто-путь). |
amount_usdt_cents | int64 | Запрошенная gross-сумма, USDT-центы. |
fee_usdt_cents | int64 | Комиссия, USDT-центы (0 для истории до внедрения fee-механизма). |
net_usdt_cents | int64 | Чистая on-chain сумма = gross − fee. |
tx_hash | string | On-chain хэш; пуст, пока вывод не отправлен в сеть. |
status | string | PENDING (ждёт одобрения) / APPROVED (одобрен, не отправлен) / COMPLETED (broadcast прошёл) / CANCELLED (отклонён оператором). FAILED — зарезервирован, не персистится. |
tron_address | string | Адрес назначения. |
События вебхуков
| Событие | Статус заявки | Описание |
|---|---|---|
p2p.finance.withdrawal_completed | APPROVED | Вывод USDT завершён (on-chain tx отправлена) |
p2p.finance.withdrawal_failed | CANCELLED | Вывод отклонён оператором / отменён до broadcast; резерв возвращён на баланс |
Webhook при завершении вывода
Когда заявка достигает терминального успеха (APPROVED), Syncra отправляет на
ваш webhook URL событие (плоский CallbackEventPayload, без вложенного data —
см. Callbacks):
{
"event": "p2p.finance.withdrawal_completed",
"deal_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"merchant_id": "8f870a31-b88d-4cb0-a882-628d08cbda88",
"tenant_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100000,
"currency": "USDT",
"status": "APPROVED",
"timestamp": "2026-07-12T17:22:30Z"
}Подпись webhook передаётся в заголовке X-Signature (см.
Callbacks). Подробнее об обработке webhook и проверке подписи — в
разделе Callbacks → Проверка подписи.
TRC-20 адрес для пополнения (Deposit Address)
Помимо вывода, кошелёк мерчанта имеет TRC-20 адрес для зачисления USDT-обеспечения (collateral) на операционный баланс мерчанта. Пополнения с этого адреса расходуются на выплаты (PayOut), крипто-выводы и комиссии платформы.
Deposit address — это адрес самого мерчанта для пополнения своего операционного баланса. Игроки не платят на этот адрес: в сделках PayIn игроки переводят по реквизитам трейдеров (карта/СБП/IBAN), полученным на платёжной странице (см. PayIn).
Адрес управляется двумя операциями:
| Метод | Эндпоинт | Назначение |
|---|---|---|
| GET | /wallet/deposit-address | Read-only: текущий активный адрес. |
| POST | /wallet/deposit-address | Генерация (первичная) или ротация адреса. |
Получение текущего адреса
GET /api/v1/p2p/merchant/wallet/deposit-addressВозвращает текущий активный TRC-20 адрес мерчанта. Поля tenant_id /
merchant_id в запросе — advisory (при HMAC-аутентификации реальный
скоуп берётся из подписанного токена — claims-win, см.
Коды ошибок).
Пример ответа (200 OK)
Вместе с адресом возвращается updated_at — время последней генерации/ротации
адреса (RFC3339):
{
"trc20_address": "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a",
"updated_at": "2026-08-23T18:58:54Z"
}Если адрес ещё не сгенерирован (404 Not Found)
Стандартная RFC 9457-обёртка (detail — вложенный JSON gRPC-деталей):
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "{\"code\":5,\"message\":\"deposit address not provisioned yet — POST /api/v1/p2p/merchant/wallet/deposit-address to generate one\",\"details\":[]}",
"instance": "/api/v1/p2p-engine"
}Генерация и ротация адреса
POST /api/v1/p2p/merchant/wallet/deposit-addressГенерирует адрес (первый вызов) или ротирует его (повторные вызовы): платформа атомарно резервирует следующий BIP44 derivation index, выводит адрес через tron-wallet-api и сохраняет его в строке мерчанта. Тело запроса необязательно — скоуп resolves из аутентификационного контекста.
Пример ответа (200 OK)
{
"trc20_address": "TYHCiW3XN5sP9cWJ5T4Dfs9Y18h46t9w1a",
"wallet_derivation_index": 17
}Что происходит со старым адресом при ротации
- Новый адрес немедленно заменяет старый в строке мерчанта:
GET /wallet/deposit-addressи все последующие пополнения ориентируются на новый адрес. - Все ранее выпущенные адреса навсегда остаются в постоянном
block-polling-наборе платформы (
p2p_counterparty_addresses): USDT, отправленные на старый адрес после ротации, не теряются — они продолжают отслеживаться и зачисляться на баланс мерчанта. - После ротации сохранённый на вашей стороне адрес следует обновить
(сверьте его через
GET /wallet/deposit-address).
Существует два POST-пути генерации. Каноничный путь для
сервер-сервер интеграции мерчанта — POST /api/v1/p2p/merchant/wallet/deposit-address (без merchant_id в пути,
скоуп — из токена). Legacy-путь POST /api/v1/p2p/merchants/{merchant_id}/deposit-address остаётся рабочим и
используется для admin-impersonation (оператор платформы действует от
имени мерчанта); при HMAC-аутентификации merchant_id в пути обязан
совпадать с claims токена, при merchant-JWT — только с собственным
мерчантом (IDOR-защита).
Связанные материалы
- Баланс — мультивалютные балансы мерчанта.
- Идемпотентность — как идемпотентность вывода обеспечивается платформой.
- Callbacks — webhook-уведомления, включая
p2p.finance.withdrawal_completed. - Коды ошибок — обработка
INSUFFICIENT_BALANCEи других ошибок.