Agent skill

Paysafe Webhooks Skill

Receive and verify Paysafe Payments API webhooks. Use when setting up Paysafe webhook handlers, debugging Signature header verification, or handling events like PAYMENT_HANDLE_COMPLETED, PAYMENT_COMPLETED, and SETTLEMENT_COMPLETED.

Install this skill

npx skills add hookdeck/webhook-skills --skill paysafe-webhooks


When to Use This Skill

  • How do I receive Paysafe webhooks?
  • How do I verify the Paysafe Signature header?
  • Why is my Paysafe webhook signature verification failing?
  • How do I handle PAYMENT_HANDLE_COMPLETED or SETTLEMENT_COMPLETED events?
  • Handling redirect-flow payment methods (Skrill, Paysafecard, PayPal, Interac) that finish asynchronously

Verification (core)

Paysafe sends a Signature header containing the base64 HMAC-SHA256 of the raw JSON request body, keyed with the HMAC key from the Business Portal. Use the key as a raw UTF-8 string (do not base64-decode it). There is no timestamp in the signed content.

const crypto = require('crypto');

function verifyPaysafeWebhook(rawBody, signatureHeader, secret) {
  if (!rawBody || !signatureHeader || !secret) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');
  try {
    return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
  } catch {
    return false; // different lengths = invalid
  }
}
import base64, hashlib, hmac

def verify_paysafe_webhook(raw_body: bytes, signature_header: str | None, secret: str) -> bool:
    if not raw_body or not signature_header:
        return False
    expected = base64.b64encode(hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()).decode()
    return hmac.compare_digest(signature_header.encode(), expected.encode())

Raw body only. Verify the exact bytes received before parsing. In Express use express.raw({ type: '*/*' }) so any content type verifies.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Common Event Types

Payload fields: payload, attemptNumber, type, resourceId, links, eventDate, eventName.

eventNameDescription
PAYMENT_HANDLE_INITIATEDPayment handle request received, awaiting downstream processor
PAYMENT_HANDLE_PAYABLEHandle is PAYABLE and can be used for a payment call
PAYMENT_HANDLE_COMPLETEDPayment associated with the handle was initiated
PAYMENT_HANDLE_FAILEDHandle request failed or was declined
PAYMENT_COMPLETEDPayment request completed
PAYMENT_FAILEDPayment failed or was declined
PAYMENT_CANCELLEDPayment fully voided (reversed)
PAYMENT_HELDPayment placed on hold for risk review
SETTLEMENT_COMPLETEDSettlement request completed
SETTLEMENT_FAILEDSettlement failed or was declined

Other categories include void authorization (VOIDAUTH_*), registration, refunds, original/standalone credits, and payment scheduler. See Paysafe webhook events.

Notes

  • Payloads are minimal; use links / resourceId to fetch the full resource from the Paysafe API.
  • Paysafe's doc example shows the resource body rather than the eventName envelope, so handle both and dispatch on eventName when present (see references/overview.md).
  • Return 2xx to acknowledge. On a 4xx or 5xx response Paysafe retries up to three times.
  • Webhooks are configured in the Business Portal; Paysafe strongly recommends them for redirect-flow payment methods.

Environment Variables

PAYSAFE_WEBHOOK_SECRET=your_paysafe_hmac_key_here   # HMAC key from the Business Portal

Local Development

npx hookdeck-cli listen 3000 paysafe --path /webhooks/paysafe

No account required. Use port 8000 for FastAPI.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 4, 2026

View on GitHub →