Core
Webhooks & signing
Subscribe to events with HMAC-SHA256 signed deliveries.
Subscribe to event types from your dashboard or via the API. Each delivery includes a signed header so you can verify the payload was issued by MoonPoay.
text
X-MoonPoay-Signature: t=1714672890,v1=2c8a8…b7 X-MoonPoay-Event: payment.completed X-MoonPoay-Delivery: dlv_1zP9e…
Verifying a delivery
javascript
import crypto from "node:crypto";
export function verify(payload, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(
header.split(",").map((p) => p.split("=")),
);
const t = Number(parts.t);
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${payload}`)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))) {
throw new Error("invalid_signature");
}
if (Math.abs(Date.now() / 1000 - t) > toleranceSec) {
throw new Error("stale_timestamp");
}
}Always verify with a constant-time comparison. Reject any delivery whose timestamp is more than 5 minutes old to defend against replay.
Event types
- payment.created
- payment.processing
- payment.completed
- payment.failed
- payment.expired
- refund.created
- refund.succeeded
- refund.failed
- checkout.session.completed
- checkout.session.expired
- settlement.completed
- chargeback.created
Register an endpoint
bash
curl https://sandbox-api.key2pays.com/v1/webhooks \
-H "Authorization: Bearer sk_test_51N8mP...exampleK3Y" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/moonpoay",
"events": ["payment.completed", "payment.failed", "refund.succeeded"]
}'