Tapaya

Перевірка й тестування

Перевіряйте початкове тіло запиту, обробляйте дублікати та безпечно тестуйте свій обробник.

Перевіряйте кожен вебхук перед використанням його payload, а потім надішліть тест, щоб переконатися, що ваш обробник працює від початку до кінця.

Перш ніж почати

  • Збережена цільова адреса та її signing secret (ключ підпису) twhsec_.... Не використовуйте свій Server Secret Token для перевірки вебхуків.
  • Маршрут обробника, який може зчитати початкове тіло запиту, перш ніж будь-який парсер JSON змінить його.
  • Сховище з обмеженням унікальності для id події, щоб дублікати були безпечними.

Отримайте вебхук

Зчитайте початкове тіло

Tapaya надсилає JSON POST із такими заголовками. Зберігайте тіло як початкові байти; розібране й повторно серіалізоване тіло вже не відповідає підпису.

Content-Type: application/json
X-Tapaya-Event-Id: 019a0000-0000-7000-8000-000000000001
X-Tapaya-Signature: t=1793361600,v1=<lowercase hexadecimal HMAC-SHA256>
  • В Express використовуйте для цього маршруту express.raw({ type: "application/json" }) перед глобальним парсером JSON.

Перевірте підпис

Обчисліть HMAC-SHA256 для часової позначки, крапки та початкового тіла, а потім порівняйте результат із v1 за допомогою порівняння зі сталим часом виконання.

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"));
}
  • Використовуйте повний ключ twhsec_... як ключ UTF-8. Не декодуйте його та не видаляйте префікс.
  • Відхиляйте часові позначки, віддалені від вашого годинника більш ніж на п'ять хвилин, і підтримуйте синхронізацію годинника.
  • Поки ви виконуєте ротацію signing secret, спочатку перевіряйте новий ключ, потім старий, доки черга не спорожніє.

Збережіть і підтвердьте

Приймайте подію лише після того, як її надійно збережено, щоб збій на вашому боці запускав повторну спробу.

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);
  },
);
  • Повертайте 2xx для події, яку ви вже зберегли. Повторні спроби зберігають той самий id і тіло, але мають нові часову позначку та підпис.
  • Повертайте відповідь поза 2xx, якщо не можете зберегти нову подію, щоб доставка повторила спробу.
  • Обробляйте збережені події з власної черги. Порядок доставки не гарантовано, тому звіряйте старіші знімки з поточним станом.

Надішліть тестовий вебхук

Тестова дія в Platform надсилає один підписаний запит webhook.test на збережену URL-адресу. Вона працює, навіть якщо вебхуки вимкнено або типу немає у вашій підписці, і не створює платежу, повернення коштів або мерчанта.

  1. Відкрийте сторінку API Keys своєї організації та збережіть цільову адресу.
  2. Скористайтеся тестовою дією вебхука.
  3. Перевірте результат, тривалість і надісланий JSON, які показує 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."
  }
}

Локальні обробники й обробники автоматизації

Спрямуйте вебхук на машину для розробки або на no-code-інструмент, поки ваш робочий сервер не готовий.

Обробка помилок

Чогось бракує на цій сторінці?Надішліть запит на функцію. Ми додамо цю сторінку, щоб команда бачила контекст.