Billing Service
Immutable double-entry ledger backed by PostgreSQL. NMI + value.io under the custom TSYS deal. Explicit commercial state machines for subscriptions, dunning, refunds, disputes. Verified event inbox to entitlement projection. Boja is the merchant of record.
Double-Entry Ledger — $100 Sale Example
Every money movement produces a balanced journal entry. No funds change hands without a matching set of postings that sum to zero per currency.
Billing Event Flow
NMI / value.io callback or billing source event
→ signature/authenticity verification
→ idempotent billing_events insert (source event id)
→ payment/commercial state + balanced ledger posting
→ subscription projection + transactional outbox
→ entitlement grants/revocations
→ PostgreSQL commit
→ Zero updates affected clients
Four Separate Records
Billing concerns are split into four tables with distinct responsibilities. Mixing these would create audit risk and make state debugging impossible.
| Record | Tables | Purpose |
|---|---|---|
| Immutable money ledger | journal_entries, postings, ledger_accounts |
Every money movement, balanced, append-only |
| Payment execution | payment_intents, provider_transactions |
Command execution, provider state tracking |
| Lifecycle state | billing_subscriptions, dunning_attempts, refunds, disputes |
Commercial state machines for each subscription lifecycle |
| Entitlement projection | entitlements, entitlement_grants |
Access rights derived from billing facts; safe for Zero to read |
Dunning Policy
The hu2-baseline versioned dunning policy defines retry cadence and terminal conditions. Dunning policies are versioned — active subscriptions reference the policy version at time of enrollment.
| Attempt | Timing | Action |
|---|---|---|
| 1 | Payment fails | Retry immediately — transient network/processor failure |
| 2 | +24 hours | Retry with account updater check |
| 3 | +72 hours | Customer reminder email dispatched |
| 4–6 | Bounded cadence | Retries with decline code classification |
| Terminal | After policy window | Grace period → access revocation |
Key Invariants
No NMI/value.io calls on login, sync-read, or mutation authorization paths. Billing state is a projection, not a real-time check gate.
- Billing internals never leak into authorization: Zero publishes only safe effective entitlements, never raw ledger rows or subscription state.
- Every journal must balance per currency: Postings are append-only; no row is ever updated or deleted.
- Idempotent event ingestion: Each external event is keyed by source event ID. Duplicate delivery is a no-op.
- External subscription sources enter via verified adapter only: Whop, Stripe, or any future source writes to
billing_events— never directly to entitlements.
V1 Scope & Exclusions
NMI + value.io processors, subscriptions, seller payouts, affiliate commissions/reserves, 27-day account updater window, hu2-baseline dunning policy.
Trials, coupons, proration, subscription pauses, escrow/wallet, Ethoca dispute automation, crypto payments, gift card balances, non-NMI payment processors.