0

I am adding the observer in the didFinishLaunchingWithOptions like this and then call receipt validation:

IAPManager.shared.startObserving()
IAPManager.shared.IAPResponseCheck(iapReceiptValidationFrom: .didFinishLaunchingWithOptions)

And removing it in the applicationWillTerminate like this:

IAPManager.shared.stopObserving()

I am also checking the state of the purchase at applicationWillEnterForeground by calling the receipt validation:

IAPManager.shared.IAPResponseCheck(iapReceiptValidationFrom: .applicationWillEnterForeground)

IAP Manager class in short:

class IAPManager: NSObject {
    static let shared = IAPManager()     
    private override init() { super.init() }

    func startObserving() {
        SKPaymentQueue.default().add(self)
    }

    func stopObserving() {
        SKPaymentQueue.default().remove(self)
    }
}

In the IAP manager class inside of updatedTransactions, I am verifying the receipt and then finishing the transaction after each purchase & restore like this:

case .purchased:
    self.IAPResponseCheck(iapReceiptValidationFrom: .purchaseButton)
    SKPaymentQueue.default().finishTransaction(transaction)
    
case .restored:
    totalRestoredPurchases += 1
    SKPaymentQueue.default().finishTransaction(transaction)

And lastly call the receipt validation inside of paymentQueueRestoreCompletedTransactionsFinished:

func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
    if totalRestoredPurchases != 0 {
        self.IAPResponseCheck(iapReceiptValidationFrom: .restoreButton)
    } else {
        print("IAP: No purchases to restore!")
        onBuyProductHandler?(.success(false))
    }
}

But my receipt validation Func gets called multiple times randomly when I came back from background to foreground, or restart the app.

func IAPResponseCheck(iapReceiptValidationFrom: IAPReceiptValidationFrom) {
        print("iapReceiptValidationFrom \(iapReceiptValidationFrom)")

}

I search on the internet I found that it is happening because somehow there are multiple observers are added. But I add it according to apple's guidelines. So according to my implementation what am I missing here?

I want to call my receipt validation func just one time

Tulon
  • 4,011
  • 6
  • 36
  • 56
  • Add it in `didFinishLaunching` and never remove it. Process transactions as they arrive. Are you running in the sandbox? Are you purchasing auto-renewing subscriptions? If so you would expect renewal transactions every couple of minutes since IAP subscriptions run at an accelerated pace in the sandbox – Paulw11 Feb 28 '22 at 06:07
  • Thanks, @Paulw11. Yes, it is in the sandbox and yes it is auto-renewable. But If I don't remove it in `applicationWillTerminate`, then will it not be added a second time when the app is launched? – Tulon Feb 28 '22 at 06:19
  • And how can I stop these auto multiple calls when the app is launched and come from background to foreground? It calls fine for the first time, but if I restore the purchase then relaunch the app or come from background to foreground, these are kept calling multiple times `paymentQueueRestoreCompletedTransactionsFinished ` & `updatedTransactions ` – Tulon Feb 28 '22 at 06:24
  • You shouldn't do anything in `paymentQueueRestoreCompletedTransactionsFinished` except update your UI - Re-enabling a "Restore purchases" button, for example. In general it shouldn't matter how many times you receive transactions; just process them. What does `IAPResponseCheck` do? Apple advises against refreshing the receipt on every launch as it can lead to unexpected prompts for the user's store password – Paulw11 Feb 28 '22 at 07:16
  • In that case, where should I verify my receipt validation for the restore purchase? If I put it inside of `case .restored:` It even calls more! In `IAPResponseCheck` I am checking the expiration date from all four cases, which you said is against Apple's recommendation. But without implementing the server notification, do you think is there any alternative exists except call `IAPResponseCheck` and get the update constantly? :( – Tulon Feb 28 '22 at 08:06
  • 1
    There are only three cases you should need to validate the receipt: 1) When the user makes a purchase. 2) When the subscription renews - This is the same as case 1 really since a new transaction is just delivered to your transaction observer when your app opens and 3) after the user restores purchase and you get a .restored - You handle this the same as a new transaction. Depending on which version of ios you are targeting, look at Storekit2. It is much simpler API and you don't need to worry about receipt validation – Paulw11 Feb 28 '22 at 09:10
  • @Paulw11 Thanks I got it now. In my application, IAP works like the main authentication. It gives permission to the user to access inside of my application depending on the purchase status, which I am getting from the receipt validation. So I am calling it from `didFinishLaunchingWithOptions` & `applicationWillEnterForeground`. Do you think I need to find some alternative to this? I can't implement Storekit 2.0 or don't have server right now. – Tulon Mar 01 '22 at 04:00
  • 1
    Generally you would store the subscription status in the keychain. Only if you detect an inactive or expired subscription would you refresh the receipt to check if there is a renewal. – Paulw11 Mar 01 '22 at 06:09
  • Do you have any comments on this? https://stackoverflow.com/questions/71458458/properly-call-receipt-validation-after-finishing-all-pending-transaction-iap-swi – Tulon Mar 13 '22 at 17:06
  • Please add a comment at least and close the question. I need to know the answer :( – Tulon Mar 14 '22 at 02:54

0 Answers0