home / skills / phrazzld / claude-config / stripe-local-dev

stripe-local-dev skill

/skills/stripe-local-dev

This skill auto-syncs ephemeral Stripe webhook secrets with your local environment before dev server start, preventing 400 signature errors.

npx playbooks add skill phrazzld/claude-config --skill stripe-local-dev

Review the files below or copy the command above to add this skill to your agents.

Files (4)
SKILL.md
2.9 KB
---
name: stripe-local-dev
description: |
  Fix Stripe webhook 400 errors in local development. The problem: `stripe listen`
  generates a new secret each time it starts, but your app uses a stale secret from
  .env.local. This skill auto-syncs the ephemeral secret before your dev server starts.

  Auto-invoke when: stripe webhooks return 400, "signature verification failed",
  "No signatures found matching", webhooks not working locally, checkout succeeds
  but subscription doesn't update, STRIPE_WEBHOOK_SECRET mismatch, setting up
  stripe listen, configuring pnpm dev with Stripe.
effort: high
---

# /stripe-local-dev

Ensure Stripe webhooks work in local development by auto-syncing ephemeral secrets.

## The Problem

Stripe CLI generates a **new webhook secret** every time `stripe listen` starts. If your dev script auto-starts the listener but doesn't sync the secret, you get:

```
Webhook error: signature verification failed
No signatures found matching the expected signature for payload
```

## The Solution Pattern

**Auto-start requires auto-sync.** Use `dev-stripe.sh`:

1. Extract secret via `stripe listen --print-secret`
2. Sync to environment (Convex env OR .env.local)
3. THEN start forwarding

## Architecture Decision

| Webhook Location | Secret Sync Target | Restart? | Recommendation |
|-----------------|-------------------|----------|----------------|
| Convex HTTP (`convex/http.ts`) | `npx convex env set` | No | Best |
| Next.js API Route | `.env.local` | Yes | Requires orchestration |

**Prefer Convex HTTP webhooks** - secret sync is instant, no restart needed.

## Implementation

### Option A: Convex HTTP Webhooks (Recommended)

Copy script:
```bash
cp ~/.claude/skills/stripe-local-dev/scripts/dev-stripe-convex.sh scripts/dev-stripe.sh
chmod +x scripts/dev-stripe.sh
```

Update package.json:
```json
"stripe:listen": "./scripts/dev-stripe.sh"
```

### Option B: Next.js API Webhooks

Copy script:
```bash
cp ~/.claude/skills/stripe-local-dev/scripts/dev-stripe-nextjs.sh scripts/dev-stripe.sh
chmod +x scripts/dev-stripe.sh
```

Update package.json:
```json
"stripe:listen": "./scripts/dev-stripe.sh"
```

**Note**: Next.js needs restart to pick up env changes. The script warns about this.

## Verification

After setup, run:
```bash
pnpm dev
# Then in another terminal:
stripe trigger checkout.session.completed
# Check logs for 200 response, not 400
```

## Quick Diagnostics

| Symptom | Cause | Fix |
|---------|-------|-----|
| All webhooks return 400 | Stale secret | Restart `pnpm dev` or run sync script |
| "signature verification failed" | Secret mismatch | Check CLI output matches env |
| Works once, fails after restart | No auto-sync | Add `dev-stripe.sh` script |
| CLI shows delivered, app shows error | Wrong env target | Check sync target (Convex vs .env.local) |

## Related Skills

- `/check-stripe` - Audit Stripe integration
- `/stripe-health` - Webhook health diagnostics
- `/stripe-audit` - Comprehensive Stripe audit

Overview

This skill ensures Stripe webhooks work reliably in local development by auto-syncing the ephemeral webhook secret that stripe listen generates. It detects when the CLI rotates the secret and updates your environment (Convex env or .env.local) before the dev server handles requests. The result is fewer 400 signature verification errors and consistent local webhook handling.

How this skill works

When invoked, the script runs stripe listen --print-secret to capture the current ephemeral webhook secret. It then writes that secret to the configured target (Convex environment via npx convex env set, or the .env.local file) and only then starts or continues the local forwarding process. For Next.js targets the script warns and can restart the dev server so the new env is picked up; for Convex HTTP webhooks the update is instantaneous and no restart is required.

When to use it

  • Local development using stripe listen where webhooks return 400 or show signature verification errors
  • When webhook delivery shows "No signatures found matching" or checkout succeeds but subscriptions don’t update
  • When STRIPE_WEBHOOK_SECRET in your environment does not match the stripe listen output
  • When configuring pnpm/dev scripts to start stripe listen alongside your dev server
  • When you want automatic synchronization of ephemeral secrets to avoid manual env updates

Best practices

  • Prefer Convex HTTP webhooks so the secret sync is instant and no server restart is needed
  • Install and use the provided script as your centralized stripe:listen command in package.json
  • Keep secret writes atomic: update env target first, then start forwarding to avoid race conditions
  • For Next.js, coordinate a controlled restart of pnpm dev after env changes to ensure the new secret is loaded
  • Log the stripe listen output during sync so you can verify the printed secret matches the env target

Example use cases

  • Automatic sync for a developer running pnpm dev and stripe listen in separate terminals
  • CI-like local workflows where a script boots stripe listen then starts the dev server
  • Teams using Convex where npx convex env set propagates the webhook secret without restarts
  • Local debugging when webhooks deliver but your app rejects them with signature errors
  • Onboarding scripts for new contributors to set up Stripe forwarding and secrets automatically

FAQ

What causes the signature verification failed error locally?

stripe listen generates a new ephemeral webhook secret each time it starts; if your app still has the old secret in its environment, signatures will not match and Stripe requests return 400.

Do I always need to restart my dev server after the sync?

Not if you sync to a remote config like Convex HTTP (npx convex env set). If you write to .env.local (Next.js API routes), you must restart the server so it picks up the new environment variable.