Restoring In App Purchases Separately

Right now the final step in finishing my app comes with finishing the in App Purchases.

I think i'm really close to finishing but there is something fundamental I am missing to get over the line.

I have my code set up so that with sandbox testing I can succesfully buy the non-consumable product which unlocks a new view.

The only problem I am having is that if I purchase one non-consumable, shutdown my app, re open it and restore the purchase, as soon as I have done that all other in app purchases can be restored without even purchasing them. Meaning once one product is bought and restored something in my code unlocks all other in app purchases for free.


Now I think I know where something needs to be put but i'm just not sure exactly what it is.

For the code i'll show you two seperate in app purchases and hopefully you'll be able to see something that is either missing or is just wrong.

First is the Rap Genre purchase view controller:


- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:(BOOL)animated];
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

- (IBAction)Restore:(id)sender {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

- (IBAction)Buy:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}

-(void)getProductID:(ViewController *)viewController {
_homeController = viewController;
if ([SKPaymentQueue canMakePayments]) {
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:
    [NSSet setWithObject:self.productID]];
    request.delegate = self;
    [request start];
} else
    _ProductDescription.text = @"Please enable in app purchase within your settings";
}

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
   /
if (products.count !=0) {
    _product = products[0];
    _ProductTitle.text = _product.localizedTitle;
    _ProductDescription.text = _product.localizedDescription;
    _buyButton.enabled = YES;
} else {
    _ProductTitle.text = @"Product not found";
}
products = response.invalidProductIdentifiers;
for (SKProduct *product in products) {
    NSLog(@"%@", product);
}
}

-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
    switch (transaction.transactionState) {
        case SKPaymentTransactionStatePurchased:[self UnlockPurchase];
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            break;
        case SKPaymentTransactionStateFailed:NSLog(@"Failed");
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            break;
        case SKPaymentTransactionStateRestored:
            [self UnlockPurchase];
            NSLog(@"Transaction state -> Restored");
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            break;
        default:
            break;
    }
}
}
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
NSLog(@"received restored transactions: %lu", (unsigned long)queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions) {
    if (transaction.transactionState == SKPaymentTransactionStateRestored) {
        NSLog(@"Transaction State -> Restored");
        /
       /
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
         break;
    }
}
}
-(void)UnlockPurchase {
/
_buyButton.enabled = NO;
[_buyButton setTitle:@"Purchased" forState:UIControlStateDisabled];
[_homeController Purchased];


Then the Country Genre:


- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:(BOOL)animated];
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

- (IBAction)RestoreCountry:(id)sender {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

- (IBAction)BuyCountry:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_countryProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}

-(void)getCountryProductID:(ViewController *)viewController {
_homeController = viewController;
if ([SKPaymentQueue canMakePayments]) {
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:self.countryProductID]];
    request.delegate = self;
    [request start];
} else
    _countryProductDescription.text = @"Please enable in app purchase within your settings";
}

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
if (products.count !=0) {
    _countryProduct = products[0];
    _countryProductTitle.text = _countryProduct.localizedTitle;
    _countryProductDescription.text = _countryProduct.localizedDescription;
    _countryBuyButton.enabled = YES;
} else {
    _countryProductTitle.text = @"Product not found";
}
products = response.invalidProductIdentifiers;
for (SKProduct *product in products) {
    NSLog(@"%@", product);
}
}

-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
    switch (transaction.transactionState) {
        case SKPaymentTransactionStatePurchased:[self UnlockCountryPurchase];
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            break;
        case SKPaymentTransactionStateFailed:NSLog(@"Failed");
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            break;
        case SKPaymentTransactionStateRestored:
            [self UnlockCountryPurchase];
            NSLog(@"Transaction state -> Restored");
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            break;
        default:
            break;
    }
}
}

-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
NSLog(@"received restored transactions: %lu", (unsigned long)queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions) {
    if (transaction.transactionState == SKPaymentTransactionStateRestored) {
        NSLog(@"Transaction State -> Restored");
      
       [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        break;
    }
}
}
-(void)UnlockCountryPurchase {
/
_countryBuyButton.enabled = NO;
[_countryBuyButton setTitle:@"Purchased" forState:UIControlStateDisabled];
[_homeController PurchasedTwo];
}



I believe i'm missing something within paymentQueueRestoreCompletedTransactionsFinished

that specifies exactly what should be restored and what shouldn't but i'm not sure how to implement it.`

In updatedTransactions you want to get the value for transaction.payment.productInformation and only credit that product

I'm a little lost and could use a bit more detail.

The way I interpreted your answer was to change the code like so:


-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {

    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:[self UnlockPurchase];
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:NSLog(@"Failed");
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                if ((_productID = transaction.payment.productIdentifier)) {
                    [self UnlockPurchase];
                    NSLog(@"Transaction state -> Restored");
                }
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            default:
                break;
        }
    }

}


But the same thing keeps happening. Once one is bought all of them can be restored.

And with your changes do i still need -(void)paymentQueueRestoreCompletedTransactionsFinished or is it unnecessary.


Apologies if i'm just being dumb but i'm finding it difficult to find the answer I need throughout the web.

Accepted Answer

and then in unlockPurchase you only grant the IAP for _productID

All good!!

Thanks for taking the time to help me out.

Restoring In App Purchases Separately
 
 
Q