In-App Purchases & Subscriptions

Como respondo automaticamente a solicitações de reembolso da Apple para compras no aplicativo?

Descubra como a automação de reembolsos da Apple pode simplificar o tratamento de solicitações de reembolso e reduzir o trabalho manual para desenvolvedores de aplicativos.

5 min read
Como respondo automaticamente a solicitações de reembolso da Apple para compras no aplicativo?

Perdi quase um dia inteiro por causa do Undelivered Consumption Percentage Non Zero Error. Não porque seja um erro complicado. Mas porque eu tinha definido o delivery Status como algo diferente de DELIVERED e deixado o consumption Percentage em 60000, e a Apple rejeita essa combinação sem muita cerimônia.

O que é um jeito indireto de chegar à pergunta real.

Se você está tentando descobrir como responder automaticamente a solicitações de reembolso da Apple, é um trabalho menor do que a maioria das equipes imagina. A parte difícil nunca foi a chamada de API. É ter os dados para colocar nela. O passo a passo campo por campo está em Apple CONSUMPTION_REQUEST explained. Este aqui é sobre configurar tudo para que ninguém precise ficar de vigília.

Principais conclusões

  • A automação de reembolso da Apple é um webhook mais um PUT. A Apple envia um CONSUMPTION_REQUEST, e você responde em até 12 horas.

  • Você precisa de uma chave In-App Purchase, não da sua chave de API do App Store Connect. Elas não são intercambiáveis.

  • Três dos cinco campos são obrigatórios, e dois deles têm restrições que só aparecem na forma de erros.

  • Sem o consentimento do cliente, você não deve responder de forma alguma, e não é possível coletar o consentimento retroativamente.

  • O endpoint foi migrado para v2. O ambiente de produção é api.storekit.apple.com, e o sandbox dá 5 minutos em vez de 12 horas.

Como as solicitações de reembolso da Apple para desenvolvedores realmente funcionam

O cliente abre reportaproblem.apple.com, encontra a compra feita nos últimos 90 dias, escolhe um motivo e envia. Se você configurou as App Store Server Notifications V2, seu endpoint recebe um CONSUMPTION_REQUEST como um payload JWS assinado, contendo a transação, o produto e o motivo escolhido.

Esse webhook é todo o gatilho para um reembolso de compra no aplicativo da App Store. Nada aparece no seu dashboard, e não há fila para verificar. É por isso que as equipes descobrem que esse sistema existe semanas depois de já estarem perdendo dinheiro com ele.

Depois disso, você tem 12 horas.

Uma coisa que vale corrigir, porque é repetida em todo lugar. Isso não se limita a consumíveis. A documentação atual da Apple diz que um CONSUMPTION_REQUEST chega para qualquer tipo de produto: consumível, não consumível, assinatura sem renovação automática, assinatura autorrenovável. Guias mais antigos ainda dizem que é só para consumíveis e assinaturas. Eles estão desatualizados.

O que "responder" significa, na prática

Você envia um PUT com um corpo JSON para um endpoint.

PUT https://api.storekit.apple.com/inApps/v2/transactions/consumption/{transactionId}

O sandbox é api.storekit-sandbox.apple.com, mesmo caminho. O sucesso retorna 202 Accepted, não 200, o que confunde quem está validando códigos de status.

A autenticação é um JWT ES256 de curta duração, assinado com uma chave In-App Purchase, gerada no App Store Connect, em Users and Access, dentro de Integrations. Não é sua chave de API do App Store Connect. O arquivo .p8 é baixado exatamente uma vez, no momento da criação; então, se você o perdeu, vai precisar gerar um novo e rotacionar tudo que o usa. Pergunte como eu sei.

Os cinco campos, e os dois que mordem

O corpo é um Consumption Request. Três obrigatórios, dois opcionais.

  • Customer Consented (obrigatório). Verdadeiro somente se você tiver consentimento genuíno. Mais detalhes abaixo, porque esse campo é o portão para tudo.

  • Delivery Status (obrigatório). Se a compra chegou e funcionou.

  • Sample Content Provided (obrigatório). Se existia uma amostra gratuita, teste ou informação de funcionalidade antes da compra.

  • Consumption Percentage (opcional). Quanto foi consumido, em miliunidades. De 0 a 100000, então 45.2 por cento é 45200.

  • Refund Preference (opcional). GRANT_FULL, GRANT_PRORATED, DECLINE ou NO_PREFERENCE.

Duas restrições que vão custar uma tarde de trabalho cada uma.

Se o Delivery Status não for DELIVERED, o Consumption Percentage precisa ser 0. Qualquer outro valor falha com Undelivered Consumption Percentage Non Zero Error. Foi o que consumiu meu dia.

E, se você enviar GRANT_PRORATED para uma assinatura autorrenovável, não inclua o consumptionPercentage de forma alguma. A Apple calcula esse valor com base no tempo transcorrido no período de cobrança e rejeita o seu com Consumption Percentage Auto Renewable Subscription Error. Pelo menos os nomes dos erros são descritivos, o que já é mais do que eu esperava.

O consentimento é o portão, não uma formalidade

A documentação da Apple é incomumente direta aqui: se o cliente não consentiu, não responda à notificação. Não é responder com Customer Consented definido como false. É não responder.

Você também não pode coletar o consentimento depois que uma solicitação chega. Ele precisa existir previamente, o que significa que esse texto deve estar nos seus termos ou na sua política de privacidade antes de você lançar qualquer parte disso, não depois do seu primeiro reembolso contestado. A orientação da Apple é que o consentimento deve ser dado livremente, específico, informado e inequívoco, que opt-in é melhor que opt-out, e que obtê-lo é responsabilidade exclusivamente sua.

