CCedarAPI v1 · стабильная
Руководства / Приём вебхуков

Приём вебхуков

Вебхук - это наш POST-запрос на ваш адрес в момент, когда что-то произошло: заказ создан, оплачен, собран, отменён. Опрашивать /v1/orders по таймеру при этом не нужно.

Как включить

  1. В кабинете укажите адрес приёмника. Только https, только порт 443, самоподписанный сертификат мы не примем.
  2. Отметьте события, которые вам нужны. Подписка на всё подряд превращает журнал в шум.
  3. Скопируйте секрет подписи. Он показывается один раз и меняется отдельной кнопкой.
  4. Отправьте тестовое событие из кабинета и убедитесь, что приёмник ответил 2xx.

Что приходит

http
POST /hooks/cedar HTTP/1.1
Content-Type: application/json
X-Event-Id: 0f1c8ad4-5b21-4f77-9a30-2c6e1b884d10
X-Event-Type: order.paid
X-Event-Time: 2026-08-16T11:20:41Z
X-Signature: sha256=9c1f0b...

{
  "id": "0f1c8ad4-5b21-4f77-9a30-2c6e1b884d10",
  "type": "order.paid",
  "order": {
    "id": "ORD-20481",
    "total": 387000,
    "items": [ { "sku": "TRM-1180", "qty": 3 } ]
  }
}

Тело всегда JSON в UTF-8, время всегда UTC. Полей в объекте order со временем становится больше, поэтому разбирайте его по именам и не падайте на незнакомом ключе.

Проверка подписи

Подпись считается как HMAC SHA-256 от сырого тела запроса. Считать её по разобранному и заново собранному JSON нельзя: порядок ключей изменится, и подпись не сойдётся.

javascript
import crypto from 'node:crypto';

function valid(rawBody, header, secret) {
  const mine = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const sent = String(header || '').replace('sha256=', '');
  const a = Buffer.from(mine, 'utf8');
  const b = Buffer.from(sent, 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Сравнивайте за постоянное времяОбычное сравнение строк выходит из цикла на первом несовпавшем символе, и по времени ответа подпись подбирается побайтно. timingSafeEqual и его аналоги существуют ровно для этого.

Повторы и идемпотентность

Мы ждём ответ 5 секунд. Любой ответ 2xx считаем принятым, всё остальное - неудачей и повторяем доставку шесть раз: через минуту, 5 минут, 15 минут, час, 6 часов и сутки. После последней попытки подписка ставится на паузу, а в кабинет приходит уведомление.

Одно событие может прийти дваждыПри повторе X-Event-Id тот же самый. Храните принятые идентификаторы 72 часа и отвечайте на повтор 2xx, ничего не делая: сеть рвётся чаще, чем кажется, и заказ, собранный дважды, стоит дороже лишней таблицы.

События

СобытиеКогда приходит
order.createdЗаказ создан на витрине, оплата ещё не подтверждена
order.paidОплата подтверждена, заказ можно собирать
order.assembledСборка завершена, ждём передачу в доставку
order.shippedЗаказ передан в доставку, есть трек-номер
order.canceledОтмена целиком или последней позиции заказа
stock.lowОстаток позиции опустился ниже порога, заданного в кабинете
price.rejectedЦена не принята витриной: чаще всего ниже минимальной

Отладка

Журнал доставок в кабинете хранит 30 дней: по каждому событию видно тело, заголовки, код ответа и время. Повторить доставку можно кнопкой - это нормальный способ починить приёмник после релиза, не дожидаясь следующего заказа.

Если приёмник отвечает 2xx, но событие потерялось у вас, смотрите X-Event-Id в своих журналах: он же лежит в журнале доставок и в ответе метода /v1/hooks/deliveries.

На этой страницеКак включитьЧто приходитПроверка подписиПовторыСобытияОтладка