Webhook

Webhook sendet ein GAMEMONITORING-Ereignis nach einer Aktion auf der Plattform an Ihr System. Es ist eine normale POST-Anfrage mit JSON-Body, damit eine Website, ein Panel oder ein Spielservice automatisch reagieren kann.

Wenn Sie Belohnungen für Stimmen einrichten, verbinden Sie zuerst den Webhook nach dieser Anleitung und nutzen danach den eigenen Ablauf: Belohnungen für Stimmen.

Verbindung

Diese Einstellung verbindet ein GAMEMONITORING-Projekt mit Ihrem Handler und stellt einen Signatur-Token zur Prüfung eingehender Anfragen bereit.

  1. Öffnen Sie Meine Projekte, erstellen Sie ein Projekt oder wählen Sie ein bestehendes aus und gehen Sie dann zu den Webhook-Einstellungen.
  2. Erstellen Sie einen öffentlichen HTTPS-Endpunkt, der POST mit Content-Type: application/json akzeptiert und Anfragen nicht weiterleitet.
  3. Tragen Sie in den Webhook-Einstellungen die vollständige Handler-URL ein, zum Beispiel https://panel.example.com/gamemonitoring-webhook, und speichern Sie sie.
  4. Kopieren Sie den Signatur-Token aus demselben Block und tragen Sie ihn im Handler-Skript ein.
  5. Prüfen Sie im Handler signature, verarbeiten Sie das benötigte event_type und geben Sie eine erfolgreiche 2xx-Antwort erst zurück, nachdem das Ereignis verarbeitet wurde.

Für lokale Tests können Sie den Handler auf Ihrem Computer starten und über eine öffentliche HTTPS-URL erreichbar machen. Verwenden Sie dafür ngrok oder einen anderen Tunneling-Dienst und tragen Sie die generierte öffentliche URL in den Webhook-Einstellungen ein.

Senden Sie nach der Einrichtung einen Test-Webhook aus der Oberfläche und prüfen Sie den Zustellstatus. Für die Beispiel-URL muss Ihr Server POST auf /gamemonitoring-webhook annehmen.

Wenn der Test fehlschlägt, orientieren Sie sich an der Handler-Antwort: 401 bedeutet Signaturfehler, 403 oder eine HTML-Prüfseite weist meist auf WAF oder Bot-Schutz hin, und Timeout bedeutet, dass die URL aus dem Internet nicht erreichbar ist oder zu langsam antwortet.

Anforderungen an den Handler

  • Die URL muss aus dem Internet erreichbar sein. Lokale Adressen, private Netzwerke und URLs mit Login oder Passwort sind nicht geeignet.
  • HTTPS wird für Produktion empfohlen. HTTP wird unterstützt, schützt Daten bei der Übertragung aber schwächer.
  • Der Handler muss die Methode POST und einen JSON-Body ohne Weiterleitungen annehmen.
  • Geben Sie 2xx erst zurück, nachdem Ihr System das Ereignis verarbeitet hat. In der Regel reicht 204 No Content.
  • Wenn das Ereignis nicht sicher verarbeitet werden kann, geben Sie einen Fehlercode zurück. 3xx, 4xx, 5xx, Timeout und Verbindungsfehler gelten als fehlgeschlagene Zustellung.
  • Wenn Sie Firewall, Bot-Schutz oder eine Allowlist verwenden, fügen Sie die IP-Adressen von GAMEMONITORING als Ausnahmen hinzu.
  • Geben Sie im Antwort-Body keine Tokens, Stack Traces, SQL-Fehler oder andere interne Details zurück. Die Handler-Antwort wird in der Oberfläche angezeigt, daher muss der Fehlertext sicher und verständlich sein.

Ereignisdaten

Jeder Webhook kommt mit einem JSON-Body und Basisfeldern:

  • event_type — welches Ereignis verarbeitet werden muss.
  • event_id — eindeutige Ereignis-ID. Verwenden Sie sie zusammen mit event_type für Idempotenz und Schutz vor wiederholter Zustellung.
  • is_test — kennzeichnet eine Testzustellung aus der Oberfläche.
  • signature — Signatur des Ereignis-Bodys.
