Stripe Webhook Integration
Stripe webhooks allow you to receive real-time notifications about events in your Stripe account, such as successful payments, failed charges, subscription updates, and more.
Quick Start
- Get your Unhook URL:
https://unhook.sh/wh_YOUR_ID
- Configure in Stripe Dashboard: dashboard.stripe.com/webhooks
- Start receiving events locally:
unhook listen
Configuration Steps
1. Access Stripe Webhook Settings
Navigate to the Stripe Dashboard:
2. Create a New Endpoint
- Click “Add endpoint”
- Enter your Unhook URL:
https://unhook.sh/wh_YOUR_ID
- Select API version (optional - defaults to your account version)
3. Select Events to Listen For
Choose the events relevant to your application:
Payment Events
payment_intent.succeeded
- Payment completed successfully
payment_intent.payment_failed
- Payment attempt failed
charge.succeeded
- Charge was successful
charge.failed
- Charge failed
charge.refunded
- Charge was refunded
charge.dispute.created
- Customer disputed a charge
Customer Events
customer.created
- New customer created
customer.updated
- Customer details updated
customer.deleted
- Customer deleted
customer.source.created
- Payment method added
customer.source.deleted
- Payment method removed
Subscription Events
customer.subscription.created
- New subscription started
customer.subscription.updated
- Subscription modified
customer.subscription.deleted
- Subscription cancelled
customer.subscription.trial_will_end
- Trial ending soon
invoice.payment_succeeded
- Subscription payment successful
invoice.payment_failed
- Subscription payment failed
Checkout Events
checkout.session.completed
- Checkout session successful
checkout.session.expired
- Checkout session expired
checkout.session.async_payment_succeeded
- Async payment completed
checkout.session.async_payment_failed
- Async payment failed
account.updated
- Connected account updated
account.application.authorized
- OAuth connection authorized
account.application.deauthorized
- OAuth connection removed
payout.created
- Payout initiated
payout.paid
- Payout completed
payout.failed
- Payout failed
After selecting events, configure additional settings:
- Description: Add a meaningful description (e.g., “Production webhooks for MyApp”)
- Listen to: Choose between live mode and test mode events
- Click “Add endpoint” to save
Webhook Signing Secret
Stripe signs all webhook events for security. To verify signatures:
- After creating the endpoint, click on it to view details
- Click “Reveal” under Signing secret
- Copy the
whsec_...
value
- Add it to your environment variables:
STRIPE_WEBHOOK_SECRET=whsec_abc123...
Testing Webhooks
Using Stripe CLI
# Install Stripe CLI from https://stripe.com/docs/stripe-cli#install
# Login to your Stripe account
stripe login
# Trigger test events
stripe trigger payment_intent.succeeded
stripe trigger customer.subscription.created
Using Stripe Dashboard
- Go to your webhook endpoint in the Dashboard
- Click “Send test webhook”
- Select an event type
- Click “Send test webhook”
Event Payload Examples
Payment Intent Succeeded
{
"id": "evt_1234567890",
"object": "event",
"api_version": "2023-10-16",
"created": 1234567890,
"data": {
"object": {
"id": "pi_1234567890",
"object": "payment_intent",
"amount": 2000,
"currency": "usd",
"status": "succeeded",
"customer": "cus_1234567890",
"metadata": {
"order_id": "12345"
}
}
},
"type": "payment_intent.succeeded"
}
Customer Subscription Created
{
"id": "evt_1234567890",
"object": "event",
"api_version": "2023-10-16",
"created": 1234567890,
"data": {
"object": {
"id": "sub_1234567890",
"object": "subscription",
"customer": "cus_1234567890",
"status": "active",
"current_period_start": 1234567890,
"current_period_end": 1234567890,
"items": {
"data": [{
"id": "si_1234567890",
"price": {
"id": "price_1234567890",
"product": "prod_1234567890",
"unit_amount": 999,
"currency": "usd"
}
}]
}
}
},
"type": "customer.subscription.created"
}
Best Practices
1. Verify Webhook Signatures
Always verify that webhooks are coming from Stripe:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
} catch (err) {
console.log(`Webhook Error: ${err.message}`);
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// Handle successful payment
break;
default:
console.log(`Unhandled event type ${event.type}`);
}
response.send();
});
2. Handle Idempotency
Stripe may send the same event multiple times. Use the event ID to ensure idempotent processing:
// Store processed event IDs to prevent duplicate processing
const processedEvents = new Set();
if (processedEvents.has(event.id)) {
console.log(`Event ${event.id} already processed`);
return response.send();
}
processedEvents.add(event.id);
// Process the event...
3. Respond Quickly
Return a 2xx response quickly to acknowledge receipt:
// Acknowledge receipt immediately
response.status(200).send();
// Process the webhook asynchronously
processWebhookAsync(event);
4. Handle Failures Gracefully
Implement proper error handling and retries:
try {
await processWebhook(event);
} catch (error) {
console.error('Webhook processing failed:', error);
// Log to monitoring service
// Queue for retry if appropriate
}
Common Issues
Missing Events
- Ensure you’ve selected all required events in the webhook configuration
- Check if you’re listening to the correct mode (live vs test)
- Verify your endpoint URL is correct
Signature Verification Failures
- Make sure you’re using the correct endpoint secret
- Ensure the raw request body is used for verification (not parsed JSON)
- Check that your framework isn’t modifying the request body
Timeout Errors
- Respond to webhooks within 20 seconds
- Process heavy operations asynchronously
- Return 200 immediately, then process
Useful Links
Support
Need help with Stripe webhooks?
Responses are generated using AI and may contain mistakes.