Back to blog
Article

Proration math when a customer upgrades mid-cycle

Proration math when a customer upgrades mid-cycle
S

StriveBit

4 min readSaaS Development

Proration math when a customer upgrades mid-cycle

A customer on the ₹2,000/month Pro plan upgrades to the ₹5,000/month Business plan on day 17 of a 30-day billing cycle. The invoice you generate needs to reflect two things: the unused portion of Pro, and the remaining portion of Business. Get this wrong and you either overcharge a customer who notices, or undercharge a customer who doesn't.

Stripe and Razorpay both compute proration automatically when you change a subscription's price mid-cycle. The math is straightforward: they calculate the per-day cost of the old plan for the days already consumed, the per-day cost of the new plan for the days remaining, and net the two. The customer sees a single adjustment line on the next invoice.

The problem is that the invoice line reads something like `Prorated subscription adjustment — ₹2,833.00` and the customer has no idea what that number means. We've had support tickets asking why the upgrade cost more than the price difference. The answer is that the customer is paying for part of the new plan this cycle, minus a credit for the part of the old plan they didn't use. But the invoice doesn't say that.

Here is the actual computation for the example above, done manually so you can see what the billing engine is doing:

from datetime import date, timedelta

cycle_start = date(2024, 3, 1)
cycle_end = date(2024, 3, 31)
upgrade_date = date(2024, 3, 17)

old_price = 2000
new_price = 5000

total_days = (cycle_end - cycle_start).days
days_used = (upgrade_date - cycle_start).days
days_remaining = total_days - days_used

old_per_day = old_price / total_days
new_per_day = new_price / total_days

unused_credit = old_per_day * days_remaining
new_charge = new_per_day * days_remaining

proration = new_charge - unused_credit
print(f"Proration: ₹{proration:.2f}")

This prints `Proration: ₹3000.00`. The customer pays ₹3,000 now, not ₹5,000, because they already paid for 16 days of Pro and get credited for the 14 days they didn't use. The next cycle, they pay the full ₹5,000.

The tradeoff here is whether to compute proration yourself or let the billing provider handle it. We let the provider handle it. Stripe's proration logic is battle-tested, supports edge cases like trial periods and coupons, and the alternative is maintaining a billing engine that must never be wrong. The cost is that you lose some control over how the invoice is presented.

What we do control is the customer-facing explanation. After every plan change, we send an email that breaks down the proration in plain language before the invoice arrives. The email says: "You upgraded from Pro to Business on March 17. Your current billing cycle runs March 1 to March 31. You're credited ₹933.33 for the unused Pro days and charged ₹2,333.33 for the remaining Business days. Your invoice for this cycle is ₹3,000.00. Starting April 1, you'll be billed ₹5,000.00 per month."

We generate that email from the same proration numbers the billing provider gives us via webhook, so the two always match. The invoice itself stays as the provider formats it. The email is where the customer actually understands what happened.

One thing we learned the hard way: if a customer downgrades mid-cycle, most billing providers don't issue a refund. They apply a credit to the next invoice. This is legal and standard, but customers don't expect it. The same email pattern works: state the credit amount, state that it applies to the next cycle, and state the new monthly price. Skip the email and you'll get a chargeback.

The proration calculation is not the hard part. The hard part is making sure the customer understands the number on the invoice before they see it.

Back to all articles

Ready to build something great?

We help ambitious teams build software that lasts. If you're interested in working with us or want to discuss your project, let's connect.

Get in touch