Guia de configuração — Fazendinha Retrô

Do zero até o jogo publicado no Firebase, com domínio próprio, na Google Play (com pagamentos) e no iPhone.

0 de 0 passos

Antes de começar

Leia esta aba inteira uma vez. Cada passo das próximas abas tem uma caixinha: marque quando terminar (fica salvo neste navegador).

Como o projeto é feito

Jogo

Site feito em React + TypeScript + Phaser, gerado pelo Vite na pasta dist/. Funciona sozinho no navegador (salva no aparelho) e, com o Firebase configurado, também na nuvem.

Painel do administrador

Página separada em /admin/ (não vai junto no jogo). Login com e-mail/senha ou Google, só para quem tem papel de superadmin, admin ou suporte.

Servidor

Firebase: Hosting (site), Authentication (contas), Firestore (dados), Cloud Functions (banir, papéis, conferir compras, estatísticas).

Android

TWA (Trusted Web Activity) gerado pelo Bubblewrap: um app da Play Store que abre o site publicado em tela cheia. Pagamentos pelo Google Play Billing.

iPhone

TWA não existe no iPhone. Na App Store o caminho é o Capacitor (precisa de Mac), e a Apple exige o pagamento dela (StoreKit). Veja a aba iPhone.

Uma base só

O mesmo código serve web, Android e iPhone. Atualizou o site, o app Android já mostra a versão nova (não precisa reenviar à loja).

O que você vai precisar

ItemCustoPara quê
Conta GooglegrátisFirebase, Play Console
Firebase plano Blaze (pago por uso)tem cota grátis; precisa de cartãoCloud Functions e notificações do Google Play
Domínio próprio (fazendinharetro.com)GoDaddy, registro.br ou outroEndereço do jogo e do app Android
Conta de desenvolvedor Google PlayUS$ 25 (uma vez)Publicar o app Android
Perfil de pagamentos no Play ConsolegrátisVender itens no app e receber
Apple Developer Program + um Mac com XcodeUS$ 99 por anoSó se for publicar no iPhone
Computador com Node.js 22 e GitgrátisRodar os comandos
Java JDK 17 e Android SDKgrátis (o Bubblewrap instala)Gerar o app Android
Segurança, regra de ouroA chave de API do Firebase (VITE_FIREBASE_API_KEY) é pública por natureza e vai no site. Quem protege os dados são as regras do Firestore e as Cloud Functions, que já estão prontas neste projeto. Os arquivos que nunca vão para o Git: a chave de assinatura do Android (*.keystore), senhas e o arquivo functions/.env.*.

Mapa do projeto

Arquivo / pastaO que é
.env.exampleModelo das chaves do Firebase do site (copie para .env)
firebase.jsonHosting (site + /admin), regras, functions, emuladores
firestore.rules / firestore.indexes.jsonQuem pode ler e escrever cada dado / índices das buscas do painel
functions/Cloud Functions (TypeScript): banir, papéis, compras, estatísticas
src/admin/ e admin/index.htmlPainel do administrador
src/services/billing.tsPagamentos do Google Play no jogo (Digital Goods API)
public/.well-known/assetlinks.jsonLiga o site ao app Android (sem ele, aparece a barra do navegador)
public/manifest.webmanifest e public/icons/Nome, cores e ícones do app
twa/twa-manifest.jsonRespostas de referência para o Bubblewrap

Configurar o Firebase

Cria o servidor: contas, banco de dados e as funções do painel. Leva uns 40 minutos.

1Instale as ferramentas

Instale o Node.js 22 (LTS) e depois, no terminal:

npm install -g firebase-tools
firebase login

Baixe o projeto e instale as dependências:

git clone https://github.com/jamesrmoro/toninho-da-horta.git
cd toninho-da-horta
npm install
npm --prefix functions install

2Crie o projeto no Firebase

  1. Abra o Console do Firebase e clique em Criar um projeto.
  2. Nome: toninho-da-horta (o ID final aparece embaixo do nome, anote: é o ID do projeto).
  3. Google Analytics: pode ativar (opcional).

3Mude para o plano Blaze

No canto de baixo à esquerda, Fazer upgrade → Blaze. Ele é obrigatório para Cloud Functions e para receber as notificações do Google Play. Um jogo pequeno costuma ficar dentro da cota grátis.

