Plateshka

Вебхуки

Подпись, защита от повторов, retry и порядок событий без двойной выдачи заказа.

Вебхук — серверное уведомление о смене состояния счёта. Он нужен для действий, которые нельзя доверять браузеру: выдачи товара, продления доступа, возврата и ручной проверки чарджбэка.

Минимальный обработчик

Подпись проверяется по исходным байтам запроса. В Express маршрут с express.raw должен стоять до общего express.json для этого адреса.

import express from 'express';
import { parseWebhookEvent, WEBHOOK_SIGNATURE_HEADER } from '@plateshka/sdk';

const app = express();

app.post('/webhooks/plateshka', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.header(WEBHOOK_SIGNATURE_HEADER) ?? '';
  const event = parseWebhookEvent(req.body, signature, process.env.PLATESHKA_WEBHOOK_SECRET!);

  if (await events.insertIfAbsent(event.id)) {
    await queue.publish(event);
  }

  res.sendStatus(204);
});

Если подпись неверна, верните 400 и не сохраняйте событие как доверенное.

Как устроена подпись

Заголовок x-plateshka-signature имеет вид t=<секунды>,v1=<hex>. HMAC-SHA256 считается по строке <t>.<rawBody>. SDK проверяет подпись сравнением постоянного времени и по умолчанию допускает расхождение часов до 300 секунд.

Не используйте JSON.stringify(req.body) после разбора JSON: пробелы и порядок ключей изменятся, и подпись перестанет совпадать.

Идемпотентность обработчика

Одна доставка может прийти повторно. event.id и заголовок x-plateshka-event-id сохраняют одно значение для повторов одного события. Поставьте уникальный индекс на этот идентификатор и запускайте бизнес-действие только после успешной первой вставки.

insert into payment_events (event_id, payload)
values ($1, $2)
on conflict (event_id) do nothing;

Сначала зафиксируйте идентификатор события и изменение заказа в одной транзакции, затем отвечайте 2xx. Отправку письма или файла лучше вынести в собственную очередь.

Повторные доставки

Любой ответ 2xx завершает доставку. Сетевой сбой, тайм-аут, редирект или другой статус ставит событие на повтор с увеличивающейся задержкой. Точное расписание и число попыток задаются платформой и могут меняться; обработчик не должен зависеть от интервала.

Подпись пересчитывается для каждой попытки с новой меткой времени. Тело события и event.id остаются теми же.

Порядок событий

Доставка не гарантирует строгий порядок: повтор старого события может прийти после нового. Сортируйте факты по createdAt, не откатывайте заказ в прежнее состояние и при сомнении сверяйте счёт через GET /v1/invoices/{id}.

Что может пойти не так

  • JSON parser изменил тело до проверки подписи.
  • Обработчик вернул 2xx до фиксации события и потерял работу после падения.
  • Повтор invoice.success второй раз выдал товар.
  • Поздний processing перезаписал уже полученный success.

Следующий шаг

Добавьте правила из раздела ошибки и повторные запросы, затем проверьте формат событий API.

На этой странице