【问题标题】:How can you get Original Application Version in Production?如何在生产中获得原始应用程序版本?
【发布时间】:2019-05-28 04:07:07
【问题描述】:

我们最近将一款可购买的应用转变为“免费增值”模式。我们正在使用 Bundle.main.appStoreReceiptURL 来提取收据,然后检查“original_application_version”以查看用户是否从 App Store 下载了较旧的付费版本,或者他们是否下载了具有非消耗性应用内购买的较新的免费版本升级到完整版。

这在测试 Sandbox 时非常有效,但在生产环境中,旧版本的应用无法正确验证它们是在免费增值版本之前下载的。

使用 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 状态代码来为我们触发沙盒验证。但是,解决此问题特别棘手,因为我们无法针对 Apple 的生产 URL 本身进行测试。

有没有人知道为什么这会在 Sandbox 中起作用,但在生产中不起作用?

【问题讨论】:

  • 不是您所询问的具体问题的答案,但我强烈建议您重新考虑使用 UserDefaults 来跟踪是否应该提供付费功能。存在可以轻松修改用户默认值的工具,即使在非越狱设备上也是如此。 This article 详细说明了如何利用用户默认值。
  • @JamieEdge 感谢您的提示!我一定会改变这一点。 CoreData 是否与UserDefaults 具有相同的风险?每次用户打开时都验证收据似乎有点过分。
  • 是的,在文件系统上存储数据(例如与 Core Data 一起使用的底层 SQLite 数据库)几乎总是会带来这种风险,因为值存储在应用程序的沙箱目录中,可以被查看和修改(UserDefaults 在沙箱中使用 plist 文件)。钥匙串在非越狱设备上更难修改 - 值可能可以从备份中提取,但我不知道有任何简单的方法来修改值(但它很有可能)。

标签: ios swift receipt-validation


【解决方案1】:

看起来获取收据根本没有问题。

original_application_version 的一些旧值格式不正确,导致我们无法获取应用版本进行比较。

【讨论】:

  • 你能说说它们是如何格式化的吗?我现在通过检测字符串是否包含点(句号)但没有一个用户得到正确的结果来检查生产应用程序中的 original_application_version。
  • 由于 original_application_version 使用 Build 字段,开发人员可以根据需要进行设置,因此无法保证其格式。但是,您可以通过登录 App Store Connect,打开您的应用程序,然后打开“活动”选项卡来查看这些值过去的值。展开每个版本并查看 Build 列中列出的值。这些是为给定构建的 original_application_version 返回的值。
  • 谢谢,很遗憾,我的应用程序已有 10 年历史,我无法在 AppStoreConnect 中查看所有旧版本。所以我想我应该在记录我的内部版本号方面做得更好:)
  • @Mr.Zystem 如果您使用 Git 或其他版本控制系统,您可以查看 plist.info 文件的历史记录。 CFBundleVersion 值是 XCode 中的内部版本号,是作为 original_application_version 返回的值。
  • @Makalele 问题最终是我们的团队如何对先前发布的版本进行版本化,以及我们对original_application_version 返回的内容的错误假设。解决问题的更改是通过更改正则表达式来解决的,但这将特定于每个单独的项目,具体取决于他们在旧版本时使用的版本格式。结果,真的没有什么可分享的。假设以前的版本控制符合正则表达式,上面的代码在技术上应该可以工作。
猜你喜欢
  • 1970-01-01
  • 2021-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-14
  • 2015-12-14
相关资源
最近更新 更多