Question

I'm making an in-app purchase, but I have a bug that I can't find where it comes from...

First of all, I have a button and when you click it, my app Request the product, catches the response, you pay, and you get the product (everything works OK). But here comes my problem. If I click the button to buy anything again, I get TWO alerts that I bought something TWO times. Even if I click for a third time, i get THREE alerts that I bought something THREE times, and four and five according to the number of times I clicked.

So it seems like some variable keeps storing the requests.. Here's my code:

This validates the product ID

- (void) validateProductIdentifiers
{ 
NSString *monedas = @" ID FROM PRODUCT ";
NSSet *product = [NSSet setWithObject:monedas];

productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:product];

productsRequest.delegate = self;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[productsRequest start];
NSLog(@"STARTED REQUEST");
}

This handles the response from Apple

- (void)productsRequest:(SKProductsRequest *)request
 didReceiveResponse:(SKProductsResponse *)response
{


productsRequest=nil;

int found=0;

SKProduct *paraPagar;
skProducts = response.products;

for (SKProduct * skProduct in skProducts) {
             NSLog(@"Found product: %@ %@ %0.2f",
          skProduct.productIdentifier,
          skProduct.localizedTitle,
          skProduct.price.floatValue);
    found=1;

    paraPagar = skProduct;
}
if (found==1){ 
    payment = [SKMutablePayment paymentWithProduct:paraPagar];
    payment.quantity = 1;

    [[SKPaymentQueue defaultQueue]addPayment:payment];

}else{ 

 //error (not relevant code)

}
}

Accepting payment and finishing transaction

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



for (SKPaymentTransaction *transaction in transactions) {
    switch (transaction.transactionState) {

        case SKPaymentTransactionStatePurchased:
            NSLog(@"BOUGHT");

            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            [self completeTransaction:transaction];


            break;
        case SKPaymentTransactionStateFailed:
            NSLog(@"FAILED");
           [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

            [self failedTransaction];
            break;
        case SKPaymentTransactionStateRestored:
            NSLog(@"RESTORED");
            //[self restoreTransaction:transaction];
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        case SKPaymentTransactionStatePurchasing:
            NSLog(@"PURCHASING");
        default:
            break;
    }

}

}

Thank you very much for your time!

Was it helpful?

Solution

My problem was that the observer was being duplicated every time I clicked on the button or when I changed views.

The solution is adding a flag, to see if the observer has already been added.

static bool hasAddObserver=NO;

PAYMENT METHOD{
if (!hasAddObserver) {
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    hasAddObserver=YES;
}
.....

OTHER TIPS

I had the same problem, I would click buy on an IAP and would receive multiple responses and purchases from Apple.

addTransactionObserver() was being called every time I left and came back to the view via viewDidLoad().

I needed to un-observe it in viewWillDisappear():

override func viewWillDisappear() {
  SKPaymentQueue.defaultQueue().removeTransactionObserver(self)
}

I could't use a flag to prevent multiple observers like in mursang's answer because I listened for transactions in other views.

Are you remembering to call

[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

when the purchase is completed ?

Swift 3: According to @sponrad answer

override func viewWillDisappear(_ animated: Bool) { 
    SKPaymentQueue.default().remove(self)
}

[addTransactionObserver] should be run only one time.

so you can use the singleton pattern to solve this problem.

+ (InAppPurchase *)sharedInstance
{
    static dispatch_once_t once;
    static InAppPurchase * sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] initWithProductIdentifiers:nil];
    });
    return sharedInstance;
}

- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {

    if ((self = [super init])) {
        // Add self as transaction observer
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    }
    return self;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top