iOS In-App Purchases getting stuck in 'Purchasing' state - ios

I'm getting a weird error with in-app purchases for a project.
The client’s reporting that when they purchase an auto-renewing subscription, nothing happens; logging indicates that the app is correctly adding the payment to the queue, but it never gets any state change notifications on that transaction (i.e. it stays in the Purchasing state forever).
When the app launches, it registers as a listener to the payment queue (as per Apple's guidelines), and it sees the transactions from earlier still there, and still in the Purchasing state. For example, in one log that I've received, it's found that there are six transactions in the queue, and all of them are in the Purchasing state.
What I suspect is happening is that a payment has gotten stuck somehow in the Purchasing state, and has never transitioned to Purchased or Failed. As a result, other transactions are piling up behind it. However, my searching hasn't found any other reports of this kind.
The end result here is that there's never any UI that appears when the user, on some (but not all) of their devices, begins an in-app purchase.
My background reading suggests that it's potentially related to the following questions:
Multitasking and SKPaymentQueue I get a "stuck" SKPaymentTransaction with a transactionState of SKPaymentTransactionStatePurchasing
SKPaymentTransactionObserver not getting a callback on app switch
However, when I attempted to reproduce their behaviour (of switching away from the app in the middle of a transaction) by pressing the Home button to switch away, the button press was ignored by the system; I tried to force the issue by killing the app from Xcode, but that actually just kept the UI on screen while the app was removed.
This issue isn’t being reported by all testers, and I’m not able to reproduce it myself, but the log is very clear about what the purchases are doing on the devices encountering this problem. My question: under what circumstances could a purchase be ‘stuck’ like this?
For context, this is all on the sandbox environment, and on iOS 11.2+.

Related

in-app purchase consumable product "already bought"

My app only offers consumable in-app purchase products.
I can successfully purchase a product once but when I try to purchase it again, a popup "This In-App purchase has already been bought. It will be restored for free." appears and the first transaction is restored: the payment queue delegate is notified that a new transaction (with the same identifier as the successful transaction) is updated to the purchased state. I never get a purchased transaction with a new identifier and hence consider that the purchase failed.
Also, each time the app is put in foreground again or at startup, the payment queue delegate is notified that a transaction has been updated (as if it had never been finished). Even though the app properly finishes the transaction each time.
Side notes:
I guarantee that all purchased or failed transactions are finished (confirmed by the SKPaymentQueueDelegate removedTransactions method being called each time). I even tried to call finishTransaction from the main thread with no success.
Right before the subsequent purchase attempts, the transactions queue is empty.
I'm also pretty sure I was able to purchase several times the same product last week, with no change in code (same app version from Testflight).
I observe this behaviour when building the app with Xcode or when distributed via Testflight. It has not been published yet so I cannot check how it behaves in the Production environment.
Do you have any clue on what's going on? Could it be a side effect of using the Sandbox environment?
Thanks for your help,
The issue has disappeared yesterday, what other developers have confirmed.
It definitely was a Sandbox issue and it would have been nice if Apple engineers had confirmed it too...

iOS: previously purchased in-app product is restored while purchasing, but transactions are not updated

I have a Xamarin app for iOS with in-app purchases. During testing I've purchased one consumable product but did not finished the transction (crashed on error). Now if I reinstall the app it allows to purchase this product but of course restores it instead and show message "the product was restored for free".
This behaviour is expected, but UpdatedTransactions of queue listener was not fired. I could check the queue directly via StoreKit.SKPaymentQueue.DefaultQueue.Transactions, but I need to know when user interaction finishes (user presses OK in that message dialog). But no listener methods are fired.
As I understand from Apple docs there must be transactions' update after this. Maybe it is fired BEFORE I try to purchase. But then, should I check the queue manually before the purchase?
I'm not using Xamarin, but StoreKit is underlaying iOS feature, therefore it cannot work otherwise in Xamarin. The event must be fired earlier. As soon as you add transaction observer ([[SKPaymentQueue defaultQueue] addTransactionObserver:self]), you will receive update callback with all pending transactions. If this is done when app is launched (and it should be), by the time you reach purchase screen you will have transactions updated. For me, this is the trickiest part of IAP development as you have to handle them on application start as well as in all of the screens where you have them and callbacks can happen anytime.
Please also note, that this pending transaction might appear multiple times on the transaction list (I was mainly using non-consumables, so new transaction is added every time you relaunch the app without closing all the pending transactions). For that purpose I had component that is being added as transaction observer as soon as app starts, listens for updated transactions and stores them in instance variable for later use. Before starting a new purchase, I would check if there's a pending transaction with same product id and act accordingly.
Hope this helps.

How to get a full list of user's in-app purchases/transactions from iOS App Store?

Here is my situation: in my app I have a set of consumable in-app purchases. All purchases sent to server to be validated and activated. But there is a possibility to encounter situation when user performed a purchase (paid money) but something happens during purchase activation (lost connection, server error, etc). In result both app and server do not know about purchase (lets take situation that app lost purchase data for some reason). So I need to get user's purchases/transaction from app store.
I've tried [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
but it does not return consumable purchases.
Is there any way to get a list of all purchases/transactions from app store?
Two possibilities:
1) If the interaction fails with your server, don't finish the transaction (ie, don't remove the transaction from the queue). That way your transaction observer will get it again and you can try your server again.
2) Finish the transaction, but store enough info on the app (eg, NSUserDefaults) to try again with the server later. You can start a timer that will try again later.

