Empty "in_app" array in the Apple receipt

Our app is offering auto-renewable subscription products only. One of our customers told us that she was having a problem to purchase a monthly auto-renewable subscription in our app.


In our server database, we found the customer's transaction receipt's payload which was used to post to the Apple's receipt verification API (https://buy.itunes.apple.com/verifyReceipt). Then we tried to post the receipt's payload to the API. After that, in the information we received from the Apple through the API we found out that the key of "in_app" in the "receipt" is an empty array, indicating that there was no product in the receipt and causing the customer receiving the purchase error in the app from our server. Most of our customers were able to purchase the subscription, so this was a rare case.


The customer told us that she was never charged by the Appe store after the multiple attempts she had made to purchase the subscription product, meaning that she did not purchase the subscription product successfully.


Our questions are:

1) Is it possible a receipt's payload being generated without a successful purchase of an auto-renewable subscription product?

2) How is the receipt's payload generated and sent to our server?


Please advise.

Thank you.

Post not yet marked as solved Up vote post of Leong Kee Down vote post of Leong Kee
10k views

Replies

>How is the receipt's payload generated and sent to our server?

Your app's code (code you wrote in the app) does this. Usually a call to updatedTransactions starts that process. It is possible your app responds to a user tapping a button saying 'update my subscription' and that causes your code to send the current receipt on that device to your server for your server to process. A server can send any receipt to the Apple servers and receive back the latest receipt so this is a method to update the subscription on a new device owned by the user.


> The customer told us that she was never charged by the Appe store

That certainly explains why there was no in app receipt field in the receipt.


>Is it possible a receipt's payload being generated without a successful purchase of an auto-renewable subscription product?

Yes, all apps have a receipt. If a user has not purchased the subscription then there will be no in app purchase field in the receipt.


Your customer needs to make the purchase. It is possible the Apple App Store is mishandling the customer's attempt to make the purchase. It is more likely the customer has never made the purchase either because - 1) they are unskilled at making the purchase or 2) they are trying to scam you. Regarding #1 - ask the customer if they have ever made another In App Purchase succcessfully from that device and if they are authorized to make IAPs from their Apple ID. Regarding #2 - ask the customer if their device is jailbroken or if they are using third party software to divert their IAP requests and push a false 'purchased' transaction into their updatedTransactions method.

Hi PBK, thank you for your reply.


There is an updated information here:

Our customer just informed us with the new information that she had actually been charged by the Apple store when she found the subscription confirmation email. She had forwarded to us the subscription confirmation email to prove that she had actually made the payment to the monthly subscription purchase.


From this case, the customer had actually gone through the normal process to make the payment to purchase our subscription product. The customer's subscription confirmation email shows that she has purchased the monthly subscription at Jun 10, 2018 and her subscription will renew unless she cancels by Jul 9, 2018. This indicates that her subscription is still valid until July 9, 2018.


We try to send the receipt's paylod of the customer's subscription to the Apple's receipt verification URL again and then we still get the empty "in_app" array in the receipt information we received from the Apple, there is still no product in the receipt.


Please advise.

Thank you.

In the return from the Apple servers you want to look at the "latest_receipt_info" field (or process the "latest_receipt" field).

https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1

Does your code allow you to have the customer refresh their receipt or restoreCompletedTransactions?

The customer purchased the IAP using an Apple ID. Are you sure that the receipt you have is from the same Apple ID?

> In the return from the Apple servers you want to look at the "latest_receipt_info" field (or process the "latest_receipt" field).

The returned information from the Apple server does not contain the "latest_receipt_info" field for the receipt.


> Does your code allow you to have the customer refresh their receipt or restoreCompletedTransactions?

After the customer has made the payment to the subscription purchase, the purchase receipt's payload will be sent to our server. Then, our server will process with the receipt and save the record to our server database. The customer will not be able to refresh their receipt if our server is failed to process their receipt. We do offer the in-app purchase restoring function but the function will only work for the records that are successfully processed by our server and saved into our server database.


> The customer purchased the IAP using an Apple ID. Are you sure that the receipt you have is from the same Apple ID?

We have confirmed that the receipt we have belongs to the customer. However, is there any way to know which Apple ID the receipt belongs to?


Please advise.

Thank you.

I don't believe there is any way of associating a receipt to an iTunes Account - but Apple must know the association somehow.


How does a user of your app transfer the subscription to a new device?

