Apple allows three types of purchases within the app and Apple terms them as consumables, non-consumables and subscriptions.
Consumables are products that are “consumed” immediately. This means, if the purchase is made today, and the user wants to purchase it again tomorrow, he will be charged again when he attempts a purchase.
Non-Consumables are features that are purchased exactly once. Apple automatically takes care of “remembering” those purchases and allows the user to purchase them again for free, just like downloading apps you already purchased.
Subscriptions are the most complicated part, They behave like non-consumables during the subscribed period and like consumables after that. You as a developer have to ensure that anything that is subscribed by the user is available across all of his iTunes synced devices when they are purchased from one device. Hence, do not lock in-app purchases to UDIDs. This might even get your app rejected. The StoreKit, as on date, doesn’t have any built-in mechanism to do it automatically which is why subscriptions are a bit tougher to develop.
One important point to note is that, in-app purchases cannot be used to deliver product updates. Changes to the binary has to be separately submitted. However, if you are a game developer, game data, maps, levels and other “data files” are allowed for in-app purchase.
In this post, we will focus on how to prepare your app for enabling features for the “pro” version from the “lite”. Or technically, we will focus on how to bring in, consumables and non-consumables into your app. We will leave the subscriptions part to another blog post as it’s quite complicated and involves some server side programming as well.

Preparing your iTunes Connect

To start with, in-app purchases, you need to do some ground work on your iTunes Connect account. A three step process that Apple thinks every developer should know. (Unfortunately I couldn’t find any official documentation for this)

Step 1:

First is to create an App ID and enable in-app purchases for that. This App ID shouldn’t have any wild card characters or else, the in-app purchases option will be grayed. I would always recommend to use a different App ID for every application you create.

Step 2:

Create provisioning profiles (Development and Distribution) using this App ID. Again it’s a good practice to create different provisioning profiles for every app. There are many tutorials on how to create this here and Apple’s own documentation here (This link will prompt you to login).

Step 3:

You need to create product references in your iTunes account. Each individual in-app purchase should be uniquely identifiable. Apple recommends using the reverse DNS notation, something like, com.mycompany.myiproduct.ifeature Before creating product ids, you need to associate it with a existing application in AppStore. If your app is not yet live, you can create a dummy, placeholder application, fill in the metadata (which you can anyway change it later) and check “upload binary later”.
To create a new in-app purchase, open your itunes connect and choose “Manage In-App purchases”. Choose the app for which you want to setup in-app purchases and click next. You should see a screen like this.
iTunes Connect in-app Purchase
The reference name is the name that appears during the in-app purchase prompt. Any name like, Levels 10 – 50 will be fine. The Product ID should be unique. This is used for reporting as well as within your app for requesting a purchase (more on this later). You can select the type as consumable or non-consumable. When you say an in-app purchase is consumable, your users will be charged everytime they purchase it. This is perfect for a radio app that requires users to pay for listening to a song everytime. If it’s a product feature, set it as non-consumable. Non-consumable products are purchased exactly once. When the user attempts to purchase it again, it will be delivered to him for free.
Type in the other required detail in this page and click save.

Step 4:

The fourth and final step is to create test user accounts. After you program the app, you might want to test the app. You can use these accounts to login to the App Store. The purchases will be processed as if it were real but no financial transactions will take place.
This completes your iTunes connect configuration. Take a deep breath. We have just started. A lot more to go.

Writing the StoreKit Code

Now that you have setup the iTunes account, let’s start by writing the actual code to interact with the AppStore and allow users to make purchases. For coding help, nothing beats the Apple’s official StoreKit programming guide. However, the guide has one bug that crashes the program. We will see how to circumvent it properly here

Step 1: Adding StoreKit.Framework

The first step here is to add the StoreKit.Framework to your project.

Step 2: Parental Controls

It’s important to check whether the iPhone/iPod doesn’t have parental control restrictions. When you try to initiate a purchase when parental controls are on, you might crash your app. The apple docs code seems to have an error here. The function, canMakePayments is a static method of class SKPaymentQueue and not a member function. The working code looks like this.
if ([SKPaymentQueue canMakePayments])
{
... // Display a store to the user.
}
else
{
... // Warn the user that purchases are disabled.
}

Step 3: Retrieving the product information and populating the UI

