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

After receiving reports of a couple missing purchases each day, we have not had any missing reports since 10/6. Has anyone else noticed the change?


Additionally, we seem to be getting completed transactions NOW for transactions that were misisng from weeks ago. We have not deployed any updates in weeks so I do not believe this change is related to anything we have done.

I can confirm that, we have not had missing transactions in last 2-3 days. So we will see next days...

We received a response in our support ticket that a remote change was made on Apple's end that should have fixed this problem. We not received any issues in several days and many weeks-old, missing transactions are now being successfully processed. Thanks for your help with this issue.

We also are seeing fairly normal behavior for in app purchases, and receiving reports from our players that their previously paid-for purchases are being granted.

I receive about 1 report from users yesterday but before I receive about 7 reports from users every day . if the bug have already been fixed I should not get any recieve. why ?

This issue went away for a couple of weeks, but it seems to be back now. This may be related to the fact that iOS 9.1 asks you to accept new terms & conditions for the App Store after updating.


Anyone else seeing the same thing?

We've had multiple reports of a similar issue with a non-renewing subscription.


Their app is basically broken forever once it gets into this state.


Both reports said something to the effect of having the payment method dialog show up (where iTunes asks for the CVV or whatever, new expiration date, etc) before the purchase. After that they get the "Already purchased" error even though they didn't actually purchase anything (no funds changed hands).


A receipt is actually generated and verify succeeds, but the receipt returned does not include any new transactions (i.e. receipt generated 2015-11-23 has no 2015 transactions in it for a yearly non renewing sub).


This is a serious issue causing lost revenue.

As I read what you wrote, the behavior is normal.


Normal behavior: When a user has purchased one or more non-renewing subscriptions and their latest subscription expires, they can purchase it again. Before they complete the purchase they get a UIAlert stating "You have already purchased this subscription. Tap 'Buy' to renew or extend it". If they tap "Buy" they get charged and the app gets a call to updatedTransactions with a purchased transaction and a receipt with an extra purchase recorded in it. If they tap "Cancel" then they do not get charged and the app gets a call to updatedTransactions with a failed transaction and a receipt that does not have an extra purchase in it.


Completely consistent with the above normal behavior you wrote:

Both reports said .... they get the "Already purchased" error (sic)....no funds changed hands..... the receipt returned does not include any new transactions.


If "no funds changed hands" then they didn't purchase anything and they should not expect a renewal.

This is not fixed for me in the latest OS. Can anyone confirm the following behavior for consumables?

If I try to purchase a consumable and never close/finish it, the app will keep asking me for itunes password whenever it goes to the foreground.


updatedTransactions is no longer called automatically for old transactions stuck in the queue even if I have the observer set.

Another problem is, even if I force those older transactions to go through updatedTransactions, they seem to fail IAP verification because those transaction ids are no longer in the receipt.


Anyone seeing this still or have a workaround?

You asked - "If I try to purchase a consumable and never close/finish it, the app will keep asking me for itunes password whenever it goes to the foreground." - This is the expected behavior when the transactionObserver is active. When an iAP app is brought to the foreground, the transactionObserver initiates a query to the StoreKit asking for incompleteTransactions. The StoreKit server responds and if there are incompleteTransactions, the updatedTransactions delegate method is called.


You then stated - "updatedTransactions is no longer called automatically for old transactions stuck in the queue even if I have the observer set."

Response - it might be that the StoreKit is responding with 0 incompleteTransactions. The queue which you refer to here is on the StoreKit server. This would be a bug report for iTunes Connect to investigate.


You stated - "Another problem is, even if I force those older transactions to go through updatedTransactions, they seem to fail IAP verification because those transaction ids are no longer in the receipt."

Response - I'm unclear as to how you "forced the older transactions to go through updatedTransactions". There's no StoreKit API to trigger this action - so far as I'm aware.


I can assist you further with one on one investigation if you would submit for a DTS incident support.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

Thanks for the reply. I will submit a report but will also reply here for others.

You said "The StoreKit server responds and if there are incompleteTransactions, the updatedTransactions delegate method is called."

This does not seem to be true. That is why I said I had to "force the older transactions to go through updatedTransactions" by doing this.


[self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:[SKPaymentQueue defaultQueue].transactions];

This seems to match what you posted on Oct 2.

"why isn't the updatedTransactions dlegate mathod being called regarding the "incomplete" transaction."


Plus furthermore, for the incomplete transactions, I sometimes don't see the its transaction id in the receipt. This gives me no reliable way to verify them other than to just close the transaction.