Webhook-Ereignisbeispiel
{
  "event_id": "9824cabb-2203-437e-9b6c-aba43dde3e4b",
  "event_type": "example.event",
  "is_test": false,
  "signature": "0ac4c97a5d934599dbd78985c4bcbb6926e77b4809d2be56333b1b25f638f064"
}

So lesen Sie das Beispiel: event_type zeigt, welches Ereignis verarbeitet werden muss; event_id wird für Idempotenz vor Zustandsänderungen benötigt; is_test: true bedeutet eine technische Zustellprüfung; signature ist keine Geschäftsdaten und wird nur zur Authentifizierung der Anfrage verwendet.

Die Verarbeitungslogik hängt von event_type ab. Für server.vote nutzen Sie den eigenen Ablauf: Belohnungen für Stimmen.

Wenn is_test true ist, prüfen Sie die Signatur und geben 2xx zurück, ändern aber keinen Kontostand, geben keine Gegenstände aus und starten keine produktiven Vorgänge.

Antwortbeispiel des Handlers

Schließen Sie jedes eingehende Ereignis mit einem klaren Ergebnis ab:

  • 204 No Content — die Signatur ist korrekt, und das Ereignis wurde verarbeitet oder sicher übersprungen. Geben Sie dieselbe Antwort für eine Testzustellung und für ein bereits verarbeitetes Ereignis zurück.
  • 400 Bad Request — Pflichtfelder fehlen. Das bedeutet einen Handler-Fehler oder einen unerwarteten Request-Body; Geschäftslogik darf nicht gestartet werden.
  • 401 Unauthorized — die Signatur ist falsch. Führen Sie keine API-Anfragen aus, ändern Sie keine Datenbank und geben Sie keine Belohnungen aus.
  • 500 Internal Server Error — Ihre Datenbank, Queue oder ein internes System ist vorübergehend nicht verfügbar. Die Zustellung bleibt fehlerhaft und kann nach Behebung der Ursache wiederholt werden.

Beispiel: Wenn der Handler das Ereignis empfangen, die Signatur geprüft, event_type + event_id gespeichert und das Ereignis verarbeitet hat, kann er 204 zurückgeben. Ist die Datenbank nicht verfügbar und kann das Ereignis nicht gespeichert werden, geben Sie besser 500 zurück, damit die Zustellung nicht zu früh als erfolgreich markiert wird.

Signaturprüfung

Die Signatur befindet sich im Feld signature. Prüfen Sie sie vor jeder Geschäftslogik, vor API-Anfragen und vor Datenbankänderungen.

Nehmen Sie zur Prüfung alle Felder des Ereignis-Bodys außer signature, sortieren Sie die Schlüssel alphabetisch und bauen Sie eine Zeichenkette key=value, verbunden mit &. Boolesche Werte werden als true oder false geschrieben.

Signatur-String
event_id=9824cabb-2203-437e-9b6c-aba43dde3e4b&event_type=example.event&is_test=false

Für das Beispiel oben wird der Signatur-String nur aus event_id, event_type und is_test gebaut. Danach berechnen Sie HMAC-SHA256 mit dem Signatur-Token aus den Webhook-Einstellungen und vergleichen das Ergebnis mit signature aus der Anfrage.

Vorberechnete Signaturen in den Beispielen nutzen den Demo-Token paste-webhook-token-here. In Ihrem Handler verwenden Sie den Token aus den Webhook-Einstellungen.

Im Handler:

  • bauen Sie den Signatur-String aus sortierten Schlüsseln;
  • berechnen Sie HMAC-SHA256 mit dem Signatur-Token;
  • vergleichen Sie das Ergebnis mit signature über eine Funktion mit konstanter Vergleichszeit: hash_equals in PHP, timingSafeEqual in Node.js oder compare_digest in Python;
  • geben Sie 401 zurück, wenn die Signatur falsch ist.

Schritt 1. Basis-Handler

