Stripe: downgrade a user at "period end"

Stripe PaymentsDowngradeTemboo

Stripe Payments Problem Overview


Is it possible to downgrade a user at period end instead of immediately? I've combed through the API Docs but haven't been able to figure out how to accomplish this.

As a workaround I'm currently immediately canceling the user's subscription, then subscribing them to the lesser subscription, trialing until the end of the month. This won't work though - I need to be able to delay the downgrade until the period end (but "record" it w/ Stripe at the time the downgrade is requested).

Clearly there are ways to accomplish this with webhook callbacks and by tracking user subscriptions locally but I'd like to avoid that if possible.


EDIT

Before anyone asks - I'm using Temboo's PHP SDK. However I'm not looking for a language-specific approach, just a high level how-to (if it's possible).

Stripe Payments Solutions


Solution 1 - Stripe Payments

Most of the solutions presented here look like hacks after stripe's release of subscription schedules, which is probably the most elegant solution. In fact, stripe documentation has an example illustrating exactly the same scenario here.

Step 1: Get current_period_end value from the existing subscription that you wish to downgrade.

Step 2: Create a new subscription schedule from the existing subscription.

$subscriptionSchedule = $stripe->subscriptionSchedules->create([
  'from_subscription' => 'sub_G678SASEGF',
]);

Step 3: Update the newly created schedule with two phases. phase 0 is the current phase that ends at current_period_end and phase 1 is the next phase that start at current_period_end with the downgraded price plan.

