Desenvolvedores

Integrações e widget público para divulgar servidores.

Visão geral

O GameServers BR permite que o criador de um servidor gere um widget incorporável (iframe ou script) para exibir métricas públicas em tempo quase real, com proteção por token e limite de requisições.

Importante
O embed público (para sites externos) só funciona quando o servidor está aprovado e não bloqueado. No painel do criador existe uma prévia interna (apenas para o dono/staff), mesmo se o servidor estiver pendente/reprovado.

O que o widget exibe

  • Quantidade total de views (views_total)
  • Avaliação média (rating_avg) e total de avaliações (rating_count)
  • Número total de votos (votes_total)
  • Contadores por período: votos (24h / 7d / 30d)
  • Status online/offline (opcional; depende de IP/porta configurados)

Exemplos responsivos (iframe)

Use estes padrões para o widget se adaptar ao seu layout.

1) Largura fluida (recomendado)
<div style="max-width: 360px;">
  <iframe
    src="https://GAMESERVERSBR.COM.BR/widget/servidor/ID/TOKEN?theme=dark&lang=pt"
    style="width:100%; height:260px; border:0;"
    loading="lazy"
  ></iframe>
</div>
2) Centralizado em landing page
<div style="display:flex; justify-content:center;">
  <iframe
    src="https://GAMESERVERSBR.COM.BR/widget/servidor/ID/TOKEN"
    width="320"
    height="260"
    style="border:0;"
  ></iframe>
</div>
Obs.: o widget tem altura fixa; ajuste height conforme necessário.

Playground (gera o código automaticamente)

Preencha os campos abaixo para gerar o snippet pronto e visualizar uma prévia.

Métricas
Snippet
Prévia
A prévia usa iframe (mesmo quando o tipo é script).

Como ativar (criador do servidor)

  1. Acesse: Painel → Painel do servidor
  2. Abra a aba Webhook/Widget
  3. Ative o webhook/widget, ajuste tema/idioma/métricas e copie o código
  4. Se o link/token vazar, use Regenerar token

Opção 1: incorporar via iframe (recomendado)

Compatível com a maioria dos sites e fóruns que aceitam iframe.

Exemplo
<iframe
  src="https://GAMESERVERSBR.COM.BR/widget/servidor/ID/TOKEN?theme=dark&lang=pt"
  width="320"
  height="260"
  style="border:0;"
  loading="lazy"
  referrerpolicy="no-referrer-when-downgrade"
></iframe>
Dica: ajuste width e height para encaixar no seu layout.
Parâmetros (querystring)
  • theme: dark ou light
  • lang: pt ou en
  • refresh: 15 a 600 (segundos)
  • metrics: lista separada por vírgula (ex.: views,rating,votes_total,status)

Opção 2: incorporar via script (quando iframe não é permitido)

Esta opção insere um iframe automaticamente no local do script. Use quando o site aceitar <script>, mas não permitir colar <iframe> diretamente.

<div data-wyd-widget="1"></div>
<script async src="https://GAMESERVERSBR.COM.BR/widget/servidor/ID/TOKEN.js?theme=dark&lang=pt"></script>
Parâmetros extras no .js: width, height, metrics, refresh.

Segurança e limites

  • Assinatura ativa: o widget público requer que o servidor tenha assinatura ativa e que o plano inclua webhook/widget.
  • Token por servidor: a URL do widget inclui um token. Se vazar, regenere no painel.
  • Ativar/desativar: o criador pode desativar o widget a qualquer momento.
  • Rate limit: limite por IP/servidor para evitar abuso.
  • Status online: é opcional e cacheado (para reduzir carga).

Dados (JSON) para integração externa

O widget consome um endpoint JSON. Você pode usar o mesmo endpoint para integrações customizadas.

Endpoint
GET https://GAMESERVERSBR.COM.BR/widget/servidor/ID/TOKEN/data
Dica: em prévias internas (painel do dono/staff) o app pode usar ?preview=1. Em sites externos, o modo preview não deve ser usado.
Exemplo de payload
{
  "server": {
    "id": 123,
    "title": "Meu Servidor",
    "slug": "meu-servidor",
    "url": "https://GAMESERVERSBR.COM.BR/servidor/meu-servidor"
  },
  "metrics": {
    "views_total": 1024,
    "votes_total": 250,
    "rating_avg": 4.65,
    "rating_count": 57,
    "votes_day": 12,
    "votes_week": 60,
    "votes_month": 180,
    "status": {
      "online": true,
      "checked_at": 1730000000,
      "latency_ms": 42
    }
  },
  "generated_at": "2026-01-18T00:00:00Z"
}

