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

Gabotronix's avatar

Stripe webhooks with laravel

Hi everybody, I have a laravel app where I want to enable Stripe payments, particuraly the asynchronous flow using webhooks! I haven't used webhooks yet so I'm pretty confused and have a few questions:

First I create a paymentIntent in the server and send the PI secret to client.

public function createPaymentIntent(Request $request)
    {

            $intent = PaymentIntent::create([
                'setup_future_usage' => 'off_session',
                'amount' => 1099,
                'currency' => 'usd',
            ]);

            return response()->json([
                'intent_secret' => $intent->client_secret,
            ], 200); 

        
    }

Now in client I execute confirmCardPayment, this returns a Promise

this.stripe.confirmCardPayment(
        this.intent_secret,
        {
            payment_method: {card: this.cardNumberElement}
        }
        )
        .then(function(result)
        {
            if (result.error) 
            {
            // Display error.message in your UI.
            }
            else
            {
            // The payment has succeeded
            // Display a success message
            }
        });

Now this is when it gets confusing, apparently I have to setup webhooks to listen for when auth/payment is completed, I checked the docs and I salvaged their PHP example into my laravel code but I have so many questions:

public function stripeWebhook(Request $request)
    {

        // You can find your endpoint's secret in your webhook settings
        $endpoint_secret = config('services.stripe.webhooksecret');

        $payload = @file_get_contents('php://input');
        $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
        $event = null;

        try
        {
            $event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
        } 
        catch(\UnexpectedValueException $e)
        {
                // Invalid payload
                return response()->json([
                    'message' => 'Invalid payload',
                ], 200);
        }
        catch(\Stripe\Exception\SignatureVerificationException $e)
        {
            // Invalid signature
            return response()->json([
                'message' => 'Invalid signature',
            ], 200);
        }

        if ($event->type == "payment_intent.succeeded")
        {
            //As I understand here is where I should do things like send order info by mail and deplete stock accordingly

            $intent = $event->data->object;

            //$this->completeOrderInDatabase()
            //$this->sendMail();

            return response()->json([
                'intentId' => $intent->id,
                'message' => 'Payment succeded'
            ], 200); 
        } 
        elseif ($event->type == "payment_intent.payment_failed")
        {
            //Payment failed to be completed
            
            $intent = $event->data->object;
            $error_message = $intent->last_payment_error ? $intent->last_payment_error->message : "";

            return response()->json([
                'intentId' => $intent->id,
                'message' => 'Payment failed: '.$error_message
            ], 400); 
        }

        
    }

So first, am I supposed to create a POST route called domain.com/webhook from where I will call my webhookController method?

Second, what is payload and signature? these seem like required params but I don't know what exactly they are...

Where am I supossed to attach the payment method to the customer? In the docs I'm supossed to get the payment method from PaymentIntent object but is that in client or server.

https://stripe.com/docs/payments/payment-intents/migration#saving-cards-checkout

From the docs:

$payment_method = \Stripe\PaymentMethod::retrieve('{{PAYMENT_METHOD_ID}}');
$payment_method->attach(['customer' => '{{CUSTOMER_ID}}']);
0 likes
1 reply
Sti3bas's avatar

So first, am I supposed to create a POST route called domain.com/webhook from where I will call my webhookController method?

Yes. Make sure you've added this route as an exception in VerifyCsrfToken middleware.

Second, what is payload and signature? these seem like required params but I don't know what exactly they are...

These are not parameters, it's data sent by Stripe. Signature allows you to verify that the events were sent by Stripe.

Where am I supossed to attach the payment method to the customer? In the docs I'm supossed to get the payment method from PaymentIntent object but is that in client or server.

You should be able to get payment method with result.paymentIntent.payment_method after confirming card payment. Then you can pass payment method id to the server and attach it to the customer.

All in all, you can check source code of Laravel Cashier and how it's handling webhooks to get a better understanding: https://github.com/laravel/cashier/blob/10.0/src/Http/Controllers/WebhookController.php

Please or to participate in this conversation.