MKStoreKit started off in a pet project a couple of years ago and I wrote the first version in 2009.
Since then, it has seen tremendous adoption rates that, it has been the “go-to” framework for implementing In-App purchases in any iOS app today.

On iOS 4.3, Apple added a new type of subscription framework called, auto-renewable subscriptions.

Today, I’m adding this support to MKStoreKit along with several new features. If you have been following me on Twitter (@mugunthkumar) or on Github, you would have gotten the code a week ago.
Now that, I’m done with my testing it’s finally ready for some real-world adoption in your projects.

What’s New

  1. The main feature includes support for Auto-renewable subscriptions.
  2. The second important change is to move configuration related changes to a separate file,
    MKStoreKitConfigs.h and

    The plist contains a list of products you support and MKStoreKit automatically reads this and creates StoreKit requests on your behalf. This feature was partly inspired by a issue raised here by a contributor, dfabulich. But I didn’t like adding a datasource to MKStoreKit for various reasons. I think this method is cleaner than that. MKStoreKit now behaves like a standalone drop-in package.

  3. MKStoreKitDelegate removed in favour of Blocks
  4. Keychain support using SFHFKeychainUtils

    How to use

    The most important change to MKStoreKit is the use of a plist file instead of Macro strings for your product ids.
    The following screenshot shows the plist file organization.
    There are three main sections, Consumables, Non-consumables and Subscriptions. You specify your product inside each category based on how you added them on iTunes connect.
    After that, MKStoreKit does the rest.
    Non-consumable and Subscriptions are straight forward. I think I might need to explain a bit about Consumable support.

    Every consumable entry in this plist needs to be a dictionary with “Count” and “Name” defined. Imagine that your app is a fish tank app with eggs, plants and fishes as consumables. Fishes are going to be sold on a per-fish basis. However, you might be interested in selling eggs as discounted basket. To allow this business model, I’ve added a new entry called unique name to every consumable.

    If your products, for example,

    com.myfishapp.eggbasket50 = $2
    com.myfishapp.eggbasket500 = $15
    com.myfishapp.eggbasket5000 = $ 125

    are in fact same in your business (in this case they are all eggs, just that the count is different and subsidized for bulk purchases), you can define them on plist as individual consumable mapping to the same name.
    MKStoreKit will then treat all these products as same when remembering purchases.

    So instead of you remember how many fish eggs this person has purchased, you can leave that task to MKStoreKit. You get a nice wrapper around them and call these methods on MKStoreKit

    - (BOOL) canConsumeProduct:(NSString*) productName quantity:(int) quantity;

    to check for product availability and

    - (BOOL) consumeProduct:(NSString*) productName quantity:(int) quantity;

    to notify MKStoreKit to deduct the consumable’s balance.

    This support was originally added to MKStoreKit 3.5, but it didn’t support product bundles. MKStoreKit v4 adds support for this.

    Subscriptions expiry notification

    MKStoreKit automatically posts notifications when your auto-renewable subscriptions are renewed or failed validation (expired). Observe the following notifications,


    on your view controller or AppDelegate and take corresponding actions.

    A note for MKStoreKit 3 users

    MKStoreKit 4 uses keychain to remember purchases. This is partly to support In-App purchases on the upcoming operating system, OS X Lion. On iOS, all apps are sand-boxed and outside access to NSUserDefaults is not possible (unless the device is jail-broken). So remembering purchases on NSUserDefaults was *good enough*. On Lion, however, NSUserDefaults file is just another plist that could be edited on any text editor and technically “buy” our in-app feature. To circumvent this piracy, MKStoreKit 4 will use keychain instead of NSUserDefaults.
    As such if you have any consumables, they will not be automatically migrated to keychain store by MKStoreKit. You should do that part yourself. Non-consumables and Subscriptions doesn’t have this issue.

    Source Code

    The complete source is available on Github

    Demo Project

    There isn’t a demo project available because of the nature in which In-App purchases work.
    The only way would be to create a dummy project from my iTunes store account and create IAP. But that would add maintenance nightmare for me. However, since MKStoreKit is self-contained, you shouldn’t normally have trouble integrating it into the product.


    It’s licensed under Zlib as I feel that’s the most open license ever.

    A word on third-party components

    MKStoreKit uses the following third-party components.

    1. JSONKit by John Engelhart (BSD or Apache Licensed)
    2. NSData base64 encoding additions by Matt Gallagher (Zlib license)
    3. SFHFKeychianUtils by Buzz Andersen

    Final words

    I’m still making some touchups and final set of changes. But I think they will mostly be *cosmetic* (code refactoring). Feel free to pull/fork MKStoreKit and leave your feedback.

    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


