Ваш сервер получает событие.
Dedyx доставляет события PAID с подписью на обязательный HTTPS-адрес. Сначала проверьте, затем выполните заказ.
Настройка приёмника
Ограничение частоты: 10 запросов / 60 секунд (общий счётчик настроек вебхука · на мерчанта). Общие счётчики и дополнительные лимиты по IP.
curl -X PUT https://api.dedyx.com/api/v1/merchant/webhook \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"url":"https://your-company.example/webhooks/dedyx"}'Используйте публичный HTTPS URL на порту 443 без встроенных данных доступа и фрагмента. Приватные сети, loopback, адреса метаданных, HTTP и перенаправления отклоняются. Общий список разрешённых URL не требуется.
Первый ответ содержит индивидуальный для мерчанта signing_key и key_id. Сохраните полную whsec_... строку. Следующие PUT обновляют URL, сохраняя текущий ключ.
Ограничение частоты: 120 запросов / 60 секунд (общие чтения · на мерчанта). Общие счётчики и дополнительные лимиты по IP.
Получайте настройки без раскрытия секрета подписи.
Событие платежа
{
"event_id": "evt_example",
"payment_id": "pay_0123456789abcdef0123456789abcdef",
"status": "PAID",
"expected_amount": "49.00",
"transaction_id": "9f3c7a2e8b104d65c0a9f1726e83b4d5a7610c2f94e8b36d5a07c1e2694f83bd",
"transaction_event_index": 0,
"description": "Order #1024"
}Сопоставляйте платёж со своими заказами. Несколько событий USDT Transfer могут иметь один ID транзакции; transaction_event_index указывает позицию в полном логе receipt.
Проверка подписи
| Заголовок | Назначение |
|---|---|
X-Dedyx-Timestamp | Unix-секунды; обновляются при каждой попытке. |
X-Dedyx-Signature | v1= + hex HMAC-SHA256 от timestamp + "." + исходного тела. |
X-Dedyx-Event-ID | Постоянный ID события при повторах. |
X-Dedyx-Key-ID | Выбирает сохранённый ключ подписи. |
import hashlib
import hmac
import time
def verify_webhook(raw_body, timestamp, signature, signing_key):
if not timestamp.isdigit() or len(timestamp) > 12:
return False
if abs(int(time.time()) - int(timestamp)) > 300:
return False
expected = "v1=" + hmac.new(
signing_key.encode(),
timestamp.encode() + b"." + raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)Проверяйте исходные байты запроса до разбора JSON, используя полную строку ключа подписи. Затем проверяйте заказ, сумму, статус и совпадение ID события в заголовке и теле.
Повторы и дубликаты
Только 2xx означает успешную доставку. При потере ответа запрос может повториться. Атомарно сохраняйте event_id вместе с бизнес-действием. Возвращайте 2xx без повторного выполнения уже обработанного заказа. Рабочий пример PostgreSQL.
Обычно доставка повторяется до восьми раз с увеличением задержки. Исчерпанные события доступны для защищённого повтора мерчантом или восстановления оператором. Неудачный вебхук не отменяет платёж. Сверяйте состояние через защищённый details платежа; Проверяйте доставку через GET /api/v1/payments/{payment_id}/webhooks и повторяйте исчерпанное событие через POST /api/v1/merchant/webhook/events/{event_id}/retry. Ожидающие и доставленные события повторять нельзя. Лимит — пять запросов в час на мерчанта; каждый повтор записывается в аудит и сохраняет ID и тело события.
Ротация ключа подписи
Ограничение частоты: 10 запросов / 60 секунд (общий счётчик настроек вебхука · на мерчанта). Общие счётчики и дополнительные лимиты по IP.
Новый секрет показывается один раз. Новые попытки доставки сразу используют новый ID ключа. Сохраняйте предыдущий ключ пять минут для запросов в пути. Эта ротация не меняет API-ключ.
Диагностика и повтор доставки
Читайте события платежа через GET /api/v1/payments/{payment_id}/webhooks. Ответ содержит ограниченный список (до 100 событий): event_id, payment_id, status, attempts, временные метки и error_category. Status: pending, delivered или failed; pending включает попытку в процессе. Callback URL, тело события и секрет подписи не возвращаются. Метод делит лимит чтений мерчанта.
Используйте POST /api/v1/merchant/webhook/events/{event_id}/retry с X-API-Key и без тела для нового цикла исчерпанного недоставленного события. Pending/delivered возвращает 409; чужое или отсутствующее событие — 404. Пять запросов в фиксированном окне 3600 секунд на мерчанта (с первого запроса), включая ответы 404 и 409; 429 содержит Retry-After. Попытки нового цикла сбрасываются в ноль. ID и тело события сохраняются. Доставка использует текущие приёмник и ключ подписи. Аудит и повтор атомарны; новый платёж не создаётся и квота создания не расходуется.
Таймаут может означать, что приёмник обработал событие, но ответ потерялся. Исключайте повтор event_id и повторное выполнение заказа даже после ручного повтора.