Is In-App Purchase Right For Your App?

Introduction

Developing your app to support in-app purchase will* increase your revenue.

* okay; may.

When Apple opened the App Store to developers in 2008 developers could sell software either with an up-front cost or for free. Apple rules state that each app must be functional. Demo (or time-limited) software wasn’t and still isn’t allowed in the store. There was no option to sell anything else from within the app. That came later, announced at WWDC in 2009. But only for paid apps. Many apps dropped in price to “tier 1″ ($0.99 USD), enabling them to offer in-app purchase. Then in October Apple removed the restriction, allowing free apps to offer paid content or features.


To be accepted into the store at all, even a free app needs to offer some functionality. Inside the app you can sell additional functionality, or “premium features”. Now you’re giving the app away it’s a statistics game: what percentage of your users will make purchases?

Why Use In-App Purchase?

Selling software is more difficult if your users can’t give it a try first. Reputable developers such as Apple can charge a higher price for theirs because people know the quality they produce. But what if you’re not widely known? Are enough people going to take a gamble on you? If they have to pay you a reasonable price probably not. Outside the App Store we’re used to 30-day trial software, we’ve had it for years. This allows people to see whether your software is worth what you say its worth without taking a risk up-front. But as Apple don’t allow demo software, they expect people to gamble their money on apps based purely on screenshots and user reviews. I use the number of reviews as a gauge for how many copies have been sold. Ever noticed how many reviews there are for higher-priced apps? And by higher-priced I mean $10. Not many. I believe this situation has contributed to the “race to the bottom” as developers lowered prices to a point where users would be prepared to lose the money just to try it out. If the software was decent, they got an excellent deal.

By allowing the app to install for free, users can experience the quality of your work as a developer without risk. It gives them confidence to purchase what you have to offer in-app. Even some negative reviews might not hurt your sales because people can judge for themselves without cost. You can price more fairly (to you) having taken away the risk to the user.

When In-App Purchase Works (and when it doesn’t)

I develop Easy Books, a bookkeeping app for iOS and Mac for contractors and small businesses. It’s free to download and uses in-app purchase for different elements of the app. Not everyone uses all the features. I could have produced three different apps, but I decided to make one app and charge separately for the features people want. It’s worked well for me because it keeps the cost down for users who only need basic bookkeeping, while allowing others to make use of invoicing and time tracking.

The model works really well if your app behaves in a fully functional way, so in the case of a game you may get the first 10 levels and purchase more later. Personally I don’t like to see it used in kid’s games though.

If your app does one thing, or is likely to be used only once, it probably won’t make good sense to add in-app purchase.

If you already have a paid app in the store, it’s probably not a good idea to add in-app purchase, unless you’re adding a new feature to the app. It isn’t possible to tell when your app was first purchased, so it isn’t possible to unlock features to users who paid the full price if you want to move to a free + paid in-app purchase model.

You can only sell virtual goods (see Apple Developer Documentation for StoreKit). So selling real-world products and services such as customer support isn’t allowed by Apple. Specifically they say “You must deliver a digital good or service within your application. Do not use In-App Purchase to sell real-world goods and services”.

The same revenue split applies when selling in-app purchase items. After taking off any VAT, Apple keep 30% and pay developers 70%, paid monthly.

As a developer you receive a daily report from Apple in iTunes Connect that lists the in-app purchase products and the numbers sold. It isn’t possible to determine who purchased what, or even whether the same user purchased more than one of your in-app purchase items. You can however break down sales by region. It’s worth mentioning this because many people falsely assume their relationship is with you the developer, whereas in fact it’s with Apple. This is most likely to become a problem if a user contacts you directly about getting a refund: developers have no way of telling whether the user has paid for anything and have no power to credit a user’s Apple ID. I recommend directing people to Apple’s support page because Apple offer refunds if the purchase was made by mistake.

Be aware that making your app free to download means any user can leave a review for your app without paying anything. You’ll get a lot more reviews this way, but it can skew your star rating because you’ll tend to get more negative reviews if it cost your users nothing (see Post Purchase Rationalisation).

Types of In App Purchases

Type iOS Mac Comments
Non-consumable Y Y Can be purchased once and will be restored to each device.
Consumable Y Y Can be purchased more than once, is not restored to other devices.
Non-renewing Subscription Y Just like a consumable, except the user is presented with the option to renew/extend if they’ve purchased it previously.
Auto-renewing Subscription Y Subscriptions are renewed by Apple, you can request an updated receipt from any of the past receipts and provide content as long as the latest receipt is valid.

There is also now a free subscription for Newsstand apps.

Okay, How do I Start?

The Apple Documentation is good and provides some code examples. Essentially you’ll need to follow these steps, and I’m assuming here that you are already an iOS developer, familiar with Xcode, App IDs, Provisioning Profiles and setting up a product in iTunes Connect.

  • Create an explicit App ID (not a wildcard App ID). In-App Purchases cannot be made from an app associated with a wildcard App ID.
  • Create the In-App Purchase item in iTunes Connect from your app’s detail page. The Product Identifier is what you’ll refer to in your code.
  • Write an app.
  • Validate the receipt received from StoreKit (optional).
  • Provide content/premium feature.

