User has a subscription, but StoreKit reports no completed or incomplete transactions

Originator:JAltreuter
Number:rdar://48860805 Date Originated:March 13 2019
Status:Closed Resolved:April 1 2019
Product:iOS Product Version:12.0.4
Classification:Serious Bug Reproducible:Unable
 
Area:
StoreKit

Summary:

My app offers an auto-renewing subscription for premium features.  Recently a user wrote in to say he had purchased a subscription (providing screenshots of his receipt and the subscription page on his phone as evidence) but hadn't been granted premium features.  

Based on my investigation, unless the user fabricated the screenshots he sent to confirm his purchase, despite the fact that in the Settings App he can see an active subscription to my app, I am confident that he is now in a state where StoreKit is reporting no incomplete transactions, and also reports no completed transactions when he tries to restore them.

Steps to Reproduce:

I am unable to reproduce the error, however I received logs from this user that made it clear that the following had happened:

1. SKPaymentQueue.default().add(_:) was called with a subscription SKProduct
2. The transaction queue was updated with a single transaction in the purchasing state
3. The transaction queue was updated with a single transaction in the failed state (error message "Cannot connect to iTunes Store" so probably network issues).  FinishTransaction was called with this transaction.
4. The App Store started a subscription for this user and charged the user's account (we've received screenshots from the user as confirmation)
5. Some time later, the app exited and was launched again
6. Several hours after the app was launched, the user requested to restore purchases.  The first result of this request was that a transactionObserver was installed.  Because this was the first call to addTransactionObserver since the app was launched, any unfinished transactions would at this point be passed to the updatedTransactions function, but none were passed.
7. Immediately after installing the transactionObserver, SKPaymentQueue.default().restoreCompletedTransactions() was called.  Any finished transactions would at this point be passed to the updatedTransactions function, but none were passed.

Expected Results:
Since the App Store did process this user's subscription payment, I would expect one of the following to happen: 
- The transaction didn't complete on the client, so it would be passed to the updatedTransactions function when a transactionObserver was first installed after the app started.
- The transaction did complete on the client, so it would be passed to the updatedTransactions function when restoreCompletedTransactions was called

Actual Results:
No transactions were passed to updatedTransactions when the transactionObserver was installed for the first time after the app started, or when restoreCompletedTransactions was called.

Version/Build:
iOS 12.1.4

Configuration:

Comments

Apple Developer Relations response

Engineering has provided the following feedback regarding this issue:

As you described, properly implementing StoreKit APIs has resolved your issue.

By JAltreuter at April 4, 2019, 6:32 p.m. (reply...)

After speaking with Apple Developer Technical Support, it was recommended to me that I should install a transactionObserver at startup instead of waiting for the user to perform an action associated with purchasing a product. Making this change appears to have resolved my issue; the payment queues of the affected users were updated with their transactions immediately after startup.

I do still believe that StoreKit was misbehaving in the situation I described originally, so I'll keep this bug report open. But fortunately the bug no longer appears to be affecting my app.


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!