Evite sustosNo Google Cloud, em Faturamento → Orçamentos e alertas, crie um alerta de, por exemplo, R$ 50 por mês.

4Registre o app Web e copie as chaves

  1. Na página inicial do projeto, clique no ícone </> (Web), apelido "Fazendinha Retrô site". Não marque o Hosting aqui.
  2. Ele mostra um firebaseConfig. Copie o arquivo de modelo e preencha com esses valores:
cp .env.example .env.production
VITE_FIREBASE_API_KEY=AIza...
VITE_FIREBASE_AUTH_DOMAIN=SEU-PROJETO.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=SEU-PROJETO
VITE_FIREBASE_APP_ID=1:123456:web:abc...
VITE_FIREBASE_STORAGE_BUCKET=SEU-PROJETO.firebasestorage.app
VITE_FIREBASE_FUNCTIONS_REGION=southamerica-east1

Esse arquivo é só da produção (usado no npm run build e no npm run deploy) e fica fora do Git. No seu computador, o jogo nunca se conecta à produção: veja a aba 3, Dev e produção.

Já criou um arquivo .env?Renomeie para .env.production: mv .env .env.production

5Ative os tipos de login (cadastro, login, esqueci a senha e Google)

Authentication → Começar → Método de login (Sign-in method) e ative os dois (para jogar é preciso entrar com uma conta):

  • E-mail/senha: ligue só a primeira chave ("E-mail/senha"); a do "link por e-mail" pode ficar desligada. É o que faz funcionar Criar conta, Entrar com e-mail e Esqueci a senha.
  • Google: ative, escolha o nome público do projeto (Fazendinha Retrô) e o e-mail de suporte (o seu). Salve.

No jogo, ao tocar em Jogar, a pessoa entra ou cria a conta antes de começar:

  • Criar conta: nome, e-mail e senha (mínimo 6). O Firebase manda um e-mail para confirmar o endereço.
  • Entrar com e-mail e Esqueci a senha: o link chega por e-mail e abre uma página do próprio Firebase para criar a senha nova.
  • Entrar com Google: abre a janela do Google; no app instalado, se a janela for bloqueada, entra pela própria página.
E-mails em português e com o nome do jogoAuthentication → Modelos (Templates):
  1. Em cada modelo (Verificação de endereço de e-mail e Redefinição de senha), clique no lápis e troque o Nome do remetente para Fazendinha Retrô.
  2. Em Idioma do modelo, escolha Português (Brasil) (o jogo já pede os e-mails em português).
  3. Depois de ligar o domínio (aba 4), em Personalizar URL de ação use https://jogo.fazendinharetro.com/__/auth/action, assim o link do e-mail abre no seu endereço.
A tela do Google mostra "Toninho" ou o ID do projeto?Troque o nome em Google Cloud → APIs e serviços → Tela de consentimento OAuth: Nome do app Fazendinha Retrô, logo (use docs/logos/logo-quadrado-120.png, 120×120), e-mail de suporte, Página inicial https://fazendinharetro.com, Política de Privacidade https://fazendinharetro.com/privacidade, Termos de Serviço https://fazendinharetro.com/termos e, em Domínios autorizados, fazendinharetro.com.

6Crie o banco de dados (Firestore)

Firestore Database → Criar banco de dados → local southamerica-east1 (São Paulo) → modo de produção. As regras certas são enviadas no passo 9.

7Ligue o projeto à pasta

firebase use --add

Escolha o seu projeto na lista e, quando ele pedir um apelido (alias), digite default. Isso cria o arquivo .firebaserc (ele fica fora do Git).

Se preferir criar o arquivo direto (troque pelo ID do seu projeto, tudo em minúsculas):

printf '{ "projects": { "default": "seu-projeto-id" } }\n' > .firebaserc

8Diga quem é o superadmin

Crie o arquivo functions/.env.SEU-PROJETO (veja functions/.env.example):

SUPERADMIN_EMAILS=seu.email@gmail.com
PLAY_PACKAGE_NAME=app.toninhodahorta.twa

Quem estiver nessa lista vira superadmin no primeiro login no painel (o e-mail precisa ser verificado: use Entrar com Google). Pode ter mais de um, separados por vírgula.

9Publique tudo

npm run deploy

(É o mesmo que npm run build seguido de firebase deploy.) Isso envia o site, o painel, as regras, os índices e as funções. Na primeira vez o Firebase pode pedir para ativar algumas APIs (Cloud Build, Artifact Registry, Cloud Scheduler, Pub/Sub, Eventarc): aceite e rode de novo se der erro.

Se der erro de IAM ("We failed to modify the IAM policy")Confira com firebase login:list se está logado com a conta Proprietária do projeto e tente de novo depois de 5 minutos. Se persistir, abra o Cloud Shell (com o projeto selecionado no topo) e cole, trocando o ID na primeira linha:
PROJECT_ID=seu-projeto-id
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com" --role="roles/iam.serviceAccountTokenCreator"
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" --role="roles/run.invoker"
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" --role="roles/eventarc.eventReceiver"
Depois rode npm run deploy de novo. Enquanto isso, o site, o painel e as regras já podem ir: firebase deploy --only hosting,firestore.
EndereçosJogo: https://SEU-PROJETO.web.app
Painel: https://SEU-PROJETO.web.app/admin/

9bLiberar o seu acesso ao painel (sem Cloud Functions)

O painel fica em https://jogo.fazendinharetro.com/admin/ e só abre para quem está na lista da equipe. Sem o plano Blaze, a lista é feita à mão no Firestore:

  1. Firebase → Firestore Database → Iniciar coleção → ID da coleção: admins.
  2. ID do documento: o seu e-mail em minúsculas, exatamente como na sua conta (ex.: voce@gmail.com).
  3. Campo: role, tipo string, valor superadmin. Salve.
  4. Publique as regras novas (firebase deploy --only firestore) e entre no painel com Entrar com Google usando esse e-mail. Conta de e-mail e senha também funciona, desde que o e-mail esteja confirmado.

Para tirar alguém da equipe, apague o documento dessa pessoa em admins. Os papéis possíveis são superadmin, admin e suporte (só leitura).

10Entre no painel pela primeira vez

  1. Abra /admin/, clique em Entrar com Google com o e-mail do passo 8.
  2. Na tela Usuários, abra alguém da equipe (a pessoa precisa ter entrado no jogo ou no painel uma vez) e escolha o Papel na equipe:
    • Superadmin: tudo, inclusive mudar papéis.
    • Administrador: banir, desbanir e configurações.
    • Suporte: só visualiza.
Os números da Visão geralSão recalculados de hora em hora pela função updateStats. Para ver logo, clique em Atualizar agora no topo da Visão geral.

11Cadastre os produtos à venda

No Firestore, crie a coleção products. O ID do documento é o ID do produto (igual ao do Play Console, aba 5). Campos:

CampoTipoExemplo
namestringSaquinho de moedas (500)
priceCentsnumber490 (= R$ 4,90)
currencystringBRL
typestringconsumivel (pode comprar de novo) ou nao_consumivel (uma vez só)
activebooleantrue

12(Opcional) Teste tudo no computador, sem gastar

firebase emulators:start

Sobe Auth, Firestore, Functions e Hosting locais, com um painel em http://localhost:4000.

Dois ambientes: desenvolvimento e produção

A produção é o site publicado, com os dados reais dos jogadores. O desenvolvimento (dev) roda inteiro no seu computador, com os emuladores do Firebase: dá para testar tudo à vontade sem tocar na produção. Quando você precisar dos dados reais no dev, um superadmin autoriza no painel de produção.

Desenvolvimento (local)Produção
Onde rodaSeu computador (emuladores)Firebase na nuvem
Comandosnpm run emulators + npm run dev:fullnpm run deploy
Configuração do site.env.emulador (já vem pronto).env.production (suas chaves)
Configuração das funçõesfunctions/.env.localfunctions/.env.SEU-PROJETO
EndereçosJogo http://localhost:5173, painel /admin/, emuladores http://127.0.0.1:4000https://seudominio.com.br e /admin/
Selo no painelDEV (local)PRODUÇÃO
Três jeitos de rodar no computadornpm run dev: só o jogo, salvando no aparelho (painel em modo demonstração).
npm run dev:full: jogo e painel ligados aos emuladores (ambiente de dev completo).
Nenhum deles conecta na produção: é proposital.

1Instale o Java (só uma vez)

O emulador do banco de dados precisa do Java 11 ou mais novo. No Windows: instale o Temurin JDK 21 e feche e abra o terminal. Para conferir:

java -version

2Configure as funções locais

Na primeira vez, o npm run emulators cria o arquivo functions/.env.local. Abra e ajuste:

SUPERADMIN_EMAILS=seu.email@gmail.com
PLAY_PACKAGE_NAME=app.toninhodahorta.twa
PROD_PROJECT_ID=toni-64163

PROD_PROJECT_ID é o ID do seu projeto de produção: é de lá que a sincronização autorizada copia os dados.

3Suba o ambiente de dev (dois terminais)

Terminal 1, os emuladores (deixe aberto; Ctrl+C para sair, e os dados locais ficam salvos em .emulator-data):

npm run emulators

Terminal 2, o jogo e o painel:

npm run dev:full

Abra http://localhost:5173/admin/. O selo do topo deve mostrar DEV (local).

4Entre no painel local

Clique em Entrar com Google. No emulador aparece uma janela de teste: Add new account, digite o e-mail que está em SUPERADMIN_EMAILS e confirme. Você entra como superadmin (no dev, o e-mail não precisa ser verificado). As contas de teste aparecem em http://127.0.0.1:4000/auth.

5Na PRODUÇÃO: autorize a sincronização

  1. Abra o painel publicado: https://seudominio.com.br/admin/ → Ambientes.
  2. Escolha a validade (10, 30 ou 60 minutos) e se os dados vêm anonimizados (recomendado: nomes e e-mails são trocados, por causa da LGPD).
  3. Clique em Autorizar sincronização. Aparece um código como TDH-ABCD-EFGH-JKMN-PQRS. Ele aparece só nessa hora: copie e envie com segurança.

Só o superadmin autoriza. O código funciona uma vez, expira sozinho, pode ser revogado na mesma tela e tudo fica na Auditoria da produção.

6No DEV: sincronize

  1. No painel local → Ambientes, cole o código.
  2. Deixe marcado "Apagar os dados locais antes" para o dev ficar igual à produção (suas contas de teste continuam).
  3. Clique em Sincronizar agora. Em seguida, na Visão geral, clique em Atualizar agora.

São copiados: perfis dos jogadores, contas (sem senhas), pedidos, produtos, configurações, estatísticas e a auditoria recente. Os tokens de compra nunca saem da produção. O progresso salvo dos jogadores só vem se você marcar essa opção ao autorizar.

7Publique quando estiver pronto

npm run deploy

Só o site: npm run deploy:site. As mudanças nas funções (functions/) e nas regras vão junto no npm run deploy.

Publicar e usar o endereço jogo.fazendinharetro.com

O site fica no Firebase Hosting, com HTTPS grátis. Primeiro ele sai no endereço do Firebase (SEU-PROJETO.web.app); depois você liga o jogo.fazendinharetro.com (um subdomínio do fazendinharetro.com; a raiz fica para a landing page, no passo 7). O domínio próprio também é o que o app Android usa.

1Baixe a versão mais nova e publique

git pull
npm install
npm run deploy

O npm run deploy monta o jogo com as chaves do .env.production e envia site, painel, regras e funções. Nas atualizações do dia a dia, basta:

npm run deploy:site

Abra https://SEU-PROJETO.web.app, toque em Entrar ou criar conta e teste: criar conta, sair, entrar com e-mail, esqueci a senha e Google.

2Onde fica o DNS do domínio

O domínio fazendinharetro.com está na GoDaddy, com o DNS da própria GoDaddy (servidores domaincontrol.com). Os registros ficam em Meus produtos → fazendinharetro.com → DNS → Registros DNS. Na GoDaddy, @ significa a raiz do domínio.

3Conecte o domínio no Firebase

  1. Firebase → Hosting → Adicionar domínio personalizado → digite jogo.fazendinharetro.com → Continuar.
  2. Ele mostra um registro TXT (prova que o domínio é seu) e um registro A ou CNAME (endereço do servidor), todos com o nome jogo. Deixe essa tela aberta.
  3. Na GoDaddy: Meus produtos → fazendinharetro.com → DNS → Registros DNS → Adicionar novo registro e adicione exatamente o que o Firebase mostrou:
    • Tipo TXT, nome jogo (ou o nome que o Firebase mostrar, como _acme-challenge.jogo), valor copiado do Firebase.
    • Tipo A, nome jogo, valor o IP que o Firebase mostrou (um registro A para cada IP), ou tipo CNAME, nome jogo, valor SEU-PROJETO.web.app., se for isso que ele pedir.
    • Na GoDaddy o campo Nome é só a parte da frente (jogo), sem o .fazendinharetro.com.
  4. Volte ao Firebase e clique em Verificar. A ligação costuma sair em minutos; o certificado HTTPS pode levar até 24 horas (enquanto isso aparece "pendente").
  5. A raiz fazendinharetro.com (sem o jogo) fica para a landing page, que é outro site (passo 7).
Já tinha começado com game.fazendinharetro.com?No Hosting, remova o domínio game. Na GoDaddy, apague os registros com nome game. Em Authentication → Domínios autorizados e nas credenciais do Google Cloud, troque game por jogo. Se já tinha mudado o VITE_FIREBASE_AUTH_DOMAIN, troque também.
Conferir o DNSEm dnschecker.org veja se o IP do Firebase já aparece. A propagação pode levar algumas horas.

4Autorize o domínio no login

  1. Firebase → Authentication → Configurações → Domínios autorizados → Adicionar domínio: jogo.fazendinharetro.com. Sem isso, o login falha no domínio novo com "domínio não autorizado".
  2. Para o Google mostrar jogo.fazendinharetro.com na janela de login (em vez de SEU-PROJETO.firebaseapp.com) e funcionar melhor no Safari e no app instalado, troque no .env.production:
    VITE_FIREBASE_AUTH_DOMAIN=jogo.fazendinharetro.com
  3. Em Google Cloud → APIs e serviços → Credenciais, abra o Web client (auto created by Google Service) e adicione:
    • Origens JavaScript autorizadas: https://jogo.fazendinharetro.com
    • URIs de redirecionamento autorizados: https://jogo.fazendinharetro.com/__/auth/handler
    Salve (pode levar uns minutos para valer).
  4. Publique de novo: npm run deploy:site.
Ordem importaSó troque o VITE_FIREBASE_AUTH_DOMAIN depois que https://jogo.fazendinharetro.com já abrir o jogo com cadeado (HTTPS). Antes disso, deixe SEU-PROJETO.firebaseapp.com.

5Confira

  • Jogo: https://jogo.fazendinharetro.com/
  • Login: menu inicial → Entrar ou criar conta → criar, sair, entrar, esqueci a senha e Google.
  • Contas criadas: Firebase → Authentication → Usuários; dados do jogo em Firestore → users.
  • Painel: https://jogo.fazendinharetro.com/admin/ (também /painel)
  • Prévia ao compartilhar o link (WhatsApp, redes): imagem /og-image.png com o logo.
  • Ligação com o Android: https://jogo.fazendinharetro.com/.well-known/assetlinks.json (preenchido na aba 5). O twa/twa-manifest.json já aponta para jogo.fazendinharetro.com.
  • Guia: https://jogo.fazendinharetro.com/guia.html

6(Recomendado) Proteções extras

  • Authentication → Configurações → Proteção contra enumeração de e-mail: deixe ligada (o "Esqueci a senha" não revela se o e-mail tem conta).
  • App Check (Firebase → App Check, com reCAPTCHA Enterprise no site e Play Integrity no Android) bloqueia chamadas que não venham do seu app.
  • Equipe: use contas Google com verificação em duas etapas.
  • Cada ação do painel fica na Auditoria; ninguém apaga pelo painel.

7Landing page no fazendinharetro.com (segundo site)

A página de apresentação fica na pasta landing/ e vai para um segundo site do mesmo projeto. O jogo continua em jogo.fazendinharetro.com; a landing fica em fazendinharetro.com.

Uma vez só, crie o site e dê nome aos dois (troque SEU-PROJETO-ID; se fazendinharetro estiver ocupado, use outro ID, como fazendinharetro-site):

firebase hosting:sites:create fazendinharetro
firebase target:apply hosting game SEU-PROJETO-ID
firebase target:apply hosting landing fazendinharetro

