WebhookWatch · Integration Guides

Stripe Webhook Testen

Real-time webhook debugging for Dutch developers — capture, inspect, and replay every Stripe event with zero latency.

Step-by-Step

Setup Guide: Connect Stripe to WebhookWatch

Get your Stripe dashboard sending events to WebhookWatch in under three minutes. Every endpoint comes with a unique URL, automatic retry handling, and signature verification out of the box.

💻

1. Generate Your Endpoint URL

Log in to WebhookWatch, click New Endpoint, and copy the generated URL — for example https://w.webhookwatch.nl/ep/7f3a9c2d. This URL forwards all incoming payloads to your dashboard in real time.

🔗

2. Add the Endpoint in Stripe

Open your Stripe Dashboard, navigate to Developers → Webhooks, click Add Endpoint, and paste the WebhookWatch URL. Select the events you want to listen for — we recommend starting with payment_intent.succeeded, charge.failed, and invoice.payment_failed.

3. Verify & Start Capturing

Stripe sends a ping event to confirm the endpoint. Once WebhookWatch receives it, your endpoint status turns green. Trigger a test charge in your Stripe sandbox and watch the full payload appear in your WebhookWatch timeline within milliseconds.

<50ms Average delivery latency
12 Automatic retries per event
99.97% Uptime SLA

Reference

Stripe Event Examples

Below are the most common Stripe events you'll encounter during development. WebhookWatch captures the full JSON payload, headers, and timing metadata for every single one.

payment_intent.succeeded

Fired when a PaymentIntent transitions to succeeded. Contains the amount captured, currency, payment method details, and the connected account ID.

{
  "id": "evt_1N2qR4Lp8kYz3mXv",
  "type": "payment_intent.succeeded",
  "data": {
    "object": {
      "id": "pi_3N2qR4Lp8kYz3mXv0b1c",
      "amount": 4995,
      "currency": "eur",
      "status": "succeeded",
      "payment_method": "pm_card_visa",
      "customer": "cus_PqR7sT8uVwXy"
    }
  },
  "created": 1718345622,
  "livemode": false
}

charge.failed

Triggered when a charge attempt declines. The payload includes the decline code (e.g., insufficient_funds, card_not_verified) and the raw Stripe error message.

{
  "id": "evt_1N2qR5Lp8kYz3mYw",
  "type": "charge.failed",
  "data": {
    "object": {
      "id": "ch_3N2qR5Lp8kYz3mYw1d2e",
      "amount": 12500,
      "currency": "eur",
      "status": "failed",
      "failure_code": "insufficient_funds",
      "failure_message": "Your card has insufficient funds.",
      "customer": "cus_PqR7sT8uVwXy"
    }
  },
  "created": 1718345701,
  "livemode": false
}

invoice.payment_failed

Sent when a subscription invoice fails to collect payment. Useful for triggering dunning workflows or notifying the customer to update payment details.

{
  "id": "evt_1N2qR6Lp8kYz3mZx",
  "type": "invoice.payment_failed",
  "data": {
    "object": {
      "id": "in_1N2qR6Lp8kYz3mZx2f3g",
      "subscription": "sub_1N2qR6Lp8kYz3mZx",
      "customer": "cus_PqR7sT8uVwXy",
      "amount_due": 2990,
      "currency": "eur",
      "status": "open",
      "attempt_count": 2,
      "next_payment_attempt": 1718432022
    }
  },
  "created": 1718345890,
  "livemode": false
}

customer.subscription.updated

Fired when a subscription changes — plan upgrades, downgrades, trial end dates, or proration adjustments. Always check data.previous_attributes to detect what changed.

{
  "id": "evt_1N2qR7Lp8kYz3m0y",
  "type": "customer.subscription.updated",
  "data": {
    "object": {
      "id": "sub_1N2qR7Lp8kYz3m0y",
      "customer": "cus_PqR7sT8uVwXy",
      "status": "active",
      "items": {
        "data": [
          {
            "plan": {
              "id": "price_1N2qR7Lp8kYz3m0y",
              "nickname": "Pro Plan",
              "amount": 4900,
              "currency": "eur"
            }
          }
        ]
      },
      "current_period_end": 1720937622
    },
    "previous_attributes": {
      "status": "trialing"
    }
  },
  "created": 1718346012,
  "livemode": false
}

Security

Signature Verification

Stripe signs every webhook payload with your endpoint secret. WebhookWatch validates the Stripe-Signature header automatically and displays the verification result alongside each captured event. Here's how to verify signatures in your own code when you move from testing to production.

Node.js / Express

const express = require('express');
const stripe = require('stripe')('sk_test_...');

const app = express();
const endpointSecret = 'whsec_a1b2c3d4e5f6...';

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(
      req.body, sig, endpointSecret
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      console.log('Payment succeeded:', event.data.object.id);
      break;
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }

  res.json({received: true});
});

PHP

<?php
require 'vendor/autoload.php';

\Stripe\Stripe::setApiKey('sk_test_...');

$payload = @file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$endpoint_secret = 'whsec_a1b2c3d4e5f6...';

try {
  $event = \Stripe\Webhook::constructEvent(
    $payload, $sig_header, $endpoint_secret
  );
} catch (\Exception $e) {
  http_response_code(400);
  exit("Webhook Error: " . $e->getMessage());
}

if ($event->type === 'payment_intent.succeeded') {
  $intent = $event->data->object;
  echo "Payment received: €" . ($intent->amount / 100);
}
256-bit HMAC-SHA256 signing
0 Tolerance for tampered payloads
3 Timestamp tolerance window (seconds)

Your endpoint secret is visible in the Stripe Dashboard under Developers → Webhooks → [your endpoint] → Reveal signing secret. Never commit this value to public repositories. WebhookWatch stores your secrets encrypted at rest using AES-256-GCM.