Na prática: uma frase dizendo que você pode compartilhar dados de compra e uso com a Apple para ajudar a analisar solicitações de reembolso, além de uma forma de retirar esse consentimento. Faça sua própria revisão jurídica disso. Não sou advogado, e isso não é um conselho jurídico.

Como os desenvolvedores automatizam solicitações de reembolso da App Store

O pipeline, na ordem em que eu o construiria:

  1. Configure uma Production Server URL no App Store Connect, em App Information, e escolha Version 2. Solicitações de consumo não trafegam pela V1.

  2. Verifique a cadeia de assinatura JWS em relação aos certificados raiz da Apple antes de confiar em qualquer campo.

  3. Associe a transação a um usuário. O appAccountToken que você anexou na compra é o caminho rápido. Sem ele, você fica fazendo busca reversa por produto e data, o que fica confuso rapidamente.

  4. Confira o consentimento. Sem consentimento, sem resposta. Registre e siga adiante.

  5. Busque o uso real. Não uma estimativa, não um valor padrão. Se você não conseguir responder "quanto disso eles usaram", essa é a verdadeira lacuna na sua automação.

  6. Monte o ConsumptionRequest, aplicando as duas restrições acima.

  7. Envie o PUT, confirme o 202 e armazene o payload que você enviou.

O passo 5 é onde a maioria das tentativas trava. A chamada de API é um trabalho de uma tarde. Ter dados honestos de consumo por usuário para uma compra feita sete semanas atrás é um problema de modelagem de dados que talvez você ainda não tenha resolvido.

O que faz isso se sustentar em produção

Além do caminho ideal, quatro coisas:

Idempotência. A Apple reenvia notificações, e seu handler vai ver a mesma duas vezes. Faça a deduplicação pelo UUID da notificação, ou você vai registrar respostas duplicadas.

Alertas que realmente avisam alguém. Um handler que falha silenciosamente é pior do que não ter handler nenhum, porque você vai acreditar que está protegido.

Um ambiente de testes em sandbox. O sandbox dá 5 minutos em vez de 12 horas, então você não pode testar isso manualmente de forma despretensiosa. Automatize com um script.

E um registro do que você enviou, por caso. Quando um reembolso é negado, você vai querer saber qual preferência você registrou e por quê. Quando um é concedido contra o seu DECLINE, você também vai querer saber disso.

Estimativa honesta: alguns dias de trabalho de integração se você já construiu esse tipo de infraestrutura de webhook antes, e algumas semanas para confiar nela. A maior parte disso não é do lado da Apple.

Ou deixe outra coisa fazer isso

Se você já usa RevenueCat ou Adapty, confira primeiro o que eles oferecem. Ambos adicionaram recursos de resposta a reembolsos, e seus recibos e mapeamento de clientes já estão lá.

Um software de gestão de reembolsos da Apple feito especificamente para isso vai além. Refund Sensor funciona com as chaves do App Store Connect que você já tem, responde a cada solicitação com evidências da transação dentro do prazo e registra o resultado de cada caso. Sem SDK, sem alterações de código, cerca de cinco minutos para conectar. Comparamos o mercado mais amplo em Apple refund management tools, se preferir pesquisar outras opções.

O lado do cliente, já que as pessoas perguntam

Sobre como solicitar reembolso de compras na App Store, do ponto de vista do usuário: acesse reportaproblem.apple.com, faça login, encontre a compra feita nos últimos 90 dias e escolha um motivo. A Apple informa aos clientes que eles terão uma resposta em até 48 horas.

Você também pode exibir esse mesmo formulário dentro do seu aplicativo usando o refund request sheet do StoreKit, que direciona usuários insatisfeitos para a Apple em vez de para uma avaliação de uma estrela. Ele não pode conceder um reembolso. Só a Apple pode fazer isso. Apple Support's guide to requesting a refund cobre o que o cliente vê.

Vale dizer claramente, porque a gestão de reembolsos da App Store é frequentemente supervalorizada: você não pode aprovar ou negar um reembolso. Você pode apresentar evidências à Apple enquanto ela decide, e indicar uma preferência. É toda a influência que você tem, e já é mais do que a maioria dos desenvolvedores usa.

Fontes

Perguntas frequentes

Sim. É para isso que existem a notificação CONSUMPTION_REQUEST e o endpoint Send Consumption Information. Você não pode conceder ou negar o reembolso, mas a Apple usa o que você envia como um dos fatores na decisão.

Receba as App Store Server Notifications V2, verifique a assinatura JWS, associe a transação a um usuário, confira o consentimento, monte dados reais de consumo e envie um PUT para o endpoint de consumo v2 dentro de 12 horas. Todo o resto é idempotência, alertas e registro de dados.

Uma chave In-App Purchase, em Users and Access, dentro de Integrations. Sua chave de API do App Store Connect não autentica essas chamadas, e você vai receber um 401 que parece um problema de relógio.

Não, é opcional. Mas se o deliveryStatus não for DELIVERED, ele precisa ser 0, e, para uma assinatura autorrenovável com valor proporcional, você precisa omiti-lo completamente.

Não responda. A orientação da Apple é explícita sobre isso, e o consentimento não pode ser coletado depois que a solicitação chega.

É um fator de entrada, não uma garantia. A Apple diz que usa uma variedade de fatores. Na prática, um DECLINE bem fundamentado em uma compra fortemente consumida é recusado com muito mais frequência do que uma solicitação que ninguém respondeu

#:Google Play chargeback#Google Play chargeback handling#Chargeback automation#Google Play refunds#App revenue protection#Chargeback management for apps
Refund SensorRefund Sensor TeamRefund defense for App Store and Google Play developers