Архитектура ISP (OSS/BSS)
Billing & Finance Service
Баланс, платежи, двойная запись (ledger), dunning, счета, интеграция с банками.
Billing & Finance Service
Bounded Context: Billing & Finance
Владелец данных: Account, Transaction, Invoice, DunningState
Технологии: NestJS (Bun 1.3+), Drizzle ORM, PostgreSQL, ConnectRPC, RabbitMQ, BullMQ, Redis
Ответственность
Billing & Finance — финансовое ядро платформы. Реализует полный цикл Revenue Management — от тарификации до сверки с банками. Аналог модулей Finance в Hydra Billing и Billing в Splynx.
- Accounts: Лицевые счета клиентов (balance, status, credit limit).
- Ledger: Двойная запись (double-entry) — каждая операция = проводка (debit/credit). Баланс — это производная от журнала транзакций, а не отдельное поле.
- Rating Engine: Расчёт стоимости с учётом тарифа, скидок, proration (пропорциональный перерасчёт при смене тарифа в середине периода).
- Payments: Приём платежей от банков (Сбербанк, Тинькофф, ЮKassa, касса, терминалы), autopay (рекуррентные списания с привязанной карты через токенизацию).
- Invoicing: Генерация счетов (PDF), актов сверки, УПД для юрлиц. Интеграция с 1С.
- Dunning: Многоступенчатая автоматическая работа с задолженностью (напоминание → captive portal → ограничение скорости → отключение → списание безнадёжного долга).
- Reconciliation: Ежедневная сверка баланса платформы с банковскими выписками. Автоматическое обнаружение расхождений.
Ключевые бизнес-инварианты
| Инвариант | Описание | Нарушение = |
|---|---|---|
| Append-only ledger | Баланс никогда не обновляется напрямую — только через создание Transaction | Финансовая несогласованность |
| Double-entry | Каждая операция = debit одного счёта + credit другого (или системного) | Невозможность reconciliation |
| Idempotent payments | external_id уникален для каждого источника — повторный webhook не создаёт дубль | Двойное зачисление |
| Balance consistency | balance_after в каждой Transaction = предыдущий balance_after ± amount | Потеря денег |
| Audit trail | Все ручные корректировки (ManualAdjust) требуют reason и логируются в audit log | Нарушение комплаенса |
Агрегат: Account
Loading diagram...
Правила агрегата:
- Balance изменяется ТОЛЬКО через создание Transaction (append-only ledger).
- Каждая Transaction фиксирует
balance_afterдля аудита. external_idуникален для каждого источника (дедупликация платежей от банков).
Dunning State Machine
Loading diagram...
| Стадия | Действия | Срок |
|---|---|---|
| None | Нормальная работа | — |
| Soft | SMS "Пополните баланс" + Captive Portal | D+0 (сразу при < 0) |
| Hard | Ограничение скорости (64kbps) + повторное SMS | D+3 |
| Terminate | Полная блокировка → автоматическое расторжение | D+N (настраивается) |
При каждом переходе публикуется DunningStageChangedEvent → Provisioning (suspend/resume) + Notification (SMS).
ConnectRPC API
service BillingService {
rpc GetAccount(GetAccountRequest) returns (GetAccountResponse);
rpc GetBalance(GetBalanceRequest) returns (GetBalanceResponse);
rpc ListTransactions(ListTransactionsRequest) returns (ListTransactionsResponse);
rpc RegisterPayment(RegisterPaymentRequest) returns (RegisterPaymentResponse);
rpc ManualAdjust(ManualAdjustRequest) returns (ManualAdjustResponse);
rpc GenerateInvoice(GenerateInvoiceRequest) returns (GenerateInvoiceResponse);
rpc GetInvoice(GetInvoiceRequest) returns (GetInvoiceResponse);
}| Метод | RBAC | Idempotent | Описание |
|---|---|---|---|
GetBalance | isp-viewer+ | — | Текущий баланс + dunning stage |
ListTransactions | isp-viewer+ | — | История операций (курсорная пагинация) |
RegisterPayment | isp-operator+ | ✅ (external_id) | Зачисление от банка/кассы |
ManualAdjust | isp-admin+ | ✅ | Ручная корректировка (audit log!) |
GenerateInvoice | isp-operator+ | ✅ | Сформировать счёт за период |
Доменные события
Exchange: billing.events (Topic)
| Routing Key | Событие | Потребители |
|---|---|---|
payment.received | PaymentReceivedEvent | OMS, Notification |
balance.negative | BalanceNegativeEvent | Notification |
dunning.stage_changed | DunningStageChangedEvent | Provisioning, Notification |
invoice.issued | InvoiceIssuedEvent | Notification |
charge.completed | ChargeCompletedEvent | — (audit) |
Входящие команды
Exchange: billing.commands (Direct)
| Routing Key | Команда | Источник | Описание |
|---|---|---|---|
charge.account | ChargeAccountCommand | OMS | Списание (Saga-шаг) |
refund | RefundCommand | OMS | Возврат (Saga-компенсация) |
Зависимости
Loading diagram...
Background Jobs (BullMQ)
| Job | Расписание | Queue | Описание |
|---|---|---|---|
billing.recurring_charge | Ежедневно, 03:00 | billing_critical | Массовое рекуррентное списание |
billing.dunning_check | Каждые 4 часа | billing_background | Проверка и переходы dunning |
billing.generate_invoice | 1-е число месяца | billing_critical | Массовая генерация счетов |
billing.generate_receipt | По событию | default | PDF-квитанция после оплаты |
billing.reconciliation | Ежедневно, 06:00 | billing_background | Сверка с банковскими выписками |
Интеграция с банками
Loading diagram...
Ссылки:
- Protobuf-контракт: billing/v1/billing_service.proto
- Outbox/Inbox: Паттерны интеграции
- Слой BSS: BSS Layer