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

isaachatilima's avatar

Call to a member function stripe() on null

I have an action class to create a subscription and my User model has the Billable trait. in my database I can see stripe_id, pm_type and pm_last_four values and on stripe dashboard user is there but when I try to create a subscription I get call to member function stripe() on null. Below is my snippet, did I miss something?

    try {
        Stripe::setApiKey(config('cashier.secret'));

        $user = $request->user();

        if (! $user->hasStripeId()) {
            $user->createAsStripeCustomer();
        }

        $setupIntent = SetupIntent::retrieve($request->setup_intent);
        $user->updateDefaultPaymentMethod($setupIntent->payment_method);
        $user->save();

        License::create([
            'unit_price' => $request->unit_price,
            'quantity' => $request->quantity,
            'total_price' => $request->unit_price * $request->quantity,
            'status' => 'active',
            'payment_method' => $setupIntent->payment_method,
            'user_id' => $user->id,
        ]);

        $user->newSubscription('default', 'price_1R7ZzjLCIwOX44eiG2msya0I')
            ->quantity($request->quantity)
            ->create($setupIntent->payment_method);

        return 'success';
    } catch (Exception $e) {
        Log::error('Error occurred while processing purchase: '.$e->getMessage());

        return 'error';
    }
0 likes
3 replies
Snapey's avatar

always helpful to say which line throws the error

You can use the stack trace to see the calling line of your action

tisuchi's avatar

@isaachatilima It's probably because you are not getting the user instance properly in this line:


 $user = $request->user();
martinbean's avatar

I have an action class

@isaachatilima Then why in the second line are you using the request? An action is meant to be usable in any context; not just HTTP requests. Pass the User instance as an argument to your action class instead.

You’re also manually setting the API key on the Stripe SDK. Use a service to do this instead, instead of setting the API key every time you want to interact with the Stripe SDK. You should also instead inject the Stripe client to your action class so that it can be mocked and make your action class testable:

The action class now:

  • Has its dependencies (the Stripe client instance) injected in its constructor
  • Required parameters are passed to the action’s handle method
  • Database queries are wrapped in a transaction so that if something fails, your database isn’t left in an inconsistent state where some records have been inserted/updated but not others
  • The license model is created using a relation on the user, to automatically associate the model with the user
  • An enumeration is used for setting the status (from allowed values), instead of just passing an arbitrary string
  • A useful result (the newly-created subscription instance) is returned from the method (since it is called create subscription) rather than again, an arbitrary string indicating the result

You also don’t need to catch exceptions. Laravel has a built-in exception handler. In your class, you just catch any and all exceptions, just to log the message. So if an exception is thrown, you’re going to get zero helpful context (such as the file, line number, stack trace) that you would have got if you just let Laravel handle and log the exception. Please don’t “black hole” exceptions like this.

Please or to participate in this conversation.