BlogRight arrowTutorialRight ArrowCompras no aplicativo para Android, parte 5: validação de compras do lado do servidor.
BlogRight arrowTutorialRight ArrowCompras no aplicativo para Android, parte 5: validação de compras do lado do servidor.

Compras no aplicativo para Android, parte 5: validação de compras do lado do servidor.

Compras no aplicativo para Android, parte 5: validação de compras do lado do servidor.
Listen to the episode
Compras no aplicativo para Android, parte 5: validação de compras do lado do servidor.

A validação do lado do servidor (server-side) pode ajudá-lo a validar a autenticidade da compra. O dispositivo fará uma solicitação aos servidores do Google para confirmar se a compra foi realmente realizada e se é válida.

Neste guia, vamos discutir como configurar a validação do lado do servidor para aplicativos (apps) Android. 

Por que validar as compras?

Deve-se salientar que a validação do lado do servidor não é obrigatória — as compras no aplicativo ainda serão realizadas sem ela. No entanto, há alguns benefícios significativos em relação à validação:

  1. Fazer o analytics avançado do pagamento, o que é particularmente importante no caso de assinaturas, uma vez que tudo o que acontece após a ativação não é processado pelo dispositivo. Sem o processamento da compra pelo lado do servidor, não é possível recuperar o status atual da assinatura e saber se o usuário renovou ou cancelou a assinatura, se ocorreu algum problema de pagamento, e assim por diante.
  2. Verificar a autenticidade da compra. Você deve se certificar de que a transação não é fraudulenta, e que o usuário realmente pagou pelo produto.
  3. Cruzar assinaturas em diferentes plataformas. Caso seja possível verificar o status da assinatura do usuário em tempo real, é possível sincronizá-la com outras plataformas. Por exemplo, o usuário que adquiriu a assinatura em um dispositivo iOS poderá utilizá-la no Android, na Web e em outras plataformas. 
  4. Conseguir controlar o acesso ao conteúdo do lado do servidor, o que o protege dos usuários que tentam acessar os dados sem assinatura, simplesmente fazendo solicitações ao servidor. 

Segundo nossa experiência, a primeira vantagem, por si só, já é suficiente para configurar o processamento de compras no lado do servidor.

⭐️ Download our guide on in-app techniques which will make in-app purchases in your app perfect

Validação do pagamento

Podemos resumir a validação do pagamento no Android com este esquema:

Autenticação para solicitações da API do Google Play Developer

Para trabalhar com a API do Google Play Developer, você deve primeiro gerar uma chave para assinar as solicitações. Primeiro, você deve vincular sua conta Google Play Console (onde você gerencia o aplicativo) à sua conta Google Cloud (onde você irá gerar uma chave para assinatura da solicitação). Depois que tudo estiver configurado, você deve conceder ao usuário direitos de gerenciamento de compras. Para descrever este processo, é necessário um artigo específico. Felizmente, já tratamos do assunto em um guia passo a passo que está disponível na documentação da Adapty

Lembre-se que normalmente você deve esperar 24 horas ou mais depois de gerar uma chave para que ela seja ativada. Para evitar essa situação, basta atualizar a descrição de qualquer produto ou assinatura no sistema, o que ativará instantaneamente a chave. 

Utilizamos a biblioteca oficial google-api-python-client (Biblioteca cliente da API do Google para Python) para trabalhar com a API do Google Play Developer. Esta biblioteca está disponível na maioria dos idiomas conhecidos, e eu recomendo usá-la, pois ela é compatível com todos os métodos que você possa precisar.

Validação de transações de assinatura

Ao contrário da validação do lado do servidor iOS, no Android, a assinatura e a validação de outros produtos podem ser implementadas usando uma variedade de métodos. Portanto, ao validar uma transação, é preciso saber se você está lidando com um produto ou com uma assinatura. Na prática, isto significa que se deve transferir estes dados do aplicativo móvel, assim como manter a bandeira no banco de dados caso seja necessária a revalidação do token.

A segunda diferença mais importante é que enquanto cada transação tem seu próprio token no Android, todas as transações no iOS usam um segredo compartilhado (shared secret) específico do aplicativo para armazenar todo o histórico da transação. Isto significa que se você deseja restaurar as compras do usuário a qualquer momento, você deve armazenar todos os tokens de compra, ao invés de escolher apenas um token de forma aleatória.

Para validar a assinatura, você deve usar o método purchases.subscriptions.get.  Basicamente, trata-se de uma chamada para solicitação GET:

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}

