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.

In-App purchases is a great way for developers to upsell by giving away their app for free and then allow them to charge for features when users start using it. This freemium model has indeed worked very well for upselling your app in the AppStore. But unfortunately, there isn’t an Apple allowed way to allow reviewers to “download” your in-app purchases for free (like giving away promotional codes for your in-app purchases). So most developers again resort to the same “lite”, “pro” model.

After raising the issue to Apple, I even got a official reply that it’s not possible currently to allow reviewers to use your in-app purchases for free.

noinappurchases

However, developers’ creativity knows no bounds. In this post, I’ll present a method to allow reviewers to use your in-app purchases for free without having multiple versions of the same app on the app store. The source code for the same is also available royalty-free (as always) for using it in your own apps. Before diving in, it’s advised that you read through my previous tutorial on how to do in-app purchases

Focus

As I previously wrote, you can sell consumables, non-consumables and subscriptions using the in-app purchases model. This article is focussed primarily on consumables and non-consumables. However, you can extend it to subscriptions as well by adding a bit more server side PHP programming. This post however focusses on adding this feature to consumable and non-consumable items only.

A Quick Recap

By now, you must be knowing the flow of a in-app purchase request. If you don’t, read through my in-app purchases tutorial. As a recap,

1) You list the items available for sale from your store to the user.

2) When the user chooses a feature, you prepare a SKPayment object and add it to the queue.

3) Listen to notifications and record the purchases within your apps’ NSUserDefaults.

Or it’s even more simple if you had used my MKStoreKit.

The Idea

The idea here is to maintain a list of device IDs on the developer server and check whether the current device is exempted to use the feature without purchasing. If the device is allowed, rather than initiating a purchase, temporarily set the variables as if the transactions were made.

The complete source code, MKStoreKit V2.0 is attached at the end of the post.

You should be glad to know that, you don’t have to make changes to your calling code to add this feature.
In MKStoreKit, there is a function called buyFeature which initiates a in-app purchase request. In version 2.0, this function is modified to make a check first to your server (server code is also attached to this post) passing the UDID of the current device. If your server responds with a YES, it activates the feature temporarily without “actually purchasing”. This will enable your reviewers to review your app without buying your in-app purchase.

– (BOOL) canCurrentDeviceUseFeature: (NSString*) featureID;

This is the function that is called. Currently it checks the server mentioned in the variable ownServer (presently set to nil). To enable this function, you have to do the server side changes as explained in the next step.

Server Side Changes

Setup a database with two tables setup in your server. One table for storing a list of available products and another for storing new review requests as they come in.

The products table has the following fields
productid productName productDesc

The requests table has the following fields
udid productid email message status lastUpdated

You can use the SQL file attached to create these tables. Add a user to your MySql database and fill in the user id and password into the two php files.

Copy the server files from the ServerCode folder to some location like
http://api.mycompany.com/inapp/

change the “ownServer” variable in MKStoreManager.m to
http://api.mycompany.com/inapp/featureCheck.php

The featureCheck.php file checks the requests table for the UDID and the featureID. If the status of that row is 1, it returns YES. If your server returns YES for a particular UDID, the app activates the purchase for the current session without initiating a StoreKit purchase. Note that, this featureCheck happens everytime the app is started. Hence, if you deactivate a UDID on your server after the reviewer has finished reviewing, he will not be able to continue using it for free (Which means, you have actually given the reviewer a sneak-peek to your feature. Even if he likes it, he has to buy it)

How to send your review request?

There are atleast three ways of doing this.
One way is to ask your reviewers to send the UDID to you by email. You can ask them to use the Ad Hoc Helper by Erica Sadun You can then manually add it into the database using the AddDevice.html (present in the Server Code folder)
Second way is, You can “pretty up” the AddDevice.html and host it somewhere in your server. Send a link to this to your reviewers for filling their UDID/Product ID.
Third, as in my case, I’ve created a separate iPhone App for doing this. The only reason for doing so is, filling the UDID is very very cumbersome and error prone. If anyone knows a way to read the UDID of a device from a webapp, do let me know. (The big5 code didn’t work for me)

Going forward

I understand that all this server side setup is quite cumbersome. The server code isn’t even polished like the MKStoreKit. If Apple approves this method (which I will know in another 20 days), In MKStoreKit 3.0, I’ll probably throw of the whole server side code and replace it with a much elegant method by using a Google Spreadsheet. I haven’t yet digged around with the spreadsheets API. This way, you can implement the same feature without even owning a server :).

