How to Reliably Test Stripe Checkout with Laravel Dusk

A client came to us a while back with a Laravel subscription app that was bleeding money. Users would go through the Stripe checkout, their card would be charged, but the app wouldn’t grant them access. Total mess. Their previous developer insisted that you couldn’t reliably test Stripe with Dusk because of the webhooks. He was wrong.

Testing payment flows isn’t optional. It’s the most critical part of the whole machine. Stripe Checkout and the Customer Portal are fantastic tools, but they work asynchronously. When a user subscribes, Stripe pings your application via a webhook to confirm the payment. If your test suite doesn’t account for that, your tests will fail every single time, making you think your code is broken when it’s really your testing setup that’s failing.

The Right Way to Test Stripe with Dusk

The core of the problem is getting the Stripe CLI to run during your Dusk test suite. Without it, Stripe has no way to forward webhooks to your local test environment. My first attempt at this was a blind spot—I treated the Stripe checkout like a simple form submission. The test would navigate to Stripe, fill in the details, submit, and then… nothing. The app’s state never updated because the webhook never arrived. A total nightmare to debug the first time.

The real fix is to manage the Stripe CLI as a background process directly within your test case. You start it before your tests run and kill it when they’re done. Simple. Trust me on this, it saves hours of headaches. The original idea for this approach comes from a great post over at carlalexander.ca, but I’ve tweaked it for our agency’s standard practices.

namespace Tests\Browser;

use Symfony\Component\Process\Process;
use Tests\DuskTestCase;

class StripeTest extends DuskTestCase
{
    /**
     * @var Process
     */
    private $process;

    protected function setUp(): void
    {
        parent::setUp();

        // Start the Stripe CLI to listen for webhooks
        $this->process = new Process([
            'stripe', 'listen', 
            '--api-key', env('STRIPE_SECRET'), 
            '--forward-to', sprintf('%s/stripe/webhook', env('APP_URL'))
        ]);
        $this->process->start();

        // Give it a second to boot up.
        sleep(3); 
    }

    protected function tearDown(): void
    {
        parent::tearDown();

        // Stop the process
        $this->process->stop();
    }
}

Here’s the kicker: this code uses the Symfony Process component to run `stripe listen` in the background. The `setUp()` method fires it up before a test begins, and `tearDown()` shuts it down. This ensures that for every test in this class, your application can receive webhooks from Stripe, just like it would in production. The little `sleep(3)` is important; sometimes the process needs a moment to initialize properly before Dusk plows ahead.

So, What’s the Point?

Automating your payment gateway tests isn’t a “nice-to-have.” It’s fundamental. When you manually test, you’re just hoping you don’t miss anything. An automated suite gives you a repeatable guarantee that your integration works. This setup gives you a solid foundation for writing tests that cover:

  • Successful subscription creations.
  • Subscription cancellations via the customer portal.
  • Handling failed payments and the resulting webhooks.

Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.

Stop skipping your payment tests because they seem too complex. A few lines of setup code are all that stands between you and a rock-solid, fully-tested billing system. Now you have no excuse.

Leave a Reply

Your email address will not be published. Required fields are marked *