Auto-Renewable In App Purchase Fails in Production

Hello All,


We have recently got our app live on the App Store, which feels quite nice 🙂


After I have downloaded the live version from the App Store the first thing that I've tested was the IAP and it didn't go well.


So I'll start by what happens and then give some background info as well as notes regarding the code.


What happens:

The purchase is requested and then the StoreKit kicks in with its usual interface. Then I keep up with the dialogue until it asks for the confirmation regarding the payment. Then I say OK and here it gets interesting. Two things happen at the same time: First is that I get the message that the payment was successful and at the same time (probably even a little earlier than the success message) I get an error that goes "Cannot connect to the iTunes Store".


Then I go check my account to see that the payment has really gone through - so I actually have bought the product successfully. Then I go back in the App thinking that I should be able to restore that purchase and try to do so. And I'm able to restore the purchase and then the product is actually enabled by the app logic.


Development phase:

During the development phase I have tested the IAP process in a very detailed way, trying to fulfill all the test scenarios in the guideline. The app was able to carry out the purchase processes successfully even when one was interrupted and then it was handled later on. It's able to validate the receipt using our backend server correctly and it was able to restore the products. These tests I have carried out both with Debug and Release builds.


Notes regarding the code:

First of all, I want to say that the transaction observer right before returning true from didFinishLaunchingWithOptions delegate method and it's never removed throughout the lifecycle of the app.


The receipt (I'm not sure whether it's related but anyway) is validated online. And I have confirmed on the server side that the receipt is validated for the production environment.


Since I never remove the transaction observer from the payment queue, I have to assume that I only get the transaction failed state from the queue. Because that's the only case when I display an error message during a new purchase. And since the product is never enabled by the logic, I can say that I never get the transaction purchased state from the queue. One thing to mention here is that I finish the transactions right away when they have the state failed. Could this be an issue?


Another question is:

I'm adding the transaction observer in the main thread. But when I get a call on the the paymentQueue(SKPaymentQueue, updatedTransactions: [SKPaymentTransaction]) method; I pass the rest of the app logic handling to another thread which has the qOs: .utility and come back to the main thread if only I have something to update on the UI as a result of the logic. Is there anything wrong here?


Well, this is all I can think of right now but if there is something I have skipped to mention, please let me know and I'll give the necessary information.


Thank you in advance and have a good day.


Cheers,

Fero

You indicated that - "Two things happen at the same time: First is that I get the message that the payment was successful and at the same time (probably even a little earlier than the success message) I get an error that goes "Cannot connect to the iTunes Store".

Could it be that before you confirmed the payment that you had to update your credit card info? The symptoms you provided were - 1 the payment was successful, but the app got an error. When I hear this user level symptom, I think "Interrupted purchase flow". I've included a response on this issue below.


You then state that your app installs the transactionObserver in the didFinishLaunchingWithOptions delegate method - this is good. So what happens when you force quit the app and relaunch the app. Supposedly since the In-App Purchase has been charged for, the iTunes Store has created an incompleteTransaction which is pending completion. Relaunching the app means that the transactionObserver will be installed which should detect the incompleteTransaction. Note that the restoreCompletedTransactions method only restores "completed" transactions - those transaction notifications which the app has called finishTransaction on.


There may be a bug report here in that the app is only seeing the failed transaction notice here and not the following successful transaction. However, force quitting the app and re-launching it should get the purchase recognized, if the transactionObserver detects it.


Here's my interrupted purchase flow description

The interrupted purchase flow situation occurs whenever StoreKit determines that there is a problem with the user account making the purchase - the name stems from the fact that the application purchase process is interrupted and the application is backgrounded while the Settings app comes forward to either.

1. have the user fix / update their credit card info

2. agree to a new StoreKit Terms and Conditions.


Upon fixing the issue, the user is asked whether they want to continue with the purchase process. When they agree, StoreKit processes the purchase request, then will present a dialog that the purchase was successful.


While this is happening the iAP app is in the background, there is no notification to the app that the purchased was successful. So how is the app informed of the successful purchase. The notification process relies on the transactionObserver being and remaining active through the lifecycle of the app.


