Webhook

Webhook, platformdaki bir işlemden sonra GAMEMONITORING olayını sisteminize gönderir. JSON gövdeli standart bir POST isteğidir; web sitesi, panel veya oyun servisi bu sayede otomatik tepki verebilir.

Oy ödülleri ayarlıyorsanız önce bu rehbere göre Webhook’u bağlayın, ardından ayrı akışı kullanın: Oy ödülleri.

Bağlantı

Bu ayar bir GAMEMONITORING projesini handler’ınıza bağlar ve gelen istekleri doğrulamak için imzalama tokenı sağlar.

  1. Projelerim sayfasını açın, bir proje oluşturun veya mevcut projeyi seçin, ardından Webhook ayarlarına gidin.
  2. Content-Type: application/json ile POST kabul eden ve istekleri yönlendirmeyen herkese açık bir HTTPS endpoint oluşturun.
  3. Webhook ayarlarında tam handler URL’sini girin, örneğin https://panel.example.com/gamemonitoring-webhook, ve kaydedin.
  4. Aynı bloktaki imzalama tokenını kopyalayın ve handler scriptinize ekleyin.
  5. Handler’da signature doğrulayın, gerekli event_type değerini işleyin ve başarılı 2xx yanıtını yalnızca olay işlendikten sonra döndürün.

Yerel test için handler’ı bilgisayarınızda çalıştırıp herkese açık bir HTTPS URL üzerinden erişilebilir hale getirebilirsiniz. Bunun için ngrok veya başka bir tünelleme servisi kullanın ve oluşturulan herkese açık URL’yi Webhook ayarlarına girin.

Kurulumdan sonra arayüzden test Webhook’u gönderin ve teslimat durumunu kontrol edin. Örnek URL için sunucunuz /gamemonitoring-webhook yolunda POST kabul etmelidir.

Test başarısızsa önce handler yanıtına bakın: 401 imza hatasıdır, 403 veya HTML doğrulama sayfası genellikle WAF ya da bot protection sorununu gösterir, timeout ise URL’nin internetten erişilemediği veya çok yavaş yanıt verdiği anlamına gelir.

Handler gereksinimleri

  • URL internetten erişilebilir olmalıdır. Yerel adresler, özel ağlar ve kullanıcı adı veya parola içeren URL’ler uygun değildir.
  • Production için HTTPS önerilir. HTTP desteklenir, ancak aktarım sırasında veriyi daha zayıf korur.
  • Handler POST metodunu ve JSON gövdeyi yönlendirme olmadan kabul etmelidir.
  • 2xx yanıtını yalnızca sisteminiz olayı işledikten sonra döndürün. Genellikle 204 No Content yeterlidir.
  • Olay güvenli şekilde işlenemiyorsa hata kodu döndürün. 3xx, 4xx, 5xx, timeout ve bağlantı hatası başarısız teslimat sayılır.
  • Firewall, bot protection veya allowlist kullanıyorsanız GAMEMONITORING IP adreslerini istisnalara ekleyin.
  • Yanıt gövdesinde token, stack trace, SQL hatası veya başka dahili detaylar döndürmeyin. Handler yanıtı arayüzde gösterilir; bu nedenle hata metni güvenli ve anlaşılır olmalıdır.

Olay verileri

Her Webhook JSON gövde ve temel alanlarla gelir:

  • event_type — işlenmesi gereken olay.
  • event_id — benzersiz olay ID’si. Idempotency ve tekrar teslimat koruması için event_type ile birlikte kullanın.
  • is_test — arayüzden gelen test teslimatını belirtir.
  • signature — olay gövdesinin imzası.
Webhook olayı örneği
{
  "event_id": "9824cabb-2203-437e-9b6c-aba43dde3e4b",
  "event_type": "example.event",
  "is_test": false,
  "signature": "0ac4c97a5d934599dbd78985c4bcbb6926e77b4809d2be56333b1b25f638f064"
}

Örneği okuma şekli: event_type hangi olayın işleneceğini gösterir; event_id durum değişikliği öncesinde idempotency için gerekir; is_test: true teknik teslimat kontrolü anlamına gelir; signature iş verisi değildir ve yalnızca isteği doğrulamak için kullanılır.

İşleme mantığı event_type değerine bağlıdır. server.vote ve project.vote için ayrı akışı kullanın: Oy ödülleri.

is_test true ise imzayı doğrulayın ve 2xx döndürün, ancak bakiye değiştirmeyin, eşya vermeyin ve production işlemleri başlatmayın.

Handler yanıtı örneği

