I've just made iOS app with IAP to purchase virtual coins(Consumable). And the backend is Ruby on Rails. I have some concerns for now about the whole solution.
My current IAP process describes as following:
STEP 1 - iOS Client sends request to our backend and get product ids.
STEP 2 - Client retrieve product list through Store Kit with ids from step1
STEP 3 - User click buy and client sends a payment request to Store Kit
STEP 4 - When finish transaction the client send the receipt token to our backend
STEP 5 - Server verifies the receipt first to make sure it's a valid verification
STEP 6 - Server calls App Store to verify the receipt and deal with the exchange(Recharge account in DB)
STEP 7 - Client gets the response back and displays account balance
I worked on e-commerce/payment solutions before, and IAP takes the other way, though usually the payment gateway will send the receipt server-to-server for verification. And we always have message queue sort of solutions to make sure all transactions are stable and maintainable.
My questions are:
What's the best practice to verify IAP receipt? How to avoid the interruption of verification from our server? (e.g., User spend money and finish the transaction on the client, but fails when verify the receipt on server. No IAP restore transaction support.)
Apple IAP more likely a perfect client solution. Especially in my case, after user's payment, he wants to receives the coins immediately, and not the case we tell him the transaction is processing and his account will be fulfilled soon. To have responsive UX on mobile device is prioritized. Do you still use message queue stuff on server?
Thanks for your time.
You might find http://helios.io/ useful. It combines four useful iOS related gems, one of which is https://github.com/mattt/rack-in-app-purchase
Question 1: According to Apple's docs here, you should always validate receipts on the server. There are a couple of ruby gems that can help with talking to Apple's server on the backend:
https://github.com/gabrielgarza/monza
Hosted: https://getmonza.com
https://github.com/nomad/venice
https://github.com/nov/itunes-receipt
Question 2: This depends how concerned you are with fraud. If you don't validate receipts then you expose yourself to fraud. I would highly recommend validating on the server side.
Checkout this WWDC video that for a nice fundamentals on In App Purchases.
If I understand the question correctly, it looks like there's a gem called itunes-receipt that can be used for this. The gem can be found here: https://github.com/nov/itunes-receipt
There's also an NSScreencast addressing this issue, and the instructions within the show notes look fairly complete. http://nsscreencast.com/episodes/45-validating-iap-receipts
Related
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.
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.
We are using server side validation of the payment like so -
User makes payment.
Store kit API sends transaction receipt to App.
App sends base64 encoded transaction receipt to our server.
Our server calls https://buy.itunes.apple.com/verifyReceipt and validates the transaction recept.
User is marked as paid.
For a particular user, we didn't get the transaction receipt at the server, due to which the receipt couldn't be verified. We are guessing something went wrong in steps 2 and 3.
If there were connection problems at the time of sending receipt to server, the app retries again on subsequent app resume.
Now we have one missing transaction receipt and an angry user. How do you suggest we go forward? How can we prevent this in future? Are there any guidelines or best practices that we can follow to prevent such situation?
Thank you.
Based on my experience, the likely issues are
The base64 data got url-encoded along the way and so + and / got messed up - replace these with safer characters before transfer
The whole transaction is bogus.
The way to check for the second case is to look at your account and see if there is a matching purchase record. Unfortunately, the web site can be a bit difficult to review unless you have a low purchase volume.
The are two things you need in your code to correctly handle errors on your server or on, if it happens, Apple's end.
Do not call finishTransaction: until you have successfully communicated with your server (it wouldn't help in this case but worth noting)
Have a "Reload Purchases" button or action that calls restoreCompletedTransactions: on the SKPaymentQueue defaultQueue. For non-consumable/entitlement objects, this will resend all transactions with receipts that can be re-verified on your server.
If the problem you are facing is with non-consumables/entitlements, then the second item is the way out.
I'm building an iOS app, which provides a service which our website already provides. It's a subscription based service, where all features are enabled when you're subscribed. To get people to sign up for regular payments on the app, it seems like I'll have to go through Apple's in app purchasing API, with auto-renewing subscriptions. That's fine, but the problem is the service users will purchase through the app must also be available on the site. But reading through walkthroughs and the developer guide, it looks like the only way to process transactions is through the app itself, which then can be set up to pass the info to the server. But then I'm imagining this scenario:
User purchases a subscription on the iOS app, goes back and forth between using the site and the iOS app. On the day the user is to be charged, and the days after, they're using just the website, for whatever reason. The server doesn't know they've been billed though, and so features are disabled. In order for the server to find out the user has been billed by Apple, the user has to open the app on their iphone or ipad and sync it with both Apple and my server.
Is there any way for my server to ping Apple directly and get information about the purchases made for my app for a given user? I haven't been able to find anything; the two suggested layouts, with or without server, both use the iOS app as the sole communicator with Apple, aside from having the server verify that information is valid. If it's not possible for the server to get this information first hand, what other possible solution could there be?
Try to not use Apple's built in payment system, and risk getting rejected (with the app duplicating a service that's been available for years online, do we no longer have to use their in app purchasing system anyway?)
If a user is paying through Apple, have the server give them a generous buffer between expected payment dates and when features are disabled (makes the problem happen less frequently but doesn't solve it)
When their account seems expired to the server but it doesn't expect that, email the user a message saying they have to open the app or else their account will not be credited for time purchased (seems like a strange and not really great thing to have to ask a user to do in order to use the service)
This is about all I've been able to think up so far. Anyone have any advice on these solutions, or know of others, or know who I could talk to to try and figure this out?
Yes, you can check on the status of a user's account from your server. There are a few caveats:
First and foremost, Auto-Renewing Subscriptions are reserved for periodicals such as magazines and newspapers. If your app doesn't resemble those, Apple may reject it (as they did mine) and request that you use Non-Renewing Subscriptions (read: Manually-Renewing Subscriptions).
Second: This scenario would require you to store all receipts that you receive on the app, on your server.
Finally: I don't know how your username/password system works, but the user would have to login with the same credentials on your app as they do on your website.
Here's how you check the status of a user's account: Store at least one receipt per user on your server. When you want to check the status, follow Apple's procedure for Verifying App Store Receipts. Send them that one receipt and they'll respond with the latest receipt and the expiration date. Now you'll know, at any given moment, if a person's account is current or not.
I've got in-app purchases working just fine, and I'm going the server validation route. The server needs to know whether I'm in the sandbox or not, so for now I'm just sending it a "&sandbox=1" parameter. Of course when the full version of the app is out I won't be sending this parameter.
I'd rather not have this hardcoded in my app, as that will make testing difficult in the future, and it's one more (big) thing to remember to change before submitting builds to Apple.
Is there a way I can ask StoreKit if I am in the sandbox so I can then determine whether or not I need to send this parameter to my server? Alternatively, is there any other best practice for handling server validation?
Thinking about this more, should I just have the server always check the live system first, then the sandbox? If apple IDs are segregated between the live and sandbox systems then it wouldn't do any harm would it?
Thanks.
After a bit of digging I found this from Apple's Technical Note TN2259:
How do I verify my receipt (iOS)?
Always verify your receipt first with the production URL; proceed to verify with the sandbox URL if you receive a 21007 status code. Following this approach ensures that you do not have to switch between URLs while your application is being tested or reviewed in the sandbox or is live in the App Store.
So it looks like I should axe the &sandbox parameter completely and just do that. I really had to dig for this answer so I'm posting it here in hopes that someone else runs across it!
I encountered that very same problem, where my app was rejected because the "production" version of my app that I submitted was hardcoded to connect to a PHP script on my server that validates receipts with the real AppStore server (whereas my development build points to another PHP script that validates receipts with the sandbox server). However, after a few exchanges with Apple engineers, I found out that they use sandboxed user accounts to tests submitted applications, which explains why they got an error.
Instead of conditionally building my app to point to one script or the other, I will use a single script that tries the production server first and then falls back to the sandbox server if it receives the 21007 status code, as explained above!
Thanks a lot!
Always verify your receipt first with the production URL; proceed to verify with the sandbox URL if you receive a 21007 status code.
Unfortunately, the technical note fails to mention this is only valid for auto-renewing subscriptions!
As the In-App Purchase Programming Guide mentions below table 7-1:
Important The non-zero status codes here apply only when recovering information about a auto-renewable subscription. Do not use these status codes when testing responses for other kinds of products.
For non-renewing subscriptions, the production server does not return a status code, but a proper receipt.
In case you are forced to use non-renewing and implement your own subscription expiring logic, a possible solution is to send your app version along to your server, and keep track of which versions are in development at the moment, as such you can redirect to the sandbox.itunes server to verify receipts where appropriate, and mimic the x-minute expiring time of a subscription (as sandbox.itunes does for auto-renewing) for development on your server.