Todos os parâmetros são obrigatórios:

  • packageName: Identificador do aplicativo, por exemplo, com.adapty.sample_app.
  • subscriptionId: Identificador da assinatura que deve ser validada, por exemplo, com.adapty.sample_app.weekly_sub.
  • token: Token exclusivo da transação. Ele aparece assim que a compra é processada no lado do aplicativo móvel.

Primeiro, vamos verificar as mensagens de erro às quais você deve prestar atenção para garantir que tudo funcione como planejado:

  • 400, Invalid grant: account not found (400, Concessão inválida, conta não encontrada): Esta mensagem de erro significa que a chave de autenticação da solicitação foi gerada incorretamente. Verifique se suas contas estão vinculadas, se você está usando a conta correta com todas as permissões exigidas, e se todas as APIs necessárias foram ativadas. Consulte a seção abaixo e consulte um guia sobre como configurar tudo. Lembre-se da dica sobre a atualização da descrição do produto.
  • 400, The purchase token does not match the package name (400, O token de compra não corresponde ao nome do pacote): Esta mensagem de erro é normalmente detectada em transações fraudulentas. Caso você a veja durante o teste, certifique-se de não estar usando um token de compra no aplicativo que pertence a um aplicativo distinto.
  • 403, Quota exceeded for quota metric 'Queries' and limit 'Queries per day' of service 'androidpublisher.googleapis.com' ( 403, Cota excedida para a métrica de cota "Consultas" e limite "Consultas por dia" de serviço 'androidpublisher.googleapis.com') : Esse erro significa que a cota diária de solicitações da API do Google foi excedida. Por padrão, você pode executar até 200.000 solicitações por dia. Pode-se aumentar o valor desta cota, mas ela deve ser suficiente para a maioria das aplicações. Caso você se depare com este limite, talvez seja melhor verificar novamente a lógica de seu aplicativo e garantir que tudo esteja certo.
  • 410, The subscription purchase is no longer available for query because it has been expired for too long (410, A compra por assinatura não está mais disponível para consulta porque já expirou): Esta mensagem de erro aparece em transações para as quais a assinatura expirou há mais de 60 dias. Não se trata de uma mensagem de erro de fato e não deve ser processada como tal.
Start for free

You don't need to write server code yourself,

because we did it for you. Try Adapty SDK!

Start for free

Transação de assinatura 

Caso a validação ocorra com sucesso, você receberá os dados da transação como resposta.

Dados da transação (para uma assinatura):

{
    "expiryTimeMillis": "1631116261362",
    "paymentState": 1,
    "acknowledgementState": 1,
    "kind": "androidpublisher#subscriptionPurchase",
    "orderId": "GPA.3382-9215-9042-70164",
    "startTimeMillis": "1630504367892",
    "autoRenewing": true,
    "priceCurrencyCode": "USD",
    "priceAmountMicros": "1990000",
    "countryCode": "US",
    "developerPayload": ""
}

Para saber se o usuário pode acessar as opções premium oferecidas pelo aplicativo — ou seja, se ele tem uma assinatura ativa — é necessário:

  1. Verifique os parâmetros startTimeMillis e expiryTimeMillis. O instante atual deve estar entre os valores destes dois parâmetros.
  2. Além disso, é preciso garantir que o parâmetro paymentState não tenha o valor '0'. Isso significa que a compra da assinatura ainda está pendente, portanto, não há necessidade de conceder ao usuário acesso à função premium no momento. 
  3. Caso a transação tenha a propriedade autoResumeTimeMillis , a assinatura será pausada. Na prática, isto significa que o usuário não deve ter acesso à função premium antes da data especificada.

