PDE must ship before credit notes can reference clean invoices

credit notes enable automatic reinstatement in the brain

The invoice is the unit of collection
thesis

The invoice is the central record — not the subscription. All payment retry logic, dunning state, and revenue recognition pivot on invoice events and state transitions.

The invoice amount should be the debt
thesis

The amount shown on a Stripe invoice should equal the actual amount the customer owes. Today it doesn't — the invoice shows the original billed amount while the real debt is hidden in metadata.

Hiding debt from the user is counterintuitive and a losing battle
thesis

Trying to prevent the user from paying the original invoice is counterintuitive AND a losing battle. They see it, they click it, and the system either blocks them or double-charges.

The two-button problem
pattern

When payment fails and the invoice goes uncollectible, the user ends up with two invoices in their dashboard: the original they recognize and a synthetic consolidated invoice they don't. They don't know which to pay.

Invoice state: Draft — editable, no dunning
entity

Draft invoices are created but not finalized. Editable. Payment not yet due. Dunning doesn't process draft invoices.

Invoice state: Open — locked, payment due
entity

Finalized invoices are ready for payment. Invoice details locked. Payment due date set.

Invoice state: Paid — terminal success
entity

Payment succeeded. Revenue recognized. Dunning state cleared. All retry counters cleared.

Invoice state: Uncollectible — terminal failure
entity

Retries exhausted. Marked as uncollectible. Account may be flagged bad_debt. May trigger subscription cancellation.

Credit note mechanism
mechanism

When an invoice goes uncollectible, issue a credit note for the unused in-advance service. The invoice amount adjusts. That adjusted amount is the debt. No metadata. No consolidation.

Credit note pipeline: classify → calculate → issue → adjust
mechanism

Pipeline: receive invoice.marked_uncollectible event → classify line items as in-advance or in-arrears → calculate unused fraction per in-advance item → issue credit note via Stripe API → invoice amount_remaining adjusts to the debt amount.

Credit note calculation formula
mechanism

For each in-advance line item: (days_remaining / total_days_in_period) × line_item_amount. Sum these to get the credit note total. In-arrears items get zero credit.

Reinstatement flow: pay original → lift flag → new subscription
mechanism

Five steps: user returns → sees original invoice with adjusted amount → pays via Unified Checkout → bad-debt flag lifted → new subscription created with fresh BCA. No synthetic invoices.

The 906 migration: retroactive credit notes
mechanism

Migrating the 906 to honest invoicing means issuing retroactive credit notes against their uncollectible invoices for unused in-advance time. The invoice amount adjusts. When the user returns, the new flow pays the original invoice at its corrected amount.

Migration phase 4: Consistency verification
mechanism

After all batches complete, run consistency check across all 906 accounts. Verify: credit note exists, amount_remaining correct, old metadata preserved (historical), reinstatement path works.

Q4: bad debt reduced 93% — 14K → ~1K accounts
fact

Bad debt dropped 93% — from ~13.7K to ~1.3K in three days during the November cleanup. The strongest proof that the recompute engine works at scale.

Prerequisite: Stripe credit note on uncollectible — unvalidated
constraint

If Stripe rejects credit notes on uncollectible invoices, the entire mechanism needs rethinking. This is the load-bearing prerequisite.

Plan risk: Stripe rejects credit notes on uncollectible — entire approach blocked
constraint

If Stripe rejects credit notes on uncollectible invoices, the entire approach is blocked. Fallback: 1:1 replacement invoices (simpler than consolidated, but still synthetic).