Creating a Webhook Listener
Implement a webhook listener to receive real-time event notifications from WickiePay.
Requirements
Your webhook endpoint must:
- Accept HTTP POST requests
- Return 200 OK within 30 seconds
- Be accessible via HTTPS (TLS required)
- Validate the webhook signature
Node.js Example
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.WICKIEPAY_WEBHOOK_SECRET;
// Verify webhook signature
function verifySignature(payload, signature) {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
app.post('/webhooks/wickiepay', (req, res) => {
const signature = req.headers['x-webhook-signature'];
// 1. Verify signature
if (!verifySignature(req.body, signature)) {
console.error('Invalid webhook signature');
return res.status(401).send('Invalid signature');
}
// 2. Process the event
const { event, data } = req.body;
switch (event) {
case 'payment.completed':
handlePaymentCompleted(data);
break;
case 'payment.expired':
handlePaymentExpired(data);
break;
case 'channel.payment.confirmed':
handleChannelDeposit(data);
break;
default:
console.log(`Unhandled event: ${event}`);
}
// 3. Return 200 OK
res.status(200).send('OK');
});
function handlePaymentCompleted(data) {
console.log(`Payment ${data.uuid} completed for ${data.reference}`);
// Update your order status, credit customer, etc.
}
function handlePaymentExpired(data) {
console.log(`Payment ${data.uuid} expired for ${data.reference}`);
// Mark order as expired, notify customer
}
function handleChannelDeposit(data) {
console.log(`Channel deposit confirmed: ${data.uuid}`);
// Credit customer balance
}
app.listen(3000, () => console.log('Webhook listener running on port 3000'));
Python Example
from flask import Flask, request, jsonify
import hmac
import hashlib
import json
app = Flask(__name__)
WEBHOOK_SECRET = os.environ['WICKIEPAY_WEBHOOK_SECRET']
def verify_signature(payload, signature):
expected = hmac.new(
WEBHOOK_SECRET.encode(),
json.dumps(payload).encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.route('/webhooks/wickiepay', methods=['POST'])
def webhook():
signature = request.headers.get('X-Webhook-Signature')
payload = request.get_json()
if not verify_signature(payload, signature):
return 'Invalid signature', 401
event = payload['event']
data = payload['data']
if event == 'payment.completed':
# Handle completed payment
print(f"Payment {data['uuid']} completed")
return 'OK', 200
Best Practices
Idempotency
Always handle duplicate webhook deliveries gracefully. Use the uuid field to check if you've already processed an event.
- Respond quickly — Return 200 OK before doing heavy processing. Queue the work for async processing.
- Verify signatures — Always validate the
X-Webhook-Signatureheader. - Log everything — Store raw webhook payloads for debugging and auditing.
- Handle retries — WickiePay retries failed deliveries up to 3 times with exponential backoff.
Testing Webhooks
Use the Sandbox to generate test events, or use tools like ngrok to expose your local development server.
Next Steps
- Event Replay — Re-send missed webhook events
- Event Types — All available events