Medir o churn com as App Store Server Notifications V2
Como medir o churn de subscrições iOS com as App Store Server Notifications V2: configuração, significado de cada notificação, churn voluntário vs faturação.
O App Store Connect dá-lhe relatórios de vendas e de subscrições, mas se quiser números de churn em que possa confiar e sobre os quais possa agir (quem saiu, porquê e quem está prestes a sair), precisa do seu próprio registo de eventos. No iOS, esse registo vem das App Store Server Notifications V2: o servidor da Apple chama o seu sempre que uma subscrição muda de estado.
Este guia mostra como configurá-las de forma fiável, como cada notificação corresponde a uma métrica de churn e como preencher as lacunas. Baseia-se na documentação da Apple: Enabling App Store Server Notifications, Responding to App Store Server Notifications, a referência de notificationType e Get Notification History (todos consultados em novembro de 2026).
Configurar o endpoint
Os requisitos da Apple são curtos:
- Um URL HTTPS no seu servidor, com TLS 1.2 ou posterior. Se indicar uma porta, tem de ser 443 ou 1024 e superiores.
- Escolha a versão 2 para o URL no App Store Connect. A Apple recomenda a V2 para todas as novas implementações. Pode definir um URL separado para sandbox, ou reutilizar o mesmo.
- Se a sua firewall usar uma lista de permissões, adicione
17.0.0.0/8, que cobre sandbox e produção. - Devolva HTTP 200 a 206 quando tiver guardado a notificação. Qualquer outro código conta como falha.
- Teste com o endpoint Request a Test Notification, que envia uma notificação
TEST, e depois verifique o resultado com Get Test Notification Status.
Cada notificação chega como um signedPayload em formato JWS. Verifique a assinatura antes de confiar em qualquer coisa que contenha e depois descodifique o payload.
Entrega, novas tentativas e duplicados
As coisas correm mal: deploys, indisponibilidades, timeouts. A documentação da Apple descreve o que acontece nesses casos.
- Novas tentativas. Na V2, se o seu servidor não devolver um código de sucesso, a Apple tenta de novo cinco vezes, 1, 12, 24, 48 e 72 horas depois da tentativa anterior. As novas tentativas só acontecem em produção; em sandbox, a Apple envia cada notificação uma vez.
- Duplicados. Cada payload tem um
notificationUUID, que a Apple diz para usar na identificação de duplicados. Guarde-o com uma restrição de unicidade e ignore as repetições. - Histórico. O Get Notification History devolve as notificações que a Apple tentou enviar nos últimos 180 dias em produção (30 dias em sandbox), 20 registos por página, com filtro opcional por tipo, subtype ou transação. Execute-o depois de qualquer indisponibilidade.
- Estado atual. Os registos do histórico refletem o estado no momento do envio. Para o estado atual de uma subscrição, a Apple remete para o Get All Subscription Statuses.
Um bom padrão: guarde primeiro a notificação em bruto, devolva 200 e depois processe-a a partir de uma fila. Nunca faça trabalho lento antes de responder à Apple.
O que está numa notificação
O payload descodificado tem um notificationType, um subtype opcional, um signedDate e um objeto data. Para subscrições, data contém:
signedTransactionInfo: a transação (produto, datas, oferta usada, revogação se existir).signedRenewalInfo: o que vai acontecer na próxima renovação, incluindoautoRenewStatus,expirationIntent,isInBillingRetryPeriod,gracePeriodExpiresDateeeligibleWinBackOfferIds.status: o estado da subscrição à data designedDate:1ativa,2expirada,3em billing retry,4em Billing Grace Period,5revogada.
Para associar uma subscrição a um dos seus utilizadores, defina um appAccountToken na compra. A Apple devolve-o na informação da transação e da renovação. Sem ele, só tem os IDs de transação da Apple.
Associar as notificações às métricas de churn
Eis como os tipos de notificação da Apple se traduzem nos números que a maioria das equipas acompanha. As descrições seguem a tabela de notificationType da Apple.
| Métrica | Notificação | Subtype |
|---|---|---|
| Novo subscritor | SUBSCRIBED |
INITIAL_BUY |
| Subscritor que regressa (incluindo win-back offers) | SUBSCRIBED |
RESUBSCRIBE |
| Renovação | DID_RENEW |
nenhum |
| Intenção de cancelamento (renovação desativada) | DID_CHANGE_RENEWAL_STATUS |
AUTO_RENEW_DISABLED |
| Cancelamento revertido | DID_CHANGE_RENEWAL_STATUS |
AUTO_RENEW_ENABLED |
| Falha de faturação | DID_FAIL_TO_RENEW |
GRACE_PERIOD ou nenhum |
| Faturação recuperada | DID_RENEW |
BILLING_RECOVERY |
| Churn voluntário | EXPIRED |
VOLUNTARY |
| Churn involuntário (faturação) | EXPIRED |
BILLING_RETRY |
| Churn por aumento de preço | EXPIRED |
PRICE_INCREASE |
| Produto retirado de venda | EXPIRED |
PRODUCT_NOT_FOR_SALE |
| Reembolso | REFUND |
nenhum, ou SEATS para multiseat |
| Downgrade agendado | DID_CHANGE_RENEWAL_PREF |
DOWNGRADE |
O expirationIntent na informação de renovação dá a mesma divisão sob forma de número: 1 o cliente cancelou, 2 erro de faturação, 3 sem consentimento para um aumento de preço ou conversão de oferta, 4 produto indisponível, 5 outro (referência).
Conte o churn na expiração, não no cancelamento
Na App Store, um cancelamento apenas interrompe a renovação. O cliente mantém o acesso até ao fim do período e pode voltar a ativar a renovação. Assim:
- Use
AUTO_RENEW_DISABLEDcomo indicador antecipado: subscrições em risco neste período. - Use
EXPIREDcomo o evento de churn: subscrições que terminaram realmente.
Se contar o churn em AUTO_RENEW_DISABLED, vai sobrestimá-lo e perder todos os clientes que mudaram de ideias.
O churn de faturação tem uma cauda longa
A referência da Apple diz que, depois de uma renovação falhada, a App Store continua a tentar cobrar durante 60 dias, ou até o cliente corrigir a faturação ou cancelar. Com o Billing Grace Period ativo, o cliente mantém o acesso durante o período de carência; GRACE_PERIOD_EXPIRED marca o seu fim enquanto as novas tentativas continuam. Uma subscrição em billing retry ainda não está nem ativa nem perdida. Reporte-a numa categoria própria, e só a conte como churn quando chegar EXPIRED com BILLING_RETRY.
Note também o recentSubscriptionStartDate na informação de renovação: a Apple calcula-o ignorando interrupções de 60 dias ou menos, o que é prático para antiguidade e coortes.
Um relatório de churn simples a partir das notificações
Com todas as notificações guardadas, um relatório mensal resume-se a meia dúzia de queries:
- Ativas no início do mês: subscrições cujo último estado antes do mês era ativa, em período de carência ou em billing retry (decida uma vez se o retry conta como ativo e mantenha a decisão).
- Perdidas: notificações
EXPIREDno mês, divididas por subtype. - Taxa de churn voluntário:
EXPIRED/VOLUNTARYa dividir pelas ativas no início. - Taxa de churn involuntário:
EXPIRED/BILLING_RETRYa dividir pelas ativas no início. - Recuperação de faturação:
DID_RENEW/BILLING_RECOVERYa dividir porDID_FAIL_TO_RENEWna mesma coorte. - Em risco agora: subscrições cuja última informação de renovação tem a renovação automática desativada e uma expiração futura.
- Reativações:
SUBSCRIBED/RESUBSCRIBE.
Para as fórmulas gerais e as armadilhas (misturar planos mensais e anuais, contar períodos de teste), veja como calcular a taxa de churn.
Lacunas comuns
- Ouvir só a V1, ou só a produção. Teste a V2 em sandbox antes de mudar a produção; a Apple recomenda exatamente essa ordem.
- Nenhum job de reconciliação. Mesmo com novas tentativas, execute o Get Notification History periodicamente e depois de incidentes.
- Ignorar a Partilha com a família e os grupos.
SUBSCRIBEDeREVOKEtambém são disparados para membros da família e lugares de grupo. Decida se esses contam como subscritores nas suas métricas. - Tratar reembolsos como cancelamentos. Um
REFUNDtermina o acesso e devolve dinheiro; reporte-o à parte. - Sem
appAccountToken. Obtém números limpos da App Store, mas não os consegue cruzar com dados de utilização, de plano ou de aquisição, que é de onde vêm as explicações do churn.
Fazer isto com o NoChurn
O NoChurn dá-lhe um URL para colar como endpoint das App Store Server Notifications. Verifica e elimina duplicados de cada notificação, mantém a cronologia por cliente (associada pelo seu appAccountToken), separa o churn voluntário, de faturação e por aumento de preço, e assinala a renovação automática desativada como sinal de risco precoce no score de churn de 0 a 100. Pode adicionar os seus próprios atributos por API para ver o que provoca o churn. A Apple fica com a faturação, as novas tentativas e o botão de cancelar. Veja NoChurn + App Store.