Documentation

Complete guide to webhooks, debugging, and real-time event monitoring with HookMetry

Integration Code Examples

JS
Node.js / Express

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'));

PY
Python / Flask

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
PHP

<?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:

  1. Create an endpoint in HookMetry with your chosen validation type
  2. Copy the generated webhook URL
  3. Send test requests using cURL or your application
  4. View real-time logs, validation results, and full payloads
  5. Replay failed requests to debug issues