Coming soon. Steadi Pay is not live yet. Accounts are for sandbox testing only, and no real payments can be made.

Docs

Webhooks

Steadi Pay POSTs a signed JSON event when a payment succeeds or fails. Verify the signature before you fulfil the order.

Add an endpoint

In the portal, Developers → Your apps, add an HTTPS URL. The webhook secret is shown once. We POST the event body as JSON with these headers:

  • SteadiPay-Signature: t={unix},v1={hmac}
  • SteadiPay-Event-Id
  • Content-Type: application/json

Events

typeWhen
payment.succeededProvider confirmed the collection. Safe to fulfil.
payment.failedConfirmed failure or a failed attempt. No money was taken.
{
  "id": "evt_...",
  "type": "payment.succeeded",
  "created_at": "2026-09-19T12:01:22.000Z",
  "data": {
    "id": "pay_...",
    "status": "succeeded",
    "merchant_reference": "TICKET-8841",
    "gross_amount_minor": "25650",
    "merchant_amount_minor": "25000",
    "fee_amount_minor": "650"
  }
}

Verify the signature

Read the raw body as a string. Parse t and v1 from the header. Reject the event if t is more than 300 seconds off. Compute HMAC-SHA256 of t + "." + rawBody using the endpoint secret. Compare to v1 with a constant-time check.

import { createHmac, timingSafeEqual } from "crypto";

function verifySteadiPay(secret, header, rawBody) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const ts = Number(parts.t);
  if (!ts || Math.abs(Date.now() / 1000 - ts) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? "");
  return a.length === b.length && timingSafeEqual(a, b);
}

Return HTTP 2xx quickly. We time out after 10 seconds.

Retries

Failed deliveries are retried at 30s, 1m, 5m, 15m, 1h and 6h. You can replay a delivery from the portal. Treat events as idempotent: store SteadiPay-Event-Id or the payment id and ignore duplicates.

Do not

  • Fulfil from an unsigned body.
  • Fulfil from the return URL without a verify or a valid webhook.
  • Log the webhook secret or the raw signature in customer tickets.