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

01 schema overview

Обзор схемы pm.* PostgreSQL: 11 таблиц, их связи и конвенции хранения.

Назначение каталога

Каталог database/ — справочник по физической модели данных Payment Manager. Вся информация PM хранится в собственной схеме PostgreSQL pm.*; схема public.* принадлежит Auth Center (Serverpod) и для PM доступна только на чтение через search_path=pm,public (см. CLAUDE.md).

Источник истины модели — src/shared/schema.ts (Drizzle ORM). DDL генерируется в каталог drizzle/migrations/ и применяется только через drizzle-kit migrate.

ER-диаграмма

Логические связи между таблицами по intent_id (UUID) не оформлены как FK в БД, но фактически держат всю цепочку платежа.

erDiagram
    service_key      ||--o{ intent          : "service_id"
    tb_account_map   ||--o{ intent          : "from_account_name / to_account_name"
    tb_account_map   ||--o{ tx_history      : "account_name"
    intent           ||--o{ intent_event    : "intent_id"
    intent           ||--o{ tx_history      : "intent_id"
    intent           ||--o{ outbox_event    : "intent_id"
    intent           ||--o| psp_tx_map      : "intent_id (UNIQUE)"
    payment_route    }o--o{ intent          : "operation_type + amount + channel"
    fee_rule         }o--o{ intent          : "operation_type → pre/post fee"
    limit_rule       }o--o{ intent          : "operation_type + channel + direction"
    auth_policies    }o--o{ intent          : "scope (operationType:channel)"

    service_key {
      varchar     service_id PK
      jsonb       permissions
      boolean     active
      timestamptz created_at
    }
    tb_account_map {
      serial      id
      varchar     account_name PK
      uuid        tb_account_id UK
      integer     tb_ledger
      varchar     account_type
      integer     user_id
      char        currency
      text        ipps_wallet_id
    }
    intent {
      uuid        id PK
      uuid        idempotency_key
      varchar     service_id
      integer     user_id
      varchar     operation_type
      varchar     channel
      varchar     from_account_name
      varchar     to_account_name
      bigint      amount
      varchar     status
      bigint      pre_fee_amount
      bigint      post_fee_amount
      uuid_array  tb_transfer_ids
      timestamptz expires_at
    }
    payment_route {
      serial      id PK
      varchar     operation_type
      bigint      amount_min
      bigint      amount_max
      varchar     channel
      boolean     active
    }
    fee_rule {
      serial      id PK
      varchar     name
      varchar     operation_type
      text        expression
      char        timing
      integer     priority
    }
    intent_event {
      serial      id PK
      uuid        intent_id
      varchar     status_from
      varchar     status_to
      jsonb       payload
    }
    tx_history {
      serial      id PK
      uuid        intent_id
      integer     user_id
      varchar     account_name
      varchar     direction
      bigint      amount
      bigint      fee_amount
      jsonb       attributes
    }
    outbox_event {
      serial      id PK
      uuid        intent_id
      varchar     action
      varchar     status
      integer     retry_count
    }
    psp_tx_map {
      uuid        id PK
      uuid        intent_id UK
      text        psp_name
      text        state
      text        query_rq_uid
      text        confirm_rq_uid
      text        leased_by
    }
    limit_rule {
      serial      id PK
      varchar     operation_type
      varchar     channel
      varchar     direction
      varchar     window
      bigint      amount_limit
      integer    count_limit
    }
    auth_policies {
      bigserial   id PK
      varchar     scope
      jsonb       condition
      varchar     required_step_up
      integer     priority
    }

Список таблиц

Таблица Что хранит Документ
pm.intent Платёжное намерение — корневая сущность всей оркестрации ./02-intent.md
pm.tx_history Денежная история по account_name (DEBIT/CREDIT) ./03-tx-history.md
pm.payment_route Выбор канала по operationType + amount ./04-payment-route.md
pm.fee_rule Правила комиссий (PRE/POST), sandboxed JS-выражения ./05-fee-rule.md
pm.tb_account_map Маппинг account_name ↔ TigerBeetle account UUID + ledger ./06-tb-account-map.md
pm.psp_tx_map State-machine PSP-адаптера (IPPS; QP/WISE — заготовки) ./07-psp-tx-map.md
pm.auth_policies Политики step-up аутентификации (PIN/OTP/BIOMETRIC/KYC_UPLIFT) ./08-auth-policies.md
pm.outbox_event Очередь post_pending/void_pending для двухфазных TB-трансферов (OutboxWorker) ./09-outbox-event.md
pm.intent_event Аудит-лог переходов статусов intent и psp_tx_map ./10-intent-event.md
pm.service_key Регистрация сервисов-клиентов и их HMAC-разрешения ./11-service-key.md
pm.limit_rule Лимиты на операции (PER_TX/DAILY/MONTHLY) по сумме и счёту ./12-limit-rule.md

Конвенции

  • Схема. Все таблицы — в pm.*. Серверподовская схема public.* для PM строго read-only.
  • Деньги. Суммы всегда BIGINT в satang (минимальная единица THB, 1 THB = 100 satang). Поля: amount, pre_fee_amount, post_fee_amount, fee_amount, amount_min/amount_max, amount_limit. Никаких numeric/float для денег.
  • Валюта. currencyCHAR(3), по умолчанию 'THB'.
  • Время. Все временные поля — TIMESTAMPTZ (timestamp with time zone), по умолчанию now().
  • Enum-литералы. Реализованы как VARCHAR(N) + CHECK (а не PostgreSQL ENUM), TypeScript-типы заданы через .$type<...>(). Полный перечень: см. экспортируемые алиасы (IntentStatus, PspState, TxDirection, OutboxAction, OutboxStatus, StepUpLevel, AccountType, PspName) в src/shared/schema.ts.
  • Идентификаторы. Бизнес-ключи (intent.id, psp_tx_map.id, tb_account_map.tb_account_id) — UUID. Технические — SERIAL/BIGSERIAL.
  • Связи через intent_id. FK на intent.id не объявлены в DDL, чтобы не блокировать запись событий при concurrent-обновлениях; целостность держится прикладным кодом и индексами (intent_event_intent_id_idx, tx_history_intent_id_idx, outbox_event_intent_id_idx, psp_tx_map.intent_id UNIQUE).
  • Каналы (intent.channel). Реальные channel-имена из src/channels/*.ts: INTERNAL_P2P (синхронный settle), IPPS_TRANSFER, SERVICE_TRANSFER, ADMIN (служебные операции от админки), MERCHANT_INVOICE (двухфазный, через TwoPhaseChannel). Привязка operationType → channel определяется pm.payment_route. Тип PspName (IPPS/QP/WISE) в pm.psp_tx_map.psp_name — это перечень PSP-адаптеров (сейчас активен только IPPS; QP/WISE — заготовки на будущее).
  • Статусы intent.status. Только CREATED, VALIDATED, AUTHORIZED, SETTLING, SETTLED, FAILED, MANUAL_REVIEW, CANCELED, EXPIRED.

Миграции

DDL и порядок применения миграций описаны в ./13-migrations.md. Все изменения схемы — только через drizzle-kit generate → review SQL → drizzle-kit migrate. Ручной DDL в pm.* запрещён (см. CLAUDE.md, раздел Critical Rules).