Перейти к содержанию

Admin Panel ↔ Payment Manager

Документ описывает, как Admin Panel (SvelteKit) интегрирована с Payment Manager: какие endpoint'ы вызывает, какой service key использует и почему прямой доступ к TigerBeetle разрешён только в read-only режиме.


1. Назначение интеграции

Admin Panel — внутренний SvelteKit-сервис для операторов (бухгалтеры, support, compliance). Через него выполняются:

  • Финансовые операции (TOPUP, withdrawal equity close, float reload, бонусы, корректировки, выплаты merchant/agent settlement) — все идут через канал ADMIN (operationType: ADMIN_TRANSFER).
  • Просмотр состояния системы — балансы счетов, история транзакций, статус intent'ов, очередь MANUAL_REVIEW.
  • Резолв «зависших» intent'ов — через POST /admin/intents/:id/resolve (см. ../api/admin.md).
  • Управление настройками — fee rules, лимиты, уровень логирования, health-check.

Admin Panel взаимодействует с PM двумя способами:

Способ Что делает Где
HTTP вызовы в PM с HMAC Все мутирующие операции (write) + получение балансов и истории POST /intents, POST /admin/*, GET /accounts/balance, GET /history
Прямой TigerBeetle SDK (read-only) Быстрый lookup аккаунтов, query балансов и transfer'ов для построения дашбордов TigerBeetle cluster

2. NO-GO — главное правило интеграции

Admin Panel НИКОГДА не делает write в TigerBeetle напрямую.

Все финансовые мутации (создание/закрытие transfer'ов, изменение состояния аккаунтов) проходят исключительно через POST /intents в Payment Manager с HMAC-подписью. Это гарантирует:

  • единый аудит-трейл (tx_history, event_log, intent-статусы);
  • проверку через rule-engine (комиссии, лимиты, разрешения);
  • срабатывание уведомлений и обновление балансов в одной транзакции;
  • невозможность «теневых» проводок мимо saga'и и outbox'а.

Прямой TigerBeetle SDK в Admin Panel разрешён только для чтения: lookupAccounts, lookupTransfers, getAccountBalances, getAccountTransfers, queryAccounts, queryTransfers. Любая попытка вызвать createAccounts / createTransfers из Admin Panel — нарушение архитектуры (см. корневой CLAUDE.md, раздел «Доступ к TigerBeetle»).

Создание новых TB-аккаунтов также не делается напрямую — для этого есть POST /accounts с service key admin-tool (см. ниже).

3. Service keys

Admin-инфраструктура использует два разных service key — у них разные права и разные правила формирования X-User-Id:

Service key Назначение X-User-Id
admin-panel Финансовые операции через POST /intents (ADMIN_TRANSFER) 0 (системный, реального пользователя нет)
admin-tool Создание TB-аккаунтов (POST /accounts для merchant/agent), force-resolve MANUAL_REVIEW userId целевой сущности

Подробное описание разрешений каждого ключа, regex-паттернов и allowedOperationTypes — в ../reference/passport/06-service-keys.md.

Канонический seed-источник прав — drizzle/seed.ts. Секреты обоих ключей берутся из env-переменной SERVICE_SECRETS (JSON-объект), никогда не хранятся в БД.

HMAC-схема для Admin Panel идентична общей схеме PM:

X-Service-Id:  admin-panel
X-Timestamp:   <unix seconds>
X-Signature:   hex(hmac_sha256(stringToSign, SECRET))
X-User-Id:     0

stringToSign = {timestamp}\n{METHOD}\n{PATH}\n{sha256hex(rawBody)}. Подпись формируется на сервере SvelteKit (server route'ы) — секрет никогда не должен попадать в браузер. Подробнее об HMAC и открытом вопросе про POST /admin/* см. ../api/auth.md.

4. Список admin-операций

Полный перечень endpoint'ов с request/response-схемами, error-кодами и примерами:

  • POST /intents (operationType=ADMIN_TRANSFER) — универсальная финансовая операция: см. ../api/intents.md и ../reference/passport/02-channels.md (канал ADMIN).
  • POST /admin/intents/:id/resolve — force-resolve intent в MANUAL_REVIEW (только admin-tool).
  • POST /accounts — создание merchant/agent кошельков (primary + settlement сразу) с service key admin-tool. Идемпотентно: повторный вызов вернёт 200 с теми же tbAccountId.
  • GET /admin/fee-rules, POST /admin/fee-rules — управление fee rules.
  • GET /admin/health — расширенный health (TB, PG, Redis, очереди).
  • POST /admin/debug-level — динамическое изменение уровня логирования.
  • GET /accounts/balance?account=<name>, GET /history?... — чтение балансов и истории (через PM, не напрямую в TB).

Все эти эндпоинты, их параметры, права доступа и коды ошибок описаны в ../api/admin.md.

Типовые финансовые сценарии (TOPUP, withdrawal equity close, float reload, бонусы, payout merchant/agent settlement) и соответствующие пары fromAccountName / toAccountName — см. в ../api/intents.md (раздел «ADMIN_TRANSFER» / channel ADMIN).

Матрица прав по entityType для POST /accounts

entityType Кто вызывает Что создаётся
"user" auth-center (или admin-panel для ручного recovery) USER_WALLET
"merchant" admin-tool MERCHANT_WALLET + MERCHANT_SETTLEMENT
"agent" admin-tool AGENT_WALLET + AGENT_SETTLEMENT

Settlement-аккаунты (*.settlement.THB) создаются только через POST /accounts (одной транзакцией с primary) и не могут использоваться как fromAccountName / toAccountName в ADMIN_TRANSFER — их пополнение происходит исключительно через fee-rule expression'ы. Для выплаты со settlement используется отдельный ADMIN_TRANSFER из *.settlement.THB в *.THB (primary).

5. Прямой TigerBeetle SDK (read-only)

Admin Panel держит собственный TB-клиент (отдельный от PM-кластера соединений) и использует его только для чтения. Это оправдано, потому что:

  • дашборды и отчёты делают тяжёлые batch-lookup'ы — гонять их через REST-прокси PM неэффективно;
  • TB SDK сам по себе read-safe (createAccounts/createTransfers требуют сознательного вызова, мы их просто никогда не пишем в коде Admin Panel);
  • read-only операции не нарушают инвариант transit.balance = 0 и не требуют HMAC-аудита.

Разрешённые TB-методы из Admin Panel:

Метод Зачем
lookupAccounts(ids[]) Получить состояние конкретных аккаунтов по их детерминированным id
lookupTransfers(ids[]) Резолв transfer-id'ов в детали (для drill-down из tx_history)
getAccountBalances(filter) Исторический временной ряд балансов (графики)
getAccountTransfers(filter) Постраничный список transfer'ов аккаунта
queryAccounts(filter) Поиск аккаунтов по ledger / code / user_data
queryTransfers(filter) Поиск transfer'ов по code / user_data / временному окну

Балансы для пользовательских экранов (не дашборд оператора) Auth Center по-прежнему берёт через nginx→PM-прокси (GET /api/pm/accounts/balance). Admin Panel — единственный сервис кроме PM, которому позволен прямой TB-клиент, и только read-only.

6. Идемпотентность и ретраи

Все POST /intents (включая ADMIN_TRANSFER) должны иметь уникальный idempotencyKey (UUIDv4). Если SvelteKit ретраит запрос из-за сетевой ошибки, он использует тот же idempotencyKey — PM вернёт оригинальный результат без повторного transfer'а.

import { randomUUID } from 'node:crypto'
const idempotencyKey = randomUUID() // один раз на инициирование операции, не на ретрай

Подробности про идемпотентность — в ../api/intents.md.

7. Архитектурные инварианты, которые держит Admin Panel

При работе через Admin Panel оператор не должен иметь возможности нарушить ключевые инварианты PM:

  • transit.balance = 0 — Admin Panel в принципе не работает с TRANSIT-аккаунтами; канал ADMIN использует прямой from → to transfer без transit'а, поэтому инвариант сохраняется автоматически.
  • debits_must_not_exceed_credits на USER_WALLET / MERCHANT_WALLET / AGENT_WALLET — попытка списать больше остатка вернёт 409 от TigerBeetle (см. ../api/errors.md).
  • Аудит — каждый ADMIN_TRANSFER пишется в tx_history с comment и metadata.operatorId. Оператор обязан заполнять comment человекочитаемой ссылкой на первоисточник (ref банковской выписки, ID тикета, ID intent'а).
  • HMAC-секреты admin-panel / admin-tool не покидают сервер — все подписи формируются в SvelteKit server route'ах; браузерный код вызывает только публичный SvelteKit API.

8. Заметка о миграции документации

Старый файл docs/ADMIN_PANEL.md (в корне docs/) будет удалён в Phase 9 документационного overhaul'а. Вся актуальная информация по интеграции Admin Panel ↔ PM теперь распределена так:

При расхождениях между старым docs/ADMIN_PANEL.md и файлами в docs/dev/ приоритет имеют документы в docs/dev/.


См. также