Zum Hauptinhalt springen
Druckprodukte ab 1 Stück

Webhooks: Bestellupdates automatisch empfangen

11 Min. LesezeitAktualisiert

Mit Webhooks schickt Printen.eu Ihnen eine Nachricht, sobald mit einer Bestellung etwas passiert: wartet auf Guthaben, in Produktion, versandt mit Sendungsverfolgung, zugestellt oder storniert. So müssen Sie die API nicht ständig abfragen. In diesem Artikel fügen Sie einen Endpunkt hinzu, prüfen die Signatur und bauen eine Verarbeitung, die mit Wiederholungen umgehen kann.

In diesem Artikel

Wie funktionieren Webhooks?

Sie geben uns eine https-URL Ihres eigenen Servers. Bei jedem Ereignis senden wir dorthin eine POST-Anfrage mit einer JSON-Nachricht. Ihr Server antwortet mit einem 2xx-Status, und wir wissen, dass die Nachricht angekommen ist.

Vom Ereignis zur Verarbeitung.
  • X-Printen-Event: die Art des Events, zum Beispiel order.shipped.
  • X-Printen-Delivery: eine id dieser Zustellung. Praktisch für Ihre Logs.
  • X-Printen-Timestamp: der Sendezeitpunkt in Sekunden seit 1970 (Unix-Zeit).
  • X-Printen-Signature: die Signatur (v1= gefolgt von einem Hex-Code), mit der Sie prüfen, dass die Nachricht wirklich von uns stammt.

Wie fügen Sie einen Endpunkt hinzu?

Die Registerkarte Webhooks in Ihrem Dashboard.
  1. Registerkarte öffnen

    Öffnen Sie in Ihrem Dashboard Webhooks.

  2. URL eingeben

    Geben Sie unter URL (https) die Adresse Ihres eigenen Servers ein, zum Beispiel https://ihredomain.de/printen-webhook.

  3. Events wählen

    Haken Sie an, welche Events Sie empfangen möchten. Standardmäßig sind order.shipped, order.payment_failed und order.awaiting_funds angehakt.

  4. Webhook hinzufügen

    Klicken Sie auf Webhook hinzufügen. Sie sehen nun das Geheimnis mit dem Hinweis Speichern Sie dieses Geheimnis jetzt. Sie sehen es nur einmal.

  5. Geheimnis speichern

    Legen Sie das Geheimnis sofort in der Konfiguration Ihres Servers ab, zum Beispiel als Umgebungsvariable. Sie brauchen es, um die Signatur zu prüfen.

Sie können einen Endpunkt auch über die API mit POST /webhooks hinzufügen. Lassen Sie events weg oder senden Sie eine leere Liste, erhalten Sie alle Events. Das Geheimnis steht nur in dieser einen Antwort.

Endpunkt über die API hinzufügen
curl -X POST https://www.printen.eu/api/reseller/v1/webhooks \
  -H "Authorization: Bearer prk_live_..." -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/printen-webhook","events":["order.shipped","order.delivered"]}'
Antwort
{
  "data": {
    "id": "WEBHOOK_ID",
    "url": "https://example.com/printen-webhook",
    "events": ["order.shipped", "order.delivered"],
    "allEvents": false,
    "active": true,
    "createdAt": "2026-10-10T09:00:00.000Z",
    "updatedAt": "2026-10-10T09:00:00.000Z",
    "secret": "WEBHOOK_SECRET"
  }
}
  • Die URL muss https verwenden und öffentlich erreichbar sein. Adressen wie localhost, interne Netzwerkadressen und Namen, die auf .local, .internal oder .lan enden, werden abgelehnt.
  • Sie können höchstens 10 Endpunkte pro Konto haben.
  • Jeder Endpunkt hat sein eigenes Geheimnis.

Welche Events gibt es?

EventWannWas Sie meist tun
order.createdEine Bestellung wurde angelegt, über die API, das Dashboard oder einen verbundenen Shop.Unsere Bestellnummer bei Ihrer eigenen Bestellung speichern.
order.submittedDie Bestellung ist bezahlt und geht in die Produktion.Die Bestellung in Ihrem System auf "in Bearbeitung" setzen.
order.awaiting_fundsIhr Guthaben reicht nicht; die Bestellung wartet.Sich selbst benachrichtigen und aufladen.
order.payment_failedDie Zahlung mit Ihrer hinterlegten Karte ist fehlgeschlagen.Karte prüfen und die Bestellung im Dashboard erneut versuchen.
order.in_productionDie Produktion hat begonnen.Den Status anzeigen. Ab jetzt ist keine Stornierung mehr möglich.
order.shippedDas Paket ist versandt; tracking enthält Nummer und Link.Die Sendungsverfolgung an Ihren Kunden senden oder in Ihren Shop eintragen.
order.deliveredDas Paket wurde zugestellt.Die Bestellung abschließen.
order.cancelledDie Bestellung wurde storniert.Die Bestellung in Ihrem System stornieren und eine eventuelle Erstattung verbuchen.
order.file_problemEs gibt ein Problem mit der Druckdatei. Wir melden uns bei Ihnen.Ihre Datei prüfen und auf unsere Nachricht antworten.