Now the you have designed a gorgeous looking view, you can show it to the user using, say presentmodalviewcontroller or any similar method. Do note that StoreKit doesn’t provide the UI to be displayed. It’s upto the developer to design the UI. The first step is to query your “In-App Purchases” and show the user, the available list of options. Retrieving the product information from AppStore is a couple of lines of code as illustrated here.
- (void) requestProductData
{
SKProductsRequest *request= [[SKProductsRequest alloc] 
initWithProductIdentifiers: [NSSet setWithObject: kMyFeatureIdentifier]];
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:
        (SKProductsResponse *)response
{
NSArray *myProduct = response.products;
// populate UI
[request autorelease];
}
The kMyFeatureIdentifier you pass to initWithProductIdentifiers is the uniquely identifiable product id you created in your AppStore account. Remember the reverse DNS product id? You CANNOT specify a wild card character here. So, if you have multiple products, you have to initialize the request object with a list of product using
SKProductsRequest *request= [[SKProductsRequest alloc] 
initWithProductIdentifiers:
[NSSet setWithObjects: myGreatFeature1, myGreatFeature2, nil]];
You can pass as many features as possible to setWithObjects and be sure to end the last object with nil.
Setting the delegate to self and calling the start method will invoke the “didReceiveResponse” delegate. The delegate will give you an array of products and the request itself. Use the array to populate your store UI and release your request here. (This is the same request you created initially).

Step 4: Adding a Transaction Observer

This is a very important step. You can do this as soon as your app is open or when you start a “In-App” purchase session. Do note that, in-app purchase requests are continued even if the user quits the app in between. So imagine a case, when user buys an item, but before the transaction is processed, he gets a phone call that interrupts everything. Though the actual transaction is not interfered (as it happens on Apple servers), your application will never know what happened. To ensure that you get all transaction notifications, (completed/ pending/restored), you have to register a class that receives the callbacks from AppStore. This class should implement and it’s delegate methods.
This is the code for registering the your store observer.
MKStoreObserver *observer = [[MKStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
(If you can’t follow anything here, just head below for the real source code, but it’s advised that you read through this, understand and then use the code in your app).

Step 4: Implementing the callback

Within your MKStoreObserver class, you have to implement the callback function paymentQueue:updatedTransactions:
This function will receive updates on the transactions as and when it’s made. Because your transactions take place even when the app is closed, you should be ready to receive these notifications as soon as you open the app. So the best place is to initialize it in applicationDidFinishLaunching or equivalent method.
Now in the updatedTransactions functions, handle the three types of transactions, purchased, failed and restored.
- (void)paymentQueue:updatedTransactions(SKPaymentQueue *)queue 
updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
// take action to purchase the feature
[self provideContent: transaction.payment.productIdentifier];
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code != SKErrorPaymentCancelled)
{
// Optionally, display an error here.
}
// take action to display some error message
break;
case SKPaymentTransactionStateRestored:
// take action to restore the app as if it was purchased
[self provideContent: transaction.originalTransaction.payment.productIdentifier];
default:
break;
}
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
}
Purchase and failed are seemingly straightforward. You will receive a restored transaction message when your app was quit before the transaction was completed. You should always do the “same” thing when your purchase is new or restored. If you want to charge your users for every download, then probably, set the in-app purchase to be a “consumable” item. But be sure to use that only for purchases that are really “consumable”. Like a live radio show or a podcast and not for unlocking additional levels. Users expect that a level they have unlocked will stay forever.
Three things to note here
1) You should remove the transaction from the payment queue after the transaction is complete. Otherwise, the transaction will be re-attempted, which is not what the users expect (and your app will most likely get rejected).
2) You should provide the content (or unlock the feature) before completing the transaction. When you receive the message SKPaymentTransactionStatePurchased, it means that the users’ credit card has been charged. It’s high time that you provide the feature.
3) You should not display an error when the transaction fails because the user rejected it. Display any error message only when,
if (transaction.error.code != SKErrorPaymentCancelled)</code>
Apple recommends you not to display an error, because that is not an error. The user has purposely cancelled the transaction (probably because the price was too high?). You can audit it and go on. But for heaven sake, don’t display an error like, “Unable to process transaction because user cancelled operation!”. It will be soooo Windozy…

