Todos os artigos
Deep dive8 min de leitura

Revogar Acesso Após um Reembolso: A Etapa que Apple e Google Não Fazem por Você

Apple e Google podem processar um reembolso enquanto o cliente continua com a compra. Veja exatamente quando o acesso é removido automaticamente, quando o seu servidor precisa fazer isso, e a notificação que quase nenhuma integração trata.

Um smartphone brilhando sobre uma mesa à noite, ilustrando o momento em que o acesso a uma compra reembolsada ainda não foi revogado

Principais conclusões

  • No Google Play, reembolsar um pedido não remove o acesso do usuário por si só. O acesso só é removido quando a opção "Remove entitlement" é marcada no Play Console, ou quando a chamada de API passa revoke=true, o que não é o padrão.
  • Um reembolso emitido sem a opção de revogação nunca aparece na Voided Purchases API. Um processo de revogação que lê apenas esse feed vai perder silenciosamente todos esses pedidos.
  • A Apple pode revogar uma licença de itens não consumíveis e assinaturas automaticamente, mas um item consumível já é considerado entregue no momento em que é gasto, então o StoreKit não tem mecanismo para revogá-lo. O seu servidor precisa deduzir o saldo por conta própria.
  • A Apple também pode desfazer o próprio reembolso. Uma notificação REFUND_REVERSED significa que um reembolso concedido anteriormente foi revertido, revocationDate volta para null, e qualquer acesso ou saldo que você removeu precisa ser restaurado.
  • O campo voidedReason do Google carrega nove valores, de remorse e accidental_purchase até fraud, friendly_fraud e unacknowledged_purchase, então o mesmo feed que indica quando revogar acesso também indica quais revogações são fraude e quais são um bug na sua própria integração.
  • Cada dia em que uma compra reembolsada mantém seu acesso é um dia em que você está pagando pelo processamento, armazenamento e chamadas de API por trás dela, com dinheiro que você já devolveu.

Aprovar um reembolso e remover o acesso são duas etapas separadas, e apenas uma delas acontece automaticamente. Desenvolvedores que presumem que um reembolso processado equivale a uma compra revogada estão errados nas duas principais lojas, de formas diferentes e por motivos diferentes. Saber exatamente onde a automação para é o que é preciso para revogar o acesso após um reembolso, em vez de descobrir meses depois que isso nunca aconteceu.

Por que um reembolso não revoga o acesso por padrão

Apple e Google tratam o dinheiro e o acesso como duas operações diferentes. O lado financeiro é política da loja: uma vez aprovado o reembolso, ele é final e o registro é atualizado. O lado do acesso é opcional, ou é trabalho do desenvolvedor, ou ambos, dependendo da plataforma e do tipo de compra. Nenhuma das lojas presume que você quer o acesso removido no instante em que o reembolso é concluído, porque muitos casos legítimos (um reembolso parcial, um crédito de cortesia, um gesto de suporte) não devem retirar o produto de forma alguma.

Google Play: a caixa de seleção que ninguém percebe

No Play Console, reembolsar um pedido é um formulário em duas partes: escolher o valor do reembolso e depois decidir separadamente se marca "Remove entitlement". Deixar essa opção desmarcada é o caminho padrão que a maioria dos agentes de suporte segue, porque a maior parte dos pedidos de reembolso são gestos de boa vontade em que o cliente mantém o que comprou. O problema é que nada na tela de confirmação do reembolso avisa que é assim também que um cliente mantém uma assinatura ou item dentro do aplicativo depois de um reembolso que você pretendia ser definitivo.

O caminho da API tem a mesma armadilha

A mesma escolha existe no código. O endpoint orders.refund aceita um parâmetro de consulta opcional chamado revoke; quando é true, o acesso ao item é encerrado imediatamente, e no caso de uma assinatura recorrente todo pagamento futuro é cancelado junto. Omitir o parâmetro faz com que ele assuma false por padrão: o reembolso é registrado, o registro da compra é atualizado, e o usuário continua usando o produto exatamente como antes.