To use our app, a user needs to create a new account. After a user has purchased a subscription, our server will link the user's subscription receipt to the user's account and save the linkage into our server database. The user can log in to their account on any new device and our server will check the validation of the user's subscription.

Ok. But then you are stuck with this:


> In the return from the Apple servers you want to look at the "latest_receipt_info" field (or process the "latest_receipt" field).

The returned information from the Apple server does not contain the "latest_receipt_info" field for the receipt.


That indicates no IAPs. Unless the receipt is not the correct receipt - but you have no way of getting a new receipt from the customer.

We did more research and found out that Apple's Ask-To-Buy feature might cause the problem of the empty "in_app" array in the Apple receipt.


We asked the customer whether she was using that feature and she had confirmed with us that she was using the Ask-To-Buy parental control feature to approve the subscription purchase. So we concluded that the problem of the empty "in_app" array (no product) in the Apple receipt was actually caused by the Apple's Ask-To-Buy feature.


Is the Apple's Ask-To-Buy feature having such bug?

When the purchase was first made (by a user who needed permission) there was a call to updatedTransactions on their device with a 'deferred' transaction. I assume your code incorrectly sent the receipt to your server and that receipt had no IAP. That is normal procedure, no bug.


After the purchase is approved, the next time the user opens their app and launches a transactionObserver they will receive a call to updatedTransactions. That call will indicate a 'purchased' transaction and should trigger your code's correct response.


So perhaps the question is - what does it take to get your code to open a transaction observer?

How could I test the Apple's Ask-To-Buy feature by using a sandbox tester account?


In the Sandbox environment, I reached the stage of "Ask Permission" with the message of "A request to subscribe to < Subscription Name > will be sent to your parents." in the app and I tapped on the "Ask" button, after that nothing happened. How could I approve the purchase for the sandbox tester account?


I created a family group on the iCloud by using my real Apple ID with credit card information and invited a sandbox tester account. Then it sent an invitation to the sandbox tester account and I needed to use the sandbox tester account to accept the invitation. However, how could I accept the invitation for a sandbox tester account?


Please advise.

Thank you.

Do you have any solution? We have same troubles too.

I've provided a response as to whether the "Ask-to-Buy" feature can be tested in the sandbox environment - below. However as there appears to be an indication that the "Ask-to-Buy" feature is causing the empty in_app array issue, if anyone is able to demonstrate this issue with a production app, this is an important bug report to be investigated by the App Store server engineering team. The App Store server is where the appStoreReceipt is updated and is responsible for the contents of the appStoreReceipt. If a developer can demonstrate that the uupdatedTransactions delegate method is being called with the SKPaymentTransactionStatePurchased state, and the subsequent validation of the appStoreReceipt shows an empty in_app array, this is a bug report issue - contact me directly at my email address - rkubota@apple.com - but only if you can replicate this issue yourself using your production application - and I will provide specific information on how to get this issue investigated.

With regards to Ask-To-Buy support, there is no support for this function in the Sandbox nor in the TestFlight environment. “Ask to Buy” is not an API implementation, but a support process implemented by iTunesConnect which requires StoreKit interaction with a specific iTunes account. This support only exists in the production environment. I suggest that you submit an enhancement request - via the Apple Developer bug report web page - http://bugreport.apple.com


The primary reason for wanting this support is to be able to test the “Ask To Buy” purchase process. This makes sense, but there is no way to simulate this process. Instead, I can only advise you that in the “Ask to Buy” scenario - that the application must not assume that after a purchase attempt via the addPayment method, that the transaction will be either immediately successful or a failure. Instead, the “Ask to Buy” scenario means that the application won’t know until “later” (which could be hours later) of a formal response.


The simple means to support “Ask to Buy” - call the SKPaymentQueue -addTransactionObserver once in the didFinishLauchingWithOptions delegate method and never call the removeTransactionObserver method - make sure that the observer is always active.


Some additional notes - “Ask to Buy” purchases are only deferred for 24 hours. If the parent does not approve (or fail) the “Ask to Buy” purchase within 24 hours, the app can assume that the transaction will never complete. The application is not notified that the transaction failed due to inaction. It is also the responsibility of the app to track this 24 hour timeout period.


If a user initiates multiple “Ask to Buy” purchase attempts, each new request for the same item, overwrites the previous request and restarts the 24 hour timer. This is to prevent multiple purchase requests from accumulating.


The application will receive notice of the approved (or failed transaction if the parent denies the request) using the transactionObserver mechanism. This means that the app must either be re-launched, or moved from the background to the foreground. There is no call to the updatedTransaction delegate method if the app is active at the time of purchase approval.


