In-App Purchases in iOS 6 Tutorial: Consumables and Receipt Validation
This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer. A while back on the weekly tutorial poll on the sidebar, you guys voted for a tutorial on In-App Purchases: consumables and receipt validation. As always, your wish is my command! :] This tutorial picks up where we left […] By Ray Wenderlich.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
In-App Purchases in iOS 6 Tutorial: Consumables and Receipt Validation
15 mins
Receipt Validation
When you make an In-App Purchase, you can't 100% trust that the response that comes over the network saying "everything went OK" really came from Apple without using a technique called "receipt validation."
When you make an In-App Purchase, Apple sends you back a special piece of data called a "receipt." This is a private piece of data that records cryptographically-signed information about the transaction. The idea is that for your app to be secure, you shouldn't blindly trust that a purchase completed - you should send the receipt to a special "receipt validation" server that Apple has set up to double-check that everything is OK.
The dangers of not performing receipt validation were proven pretty spectacularly recently. A Russian hacker developed an easy-to-install In-App Purchase hack that allowed users to receive almost any In-App Purchase for free - at least, if the app wasn't doing proper receipt validation.
The hack is a classic "man in the middle" attack. You configure DNS records so your devices is routed to the "hack" servers rather than Apple, and you configure a fake certificate on your device so the OS trusts the "hack server" you're connected to. Then, whenever you make a request to make an In-App Purchase, the request goes to the "hack server" instead. The hack server will always say "done and paid for!" so the app will unlock the content without knowing it's been had - for free!
This hack will no longer work on iOS 6. However, there are other variants hackers could employ in the future, so it's still a good idea to use receipt validation.
Note: For more information on the In-App Purchase hack described above, check out this article.
Note: For more information on the In-App Purchase hack described above, check out this article.
The No-Server Solution
Apple's official recommendation to perform receipt validation is to connect to your own server, which then connects to Apple's servers to validate the receipts.
For a number of reasons, this is more secure than connecting to Apple directly, and this is the approach you take in iOS 6 by Tutorials.
But in this tutorial, the focus is on the simplest and easiest way to implement In-App Purchases, and it would be a major pain to have to set up your own server just to validate receipts.
A lot of other developers feel the same way, and so they wrote code to connect to Apple's validation server directly from their apps rather than going through an intermediate server (despite Apple's recommendations). It became so common that Apple provided some sample code demonstrating a fairly secure way to verify receipts by connecting to Apple's servers directly.
In this section, you're going to integrate Apple's provided code into the app to validate receipts before unlocking the purchases.
Note: The original code was missing some pieces (such as the base64 routines) as well as some logic (like returning results to a caller), which I added in. Also, I cannot vouch 100% for the robustness of the Apple code, as it looks a little thrown together, so use at your own risk!
Note: The original code was missing some pieces (such as the base64 routines) as well as some logic (like returning results to a caller), which I added in. Also, I cannot vouch 100% for the robustness of the Apple code, as it looks a little thrown together, so use at your own risk!
Go ahead and download the resources for this tutorial, and drag the folder into your project. Make sure Copy items into destination group's folder (if needed) is checked, Create groups for any added folders is selected, and the In-App Rage target is checked, and click Finish.
The code requires the Security framework, so let's add it to your project. To do this, click on your In App Rage project root in the Project Navigator and then the In App Rage target. Select the Build Phases tab, scroll down to the Link Binary with Libraries section, expand it if necessary, and click the + button. Select Security.framework and click Add. At this point, your list of libraries should look like the following:
Then switch to IAPHelper.m and add the following import to the top of the file:
#import "VerificationController.h"
And add the following method:
- (void)validateReceiptForTransaction:(SKPaymentTransaction *)transaction {
VerificationController * verifier = [VerificationController sharedInstance];
[verifier verifyPurchase:transaction completionHandler:^(BOOL success) {
if (success) {
NSLog(@"Successfully verified receipt!");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
} else {
NSLog(@"Failed to validate receipt.");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
}];
}
This method simply calls Apple's (somewhat modified) code to verify the transactions, and either provides the content or not, based on the results.
The final step is to call this new method from completeTransaction: and restoreTransaction:, instead of providing the content right away. Replace those two methods with the following:
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(@"completeTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(@"restoreTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
Build and run, and your app should work as usual - but now with a great deal more security!
Note: If you are going to use this verification controller in your app, remember that it is currently configured to use the sandbox server (see the verifyPurchase:completionHandler: method). You will need to switch this to production before you release your app.
Note: If you are going to use this verification controller in your app, remember that it is currently configured to use the sandbox server (see the verifyPurchase:completionHandler: method). You will need to switch this to production before you release your app.
Where To Go From Here?
Here is the final sample project from the tutorial series.
Congrats - you now have implemented both non-consumable and consumable In-App Purchases, added the ability for users to restore transactions, and are validating receipts!
Like I mentioned in the previous tutorials, for a lot of simple apps this is more than enough. But if you want to take things even further and learn how develop a robust and extensible server-based system, check out iOS 6 by Tutorials!
I hope you enjoyed this series, and wish you best of luck with your consumable In-App Purchases! Just don't be too sneaky/evil, one Zynga is enough ;]
If you have any questions or comments on this tutorial or In-App Purchases in general, please join the forum discussion below!