Writing the app is obviously the hard part. I can’t really help you there. For the in-app purchase code, see the example code shown in the Apple docs which I suggest following.

Reachability Class

While I recommend using the Apple example code, I also make use of the Reachability Class provided by Apple. Combine the call to [SKPaymentQueue canMakePayments] with a check on the reachability state. If your app has network connectivity, go ahead and request the products in your call to [[SKProductsRequest alloc] initWithProductIdentifiers:]. If not, the store is not available. The Reachability class will call you back if the network becomes available, and you can request the products then.

When the list of products is returned to you in the delegate method productsRequest:didReceiveResponse: you should keep hold of them because you’ll need each SKProduct object later when purchasing. I prefer to make the calls when the app starts (and each time it returns from the background) because you’ll have the current prices and product descriptions available when the user visits your purchasing screen. The user experience is better if they don’t have to wait for prices to appear.

Transaction Observer

You should register as a transaction observer so that StoreKit can call your implementation of the delegate methods when purchases are successful, products are restored and so on. When you register a transaction observer with StoreKit, do not be tempted to un-register. I used to register as an observer when the product list was returned, then de-register if the network became unreachable. Doing this on a Mac App causes the app to crash if the network is intermittent. And there really isn’t any need to de-register, so just register once and leave it at that.

Handling Purchases

When the user taps (or clicks) your buy button, update the UI to show a spinner or give some feedback that you’re in the process of purchasing the product they’ve tapped on. Call [[SKPaymentQueue defaultQueue] addPayment:payment] and wait. StoreKit will call your delegate methods. On failure, inform the user and return the button to its “buy” state because they may be able to fix the problem and try again. On success, your implementation of paymentQueue:updatedTransactions: will be called with an array of SKPaymentTransaction objects. For each one, determine the product identifier from transaction.payment.productIdentifier. It is your choice now whether to provide the content/feature straight away, or whether to validate the receipt first.

Validating Receipts

A Russian hacker set up a server, explained how to install a root certificate and change the DNS settings so he could provide access to in-app purchase items for free. Quite what he had to gain from this, nobody knows. One has to hope he will one day appear at NSConference to explain how clever it all was. Anyway, Apple responded by trying to shut down his system, and explained to developers what they should do in their apps. The vulnerability affects iOS 5.1 and earlier. Apps running on iOS 6 cannot be hacked using this method.

You may wish to validate receipts on your server, especially if you provide access to content at a cost to yourself, perhaps by licensing costs or access to web services. This is not trivial to do, and requires you set up a protocol between your app and your server. NSURLConnection is probably going to come in handy here. Assuming you have that set up, here are the basic steps to validate an in-app purchase receipt on iOS.

The transaction receipt is an NSData object that exists for each transaction passed to you in paymentQueue:updatedTransactions:. This data should be encoded in base64 format and passed to your server. Your server should follow Apple’s recommended steps by creating a JSON object containing the base64 encoded receipt and sending it to https://buy.itunes.apple.com/verifyReceipt or https://sandbox.itunes.apple.com/verifyReceipt. It is useful to try the production URL first, and if that fails to try the sandbox URL. When your app is in review, Apple test using the sandbox so trying both is a good idea to avoid rejection.

The response from Apple is a decode of the receipt (if valid). This gives access to some key properties:

  • bid. Bundle ID. Check this is your app.
  • transaction_id. Check this hasn’t been used for verification before.
  • product_id. This is your product identifier.
  • original_purchase_date. This is the date of purchase.

If everything looks good, return a positive response to the device. The device should then take this as a cue to unlock the feature.

Validating in-app purchases on Mac is slightly different. Apple documentation states the process is the same, but many of us were confused because there is no transactionReceipt property returned in the method paymentQueue:updatedTransactions:. I used one of my DTS tickets to find out more and wrote about what I found on StackOverflow. Essentially the process is the same, except that your Mac app’s receipt file is updated with each purchase. This binary file should be base64 encoded as before and passed to your server. Note the format returned by Apple is different from the iOS receipts, which contain one in-app purchase product. The Mac App Store receipt file contains information about the app itself, plus an array of in-app purchases. Also note the different names of the properties such as Bundle_ID on Mac App Store as compared to iOS App Store.

  • bundle_id
  • in_app[n]->transaction_id
  • in_app[n]->product_id
  • in_app[n]->original_purchase_date

