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

dev_2108's avatar

Laravel cashier checkout() does not save the subscription to the database

Good evening. Guys, tell me, please, I have a problem with Stripe subscriptions. I'm using a package - Laravel Cashier and I want to subscribe using Subscription Checkouts. Here is my piece of code

public function subscriptionCheckout(Request $request)
    {
        $user = auth()->user();
        return $user
            ->newSubscription('default', 'price_1N8z5gIXJCCFYtSmyBwtqii5')
            ->checkout([
                'success_url' => route('dashboard'),
                'cancel_url' => route('home'),
            ]);
    }

The subscription on the Stripe Dashboard is saved, but not in the database in the subscription table. Tell me, please, what else should be added?

Previously saved subscriptions like this

public function sunscribe($user, $paymentMethod, $plan)
     {
         $subscription = $user->newSubscription("$plan->name", $plan->plan_id);
         $subscription->create($paymentMethod);
     }

but didn't use, Subscription Checkouts, everything was saved normally, both on the Dashboard and in the Database

The documentation states

Using Stripe Checkout for subscriptions requires you to enable the customer.subscription.created webhook in your Stripe dashboard. This webhook will create the subscription record in your database and store all of the relevant subscription items.

I subscribed to all events

0 likes
18 replies
vincent15000's avatar

Have you enabled the created webhook ? And have you added a route in Laravel to perform a task when the webhook is activated ?

dev_2108's avatar

@vincent15000 yes, I have a route that handles Stripe's webhooks. Route::post('/stripe/webhook', [StripeWebhookController::class, 'handleWebhook']);

On Stripe's dashboard, I added this endpoint to the webhooks section. This route successfully fires on the events I'm listening for. also i listen to customer.subscription.created event as stated in the documentation. But I don't know how to handle it correctly.


public function handleWebhook(Request $request)
    {
        Stripe::setApiKey(config('services.stripe.secret'));
        $endPointSecret = config('services.stripe.webhook_secret');
        $payload = @file_get_contents('php://input');
        $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
        $event = null;

        try {
            $event = Webhook::constructEvent(
                $payload, $sig_header, $endPointSecret
            );
        } catch(\UnexpectedValueException $e) {
            // Invalid payload
            logger('Logger Invalid payload');
            http_response_code(400);
            exit();
        } catch(\Stripe\Exception\SignatureVerificationException $e) {
            // Invalid signature
            logger('Logger Invalid signature');
            http_response_code(400);
            exit();
        }

        switch ($event->type) {
            case 'invoice.payment_succeeded':
                /*
                
                */
            case 'customer.subscription.trial_will_end':
            /*
            
            */
            case 'invoice.payment_failed':
            /*
           
           */
            case 'customer.subscription.created':
            /*
            
            */

            default:
                break;
        }

        http_response_code(200);
    }
1 like
thinkverse's avatar

@dev_2108 if you don't know how to correctly handle billing, then I'd suggest using Laravel's first-party package Cashier instead. That will handle pretty much everything for you from subscriptions, payments, and webhook. All you need to do there is use the provided functions it gives you to interact with it in your application.

1 like
dev_2108's avatar

@thinkverse Thank you. This is my first time working with subscriptions. That's why I'm asking for help... I am using this package, which you wrote about. My subscriptions are not saved in the subscription table that comes with the package. Subscriptions are created only on Dashboard Stripe.

1 like
vincent15000's avatar

@dev_2108 I have tested subscriptions without this package and it works fine just followed step by step the Laravel documentation.

thinkverse's avatar

@dev_2108 if you're using Cashier you don't need to implement the webhooks yourself. Cashier already comes with it built-in.

To ensure your application can handle Stripe webhooks, be sure to configure the webhook URL in the Stripe control panel. By default, Cashier's webhook controller responds to the /stripe/webhook URL path.

Reading on further in the documentation and it tells you which events to enable in Stripe, and which endpoint you should use, again it's: /stripe/webhook.

