🪙 XBO - крипто-шлюз (USDT-TRC20 → фиат → ностро)

Интеграция с XBO: приём USDT-депозитов от клиентов, OTC-конвертация в фиат и вывод на наши ностро. Код - ветка feature/xbo-integration головного capital (в main НЕ смержено). Полная спека потока - в проекте: 14 Iorys_Copy/00 XBO интеграция/СПЕКА - поток USDT-TRC20 → фиат → ностро.md. Обновлено: 16.09.2026.

Модель хранения: омнибус-«котёл»

У XBO по базовому API один аккаунт + много tagged-адресов → общий USDT-баланс («котёл»), нативных субаккаунтов нет. Сегрегацию по клиентам держим на нашей стороне: per-client суб-леджер (XboDepositAddress адрес↔company_id, XboTransaction + общий client-wallet ledger в capital_shared/wallet.py). Наш ledger = источник истины «кому сколько принадлежит»; баланс XBO - лишь агрегат котла.

Сквозной поток (целевой)

Клиент ──(выдан TRC20-адрес, clientTag=cap-<company_id>)── шлёт USDT
  → котёл XBO ──(вебхук crypto-deposit, HMAC)── зачисление в наш ledger (USDT)
  → поручение на конвертацию (авто или ручное)
  → OTC: request-for-quote → convert (USDT→фиат в котле, проводки в ledger)
  → выбор ностро (Harbor Hills / Papaya / EMQ)
  → POST /v1/fiat/withdrawals на IBAN ностро → вебхук статуса → completed

✅ Что реализовано (этап 1, в ветке)

Компонент Где Что делает
XBO-клиент (HMAC) shared/capital_shared/xbo.py подпись ts+METHOD+path+base64(SHA256(body)); режим simulate (весь поток на фейках)
Выдача TRC20-адреса xbo.py::create_usdt_deposit_address POST /v2/deposit-addresses/, clientTag=cap-<id>
Вывод фиата xbo.py::withdraw_fiat (+quote) POST /v1/fiat/withdrawals, суммы строкой (точный decimal)
Верификация вебхука xbo.py::verify_webhook HMAC fail-closed, anti-replay ±300s
Модели models.py::XboDepositAddress, XboTransaction адрес↔company (unique по company+currency+network); транзакции Numeric(24,8), идемпотентность по xbo_id
Вебхук + зачисление backoffice::/webhooks/xbo кредит в client-wallet ledger, потолок депозита, только USDT
Операторские действия backoffice::/companies/{id}/xbo/* выдать адрес (accounts.manage), вывод фиата (payments.manage); CSRF + audit + идемпотентность
Клиентская страница cabinet::/deposit-usdt read-only: свой адрес + история, изоляция по company_id
Тесты ветка 180 + 73 теста simulate-модуля

🎉 Auth пробит (16.09.2026)

Реальный API XBO отвечает. Причина прежних 401 была наша: секрет XBO - base64-строка (88 симв = 64 байта), а код подписывал сырыми байтами UTF-8. Исправление: HMAC-ключ = base64.b64decode(secret)_sign и verify_webhook, хелпер _secret_key с восстановлением паддинга). Дополнительно: браузерный User-Agent обязателен (иначе Cloudflare 1010). Проверено вживую: GET /v1/currencies/ = 200, POST /v2/deposit-addresses/ = 201 (выдан реальный TRC20-адрес). Ключи - в capital-prod:.env по именам XBO_API_KEY / XBO_API_SECRET (+ XBO_SIMULATE=true); значения - мимо чата.

Фикс закоммичен 16.09: 3e39f4e в feature/xbo-integration (запушен). Вошло: base64-ключ HMAC, User-Agent, GET /v1/account/balances вместо 404-го /v1/balance, обновлённые тесты (backoffice 182 passed, cabinet 73). IP-whitelist у XBO не существует, ключ активен - 401 были только из-за подписи.

❌ Что доделать до боевого (этап 2)

# Пробел Критичность
1 OTC-конвертация: request-for-quote / convert + ledger-проводки (xbo-conv:<order>) 🔴 ядро потока
2 Справочник Nostro (Harbor Hills / Papaya / EMQ + IBAN) + привязка вывода к ностро 🔴
3 ConversionOrder + режимы авто/ручной + гейты (лимит авто, KYC GREEN, идемпотентность) 🔴
4 TRC20-only (убрать ERC20), зафиксировать точный networkCode 🟠/🔴
5 Точность 8 dp (крипта) ↔ 2 dp (ledger): политика dust 🟠
6 Схема подписи вебхука - зафиксировать одну (сейчас перебор 2 гипотез) 🔴
7 Регистрация callback-url + сверка путей API с живой докой 🟠

Решения, которые ждут CEO (перед флипом)

Правила безопасности