I have a consumable IAP in an app. The app has users and login/ logout functionality to switch between them. The amount of the consumable product associated with an user is stored on a server. So when the purchase is made a call to the server is made to "inform" it to update with the new amount of product. This call could potentially fail. From what I read what I should do there is to retry in background until it succeeds. I wonder how to handle the following cases:
1. The user logs out and kills the app. Then on start of the app SKPaymentTransactionObserver is called with SKPaymentTransactionStatePurchased because the transactions has not been finished. How do I know for which user of the app this transaction is. Should I store transaction ID-s alongside some user specific information to continue retrying to update with the tokens for this user. Or is there a better approach?
2. The state of the transaction is SKPaymentTransactionStateDeferred. Similar scenario as in point 1. The user logs out and either doesn't log in or logs in with different user. SKPaymentTransactionStatePurchased is received at some point and the app should update the correct user with the purchased product. The app could also be restarted in the process. Should again the transaction id be saved alongside some user information to make the update to the backend? Or is there a better approach.
3. The user logs out. The user logs in the app on another device. SKPaymentTransactionStatePurchased probably won't be called and the user will receive nothing. Maybe I should just block the UI until the purchase is updated on the server also but I worry of some big delays. Also if the user kills the app in the waiting they will be charged but won't receive anything. So can I do something?
Probably there are cases I am missing here, but these are the ones that come to my mind. I don't see similar questions very often and some specific handling is not mentioned so I wonder if things are not easier than I make them to be. The transaction observer is added in didFinishLaunchingWithOptions in AppDelegate and removed in applicationWillTerminate in AppDelegate.
You seem to be describing two separate systems so it becomes confusing when you say 'logs out'. The first is the app's interaction with StoreKit to purchase and receive a consumable IAP. The second involves your server and recording the purchase for the user. These are two different issues.
The purchase from StoreKit is controlled by the observer and a call to finishTransaction. If something gets interrupted in that process you don't call finishTransaction and the next time the app adds an observer the purchase will come through again. There are complications if multiple users share a device and share their Apple ID. It is very hard to differentiate between 'different users' who share an Apple ID. The only way to handle this is to continually monitor who is logged into your system and record that during paymentRequest. Use that information to object when they try to credit your server and don't call finishTransactions. (Or you could simply give your IAP credit to any one of them and let them fight over it. They should not be sharing their Apple ID.)
Once the app has obtained the purchase from StoreKit it needs to store it somewhere. If it gets interrupted during that store process it can either rely on not having called finishTransaction (as described above) and get the process restarted at a later time or it can take on the responsibility of retrying itself and dismiss StoreKit by calling finishTransactions. If you have multiple users then the complications mentioned above become double here. If you do not, then you can rely on StoreKit to restart the process from the beginning. I prefer to take over the process myself by storing something in NSUserDefaults or the files system (always calling finishTransactions from within updatedTransactions) and responding to that NSUserDefault/files entry each time the app opens. If there are multuiple users then you can store that information in NSUserDefaults and rely on it next time the app opens. That 'information' could be the user's ID on your system at time of purchase or paymentRequest although they will rarely be different. ('Sign' the entry in NSUserDeafults to prevent theft.)
Regarding the deferred state - you can ignore this. If the transaction comes through tehre will be a new call to updatedTransactions and it will be handled like an original purchase above.
Your #3 is confusing - the user is logging out of which system - if StoreKit then it will sit there awaiting the next time the user logs into StoreKit under their AppleID using that device. If it is your system then everything above still applies. Either you rely on not finishing the transaction and letting the purchase hit updatedTransactions or on the system you created yourself. Again, with multiple users you need to store some information at the time of the paymentRequest.