Depois disso: npm run deploy:site publica só o jogo, npm run deploy:landing só a landing e npm run deploy tudo.

npm run deploy:landing
  1. Firebase → Hosting → site fazendinharetro → Adicionar domínio personalizado → fazendinharetro.com (deixe "Redirecionar" desmarcado).
  2. Na GoDaddy, cadastre com o nome @ (a raiz) o TXT e o registro A que o Firebase mostrar, e apague qualquer outro A/AAAA com nome @. Se o Firebase pedir para remover o 15.197.148.33 e ele não estiver na lista, é a página estacionada antiga ainda em cache: espere uma ou duas horas e clique em Verificar de novo.
  3. (Opcional) www.fazendinharetro.com no mesmo site, com Redirecionar para fazendinharetro.com.
Imagens novas da landingA aba Landing page da página de prompts tem 11 imagens. Salve cada uma em geracao-de-imagens/landing/ com o nome do cartão (ex.: lp_hero_sky.png), rode npm run landing:imagens e npm run deploy:landing: a página troca a arte sozinha.

Publicar na Google Play (TWA) com Google Play Billing

O app Android é um TWA: abre o site do seu domínio em tela cheia, sem barra do navegador. Os pagamentos usam o Google Play Billing pela Digital Goods API, já preparada em src/services/billing.ts.

Regra da Play StoreItens digitais vendidos dentro do app (moedas, sementes, passes) precisam usar o Google Play Billing. Não mostre outro meio de pagamento no app Android.

1Crie a conta no Play Console

play.google.com/console, taxa única de US$ 25 e verificação de identidade. Crie também o perfil de pagamentos (para vender e receber).

Conta pessoal novaO Google exige um teste fechado com um número mínimo de testadores durante alguns dias antes de liberar a produção (hoje, 12 testadores por 14 dias). Contas de empresa (CNPJ, com D-U-N-S) não têm essa exigência. Confira a regra atual no Play Console.

2Instale o Bubblewrap

npm install -g @bubblewrap/cli
bubblewrap doctor

Na primeira execução ele oferece baixar o JDK 17 e o Android SDK: responda sim.

3Gere o projeto Android

Com o site já publicado no seu domínio (aba 4):

mkdir -p twa/app && cd twa/app
bubblewrap init --manifest https://seudominio.com.br/manifest.webmanifest

Respostas (as mesmas de twa/twa-manifest.json):

PerguntaResposta
Domainseudominio.com.br
URL path/?source=twa (é assim que o jogo sabe que está no Android)
Application IDapp.toninhodahorta.twa (igual ao PLAY_PACKAGE_NAME; não dá para mudar depois)
Display modefullscreen
Include support for Play Billing?Yes
Key storedeixe criar uma nova e guarde o arquivo e as senhas fora do Git (sem elas você não atualiza o app nunca mais)

4Compile

bubblewrap build

Gera app-release-bundle.aab (vai para a loja) e app-release-signed.apk (para instalar no seu celular e testar).

5Crie o app e envie para teste interno

  1. Play Console → Criar app → nome, idioma Português (Brasil), Jogo, Gratuito.
  2. Testar e lançar → Teste interno → Criar versão → envie o .aab. Aceite o Play App Signing.
  3. Adicione o seu e-mail como testador e instale pelo link de teste.

6Ligue o site ao app (assetlinks)

  1. Play Console → Configuração → Integridade do app → Assinatura de apps: copie a impressão digital SHA-256 da chave de assinatura do app (e, para testar o APK local, também a da chave de upload).
  2. Cole em public/.well-known/assetlinks.json:
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "app.toninhodahorta.twa",
    "sha256_cert_fingerprints": [
      "AA:BB:...:CHAVE-DE-ASSINATURA-DO-PLAY",
      "11:22:...:CHAVE-DE-UPLOAD"
    ]
  }
}]
npm run build
firebase deploy --only hosting

Se o app abrir com uma barrinha de endereço no topo, o assetlinks está errado ou ainda não foi publicado (feche e abra o app depois de corrigir).

7Crie os produtos no Play Console

Monetizar → Produtos → Produtos no app → Criar produto. O ID precisa ser exatamente o mesmo do documento em products/ do Firestore (aba 2, passo 11). Defina o preço e Ative.

