Todos os artigos
Deep dive7 min de leitura

Existe um endpoint que retorna todo o histórico de reembolsos da App Store de um cliente, e aqui está o que ele devolve

O endpoint Get Refund History da Apple retorna o histórico completo de reembolsos da App Store de um cliente como transações assinadas. Aqui está cada campo, como o token revision pagina, por que ele é por cliente e não por app, e quanto custa um reembolso que passa despercebido.

Uma cena de mesa vista de cima com um smartphone, um livro de registros em papel com transações e uma lupa, ilustrando a obtenção do histórico de reembolsos da App Store de um cliente pelo endpoint Get Refund History da Apple

Principais conclusões

  • Get Refund History é um endpoint da App Store Server API que retorna as compras no app reembolsadas de um cliente para o seu app como uma lista de transações assinadas, para que você possa conciliar reembolsos e revogar o acesso mesmo quando uma notificação nunca chegou até você.
  • Você chama GET /inApps/v2/refund/lookup/{transactionId} com qualquer id de transação desse cliente, e a Apple retorna os reembolsos dele em todos os tipos de compra do seu app, não apenas o que você consultou.
  • A resposta tem três campos: signedTransactions, até 20 transações JWS por página ordenadas do reembolso mais antigo primeiro, além de um token revision e um booleano hasMore para paginação.
  • Guarde o token revision final. Passe-o de volta na próxima vez e a Apple retorna apenas os reembolsos mais recentes que aquele ponto, o que transforma um despejo completo do histórico em uma lista curta de linhas novas a cada execução.
  • Cada transação decodificada carrega revocationDate e revocationReason. Um revocationReason de 1 significa que o cliente pediu reembolso por um problema real ou percebido no seu app, e 0 significa outro motivo, como uma compra acidental.
  • O endpoint é por cliente, não por app. Não há uma única chamada que liste todos os reembolsos do seu app inteiro, então você concilia por conta a partir de um id de transação, ou lê seu feed de notificações REFUND para a visão de todo o app.
  • O motivo para configurá-lo é dinheiro. Um reembolso que você nunca detecta mantém uma conta ativa, e você continua pagando computação, chamadas à API do modelo, armazenamento e repasses por um cliente que a App Store já ressarciu.

A Apple mantém um registro consultável de cada reembolso que concedeu na conta de um cliente para o seu app, e uma chamada o retorna. O endpoint é o Get Refund History, parte da App Store Server API, e ele te entrega o histórico completo de reembolsos da App Store desse cliente como uma lista de transações assinadas. Você passa um id de transação, recebe de volta o que a Apple reembolsou, e concilia com o que você ainda tem ligado.

Aqui está por que vale a pena. Um reembolso que você nunca vê é um reembolso que você continua pagando. O dinheiro já se foi, mas a conta permanece ativa, e a cada hora que fica você continua gastando com computação, chamadas à API do modelo, armazenamento e qualquer repasse ligado a esse cliente. Suas notificações de reembolso deveriam detectar isso no momento em que acontece. O Get Refund History é a rede de segurança para quando elas não detectam, depois de uma queda, de um deploy que perdeu um webhook, ou de um caso de suporte em que você precisa do quadro completo em uma única chamada.

O que o endpoint de histórico de reembolsos da App Store retorna

Você chama GET /inApps/v2/refund/lookup/{transactionId} na App Store Server API, assinada com o mesmo JWT que você usa em todas as outras chamadas a ela. O id de transação no caminho pode ser qualquer transação do cliente. A Apple o lê como uma identidade, não como um filtro, e retorna as compras reembolsadas desse cliente em todo o seu app: consumíveis, não consumíveis, assinaturas renováveis automaticamente e não renováveis igualmente. A V1 mais antiga desse endpoint retornava até 50 reembolsos em uma única resposta e está obsoleta. A versão atual pagina, então você lida com clientes de históricos longos sem uma carga gigante.

A resposta são três campos

CampoO que contém
signedTransactionsAté 20 transações reembolsadas deste cliente, cada uma um JWS assinado que você verifica e decodifica. Ordenadas do reembolso mais antigo primeiro, por revocationDate. Um array vazio significa que o cliente não tem reembolsos no seu app
revisionUm token de paginação. Passe-o de volta para obter a próxima página, e guarde o último para buscar apenas os reembolsos novos na próxima vez
hasMoreVerdadeiro quando a Apple tem mais transações reembolsadas do que esta página retornou, então você chama de novo com o revision

