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

websanova's avatar

Laravel Cashier Subscriptions Sync?

I'm using Laravel Cashier and Stripe.

There have been some changes to some of the customers subscriptions made directly on Stripe. Like plan change or cancellations.

Of course these are now not reflected locally within the app. I was looking around but didn't see any kind of syncSubscriptions function.

Does anyone have experience with this or have a code snippet I could quickly add?

0 likes
3 replies
rowanxmas's avatar

Did you ever find a solution to this? If not I need this as well and would be happy to share it.

websanova's avatar

I ended up doing something like this.

But probably you will need to tweak for your own purposes.

It's also kind of assuming only one subscription for the user.

It's just a trait that gets added to the User model.

So you can run $user->syncSubscriptions() at any time. It shouldn't break anything if there were no changes on Stripe. But otherwise should update/find any changed subscription for the user.

<?php

namespace Laravel\Cashier;

use Stripe;
use Carbon\Carbon;
use Laravel\Cashier\Subscription;

trait SubscriptionsSync {

    public function syncSubscriptions()
    {
        Stripe\Stripe::setApiKey(env('STRIPE_KEY'));

        // NOTE: We will only set one primary subscription.

        $subscription = null;
        $subscription_match = null;
        $subscription_active = null;
        $subscription_current = $this->current_subscription;

        $subscriptions = Stripe\Subscription::all([
            'customer' => $this->stripe_id,
            'status' => 'all',
            'limit' => 100
        ]);

        foreach ($subscriptions->data as $sub) {
            if ($sub->id === $subscription_current->stripe_id) {
                $subscription_match = $sub;
            }

            if (!$subscription_active && $sub->status === 'active') {
                $subscription_active = $sub;
            }
        }

        // Matching subscription and that subscription is active on Stripe.
        // Really this is in case there happens to be more than one subscription
        // so we want to try to get the proper matching one if we can.
        if (@$subscription_match->status === 'active') {
            $subscription = $subscription_match;
        }

        // No active match but there is some active subscription on Stripe.
        // Just get the first active subscription from the array.
        elseif ($subscription_active) {
            $subscription = $subscription_active;
        }

        // If there is no active subscriptions we will try to update any
        // canceled subscriptions that might exist in the array.
        elseif (@$subscription_match->status === 'canceled') {
            $subscription = $subscription_match;
        }

        if ($subscription) {
            $ends_at = @$subscription->ended_at ?: null;

            if (!$ends_at && @$subscription->canceled_at) {
                $ends_at = @$subscription->current_period_end ?: null;
            }

            $trial_ends_at = @$subscription->trial_end ?: null;

            $data = [
                'name' => 'primary',
                'stripe_id' => $subscription->id,
                'stripe_plan' => $subscription->plan->id,
                'quantity' => $subscription->quantity,
                'trial_ends_at' => $trial_ends_at ? Carbon::createFromTimestamp($trial_ends_at) : null,
                'ends_at' => $ends_at ? Carbon::createFromTimestamp($ends_at) : null
            ];

            if ($subscription_current) {
                $subscription_current->update($data);
            }
            else {
                $data['user_id'] = $this->id;

                Subscription::create($data);
            }
        }
    }
}
1 like
websanova's avatar

Oh, and the current_subscription is just a little attribute in the User model as well.

public function getCurrentSubscriptionAttribute()
{
    if ($this->relationLoaded('subscriptions') {
        return $this->subscription('primary');
    }
}
1 like

Please or to participate in this conversation.