Assinaturas ficam em Monetizar → Produtos → Assinaturas (a verificação de assinaturas ainda precisa de uma função própria; os produtos avulsos já estão prontos).

8Dê ao servidor acesso aos pedidos

  1. No Google Cloud do mesmo projeto, ative a Google Play Android Developer API.
  2. Descubra a conta de serviço das funções: em IAM e administrador → Contas de serviço, é a NUMERO-compute@developer.gserviceaccount.com (conta padrão do Compute, usada pelas Functions de 2ª geração).
  3. No Play Console: Usuários e permissões → Convidar novos usuários → cole esse e-mail → em Permissões do app escolha o seu app → marque Ver dados financeiros e Gerenciar pedidos e assinaturas → Convidar.
PaciênciaA permissão pode levar até 24 horas para valer. Antes disso a verificação responde "sem permissão".

9Receba reembolsos e cancelamentos automaticamente

  1. O deploy já criou a função playRtdn, que escuta o tópico do Pub/Sub play-billing. Confira em Pub/Sub → Tópicos (crie com esse nome se não existir).
  2. No tópico play-billing: Permissões → Adicionar principal → google-play-developer-notifications@system.gserviceaccount.com → papel Publicador do Pub/Sub.
  3. Play Console → Monetizar → Configuração de monetização → Notificações do desenvolvedor em tempo real → tópico projects/SEU-PROJETO/topics/play-billing → Enviar mensagem de teste.
  4. Veja nos logs: firebase functions:log --only playRtdn deve mostrar "Notificação de teste do Google Play recebida".

10Compre de teste sem pagar

Play Console → Configuração → Teste de licença → adicione os e-mails da equipe. Esses e-mails compram com cartões de teste, e os pedidos aparecem no painel com a etiqueta teste (fora da receita).

11Como o jogo vende (para o desenvolvedor)

O fluxo já está pronto. Quando a loja de dinheiro real for criada no jogo, basta usar:

import { billingAvailable, buy, getPrices, restorePurchases } from './services/billing';
import { services } from './services';

const verify = (id: string, token: string) => services().verifyPurchase(id, token);

if (await billingAvailable()) {                 // só no app Android
  const prices = await getPrices(['moedas_pequeno']);   // preço na moeda do jogador
  const r = await buy('moedas_pequeno', verify);        // abre a tela do Google
  if (r.ok) { /* o item foi liberado em users/{uid}/grants/{orderId} */ }
}
await restorePurchases(verify);                  // ao abrir o jogo

Caminho de uma compra: tela do Google → token → função verifyPlayPurchase confere com o Google, grava em orders/, libera em users/{uid}/grants/ e confirma (consome ou reconhece) a compra. Sem confirmação, o Google devolve o dinheiro em 3 dias.

12Ficha da loja e lançamento

  • Ficha da loja principal: ícone 512×512, imagem de destaque 1024×500 e capturas de tela (a página de prompts gera esses itens).
  • Política de privacidade (URL pública), classificação de conteúdo, público-alvo e segurança dos dados (conta Google/anônima, compras).
  • Promova a versão: teste interno → teste fechado → produção.

13Atualizações do app Android

Mudou só o jogo? firebase deploy --only hosting e pronto. Só gere um novo .aab quando mudar nome, ícone, cores ou o pacote: aumente appVersionCode em twa-manifest.json e rode bubblewrap update e depois bubblewrap build. Para obrigar os jogadores a atualizar, use Versão mínima nas Configurações do painel.

iPhone (iOS)

O TWA é uma tecnologia do Android e não existe no iPhone. Existem dois caminhos.

A) Atalho na tela inicial (grátis, hoje)

No Safari: abra o jogo → Compartilhar → Adicionar à Tela de Início. Abre em tela cheia, com o ícone do jogo, já está pronto neste projeto. Não fica na App Store e não tem compras dentro do app.

B) App na App Store (Capacitor)

Empacota o mesmo site num app nativo. Precisa de Mac com Xcode e do Apple Developer Program (US$ 99/ano). Compras obrigatoriamente pela Apple (StoreKit).

1Crie o app iOS com Capacitor (no Mac)

