Integrations / Stripe
Stripe
Capture, inspect, and replay Stripe webhook events. Never miss a payment_intent.succeeded again.
stripe-signature header. No extra config needed.Setup
Create a Paggio endpoint
Go to Dashboard → Webhooks → New Endpoint.
Set the destination URL to your app's Stripe webhook handler, e.g.:
https://your-app.com/api/stripe/webhookCopy your Paggio webhook URL
Copy the endpoint URL from your new endpoint card:
https://paggio.dev/api/webhook/your-endpoint-slugAdd to Stripe Dashboard
Go to the Stripe Webhooks dashboard → Add endpoint.
Paste your Paggio URL and select the events you want to listen to.
Send a test event from Stripe
In the Stripe Dashboard, click "Send test webhook". You should see it appear in Paggio instantly.
Testing locally
Instead of using the Stripe CLI, you can use Paggio to capture real Stripe events and replay them to localhost whenever you need:
Point Stripe at your Paggio URL
Use the Paggio URL as your Stripe webhook endpoint (even in test mode).
Trigger a real Stripe event
Make a test payment in Stripe — the event is captured in Paggio.
Replay to localhost
Open the event in Paggio → Replay panel → select "Local" → click Replay.
The event is forwarded to http://localhost:3000/api/stripe/webhook.
Verifying Stripe signatures
Paggio stores the original stripe-signature header. If you verify signatures in your handler, note that replayed events have the same signature as the original. Stripe signatures are time-based and will expire. Disable signature verification in your local dev handler, or use Stripe's tolerance setting.
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const body = await req.text();
const signature = req.headers.get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!,
// Increase tolerance for replays (default: 300s)
600
);
} catch (err) {
return Response.json({ error: 'Invalid signature' }, { status: 400 });
}
switch (event.type) {
case 'payment_intent.succeeded':
// handle payment...
break;
}
return Response.json({ received: true });
}tolerance value to constructEvent().Common Stripe event types
| Event | When it fires |
|---|---|
| payment_intent.succeeded | Customer completes a payment |
| payment_intent.payment_failed | Payment fails |
| customer.subscription.created | New subscription created |
| customer.subscription.deleted | Subscription cancelled |
| invoice.payment_succeeded | Invoice paid successfully |
| invoice.payment_failed | Invoice payment fails |
| checkout.session.completed | Checkout session completed |