Ir para o conteúdo
Google Play Refund Management

Um reembolso do Google Play não vai te avisar, mas a Voided Purchases API vai

Um reembolso no Google Play acontece silenciosamente: a cobrança é revertida, o cliente continua usando o app, e nada muda do seu lado a menos que você verifique. A Voided Purchases API é onde você verifica. Aqui está a API campo por campo, os limites que a delimitam e por onde o dinheiro escoa quando ela não está conectada.

5 min read
Um reembolso do Google Play não vai te avisar, mas a Voided Purchases API vai

Um reembolso no Google Play acontece silenciosamente. A cobrança é revertida, o cliente continua usando o app, e nada muda do seu lado a menos que você verifique deliberadamente. Verificar é exatamente para isso que a Voided Purchases API existe. Ela retorna os pedidos que foram cancelados, reembolsados ou estornados, para que você possa retirar o acesso a tudo que o cliente parou de pagar. Conecte um job programado a ela, leia o que retorna e revogue o direito de uso. Esse é todo o mecanismo.

Uma coisa pega quase todas as equipes de surpresa, e ela está na política, não no código. Essa API só lista pedidos que foram efetivamente revogados. Reembolse uma compra no Play Console sem marcar a opção de revoke e esse pedido nunca aparece aqui, então seu job roda perfeitamente limpo enquanto um cliente reembolsado sai levando tudo o que você vendeu. A seguir, cobrimos a API campo por campo, os limites que a delimitam e por onde o dinheiro escoa silenciosamente quando ela não está conectada.

Principais conclusões

  • A Voided Purchases API, acessada pelo método purchases.voidedpurchases.list, lista pedidos que o Google Play cancelou, reembolsou ou estornou, o que permite que você construa um sistema que retira o acesso a compras que o cliente não possui mais.

  • Apenas pedidos revogados aparecem. Um reembolso feito pelo desenvolvedor sem revogar permanece invisível aqui, então retirar o acesso significa reembolsar com o revoke ativado.

  • Seu alcance é uma janela móvel de 30 dias. Como o startTime não pode retroceder mais de 30 dias, qualquer servidor que fique offline por mais de um mês perde essas anulações permanentemente, e é por isso que o polling precisa rodar em uma programação.

  • voidedSource identifica quem disparou a anulação: 0 para o usuário, 1 para o desenvolvedor, 2 para o Google. voidedReason explica o motivo, variando de 0 para Other até 7 para Chargeback e 8 para Unacknowledged_purchase.

  • As notificações de desenvolvedor em tempo real disparam uma VoidedPurchaseNotification no instante em que uma compra é anulada, mas trate isso como um alerta. Confirme na Voided Purchases API antes de revogar qualquer coisa.

  • Diferencie as renovações de assinatura pelo orderId, nunca pelo purchaseToken. Um único purchaseToken abrange todas as renovações de uma assinatura, então o token sozinho não consegue separar um período do outro.

  • Os limites são 6,000 consultas por dia e 30 em qualquer intervalo de 30 segundos, então delimite cada requisição a uma janela de tempo e percorra as páginas usando o token de continuação, nunca uma requisição por pedido.

O que a API retorna

O endpoint resolve uma única pergunta: quais pedidos deste app foram anulados recentemente. Uma anulação reúne três resultados que devolvem o dinheiro do cliente: um cancelamento, um reembolso ou um chargeback. Ela alcança tanto produtos avulsos dentro do app quanto assinaturas, e um parâmetro define o escopo. Deixe type em 0, o padrão, e só as compras de produtos avulsos anuladas retornam. Defina type como 1 e você recebe compras avulsas anuladas e compras de assinatura anuladas juntas.

Cada item na resposta é um objeto de compra anulada com um conjunto curto e valioso de campos:

  • orderId identifica de forma exclusiva uma compra avulsa, uma assinatura ou uma renovação individual dentro dela. Trate isso como sua chave de junção.

  • purchaseToken identifica uma compra avulsa ou uma assinatura, mas não separa renovações, então use orderId para isso.

  • purchaseTimeMillis registra quando a compra aconteceu, em milissegundos desde o epoch.

  • voidedTimeMillis registra quando ela foi cancelada, reembolsada ou estornada, também em milissegundos desde o epoch.

  • voidedSource indica quem iniciou a anulação, onde 0 é o usuário, 1 é o desenvolvedor e 2 é o Google.

  • voidedReason indica a causa como um número inteiro de 0 a 8.

  • voidedQuantity traz a quantidade anulada de um reembolso parcial baseado em quantidade, e só aparece quando includeQuantityBasedPartialRefund é true.

Como um purchaseToken cobre uma assinatura inteira, enquanto cada renovação recebe um orderId recém-gerado, basear seus direitos de uso no token fará você revogar o período errado. Use o orderId como chave em vez disso.

Leia o voidedReason antes de mexer em qualquer coisa

O voidedReason é o que transforma uma lista simples em uma decisão real, porque um reembolso por arrependimento e um chargeback bancário compartilham o mesmo feed, mas não têm nada em comum. O conjunto completo é o seguinte:

1. Other não carrega nenhuma categoria atribuída. Revogue e siga em frente.

2. Remorse é um cliente que mudou de ideia, um reembolso do dia a dia.

3. Not_received é uma alegação de que o produto nunca chegou, algo que vale a pena investigar na sua entrega.

4. Defective significa que não funcionou, um sinal de qualidade que você deve registrar.

5. Accidental_purchase é uma compra não intencional, com frequência em um dispositivo compartilhado.

6. Fraud é uma transação que o Google sinalizou como fraudulenta.

7. Friendly_fraud é um chargeback em que o verdadeiro titular do cartão contesta uma cobrança que ele mesmo fez.

8. Chargeback é o banco do cliente revertendo o pagamento, definitivo perante o banco e agora cobrado de você.

9. Unacknowledged_purchase é o Google reembolsando automaticamente uma compra que seu app nunca confirmou (acknowledge).

O motivo 9 é um reembolso que você mesmo provocou. Qualquer compra que seu app deixa sem confirmação (unacknowledged) tem o dinheiro devolvido pelo Google, então um unacknowledged_purchase neste feed é receita perdida por uma única chamada não feita, não por qualquer escolha do comprador.

A janela de 30 dias que esvazia a lista silenciosamente

A API não alcança mais do que os últimos 30 dias. O startTime tem como padrão o momento atual menos 30 dias e recusa qualquer valor anterior a esse, e o endTime tem como padrão o momento presente, então o que você tem é uma janela móvel de um mês, não um arquivo permanente.

A implicação é dura. Deixe um job de polling quebrar e passar despercebido por cinco semanas, e as anulações da primeira semana já terão saído da API, sem nenhuma chamada capaz de recuperá-las. Esses pedidos ficam sem revogação, e você nem saberia que aconteceram, a menos que os tivesse capturado em outro lugar. A rede tem um buraco exatamente do tamanho da sua maior interrupção. Faça polling pelo menos uma vez por dia e reconcilie com as notificações em tempo real, tratando os 30 dias como o relógio rígido de exclusão que eles realmente são.

Se um pedido aparece ou não depende do revoke

Esse é o motivo número um pelo qual as equipes chamam a API de quebrada. Ela retorna pedidos revogados e mais nada. Reembolsos iniciados por usuários, cancelamentos, chargebacks e reembolsos iniciados pelo Google revogam automaticamente, então sempre aparecem no feed. Um reembolso iniciado pelo desenvolvedor é a exceção. Emitir o reembolso você mesmo, seja pelo Play Console ou pela Orders API, deixa a escolha de revogar como uma decisão separada que você precisa tomar. Recuse essa opção e o pedido fica quitado com o cliente, mas nunca aparece neste feed.

A conclusão é curta. Quando seu objetivo é cortar o acesso, reembolse com o revoke ativado. Ignore isso e você terá devolvido o dinheiro deixando a porta aberta, e o seu job de revogação, por mais cuidadosamente construído que seja, não terá nada com que trabalhar.

Fazendo polling sem estourar a cota

O endpoint tem limite de taxa, e os limites são baixos o suficiente para que um loop descuidado os estoure. Você tem 6,000 consultas por dia, contadas no horário do Pacífico, e nunca mais que 30 em qualquer intervalo de 30 segundos. Esse orçamento favorece o polling em janelas e penaliza qualquer design de uma chamada por pedido.

Janelas de tempo e o token de continuação

O maxResults fica em 1,000 por padrão, e esse também é o valor máximo permitido. Caso uma janela contenha mais anulações do que uma página comporta, a resposta vem com um objeto tokenPagination contendo um nextPageToken. Devolva esse token na sua próxima chamada para avançar pelas páginas. Fixe os limites da janela com startTime e endTime, continue paginando até o token se esgotar, e só então avance a janela. Essa cadência mantém você longe tanto do limite de rajada de 30 segundos quanto da cota diária.

As notificações em tempo real fecham a lacuna diária

Mesmo um polling diário deixa você cego por até 24 horas, e lacunas longas são exatamente o que a janela de 30 dias penaliza. As notificações de desenvolvedor em tempo real fecham essa defasagem. No instante em que uma compra é anulada, uma VoidedPurchaseNotification é publicada em um tópico do Cloud Pub/Sub sob seu controle, e seu backend a consome em segundos. O payload dela permanece compacto:

  • purchaseToken é o token da compra original.

  • orderId é o id da transação anulada, gerado de forma inédita a cada renovação de assinatura.

  • productType é 1 para uma assinatura e 2 para uma compra avulsa.

  • refundType distingue um reembolso total, marcado como 1, de um reembolso parcial baseado em quantidade, marcado como 2.

Trate a notificação como um alerta, não como verdade absoluta. Assim que uma VoidedPurchaseNotification chega, consulte a Voided Purchases API para verificar a situação real do pedido, e só então revogue. O alerta diz para você verificar; a API diz a verdade.

Quanto isso custa em dinheiro