Beginnen Sie mit einem Handler, der jeden Webhook annehmen kann: Er liest JSON, prüft signature, verarbeitet die Testzustellung, prüft Basisfelder und gibt 204 zurück. In diesem Schritt bestätigt der Handler nur, dass die Zustellung korrekt angenommen wird. Ereignisspezifische Logik fügen Sie hinzu, nachdem dieser Basispfad funktioniert.

php
<?php
// Replace this token with the signing token from your GAMEMONITORING webhook settings.
$secret = 'paste-webhook-token-here';

// Read and decode the JSON body sent by GAMEMONITORING.
$event = json_decode(file_get_contents('php://input'), true) ?: [];

// Test deliveries are signed too. Normalize the boolean value to the lowercase
// string used by GAMEMONITORING when the signature is calculated.
$isTest = ($event['is_test'] ?? false) === true;
$signingData = array_replace($event, ['is_test' => $isTest ? 'true' : 'false']);

// Build the exact signing string: all body fields except signature,
// sorted by key and joined as key=value pairs with &.
$fields = array_values(array_filter(array_keys($event), fn($field) => $field !== 'signature'));
sort($fields, SORT_STRING);

// Calculate HMAC-SHA256 with the webhook token from your settings.
$signing = implode('&', array_map(fn($field) => $field . '=' . (string) ($signingData[$field] ?? ''), $fields));
$expected = hash_hmac('sha256', $signing, $secret);
$actual = (string) ($event['signature'] ?? '');

// Reject the request before doing any work when the signature is invalid.
if (!hash_equals($expected, $actual)) {
    http_response_code(401);
    exit;
}

// Test deliveries must not change balance, inventory, roles, or production data.
if ($isTest) {
    http_response_code(204);
    exit;
}

// Real deliveries must include an event type and a stable event id.
$eventType = (string) ($event['event_type'] ?? '');
$eventId = (string) ($event['event_id'] ?? '');

if ($eventType === '' || $eventId === '') {
    http_response_code(400);
    exit;
}

// At this point the webhook is trusted. Add event-specific logic here.
syslog(LOG_INFO, 'Accepted webhook event ' . $eventType . ' #' . $eventId);

// 204 tells GAMEMONITORING that the delivery was accepted successfully.
http_response_code(204);

Schritt 2. Deduplikation hinzufügen

Webhook nutzt At-least-once-Zustellung: dasselbe Ereignis kann mehrmals ankommen. Bevor der Handler den Zustand Ihres Systems ändert, machen Sie diese Operation anhand von event_type + event_id idempotent.

Erstellen Sie zuerst eine Tabelle, die das Paar event_type und event_id mit eindeutigem Schlüssel speichert. Wenn der Datensatz bereits existiert, wurde das Ereignis schon verarbeitet.

Tabelle verarbeiteter Webhook-Ereignisse
CREATE TABLE gamemonitoring_webhooks (
  event_type varchar(64) NOT NULL,
  event_id varchar(100) NOT NULL,
  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (event_type, event_id)
);

Erweitern Sie danach den Basis-Handler: Speichern Sie nach Signaturprüfung und Prüfung der Basisfelder event_type + event_id und führen Sie Zustandsänderungen in derselben Transaktion aus.

php
<?php
// Replace this token with the signing token from your GAMEMONITORING webhook settings.
$secret = 'paste-webhook-token-here';

// Read and decode the JSON body sent by GAMEMONITORING.
$event = json_decode(file_get_contents('php://input'), true) ?: [];

// Test deliveries are signed too. Normalize the boolean value to the lowercase
// string used by GAMEMONITORING when the signature is calculated.
$isTest = ($event['is_test'] ?? false) === true;
$signingData = array_replace($event, ['is_test' => $isTest ? 'true' : 'false']);

// Build the exact signing string: all body fields except signature,
// sorted by key and joined as key=value pairs with &.
$fields = array_values(array_filter(array_keys($event), fn($field) => $field !== 'signature'));
sort($fields, SORT_STRING);

// Calculate HMAC-SHA256 with the webhook token from your settings.
$signing = implode('&', array_map(fn($field) => $field . '=' . (string) ($signingData[$field] ?? ''), $fields));
$expected = hash_hmac('sha256', $signing, $secret);
$actual = (string) ($event['signature'] ?? '');

