Tapaya
PřehledWebhookyOvěření a testování

Ověření a testování

Ověřujte původní tělo požadavku, zpracovávejte duplicity a bezpečně testujte příjemce.

Před použitím payloadu ověřte každý webhook a poté odešlete test, abyste si potvrdili, že příjemce funguje od začátku do konce.

Než začnete

  • Uložená cílová adresa a její podpisový klíč twhsec_.... K ověřování webhooků nepoužívejte Server Secret Token.
  • Routa příjemce, která dokáže načíst původní tělo požadavku dřív, než ho změní jakýkoli JSON parser.
  • Úložiště s unikátním omezením na id události, aby byly duplicity bezpečné.

Přijetí webhooku

Načtěte původní tělo

Tapaya posílá JSON požadavek POST s těmito hlavičkami. Tělo ponechte jako původní bajty; rozparsované a znovu serializované tělo už podpisu neodpovídá.

Content-Type: application/json
X-Tapaya-Event-Id: 019a0000-0000-7000-8000-000000000001
X-Tapaya-Signature: t=1793361600,v1=<lowercase hexadecimal HMAC-SHA256>
  • V Express použijte pro tuto routu express.raw({ type: "application/json" }) před globálním JSON parserem.

Ověřte podpis

Vypočítejte HMAC-SHA256 z časového razítka, tečky a původního těla a porovnejte ho s v1 v konstantním čase.

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"));
}
  • Jako klíč v UTF-8 použijte celý řetězec twhsec_.... Klíč nedekódujte a neodstraňujte jeho prefix.
  • Odmítněte časová razítka vzdálená od vašich hodin více než pět minut a udržujte hodiny synchronizované.
  • Během obměny podpisového klíče nejprve zkuste nový klíč, poté starý, dokud se fronta nevyprázdní.

Uložte a potvrďte

Událost potvrďte až po jejím bezpečném uložení, aby chyba na vaší straně vyvolala opakovaný pokus.

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);
  },
);
  • Pro událost, kterou už máte uloženou, vraťte 2xx. Opakované pokusy zachovávají stejné id i tělo, ale mají nové časové razítko a podpis.
  • Pokud novou událost nelze uložit, vraťte odpověď mimo 2xx, aby se doručení opakovalo.
  • Uložené události zpracovávejte z vlastní fronty. Pořadí doručení není zaručené, proto starší snímky porovnávejte s aktuálním stavem.

Odešlete testovací webhook

Testovací akce v Platform odešle jeden podepsaný požadavek webhook.test na uloženou URL. Funguje i tehdy, když jsou webhooky vypnuté nebo typ není ve vašem odběru, a nevytváří žádnou platbu, vratku ani obchodníka.

  1. Otevřete API Keys své organizace a uložte cílovou adresu.
  2. Použijte testovací akci webhooku.
  3. Zkontrolujte výsledek, dobu trvání a odeslaný JSON, které Platform zobrazí.
{
  "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."
  }
}

Lokální a automatizační příjemci

Když váš produkční server ještě není připravený, nasměrujte webhook na vývojový počítač nebo no-code nástroj.

Řešení chyb

Chybí vám něco na této stránce?Navrhněte funkci. Připojíme tuto stránku, aby tým viděl souvislosti.