Follow me on Twitter

  • guhthe


    Is there a new update to make this work for iOS7?
    Because for this version it’s always crash when I try to use it.

    • TrainerLeipzig

      Hey Mugunth,

      thanks for your great StoreKit. But it isn’t work under iOS7…
      Failed transaction: SKPaymentTransaction: 0x1655d950
      error: Error Domain=SKErrorDomain Code=0
      is the NSLog-Message. The sharedInstance find the products.

      • soch

        try to logout of the app store on iOS device and use a new test account

    • soch

      where is it crashing? See my answer for subscription active method.

  • fbartolom

    It is not clear what section to use for non renewing subscriptions. If I enter them into the subscription section I have a weird outcome. Should I handle them as consumable or what?

  • soch

    I was getting following error then I logged out of the iTunes account & created a test account for sandbox testing. It’s working for non-renewing subscriptions.

    Failed transaction:

    2014-06-20 22:46:29.131[530:60b] error: Error Domain=SKErrorDomain Code=2 “Cannot connect to iTunes Store” UserInfo=0x16582110 {NSLocalizedDescription=Cannot connect to iTunes Store}

  • soch

    Was crashing for MKStoreManager.isSubscriptionActive.

    Re-used the code from MKSKSubscriptionProduct.isSubscriptionActive

    Change the method under MKStoreManager.isSubscriptionActive.
    – (BOOL) isSubscriptionActive:(NSString*) featureId


    MKSKSubscriptionProduct *subscriptionProduct = [self.subscriptionProducts objectForKey:featureId];

    if(!subscriptionProduct.receipt) return NO;
    id jsonObject = [NSJSONSerialization JSONObjectWithData:subscriptionProduct.receipt options:NSJSONReadingAllowFragments error:nil];

    //NSData *receiptData = [NSData dataFromBase64String:[jsonObject objectForKey:@”latest_receipt”]]; — this key is absent in iOS 7.0

    NSLog(@”Dict = %@”,[jsonObject objectForKey:@”receipt”]);

    /*Dict = {

    bid = “com…..”;

    bvrs = “1.0”;

    “item_id” = 891968086;

    “original_purchase_date” = “2014-06-21 08:40:24 Etc/GMT”;

    “original_purchase_date_ms” = 1403340024866;

    “original_purchase_date_pst” = “2014-06-21 01:40:24 America/Los_Angeles”;

    “original_transaction_id” = 1000000114747842;

    “product_id” = “….”;

    “purchase_date” = “2014-06-21 08:40:24 Etc/GMT”;

    “purchase_date_ms” = 1403340024866;

    “purchase_date_pst” = “2014-06-21 01:40:24 America/Los_Angeles”;

    quantity = 1;

    “transaction_id” = 1000000114747842;



    NSTimeInterval purchaseSec =[[[jsonObject objectForKey:@”receipt”] valueForKey:@”purchase_date_ms”] doubleValue]/1000.0f;

    NSLog(@”%4.4f” ,[[NSDate date] timeIntervalSince1970]);

    //return purchaseSec + 365*24*60*60 >[[NSDate date] timeIntervalSince1970];

    if([[jsonObject objectForKey:@”receipt”] objectForKey:@”expires_date”]){

    NSTimeInterval expiresDate = [[[jsonObject objectForKey:@”receipt”] objectForKey:@”expires_date”] doubleValue]/1000.0;

    return expiresDate > [[NSDate date] timeIntervalSince1970];


    NSString *purchasedDateString = [[jsonObject objectForKey:@”receipt”] objectForKey:@”purchase_date”];

    if(!purchasedDateString) {

    NSLog(@”Receipt Dictionary from Apple Server is invalid: %@”, jsonObject);

    return NO;


    NSDateFormatter *df = [[NSDateFormatter alloc] init];

    //2011-07-03 05:31:55 Etc/GMT

    purchasedDateString = [purchasedDateString stringByReplacingOccurrencesOfString:@” Etc/GMT” withString:@””];

    NSLocale *POSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@”en_US_POSIX”];

    [df setLocale:POSIXLocale];

    [df setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

    [df setDateFormat:@”yyyy-MM-dd HH:mm:ss”];

    NSDate *purchasedDate = [df dateFromString: purchasedDateString];

    int numberOfDays = [purchasedDate timeIntervalSinceNow] / (-86400.0);

    return (subscriptionProduct.subscriptionDays > numberOfDays);