Stripe Webhooks in LaunchKit: Complete Setup Guide
Set up Stripe webhooks for local development and production. Handle checkout.session.completed, verify signatures, and debug common issues.
Building tools for makers

What Are Webhooks?
Webhooks are Stripe's way of telling your app when something happens. Customer completed checkout? Webhook. Subscription renewed? Webhook. Payment failed? Webhook.
Without webhooks, your app would need to constantly poll Stripe asking "did anything happen yet?" Instead, Stripe pushes events to an endpoint you control, and your code reacts accordingly.
How LaunchKit Handles Webhooks
LaunchKit's webhook handler lives at /app/api/webhook/stripe/route.ts. When Stripe sends an event, this route:
- Verifies the signature (proves the request came from Stripe)
- Parses the event type
- Updates your Supabase database accordingly
- Returns a 200 response so Stripe knows it succeeded
The key event is checkout.session.completed—this fires when a customer successfully pays, and LaunchKit uses it to grant access by updating the user's profile.
Local Development Setup
Step 1: Install the Stripe CLI
The Stripe CLI forwards webhook events from Stripe to your local machine.
# macOS
brew install stripe/stripe-cli/stripe
# Windows (with scoop)
scoop install stripe
# Or download from stripe.com/docs/stripe-cliStep 2: Authenticate
Link the CLI to your Stripe account:
stripe loginThis opens a browser window. Authorize the CLI and you're connected.
Step 3: Forward events locally
Start the forwarding with:
stripe listen --forward-to localhost:3000/api/webhook/stripeThe CLI outputs a webhook signing secret starting with whsec_. Copy this and add it to your .env.local:
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxStep 4: Test with a real payment
Make sure your app is running (npm run dev), then complete a test checkout. Use Stripe's test card:
Card: 4242 4242 4242 4242
Expiry: Any future date
CVC: Any 3 digitsWatch the CLI output—you should see the checkout.session.completed event arrive, and your terminal will show whether the webhook handler returned 200 or an error.
Production Setup
Step 1: Create the webhook endpoint
In your Stripe Dashboard → Developers → Webhooks, click "Add endpoint" and enter:
Endpoint URL: https://yourdomain.com/api/webhook/stripeStep 2: Select events
Click "Select events" and choose the events LaunchKit handles:
checkout.session.completed— Customer paid successfullycustomer.subscription.updated— Subscription changedcustomer.subscription.deleted— Subscription cancelled
For one-time purchases, only checkout.session.completed is essential.
Step 3: Copy the signing secret
After creating the endpoint, click on it and reveal the "Signing secret". Copy this whsec_... value and add it to your production environment variables in Vercel.
STRIPE_WEBHOOK_SECRET=whsec_live_xxxxxxxUnderstanding the Webhook Handler
Here's what happens inside LaunchKit's webhook route:
// 1. Get the raw body (required for signature verification)
const body = await request.text();
const signature = request.headers.get("stripe-signature");
// 2. Verify the signature
const event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
// 3. Handle the event
if (event.type === "checkout.session.completed") {
const session = event.data.object;
const customerId = session.customer;
const customerEmail = session.customer_email;
// Update user's access in Supabase
await supabase
.from("profiles")
.update({ has_access: true, customer_id: customerId })
.eq("email", customerEmail);
}
// 4. Return 200 to acknowledge receipt
return NextResponse.json({ received: true });Important: Use request.text()
Next.js 15 automatically parses JSON bodies, but Stripe signature verification needs the raw string. Always use request.text() instead of request.json() for webhook routes.
Debugging Webhook Issues
Check the Stripe Dashboard
Go to Developers → Webhooks and click on your endpoint. You'll see a list of recent webhook attempts with their HTTP status codes:
- 200: Success
- 400: Usually a signature verification failure
- 500: Your code threw an error
Click any failed attempt to see the request payload and your server's response.
Common issues
- "Webhook signature verification failed": Wrong
STRIPE_WEBHOOK_SECRET. Make sure you're using the secret from the correct endpoint (CLI for local, dashboard for production). - Webhook never arrives: For local dev, make sure the Stripe CLI is running. For production, check that your endpoint URL is correct and your server is accessible.
- "No such customer": You might be using test mode events with live mode keys, or vice versa.
- User not found in database: The webhook fires before the user exists in your database. LaunchKit handles this by matching on email, which Stripe includes in the checkout session.
Testing Without Real Payments
You can trigger test events directly from the Stripe CLI:
# Trigger a checkout.session.completed event
stripe trigger checkout.session.completed
# See all available event types
stripe trigger --helpThis sends a synthetic event to your local webhook endpoint—useful for testing your handler without completing actual checkouts.
Security Best Practices
- Always verify signatures: Never trust incoming webhooks without verification. Anyone could send a POST to your endpoint.
- Use HTTPS in production: Stripe won't send webhooks to HTTP endpoints (except localhost).
- Respond quickly: Stripe expects a response within 20 seconds. For long-running tasks, acknowledge the webhook first, then process in the background.
- Handle retries: Stripe retries failed webhooks for up to 3 days. Make your handlers idempotent—running them twice shouldn't cause problems.
Quick Reference
Keep this handy during setup:
# Local development
stripe listen --forward-to localhost:3000/api/webhook/stripe
# Test card for checkout
4242 4242 4242 4242 | Any future date | Any CVC
# Trigger test event
stripe trigger checkout.session.completed
# Check webhook logs
https://dashboard.stripe.com/webhooksReady to ship faster?
LaunchKit gives you auth, payments, CRM, and everything you need to launch your SaaS in days, not months.
Get LaunchKitWritten by
LaunchKit TeamWe're a small team passionate about helping developers and entrepreneurs ship products faster. LaunchKit is our contribution to the maker community.
Related Articles

How Long It Really Takes to Launch a SaaS
Ignore the 'ship in a weekend' hype. Here's the honest timeline for launching a SaaS that can actually generate revenue.

Claude Code for SaaS Founders: Idea to Revenue
Complete playbook for solo founders using Claude Code to launch a SaaS. From validation to first paying customer in 3 weeks.

Build a SaaS MVP in 24 Hours with Claude Code
Step-by-step tutorial: Build a complete SaaS MVP in 24 hours using Claude Code, Next.js, and Supabase. Includes auth, payments, CRM, and deployment.