【问题标题】:Check if an Auto-Renewable Subscription is still valid检查自动续订订阅是否仍然有效
【发布时间】:2017-09-10 13:08:46
【问题描述】:

我想在打开应用程序时检查自动续订订阅状态。

这是为了确保用户仍然订阅该服务。我如何做到这一点?

有什么想法吗?谢谢

P.S.:我使用的是SwiftyStoreKit

【问题讨论】:

  • 您需要验证收据的应用内部分。请参阅 Apple 提供的应用内购买编程指南

标签: ios swift storekit swiftystorekit


【解决方案1】:

这里有几种方法可以进行收据验证以检查用户是否授予订阅。这里有两种正确的做法:

  1. 按照here 的格式在本地进行收据验证。
  2. 按照here 的规定远程执行收据验证。提到不应在应用程序内将收据发送到应用程序商店。简短总结:

    • 您的应用将收据发送到您的后端。
    • 您的后端将收据发送到 Apple 后端进行验证。
    • 您的后端收到来自苹果的响应。
    • 您的后端将结果发送回您的应用是收据有效还是无效。

在这两种方式中,您都会获得应用内购买的列表。它还将包含过期的订阅。您需要浏览所有订阅并检查到期日期。如果它仍然有效,您必须授予用户订阅。

据我了解,您正在使用 SwiftyStoreKit,这是local receipt validation 的开放任务。

【讨论】:

  • 是的,我正在使用 SwiftyStoreKit,但是SwiftyStoreKit.restorePurchases 不允许我检查购买日期,这意味着我无法检查恢复购买的有效性
  • 是的。你是对的。每次执行 restorePurchases 身份验证(用户名和密码)时,都会向用户显示对话框。
  • Ramis,所以,根本不可能获得这些 restorePurchases 的日期/有效期?
  • 以我的经验这是不可能的。
【解决方案2】:

您可以使用此功能进行检查。它适用于 swift4

func receiptValidation() {
let SUBSCRIPTION_SECRET = "yourpasswordift"
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
    var receiptData:NSData?
    do{
        receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
    }
    catch{
        print("ERROR: " + error.localizedDescription)
    }
    //let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
    let base64encodedReceipt = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithCarriageReturn)

    print(base64encodedReceipt!)


    let requestDictionary = ["receipt-data":base64encodedReceipt!,"password":SUBSCRIPTION_SECRET]

    guard JSONSerialization.isValidJSONObject(requestDictionary) else {  print("requestDictionary is not valid JSON");  return }
    do {
        let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
        let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt"  // this works but as noted above it's best to use your own trusted server
        guard let validationURL = URL(string: validationURLString) else { print("the validation url could not be created, unlikely error"); return }
        let session = URLSession(configuration: URLSessionConfiguration.default)
        var request = URLRequest(url: validationURL)
        request.httpMethod = "POST"
        request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
        let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
            if let data = data , error == nil {
                do {
                    let appReceiptJSON = try JSONSerialization.jsonObject(with: data)
                    print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
                    // if you are using your server this will be a json representation of whatever your server provided
                } catch let error as NSError {
                    print("json serialization failed with error: \(error)")
                }
            } else {
                print("the upload task returned an error: \(error)")
            }
        }
        task.resume()
    } catch let error as NSError {
        print("json serialization failed with error: \(error)")
    }



}
}

【讨论】:

  • 我如何获得 SUBSCRIPTION_SECRET?
  • 是的,没有关于实际收据到期的信息。您既没有授予权限,也没有限制这段代码的访问权限
  • 不建议直接从 App 向 AppStore 发送请求。从应用程序端签名验证就足够了。 “警告不要从您的应用程序调用 App Store 服务器 verifyReceipt 端点。您无法直接在用户设备和 App Store 之间建立受信任的连接,因为您无法控制该连接的任何一端,这使得它容易受到中间人攻击。” developer.apple.com/documentation/storekit/in-app_purchase/…
【解决方案3】:

我想为那些仍然偶然发现这个问题的人提供一个使用RevenueCat SDK 的替代解决方案。

AppDelegate.swift

使用您的 api 密钥和可选的用户标识符配置 RevenueCat Purchases SDK

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    Purchases.configure(withAPIKey: "<...>", appUserID: "<...>")

    ...

    return true
}

订阅状态功能

下面的函数检查PurchaserInfo 以查看用户是否仍有有效的“权利”(或者您可以直接检查有效的产品ID)。

func subscriptionStatus(completion: @escaping (Bool)-> Void) {
    Purchases.shared.purchaserInfo { (info, error) in

        // Check if the purchaserInfo contains the pro feature ID you configured
        completion(info?.activeEntitlements.contains("pro_feature_ID") ?? false)

        // Alternatively, you can directly check if there is a specific product ID
        // that is active.
        // completion(info?.activeSubscriptions.contains("product_ID") ?? false)
    }
}

获取订阅状态

您可以根据需要随时调用上述函数,因为结果被 Purchases SDK 缓存它在大多数情况下会同步返回,不需要网络请求。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    subscriptionStatus { (subscribed) in
        if subscribed {
            // Show that great pro content
        }
    }
}

如果您使用的是 SwiftyStoreKit,RevenueCat 语法非常相似,并且有一个 migration guide 可用于帮助切换。

【讨论】:

  • RevenueCat 在开发基于订阅的应用程序时为我节省了大量时间。强烈推荐!
【解决方案4】:

另一个使用 Qonversion SDK 处理自动更新 iOS 订阅的解决方案。

AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Qonversion.launch(withKey: "yourProjectKey")
    
    
    return true
  }

获取订阅状态

将 App Store 订阅链接到 Qonversion 产品,并将产品链接到权限。 然后你只需要在你的应用程序开始时触发checkPermissions 方法来检查用户的订阅是否仍然有效。此方法将检查用户收据并返回当前权限。然后对于仍然有效的订阅,您可以获取订阅者是否关闭自动续订、是否处于宽限期(计费重试状态)等详细信息。

Qonversion.checkPermissions { (permissions, error) in
  if let error = error {
    // handle error
    return
  }
  
  if let premium = permissions["premium"], premium.isActive {
    switch premium.renewState {
       case .willRenew, .nonRenewable:
         // .willRenew is the state of an auto-renewable subscription 
         // .nonRenewable is the state of consumable/non-consumable IAPs that could unlock lifetime access
         break
       case .billingIssue:
         // Grace period: permission is active, but there was some billing issue.
         // Prompt the user to update the payment method.
         break
       case .cancelled:
         // The user has turned off auto-renewal for the subscription, but the subscription has not expired yet.
         // Prompt the user to resubscribe with a special offer.
         break
       default: break
    }
  }
}

您可以查看我们的示例应用程序,该应用程序演示了自动续订订阅实现here

【讨论】:

    猜你喜欢
    • 2019-07-21
    • 1970-01-01
    • 2011-07-04
    • 2020-02-23
    • 2021-12-12
    • 1970-01-01
    • 2016-08-04
    • 2012-08-26
    • 1970-01-01
    相关资源
    最近更新 更多