如何在Swift中的App Purchase中检查用户是否已支付自动续订订阅

1aaf6o9v  于 2023-03-28  发布在  Swift
关注(0)|答案(2)|浏览(183)

我目前在iTunesConnect中设置了Non-ConsumableAuto Renewal Subscription In App Purchase。我的问题是,我不确定如何检查Auto Renewal Subscription是否可以为用户解锁内容。我在Non-Consumables In App Purchases中没有问题,我通过检查UserDefaults中是否存在产品ID来验证它们,如果存在,我解锁内容,否则我通知用户,但这种方法不适用于应用程序购买中的Auto Renewal Subscription。当我测试它时,我可以通过应用程序商店进行购买交易,但当我尝试查看产品ID是否存在于UserDefaults中时,它返回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为您的订阅式应用程序,它使您的生活更轻松。

6rqinv9w

6rqinv9w1#

请检查此link以获取自动续订订阅。
您可以从您的In-App管理器类中使用以下函数。

func verifyReceipt(_ transaction:SKPaymentTransaction?)

验证后,您将获得下面的响应代码和详细信息,为您的最后日期的订阅。请检查此链接。
注意:不要忘记在自动续订订阅的收据验证中传递“密码”字段。

67up9zun

67up9zun2#

以下是如何检查用户是否将使用StoreKit 2续订自动续订订阅:

try await subscribedProduct.subscription?.status.first?.renewalInfo.payloadValue.willAutoRenew

如果您需要完整的playground代码以获得更多上下文:

import Foundation
import StoreKit

public enum StoreError: Error {
    case failedVerification
}

@MainActor
func willRenewSubscription(from subscriptions: [Product]) async -> Bool {
    for await result in Transaction.currentEntitlements {
        do {
            let transaction = try checkVerified(result)
            switch transaction.productType {
            case .autoRenewable:
                if let subscription = subscriptions.first(where: { $0.id == transaction.productID }) {
                    Task {
                        let status = try await subscription.subscription?.status.first?.renewalInfo.payloadValue.willAutoRenew
                        return status
                    }
                } else {
                    return false
                }
            default:
                return false
            }
        } catch {
            return false
        }
    }
    return false
}

func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
    switch result {
    case .unverified:
        throw StoreError.failedVerification
    case .verified(let safe):
        return safe
    }
}

使用方法:

Task {
    let willRenew = await willRenewSubscription(from: [])
    print(willRenew)
}

相关问题