Home > front end >  Swift IAP subscription processing all transactions
Swift IAP subscription processing all transactions

Time:02-22

I have a question in IAP subscription processing in swift. I am using IAP subscription in my app, the logic works perfectly and i have no issues. I would like to check on processing the transactions from paymentQueue. I notice that it is looping all the transactions from paymentQueue and i have to verify the receipt for all transactions . Is this necessary to loop and check all the transactions, or should i just focus on the last transaction. Will the last transaction be the most recent one. Below is code snippet for the portion of code.

In the output console, I get the following Lines (Last three lines are looping 140 times): Total Transaction Count - 140

Start refreshing reciept...

Inside Parsing Reciept....

Transaction Purchased

Question: Is it necessary to process all the transactions (140 in this example) This 140 is for the same product. Any way i can avoid this looping, so that it improves processing.

Thanks in advance.

extension IAPManager: SKPaymentTransactionObserver {
    
    public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        
        print(" Total Transaction Count -  \(queue.transactions.count)")
        for transaction in transactions {
            switch (transaction.transactionState) {
            case .purchased:
                notifyIsPurchased(transaction: transaction)
                SKPaymentQueue.default().finishTransaction(transaction)
                print ("Transaction Purchased")
                break
            case .failed:
                SKPaymentQueue.default().finishTransaction(transaction)
                self.failureBlock?(transaction.error)
                cleanUp()
                break
            case .restored:
                notifyIsPurchased(transaction: transaction)
                SKPaymentQueue.default().finishTransaction(transaction)
                print("restored")
                break
            case .deferred:
                print("defered")
                break
            case .purchasing:
                print("purchasing")
                break
            default:
                break
            }
        }
    }

    private func notifyIsPurchased(transaction: SKPaymentTransaction) {
        refreshSubscriptionsStatus(callback: {
            self.successBlock?()
            self.cleanUp()
        }) { (error) in
            // couldn't verify receipt
            self.failureBlock?(error)
            self.cleanUp()
        }
    }

    func refreshSubscriptionsStatus(callback : @escaping SuccessBlock, failure : @escaping FailureBlock) {
        
        print("Start refreshing reciept...")
        self.refreshSubscriptionSuccessBlock = callback
        self.refreshSubscriptionFailureBlock = failure
        
        guard let receiptUrl = Bundle.main.appStoreReceiptURL else {
            print("No reciept")
            refreshReceipt()
            return
        }
        
        let urlString = "https://sandbox.itunes.apple.com/verifyReceipt"
        //let urlString = "https://buy.itunes.apple.com/verifyReceipt"
        let receiptData = try? Data(contentsOf: receiptUrl).base64EncodedString()
        let requestData = ["receipt-data" : receiptData ?? "", "password" : self.sharedSecret, "exclude-old-transactions" : true] as [String : Any]
        var request = URLRequest(url: URL(string: urlString)!)
        request.httpMethod = "POST"
        request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
        let httpBody = try? JSONSerialization.data(withJSONObject: requestData, options: [])
        request.httpBody = httpBody
        
        URLSession.shared.dataTask(with: request)  { (data, response, error) in
        DispatchQueue.main.async {
            if data != nil {
                if let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments){
                    //print (json)
                    self.parseReceipt(json as! Dictionary<String, Any>)
                    return
                }
            } else {
                print("error validating receipt: \(error?.localizedDescription ?? "")")
            }
            self.refreshSubscriptionFailureBlock?(error)
            self.cleanUpRefeshReceiptBlocks()
        }
        }.resume()
    }

    private func parseReceipt(_ json : Dictionary<String, Any>) {
        
        print("Inside Parsing Reciept....")
        
        
        guard let receipts_array = json["latest_receipt_info"] as? [Dictionary<String, Any>] else {
            self.refreshSubscriptionFailureBlock?(nil)
            self.cleanUpRefeshReceiptBlocks()
            return
        }

        process the receipt array and check if it is expired or valid...

    }

CodePudding user response:

There is no 100% guarantee that last transaction in your [SKPaymentTransaction] is your most recent transaction according to Apple documentation where is written that transactions property is:

An array of the transactions that were updated.

It is not clearly stated there that transactions are properly sorted, although arrays are usually used for elements where order matters.. However you can easily sort your transactions yourself by accessing transactionDate property. Here is an example:

func paymentQueue(_ queue: SKPaymentQueue,
                      updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {
        let ascendingTransactions = transactions.filter({$0.transactionDate != nil})
                                                .sorted(by: {$1.transactionDate! > $0.transactionDate!})
        let latestTransaction = ascendingTransactions.last
        
    }
}
  • Related