Vamos ver as principais propriedades de uma transação de assinatura:

  • kind: Tipo de transação. Para a assinatura, tem sempre o valor do androidpublisher#subscriptionPurchase. Com este parâmetro, você sabe se está lidando com uma assinatura ou com um produto, e escolhe a lógica de processamento adequada.
  • paymentState: Status do pagamento. Esta propriedade não está disponível para transações expiradas. Os valores possíveis são:
    0: Esta compra ainda não foi processada. Em alguns países, o usuário pode pagar a assinatura no local de domicílio. Ou seja, o usuário inicia a compra da assinatura em seu dispositivo e paga por ela em um terminal próximo. Geralmente, é um caso bastante raro, mas ainda assim é preciso considerá-lo.
    1: Uma assinatura foi adquirida.
    2: A assinatura está no período de avaliação.
    3: A assinatura será promovida ou rebaixada no próximo período. Isto significa que o plano de assinatura será alterado.
  • acknowledgementState: Status de confirmação da compra. É um parâmetro importante que reconhece se o usuário obteve o acesso ao que pagou. O valor '0' significa que eles não têm, e '1' significa que eles têm. O desenvolvedor é responsável por definir o status, o que pode ser feito do lado do aplicativo móvel e do lado do servidor. Caso você não reconheça a compra no prazo de 3 dias após ela ter sido realizada, ela será automaticamente reembolsada. Recomendo implementar a seguinte lógica: ao receber uma transação contendo acknowledgementState=0, o parâmetro é alterado pelo servidor. A seguir, vou explicar como proceder. 
  • orderId: Identificador exclusivo da transação. Cada compra ou renovação de assinatura terá seu próprio identificador, que pode ser usado para verificar se a transação em questão já foi processada anteriormente. Cada identificador de renovação terá uma primeira metade constante, à qual serão adicionados dois pontos e a contagem de renovação da assinatura (que começa com 0). Caso a assinatura tenha o identificador GPA.3382-9215-9042-70164 quando ativada, então a primeira renovação será identificada pelo GPA.3382-9215-9042-70164..0, a segunda pelo GPA.3382-9215-9042-70164..1, etc. Desta forma, você pode construir cadeias de transações e acompanhar a contagem da renovação.
  • startTimeMillis: Data de início da assinatura.
  • expiryTimeMillis: Data de expiração da assinatura.
  • autoRenewing: Bandeira que indica se a assinatura deve ou não ser renovada para o próximo período.
  • priceCurrencyCode: A moeda de compra em um formato de três letras, por exemplo, USD.
  • priceAmountMicros: Preço de compra. Para obter o valor do preço normal, divida este valor por 1000000. Ou seja, 1990000 corresponde na verdade a 1,99. 
  • countryCode: O país de compra em um formato de duas letras, por exemplo, US.
  • purchaseType: Tipo de compra. É um botão não estará presente na maioria dos casos. Ainda assim é importante contabilizá-lo, pois ajuda a entender se a compra foi feita em um ambiente Sandbox. Os valores possíveis são:
    0: A compra foi feita em um ambiente Sandbox, portanto, não deve ser incluída nos dados de analytics.
    1: A compra foi feita utilizando um código promocional. 
  • autoResumeTimeMillis: Data de renovação da assinatura. Só está presente para assinaturas que tenham sido previamente pausadas. Caso este parâmetro esteja presente, não é necessário dar ao usuário acesso à função premium antes da data determinada.
  • cancelReason: A razão pela qual a assinatura não será renovada. Os valores possíveis são:
    0: O usuário cancelou a renovação automática da assinatura.
    1: A assinatura foi cancelada pelo sistema. Geralmente, o motivo do cancelamento está relacionado a um problema de cobrança.
    2: O usuário migrou para um plano de assinatura diferente.
    3: O desenvolvedor cancelou a assinatura.
  • userCancellationTimeMillis: Dados de cancelamento da renovação da assinatura. Só está presente se o motivo do cancelamento for 0. A assinatura ainda pode estar ativa — para ter certeza, consulte o valor do parâmetro expiryTimeMillis .
  • cancelSurveyResult: Objeto que armazena o motivo do cancelamento da assinatura, que estará presente caso o usuário tenha deixado algum feedback sobre o assunto.
  • introductoryPriceInfo: Objeto que armazena os dados de preços iniciais. Por exemplo, pode ser uma oferta especial de 1 mês com 50% de desconto.
  • promotionType: Tipo de código promocional que foi usado para ativar a assinatura. Os valores possíveis são:
    0: One-time promo code.
    1: Código promocional personalizado que pode ser usado por vários clientes. Esses códigos são normalmente usados em parcerias com blogueiros. 
  • promotionCode: Código promocional personalizado que foi usado para ativar a assinatura. Este parâmetro não está presente para códigos promocionais únicos. 
  • priceChange: Objeto que armazena dados sobre futuras mudanças de preço, bem como se o usuário concordou com essas mudanças.

Reconhecimento de assinatura

