feat(payments): add the payments schema, currency domain and money type
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m45s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m45s
Stand up the payments data foundation: a self-contained `payments` Postgres schema for the in-game currency, wallets, benefits, catalog, orders and the append-only operations ledger, behind a domain package — nothing wired to real money yet. - Migration 00010: the `payments` schema and a NOLOGIN confinement role (ALL on payments.*, nothing on backend); the ledger with a BEFORE UPDATE/DELETE append-only trigger and a partial idempotency index; the materialised balances/benefits; the catalog (atoms seeded) + products + per-method prices; the typed single-row config; orders and payment_events. There is no cross-schema foreign key — account_id is a plain uuid kept consistent in code, which keeps the domain extractable. Expand-contract and reversible. - Money is a bigint in the currency's minor units carried by a `Money` value type (exact, math/big): no float ever touches an amount, and a whole-unit currency cannot hold a fraction. - Extend jetgen to generate the payments schema; construct the service in the composition root behind a narrow interface with a boot reachability check. - Tests: integration (role confinement via SET ROLE, the append-only trigger, CHECK constraints, the idempotency index, and a forward+backward migration), Money unit tests, and an import-boundary test keeping the payments jet code private to the domain. - Docs: PLAN.md, docs/PAYMENTS.md (+ _ru mirror) updated to the built model.
This commit is contained in:
+13
-8
@@ -48,8 +48,9 @@
|
||||
| `telegram` | Покупка за Stars TG |
|
||||
| `direct` | Покупка через Robokassa (web / native) |
|
||||
|
||||
Один аккаунт держит все три сегмента одновременно: `баланс = (account_id, source)`, ровно
|
||||
три записи. Сегментация **не** по привязке (identity) — см. §6.
|
||||
Один аккаунт держит все три сегмента одновременно: `баланс = (account_id, source)`, до трёх
|
||||
записей — запись материализуется лениво при первом пополнении сегмента, отсутствующий сегмент
|
||||
читается как ноль. Сегментация **не** по привязке (identity) — см. §6.
|
||||
|
||||
Почему сегментировано, а не один общий баланс: правила сторов запрещают активировать
|
||||
ценность, оплаченную мимо их кассы. Фишки, пополненные внутри VK (Голоса), тратятся только
|
||||
@@ -304,9 +305,11 @@ INSERT** (никогда UPDATE/DELETE — полный аудит). Балан
|
||||
|
||||
Платёжный домен живёт в **своей схеме `payments`** в общем инстансе Postgres, со своим
|
||||
DB-ролём (права только на `payments`) и доменным пакетом за жёстким интерфейсом.
|
||||
Cross-schema внешние ключи к `backend.accounts` держат трату «Фишки↔бенефит» атомарной в
|
||||
одной транзакции. Сохранность — **PITR** (непрерывный WAL-архив), независимо от топологии
|
||||
базы.
|
||||
**Cross-schema внешнего ключа** к `backend.accounts` **нет** — идентификатор аккаунта здесь
|
||||
обычное значение, согласуемое в коде и связываемое с tombstone-аккаунтом / досье
|
||||
retained-identities по стабильному id. Это держит трату «Фишки↔бенефит» атомарной **внутри
|
||||
`payments`** и делает домен извлекаемым в свою базу. Сохранность — **PITR** (непрерывный
|
||||
WAL-архив), независимо от топологии базы.
|
||||
|
||||
Основные таблицы (финальные имена/колонки — в `PLAN.md`):
|
||||
|
||||
@@ -322,9 +325,11 @@ Cross-schema внешние ключи к `backend.accounts` держат тра
|
||||
(pending/paid/expired).
|
||||
- **payment_events** — succeeded/failed/refunded для диспетчера.
|
||||
|
||||
Legacy `accounts.hint_balance` и `accounts.paid_account` **устаревают** и удаляются
|
||||
(expand-contract) в пользу сегментированной модели; ни то, ни другое в проде никогда не
|
||||
выставлялось (потока покупки не было), поэтому legacy-значения обнуляются.
|
||||
Legacy `accounts.hint_balance` и `accounts.paid_account` **устаревают** в пользу
|
||||
сегментированной модели и удаляются в отдельной **contract-миграции** (expand-contract, после
|
||||
того как валютное ядро переключит чтения, и после Release 2 — откат образа остаётся безопасным
|
||||
для БД); ни то, ни другое в проде никогда не выставлялось (потока покупки не было), поэтому
|
||||
legacy-значения обнуляются.
|
||||
|
||||
## 15. Словарь
|
||||
|
||||
|
||||
Reference in New Issue
Block a user