0

I'm having trouble implementing in-app purchases in my mobile app.

Basically, sometimes users get charged twice. and also when users get interrupted in the app - like they are redirected back to itunes to finish payment and then back to the app, they see the error "an error occured .."

have I implemented in-app purchases correctly?

Here is my code:

What am I doing wrong?

- (void) myLeftAction {
    [self performSegueWithIdentifier: @"login" sender: self];
}

- (void)viewDidLoad {
    [super viewDidLoad];


    UISwipeGestureRecognizer * recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(myLeftAction)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
    [self.view addGestureRecognizer:recognizer];


    done = NO;
    paymentProcessing = NO;

    shownProducts = [NSArray arrayWithObjects: @"com.example.a", @"com.example.b", @"com.example.c", @"com.example.d", nil];

    hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.labelText = @"Loading";

    [self fetchAvailableProducts];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)fetchAvailableProducts {
    if ([SKPaymentQueue canMakePayments])
    {
        NSSet *productIdentifiers = [NSSet setWithObjects: @"com.example.a", @"com.example.b", @"com.example.c", @"com.example.d",nil];
        productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
        productsRequest.delegate = self;
        [productsRequest start];
    }
    else {
        UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
                                  @"Purchases are disabled in your device. Please enable in-app purchases before continuing." message:nil delegate:
                                  self cancelButtonTitle:@"Ok" otherButtonTitles: nil];
        [alertView show];
    }
}

- (BOOL)canMakePurchases {
    return [SKPaymentQueue canMakePayments];
}

- (void)purchaseMyProduct:(SKProduct*)product {
    if ([self canMakePurchases]) {
        SKPayment *payment = [SKPayment paymentWithProduct:product];
        [[SKPaymentQueue defaultQueue] addTransactionObserver: self];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    else{
        paymentProcessing = NO;

        UIAlertView *alertView = [[UIAlertView alloc]initWithTitle: @"Purchases are disabled on your device. Please enable in-app purchases before continuing." message:nil delegate: self cancelButtonTitle:@"Ok" otherButtonTitles: nil];
        [alertView show];
    }
}

-(IBAction)purchase:(id)sender
{
    if(done && !paymentProcessing) {
        paymentProcessing = YES;
        chosenProduct = [validProducts objectAtIndex: [sender tag]];
        [self purchaseMyProduct: chosenProduct];
    }
}

#pragma mark StoreKit Delegate

-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:
                [self occuringTransaction: transaction];
                break;
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction: transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction: transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction: transaction];
                break;
            default:
                break;
        }
    }
}

- (void) occuringTransaction:(SKPaymentTransaction *)transaction
{
    [hud show: YES];
    NSLog(@"Purchasing");
}

- (void) processPurchase: (SKPaymentTransaction *)transaction
{
    id<FBGraphUser> fb = [[Auth sharedInstance] user];

    NSString * firstName = fb.first_name;
    NSString * lastName = fb.last_name;
    NSString * email = [fb objectForKey: @"email"];
    NSString * facebook_id = fb.id;

    PFObject * result = [PFObject objectWithClassName:@"payments"];
    result[@"firstName"] = firstName;
    result[@"lastName"] = lastName;
    result[@"email"] = email;
    result[@"facebookId"] = facebook_id;
    result[@"package"] = transaction.payment.productIdentifier;
    result[@"transactionId"] = transaction.transactionIdentifier;
    result[@"transactionError"] = transaction.error.localizedDescription;

    NSString *dateString = [NSDateFormatter localizedStringFromDate: transaction.transactionDate
                                                          dateStyle:NSDateFormatterShortStyle
                                                          timeStyle:NSDateFormatterFullStyle];

    result[@"transactionDate"] = dateString;
    result[@"transactionState"] = [NSString stringWithFormat: @"%ld", transaction.transactionState];

    [result saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
     {
         [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

         UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
                                   @"Purchase is completed successfully. You can now choose the hashtags you want shown in your review on lulu." message:nil delegate:
                                   self cancelButtonTitle:@"Ok" otherButtonTitles: nil];
         [alertView show];

         NSString * category = @"";

         if(
            [transaction.payment.productIdentifier isEqualToString: @"com.fakemylulu.player_1"] ||
            [transaction.payment.productIdentifier isEqualToString: @"com.fakemylulu.player_3"]
            ) {
             category = @"a";
         }
         else {
             category = @"b";
         }

         paymentProcessing = NO;

         [hud hide:YES];

         //push view manually
         UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
         fmHashtagsViewController *ivc = [storyboard instantiateViewControllerWithIdentifier:@"hashtags"];
         ivc.category = transaction.payment.productIdentifier;
         [self.navigationController pushViewController:ivc animated:NO];
     }];

}

