Laravel + Cashier. Built in methods such as subscribed() or subscribedToProduct() are not behaving as expected

20 Views Asked by At

I am attempting to create some middleware that checks to see if a user is subscribed, that their subscription is active (i.e. trial is not expired, card has not been declined, etc.), and what package they are subscribed to in order to ensure they have the appropriate permissions to access certain pages.

I have three products set up in Stripe. They are as follows:

Basic Plan:

  • Product ID: prod_PWke60obsPBzad
  • Price ID: price_0Ohh9CNGPb5LgdzfVOU47SSe

Premium Plan:

  • Product ID: prod_PgNVBv2qbaoKgR
  • Price ID: price_0Or0kfNGPb5Lgdzf7LtFHXqv

Ultra Plan:

  • Product ID: prod_PgNWTrq9z3fvJb
  • Price ID: price_0Or0lZNGPb5Lgdzf3KqZ8Xzo

I have it working so that when a user registers (using boilerplate Laravel authentication) it creates a customer in Stripe, and it subscribes them to a 30 day free trial of the basic package.

According to Laravel's documentation, I should be able to use the following command to check if they are subscribed to a package:

@if ($user->subscribed())
    <p>You are subscribed.</p>
@endif

However, this returns false, despite verifying that there is a subscription set up in stripe, a customer set up in stripe, and all id's match up accordingly.

Here is where it starts to get a little haywire.

If I pass an attribute into the subscribed() method, I can get it to come back as true.

@if($user->subscribed('basic'))
    <p>You are subscribed.</p>
@endif

However – if I upgrade their package (using Stripes built-in billing portal) to the premium or ultra package, and I try:

@if($user->subscribed('premium'))
    <p>You are subscribed.</p>
@endif

It returns false. Whereas, as soon as I go back to passing in "basic", it returns true again.

Here are all of the details of my application thus far:

Subscriptions Table (Created when I installed Cashier): enter image description here

SubscriptionItems Table (Also created when I installed Cashier): enter image description here

Plans Table (Created by me) enter image description here

In my RegisteredUserController.php (the file that controls when a new user registers), I have the following code:

public function store(Request $request): RedirectResponse {
    $request->validate([
        'first_name' => ['required', 'string', 'max:255'],
        'last_name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
        'password' => ['required', 'confirmed', Rules\Password::defaults()],
    ]);

    $user = User::create([
        'unique_id' => uniqid('u-'),
        'first_name' => $request->first_name,
        'last_name' => $request->last_name,
        'email' => $request->email,
        'user_role' => 'agency',
        'account_status' => 'trial',
        'password' => Hash::make($request->password),
    ]);

    //Create a customer in Stripe & save the customer ID
    $stripeCustomer = $user->createAsStripeCustomer();
    $user->stripe_id = $stripeCustomer->id;
    $user->save();

    //Create the subscription with a free trial
    $user->newSubscription('basic', 'price_0Ohh9CNGPb5LgdzfVOU47SSe')->trialDays(30)->add();

    //Proceed to log the user in and direct them to their dashboard
    event(new Registered($user));
    Auth::login($user);
    return redirect(RouteServiceProvider::HOME);
}

This effectively creates the user in my applications database, as well as creates a customer in Stripe. It also subscribes them to the basic plan, with a 30 day free trial.

In my web.php routes file, I have the following routes defined:

Route::post('/stripe/update_subscription', [SubscriptionController::class, 'updateSubscription'])->name('updateSubscription');


Route::middleware('auth')->group(function () {
    Route::get('/billing', function(Request $request) {
        return $request->user()->redirectToBillingPortal(route('dashboard'));
    })->name('billing');

    Route::get('/dashboard', [AppController::class, 'dashboard'])->middleware([Subscribed::class])->name('dashboard');
}

The first route is for a webhook to update the subscription details in my own database, when a users subscription package has changed. This is outside of my authentication routes, because Stripe is not authenticating itself to pass a JSON response to my application.

The second route utilizes the built-in redirectToBillingPortal method, which lets the user manage their subscription through Stripe. This is how I've been changing the users package.

And the last route is just a simple dashboard view, with some middleware in place to check that the users subscription is valid. This is where I am seeing the bulk of my issues.

Here is the code in my middleware:

public function handle(Request $request, Closure $next): Response {
    $user = Auth::user();
    $subscription = Subscription::where('user_id', '=', $user->id)->first();
    $subItem = SubscriptionItem::where('subscription_id', '=', $subscription->id)->first();
    $plan = Plan::where('stripe_plan', '=', $subItem->stripe_product)->first();
    
    $isSubscribed = $user->subscribed(); //returns false
    //$isSubscribed = $user->subscribed('basic'); //returns true
    //$isSubscribed = $user->subscribed('premium'); //returns false
    //$isSubscribed = $user->subscribed('ultra'); //returns false
    //$isSubscribed = $user->subscribed('prod_PgNVBv2qbaoKgR'); //returns false
    //$isSubscribed = $user->subscribed('price_0Or0kfNGPb5Lgdzf7LtFHXqv'); //returns false
    //$isSubscribed = $user->subscribed('si_PguVbMh84j5Ejw'); //returns false

    dd($isSubscribed);
}

As you can see, it is only when I pass "basic" along, that I can get a "true" response – even though I changed the users subscription to a different package in Stripe.

I have tried running: php artisan cache:clear, php artisan config:clear, php artisan config:cache, etc., but that doesn't seem to have any impact either.

I am using Laravel 10, and php8.2.

Can anybody please shed some light on this? It's driving me crazy.

0

There are 0 best solutions below