Webhook

Un Webhook livrează un eveniment GAMEMONITORING către sistemul dvs. imediat după o acțiune pe platformă. Este o cerere obișnuită POST cu body JSON, astfel încât site-ul, panoul sau serviciul de joc să poată reacționa automat.

Pentru recompense după vot, configurați mai întâi Webhook-ul, apoi folosiți fluxul dedicat: Recompense pentru voturi.

Conectare

Această configurare leagă un proiect GAMEMONITORING de handlerul dvs. și oferă tokenul de semnare pentru verificarea cererilor primite.

  1. Deschideți Proiectele mele, creați un proiect sau alegeți unul existent, apoi mergeți la setările Webhook.
  2. Creați un endpoint HTTPS public care acceptă POST cu Content-Type: application/json și nu face redirecționări.
  3. În setările Webhook, introduceți URL-ul complet al handlerului, de exemplu https://panel.example.com/gamemonitoring-webhook, și salvați-l.
  4. Copiați tokenul de semnare din același bloc și introduceți-l în scriptul handlerului.
  5. În handler, verificați signature, procesați event_type necesar și returnați un răspuns 2xx reușit doar după procesarea evenimentului.

După configurare, trimiteți un Webhook de test din interfață și verificați statusul livrării. Pentru URL-ul din exemplu, serverul trebuie să accepte POST pe /gamemonitoring-webhook.

Dacă testul eșuează, începeți cu răspunsul handlerului: 401 înseamnă problemă de semnătură, 403 sau HTML challenge indică de obicei WAF ori bot protection, iar timeout-ul înseamnă că URL-ul nu este accesibil din internet sau răspunde prea lent.

Cerințe pentru handler

  • URL-ul trebuie să fie accesibil din internet. Adresele locale, rețelele private și URL-urile cu login sau parolă nu sunt potrivite.
  • HTTPS este recomandat pentru producție. HTTP este acceptat, dar oferă protecție mai slabă pentru datele în tranzit.
  • Handlerul trebuie să accepte POST și body JSON fără redirecționări.
  • Returnați 2xx doar după ce sistemul dvs. a procesat evenimentul. De obicei 204 este suficient.
  • Dacă evenimentul nu poate fi procesat în siguranță, returnați un status de eroare. 3xx, 4xx, 5xx, timeout sau eroarea de conexiune sunt considerate livrare eșuată.
  • Dacă folosiți firewall, bot protection sau allowlist, adăugați IP-urile GAMEMONITORING la excepții.
  • Nu returnați în răspuns tokenuri, stack trace, erori SQL sau alte detalii interne. Răspunsul handlerului este afișat în interfață, deci textul erorii trebuie să fie sigur și clar.

Datele evenimentului

Fiecare Webhook vine ca body JSON cu câmpuri principale:

  • event_type — ce eveniment trebuie procesat.
  • event_id — ID-ul unic al evenimentului. Folosiți-l împreună cu event_type pentru idempotency și protecție împotriva livrării repetate.
  • is_test — indică o livrare de test din interfață.
  • signature — semnătura body-ului evenimentului.
Exemplu de eveniment Webhook
{
  "event_id": "9824cabb-2203-437e-9b6c-aba43dde3e4b",
  "event_type": "example.event",
  "is_test": false,
  "signature": "0ac4c97a5d934599dbd78985c4bcbb6926e77b4809d2be56333b1b25f638f064"
}

Cum citiți exemplul: event_type arată ce eveniment trebuie procesat, event_id se salvează înainte de schimbări de stare, is_test: true înseamnă doar verificarea livrării, iar signature este folosită doar pentru autentificarea cererii.

Logica de procesare depinde de event_type. Pentru server.vote, folosiți fluxul separat: Recompense pentru voturi.

Dacă is_test este true, verificați semnătura și returnați 2xx, dar nu modificați balanța și nu acordați obiecte.

Exemplu de răspuns al handlerului

Alegeți un rezultat clar pentru fiecare eveniment primit:

  • 204 No Content — semnătura este validă și evenimentul este procesat. Returnați la fel pentru test și pentru un duplicat deja procesat.
  • 400 Bad Request — lipsesc câmpuri obligatorii. Nu rulați logica de business.
  • 401 Unauthorized — semnătura este invalidă. Nu faceți cereri API, nu modificați baza de date și nu acordați recompense.
  • 500 Internal Server Error — baza, coada sau serviciul intern este temporar indisponibil. Livrarea rămâne eșuată și poate fi retrimisă după remediere.

Exemplu: dacă handlerul a verificat semnătura, a salvat event_type + event_id și a procesat evenimentul, poate returna 204. Dacă baza este indisponibilă, returnați 500 ca livrarea să nu fie marcată prea devreme ca reușită.