StoreKit items getting purchased outside the app (redirected from my app to the App Store app)

I have an app using the non-renewing subscription model. It works great when using in-app purchase test accounts. However, on Apple's production servers, sometimes the user gets taken away from my app to the app store for one reason or another (sometimes it's because of updated billing info, sometimes it's to answer security questions). This is after the payment has been added to the queue and in Apple's own payment flow. Once the user hits the alert view option to go into the app store, my transaction Observer gets a transaction with the SKPaymentTransactionStateFailed state. That's fine. However, after the user updates their billing info or confirms their security question, they're asked (still outside the app) if they still want to purchase the in-app purchase. When that goes through, they are taken back to my app (which has closed itself), and nothing comes back from the transaction observer. The queue only gets updated with the purchased product when the list of products is retrieved. The observer registers for notifications before
So my questions are:
How do I handle purchases made outside my app (in the app store app)?
If there are purchases made before the app opened (but not completed), at what point does the queue get updated? I know that I should have the observer going at all times, but I want to avoid having the user purchase the item twice, not knowing they had already purchased it.
Through some trial and error, I seemed to have solved my problem
It turns our that I wasn't initializing my transaction observer for SKPaymentQueue soon enough. It needs to be initialized and added as a transaction observer in the application:didFinishLaunchingWithOptions: method, and no later. As for when the transaction comes in, the paymentQueue:updatedTransactions method of your transaction observer will get called with a purchased transaction the next time your app is active. It's important to note that your app may or may not close once for one reason or another when you're taken to the App Store, and if it does end up closing, the method will get called the next time the app opens.

SKPaymentQueue: What happens if I never call finishTransaction:?

My app will allow the user upgrade to Pro features via an In-App Purchase. I am validating the receipt data with my own server, and one the confirmation comes back from my server, I call finishTransaction:
What will happen if the validation fails (perhaps not due to piracy attempt) and I dont call finishTransaction: ? Will it stay in the queue indefinitely?
I always found that I got strange behavior afterward at some point, if FinishTransaction wasn't called.
The best option is to set a setting in StandardUserDefaults, etc. indicating "I bought this but it failed", and call FinishTransaction. Then when they restart the app (or foreground the app) you could check for this and send it to your server.
Also, if your purchase is not a consumable (i.e not buying coins or money), you can just call FinishTransaction and tell the user to activate RestoreTransaction from a button in your settings menu. This button is required by Apple for non-consumable purchases anyway.
Here's what Apple tells you to do: When you get a notification that an in-app purchase has been made successfully, then there are two possibilities: Either the user paid money to your bank account, or a hacker forged that notification.
So you send the receipt to your server, which checks the receipt. Make one hundred percent sure that it doesn't reject any correct receipts. When it tells your app that the receipt is valid and has been processed, your app calls finishTransaction. If it tells your app that the receipt is invalid, nobody knows exactly what to do, but calling finishTransaction won't hurt. If anything else goes wrong (your server crashed, internet went down etc. ) you do not call finishTransaction, because if you do, the customer loses their money without getting anything and will be quite angry. In that case, you will be told about the transaction again at some later time. Possibly on a different phone, if the user has two or more devices. So be prepared that a transaction could arrive at any time, even if your app hasn't seen any purchases on that device.
That's why it is wrong to store anything in user defaults. The device where the user presses the "buy" button isn't necessarily the one where the purchase ends up being made. (There are people with many iOS devices, even in a simple case a user might switch between their iPhone and their iPad).
if the request to the server failed, in what way will my app get notified again to retry the request to my server?
If you didn't finish a transaction then it stays in SKPaymentQueue. And when your app connects to SKPaymentQueue it receives all transactions connected with it.
So your app will need to finish all unfinished transactions.

Resources