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

Интеграция Webhook

Вебхуки позволяют вашим системам получать уведомления в реальном времени при событиях в вашем аккаунте Wink — новые бронирования, отмены, обновления платежей и многое другое. Это руководство проведет вас через процесс настройки и лучшие практики.

Это руководство предназначено для разработчиков, интегрирующих Wink с внешними системами, такими как системы управления недвижимостью (PMS), менеджеры каналов, CRM или пользовательские панели управления.

  1. Вы регистрируете URL вебхука в Wink.
  2. Когда происходит событие (например, новое бронирование), Wink отправляет HTTP POST на ваш URL.
  3. Ваш сервер обрабатывает полезную нагрузку и отвечает 200 OK.
  1. Войдите в свой аккаунт (Extranet, Studio или TripPay — все поддерживают вебхуки).
  2. Перейдите в Applications, затем Webhooks. См. Webhooks.
  3. Нажмите Create webhook.
  4. Введите название (например, “PMS Booking Sync”).
  5. Введите ваш URL вебхука — HTTPS-эндпоинт на вашем сервере.
  6. Выберите события — выберите конкретные события для подписки или оставьте пустым, чтобы получать все события.
  7. Включите переключатель Enabled.
  8. Нажмите Save — ответ покажет ваш секрет подписи один раз; сохраните его сейчас.

Wink публикует сегодня 70 типов событий вебхуков по бронированиям, объектам, аккаунтам (управляющим сущностям) и инвентарю (типы номеров, тарифные планы, основные тарифы, доп. услуги, удобства, каналы продаж, акции). Наиболее распространённые:

КатегорияПримеры
Бронированиеbooking.create, booking.cancelled, booking.refund.partial, booking.refund.full, booking.review.created
Объектproperty.created, property.status.updated, property.policy.updated
Инвентарьroom_type.updated, rate_plan.created, master_rate.updated, special_rate.created, sales_channel.created
Аккаунтmanaging_entity.created, managing_entity.status.updated, managing_entity.manager.added

Полный сгенерированный список — с описанием, кто его получает, и ссылкой на страницу справки по каждому событию — это Каталог событий вебхуков. Страница справки для каждого события (JSON тело, заголовки, политика повторных попыток) находится в Webhooks API.

Просмотреть все типы событий

Каждая доставка — это HTTP POST на ваш URL вебхука с Content-Type: application/json и следующим конвертом:

{
"id": "0198a4f2-6b0e-7c1d-9a3e-2f4b8c6d1e0a",
"type": "booking.create",
"occurredAt": "2026-08-15T09:30:00Z",
"ownerIdentifier": "3c6b1a5d-8e2f-4a0b-9c7d-6e4f0a8b2c51",
"recipientRole": "SUPPLIER",
"schemaVersion": 2,
"object": { "...": "event-specific payload, e.g. BookingWebhookPayload" }
}
  • id — идентификатор события; одинаков для каждого эндпоинта вашего аккаунта, получающего это событие, и для каждой повторной попытки. Используйте его как ключ идемпотентности.
  • type — ключ типа события (также отправляется в заголовке Wink-Event-Type). Ветвите обработку по type и schemaVersion, чтобы распарсить object.
  • object — курируемое резюме ресурса, к которому относится событие (идентификаторы, статус, поля, с которыми вы работаете), плюс links.self — канонический REST URL поставщика полной сущности. Запрашивайте его с помощью своих API-учетных данных, если нужно больше, чем резюме; если вы получаете событие как реселлер или турагент, используйте соответствующий ресурс вашего API для того же идентификатора.

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

ЗаголовокЗначение
Wink-VersionВерсия контракта, 2.0.
Wink-Event-IdТо же, что id в теле — ваш ключ идемпотентности.
Wink-Delivery-IdУникальный для эндпоинта и события; меняется только при повторной доставке.
Wink-Event-TypeТо же, что type в теле.
Wink-Delivery-AttemptНомер попытки доставки, начиная с 1.
Wink-SignatureHMAC-подпись — см. ниже.

Каждый вебхук имеет секрет подписи (whsec_…), который Wink показывает один раз при создании вебхука или смене секрета. Храните его как пароль. Каждая доставка содержит

Wink-Signature: t=1755250200,v1=5d41402abc4b2a76b9719d911017c592…

где t — Unix-временная метка (в секундах), а v1 — строчная hex-строка HMAC-SHA256 от строки t + "." + rawBody, вычисленная с вашим секретом, а rawBody — точные байты тела запроса, как получены — не сериализуйте JSON заново перед проверкой. В течение 24 часов после смены секрета заголовок содержит второе значение v1=, подписанное старым секретом; принимайте доставку, если любой v1 совпадает.

