Git Product home page Git Product logo

mkstorekit's Introduction

#MKStoreKit

This is version 6.1 of MKStoreKit. iOS 8+ only. MKStoreKit 6 is a complete revamp is not API compatible with previous versions of MKStoreKit. Refactoring should however be fairly simple.

The code base is still in early beta. Use with caution

The library contains just three files, MKStoreKit.h/m and MKStoreKitConfigs.plist The MKStoreKit is a singleton class that takes care of everything. Just drag these four files into the project. You then have to initialize it by calling [[MKStoreKit sharedKit] startProductRequest] in your application:didFinishLaunchingWithOptions. From then on, it does the magic. The MKStoreKit purchases, remembers and even handles remote validation of auto-renewable subscriptions.

Features

  • Super simple in app purchasing
  • Built-in support for remembering your purchases
  • Built-in receipt validation (remote)
  • Built-in virtual currency manager
  • Built-in Hosted Content Download Manager

Sample Code

The Sample Code shows how to use MKStoreKit.

###Config File Format MKStoreKit uses a config file, MKStoreKitConfigs.plist for managing your product identifiers. The config file is a Plist dictionary containing three keys, "Consumables", "Others" and "SharedSecret"

Consumables is the key where you provide a list of consumables in your app that should be managed as a virtual currency. Others is the key where you provide a list of in app purchasable products SharedSecret is the key where you provide the shared secret generated on your iTunesConnect account.

###Initialization

Initialization is as simple as calling In Objective-C

[[MKStoreKit sharedKit] startProductRequest]

In Swift

    MKStoreKit.sharedKit().startProductRequest()

