MugunthKumar / MKStoreKit

The "Goto" In App Purchases Framework for iOS 8+
2.09k stars 430 forks source link

onTransactionCompleted called improperly during restore #156

Closed iwasrobbed closed 9 years ago

iwasrobbed commented 11 years ago

There is a bug where the onTransactionCompleted block within MKStoreManager.m is being called even though we are just trying to restore a feature (not purchase a feature).

Let's say you have some code like this in a method:

// We are purchasing the app
if (purchasingApp) {

    // Start the purchasing process
    [storeManager buyFeature:self.featureID onComplete:^(NSString *purchasedFeature, NSData *purchasedReceipt, NSArray *availableDownloads) {
        NSLog(@"Callback for purchasing feature was called.");
    }
    onCancelled:^{
        NSLog(@"Canceled purchase.");
    }];

// We are restoring a prior purchase
} else if (restoringApp) {

    // Start the restore process
    [storeManager restorePreviousTransactionsOnComplete:^{

        if ([MKStoreManager isFeaturePurchased:self.featureID]) {
            NSLog(@"Callback for restoring feature was called.");
        } else {
            NSLog(@"The user has never purchased this before.");
        }

    } onError:^(NSError *error) {
        NSLog(@"There was an error restoring feature.");
    }];
}

What happens when you try to restore a feature is the restorePreviousTransactionsOnComplete method is called and StoreKit does it's thing asynchronously and eventually that completion block is called.

However, before the restorePreviousTransactionsOnComplete completion block is called, the block which was passed in to buyFeature:onComplete: is actually called first and whatever code is inside of that block executes (in this case, it logs Callback for purchasing feature was called. to the console).

The issue is in the fact that the MKStoreManager singleton keeps a reference to the buyFeature:onComplete completion block and never nils it out or checks if we're actually restoring instead.

A simple fix for now is to add:

self.onTransactionCompleted = nil;

to the top of the restorePreviousTransactionsOnComplete method within MKStoreManager.m

The source of the bug is in

[self rememberPurchaseOfProduct:productIdentifier withReceipt:receiptData];
      if(self.onTransactionCompleted)
        self.onTransactionCompleted(productIdentifier, receiptData, hostedContent);

which is within the provideContent:forReceipt:hostedContent:

phongleq commented 10 years ago

I'm having the same issue when call the [MKStoreKit shareInstance] in my app delegate. It would call onTransactionCompleted even though it just verifying subscriptions. I just set them to nil after the block is called.

gauravlnx commented 10 years ago

Same issue is working as a poison pill in my app. I am feeling this issue if I try to purchase a product but cancel the purchase. After that if I try to restore transactions, my app crashes. The completion block contains a ViewController transition code which gets called during restore. I am going to set them nil after either the onCompleted or onCancelled is called.