Step 5: The actual purchase

Now that your architecture is ready, you can go ahead and initiate the purchase by calling the function below when the user clicks your “Buy” button on the UI.
SKPayment *payment = [SKPayment paymentWithProductIdentifier:myGreatFeature1];
[[SKPaymentQueue defaultQueue] addPayment:payment];
Optionally, you can add something like payment quantity for “consumable” items.
payment.quantity = n; // number of "items" that user wishes to purchase.
Finally, after providing the feature, you should “remember” that the user has purchased the app. Apple’s recommended way is to use NSUserDefaults, the same way you store your settings.

Testing your app

Alright. Now let’s test the app. Note that, the app cannot be testing from iPhone simulator. The StoreKit communicates with AppStore.app to complete transactions which is not present in the Simulator. So connect your iPhone and run the app.
Remember step 3 you did in iTunes connect? You created some test accounts? Well, they are used for testing your app. Before using that, sign out of AppStore by opening Settings.app -> Store -> Sign Out.
Start the app. (You should NOT sign into the AppStore with the test user account. It will anyway ask you to provide your credit card which we are not interested in)
Open the Store UI and initiate the purchase. You will get a prompt like, “Do you want to buy 1 “ABC feature” for 2.99$? Tap Buy. You will be prompted to login. Provide your test account login. The AppStore provides a secure connection to the iTunes account and notifies you whether the purchase was successful through the callback paymentQueue:updatedTransactions. When you use the test account, the scenarios will be exactly same expect that no one will be charged. These test accounts run inside a sandbox.
In case your app got quit by a phone call, the transactions continue and you will get a restoreTransactions the next time your app is opened. This is why you should start listening to transactions as soon as you open the app (not just when the user opens the store UI)
That’s it. I wouldn’t say its easy. But for developers who have some intermediate knowledge in iPhone development and Objective C, it shouldn’t be a big deal. Remember that when your app is free, you get 10 times more downloads (as reported by admob statistics). When people actually use your app, there is a high chance that they buy your in-app features. That’s upselling becomes easy. Given that this is a very strong business model for the already saturated AppStore, you should start incorporating this model into your code soon.

Source code

The source code, MKStoreKit, contains four files. MKStoreManager.h/m and MKStoreObserver.h/m. The StoreManager is a singleton class that takes care of *everything* Include StoreKit framework into your product and drag these four files into the project. You then have to initialize it by calling [MKStoreManager sharedStorageManager] in your applicationDidFinishLaunching. From then on, it does the magic. The MKStoreKit automatically activates/deactivates features based on your userDefaults. When a feature is purchased, it automatically records it into NSUserDefaults. For checking whether the user has purchased the feature, you can call a function like,
if([MKStoreManager featureAPurchased])
{
//unlock it
}
To purchase a feature, just call
[[MKStoreManager sharedManager] buyFeatureA];
It’s that simple with my storekit. The source code will be uploaded when this post is read by at least 1000 people. Please spread the word.
As always, all my source code can be used without royalty into your app. Just make sure that you don’t remove the copyright notice from the source code if you make your app open source. You don’t have to attribute me in your app, although I would be glad if you do so :)
Downloads:
Update: MKStoreKit 3.0, an updated version of the one presented here is available. Please check it out and use 3.0 instead of this.
Version 3: MKStoreKit 3.0 Please use version 3 of this code instead. You can read more about it here
Version 2:MKStoreKit V2.0
Version 1:MKStoreKit.zip

Troubleshooting

Despite all this, In-App Purchases remain a biggest and the most PITA situation for any iPhone developer. If you can’t get it work, check whether your “didReceiveResponse” delegate returns the product id you passed as invalid. You can confirm this by adding a NSLog statement inside the delegate. Double check if your invalid product id returned here matches the product id you created in iTunes connect. If they are same, check if your product id on iTunes connect says “Cleared for Sale”. For this, you have to provide a screenshot and “Developer Approve” it.
Another case is, if this is your first app, then chances are that, your “Paid Applications Contract” isn’t yet in effect. If this is the case, you have to wait till Apple approves your bank details.
If these two doesn’t work, you might have to wait for 12-24 hrs, till Apple propagates your iTunes connect information to all it’s servers. See the last line in this documentation for more details.