Como Implementar Compras no App (IAP) em Jogos

Aprenda a implementar compras no app em jogos: tipos de IAP, configuração na Google Play e App Store, código em Godot 4 e validação segura de compras.
Como Implementar Compras no App (IAP) em Jogos
Compras no app são o modelo de monetização dominante no mobile: o jogo é grátis pra baixar e a receita vem de quem compra moedas, skins, passes e afins lá dentro. O problema é que implementar IAP envolve três frentes ao mesmo tempo (console da loja, código do jogo e validação da compra), e tutorial nenhum costuma cobrir as três. Resultado: dev que faz a compra "funcionar" no teste e descobre em produção que a loja estornou tudo sozinha.
Esse guia cobre o caminho inteiro: os tipos de produto, a configuração na Google Play e na App Store, a arquitetura do código no Godot 4 e os erros que custam dinheiro de verdade. Não é o tipo de feature que você testa apertando play no editor, então a ordem das etapas importa mais do que de costume.
Os três tipos de compra no app
Antes de criar qualquer produto na loja, você precisa classificar o que está vendendo. As duas lojas trabalham com os mesmos três tipos, e escolher errado dá retrabalho:
Consumível: o jogador compra, usa e pode comprar de novo. Pacote de moedas, gemas, vidas extras, boosts. É o tipo que sustenta a maioria dos free-to-play, porque o mesmo jogador pode comprar dezenas de vezes. No código, exige um passo a mais: depois de entregar o item, você "consome" a compra pra liberar uma nova compra do mesmo produto.
Não consumível: compra única, permanente, atrelada à conta. Remover anúncios, desbloquear o jogo completo, uma skin específica. A loja lembra que aquele usuário comprou, e você precisa oferecer um botão de "restaurar compras" pra quando ele trocar de aparelho.
Assinatura: cobrança recorrente. Passe mensal, conteúdo VIP. É o tipo mais chato de implementar (renovação, cancelamento, período de carência) e só faz sentido se o seu jogo entrega valor recorrente de verdade. Minha opinião: se é o seu primeiro jogo com IAP, comece com consumíveis e não consumíveis. Assinatura vem depois, quando você já tem retenção que justifique.
Um detalhe de design que vale registrar desde já: defina os IDs dos produtos com um padrão e nunca mais mude. Algo como moedas_100, moedas_550, remover_anuncios. Esses IDs vão aparecer no console da loja, no seu código e no seu backend, e renomear depois é dor de cabeça garantida.
Configurando os produtos nas lojas
Essa parte é burocrática, mas é pré-requisito: o código só consegue listar e vender produtos que existem no console da loja.
Google Play Console
O cadastro de desenvolvedor na Google Play custa uma taxa única de US$ 25. Com a conta criada, o fluxo é:
- Crie o app no console e configure o perfil de pagamentos (sem conta de pagamentos ativa, a aba de monetização nem libera).
- Suba um build com a permissão de billing pra uma trilha de teste (internal testing serve). A Play Store só deixa criar produtos pra apps que já têm um build com
com.android.vending.BILLINGno manifest, e o plugin de billing já adiciona essa permissão por você. - Crie os produtos em Monetizar > Produtos > Produtos no app. Cada um recebe ID, nome, descrição e preço. Defina o preço base e a loja converte pras outras moedas (você pode ajustar país a país depois).
- Ative cada produto. Produto criado mas inativo não aparece pra venda. É um esquecimento clássico.
App Store Connect
Na Apple, o Apple Developer Program custa US$ 99 por ano. O caminho:
- Aceite os contratos em Agreements, Tax, and Banking e preencha os dados bancários e fiscais. Sem isso, IAP não funciona nem em sandbox.
- Crie os produtos dentro da página do app, na seção de In-App Purchases, com o mesmo cuidado nos IDs.
- Submeta os produtos pra revisão. Na Apple, cada produto passa por review junto com o binário. O primeiro IAP do app precisa ser enviado junto com uma versão nova do app.
Sobre as taxas: nos programas pra desenvolvedores menores, as duas lojas cobram 15% sobre a receita até US$ 1 milhão por ano, e o modelo padrão acima disso é 30%. Coloque isso na conta quando for precificar: de cada R$ 10 que o jogador paga, uma fatia relevante nunca chega em você.
Implementando compras no app no Godot
No Godot 4, IAP de Android passa pelo plugin oficial de Google Play Billing (mantido no GitHub da organização godot-sdk-integrations), e o iOS tem plugin próprio de in-app purchase. Os dois só existem no build de verdade rodando no dispositivo, e é daí que vem a primeira regra de arquitetura: isole todo o código de loja num autoload e nunca chame o plugin direto do resto do jogo.
O motivo é prático. No editor, o plugin não existe. Se a sua loja de moedas chama o singleton de billing direto, o jogo quebra em qualquer plataforma que não seja a do plugin. Com um autoload no meio, o resto do jogo só conhece sinais e métodos seus:
extends Node
# Autoload: IAPManager
# O resto do jogo só conversa com este script, nunca com o plugin.
signal compra_concluida(product_id: String)
signal compra_falhou(product_id: String, motivo: String)
var billing = null
func _ready() -> void:
if Engine.has_singleton("GodotGooglePlayBilling"):
billing = Engine.get_singleton("GodotGooglePlayBilling")
# Aqui você conecta os sinais do plugin (conexão, compra,
# consumo) conforme o README da versão que instalou.
else:
# Editor, desktop, build sem o plugin: a loja fica desativada
# e o jogo continua funcionando normalmente.
print("Billing indisponível nesta plataforma.")
func loja_disponivel() -> bool:
return billing != null
O Engine.has_singleton() é o jeito documentado de detectar plugin de plataforma no Godot, e esse if é o que deixa o mesmo projeto rodar no editor, no desktop e no Android sem gambiarra. Os nomes exatos dos métodos e sinais do plugin variam entre versões, então trate o README do plugin como fonte da verdade na hora de conectar, não um post de blog (incluindo este).
Entregando o produto sem entregar duas vezes
A parte que mais derruba implementação de IAP não é iniciar a compra, é processar o resultado. As lojas podem reentregar uma compra que você já processou: o jogo fechou no meio do fluxo, o jogador reinstalou, a conexão caiu na hora errada. Se o seu código soma as moedas toda vez que recebe o evento, você entrega em dobro. Se ignora a reentrega, o jogador pagou e não recebeu.
A solução é idempotência: cada compra vem com um identificador único da transação (na Play Store, o order ID; o token de compra também serve como chave). Guarde os que você já processou e cheque antes de entregar:
const PACOTES_MOEDAS := {
"moedas_100": 100,
"moedas_550": 550,
"moedas_1200": 1200,
}
var pedidos_processados: Array = []
func processar_compra(order_id: String, product_id: String) -> void:
if order_id in pedidos_processados:
return # a loja reentregou algo que já foi pago e entregue
if product_id in PACOTES_MOEDAS:
Save.moedas += PACOTES_MOEDAS[product_id]
elif product_id == "remover_anuncios":
Save.sem_anuncios = true
else:
compra_falhou.emit(product_id, "produto desconhecido")
return
pedidos_processados.append(order_id)
Save.salvar() # salve ANTES de consumir/confirmar na loja
compra_concluida.emit(product_id)
A ordem dos passos no comentário final não é detalhe. O fluxo seguro é: receber a compra, entregar o item, salvar no disco, e só então consumir (consumível) ou confirmar (não consumível) na loja. Se o jogo crashar antes de consumir, a loja reentrega na próxima abertura e o seu check de idempotência segura a duplicata. Se você consumir antes de salvar e crashar no meio, o jogador pagou por nada.
E um aviso que vale dinheiro: na Google Play, compra não confirmada (acknowledged) é estornada automaticamente pela loja depois de alguns dias. Se os seus testers compram, recebem o item e dias depois o dinheiro volta sozinho, é quase certeza que o seu código está pulando a confirmação.
Validação: nunca confie no cliente
Tudo que roda no aparelho do jogador pode ser adulterado. Existem apps inteiros dedicados a forjar compras em jogos Android, e um jogo que entrega o produto só porque o cliente disse "comprei" é alvo fácil.
A defesa de verdade é validar no servidor: o jogo manda o token da compra pro seu backend, o backend consulta a API da loja (Google Play Developer API ou App Store Server API) perguntando se aquela transação existe e está paga, e só então autoriza a entrega. Pra jogo com economia online, ranking ou qualquer coisa competitiva, isso não é opcional.
Pra um jogo single-player offline, seja honesto com o trade-off: montar backend só pra isso pode não compensar, e a validação local (a própria biblioteca da loja já confere a assinatura da compra) filtra o ataque preguiçoso. O pirata dedicado vai passar, mas ele não ia pagar de qualquer jeito. Decida pelo dano real que uma compra forjada causa no seu jogo, não por princípio.
Testando sem gastar dinheiro de verdade
As duas lojas têm ambiente de teste, e você deve viver nele antes do lançamento:
- Google Play: cadastre contas como license testers no console. Essas contas compram com cartão de teste, sem cobrança real, num build distribuído por trilha de teste. Teste o caminho feliz, mas teste principalmente os feios: fechar o app no meio da compra, comprar sem internet, comprar duas vezes seguidas.
- App Store: crie contas de sandbox no App Store Connect e use num device de verdade. O fluxo de sandbox da Apple tem fama justa de ser temperamental, então reserve tempo pra ele.
O teste que separa implementação amadora de profissional é o de interrupção: inicie uma compra, mate o app antes da confirmação, reabra. O item chegou? Chegou uma vez só? Se sim pros dois, o seu fluxo de reentrega e idempotência funciona.
O preço de errar o design
Implementação é metade do problema. A outra metade é o que você vende. Dois princípios que defendo pra quem está começando:
Venda progresso opcional, não a saída de uma frustração que você criou. Jogador percebe quando o jogo foi desenhado pra doer até ele pagar, e a review da loja vira o lugar onde ele conta isso pra todo mundo.
Poucos produtos, claros. Três pacotes de moedas com proporção honesta entre preço e quantidade convertem melhor que quinze SKUs confusos, e dão muito menos manutenção nos consoles das lojas.
Fechando
Compras no app têm uma ordem certa: classifique os produtos (consumível, não consumível, assinatura), cadastre nas lojas com IDs que você nunca vai renomear, isole o billing num autoload, entregue com idempotência, salve antes de consumir e confirme toda compra pra loja não estornar. Validação server-side entra quando o dano de uma compra forjada justifica o backend.
Nada disso é difícil isoladamente. O que quebra os projetos é tratar IAP como feature de fim de projeto, encaixada na última semana. Monte o fluxo cedo, num build de teste, com license testers, e lance sabendo que o caminho do dinheiro funciona. É a única parte do jogo em que um bug devolve o pagamento do jogador sozinho.