Wie sieht eine Nachricht aus?

Jede Nachricht ist gleich aufgebaut. Unten ein Beispiel für order.shipped.

Beispiel: order.shipped
{
  "id": "EVENT_ID",
  "type": "order.shipped",
  "created": "2026-10-10T14:32:05.000Z",
  "data": {
    "order": {
      "id": "ORDER_ID",
      "orderNumber": "RS-3F9A1C7B2E",
      "externalOrderId": "1001",
      "externalOrderNumber": "1001",
      "state": "SUBMITTED",
      "status": "SHIPPED",
      "tracking": {
        "code": "3SABCD1234567",
        "url": "https://example.com/track/3SABCD1234567"
      },
      "items": [
        {
          "productId": "PRODUCT_ID",
          "name": "Poster",
          "quantity": 1,
          "options": { "size": "A2" }
        }
      ]
    }
  }
}
FeldBedeutung
idEindeutige id des Events. Behandeln Sie sie als Text ohne feste Struktur und erkennen Sie damit doppelte Nachrichten.
typeDie Art des Events, identisch mit dem Header X-Printen-Event.
createdWann das Event entstanden ist (ISO 8601).
data.order.orderNumberUnsere Bestellnummer, wie sie im Dashboard steht.
data.order.externalOrderIdIhre eigene Bestell-id, wenn Sie sie beim Bestellen mitgesendet haben.
data.order.state und statusDer Status auf Ihrer Seite (zum Beispiel SUBMITTED) und der Produktionsstatus (zum Beispiel SHIPPED). Siehe Bestellstatus.
data.order.trackingSendungsnummer und Tracking-Link, sobald die Bestellung versandt ist.
Zusätzliche Felder in dataBei order.awaiting_funds und order.payment_failed steht der Grund in reason. Bei order.cancelled kann refundedCents enthalten sein. In der Sandbox hat order.submitted das Feld test.

Wie prüfen Sie die Signatur?

Jeder kann eine Anfrage an Ihre URL senden. Prüfen Sie deshalb bei jeder Nachricht die Signatur, bevor Sie etwas damit tun. Die Signatur ist ein HMAC-SHA256 mit dem Geheimnis Ihres Endpunkts.

  1. Header lesen

    Lesen Sie X-Printen-Timestamp und X-Printen-Signature aus der Anfrage.

  2. Zeit prüfen

    Lehnen Sie die Nachricht ab, wenn der Zeitstempel mehr als 5 Minuten (300 Sekunden) von der aktuellen Zeit abweicht. So kann niemand eine alte Nachricht erneut abspielen.

  3. Erwartete Signatur berechnen

    Berechnen Sie mit Ihrem Geheimnis einen HMAC-SHA256 über den Text timestamp.rawBody: den Zeitstempel, einen Punkt und dann den rohen Body. Stellen Sie v1= voran.

  4. Sicher vergleichen

    Vergleichen Sie das Ergebnis mit X-Printen-Signature mit einem Vergleich in konstanter Zeit. Stimmen sie nicht überein, antworten Sie mit 401 und verarbeiten nichts.

Node.js
import crypto from 'node:crypto';