Her gelen olayı tek ve net bir sonuçla bitirin:

  • 204 No Content — imza doğru, olay işlendi veya güvenli şekilde atlandı. Test teslimatı ve daha önce işlenmiş olay için aynı yanıtı döndürün.
  • 400 Bad Request — zorunlu alanlar eksik. Bu handler hatası veya beklenmeyen istek gövdesi anlamına gelir; iş mantığını çalıştırmayın.
  • 401 Unauthorized — imza geçersiz. API isteği yapmayın, veritabanını değiştirmeyin ve ödül vermeyin.
  • 500 Internal Server Error — veritabanı, kuyruk veya dahili sistem geçici olarak kullanılamıyor. Teslimat başarısız kalır ve sebep düzeltildikten sonra tekrar denenebilir.

Örnek: handler olayı aldı, imzayı doğruladı, event_type + event_id kaydetti ve olayı işlediyse 204 dönebilir. Veritabanı kullanılamıyorsa ve olay kaydedilemiyorsa, teslimatın erken başarılı sayılmaması için 500 döndürmek daha doğrudur.

İmza doğrulama

İmza signature alanında bulunur. Her türlü iş mantığından, API isteğinden ve veritabanı değişikliğinden önce doğrulayın.

Doğrulama için signature hariç olay gövdesindeki tüm alanları alın, anahtarları alfabetik sıralayın ve & ile birleştirilmiş key=value string’i oluşturun. Boolean değerler true veya false olarak yazılır.

İmzalama string’i
event_id=9824cabb-2203-437e-9b6c-aba43dde3e4b&event_type=example.event&is_test=false

Yukarıdaki örnek için imzalama string’i yalnızca event_id, event_type ve is_test alanlarından oluşur. Ardından Webhook ayarlarındaki imzalama tokenı ile HMAC-SHA256 hesaplayın ve sonucu istekteki signature ile karşılaştırın.

Örneklerdeki hazır imzalar demo tokenı paste-webhook-token-here ile hesaplanmıştır. Kendi handler’ınızda Webhook ayarlarındaki tokenı kullanın.

Handler’da:

  • sıralanmış anahtarlardan imzalama string’ini oluşturun;
  • imzalama tokenı ile HMAC-SHA256 hesaplayın;
  • sonucu constant-time karşılaştırma fonksiyonuyla signature ile karşılaştırın: PHP için hash_equals, Node.js için timingSafeEqual, Python için compare_digest;
  • imza geçersizse 401 döndürün.

Adım 1. Temel handler

Herhangi bir Webhook’u kabul edebilen handler ile başlayın: JSON okur, signature doğrular, test teslimatını işler, temel alanları kontrol eder ve 204 döner. Bu adımda handler yalnızca teslimatın doğru kabul edildiğini doğrular. Olay bazlı mantığı bu temel yol çalıştıktan sonra ekleyin.

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);

Adım 2. Tekilleştirme ekleyin

Webhook at-least-once modelini kullanır: aynı olay birden fazla kez gelebilir. Handler sistem durumunu değiştirmeden önce bu işlemi event_type + event_id ile idempotent yapın.

Önce event_type ve event_id çiftini benzersiz anahtarla saklayan bir tablo oluşturun. Kayıt zaten varsa olay daha önce işlenmiştir.

İşlenmiş Webhook olayları tablosu
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)
);

Sonra temel handler’ı genişletin: imza ve temel alanlar doğrulandıktan sonra event_type + event_id kaydedin ve durum değişikliklerini aynı transaction içinde yapın.

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);
}

Nickname, kullanıcı ID’si veya server ID’sini tekilleştirme anahtarı olarak kullanmayın: aynı kullanıcı farklı olaylar oluşturabilir veya izin verilen bir aksiyonu daha sonra tekrar yapabilir. Anahtar event_type + event_id olmalıdır.

Örnek: handler sistem durumunu değiştirdi ama GAMEMONITORING 204 almadan bağlantı koptu. Daha sonra teslimat tekrarlandığında aynı olay yeniden gelir. Handler kayıtlı event_type + event_id bulmalı, tekrar durum değişikliği yapmamalı ve 204 dönmelidir.

Handler geçici olarak olayı işleyemiyorsa hata yanıtı döndürün. Sebep düzeltildikten sonra, bu olay için tekrar gönderme varsa teslimat arayüzden tekrar gönderilebilir.

Testler ve tekrar gönderme

Test teslimatı (is_test: true) handler URL’sini, imzayı ve HTTP yanıtını kontrol eder. Handler aynı işleme yolunu izlemelidir: JSON okur, signature doğrular, is_test değerini tanır ve başarılı 2xx yanıtı döner.

Test olayı bakiye, envanter, roller, abonelikler veya diğer production verilerini değiştirmemelidir. Test için teknik log ve 204 yanıtı yeterlidir.

Teslimat hata ile biterse arayüzde durum, HTTP kodu ve handler yanıtı gösterilir. Sebep düzeltildikten sonra, bu olay için tekrar gönderme varsa başarısız teslimat yeniden gönderilebilir.