restoreCompletedTransactions returns varying amount transactions

I'm testing restoring purchases, and in paymentQueue(queue: updatedTransactions transactions:) I print the amount of transactions to process. This number is all over the place. I get anywhere from 5 to over 100. Why is this?

Replies

Is the number varying with the same testuser or with different testusers?

Seem to recall hearing this was a sandbox issue in the past - you might want to file bugs and see what comes back.

Only the same test user. I created a second test user to work around temporarily and it's not having issues so far.

So with one test user is it 'jumping around' or is it constant? Are you calling finishTransaction in between purchases? How many times are you calling finishTransactions? Could that explain the 'jumping around'?

OK. With one test user, it is jumping around.


func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {

        print ("Number of transactions to process   \(transactions.count)")
        
        var xyz = 0
        
        for transaction in transactions{
            
            xyz += 1
            
            switch transaction.transactionState{
                
            case .purchased:
                print ("purchased")  
                //call my payed function
                SKPaymentQueue.default().finishTransaction(transaction)
                SKPaymentQueue.default().remove(self)
                statusLabel.text = "Suscribed"
                statusLabel.textColor = .softGreen()
                break
            
            case .restored:
                print ("restored")
                SKPaymentQueue.default().finishTransaction(transaction)

               break
                
            case .failed:
                
                print ("failed")
                
                print ("transaction error   \(transaction.error!.localizedDescription)")                

                let failedAlert = UIAlertController(title: "Failed", message: "Your payment failed!", preferredStyle: .alert)
                let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
                failedAlert.addAction(okAction)
                self.present(failedAlert, animated: true, completion: nil)
                SKPaymentQueue.default().finishTransaction(transaction)
                SKPaymentQueue.default().remove(self)
                break
            
            case .deferred:
                print ("deferred")
                break
            default:
                print ("default")
                break
            }
            print (xyz)
        }
    }


As you can see at line 3, I am printing the number of transactions to process. That is the number that jumps around when I test payments using a specific test user.

It seems like you are referring to one user having many transactions for which the app has not yet called finishTransaction. I think it unwise to not call finishTransaction within the updatedTransaction method when the transaction is first reported or, at most, shortly after that first call - giving you just enough time to interact with your own server. You will find that a user cannot restore a transaction before the first transaction is finished and various other issues arise before that transaction is finished. Don't rely on the StoreKit queue to store transactions for you.

I'm not using my own server. I use the Apple servers to verify the receipt, and then the app handles what happens next. If I can't rely on the StoreKit to store transactions for me what else can I do?



P.S.

Sorry if I'm being troublesome. I'm only an amateur...for now...

You need to learn how to store information in the app. You can use NSUserDefaults or the file system. You need to store the information contained in the transaction and then call finishTransaction. Use your stored information to set teh state of the app.


For example, just store a receipt in NSUserDefaults. When you want to get information, send that stored receipt to the Apple servers. No need to rely on StoreKit's queue to redeliver that receipt.

The store receipt is stored in the StoreKit folder tho. Which is never deleted as far as I know. I simply use this on startup:


if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager.default.fileExists(atPath: appStoreReceiptURL.path){
            barTitle.text = "Checking your subscription"
            privateReceiptValidation(url: "https://buy.itunes.apple.com/verifyReceipt", VC: self)

        }


The privateReceiptValidation function sends the receipt to the Apple servers to verify it and decomplite it as a json file.


Unless you mean save the json file in the app so I don't have to connect to the servers each startup? And if the subscription ended download a new receipt, etc.


I know how to use UserDefaults, and my use of the apps documents folder is key to this app.


BTW I should have stated that this takes place on iOS.

First there is no need to check the recipt each time the user starts the app. Check it once, record the expiration date and then check it again only after that expiration date. Getting the receipt from bundle.main.appStoreReceiptURL will work fine. If there is no receipt at that location then show a button for the user to tap marked 'check my subscription status'. Then do a restoreCompletedTransactions. That will trigger a log in to the App Store and will send a transaction to updatedTransactions.


If you do that there is no reason not to call finishTransaction on all transactions at the time of purchase - the issue at question in this thread.

If I don't call finishTransaction on each transaction at the time of purchase, where else do I do it? Every example I've seen of how to setup IAP has a switch statement which finishes the transaction in each case of purchased, restored, and failed.

You may want to verify the receipt using your server. After your server verifies the receipt it sends a message back to your app saying 'ok'. Apple suggests ("Best practice") that you retain the transaction and finish it only after you have secured the subscription. That way, as you envision, the next time you open a transactionObserver the transaction will come back to the app and restart that process in the unlikely situation where your server or Apple's server have not responded in a timely fashion. Otherwise, in the unlikely situation where your server or the Apple server fails, if you have called finishTransaction you will forever lose the transaction.


Because of myriad problems with not finishing the transaction promptly, I think that 'Better practice" is to finishTransaction in updatedTransactions and take on the responsibility of following up in that unlikely situation by recovering the receipt and restarting the verification process.


Your original post suggested that you don't finishTransaction in updatedTransactions. It also suggested another way of doing that.

I do call finishTransaction in updatedTransactions. I posted the code in a previous post but I'll post it again. Please look at it and tell me if I'm simply misusing terms or what. What I mean when I say that each transaction is finished once the purchase is made is exactly what happens in my code below. After each case where the transaction state is purchase I call SKPaymentQueue.default().finishTransaction(transaction). I call that statement if the case is restored or failed as well. Is that wrong? If each transaction has been finished which would the number of transactions change?






func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {  

        print ("Number of transactions to process   \(transactions.count)")  

        var xyz = 0  

        for transaction in transactions{  

            xyz += 1  

            switch transaction.transactionState{  

            case .purchased:  
                print ("purchased")    
                //call my payed function  
                SKPaymentQueue.default().finishTransaction(transaction)  
                SKPaymentQueue.default().remove(self)  
                statusLabel.text = "Suscribed"  
                statusLabel.textColor = .softGreen()  
                break  

            case .restored:  
                print ("restored")  
                SKPaymentQueue.default().finishTransaction(transaction)  

               break  

            case .failed:  

                print ("failed")  

                print ("transaction error   \(transaction.error!.localizedDescription)")                  

                let failedAlert = UIAlertController(title: "Failed", message: "Your payment failed!", preferredStyle: .alert)  
                let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)  
                failedAlert.addAction(okAction)  
                self.present(failedAlert, animated: true, completion: nil)  
                SKPaymentQueue.default().finishTransaction(transaction)  
                SKPaymentQueue.default().remove(self)  
                break  

            case .deferred:  
                print ("deferred")  
                break  
            default:  
                print ("default")  
                break  
            }  
            print (xyz)  
        }  
    } 

You started out saying "I print the amount of transactions to process. This number is all over the place. I get anywhere from 5 to over 100."


If you are calling finishTransactions then this number should be 1. It should never get higher than 1 unless you are subscribed to an autorenewable subscription and fail to add a transaction observer for many subscription cycles. In the sandbox the maximum that would be is 5.

This is an autorenewable subscription. Maybe I didn't add an observer somewhere? I'll do more research and go through my code. I'll post what I find.