Webhook trust

Verify the event before you update the order.

XPayr signs webhook payloads with your webhook secret. Store the raw request body, verify X-XPayr-Signature, then process the event idempotently.

Headers to read

  • X-XPayr-Signature: HMAC SHA-256 signature prefixed with sha256=.
  • X-XPayr-Event: event type such as payment.completed.
  • X-XPayr-Delivery: delivery/event identifier where available.

Node.js verification

import crypto from "node:crypto";

export function verifyXPayrWebhook(rawBody, signatureHeader, secret) {
  const received = String(signatureHeader || "").replace(/^sha256=/, "");
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(received, "hex"),
    Buffer.from(expected, "hex")
  );
}

PHP verification

function verify_xpayr_webhook(string $rawBody, string $signatureHeader, string $secret): bool
{
    $received = preg_replace('/^sha256=/', '', trim($signatureHeader));
    $expected = hash_hmac('sha256', $rawBody, $secret);

    return is_string($received) && hash_equals($expected, $received);
}

Events to handle

At minimum, handle payment.completed, payment.failed, payment.expired, and test.webhook. Use the session ID and your own metadata order ID to reconcile.