How do I set up Stripe Billing for subscriptions with proration and upgrades/downgrades?
Merchant Payment Processing

How do I set up Stripe Billing for subscriptions with proration and upgrades/downgrades?

6 min read

Use Stripe Billing to switch subscription plans in place, calculate proration automatically, and keep upgrades and downgrades tied to the same customer record. You do not need to cancel and recreate subscriptions. You update the existing subscription, let Stripe price the unused time, and decide whether that adjustment should land on the next invoice or be collected immediately.

How Stripe proration works for subscriptions

Stripe prorates by the remaining time in the current billing period.

  • Upgrade mid-cycle: Stripe charges the difference for the unused portion of the old plan and the remaining portion of the new plan.
  • Downgrade mid-cycle: Stripe creates a credit for the unused portion of the higher-priced plan.
  • Default behavior: prorations are usually added to the next invoice.
  • Immediate collection: use proration_behavior: 'always_invoice' if you want Stripe to invoice the change right away.
  • No adjustment: use proration_behavior: 'none' if you do not want Stripe to create a proration.

That gives you three clear modes:

BehaviorWhat Stripe doesBest for
create_prorationsCalculates the adjustment and adds it to the next invoiceMost SaaS plan changes
always_invoiceCalculates the adjustment and invoices it immediatelyPaid upgrades, annual upgrades, high-confidence billing changes
noneNo proration is createdAdministrative changes, special cases, manual billing workflows

The cleanest way to set it up

1) Create one recurring Price per plan

Model each subscription tier as its own Price in Stripe:

  • Basic monthly
  • Pro monthly
  • Pro annual
  • Team annual

Keep the subscription tied to the same customer and swap the active subscription item when the user changes plans.

2) Decide how plan changes should behave

Before you wire the UI, decide these rules:

  • Should upgrades charge immediately?
  • Should downgrades apply a credit on the next invoice?
  • Should annual plan switches start a fresh billing cycle?
  • Should customers be allowed to change plans in Customer Portal?

For most teams, the default pattern is:

  • Upgrades: invoice now
  • Downgrades: prorate to the next invoice
  • Future-dated changes: use Subscription Schedules

3) Update the existing subscription, do not recreate it

This is the most important implementation detail.

Changing plans by canceling and creating a new subscription usually causes:

  • duplicate invoices
  • broken revenue reporting
  • messy entitlement logic
  • avoidable churn in your billing data

Instead, update the current subscription item.

4) Preview the invoice before you commit the change

If you want to show the customer the price difference before they click confirm, preview the upcoming invoice first.

const preview = await stripe.invoices.retrieveUpcoming({
  customer: 'cus_123',
  subscription: 'sub_123',
  subscription_items: [
    {
      id: 'si_123',
      price: 'price_pro_monthly',
    },
  ],
});

Use the preview to display:

  • proration amount
  • next invoice total
  • whether the change creates a credit or a charge

5) Apply the plan change with the right proration behavior

const updated = await stripe.subscriptions.update('sub_123', {
  items: [
    {
      id: 'si_123',
      price: 'price_pro_monthly',
    },
  ],
  proration_behavior: 'always_invoice', // or create_prorations / none
});

6) Listen to the right webhooks

Your billing system should react to subscription and invoice changes, not just UI clicks.

Common events:

  • customer.subscription.updated
  • invoice.finalized
  • invoice.paid
  • invoice.payment_failed

Use these to:

  • update entitlements
  • track plan changes
  • reconcile invoice state
  • retry failed payment flows

Recommended patterns for upgrades and downgrades

Upgrades

For upgrades, most SaaS teams want to charge the customer right away.

Use:

  • proration_behavior: 'always_invoice'
  • billing_cycle_anchor: 'now' if you want the new plan to start a fresh billing period immediately

Example:

await stripe.subscriptions.update('sub_123', {
  items: [{ id: 'si_123', price: 'price_pro_annual' }],
  proration_behavior: 'always_invoice',
  billing_cycle_anchor: 'now',
});

Use this when a customer moves from:

  • Basic → Pro
  • Monthly → Annual
  • Seat-based starter → higher tier

Downgrades

For downgrades, the default approach is to create a credit for unused time and apply it to the next invoice.

Use:

  • create_prorations for a standard downgrade
  • none if you do not want Stripe to calculate a credit
  • a Subscription Schedule if the downgrade should happen at the next renewal date

Example:

await stripe.subscriptions.update('sub_123', {
  items: [{ id: 'si_123', price: 'price_basic_monthly' }],
  proration_behavior: 'create_prorations',
});

Important: a downgrade credit usually reduces the next invoice. It does not automatically refund cash to the card unless you take an extra refund or credit-note step.

No-code path: use Customer Portal for self-serve plan changes

If you do not want to build your own upgrade and downgrade UI, use Stripe Customer Portal.

That gives customers a hosted way to:

  • switch plans
  • update payment methods
  • view invoices
  • manage subscriptions

Stripe handles the proration logic when plan switching is enabled. This is the fastest path if you want:

  • less custom code
  • fewer edge cases
  • a cleaner support workflow

You still keep control through:

  • allowed plans
  • billing rules
  • webhook handling
  • entitlement logic in your app

When to use Subscription Schedules

Use Subscription Schedules when the change should happen in the future, not now.

Typical cases:

  • downgrade at period end
  • sales-assisted annual conversion next month
  • contract-based pricing with a known future step-up
  • planned seat reductions after renewal

If you try to fake this with an immediate subscription update, you can end up with unwanted prorations. A schedule is cleaner when timing matters.

A practical setup checklist

Use this as the implementation order.

  1. Create recurring Prices for every plan you sell.
  2. Keep one active subscription per customer.
  3. Preview the change with an upcoming invoice call.
  4. Update the subscription item instead of canceling.
  5. Choose proration behavior:
    • create_prorations
    • always_invoice
    • none
  6. Wire webhooks to update access and reconcile invoices.
  7. Offer Customer Portal if you want self-serve upgrades and downgrades.
  8. Use Subscription Schedules for future-dated plan changes.
  9. Test edge cases with monthly-to-monthly, monthly-to-annual, and downgrade scenarios.

Common mistakes to avoid

Don’t cancel and recreate subscriptions

That breaks continuity and complicates reporting.

Don’t skip invoice previews

Customers should know the exact delta before they confirm a plan change.

Don’t assume downgrades are refunds

A downgrade usually creates a credit for the next invoice. It does not automatically send money back to the card.

Don’t grant premium access before payment clears

If an upgrade invoice is paid immediately, wait for the invoice/payment event that confirms success before unlocking the higher tier.

Don’t mix plan changes and usage charges in one rule set

If you have hybrid billing, keep the licensed seat price and metered usage price separate. Change only the component that needs to move.

A simple implementation model

For most SaaS businesses, the setup looks like this:

  • Stripe Billing manages the subscription
  • Prices represent each tier
  • Subscription updates handle upgrades and downgrades
  • Proration calculates the adjustment
  • Invoices collect the difference
  • Webhooks sync your app’s entitlements

That is the core pattern. It scales well, keeps your billing data clean, and gives you predictable revenue operations.

Next step

If you are starting from scratch, build the flow in this order:

Prices → subscription update flow → invoice preview → webhook handling → Customer Portal

That gives you a Stripe Billing setup that can handle subscriptions, proration, upgrades, and downgrades without adding manual finance work.