Přeskočit na hlavní obsah

Webhooky

Automaticke notifikace o udalostech v rezervacnim systemu.

Co jsou webhooky?

Webhooky umoznuji prijmat okamzite notifikace, kdyz se v systemu neco stane - napr. nova rezervace, zmena statusu, zruseni.

Namisto pravidelneho dotazovani (polling) API, nas server posle HTTP pozadavek na vasi URL.

Nastaveni

  1. Prihlaste se do aplikace
  2. Prejdete do Nastaveni projektuAPI & Integrace
  3. V sekci Webhook zadejte URL vaseho endpointu
  4. Kliknete na Ulozit webhook

Format pozadavku

Webhook posila POST pozadavek s JSON payloadem:

POST /vas-webhook-endpoint HTTP/1.1
Host: vas-server.cz
Content-Type: application/json
X-Webhook-Signature: sha256=...

{
"event": "reservation.created",
"timestamp": "2024-12-15T10:30:00Z",
"projectId": "proj-123",
"data": {
...
}
}

Typy udalosti

reservation.created

Nova rezervace byla vytvorena.

{
"event": "reservation.created",
"timestamp": "2024-12-15T10:30:00Z",
"projectId": "proj-123",
"data": {
"reservation": {
"id": "res-456",
"date": "2024-12-20",
"from": "14:00",
"to": "15:00",
"status": "PENDING",
"guests": 2,
"note": "Prosim stolek u okna",
"customer": {
"firstName": "Jan",
"lastName": "Novak",
"email": "jan@example.com",
"phone": "+420123456789"
},
"services": [
{
"id": "svc-1",
"name": "Strihani vlasu"
}
],
"table": {
"id": "tbl-1",
"name": "Stolek 5"
},
"createdAt": "2024-12-15T10:30:00Z"
}
}
}

reservation.confirmed

Rezervace byla potvrzena.

{
"event": "reservation.confirmed",
"timestamp": "2024-12-15T11:00:00Z",
"projectId": "proj-123",
"data": {
"reservation": {
"id": "res-456",
"status": "CONFIRMED",
"previousStatus": "PENDING",
"confirmedBy": "admin@example.com"
}
}
}

reservation.cancelled

Rezervace byla zrusena.

{
"event": "reservation.cancelled",
"timestamp": "2024-12-15T12:00:00Z",
"projectId": "proj-123",
"data": {
"reservation": {
"id": "res-456",
"status": "CANCELLED",
"previousStatus": "CONFIRMED",
"reason": "Zakaznik zrusil",
"cancelledBy": "customer"
}
}
}

reservation.completed

Rezervace byla dokoncena.

{
"event": "reservation.completed",
"timestamp": "2024-12-20T15:30:00Z",
"projectId": "proj-123",
"data": {
"reservation": {
"id": "res-456",
"status": "COMPLETED"
}
}
}

Overeni podpisu

Kazdy webhook obsahuje podpis v hlavicce X-Webhook-Signature pro overeni autenticity.

Node.js

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

return `sha256=${expectedSignature}` === signature;
}

// Pouziti v Express
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body.toString();

if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}

const event = JSON.parse(payload);

switch (event.event) {
case 'reservation.created':
handleNewReservation(event.data.reservation);
break;
case 'reservation.cancelled':
handleCancellation(event.data.reservation);
break;
}

res.status(200).send('OK');
});

PHP

<?php
function verifyWebhookSignature($payload, $signature, $secret) {
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signature);
}

$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

if (!verifyWebhookSignature($payload, $signature, WEBHOOK_SECRET)) {
http_response_code(401);
exit('Invalid signature');
}

$event = json_decode($payload, true);

switch ($event['event']) {
case 'reservation.created':
handleNewReservation($event['data']['reservation']);
break;
case 'reservation.cancelled':
handleCancellation($event['data']['reservation']);
break;
}

http_response_code(200);
echo 'OK';

Retry logika

Pokud vas endpoint nevrati 2xx odpoved, system zkusi pozadavek opakovat:

PokusZpozdeni
1okamzite
21 minuta
35 minut
430 minut
52 hodiny

Po 5 neuspecnych pokusech je webhook oznacen jako chybny.

Doporuceni

Rychla odpoved

Webhook endpoint by mel odpovedet co nejrychleji (< 5 sekund). Zpracovani provadejte asynchronne.

app.post('/webhook', async (req, res) => {
// Okamzite potvrdte prijem
res.status(200).send('OK');

// Zpracujte asynchronne
processWebhookAsync(req.body);
});

Idempotence

Webhook muze byt dorucen vicekrat. Implementujte idempotentni zpracovani:

const processedEvents = new Set();

function handleWebhook(event) {
const eventKey = `${event.event}-${event.data.reservation.id}-${event.timestamp}`;

if (processedEvents.has(eventKey)) {
console.log('Event already processed:', eventKey);
return;
}

processedEvents.add(eventKey);
// Zpracovat event...
}

Logovani

Logujte vsechny prijate webhooky pro debugovani:

app.post('/webhook', (req, res) => {
console.log('Webhook received:', {
event: req.body.event,
timestamp: req.body.timestamp,
reservationId: req.body.data?.reservation?.id
});

// ...
});

Testovani

Pro testovani webhooků doporucujeme:

  1. webhook.site - Ziskate docasnou URL pro testovani
  2. ngrok - Tunnelování lokalniho serveru
  3. Postman - Simulace webhook pozadavku