Complete guide to webhooks, debugging, and real-time event monitoring with HookMetry
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Webhook endpoint with signature validation
app.post('/webhooks/stripe', async (req, res) => {
const signature = req.headers['stripe-signature'];
const secret = process.env.STRIPE_WEBHOOK_SECRET;
try {
// Verify Stripe signature
const event = verifyStripeSignature(req.body, signature, secret);
// Handle different event types
switch (event.type) {
case 'payment_intent.succeeded':
await handlePaymentSuccess(event.data.object);
break;
case 'payment_intent.failed':
await handlePaymentFailure(event.data.object);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.json({ received: true });
} catch (err) {
console.error('Webhook error:', err);
res.status(400).send(`Webhook Error: ${err.message}`);
}
});
function verifyStripeSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(JSON.stringify(payload)).digest('hex');
if (digest !== signature) {
throw new Error('Invalid signature');
}
return payload;
}
app.listen(3000, () => console.log('Server running on port 3000'));from flask import Flask, request, jsonify
import hmac
import hashlib
import os
app = Flask(__name__)
@app.route('/webhooks/github', methods=['POST'])
def github_webhook():
# Get signature from headers
signature = request.headers.get('X-Hub-Signature-256')
secret = os.environ.get('GITHUB_WEBHOOK_SECRET')
# Verify signature
if not verify_github_signature(request.data, signature, secret):
return jsonify({'error': 'Invalid signature'}), 401
# Parse payload
event = request.json
event_type = request.headers.get('X-GitHub-Event')
# Handle different events
if event_type == 'push':
handle_push(event)
elif event_type == 'pull_request':
handle_pull_request(event)
else:
print(f'Unhandled event: {event_type}')
return jsonify({'status': 'success'}), 200
def verify_github_signature(payload, signature_header, secret):
"""Verify GitHub webhook signature"""
if not signature_header:
return False
# GitHub sends: sha256=<signature>
hash_algo, signature = signature_header.split('=')
# Compute expected signature
mac = hmac.new(secret.encode(), payload, hashlib.sha256)
expected = mac.hexdigest()
return hmac.compare_digest(expected, signature)
def handle_push(event):
commits = event.get('commits', [])
print(f"Received {len(commits)} new commits")
# Your business logic here
def handle_pull_request(event):
action = event.get('action')
pr_number = event['pull_request']['number']
print(f"PR #{pr_number} was {action}")
# Your business logic here
if __name__ == '__main__':
app.run(port=5000)<?php
// webhook.php
header('Content-Type: application/json');
// Get raw POST data
$payload = file_get_contents('php://input');
$data = json_decode($payload, true);
// Get signature from headers
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$secret = getenv('WEBHOOK_SECRET');
// Verify HMAC signature
if (!verifySignature($payload, $signature, $secret)) {
http_response_code(401);
echo json_encode(['error' => 'Invalid signature']);
exit;
}
// Process webhook
$eventType = $data['type'] ?? 'unknown';
switch ($eventType) {
case 'order.created':
handleNewOrder($data);
break;
case 'order.completed':
handleCompletedOrder($data);
break;
default:
error_log("Unhandled event: " . $eventType);
}
http_response_code(200);
echo json_encode(['status' => 'success']);
function verifySignature($payload, $signature, $secret) {
$expected = hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signature);
}
function handleNewOrder($data) {
// Your business logic
error_log("New order: " . $data['id']);
}
function handleCompletedOrder($data) {
// Your business logic
error_log("Completed order: " . $data['id']);
}
?>Testing Tips:
Use HookMetry to test these implementations without modifying your provider settings: