paymentQueue FinishTransaction never gets called after in app purchase

I have implemented this StoreObserver and I have registered it in AppDelegate but my paymentQueue func never gets called whenever there is a successful purchase. The reason I need it to be called is firstly to finish the transaction but to also send the device receipt to the server. Currently, my receipt is always null.

The purchases themselves work OK, both in XCode and in Sandbox.

I can't figure out what I'm doing wrong in terms of the setup of the storeobserver. Any help will be greatly appreciated.

AppDelegate.swift


let iapObserver = StoreObserver.shared

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
    
    
    UIApplication.shared.registerForRemoteNotifications()
    UNUserNotificationCenter.current().delegate = self
    SKPaymentQueue.default().add(iapObserver)
    return true
}

StoreObserver.swift


  class StoreObserver: NSObject, SKPaymentTransactionObserver {
       static var shared = StoreObserver()
    
        func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
            
            for transaction in transactions {
                switch (transaction.transactionState) {
                case .purchased:
                    SKPaymentQueue.default().finishTransaction(transaction)
                    
//print the receipt but it is always nil. (I will be saving this on the backend for validation)
                    if let url = Bundle.main.appStoreReceiptURL,
                       let data = try? Data(contentsOf: url) {
                        var receiptDataString = data.base64EncodedString()
                        print(receiptDataString)
                    }
                    
                    break
                case .failed:
                    SKPaymentQueue.default().finishTransaction(transaction)
                    break
                case .restored:
                    SKPaymentQueue.default().finishTransaction(transaction)
                    break
                case .deferred, .purchasing:
                    break
                default:
                    break
                }
            }
        }
            
            func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
               //
            }
            
        func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
           //
        }
            
        func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
           //
        }
            
            func paymentQueue(_ queue: SKPaymentQueue, updatedDownloads downloads: [SKDownload]) { 
               //
            }
         
        
    }

Replies

Few notes:

  1. Consider updating to StoreKit2, especially if your app minimum OS version is iOS 15 or greater.

  2. FinishTransaction is only to be called AFTER you have verified the purchase and delivered it the customer. That way if there is an issue you don't lose track of delivering the purchase. These unfinished transactions remain the customers queue until you finish them. As you can imagine, apps can get interrupted for any reason, albeit rare: bad connection, backend outages, phone call, app crash, etc.

  3. for code review you can leverage help from Developer Technical Support: https://developer.apple.com/support/