🔌 Интеграции Capital
Четыре внешних партнёра: SumSub (KYC/AML + основа под KYT), Papaya (SEPA-рельс/ностро), NS Cards (эмитент карт), XBO (крипто-шлюз USDT - в ветке, см. ниже). Плюс Resend (SMTP). Все денежные партнёры реализованы с режимом simulate - полный цикл работает без кредов и без сети; флип на live = впрыснуть креды и снять
*_SIMULATE. Значения секретов - только по ИМЕНАМ env-переменных (см. единый пульт).
🪪 SumSub (KYC/KYB) - сервис capital-kyc
Отдельный сервис-мост. Уровни верификации: kyc-director-msb-ca-v1 (физлицо-
директор) и kyb-msb-ca-v1 (компания).
Эндпоинты сервиса:
- POST /applicants - создать applicant физлица + вернуть verification_url
(rate-limit 120/мин на IP).
- POST /kyb/companies, POST /kyb/companies/{id}/beneficiaries - KYB-поток компании.
- GET /applicants/{id}/status - статус ревью.
- POST /webhooks/sumsub - приём вебхуков (плюс GET для проверки доступности).
- GET /health - liveness + проверка конфигурации.
Внутренние эндпоинты защищены заголовком X-Internal-Token, когда задан
SUMSUB_INTERNAL_TOKEN.
Вебхук POST /webhooks/sumsub:
- HMAC fail-closed: алгоритм из X-Payload-Digest-Alg (default HMAC_SHA256_HEX),
дайджест из X-Payload-Digest, секрет - env SUMSUB_WEBHOOK_SECRET. Нет секрета/
подписи → 401.
- Идемпотентность: дедуп по кортежу (raw_body, applicantId, type, answer) через
WebhookStore (в volume kyc_data); дубль → {"ok": true, "duplicate": true}.
- Форвард вердикта в Ledger: applicantReviewed → reviewResult.reviewAnswer
(GREEN/RED); applicantPending → PENDING; applicantOnHold → ON_HOLD;
прочие события - не форвардятся. Адрес Ledger - SUMSUB_LEDGER_BASE_URL, защита -
SUMSUB_LEDGER_INTERNAL_TOKEN.
Env-имена: SUMSUB_APP_TOKEN, SUMSUB_SECRET_KEY, SUMSUB_WEBHOOK_SECRET,
SUMSUB_LEVEL_NAME, SUMSUB_KYB_LEVEL_NAME, SUMSUB_INTERNAL_TOKEN,
SUMSUB_LEDGER_BASE_URL, SUMSUB_LEDGER_INTERNAL_TOKEN, SUMSUB_TRUST_PROXY.
Статус (2026-09-15): вебхук ЖИВОЙ (kyc.algorys.cc/webhooks/sumsub, HMAC активен,
401 без подписи). ⬜ URL вебхука в дашборде SumSub - подтвердить у CEO.
🛤 Papaya (SEPA-рельс / ностро) - capital_shared/rails.py
Класс PapayaRail. RailConfig: base_url, auth_url, client_id,
client_secret, timeout (~20с), simulate.
Auth: OAuth2 client-credentials → {auth_url}/oauth/token (Basic-заголовок из
client_id:client_secret), Bearer-токен кэшируется до expires_in − 30с,
переиспользуется между вызовами.
Методы (покрытие спеки PAYM_001):
- Платежи: create_sepa_sct, create_sepa_instant, get_transaction_status.
- Жизненный цикл транзакции: refund_transaction, cancel_transaction,
approve_transaction.
- VoP: verify_payee (сверка IBAN/имя до платежа).
- Ностро: get_loro_balance, get_loro_statement.
- Провижн: register_legal_client, create_account, get_registration_status.
- Контроль: block_client/unblock_client, block_account/unblock_account,
close_account.
Статусы - pull-based (GET, async-вебхуков нет) → колбэк-ресивер НЕ нужен. Прогрессия в simulate: INITIATED → PENDING → PROCESSING → COMPLETED (по опросам).
Ошибки: RailUnavailable (не сконфигурирован), RailError (сеть/HTTP/партнёр,
без утечки тела). Fail-safe: ошибка рельса при проведении платежа → rollback (платёж
не помечается executed); rail off → локально как раньше.
⚠️ Сеть: API партнёра - http:// + IP-allowlist, только приватная сеть/VPN.
Хосты: prod partn-bank.finbusinessadapters.tech, sandbox
partnerbanking.sbtechnical.tech:8077.
Env-имена: RAIL_ENABLED, RAIL_SIMULATE, RAIL_BASE_URL, RAIL_AUTH_URL,
RAIL_CLIENT_ID, RAIL_CLIENT_SECRET.
Статус: RAIL_SIMULATE=true на prod. Флип на live: sandbox client_id/secret +
VPN/IP-allow от партнёра → RAIL_SIMULATE=false, RAIL_ENABLED=true. Блокер -
оплата инвойса партнёра (CEO). Контакт по ностро-интеграции: Liza Bulat @lizabulat.
💳 NS Cards (эмитент карт) - capital_shared/cards.py
Класс NSCardsClient. CardsConfig: base_url, token (Bearer/company API key),
timeout, simulate. Пер-программная конфигурация (config_for_program) с fallback
на глобальные дефолты; токен резолвится из env по коду программы.
Методы: order_card (virtual/physical, holder_name/currency/form_factor/brand),
get_card, get_sensitive_data (транзиентно возвращает PAN/expiry/CVV - НЕ
персистится), freeze_card/unfreeze_card, close_card, list_transactions,
create_3ds_challenge, verify_3ds.
🔒 PAN не хранится: в БД только pan_last4 + opaque issuer_card_id. В simulate -
явно синтетический демо-PAN. Клиентский reveal - двухшаговый (3DS step-up):
reveal → OTP-челлендж эмитента → verify_3ds → показ PAN один раз + аудит.
Вебхук POST /webhooks/nscards (в backoffice, в PUBLIC_PATHS): HMAC-SHA256
fail-closed (нет секрета/подписи → 401), идемпотентно по (card, issuer_tx_id) →
дебет/кредит счёта через единый хелпер _apply_card_auth (им же идёт simulate-spend).
Недостаток средств / превышение спенд-лимита → declined без движения; refund → кредит.
Env-имена: CARDS_ENABLED, CARDS_SIMULATE, NSCARDS_BASE_URL, NSCARDS_TOKEN,
NSCARDS_WEBHOOK_SECRET.
Статус: CARDS_SIMULATE=true на prod. Флип на live: company API key от NS Cards →
NSCARDS_TOKEN + CARDS_SIMULATE=false. Блокер - оплата инвойса партнёра (CEO).
🧭 KYT - мониторинг транзакций - capital_shared/kyt.py
screen_payment(...) гоняется при проведении платежа. Правила (каждое даёт reason,
платёж не блокируется - только алерт комплаенсу):
- high-value: amount ≥ KYT_LARGE_AMOUNT (default 10000);
- high-risk country: страна бенефициара в HIGH_RISK_COUNTRIES;
- velocity: daily_total + amount ≥ KYT_VELOCITY_AMOUNT (default 50000);
- structuring: сумма в 10%-полосе под порогом (возможное дробление).
Хит → AmlCase(subject_type="payment", raised_by="kyt"), уровень medium (санкции/
high-risk-страна → high). Env: KYT_ENABLED, KYT_LARGE_AMOUNT, KYT_VELOCITY_AMOUNT,
HIGH_RISK_COUNTRIES. Это «SumSub KYT» из целевой архитектуры - пока свой движок;
позже KYT можно повесить на ностро через SumSub (блокер - live-ностро).
🪙 XBO (крипто-шлюз USDT) - capital_shared/xbo.py (ветка feature/xbo-integration)
Приём USDT-TRC20-депозитов клиентов (tagged-адреса, омнибус-котёл XBO) → OTC-
конвертация в фиат → вывод на наши ностро. Сегрегация по клиентам - на нашей
стороне (per-client суб-леджер). Auth пробит 16.09: HMAC-ключ =
base64.b64decode(secret) (не сырые байты) + браузерный User-Agent против
Cloudflare 1010; реальный API отвечает (200/201). Этап 1 (адреса, вебхук,
зачисление, вывод фиата, simulate) - в ветке; этап 2 (OTC, справочник Nostro,
ConversionOrder, боевой флип) - доделать, флип только отдельной сессией с CEO.
Env-имена: XBO_API_KEY, XBO_API_SECRET, XBO_SIMULATE, XBO_ENABLED.
Подробно: 08 - XBO (USDT-TRC20 - фиат - ностро).md.
📧 Resend (SMTP) - capital_shared/email.py
send_email(cfg, to, subject, text). Отправитель no-reply@algorys.cc, домен
algorys.cc verified в Resend (DKIM resend._domainkey + SPF/MX в Cloudflare).
Письма онбординга (email-verify) и «счёт активен». Env-имена: SMTP_HOST, SMTP_PORT,
SMTP_USER, SMTP_PASSWORD, SMTP_FROM, NOTIFY_EMAIL_ENABLED (в кабинете - с
префиксом CABINET_). Статус: живая end-to-end (доставка в Gmail подтверждена).
Матрица режимов
| Партнёр | Тумблеры | На prod сейчас | Что нужно для live |
|---|---|---|---|
| SumSub | SUMSUB_* |
вебхук живой (HMAC) | URL вебхука в дашборде SumSub |
| Papaya | RAIL_ENABLED, RAIL_SIMULATE |
RAIL_SIMULATE=true |
creds + VPN/IP-allow, снять simulate |
| NS Cards | CARDS_ENABLED, CARDS_SIMULATE |
CARDS_SIMULATE=true |
company API key, снять simulate |
| XBO | XBO_ENABLED, XBO_SIMULATE |
ветка; auth пробит, XBO_SIMULATE=true |
мерж ветки + этап 2 + флип с CEO |
| Resend | NOTIFY_EMAIL_ENABLED |
живая | - |