Anybody know how to make restore option using IAP.
I use non consumable product for purchase.
I know that I have to implement this delegate methods:
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
but I still can not figure out with a process invoke this method.
I assume that I need to invoke this method [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; before I get invoke callback method.
Can you explain step by step how it work.
You assume right! The only thing you have to invoke is:
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
This restores all the completed transactions that the user has made. For each transaction this SKPaymentTransactionObserver method is called (the same method is also called each time a user makes a purchase):
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
default:
break;
}
}
}
Using the transactionState you can distinguish whether the transaction was a original purchase (SKPaymentTransactionStatePurchased) or a restore (SKPaymentTransactionStateRestored) if you need to do that.
If you need to know when the restore is finished you can use:
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
NSLog(#"%d items restored", queue.transactions.count);
}
Related
I want to know that
do one have to put two buttons separate one for Upgrade and Other for Restore for restore previous transactions??
What steps should be taken by my self to avoid reject chances of App for Restore transaction.
I used below code for restore non-consumable In App Purchase.
If any changes in below code please let me know.
- (void)buyProduct:(SKProduct *)product {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
//[[SKPaymentQueue defaultQueue] addPayment:payment];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState)
{
// Call the appropriate custom method for the transaction state.
case SKPaymentTransactionStatePurchasing:
[self showTransactionAsInProgress:transaction deferred:NO];
break;
case SKPaymentTransactionStateDeferred:
[self showTransactionAsInProgress:transaction deferred:YES];
break;
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
// For debugging
NSLog(#"Unexpected transaction state %#", #(transaction.transactionState));
break;
}
};
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
NSLog(#"Restore Completed Transactions Failed WithError...%#",error);
[self failedMessage:#"Restore Completed Transactions Failed"];
[self stopIndicator];
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSMutableArray *arrPurchasedItemIDs = [[NSMutableArray alloc] init];
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
[arrPurchasedItemIDs addObject:productID];
NSLog(#"arrPurchasedItemIDs : %#",arrPurchasedItemIDs);
}
NSLog(#"Restore Completed");
[self completeMessage:#"Restore Completed"];
[self stopIndicator];
}
Here how you can separate the Restore purchase code :
-(void)doClickRestore {
[APP_DEL doStartSpinner];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
// Purchase success Transaction
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
// Purchase fail Transaction
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
{
[self doStopSpinner];
[self restoreTransaction:transaction];
}
default:
break;
}
}
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction {
[self doStopSpinner];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
Note :
Clicking on Restore purchases will restore all purchased non consumable inApp purchases
Hope it will helps.
These below two mehods are optional but if you want you can use it.
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
I have successfully implemented IAP in ios sdk.
But the problem is when user click on Restore purchase button I initiate the restore by this code:
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
And also I have written this code to handle and initiate the purchase:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for(SKPaymentTransaction *transaction in transactions){
switch(transaction.transactionState){
case SKPaymentTransactionStatePurchasing: NSLog(#"Transaction state -> Purchasing");
//called when the user is in the process of purchasing, do not add any of your own code here.
break;
case SKPaymentTransactionStatePurchased:
//this is called when the user has successfully purchased the package
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[SVProgressHUD dismiss];
[self doRemoveAds]; //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
break;
case SKPaymentTransactionStateRestored:
[SVProgressHUD dismiss];
NSLog(#"Transaction state -> Restored");
//add the same code as you did from SKPaymentTransactionStatePurchased here
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[SVProgressHUD dismiss];
//called when the transaction does not finish
if(transaction.error.code == SKErrorPaymentCancelled){
[SVProgressHUD dismiss];
NSLog(#"Transaction state -> Cancelled");
//the user cancelled the payment ;(
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}
When I select Restore then A dialogs come which ask for password. When I cancel it The progress hud doesn't hides.
How can I do this. How to update UI when user cancel purchase in between the process.
Here is the image.
EDIT now when i use the delegate method
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
[SVProgressHUD dismiss];
}
When It asks for password the SVProgressHUD hides. No matter I press cancel or OK. How to handle this.
And How to update the UI and continue Purchase when user enters correct password.
Your delegate should implement this method:
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
The error in this case will indicate a cancel action.
I have a problem which either allows me to download apple hosted content or finishTransaction but not both;
#pragma mark -
#pragma mark SKPaymentTransactionObserver methods
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchasing:
NSLog(#"Purchasing");
break;
case SKPaymentTransactionStatePurchased:
NSLog(#"Transaction State Purchased");
[[SKPaymentQueue defaultQueue]startDownloads:transaction.downloads];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"Transaction State Failed");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"Transaction State Restored");
[[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
buyButton.enabled = NO;
[buyButton setTitle:#"Purchased"
forState:UIControlStateDisabled];
//break;
default:
break;
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads;
{
for (SKDownload *download in downloads) {
switch (download.downloadState) {
case SKDownloadStateFinished:
NSLog(#"Completing transaction-B");
[self processDownload:download];
[queue finishTransaction:download.transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:download.transaction];
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
break;
case SKDownloadStateActive:
NSLog(#"%f", download.progress);
NSLog(#"%f remaining", download.timeRemaining);
break;
default:
break;
}
}
}
-(void)completeTransaction:(SKPaymentTransaction *)transaction
{ //checked!!
NSLog(#"Complete transaction");
//[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
buyButton.enabled = NO;
[buyButton setTitle:#"Purchased"
forState:UIControlStateDisabled];
}
generally the code works fine;
the product is displayed
I can select to buy
login request
then as per the code above the download works and is processed (moved to the Document directory).
However, if i finishTransaction in updatedTransactions then the download will not occur. (makes sense!)
if I call [self completeTransaction:transaction] to finishTransaction from updatedTransactions I get the same.
If i neglect to call completeTransaction (as per the code) then content is downloaded but then I can't seem to call finish transaction.
where/how do I call finish transaction?
so close but so far!!!
I was calling finish transaction too early and my download was not able to finish.
I implemented IAP in my app with this code:
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
[_loadingIndicator startAnimating];
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self unlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self unlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"Transaction failed");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
default:
break;
}
}
}
When purchase button is pressed and an alert view pops up and asks for Apple ID and password, if user presses Cancel button, the code goes into SKPaymentTransactionStatePurchased: case and the app unlocks the product. What is wrong with the code?
I found what was going on. Local IAP Store tweak from Cydia was running.
I installed it to check if it is possible to bypass ITunes Store with this tweak and find a workaround to prevent it and then I forgot to turn it off.
iOS In-App purchases: When and why does SKPaymentTransactionStateRestored get sent back as status of a transaction?
Does it related to having a recurring subscription plan?
You get SKPaymentTransactionStateRestored transactions instead of SKPaymentTransactionStatePurchased transactions when you are restoring the user's purchases by sending a restoreCompletedTransactions message to the SKPaymentQueue.
General rule is to process both callbacks: for purchase and restore purchase.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
[self recordTransaction: transaction content:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
[self recordTransaction: transaction content:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}