On the server side, when the interrupted transaction flow is detected, the server automatically queues a failed transaction event for the app and user. This occurs because the user might not fix the failure issue. When the user does fix the issue, then the successful transaction event is queued behind the failure event - again on the server - there is no notification to the app while it remains in the background.


In the normal situation, the user brings the app to the foreground. Assuming that the transactionObserver is active - it gets activated when the app is moved from the background to the foreground. The transactionObserver sends a query to StoreKit asking if there are any queued transactions to process - In the interrupted flow situation, the failed transaction is returned first. The updatedTransactions delegate method is called with the failed transaction indication. As per the StoreKit guide, the application must call finishTransaction on this failed transaction.


Next, the StoreKit returns the queued successful transaction to the app. The updatedTransactions delegate method is called with the successful transaction indication. After processing the successful transaction indication, the app calls finishTransaction. Until the finishTransaction is called, a transaction is considered “incomplete”.


Note that in the case of the interrupted purchase flow - a single call to addPayment can result in both a failed transaction event followed by a successful transaction event.


So what happens if after fixing the situation as described above, if the user does not return immediately to the app, and for some reason, iOS jettisons the app to make room for some other app. In this case, the failed and successful transaction remain incomplete on the StoreKit server. When the app is relaunched, and if the addTransactionObserver is not called in the didFinishLaunchingWithOptions delegate method as per the StoreKit Program Guide and the iAP Best Practices Tech Note 2387

<https://developer.apple.com/library/ios/technotes/tn2387/_index.html#//apple_ref/doc/uid/DTS40014795>


the incomplete transactions are not processed. As the transactions are incomplete, the restoreCompletedTransactions method will not restore them.


One could argue that the app could check the application receipt, but to my knowledge, the app receipt is updated when the transactionObserver processes a transaction or when the SKReceiptRefreshRequest is issued.


Of course, this is all a guess on my part. I’d check to see if your app calls addTransactionObserver in the didFinishLaunchingWithOptions delegate method.


As for the handling of the customer, if this is a non-consumable purchase type, he can attempt the purchase a second time - StoreKit will detect that he’s already purchased the item and that will be restored for free. The transaction will be process interruption free and should be completed.


Some additional notes

1. To replicate the interrupted purchase flow situation, install the production app, then enter the Settings app and clear out the credit card info for the current user. Now launch the in app purchase app and attempt a purchase

2. the interrupted purchase flow process cannot be replicated in the development environment - I’ve advised developers to submit an enhancement request using the Apple Developer Bug Report web page - http://bugreport.apple.com to request that iTunesConnect provide a means to simulate an interrupted purchase flow process to test in app purchase apps under in the development environment. However, the StoreKit programming guide, in the test procedures does present a means to replicate an incompleteTransaction in the sandbox by interrupting the application in the updatedTransactions delegate method in the Xcode debugger, then relaunching the app to see that the transactionObserver detects the incompleteTransaction on app relaunch.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

To simplify - perhaps what is happening is that StoreKit is sending two transactions in rapid succession - a failed transaction followed by a purchased transaction. This can happen because your credit crad info might not have been up to date before you executed the purchase. Your code may process the first failed transaction and then fail to process the second purchased transaction until the next time you launch the app. Is that a possibility?

Hello Rich,


Thank you for your detailed response.


I have seen the issue regarding the credit card info update in the forum prior to posting this question. But I still posted it because it doesn't happen in my case. So I stay within the app (the Settings app doesn't take over) throughout this process. Therefore I don't think that this is the issue but I'll anyway test this too for next version to be able to ready for that too. But first I should anyway fix the current issue.


Since the transactionObserver is alive throughout the lifecycle of the app; there is just one way I can miss or never get the payment successful notification. Is it possible that the same payment fails and succeeds for some reason; and since I have finished the transaction upon the first .failed notification and hence not get notified about the success? This is one scenario that I can come up with. Because I never get that call from the StoreKit again afterwards even if I force quit the app and start over.