A sample initializiation code that you can add to your application:didFinishLaunchingWithOptions: is below In Objective-C

  [[MKStoreKit sharedKit] startProductRequest];
  
  [[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitProductsAvailableNotification
                                                    object:nil
                                                     queue:[[NSOperationQueue alloc] init]
                                                usingBlock:^(NSNotification *note) {
    
    NSLog(@"Products available: %@", [[MKStoreKit sharedKit] availableProducts]);
  }];
  
  
  [[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitProductPurchasedNotification
                                                    object:nil
                                                     queue:[[NSOperationQueue alloc] init]
                                                usingBlock:^(NSNotification *note) {
                                                  
                                                  NSLog(@"Purchased/Subscribed to product with id: %@", [note object]);
                                                }];
  
  [[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitRestoredPurchasesNotification
                                                    object:nil
                                                     queue:[[NSOperationQueue alloc] init]
                                                usingBlock:^(NSNotification *note) {
                                                  
                                                  NSLog(@"Restored Purchases");
                                                }];
  
  [[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitRestoringPurchasesFailedNotification
                                                    object:nil
                                                     queue:[[NSOperationQueue alloc] init]
                                                usingBlock:^(NSNotification *note) {
                                                  
                                                  NSLog(@"Failed restoring purchases with error: %@", [note object]);
                                                }];

In Swift

    MKStoreKit.sharedKit().startProductRequest()
    NSNotificationCenter.defaultCenter().addObserverForName(kMKStoreKitProductsAvailableNotification,
      object: nil, queue: NSOperationQueue.mainQueue()) { (note) -> Void in
        print(MKStoreKit.sharedKit().availableProducts)
    }

    NSNotificationCenter.defaultCenter().addObserverForName(kMKStoreKitProductPurchasedNotification,
      object: nil, queue: NSOperationQueue.mainQueue()) { (note) -> Void in
        print ("Purchased product: \(note.object)")
    }

###Checking Product Status You can check if a product was previously purchased using -isProductPurchased as shown below.

if([MKStoreManager isProductPurchased:productIdentifier]) {
//unlock it
}

You can check for a product's expiry date using -expiryDateForProduct as shown below.

if([MKStoreManager expiryDateForProduct:productIdentifier]) {
//unlock it
}

Warning: This method will return [NSNull null] for products that are not auto-renewing subscriptions.

###Making a Purchase To purchase a feature or to subscribe to a auto-renewing subscription, just call

[[MKStoreKit sharedKit] initiatePaymentRequestForProductWithIdentifier:productIdentifier];

Observe kMKStoreKitProductPurchasedNotification to get notified when the purchase completes

  [[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitProductPurchasedNotification
                                                    object:nil
                                                     queue:[[NSOperationQueue alloc] init]
                                                usingBlock:^(NSNotification *note) {
                                                  
                                                  NSLog(@"Purchased/Subscribed to product with id: %@", [note object]);
                                                  
                                                  NSLog(@"%@", [[MKStoreKit sharedKit] valueForKey:@"purchaseRecord"]);
                                                }];

It can't get simpler than this!

Missing Features

  • Keychain support for products
  • Local receipt verification using asn1c

Untested Features

  • Non-renewing subscriptions
  • Free subscriptions for news stand
  • Mac OS X support

These three features might work, but they are not tested yet.

###Licensing

MKStoreKit is licensed under MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


###Attribution free licensing In case you need attribution free licensing for MKNetworkKit, you can buy one from my license store.

mkstorekit's People

Contributors

bmateus avatar bobbypage avatar darktable avatar dfabulich avatar eric-sofisoftwarellc avatar iosdeveloper avatar jyap808 avatar l4u avatar macarse avatar marzapower avatar mugunthkumar avatar pluton8 avatar yas375 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mkstorekit's Issues

Support for Auto-renewable transactions

MKStorekit should check for receipt validity for auto-renewable transactions.
(This code is partially written already and I'm testing it. Aiming to release in a week)

Crush after buyFeature:kFeatureAId on iOS5

App crushes after [[MKStoreManager sharedManager] buyFeature:kFeatureAId on iOS5.
It just says SIGABRT.

I'm using Xcode 4.2 and ARC enabled on MKStorekit. It works fine on IOS4, but it crushes on iOS5 simulator.

It crushes on this part.
[[SKPaymentQueue defaultQueue] addPayment:payment];

My device is iOS4.3, so I haven't not tried it on actual iOS5 device. But, at least it crushes on iOS5 simulator instead of
In-App Purchasing disabled pop menu which is shown on iOS4 simulator.

Does anybody have the same issue?

receiptDataString in verifyReceipt has to be base 64 encoded!

else iTunes returns status 21002.

Original:
NSString *receiptDataString = [[NSString alloc] initWithData:receiptData encoding:NSASCIIStringEncoding];
Changed to:
NSString *receiptDataString = [receiptData base64EncodedString];

I'm using the NSData+Base64 category by Matt Gallagher for data encoding

KeyChain

Does this support storing the status in the KeyChain rather than NSUserDefaults?

Proper implementation of MKStoreKitConfigs.plist

Hi I am curious as to what I need to enter in for the autorenewable subscriptions in the MKStoreKitConfigs.plist file.

The example says

com.mugunthkumar.subinapptest.wk1 Number 7

Does the 7 refer to the days of the subscription? I'd like to implement a 4 type of subscription:

com.mycompany.myapp.oneweek = 7 days
com.mycompany.myapp.onemonth = 1 month
com.mycompany.myapp.threemonths = 3 months
com.mycompany.myapp.sixmonths = 6 months
com.mycompany.myapp.oneyear = 1 year

I think I do it this way, not sure though:

com.mycompany.myapp.oneweek Number 7
com.mycompany.myapp.onemonth Number 30
com.mycompany.myapp.threemonths Number 91
com.mycompany.myapp.sixmonths Number 182
com.mycompany.myapp.oneyear Number 365

Please help!!

Also, is this subscription model similar to consumables that just auto-renew? Example: if I buy 7 days and on the 3rd day I want to buy the 1 year subscription will; at that point in time after the transaction finishes leave the subscription to 1 year 4 days? Or does it override the time already purchased?

Thanks,

Michael

Trying to update to 4.0...

Hello,

I just updated my app to use MKStoreKit 4.0 but it is not working. I am not sure I have the MKStoreKitConfigs.plist correct. I only have the one app, with not add on's so I have set the Non-Consumables only.

  1. Is this correct?

Next, I changed my code to do exactly what was in the read me that came with 4.0 but when I step through my app it gets to the call to buyFeature and skips the whole function.

  1. What am I doing wrong?

Thanks for the help

REVIEW_ALLOWED 1 + static completionBlock problem

buy only one item if REVIEW_ALLOWED 1
static problem )

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

[[MKStoreManager sharedManager] buyFeature:@"bla.bla.bla2"
onComplete:^(NSString* purchasedFeature)
{
NSLog(@"Purchased: %@", purchasedFeature);

}
onCancelled:^
{
NSLog(@"User Cancelled Transaction");
}];

dyld: Symbol not found: _OBJC_CLASS_$_NSUbiquitousKeyValueStore

Getting this error dyld: Symbol not found: OBJC_CLASS$_NSUbiquitousKeyValueStore

I need to support iOS4, the error occurs on an 2nd gen iTouch.

I believe caused by code in MKStoreKit / MKStoreManager.m - NSUbiquitousKeyValueStore which isn't available on iOS4.

I error occurs before any code is runs.

Help.

Help: Dynamically Purchase Itens?

Hi,
I'm building a project that fetches content dynamically and the static MKStoreKitConfigs.plist model doesn't seams to work out of the box for me. So I'm now re-writing the config file on the fly... It seams to work ok, obviously only if I make this change before instantiating the sharedManager singleton.

But there is still a problem, if the content and the product itens have to change in the runtime after I had already instantiated the manager, how can I "refresh" the product list? Is there a way to recall requestProductData? It is secure to let this method be visible and use it outside the manager?

Thank you guys in advance!
And by the way, thank you a lot for the amazing framework!

MKStoreKit usage question...

Hello,

I am using MKStore kit for inApp Purchases and (I think) I need a way to determine if the app was purchased and if it was enable certain features of the App. So, to do this, I use the following code:

[MKStoreManager sharedManager];
[[MKStoreManager sharedManager] restorePreviousTransactions];
if([MKStoreManager isFeaturePurchased:kFeatureAId])
{
       .
       .
       .
}

But as soon as this is executed a dialog prompting for the login is displayed, tat is not what I want to happen.

How can I tell if the app was purchased so that I can set some features and disable the ability to purchase again.

Any help is greatly appreciated.

Thank you.

BAD ACCESS ERROR

I have to errors with featureCheck and VerifyProduct on server,]
to avoid this errors, I cahnge this code -

replace onReviewRequestVerificationSucceeded();
to
onReviewRequestVerificationSucceeded(responseString);
in the MKSKProduct.m

  • (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
    NSString *responseString = [[NSString alloc] initWithData:sDataFromConnection
    encoding:NSASCIIStringEncoding];

    sDataFromConnection = nil;

    if([responseString isEqualToString:@"YES"])
    {
    if(onReviewRequestVerificationSucceeded)
    {
    onReviewRequestVerificationSucceeded(responseString);
    ....

Do this with this code to -

  • (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
    NSString *responseString = [[NSString alloc] initWithData:self.dataFromConnection
    encoding:NSASCIIStringEncoding];

    self.dataFromConnection = nil;

    if([responseString isEqualToString:@"YES"])
    {
    if(self.onReceiptVerificationSucceeded)
    {
    self.onReceiptVerificationSucceeded(responseString);
    ...

addToQueue bug?

In your add to queue method, you're using indexOfObject to find index of product identifier passed to this method, I had to amend this code because they have always been referencing different objectsm I suspect that it could be allIds that are created by copying value, result was always NSNotFound:

NSArray *allIds = [self.purchasableObjects valueForKey:@"productIdentifier"];
int index = [allIds indexOfObject:productId];

I had to change it to:

    BOOL(^cmpTest)(id obj, NSUInteger idx, BOOL *stop) = ^(id obj, NSUInteger idx, BOOL *stop){
        if([obj isEqualToString:productId]) {
            *stop = YES;
            return YES;
        } else {
            return NO;
        }
    };

    NSArray *allIds = [self.purchasableObjects valueForKey:@"productIdentifier"];
    int index = [allIds indexOfObjectPassingTest:cmpTest];

Maybe something you may want to take a look into.

cheers,
Marcin

Question about server mode and blocks?

Hello,

I have two questions about your MKStoreKit.

  1. If my IAP is in Server mode, that means, my products id will from database in server (I follow the same rule as your plist file in database). What should I do in MKStore kit? Or in another word, do I need to change your source code for that?
  2. Question about buyFeature method. I know you're using block instead of delegate. But here comes a problem, let's say, I'm buying a product which is in process and the my onComplete block is not called, then I want to buy another product, then you property of the block is replaced with the new block, so once your first product's onComplete is called, it will call to second buy's block. Am I right? Then how to solve these kind of problem? Maybe use a dictionary for the onComplete block?

Thanks

Auto-renewable restore issue

When restoring auto-renewable subscriptions MKStoreKit occasionally produces false positives where it considers valid but expired receipts as purchases. This is consistent, certain receipts will always trigger a purchase notification and be considered active even if they've expired and are marked with the 21006 status code (valid receipt but expired subscription).

This is kind of a major issue since once a user gets one of these receipts that throws MKStoreKit off, they can just restore to get free subscriptions in perpetuity.

Using filter_input() returns HTTP 500 on AddDevice.html

Hello,

I uploaded the server code and I used the script to setup my database, and whenever I tried to add data using AddDevice.html I got a http 500 code.

When I removed the filter_input functions and accessed _POST directly it worked.

$prod = $_POST['productid']; 
//$prod = filter_input(INPUT_POST, 'productid', FILTER_SANITIZE_NUMBER_INT);

$udid = $_POST['udid']; 
//$udid = filter_input(INPUT_POST, 'udid', FILTER_SANITIZE_STRING);

$email = $_POST['email']; 
//$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL;

$message = $_POST['message'];
//$message = filter_input(INPUT_POST, 'message', FILTER_SANITIZE_ENCODED);

Can anyone help me figuring this out?

Thank you!

UniqueIdentifier deprecated in code

I am not sure if i have an older version of the code, as I'm no GitGuru but the error i get:

'uniqueIdentifier' is deprecated (declared at /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIDevice.h:64)

is located at MKStoreProduct.m for code:

    NSString *uniqueID = [[UIDevice currentDevice] uniqueIdentifier];
    // check udid and featureid with developer's server

Empty the saved onComplete and onCancel handlers as soon as either happens!

Hi,

Just figured out that MKStorekit ends up calling the last, previously registered onComplete block with the buyFeature call, when transactions are restored.

Let me explain:

We have a nonconsumable feature called Paging expansion. This gets triggered with buyFeature when the user wants to get more results. When this buyFeature is called, the block passed for onComplete gets assigned and stored. Suppose the user:

  1. cancels the purchase,
  2. and then goes and presses a Restore transactions button (which we've placed in our Settings screen),
  3. and they have pending transactions that are restorable, then they are restored and "remembered".

The problem is that due to the earlier buyFeature call, the onComplete block gets triggered even with the restoreTransaction call.

The onComplete block (at least the way we've coded it) is expected to have the context that it was called in alive, but since the user navigated out, and went into the settings screen, the self object in our case has been deallocated, and the onComplete crashes the app!

Perhaps the better approach would be to nil the onComplete and onTransactionCanceled when either event happens, so that they cannot get called beyond the scope of success/failure that the buyFeature modals into.

No callback when there's nothing to restore

Create a fresh test user with no purchases.

Instantiate the store manager, call restorePreviousTransactions, and start a spinner.

Expected: My delegate should receive a notification to stop the spinner.

Actual: There's no callback, because no updated transactions appear in the payment queue. The spinner never stops.

auto renewable subscriptions return isFeaturePurchased false

Hi All,

I have a problem identifing if an feature that has subscriptions has an active subscription. It seems that mkstorekit does not make a difference between feature and productid when it comes to autosubscription. I did some debugging and explaine below.

Buy process:

SKProduct *product = [[[MKStoreManager sharedManager] purchasableObjects] objectAtIndex:1];
[[MKStoreManager sharedManager] buyFeature:[product productIdentifier] ......

product.productIdentifier is here the productID as seen in itunes. f.ex premium1 (1 month subscription for feature premium), premium6, premium12 etc.. All premiumX product belong to the same feature premium (no X)

In this case i continue with subscribing to premium1 (so i buy feature premium for 1 month)

it seems that the "thing2 stored in
SecItemUpdate is

(gdb) po query
{
acct = premium1;
class = genp;
labl = MKStoreKit;
svce = MKStoreKit;
}

when i later on check if my feature is active with the following code

[MKStoreManager isFeaturePurchased:@"premium"] returns false (this returns data from keychain)

however

[[MKStoreManager sharedManager] isSubscriptionActive:@"premium1"] returns true ( but this requires a newtork connection to itunes)

So how is this suposed to work. It seems that the connection between feature and productId is not made. It should be so that when checking for a feature any valid subsciption beloning to that feature implies that the feature is active.

regards

MKStoreKit doesn't distinguish failures from cancellations

Create an MKStoreKitDelegate that implements the transactionCanceled method. Configure it to show a spinner when you call buyProduct, and hide the spinner on transactionCanceled, but don't show an error message.

Note that MKStoreManager.h says:

- (void)transactionCanceled;
// as a matter of UX, don't show a "User Canceled transaction" alert view here
// use this only to "enable/disable your UI or hide your activity indicator view etc.,

Turn on airplane mode, disabling all network connectivity, and call buyFeature on MKStoreManager.

Actual: MKStoreManager will call the delegate's transactionCanceled method. The spinner will stop, but the user will receive no error message.

Expected: MKStoreKitDelegate should have a transactionFailed method that is called for failed transactions whose error code is not SKErrorPaymentCancelled or SKErrorPaymentNotAllowed.

Fix error {"status":21002, "exception":"java.lang.NullPointerException"}

I sugest in MKSKProduct.m add Base64 encode, else you have error 21002 from Apple when you are use verifyProduct.php

NSString *receiptDataString = [[NSString alloc] initWithData:self.receipt 
                                                encoding:NSASCIIStringEncoding];

replace with

NSString *receiptDataString = [[NSString alloc] initWithData:self.receipt 
                                                    encoding:NSASCIIStringEncoding];
receiptDataString = [[[NSString stringWithFormat:@"%@",receiptDataString] dataUsingEncoding:NSASCIIStringEncoding] base64EncodedString];

And add #import "NSData+Base64.h" for base64EncodedString

receipt verification fails: how to notify user?

Changes to MKStoreManager.m :

Made following changes so that when the receipt verification fails (for non-consumables, product model), the user is notified:

-(BOOL) provideContent: (NSString_) productIdentifier
forReceipt:(NSData_) receiptData
{
NSLog (@" Contacting Apple for receipt verification");
// ping server and get response before serializing the product
// this is a blocking call to post receipt data to your server
// it should normally take a couple of seconds on a good 3G connection
if(![self verifyReceipt:receiptData]) {
if([_delegate respondsToSelector:@selector(transactionFailed)])
[_delegate transactionFailed];
return NO;
}

....
}

Also added following for receipt verification for non-server model:

import "NSData+Base64.h" //---> Matt Gallagher

import "JSON.h" //- > http://code.google.com/p/json-framework

import "CJSONSerializer.h" // touch json

  • (BOOL)verifyReceipt:(NSData*) receiptData{

    NSString *base64String = [receiptData base64EncodedString];
    NSDictionary *base64Dictionary = [NSDictionary dictionaryWithObjectsAndKeys:base64String,@"receipt-data",nil];

    NSLog(@"string to send: %@",base64Dictionary);

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"]];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json-rpc" forHTTPHeaderField:@"Content-Type"];

    NSError *error;
    NSData *jsonData = [[CJSONSerializer serializer] serializeObject:base64Dictionary error:&error];

    NSLog(@"%@", jsonData);

    [request setHTTPBody:jsonData];
    NSLog(@"will create connection");

    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

    // Store incoming data into a string
    NSString *jsonString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

    // Create a dictionary from the JSON string
    NSDictionary *json_dict = [[NSDictionary alloc] initWithDictionary:[jsonString JSONValue]];

    NSLog(@"jsonstring =%@ jsonDict=%@",jsonString,json_dict );

    BOOL returnValue;
    if ([[json_dict valueForKey:@"status"] intValue] == 0) {
    returnValue = YES;
    } else {
    returnValue = NO;
    }

    [json_dict release];
    [jsonString release];

    return returnValue;

    //return NO;

}

Do I need to change MKStoreObserver.m like following ???? :

  • (void) completeTransaction: (SKPaymentTransaction *)transaction
    {
    //[self verifyReceipt:transaction];

    if( [[MKStoreManager sharedManager] provideContent:transaction.payment.productIdentifier forReceipt:transaction.transactionReceipt]) {

    [self finishTransaction:transaction wasSuccessful:YES];
    } else {
    [self finishTransaction:transaction wasSuccessful:NO];
    }
    }

  • (void) restoreTransaction: (SKPaymentTransaction *)transaction
    {
    if ([[MKStoreManager sharedManager] provideContent: transaction.originalTransaction.payment.productIdentifier forReceipt:transaction.transactionReceipt] ) {

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    } else {
    [self finishTransaction:transaction wasSuccessful:NO];
    }
    }

Server side code

In requestAccess.php, the line that gets the product ID shouldn't look like this:

$prod =filter_input(INPUT_POST, 'productid', FILTER_SANITIZE_STRING);

instead of

$prod =filter_input(INPUT_POST, 'productid', FILTER_SANITIZE_NUMBER_INT);

?

List the features that got restored in restorePreviousTransactionsOnComplete:onError:

It would be great if we could have an NSArray parameter in the OnComplete: block of restorePreviousTransactions. That way we could list / alert the user which all features got unlocked because of the restore.

In our case we have 9 in app purchases and in our restorePreviousTransactions success handler we have to either iterate through all our known feature ids with an isFeaturePurchased: call or worse, show the user a generic message with something like "All that was restorable has been restored."

Another way this could be tackled, perhaps, is to have a central notification that is posted just at the time of "remembering" a purchased product. This would work for all cases, purchase, restore, etc. just at the point of time of saving. Developers who wanted to could handle it generically in a single place.

Memory Leaks

  • Double release of responseString in MKSKProduct.
  • purchasableObjects and storeObserver are retain objects but the local objects created are not released.

Question about MKStoreKist Server...

Hello,

I have used MKStoreKit and it works great, but I never really understood what the server code is for. I have read through all I could find, but it is not really explained.

Can someone tell me why I would use the server code?

Thanks

https query to api server [SSL implementation]

I suggest add next feature -
If you add to MKStoreKitConfigs.h this -

define OWN_SERVER @"https://address_your_server/api"

you will trouble with connection by SSL protocol.

You can do that -
Change MKSKProduct.m,
add to it this block -
// Dummy interface to avoid a warning.
@interface NSURLRequest (DummyInterface)

  • (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
  • (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host;
    @EnD

Then change procedure, for example -

+(void) verifyProductForReviewAccess:(NSString_) productId
onComplete:(void (^)(NSNumber_)) completionBlock
onError:(void (^)(NSError*)) errorBlock

....
[theRequest setHTTPMethod:@"POST"];
[theRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

    [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];

...
}

As you see, I'm add this - [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];

Do the same with

  • (void) verifyReceiptOnComplete:(void (^)(void)) completionBlock
    onError:(void (^)(NSError*)) errorBlock

review request cannot be checked now: (null)

I have added MKStoreKit to an existing app and I keep getting the error "Review request cannot be checked now: (null)". I suspect it might be due to the kConsumableFeatureBId in the configuration file. I understand this to be the product id + the product number. What is the product number, and where can it be found?

MKStoreKit 4.3 Subscription

So as far as the new update it looks like it does see the subscriptions correctly if you run the #1 sample below in the app delegate. It shows the correct info YES if owned and NO if not owned. But if you call #2 or #3 after the app loads completely it returns a NO no matter what. It seems like it does not retain the original proper answer or it is doing something else after the app completely loads that kills the proper subscription answer. Does anyone else see this or have an idea ?
#1

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {

    [MKStoreManager sharedManager];
    NSLog(@"Is subscription owned ? : %@", ([[MKStoreManager sharedManager] isSubscriptionActive:kFeatureZId]) ? @"YES" : @"NO");

}
#2

NSLog(@"Is subscription owned ? : %@", ([[MKStoreManager sharedManager] isSubscriptionActive:kFeatureZId]) ? @"YES" : @"NO");
#3

[MKStoreManager sharedManager];
NSLog(@"Is subscription owned ? : %@", ([[MKStoreManager sharedManager] isSubscriptionActive:kFeatureZId]) ? @"YES" : @"NO");

Won't display IAP buy window till SECOND click

I am running the latest MKStoreKit code, and don't get the IAP window after running

[[MKStoreManager sharedManager] buyFeature . . .

until it executes twice or thereafter. A very bizarre error.

The only time this doesn't happen is when I removeAllKeychainData first, obviously this isn't something I want to do.

So to recap, I get the IAP functionality on the second, third, fourth, executions, but not the first.

Please help!

MKStoreKit 4.1 Auto-renewable issue

hi,
I am having the problem with subscription 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?

Thanks in advance

ARC is really supported?

I've downloaded the ARC branch. But there are still a lot of compiling error for ARC.

  1. For JSonKit, I've managed to replace that one with Apple's own one.
  2. But for SFHFKeychainUtils, we have to fix error by our self or somebody already done this?

Thanks

Call requestProductData after going online

Now products information is loaded from apple servers when singleton MKStoreManager.sharedManager first time called. In method

- (void) requestProductData;

But if I call MKStoreManager.sharedManager when device in offline, requestProductData would be never called second time.

I think it will be better, if framework will request data second time if products information is empty.

/Users/Andrew/Documents/Non-generated/TableView/Classes/SettingsViewController.m:96:48: Incompatible block pointer types sending 'void (^)(NSString *)' to parameter of type 'void (^)(NSString *, NSData *)'

/Users/Andrew/Documents/Non-generated/TableView/Classes/SettingsViewController.m:96:48: Incompatible block pointer types sending 'void (^)(NSString *)' to parameter of type 'void (^)(NSString *, NSData *)'

Help???? for the [[MKStoreManager sharedManager] buyFeature:kFeatureAId
onComplete:^(NSString* purchasedFeature) <---- This line
{
NSLog(@"Purchased: %@", purchasedFeature);
}
onCancelled:^
{
NSLog(@"User Cancelled Transaction");
}];

Auto-renewal plist days info... really necessary?

Hi everybody, I recently began to test this framework, and here are my two cents... Turns out that in the receipt that Apple sends you, there is field called Expire_Date or something like that, it just tells you the date (until the precise second, useful also when testing, when you have a subscription that expire 3 minutes later). With this info, there is no need to store the number of days of a subscription product in the plist file. I'm correct? Greetings and thank you for this amazing framework.

PS: Also, I saw that I need to store the shared secret on the code.. Is there any way to support server model with subscription products? actually Apple recommends this

Gian Franco Zabarino

Crash when updating from iCloud on MKStoreManager

I was using MKStoreKit just fine, but when I logged out from my ITC test account and deleted the app, this is what I get when I start:
+(void) updateFromiCloud:(NSNotification*) notificationObject

The problem seems to be what SFHFKeychainUtils tries to add to the keychain

This is the key: WebKitWebSecurityEnabled and this is the object: 1

Here's the exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFBoolean dataUsingEncoding:]: unrecognized selector sent to instance 0x3ef2f9f0'

Here's the call stack:

-[NSCFBoolean dataUsingEncoding:]: unrecognized selector sent to instance 0x3ef2f9f0
0 CoreFoundation 0x343e78d7 __exceptionPreprocess + 186
1 libobjc.A.dylib 0x346371e5 objc_exception_throw + 32
2 CoreFoundation 0x343eaacb -[NSObject doesNotRecognizeSelector:] + 174
3 CoreFoundation 0x343e9945 __forwarding
+ 300
4 CoreFoundation 0x34344680 _CF_forwarding_prep_0 + 48
5 Katapakote 0x00062201 +[SFHFKeychainUtils storeUsername:andPassword:forServiceName:updateExisting:error:] + 984
6 Katapakote 0x00099a4f __35+[MKStoreManager updateFromiCloud:]_block_invoke_0 + 114
7 CoreFoundation 0x343e70fb ____NSDictionaryEnumerate_block_invoke_0337 + 22
8 CoreFoundation 0x34333000 CFBasicHashApply + 140
9 CoreFoundation 0x343e43f9 __NSDictionaryEnumerate + 572
10 Katapakote 0x000999c9 +[MKStoreManager updateFromiCloud:] + 80
11 Foundation 0x35dbc50f __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 18
12 CoreFoundation 0x343b3577 ___CFXNotificationPost_block_invoke_0 + 70
13 CoreFoundation 0x3433f0cf _CFXNotificationPost + 1406
14 Foundation 0x35d303fb -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
15 Foundation 0x35e63191 -[NSUbiquitousKeyValueStore _postDidChangeNotificationExternalChanges:sourceChangeCount:] + 296
16 Foundation 0x35e64131 __block_global_3 + 76
17 libdispatch.dylib 0x37010d55 _dispatch_call_block_and_release + 12
18 libdispatch.dylib 0x3701be8d _dispatch_main_queue_callback_4CF$VARIANT$up + 196
19 CoreFoundation 0x343ba2dd __CFRunLoopRun + 1268
20 CoreFoundation 0x3433d4dd CFRunLoopRunSpecific + 300
21 CoreFoundation 0x3433d3a5 CFRunLoopRunInMode + 104
22 GraphicsServices 0x30bc1fcd GSEventRunModal + 156
23 UIKit 0x377be743 UIApplicationMain + 1090
24 Katapakote 0x00004587 main + 66
25 Katapakote 0x00004008 start + 40

Failed transaction but customer is charged

Hi,

I have been using MKStorekit for a few weeks and I have a couple of apps in the app store. I'm quite happy with it except occasionally I receive emails customers complaining that they purchased the feature but they did not get it.

My app has a "Contact us" button, which brings up an email composing window with the app log attached by default. Unless they remove it I always see the message "error: Error Domain=SKErrorDomain Code=2 "Cannot connect to iTunes Store"
I suspect that it could be related to Jailbroken devices.

It is a non-consumable in-app purchase and I use
[[MKStoreManager sharedManager] buyFeature:appFeatureId] for purchases and
[MKStoreManager isFeaturePurchased:appFeatureId] for checking the purchases.

Q1: My understanding is that [MKStoreManager isFeaturePurchased:appFeatureId] checks purchases locally, is there any way to verify the purchases directly with Apple server?

I know customers can just "purchase" feature again and they get the "Feature already purchased" massage and from that on it is all good, but it is less than ideal because they are reluctant to do that because they are afraid of being charged twice.

Thanks in advance

Keychain receipts lead to a share-with-friends vulnerability in MKStoreKit

I ran into this while debugging my app, and I'm not sure if there is a way to work around this.

Basically, since the keychain persist even when the iTunes user is changed I can share any in-app purchase made using MKStoreKit with a friend.

  1. purchase item.
  2. go to friends iPhone/iPad.
  3. sign out their iTunes account, sign in mine.
  4. go to app, restore purchases.
  5. sign out my iTunes account, sign in theirs.
  6. in-app purchases are still available, and will be unless something resets the keychain (if the user uses encrypted backups, I think the keychain would persist onto a new device or from a restore-from-backup).

Again, I'm not sure if there is a way to detect the current iTunes Apple id and make the keychain tied to that in some way...? Or perhaps I'm mis-understanding how this works. I haven't tried this on an app in the wild yet, so I'm not sure if this is a real-world vulnerability or just in the code I'm currently staging.

Allow users to pass in a set of products

Right now users have to modify MKStoreManager.h and MKStoreManager.m to replace the product list with their own list of products.

It would be nice if the library would allow me to pass in a list of products before initializing the store manager.

For example, MKStoreKitDelegate might have a "products" property that returns a list of products. Then, as long as I set the delegate before initializing the store manager, MKStoreManager could retrieve the product list at runtime.

4.3 breaks non-renewable subs

The code added in [24e9634]
- (BOOL) isSubscriptionActive:(NSString*) featureId
only relates to the dictionary contents of auto renewable subscriptions. If the subscription is a non-reneable one, it always returns NO.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.