Vai al contenuto

Consegna firmata

Verifica prima. Elimina i duplicati prima degli effetti.

I webhook sono notifiche limitate all’applicazione, non una fonte di accesso al tenant. Il destinatario deve autenticare il corpo grezzo, verificarne la validità temporale e salvare atomicamente l’id evento.

Contratto di consegna

ParteContratto
Eventibooking.confirmed, booking.cancelled, booking.reschedule_requested, booking.rescheduled, booking.updated
HeaderX-Fenix-Event-Id, X-Fenix-Delivery-Id, X-Fenix-Timestamp, X-Fenix-Signature e Idempotency-Key
Nomi di compatibilitàX-Fenix-*, data-fenix-* e FENIX_* sono nomi di protocollo legacy stabili. Mantienili invariati nelle integrazioni esistenti.
Firmav1=<HMAC-SHA256(timestamp + "." + raw_body)> usando il segreto whsec_ monouso
Validità temporaleAccetta solo una finestra timestamp di cinque minuti e rifiuta version/type/reference non validi.
TentativiCinque tentativi limitati con attese di 1m, 5m, 30m e 2h. 2xx completa; 3xx viene rifiutato e non seguito.

Flusso minimo di verifica

js
import { createHmac, timingSafeEqual } from 'node:crypto';

const rawBody = await request.text();
const timestamp = request.headers.get('x-fenix-timestamp') || '';
const signature = request.headers.get('x-fenix-signature') || '';
const ageSeconds = Math.abs(Date.now() - Number(timestamp) * 1000) / 1000;
const expected = 'v1=' + createHmac('sha256', process.env.FENIX_WEBHOOK_SECRET)
  .update(timestamp + '.' + rawBody)
  .digest('hex');
const received = Buffer.from(signature);
const candidate = Buffer.from(expected);

if (!Number.isInteger(Number(timestamp)) || ageSeconds > 300 ||
  received.length !== candidate.length || !timingSafeEqual(received, candidate)) {
  return new Response(null, { status: 400 });
}

const event = JSON.parse(rawBody);
if (await events.has(event.id)) return new Response(null, { status: 204 });

await database.transaction(async () => {
  await events.insert({ id: event.id });
  await applyBookingEvent(event);
});

return new Response(null, { status: 204 });

Politica di elaborazione del destinatario

  • Verifica la firma sul corpo grezzo non modificato prima di elaborare JSON. Convalida formato v1, finestra timestamp e schema evento atteso.
  • Salva atomicamente X-Fenix-Event-Id prima degli effetti. X-Fenix-Delivery-Id identifica un tentativo; Idempotency-Key ripete l’id evento per la deduplicazione.
  • Restituisci 2xx solo quando l’evento è gestito in sicurezza. I redirect sono rifiutati; gli errori ricevono al massimo cinque tentativi con attese di 1m, 5m, 30m e 2h.
  • Tratta i payload webhook come dati operativi. Escludili dalla telemetria browser e oscurali in log, ticket e rapporti.

Sicurezza dell’endpoint

  • Registra solo un hostname HTTPS pubblico. URL con credenziali, frammenti, query string o porte personalizzate vengono rifiutati.
  • La piattaforma risolve e convalida il DNS prima della configurazione e a ogni consegna; blocca destinazioni loopback, private, link-local, riservate e miste.
  • Il segreto di firma whsec_ monouso è mostrato al titolare solo alla creazione o rotazione. Conservalo in un secret manager server, ruotalo dopo un’esposizione e non inviarlo al supporto.
  • Sospendi l’endpoint durante un incidente. Ripeti solo consegne fallite o saltate quando il destinatario può deduplicare l’id evento originale.
Webhook | Sviluppatori Feny