
Photo by Markus Winkler on Unsplash
How to Handle SaaS Upgrades/Downgrades in Stripe Without Breaking Access
How to Handle SaaS Upgrades/Downgrades in Stripe Without Breaking Access
Plan changes are one of the trickiest parts of SaaS billing. Upgrade too eagerly and you overcharge. Downgrade poorly and users keep premium features for free. Here's how to handle both correctly with Stripe.
Upgrade: Give Access Immediately
When a user upgrades (Starter → Pro), they should get premium features right away.
Implementation
Use stripe.subscriptions.update() with the new price ID. Stripe handles proration automatically — the user gets a credit for unused time on the old plan and is charged the difference for the new plan.
Proration Options
| Option | Behavior | Best For |
|---|---|---|
create_prorations (default) | Charge difference immediately | Standard upgrades |
none | No proration, new price starts next period | Simplicity |
always_invoice | Generate and charge a proration invoice immediately | When you need immediate payment |
Access Control
- Update subscription in Stripe
- Webhook
customer.subscription.updatedfires - Update user's plan in your database
- User immediately has access to new plan features
Downgrade: Keep Access Until Period End
When a user downgrades (Pro → Starter), they should keep their current plan until the end of the billing period.
Implementation
Use stripe.subscriptions.update() with the new price ID and proration_behavior: 'none'. Schedule the change for the end of the current period using billing_cycle_anchor: 'unchanged'.
Alternatively, update the subscription's items with proration_behavior: 'none' — the new price takes effect at the next invoice.
Access Control
- Schedule downgrade in Stripe
- Store the pending plan change in your database
- User keeps current (higher) plan features until period ends
- Webhook fires at period end with the new plan
- Update user's access in your database
Edge Cases to Handle
User Upgrades Then Downgrades in Same Period
Scenario: User is on Starter, upgrades to Pro on day 5, then wants to downgrade back to Starter on day 10.
Solution: Process the downgrade normally — schedule it for end of period. The user keeps Pro access until then. The proration from the upgrade still applies.
User on Trial Wants to Change Plans
Scenario: User is on a Pro trial but wants to switch to Business before the trial ends.
Solution: Update the subscription's price. The trial continues on the new plan with the same end date.
User Cancels Then Wants to Resubscribe
Scenario: User canceled (set to expire end of period), but changes their mind.
Solution: Use stripe.subscriptions.update() with cancel_at_period_end: false to remove the scheduled cancellation.
Database Schema for Plan Changes
| Field | Purpose |
|---|---|
currentPlan | The plan the user currently has access to |
scheduledPlan | The plan they'll switch to at period end (if downgrading) |
currentPeriodEnd | When the current billing period ends |
cancelAtPeriodEnd | Whether the subscription is set to cancel |
Golden rule: Always check currentPlan for access control, not scheduledPlan. The user should have access based on what they're currently paying for.
Testing Plan Changes
| Test Case | Expected Result |
|---|---|
| Upgrade Starter → Pro | Immediate access to Pro features, prorated charge |
| Downgrade Pro → Starter | Keep Pro access until period end, then switch |
| Cancel subscription | Keep access until period end, then revoke |
| Reactivate after cancel | Access restored, cancel flag removed |
| Upgrade during trial | Trial continues on new plan |
| Multiple changes in one period | Latest change takes effect, proration is correct |
Building billing? Read How to Build SaaS Billing With Stripe.
Need multi-tier plans? See Building Multi-Tier SaaS Plans.







