Вебхуки
Обработка webhook
После каждого завершённого платежа Finik отправляет POST на ваш webhookUrl. Проверьте его, затем обновите заказ.
Пример payload
POST /webhooks/finik
{
"id": "transaction-id-15423_DEBIT", // ID операции в Finik (суффикс _DEBIT — списание средств)
"transactionId": "transaction-id-241234", // ID транзакции платежа — используйте для идемпотентности (дедупликации)
"status": "success", // статус платежа; может прийти в любом регистре — проверяйте регистронезависимо
"amount": 100, // сумма платежа
"fields": { // поля платёжной формы и ваши additionalData
"amount": 100, // сумма, зафиксированная на форме
"paymentId": "payment-id-241234", // ID платежа, который вы передали в POST /v1/payment
"fieldId1": "value1", // значение вашего additionalData-поля с fieldId "fieldId1"
"fieldId2": "value2" // значение вашего additionalData-поля с fieldId "fieldId2"
},
"requestDate": 1737369012345, // время создания запроса (UNIX, мс)
"transactionDate": 1737369012345, // время проведения транзакции (UNIX, мс)
"service": { "id": "VISA" } // приходит, только если у вас подключён приём по картам Visa
}Что делать
- Проверьте подпись — Постройте каноническую строку из входящего запроса (метод, путь, host + все заголовки
x-api-*, query, тело), затем проверьте заголовокsignatureпо публичному ключу Finik для вашей среды. - Обеспечьте идемпотентность — Используйте
transactionId(илиid) для дедупликации. Finik может доставить один webhook несколько раз (доставка «хотя бы раз»). - Отвечайте быстро — Верните
200 OKсразу и выполняйте тяжёлую работу асинхронно.
Повторные попытки доставки
Webhook доставляется по принципу «хотя бы один раз». Если ваш сервер не ответил 200 OK (или ответил с ошибкой), Finik повторяет отправку.
- Первые 5 попыток — каждые 30 секунд.
- Дальше — интервал удваивается (экспоненциальная задержка): начинается с 60 секунд и ограничен сверху 24 часами (86 400 секунд).
| Попытка | Задержка |
|---|---|
1 | 60 сек (1 мин) |
2 | 120 сек (2 мин) |
3 | 240 сек (4 мин) |
4 | 480 сек (8 мин) |
5 | 960 сек (16 мин) |
6 | 1920 сек (32 мин) |
7 | 3840 сек (1 ч 4 мин) |
8 | 7680 сек (2 ч 8 мин) |
9 | 15360 сек (4 ч 16 мин) |
10 | 30720 сек (8 ч 32 мин) |
11 | 61440 сек (17 ч 4 мин) |
12 | 122880 сек → ограничено до 86400 сек (24 ч) |
Достигнув 24 часов, интервал больше не растёт — все последующие попытки выполняются раз в сутки.
Как правильно проверить подпись
Ниже примеры, как правильно проверить подпись.
Установите @mancho.devs/authorizer и node-fetch, затем:
check-signature.ts
import { Signer } from "@mancho.devs/authorizer";
import * as dotenv from "dotenv";
dotenv.config({ path: ".env.development" });
const publicKey = process.env.FINIK_BETA_PUBLIC_KEY;
const requestData = {
body: {
fields: {
amount: 1,
webhook_url: "https://huntpro.kg/finik-callback",
paymentId: "141fc797-0366-480e-8ba7-2982632b2b2c",
success_redirect_url:
"https://api.acquiring.averspay.kg/v1/redirect?paymentId=141fc797-0366-480e-8ba7-2982632b2b2c&clientId=1295102413&status=succeeded",
name: "HuntPro",
qrTransactionId: "aaa76e79-f808-4ac5-b88e-76a9b4055832",
},
amount: 1,
transactionDate: 1781697937466,
clientId: "25c1aabe-fc4a-4968-85d9-90600466e9a5",
id: "320259584_aaa76e79-f808-4ac5-b88e-76a9b4055832_DEBIT",
status: "succeeded",
transactionId: "141fc797-0366-480e-8ba7-2982632b2b2c",
data: {
accountId: "8d4e1d11-e50c-4278-a5e2-4e55de196c6a",
description: "Order 2606",
webhookUrl: "https://huntpro.kg/finik-callback",
name_en: "HuntPro",
},
},
httpMethod: "POST",
headers: {
"Content-Type": "application/json",
Host: "huntpro.kg",
"x-api-timestamp": "1781763261255",
},
path: "/finik-callback",
queryStringParameters: null,
};
const signature = "jS1a8HW4BTIea8iCmoW/EF7FXUA0JSsHe/kSRBfCYdxFwvyrMe0SOfU9pAKzOOO0bIsa9FW+WqAzqxExHjcscmG8k/vwgB+dCMcM04K91vTL6eZAVOE/Hcx2iALNZnG6UVnP/jnUt8MjK+xwBhamwFktxdUovf+cVFZ2io10RAT1pOEIHtZceXbQeg7XlhcPuTAKmOuF1B4gGoksRtGglGwwyvYaa9ml8OPMP3uoCaauYqT0aUKLTNsJdFfO0Q3Dvsy9uyWvuVa5t6adbIm+SHlbz9VGSZ2xrwgETUBVj86dfA3oiAgERLKhQTBSMTxRYlUKhPVHGfR3kjAhD3g4JQ==";
async function checkSignature() {
const isValid = await new Signer(requestData as any).verify(
publicKey!,
signature,
);
console.log("Is valid signature:", isValid);
}
checkSignature();