1.4 Data Models
Черновик модели данных платформы PSP Aggregator для backend: таблицы, enum, связи и JSON-схема атрибутов оффера.
Черновик модели данных платформы PSP Aggregator для backend: таблицы, enum, связи и JSON-схема атрибутов оффера.
[!WARNING] Черновик (Draft). Документ предназначен для расширения и детализации backend-командой. Бизнес-правила — в Core и User Stories, этот файл их не заменяет.
1. ER-диаграмма
erDiagram
USERS ||--o{ DEALS : "creates/owns"
PROVIDERS ||--o{ OFFERS : "has"
OFFERS ||--o{ DEAL_ITEMS : "included in"
DEALS ||--|{ DEAL_ITEMS : "contains"
DEALS ||--o{ DEAL_EVENTS : "logs"
USERS {
uuid id PK
enum role "SuperAdmin, Admin, Agent, Merchant"
string email "UNIQUE"
string password_hash
string company_name
enum status "Pending, Active, Blocked, Inactive, Rejected"
uuid invited_by_agent_id FK "Nullable"
string client_alias "Nullable"
timestamp created_at
}
PROVIDERS {
uuid id PK
string real_name
text description
jsonb integration_methods
string support_contacts
enum status "Active, Paused"
timestamp created_at
}
OFFERS {
uuid id PK
uuid provider_id FK
string alias "Public Name"
decimal raw_rate
decimal platform_margin
jsonb attributes
enum status "Active, Paused"
uuid created_by FK "Admin ID"
}
DEALS {
uuid id PK
uuid initiator_id FK "Agent/Admin/Merchant"
uuid merchant_id FK "Nullable"
enum source "Inbound, Agent_Outbound, Admin_Outbound"
enum status "Draft, Negotiation, Agreed, Declined, Revoked, Terminated, Archived"
uuid assignee_id FK "Nullable"
string merchant_name_brand
string merchant_country
string merchant_currency
uuid chosen_provider_id FK "Set when Agreed"
timestamp expires_at
timestamp created_at
}
DEAL_EVENTS {
uuid id PK
uuid deal_id FK
uuid actor_id FK
enum event_type "Status_Change, Margin_Update, Special_Terms_Request, Note_Added"
jsonb payload
text comment
string field_reference "[Post-MVP]"
timestamp created_at
}
DEAL_ITEMS {
uuid id PK
uuid deal_id FK
uuid offer_id FK
uuid provider_id FK
decimal agent_margin
decimal raw_rate_snapshot
decimal platform_margin_snapshot
decimal final_fee_snapshot
}2. Описание таблиц
2.1 users (Пользователи)
Единая таблица для SuperAdmin, Admin, агентов и мерчантов. Провайдер в эту таблицу не входит.
id(UUID): уникальный идентификатор.role(Enum):SuperAdmin,Admin,Agent,Merchant. У записи вPendingроль ещё не назначена.email(String): уникальный ключ (UNIQUE).company_name(String): официальное название компании при регистрации.status(Enum):Pending,Active,Blocked,Inactive,Rejected. Новые регистрации попадают вPendingдо обработки SuperAdmin.invited_by_agent_id(UUID, FK, Nullable): ID агента, который привёл мерчанта. В MVP = NULL (приглашает SuperAdmin).[Post-MVP].client_alias(String, Nullable): псевдоним мерчанта.[Post-MVP].
2.2 providers (Провайдеры)
Системная таблица платежных систем. Скрыта от агентов и мерчантов.
id(UUID): уникальный идентификатор.real_name(String): настоящее имя платежной системы.status(Enum):ActiveилиPaused. Пауза каскадно паузит связанные офферы.
2.3 offers (Офферы каталога)
Публичные тарифы, которые Admin / SuperAdmin создают на базе провайдеров.
id(UUID): уникальный идентификатор.provider_id(UUID, FK): ссылка на провайдера.alias(String): публичный псевдоним для каталога (напримерCard-EU-01).raw_rate(Decimal):Raw Provider Rate.platform_margin(Decimal):Platform Margin.attributes(JSONB): характеристики оффера (см. схему ниже).status(Enum):ActiveилиPaused.
2.4 deals (Сделки / тикеты)
Главная бизнес-сущность. Ticket-based: итерации внутри одного тикета.
id(UUID): уникальный идентификатор (используется для Secure-ссылки).initiator_id(UUID, FK): кто создал сделку.merchant_id(UUID, FK, Nullable): мерчант. Пусто, пока мерчант ещё не зарегистрирован.source(Enum):Inbound,Agent_Outbound,Admin_Outbound.status(Enum):Draft,Negotiation,Agreed,Declined,Revoked,Terminated,Archived. Истечение срока Secure-ссылки →Revoked.assignee_id(UUID, FK, Nullable): на ком сейчас мяч (Assignee). Agent, Admin или Merchant.merchant_name_brand(String): название бренда, еслиmerchant_idещё неизвестен.merchant_country/merchant_currency: гео-данные мерчанта для аналитики.chosen_provider_id(UUID, FK): заполняется только при переходе вAgreed.expires_at(Timestamp): срок жизни текущей Secure-ссылки (1 неделя с момента отправки).
2.5 deal_events (Журнал событий / Audit Trail)
Лента изменений тикета и текстовых обоснований.
id(UUID): уникальный идентификатор события.deal_id(UUID, FK): ссылка на сделку.actor_id(UUID, FK): пользователь, совершивший действие.event_type(Enum):Status_Change,Margin_Update,Special_Terms_Request,Note_Added.payload(JSONB): детали изменения (например старая маржа → новая).comment(Text, Nullable): текстовое обоснование. В MVP — к кнопкеRequest Special Terms.field_reference(String, Nullable):[Post-MVP]указатель на поле для точечного торга.
2.6 deal_items (Элементы сделки)
Связь сделки с офферами и снимки цены.
id(UUID): уникальный идентификатор элемента.deal_id(UUID, FK): ссылка на сделку (Proposal).offer_id(UUID, FK): ссылка на оффер.agent_margin(Decimal):Agent Marginна момент сборки.raw_rate_snapshot/platform_margin_snapshot/final_fee_snapshot(Decimal): замороженныеRaw Provider Rate,Platform MarginиTotal Fee.
3. Структура атрибутов оффера (JSONB Schema)
Поле attributes в offers хранит конфигурацию условий. Типизация нужна для динамической формы оффера и фильтров каталога.
{
"general": {
"regions": ["Africa", "Europe"],
"countries": ["DE", "NO", "FI", "KE"],
"authorization_currencies": ["EUR", "KES", "NGN", "JPY"],
"traffic_type": ["Trusted", "Untrusted", "FTD/STD", "with KYC"],
"integration_types": ["Hosted", "API", "H2H"]
},
"payin": {
"methods": ["Visa/MC", "M-Pesa", "OpenBanking"],
"commission": "3.2% min 0.20 EUR",
"limits": {
"min": "1 KES",
"max": "122500 KES"
},
"kyc_conditions": "499 EUR (w/o KYC), 15 000 EUR (with KYC)"
},
"payout": {
"methods": ["Airtel money", "OpenBanking"],
"commission": "2% + 0.30 EUR",
"limits": {
"min": "10 KES",
"max": "250000 KES"
},
"kyc_conditions": "Особые условия, если есть"
},
"settlement": {
"currencies": ["USDT", "USDC"],
"commission": "1.5%",
"terms": "T+1, twice a week",
"min_amount": "5000 USDC",
"exchange_rate": "Provider exchange rate",
"destination": "Kraken или внешний кошелек"
},
"fees_and_holds": {
"rolling_reserve": "10% / 180 дней",
"refund_fee": "1 EUR",
"chargeback_fee": "80 EUR"
},
"additional": {
"special_requirements": "URL, KYB обязательны",
"user_flow": "видео-флоу, редирект",
"notes": "Любая другая важная информация"
}
}Провайдер (providers) может хранить приватные атрибуты в похожем JSON (SLA, имена менеджеров, доступы к API). Они не наследуются в оффер и скрыты ото всех, кроме Admin / SuperAdmin.