Event Delivery

Webhooks

IPPAN Pay delivers real-time event notifications to your server via HTTP POST. Every webhook is signed with HMAC-SHA256, retried on failure, and ordered by event creation time.

Real-time delivery

Events are dispatched within milliseconds of state changes. No polling required.

Cryptographic signatures

Every payload is signed with your webhook secret using HMAC-SHA256. Verify before processing.

Automatic retries

Failed deliveries are retried up to 6 times with exponential backoff over 24 hours.

Available events

Subscribe to the events that matter to your integration. Each event type corresponds to a specific state transition in the IPPAN Pay lifecycle.

EventDescription
payment.succeededA payment has been captured and confirmed on IPPAN L2.
payment.failedA payment attempt was declined or timed out.
settlement.finalizedA settlement batch has been finalized with a deterministic proof.
invoice.paidAn invoice has been fully paid by the customer.
payout.completedA payout has been delivered to the beneficiary and settled on-chain.

Example payload

Every webhook POST contains a top-level event object with a nested data field holding the full resource snapshot at the time of the event.

payment.succeeded
{
  "id": "evt_Nv3mTx8qRz",
  "object": "event",
  "type": "payment.succeeded",
  "created_at": "2026-03-28T14:22:12Z",
  "data": {
    "id": "pay_7f3dKz1mNv",
    "object": "payment",
    "amount": 50000,
    "currency": "USDC",
    "status": "succeeded",
    "merchant_id": "mer_9x8kLm2nQp",
    "proof_url": "https://proofs.l2.ippan.com/pay_7f3dKz1mNv",
    "metadata": {
      "order_id": "ord_abc123"
    }
  }
}

Signature verification

Always verify the x-ippan-signature header before processing a webhook. Use constant-time comparison to prevent timing attacks.

Manual verification (Node.js)
import crypto from "node:crypto";

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload, "utf8")
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
app.post("/webhooks/ippan", (req, res) => {
  const signature = req.headers["x-ippan-signature"] as string;
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    process.env.IPPAN_WEBHOOK_SECRET!
  );

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = req.body;
  switch (event.type) {
    case "payment.succeeded":
      // Fulfill the order
      break;
    case "settlement.finalized":
      // Update internal records
      break;
  }

  res.status(200).json({ received: true });
});
SDK verification (recommended)
import { IppanPay } from "@ippan/pay-sdk";

const client = new IppanPay({ apiKey: process.env.IPPAN_API_KEY! });

// The SDK handles HMAC verification automatically
const event = client.webhooks.constructEvent(
  rawBody,
  req.headers["x-ippan-signature"],
  process.env.IPPAN_WEBHOOK_SECRET!
);

// event is now typed and verified
console.log(event.type); // "payment.succeeded"

Retry policy

If your endpoint returns a non-2xx status code or times out (30s), IPPAN Pay retries delivery with exponential backoff. After 6 failed attempts, the event is marked as failed and visible in your dashboard.

AttemptDelay after failure
1st retry30 seconds
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry8 hours
6th retry24 hours