【问题标题】:How to check if user has paid for an Auto Renewal Subscription In App Purchase in Swift如何检查用户是否已在 Swift 中为 App 购买中的自动续订订阅付费
【发布时间】:2019-10-12 15:19:32
【问题描述】:

我目前在iTunesConnect 中有一个Non-Consumable 和一个Auto Renewal Subscription 应用内购买设置。我的问题是我不确定如何检查是否可以为Auto Renewal Subscription 的用户解锁内容。我对Non-Consumables 没有问题在应用购买中,我通过检查UserDefaults 中是否存在产品 ID 来验证它们,如果存在,我解锁内容,否则我通知用户,但此方法不适用于 @ 987654327@ 应用内购买。当我测试它时,我可以通过 App Store 进行购买交易,但是当我尝试查看 UserDefaults 中是否存在产品 ID 时,它返回 false。事实上,我手动检查了密钥是否存在,它只显示Non-Consumable 购买的密钥。

这是我正在使用的代码。

这是我多年来在应用购买中验证Non-Consumable 的工作代码。

这是我正在使用的 In App Manager 类。

import UIKit
import StoreKit

protocol IAPManagerDelegate {
    func managerDidRestorePurchases()
}

class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver, SKRequestDelegate {
    static let sharedInstance = IAPManager()
    var request:SKProductsRequest!
    var products:NSArray!
    var delegate:IAPManagerDelegate?

    func setupInAppPurchases(){
        self.validateProductIdentifiers(self.getProductIdentifiersFromMainBundle())

        SKPaymentQueue.default().add(self)
    }

    func getProductIdentifiersFromMainBundle() -> NSArray {
        var identifiers = NSArray()
        if let url = Bundle.main.url(forResource: "iap_product_ids", withExtension: "plist"){
            identifiers = NSArray(contentsOf: url)!
        }
        return identifiers
    }

    func validateProductIdentifiers(_ identifiers:NSArray) {
        let productIdentifiers = NSSet(array: identifiers as [AnyObject])
        let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
        self.request = productRequest
        productRequest.delegate = self
        productRequest.start()
    }

    func createPaymentRequestForProduct(_ product:SKProduct){
        let payment = SKMutablePayment(product: product)
        payment.quantity = 1
        SKPaymentQueue.default().add(payment)
    }

    func verifyReceipt(_ transaction:SKPaymentTransaction?){
        let receiptURL = Bundle.main.appStoreReceiptURL!
        if let receipt = try? Data(contentsOf: receiptURL){

            let requestContents = ["receipt-data" : receipt.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))]