If you’re interested in testing this process for Mac App Store receipts, follow these steps:

  • Find an app you’ve downloaded from the Mac App Store in your Applications folder.
  • Use “Show package contents” to find the receipt file in the folder Contents/_MASReceipt.
  • Start Terminal and use the command base64 -i <path-to-receipt-file>
  • Next type curl -d '{ "receipt-data":"<base64-text-from-above>" }' https://buy.itunes.apple.com/verifyReceipt
  • You should get a response from Apple’s server that looks something like this:
    {"status":0, "environment":"Production", "receipt":{"adam_id":"408921426", "bundle_id":"com.apple.Aperture", "application_version":"3.4.3", "download_id":"80001897676463", "in_app":[]}}

Unlocking Features

On iOS, users don’t have easy access to the file system and it’s safe just to store the fact that a product has been purchased by an entry in NSUserDefaults or as a file stored in the app library.

On the Mac, users can easily edit a text file, perhaps changing the user default for their purchases from N to Y. So if you want to lock this down a little, you could consider obfuscating the storage of this flag. Perhaps couple the value with a hash of the machine’s unique identifier, preventing the file from being simply passed to other users and copied to another machine. Or have a look at my initial modifications on AquaticPrime. I started this work to remove the hundred or so warnings when building with AquaticPrime on Lion. Apple deprecated the openssl framework APIs when linked dynamically, so it was either update the code to remove the warnings or statically link openssl. I opted for the former because I didn’t want to add to the size of the binary. The resulting AquaticPrime class now uses the Apple Security Framework instead and can validate AquaticPrime license files (but has problems producing them: that still needs to be done elsewhere).

Problems

When thinking about moving from a paid app to a free app with in-app purchase there are potential problems. You’re adding a lot of complexity.

This is especially true if you’re validating receipts on your own server: now you need to implement a transfer mechanism to pass the receipt to your server and handle the response. What if your server is down? Now you’ll need to store the receipt somewhere and implement a queue so they get passed to your server later. For the consumable product type you don’t have the option of asking the user to use the restore button if the purchase succeeded but your server failed to validate it.

There are other things that can go wrong too:

Didn’t I Purchase this Already?

Expect to provide a lot more support. Users have a hard time understanding why their purchases are not automatically available on all devices. Come to think about it so do I. It should be possible for Apple to call your delegate methods, passing past purchases through to your app without your user needing to do anything. But for whatever reason this isn’t how it works. You’ll need to add a restore button somewhere in your user interface. Users can tap that to restore their past purchases if they’ve purchased non-consumable products. They will be asked for their Apple ID password though, so you can’t perform the tap on their behalf.

Too Many Apple IDs

When a user installs your app they enter their Apple ID and password to begin the download. Any in app purchases must also be from this same Apple ID. People seem to be using more than one Apple ID more often, and this causes quite a few support issues along the lines of “I can’t seem to purchase add-ons”.

I Disabled In-App Purchases. Why I Can’t Purchase In App?

On iOS, users can disable in-app purchases globally on the device. This is a system setting under Settings > General > Restrictions. With this restriction setting turned on, you cannot purchase anything via StoreKit. But you also cannot restore past purchases either. Quite a few support discussions have eventually turned out to be due to the user turning this setting on.

Validate All Your Incoming Data

Of course that’s just good general advice. But I’ve made this mistake in the past and it bit me on 21 June 2012. If you were trying to use my app on that day it crashed shortly after launch. The problem was traced to the delegate method productsRequest:didReceiveResponse: and specifically the localizedTitle property of each SKProduct object in the array passed from Apple’s StoreKit. It suddenly started to be passed as a nil value, and since I was using it as the key in an NSMutableDictionary, both the iOS and Mac app crashed when they received the response from StoreKit. Apple fixed the problem at their end towards the end of the day. It was our busiest day at work; we just answered support questions and tried to keep everyone updated with what we were doing to fix it. The app had a number of one-star reviews that day too, and I believe it’s unusual, but Apple agreed to remove them. They also credited my DTS incident back to me.

Conclusion

By adding IAP to your app, you can start small and add features as time goes on. That should get you into the store sooner so you can start making some money to fund all those other features you wanted to add. But be careful to avoid bloating your app: most iOS apps are tightly focused on doing one thing well.

From my own experience, carving the app into modules of different (but related) functionality has allowed me to set the price for each feature so people don’t need to make a big purchase at the start. They may later have a need for – and decide to buy – something else from me.

Implementing in-app purchase for free apps is a great way to increase the number of downloads. The more people try it out the better for you. Even if your app isn’t right for someone, they may show someone else who later becomes a customer. By taking away the initial risk barrier, you go some way to level the field between you and established developers: if you take reputation out of the buying decision, you can compete on the quality of your app.

About the Author:


Photo of Mathew Waters

Mathew Waters

Mathew has had a passion for technology ever since he began to take things apart. Having left corporate life a few years ago, he now concentrates on developing for iOS and the Mac. He has recently discovered that the “i” in iDeveloper stands for indie, is easily excited by shiny new things, and enjoys flying R22 and R/C helicopters.

Twitter: @mathew_waters
App.net: @muddy
Website: Geode Software