Vérifier et tester
Vérifiez le corps brut de la requête, gérez les doublons et testez votre récepteur en toute sécurité.
Vérifiez chaque webhook avant d’utiliser son payload, puis envoyez un test pour confirmer que votre récepteur fonctionne de bout en bout.
Avant de commencer
- Une destination enregistrée et son signing secret,
twhsec_.... N’utilisez pas votre Server Secret Token pour vérifier les webhooks. - Une route de réception capable de lire le corps brut de la requête avant qu’un analyseur JSON ne le modifie.
- Un stockage avec une contrainte d’unicité sur l’
idde l’événement, afin que les doublons soient sans risque.
Recevoir un webhook
Lire le corps brut
Tapaya envoie un POST JSON avec ces en-têtes. Conservez le corps sous forme d’octets d’origine ; un corps analysé puis sérialisé à nouveau ne correspond plus à la signature.
Content-Type: application/json
X-Tapaya-Event-Id: 019a0000-0000-7000-8000-000000000001
X-Tapaya-Signature: t=1793361600,v1=<lowercase hexadecimal HMAC-SHA256>- Avec Express, utilisez
express.raw({ type: "application/json" })sur cette route, avant un analyseur JSON global.
Vérifier la signature
Calculez un HMAC-SHA256 de l’horodatage, d’un point et du corps brut, puis comparez-le à v1 en temps constant.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyTapayaWebhook(rawBody, signatureHeader, secret, now = Date.now()) {
if (!Buffer.isBuffer(rawBody) || typeof signatureHeader !== "string") {
throw new Error("Missing raw body or signature");
}
if (typeof secret !== "string" || !secret.startsWith("twhsec_")) {
throw new Error("Missing signing secret");
}
const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(signatureHeader);
if (!match) throw new Error("Malformed signature");
const timestamp = Number(match[1]);
if (!Number.isSafeInteger(timestamp) || Math.abs(now / 1000 - timestamp) > 300) {
throw new Error("Expired signature");
}
const expected = createHmac("sha256", Buffer.from(secret, "utf8"))
.update(`${match[1]}.`, "utf8")
.update(rawBody)
.digest();
const received = Buffer.from(match[2], "hex");
if (!timingSafeEqual(expected, received)) throw new Error("Invalid signature");
return JSON.parse(rawBody.toString("utf8"));
}- Utilisez le secret
twhsec_...complet comme clé UTF-8. Ne le décodez pas et ne retirez pas son préfixe. - Rejetez les horodatages qui s’écartent de plus de cinq minutes de votre horloge, et maintenez l’horloge synchronisée.
- Pendant le renouvellement du signing secret, essayez d’abord le nouveau secret, puis l’ancien, jusqu’à ce que la file d’attente soit vidée.
Stocker et accuser réception
N’acceptez l’événement qu’une fois qu’il est stocké de manière sûre, afin qu’un échec de votre côté déclenche une nouvelle tentative.
import express from "express";
import { verifyTapayaWebhook } from "./verify-tapaya-webhook.mjs";
import { saveEventOnce } from "./events.mjs"; // your store, unique on event id
const app = express();
app.post(
"/webhooks/tapaya",
express.raw({ type: "application/json" }),
async (req, res) => {
let event;
try {
event = verifyTapayaWebhook(
req.body,
req.get("X-Tapaya-Signature"),
process.env.TAPAYA_WEBHOOK_SECRET,
);
} catch {
return res.sendStatus(400);
}
if (event.organizationId !== process.env.TAPAYA_ORGANIZATION_ID) {
return res.sendStatus(400);
}
await saveEventOnce(event); // skips an id it already stored
res.sendStatus(204);
},
);- Renvoyez
2xxpour un événement que vous avez déjà stocké. Les nouvelles tentatives conservent le mêmeidet le même corps, avec un nouvel horodatage et une nouvelle signature. - Renvoyez une réponse autre que
2xxlorsque vous ne pouvez pas stocker un nouvel événement, afin que la livraison soit retentée. - Traitez les événements stockés depuis votre propre file d’attente. L’ordre de livraison n’est pas garanti, effectuez donc le rapprochement des instantanés plus anciens avec l’état actuel.
Envoyer un webhook de test
L’action de test de la Platform envoie une requête webhook.test signée à l’URL enregistrée. Elle fonctionne même si les webhooks sont désactivés ou si le type ne figure pas dans votre abonnement, et elle ne crée aucun paiement, remboursement ou commerçant.
- Ouvrez API Keys pour votre organisation et enregistrez la destination.
- Utilisez l’action de test de webhook.
- Vérifiez le résultat, la durée et le JSON envoyé affichés dans la Platform.
{
"id": "019a0000-0000-7000-8000-000000000010",
"type": "webhook.test",
"apiVersion": "1",
"organizationId": "019a0000-0000-7000-8000-000000000002",
"merchantId": null,
"createdAt": "2026-10-30T12:00:00.000000Z",
"test": true,
"data": {
"message": "This is a test webhook from Tapaya. No payment was created."
}
}Récepteurs locaux et d’automatisation
Faites pointer un webhook vers une machine de développement ou un outil sans code lorsque votre serveur de production n’est pas prêt.
Gérer les erreurs
Types d’événements
Choisissez les changements de paiement, de remboursement, de commerçant et d’onboarding nécessaires à votre intégration.
Présentation
Acceptez les paiements par carte partout où vos commerçants rencontrent leurs clients, du comptoir de la boutique à la table du restaurant et à la borne de recharge pour véhicules électriques, sur les téléphones et tablettes qu’ils utilisent déjà.