Webhooks -- Integrationsleitfaden (Empfänger-Seite)
Dieses Dokument beschreibt, wie ein externes System (ERP, n8n, Eigenentwicklung) die Webhooks empfängt und verarbeitet.
HTTP-Request Format
Jeder Webhook wird als HTTP POST gesendet:
wide760Authentifizierung
Neben der HMAC-Signatur (die immer mitgesendet wird) kann pro Endpunkt eine zusätzliche Authentifizierung konfiguriert werden. Das Zielsystem erhält dann einen entsprechenden Header:
Methode | Header | Beispiel |
|---|---|---|
Keine | – | Nur |
Basic Auth |
|
|
Bearer Token |
|
|
Custom Header (API Key) |
|
|
Empfangenen Auth-Header prüfen
phpwide760jswide760Die Authentifizierung ersetzt nicht die Signatur-Verifikation — beide sollten geprüft werden. Die Signatur stellt sicher, dass der Payload nicht manipuliert wurde, die Authentifizierung schützt den Endpunkt vor unbefugtem Zugriff.
Signatur verifizieren
Jeder Request wird mit HMAC-SHA256 signiert. Die Signatur steht im Header X-Shop-Signature im Format:
Verifikations-Algorithmus
Extrahiere
timestampundsignatureaus dem HeaderBaue den signierten String:
{timestamp}.{request_body}Berechne HMAC-SHA256 mit dem Endpunkt-Secret
Vergleiche die berechnete Signatur mit
v1aus dem Header
Beispielcode (PHP)
phpwide760 300) { return false; } $expectedSignature = hash_hmac('sha256', $timestamp . '.' . $payload, $secret); return hash_equals($expectedSignature, $signature); } // Verwendung $payload = file_get_contents('php://input'); $signatureHeader = $_SERVER['HTTP_X_SHOP_SIGNATURE'] ?? ''; $secret = 'dein-endpunkt-secret'; // aus der Webhook-Konfiguration if (!verifyWebhookSignature($payload, $signatureHeader, $secret)) { http_response_code(401); exit('Invalid signature'); } $data = json_decode($payload, true); // Webhook verarbeiten...]]>Beispielcode (Node.js)
jswide760 { const [key, value] = part.split('=', 2); parts[key] = value; }); const timestamp = parts.t || ''; const signature = parts.v1 || ''; // Replay-Schutz if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) { return false; } const expected = crypto .createHmac('sha256', secret) .update(timestamp + '.' + payload) .digest('hex'); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature)); }]]>Beispielcode (Python)
pywide760 bool: parts = dict(p.split('=', 1) for p in signature_header.split(',')) timestamp = parts.get('t', '') signature = parts.get('v1', '') # Replay-Schutz if abs(time.time() - int(timestamp)) > 300: return False expected = hmac.new( secret.encode(), f"{timestamp}.{payload}".encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature)]]>Idempotency
Jeder Webhook-Versand hat einen eindeutigen X-Idempotency-Key Header. Da Webhooks bei Fehlern wiederholt werden, kann derselbe Event mehrfach ankommen.
Empfehlung: Speichere verarbeitete Idempotency-Keys und überspringe Duplikate:
phpwide760exists('processed_webhooks', ['key' => $idempotencyKey])) { http_response_code(200); // Erfolgreich, aber bereits verarbeitet exit; } // Webhook verarbeiten... $db->insert('processed_webhooks', ['key' => $idempotencyKey, 'processed_at' => now()]);]]>Test-Dispatches erkennen
Manuell ausgelöste Events (über den Test-Bereich im Backend) enthalten das Feld "_test": true im Payload:
Das Zielsystem kann dieses Feld prüfen um Test-Aufrufe anders zu behandeln (z.B. nicht in Produktionsdaten übernehmen).
Antwort-Verhalten
HTTP-Statuscode | Interpretation |
|---|---|
2xx (200, 201, 204) | Erfolgreich zugestellt |
3xx | Wird als Fehler gewertet (kein Redirect-Follow) |
4xx | Fehler, wird wiederholt |
5xx | Fehler, wird wiederholt |
Timeout (> 30s) | Fehler, wird wiederholt |
Wichtig: Antworte mit HTTP 200 so schnell wie möglich. Verarbeite die Daten asynchron, wenn die Verarbeitung länger dauert.
Retry-Verhalten
Bei einem Fehler wird der Webhook wiederholt:
Versuch | Wartezeit | Zeitpunkt (ab erstem Versuch) |
|---|---|---|
2 | 1 Minute | nach 1 Min |
3 | 5 Minuten | nach 6 Min |
4 | 15 Minuten | nach 21 Min |
5 | 1 Stunde | nach 1h 21min |
Nach 5 fehlgeschlagenen Versuchen wird der Webhook als failed markiert.
Nach 10 aufeinanderfolgenden Fehlern (über mehrere Events hinweg) wird der Endpunkt automatisch deaktiviert (Circuit Breaker). Der Shop-Administrator wird per notification.webhook_disabled-Event an andere Endpunkte benachrichtigt.
Event-Routing
Der Event-Name steht im Header X-Shop-Event und im Payload-Feld event. Verwende diesen um zu entscheiden, wie die Daten verarbeitet werden:
n8n Workflow erstellen
Webhook-Node einrichten
Erstelle einen neuen Workflow in n8n
Füge einen Webhook-Node hinzu
Setze die Methode auf POST
Kopiere die generierte URL und trage sie im Shop-Backend als Endpunkt-URL ein
Setze den Pfad z.B. auf
/shop-webhook
Signatur-Verifikation in n8n
Füge nach dem Webhook-Node einen Code-Node hinzu:
jswide760 { const [k, v] = p.split('=', 2); parts[k] = v; }); const expected = crypto .createHmac('sha256', secret) .update(parts.t + '.' + body) .digest('hex'); if (expected !== parts.v1) { throw new Error('Invalid webhook signature'); } return $input.all();]]>Event-basiertes Routing in n8n
Füge einen Switch-Node hinzu, der auf {{ $json.event }} prüft:
order.created-> Bestellung in ERP anlegenorder.line_item.created-> Position wurde freigegebendraft_order.created-> Bestellung vormerken (wartet auf Zahlung/Freigabe)
Checkliste für die Integration
[ ] HTTPS-Endpunkt eingerichtet
[ ] Signatur-Verifikation implementiert
[ ] Idempotency-Prüfung eingebaut
[ ] Schnelle Antwort (< 5 Sekunden, idealerweise < 1 Sekunde)
[ ] Asynchrone Verarbeitung für aufwendige Operationen
[ ] Unbekannte Events werden ignoriert (nicht als Fehler gewertet)
[ ]
_test-Flag wird geprüft um Test-Dispatches zu erkennen[ ] Logging für Debugging eingerichtet
[ ] Test-Ping im Backend erfolgreich gesendet