Протокол
Вебхуки
При каждом изменении статуса на notify_url (или webhook сайта) уходит POST:
{
"webhook_id": "9127-…",
"event": "completed",
"id": "pay_ab12cd",
"ext_id": "order_8841",
"amount": "10000",
"matched_amount": "10000",
"from_status": "awaiting_payment",
"status": "completed",
"updated_at": "2026-09-17T12:14:00Z",
"method": "sbp",
"phone_number": "+7919…",
"bank": "Сбербанк",
"holder": "Иван И."
}amount и matched_amount — целые рубли, значения строками. event дублирует status. webhook_id совпадает с X-Idempotency-Key.
| Заголовок | Описание | ||
|---|---|---|---|
X-Api-Key | Ваш vp_live_… | ||
X-Timestamp | Unix-секунды. Проверяйте \ | now − ts\ | < 300 |
X-Signature | HMAC-SHA256(secret, timestamp + "." + body), hex | ||
X-Idempotency-Key | Дедупликация повторов |
Подпись считается от строки timestamp + "." + тело. Секрет — vps_… из кабинета.
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, headers, secret) {
const ts = headers["x-timestamp"];
const sig = headers["x-signature"];
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}Python
import hashlib, hmac, time
def verify(raw: bytes, headers: dict, secret: str) -> bool:
ts = headers.get("X-Timestamp", "")
sig = headers.get("X-Signature", "")
if abs(time.time() - int(ts)) > 300:
return False
expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)PHP
function verifyWebhook(string $raw, array $headers, string $secret): bool {
$ts = $headers['X-Timestamp'] ?? '';
$sig = $headers['X-Signature'] ?? '';
if (abs(time() - (int)$ts) > 300) return false;
$expected = hash_hmac('sha256', $ts . '.' . $raw, $secret);
return hash_equals($expected, strtolower($sig));
}Если ваш сервер вернул не 2xx, доставка повторяется из кабинета кнопкой «Повторить». Отвечайте 2xx и опирайтесь на X-Idempotency-Key.
В кабинете: URL на каждый сайт, кнопка «Проверить», журнал доставок.