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
    MKStoreKitConfigs.plist

    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.
    MKStoreKitConfigs.plist
    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,

    kSubscriptionsPurchasedNotification
    kSubscriptionsInvalidNotification

    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.

    Licensing

    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 mugunth.kumar@gmail.com

    Mugunth

Follow me on Twitter

Related posts:

  1. Using MKStoreKit in your apps MKStoreKit, as you probably know is a framework for implementing...
  2. In App Purchases: Troubleshooting Code=0 “Cannot connect to iTunes Store” (SKErrorUnknown) When using MKStoreKit or any other equivalent framework or regular...
  3. Migrating your code to Objective-C ARC Recently, Apple introduced several new developer stuff including Xcode 4,...

  • Phil

    Hi,

    Great work on building this IAP framework. For auto-renewable subscriptions of say 6 months, what  should I put in the days field in the plist file, since each months days may vary ?? 180 ?

    Thanks,
      Phil.

  • Jesse

    Seems like a great framework, and your addition of .plist loading for the features is a good idea, however it really should have the flexibility to be able to load that from any file, instead of one in the app bundle.  Being able to update your IAP content list by downloading it from a server without requiring an app update is a critical feature, and one that apple recommends in their documentation.

    Obviously modifying your code to support that is pretty easy now that you use a property list, but it’s puzzling why you’ve decided not to, either with a new delegate callback requesting the filename, or something similar.

  • Mag

    Is there a reason you’re not using a notifier for notifying isSubscriptionActive as you do when it’s invalid?

  • Leshinc

    Hi, Mugunth

    I am using MKStoreKit for Auto-Renewable Subscriptions.

    The system go to confirm shared secret step. After I confirm, the system will not entry onComplete block and onCancelled.

    How can I fix this issue ?

    Help me pls……

  • Martin

    if “kFeatureAId” is an auto-renewable subscription, the “onComplete” is never executed, regardless whether the purchase was successfull or not while “onCancelled” is executed it it failed.

    Any idea how to fix this?

    [[MKStoreManager sharedManager] buyFeature:kFeatureAId
                                    onComplete:^(NSString* purchasedFeature)
     {
         NSLog(@”Purchased: %@”, purchasedFeature);
     }
                                   onCancelled:^
     {
         NSLog(@”User Cancelled Transaction”);
     }];

  • guest

    it’s confusing that you named it “MK”StoreKit, since apple uses that as a prefix for Map Kit.  Just a thought :D

  • Steve

    Hello Mugunth

    I am very to to objective c, I have written first app, trying to implement in app purchase (an annual subscription) using your MKStoreKit. 

    I am having the same problem as Martin (1 week ago): in the sandbox, if “kFeatureAId” is an auto-renewable subscription, the “onComplete” is never executed, regardless whether the purchase was successfull or not,  while “onCancelled” is executed when the buyer cancels.

    The next time the app is started [MKStoreManager isFeaturePurchased: kFeatureAId] does not recognise that the purchase has been made and pro pmts the user to buy. When the user buys, a message comes up that you already have paid for this subscription.

    Does anyone have any ides what I could be doing wrong?

    • http://twitter.com/vbgoz Vibhor Goyal

      I am facing the same issue. Did you find any solution?

      • Steve

        No

  • Pingback: Cleverson Leal » Rescue City: Prólogo

  • RafCad

    Hi all,

    Im trying to use the MKStoreKit but im getting the message: “Problem in iTunes connect configuration for product”

    My app its not 100% ready, i didnt submited any code to itunesconnect. My app status is “Prepare for Upload” and my In-App Purchase status is “Ready to submit”.

    Its possible to test at this point? Or i have to do something else?
    My In-App Purchase its from non-consumable time, and the only thing i did its to add it on MkStoreKitConfig.plist. Is that correct?

    Any help would be appreciated.

  • Pingback: Cleverson Leal » Rescue City: Prologue

  • http://twitter.com/thiago_peres Thiago Peres

    Hello,

    Could you please please please make a step by step?

    In none of your posts I saw what the server files are for…

    You could make a wiki on github I would be happy to contribute.

    Thank you

  • Christoph

    Hello all, hello Mugunth,

    I have 2 questions. I implement die MKStoreKit latest version (4.1). It work’s great, but their are two questions to the auto-renewable subscription.

    First: If I buy an auto-renewable subscription in the sandbox an the subscription is inactive, how to renewable my subscription? I search the method. In the Apple Programming Guide I read that I need the restore-method for non-consumable products. How can I do this with MKStoreKit?
    I use this code: [[MKStoreManager sharedManager] restorePreviousTransactionsOnComplete:^ onError:^(NSError *error)] 
    I become more then one restored-status. After I restart my app the subscription all so it is inactive.

    Second: How can I solve the restore-problem with two different devices? I install the app at the first device and buy a subscription on this. After that I install the app on a second device with the same Apple ID. How can I restore my subscription on my second device.

    Thanks for your help.
    Christoph

  • Bu_walid_23

    how to dawnload it ?

  • Peter

    2 questions about the sandbox/buy thing:Where should NDEBUG be defined? I made a release build and the binary contains the sandbox-adress instead of the buy-address.Is the kReceiptValidationURL relevant for non-consumables? if not, I don’t need to set NDEBUG?Thx in advance

  • Marc Cramdal

    Regarding the migration from older versions, you are saying: “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.”

    However, it looks like there is no code that automatically migrates non-consumable purchases? Am I wrong?

  • Karl

    I don’t know if its apples new policy, but my app has now been rejected for the 3rd time because of this.

    apparently, apple does not like transferring the purchase data to a server, even though I’m not even using it.

    first was my fault.

    second rejection was because in the “verifyProductForReviewAccess” function, you pass the UDID.. apparently this is a no-no, sensitive information. 

    so i comment everything out.

    then they say in the “verifyReceiptOnComplete” you are passing the receipt.. they don’t like that either, because it contains sensitive information.

    so now that i’ve blown more than an entire month in review, and now its xmas shutdown, so I’m TOTALLY out of luck to hit the xmas season. I’m taking this out all together, and just charge for the app.

    be warned anyone using this, stay away unless you have months to debug it.

    • Anonymous

      Apple has changed their policies recently. Hold on, I’m updating this MKStoreKit

      • Vincent

        sam here ! how do you plan to fix this ? thanks in advance.

      • Karl

        I think you will have to remove all the server side stuff. That seems to be what they are complaining about.  Im getting ready to resubmit without the in-app purchasing. hopefully ill re-integrate this on an update someday.

        but right now, its a guaranteed rejection after 3 weeks of “in-review” if you want to use it.

        • Giles

          is this fixed yet?

          • http://3DTOPO.com 3DTOPO Inc.

            The silence is killing me!

          • Doug

            looks like MKStorekit sends everything in the clear between the server and the iOS device, meaning anyone sniffing would see all the end user’s information.  

            I would imagine the iOS code should send an encrypted blob to the server (since we are not using https due to costs), where the php code should unencrypt. 
            i.e. http://stackoverflow.com/questions/6641693/iphone-encrypt-function-that-would-be-decrypt-on-php-server

            That may be all they care about, but having dealt with apple, they never come out and say how you can SOLVE their complaints, and they only hint as to why they rejected.

            I may fork and add encryption  but I would of course prefer to see Kumar do it :)

          • Anonymous

            If your server is https, encryption happens automatically

          • Doug

            of course.  Are you seeing review rejections from users that had employed https?  I just assumed most people (myself included) want to avoid the cost of https certificates. 

            Apple likely is a bit sheepish providing uuids to insecure servers that can be scraped.  I suggest jumbling the uuid into a large  unique blob as a way to appease them and keep the excellent server feature alive.

            (assuming security is the heart of their rejection)

  • Marin

    Hey Mugunth, awesome code – I’m been using code from the mkstorekit since its very first version :) Just got ver 4 from github and I was wondering whether you forgot to add “return uniqueID;” at line 66 in MKSKProduct.m ? There’s a warning that +(NSString*)deviceId doesn’t return a value when compiling for iOS. Have a look :)

    best, Marin

  • Marin

    If there’s something else coming in from iCloud besides NSString there’s a crash, because SFHFKeychainUtils expects an NSString as the password parameter and calls “dataUsingEncoding” on it. So when some other value comes in from iCloud (NSArray for example) the code crashes there … I added : MKStoreManager.m : 86
            if ([(NSObject*)obj isKindOfClass:[NSString class]]) {

  • Pingback: AppDev by egarce - Pearltrees

  • Dan

    [[MKStoreManager sharedManager] restorePreviousTransactionsOnComplete:… required for subscriptions to work?

    I have implemented auto-renewable subscriptions with MKStoreKit 4.1. I find that I must run restorePreviousTransactionsOnComplete on every start of the app or the subscription comes up as inactive.
    The app is always starting up with:Receipt Dictionary from Apple Server is invalid: (null)

    Do I need to implement anything else in order to remember the subscriptions so that they are not inactive?

  • Gianluca

    Hello Mugunth,
    I was using your MKStoreKit in an app and have found a weird problem. I tested it on different devices, seems that in my case the purchasing process works well on iOS5, while bad on iOS4.
    Summarizing the test conditions, in both the cases (iOS4 and iOS5),
    a) we ran the app after being logged off from the AppStore.
    b) the app has 1 non-consumable product so the situation is very simple.
    c) keychain and iCloud don’t contain the product related data, as the product has been created from scratch on iTunesConnect before this test (code aligned)
    d) we didn’t confirm the purchasing in the iOS Alert View (so at the end of the test the product is not purchased)

    I did saw the following:
    - On iOS5 everything works correctly, so we can see the iOS Alert View which asks to confirm the purchasing (tested on two devices)
    - On iOS4, after pushing our “Buy” button, the purchasing process starts but nothing happens. What I see is that, inside the productsRequest:didReceiveResponse: of MKStoreManager, the product featureId is inserted in the invalidProductIdentifiers, but without a known reason.

    I don’t know how often this could happen, but in this case the result is that the addToQueue: method returns without completing the purchasing (in fact index == NSNotFound), and there is no way to understand that an error occurred from the caller/UI point of view.

    Question is: do you have any suggestion that could help us on identifying the reason of the problem on iOS4? Maybe it’s our fault.

    Anyway, I would suggest two changes:
    1. to change the addToQueue: method into
           -(BOOL) addToQueue:(NSString*) productId, in order to return something in case of error
    2. to add an onError block to buyFeature: method, in order to give the UI view controller an opportunity to manage the error.

    Thanks a lot in advance (and sorry for the long message)

  • VSDesigner

    I used the older version and it worked just great. Wanted to use 4.0 for the support with Auto Renewable Subscriptions and I have everything setup and everything seems to work just great until the receiptData is verified by Apple. I keep getting a 21004 error response. Which indicates ‘The shared secret you provided does not match the shared secret on file for your account.’, which is not the case.
    I can take that same exact receiptData and share secret and run it via the php script from the older version of MKStoreKit and it comes back success with code 0.

    In the MKStoreKitConfigs.h, I have, tried it with & without . with gets 21004, without gets no response.
    #define kSharedSecret @”"

    • Anonymous

      Are you using the shared secret from your iTunes connect?

      Mugunth
      Author | Developer | Trainer
      iOS 5 Programming book
      iBooks: http://mk.sg/ibook
      Amazon: http://mk.sg/ios5book

      • VSDesigner

         Yes I am.

        I use the same share secret in a php script and it works just fine.

  • Dan

    Thanks for MKStoreKit – it’s really great!

    I was wondering under what circumstances do I need to show a “Restore Transactions” button?
    the transactions are now stored in iCloud but there are plenty of devices not on an iCloud enabled iOS version. I imagine everyone will not have it set up either.

    Apart from when isFeaturePurchased returns YES – is there any other time when I can safely hide the “restore transactions” button?

    thanks!