Como já foi mencionado acima, caso a assinatura não seja reconhecida no prazo de 3 dias após a compra, ela será cancelada e reembolsada automaticamente.. Para ser honesto, eu realmente não entendo a lógica disso, e nunca me deparei com esse tipo de questão em nenhum outro sistema de processamento de pagamentos, inclusive o iOS. De qualquer forma, caso você receba uma transação contendo o acknowledgementState=0, você deve reconhecer a assinatura.

Para isso, você deve usar o método purchases.subscriptions.acknowledge. Este método executa uma solicitação POST.

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}:acknowledge

Os parâmetros são os mesmos usados para a validação da solicitação. Caso a solicitação seja executada com sucesso, a assinatura será reconhecida, o que significa que você não perderá seu dinheiro. 

Caso a compra da assinatura ainda não tenha sido concluída, não há necessidade de reconhecê-la. 

Cancelamento da renovação de assinatura, revogação, reembolso e adiamento

Além da validação e reconhecimento da assinatura, a API do Google Play Developer também pode ser usada para outras operações relacionadas à assinatura. Deve-se destacar que tais operações são bastante raras e, com exceção da renovação, são suportadas pelo Google Play Console. Mesmo assim, vou listá-los para dar uma ideia geral do escopo das soluções de API. Todas estas solicitações têm os mesmos parâmetros exigidos pelos métodos mencionados anteriormente, entre os quais, packageName, subscriptionId e token.

  • Cancelamento da renovação. O método purchases.subscriptions.cancel. Ela cancela a renovação automática de uma assinatura selecionada. Entretanto, a assinatura ainda estará disponível durante todo o período de cobrança vigente.
  • Reembolso da assinatura. O método purchases.subscriptions.refund . Faz o reembolso da assinatura. Entretanto, o usuário ainda terá acesso à assinatura, e ela será automaticamente renovada no próximo período. Na maioria dos casos, o usuário também deve revogar a assinatura ao emitir um reembolso.
  • Revogação da assinatura. O método purchases.subscriptions.revoke . Faz a revogação imediata da assinatura, o que significa que o usuário não poderá acessar as funções premium. A assinatura não será renovada. Este método é normalmente usado junto com a emissão de um reembolso.
  • Prorrogação da assinatura O método purchases.subscriptions.defer. Faz a prorrogação da assinatura até a data especificada. Na solicitação, especifique a data de expiração da assinatura, bem como a data pela qual você deseja substituí-la. Esta última deve resultar em um período de assinatura mais longo do que a primeira.

Validação de produto (não de assinatura)

A validação de um produto é semelhante à validação da assinatura. Você pode usar o método purchases.products.get para executar uma solicitação GET.

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}

Já temos familiaridade com todos estes parâmetros ao observarmos os exemplos descritos acima.

Dados da transação (para um produto):

{
  "purchaseTimeMillis": "1630529397125",
  "purchaseState": 0,
  "consumptionState": 0,
  "developerPayload": "",
  "orderId": "GPA.3374-2691-3583-90384",
  "acknowledgementState": 1,
  "kind": "androidpublisher#productPurchase",
  "regionCode": "RU"
}

