Handle Stripe checkout webhooks

David Carr

Laravel Framework Stripe API

Continuing on from my last post Sell products with Stripe let's first setup a webhook on stripe by going to Developers -> Webhooks https://dashboard.stripe.com/webhooks 

Add a new webhook, provide a URL for the webhook to go to such as https://domain.com/webhooks/stripe

select the events to listen to since I'm dealing with the hosted checkout for one-off products I want the checkout session.checkout.completed event.

Once created click into the webhook and press reveal under signing secret to reveal the webhook API key. Add this key to your .env file 

STRIPE_WEBHOOK_SECRET=

Next open App/Http/Middleware/VerifyCsrfToken.php to whitelist an endpoint to allow Stripe to send POST requests in.

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'webhooks/stripe',
    ];
}

Create a route 

Route::post('webhooks/stripe', [WebhooksController::class, 'collect']);

In the controller create a method, set the stripe API key, secret and collect POST data using php//input

Then in a try-catch verify the webhook API key with Stripe.

public function collect()
{
    Stripe::setApiKey(config('services.stripe.secret'));
    $secret     = config('services.stripe.webhook');
    $payload    = file_get_contents("php://input");
    $sig_header = $_SERVER["HTTP_STRIPE_SIGNATURE"];
    $event      = null;

    try {
        $event = Webhook::constructEvent($payload, $sig_header, $secret);
    } catch (\UnexpectedValueException $e) {
        // Invalid payload
        http_response_code(400);
        return true;
    } catch (SignatureVerification $e) {
        // Invalid signature
        http_response_code(400); // PHP 5.4 or greater
        return true;
    }

    // Handle the checkout.session.completed event
    if ($event->type === 'checkout.session.completed') {
        $this->handle_checkout_session($event);
    }

    http_response_code(200);
}

Finally checking the $event->type matches the event checkout.session.completed all another method and pass in the event.

Inside the event drill down to the metadata which will contain any data sent to stripe, open you will put a user id and product in so you can process orders.

public function handle_checkout_session($eventData)
{
    $meta = $eventData->data->object->metadata;

    if (isset($meta->user_id)) {
        $purchase = Purchase::create([
            'user_id'    => $meta->user_id,
            'product_id' => $meta->product_id,
            'data'       => json_encode($eventData),
        ]);

        Mail::
            to($purchase->user->email)
            ->send(new PurchasedProduct($purchase));
    }
}

In this case, I create a transaction log and send an email to the customer.

The important thing is in the collect method to respond to Stripe as quick as possible by sending an HTTP status code and after sending a response process the data.

 

 

 

Fathom Analytics $10 discount on your first invoice using this link

David Carr - Laravel Developer

Hi, I’m David Carr

A Senior Developer at Vivedia
I love to use the TALL stack (Tailwind CSS, Alpine.js, Laravel, and Laravel Livewire)

I enjoy writing tutorials and working on Open Source packages.

I also write books. I'm writing a new book Laravel Testing Cookbook, This book focuses on testing coving both PestPHP and PHPUnit.

Sponsor me on GitHub

Subscribe to my newsletter

Subscribe and get my books and product announcements.

Laravel Testing Cookbook

Help support the blog so that I can continue creating new content!

Fathom Analytics $10 discount on your first invoice using this link

Subscribe to my newsletter

Subscribe and get my books and product announcements.

© 2006 - 2023 DC Blog. All code MIT license. All rights reserved.