Перевірка й тестування
Перевіряйте початкове тіло запиту, обробляйте дублікати та безпечно тестуйте свій обробник.
Перевіряйте кожен вебхук перед використанням його 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 за допомогою порівняння зі сталим часом виконання.
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, спочатку перевіряйте новий ключ, потім старий, доки черга не спорожніє.
Збережіть і підтвердьте
Приймайте подію лише після того, як її надійно збережено, щоб збій на вашому боці запускав повторну спробу.
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-адресу. Вона працює, навіть якщо вебхуки вимкнено або типу немає у вашій підписці, і не створює платежу, повернення коштів або мерчанта.
- Відкрийте сторінку API Keys своєї організації та збережіть цільову адресу.
- Скористайтеся тестовою дією вебхука.
- Перевірте результат, тривалість і надісланий 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-інструмент, поки ваш робочий сервер не готовий.
Обробка помилок
Типи подій
Виберіть зміни платежів, повернень коштів, мерчантів та онбордингу, потрібні вашій інтеграції.
Огляд
Приймайте платежі карткою всюди, де ваші мерчанти зустрічаються з клієнтами, від каси в магазині та столика в ресторані до зарядної станції для електромобілів, на телефонах і планшетах, якими вони вже користуються.