Tapaya
PanoramicaWebhookVerifica e test

Verifica e test

Verifichi il corpo originale della richiesta, gestisca i duplicati e testi il ricevitore in modo sicuro.

Verifichi ogni webhook prima di utilizzarne il payload, quindi invii un test per confermare che il ricevitore funzioni dall'inizio alla fine.

Prima di iniziare

  • Una destinazione salvata e il relativo signing secret (chiave di firma), twhsec_.... Non utilizzi il Server Secret Token per verificare i webhook.
  • Una route del ricevitore in grado di leggere il corpo originale della richiesta prima che un parser JSON lo modifichi.
  • Un archivio con un vincolo di unicità sull'id dell'evento, così che i duplicati siano gestiti in modo sicuro.

Ricevere un webhook

Leggere il corpo originale

Tapaya invia una richiesta POST JSON con questi header. Mantenga il corpo come byte originali; un corpo sottoposto a parsing e serializzato di nuovo non corrisponde più alla firma.

Content-Type: application/json
X-Tapaya-Event-Id: 019a0000-0000-7000-8000-000000000001
X-Tapaya-Signature: t=1793361600,v1=<lowercase hexadecimal HMAC-SHA256>
  • Con Express, utilizzi express.raw({ type: "application/json" }) per questa route, prima di un parser JSON globale.

Controllare la firma

Calcoli HMAC-SHA256 sul timestamp, un punto e il corpo originale, quindi lo confronti con v1 tramite un confronto a tempo costante.

verify-tapaya-webhook.mjs
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"));
}
  • Utilizzi il segreto completo twhsec_... come chiave UTF-8. Non lo decodifichi e non ne rimuova il prefisso.
  • Rifiuti i timestamp che differiscono di oltre cinque minuti dal Suo orologio e mantenga l'orologio sincronizzato.
  • Durante la rotazione del signing secret, provi prima il nuovo segreto, poi quello precedente, finché la coda non si è svuotata.

Memorizzare e confermare

Accetti l'evento solo dopo averlo memorizzato in modo sicuro, così che un errore da parte Sua generi un nuovo tentativo.

server.mjs
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);
  },
);
  • Restituisca 2xx per un evento già memorizzato. I tentativi successivi mantengono lo stesso id e lo stesso corpo, con un timestamp e una firma nuovi.
  • Restituisca una risposta diversa da 2xx quando non può memorizzare un nuovo evento, così che la consegna riprovi.
  • Elabori gli eventi memorizzati dalla Sua coda. L'ordine di consegna non è garantito, quindi riconcili le istantanee meno recenti con lo stato corrente.

Inviare un webhook di test

L'azione di test della Platform invia una richiesta webhook.test firmata all'URL salvato. Funziona anche se i webhook sono disabilitati o il tipo non è incluso nella Sua iscrizione, e non crea alcun pagamento, rimborso o esercente.

  1. Apra API Keys per la Sua organizzazione e salvi la destinazione.
  2. Utilizzi l'azione di test del webhook.
  3. Controlli il risultato, la durata e il JSON inviato mostrati nella 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."
  }
}

Ricevitori locali e di automazione

Indirizzi un webhook verso una macchina di sviluppo o uno strumento no-code quando il Suo server di produzione non è ancora pronto.

Gestire gli errori

Manca qualcosa in questa pagina?Richieda una funzionalità. Alleghiamo questa pagina, così il team vede il contesto.