Omer Waqas Khan Omer Waqas Khan - 2 months ago 17
iOS Question

iOS In-App Purchases handling when app goes to background (Unfinished interrupted transactions)

In my app In-App Purchases are working fine.

The issue I am facing is that;


  • if I initiate a subscription process and send app to background by pressing Home button on iPhone.

  • then In-App purchases API keeps on working and prompts user for iTunes credentials, while app is in background and user completes the process of purchasing the subscription successfully.

  • Now user has purchased the subscription through In-App API while app is in background but I am not getting that how to handle this scenario as, if user kills app without taking it back to foreground then the subscription purchase information will never be forwarded to our server and we will not be able to update user account in our server and user will not be able to use the special features of the app.



To send the latest receipt and In-App Purchase info to server, I get notified through below method:

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(@"updated transaction");
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
NSLog(@"transationStatePurchased");
// here I send data to server, but it never runs if app is in background.
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(@"transationStateFailed");
[self failedTransaction:transaction];
break;
default:
break;
}
}
}


But the above method always works when app is in foreground so the issue is how can I get notified about the complete transactions even if above method does not executes.

Answer

OK, So I have got the answer.

For others in easy words.

If you have an interrupted In-App Purchase which was due to any reason was completed but the receipt/information of that transaction of In-App Purchase, could not be sent to your server. Simply do this

Most Important: NEVER CALL [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; BEFORE SAVING THE RECEIPT ON YOUR SERVER, CALL IT WHEN PROCESS OF SAVING THE RECEIPT COMPLETES SUCCESSFULLY, SO THAT IF TRANSACTION GETS INTERRUPTED YOU WOULD HAVE INFORMATION REGARDING IT, NEXT TIME WHEN USER WILL START THE APP.

In your app delegate or in your first view controller or wherever you want, add this: (In my scenario I need to update user data before login so that user could use the special features of the app)

  1. Add #import <StoreKit/StoreKit.h> to your .h file.
  2. Add Delegate <SKPaymentTransactionObserver> to .h file.
  3. Add observer [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; to - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  4. And delegate Method

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

 for (SKPaymentTransaction *transaction in transactions) {
    switch (transaction.transactionState) {
        case SKPaymentTransactionStatePurchasing:
            NSLog(@"Purchasing");
            break;
        case SKPaymentTransactionStatePurchased:
            if ([transaction.payment.productIdentifier isEqualToString:kProductID]) {

                NSLog(@"Purchased ");
                UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
                                          @"Purchase is completed succesfully" message:nil delegate:
                                          self cancelButtonTitle:@"Ok" otherButtonTitles: nil];
                [alertView show];
            }
            [self sendReceiptDataToServer:transaction];
            break;
        case SKPaymentTransactionStateRestored:
            NSLog(@"Restored ");
            [self methodAfterTransactionSuccessful:transaction];
            break;
        case SKPaymentTransactionStateFailed:
            NSLog(@"Purchase failed ");
            break;
        default:
            break;
    }
}}

-(void) sendReceiptDataToServer:(SKPaymentTransaction*)transaction {
    NSLog(@"transaction successful");
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}