A API é encanamento, mas o motivo para instalar esse encanamento é uma conta, e dois dos números dela estão subindo.

A partir de 3 de agosto de 2026, a conta do chargeback é sua

A partir de 3 de agosto de 2026, o Google transfere o custo de um chargeback para o desenvolvedor. Você perde o valor da compra e ainda cobre a taxa de chargeback do banco. Um voidedReason igual a 7 deixa de ser apenas uma venda perdida e passa a ser um item de custo com uma taxa anexada. O chargeback em si não pode ser revertido, já que é definitivo perante o banco, mas identificar a anulação rapidamente permite revogar o direito de uso e, para tudo que ainda está sendo entregue, parar de gastar com um cliente que foi reembolsado e depois estornado.

Você continua bancando um cliente que já foi reembolsado

O valor da venda desaparece no momento em que a anulação chega. O que ainda está sob seu controle é o custo de continuar entregando o produto. Para cada hora que um direito de uso reembolsado permanece ativo, as contas que o cliente parou de pagar continuam chegando do seu lado: o processamento, as chamadas ao provedor de modelo, o armazenamento e qualquer pagamento a criadores ou parceiros que a atividade dele dispara. Construir o pipeline de revogação sobre essa API é como esse gasto é interrompido. Ignore isso e você estará bancando o produto para pessoas que a loja já reembolsou.

Friendly fraud é uma tendência, não um incidente

Um voidedReason igual a 5 ou 6 raramente aparece sozinho. Fraude e friendly fraud se concentram em torno de contas, dispositivos e, ocasionalmente, promoções específicas. Como a API anexa voidedSource e voidedReason a cada anulação, você tem o suficiente para identificar tendências de abuso por conta, em vez de absorver cada estorno como uma perda isolada. Uma conta que sofre um chargeback pela segunda vez está te dizendo o que o primeiro reembolso não disse.

Juntando tudo

O modelo inteiro é pequeno depois que as peças estão em mãos. Escute a VoidedPurchaseNotification em tempo real para que nada fique esperando um dia inteiro. Trate a Voided Purchases API como a fonte da verdade, indexada por orderId para que as renovações nunca se misturem. Leia voidedSource e voidedReason para que um chargeback seja tratado separadamente de um reembolso por arrependimento. Faça polling em uma cadência apertada o suficiente para que a janela de 30 dias nunca morda, e reembolse com o revoke ativado sempre que sua intenção for cortar o acesso.

Essa é a camada que o Refund Sensor administra para você. Ele consome as notificações em tempo real, reconcilia cada anulação com a API, revoga o pedido exato em vez do produto inteiro, e mantém um chargeback bancário separado de um reembolso comum, para que os casos mais caros apareçam em vez de se esconder. Você tem o acesso retirado em segundos e um registro completo de quem anulou o quê e por quê, sem precisar montar nenhum pipeline de Pub/Sub ou job de polling.

Onde essas regras estão documentadas

Perguntas frequentes

Porque o feed traz apenas pedidos revogados. Reembolsos iniciados pelo usuário, junto com cancelamentos, chargebacks e reembolsos iniciados pelo Google, revogam sozinhos e sempre aparecem. Um reembolso que você mesmo emite só aparece se você também optar por revogá-lo. Reembolse sem revogar e o pedido fica quitado, mas invisível aqui, então ative o revoke sempre que seu objetivo for retirar o acesso.

Trinta dias, e nada além disso. Por padrão, o startTime fica no momento atual menos 30 dias e recusa qualquer valor anterior, então o endpoint se comporta como uma janela móvel de um mês, não como um arquivo. Assim que uma anulação ultrapassa os 30 dias, ela desaparece sem nenhuma forma de recuperá-la, e é exatamente por isso que você faz polling em uma programação e reforça isso com notificações em tempo real.

As duas têm um papel. Uma VoidedPurchaseNotification chega até você em segundos e diz para você verificar, mas a orientação do Google é tratá-la como um sinal, não como a verdade. Confirme o estado atual pela Voided Purchases API e só então revogue. A notificação elimina o atraso; a API fornece o voidedSource e o voidedReason oficiais sobre os quais você realmente age.

Olhe o voidedReason. Um 7 é um chargeback, em que o banco do cliente reverteu o pagamento, e um 6 é friendly fraud. Um 1 é um reembolso comum por mudança de ideia. Essa distinção importa porque, a partir de 3 de agosto de 2026, o Google repassa o valor do chargeback e a taxa do banco para o desenvolvedor, então um 7 custa mais caro do que um reembolso simples.

Sim. Defina o parâmetro type como 1 e você recebe compras avulsas anuladas junto com compras de assinatura anuladas; o padrão, 0, retorna apenas produtos avulsos. Para assinaturas, identifique o período exato da anulação pelo orderId, já que um único purchaseToken abrange todas as renovações, enquanto cada transação de renovação tem seu próprio orderId.

#voided purchases api#refunds#chargebacks#revoke access#rtdn#play billing
Refund SensorRefund Sensor TeamRefund defense for App Store and Google Play developers