🪙 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 (перед флипом)
- Боевой флип (
XBO_ENABLED=true,SIMULATE=false, callback-url, микро-тест реального USDT) - отдельной сессией, не в фоне. - Учётная модель: точность крипты 8 dp vs ledger 2 dp (политика dust).
- Вывод фиата: operator-funded vs client-funded (списывать ли фиат клиента при выводе на ностро, или это казначейский провод банка).
Правила безопасности
- Клиент не выбирает ностро и не задаёт IBAN - вывод только на наши ностро.
convertиwithdrawу оператора - раздельные подтверждения (видит курс до фиксации).- Все money-действия: RBAC + CSRF + audit + идемпотентность (
wallet.post_entry). - IBAN/реквизиты ностро - секреты уровня реквизитов: в БЗ и синк-папках не хранить.