Billing
Manages premium billing, invoices, payments, and the double-entry ledger — the financial settlement layer between the plan and its members.
Overview
The Billing service handles all money movement on the platform. When a policy activates, Billing consumes the enrollment.policy.activated event and generates the first premium invoice. Subsequent invoices are generated on each billing cycle by a scheduled job. The service processes incoming payments, applies them to open invoices, records adjustments, and maintains a double-entry ledger of all financial activity.
Charges represent amounts owed for individual coverage periods. Invoices group charges into billable units. Payments reduce the outstanding invoice balance. Adjustments handle credits, debits, and retroactive corrections. The ledger provides an immutable audit trail of all financial events. Explanation of Benefits (EOB) documents are generated from claims data and surfaced through Billing's EOB API.
Installment schedules allow members to split premium payments into smaller periodic amounts. An outbox worker ensures payment and invoice events are reliably published to Kafka even if the broker is temporarily unavailable.
Responsibilities
- Generate premium invoices on policy activation and on each billing cycle
- Process and record incoming premium payments
- Manage billing adjustments (credits, debits, retroactive corrections)
- Maintain a double-entry ledger of all financial transactions
- Manage installment payment schedules
- Generate and serve EOB documents for adjudicated claims
- Publish billing events consumed by Notifications and Eligibility
- Expose internal invoice endpoint for Claims service integration
Database
Schema: billing
| Table | Purpose |
|---|---|
charges | Individual premium charges per coverage period |
invoices | Grouped invoices with status lifecycle (DRAFT → OPEN → PAID/VOID) |
invoice_line_items | Line items within an invoice linked to charges |
payments | Recorded payment transactions against invoices |
adjustments | Manual and automated billing adjustments |
ledger_entries | Double-entry ledger of all financial events |
outbox | Transactional outbox for reliable Kafka event publishing |
projection_checkpoints | Kafka consumer offset tracking for the enrollment projection |
API Routes
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /charges | JWT | List charges (filterable) |
GET | /charges/{locator} | JWT | Get charge by locator |
PATCH | /charges/{locator}/void | JWT | Void a charge |
GET | /invoices | JWT | List invoices (filterable) |
GET | /invoices/{locator} | JWT | Get invoice by locator |
PATCH | /invoices/{locator}/finalise | JWT | Finalise a draft invoice |
PATCH | /invoices/{locator}/void | JWT | Void an invoice |
POST | /invoices/{locator}/number/generate | JWT | Generate an invoice display number |
POST | /payments | JWT | Record a payment |
GET | /payments | JWT | List payments (filterable) |
GET | /payments/{locator} | JWT | Get payment by locator |
PATCH | /payments/{locator}/void | JWT | Void a payment |
POST | /adjustments | JWT | Create a billing adjustment |
GET | /adjustments | JWT | List adjustments |
GET | /adjustments/{locator} | JWT | Get adjustment by locator |
PATCH | /adjustments/{locator}/apply | JWT | Apply an adjustment |
PATCH | /adjustments/{locator}/reverse | JWT | Reverse an applied adjustment |
GET | /ledger | JWT | List ledger entries |
GET | /ledger/{locator} | JWT | Get a ledger entry |
GET | /eob | JWT | List EOB documents |
GET | /eob/{claimLocator} | JWT | Get EOB for a claim |
POST | /installments | JWT | Create an installment schedule |
PATCH | /installments/{locator}/cancel | JWT | Cancel an installment schedule |
GET | /internal/invoices/{locator} | Internal | Fetch invoice for Claims (no JWT) |
Events
Publishes
| Topic | When |
|---|---|
billing.invoice.generated | A new invoice is finalised and ready for payment |
billing.payment.received | A payment is successfully recorded |
billing.payment.missed | A payment due date passes without payment |
Consumes
| Topic | Action taken |
|---|---|
enrollment.policy.activated | Generates first invoice and initialises billing account for the new policy |
Dependencies
| Service | How used |
|---|---|
| Enrollment | Fetches policy and term details when processing billing events |
| Claims | Fetches claim data for EOB generation |
| Policy Admin | Fetches product pricing rules for invoice calculation |
Key Design Decisions
Double-entry ledger: Every financial event (charge creation, payment receipt, adjustment) writes corresponding debit and credit entries to the ledger_entries table. This provides an audit trail and enables balance reconciliation independent of the invoice/payment tables.
EOB as a billing concern: EOB documents are generated from adjudicated claims data but surfaced through Billing's API. This keeps member financial summaries in one service rather than splitting them across Claims and Billing.