🔌 Интеграции 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: applicantReviewedreviewResult.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 живая -