Durante os testes de IAP antes do lançamento, encontramos um problema clássico, porém muito confuso:
- O usuário toca em “Continuar compra”
- O botão muda para “Processando”
- Alguns segundos depois volta para “Continuar compra”
- A janela de confirmação de compra da App Store não aparece
Este artigo registra o caminho completo da investigação, os logs-chave, a causa raiz e uma estratégia de correção aplicável antes de publicar.
1. Sintoma
A lista de produtos na tela de compra carregava normalmente, mas após tocar em comprar:
purchase()era chamado- Nenhum diálogo do sistema aparecia
- A UI acabava voltando para o estado de não comprado
À primeira vista, parecia que o fluxo de compra nem tinha começado, mas os logs mostraram outra realidade.
2. Primeira rodada: confirmar se o fluxo de compra realmente executa
Adicionamos logs em todos os pontos críticos do fluxo:
- Parâmetros do clique no botão
- Entrada de
purchaseSelectedProduct()e ramificações deguard - Antes/depois de
product.purchase() Transaction.updatesrefreshEntitlements()Transaction.currentEntitlementsTransaction.latest(for:)
Resultado:
purchase()retornava.success- A verificação da transação passava, e
transaction.finish()também era executado - Mas
entitledIDscontinuava vazio
Isso indica que: não era “a compra não iniciou”, e sim “os entitlements não foram reconhecidos após a compra”.
3. Descobertas decisivas após aprofundar
Com logs mais detalhados, surgiram sinais importantes:
- A contagem de iterações de
Transaction.currentEntitlementsera0 Transaction.latest(for: monthly)tinha um registro válido, mas já expirado- Mais importante: antes da compra havia transações antigas em
Transaction.unfinished - O
purchase()atual retornava uma transação de uma cadeia antiga, e não de um novo fluxo interativo
Isso explica por que “não aparece popup”:
- O StoreKit primeiro retorna uma transação antiga processável (unfinished / cadeia histórica)
- Seu código finaliza essa transação com
finish() - A requisição parece “bem-sucedida”, mas não passa pelo novo caminho de confirmação esperado pelo usuário
4. Resumo da causa raiz
Não foi uma falha de ponto único, e sim a combinação de dois fatores:
- Unfinished transactions interferindo no fluxo de compra
- Cadeia de assinatura sandbox em estado expirado (mesmo com
latest transaction, pode não ser um entitlement ativo)
Comportamento final: sem popup, sucesso aparente e entitlements vazios.
5. Estratégia de correção (validada na prática)
5.1 Limpar unfinished transactions proativamente antes da compra
Na inicialização do app e antes de comprar, varra Transaction.unfinished e execute finish() nas transações verificadas, para evitar que transações antigas “sequestrem” o novo fluxo.
5.2 Manter e reforçar logs de diagnóstico (recomendado antes do lançamento)
Vale manter estes logs ativos até o release:
- Snapshot de
unfinished transaction subscription status(state, renewal info)- Contagem de iterações de
currentEntitlements - Detalhes de
transaction/expirationemlatest(for:)
5.3 Usar validação de entitlement em dupla via (hardening opcional)
Se em alguns ambientes currentEntitlements ficar temporariamente vazio, use latest(for:) como fallback (sempre validando status ativo).
6. Checklist de IAP pré-lançamento
- Antes de cada teste, verifique o estado de assinatura da conta Sandbox (expirada ou não)
- Faça log da contagem de
unfinishednos caminhos críticos - Teste três cenários: primeira compra / renovação após expirar / restaurar compras
- Verifique consistência entre
currentEntitlementselatest - Se ocorrer “sem popup”, cheque primeiro se caiu em um caminho de transação antiga
7. Conclusão
“Tocar em comprar sem popup” não significa necessariamente problema de UI ou de timing da chamada.
No StoreKit 2, estado das transações, fila de unfinished e ciclo de vida da assinatura impactam diretamente o comportamento final.
Adicionar esses logs e a rotina de limpeza antes do lançamento reduz muitos problemas de IAP difíceis de reproduzir em produção.