Desenvolvedores

Saldo no servidor de jogo

Como o jogador vê o saldo, recarrega, compra com o saldo e liga a conta da loja dentro do jogo, com as rotas que o script do servidor chama.

Com o crédito ligado numa loja de servidor de jogo, a loja dentro do jogo mostra o saldo do jogador, vende os pacotes de recarga pelo Pix e deixa pagar a compra com o saldo, sem sair do jogo.

Qual carteira o jogador usa#

Dentro do jogo quem diz quem é o jogador é o servidor, pelo token. A carteira que vale é, nesta ordem:

  1. A da conta da loja que o jogador vinculou. O comprador gera um código na área dele (Minha conta > Créditos) e o jogador digita esse código no jogo. Isso prova os dois lados: quem é dono da conta e quem está no servidor. A partir daí o saldo é o mesmo no site e no jogo.
  2. A carteira do próprio jogador. Sem vínculo, vale a carteira do cliente que nasce das compras no jogo sem e-mail (o e-mail tipo.id@jogador.invalid, pelo identificador mais estável que o servidor mandou: UUID, license, Steam, Discord e assim por diante).

Uma compra no site com o ID do jogador digitado não liga nada: o ID digitado não prova quem é o jogador, e quem ganharia acesso ao saldo seria outra pessoa.

Quais identificadores contam#

O vínculo e a carteira seguem as contas que valem em qualquer servidor e que o jogo confere no login: license (ou license2) e Steam no FiveM e no RedM, UUID e gamertag no Minecraft. Quando o servidor manda alguma delas, só elas contam. O ID da cidade (vRP, Creative, citizenid) e o nick ficam de fora, porque mudam de dono: outra cidade da mesma loja, um reset da cidade ou um nick trocado dão o mesmo valor a outra pessoa. O Discord também fica de fora quando vem junto: o ID dele é público.

  • O ID, o nick e o Discord só contam quando o servidor não manda nenhuma conta global.
  • Vínculo antigo feito só pelo ID, pelo nick ou pelo Discord deixa de valer quando o servidor passa a mandar uma conta global do jogador. Ele vincula de novo, com um código novo.
  • O histórico de pedidos da loja do jogo (POST /shop/history) segue a mesma regra: cada jogador vê só os pedidos da conta global dele.

O vínculo por código#

  • O código tem 8 letras e números (ABCD-EFGH), vale 15 minutos e uma vez só. Gerar outro cancela o anterior, e cada conta gera no máximo 6 por hora.
  • As contas globais que o servidor atestou passam a apontar para a conta (o ID, o nick e o Discord só quando não vem nenhuma). Um identificador aponta para uma conta só: digitar o código de outra conta troca o vínculo.
  • O saldo que o jogador já tinha na carteira dele passa para a conta, com uma entrada de Transferência nos dois extratos.
  • Se uma recarga que passou para a conta for reembolsada ou contestada depois, o que a carteira do jogador não cobre sai da conta, até o valor que passou. A conta pode ficar com dívida e não gasta até ser acertada.
  • Carteira do jogador com dívida não vincula até a dívida ser acertada. Senão bastaria vincular para sair dela.
  • O cliente desfaz o vínculo na área dele. O jogador volta a usar a própria carteira.

Recarga e compra no jogo#

  • Recarga: os pacotes vêm em credit.packages do catálogo e são pagos pelo Pix normal (POST /shop/orders). O saldo cai na carteira que o jogador usa no jogo, seja qual for o e-mail digitado.
  • Compra com o saldo: POST /shop/credit/orders. O pedido sai pago na hora (ou em revisão, se o antifraude da loja segurar) e a entrega entra na fila do servidor como numa compra por Pix.
  • Produto por assinatura pago com o saldo dentro do jogo pede a conta vinculada: o link de cada renovação chega por e-mail.

O que a compra com saldo exige#

