How to find user's lost in-app purchases on iOS? - ios

So I followed the official manual and implemented this:
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1
user pays in app
app gets receipt info from Apple
this receipt info is sent to my server
my server verified receipt by calling Apple API and activates membership for client
The obvious problem is that 3. can fail. I have clients complaining they paid, they are sending me SS of the amount being deducted, but my server was never notified. And I have no way to find these users. Is there some CP where I can search by customer e-mail or transaction ID to check if this is Photoshopped screenshot or valid one?
Is there some API that can be called to list transactions by product and e-mail of client?
https://appstoreconnect.apple.com/ - Apple CP is useless for this

There is no method to correlate the customers details with your transaction details. Only Apple can do this.
My first suspicion is that you may have a logic problem in your purchasing process. If implemented correctly, a transient failure at step 3 doesn't matter.
You should:
Create your transaction queue observer a soon as your app starts. This will enable any pending transactions to be delivered to your observer
When you get a purchase transaction in your observer you verify with your server
Only once you have a response from your server that the purchase has been recorded successfully do you call finishTransaction.
This way if something goes wrong with your server or the app crashes the transaction is still pending in the queue.
If you are using auto-renewing and/or non-consumable IAP then I
strongly suggest you provide a "restore purchases" button in your
UI. This makes it simple for the user if something goes wrong or when
they move to a new device.
If you have users who claim that they did not get what they paid for then you can refer them to Apple App Store support who can refund the transaction.

If step three fails you can fall back to local verification and then let the user through for this session (or some number of sessions before you require it to succeed). Unfortunately local authentication is a pain in the ass because the receipt is encrypted. See this link for an example: https://github.com/andrewcbancroft/SwiftyLocalReceiptValidator . You can also report failures of step 3 to your analytics tool so you can see who is actually affected by this issue (obviously this only works if the analytic eventually get an internet connection.

Related

Apple In-App Purchase and Receipt Refresh

I have a side project and I recently worked on my receipt manager to make it stronger and to relies more on the receipt of the app rather than persistently storing a value after a transaction.
However, there are 2 main points which although I read Apple docs and other answers on the web, I'm still confused about:
1. When a user restore their purchase, does the receipt get refreshed?
I made several tests in sandbox, and I have seen that when restoring, the receipt gets refreshed, and when I verify the receipt through the iTunes server verification, it returns a JSON including the latest transactions. This is very helpful because even if I close/open the app, the app receipt is updated and I can always verify it without refreshing it.
However, in production, this didn't work. The app receipt wasn't refreshed after restoring purchases and my users got asked to restore purchases continuously. Can anyone answer on this point?
2. Does the refresh receipt request triggers an alert asking for the Apple ID's password in production?
From the previous point, I thought ok, I will force receipt refresh after a user restores their purchases. However, in development / sandbox, I get asked to insert my sandbox user's pass every time I try to refresh the receipt (although I could restore purchases without a password request before asking for the refresh). I read a lot about this and someone says that might not happen in production. Does any of you have a clarification on this?
Note:
I know that when restoring / purchasing I get back a transaction with the receipt, however, I need to use the App Receipt to verify transactions (and this is also what Apple says to do).
Thank you in advance.
1. Refreshing the receipt
In theory, calling restore purchases should get the latest receipt. In the instances where you are experiencing issues, take a look at SKReceiptRefreshRequest. Typically, I use this in production when a call to restore purchases has encountered errors.
Use it wisely, triggering that API can lead to showing the Sign In prompts for the App Store.
2. When is the user asked to sign in?
Sadly, I have seen this vary so I cannot give a definitive answer. More often than not, a call to restore purchases should not trigger a sign in. Explicitly using SKReceiptRefreshRequest will.
If the user is not signed in to the store, calling any Store API like attempting a purchase or restoring purchases could trigger the sign in flow.
What Apple Says
From the docs
Refreshing a receipt doesn't create new transactions; it requests the latest copy of the receipt from the App Store. Refresh the receipt only once; refreshing multiple times in a row has the same result.
Restoring completed transactions creates a new transaction for every transaction previously completed, essentially replaying history for your transaction queue observer. Your app maintains its own state to keep track of why it’s restoring completed transactions and how to handle them. Restoring multiple times creates multiple restored transactions for each completed transaction.
My Recommendation
Store the hash of the last receipt you used on device. You can use this hash to check against the latest receipt so you know if anything has changed. Whenever your App resumes you can always check if the current receipt hash is different from the last cached value.
Try and submit the receipt as soon as possible. Typically when the App has launched.
If a user tries to manually restore purchases, I would start with a call to restoreCompletedTransactions. This can trigger an App Store sign in but is often less likely. Most of the time this is enough as the receipt on the device is often pretty up to date.
If the user tries another restore purchases, OR if the call failed, then move to SKReceiptRefreshRequest to guarantee a fresh receipt.
When using SKReceiptRefreshRequest, I would recommend wrapping this behind UIAlertController. I normally show something that indicates it has failed and have a "Retry" button that uses the request. This will trigger a new store sign in.
Use restoreCompletedTransactions to get a playback of all completed transactions the device is aware of.
When a user restore their purchase, does the receipt get refreshed?
Yes, it should. But it also sounds like you're doing some server-side validation? If that's the case, you can send any receipt from the user to the /verifyReceipt endpoint to get the latest status. You don't need to send the latest receipt, since /verifyReceipt will also refresh it.
Does the refresh receipt request triggers an alert asking for the Apple ID's password in production?
There's no clear Apple documentation on this, but it definitely will if there's no receipt file present in the app (rare in production). But if you're doing server-side validation (see #1), then you can send up any receipt you have, no need to refresh it. So you're only refreshing the receipt if nothing is present, which will trigger the sign-in. Keep in mind a receipt file is not present on the device after installing in sandbox - only after a purchase. This differs a lot from production where a receipt file is generated after installation.
From what it sounds like you're trying to do, my recommendation would be to check if any receipt file is present at launch, send it to /verifyReceipt to get the latest status for the user and cache the result. You can do this on every app launch.
In a perfect world you're storing the receipt server-side and keeping it up-to-date there, but you mentioned side project so that sounds like overkill. However, an out-of-the box solution that correctly implements all of this and will scale with you - such as RevenueCat - is another alternative (Disclaimer: I work there).
After many tests and after I sent my app in production, I'm now able to answer my questions properly:
1. When a user restores their purchase, does the receipt get refreshed?
YES, this is immediate as for Sandbox, BUT the problem is that the receipt DOESN'T include non-consumable purchases.
This means in other words that the receipt will include the purchased subscriptions, but you won't find purchases of non-consumable products.
However, when the user restores or purchases, you get the transactions in return, and you can extract the non-consumable products, and store this info somewhere like UserDefaults or Keychain, so you can use them when the user opens your app.
For the rest, the best approach is to always validate and check the receipt when the app is opened.
2. Does the refresh receipt request trigger an alert asking for the Apple ID's password in production?
YES. For sure it does the first time.
Thank you to Daniel and enc for their answers that can still be useful.