AçãoAcesso removido?Aparece na Voided Purchases API?
Reembolso no Play Console, "Remove entitlement" desmarcadoNãoNão
Reembolso no Play Console, "Remove entitlement" marcadoSim, imediatamenteSim
API orders.refund, parâmetro revoke omitido (assume false por padrão)NãoNão
API orders.refund, revoke=trueSim, imediatamenteSim

Essa última linha é a que pega as integrações de surpresa. A Voided Purchases API existe para permitir que você construa um pipeline de revogação sem verificar o Play Console manualmente, mas ela só lista pedidos que foram efetivamente anulados com revogação. Um reembolso processado sem a caixa marcada, ou sem revoke=true, é invisível para esse feed por design, não por falha.

Um rack de servidor com uma única luz de status vermelha entre fileiras de luzes verdes, representando uma decisão silenciosa de revogação

Lendo o campo voidedReason Quando uma compra aparece como anulada, o campo voidedReason informa o motivo, em uma escala de nove valores: other, remorse, not_received, defective, accidental_purchase, fraud, friendly_fraud, chargeback e unacknowledged_purchase. Isso não é apenas controle contábil. Remorse e accidental_purchase são reembolsos comuns. Fraud, friendly_fraud e chargeback são os que valem a pena encaminhar para uma fila de análise de fraude, em vez de um simples caminho de revogar e esquecer, porque a mesma conta tem estatisticamente mais chance de repetir o comportamento.

Um detalhe operacional importa se você construir integrações diretamente sobre esse feed: purchases.voidedpurchases.list tem um limite de 6.000 consultas por dia e 30 consultas em qualquer janela de 30 segundos. Um design que faz polling em loop apertado atinge esse limite rapidamente; o padrão pretendido é uma busca agendada com tokens de paginação, não polling contínuo.

Apple: a App Store pode revogar uma licença, mas não um item consumível

As App Store Server Notifications V2 da Apple enviam uma notificação REFUND no momento em que um reembolso é concluído, tanto para consumíveis quanto para não consumíveis, assinaturas de renovação automática e assinaturas sem renovação. O que acontece depois dessa notificação depende inteiramente do tipo de compra, porque o próprio mecanismo de revogação do StoreKit cobre apenas dois dos quatro tipos.

Por que os itens consumíveis são a exceção

Um item não consumível ou uma assinatura tem um direito permanente que os sistemas da Apple conseguem desativar. Um item consumível não: uma vez utilizado, é considerado entregue, e não sobra nada no StoreKit para revogar. Se um cliente comprou 500 moedas, gastou-as e recebeu um reembolso, as moedas já se foram do ponto de vista da Apple. O único registro que ainda as tem é o seu. A notificação REFUND é o seu único sinal para encontrar o ID da transação original correspondente e deduzir esse saldo dos seus próprios registros; nada do lado da Apple vai fazer isso por você.

NotificaçãoO que significaO que o seu servidor deve fazer
REFUNDApple reembolsou uma compra consumível, não consumível, de renovação automática ou sem renovaçãoRevogar imediatamente o acesso de itens não consumíveis e assinaturas; deduzir o saldo consumível você mesmo
REFUND_DECLINEDApple recusou o pedido de reembolso de um clienteNada. O acesso nunca esteve em risco
REFUND_REVERSEDApple reverteu um reembolso que já havia concedidoRestaurar qualquer acesso ou saldo que a notificação REFUND fez você remover
CONSUMPTION_REQUESTApple quer suas evidências antes de decidir sobre um reembolsoResponder em até 12 horas; isso é disparado antes de um REFUND, não depois

A notificação que quase ninguém trata

REFUND_REVERSED é a que a maioria das integrações ignora completamente, porque descreve a Apple desfazendo sua própria decisão, um caso para o qual poucos desenvolvedores planejam na primeira tentativa. Quando ela chega, revocationDate na transação volta para null: no que diz respeito à Apple, a compra é válida novamente. Se o seu servidor apagou um direito de acesso, cancelou o registro de uma assinatura ou zerou um saldo consumível quando o REFUND original chegou, REFUND_REVERSED é o sinal para desfazer tudo isso.

