Вебхуки
Подпись, защита от повторов, 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.