Do note that during development and testing, Stripe cannot call any localhost or .test sites that resolve to your local IP, so to test webhooks locally you need to either use the Stripe CLI to test your webhook endpoint or forward your local site using a service like ngrok or expose.

1 like
dev_2108's avatar

@thinkverse as I wrote above, I have a route in the project,


Route::post('/stripe/webhook', [StripeWebhookController::class, 'handleWebhook']);

which keeps track of Stripe's events. and the handleWebhook method of my controller works, the data sent by Stripe gets there, I can process it. I can't figure out what I need to do in order for my subscription to be stored in the database after this code


public function subscriptionCheckout(Request $request)
    {
        $user = auth()->user();
        return $user
            ->newSubscription('default', 'price_1N8z5gIXJCCFYtSmyBwtq4ih')
            ->checkout([
                'success_url' => route('dashboard'),
                'cancel_url' => route('home'),
            ]);
    }

should I write something in case, when the event fires customer.subscription.created

public function handleWebhook(Request $request)
    {
        Stripe::setApiKey(config('services.stripe.secret'));
        $endPointSecret = config('services.stripe.webhook_secret');
        $payload = @file_get_contents('php://input');
        $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
        $event = null;

        try {
            $event = Webhook::constructEvent(
                $payload, $sig_header, $endPointSecret
            );
        } catch(\UnexpectedValueException $e) {
            // Invalid payload
            logger('Logger Invalid payload');
            http_response_code(400);
            exit();
        } catch(\Stripe\Exception\SignatureVerificationException $e) {
            // Invalid signature
            logger('Logger Invalid signature');
            http_response_code(400);
            exit();
        }

        switch ($event->type) {
            case 'customer.subscription.created ':
// here ?

1 like
thinkverse's avatar

@dev_2108 and as I wrote as well, you don't need to do that yourself if you're already using Laravel Cashier. It registers the route for you, handles the signature verification if the STRIPE_WEBHOOK_SECRET environment variable is set, and it creates the subscriptions in your database for you and handles charges backs, cancellations, etc. If you're using Cashier and want to react to incoming webhooks then subscribe to the events sent by Cashier.

If you really insist on implementing the webhook parts yourself, which is fine I guess. Though I'd rather use the battle-tested approach for peace of mind. What you need to do is parse the request, luckily Laravel has you covered there as well.

I don't know what Webhook::constructEvent is supposed to do, but verifying the signature is a breeze.

use Stripe\Exception\SignatureVerificationException;
use Stripe\WebhookSignature;

public function handleWebhook(Request $request)
{
        try {
            WebhookSignature::verifyHeader(
                $request->getContent(),
                $request->header('Stripe-Signature'),
                config('services.stripe.secret'),
				300
            );
        } catch (SignatureVerificationException $exception) {
            throw new Exception($exception->getMessage(), $exception);
        }

		// Webhook signature verified, begin parsing payload…
        $payload = json_decode($request->getContent(), true);
}

That's pretty much it as far as verifying the signature and parsing the payload into an array that you can then use to create, update or delete from your Subscriptions model.

You also need to be sure you're updating the right user, and you can do that by using Cashier::findBillable and passing in the Stripe ID.

$user = Cashier::findBillable($payload['data']['object']['customer']);

With that you can access the users subscriptions via the relationships and can now insert and update the related models.

And let me reiterate, you don't have you do this yourself, it's a hassle dealing with edge cases, and Cashier has you covered already.

2 likes
dev_2108's avatar

@thinkverse Does Laravel Cashier register the /stripe/webhook route itself? And all I need is just to add this endpoint on Stripe's dashboard, and my subscriptions will be saved to the database on their own? did I understand you correctly?

my-domain /stripe/webhook

1 like
MGT's avatar

@thinkverse I am having the same problem as OP after trying to use both the Stripe CLI and Ngrok - in both cases I get a 200 response from the webhooks and updated subscription info in Stripe's Dashboard.

Are there any other reasons why the checkout() method could fail to store user data in the subscriptions table?

Perhaps something wrong in the front-end? I am sending the user to the Stripe Checkout page through the /subscription-checkout route.

1 like
dev_2108's avatar

@MGT my problem was that in my project i created a route /stripe/webhook. And exactly the same route is in the Laravel Cashier package. And I, with my route, overwrote the package route. When I changed the url of my route, the route from the package started working correctly and saving all subscriptions. And don't forget to add webhooks, both your own and the package's, to the Stripe panel. And subscribe to the appropriate events from the documentation, @thinkverse posted the link above. I hope this helps you solve the problem

1 like
nibras's avatar

@dev_2108 Hi i read all your chat and its the same issue Iam facing that there is no subscriptions data entering to the database and its in stripe dashboard ,I have checkout controller code and handle same as like you and how did you solve it

abdullahabid04's avatar

I faced a similar issue where subscriptions were being created on the Stripe dashboard but weren't being saved in the database. After digging into the problem, I realized that I had created a custom route for handling webhooks, like this:

Route::post('/stripe/webhook', [StripeWebhookController::class, 'handleWebhook']);

The issue was that Laravel Cashier already has a built-in route for handling Stripe webhooks, which also listens at /stripe/webhook. By creating my custom route, I was overwriting Cashier's route, causing the subscription records to not be saved in the database.

To resolve this:

  1. Remove the custom webhook route from the web.php routes file.
  2. Ensure that the Stripe webhook is correctly configured in the Stripe dashboard, pointing to the /stripe/webhook endpoint of the application.
  3. Let Cashier handle everything—it registers the necessary route and handles the subscription saving process automatically.

Once I removed the custom route and allowed Cashier's built-in webhook handling to do its job, everything started working perfectly. The subscriptions were stored in the database, and the process was seamless.

dimitriadis's avatar

Hi there, 

I'm experiencing the same behavior as dev_2108. Even though I haven't added any endpoints for stripe/webhook but my subscriptions in DB are not added.

If I call the checkout() function, then the users are redirected to the Stripe checkout page to complete the payment, but when they return, the subscription tables are not filled with the new entries. The webhooks are executed successfully, as far as I can see from the Stripe dashboard.

If I call create(), then the subscription entries are successfully added to DB, but the users are not redirected to Stripe checkout to complete their payment.

Any ideas?

1 like
PhilKz's avatar

I'm adding to this thread because I recently experienced a similar problem, and this seems to be the thread where everyone having this issue gets sent to by Google.

If you are experiencing this issue, check if your user is being assigned a Stripe ID ("stripe_id") in your users table. Cashier needs to have this before it receives a Webhook about a subscription being created in order to associate your user with the subscription.

//Laravel\Cashier\Http\Controllers\WebhookController;

...

/**
     * Handle customer subscription created.
     *
     * @param  array  $payload
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function handleCustomerSubscriptionCreated(array $payload)
    {
		//Without the stripe_id already set, Cashier can't match the customer to your user.
        $user = $this->getUserByStripeId($payload['data']['object']['customer']);

        if ($user) {
		//Logic for saving a subscription to your local DB.
		}
		...
	}

In my case, the issue arose because my Laravel + Cashier app was an API backend for a Next.js frontend. The Cashier methods for initiating a checkout session don't work for this scenario, so I had to create a session using the StripePHP library. This skips the step where Cashier will find your user's Stripe ID or make a new one. To fix this, I added this to my code:

Route::middleware(['auth:sanctum'])->post('/subscription-checkout', function (Request $request){
    $user = $request->user();
    // Either get the Stripe ID associate with the user's existing Stripe customer ID, or create the user as a new Stripe customer.
    $user->createOrGetStripeCustomer();
	//Other logic initiating a Stripe sesssion.
	$session = Session::create([
		//Send the Stripe ID as part of the request to create a new Stripe session.
        'customer' => $user->stripe_id,
		...
	]);
		...

Because checkout(...) in Cashier can handle either one-off/guest payments, or subscriptions for logged in users, it may be that you initiated a Checkout session in a way that doesn't have a user and so doesn't create a Stripe ID for them. Double check you are calling the method the right way for what you want to achieve.

Please or to participate in this conversation.