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
- Prihlaste se do aplikace
- Prejdete do Nastaveni projektu → API & Integrace
- V sekci Webhook zadejte URL vaseho endpointu
- 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:
| Pokus | Zpozdeni |
|---|---|
| 1 | okamzite |
| 2 | 1 minuta |
| 3 | 5 minut |
| 4 | 30 minut |
| 5 | 2 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:
- webhook.site - Ziskate docasnou URL pro testovani
- ngrok - Tunnelování lokalniho serveru
- Postman - Simulace webhook pozadavku