// Reject the request before doing any work when the signature is invalid.
if (!hash_equals($expected, $actual)) {
    http_response_code(401);
    exit;
}

// Test deliveries must not change balance, inventory, roles, or production data.
if ($isTest) {
    http_response_code(204);
    exit;
}

// Real deliveries must include an event type and a stable event id.
$eventType = (string) ($event['event_type'] ?? '');
$eventId = (string) ($event['event_id'] ?? '');

if ($eventType === '' || $eventId === '') {
    http_response_code(400);
    exit;
}

// At this point the webhook is trusted. Deduplicate it before event-specific logic.
$pdo = null;

try {
    // Add your local database connection for deduplication and event-specific work.
    $pdo = new PDO('mysql:host=127.0.0.1;dbname=game;charset=utf8mb4', 'game', 'password', [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]);

    // Keep deduplication and the real state change in one transaction.
    // If any step fails, return 500 so the delivery can be retried.
    $pdo->beginTransaction();

    // Store the event once. This requires the table to have a unique key on
    // (event_type, event_id). Duplicate deliveries affect zero rows.
    $deduplicate = $pdo->prepare('INSERT IGNORE INTO gamemonitoring_webhooks (event_type, event_id) VALUES (?, ?)');
    $deduplicate->execute([$eventType, $eventId]);

    // The event was already processed earlier. Return success without changing
    // state again, because duplicate delivery is expected.
    if ($deduplicate->rowCount() === 0) {
        $pdo->commit();
        http_response_code(204);
        exit;
    }

    // Add event-specific database changes here. Keep them after the
    // deduplication insert and inside this same transaction.

    // Commit only after deduplication and event-specific work both succeed.
    $pdo->commit();

    // Log only newly processed real events after the transaction succeeds.
    syslog(LOG_INFO, 'Accepted webhook event ' . $eventType . ' #' . $eventId);

    http_response_code(204);
} catch (Throwable $error) {
    // Roll back partial database work so the event can be retried safely.
    if ($pdo instanceof PDO && $pdo->inTransaction()) {
        $pdo->rollBack();
    }

    // 500 keeps the delivery failed instead of marking unfinished work as done.
    http_response_code(500);
}

Verwenden Sie Nickname, Benutzer-ID oder Server-ID nicht als Deduplikationsschlüssel: Ein Benutzer kann verschiedene Ereignisse auslösen oder eine erlaubte Aktion später wiederholen. Der Schlüssel muss event_type + event_id sein.

Beispiel: Der Handler hat den Zustand Ihres Systems geändert, aber die Verbindung brach ab, bevor GAMEMONITORING 204 erhielt. Später wird die Zustellung wiederholt, und dasselbe Ereignis kommt erneut an. Ihr Handler muss die bereits gespeicherten event_type + event_id finden, die wiederholte Zustandsänderung überspringen und 204 zurückgeben.

Wenn der Handler ein Ereignis vorübergehend nicht verarbeiten kann, geben Sie eine Fehlerantwort zurück. Nach Behebung der Ursache kann die Zustellung aus der Oberfläche wiederholt werden, sofern eine Wiederholung für dieses Ereignis verfügbar ist.

Tests und erneute Zustellung

Eine Testzustellung (is_test: true) prüft URL, Signatur und HTTP-Antwort des Handlers. Der Handler muss denselben Verarbeitungspfad durchlaufen: JSON lesen, signature prüfen, is_test erkennen und eine erfolgreiche 2xx-Antwort zurückgeben.

Ein Testereignis darf Kontostand, Inventar, Rollen, Abonnements oder andere produktive Daten nicht ändern. Für den Test reichen ein technischer Logeintrag und die Antwort 204.

Wenn die Zustellung fehlschlägt, zeigt die Oberfläche Status, HTTP-Code und Handler-Antwort. Nach Behebung der Ursache kann eine fehlgeschlagene Zustellung erneut gesendet werden, sofern eine Wiederholung für dieses Ereignis verfügbar ist.