I might as well add in features to migrate your existing customers who use your pro version to the version with in-app purchases. Stay tuned!

Source Code (Much awaited)

MKStoreKit V2.0 Please use version 3 of this code instead. You can get it from the blog post below.
If you cannot successfully use this, you can hire me to do it for you. :)

Update 4: (8th July 2011) MKStoreKit 4.0, an updated version of the one presented here is available. Please check it out and use 4.0 instead of this.

Support me

Hourly rates of a iPhone developer is skyrocket high. I believe this code would have saved your coding hours by at least a day. You can consider supporting further development by funding me through PayPal.  My PayPal email is mugunth.kumar@gmail.com

Follow me on Twitter

  • Pingback: iPhone Tutorial – In-App Purchases | blog.mugunthkumar.com()

  • Very nice Tutorial. Thanks for this kind of tutorial I really find it interesting.

  • Pingback: iphone 3.0 changes | IPHONE()

  • We wrote our own "promo code" system for our App – as of right now the user my paste the promo code in a field to unlock – but for version 1.1 we are going to leverage the URL Schema for our app so the user has to click a link and the feature is unlocked.

    It works for us since our App is heavily server based – the promo codes are one use and it has worked well so far.

    • pilot34

      Hi! I’m searching such system now. Was your project published?

  • Jorge

    Hi!
    I'm using MKStoreKit v2 and it worked perfect from the beginning. The only thing is after completing a succesful purchase if I call [MKStoreManager featureXPurchased] I get the right response (YES) but if I close the app and launch it again, calling to the same method gives me a NO.
    Is it because I'm using a test account? Should I do something different?

    Thanks!

    • It should return YES. Can you elaborate more..?

      • Jorge

        Ok :-)

        I implemented a method, let's say featureXPurchased. I complete the purchase of feature X and calling

        [MKStoreManager featureXPurchased] = YES

        That's right cause I already purchased feature X.

        If I restart the app and call again [MKStoreManager featureXPurchased] the answer is NO and it's wrong cause I already purchased feature X.

        Should I initialize MKStoreManager in some way when the app loads so that i get the right response?

        • yes.
          You should initialize storemanager in your applicationdidfinishlaunch
          The class is singleton. So there is no init method. Just calle [MKStoreManager sharedManager] after your app starts. That should do it.

          • Jorge

            Worked like a charm!

            Thank you very much and congrats for your great work.

  • Jorge

    Hi!

    There's this delegate method to know when a transaction has finished with a purchase

    – (void)productPurchased:(NSString *)productId;

    Is there any delegate I can implement to know when the transaction has failed or user has cancelled it?

    Thanks!

    • Hmm… As of now, no.
      But you can add it if you want into the MKStoreKitDelegate.

  • Jorge

    Ok, I'll try to add it myself.

    It would be a nice update anyway.

    Thanks again.

    • Joao

      Did anyone managed to implement the failed or cancelled transaction?

  • My ipod 1G is my most valuable iPod, Pda and far more, also has has been ever since I bought it. It continues to work on the newest iPod system software program, and also any iphone app I need to have it to operate. I’m working with it to leave this comment at the moment. It’s safe to point out it’s a lot more than simply just an “excellent hobbyist system” — it can be an fantastic iPod

  • Pingback: In-App Purchasing at Under The Bridge()

  • Pingback: Store Kit In-App Purchase Tutorials | iPhone and iPad Development Tutorials and Programming Tips()

  • While Apple iPhone 3.1.3 firmware was minor terms of new features, it’s had the side effect of opening up a huge market for scam sites. These sites will promise you a 3.1.3 jailbreak for newer devices like the iPod touch 3G, or a baseband 05.12 software unlock. Do not fall in this scams , you can jailbreak iPhone 3G 3GS firmware 3.1.3 with Sn0wbraze V1.5 for free

  • Frios

    Thank you for this tutorial. I have a question about the MKstoreKitDelegate protocol. My productPurchased method never gets called and I may not be setting things up right. Please excuse my naivete.

    I implement the productPurchased: method in the MKStoreManager class. I set the .h file interface line like so

    @interface MKStoreManager:NSObject

    I then assign

    _delegate = self;

    in the sharedManager method.

    But in the provideContent method, the respondsToSelector call always fails. Any ideas what I am doing wrong?

    • Frios

      The interface line above dropped the angle bracket MKStoreKitDelegate close angle bracket

    • Frios

      Ok figured out that I had to call

      [MKStoreManager setDelegate:[MKstoreManager sharedManager]];

      Thanks

    • Frios

      Ok figured out that I had to call

      [MKStoreManager setDelegate:[MKstoreManager sharedManager]];

      Thanks

  • liverBird

    what about enabling the restore function that apple recommends to do? anyone done that yet?

  • kaustubh

    Hi, Thanks for your excellent articles. Maybe I did not see your comment , but please tell me if Apple approved this method of free in-app purchase.Thanks.

    • Yes apple approves this. They specifically called me regarding this and gave a go ahead!

      Sent from my iPhone

  • Alex

    Hello
    I am really new with this language at all but after your tutorials i feel good to start with IAP :) .
    Thanks. If you need some help with the PHP-Part of your app, hit me with a mail.

  • Jailbreak & Unlock Software For Iphone 3g 3.0 Os quik2unlockbycode Store UNLOCK AND JAILBREAK SOFTWARE FOR IPHONE 3G FIRMWARE VERSION 3.0 OS IPHONE 3GS NOT SUPPORTED STEP BY STEP EASY JAILBREAK UNLOCK INSTRUCTIONS FOR YOUR IPHONE 3.G with new FIRMWARE VERSION 3.0 OS UNLOCK YOUR IPHONE 3G OS FROM ….

  • Pingback: Introducing MKStoreKit – Version 3 | MK Blog()

  • Great job with MKStoreKit!

    Did you consider using promo codes vs UUIDs? I wonder if from a user experience it would be better to send reviewers a code vs asking them for their UUID which they enter when the purchase. The promo code could be setup for single use, multiple use for a given products (with an expiry date) or for use across a set of products.

    • James

      Do apple allow people to subscribe to non free apps using promo codes – don’t they insist that customers get the app via in app purchase ?  If they do allow it, do you just add a screen before the in app purchase – “Do you have a promo code, if so enter it here” and entering it in some way bypasses the in app purchase system ?

  • Anton

    Hi! I’m trying to use MKStoreKit. I have just one non-consumable feature. Seems like I made everything right and my app even offer to buy feature, but after the message that feature is bought my app is closed and iTunes Store is opened and after it my app crashes couple of moments after launching. Do you know what could be a reason for such behaviour?

  • Ken

    I wonder if the rules have changed, because I did this with my app and was rejected because “Apps that unlock or enable additional features or functionality with mechanisms other than the App Store will be rejected”

  • Christian

    Have you made any progress on migrating from a paid, ‘pro’ app to a free app with in-App purchases?  I currently have an app released with regular and lite version.  I want to discontinue the lite version, make the regular version free and add in-app purchases.  Not sure how to deal with the existing customers though…  Any insight would be great.

    Thanks,
    Christian

  • James

    We will soon have an app in the istore with in app purchase. We have been asked by third parties to give the app to their customers free, the third parties paying us a lump sum. Is this possible and do we have to share the lump sum with apple ?

  • I hit a bug with using this in v 4 of your excellent kit  onReviewRequestVerificationSucceeded was not passing back a parameter in connectionDidFinishLoading

    Here is the corrected version of the method.
    + (void)connectionDidFinishLoading:(NSURLConnection *)connection

    {

        NSString *responseString = [[[NSString alloc] initWithData:sDataFromConnection 

                                                          encoding:NSASCIIStringEncoding] 

                                    autorelease];

        [sDataFromConnection release], sDataFromConnection = nil;

    if([responseString isEqualToString:@”YES”])

    {

            if(onReviewRequestVerificationSucceeded)

            {

                onReviewRequestVerificationSucceeded([NSNumber numberWithBool:YES]);

                [onReviewRequestVerificationSucceeded release], onReviewRequestVerificationFailed = nil;

            }

    }

        else

        {

            if(onReviewRequestVerificationFailed)

                onReviewRequestVerificationFailed(nil);

            

            [onReviewRequestVerificationFailed release], onReviewRequestVerificationFailed = nil;

        }

    [responseString release];

        

  • pilot34

    There is a way to read udid from web app: through Profile Service Payload like Testflight. I can’t find ready to use system for in-apps promo-codes. Can’t believe, that there is no such project.

    • pilot34

      But now we haven’t got access to UDID in app, so system should ask some another id, maybe mac address..