            do {
                let requestData = try JSONSerialization.data(withJSONObject: requestContents, options: JSONSerialization.WritingOptions(rawValue: 0))
                //  PRODUCTION URL
                // let storeURL = URL(string: "https://buy.itunes.apple.com/verifyReceipt")
                //  TESTING URL: Uncomment for testing InAppPurchases
                let storeURL = URL(string: "https:/sandbox.itunes.apple.com/verifyReceipt")

                var request = URLRequest(url: storeURL!)
                request.httpMethod = "Post"
                request.httpBody = requestData

                let session = URLSession.shared
                let task = session.dataTask(with: request,
                                            completionHandler: { (responseData, response, error) -> Void in
                    do {
                        let json = try JSONSerialization.jsonObject(with: responseData!, options: .mutableLeaves) as! NSDictionary
                        if (json.object(forKey: "status") as! NSNumber) == 0 {
                            if let latest_receipt = json["latest_receipt_info"]{
                                self.validatePurchaseArray(latest_receipt as! NSArray)
                            } else {
                                let receipt_dict = json["receipt"] as! NSDictionary
                                if let purchases = receipt_dict["in_app"] as? NSArray{
                                    self.validatePurchaseArray(purchases)
                                }
                            }
                            if transaction != nil {
                                SKPaymentQueue.default().finishTransaction(transaction!)
                            }
                            DispatchQueue.main.sync(execute: { () -> Void in
                                self.delegate?.managerDidRestorePurchases()
                            })
                        } else {
                            print(json.object(forKey: "status") as! NSNumber)
                        }
                    } catch {
                        print(error)
                    }
                })
                task.resume()
            } catch {
                print(error)
            }
        } else {
            print("No Receipt")
        }
    }

    func validatePurchaseArray(_ purchases:NSArray){
        for purchase in purchases as! [NSDictionary]{
            self.unlockPurchasedFunctionalityForProductIdentifier(purchase["product_id"] as! String)
        }
    }

    func unlockPurchasedFunctionalityForProductIdentifier(_ productIdentifier:String){
        UserDefaults.standard.set(true, forKey: productIdentifier)
        UserDefaults.standard.synchronize()
        DispatchQueue.main.async {
            UIApplication.shared.isNetworkActivityIndicatorVisible = false
        }
    }

    func lockPurchasedFunctionalityForProductIdentifier(_ productIdentifier:String){
        UserDefaults.standard.set(false, forKey: productIdentifier)
        UserDefaults.standard.synchronize()
        UIApplication.shared.isNetworkActivityIndicatorVisible = false
    }

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        let inAppPurchases = response.products
        // Sort the items
        self.products = inAppPurchases.reversed() as NSArray
    }

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions as [SKPaymentTransaction]{
            switch transaction.transactionState{
            case .purchasing:
                print("Purchasing")
                UIApplication.shared.isNetworkActivityIndicatorVisible = true
            case .deferred:
                print("Deferrred")
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
            case .failed:
                print("Failed")
                //print(transaction.error?.localizedDescription)
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
                SKPaymentQueue.default().finishTransaction(transaction)
            case.purchased:
                print("Purchased")
                self.verifyReceipt(transaction)
            case .restored:
                print("Restored")

            }
        }
    }

    func restorePurchases(){
        let request = SKReceiptRefreshRequest()
        request.delegate = self
        request.start()
    }

    func requestDidFinish(_ request: SKRequest) {
        self.verifyReceipt(nil)
    }
}

这是我在UITableView 中展示In App Purchases 的方式。

class StoreTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, IAPManagerDelegate {
    @IBOutlet weak var premiumFeaturesTable: UITableView!
    @IBOutlet weak var buttonClose: UIButton!
    @IBOutlet weak var buttonRestore: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        IAPManager.sharedInstance.delegate = self
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return IAPManager.sharedInstance.products.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell =  tableView.dequeueReusableCell(withIdentifier: "cellInAppPurchase")as! CustomCellForInAppPurchasesTableViewCell
        let product = IAPManager.sharedInstance.products.object(at: indexPath.row) as! SKProduct

         cell.labelIAppItem.text = product.localizedTitle
        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        IAPManager.sharedInstance.createPaymentRequestForProduct(IAPManager.sharedInstance.products.object(at: indexPath.row) as! SKProduct)
    }

    @IBAction func closeViewController(_ sender: AnyObject) {
        self.presentingViewController!.dismiss(animated: true, completion: nil)
    }

    @IBAction func restorePurchases(_ sender: AnyObject) {
        IAPManager.sharedInstance.restorePurchases()
    }
}

这是我解锁内容的方式

if NSUserDefaults.standardUserDefaults().boolForKey("com.theAppID.app"){
    // Unlock content.
}else{
    // Notify user.
}

同样,Non-Consumables 一切正常,但对于 Auto Renewal Subscriptions,我不确定如何在用户购买后解锁内容。

我错过了什么,根据上面的代码检查用户是否支付了Auto Renewal Subscription 的正确方法是什么?

编辑:最简单的答案是……为您的基于订阅的应用程序使用 RevenueCat,它让您的生活更轻松。

【问题讨论】:

    标签: ios swift in-app-purchase


    【解决方案1】:

    请查看此link 以获取自动续订订阅。

    您可以在应用内管理器类中使用以下功能。

    func verifyReceipt(_ transaction:SKPaymentTransaction?) 
    

    验证后,您将获得以下响应代码和您最后一次订阅日期的详细信息。请检查此link

    注意:不要忘记在自动续订的收据验证中传递“密码”字段。

    【讨论】:

    • 非常感谢您的帮助。我会试一试,看看我能不能解决这个问题,因为它似乎比我想象的要复杂一些。
    猜你喜欢
    • 2011-04-17
    • 2011-01-05
    • 2011-07-04
    • 1970-01-01
    • 2019-02-27
    • 1970-01-01
    • 2020-04-06
    • 2017-10-29
    • 1970-01-01
    相关资源
    最近更新 更多