IOS StoreKit - When to Call - (void) restoreCompletedTransactions?

I have many IAP IAP purchases inside my application. Users can purchase them just fine.

My problem is that I integrate with Flurry to track real purchases versus restoring purchases, but my SKPaymentTransaction transactionState always returns as SKPaymentTransactionStatePurchased , not SKPaymentTransactionStateRestored .

Apparently SKPaymentTransactionStateRestored is only called when - (void)restoreCompletedTransactions , but when do I call this method?

My thought process is that the purchase should look something like this: 1) The user selects the product, 2) The user asks if they want to purchase the product for the amount of X. 3) The server checks if the user has purchased before, and if they restored it, by setting SKPaymentTransactionStateRestored . Otherwise, complete the transaction and set SKPaymentTransactionStatePurchased . This seems to be wrong, and I can call - (void)restoreCompletedTransactions somewhere in the middle

Thanks,

Will

+7
source share
4 answers

EDIT:

I originally posted a very long unnecessary method to get what I needed, but as you will see below, Matt helped me figure out the variable I was looking for.

As an example, suppose a user who previously purchased my application bought all inaccessible IAPs and then uninstalled the application. When a user reinstalls the application, I want to be able to determine when they will start “buying” products again, will it be the original (first) purchase or purchase of restoration?

I implemented the “Restore all purchases” button, but let the user ignore / not see it and try to select the product they bought before.

As with a regular purchase, I do the following:

 if ([SKPaymentQueue canMakePayments]) { self.productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:productID]]; self.productRequest.delegate = self; [self.productRequest start]; } else { //error message here } 

After the user has registered in their iTunes account, the application will inform them that they have already purchased this, and now it will be restored. The following delegate method will be called:

  -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: { [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; if(transaction.originalTransaction) { NSLog(@"Just restoring the transaction"); } else { NSLog(@"First time transaction"); } break; } default: break; } } } 

Regardless of whether the transaction was a reinstatement or purchase for the first time, transaction.transactionState will equal SKPaymentTransactionStatePurchased .

Now from now on we can determine whether this purchase is an original or replacement purchase?

Just. As seen above, just check if transaction.originalTransaction initialized. Apple Note: // Valid only if the status is SKPaymentTransactionStateRestored.

If SKPaymentTransaction originalTransaction initialized, it means that there was a previous transaction. Otherwise, this transaction is original!

Thanks again to Matt for pointing me in the right direction and making the logic a lot cleaner!

+7
source

The canonical expectation looks like you are providing a restore button that calls restoreCompletedTransactions . As for what happens, if the user ignores this and simply tries to go through your purchase interface to return the functions that he has already acquired, you may be worried about yourself uselessly; docs say:

If a user tries to purchase a restored product (instead of using the recovery interface that you deployed), the application receives a normal transaction for this item, not a recovery transaction. However, the user is not charging again for this product. Your application must process these transactions identically to the transactions of the original transaction.

Dialog boxes interacting with the user will be displayed in the repository ("Have you already bought this, do you want to download it again for free?"), And a notification about free re-purchase will paymentQueue:updatedTransactions: in your paymentQueue:updatedTransactions: as usual, and you can use it originalTransaction.transactionIdentifier to identify it with the original purchase.

+6
source

If you make purchases in the application, you need to place the restore button somewhere in your application , otherwise this will be the reason for the rejection. You can look Get a list of purchased products, inApp Purchase iPhone for one case.

EDIT 2: Perhaps there are other ways than putting a button to avoid rejection. One of the methods suggested in the comments is to restore the application to launch, which seems to me sufficient to comply with Apple recommendations.

We also look at the last part of this lesson, which mentions the same problem, and shows one simple way to implement it: http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios- 6-tutorial

EDIT:

 //Draft implementation of transaction observer delegate -(void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction * transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: ... case SKPaymentTransactionStateFailed: ... case SKPaymentTransactionStateRestored: ... default: break; } }; } 
+2
source

The Apple IOS SDK documentation is actually misleading on this issue as it says that:

 @property(nonatomic, readonly) SKPaymentTransaction *originalTransaction The contents of this property are undefined except when transactionState is set to SKPaymentTransactionStateRestored. 

The problem is that when the user clicks the purchase button, looks at the purchase process and finally receives a message that he has already paid and that he can download it for free, your supervisor will not send you SKPaymentTransactionStateRestored. It really sends you SKPaymentTransactionStatePurchased.

The good news is that despite the documentation, you will actually get the originalTransaction property, even if your transaction status is SKPaymentTransactionStatePurchased. Just check if this property is null or not, and you will find out if the transaction was a new purchase or a purchased old purchase for free.

0
source

All Articles