Without intending to hijack this thread (which seems to have identified a possible bug), let me please comment that it seems like you may be trying to use an unfinished transaction over a long term to repeatedly verify the purchase of a consumable IAP. (If not -ignore this.) If that is so, I suspect it will not work and will cause all kinds of problems. A transaction should be finished ASAP. If the user/internet quits something at an awkward point then the unfinished transaction should still be available and get pushed to updatedTransactions the next time...etc. etc. etc (and here the bug report is important). But that reuse of an unfinished transaction is indeed rare. If you keep the transaction in the unfinished state to continually prime your updatedTransaction method (and refuse to call finishTransaction even though you got it) then you will be requiring the user to log in each time they open your app and, even more exciting, you may end up sending that request to login to other apps on the same device when they add a transaction observer even if, and especially if, they are logged in to a different account on iTunes! Search this forum for 'the endless loop' if you want horror stories on what can happen with unfinished transactions. Use the receipt to verify the purchase - send it to Apple servers (easy) or decode it yourself (hard).

Nope. I just want to make sure any unfinished transactions will be able to go through my code path to a finished state.

That involves going through updatedTransactions delegate and then verifying the receipt.

IF there is an unfinished transaction when the app goes into the foreground, it asks for the store password (I know this is correct behavior.)

But it doesn't call the updatedTransactions delegate automatically even though I've set the transaction observer. (This is NOT correct behavior.) I think this might be an iOS 9 bug? So I'm wondering if I can work around this by just calling it manually like so when the app goes into foreground.

[self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:[SKPaymentQueue defaultQueue].transactions];

That is problem 1.


Problem 2 is that sometimes with the unfinished transactions, I don't see their transaction ids in the latest receipt. So this gives me no way to verify it which creates the 'endless loop' unless I hack in some code to finishTransaction().


IF there are purchased consumables transactions in my [SKPaymentQueue defaultQueue].transactions, then it SHOULD be in my latest receipt right?

The updatedTransactions delegate method is defined by the application for StoreKit to call when the TransactionObserver detects a transaction to be processed. The application does not call the delegate method itself. If there is an incompleteTransaction and the observer detects it, but the updatedTransactions delegate method is not called, that would be a bug. The evidence for this would require a console log with the StoreKit profile active. The log would show that transactionObserver queryiing the StoreKit for incompleteTransactions and the log would show that the StoreKit responded and the number of incompleteTransactions - the transaction information is encoded, as is all StoreKit info so such details are not possible to see in the log.


Manually calling updatedTransactions is not a solution here - I suspect that my October 2 response could have been clearer, but that was not what I had in mind.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

I responded to problem 1 in my response to alvinw. As for problem 2, transactionIDs are a iTC value. They are best associated with auto-renewing subscriptions but for other purchase types, we've not provided much information about them except as mentioned in the Receipt Validation Programming Guide. As for consumable purchase items, their presence in the application receipt requires some explanation. The StoreKit will record a consumable item in the application receipt so long as the consumable transaction is incomplete. So this sounds contrary to things I may have mentioned in the past. If you are already familiar with the following, forgive my explanation, but this is for new users who may not be so familiar with this process.


The way things work for a consumable purchase - lets start with the case that the application receipt has an empty in_app array - actually, an in_app array where there is no consumable purchase item present. So the user purchases a consumable item. The StoreKit processes the purchase and sends a notification to the app of the successful purchase. Along with the successful purchase indication, the application receipt is updated to include the consumable purchase item in the in_app array. The presence of the consumable item is set in place in the app receipt because at the time the app is notified of the successful transaction, the consumable purchase item is an incompleteTransaction - the application has not acknowledged the transaction yet.


The application receives the successful transaction indication in the updatedTransactions delegate method. The app processes the successful transaction indication and provides the purchased content to the user. Then the app makes the finishTransaction call to acknowledge receipt of the transaction to the StoreKit. The StoreKit sees this acknowledgment and marks the consumable purchase as completed.


For the app, the application receipt is only updated when a transaction is processed or when the SKReceiptRefreshRequest is called. Following the successful transaction above, keep in mind that there has been no new transaction (I'm assuming that seconds after making the consumable purchase there is no follow on purchase attempt) so the application receipt should still show the consumable item in the application receipt. However, if the application makes the SKREceiptRefreshRequest at this time, the updated receipt will no longer show the consumable item in the application receipt; the consumable transaction has been acknowledged by the application so StoreKit removes the item from the receipt when next the receipt is updated. The same will occur when a new purchase is made. Of course if the consumable item remains in the in_app array, then either the StoreKit did not receive the completion notice triggered by the finishTransaction call or there is a bug report issue.


Lets say for example that a user installs your game app which offers a consumable purchase type for "gold coins". The user purchases the gold coins, the app receives the successful transaction notice and makes the coins available to the user. The app then calls finishTransaction on the consumable transaction. If the app checks the app receipt at this time, there will still be the consumable purchase item in the in_app array.


Now lets say that the user installs the app to a second device. When the user launches the app, that updated version of the app will have a copy of the updated receipt where the consumable item no longer appears in the in_app array. The StoreKit erased the entry as the transaction was acknowledged.


As per the StoreKit PG, restoring consumable purchases is by implementation of the app.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

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