Недавно мы перевели платное приложение на модель «Freemium». Мы используем Bundle.main.appStoreReceiptURL для извлечения квитанции, а затем проверяем «original_application_version», чтобы узнать, загрузил ли пользователь более старую платную версию из App Store или загрузил ли он более новую бесплатную версию с нерасходуемым продуктом при покупке приложения. для перехода на полную версию.
Это отлично работает при тестировании в песочнице, но в продакшене более старые версии приложения не проверяют должным образом, что они были загружены до версии freemium.
Следующий код вызывается с productionStoreURL и квитанцией, полученной из Bundle.main.appStoreReceiptURL:
private let productionStoreURL = URL(string: "https://buy.itunes.apple.com/verifyReceipt")
private let sandboxStoreURL = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")
private func verifyIfPurchasedBeforeFreemium(_ storeURL: URL, _ receipt: Data) {
do {
let requestContents:Dictionary = ["receipt-data": receipt.base64EncodedString()]
let requestData = try JSONSerialization.data(withJSONObject: requestContents, options: [])
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
URLSession.shared.dataTask(with: storeRequest) { (data, response, error) in
DispatchQueue.main.async {
if data != nil {
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any?]
if let statusCode = jsonResponse["status"] as? Int {
if statusCode == 21007 {
print("Switching to test against sandbox")
self.verifyIfPurchasedBeforeFreemium(self.sandboxStoreURL!, receipt)
}
}
if let receiptResponse = jsonResponse["receipt"] as? [String: Any?], let originalVersion = receiptResponse["original_application_version"] as? String {
if self.isPaidVersionNumber(originalVersion) {
// Update to full paid version of app
UserDefaults.standard.set(true, forKey: upgradeKeys.isUpgraded)
NotificationCenter.default.post(name: .UpgradedVersionNotification, object: nil)
}
}
} catch {
print("Error: " + error.localizedDescription)
}
}
}
}.resume()
} catch {
print("Error: " + error.localizedDescription)
}
}
private func isPaidVersionNumber(_ originalVersion: String) -> Bool {
let pattern:String = "^\\d+\\.\\d+"
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
let results = regex.matches(in: originalVersion, options: [], range: NSMakeRange(0, originalVersion.count))
let original = results.map {
Double(originalVersion[Range($0.range, in: originalVersion)!])
}
if original.count > 0, original[0]! < firstFreemiumVersion {
print("App purchased prior to Freemium model")
return true
}
} catch {
print("Paid Version RegEx Error.")
}
return false
}
Первая бесплатная версия — 3.2, это наша текущая сборка. Все предыдущие сборки были 3.1.6 или ранее.
Производственный URL-адрес не должен быть проблемой, иначе он не вернет код состояния 21007, чтобы запустить проверку песочницы для нас. Однако устранение неполадок особенно сложно, поскольку мы не можем протестировать сам производственный URL-адрес Apple.
Кто-нибудь знает, почему это будет работать в песочнице, но не в производстве?
UserDefaults
, чтобы отслеживать, должны ли быть доступны платные функции. Существуют инструменты, упрощающие изменение пользовательских значений по умолчанию даже на устройствах без взлома. В этой статье подробно рассказывается, как можно использовать пользовательские настройки по умолчанию. - person Jamie Edge   schedule 31.12.2018CoreData
те же риски, что иUserDefaults
? Кажется чрезмерным проверять квитанцию каждый раз, когда пользователь открывает. - person Brandogs   schedule 01.01.2019UserDefaults
использует plist в песочнице). Связку для ключей сложнее изменить на устройстве без взлома - значения могут быть извлечены из резервной копии, однако я не знаю какого-либо простого способа изменить значения (хотя это вполне возможно). - person Jamie Edge   schedule 01.01.2019