Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.zennopay.in/llms.txt

Use this file to discover all available pages before exploring further.

Webhook event types and the signature scheme are still being finalized. Expect the signature to share the HMAC canonicalization scheme used for server-to-server requests. 🚧

Event types

EventWhen it fires
payment_intent.capturedProvider confirmed payout; merchant has been paid
payment_intent.failedFinal failure; intent will not be retried
payment_intent.refundedRefund completed end-to-end

Delivery

  • Webhooks are POSTed as JSON to your registered endpoint.
  • We retry with exponential backoff on non-2xx responses for up to 24 hours.
  • Order is not guaranteed. Treat your handler as idempotent — keyed on event_id.

Signature verification

Each webhook carries an X-Zennopay-Signature header. Verify it by re-computing HMAC-SHA256 over the canonical request (see Authentication) using the webhook signing secret issued during onboarding (distinct from your API signing secret).
import crypto from "node:crypto";

function verifyWebhook(headers, rawBody, secret) {
  const timestamp = headers["x-zennopay-timestamp"];
  const nonce = headers["x-zennopay-nonce"];
  const signature = headers["x-zennopay-signature"];
  const bodyHash = crypto.createHash("sha256").update(rawBody).digest("hex");
  const canonical = ["POST", "/webhook", timestamp, nonce, bodyHash].join("\n");
  const expected = crypto.createHmac("sha256", secret).update(canonical).digest("base64");
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Always use a constant-time comparison.