web-application-framework laravel

This article explains how to run code after a successful Stripe payment using Webhooks.

Thumbnail

References

Prerequisites

  • You have a Laravel project.
    To learn how to run a Laravel project with NGINX, please refer to this guide.
    If you are using React, refer to this article.
  • You have already installed Laravel Cashier (Stripe).
    For more information, refer to this article.

Environment

  • Windows 11
  • Ubuntu 24.04.3 LTS (WSL2 distribution)
  • Docker Engine 28.4.0
  • Amazon Linux 2023(OS of the Docker container)
  • PHP 8.2.29 (fpm-fcgi)
  • NGINX 1.24.0
  • Laravel 12.38.1
  • laravel/cashier 16.0.5

Workflow

  1. Create Test Checkout
  2. Create Stripe Event Listener
  3. Verify Result Locally

1. Create Test Checkout

Refer to the official document and create a test checkout like the following:

use Illuminate\Http\Request;
 
Route::get('/checkout', function (Request $request) {
    $stripePriceId = 'price_deluxe_album';
 
    $quantity = 1;
 
    return $request->user()->checkout([$stripePriceId => $quantity], [
        'success_url' => route('checkout-success'),
        'cancel_url' => route('checkout-cancel'),
    ]);
})->name('checkout');
 
Route::view('/checkout/success', 'checkout.success')->name('checkout-success');
Route::view('/checkout/cancel', 'checkout.cancel')->name('checkout-cancel');

Specify the price ID you created in Stripe in $stripePriceId.
You also need to prepare the checkout.success and checkout.cancel views.

When you access /checkout, you should see the Stripe Checkout screen.
After successfully completing the payment, you will be redirected to /checkout/success.

2. Create Stripe Event Listener

Create an event listener to run code after the application receives Stripe events.
Run the following Artisan command:

php artisan make:listener StripeEventListener --event=WebhookReceived

Edit the generated listener as follows:

<?php

namespace App\Listeners;

use Illuminate\Support\Facades\Log;
use Laravel\Cashier\Events\WebhookReceived;

class StripeEventListener
{
    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(WebhookReceived $event): void
    {
        if ($event->payload['type'] === 'payment_intent.succeeded') {
            Log::info('The payment succeeded');
        }
    }
}

payment_intent.succeeded is an event that occurs when a payment is successfully completed.
You can check all Stripe event types here.

Add the listener inside the boot() method in AppServiceProvider.php:

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Event::listen(WebhookReceived::class, StripeEventListener::class); // Added
    }

3. Verify Result Locally

Verify the result locally by running the following Stripe CLI commands.
To install Stripe CLI, refer to this page.

> stripe login
> stripe listen --forward-to localhost:80/stripe/webhook

Specify your application’s port and webhook path.
Laravel Cashier listens on /stripe/webhook by default.

Access /checkout, and you should see the message The payment succeeded in your application logs.

(Bonus) Retrieve Metadata in Listener

How can you retrieve any custom values in the listener?
Add metadata during checkout:

Route::get('/checkout', function (Request $request) {
    $stripePriceId = 'price_deluxe_album';
 
    $quantity = 1;
 
    return $request->user()->checkout([$stripePriceId => $quantity], [
        'success_url' => route('checkout-success'),
        'cancel_url' => route('checkout-cancel'),
        // Add the following
        'payment_intent_data' => [
            'metadata' => ['test_key' => 'test_value'],
        ],
    ]);
})->name('checkout');

Retrieve the metadata in the listener:

    /**
     * Handle the event.
     */
    public function handle(WebhookReceived $event): void
    {
        if ($event->payload['type'] === 'payment_intent.succeeded') {
            Log::info('Test data: ' . $event->payload['data']['object']['metadata']['test_key']); // display the metadata
        }
    }

If you want to trigger an event with metadata using Stripe CLI, you can specify it like this:

Stripe trigger payment_intent.succeeded --add payment_intent:metadata['test_key']=test_value

Related articles