Repository: granit-fx/granit-dotnet
Author: jfmeyers
## Description
**Constat.** `Granit.CustomerBalance` already exists and covers **~85-90% of ORB Credits needs**:
- Aggregate `BalanceAccount` (multi-tenant, multi-currency, append-only ledger via `BalanceTransaction`)
- Transaction sources: `Promotional`, `Overpayment`, `ManualAdjustment`, `InvoiceDeduction`, `RefundCredit`, `Expiration`
- Domain methods `Credit(...)` and `Debit(...)` with `InsufficientBalanceException`
- Events: `BalanceCreditedEto`, `BalanceDebitedEto`, `CreditExpiredEto`
- Expiration job `CreditExpirationScanJob` (cron `0 */6 * * *`) + idempotent service
- **Invoicing integration already in place** via `IInvoicePrePaymentProcessor` (deducts credit before PSP charge) — idempotency via `(ReferenceId=invoiceId, Source=InvoiceDeduction)` lookup, no need for distributed saga
- Endpoints: `GET /balance`, `GET /transactions`, `POST /balance/credit`
- Permissions, metrics, ActivitySource — all in place
- Documentation page exists
**Problem.** Residual gaps vs ORB Credits to close — and avoid creating a duplicate `Granit.Credits` module that would invalidate years of investment in `CustomerBalance`.
**Solution.** Extend the existing module with 4 surgical additions, no new package.
**Alternatives considered.**
- *Create `Granit.Credits` from scratch*: rejected — would duplicate `BalanceAccount`, `BalanceTransaction`, expiration job, Invoicing integration. Wasteful and confusing.
- *Add a tiered drawdown strategy in MVP*: deferred to backlog (most B2B SaaS treat all credits equally; complicates the ledger)
## User Stories
- #1176 - Add admin debit endpoint for manual customer balance drawdown
- #1177 - Add CreditPreExpirationScanJob with CreditNearExpirationEto event
- #1178 - [TECH DEBT] Backlog — Credit drawdown ordering strategies (FIFO / EarliestExpiringFirst)
- #1179 - Document ORB to Granit.CustomerBalance mapping ADR + customer-balance.mdx update
## Expected deliverables
- [ ] `POST /api/granit/customer-balance/balance/debit` admin endpoint (manual drawdown outside invoice flow)
- [ ] `CreditPreExpirationScanJob` + `CreditNearExpirationEto` event for early-warning notifications
- [ ] ADR `035-customer-balance-orb-credits-mapping.md` documenting ORB ↔ Granit terminology mapping
- [ ] Update existing `customer-balance.mdx` doc page with "ORB alignment" section
- [ ] Backlog issue (Tech Debt): credit drawdown ordering strategies (FIFO / EarliestExpiringFirst / LargestFirst / Lifo)
## Compliance
- **GDPR**: `Reason` field on debit must not contain PII
- **ISO 27001**: Admin debit operation gated by `CustomerBalance.Credits.Manage` permission, audited via `BalanceTransaction` ledger
- **Backward compatibility**: All additive — no breaking change
## Effort
1-2 weeks (vs 3-4 weeks for from-scratch creation — savings of ~50%).
## Branch
`feature/customer-balance-orb-alignment`
## Dependencies
None hard. Can start in parallel with Phase 3.
## Parent
Part of #1155 — Align Granit framework on ORB billing concepts (Phase 4). Cross-references existing module EPIC #874.
Source: GITHUB