bizz84 / SwiftyStoreKit

Lightweight In App Purchases Swift framework for iOS 8.0+, tvOS 9.0+ and macOS 10.10+ ⛺
MIT License
6.5k stars 792 forks source link

Purchasing triggers callback in completeTransactions() instead of in purchaseProduct() #593

Open GraemeHarrison opened 3 years ago

GraemeHarrison commented 3 years ago

What I'm trying to accomplish is:

  1. User taps to buy IAP product
  2. If success, receipt is fetched and sent to my server for validation
  3. If success, IAP product transaction is set to finish
  4. Update UI.

After a user taps purchase, a function similar to this runs:

func purchase(productId: String) {

    SwiftyStoreKit.purchaseProduct(productId, quantity: 1, atomically: false) { result in

        switch result {

        case .success(let product):

            self.validateReceiptOnServer() { (success) in

                if product.needsFinishTransaction {
                    SwiftyStoreKit.finishTransaction(product.transaction)
                }

                if success {
                    self.updateUI()
                }
                else {
                    self.showError()
                }
            }

        case .error(let error):
            self.showError()
        }
    }
}

In App Delegate I have this:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    SwiftyStoreKit.completeTransactions(atomically: false) { purchases in

        for purchase in purchases {

            switch purchase.transaction.transactionState {

            case .purchased:

                self.validateReceiptOnServer() { (result) in

                    if purchase.needsFinishTransaction {
                        SwiftyStoreKit.finishTransaction(purchase.transaction)
                    }
                    switch result {
                    case .success: print("success validating receipt")
                    case .failure: print("failure validating receipt")
                    } 
                }

            case .restored:

                if purchase.needsFinishTransaction {
                    SwiftyStoreKit.finishTransaction(purchase.transaction)
                }

            case .failed, .purchasing, .deferred: break
            @unknown default: break
            }
        }
    }
    return true
}

For normal, uninterrupted purchases this setup seems to work fine. My problem is that when testing interrupted purchases, after calling SwiftyStoreKit.purchaseProduct(), the callback for SwiftyStoreKit.completeTransactions() in didFinishLaunchingWithOptions is triggered instead.

Is this a bug? And if not, how am I supposed to update my UI in the view controller that called SwiftyStoreKit.purchaseProduct()?

sabiland commented 3 years ago

I have the same issue. Sometimes AppDelegate completeTransactions is called instead of purchaseProduct completion block. How to solve this ?