Проверяйте в четыре шага: распарсить t и все v1; пересчитать HMAC по t.rawBody с вашим секретом; сравнить с помощью сравнения с постоянным временем; отклонить, если |now − t| превышает ваш лимит (рекомендуется 5 минут).

// Node.js (Express-стиль; убедитесь, что у вас есть RAW тело, а не распарсенный объект)
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWinkSignature(header, rawBody, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=').map((s) => s.trim())));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return header
.split(',')
.filter((p) => p.trim().startsWith('v1='))
.map((p) => p.trim().slice(3))
.some((v1) => v1.length === expected.length && timingSafeEqual(Buffer.from(v1, 'utf8'), Buffer.from(expected, 'utf8')));
}
// Java
static boolean verify(String header, String rawBody, String secret, long nowSeconds, long toleranceSeconds) throws Exception {
long t = Long.MIN_VALUE;
List<String> signatures = new ArrayList<>();
for (String part : header.split(",")) {
String[] kv = part.trim().split("=", 2);
if (kv[0].equals("t")) t = Long.parseLong(kv[1]);
else if (kv[0].equals("v1")) signatures.add(kv[1]);
}
if (t == Long.MIN_VALUE || Math.abs(nowSeconds - t) > toleranceSeconds) return false;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] expected = HexFormat.of().formatHex(mac.doFinal((t + "." + rawBody).getBytes(StandardCharsets.UTF_8))).getBytes(StandardCharsets.US_ASCII);
return signatures.stream().anyMatch(v1 -> MessageDigest.isEqual(expected, v1.toLowerCase().getBytes(StandardCharsets.US_ASCII)));
}

Поменяйте секрет через портал или с помощью POST /api/managing-entity/{id}/webhook/{webhookId}/rotate-secret; ответ покажет новый секрет один раз, а старый будет действовать 24 часа, пока вы не завершите обновление.

  • Отвечайте любым 2xx в течение 10 секунд для подтверждения. Тяжелую работу выполняйте асинхронно.
  • 5xx, таймаут, 408 или 429 повторяются с экспоненциальной задержкой: через 1 минуту, 5 минут, 30 минут, 2 часа, 6 часов, 12 часов, затем ежедневно — всего 10 попыток примерно за 3 дня — после чего доставка считается мертвой.
  • Любой другой 4xx считается отказом в доставке и не повторяется.
  • Каждое событие, доставка и попытка (статус, фрагмент ответа) видны в разделе Applications > Webhooks и через API (…/webhook/event/grid, …/webhook/delivery/grid). Вы можете повторно доставить любую доставку (POST …/webhook/delivery/{deliveryId}/redeliver, что запускает новую серию попыток), повторно доставить все мертвые доставки вебхука сразу (POST …/webhook/{webhookId}/redeliver-dead), или отменить одну.
  • Доставки хранятся 30 дней.

Отправьте себе синтетическое событие webhook.test из портала или с помощью POST /api/managing-entity/{id}/webhook/{webhookId}/test. Оно подписано и доставляется точно так же, как реальное событие, чтобы вы могли проверить свой эндпоинт, проверку подписи и обработку идемпотентности перед подпиской на живые события.

  • Используйте HTTPS — Wink отправляет полезные нагрузки только на HTTPS-эндпоинты.
  • Отвечайте быстро — Возвращайте 200 OK сразу после получения полезной нагрузки. Тяжелую обработку выполняйте асинхронно.
  • Идемпотентность — Ваш обработчик должен быть идемпотентным; устраняйте дубликаты по Wink-Event-Id. Wink повторяет попытки, если не получает ответ 2xx.
  • Проверяйте источник — Проверяйте заголовок Wink-Signature (см. Проверка подписей) перед обработкой; отклоняйте всё, что не проходит проверку.
  • Логирование — Логируйте каждую полученную полезную нагрузку вебхука. Это значительно облегчает отладку интеграций.

Вы можете отключить вебхук без удаления. Это приостанавливает доставку, чтобы вы могли устранить неполадки, не теряя настройки. Когда будете готовы, включите его снова.

Удаление вебхука навсегда удаляет его. Любая интеграция, зависящая от этого вебхука, перестанет получать уведомления.

  • Каталог событий вебхуков — Все типы событий, сгенерированные из каталога платформы.
  • Справочник Webhooks API — Схемы полезных нагрузок по событиям, заголовки и эндпоинты управления подписками и доставкой.
  • Webhooks — Полная справка по управлению вебхуками.
  • Applications — Управление вашими API-учетными данными.
  • Developers > APIs — Полная документация API.