Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closure inside SwiftyStoreKit.completeTransactions(atomically: true) { ... } never gets reached #500

Open
2 tasks
lsamaria opened this issue Nov 2, 2019 · 6 comments
Labels
area: purchase flows purchase processes, efficiency and failures difficulty: intermediate this issue is tough, contributions welcome status: needs analysis community analysis is needed type: bug

Comments

@lsamaria
Copy link

lsamaria commented Nov 2, 2019

Platform

iOS 13.1 and 13.2

In app purchase type

  • Consumable

Environment

  • Sandbox

Version

SwiftyStoreKit (0.15.0) // installation: pod 'SwiftyStoreKit'

Related issues

#432

https://stackoverflow.com/a/53162058/4833705

https://stackoverflow.com/q/53841379/4833705

Issue summary

the closure inside SwiftyStoreKit.completeTransactions(atomically: true) { ... } never gets reached

What did you expect to happen

I expected the closure to get entered

What happened instead

the closure was ever entered

Report

My app uses a tip jar for IAP so I only use Consumable.

I submitted my app to the App Store this morning and got a rejection saying they couldn't make an IAP when trying to make a Consumable purchase (a tip).

While testing as a sandbox tester when I checked the purchase I kept getting an error: "Unknown error. Please contact support":

SwiftyStoreKit.purchaseProduct(product, quantity: 1, atomically: true) { result in
    switch result {
        case .success(let product):
            // fetch content from your server, then:
            if product.needsFinishTransaction {

                SwiftyStoreKit.finishTransaction(product.transaction)
            }
            print("Purchase Success: \(product.productId)")

        case .error(let error):
             switch error.code {
                case .unknown:
                     print("Unknown error. Please contact support")
        // failed cases ...
    }
}

Upon further inspection even though I had this code below in AppDelegate the closure never gets called.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    // Doesn't enter closure
    SwiftyStoreKit.completeTransactions(atomically: true) { 

        // *** it never reaches this point ***

        (purchases) in

        for purchase in purchases {
            switch purchase.transaction.transactionState {
            case .purchased, .restored:
                if purchase.needsFinishTransaction {
                    // Deliver content from server, then:

                    let downloads = purchase.transaction.downloads
                    SKPaymentQueue.default().start(downloads)

                    SwiftyStoreKit.finishTransaction(purchase.transaction)
                }
                // Unlock content
            case .failed, .purchasing, .deferred:
                break // do nothing
            }
        }
    }
    return true
}

My sandbox tester email is confirmed, I signed out of the device as my real self, I logged in on the device as the sandbox tester, I'm logged into iCloud as the sandbox tester.

What could be the reason for the closure not being entered in AppDelegate?

Here's the code from the vc that I use to make the Consumable purchase:

var dataSource = [Tip]()
var sharedSecret = appStoreConnectSecretKey

let inAppProductIds = ["com.myCo.myAppName.firstTip", // 0.99
                       "com.myCo.myAppName.secondTip", // 9.99 ]

override func viewDidLoad() {
    super.viewDidLoad()

   getInAppPurchaseAmounts()
}

func getInAppPurchaseAmounts() {

    // show spinner

    let dispatchGroup = DispatchGroup()

    for productId in inAppProductIds {

        dispatchGroup.enter()

        SwiftyStoreKit.retrieveProductsInfo([productId]) { [weak self](result) in
            if let product = result.retrievedProducts.first {
                let priceString = product.localizedPrice!
                print("Product: \(product.localizedDescription), price: \(priceString)")

                let tip = Tip(displayName: product.description,
                              description: product.localizedDescription,
                              productId: productId
                              price: priceString)


                self?.addTipToDataSource(tip)

                if let sharedSecret = self?.sharedSecret {

                    self?.verifyPurchase(with: productId, sharedSecret: sharedSecret)
                }
                dispatchGroup.leave()

            } else if let invalidProductId = result.invalidProductIDs.first {
                print("Invalid product identifier: \(invalidProductId)")
                dispatchGroup.leave()

            } else {
                print("Error: \(String(describing: result.error))")
                dispatchGroup.leave()
            }
        }
    }

    dispatchGroup.notify(queue: .global(qos: .background)) { [weak self] in
        DispatchQueue.main.async { [weak self] in

            // removeSpinnerAndReloadData()
        }
    }
}

func verifyPurchase(with productId: String, sharedSecret: String) {

    let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: sharedSecret)
    SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
        switch result {
        case .success(let receipt):
            let productId = productId
            // Verify the purchase of Consumable or NonConsumable
            let purchaseResult = SwiftyStoreKit.verifyPurchase(
                productId: productId,
                inReceipt: receipt)

            switch purchaseResult {
            case .purchased(let receiptItem):
                print("\(productId) is purchased: \(receiptItem)")
            case .notPurchased:
                print("The user has never purchased \(productId)")
            }
        case .error(let error):
            print("Receipt verification failed: \(error)")
        }
    }
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    guard let cell = collectionView.cellForItem(at: indexPath) as? TipCell else { return }
    guard let indexPath = collectionView.indexPath(for: cell) else { return }

    let tip = dataSource[indexPath.item]

    purchaseProduct(with: tip.productId)
}

