Pular para o conteúdo

Pré-requisitos (capacidades A–D)

Antes de olhar uma rota, um payload ou um exemplo de assinatura, vale responder a uma pergunta mais básica: o seu servidor é capaz de integrar? Esta página é essa checagem. Ela lista o que o seu engine precisa saber fazer, agrupado em quatro capacidades — A, B, C e D. Nenhuma delas é preferência ou boa prática opcional: cada requisito existe porque a ausência dele quebra um fluxo, e vários custam dinheiro, porque estorno de compra é pago pela plataforma.

Para o dono de servidor que está avaliando: leia isto como uma lista de compras técnica. Você não precisa usar o nosso Agent nem o nosso Lua de referência — precisa entregar estas capacidades por algum caminho. O Agent e a referência são um caminho pronto para quem não quer construir do zero. Se o seu engine é da família TFS, boa parte disto já vem resolvida pela referência.

Esta é a capacidade que decide se você consegue integrar. Se o seu engine não tem criptografia de chave pública, a assinatura precisa nascer fora dele — e é exatamente o que o Agent faz.

#RequisitoSustentaSem ele
A1DEVE assinar JWS RS256 destacado em toda chamada de saída ao contratotodas as rotas servidor→plataformanenhuma rota é aceita — o contrato exige assinatura sempre, não só API key
A2DEVE nunca bloquear o loop principal do jogo esperando rede (transporte assíncrono)todasum freeze de rede congela todos os jogadores, não só quem integrou
A3DEVE manter o relógio sincronizado por NTPtodasa janela de frescor é apertada (exp − iat ≤ 300s, tolerância de 60s); relógio torto vira 401 intermitente e difícil de diagnosticar
A4DEVE expor um endpoint de webhook https que só verifica assinatura, nunca assina, e é idempotente por transactionIdwebhooks transfer_order e transaction_terminalou o endpoint aceita ordem forjada de transferir item, ou entrega em duplicata — a entrega é at-least-once

“O market precisa ser durável” é a intuição certa, mas imprecisa. Não é o market inteiro: são três durabilidades pontuais, cada uma uma âncora que precisa sobreviver a um evento específico.

#RequisitoSustentaSem ele
B1DEVE ter uma identidade estável do anúncio (itemRef) que sobrevive a um ciclo de save do servidorregistrar anúncio (POST /v1/api/market/listings), ajustar ou retirar anúncio (PUT /v1/api/market/listings), criar transação (POST /v1/api/transactions)o anúncio vira órfão no primeiro save; a plataforma não consegue mais reencontrá-lo para ajustar ou retirar
B2DEVE manter um registro local durável do que foi anunciado (itemRef ↔ oferta), independente da tabela nativa do marketajustar ou retirar anúnciosem memória do que foi anunciado, é impossível detectar que uma oferta sumiu e chamar WITHDRAW — nasce o anúncio fantasma
B3DEVE entregar em destino durável que sobrevive a rollback/crash (depot durável, nunca inventário de quem pode deslogar)confirmar entrega (POST /v1/api/transactions/{transactionId}/delivery), webhook transfer_orderitem entregue evapora num crash antes do próximo save; a transação fica concluída sem o item ter chegado

A frase para levar a cada uma:

  • B1 — sua chave de anúncio não pode derivar de um id que o seu servidor renumera. Descubra o que sobrevive a um ciclo de save antes de escolher a chave.
  • B2 — você precisa lembrar, do seu lado, o que anunciou, porque a sua tabela de market apaga a linha exatamente no evento a que você precisa reagir (venda por moeda do jogo, retirada, expiração).
  • B3 — item entregue tem de continuar entregue depois de um crash. Se o seu save só roda em logout, force um após a entrega, ou torne a entrega reconciliável pela consulta de transação.

A plataforma não gerencia estoque e não conhece o jogo. Quem arbitra item, posse e estoque é o seu servidor.

#RequisitoSustentaSem ele
C1DEVE verificar posse e estoque do item no seu lado; criar a oferta no market é o que reserva (tira o item do inventário do vendedor)registrar anúncioanúncio de item que o vendedor não tem; a falha aparece só na entrega, quando o dinheiro já se moveu
C2DEVE sincronizar o anúncio com o market do jogo: detectar consumo/retirada e chamar a rota de ajustar ou retirar anúncioajustar ou retirar anúncioa plataforma vende o que não existe mais → estorno, pago por ela
C3DEVE provar que a chamada veio de sessão de jogo viva nas rotas transportadas pelo client — nonce de uso único, TTL curtocriar transação, registrar anúncioqualquer um que capture um envelope anuncia ou compra em nome de outro
C4DEVE aplicar rate limit nas rotas transportadas pelo client antes de verificar a assinaturacriar transação, registrar anúncioverificar RS256 custa CPU; uma enxurrada de payloads inválidos vira negação de serviço mesmo sendo toda rejeitada

O client do jogo roda na máquina do jogador. Onde ele entra no fluxo, entra como transporte e UX — nunca como autoridade.

#RequisitoSustentaSem ele
D1DEVE manter nenhuma decisão de negócio no client; ele transporta o envelope assinado (que nasce no servidor) e desenha a UIcriar transação, registrar anúncioos módulos são texto puro no disco do jogador, sem assinatura nem verificação — autorizar ali é entregar a chave a quem edita o arquivo
D2DEVE converter o encoding na fronteira do client (a plataforma responde UTF-8; o client pode renderizar outro charset)qualquer texto exibido ao jogadora message em pt-BR chega com acento quebrado (Anúncio), e a mensagem pronta para exibir deixa de ser exibível

Tudo acima é engine-agnóstico. Num servidor TFS 0.3, cada requisito esbarra num gap concreto do engine — e o Lua e o módulo de client de referência já cobrem esses gaps. É o valor de partir da referência em vez do zero: o itemRef estável, o flush de save antes de ler o market, a fila em diretório em vez de tabela Lua compartilhada e a conversão de encoding já vêm resolvidos.

Sobram do seu lado apenas os requisitos que dependem de escolha de operação: C2 (sincronizar), C3 (nonce de sessão) e C4 (rate limit). Se o engine é de outra família, você reimplementa A–D pelo caminho dele, e a referência serve como exemplo executável do formato de assinatura e do modelo de fila.