Setup and Verify Webhooks
This guide shows you how to set up a webhook endpoint, register it with NextAPI, and verify incoming signatures.
Step 1: Create Your Endpoint
Your webhook endpoint must:
- Accept
POSTrequests - Return a
2xxstatus within 30 seconds - Be accessible via HTTPS
- Node.js (Express)
- Python (Flask)
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use('/webhooks/nextpay', express.raw({ type: 'application/json' }));
const WEBHOOK_SECRET = process.env.NEXTPAY_WEBHOOK_SECRET;
app.post('/webhooks/nextpay', (req, res) => {
const signature = req.headers['x-nextpay-signature'];
const payload = req.body;
// Verify signature
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
// Parse and handle the event
const event = JSON.parse(payload);
console.log(`Received event: ${event.type}`);
switch (event.type) {
case 'payout_request.completed':
// Handle completed payout
break;
case 'payout_request.failed':
// Handle failed payout
break;
case 'payment_intent.paid':
// Handle successful payment
break;
}
res.status(200).send('OK');
});
app.listen(3000);
import hmac
import hashlib
import json
import os
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = os.environ['NEXTPAY_WEBHOOK_SECRET']
@app.route('/webhooks/nextpay', methods=['POST'])
def handle_webhook():
signature = request.headers.get('x-nextpay-signature')
payload = request.get_data()
# Verify signature
expected = hmac.new(
WEBHOOK_SECRET.encode(),
payload,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
return 'Invalid signature', 401
# Parse and handle the event
event = json.loads(payload)
print(f"Received event: {event['type']}")
if event['type'] == 'payout_request.completed':
pass # Handle completed payout
elif event['type'] == 'payout_request.failed':
pass # Handle failed payout
elif event['type'] == 'payment_intent.paid':
pass # Handle successful payment
return 'OK', 200
Step 2: Register Your Webhook
Use the API to register your endpoint URL and select which events to receive:
curl -X POST "https://api.partners.nextpay.world/v2/webhooks" \
-H "Content-Type: application/json" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d '{
"url": "https://your-domain.com/webhooks/nextpay",
"events": [
"payout_request.completed",
"payout_request.failed",
"payment_intent.paid"
]
}'
The response includes your webhook id and the secret used for signature verification. Store the secret securely — it is only shown once.
Step 3: Handle Deduplication
Webhook events may be delivered more than once. Use the event id to prevent duplicate processing:
const processedEvents = new Set();
function handleEvent(event) {
if (processedEvents.has(event.id)) {
return; // Already processed
}
// Process the event...
processedEvents.add(event.id);
}
In production, store processed event IDs in your database rather than in-memory.
Step 4: Test Locally
Use a tunneling tool like ngrok to expose your local server for testing:
ngrok http 3000
Then register the ngrok URL as your webhook endpoint in the sandbox environment.
Troubleshooting
| Issue | Solution |
|---|---|
| 401 Unauthorized | Check your webhook secret and signature verification logic |
| Timeout errors | Acknowledge quickly, process async. Return 200 before heavy work. |
| Missing events | Verify your webhook is registered for the correct event types |
| Duplicate events | Implement deduplication using event IDs |
Related
- Understanding Webhooks — how webhooks work
- API Reference: Create Webhook — full endpoint docs
- API Reference: List Webhooks — manage your webhooks