O que uma transação reembolsada te diz

Cada entrada em signedTransactions é um JWS. Verifique-o contra a cadeia de certificados da Apple, decodifique-o, e você tem um payload de transação comum com os campos de reembolso preenchidos. Estes são os que importam aqui.

CampoO que te diz
transactionIdO id da transação reembolsada, sua chave de junção de volta à compra que você registrou
originalTransactionIdO id da primeira compra da cadeia, como você conecta as renovações de uma assinatura
productIdO produto que foi reembolsado, para você revogar o direito certo e nada mais
revocationDateO horário UNIX, em milissegundos, em que a Apple reembolsou a transação
revocationReasonPor que a Apple reembolsou. 1 significa um problema real ou percebido com o seu app, 0 significa outro motivo, como uma compra acidental
price, currencyO valor, em milliunits, e seu código de moeda ISO 4217, para você somar o dinheiro devolvido
appAccountTokenO UUID que você anexou na compra, a forma mais limpa de mapear um reembolso de volta ao seu próprio usuário

O token revision é como você para de reler a lista inteira

A forma ingênua de usar este endpoint é consultar um cliente e percorrer todas as páginas toda vez. Isso funciona, e em um cliente com cinquenta reembolsos são cinquenta linhas que você já conhecia mais a única nova. O token revision existe para eliminar esse desperdício. Cada resposta carrega um revision. Quando hasMore é verdadeiro, você o passa de volta para obter a próxima página. Quando você chega ao fim, guarda o último revision que viu.

O que este endpoint não fará

Há uma expectativa a abandonar antes de construir sobre ele. O Get Refund History é por cliente, não por app. Você não pode pedir a ele todos os reembolsos que seu app recebeu na semana passada. Ele responde a uma pergunta, quais reembolsos esta conta tem, e você precisa chegar com um id de transação dessa conta para perguntar. Os desenvolvedores batem nessa parede o tempo todo e saem procurando um endpoint de reembolsos de todo o app que não existe.

A visão de todo o app fica em outro lugar. Seu feed de App Store Server Notifications envia uma notificação REFUND no momento em que a Apple concede cada uma, e o Get Notification History permite reproduzir esse feed filtrado pelos tipos de reembolso em um intervalo de datas. Então a divisão é limpa. As notificações e o histórico delas te dão o fluxo de todo o app. O Get Refund History te dá a lista autoritativa de uma conta, sob demanda, que é o que você quer em um balcão de suporte ou depois de uma queda.

Uma lupa sobre uma linha destacada de um livro de registros de transações em papel ao lado de um smartphone, ilustrando a consulta dos reembolsos de um único cliente no histórico de reembolsos da App Store

Quanto um reembolso não detectado te custa em dinheiro

O endpoint é encanamento. A conta é o motivo pelo qual você instala o cano. Cada reembolso naquela lista é dinheiro já devolvido, e a única variável que resta sob seu controle é por quanto tempo você continua gastando com uma conta que não paga mais.

Você continua pagando para atender uma conta reembolsada

O preço da compra some no instante em que a Apple concede o reembolso. O que continua rodando é o custo da entrega. Para um app que faz trabalho real por usuário, isso é computação, chamadas à API do modelo, armazenamento e qualquer repasse a criador ou parceiro ligado ao uso dele. Um cliente reembolsado cujo acesso você nunca corta é uma assinatura que você banca do próprio bolso. Conciliar contra o Get Refund History e revogar com base no que você encontra é como você desliga esse medidor quando uma notificação escapou.

Um motivo de reembolso 1 é um relatório de defeito disfarçado

revocationReason te custa em dobro se você o ignora. O primeiro custo é o reembolso em si. O segundo é cada reembolso futuro pela mesma causa. Quando um produto continua voltando com revocationReason 1, um problema real ou percebido no seu app, a Apple está te entregando uma amostra rotulada do que faz os clientes pedirem o dinheiro de volta. Acompanhe a tendência por produto e você poderá tapar o vazamento em vez de pagá-lo um reembolso de cada vez.

