Why Webhooks?
Webhooks enable Stripe to notify your API when payment events occur. This is essential because:- Payments are processed asynchronously
- Users may close their browser before payment completes
- Network issues can prevent frontend notification
- Webhooks provide server-side confirmation of payment status
The API uses webhooks to update purchase status from
pending to completed when payments succeed.Webhook Events
The API handles these Stripe events:| Event | Description | Handler |
|---|---|---|
payment_intent.succeeded | Payment completed successfully | Updates purchase to completed |
payment_intent.payment_failed | Payment declined or failed | Updates purchase to failed |
Other Stripe events are ignored. See
src/controllers/paymentController.js:80-91 for event handling logic.Setup Webhooks
Create webhook endpoint in Stripe
For Development (using Stripe CLI):Output:
- Install the Stripe CLI:
- Login to Stripe:
- Forward webhooks to your local server:
- Copy the webhook signing secret and add to
.env:
Create webhook endpoint in Stripe (Production)
For Production:
- Go to Stripe Dashboard → Developers → Webhooks
- Click “Add endpoint”
- Enter your webhook URL:
-
Select events to listen to:
payment_intent.succeededpayment_intent.payment_failed
- Click “Add endpoint”
-
Copy the “Signing secret” (starts with
whsec_) - Add to production environment variables:
Webhook Security
The API verifies webhook authenticity using Stripe signatures. This prevents malicious actors from sending fake webhook events.How Signature Verification Works
Request flow:Express Configuration
Ensure your Express app is configured correctly:Webhook Handler Implementation
The webhook handler processes payment events:Payment Success Handler
Payment Failure Handler
Testing Webhooks
Using Stripe CLI
Forward webhooks to local server:Using Stripe Dashboard
- Go to Stripe Dashboard → Developers → Webhooks
- Click on your webhook endpoint
- Click “Send test webhook”
- Select event type (e.g.,
payment_intent.succeeded) - Optionally customize the payload
- Click “Send test webhook”
- View the response and logs
Manual Testing with cURL
Webhook Reliability
Stripe Retry Logic
If your webhook endpoint returns a non-2xx status code, Stripe automatically retries:- Retry schedule: Exponential backoff over 3 days
- First retry: Immediately
- Subsequent retries: 1 hour, 2 hours, 4 hours, etc.
- Maximum attempts: ~10 retries over 72 hours
Idempotency
Webhook handlers should be idempotent (safe to process multiple times):Monitoring Webhooks
Check webhook health in Stripe Dashboard:- Go to Developers → Webhooks
- View delivery success rate
- Investigate failed deliveries
- Check response times
- Configure email notifications in Stripe Dashboard
- Monitor error logs in your application
- Use uptime monitoring services
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| 400 Webhook Error | Invalid signature | Verify STRIPE_WEBHOOK_SECRET is correct |
| 400 Webhook Error | Body parsed as JSON | Use express.raw() for webhook route |
| Purchase not updating | Wrong environment (test vs live) | Ensure webhook secret matches Stripe mode |
| Webhook not received | Firewall blocking Stripe IPs | Whitelist Stripe IP ranges |
| High latency | Slow database queries | Add indexes on stripePaymentIntentId |
Production Checklist
Environment configuration
- Production webhook endpoint created in Stripe Dashboard
-
STRIPE_WEBHOOK_SECRETset to production secret - HTTPS enabled on webhook URL (required by Stripe)
- Domain verified and SSL certificate valid
Security
- Webhook signature verification enabled
- Raw body parsing configured for webhook route
- Webhook secret stored securely (not in code)
- Rate limiting configured (prevent DoS)
Monitoring
- Webhook delivery monitoring enabled
- Error alerting configured
- Logging implemented for webhook events
- Database indexes on
stripePaymentIntentId
Advanced Configuration
Webhook Timeouts
Stripe expects webhook responses within 5 seconds. If your handler takes longer:Multiple Webhook Endpoints
For microservices, you can configure multiple endpoints:Webhook Event Filtering
In Stripe Dashboard, select only required events to reduce noise:payment_intent.succeeded✓payment_intent.payment_failed✓charge.succeeded✗ (not needed)customer.created✗ (not needed)
Source Code References
- Webhook handler:
src/controllers/paymentController.js:64 - Payment success:
src/controllers/paymentController.js:97 - Payment failure:
src/controllers/paymentController.js:115 - Stripe configuration:
src/config/stripe.js
Next Steps
Payment Flow
Learn the complete payment workflow
Stripe API Reference
Official Stripe webhook documentation