Verificarea semnăturii

Semnătura se află în câmpul signature. Verificați-o înainte de orice logică de business, cereri API și modificări în baza de date.

Pentru verificare, luați toate câmpurile body-ului în afară de signature, sortați cheile alfabetic și construiți un șir key=value unit prin &. Valorile boolean se scriu ca true sau false.

Șirul pentru semnătură
event_id=9824cabb-2203-437e-9b6c-aba43dde3e4b&event_type=example.event&is_test=false

Pentru exemplul de mai sus, șirul de semnare se construiește doar din event_id, event_type și is_test. Apoi calculați HMAC-SHA256 cu tokenul din setările Webhook și comparați cu signature din cerere.

Semnăturile precalculate din exemple folosesc tokenul demonstrativ paste-webhook-token-here. În handlerul dvs., folosiți tokenul din setările Webhook.

În handlerul dvs.:

  • construiți șirul de semnare din cheile sortate;
  • calculați HMAC-SHA256 cu tokenul de semnare;
  • comparați rezultatul cu signature folosind o funcție cu timp constant de comparare: hash_equals în PHP, timingSafeEqual în Node.js sau compare_digest în Python;
  • returnați 401 dacă semnătura este invalidă.

Handler minim

Acest exemplu poate primi orice Webhook: citește JSON, verifică signature, procesează livrarea de test, validează câmpurile principale și returnează 204. Adăugați logica specifică evenimentului după verificarea semnăturii.

php
<?php
$secret = 'paste-webhook-token-here';

$event = json_decode(file_get_contents('php://input'), true) ?: [];
$isTest = ($event['is_test'] ?? false) === true;
$signingData = array_replace($event, ['is_test' => $isTest ? 'true' : 'false']);

$fields = array_values(array_filter(array_keys($event), fn($field) => $field !== 'signature'));
sort($fields, SORT_STRING);

$signing = implode('&', array_map(fn($field) => $field . '=' . (string) ($signingData[$field] ?? ''), $fields));
$expected = hash_hmac('sha256', $signing, $secret);
$actual = (string) ($event['signature'] ?? '');

if (!hash_equals($expected, $actual)) {
    http_response_code(401);
    exit;
}

if ($isTest) {
    http_response_code(204);
    exit;
}

$eventType = (string) ($event['event_type'] ?? '');
$eventId = (string) ($event['event_id'] ?? '');

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

error_log('Accepted webhook event ' . $eventType . ' #' . $eventId);

http_response_code(204);

Livrare repetată și deduplicare

Webhook este livrat în model at-least-once: același eveniment poate ajunge de mai multe ori. Orice acțiune care schimbă starea sistemului dvs. trebuie să fie idempotentă pe baza event_type + event_id.

Salvați perechea event_type și event_id într-un tabel cu cheie unică. Dacă înregistrarea există deja, evenimentul a fost procesat: returnați 204 și nu modificați starea din nou.

Tabel cu evenimente Webhook procesate
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)
);

Când un Webhook modifică baza dvs., salvați evenimentul și executați acțiunea în aceeași tranzacție. Dacă handlerul acordă o recompensă, inserarea event_type + event_id și actualizarea recompensei trebuie să reușească împreună. Dacă inserarea nu creează un rând nou, evenimentul a fost deja procesat.

Nu folosiți nickname-ul, ID-ul utilizatorului sau ID-ul serverului ca cheie de deduplicare: un utilizator poate declanșa evenimente diferite sau poate repeta mai târziu o acțiune permisă. Cheia trebuie să fie event_type + event_id.

Exemplu: handlerul a acordat recompensa, dar conexiunea s-a întrerupt înainte ca GAMEMONITORING să primească 204. La retrimitere vine același eveniment. Handlerul trebuie să găsească event_type + event_id salvate, să nu acorde recompensa a doua oară și să returneze 204.

Dacă handlerul nu poate procesa temporar evenimentul, returnați un răspuns eșuat. După remedierea cauzei, livrarea poate fi retrimisă din interfață, dacă retrimiterea este disponibilă pentru acel eveniment.

Teste și retrimitere

O livrare de test (is_test: true) verifică URL-ul handlerului, semnătura și răspunsul HTTP. Handlerul ar trebui să treacă prin același flux de procesare: citește JSON, verifică signature, recunoaște is_test și returnează un răspuns 2xx reușit.

Un eveniment de test nu trebuie să modifice balanța, inventarul, rolurile, abonamentele sau alte date de producție. Pentru test sunt suficiente un log tehnic și răspunsul 204.

Dacă livrarea eșuează, interfața arată statusul, codul HTTP și răspunsul handlerului. După remedierea cauzei, livrarea eșuată poate fi retrimisă dacă retrimiterea este disponibilă pentru acel eveniment.