rich kubota - rkubota@apple.com


developer technical support CoreOS/Hardware/MFI

> This means that the app must either be re-launched, or moved from the background to the foreground.


There is no documentation that moving from background to foreground will cause each transaction remaining in the queue to call updatedTransactions. The documents just state "StoreKit calls the observer every time your app is launched until the transaction is properly finished". Can you confirm that StoreKit will call your observer's updatedTransaction method each time the app enters foreground with an active observer? Can you confirm that StoreKit will call your observer's updatedTransactions method each time you execute an addTransactionObserver if there was no observer?


> There is no call to the updatedTransaction delegate method if the app is active at the time of purchase approval.


Are you sure of this? This is not the same response as in a purchase transaction. I'm surprised that they are different.

PBK - in response


You stated - "There is no documentation that moving from background to foreground will cause each transaction remaining in the queue to call updatedTransactions."

Response - this is correct and I'm trying to address this issue in the latest release of Developer Documentation. I think your reference above comes from another forum discussion posting. My intent was to state - "moving an application from the background to the foreground will trigger the transactionObserver to query the App Store queue for any incompleteTransactions pending for the applicationID and iTunes User. I can confirm this.


You then asked - "Can you confirm that StoreKit will call your observer's updatedTransaction method each time the app enters foreground with an active observer?"

Response - the transactionObserver will query the App Store, however, the updatedTransaction delegate method will only be called when there is an incompleteTransaction found.


You asked - "Can you confirm that StoreKit will call your observer's updatedTransactions method each time you execute an addTransactionObserver if there was no observer?"

Response - It's my understanding that calling addTransactionObserver when there is no active transactionObserver should result in a query to the App Store. What I think you are asking for verification - if an application calls addTransactionObserver, then later calls removeTransactionObserver, then the subsequent call to addTransactionObserver should result in a new query of the App Store. I've not seen this specific question answered by the StoreKit Engineering team. It would be easy to verify if one installs the StoreKit profile and captures the device console log when an app issues the sequence of calls to addTransactionObserver, removeTransactionObserver, addTransactionObserver. For each activation of the transactionObserver, there should be a request sent to the App Store - inAppCheckDownloadQueue. If the response is 1+, then the updatedTransactions delegate method is called.


What I have seen is that if an app calls addTransactionObserver more than once (without an intervening call to removeTransactionObserver), the subsequent calls to addTransactionObserver do not result in a check of the App Store.


The StoreKit engineering team design is that the addTransactionObserver call shall be made at application launch and that the removeTransactionObserver call shall be made in the applicationWillTerminate delegate method. You've indicated to me the utility of activating and deactivating the transactionObserver. If the mechanism of calling add/remove/addTransactionObserver does not do what is expected, I'd submit a bug report so that the issue can be documented and passed to the StoreKit engineering team.


You then asked about my statement - > There is no call to the updatedTransaction delegate method if the app is active at the time of purchase approval.


Are you sure of this? This is not the same response as in a purchase transaction. I'm surprised that they are different.

Response - in my complete response I thought I had stated that there are three instances that the transactionObserver is activated - maybe not in the response you reference


1. when addTransactionObserver is first called

2. when the app with the transactionObserver active transitions from the background to the foreground and

3. when an addPayment call is processed


I may have not have stated the third case in the response you referenced - which is my mistake.


rich kubota - rkubota@apple.com


developer technical support CoreOS/Hardware/MFI

Rich - thanks for the clarifications.


I was not suggesting that an app might remove and then addTransactionObserver to restart the queue. I was suggesting that an app might not leave a transaction observer added at all times but rather launch, go to background and later re-enter foreground without an observer; wait for the user to ask specifically request to check for an ask-to-buy approval having been approved; and only in response to such a request execute an addTransactionObserver.



Regarding the "three instances the transactionObserver is activated", you had written earlier:

"If a user initiates multiple “Ask to Buy” purchase attempts, each new request for the same item, overwrites the previous request and restarts the 24 hour timer. This is to prevent multiple purchase requests from accumulating.......The application will receive notice of the approved (or failed transaction if the parent denies the request) using the transactionObserver mechanism. This means that the app must either be re-launched, or moved from the background to the foreground. There is no call to the updatedTransaction delegate method if the app is active at the time of purchase approval."


Are you now writing that this is not the case or am I misunderstanding "transactionObserver is activated"?