$stripe->subscriptionSchedules->update(
  $subscriptionSchedule->id,
  [ 
    'end_behavior' => 'release',
    'phases' => [
      [
        'start_date' => $current_period_start,
        'end_date' => $current_period_end,
        'plans' => [
          [
            'price' => $current_price_id
          ],
        ],
      ],
      [
        'start_date' => $current_period_end,
        'plans' => [
          [
            'price' => $downgraded_price_id,
          ],
        ]
    ],
  ],
]

You can always check the subscription object to see if there is a schedule active, and then retrieve the schedule to tap into any future downgrades. The advantage of this approach is that it could be applied to any downgrade and/or billing cycle change. with a multi plan approach described earlier in the answers, one subscription can only have items with the same billing cycle.

Solution 2 - Stripe Payments

As @Safinn and @Martin have mentioned, you can cancel the subscription using at_period_end: true to action the cancellation on a specific date.

In order to downgrade to a different plan, the way I get around it is to do the above and then create a free trial of the new plan ending on the same date/time. This way the old plan will be cancelled on the same date that the new plan's trial will end (causing it to be active).

This lets Stripe handle the downgrade entirely their end (which should be more straightforward IMHO), rather than setting up webhooks or tracking dates in your database.

Solution 3 - Stripe Payments

Yes, using the more recent version of the Stripe API.

(the Stripe tokens you may be using in Temboo's SDK are compatible with Stripe's normal PHP Library)

  • Create a product with multiple pricing plans

  • Subscribe a customer to one of these plan ids

  • When updating a customer to a new plan simply do as follows:

     $sub = $myUser->data['stripe_subscription_id'];
     $subscription = \Stripe\Subscription::retrieve($sub);
     \Stripe\Subscription::update($sub, [    
         'cancel_at_period_end' => false,
         'items' => [
             [
                 'id' => $subscription->items->data[0]->id,
                 'plan' => $plan,
             ],
         ],
         'prorate' => false,
         'trial_end' => $subscription->current_period_end
     ]);
     $subscription->save();
    

By setting prorate to false, trial_end to $subscription->current_period_end and cancel_at_period_end to false, you effectively tell Stripe:

> Do not charge this user until the day that their current billing ends (cancel at period end), do not refund them any money on the plan switch (prorate) and start billing again at the end of their current billing cycle (trial end.)

Which has the effect of changing their billing to the new plan when their old plan ends.

Solution 4 - Stripe Payments

Stripe has recently introduced subscription schedules that solves this problem: https://stripe.com/docs/api/subscription_schedules

Solution 5 - Stripe Payments

You should keep track of when your user joins a plan - keep a date field in your database next to the customer_id. You can use this date to determine the day of the month they joined and therefore the billing cycle. If the billing day is the 31st of the month then on shorter months Stripe will bill on the last day of those months (https://support.stripe.com/questions/subscription-date-at-end-of-month).

Now when a user wishes to downgrade they complete the action on your website when logged in. You take note of this downgrade request and store it in a, lets call it a "stripe_actionable_table" in your database. Important fields to have in this table would be:

  • actionable_date (the date to action the Stripe request - you would need some logic to determine the shorter months as mentioned above)
  • stripe_customer_id
  • what_to_do (upgrade/downgrade/cancel)
  • change_plan_to (a plan id - can be null if cancel req)

You would then have a cron that runs everyday at a certain time, and checks this stripe_actionable_table and if the day of the month matches with a row in the table then action the Stripe request. Once completed you can delete or mark the row as deleted.

Solution 6 - Stripe Payments

This is now possible using Stripe's prorate flag.

E.g.

$subscription = \Stripe\Subscription::retrieve("sub_44ty4267H50z6c");
$itemID = $subscription->items->data[0]->id;

\Stripe\Subscription::update("sub_44ty4267H50z6c", array(
  "items" => array(
    array(
      "id" => $itemID,
      "plan" => "basic-plan",
    ),
  ),
  "prorate" => false,
));

By setting prorate to false, you are effectively telling Stripe not to apply the plan change until end of the current period.

Official docs here:

https://stripe.com/docs/subscriptions/upgrading-downgrading#disable-prorations

CLARIFICATION (as per user comment below): Note that Stripe will update its own representation of the active plan immediately (only the charging of the user is deferred), so you will still need to manually manage delaying the active plan change from within your own application.

Solution 7 - Stripe Payments

This is how i do it.

I simply cancelled the existing subscription which will end as per the current billing period. And at the time of cancelling it, i saved the requested downgraded plan id of the customer within my local users table.

Then i set a webhook for customer.subscription.deleted within stripe and created a handler that will just pick the saved downgraded plan from my local users table and create a new subscription immediately using that.

Solution 8 - Stripe Payments

There doesn't seem to be a way to do it easily with Stripe.

I'm updating quantities rather than changing plans but the idea could be applied as well.

Possible solution is:

  1. Update subscription quantity without proration with Stripe.

  2. Keep previous quantity until the invoice.created event.

  3. When handling invoice.created event, compare previous quantity with quantity user is subscribed to and reduce it if necessary.

Solution 9 - Stripe Payments

For everyone looking for an up-to-date solution to this - have a look at the Stripe Subscription Schedules API Link

With this API you can simply:

  • Start a subscription on a future date,
  • Backdate a subscription to a past date and
  • Upgrade or downgrade a subscription

You can create a schedule simply like this:

\Stripe\SubscriptionSchedule::create([
  'customer' => 'cus_ABC',
  'start_date' => 'now',
  'end_behavior' => 'release',
  'phases' => [
    [
      'items' => [
        [
          'price' => 'price_ABC',
          'quantity' => 1,
        ],
      ],
      'iterations' => 12,
    ],
  ],
]);

See the docs for more

Solution 10 - Stripe Payments

> If you instead want to cancel the subscription at the end of the current billing period (i.e., for the duration of time the customer has already paid for), provide an at_period_end value of true

https://stripe.com/docs/subscriptions/canceling-pausing

I think you can update the subscription and add at_period_end: true and that should cancel it at the end of the period.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMadbreaksView Question on Stackoverflow
Solution 1 - Stripe PaymentsJai PandyaView Answer on Stackoverflow
Solution 2 - Stripe PaymentsSneakAttaackView Answer on Stackoverflow
Solution 3 - Stripe PaymentsThat Realty Programmer GuyView Answer on Stackoverflow
Solution 4 - Stripe PaymentsReinhard HöllView Answer on Stackoverflow
Solution 5 - Stripe PaymentsMartinView Answer on Stackoverflow
Solution 6 - Stripe PaymentsTom GView Answer on Stackoverflow
Solution 7 - Stripe PaymentsRohan SharmaView Answer on Stackoverflow
Solution 8 - Stripe PaymentsGyrocode.comView Answer on Stackoverflow
Solution 9 - Stripe PaymentsDorbnView Answer on Stackoverflow
Solution 10 - Stripe PaymentsSafinnView Answer on Stackoverflow