All Providers
πŸ’³

Stripe Webhooks

Payments

Stripe sends webhook events for every payment lifecycle event β€” charges, payment intents, subscriptions, invoices, and disputes. Every event is signed with a HMAC-SHA256 signature in the Stripe-Signature header.

Stripe Dashboard webhook endpoint configuration screen showing the focused empty Endpoint URL field, event-type checkboxes (payment_intent.succeeded, payment_intent.payment_failed, charge.refunded), and the Add endpoint button.
Stripe dashboard webhook configuration screen β€” schematic showing the empty endpoint URL field, event subscriptions, and primary action button.

Webhook Events

8 event types

payment_intent.succeeded

A payment was successfully completed

payment_intent.payment_failed

A payment attempt failed

customer.subscription.created

A new subscription was created

customer.subscription.deleted

A subscription was cancelled

invoice.payment_succeeded

A recurring invoice was paid

invoice.payment_failed

A recurring invoice payment failed

charge.dispute.created

A chargeback was opened

checkout.session.completed

A checkout session was completed

Signature Verification

Header
Stripe-Signature
Method

HMAC-SHA256 via stripe.webhooks.constructEvent()

View official docs

Sample Payload

payment_intent.succeeded

application/json
{
  "id": "evt_3QxKL2LkdIwHu7ix0M1234",
  "object": "event",
  "type": "payment_intent.succeeded",
  "created": 1712000000,
  "livemode": false,
  "data": {
    "object": {
      "id": "pi_3QxKL2LkdIwHu7ix0123456",
      "object": "payment_intent",
      "amount": 2000,
      "currency": "usd",
      "status": "succeeded",
      "customer": "cus_ABC123",
      "description": "Subscription payment",
      "receipt_email": "[email protected]",
      "metadata": {
        "order_id": "ord_987"
      }
    }
  }
}

Send a Sample Stripe Payload

Pick an event, enter your endpoint URL (or localhost), and fire a realistic Stripe payload with one click β€” no Stripe account needed.

Test Sender

Loading samples…

No signup

Capture & Inspect Stripe Webhooks Live

Get a free public HTTPS endpoint below, point Stripe at it, and watch events arrive in real time. Use the forwarding rule to relay them straight to your local server.

Live demo β€” no signup required

See it work in real time

Click below to get a live webhook URL instantly. Paste it anywhere β€” Stripe, GitHub, Postman β€” and watch events arrive right here.

Expires in 1 hour Β· No account needed

Forward Stripe webhooks to localhost

  1. Click Create live endpoint above to get a public HTTPS URL
  2. Paste the URL into Stripe's webhook settings
  3. In the Forwarding tab, add a rule: target = http://localhost:3000/webhooks/stripe
  4. Fire a test event from Stripe β€” it arrives in the inspector and hits your local handler simultaneously

Ready to test your Stripe webhook handler?

Free HTTPS endpoint with forwarding, retry, and event replay. No install, no CLI, no deploy.

Create Free Account

Common Stripe Webhook Errors

HTTP 400 β€” Invalid signature

Cause: Your webhook secret is wrong, or you're reading the body as a parsed object instead of the raw string.

Fix: Pass the raw request body buffer to stripe.webhooks.constructEvent(), not JSON.parse(). In Express, use express.raw({ type: 'application/json' }) before your route.

HTTP 400 β€” Timestamp too old

Cause: Stripe rejects events where the timestamp in Stripe-Signature is more than 5 minutes old to prevent replay attacks.

Fix: Ensure your server clock is synced (NTP). In testing, you can disable tolerance with { tolerance: 0 } β€” never do this in production.

Events received out of order

Cause: Stripe does not guarantee event ordering. invoice.payment_succeeded may arrive before invoice.created.

Fix: Design your handler to be idempotent. Always fetch the current state from the Stripe API instead of relying solely on the event payload.

Duplicate events

Cause: Stripe retries events if your endpoint returns a non-2xx status or times out.

Fix: Store the event ID and check for duplicates before processing. Return 200 immediately, then process asynchronously.

Signature Verification Code

Node.js
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }
  // Handle event.type here
  res.json({ received: true });
});
Python
import stripe
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhooks/stripe', methods=['POST'])
def stripe_webhook():
    payload = request.get_data()
    sig_header = request.headers.get('Stripe-Signature')
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, os.environ['STRIPE_WEBHOOK_SECRET']
        )
    except stripe.error.SignatureVerificationError:
        return 'Invalid signature', 400
    # Handle event['type'] here
    return '', 200
Ruby
require 'stripe'

post '/webhooks/stripe' do
  payload = request.body.read
  sig_header = request.env['HTTP_STRIPE_SIGNATURE']
  begin
    event = Stripe::Webhook.construct_event(
      payload, sig_header, ENV['STRIPE_WEBHOOK_SECRET']
    )
  rescue Stripe::SignatureVerificationError => e
    halt 400, "Invalid signature"
  end
  # Handle event.type here
  status 200
end
Go
import (
  "github.com/stripe/stripe-go/v76/webhook"
)

func handleWebhook(w http.ResponseWriter, r *http.Request) {
  body, _ := io.ReadAll(r.Body)
  event, err := webhook.ConstructEvent(body,
    r.Header.Get("Stripe-Signature"),
    os.Getenv("STRIPE_WEBHOOK_SECRET"))
  if err != nil {
    w.WriteHeader(http.StatusBadRequest)
    return
  }
  // Handle event.Type here
  w.WriteHeader(http.StatusOK)
}

How to Test Stripe Webhooks with WebhookWhisper

  1. 1

    Create a free WebhookWhisper endpoint

    Click "Create live endpoint" on this page. Copy the HTTPS URL β€” this is your temporary webhook receiver.

  2. 2

    Open Stripe Dashboard β†’ Webhooks

    Go to Developers β†’ Webhooks in your Stripe Dashboard. Click "Add endpoint".

  3. 3

    Paste your WebhookWhisper URL

    Enter your WebhookWhisper URL as the endpoint URL. Select the events you want to listen to (e.g. payment_intent.succeeded).

  4. 4

    Trigger a test event

    Click "Send test webhook" in the Stripe Dashboard. Watch the event arrive in WebhookWhisper's inspector in real time.

  5. 5

    Set up forwarding to localhost

    In WebhookWhisper, add a forwarding rule pointing to http://localhost:3000/webhooks/stripe. Now every Stripe event hits both the inspector and your local handler.

Stripe Webhook FAQ

Why is my Stripe webhook returning a 400 error?

The most common cause is passing a parsed JSON body to stripe.webhooks.constructEvent() instead of the raw buffer. Use express.raw() middleware in Express, or read the raw bytes before any JSON parsing.

How do I get my Stripe webhook signing secret?

In Stripe Dashboard β†’ Developers β†’ Webhooks, click your endpoint and find "Signing secret". For local testing with the Stripe CLI, run `stripe listen` and it will display a temporary secret.

Does Stripe guarantee event delivery?

Stripe retries failed deliveries for up to 3 days with exponential backoff. Your endpoint must return a 2xx status within 30 seconds or Stripe will retry.

How do I test Stripe webhooks locally without deploying?

Use WebhookWhisper: get a free public URL, point Stripe at it, then forward events to your localhost. No CLI install or tunnel needed.

Can I receive Stripe webhooks in test mode and live mode?

Yes β€” but you need separate webhook endpoints for each. Your test mode endpoint only receives events from test mode activity, and vice versa.