Recompense pentru voturi

Recompensele pentru voturi folosesc evenimentul Webhook server.vote: handlerul primește evenimentul, obține datele votului prin API, găsește jucătorul în sistemul dvs. și acordă recompensa o singură dată.

Pentru a integra această metodă, configurați mai întâi Webhook-ul proiectului și verificați signature. Datele votului se cer prin GET /votes/:vote_id.

Cum funcționează fluxul

  1. Primiți evenimentul Webhook și verificați signature. Dacă is_test este true, returnați 204 fără să încărcați votul și fără să acordați recompensa.
  2. Asigurați-vă că event_type este server.vote.
  3. Luați event_id: pentru server.vote, acesta este ID-ul votului.
  4. Obțineți datele votului prin GET /votes/:vote_id și găsiți jucătorul în sistemul dvs.
  5. Aplicați protecția împotriva procesării repetate după event_type + event_id și acordați recompensa în aceeași tranzacție.
  6. Dacă recompensa nu poate fi acordată în siguranță, returnați un răspuns de eroare, remediați cauza și retrimiteți livrarea din interfață.

Exemplu de recompensă

Să presupunem că jucătorul PlayerName a votat pentru serverul cu ID 1, iar sistemul trebuie să adauge 100 monede.

  1. GAMEMONITORING trimite un Webhook cu event_type: server.vote și event_id: 9824cabb-2203-437e-9b6c-aba43dde3e4b.
  2. Handlerul verifică signature. Dacă semnătura este invalidă, returnează 401 și se oprește.
  3. Handlerul cere GET /votes/9824cabb-2203-437e-9b6c-aba43dde3e4b și primește nickname, server și utilizator.
  4. În baza dvs., handlerul găsește contul local după nickname sau după legătura de cont.
  5. Într-o tranzacție, handlerul aplică protecția împotriva procesării repetate după server.vote + event_id și adaugă 100 monede.
  6. Gestionați livrarea repetată a aceluiași eveniment după aceleași reguli de protecție împotriva procesării repetate.

Același flux se potrivește și pentru obiecte, roluri, timp VIP, coduri promo sau cozi interne.

Evenimentul de vot

Când apare un vot pentru server, GAMEMONITORING trimite evenimentul server.vote. Body-ul conține doar date de livrare: event_type, event_id, is_test și signature. Datele complete ale votului se cer separat.

Exemplu de eveniment
{
  "event_id": "9824cabb-2203-437e-9b6c-aba43dde3e4b",
  "event_type": "server.vote",
  "is_test": false,
  "signature": "ae83b8aba88a3a9ab3b97b1f6d65664da5628a9cb64d56d5132807bca5472e4f"
}

event_id din acest eveniment este ID-ul votului. Nu folosiți body-ul Webhook ca sursă pentru nickname, server sau utilizator: aceste date vin din API.

Obținerea datelor votului

Folosiți event_id ca vote_id și cereți datele votului prin GET /votes/:vote_id:

Cerere pentru datele votului
curl -sS "https://api.gamemonitoring.ro/votes/9824cabb-2203-437e-9b6c-aba43dde3e4b"

Pentru acordarea recompensei aveți nevoie de obicei de response.nickname, response.server și datele publice response.user. Dacă recompensa depinde de un anumit server, verificați întotdeauna response.server.id.

Exemplu de mapare: response.nickname găsește contul jucătorului în baza dvs., response.server.id alege regula de recompensă, iar response.user.id poate fi salvat în jurnalul recompenselor.

Dacă API este temporar indisponibil sau returnează un răspuns neașteptat, nu acordați recompensa fără verificare. Returnați un cod de eroare, remediați cauza și retrimiteți livrarea.

Exemplu complet

Exemplul verifică semnătura, obține datele votului, previne recompensele duplicate și aplică recompensa. Înlocuiți numele tabelului de utilizatori, câmpul de balanță și regula de căutare a jucătorului cu structura sistemului dvs.

Înainte de rularea exemplului, configurați Webhook-ul proiectului, verificați GET /votes/:vote_id și înlocuiți actualizările SQL cu modelul dvs. de conturi.

php
<?php
// Set the webhook token, API URL and local database connection.
$secret = 'paste-webhook-token-here';
$apiUrl = 'https://api.gamemonitoring.ro';
$rewardAmount = '1.00';
$pdo = new PDO('mysql:host=127.0.0.1;dbname=game;charset=utf8mb4', 'game', 'password', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

// Read the JSON payload sent by GAMEMONITORING.
$data = json_decode(file_get_contents('php://input'), true) ?: [];
$isTest = ($data['is_test'] ?? false) === true;
$signingData = array_replace($data, ['is_test' => $isTest ? 'true' : 'false']);

// Prepare payload keys for signature verification.
$fields = array_values(array_filter(array_keys($data), fn($field) => $field !== 'signature'));
sort($fields, SORT_STRING);

// Build the signing string and expected HMAC.
$signing = implode('&', array_map(fn($field) => $field . '=' . (string) ($signingData[$field] ?? ''), $fields));
$expected = hash_hmac('sha256', $signing, $secret);
$actual = (string) ($data['signature'] ?? '');

// Reject requests with an invalid signature.
if (!hash_equals($expected, $actual)) {
    http_response_code(401);
    exit;
}

// Acknowledge test deliveries without changing balance.
if ($isTest) {
    http_response_code(204);
    exit;
}

// Process server vote events.
if (($data['event_type'] ?? '') === 'server.vote') {
    $eventType = (string) $data['event_type'];
    $eventId = (string) $data['event_id'];

    // Load full vote data by event_id.
    $voteUrl = $apiUrl . '/votes/' . rawurlencode($eventId);
    $voteResponse = json_decode(file_get_contents($voteUrl), true) ?: [];
    $vote = $voteResponse['response'] ?? null;
    $voteServerId = (string) ($vote['server']['id'] ?? '');

    // Use vote nickname to update the local account. Use $voteServerId for per-server rules.
    $nickname = trim((string) ($vote['nickname'] ?? ''));

    if ($nickname !== '') {
        // Keep deduplication and reward update in one transaction.
        $pdo->beginTransaction();

        try {
            // Store the event once; duplicate deliveries affect zero rows.
            $reward = $pdo->prepare('INSERT IGNORE INTO gamemonitoring_webhooks (event_type, event_id) VALUES (?, ?)');
            $reward->execute([$eventType, $eventId]);

            // Reward the local user only for a newly stored event.
            if ($reward->rowCount() === 1) {
                $balance = $pdo->prepare('UPDATE users SET balance = balance + ? WHERE nickname = ?');
                $balance->execute([$rewardAmount, $nickname]);
            }

            // Commit after deduplication and balance update succeed.
            $pdo->commit();
        } catch (Throwable $error) {
            // Roll back if any database step fails.
            $pdo->rollBack();
            throw $error;
        }
    }
}

http_response_code(204);