npm install @capacitor/core @capacitor/ios
npm install -D @capacitor/cli
npx cap init "Fazendinha Retrô" app.toninhodahorta.ios --web-dir dist
npm run build
npx cap add ios
npx cap sync ios
npx cap open ios

No Xcode: escolha o seu time (Signing & Capabilities), adicione a capacidade In-App Purchase e rode no iPhone. O jogo detecta sozinho que está no iPhone (src/services/platform.ts).

2Compras pela Apple

  • Crie os produtos no App Store Connect → Compras dentro do app, de preferência com os mesmos IDs.
  • Use um plugin de compras para Capacitor (por exemplo, RevenueCat ou cordova-plugin-purchase).
  • Valide no servidor com a App Store Server API: será preciso uma função verifyAppStorePurchase, no mesmo modelo da verifyPlayPurchase (ainda não incluída).
  • No iPhone, billing.ts responde "indisponível" de propósito: o Google Play Billing não funciona lá.

3Regras da Apple que mais reprovam

  • Se o app oferece login com Google, a Apple pede também uma opção equivalente com privacidade (na prática, Entrar com a Apple). O jogador anônimo continua funcionando.
  • O app precisa parecer um app: tela cheia, funcionar offline o básico (já tem service worker) e não depender de links externos.
  • Nenhum outro meio de pagamento para itens digitais.
  • Permita apagar a conta pelo próprio app.

Checklist final

Site

Abre em https://jogo.fazendinharetro.com com HTTPS; criar conta, entrar com e-mail, esqueci a senha e Google funcionam; o progresso aparece em users/ no Firestore.

Painel

Você entra como superadmin; a Visão geral mostra números; banir e desbanir funciona; aparece na Auditoria.

Android

O app abre sem barra de endereço; uma compra de teste aparece em Pedidos com a etiqueta "teste"; um reembolso de teste vira "Reembolsado".

Segurança

Keystore e senhas guardadas em dois lugares seguros; alerta de orçamento criado; equipe com verificação em duas etapas.

Problemas comuns

SintomaCausa e solução
Invalid project id em qualquer comando firebaseO arquivo .firebaserc tem um ID inválido (com maiúsculas ou texto de exemplo). Apague-o (rm .firebaserc) e rode firebase use --add.
Painel diz "Esta conta não tem permissão"O e-mail não está em SUPERADMIN_EMAILS, o login não foi pelo Google (e-mail não verificado) ou faltou firebase deploy --only functions depois de mudar o arquivo.
Painel em "modo demonstração" no site publicadoO .env.production não existia na hora do npm run build. Preencha e publique de novo.
No computador, o jogo não conecta no FirebaseÉ de propósito: o npm run dev nunca usa a produção. Use npm run emulators e npm run dev:full (aba 3).
Sincronização: "Código não encontrado" ou "já foi usado"Cada código vale uma vez e por poucos minutos. Gere outro no painel de PRODUÇÃO, aba Ambientes. Confira também o PROD_PROJECT_ID em functions/.env.local.
"Ainda não há estatísticas"Clique em Atualizar agora na Visão geral (no dev é sempre necessário: lá as rotinas agendadas não rodam sozinhas).
Deploy: We failed to modify the IAM policy for the projectPrimeiro deploy das funções. (1) Confira se está logado com a conta dona do projeto: firebase login:list (troque com firebase logout e firebase login). (2) Espere 5 minutos e rode npm run deploy de novo. (3) Se continuar, dê as permissões pelo Cloud Shell (passo 9 da aba 2, quadro "Se der erro de IAM").
Erro pedindo índice no painelRode firebase deploy --only firestore:indexes e espere alguns minutos.
App Android com barra do navegadorSHA-256 errado ou ausente no assetlinks.json, ou o pacote diferente. Confira o arquivo no endereço /.well-known/assetlinks.json.
Compra: "sem permissão" na verificaçãoA conta de serviço ainda não foi convidada no Play Console ou faltam até 24 h para a permissão valer (aba 5, passo 8).
Compra: "Produto não cadastrado"Falta o documento em products/ com o mesmo ID do Play Console.
Billing indisponível no celularO app foi gerado sem "Play Billing" no Bubblewrap ou o celular foi aberto pelo navegador, não pelo app.