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.
| Event | Description |
|---|---|
payment.succeeded | A payment has been captured and confirmed on IPPAN L2. |
payment.failed | A payment attempt was declined or timed out. |
settlement.finalized | A settlement batch has been finalized with a deterministic proof. |
invoice.paid | An invoice has been fully paid by the customer. |
payout.completed | A 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.
{
"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.
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 });
});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.
| Attempt | Delay after failure |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 8 hours |
| 6th retry | 24 hours |