-(void)completeTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"Completed transaction");

    if([transaction.payment.productIdentifier isEqualToString: chosenProduct.productIdentifier]) {
        [self processPurchase: transaction];
    }
}

-(void)restoreTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"Restored transaction");
    [self processPurchase: transaction];
}

-(void)failedTransaction:(SKPaymentTransaction *)transaction
{
    [hud hide: YES];
    paymentProcessing = NO;

    if(transaction.error.code != SKErrorPaymentCancelled)
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"An error occured! We could not process your payment. Please try again."
                                                        message:[transaction.error localizedDescription]
                                                       delegate:nil
                                              cancelButtonTitle:nil
                                              otherButtonTitles:@"OK", nil];
        [alert show];
    }

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    NSInteger count = [response.products count];

    if (count>0)
    {
        validProducts = response.products;

        NSUInteger index = 0;

        for (SKProduct * x in validProducts)
        {
            NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
            [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
            [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
            [numberFormatter setLocale: x.priceLocale];
            NSString *formattedPrice = [numberFormatter stringFromNumber: x.price];

            if([x.productIdentifier isEqualToString: @"com.fakemylulu.romantic_1"]) {
                [self.a setTitle: [NSString stringWithFormat: @"%@", formattedPrice] forState:UIControlStateNormal];
                self.a.tag = index;
            }
            else if([x.productIdentifier isEqualToString: @"com.fakemylulu.romantic_3"])
            {
                [self.b setTitle: [NSString stringWithFormat: @"%@", formattedPrice] forState:UIControlStateNormal];
                self.b.tag = index;
            }
            else if([x.productIdentifier isEqualToString: @"com.fakemylulu.player_1"])
            {
                [self.c setTitle: [NSString stringWithFormat: @"%@", formattedPrice] forState:UIControlStateNormal];
                self.c.tag = index;
            }
            else if([x.productIdentifier isEqualToString: @"com.fakemylulu.player_3"])
            {
                [self.d setTitle: [NSString stringWithFormat: @"%@", formattedPrice] forState:UIControlStateNormal];
                self.d.tag = index;
            }

            index += 1;
        }
        done = YES;
    } else {
        UIAlertView *tmp = [[UIAlertView alloc]
                            initWithTitle:@"Not Available"
                            message:@"No products available for purchase"
                            delegate:self
                            cancelButtonTitle:nil
                            otherButtonTitles:@"Ok", nil];
        [tmp show];
    }

    [hud hide:YES];
}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
    NSLog(@"Failed to load list of products. %@", error);

    UIAlertView *tmp = [[UIAlertView alloc]
                        initWithTitle:@"Oops"
                        message:@"There was an error retrieving the list of products. Please try this app again later."
                        delegate:self
                        cancelButtonTitle:nil
                        otherButtonTitles:@"Ok", nil];
    [tmp show];
}

- (void)dealloc {
    if ([SKPaymentQueue canMakePayments]) {
        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    }
}
  • what do you mean by the users get chanrged twice. have you not tested it in sandbox before uploading it on iTunes store. Have you not debugged it. try debugging the code to check at which point the app throws the error. Also what kind of product is it - consumable or non consumable. If it is consumable it will always be charged everytime user purchase it. – gaurish.salunke Feb 04 '14 at 07:21

1 Answers1

0

First You have to say what type of Subscription. There are three major types of In-App Purchases in iOS: 1)Non-consumables. 2)Consumables. 3)Subscriptions. In auto-renewable subscriptions It will auto renew every 5 minutes. because in sanbox account for 1 month peroid it will take 5 minutes.

If it was a Consumables:These are things the user can buy multiple times. Often they are “used up” so the user can buy them again

Non-consumables: These are things the user buys once (and only once), and then has access to forever.

Lova
  • 134
  • 7