Validate signature function
Take the validate signature snippet from our recipes, it’ll be used below and it’s important for security.
Create a Webhook endpoint
Use your dashboard to set up a webhook endpoint and receive real-time event notifications.
Webhook.php
$rawBody = file_get_contents("php://input");
$headers = getallheaders();
$secret = 'your-webhook-secret';
if (!validate_webhook($headers, $rawBody, $secret)) {
http_response_code(400);
echo json_encode(["error" => "Invalid signature"]);
exit;
}
$event = $headers['X-Webhook-Event'] ?? '';
$body = json_decode($rawBody, true);
switch ($event) {
case 'invoice.completed':
// handleInvoiceCompleted($body);
break;
default:
error_log("Unhandled event: $event");
break;
}
http_response_code(200);
echo "OK";
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const secret = process.env.WEBHOOK_SECRET;
app.use(bodyParser.json());
app.post('/webhook', (req, res) => {
if (!validateWebhook(req, secret)) {
return res.status(400).json({ error: "Invalid signature" });
}
const event = req.header("X-Webhook-Event");
const body = req.body;
switch (event) {
case "invoice.completed":
// handleInvoiceCompleted(body);
break;
default:
console.warn("Unhandled event:", event);
}
res.sendStatus(200);
});
import express, { Request, Response } from 'express';
import { validateWebhook } from './validateWebhook'; // your function
const app = express();
const secret = process.env.WEBHOOK_SECRET as string;
app.use(express.json());
app.post("/webhook", (req: Request, res: Response) => {
if (!validateWebhook(req.body, req.headers, secret)) {
return res.status(400).json({ error: "Invalid signature" });
}
const event = req.headers["x-webhook-event"] as string;
switch (event) {
case "invoice.completed":
// handleInvoiceCompleted(req.body);
break;
default:
console.warn("Unhandled event:", event);
}
res.send("OK");
});
from flask import Flask, request, jsonify
app = Flask(__name__)
secret = "your-webhook-secret"
@app.route("/webhook", methods=["POST"])
def webhook():
if not validate_webhook(request, secret):
return jsonify({"error": "Invalid signature"}), 400
event = request.headers.get("X-Webhook-Event")
data = request.get_json()
match event:
case "invoice.completed":
# handle_invoice_completed(data)
pass
case _:
print(f"Unhandled event: {event}")
return "OK", 200
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
secret := "your-webhook-secret"
if !validateWebhook(r, secret) {
http.Error(w, "Invalid signature", http.StatusBadRequest)
return
}
var body map[string]interface{}
raw, _ := io.ReadAll(r.Body)
json.Unmarshal(raw, &body)
event := r.Header.Get("X-Webhook-Event")
switch event {
case "order:created":
// handleOrderCreated(body)
case "invoice.completed":
// handleInvoiceCompleted(body)
default:
fmt.Printf("Unhandled event: %s\n", event)
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}
import javax.servlet.http.*;
import java.io.*;
import java.util.Map;
public class WebhookServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String secret = "your-webhook-secret";
Map<String, String> headers = extractHeaders(req); // implement as needed
String body = new BufferedReader(new InputStreamReader(req.getInputStream()))
.lines().reduce("", (acc, line) -> acc + line);
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> bodyMap = mapper.readValue(body, Map.class);
try {
if (!WebhookValidator.validate(bodyMap, headers, secret)) {
resp.setStatus(400);
resp.getWriter().write("{\"error\":\"Invalid signature\"}");
return;
}
String event = headers.get("X-Webhook-Event");
switch (event) {
case "invoice.completed":
// handleInvoiceCompleted(bodyMap);
break;
default:
System.out.println("Unhandled event: " + event);
}
resp.setStatus(200);
resp.getWriter().write("OK");
} catch (Exception e) {
resp.setStatus(500);
resp.getWriter().write("{\"error\":\"Internal server error\"}");
}
}
}
use actix_web::{post, web, HttpRequest, HttpResponse};
use std::collections::HashMap;
#[post("/webhook")]
async fn webhook_handler(req: HttpRequest, body: String) -> HttpResponse {
let secret = "your-webhook-secret";
let headers = req
.headers()
.iter()
.map(|(k, v)| (k.as_str().to_lowercase(), v.to_str().unwrap_or("").to_string()))
.collect::<HashMap<String, String>>();
let parsed: serde_json::Value = serde_json::from_str(&body).unwrap_or_default();
if !validate_webhook(&parsed.as_object().unwrap(), &headers, secret) {
return HttpResponse::BadRequest().json({ "error": "Invalid signature" });
}
let event = headers.get("x-webhook-event").unwrap_or(&"".to_string());
match event.as_str() {
"invoice.completed" => {
// handle_invoice_completed(&parsed)
}
_ => {
println!("Unhandled event: {}", event);
}
}
HttpResponse::Ok().body("OK")
}

