Como verificar a disponibilidade da oferta introdutória no iOS

Se você usar ofertas introdutórias em seu aplicativo de assinatura (avaliação, pagamento conforme o uso ou pré-pagamento), antes de mostrar o preço na tela de pagamento, será necessário determinar a disponibilidade da oferta introdutória ao usuário. Se o usuário já tiver elaborado uma avaliação, você deverá exibir o preço total.


imagem


Olá pessoal, estou em contato com Renat da Apphud - um serviço que simplifica o trabalho com assinaturas em aplicativos iOS. Hoje vou lhe dizer como determinar se um único usuário tem o direito de ativar ou não uma frase introdutória.


A oferta introdutória é válida dentro do mesmo grupo de assinaturas. Isso significa que o usuário pode emitir uma assinatura semanal regular sem uma avaliação, cancelar a assinatura e posteriormente emitir uma avaliação para uma assinatura mensal.


A documentação da Apple possui um diagrama mostrando quando uma frase introdutória está disponível para o usuário:



Acontece que o usuário pode usar a frase introdutória se:


  • ele não tinha usado uma frase introdutória antes

E


  • a assinatura não foi emitida ou expirou

Para verificar a disponibilidade da frase introdutória, você precisa executar 3 etapas:


1) Valide a App Store-check e retire a matriz de transações. Se não houver transações, não verificamos nada, uma frase introdutória está disponível. Se houver transações, execute as duas etapas a seguir.


2) Verifique se a frase introdutória foi usada anteriormente


3) Verifique o status atual da assinatura


Vamos considerar essas etapas com mais detalhes.


1. Validação da verificação da App Store