And to elaborate on your advise regarding the customer experience; exactly the same situation occurs when I try to purchase the same product again. Just instead of seeing the "Now you are ready" dialogue from the StoreKit this time I see "You have already purchased this product..." message; but still prior to that I have the error "Cannot connect to the iTunes Store".


Thank you again.


Regards

Hello,


As I mentioned in my response to Rich; I never get shown anything regarding updating the credit card info. Furthermore, I don't get the success notification even after restarting the app. So somehow the StoreKit must be thinking that the transaction is really completed. That's why I'm asking what happens upon the first time I received the .failed notification and upon that I finish that transaction. Would the second successful transcation be the same transaction as before? If yes, then could it be that since I have already completed that transaction once it was failed, that I don't get notified by the updatedTransactions method, since it's not an incomplete transaction anymore?


Thanks!

You also wrote:


>when I try to purchase the same product again. Just instead of seeing the "Now you are ready" dialogue from the StoreKit this time I see "You have already purchased this product..." message; but still prior to that I have the error "Cannot connect to the iTunes Store".


Under a failed transaction you should finish only that one transaction and you should not display the error message if

transaction.error.code !=SKErrorPaymentCancelled

I suspect you would have entioned this, but to be clear - can you replicate this problem yourself? I suspect not. The next thing that would be useful would be to have the customer provide a console log from their device taken when the problem occurred - course this is not for the faint of heart. Also it's not for iOS 10 users as the StoreKit profile is crippled. Trying to diagnose a StoreKit issue based on user level symptoms is tough. You've indicated that the transactionObserver is active but that relaunching the app doesn't catch an incompleteTransaction. An attempt to re-purchase the item results in the alert that the item has previousy been purchased. This makes me wonder - does the item show up in the appStoreReceipt?


We could figure out what's happening if we can replicate the problem. However, I suspect this issue is happening with just a few customers. You might suggest that the user restart their device. However, if you run into a technically savvy user, and if the user has iOS 9 (or earlier) installed you can point the user to the DTS Bug Report Profiles and Logs web page where they can follow these instructions.

You can install the profile to your device by accessing the following web page on your device

https://developer.apple.com/bug-reporting/profiles-and-logs/

Find the App Store/iTunes Store profile for iOS and tap the "profile" button. Follow the instructions to install the profile, then restart your device. After the device is restarted, you can open the Settings app -> General -> Profiles & Device Management - Look for the presence of the "iTunes and App Stores Diagnostics Profile" in the list - to know that the profile is active.

Attach the device to a macOS Sierra (10.12) system, the open the Console app on the macOS Sierra. You can select your device in the log list on the left side of the console window. However, you should note that even with the log, there might not be enough information to analyse this issue.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

Hello Rich, Yes actually I said at the beginning that I was testing this with my device after the first release of the app, which is like a week ago. So on my own device that runs with iOS 10.3.2, I can replicate the issue. I was also curious about the receipt, so modifed the code I had on my server to keep a copy of the receipt it has received and the related response from the iTunes receipt validation server. When I check the response I can see that the related product appears on the receipt. Furthermore, the server gets as well the subscription status notification after the purchase. I haven't had a chance to try it with a device which is not registered to my developer account yet but I will today, even though I'm not sure if it will change anything. If you want I can provide the latest receipt validation response, or the notification receipt that I have mentioned above. Thanks

Hello,


Do you get the solution to this issue? if yes then please guide what will be the steps to resolve. As I am having the same case where on Production build when a user tries to purchase any subscription Storekit return 2 states "You are all set" & "Cannot connect to itunes store", due to failure state we are unable to get & send the receipt to be sent on a server.


If a user tries to purchase the same item again storekit show the message "You are already purchased" & when try to restore there will be no any valid transaction return. so a user is stuck here. he will not get any premium contents in the app. It is a very critical issue for us. please help to resolve.


Thanks

I'm also interested in a possible solution/workaround. I have a very similar problem in which i see the confirmation message that says that you're subscribed but the result from the server is a failure with "UserCancelled" reason which means that the user is stuck in the purchase screen till he tap the restore button. Production only.

I am facing the same issue now, have you fixed it.

Hi

Auto-Renewable In App Purchase Fails in Production
 
 
Q