Signatures de webhooks

Chaque webhook est signé avec HMAC-SHA256 sur le corps brut de la requête en utilisant votre secret de webhook. La signature est envoyée dans l'en-tête X-ZyndPay-Signature. Comparez en temps constant. Relance : backoff exponentiel sur 24h.

Choisissez votre langage :

Verify X-ZyndPay-Signature
import crypto from 'node:crypto';
import express from 'express';

const app = express();
const SECRET = process.env.ZYNDPAY_WEBHOOK_SECRET!;

// Mount raw-body parser so we can hash the exact bytes ZyndPay signed.
app.post('/webhooks/zyndpay', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.header('x-zyndpay-signature') ?? '';
  const expected = crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');

  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send('invalid signature');
  }

  const event = JSON.parse(req.body.toString('utf8'));
  // handle event.type (e.g. 'payin.succeeded') ...
  res.status(200).send('ok');
});

Catalogue d'événements

Chaque événement porte un id unique, un type (ex. payin.succeeded), un compteur de tentatives, et un timestamp ISO. Les relances sont idempotentes — le même id est redélivré en cas d'échec.

event.typedescription
payin.succeededCustomer paid. Amount and tx hash are in the payload.
payin.failedUnderlying transfer failed or the paylink expired.
payin.expiredPaylink expired before the customer completed payment.
payout.succeededPayout confirmed on-chain.
payout.failedPayout rejected (insufficient balance, bad address, compliance).
withdrawal.succeededMerchant-initiated withdrawal confirmed on-chain.
withdrawal.failedWithdrawal rejected by the gateway or compliance.
refund.succeededRefund sent back to the original payer.
subscription.renewedRecurring subscription charge succeeded.
dispute.openedA customer or chain flagged a payment. See issue #200 workflow.
Cette page a-t-elle été utile ?