API pública (sem login)

Endpoints públicos para ranking e perfis. Retornam JSON e não usam cookies.

Atenção
Algumas rotas podem ficar temporariamente indisponíveis por manutenção/segurança. Nesses casos, a resposta pode ser 503 com {"error":"route_disabled"}.
1) Ranking de usuários
GET https://GAMESERVERSBR.COM.BR/api/rankings/users?period=daily&metric=xp&page=1&per_page=50
Parâmetros: period (daily/weekly/monthly/all), metric (xp/votes/reviews/referrals), start (YYYY-mm-dd), page, per_page.
2) Perfil público (gamification)
GET https://GAMESERVERSBR.COM.BR/api/profile/USERNAME
Erros comuns
  • 404 not_found: username inexistente
  • 503 route_disabled: rota temporariamente indisponível

API (login externo)

Para integrações externas que precisam autenticar um usuário do GameServers BR, disponibilizamos um login via API que retorna um token Bearer.

Recomendado
Use HTTPS em produção. Esta API foi desenhada para uso server-to-server.
Dica: para integrações em browser (front-end), pode ser necessário CORS. Para maior segurança, prefira integração server-to-server.

1) Login (gera token)
POST https://GAMESERVERSBR.COM.BR/api/auth/login
Content-Type: application/json
Exemplos
<?php
$baseUrl = 'https://GAMESERVERSBR.COM.BR';

$payload = [
  'email' => 'usuario@exemplo.com',
  'password' => 'SENHA_DO_USUARIO',
  'twofa_code' => '123456',
  'label' => 'Meu App',
  'ttl_days' => 30,
];

$ch = curl_init($baseUrl . '/api/auth/login');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);

$body = curl_exec($ch);
$status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status !== 200) {
  echo "Login falhou: HTTP {$status}\n{$body}\n";
  exit(1);
}

$data = json_decode((string)$body, true);
$token = (string)($data['access_token'] ?? '');
echo "TOKEN: {$token}\n";
const baseUrl = 'https://GAMESERVERSBR.COM.BR';

const loginRes = await fetch(baseUrl + '/api/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'usuario@exemplo.com',
    password: 'SENHA_DO_USUARIO',
    twofa_code: '123456',
    label: 'Meu App',
    ttl_days: 30,
  }),
});

const loginJson = await loginRes.json();
if (!loginRes.ok) throw new Error(JSON.stringify(loginJson));

const token = loginJson.access_token;

const meRes = await fetch(baseUrl + '/api/me', {
  headers: { Authorization: `Bearer ${token}` },
});

const meJson = await meRes.json();
console.log(meJson.user);
// Exemplo: pequeno server Crow que faz login via API e devolve o token.
// Dependências sugeridas: Crow + libcurl

#include <crow.h>
#include <curl/curl.h>

static size_t writeCb(void* contents, size_t size, size_t nmemb, void* userp) {
  ((std::string*)userp)->append((char*)contents, size * nmemb);
  return size * nmemb;
}

static std::pair<long, std::string> postJson(const std::string& url, const std::string& jsonBody) {
  CURL* curl = curl_easy_init();
  if (!curl) return {0, ""};

  std::string out;
  struct curl_slist* headers = nullptr;
  headers = curl_slist_append(headers, "Content-Type: application/json");

  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonBody.c_str());
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCb);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);

  curl_easy_perform(curl);

  long status = 0;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
  curl_slist_free_all(headers);
  curl_easy_cleanup(curl);

  return {status, out};
}

int main() {
  crow::SimpleApp app;
  const std::string baseUrl = "https://GAMESERVERSBR.COM.BR";

  CROW_ROUTE(app, "/login")([]{
    const std::string body = R"({
      \"email\":\"usuario@exemplo.com\",
      \"password\":\"SENHA_DO_USUARIO\",
      \"twofa_code\":\"123456\",
      \"label\":\"Meu App\",
      \"ttl_days\":30
    })";

    auto [status, resp] = postJson(baseUrl + "/api/auth/login", body);
    return crow::response((int)status, resp);
  });

  app.port(18080).multithreaded().run();
}
Dica: em produção, não armazene senha no código; use env/secret manager e prefira server-to-server.
Body (JSON)
{
  "email": "usuario@exemplo.com",
  "password": "SENHA_DO_USUARIO",
  "twofa_code": "123456",
  "label": "Meu App",
  "ttl_days": 30
}
twofa_code: opcional; exigido quando o usuário tem 2FA ativo (aceita 6 dígitos TOTP ou backup code).
ttl_days: opcional (1 a 90). Padrão: 30.
Resposta (200)
{
  "token_type": "Bearer",
  "access_token": "TOKEN_AQUI",
  "expires_at": "2026-02-20 12:00:00",
  "user": {
    "id": 123,
    "name": "Nome do Usuário",
    "email": "usuario@exemplo.com",
    "role": "user",
    "username": "meuusername"
  }
}
2) Quem sou eu (/me)
GET https://GAMESERVERSBR.COM.BR/api/me
        Authorization: Bearer TOKEN_AQUI
