SKReceiptRefreshRequest causing App Store Review to be rejected

I have submitted a feedback through Feedback Assistant, but I want to share my experience here in case any other developers suffer the same experience we did:

Please describe the issue:

We implemented Receipt Validation through SKReceiptRefreshRequest according to https://developer.apple.com/documentation/storekit/in-app_purchase/choosing_a_receipt_validation_technique?language=objc  When beta tester and App Store Connect Review team download our app for the first time from TestFlight or the Review team internal channel, and when the app calls SKReceiptRefreshRequest, the user will be prompted to manually enter their Apple ID and Password.

The App Store Connect Review team would reject the submission because they deem the behavior described above as violations of "5. 1.1 Legal: Privacy - Data Collection and Storage" for attempting to collect user data or "Guideline 2.1 - Performance - App Completeness" when they refuse to enter the AppleID credentials and fail the validation. The App Store Review team rejects the submission and insists that we must change the app. We had to go through a 3 week back and forth appeal to get on the phone with a representative to eventually pass the review.

Desired Solution:

If the manual Apple ID credential entry behavior is intended, the App Store Connect Review team should be informed to not reject app submissions that exhibit that behavior. If the behavior is not intended, the SKReceiptRefreshRequest  method should not prompt behavior to trigger, and the documentation on Receipt Validation should be revised to reflect that. Otherwise, developers who follow the documentation will find submissions rejected, waste time on debugging, or even undo the implementation to pass the review.

Please list the steps you took to reproduce the issue:

You can observe the manual Apple ID credential entry behavior in any app that implemets SKReceiptRefreshRequest:

0. Ensure that the test device is not signed in with a sandbox account in Settings -> App Store -> Sandbox Account
  1.  Ensure you have access to a build in TestFlight

  2.  Download the app

  3.  Trigger the function call SKReceiptRefreshRequest

  4.  Observe the prompt

To replicate this behavior in an Xcode project:

0. Ensure that the test device is not signed in with a sandbox account in Settings -> App Store -> Sandbox Account
  1.  Use XCode to create a new Single View App project for iOS app with Objective C as language

  2.  Change the Bundle Identifier to an allowed identifier and add this code to main.m line 19

Code Block
SKReceiptRefreshRequest *refresh =[[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
[refresh start];

3. Run the project on the test iOS device
4. Observe the prompt


In legacy Tech Note 2413 In-App Purchase FAQ, there is the QA "After I install the development app with Xcode, the appStoreReceiptURL is nil. What is the proper way to handle this situation?" The answer is to indicate to the user that the appStoreReceipt may need to be updated and ask the user for permission to update the receipt. If the user presses OK, then when the authentication dialog appears as a result of calling SKReceiptRefreshRequest, the user understand why the app is asking for their user credentials. If the app simply makes the SKReceiptRefreshRequest without any user indication that this is happening, the appearance of the Authentication dialog looks suspicious.

When the user presses the "Buy" button, they understand that the Authentication dialog is going to be presented. The user should also know to expect the Authentication dialog if the SKReceiptRefreshRequest is going to be used.

Note, the app should be prepared to handle the case that the user cancels the Authentication dialog resulting from the use of SKReceiptRefreshRequest. If there is no App Store receipt the app should continue one. In such casuist expected that the user make a standard in-app purchase and that no free-trial, or promotional offers are available for the user.

Keep in mind that only in the sandbox, TestFlight and in App Review will there never be an appStoreReceipt on launch. When the app is installed to a users system from the App Store (or Mac App Store) there will always be an appStoreReceipt.

rich kubota - rkubota@apple.com
developer technical support CoreOS/Hardware/MFI

Thank you for the reply, Rich. The tech note 2413 QA is great, and I agree that it is a good practice to inform the user that the app intends to query the receipt and ask for confirmation before showing the authentication dialog.

May I suggest that on the [Verify Receipts documentation page](
https://developer.apple.com/documentation/storekit/in-app_purchase/choosing_a_receipt_validation_technique?language=objc), a Best Practice link can be added to link to the tech note 2413 QA so that developers can know to follow that? In a perfect world every developer will read all the legacy tech notes, but that is far from the reality, where developers like us would get rejected by the App Review team and had to go through a 3 week back and forth appeal to get on the phone with a representative to eventually pass the review.
Sadly, we went through another round of rejections yesterday for 4 of our apps, even though we now implemented a prompt to inform the user why we are updating the receipt before we query it with the message: "We did not find any App Store receipt on this device. Please enter the credentials of the Apple ID that owns this app to update the receipt."

The App Store review team attributed the rejection to "Guideline 2.1 - Performance - App Completeness" and gave the reason "We discovered one or more bugs in your app. Specifically, an error message triggered when tapping on a login method. "

In contrast, another app of ours was able to pass the review even though it also queries the receipt and serves the same prompt before the query.

It seems to me that some members of the review team is oblivious about the fact that it is normal for an app to not have an appStoreReceipt on launch because it is not installed from the App Store, and they are surprised when the app calls SKReceiptRefreshRequest and asks them to sign in.

What are the developers supposed to do when certain members of the App Store review team are out of touch with their own technical requirements?

We are in the same position. After several months of wrangling with App Store Review, we finally had our app (with IAPs) approved in January. We recently submitted an update (without any changes to StoreKit functionality), which was rejected for the same reason. Fortunately, we had some instrumentation which pointed explicitly to a failure to refresh the receipt. In retrospect, it seems that our initial success was a stroke of luck.

(1) I agree with zixtix. There is no consistency in the reviewers' process. Furthermore, they offer no explanation on their specific actions (e.g., did they press the Cancel button on the sign-in dialog?). This type of information would be greatly helpful.

(2) If I hadn't found this Forum thread, we would still be stuck. Apple needs to present these requirements more prominently in the overall Human Interface guidelines.

(3) As an independent developer, my primary job should be to bring my client's vision to reality, not navigate the arcane details of the App Store. Over the past 2 years (yes, on this single project) I have read through scores of tutorials, documents and threads to piece together a basic understanding of StoreKit and how to use it. It would be tremendous if Apple would provide a single source for everything relating to the App Store.

(4) Having to insert a considerable amount of code, simply to pass App Store Review, seems a waste of any developer's time (and client's money).

We got rejected by the review team again today for the same issue. I have also found a GitHub issue thread of devs facing the same struggle dating all the way back to 2014 (just Google search "GitHub Rejected for receipt validation"). It is disappointing to see that this inconsistency issue has tormented countless devs in the past and will probably continue to do so in the future because Apple just turns a blind eye.

Also, in case this helps: one of the devs in the GitHub thread did suggest the workaround of using a timer to avoid doing receipt validation on the first 2 weeks. This will bypass the review team's scrutiny, but it does expose the app to receipt exploit during that 2 week period. It is a bitter sacrifice to make, but we may end up doing it because it is more important to us to have reliable release schedule for our users, and I could not think of a better solution right now.

If I understand correctly, you are calling SKReceiptRefreshRequest on startup if there is no receipt present - correct?

Why do you do that?

If you have IAPs, and you want to be sure that previously-purchased IAPs are correctly enabled in the app, then you just need a prominent "restore purchases" button that calls ReceiptRefresh when pressed. The app should wait for the user to press the button.

If your concern is to prevent pirated copies of your app from running on hacked devices where a (valid) receipt is not present - sorry, you can't do that in this way.

SKReceiptRefreshRequest causing App Store Review to be rejected
 
 
Q