Testing Stripe webhooks locally can be difficult when Stripe servers cannot directly access your local machine. This guide explains how to safely expose your local environment using Stripe CLI or Ngrok to test webhook events like (payment confirmations, refunds, disputes, subscription updates, and connected account verification), inspect payloads, and debug webhook issues efficiently.
You can use either the Stripe CLI or Ngrok, but you should not use both at the same time.
Feature / Use Case | Option A: Stripe CLI (Recommended) | Option B: Ngrok + Dashboard |
Best For | Fast development & rapid local iteration | Testing real frontend checkouts & live redirects |
Setup Speed | Blazing fast (under 2 minutes) | Moderate (requires dashboard configuration) |
Key Advantage | Trigger mock events instantly via terminal | Shared team/QA links & real HTTPS simulation |
Network Flow | Stripe CLI → Local server directly | Stripe → Stripe Dashboard → ngrok → Local server |
Phase 1 — Start Your Local Environment
Before configuring Stripe, make sure your local backend application is up and running.
If you are using standard Laravel:
Example: php artisan serve Or Docker/Sail: ./vendor/bin/sail up
(Note: If your app runs on a different port than 8000, adjust the port numbers in the steps below accordingly).
Phase 2 — Choose Your Testing Workflow
Choose one of the following paths to route Stripe events to your machine.
OPTION A: Stripe CLI Workflow (Fastest)
The Stripe CLI bypasses the need for web browsers and dashboards by streaming events directly to your terminal.
1. Install & Authenticate the CLI
Download and install the CLI via the
stripe login
Your browser will open automatically. Pair the session with your test mode workspace.2. Forward Events to Your Local Server
Tell the CLI to listen for events and stream them to your backend webhook route:
stripe listen --forward-to localhost:8000/api/webhooks/stripe
With stripe listen, the flow is:
Stripe → Stripe CLI (cloud) → your local HTTP endpoint
stripe listen --forward-to localhost:8000/api/webhooks/stripe
With stripe listen, the flow is:
Stripe → Stripe CLI (cloud) → your local HTTP endpoint
3. Update Your Environment Variables
The terminal will output a unique local webhook signing secret starting with whsec_. Copy it and update your .env file:
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxx
4. Trigger Test Events & Debug
Open a new terminal window to fire off mock events instantly:
# Test a standard successful checkout. stripe trigger checkout.session.completed
# Test a successful payment intent. stripe trigger payment_intent.succeeded
# Test a refund. stripe trigger charge.refunded
Watch your local logs or terminal output to verify signature validation, database updates, queue jobs, and idempotency protection.
This is an example from my local machine:
OPTION B: ngrok + Dashboard Workflow
Useful when you need a public HTTPS URL to test:
- Testing real frontend checkout flows/redirects
- External/mobile apps
- Team-shared environments.
1. Expose Your Local Server Using ngrok
Fire up ngrok to create a secure public tunnel to your local server port:
ngrok http 8000
- Laravel Sail →
80 - Laravel artisan serve →
8000 - Vite/dev server →
5173 - Node.js apps → commonly
3000
Look at your terminal and copy the public HTTPS URL generated by ngrok:
Forwarding https://abcd-1234.ngrok-free.app -> http://localhost:8000
Step 2: Inspect Requests in Real Time
Open the ngrok inspection dashboard in your browser:
http://127.0.0.1:4040/inspect/http This helps inspect:
- payloads,
- headers,
- retries,
- response codes.
3. Configure Stripe Environment Variables
Update your local .env file with your Stripe credentials and webhook configuration.
# Stripe API Keys
STRIPE_KEY=pk_test_...
STRIPE_SECRET=sk_test_...
# Public ngrok forwarding URL
STRIPE_WEBHOOK_URL=https://abcd-1234.ngrok-free.app/api/webhooks/stripe
# Stripe webhook signing secret
STRIPE_WEBHOOK_SECRET=whsec_...
4. Register Webhook Endpoint in Stripe Dashboard
Go to the
. (Developers → Webhooks)Stripe Dashboard Webhooks Section Click Add Endpoint.
Paste your ngrok URL combined with your webhook path into the Endpoint URL field:
[https://abcd-1234.ngrok-free.app/api/webhooks/stripe](https://abcd-1234.ngrok-free.app/api/webhooks/stripe)Select the specific events your application listens for.
Select the events your application listens for (recommended)
checkout.session.completed
checkout.session.async_payment_succeeded
checkout.session.async_payment_failed
payment_intent.succeeded
payment_intent.payment_failed
payment_intent.canceled
charge.refunded
account.updated
Event Purpose checkout.session.completedMain payment success confirmation checkout.session.async_payment_succeededDelayed success confirmation checkout.session.async_payment_failedDelayed payment failure payment_intent.succeededPayment completed payment_intent.payment_failedPayment failed payment_intent.canceledPayment canceled charge.refundedRefund synchronization account.updatedStripe Connect account updates
5. Monitor Events & Replay Webhooks
You have two powerful layers for debugging when using this workflow:
ngrok Inspection: Open
[http://127.0.0.1:4040](http://127.0.0.1:4040)in your local browser. You can inspect exact JSON payloads, headers, response times, and status codes hitting your machine in real-time.Stripe Dashboard Replay: If your local server crashes or validation fails mid-test, don't re-run the whole checkout flow. Go to your Dashboard Webhook page, click on the failed event under Event History, and click Retry/Resend to push the exact same payload back to your app.

Comments
Post a Comment