export function verifyPrintenWebhook(rawBody, headers, secret) {
  const timestamp = headers['x-printen-timestamp'];
  const signature = headers['x-printen-signature'] || '';
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = 'v1=' + crypto.createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`).digest('hex');
  return signature.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
PHP
<?php
function verify_printen_webhook(string $rawBody, string $timestamp, string $signature, string $secret): bool {
    if (abs(time() - (int) $timestamp) > 300) return false;
    $expected = 'v1=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
    return hash_equals($expected, $signature);
}
Python
import hashlib, hmac, time

def verify_printen_webhook(raw_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
    if abs(time.time() - int(timestamp)) > 300:
        return False
    expected = "v1=" + hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Stolperfalle: auf den rohen Body prüfen

Die Signatur wird über den Body berechnet, genau so, wie wir ihn senden, Byte für Byte. Viele Frameworks lesen den Body automatisch als JSON ein. Wandeln Sie dieses JSON danach wieder in Text um, können Leerzeichen, Reihenfolge oder Zeichen leicht abweichen, und die Signatur stimmt nicht mehr.

Express: den rohen Body behalten
import express from 'express';
import { verifyPrintenWebhook } from './verify-printen-webhook.js';

const app = express();

// express.raw (not express.json) keeps the body exactly as it was sent
app.post('/printen-webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  const rawBody = req.body.toString('utf8');
  if (!verifyPrintenWebhook(rawBody, req.headers, process.env.PRINTEN_WEBHOOK_SECRET)) {
    return res.status(401).send('invalid signature');
  }
  const event = JSON.parse(rawBody);
  await saveEventForProcessing(event); // store first, process later
  res.status(200).send('ok');
});
  • PHP: Lesen Sie den Body mit file_get_contents("php://input"), nicht mit $_POST.
  • Python (Flask): Verwenden Sie request.get_data() für die rohen Bytes, nicht request.json.
  • Next.js (App Router): Verwenden Sie await request.text() und erst danach JSON.parse.

Was, wenn Ihr Endpunkt nicht antwortet?

Eine Zustellung ist erfolgreich, wenn Sie innerhalb von 10 Sekunden mit einem 2xx-Status antworten. Alles andere gilt als fehlgeschlagen: ein Timeout, ein Fehlerstatus, aber auch eine Weiterleitung (3xx). Dann versuchen wir es nach diesem Schema erneut.

VersuchWartezeit nach dem vorigen VersuchEtwa nach dem ersten Versuch
1SofortStart
21 Minute1 Minute
35 Minuten6 Minuten
415 Minuten21 Minuten
530 Minuten51 Minuten
61 Stunde2 Stunden
72 Stunden4 Stunden
84 Stunden8 Stunden
98 Stunden16 Stunden
108 Stunden24 Stunden

Nach dem zehnten Versuch geben wir dieses Event auf. Ihr Endpunkt wird nie automatisch deaktiviert: Neue Events senden wir weiterhin.

Doppelte und ungeordnete Events verarbeiten

Durch Wiederholungen kann dasselbe Event mehrmals ankommen, und Events können in einer anderen Reihenfolge eintreffen, als sie entstanden sind. Richten Sie Ihre Verarbeitung darauf aus.

  • Auf `id` deduplizieren: Speichern Sie die id jedes verarbeiteten Events. Kommt dieselbe id erneut, antworten Sie mit 2xx und tun sonst nichts.
  • Reihenfolge über `created` prüfen: Haben Sie für diese Bestellung schon ein neueres Event verarbeitet, überschreiben Sie keinen neueren Status mit einem älteren.
  • Keinen Status zurücksetzen: Kommt order.shipped nach order.delivered, bleibt die Bestellung zugestellt.
  • Im Zweifel die Bestellung abrufen: Mit GET /orders/{id} erhalten Sie immer den aktuellen Stand.

Wie testen Sie Ihren Webhook?

  1. Endpunkt vorbereiten

    Sorgen Sie dafür, dass Ihre URL öffentlich über https erreichbar ist. Arbeiten Sie lokal, nutzen Sie einen Tunnel-Dienst, der eine öffentliche https-URL an Ihren eigenen Rechner weiterleitet.

  2. Endpunkt hinzufügen

    Fügen Sie ihn unter Webhooks hinzu und haken Sie order.created, order.submitted und order.cancelled an (oder lassen Sie die Events über die API weg, um alles zu empfangen).

  3. Sandbox-Bestellung aufgeben

    Geben Sie mit einem Testschlüssel (prk_test_) eine Bestellung über die API auf. Innerhalb weniger Minuten erhalten Sie order.created und order.submitted (mit test in data).

  4. Testbestellung stornieren

    Stornieren Sie die Bestellung mit POST /orders/{id}/cancel. Sie erhalten dann order.cancelled.

  5. Logs prüfen

    Prüfen Sie, ob die Signatur stimmte, ob Sie 2xx zurückgegeben haben und ob eine wiederholte Nachricht mit derselben id sauber ignoriert wurde.

Probleme lösen

Ich erhalte gar nichts.

Prüfen Sie, ob die URL stimmt, https verwendet und aus dem Internet erreichbar ist (kein localhost und keine interne Adresse). Prüfen Sie, ob Sie die richtigen Events angehakt haben. Beachten Sie, dass ein Event erst nach einigen Minuten ankommen kann.

Die Signatur stimmt nie.

Prüfen Sie, ob Sie das Geheimnis dieses Endpunkts verwenden (jeder Endpunkt hat sein eigenes) und ob Sie auf den rohen Body prüfen, nicht auf neu aufgebautes JSON. Siehe der rohe Body.

Ich erhalte Nachrichten doppelt.

Das ist nach einem Timeout oder Fehlerstatus normal. Deduplizieren Sie auf die id des Events. Siehe doppelte Events.

Mein Server sendet eine Weiterleitung.

Wir folgen keinen Weiterleitungen; ein 301 oder 302 gilt als fehlgeschlagen. Verwenden Sie die endgültige URL und achten Sie dabei auf www, einen Schrägstrich am Ende und http zu https.

Ich habe das Geheimnis nicht gespeichert.

Löschen Sie den Endpunkt und fügen Sie ihn neu hinzu. Sie erhalten ein neues Geheimnis; tragen Sie es in Ihre Serverkonfiguration ein.

Ich kann keinen Endpunkt mehr hinzufügen.

Sie haben das Maximum von 10 Endpunkten erreicht. Löschen Sie zuerst einen, den Sie nicht mehr nutzen.

Kommen Sie nicht weiter?

Wir helfen Ihnen gern, an Werktagen persönlich und meist noch am selben Tag.

Alle Artikel