In-App Purchase failing in iOS 9 after payment update

We have had many users reporting that they have paid for our product via in-app purchase but we have failed to unlock the content. The common denominator between these reports is iOS 9. We have offered IAP for years and haven't experienced this issue until now.


We have found that the issue occurs when the customer is prompted to update their App Store payment information during the IAP process. This takes them out of our app. Then upon returning, the content is still locked. I was also able to replicate this by purchasing our product with a store build.


The Apple staff member in this related forum https://forums.developer.apple.com/thread/6431 indicates that the transaction will move to the a SKPaymentTransactionStateFailed when the user is directed out of the app to update their payment information. After updating their information, a SKPaymentTransactionStatePurchased is received. That forum suggests that finishTransaction should be called on the failed transaction. Then, finishTransaction should be called again after SKPaymentTransactionStatePurchased is received and the content is unlocked. This is what we have been doing for a long time but this doesn't appear to be working with iOS 9. It seems that we are only getting a call for the change to SKPaymentTransactionStateFailed but not for a subsequent SKPaymentTransactionStatePurchased. I can't guaruntee that we are not getting the state SKPaymentTransactionStatePurchased call as we can't use the debugger with our store builds but our code is simple enough to say this is very likely


Here is a simplified version of our code:


- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
  for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchasing:
                break;
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
               [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
            default:
                break;
        }
    }
}


We would like to know if others are experiencing IAP issue with iOS 9? Also, is it correct to call finishTransaction after SKPaymentTransactionStateFailed? If we do, will we still receive another delegate call for SKPaymentTransactionStatePurchased?


Any help that can be offered would be appreciated.


Bill

By "refresh receipt", do you mean fetch the receipt with [NSBundle appStoreReceiptURL] and validate? The only time we trigger that is after we receive a SKPaymentTransactionStatePurchased notification. We can purchase another IAP to trigger that but I assume that will not include the lost purchase.


We are submitting a build for Apple review. If adding an option to manually refresh the reciept, in the case of transaction failures, could potentially restore the missing transaction, we would consider including that to the build.

I should have been clearer - by refresh, I mean using the SKReceiptRefreshRequest, then validating the receipt. If a second purchase attempt is made on a purchase which has not been completed, you should see the alert - to the effect that you've already purchased the item and do you want to access it again - depending on the purchase type. This would be a further indication that the StoreKit process was incomplete - which would be an additional bug report.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

Some additional info from our encounters with this problem:

our bug report is rdar://22858208


In our case, the products are consumables. Once there has been one failure, it appears that all future purchase attempts fail. For a particular product id, the user is charged for the first purchase attempt (and shows in itunes purchase history), but not subsequent attempts. Trying a different product id likewise results in charging for the first attempt with it and not subsequent ones.


In all of these failure cases, whether the user is charged or not, the app's purchase observer only gets a transaction in the SKPaymentTransactionStateFailed state, with code 0, on which we call finishTransaction.


This behavior leads me to believe that somewhere in the system, the transaction is still in an unconsumed state, otherwise I would expect the user to be charged for the subsequent attempts of the same product id, since they are consumable products.

A number of you have submitted requests for DTS support and I've responded by providing instructions as to how to submit a bug report that will get the attention of the iTunes Product Support group. I'm investigating this issue with iTC. In the meantime, I myself was able to replicate this problem with my production account using a third party app that offered an in app purchase to remove ad support. I've documented the problem extensively in the bug report. The issue appears to be that the expected process of handling incomplete transactions is not working in iOS 9.


As a recap, when the user attempts an in app purchase and the StoreKit flow issue occurs - that is the StoreKit detects a problem with the credit card info, the user is taken to the Settings App -> Apps and iTunes Store -> User Account Settings -> Payment Information screen to update their credit card info. In the meantime, the initial purchase is failed - in case the user doesn't update their info. However if the credit card info is updated, the user is asked whether they still want to make the in app purchase. If they do, the purchase is made, but now it's considered an incomplete transaction waiting for the app to query for incomplete transactions. Now the user switches back to the app. The transactionObserver processes the failed transaction that was queued for processing. Then it will query the Store based on the current user setting for "incompleteTransactions". Under previous iOS, this worked and the app was immediately notified of the successfulTransaction with a call to the updatedTransactions delegate method.


Under iOS 9, I'm not seeing the notification return the incomplete transaction. Because the transaction is incomplete, a call to restoreCompletedTransaction will not return the new transaction. However if the receipt were to be refreshed, the in_app array should show the new purchase. The problem is how to complete the transaction.


One thing I found was to attempt a second purchase of the item - this resulted in the dialog that told me that I'd already purchased the item and did I want to download the item for free. After doing this, the ad removal worked. When I deleted the app and re-installed it, the restoreCompletedTransaction call worked. I understand that this is a terrible workaround - however, I wouldn't be surprised if some users are in fact repurchasing the items to find that it works without a second charge. This becomes dicey for one app I reviewed where there are multiple in app purchases that are pricey. I continue to work with iTC to get this issue fixed.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

You have no idea how great it is to have some actual feedback from you 🙂 Most of the time we just feel like we're floundering in the dark here on the forums, the blind leading the blind. This is a breath of fresh air.


If the StoreKit flow were easier (possible?) to test, this may have been caught sooner.

Hi Rich,


Thank you for investigating this and reporting back to us.


In short, it sounds like a serious regression in iOS 9 that Apple needs to fix. As I mentioned previously, almost exactly the same thing happened about 2 years ago, if that gives the App Store / iTunes team a hint about how to fix it:


https://devforums.apple.com/message/926286


The fact that such a serious bug that involves people spending *real money* can persist for weeks on end is extremely disappointing. I would assume that as soon as this problem started occuring, iTunes Store support saw a spike in complaints and inquiries (as we did), and that should have resulted in the responsible parties being aware of the problem in short order. Given that the problem involves people spending real money, I would assume that fixing such a bug would be a top priority.


Instead, this has been going on for multiple weeks now. The Apple Developer News site does not have any posting about the fact that in-app purchases are pervasively broken, which seems to be at least as important as the notes concerning App Slicing being broken that are posted on that site:


https://developer.apple.com/news/


The Apple System Status page shows that there are no problems with the App Store:


https://www.apple.com/support/systemstatus/


Instead, developers are left to search through forum posts, which often do not receive any official reply. (Your presence in this thread is a welcome exception.)


Very poor form by Apple, here.


BTW, I think your "workaround" might work for non-consumable purchases, but I don't think it would work for apps like ours that sell non-renewing subscriptions, or apps that sell consumables.


Best,

Jeff

Thanks for getting back to us!

I've been scratching my head trying to figure out why this problem happened.

I tried to store the logs from function that handles SKPaymentTransactionStatePurchased, but I never get it from these failed purchase users.


I'd like to replicate the problem with updating the payment information, but my credit card info is up to date.

How can I force the app to get out from the app and update the payment information?


Thanks!

Assuming our issue is caused by the same problem (rdar://22858208), we do see somewhat different behavior (we are selling consumables). Specifically, the user doesn't get a dialog that they've already purchased the item. We were hoping that doing a complete transaction refresh every time a user launches the app or fails a purchase (via the appStoreReceiptURL) might work around this problem, but have not yet tested it. I'm not sure whether or not we can even test this - if a client is running an ad-hoc build and we request receipt data from the app store url, will it include live purchase receipts, or only sandbox ones?

Rich, You made reference to an "incomplete transaction" and the "user's settings for an incomplete transaction". Can you explain what you mean by that? Does it differ from a regular transaction that has not been delivered? Is there anything to do other than addTransactionObserver? Peter

An incomplete transaction is a successful purchase transaction for which the app has not called finishTransaction on.


Having an active transactionObserver should result in the app, when moving the app from the background, to the foreground, trigger the app to check for incomplete transactions - exactly the same action taken when the addTransactionObserver is called by the app when it is launched.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

Can anyone comment on whether this problem is isolated to; 1) a call to updatedTransactions not being called when first sent to the device after a failed transaction 2) a call to updatedTransactions not being called when the app re enters the foreground with an active transactionObserver 3) a call to updatedTransactions not being called when the user calls addTransactionObserver. Note that these are 3 different situations and that if the problem does not exist in case #3 there is an easier work around.

For a consumable at least, once a device / apple id is in this state, a successful transaction is not reported via updatedTransactions in any of those 3 cases.

Thanks.


Here may be the reason why the system is screwing up....check out the report of a call to removed transaction in:

"IOS9: Unfinished transaction disappears from payment queue"

Hello, can anyone comment on this?

I'd like to replicate the problem with updating the payment information, but my credit card info is up to date.

How can I force the app to get out from the app and update the payment information?


Thanks!

You can instead remove your credit card from your iTunes account.

In-App Purchase failing in iOS 9 after payment update
 
 
Q