Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ehabafia's avatar

Laravel Cashier subscription downgrade.

Hello,

I am using Laravel Cashier 12.0.1 with Laravel 12, everything seem to be working fine, except that the client doesn't want to do any refund.

So, I am trying to dow a downgrade from a higher membership to a lower membership, expecting to have the new membership starts at the end of the period of the old membership.

so, I tried the following methods:

$request->user()->subscription()->noProrate()->swap($priceId)

and

$request->user()->subscription()->swap($priceId)

which kind of worked, but the issue is that they implement the downgrade immediately and have the money difference as a credit for the customer. what the client needs is to keep the membership on the current level and implement the downgrade at the end of the belling cycle of the current plan.

I checked with chatGPT and it says that you can do that with ->swapNextCycle() but it says that you can have that in Laravel Cashier Mollie, which is not supported in Canada where the client is.

any thoughs on how to solve this?

0 likes
2 replies
LaryAI's avatar
Level 58

To achieve scheduled plan downgrades (take effect at the end of the current billing cycle) with Laravel Cashier (Stripe), you'll need to work within Stripe's API limitations, as Cashier does not provide a swapNextCycle method with Stripe—the feature exists only in Cashier Mollie.

Here's how you can approach this:

1. Schedule a Downgrade Manually in Code

Cashier's swap() (with or without noProrate()) always switches right away. Since Stripe doesn’t offer a simple "change at period end" method for plan/price, you’ll need to:

  1. Mark the user's intent to downgrade in your database.
  2. When the current billing cycle ends (via Stripe's invoice.payment_succeeded Webhook or similar), perform the swap() to the new lower plan.
  3. Notify the user that the downgrade will apply on their next renewal.

2. Example Implementation

Step A: Mark Downgrade Request

Create a column in your users table or subscriptions table (next_plan_id, for example). When the user chooses to downgrade, store the new plan’s Stripe price ID there.

// When the user requests a downgrade
$user = $request->user();
$user->next_plan = $newLowerPriceId;
$user->save();

// Optionally, notify user the downgrade is scheduled.

Step B: Listen for End of Billing Cycle

Listen for the relevant Stripe webhook (e.g., invoice.payment_succeeded or customer.subscription.updated). When the cycle ends and payment is processed:

  • Check if a next_plan is set
  • Swap the subscription to that price
// In your webhook controller:
// Assuming you've set up Cashier's webhooks

public function handleInvoicePaymentSucceeded($payload)
{
    $user = User::where('stripe_id', $payload['data']['object']['customer'])->first();

    if ($user && $user->next_plan) {
        $user->subscription()->swap($user->next_plan);
        $user->next_plan = null;
        $user->save();
        // Optionally, notify the user their downgrade is now active.
    }
}

3. Why Not Use Proration or Immediate Downgrade?

  • Any swap using Cashier's swap() operates immediately and, even with noProrate(), doesn't schedule for the period end.
  • Stripe's official API does not support scheduling a plan/price change on Subscriptions directly for later.

4. Notes

  • You may want to add logic to prevent multiple downgrade requests and to cancel scheduled changes before the invoicing.
  • Provide clear UI messaging that the downgrade will occur after the current billing cycle.

Summary:
To schedule a downgrade with Laravel Cashier (Stripe), you must handle it in your own application layer by keeping track of downgrade requests and updating the subscription when the period renews, usually responding to a webhook event.

Let me know if you need help with the full webhook controller code!

vincent15000's avatar

You have 2 possibilities to swap to another plan.

  1. You do a simple ->swap() and the plan is changed at the moment you swap (not at the end of the period) and the payment is calculated pro rata.

  2. If you want that the new plan is effective only at the end of the period (for example at the end of the month), then you have to manage this manually : store in the database that a user wants to downgrade his/her plan and swap at the end of the period (for example via a cron tab).

Please or to participate in this conversation.