I want the payment transaction to complete once the download transaction has completed but when the download finishes for some reason the transaction state is not completed. Do i have to put the payment transaction back in the queue after the download is complete here is more code.
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for (SKPaymentTransaction * transaction in transactions) {
if(transaction.downloads){
[[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
}else{
switch (transaction.transactionState){
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads;
{
for (SKDownload *download in downloads) {
if (download.downloadState == SKDownloadStateFinished) {
[self processDownload:download]; //handle download
// now we're done
NSLog(#"Download finished");
[[SKPaymentQueue defaultQueue] finishTransaction:download.transaction];
//Do I need to add the payment transaction back in the queue? Im worried the payment will go through twice?
} else if (download.downloadState == SKDownloadStateActive) {
NSString *productID = download.contentIdentifier; // in app purchase identifier
NSTimeInterval remaining = download.timeRemaining; // secs
float progress = download.progress; // 0.0 -> 1.0
NSLog(#"Product ID:%#", productID);
NSLog(#"Time remaining: %f", remaining);
NSLog(#"Download progress percent: %f", progress);
} else { // waiting, paused, failed, cancelled
NSLog(#"Warn: not handled: %d", download.downloadState);
}
}
}
These are my methods I created for my transaction state if it matters at all? None of them are being executed anyways so I don't think it matter but here it is.
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
if(transaction.downloads){
[[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
NSLog(#"Download exists");
}else{
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
Related
I have implemented in-app purchase in my app. My problem is when a new user clicks on restore button, the process is not stopped after completing 1 cycle And execute again and again. After executing 3 to 4 times process get stopped.
I want to stop process instant when a new user doesn't have any purchased transaction.
Here is code of my IAP class.
- (void)restorePurchases
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[ProgressHUD dismiss];
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[ProgressHUD dismiss];
}
// Sent when transactions are removed from the queue (via finishTransaction:).
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[ProgressHUD dismiss];
}
// Called when the transaction status is updated
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchasing:
[self transactionStart:transaction];
break;
case SKPaymentTransactionStatePurchased:
[self transactionComplete:transaction];
[[SKPaymentQueue defaultQueue]
finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self transactionFailed:transaction];
[[SKPaymentQueue defaultQueue]
finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
{
[self transactionRestored:transaction];
[[SKPaymentQueue defaultQueue]
finishTransaction:transaction];
break;
}
default:
break;
}
}
}
#pragma mark - Local method for mange transactions -
-(void)transactionStart:(SKPaymentTransaction *)transaction
{
if([self.delegate respondsToSelector:#selector(startPayment:)]){
[self.delegate startPayment:transaction];
}
}
-(void)transactionComplete:(SKPaymentTransaction *)transaction
{
if([self.delegate respondsToSelector:#selector(completePayment:)]){
[self.delegate completePayment:transaction];
}
}
-(void)transactionFailed:(SKPaymentTransaction *)transaction
{
if ([self.delegate respondsToSelector:#selector(failedPayment:)]) {
[self.delegate failedPayment:transaction];
}
}
-(void)transactionRestored:(SKPaymentTransaction *)transaction
{
if([self.delegate respondsToSelector:#selector(restoredPayment:)]){
[[AppDelegate sharedAppDelegate].arrayConacatResponse addObject:[NSString stringWithFormat:#"transactionRestored method "] ];
[self.delegate restoredPayment:transaction];
}
}
In Restore payment method
- (void)restoredPayment:(SKPaymentTransaction *)transactions
{
if ([transactions.payment.productIdentifier isEqualToString:monthlySubscription]) {
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSError *receiptError;
BOOL isPresent = [receiptURL checkResourceIsReachableAndReturnError:&receiptError];
if (isPresent)
{
NSData *receiptData = [[NSData alloc] initWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
BOOL isSubscriptionPeriodValid = [[DataBase sharedInstance] getSubscriptionStatusFromAppleWithReceipt:receiptData];
if (isSubscriptionPeriodValid)
{
[self removeFromSuperview];
}else{
// Here I want to stop
[CommonUtilities showAlertWithTitle:#"" Message:#"You have no transaction to restore."];
}
}
}
[ProgressHUD dismiss];
}
I having problem to get the previus purchases from app store. I can buy products and validate the receipt but when I try to restore them, the list of purchased transactions is empty. :(
Any idea? Something i forgoten or dosen't it work in sandbox?
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray*)transactions
{
//Is only fired when buying and not restoring.
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)restoreCompletedTransactions {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
}
-(void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self validateReceiptForTransaction:transaction];
_isPurchased = YES;
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
define kMDPulseSubscriptionProductIdentifier #"Your Product ID here...."
SKPaymentTransactionOBserver:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"Prossing.............");
break;
case SKPaymentTransactionStatePurchased:
{
[self completeTransaction:transaction];
NSError* error;
NSDictionary* jsonDict = [NSJSONSerialization
JSONObjectWithData:transaction.transactionReceipt
options:kNilOptions
error:&error];
NSLog(#"JSON Receipt: %#",jsonDict);
[[NSUserDefaults standardUserDefaults] setObject:jsonDict forKey:#"A"];
NSLog(#"Purchase was a Success.....");
}
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
NSLog(#"Purchase cancelled");
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqualToString:kMDPulseSubscriptionProductIdentifier]) {
[self purchaseSubscriptionWithMonths:1];
}
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
NSLog(#"%s","User Cancel.");
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(#"Restore completed transactions finished.");
NSLog(#" Number of transactions in queue: %d", [[queue transactions] count]);
for (SKPaymentTransaction *trans in [queue transactions])
{
NSLog(#" transaction id %# for product %#.", [trans transactionIdentifier], [[trans payment] productIdentifier]);
NSLog(#" original transaction id: %# for product %#.", [[trans originalTransaction] transactionIdentifier],
[[[trans originalTransaction] payment]productIdentifier]);
if ([[[trans payment] productIdentifier] isEqual: kMDPulseSubscriptionProductIdentifier]) {
NSLog(#"Purchase Restored");
// Do your stuff to unlock
}
}
}
- (void)restoreCompletedTransactions
{
NSLog(#"Restore Tapped in transaction process");
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
When you call this method for restoring
-(void)restoreCompletedTransactions
{
NSLog(#"Restore Tapped in transaction process");
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
//Then this delegate Function Will be fired
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
alert = [[UIAlertView alloc] initWithTitle:#"IN APP PURCHASE" message:#"Processing" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
UIActivityIndicatorView *progress= [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(125, 50, 30, 30)];
progress.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[alert addSubview:progress];
[progress startAnimating];
[alert show];
[progress release];
for (SKPaymentTransaction *transaction in queue.transactions)
{
// Use this Commented code to Log and check details about your Products
/**
NSLog(#"%d",queue.transactions.count);
NSMutableArray * purchasedItemIDs = [[NSMutableArray alloc] init];
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
[purchasedItemIDs addObject:productID];
NSLog(#"%#",purchasedItemIDs);
}*/
if( queue.transactions.count == 0 ) {
// User hav't purchased yet.
NSLog(#"User hav't purchased yet.");
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Restore" message:#"You have not purchased yet"
delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:Nil, nil];
[alertView show];
[alertView release];
}else {
// Upgrade to Full version.
NSLog(#"Upgrade to Full version.");
// Do stuffs here
//finally finish transaction
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
}
[alert dismissWithClickedButtonIndex:0 animated:YES];
}
Hope this helps!!
When I opened app it's popup's iTunes login with out any interaction of user.That popup contains my old test user account mail id sometimes my current testuser mail id aslo.I heard that i have to call
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
I called it several times where ever I thought it's needed.But still it's asking password.
And here is my code:
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for(SKPaymentTransaction *transaction in transactions)
{
NSLog(#"Updated transaction %#",transaction);
switch (transaction.transactionState)
{
case SKPaymentTransactionStateFailed:
[self errorWithTransaction:transaction];
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
case SKPaymentTransactionStatePurchasing:
NSLog(#"Purchasing...");
break;
case SKPaymentTransactionStatePurchased:
{
if(no need to download)
{
// I will download the content later by restoring transactions.
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
}
else
{
[[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
}
}
case SKPaymentTransactionStateRestored:
{
if(no need to download content)
{
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
}
else
{
[[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
}
}
break;
default:
break;
}
}
-(void) paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads
{
for (SKDownload *download in downloads)
{
switch (download.downloadState)
{
case SKDownloadStateActive:
{
// showing download progress
}
case SKDownloadStateCancelled:
{
[[SKPaymentQueue defaultQueue] finishTransaction:download.transaction];
}
case SKDownloadStateFailed:
{
// showing alert and finish transactions
[[SKPaymentQueue defaultQueue] finishTransaction:download.transaction];
}break;
case SKDownloadStateFinished:
{
// processing content and finished the transactions
[[SKPaymentQueue defaultQueue] finishTransaction:download.transaction];
}break;
}
}
}
Is this correct and is there any place to call finishTransaction: method.Please let me know is any...
thanks in advance..
I am new on iOS, i am trying to download 3 products from my service but the user needs to buy these products, i have 3 buttons, i click first one and after enter username and password, it is downloading, then i click second product, firstly it is downloading first product then second product,then i click third product, it is downloading first->second->third product.How can i remove the first product from the queue after download first product? Sorry for my bad english.
Thanks.
Best Regards.
- (IBAction)downloadButtonPressed:(id)sender {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:#"com.company.testtest123"];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue]addPayment:payment];
}
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
// Count down
#synchronized(self) {
NSInteger numOfTransaction = [[NSUserDefaults standardUserDefaults] integerForKey:#"NumberOfTransactions"];
[[NSUserDefaults standardUserDefaults] setInteger:numOfTransaction-1 forKey:#"NumberOfTransactions"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
SKProduct *validProduct =nil;
int count = [response.products count];
if (count>0) {
validProduct =[response.products objectAtIndex:0];
}else if(!validProduct){
NSLog(#"No products avaible");
}
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStatePurchased:{
//download kismi buraya
NSLog(#"_mainDict = %#",_mainDict);
float _csize = (float) 0 / 1024;
float _tsize = (float) [[_mainDict valueForKey:#"Size"] integerValue] / 1024;
NSString *strDownload = [NSString stringWithFormat:#"downloading \n (%0.2f MB/%.2f MB)",_csize,_tsize];
[SVProgressHUD showProgress:0 status:strDownload maskType:SVProgressHUDMaskTypeBlack];
[service servicePicList];
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
}
case SKPaymentTransactionStateRestored:{
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
}
case SKPaymentTransactionStateFailed:
{
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"An error encountered");
}
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
}
}
}
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
i solved the my question :) i just added this line [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; and now it is working ...:)
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStatePurchased:{
//I added this line.
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
NSLog(#"_mainDict = %#",_mainDict);
float _csize = (float) 0 / 1024;
float _tsize = (float) [[_mainDict valueForKey:#"Size"] integerValue] / 1024;
NSString *strDownload = [NSString stringWithFormat:#"downloading \n (%0.2f MB/%.2f MB)",_csize,_tsize];
[SVProgressHUD showProgress:0 status:strDownload maskType:SVProgressHUDMaskTypeBlack];
[service servicePicList];
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
}
case SKPaymentTransactionStateRestored:{
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
}
case SKPaymentTransactionStateFailed:
{
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"An error encountered");
}
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
}
}
}
}
I have been struggling with this issue since 2 days. I have implemented In App purchase successfully with downloadable content . But whenever I delete app and restore the purchase , restore doesn't actually download the content. But if I add download code in restoreTransaction it never calls finish transaction which cause the app to behave weirdly(which is resonable) , every time I delete app and reinstall it. Any help is appreciated!!
Please let me know if anyone needs further explanation. Thanks!!
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
if(transaction.downloads)
[self download:transaction];
else
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
if(transaction.downloads)
[self restoreDownload:transaction];
else
[self restoreTransaction:transaction];
default:
break;
}
};
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads
{
for (SKDownload *download in downloads)
{
switch (download.downloadState) {
case SKDownloadStateActive:
NSLog(#"Download progress = %f",
download.progress);
NSLog(#"Download time = %f",
download.timeRemaining);
break;
case SKDownloadStateFinished:
{
NSLog(#"URL %#",download.contentURL);
}
break;
default:
break;
}
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreDownload:(SKPaymentTransaction *)transaction {
NSLog(#"restoreDownload...");
//[self validateReceiptForTransaction:transaction];
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
}
- (void)download:(SKPaymentTransaction *)transaction {
NSLog(#"Download Content...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
//[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}