Event-driven notifications for payment processing
Core Idea: Payment webhooks are HTTP callbacks that notify applications about payment events in real-time, enabling automated responses to payment successes, failures, disputes, and other critical events without constant polling.
Key Elements
-
Key principles
- Asynchronous communication
- Event-driven architecture
- Real-time notifications
- Idempotent processing
- Security verification
-
Common webhook events
- Payment successes and failures
- Subscription creation, updates, cancellations
- Dispute creation and resolution
- Refund processing
- Account updates
- Payout status changes
-
Implementation workflow
- Register webhook endpoint with payment provider
- Configure events to receive
- Implement endpoint to receive webhook data
- Verify webhook signature/authenticity
- Process event data
- Return appropriate response
-
Security considerations
- Signature verification
- HTTPS-only endpoints
- Replay attack prevention
- Timeout handling
- Error recovery
-
Code example (Stripe webhook handling with Express)
// webhook.js
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const router = express.Router();
router.post('/stripe-webhook', express.raw({type: 'application/json'}), async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
// Verify webhook signature
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.log(`Webhook signature verification failed: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle specific events
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
await fulfillOrder(paymentIntent);
break;
case 'checkout.session.completed':
const session = event.data.object;
await fulfillCheckoutSession(session);
break;
case 'invoice.payment_succeeded':
const invoice = event.data.object;
await handleSuccessfulSubscriptionPayment(invoice);
break;
case 'invoice.payment_failed':
const failedInvoice = event.data.object;
await handleFailedSubscriptionPayment(failedInvoice);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
// Return success response to acknowledge receipt
res.status(200).json({received: true});
});
async function fulfillOrder(paymentIntent) {
// Implementation for order fulfillment
// - Update database
// - Trigger email
// - Provision access
console.log(`Order fulfilled for payment: ${paymentIntent.id}`);
}
// Export router
module.exports = router;
- Webhook testing approaches
- Local development with tunneling (ngrok, localtunnel)
- Payment provider's webhook testing tools
- Manually triggering events in test mode
- Logging and request inspection
Additional Connections
- Broader Context: Event-Driven Architecture (webhooks as implementation of event-driven systems)
- Applications: Order Fulfillment Automation (triggering fulfillment after payment)
- See Also: Idempotent API Design (handling duplicate webhook delivery)
References
- Stripe Webhooks Documentation: https://stripe.com/docs/webhooks
- Webhook Security Best Practices: https://docs.webhook.site/best-practices.html
#webhooks #payments #event-driven
Connections:
Sources: