Skip to main content
When a customer changes plan in the middle of a billing period, somebody owes somebody money for the unused fraction. Proration (a pure class in @duro/billing) computes the fair number.

The intuition

A period is a span of time the customer already paid for. Halfway through a month on a ₦5,000 plan, they’ve consumed ₦2,500 of value and have ₦2,500 of unused credit. If they switch to a ₦10,000 plan, the new plan’s unused portion costs ₦5,000. The difference is what changes hands.

The computation

Proration.changePlan works in daily rates so any interval behaves consistently:
  • dailyRate(amount, periodDays) = amount / periodDays
  • unusedCredit = daysRemaining × oldDailyRate
  • newCharge = daysRemaining × newDailyRate
  • result = newCharge − unusedCredit
It returns a tagged result — { amount, kind } where kind is 'charge', 'credit', or 'none' — so the caller never has to interpret a sign.
ChangedaysRemainingResult
₦5k → ₦10k, half a month left15 / 30charge ₦2,500
₦10k → ₦5k, half a month left15 / 30credit ₦2,500
same priceanynone

Where the credit goes

An upgrade’s charge can be collected immediately or rolled into the next invoice; a downgrade’s credit lands on the subscription’s balance field (integer kobo), which is netted against the next renewal. Money owed to the customer never disappears into a rounding gap — it’s a real balance carried on the row.
Proration is pure and unit-tested with the exact figures above (₦400k charge / ₦600k credit cases verified live during the build). Like the rest of @duro/billing, it touches no database — the SubscriptionService feeds it amounts and periods and stores what it returns.
Next, the heart of the system: recovery & dunning.