As transações de produto envolvem um número muito menor de propriedades do que as transações de assinatura.. Vejamos algumas propriedades importantes:

  • kind: Tipo de transação. Para produtos, o valor é sempre androidpublisher#productPurchase. Com este parâmetro, você sabe se está lidando com uma assinatura ou com um produto, e escolhe a lógica de processamento adequada.
  • purchaseState: Status do pagamento. Lembre-se que os valores de chave neste caso são diferentes do parâmetro paymentState quando se trata de assinaturas. Os valores possíveis são:
    0: A compra foi concluída.
    1: A compra foi cancelada. Isto significa que a compra estava pendente, mas o usuário nunca realizou o pagamento.
    2: A compra está pendente. Em alguns países, o usuário pode pagar a assinatura no local de domicílio. Ou seja, o usuário inicia a compra da assinatura em seu dispositivo e paga por ela em um terminal próximo. Geralmente, é um caso bastante raro, mas ainda assim é preciso considerá-lo.
  • acknowledgementState: Status de confirmação da compra. É um parâmetro importante que reconhece se o usuário obteve o acesso ao que pagou. O valor '0' significa que eles não têm, e '1' significa que eles têm. O desenvolvedor é responsável por definir o status, o que pode ser feito do lado do aplicativo móvel e do lado do servidor. Caso você não reconheça a compra no prazo de 3 dias após ela ter sido realizada, ela será automaticamente reembolsada. Recomendo implementar a seguinte lógica: ao receber uma transação contendo acknowledgementState=0, o parâmetro é alterado pelo servidor. A seguir, vou explicar como proceder. 
  • consumptionState: Status de consumo do produto. Isso é o que o iOS denomina de “consumível”. Ele é definido no lado do aplicativo móvel. Caso tenha o valor '0', significa que o produto não foi consumido; mas se for ‘1’, então foi. Caso esteja vendendo acesso vitalício ao seu aplicativo ou alguma característica premium específica, o produto não deve ser consumido, ou seja, ele deve ter o status 0. Caso esteja vendendo moedas, o usuário pode comprar várias vezes, ou seja, esses produtos devem ser consumidos devendo ter o status 1. consumptionState=0 significa que o produto só pode ser comprado uma vez, enquanto consumptionState=1 significa que pode ser comprado muitas vezes..
  • orderId: Identificador exclusivo da transação. Cada compra ou renovação de assinatura terá seu próprio identificador, que pode ser usado para verificar se a transação em questão já foi processada anteriormente.
  • purchaseTimeMillis: Data da compra.
  • regionCode: O país de compra em um formato de duas letras, por exemplo, US. Observe que o nome deste parâmetro é diferente daquele das assinaturas, cuja denominação é countryCode
  • purchaseType: Tipo de compra. É um botão não estará presente na maioria dos casos. Ainda assim é importante contabilizá-lo, pois ajuda a entender se a compra foi feita em um ambiente Sandbox. Os valores possíveis são:
    0: A compra foi feita em um ambiente Sandbox, portanto, não deve ser incluída nos dados analíticos.
    1: A compra foi feita utilizando um código promocional.
    2: A compra foi concedida para uma ação de destino, por exemplo, assistir a um anúncio no aplicativo no local de pagamento.

Como é possível verificar, a validação do produto é bastante semelhante à validação da assinatura. Há alguns pontos a serem observados, no entanto:

  • O preço não é reembolsado, embora isso fosse bastante útil para o processo de analytics.
  • Os valores do parâmetro purchaseState são significativamente diferentes dos valores do parâmetro paymentState encontrados nas assinaturas. Isso causará bugs se não for considerado. 
  • regionCode é devolvido, mesmo que seja chamado countryCode no caso de assinaturas.

Assim como acontece para a compra de assinaturas, a compra de produtos precisa ser reconhecida. Para isso, utilize o método purchases.products.acknowledge que executará uma solicitação POST

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}:acknowledge

Caso a compra ainda não tenha sido concluída, não há necessidade de reconhecê-la.

Rastreamento de reembolsos para assinaturas e produtos

Um analytics de alta qualidade é impossível sem a contabilização dos reembolsos. Infelizmente, os dados de reembolso não estão presentes na transação nem são solicitados como um evento separado, já que funcionam no iOS. Para receber a lista de transações reembolsadas, você deve usar purchases.voidedpurchases.list regularmente — por exemplo, uma vez por dia. Este método executará uma solicitação GET:

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/voidedpurchases

Em resposta à solicitação, você receberá a lista de todas as transações reembolsadas. Recomendo a busca de transações no banco de dados usando o parâmetro orderId, ao contrário do parâmetro purchaseToken. Para começar, este processo vai levar menos tempo para ser executado. Depois, todas as renovações de assinatura irão compartilhar o mesmo token, e você só vai precisar buscar o token mais recente.

Notificações do servidor para transações

As notificações do servidor (notificações do desenvolvedor em tempo real) ajudam a obter informações sobre os eventos que ocorreram do lado do Google, no seu servidor e quase ao vivo. Assim que as notificações forem configuradas, você será notificado quando ocorrem novas compras, renovações, problemas de pagamento, etc. Isto pode ajudá-lo a coletar melhores dados de analytics, bem como facilitar a gestão do status do assinante.

Para começar a receber notificações do servidor, você deve criar um tópico Pub/Sub (Compra/Assinatura) no Google Cloud, que enviará as notificações para o endereço desejado. Em seguida, o tópico deve ser indicado na seção de configuração da Monetização do Google Play Console. Para um guia detalhado com capturas de tela incluídas, consulte os documentos da Adapty.

Notificação do servidor:

{
  "message": {
    "data": "eyJ2ZXJzaW9uIjoiMS4wIiwicGFja2FnZU5hbWUiOiJjb20uYWRhcHR5LnNhbXBsZV9hcHAiLCJldmVudFRpbWVNaWxsaXMiOiIxNjMwNTI5Mzk3MTI1Iiwic3Vic2NyaXB0aW9uTm90aWZpY2F0aW9uIjp7InZlcnNpb24iOiIxLjAiLCJub3RpZmljYXRpb25UeXBlIjo2LCJwdXJjaGFzZVRva2VuIjoiY2o3anAuQU8tSjFPelIxMjMiLCJzdWJzY3JpcHRpb25JZCI6ImNvbS5hZGFwdHkuc2FtcGxlX2FwcC53ZWVrbHlfc3ViIn19",
    "messageId": "2829603729517390",
    "message_id": "2829603729517390",
    "publishTime": "2021-09-01T20:49:59.124Z",
    "publish_time": "2021-08-04T20:49:59.124Z"
  },
  "subscription": "projects/935083/subscriptions/adapty-rtdn"
}

Estamos principalmente preocupados com a chave data que contém dados de transação codificados com o algoritmo base64. A chave messageId pode ser usada para a deduplicação de mensagens, para evitar o processamento de mensagens duplicadas. 

Confira abaixo uma transação contida em uma notificação do servidor:

{
  "version": "1.0",
  "packageName": "com.adapty.sample_app",
  "eventTimeMillis": "1630529397125",
  "subscriptionNotification": {
    "version": "1.0",
    "notificationType": 6,
    "purchaseToken": "cj7jp.AO-J1OzR123",
    "subscriptionId": "com.adapty.sample_app.weekly_sub"
  }
}

A chave packageName ajuda a entender a que aplicativo este evento pertence. A chave subscriptionId informa qual assinatura está envolvida, e a chave. purchaseToken ajuda a encontrar a transação específica. No caso das assinaturas, o que se procura sempre é a última transação na cadeia de renovação, pois é a ela que este evento pertencerá. A chave notificationType contém o tipo de evento. Na minha opinião, os seguintes eventos são as mais úteis no que se refere às assinaturas:

  • (2) SUBSCRIPTION_RENEWED: A assinatura foi renovada com sucesso.
  • (3) SUBSCRIPTION_CANCELED: O usuário desativou a renovação automática da assinatura. Caso a renovação automática esteja desativada, é necessário tentar recuperar o usuário como assinante ativo.
  • (5) SUBSCRIPTION_ON_HOLD, (6) SUBSCRIPTION_IN_GRACE_PERIOD: A assinatura não poderia ter sido renovada devido a problemas de pagamento. Você deve notificar o usuário sobre o problema para que sua assinatura não seja cancelada automaticamente.
  • (12) SUBSCRIPTION_REVOKED: a assinatura foi revogada. Isto significa que o usuário perderá o acesso às funções premium previamente concedidas pela assinatura. 

Para os produtos (não assinaturas), você receberá oneTimeProductNotification no lugar da chave subscriptionNotification. A notificação também conterá a chave de SKU em vez da chave subscriptionId  Além disso, você receberá apenas 2 tipos de eventos no caso dos produtos:

  • (1) ONE_TIME_PRODUCT_PURCHASED: Compra de produtos realizada com sucesso.
  • (2) ONE_TIME_PRODUCT_CANCELED: A compra do produto foi cancelada, pois o usuário não efetuou o pagamento.

Conclusão

A validação do lado do servidor supercarrega os dados de analytics que você pode coletar para o seu aplicativo. Isso dificulta o acesso dos fraudadores ao conteúdo premium e pode ser usado para implementar assinaturas entre plataformas. Entretanto, a validação do lado do servidor pode levar muito mais tempo para ser implementada, especialmente se for necessário uma alta precisão dos dados. Para fornecer dados de alta qualidade, você deveria contabilizar uma infinidade de casos paralelos, como atualização de assinatura, crossgrade de assinatura, períodos de teste, ofertas promocionais e iniciais, período de carência, reembolsos, etc. Você também deveria conhecer e considerar todas as minúcias políticas, por exemplo, como o Google cobra apenas 15% (contra 30%) de comissão sobre as assinaturas que são renovadas por um prazo superior a um ano.

Further reading

Dark patterns and tricks in mobile apps
Dark patterns and tricks in mobile apps
June 21, 2021
5 min read
Adapty June Update: Podcast, Infrastructure, Fallbacks
Adapty June Update: Podcast, Infrastructure, Fallbacks
July 1, 2021
2 min read
First steps to better monetization
First steps to better monetization
February 15, 2022
38 min listen