Regra Detalhe
Loja dentro do jogo ligada Em Servidor, no painel.
Crédito ligado e Gastar no jogo ligado Em Créditos. Com Gastar no jogo desligado, o jogador vê o saldo e recarrega, mas gasta só pelo site.
Servidor conectado agora O servidor precisa ter feito o /poll nos últimos 3 minutos. Token copiado para fora do servidor não compra.
Jogador atestado Os identificadores vêm do servidor, nunca da tela do jogador.
Saldo para o pedido inteiro e sem dívida Igual ao site.
Teto por minuto 3 compras com saldo por carteira e 30 pela loja inteira, a cada minuto.

Segurança#

  • O token de um servidor só enxerga a loja dele. Nenhuma rota recebe ID de loja, e um servidor nunca chega à carteira de outra loja.
  • O saldo é consultado e debitado com a carteira travada no banco: duas compras ao mesmo tempo nunca gastam o mesmo saldo.
  • O servidor atesta o jogador, então um servidor comprometido poderia gastar o saldo de um jogador da própria loja. A compra é entregue ao mesmo jogador, e o teto por minuto limita o estrago. Se o token vazar, troque em Servidor > Servidores: o antigo para na hora. Para tirar o risco de vez, desligue Gastar no jogo.

Rotas#

Endereço base, com o token do servidor no cabeçalho Authorization: Bearer psg_... (ou X-Promisse-Token):

Texto
https://api.promisse.store/api/v1/game

Valores em centavos de real, inclusive o saldo. O nome da moeda e a conversão vêm em name e unit_cents (quantos centavos vale um crédito): para mostrar em créditos, divida o valor por unit_cents.

O player é o mesmo da loja dentro do jogo: { "name": "Steve", "ids": { "license": "license:abc...", "discord": "discord:123..." } }, com os identificadores que o servidor leu do jogador.

GET /shop ganhou o campo credit, null com o crédito desligado:

JSON
{
  "credit": {
    "enabled": true,
    "name": "Coins",
    "unit_cents": 10,
    "game_spend": true,
    "packages": [
      { "id": "…", "name": "Pacote 500", "summary": "", "image_url": null, "price_cents": 5000, "credit_cents": 5500, "bonus_cents": 500, "once_per_buyer": false }
    ]
  }
}

O pacote não aparece em products: ele vai para o carrinho pelo id e é pago em POST /shop/orders, como qualquer produto.

Saldo do jogador#

POST /shop/credit

JSON
{ "player": { "name": "Steve", "ids": { "license": "license:abc" } } }

Resposta:

JSON
{ "enabled": true, "name": "Coins", "unit_cents": 10, "game_spend": true, "balance_cents": 3000, "blocked": false, "linked": false }

blocked é carteira com dívida (saldo negativo). linked diz se o jogador usa uma conta vinculada. Com o crédito ou a loja do jogo desligados, volta enabled: false.

Cotação com o saldo#

POST /shop/quote aceita "method": "credit". O total_cents vem com o preço do saldo (sem o desconto do Pix), e com o player a resposta traz:

JSON
{ "credit": { "enabled": true, "name": "Coins", "unit_cents": 10, "game_spend": true, "balance_cents": 3000, "blocked": false, "linked": false, "available": true, "reason": null } }

reason é o motivo pronto para mostrar quando available é falso: saldo insuficiente, carteira bloqueada, recarga no carrinho ou gasto no jogo desligado.

Comprar com o saldo#

POST /shop/credit/orders

JSON
{
  "items": [{ "product_id": "…", "quantity": 1 }],
  "player": { "name": "Steve", "ids": { "license": "license:abc" } },
  "coupon": null,
  "ip": "200.1.2.3",
  "accept_terms": true,
  "age_confirmed": true
}

Resposta 201:

JSON
{
  "order": { "id": "…", "number": 42, "status": "paid", "delivered": false, "total_cents": 1990, "expires_at": "2026-11-04T18:30:00.000Z", "paid_at": "2026-11-04T18:00:00.000Z", "link": "https://…/order/…?t=…" },
  "balance_cents": 1010,
  "blocked": false
}