3) Logout (revoga token)
POST https://GAMESERVERSBR.COM.BR/api/auth/logout
        Authorization: Bearer TOKEN_AQUI
4) Refresh (rotaciona token)
Emite um token novo e revoga o token atual. Use para manter sessões longas sem armazenar senha.
POST https://GAMESERVERSBR.COM.BR/api/auth/refresh
Authorization: Bearer TOKEN_AQUI
Content-Type: application/json
{
  "label": "Meu App (refresh)",
  "ttl_days": 30
}
Erros comuns
  • 401 invalid_credentials: email/senha incorretos
  • 401 twofa_required: conta com 2FA ativo (envie twofa_code)
  • 401 twofa_invalid: código 2FA inválido ou reutilizado
  • 403 account_inactive: conta desativada/pendente
  • 429 rate_limited: muitas tentativas (aguarde)
  • 503 route_disabled: rota temporariamente indisponível

Integração: resgate de votos (donos de servidor)

Este fluxo permite que o dono do servidor consulte votos recentes e os resgate (1x por voto), desde que o usuário tenha o servidor favoritado. Isso é útil para converter votos em moeda/benefício no seu site/game.

Regras
  • Requer Authorization: Bearer ... (token do dono do servidor)
  • Scopes necessários: servers:votes:read e/ou servers:votes:redeem
  • O voto só é elegível se o usuário tiver favoritado o servidor
  • Cada voto pode ser resgatado apenas 1 vez
1) Listar votos elegíveis (pendentes)
GET https://GAMESERVERSBR.COM.BR/api/servers/SERVER_ID/votes/pending?limit=50
Authorization: Bearer TOKEN_AQUI
2) Resgatar um voto
POST https://GAMESERVERSBR.COM.BR/api/servers/SERVER_ID/votes/VOTE_ID/redeem
Authorization: Bearer TOKEN_AQUI
Content-Type: application/json
{
  "external_ref": "order-12345",
  "amount": 100,
  "currency": "GOLD",
  "metadata": {
    "game_user": "NickNoJogo"
  }
}
Exemplos
<?php
$baseUrl = 'https://GAMESERVERSBR.COM.BR';
$serverId = 123;
$voteId = 456;
$token = 'TOKEN_AQUI';

function httpJson(string $method, string $url, array $headers, ?array $body = null): array {
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  if ($body !== null) {
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
  }
  $resp = (string)curl_exec($ch);
  $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);
  return [$status, $resp];
}

// 1) pending
[$st1, $b1] = httpJson('GET', $baseUrl . "/api/servers/{$serverId}/votes/pending?limit=50", [
  'Authorization: Bearer ' . $token,
]);
echo "pending HTTP {$st1}\n{$b1}\n";

// 2) redeem
[$st2, $b2] = httpJson('POST', $baseUrl . "/api/servers/{$serverId}/votes/{$voteId}/redeem", [
  'Authorization: Bearer ' . $token,
  'Content-Type: application/json',
], [
  'external_ref' => 'order-12345',
  'amount' => 100,
  'currency' => 'GOLD',
  'metadata' => ['game_user' => 'NickNoJogo'],
]);
echo "redeem HTTP {$st2}\n{$b2}\n";
const baseUrl = 'https://GAMESERVERSBR.COM.BR';
const serverId = 123;
const voteId = 456;
const token = 'TOKEN_AQUI';

// 1) pending
const pendingRes = await fetch(`${baseUrl}/api/servers/${serverId}/votes/pending?limit=50`, {
  headers: { Authorization: `Bearer ${token}` },
});
const pendingJson = await pendingRes.json();
console.log('pending', pendingRes.status, pendingJson);

// 2) redeem
const redeemRes = await fetch(`${baseUrl}/api/servers/${serverId}/votes/${voteId}/redeem`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    external_ref: 'order-12345',
    amount: 100,
    currency: 'GOLD',
    metadata: { game_user: 'NickNoJogo' },
  }),
});
const redeemJson = await redeemRes.json();
console.log('redeem', redeemRes.status, redeemJson);
// Exemplo: endpoints no seu app Crow que chamam a API do portal.
// Dependências sugeridas: Crow + libcurl

