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.
Related
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...
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.
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.
I have a couple questions regarding the process of detecting that a subscription has been auto-renewed.
Currently I have my sandbox test environment all set up, I am able to purchase a 1 month subscription and all of that works nice and dandy.
My questions are as follows:
Do I need to verify the initial receipt (or really any subsequent receipts) of the in app purchases? If so, for what reasons?
Does my app get notified via a paymentQueue:updatedTransactions: call when the auto-renew has happened? Both in the sandbox environment and in the production environment.
So far, I have not seen the StoreKit notify me of a renewed subscription (in the sandbox environment) but I have also read that it is finicky and might not.
Any general tips with Auto-Renewable subscriptions that I should take into account?
Thanks for your help and time!
1. Regarding Receipt Validation
Receipt validation is your means of performing extra security checks before you unlock the users content. Think of a receipt as a lotto ticket. A client comes to you with the "Winning Ticket". Depending on how valuable you consider the reward to your Lottery you can either give said individual their winnings, or you can contact Apple and check whether the user actually purchased this ticket through them.
The client could have got this lotto ticket through some malicious means be it a jail broken device etc and so its really up to you to decide whether you want to verify the receipt or not.
2. Regarding Renewal Notification
The official documentation states the following:
After a subscription is successfully renewed, Store Kit adds a
transaction for the renewal to the transaction queue. Your app checks
the transaction queue on launch and handles the renewal the same way
as any other transaction. Note that if your app is already running
when the subscription renews, the transaction observer is not called;
your app finds out about the renewal the next time it’s launched.
So you should definitely be getting receipt renewal notifications in your app. Don't forget that you should always add yourself as the observer of StoreKit's paymentQueue in order for this to work, something like this in your app delegates didFinishLaunching would work:
//where YourObserver is an object that is alive for the apps lifecycle
[[SKPaymentQueue defaultQueue] addTransactionObserver:YourObserver];
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.