Como fechar essa lacuna, passo a passo

  • No Google Play, configure todo caminho de reembolso para usar revoke=true por padrão, seja pelo Play Console ou pela API, a menos que um caso específico de suporte exija que o cliente mantenha o acesso.
  • Faça polling em purchases.voidedpurchases.list em um cronograma com tokens de paginação, não em loop apertado, e fique abaixo de 6.000 requisições por dia e 30 a cada 30 segundos.
  • Encaminhe valores de voidedReason como fraud, friendly_fraud e chargeback para uma fila de análise, em vez de um revoke-and-close automático.
  • Na Apple, inscreva-se nas App Store Server Notifications V2 e revogue o acesso de itens não consumíveis e assinaturas no momento em que uma notificação REFUND os mencionar.
  • Armazene o ID da transação original para cada compra consumível, para que uma notificação REFUND consiga encontrar o saldo exato a deduzir sem uma busca manual.
  • Trate REFUND_REVERSED explicitamente e registre cada revogação vinculada ao seu ID de transação ou pedido, para que uma reversão possa restaurar precisamente o que foi removido.

O que essa lacuna realmente custa

Nada disso tem a ver com justiça para o cliente; tem a ver com o que continua rodando depois que o dinheiro já se foi. Uma assinatura reembolsada que continua chamando sua API, armazenando dados e consumindo processamento não é um problema moral, é um item de custo: você está pagando a conta de infraestrutura por uma compra cuja receita você já devolveu. As lojas não rastreiam esse custo para você, porque do lado delas o reembolso está concluído no momento em que o dinheiro se move. O lado do acesso é inteiramente sua conta para controlar.

Esse é o mesmo motivo pelo qual a RefundHalt trata a revogação como parte do pipeline de reembolso, e não como uma reflexão tardia: um evento REFUND e uma mudança de acesso são registrados como uma única ação, e um evento REFUND_REVERSED restaura o estado automaticamente, em vez de esperar que alguém perceba que o cliente ainda está bloqueado de algo que a Apple já devolveu.

Perguntas frequentes

Reembolsar uma compra no Google Play revoga o acesso automaticamente?
Não. O acesso só é removido se "Remove entitlement" estiver marcado no Play Console ou se a chamada da API orders.refund incluir revoke=true. Nenhuma das duas é o padrão, então um reembolso pode ser concluído enquanto o cliente continua usando o produto.
O que acontece quando uma compra consumível dentro do aplicativo é reembolsada na App Store?
A Apple envia uma notificação REFUND, mas o StoreKit não tem como revogar um item consumível porque ele é considerado entregue assim que é gasto. O servidor do desenvolvedor precisa encontrar a transação original e deduzir o saldo manualmente.
O que é a notificação REFUND_REVERSED e ela precisa de tratamento especial?
REFUND_REVERSED significa que a Apple reverteu um reembolso que já havia concedido; revocationDate volta para null e a compra é válida novamente. Qualquer acesso ou saldo que o REFUND original removeu precisa ser restaurado, e a maioria das integrações nunca escuta essa notificação.
Por que um pedido reembolsado do Google Play não apareceria na Voided Purchases API?
A API só lista pedidos que foram anulados com revogação. Se um reembolso foi processado sem a opção de revogação, o pedido simplesmente não é retornado por esse endpoint, então um pipeline que depende só dele vai perder completamente esses reembolsos.
Com que frequência posso fazer polling na Voided Purchases API do Google?
O endpoint tem limite de 6.000 consultas por dia e 30 consultas em qualquer janela de 30 segundos, então ele foi feito para uma busca agendada com tokens de paginação, não para polling contínuo.

Fontes e leituras adicionais

RefundHalt

O piloto automático de reembolsos para App Store e Google Play

Continue lendo

A próxima solicitação de reembolso já está a caminho.

Configure a RefundHalt no tempo que você levaria para ler mais um e-mail de suporte sobre um reembolso que não conseguiu contestar.