Detectar tarde ainda é melhor do que não detectar

Um estorno é definitivo com o banco e, na outra loja, agora carrega uma taxa que o desenvolvedor absorve. Um reembolso da App Store não é isso. Está liquidado, mas o direito é seu para revogar no momento em que você souber. Então mesmo um reembolso que você encontra dias atrasado por este endpoint vale a pena encontrar. Você não pode recuperar o dinheiro, mas pode parar o gasto que ainda corria por trás dele.

Como isso se encaixa com as notificações, e com o Google

Pense nas peças como um único sistema. A notificação REFUND é o sinal ao vivo, enviado ao seu servidor conforme a Apple decide. O Get Refund History é a fonte da verdade baseada em extração para um único cliente, a chamada que você faz quando o envio falhou ou quando uma pessoa precisa da conta completa à frente. No lado do Google Play a forma é a mesma ideia com nomes diferentes: uma VoidedPurchaseNotification é enviada em tempo real, e a Voided Purchases API é a lista que você extrai. Ambas as lojas te dão um fluxo e um livro de registros. O erro é confiar apenas no fluxo, porque fluxos caem.

Configurando do jeito RefundHalt

O ciclo é pequeno depois que cada peça está no lugar. Pegue uma notificação REFUND como gatilho. Concilie contra o Get Refund History para que um webhook perdido nunca deixe uma conta reembolsada ativa. Decodifique cada transação, vincule-a por appAccountToken ou transactionId de volta ao seu usuário, leia revocationReason para que um reembolso por defeito seja sinalizado e não apenas arquivado, e revogue o direito exato em vez da conta inteira. Pagine com o token revision para ler os reembolsos novos, não os antigos.

Esta é a parte que o RefundHalt roda por você. Ele escuta as notificações de reembolso, recorre ao Get Refund History quando precisa da lista autoritativa, verifica cada transação assinada, revoga a compra precisa, e guarda o revision para que cada passagem leia apenas o que mudou. Você tem o acesso cortado em segundos e um registro limpo de quem foi reembolsado, por qual produto e por quê, sem precisar montar você mesmo o polling e a verificação JWS.

Perguntas frequentes

Como vejo todos os reembolsos do meu app inteiro, não apenas de um cliente?
Você não consegue com o Get Refund History, porque ele é por cliente e precisa de um id de transação da conta que você está consultando. Para a visão de todo o app, use seu feed de App Store Server Notifications, que envia uma notificação REFUND para cada reembolso conforme a Apple o concede, e o Get Notification History para reproduzir esse feed filtrado pelos tipos de reembolso em um intervalo de datas.
Quantos reembolsos o endpoint Get Refund History retorna?
A versão atual retorna até 20 transações reembolsadas por página, ordenadas com o reembolso mais antigo primeiro, e pagina pelo restante com um token revision quando hasMore é verdadeiro. O endpoint V1 obsoleto retornava até 50 em uma única resposta. Não há limite para o total, então um cliente com histórico longo simplesmente ocupa mais páginas.
Para que serve o token revision?
É como você pagina e como evita reler todo o histórico de um cliente toda vez. Cada resposta inclui um revision. Você o passa de volta para buscar a próxima página, e guarda o último para que sua próxima consulta retorne apenas os reembolsos mais recentes que aquele ponto. Isso mantém uma conciliação agendada em uma lista curta de linhas novas.
O que revocationReason significa em uma transação reembolsada?
É o motivo pelo qual a Apple reembolsou a transação. Um valor de 1 significa que o cliente pediu reembolso por causa de um problema real ou percebido dentro do seu app, e 0 significa outro motivo, como uma compra acidental. revocationDate te diz quando o reembolso aconteceu, em milissegundos UNIX. Ler revocationReason permite separar um defeito de produto de um reembolso pontual por arrependimento.
Ainda preciso disso se já trato as notificações REFUND?
Sim, como rede de segurança. As notificações são o sinal ao vivo, mas um envio pode não chegar durante uma queda, um deploy ruim ou uma mudança de webhook, e um reembolso não detectado deixa uma conta reembolsada ativa e te custando dinheiro. O Get Refund History é a fonte da verdade baseada em extração contra a qual você concilia para que nada permaneça ligado que a Apple já reembolsou.

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.