Agent skill

PayTabs Webhooks Skill

Receive and verify PayTabs webhooks. Use when setting up PayTabs (MENA payment gateway) callback or IPN handlers, debugging the Signature header HMAC-SHA256 verification, or handling transaction notifications by tran_type (Sale, Refund) and payment_result.response_status.

Install this skill

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


When to Use This Skill

  • How do I receive PayTabs callback or IPN webhooks?
  • How do I verify the Signature header on a PayTabs webhook?
  • Why is my PayTabs webhook signature verification failing?
  • Which key signs PayTabs webhooks (the profile ServerKey)?
  • How do I tell a Sale from a Refund, or an authorised payment from a declined one?
  • How do I run PayTabs webhooks through Hookdeck?

Verification (core)

Every callback and IPN request carries a Signature header. It is the bare lowercase hex HMAC-SHA256 of the raw request body bytes, keyed with your profile ServerKey (its UTF-8 bytes, used as-is). No prefix, timestamp or id is signed, and there is no official verification SDK, so verification is manual.

Node:

const crypto = require('crypto');

function verifyPayTabsSignature(rawBody, header, secret) {
  if (typeof header !== 'string' || !secret) return false;
  // Buffer.from(x, 'hex') silently drops bad characters: check the format first
  if (!/^[0-9a-f]{64}$/i.test(header)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
  return crypto.timingSafeEqual(Buffer.from(header, 'hex'), expected);
}

Python:

import hashlib, hmac, re

def verify_paytabs_signature(raw_body: bytes, header: str | None, secret: str | None) -> bool:
    if not header or not secret:
        return False
    if not re.fullmatch(r"[0-9a-f]{64}", header, re.IGNORECASE | re.ASCII):
        return False
    expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(header.lower().encode("ascii"), expected.encode("ascii"))

In Express, read the raw body with express.raw({ type: '*/*' }) so any content type verifies. Parse the JSON only after the check passes.

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

Respond 401 for a missing, empty or malformed header and for a mismatch. Only server-to-server callback (IPN) requests are covered: the browser Return URL puts its signature in a signature form field over sorted parameters and is not a webhook.

Events

There is no event name in the body or in a header. Every callback and IPN is the full transaction as JSON, so dispatch on the transaction itself:

FieldMeaning
tran_typeTransaction type, for example Sale, Refund
payment_result.response_statusOutcome: A is authorised (the docs sample shows D for a declined payment)
payment_result.response_messageHuman-readable outcome
tran_refPayTabs transaction reference, for dedupe

The IPN fires whenever a transaction is created or changes. The callback fires once per payment request when the payment ends, whatever the customer did. See references/overview.md.

Environment Variables

PAYTABS_WEBHOOK_SECRET=your_profile_server_key

Setup (PayTabs and Hookdeck)

The signing secret is your profile ServerKey, the same key you use to call the PT2 API. There is no separate webhook secret. Set the callback URL on the payment request, or the IPN listener URL on the profile, to your endpoint. The URL must be public HTTPS, and a framework with CSRF protection must exempt the route.

For the Hookdeck source: Paste your PayTabs profile server key. Only callback (IPN) requests are supported, not the browser return URL.

PayTabs does not document retries, a timeout or the expected response, so reply 2xx quickly and make handlers idempotent. See references/setup.md.

Local Development

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

No account required. The CLI creates a guest account on first run and provides a local tunnel plus a web UI for inspecting requests. Use port 8000 for FastAPI.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 13, 2026

View on GitHub →