Гайд

Kaspi вебхуки: событие «оплачено» автоматически

2 мин чтения

У Kaspi нет вебхуков — приложение не умеет уведомлять ваш сервер об оплате. Опрашивать статус в цикле дорого и медленно. AgentPay добавляет подписанные вебхуки поверх Kaspi: как только клиент платит, на ваш endpoint прилетает событие invoice.paid. Ниже — формат, проверка подписи и доставка.

Подключение

Добавьте endpoint в настройках вебхуков. При создании вы получите секрет whsec_* — он показывается один раз, храните в .env. На каждое событие AgentPay шлёт POST на ваш URL.

Формат запроса

POST /your-webhook-handler
Content-Type: application/json
X-AgentPay-Signature: t=1735689600,v1=5257a86...
X-AgentPay-Event-Type: invoice.paid
X-AgentPay-Event-Id: 8e9d2b4a-1f5c-4a3d-9b8a-c7e1f0d3a5b6

Тело — объект события:

{
  "id": "evt_...",
  "object": "event",
  "type": "invoice.paid",
  "resource_type": "invoice",
  "resource_id": "8e9d2b4a-1f5c-4a3d-9b8a-c7e1f0d3a5b6",
  "mode": "live",
  "data": { "kaspiStatus": "Confirmed", "kaspiInvoiceId": "1234567" },
  "created_at": "2026-05-12T18:35:18.000Z"
}

Проверка подписи

Подпись — HMAC-SHA256 от строки <timestamp>.<raw_body> с вашим whsec_* в качестве ключа. Сравнивайте constant-time и отвергайте запросы старше 5 минут (защита от повторов):

import crypto from 'node:crypto';

function verifyAgentPayWebhook(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(',').map((p) => p.trim().split('=', 2)),
  );
  const t = Number(parts.t);
  const sig = parts.v1;
  if (!t || !sig) return false;
  if (Math.abs(Date.now() / 1000 - t) > 300) return false; // 5 мин

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(sig, 'hex'),
  );
}

Проверяйте подпись по сырому телу запроса (raw body), до JSON-парсинга. Любая перекодировка тела сломает HMAC.

Доставка и ретраи

  • Успех — это HTTP 2xx; таймаут 15 секунд.
  • Повторы по графику: 1 мин → 5 мин → 30 мин → 2 ч → 12 ч → 1 день → 2 дня → dead-letter (8 попыток, ~3 дня).
  • Доставка at-least-once — приёмник обязан быть идемпотентным (используйте X-AgentPay-Event-Id).

Какие события приходят

Полный жизненный цикл счёта и подписки: invoice.created, invoice.sent, invoice.paid, invoice.expired, invoice.cancelled, invoice.refunded, а также subscription.* и cashier.*. Подписаться на конкретный тип можно в настройках endpoint.

Локальная отладка

Чтобы не поднимать публичный URL, слушайте события через CLI — он форвардит их на localhost:

agentpay listen --forward-to http://localhost:3000/webhooks/agentpay

Подключите приём оплаты через Kaspi за 5 минут. 0% комиссии, деньги напрямую на ваш счёт.

Создать аккаунт

Частые вопросы

Есть ли у Kaspi собственные вебхуки?

Нет. Kaspi не уведомляет сторонние серверы об оплате. Вебхуки добавляет AgentPay поверх вашего кассира: событие invoice.paid приходит автоматически, опрашивать статус не нужно.

Как проверить, что вебхук действительно от AgentPay?

По заголовку X-AgentPay-Signature: это HMAC-SHA256 от строки «timestamp.raw_body» с вашим секретом whsec_*. Сравнивайте constant-time и отклоняйте запросы старше 5 минут.

Что будет, если мой сервер недоступен?

AgentPay повторит доставку по нарастающему графику до 8 раз в течение ~3 дней, затем отправит событие в dead-letter. Доставка at-least-once, поэтому обработчик должен быть идемпотентным.

Читайте также