XTREMETOP300

API наград за голосование

Выдавайте игрокам внутриигровые награды за голосование. Есть два способа получать голоса — оба работают с одним ключом и оба рассчитаны на сервер, который стоит за NAT или перезапускается дважды в день.

Какой способ выбрать?

Pull — начните с него

Ваш сервер раз в минуту-две спрашивает у нас, какие награды он должен выдать. Не нужны ни открытый порт, ни домен, ни TLS-сертификат. Работает даже с домашнего подключения.

Push — если у вас есть веб-эндпоинт

Мы отправляем POST на ваш URL в момент поступления голоса, с подписью, по которой можно убедиться, что запрос от нас. Мгновенно, но только если мы можем до вас достучаться.

Можно использовать оба способа. Награду, отправленную на ваш колбэк, по-прежнему можно забрать через pull, так что пропущенный push никогда не означает потерянную награду.

1. Получите ключ

Откройте свой сервер в разделе Мои серверы → Награды за голоса, включите награды и скопируйте API-ключ. Передавайте его в заголовке каждого запроса:

Authorization: Bearer YOUR_API_KEY

Никогда не передавайте ключ в URL. Он попадёт в логи доступа, логи прокси и историю браузера.

2. Получите список невыданных наград

POST /api/v1/rewards/pending возвращает невостребованные награды, начиная с самых старых.

curl -X POST https://xtremetop300.com/api/v1/rewards/pending \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"limit": 50}'

{
  "server":  { "id": 1234, "name": "My Server" },
  "count":   2,
  "more":    false,
  "rewards": [
    { "token": "8f2c...", "player": "Steve",  "server_id": 1234, "voted_at": "2026-08-25T09:14:02+00:00" },
    { "token": "1a90...", "player": "Alexia", "server_id": 1234, "voted_at": "2026-08-25T09:31:44+00:00" }
  ]
}

Чтение ничего не расходует. Выдайте награды, а затем подтвердите их:

curl -X POST https://xtremetop300.com/api/v1/rewards/claim \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tokens": ["8f2c...", "1a90..."]}'

{ "claimed": ["8f2c...", "1a90..."], "already_claimed": [], "unknown": [] }
Зачем два запроса? Если сервер упадёт между чтением и выдачей, награда останется на месте и при следующей проверке. Именно claim означает «игрок её получил». Если хотите обойтись одним запросом, передайте "claim": true в запросе pending — вы получите награды, они сразу будут отмечены как выданные, и падение сервера в неудачный момент их потеряет.

3. Или просто задайте вопрос «да/нет»

Большинству плагинов нужно лишь знать, напоминать ли игроку о голосовании при входе. GET /api/v1/rewards/check отвечает по нику игрока или по IP-адресу.

curl "https://xtremetop300.com/api/v1/rewards/check?player=Steve&hours=24" \
  -H "Authorization: Bearer YOUR_API_KEY"

{ "voted": true, "voted_at": "2026-08-25T09:14:02+00:00", "window_hours": 24 }

4. Push, если нужно мгновенно

Укажите URL колбэка в настройках наград, и мы будем отправлять на него такой POST при каждом новом голосе:

POST /your-callback HTTP/1.1
Content-Type: application/json
X-Xtreme300-Signature: sha256=4f1e...
X-Xtreme300-Timestamp: 1787654321
X-Xtreme300-Delivery: 8f2c...

{
  "event":   "vote.reward",
  "sent_at": 1787654321,
  "reward":  { "token": "8f2c...", "player": "Steve", "server_id": 1234, "voted_at": "..." }
}

Ответьте любым статусом 2xx. При любом другом ответе запрос повторяется в течение следующего часа (число попыток: 5), после чего награда просто ждёт, пока её заберут через pull. Ответ 4xx считается отказом и не повторяется, поэтому не возвращайте его при временной проблеме.

5. Проверьте подпись

Отправить POST на ваш URL колбэка может кто угодно. Только подпись отличает настоящий голос от постороннего, который решил выдать себе алмазов. Вычислите HMAC-SHA256(timestamp + "." + rawBody) с вашим API-ключом и сравните результат с заголовком.

// PHP
$body      = file_get_contents('php://input');   // the raw body, before json_decode
$timestamp = (int) $_SERVER['HTTP_X_XTREME300_TIMESTAMP'];
$signature = $_SERVER['HTTP_X_XTREME300_SIGNATURE'];

// Reject old requests first. A valid signature on a captured request replayed
// a thousand times is the whole attack.
if (abs(time() - $timestamp) > 300) {
    http_response_code(400);
    exit;
}

$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $apiKey);

// hash_equals, not ==. A normal comparison returns faster the earlier it finds
// a difference, which is enough to guess the signature one byte at a time.
if (! hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}
// Java (Bukkit / Paper / Spigot)
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));

byte[] digest = mac.doFinal((timestamp + "." + rawBody).getBytes(StandardCharsets.UTF_8));

StringBuilder hex = new StringBuilder("sha256=");
for (byte b : digest) hex.append(String.format("%02x", b));

if (!MessageDigest.isEqual(hex.toString().getBytes(), signatureHeader.getBytes())) {
    return; // not from us
}

6. Давайте игрокам ссылку, которая их уже знает

Добавьте ник игрока в ссылку на голосование, и поле будет заполнено заранее — игроку останется только нажать кнопку. Подойдёт любой из параметров player, pingUsername, username или nick — дополнительные нужны для того, чтобы кнопку, уже сделанную для другого топа, можно было перенаправить сюда без изменений.

https://xtremetop300.com/vote/1234?player=Steve

Важные правила

  • Награда создаётся только за засчитанный голос. Голоса, которые наша защита от накрутки помечает как подозрительные, ничего не приносят, так что ферма не сможет выкачивать вашу игровую валюту, не затрагивая при этом рейтинг.
  • Один голос от игрока раз в 24 ч. — тот же лимит, что и в самом топе.
  • Считайте, что каждый токен может прийти больше одного раза. Сохраняйте токены выданных наград и игнорируйте повторы. Это единственное, что плагин обязан сделать правильно.
  • Никогда не подставляйте ник игрока напрямую в консольную команду. Мы разрешаем в никах только буквы, цифры, пробелы, точки, дефисы и подчёркивания, но ваш сервер всё равно должен проверить, что ник принадлежит реальному аккаунту, прежде чем выдавать награду.
  • Наград за один pull: по умолчанию 50, максимум 200. Следите за полем more и повторяйте запрос, если оно равно true.
  • Не более 60 запросов в минуту на один сервер. Опрос раз в минуту расходует один из них.
  • Невостребованные награды удаляются через 14 дн.

Ошибки

401
Ключ отсутствует или неверен. Проверьте префикс Bearer.
403
Ключ верный, но сервер не опубликован или награды отключены.
422
Параметр отсутствует или имеет неверный формат; какой именно — указано в теле ответа.
429
Слишком много запросов. Подождите и повторите — ничего не потеряется.

Ваш сервер уже в топе? Откройте настройки сервера, чтобы включить награды. Ещё не добавили сервер? Добавьте его.