...

воскресенье, 20 октября 2019 г.

То, что нужно знать о проверке чека App Store (App Store receipt)

В StackOverflow по-прежнему появляется много вопросов о валидации App Store чеков, поэтому мы решили написать статью на эту тему в формате вопросов и ответов.

image


Что представляет собой App Store чек?

Это зашифрованный файл в формате PKCS#7, который содержит в себе информацию обо всех покупках в приложении. Находится в бандле приложения и его можно легко получить, вызвав: Bundle.main.appStoreReceiptURL.


Всегда ли есть этот файл?

Если приложение было скачано из App Store, то да, всегда. А если было установлено через Xcode или Testflight, то первоначально у приложения sandbox-чек отсутствует до первой покупки или восстановления чека.


Что значит "провалидировать чек"?

Это означает расшифровать файл, получить JSON дату и сверить совершенные пользователем покупки. Сделать это можно локально или отправив запрос в Apple.


В каких случаях разработчику требуется валидация чека?

Для валидации только что совершенной покупки.
Когда у многих был джейлбрейк это было актуально: существовали утилиты для подделки чека. Сейчас эта проблема перестала быть острой, потому что джейлбрейк стал редкостью.

При восстановлении покупок.
Если пользователь переустановил ваше приложение или запустил с другого устройства, вы должны предоставить ему доступ к функционалу, за который он уже заплатил. Расшифровав App Store чек, вы сможете выяснить, была ли приобретена встроенная покупка.

При покупке авто-возобновляемых подписок.
Для определения текущего статуса подписки и даты истечения.


Какие покупки можно восстановить при валидации?

Существует 4 вида встроенных покупок:


  • расходуемые (consumable purchases),
  • нерасходуемые (non-consumable purchases),
  • невозобновляемые подписки (non-renewing subscriptions),
  • авто-возобновляемые подписки (auto-renewable Subscriptions).
    Восстановить можно все, кроме расходуемых покупок. К ним можно отнести, например, монетки в вашем приложении – что-то, что можно купить сколько угодно раз. Вы должны сами сохранять текущее количество монеток у пользователя на своем сервере.

Какие способы валидации существуют?

Их три:


  • локальная валидация с использованием OpenSSL,
  • валидация по запросу в Apple прямо с iOS устройства,
  • валидация по запросу в Apple с использованием вашего сервера.

Какой способ валидации лучше?

Локальная валидация сложна и требует много времени и усилий на реализацию. А еще вы должны будете добавить OpenSSL библиотеку в ваш проект. В некоторых случаях придется обновлять чек.

Apple не рекомендует проверять чеки на самом iOS-устройстве. Это небезопасно: запрос можно перехватить с помощью man-in-the-middle атаки.

Лучше всего валидировать чеки на сервере. Тем более, что Apple время от времени добавляет туда новые поля, например, grace_period_expires_date и subscription_group_identifier. На своем сервере мы сможете быстро вносить изменения без обновления приложения. А еще предыдущие два метода валидации можно легко обмануть, просто поменяв системное время на iOS устройстве.


Для чего нужен Shared Secret?

Это специальная строка-ключ, которая необходима для расшифровки чеков с авто-возобновляемыми покупками. Apple использует Shared secret как параметр в HTTPS запросе к Apple.


Где взять Shared Secret?

Перейдите в App Store Connect, откройте приложение, перейдите во вкладку Функции, в разделе Встроенные покупки увидите кнопку Общий ключ для приложения. Сгенерируйте новый ключ, если его еще нет.


Пример кода для валидации чека

func validateReceipt(){

        #if DEBUG
                   let urlString = "https://sandbox.itunes.apple.com/verifyReceipt"
               #else 
                   let urlString = "https://buy.itunes.apple.com/verifyReceipt"
               #endif

        guard let receiptURL = Bundle.main.appStoreReceiptURL, let receiptString = try? Data(contentsOf: receiptURL).base64EncodedString() , let url = URL(string: urlString) else {
                return
        }

        let requestData : [String : Any] = ["receipt-data" : receiptString, 
                                            "password" : "YOUR_SHARED_SECRET", 
                                            "exclude-old-transactions" : false]
        let httpBody = try? JSONSerialization.data(withJSONObject: requestData, options: [])

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = httpBody
        URLSession.shared.dataTask(with: request)  { (data, response, error) in
            // convert data to Dictionary and view purchases
        }.resume()        
    }

Это пример валидации чека на iOS. Не забудьте заменить значение YOUR_SHARED_SECRET на ваш shared secret.

Получив data, сконвертируйте его в Dictionary:

DispatchQueue.main.async {
    if let data = data, let jsonData = try? JSONSerialization.jsonObject(with: data, options: .allowFragments){
    // your non-consumable and non-renewing subscription receipts are in `in_app` array
    // your auto-renewable subscription receipts are in `latest_receipt_info` array
  }                
}

Пример расшифрованного App Store чека

Здесь можно посмотреть пример чека с двумя транзакциями в приложении с авто-возобновляемой подпиской.


В чем отличия между in_app и latest_receipt_info?


  • latest_receipt_info содержит транзакции только авто-возобновляемых покупок.


  • in_app содержит транзакции нерасходуемых покупок и невозобновляемых подписок. Еще здесь дублируется первая транзакция вашей авто-возобновляемой подписки. Расходуемые покупки тоже появятся в массиве in_app, но потом исчезнут, когда разработчик завершит транзакцию.



Заключение

Мы в Apphud реализовали валидацию App Store чеков для приложений с авто-возобновляемыми подписками в удобном open-source SDK. А еще Apphud помогает отслеживать статус подписки, анализировать ключевые метрики, автоматически предлагать скидки отписавшимся пользователям и многое другое. Если при работе с подписками вы испытываете боль, попробуйте наше решение бесплатно.

Let's block ads! (Why?)

Комментариев нет:

Отправить комментарий