UC-WLT-001. Получение денег кредитует балансы получателей
Назначение
Описывает системный сценарий: оператор ПИН зарегистрировал акт поступления средств от застройщика
(platform-receipt.md), начисления перешли в MoneyReceived —
и кошелёк разносит полученную комиссию по счетам получателей: агенту, агентству и на счёт дохода ПИН,
а связанное реферальное вознаграждение — партнёру. С этого момента холдеры видят суммы на своих балансах.
Связан с контекстом wallet.
Metadata
| Поле | Значение |
|---|---|
| ID | UC-WLT-001 |
| Контекст | wallet (см. docs/06-services/wallet/) |
| Тип | основной |
| Статус | draft |
Актор
| Поле | Значение |
|---|---|
| Роль | System (consumer событий rewards) |
| Permissions / scope | не применяется (m2m-обработка событий) |
| Tenant | вне tenant-модели — счета user-centric / company / системные (wallet-account.md) |
Предусловие
- В
rewardsначисление комиссии подтверждено и вошло в зарегистрированный акт поступления средств — статусMoneyReceived, распределение построено тремя строкамиPlatform/Agency/Agent(commission-accrual.md, INV-4/5/9). - Событие
pin.rewards.commission-money-received.v1опубликовано с построчной детализациейDistributions[](id строки,RecipientType,RecipientUserId?,Amount).
Триггер
| Поле | Значение |
|---|---|
| Источник | Kafka event |
| Триггерящий элемент | pin.rewards.commission-money-received.v1 → consumer rewards-commission-money-received.md |
Основной Поток
- Кошелёк получает событие получения денег: начисление, агентство (
TenantId),CommissionAmountи три строки распределения; проверяет вход (ровно три строки, Σ строк =CommissionAmount, единая допустимая валюта — negative-ветки в consumer-доке). - Для каждой ненулевой строки
Distributions[]кошелёк определяет счёт-получатель поRecipientType:Agent→ счётAgentCommissionпользователяRecipientUserId;Agency→ счётAgencyCommissionагентства;Platform→ системный счётPlatformRevenue. Нулевая строка проводку не порождает. - Если счёта-получателя ещё нет — он создаётся lazy при первом кредите (wallet-account.md, INV-WLT-2).
- Проводится одна транзакция
CommissionCredit(SourceId= id начисления): дебетPlatformSettlementнаCommissionAmount, кредиты счетов-получателей на суммы строк; Σ дебетов = Σ кредитов (INV-WLT-5), валюта строк = валюте счетов (INV-WLT-4); балансы счетов иBalanceAfterпроводок фиксируются атомарно (ledger-transaction.md). - По каждой проводке холдерского счёта (
AgentCommission,AgencyCommission) публикуетсяpin.wallet.transaction-recorded.v1(wallet-transaction-recorded.md); проводкаPlatformRevenueсобытия не порождает. notificationsдоставляет холдерам «вам начислено {Amount}, баланс {BalanceAfter}».- Агент и руководитель агентства видят пополненные балансы в ЛК (UC-WLT-004).
Альтернативные Потоки
A1. Реферальное вознаграждение той же сделки
- Регистрация акта атомарно перевела связанное реферальное начисление
Pending → Approved(referral-accrual.md, INV-RA-3); в топикpin.rewards.referral-accruals.v1ушло событие смены статуса. - Кошелёк (consumer rewards-referral-accrual-status-changed.md)
проводит
ReferralCredit(SourceId= id начисления): дебетPlatformSettlement, кредитReferralCommissionпартнёра наAmountначисления. - Далее — шаги 5–7 основного потока для счёта партнёра.
A2. Повторная доставка события
- Кошелёк получает то же событие повторно (at-least-once) либо второй инстанс обрабатывает его параллельно.
- Проведение упирается в unique
(CommissionCredit, id начисления)— no-op (INV-WLT-7): балансы не меняются, событиеtransaction-recordedповторно не публикуется.
A3. Прерванная обработка
- Обработка прервалась до фиксации (сбой/рестарт): транзакция атомарна — частичных проводок не остаётся.
- Событие доставляется повторно и проводится целиком (либо no-op, если фиксация успела пройти). Итог эквивалентен однократной полной обработке.
A4. Отмена реферального начисления после Approved
- Оператор ПИН отменил рефералку из
Approved(начисление свободно от поручения, referral-accrual.md, INV-RA-5). - Кошелёк по событию
Cancelledпроводит компенсациюCorrection(SourceId= id исходнойReferralCredit-транзакции): зеркальные проводки, баланс партнёра уменьшается; повторная компенсация исключена unique-ключом (INV-WLT-6).
Постусловие
- По начислению существует ровно одна транзакция
CommissionCredit: дебетPlatformSettlementнаCommissionAmount+ кредиты счетов-получателей по ненулевым строкам распределения. - Балансы
AgentCommissionагента,AgencyCommissionагентства иPlatformRevenueувеличены на суммы своих строк; балансPlatformSettlementуменьшен наCommissionAmountначисления. - Для каждого затронутого счёта:
Balance= Σ кредитов − Σ дебетов его проводок. - События
pin.wallet.transaction-recorded.v1опубликованы по холдерским проводкам; уведомления доставлены.
Kafka Интеграционные События
| Событие | Topic | Когда публикуется | Consumers |
|---|---|---|---|
WalletTransactionRecordedKafkaEvent | pin.wallet.transaction-recorded.v1 | Атомарно с проведением каждой транзакции — по проводке холдерского счёта | notifications |
Acceptance Criteria
| # | Критерий | Проверка |
|---|---|---|
| AC1 | Given событие с тремя ненулевыми строками распределения, When кошелёк обработал его, Then проведена одна CommissionCredit-транзакция (дебет PlatformSettlement + три кредита) и балансы агента/агентства/PlatformRevenue выросли ровно на суммы строк | интеграционный тест consumer-а |
| AC2 | Given холдер без счёта, When приходит первый кредит, Then счёт нужного вида создан lazy и кредитован; параллельный кредит тому же холдеру не создаёт второй счёт | тест гонки lazy-создания (INV-WLT-2) |
| AC3 | Given уже обработанное событие, When оно доставлено повторно, Then балансы не изменились и новые транзакции не появились (no-op по (OperationKind, SourceId)) | тест двойной доставки (INV-WLT-7) |
| AC4 | Given любой затронутый счёт, When сценарий завершён, Then хранимый Balance равен пересчёту из журнала (Σ кредитов − Σ дебетов) | сверочный пересчёт (ADR-0021) |
| AC5 | Given связанная рефералка перешла в Approved, When кошелёк обработал событие статуса, Then счёт ReferralCommission партнёра кредитован на Amount — раздельно от его AgentCommission-баланса | интеграционный тест A1 |
| AC6 | Given строка с нулевой суммой (срезанный пул / fallback 0 п.п.), When событие обработано, Then нулевая проводка не создана, счёт lazy не заведён, сверка сумм сходится | тест нулевых строк |
| AC7 | Given валюта события вне допустимых платформой, When проводится транзакция, Then проведение отклонено в DLQ WALLET_CURRENCY_MISMATCH и балансы не изменены | негативный тест (INV-WLT-4) |
Обратные ссылки
Автоссылки: где используется этот документ.
- Use Cases (5): UC-RWD-017. Уволенный агент: судьба заработанных комиссий, UC-WLT-002. Выплата поручения дебетует баланс получателя, UC-WLT-003. ПИН-админ вручную корректирует баланс, UC-WLT-004. Холдер смотрит баланс и историю операций, UC-WLT-006. Компенсирующее начисление отражается в балансе после MoneyReceived
- scenarios (1): UC-OVR-010. От завершённой сделки до выплаты комиссии и реферального вознаграждения
- registries (1): Реестр use cases и user stories PIN
- index.md (1): Wallet Service
Связанные Документы
- Domain entity: wallet-account.md, ledger-transaction.md (карта проводок).
- Consumers: rewards-commission-money-received.md, rewards-referral-accrual-status-changed.md.
- Событие-источник: commission-accrual.md
(
commission-money-received), акт — platform-receipt.md; рефералка — referral-accrual.md. - Производимое событие: wallet-transaction-recorded.md.
- ADR-0065, ADR-0067.