Webhooks
Receive real-time notifications for payment events.
Handle Webhooks
Webhooks allow you to receive real-time notifications when events happen in your PayGate account, such as when a payment succeeds or fails.
How Webhooks Work
- A customer makes a payment
- PayGate sends a POST request to your webhook endpoint
- Your server processes the event and responds with a 200 status
- If we don't receive a 200, we'll retry the webhook
Webhooks are signed with your webhook secret, allowing you to verify they came from PayGate.
Set Up Webhooks
Create a Webhook Endpoint
First, create an endpoint on your server to receive webhooks:
import express from 'express'
import { Webhook } from '@paygate/node'
const app = express()
const endpointSecret = process.env.PAYGATE_WEBHOOK_SECRET!
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-paygate-signature'] as string
let event
try {
event = Webhook.constructEvent(req.body, signature, endpointSecret)
} catch (err) {
console.error('Webhook signature verification failed:', err.message)
return res.status(400).send(`Webhook Error: ${err.message}`)
}
// Handle the event
switch (event.type) {
case 'payment.succeeded':
const payment = event.data.object
console.log(`Payment ${payment.id} succeeded!`)
// Update your database, send confirmation email, etc.
break
case 'payment.failed':
console.log('Payment failed:', event.data.object.id)
// Handle failed payment
break
case 'subscription.created':
console.log('Subscription created')
break
default:
console.log(`Unhandled event type: ${event.type}`)
}
res.json({ received: true })
})
app.listen(3000)from flask import Flask, request
import paygate
app = Flask(__name__)
endpoint_secret = 'whsec_...'
@app.route('/webhooks', methods=['POST'])
def webhook():
payload = request.data
signature = request.headers.get('X-PayGate-Signature')
try:
event = paygate.Webhook.construct_event(
payload, signature, endpoint_secret
)
except ValueError as e:
return 'Invalid payload', 400
except paygate.SignatureVerificationError as e:
return 'Invalid signature', 400
# Handle the event
if event['type'] == 'payment.succeeded':
payment = event['data']['object']
print(f"Payment {payment['id']} succeeded!")
elif event['type'] == 'payment.failed':
print('Payment failed')
return {'received': True}
if __name__ == '__main__':
app.run(port=3000)<?php
require 'vendor/autoload.php';
$endpointSecret = 'whsec_...';
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PAYGATE_SIGNATURE'];
try {
$event = \PayGate\Webhook::constructEvent(
$payload,
$signature,
$endpointSecret
);
} catch (\Exception $e) {
http_response_code(400);
echo 'Webhook Error: ' . $e->getMessage();
exit();
}
// Handle the event
switch ($event->type) {
case 'payment.succeeded':
$payment = $event->data->object;
error_log("Payment {$payment->id} succeeded!");
break;
case 'payment.failed':
error_log('Payment failed');
break;
}
http_response_code(200);
echo json_encode(['received' => true]);Register Your Endpoint
Add your webhook endpoint in the Dashboard:
- Go to Settings > Webhooks
- Click Add endpoint
- Enter your endpoint URL (e.g.,
https://yoursite.com/webhooks) - Select the events you want to receive
- Save and copy your webhook secret
Test Your Webhook
Use the Dashboard to send test events:
- Go to your webhook endpoint settings
- Click Send test webhook
- Select an event type
- Check your server logs to verify it was received
Webhook Events
Payment Events
| Event | Description |
|---|---|
payment.created | A payment was created |
payment.processing | Payment is being processed |
payment.succeeded | Payment completed successfully |
payment.failed | Payment failed |
payment.cancelled | Payment was cancelled |
Subscription Events
| Event | Description |
|---|---|
subscription.created | A subscription was created |
subscription.updated | Subscription was updated |
subscription.cancelled | Subscription was cancelled |
subscription.payment_succeeded | Recurring payment succeeded |
subscription.payment_failed | Recurring payment failed |
Payout Events
| Event | Description |
|---|---|
payout.created | A payout was initiated |
payout.succeeded | Payout was successful |
payout.failed | Payout failed |
Event Object
{
"id": "evt_abc123",
"object": "event",
"type": "payment.succeeded",
"created_at": "2024-01-15T10:30:00Z",
"data": {
"object": {
"id": "pay_xyz789",
"object": "payment",
"amount": 5000,
"currency": "GHS",
"status": "succeeded",
...
}
}
}Best Practices
1. Verify Signatures
Always verify webhook signatures to ensure the request came from PayGate:
const event = Webhook.constructEvent(payload, signature, endpointSecret)2. Return 200 Quickly
Return a 200 response immediately, then process the event asynchronously:
app.post('/webhooks', async (req, res) => {
// Verify and acknowledge
const event = Webhook.constructEvent(req.body, signature, secret)
res.json({ received: true })
// Process async (use a queue in production)
processEventAsync(event)
})3. Handle Duplicate Events
Use the event ID to handle duplicate deliveries:
if (await isEventProcessed(event.id)) {
return res.json({ received: true })
}
await processEvent(event)
await markEventProcessed(event.id)4. Use HTTPS
Always use HTTPS endpoints in production. We won't deliver webhooks to HTTP endpoints in live mode.
Retry Policy
If your endpoint doesn't return a 2xx status code, we'll retry with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 8 hours |
| 6 | 24 hours |
After 6 failed attempts, the webhook is marked as failed and we'll send you an email notification.