Перейти к основному содержимому

Проверка подписи вебхука

Уведомление об оплате приходит на адрес, который знает только ваш магазин и наш сервис. Но адрес может утечь — из логов, из настроек, из истории разработки. Чтобы отличить настоящее уведомление от подделки, каждый запрос подписывается секретом вебхука.

Проверяйте подпись до того, как выдать товар или изменить статус заказа.

Заголовки подписи

ЗаголовокЗначение
X-TimestampВремя отправки, unix-время в секундах
X-SignatureПрефикс sha256= и HMAC-SHA256 в шестнадцатеричном виде

Подписывается строка из времени отправки, точки и тела запроса:

1756901234.{"wallet":{...},"project":{...},"invoice":{...},"payment":{...}}

Ключ подписи — поле «Секрет вебхука» в настройках проекта. Его можно перевыпустить там же, если значение скомпрометировано.

Порядок проверки

  1. Взять тело запроса в сыром виде, до разбора JSON.
  2. Склеить значение X-Timestamp, точку и тело.
  3. Посчитать HMAC-SHA256 с секретом вебхука.
  4. Сравнить результат с X-Signature сравнением за постоянное время.
  5. Отклонить запрос, если X-Timestamp сильно расходится с текущим временем.

Примеры реализации

<?php
$secret = 'webhook secret from your project settings';

$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';

// Guards against a replayed request
if (abs(time() - (int)$timestamp) > 300) {
http_response_code(400);
exit;
}

$expected = 'sha256='.hash_hmac('sha256', $timestamp.'.'.$body, $secret);

// hash_equals compares in constant time
if (!hash_equals($expected, $signature)) {
http_response_code(403);
exit;
}

$data = json_decode($body, true);

// Handle the order

http_response_code(200);

На что обратить внимание

Подписывается сырое тело. Если разобрать JSON и собрать заново, изменится порядок ключей и расстановка пробелов — подпись не сойдется. Считайте HMAC до разбора.

У каждой повторной доставки своя подпись. Время отправки при повторе новое, значит и подпись другая. Не сохраняйте подпись для сравнения с будущими запросами.

Секрет можно перевыпустить. Сразу после перевыпуска старый секрет перестает работать, поэтому меняйте значение в магазине одновременно.

Проверка подписи не отменяет идемпотентности. Подпись подтверждает подлинность запроса, но не то, что вы видите его впервые. Повторная доставка придет с валидной подписью — сверяйтесь по invoice.id.