Stripe subscription proration explained — change plans without wiping data
Proration is how Stripe charges or credits the difference when a customer upgrades or downgrades mid-cycle. The danger isn't the math — it's the cancel-and-recreate antipattern that silently destroys billing history, trial state, and your retention analytics. Here's the right way.
The antipattern that breaks everything
// ❌ Cancel old, create new — wipes history, breaks cohort analytics
await stripe.subscriptions.cancel(oldSubId);
await stripe.subscriptions.create({ customer, items: [{ price: newPriceId }] });
This fires subscription.deleted + subscription.created, so your analytics see a churn + a new signup for what was actually an upgrade. It can re-grant trials, drop the payment method, and lose coupon state. Your MRR retention curve becomes fiction.
The right way: modify in place
const sub = await stripe.subscriptions.retrieve(subId);
const updated = await stripe.subscriptions.update(subId, {
items: [{ id: sub.items.data[0].id, price: newPriceId }],
proration_behavior: "create_prorations",
});
Same subscription.id, history intact, proration handled, no fake churn event.
proration_behavior — the 3 options
| Value | What happens | Use when |
|---|---|---|
create_prorations | Adds proration line items to the NEXT invoice (default) | Standard upgrades; charge difference at next cycle |
always_invoice | Creates prorations AND invoices immediately (charges card now) | Upgrades where you want the money now |
none | No proration; old price runs out the current period, new price next cycle | Downgrades you don't want to credit; simple plan swaps |
The upgrade-vs-downgrade nuance
- Upgrade (higher price): usually
always_invoiceorcreate_prorations— customer pays the prorated difference for the remaining period. - Downgrade (lower price): often
none— let them finish the period they paid for, switch at renewal. Crediting downgrades immediately can create negative invoices / customer balance you then have to manage.
When to use Subscription Schedules instead
If your plan logic has phases (trial → monthly → annual, or scheduled future downgrades), Subscription Schedules wrap a subscription with explicit time-based phases — cleaner than mutating mid-cycle and reasoning about proration each time. Overkill for 2-3 simple tiers; worth it for complex phase logic.
Worried your plan-change code has the antipattern?
I audit Stripe subscription plan-change logic for the cancel-recreate bug, proration misconfigurations, and trial-gaming exposure — then deliver the fix for your stack. $199 one-time, full refund if I don't find ≥3 issues.
See the audits →