iOS Handling local SKPaymentTransaction when using backend server

I'm creating an iOS app with an auto-renewal subscription. I've been reading a lot of tutorials and documentation but I'm a bit confused about how to handle certain scenarios.
Here's how my app works:
User installs app
User creates account within signup flow
User is asked to select a plan and pay within signup flow
The payment receipt is uploaded to my server and I activate their account in my database.
My server polls the /verifyReceipt endpoint on regular basis to renew the user's account or deactivate it depending on what the latest info from apple. (or use Apple's new Status Update Notification, both serve the same purpose to get me the latest subscription info on my server)
After a month when the subscription renews I know a transaction will appear on the SKPaymentQueue on the user's device. Because of this a lot of tutorials/documentation recommend having your AppDelegate implement the SKPaymentTransactionObserver protocol so that you can handle a transaction at any time.
But, I didn't use AppDelegate. I used the view controller in signup where the user picks their plans to implement SKPaymentTransactionObserver.
My reasoning is that since I'm getting info on the backend do I need to care about the transactions that will show up in the queue in the client each month when the subscription renews? Can't I just ignore these transactions, or will I need to call queue.finishTransaction on them?
I also read some things about restoring transactions when the user deletes the app and re-installs or gets a new phone. Again, do I need to worry about this? Because I should still know about the subscription on the backend and all the user has to do when they get a new phone is log in to their account for my service and it'll check the backend to see if their subscription is active.
I guess my larger question is: When you have a backend to handle IAP auto-renewal subscriptions, can you ignore some of the stuff happening on the client with the payment queue because that feature was built for apps that don't have a backend.
It's best practice to implement the observer immediately in the AppDelegate in case something goes wrong between the user being charged by Apple and you upgrading their account - if they close the app or it crashes you may lose that transaction.
Also, I think I've had cases where I forgot to call finishTransaction and that annoying iTunes login prompt kept popping up, not sure if that was a Sandbox only event though.
Like #Paulw11 said. Don't rely on the Status Notifications. At time of writing, they don't provide enough info to update a user's status, namely any sort of user identifier. Refreshing receipts from the backend is the way to go. If a new receipt is posted to the SKPaymentQueue (say on a renewal), you can handle it like any other receipt refresh for the user on your server.
Here's a good blog post that provides more detail on what should be happening exactly on the server: iOS Subscriptions are Hard
For your restore logic, you don't need to use the StoreKit restore method if you've implemented your own restore functionality through an account based system. If that's the route you want to go, you should definitely listen to SKPaymentQueue in the AppDelegate to avoid as many edge cases as possible where you could lose track of someone's subscription status. The good 'ol "Restore Purchases" button is a great way to fix some slightly flawed in-app purchase code :)
I guess my larger question is: When you have a backend to handle IAP
auto-renewal subscriptions, can you ignore some of the stuff happening
on the client with the payment queue because that feature was built
for apps that don't have a backend.
Don't ignore the payment queue. You can ignore the "Restore Transactions" if you have your own account based restore system.