Para validar a verificação, você precisa enviar uma solicitação para a Apple, passando receiptData e sharedSecret . Substitua o valor sharedSecret pelo seu. Se você não conhece seu sharedSecret , é descrito aqui onde obtê-lo.


 func isEligibleForIntroductory(callback: @escaping (Bool) -> Void){ guard let receiptUrl = Bundle.main.appStoreReceiptURL else { callback(true) return } #if DEBUG let urlString = "https://sandbox.itunes.apple.com/verifyReceipt" #else let urlString = "https://buy.itunes.apple.com/verifyReceipt" #endif let receiptData = try? Data(contentsOf: receiptUrl).base64EncodedString() let sharedSecret = "YOUR_SHARED_SECRET" let requestData = ["receipt-data" : receiptData ?? "", "password" : sharedSecret, "exclude-old-transactions" : false] as [String : Any] var request = URLRequest(url: URL(string: urlString)!) request.httpMethod = "POST" request.setValue("Application/json", forHTTPHeaderField: "Content-Type") let httpBody = try? JSONSerialization.data(withJSONObject: requestData, options: []) request.httpBody = httpBody URLSession.shared.dataTask(with: request) { (data, response, error) in // continue here }.resume() } 

O exemplo acima usa a macro #if DEBUG para determinar o tipo de assinatura: sandbox ou production . Se você usar outras macros, precisará atualizar o código neste local.

2. Verifique se uma frase introdutória foi usada antes


Depois de receber uma resposta da Apple, traduzimos para o Dictionary e obtemos uma série de transações:


 // paste this code after "continue here" comment guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String : AnyHashable], let receipts_array = json["latest_receipt_info"] as? [[String : AnyHashable]] else { callback(true) return } // continue here 

Analisamos a matriz de transações e examinamos os valores de is_trial_period e is_in_intro_offer_period . Se um dos valores for true , o usuário já redigiu uma frase introdutória. Esses valores vêm como uma string, portanto, para confiabilidade, tentaremos converter o valor em Bool e string.


 // paste this code after "continue here" comment var latestExpiresDate = Date(timeIntervalSince1970: 0) let formatter = DateFormatter() for receipt in receipts_array { let used_trial : Bool = receipt["is_trial_period"] as? Bool ?? false || (receipt["is_trial_period"] as? NSString)?.boolValue ?? false let used_intro : Bool = receipt["is_in_intro_offer_period"] as? Bool ?? false || (receipt["is_in_intro_offer_period"] as? NSString)?.boolValue ?? false if used_trial || used_intro { callback(false) return } // continue here 

3. Verificando o status atual da assinatura


Para descobrir o status atual da assinatura, precisamos encontrar o último expires_date e comparar com a data atual. Se a assinatura não tiver expirado, a oferta introdutória não estará disponível:


 // paste this code after "continue here" comment formatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV" if let expiresDateString = receipt["expires_date"] as? String, let date = formatter.date(from: expiresDateString) { if date > latestExpiresDate { latestExpiresDate = date } } } if latestExpiresDate > Date() { callback(false) } else { callback(true) } 

Você pode encontrar um link para o código completo do método no final do artigo, no entanto, existem muitos "Mas" nesse método.


Armadilhas


  • Neste exemplo, analisamos apenas o caso de um grupo de assinaturas. Se você usar mais de um grupo de assinaturas no aplicativo, deverá passar o identificador do grupo de assinaturas para esse método e verificá-lo pelo valor de subscription_group_identifier no receipt .


  • Neste exemplo, o caso de reembolso de assinatura não é levado em consideração. Para fazer isso, verifique a presença do campo cancellation_date :


     if receipt["cancellation_date"] != nil{ // if user made a refund, no need to check for eligibility callback(false) return } 

  • E aqui o período de carência (período de carência de cobrança) não é levado em consideração. Se o usuário estiver no período de cortesia no momento em que a verificação for validada, pending_renewal_info terá o campo grace_period_expires_date . Nesse caso, você como desenvolvedor é obrigado a fornecer funcionalidade premium ao usuário sem exibir uma tela de pagamento. E, portanto, não faz sentido verificar a disponibilidade da frase introdutória.


  • Há um problema ao verificar a data de validade. A hora do sistema no dispositivo iOS pode ser desaparafusada e, em seguida, nosso código fornecerá um resultado incorreto: a assinatura será considerada ativa.


  • A validação de uma verificação no próprio dispositivo não é recomendada pela Apple. Eles falaram sobre isso várias vezes na WWDC (das 5:50) e isso é indicado na documentação . Isso é inseguro porque um invasor pode interceptar dados usando um ataque man-in-the-middle. A Apple recomenda usar o servidor para validar verificações.



Verificando a disponibilidade de uma oferta promocional


A condição para a disponibilidade da oferta promocional é mais simples - o principal é que o usuário tenha uma assinatura ativa ou expirada. Para fazer isso, procure a presença de pending_renewal_info no seu grupo de assinaturas.


Como é implementado no Apphud SDK


Basta chamar um método, passando seu product para ele, que retornará o resultado para você:


 Apphud.checkEligibilityForIntroductoryOffer(product: myProduct) { result in if result { // User is eligible to purchase introductory offer } } 

E da mesma forma para a oferta promocional:


 Apphud.checkEligibilityForPromotionalOffer(product: myProduct) { result in if result { // User is eligible to purchase promotional offer } } 

Também existem métodos para verificar a disponibilidade de vários produtos ao mesmo tempo em uma chamada:


 func checkEligibilitiesForIntroductoryOffers(products: [SKProduct], callback: ApphudEligibilityCallback) func checkEligibilitiesForPromotionalOffers(products: [SKProduct], callback: ApphudEligibilityCallback) 

Conclusão


O código completo do método pode ser baixado aqui .


Nós da Apphud já implementamos uma verificação da disponibilidade de ofertas introdutórias e promocionais em um conveniente SDK de código aberto. O Apphud também ajuda a rastrear o status da assinatura, analisar as principais métricas, oferecer descontos automaticamente a usuários não inscritos e muito mais. Se sentir dor ao trabalhar com assinaturas, experimente nossa solução gratuitamente.

Source: https://habr.com/ru/post/pt471492/


All Articles