func purchaseProduct(with productId: String) {

    SwiftyStoreKit.retrieveProductsInfo([productId]) { result in
        if let product = result.retrievedProducts.first {
            SwiftyStoreKit.purchaseProduct(product, quantity: 1, atomically: true) { result in

                switch result {
                case .success(let product):
                    // fetch content from your server, then:
                    if product.needsFinishTransaction {
                        SwiftyStoreKit.finishTransaction(product.transaction)
                    }
                    print("Purchase Success: \(product.productId)")
                case .error(let error):
                    switch error.code {
                    case .unknown:
                        print("Unknown error. Please contact support")
                    case .clientInvalid:
                        print("Not allowed to make the payment")
                    case .paymentCancelled:
                        print("Payment cancelled")
                    case .paymentInvalid:
                        print("The purchase identifier was invalid")
                    case .paymentNotAllowed:
                        print("The device is not allowed to make the payment")
                    case .storeProductNotAvailable:
                        print("The product is not available in the current storefront")
                    case .cloudServicePermissionDenied:
                        print("Access to cloud service information is not allowed")
                    case .cloudServiceNetworkConnectionFailed:
                        print("Could not connect to the network")
                    case .cloudServiceRevoked:
                        print("User has revoked permission to use this cloud service")
                    default:
                        print((error as NSError).localizedDescription)
                    }
                }
            }
        }
    }
}
@vadimleonov
Copy link

Did you solve this issue?

@lsamaria
Copy link
Author

lsamaria commented Nov 9, 2019

I resolved the unknown error issue (another issue was causing it) but til this day the closure in app delegate still never gets reached. I just tried 5 minutes ago and nothing.

1 similar comment
@lsamaria
Copy link
Author

lsamaria commented Nov 9, 2019

I resolved the unknown error issue (another issue was causing it) but til this day the closure in app delegate still never gets reached. I just tried 5 minutes ago and nothing.

@louiskabo
Copy link

What was the other issue causing the unknown error message?

@Sam-Spencer Sam-Spencer added status: needs analysis community analysis is needed type: bug labels Feb 19, 2020
@Sam-Spencer Sam-Spencer added difficulty: intermediate this issue is tough, contributions welcome area: purchase flows purchase processes, efficiency and failures labels Feb 19, 2020
@SureshDistill
Copy link

I resolved the unknown error issue (another issue was causing it) but til this day the closure in app delegate still never gets reached. I just tried 5 minutes ago and nothing.

Can you please explain how did you solve unknown error issue?

@lsamaria
Copy link
Author

lsamaria commented Nov 3, 2020

@SureshDistill It was so long ago I dont remember but from what I can tell by the comment that I made the unknown error issue was caused by something else

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: purchase flows purchase processes, efficiency and failures difficulty: intermediate this issue is tough, contributions welcome status: needs analysis community analysis is needed type: bug
Projects
None yet
Development

No branches or pull requests

5 participants