Auto-renewable subcriptions: handle purchase when user is logged out

I've read a lot of SO answers about handling auto-renewable subscriptions with your own user login system, but there is an issue that I am still not sure how to handle: what to do when the transaction observer is triggered while the user is logged out.
Apple recommends to implement a transaction observer right in the App Delegate:
Adding your app's observer at launch ensures that it will persist
during all launches of your app, thus allowing your app to receive all
the payment queue notifications.
Say the user starts a purchase, but it doesn't complete immediately (for instance because it needs to be approved by their parents, or the app crashes, etc.). The user logs out and opens the app again, and since we are observing transactions in App Delegate, we might receive a transaction immediately when launching the app, before he logs in. We are then unaware of which user to associate the subscription to.
Two ideas:
Non-ideal: should I store the fact that there is a subscription pending and assume that the first user to log in will be the right one, and then associate it in some way to them once they log in? And if so, where would I keep the receipt? KeyChain, UserDefaults? This sounds pretty clunky.
Another way that sounds better: can I store some information about the transaction when it is initiated, and then use one of these fields to actually know exactly whose user's subscription purchase just finished?
(Not particularly relevant, but FYI I am using SwiftyStoreKit).
This is how I would approach this:
When the user tries to purchase a subscription, I first have them login or create an account.
Once the user is logged in, I send their appStoreReceipt to my server and store it there. I check and make sure the user is able to purchase. (At this point they should have the subscription they are trying to purchase)
Once I get a response from the server that the user should be able to purchase I go ahead and start the in-app-purchase
When the in-app-purchase process is done, I send the updated appStoreReceipt to my server and upgrade their account.
The problem you are worried about is what if the user gets done with step 3 but never gets to step 4.
Well, since I have their receipt stored on my server (from step 2), I can just ask Apple to give the latest version of their receipt, and if it shows up that they did purchase, I upgrade their account. You can choose when the right time is to do this check, it can be every time the app launched, or every time the user logs.
Hope this helps.

How can I debug a failed in app purchase?

From time to time, our users claim that they've made an in app purchase in our IOS application but they did not receive their product.
Our purchase flow goes like this:
Once the user purchases an item, the app makes a request to server including the transaction receipt.
Server saves this purchase with the transaction receipt in "new" state.
Server tries to verify this receipt on Apple servers.
If verification succeeds, the product is granted to user.
If verification succeeds, the purchase is marked as "Success".
If verification fails, the purchase is marked as "Fail".
On some occasions, we can find the purchase attempt in between one of these steps and we recover from there.
But sometimes we cannot find any purchase records relative to this user. I personally believe that most of our users are not lying but some bugs or network problems exist instead.
Is there a way I can debug these issues? Is there any information that I can request from the customer and compare that information to some records on iTunes pages or somewhere? Are we doing any mistake during our purchase process? What can we do better to prevent these issues?
Edit
We are verifying purchases by making calls to Apple Api (by using We are verifying purchases by making calls to Apple api (by using nodejs's iap_verifier).
We do not handle any duplicates since we had none so far but I do not think that that is relevant for this specific case.
I understand that this might be an Apple outage, a problem on the Apple side, a network problem or any other problem that are not related to our servers. But I still need a way to confirm a user's purchase when he or she approaches us claiming that he or she made a purchase but did not get their product. Does such a way exist?

Testing IAP subscription when renewed from iTunes or App Store app

I've been implementing auto-renewing In App Purchases and, using the info found here, have had little trouble the purchasing, renewing, & restoring transaction flows.
The problem I am having is finding a way to test when a user renews (after, presumably, canceling some time in the past) from their account page in either iTunes or the App Store app (Detailed here). I'm assuming, since those exist outside of the sandbox and when you log into one of them using a sandbox account that account is invalidated as a test account, there is no way to actually test this use case so I'm just looking for more information on the expected behavior to try and account for it.
I know that keeping a copy of the receipt around to validate will give the latest receipt as part of the JSON payload, I'm more curious for information on how StoreKit will handle this renewal. Will paymentQueue: updatedTransaction: fire with a new SKPaymentTransaction as soon as I add a TransactionObserver or will it stay silent until calling restoreCompletedTransactions and then the new SKPaymentTransaction will be part of that?
Yes, you are correct; paymentQueue:updatedTransactions: will fire, just as if you were making the initial subscription purchase.
You can actually test this since renewals within the sandbox kick in at a much faster pace (up to six times a day) as explained here (scroll down to the bottom under "The Test Environment").
Also when you verify the transaction with Apple's verification service right after you made a test subscription purchase, look for the expires_date field in the response, it will tell you when the next renewal will be triggered.

Resources