Webhook Setup
Webhooks allow your application to receive real-time notifications about payment events. Instead of polling for status changes, the payment gateway pushes event data to your configured endpoints.
How Webhooks Work
sequenceDiagram
participant C as Customer
participant M as Payment Gateway
participant W as Your Webhook Endpoint
participant D as Your Database
C->>M: Complete Payment
M->>M: Process Payment
M->>W: POST Webhook Event
W->>W: Verify Signature
W->>D: Update Order Status
W->>M: HTTP 200 OK
Configuring Webhooks
Webhooks are configured as part of your Payment Application setup. Each application has a single callback URL where all webhook events are delivered.
Via Dashboard
- Navigate to Payment Applications in your dashboard
- Select your application (or create a new one)
- Set the Callback URL field to your webhook endpoint
- Set a Webhook Secret for signature verification
- Save the application
Via API
When creating or updating a payment application, include the callbackUrl and secret fields. See the Payment Applications guide for the full API request format.
Implementing a Webhook Handler
All webhooks are delivered as POST requests with the event type, timestamp, and signature in headers. See the Events reference for the full list of events and payload structures.
const crypto = require('node:crypto');
const express = require('express');
const app = express();
app.use('/webhooks/payments', express.raw({ type: 'application/json' }));
app.post('/webhooks/payments', (req, res) => {
const signature = req.headers['x-paymentservice-signature'];
const timestamp = req.headers['x-paymentservice-timestamp'];
const eventType = req.headers['x-paymentservice-event'];
const webhookSecret = process.env.WEBHOOK_SECRET;
// Verify signature
const signedPayload = `${timestamp}.${req.body.toString()}`;
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(signedPayload)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
const payment = event.payment;
switch (eventType) {
case 'payment.completed':
console.log('Payment completed:', payment.id);
// Update order status, send confirmation email, etc.
break;
case 'payment.received':
console.log('Payment received:', payment.id);
break;
case 'payment.underpaid':
console.log('Payment underpaid:', payment.id);
break;
case 'payment.refund':
console.log('Payment refunded:', payment.id);
break;
}
res.status(200).send('OK');
});
app.listen(3000);
Webhook Response Requirements
Your endpoint must return a 2xx HTTP status code within 10 seconds. If the request times out or returns a non-2xx response, the webhook will be retried. See Retry Logic for details.
Testing Webhooks Locally
Use a tunneling tool like ngrok to expose your local development server:
ngrok http 3000
# Use the generated URL as your callback URL:
# https://abc123.ngrok.io/webhooks/payments
Best Practices
- Always verify signatures to ensure webhook authenticity
- Respond quickly — return 200 immediately, process asynchronously if needed
- Implement idempotency — handle duplicate events gracefully
- Log all events for debugging and audit trails
- Use HTTPS for your webhook endpoint