IAP failed: SKErrorDomain Code=2 "Cannot connect to iTunes Store"

Hi,


I am receiving the following error message whenever my users try to purchase my product (so far I haven't had a single user on iOS successfully purchase any of my IAPs):


  Jul 10 11:49:53 melodyyin18@gmail.com LingolandNative: WARNING IAP: Purchase purchasing
  Jul 10 11:49:56 melodyyin18@gmail.com LingolandNative: ERR IAP: Purchase failed: error=Error Domain=SKErrorDomain Code=2 "Cannot connect to iTunes Store" UserInfo={NSLocalizedDescription=Cannot connect to iTunes Store}
  Jul 10 11:50:11 melodyyin18@gmail.com LingolandNative: INFO IAP: productsRequest received response!


What could be causing this issue?


The first thing that comes to mind is that the user is having trouble with the Internet connection, except everything else in my app that uses networking works (the app is a networked game).

I've also tested with sandbox users locally, and the purchasing has worked fine.

As feedback, either there needs to be a retry mechanism for connecting to the store, or the error message needs to be more descriptive if it's not actually a connection problem.


Thanks,

Charles

Replies

Are you certain these users are logged in using a real production environment and really trying to make a purchase? Can you purchase an IAP in the production environment? If not, is your IAP approved and marked 'cleared for sale'?

Not sure if this helps, but these users are international users.


"Are you certain these users are logged in using a real production environment"

Yes, they are real end users, not testers.


"Can you purchase an IAP in the production environment?"
Yes, we had a production user help us test this out by purchasing one of our IAPs. We checked, and his credit card was in fact charged.


"If not, is your IAP approved and marked 'cleared for sale'"

Yes, all of my IAPs are both "approved" and marked "cleared for sale."


"Are you certain these users are really trying to make a purchase?"

I can't be so sure, but from my remote logs, I do know that they are:


1.) Initiating a purchase in my UI which triggers addPayment on the defaultQueue SKPaymentQueue as such:


- (void)InitiatePurchase:(NSString*)product_name
    RLOGI(@"IAP: OBJC initiatePurchase: %@", product_name);

    if (!products_initialized) {
        RLOGE(@"IAP: Could not initiatePurchase, products not initialized!");
        return;
    }

    if (!product_name) {
        RLOGE(@"IAP: Could not initiatePurchase, nil product_name!");
        return;
    }

    if (!user_email) {
        RLOGE(@"IAP: Could not initiatePurchase, user not logged in!");
        return;
    }

    if (![[products_dict allKeys] containsObject:product_name]) {
        RLOGE(@"IAP: Could not find productToPurchase in dict!");
        return;
    }

    SKProduct *productToPurchase = products_dict[product_name];
    if (!productToPurchase) {
        RLOGE(@"IAP: Invalid productToPurchase in dict!");
        return;
    }

    SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:productToPurchase];
    payment.quantity = 1;
    payment.applicationUsername = [self hashedValueForAccountName:user_email];

    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

^ note that no error conditions were logged.


2.) The purchase shows up in updatedTransactions as an SKPaymentTransaction with transactionState SKPaymentTransactionStatePurchasing:


- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing: {
                RLOGW(@"IAP: Purchase purchasing");
                break;
            }
            case SKPaymentTransactionStateDeferred: {
                RLOGW(@"IAP: Purchase deferred");
                break;
            }
            case SKPaymentTransactionStateFailed: {
                RLOGE(@"IAP: Purchase failed: error=%@", transaction.error);

                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Purchase failed"
                                                                message:@"You were not charged."
                                                               delegate:nil
                                                      cancelButtonTitle:@"OK"
                                                      otherButtonTitles:nil];
                [alert show];

                if (self.gameUIRef != nil) { [self.gameUIRef OnPurchaseFailedCallback]; }
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            }
            case SKPaymentTransactionStatePurchased: {
                RLOGI(@"IAP: Purchase purchased");
                [self validatePurchase:transaction];
                break;
            }
            case SKPaymentTransactionStateRestored: {
                RLOGW(@"IAP: Purchase restored");
                [self validatePurchase:transaction];
                break;
            }
            default:
                RLOGE(@"IAP: Unexpected transaction state %@", @(transaction.transactionState));
        }
    }
}


3.) The purchase shows up in updatedTransactions as an SKPaymentTransaction with transactionState SKPaymentTransactionStateFailed and the error field is as shown:


At any rate, none of these suggestions would seem like resolutions that would come to mind as possible resolutions to a reasonable person upon seeing the error message: "Cannot connect to iTunes Store." As feedback, for example, one of the suggestions was that my product may not have been "approved"; if the IAP product is not approved, perhaps a more descriptive error message such as "IAP product is not approved" instead of the ambiguously vague (if not misleading) error message: "Cannot connect to iTunes Store," which suggests there either that there is an Internet connectivity problem or that the iTunes Store is down.

Hi, it's been a few days since I posted my last reply, but it seems like it is still being moderated. I just had another user who wanted to buy my IAP product get his purchase denied with the same error.


I received a screenshot from his end saying "This In-App Purchase has already been bought."


If there's anything I can do on my end to help expedite the process, please let me know!

Posted below was my original response to your post. It got deleted by the moderator for some reason. I recommend you try to purchase the IAP yourself to confirm the validity of the reports of your users:



(in response to TheChuckster) Are you certain these users are logged in using a real production environment and really trying to make a purchase? Can you purchase an IAP in the production environment? If not, is your IAP approved and marked 'cleared for sale'?

I am having the same problems. It happens to a subset of my users and I can't even replicate it.


When the user tries to either actually restore, the StoreKit alerts say that the purchase has been completed and that items will be restored. When buying the non-consumable again it says "This In-App Purchase has already been bought." Then in my logs, I can see it first does


-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions


with the transactionState being 'SKPaymentTransactionStatePurchasing' and then it is 'SKPaymentTransactionStateFailed' with error code 2 and error message "Cannot connect to iTunes Store."


Also, this does not happen to all of the non-consumables in the app. There is one I know of but unfortunately, it is the most important one.


Any ideas?

Hello. Did you manage to solve this problem?

I had a similar issue and I ended up here on the forums. Another member suggested removing all App Store Promotions and that helped me.

Did you find a resolution to this issue? I'm seeing it happen one or two times per day

First, ensure you're finishing transactions upon success/failure:


In our case, the old code was not calling `SKPaymentQueue.default().finishTransaction(transaction)` to remove it from the queue. Prior to iOS 13.4, that apparently worked fine even though the documentation says it’s required


So what would happen is the dialog would show once and the person would cancel and then from that point on the transaction would persist in the queue and automatically return as cancelled without showing the dialog again. Finishing the transaction purges it and allows the dialog to show again


---


If that doesn't work:


Per an Apple engineer's request, I filed a radar for this (FB7648374) with App Store logging and sysdiagnose


Please do the same:


- Follow the instructions for App Store logging at https://developer.apple.com/bug-reporting/profiles-and-logs/

- Install the profile on your device that enables verbose logging

- Then repro the problem on your device w/ device logs running (Xcode -> Devices -> Console logs)

- Then immediately capture a sysdiagnose

- File a new radar with that information at https://feedbackassistant.apple.com/