Os itens seguem as mesmas regras de /shop/orders: até 20 linhas, 1000 unidades por linha e os campos extras do produto.

Vincular a conta#

POST /shop/credit/link

JSON
{ "player": { "name": "Steve", "ids": { "license": "license:abc" } }, "code": "ABCDEFGH" }

O código aceita com ou sem o hífen. Resposta:

JSON
{ "linked": true, "email": "ma*****@gmail.com", "balance_cents": 4000 }

Benefícios do jogador#

POST /benefits devolve os benefícios com prazo do jogador, o que o /vip mostra no jogo. Ele recebe o mesmo player das outras rotas e segue a regra de Quais identificadores contam: com license, Steam, UUID ou gamertag, só aparecem as compras dessa conta, e o ID da cidade repetido em outra cidade da loja não mostra o VIP de outra pessoa.

JSON
{ "player": { "name": "Steve", "ids": { "id": "808", "license": "license:abc" } } }

Resposta:

JSON
{ "benefits": [{ "product_name": "VIP Ouro", "expires_at": "2026-12-04T18:00:00.000Z" }] }

O GET /benefits?player=<identificador>, um identificador por consulta, continua valendo para os scripts que perguntam assim. As duas formas dividem o balde dos comandos dos jogadores (240 por minuto).

Erros#

HTTP code Quando
401 invalid_server_token Token do servidor errado, trocado ou de servidor excluído. Vale para todas as rotas.
429 game_token_flood Mais de 60 tokens errados do mesmo endereço em um minuto. O token certo continua passando, mesmo vindo desse endereço. Numa enxurrada (600 erros por minuto), passa o token do servidor que já vinha consultando.
400 shop_player_missing O servidor não mandou identificador do jogador, ou o produto pede um que ele não tem.
400 shop_subscription_email Assinatura paga com saldo por jogador sem conta vinculada.
400, 404 credit_link_invalid Código com formato errado, inexistente, já usado ou vencido.
409 shop_disabled Loja dentro do jogo desligada.
409 credit_disabled Crédito desligado na loja.
409 credit_game_off Gastar no jogo desligado.
409 server_offline O servidor não fez /poll nos últimos 3 minutos.
409 credit_debt Carteira com dívida (na compra ou no vínculo).
422 credit_insufficient Saldo menor que o total. Traz balance_cents e total_cents.
422 credit_topup_with_credit Carrinho com pacote de recarga pago com saldo.
429 credit_rate_limited Passou de 3 compras com saldo por minuto na carteira ou de 30 na loja.

Os limites por token: /shop/credit divide o balde da loja do jogo (180 por minuto), /shop/credit/orders tem 60 a cada 10 minutos e /shop/credit/link tem 30 a cada 10 minutos.

Scripts que já mostram o saldo#

Jogo Loja dentro do jogo Saldo, compra com saldo e vínculo
FiveM e RedM Recurso promisse_loja Sim, a partir da versão 1.1.0. Atualize também o recurso promisse (entrega), que libera as rotas novas para a loja. Com o promisse antigo, a tela mostra que não conseguiu falar com a loja.
Minecraft (Spigot e Paper) Plugin, com a loja no jogo Ainda não. A loja do plugin continua vendendo por Pix e ignora os pacotes.
MTA, Fabric, Bedrock e PocketMine Não têm loja dentro do jogo Não se aplica. A entrega de um pedido pago com saldo no site funciona igual.

Em qualquer jogo, o pedido pago com saldo pelo site chega à fila do servidor como um pedido pago por Pix: o script de entrega não precisa mudar.

Para levar o saldo a outro script, use as rotas acima com os mesmos cuidados da loja do FiveM: os identificadores sempre lidos do servidor, nunca do que o jogador digitou, e um limite por jogador antes de chamar a compra.