#include <crow.h>
#include <curl/curl.h>

static size_t writeCb(void* contents, size_t size, size_t nmemb, void* userp) {
  ((std::string*)userp)->append((char*)contents, size * nmemb);
  return size * nmemb;
}

static std::pair<long, std::string> http(const std::string& method, const std::string& url, const std::string& bearer, const std::string& bodyJson) {
  CURL* curl = curl_easy_init();
  if (!curl) return {0, ""};

  std::string out;
  struct curl_slist* headers = nullptr;
  headers = curl_slist_append(headers, ("Authorization: Bearer " + bearer).c_str());
  if (method == "POST") headers = curl_slist_append(headers, "Content-Type: application/json");

  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str());
  if (method == "POST") curl_easy_setopt(curl, CURLOPT_POSTFIELDS, bodyJson.c_str());
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCb);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);

  curl_easy_perform(curl);

  long status = 0;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
  curl_slist_free_all(headers);
  curl_easy_cleanup(curl);
  return {status, out};
}

int main() {
  crow::SimpleApp app;
  const std::string baseUrl = "https://GAMESERVERSBR.COM.BR";

  // GET /pending?server_id=123
  CROW_ROUTE(app, "/pending")([&](const crow::request& req) {
    const std::string bearer = req.get_header_value("x-token"); // passe o token por header no seu app
    const std::string sid = req.url_params.get("server_id") ? req.url_params.get("server_id") : "0";
    auto [st, body] = http("GET", baseUrl + "/api/servers/" + sid + "/votes/pending?limit=50", bearer, "");
    return crow::response((int)st, body);
  });

  // POST /redeem?server_id=123&vote_id=456
  CROW_ROUTE(app, "/redeem").methods(crow::HTTPMethod::POST)([&](const crow::request& req) {
    const std::string bearer = req.get_header_value("x-token");
    const std::string sid = req.url_params.get("server_id") ? req.url_params.get("server_id") : "0";
    const std::string vid = req.url_params.get("vote_id") ? req.url_params.get("vote_id") : "0";
    const std::string body = R"({\"external_ref\":\"order-12345\",\"amount\":100,\"currency\":\"GOLD\",\"metadata\":{\"game_user\":\"NickNoJogo\"}})";
    auto [st, out] = http("POST", baseUrl + "/api/servers/" + sid + "/votes/" + vid + "/redeem", bearer, body);
    return crow::response((int)st, out);
  });

  app.port(18081).multithreaded().run();
}
Dica: trate o token como segredo (não exponha no front-end) e prefira rodar isso em back-end.
Erros comuns
  • 401 unauthorized: token inválido/expirado
  • 403 insufficient_scope: faltam scopes no token
  • 403 forbidden: servidor não pertence ao token
  • 404 vote_not_found: voto não existe ou não pertence ao servidor
  • 409 favorite_required: usuário não favoritou o servidor
  • 409 already_redeemed: voto já resgatado
  • 503 route_disabled: rota temporariamente indisponível

Webhook Mercado Pago (pagamentos/assinaturas)

Endpoint para receber eventos do Mercado Pago. Em produção, este webhook é fail-closed se não houver token/assinatura configurados.

Endpoint
POST https://GAMESERVERSBR.COM.BR/webhooks/mercadopago
Autenticação suportada
  • Token compartilhado: enviar ?token=... ou header x-webhook-token
  • Assinatura: headers x-signature + x-request-id (HMAC SHA256) quando configurada no painel do Mercado Pago

Comportamento (segurança)
  • 403 webhook_not_configured: em produção quando não existe token nem assinatura configurados
  • 401 unauthorized: token/assinatura inválidos quando proteção está configurada
  • 200 ok: evento aceito (mesmo que o evento não gere ação local)
Observação: em ambiente de desenvolvimento, eventos com live_mode=false podem ser tolerados para facilitar testes.

FAQ (problemas comuns)

Verifique se o criador ativou o webhook/widget no painel e se o token está correto. Se o token tiver sido regenerado, o embed antigo deixa de funcionar.
Se você estiver tentando incorporar em site externo, confirme também que o servidor está aprovado e não bloqueado (caso contrário o endpoint retorna Not found).

Use a opção script (.js), que injeta um iframe automaticamente. Se nem script for permitido, a alternativa é usar apenas um link/botão para a página do servidor.

O status é opcional. Para funcionar, o servidor precisa ter IP e porta preenchidos no cadastro e a métrica status deve estar habilitada.

Para evitar abuso e reduzir carga. O widget tem cache e atualiza periodicamente; não use refresh muito baixo.