Problem in In-App purchase-consumable model - iphone

I have created a non-consumable in-app purchase item and now I want to create a consumable in-app purchase item by which a user to buy it every time by using In-App purchase. Now what will have to change in following code in different model:-
in InApp purchase manager.m:
#implementation InAppPurchaseManager
//#synthesize purchasableObjects;
//#synthesize storeObserver;
#synthesize proUpgradeProduct;
#synthesize productsRequest;
//BOOL featureAPurchased;
//BOOL featureBPurchased;
//static InAppPurchaseManager* _sharedStoreManager; // self
- (void)dealloc {
//[_sharedStoreManager release];
//[storeObserver release];
[super dealloc];
}
- (void)requestProUpgradeProductData
{
NSSet *productIdentifiers = [NSSet setWithObject:#"com.comp_name.iWorkOut1" ];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
// we will release the request object in the delegate callback
}
#pragma mark -
#pragma mark SKProductsRequestDelegate methods
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
//NSArray *products = response.products;
//proUpgradeProduct = [products count] == 1 ? [[products firstObject] retain]: nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#", proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#", proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#", proUpgradeProduct.price);
NSLog(#"Product id:%#", proUpgradeProduct.productIdentifier);
}
/*for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}*/
//finally release the reqest we alloc/init’ed in requestProUpgradeProductData
[productsRequest release];
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}
#pragma -
#pragma Public methods
/* call this method once on startup*/
- (void)loadStore
{
/* restarts any purchases if they were interrupted last time the app was open*/
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
/* get the product description (defined in early sections)*/
[self requestProUpgradeProductData];
}
/* call this before making a purchase*/
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
/* kick off the upgrade transaction*/
- (void)purchaseProUpgrade
{
SKPayment *payment = [SKPayment paymentWithProductIdentifier:#"1212121"];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma -
#pragma Purchase helpers
/* saves a record of the transaction by storing the receipt to disk*/
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseProUpgradeProductId])
{
/* save the transaction receipt to disk*/
[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt" ];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
/* enable pro features*/
- (void)provideContent:(NSString *)productId
{
if ([productId isEqualToString:kInAppPurchaseProUpgradeProductId])
{
/* enable the pro features*/
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"isProUpgradePurchased" ];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{
// /* remove the transaction from the payment queue.*/
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, #"transaction" , nil];
if (wasSuccessful)
{
/* send out a notification that we’ve finished the transaction*/
[[NSNotificationCenter defaultCenter]postNotificationName:kInAppPurchaseManagerTransactionSucceededNotification object:self userInfo:userInfo];
}
else
{
/* send out a notification for the failed transaction*/
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
[self recordTransaction:transaction];
[self provideContent:transaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
[self recordTransaction:transaction.originalTransaction];
[self provideContent:transaction.originalTransaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
/* error!*/
[self finishTransaction:transaction wasSuccessful:NO];
}
else
{
/* this is fine, the user just cancelled, so don’t notify*/
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
- (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;
}
}
}
#end
in SKProduct.m:-
#implementation SKProduct (LocalizedPrice)
- (NSString *)localizedPrice
{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:self.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:self.price];
[numberFormatter release];
return formattedString;
}

I suggest you look at InventoryKit, which provides support for non-consumable, consumable and subscription in app purchases, and is open source.

Related

How to get check if a product is already purchased using in app purchase in iOS?

I have an application with in app integration in it. In my app I have two buttons for paid app buy and subscribe. When the user clicks on buy it goes check for apple validation and the buy the product.
This works properly but when the product is purchased my buy button should change to 'done' and when the application is next time run the buy button should not be visible for that particular product. Instead 'done' button should be shown. My problem is when a product is purchased the buy button is shown instead of done button.
This is my code:
-(void)checkPurchasedItems
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
//Then this delegate Function Will be fired
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(#"received restored transactions: %i", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
NSLog(#"%#",productID);
}
}
// called when a transaction has failed
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
// error!
[self finishTransaction:transaction wasSuccessful:NO];
if (transaction.error.code == SKErrorClientInvalid) {
//[self showAlert:#"In-App Purchase" withMessage:INVALID_CLIENT];
}
else if (transaction.error.code == SKErrorPaymentInvalid) {
//[self showAlert:#"In-App Purchase" withMessage:PAYMENT_INVALID];
}
else if (transaction.error.code == SKErrorPaymentNotAllowed) {
//[self showAlert:#"In-App Purchase" withMessage:PAYMENT_NOT_ALLOWED];
}
else if (transaction.error.code == SKErrorPaymentCancelled) {
// [self showAlert:#"In-App Purchase" withMessage:#"This device is not allowed to make the payment."];
NSLog(#"User Cancellation.");
}
else {
// SKErrorUnknown
NSLog(#"Unknown Reason.");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Transaction Status" message:#"Transaction Failed due to unknown reason" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
else {
// this is fine, the user just cancelled, so don’t notify
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Transaction Status" message:#"Transaction failed due to some reason" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
// [alert show];
// return;
//[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
- (void)provideContent:(NSString *)productId
{
if ([productId isEqualToString:kMyFeatureIdentifier4])
{
// enable the pro features
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"isStorePurchased"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
else if([productId isEqualToString:kMyFeatureIdentifier3])
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"isStoreSubscribed"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
}
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
NSData *receiptData = [NSData dataWithData:transaction.transactionReceipt];
transactionreceipt = [Base64 encode:receiptData];
NSLog(#"encoded String :%#",transactionreceipt);
if ([transaction.payment.productIdentifier isEqualToString:kMyFeatureIdentifier4])
{
// save the transaction receipt to disk
[[NSUserDefaults standardUserDefaults] setValue:transactionreceipt forKey:#"proUpgradeTransactionReceipt"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if ([transaction.payment.productIdentifier isEqualToString:kMyFeatureIdentifier3])
{
[[NSUserDefaults standardUserDefaults] setValue:transactionreceipt forKey:#"proUpgradeTransactionReceipt"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{
NSUserDefaults *userdefaults = [NSUserDefaults standardUserDefaults];
transactionreceipt = [userdefaults valueForKey:#"proUpgradeTransactionReceipt"];
NSLog(#"%#",transactionreceipt);
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, #"transaction" , nil];
if (wasSuccessful)
{
// send out a notification that we’ve finished the transaction
[self sendRequest];
[[NSNotificationCenter defaultCenter] postNotificationName:#"PurchaseSuccess" object:self userInfo:userInfo];
[easytblView reloadData];
}
else
{
// send out a notification for the failed transaction
// [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
}
}
- (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;
case SKPaymentTransactionStatePurchasing:
NSLog(#"Purchasing...");
break;
default:
break;
}
}
}
// called when a transaction has been restored and and successfully completed
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
[self recordTransaction:transaction.originalTransaction];
[self provideContent:transaction.originalTransaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when the transaction was successful
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
[self recordTransaction:transaction];
[self provideContent:transaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
-(void)buyProduct
{
if (arrPurchaseProducts.count>0)
{
SKProduct *selectedProduct = [arrPurchaseProducts objectAtIndex:0];
SKPayment *payment = [SKPayment paymentWithProduct:selectedProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
//selectedProduct = nil;
// payment = nil;
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSLog(#"IN-APP:productsRequest");
arrPurchaseProducts = [[NSArray alloc]initWithArray:response.products];
if ([arrPurchaseProducts count] == 1)
{
SKProduct *selectedProduct = [arrPurchaseProducts objectAtIndex:0];
SKPayment *payment = [SKPayment paymentWithProduct:selectedProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
//responseStatus = 1;
// if ([purchaseButton.title isEqualToString:#" "])
// {
NSLog(#"Purchase had been attempted already.");
// }
}
if ([arrPurchaseProducts count]>0)
{
product = [arrPurchaseProducts objectAtIndex:0];
NSLog(#"Price: %.2f",product.price.floatValue);
NSLog(#"Price Locale: %#",product.priceLocale.localeIdentifier);
NSLog(#"Product Identifier: %#",product.productIdentifier);
NSLog(#"IN-APP:array count: %i", [arrPurchaseProducts count]);
[request autorelease];
NSLog(#"IN-APP:productsRequest END");
}
//[arrPurchaseProducts release];
// arrPurchaseProducts = nil;
}
- (void)requestProductData
{
NSLog(#"IN-APP:requestProductData");
SKProductsRequest *request;
if (isSubscribe==YES)
{
request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:kMyFeatureIdentifier3]];
}
else{
request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:kMyFeatureIdentifier3]];
}
request.delegate = self;
[request start];
NSLog(#"IN-APP:requestProductData END");
}
-(IBAction)buynow:(id)sender
{
isSubscribe=NO;
isviewloadedforfirsttime=NO;
if([SKPaymentQueue canMakePayments])
{
// if (![[NSUserDefaults standardUserDefaults] valueForKey:#"isStorePurchased"])
// {
[self requestProductData];
//}
NSLog(#"IN-APP:can make payments");
}
else {
NSLog(#"IN-APP:can't make payments");
}
[self performSelector:#selector(buyProduct) withObject:nil afterDelay:2.0];
}
- (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;
case SKPaymentTransactionStatePurchasing:
NSLog(#"Purchasing...");
break;
default:
break;
}
}
}
you yourself have inside your code the case of SKPaymentTransactionStateRestored which tells when a product is being restored.
You need to add code that executes the restoreCompletedTransactions method...
add the [self checkPurchasedItems]; in the App's initial launch somewhere like the root view controller's viewDidLoad method to get the purchase button logic to show up..
keep the buttons.enabled = NO... this way the buttons are only enabled when the return methods arrive telling us what we already purchased... you should store those booleans for offline mode.
when the transactions are restored mark the buttons as enabled and/or hidden appropriately.
good luck!

Implementing in app purchase and its restoration in iphone

I have implemented in app purchase in one of my application using MKStoreManager.Now got a new guideline from apple that if you are doing in app purchase,you have to give the user,the option for restoring the already purchased application.So i have done like this.On the 'restore' button click,this method is called.
- (void) checkPurchasedItems
{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
and from here,this method is fired
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSMutableArray* purchasableObjects = [[NSMutableArray alloc] init];
NSLog(#"received restored transactions: %i", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
[purchasableObjects addObject:productID];
}
}
But now i have a doubt that how can i check this restoration is working or not.Can anyone guide me.thanks in advance.
Here is how you implement the Restoration
- (void)loadStore
{
// restarts any purchases if they were interrupted last time the app was open
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// get the product description (defined in early sections)
[self requestProUpgradeProductData];
}
- (void)requestProUpgradeProductData
{
NSSet *productIdentifiers = [NSSet setWithObject:kInAppPurchaseProUpgradeProductId];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
// we will release the request object in the delegate callback
}
After this it will call this method
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
proUpgradeProduct = [products count] == 1 ? [[products objectAtIndex:0] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
if ([self canMakePurchases]) {
if ([self respondsToSelector:#selector(purchaseProUpgrade)]) {
[self purchaseProUpgrade];
}
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[self languageSelectedStringForKey:#"Error"] message:#"Cannot connect to Store.\n Please Enable the Buying in settings" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
[SVProgressHUD dismiss];
[cancelButton setEnabled:YES];
[buyNowButton setEnabled:YES];
[restoreButton setEnabled:YES];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning" message:#"Error occured" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
NSLog(#"Invalid product id: %#" , invalidProductId);
}
// finally release the reqest we alloc/init’ed in requestProUpgradeProductData
[productsRequest release];
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
[self recordTransaction:transaction];
[self provideContent:transaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
- (void)purchaseProUpgrade
{
[SVProgressHUD showInView:self.view status:[self languageSelectedStringForKey:#"Connecting Store"] networkIndicator:YES];
SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseProUpgradeProductId])
{
// save the transaction receipt to disk
[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt" ];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
Finally this method
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful: (BOOL)wasSuccessful
{
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, #"transaction" , nil];
if (wasSuccessful)
{
//Write your transaction complete statement required for your project
}

Inapppurchase SKProductsRequest Crash my application in ios5

my store kit class crash for SKProductsRequest start i am not experienced inapppurchase concept my class working perfectly in iOS 4.3 sdk not iOS 5 sdk. i am not getting error on console and my application crashed before after SKProductsRequest start please can any one help me how to fix this issue here's my code:
#import "StorePurchase.h"
#implementation StorePurchase
#synthesize request = _request;
- (void)loadStore {
if ([SKPaymentQueue canMakePayments]) {
NSLog(#"Parental-controls are disabled");
self.request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:#"com.mycompany.app"]];
self.request.delegate = self;
[self.request start];
}
else {
NSLog(#"Parental-controls are enabled");
}
}
static bool hasAddObserver=NO;
- (void)purchase {
if (!hasAddObserver) {//flag to fix this bug
/*=====================================*/
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
hasAddObserver=YES;
}
SKMutablePayment *payment = [[SKMutablePayment alloc] init] ;
payment.productIdentifier = #"com.mycompany.app";
payment.quantity = 1;
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
SKProduct *validProduct = nil;
int count = [response.products count];
if (count > 0)
{
validProduct = [response.products objectAtIndex:0];
NSLog(#"Products found,Magic Box!");
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:#"Product Found..!"];
} else if (!validProduct)
{
NSLog(#"No products available");
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:#"Product Not Found..!"];
}
}
- (void)provideContent:(NSString *)productIdentifier {
NSLog(#"Toggling flag for: %#", productIdentifier);
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
//[_purchasedProducts addObject:productIdentifier];
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchasedNotification object:productIdentifier];
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
//[self recordTransaction: transaction];
[self provideContent: transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
//[self recordTransaction: transaction];
[self provideContent: transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchaseFailedNotification object:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
NSLog(#"<><Canceled!><>");
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchaseCanceledNotification object:error];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
// [self completeTransaction:transaction];
break;
case SKPaymentTransactionStatePurchased:
NSLog(#"Purchased");
[self completeTransaction:transaction];
//[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
//[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(#"Restored");
[self restoreTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"An error encounterd");
}
else {
NSLog(#"Cancelled!");
}
//[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[self failedTransaction:transaction];
break;
}
}
}
i am call the loadStore method after getting the error
SKProductsRequest doesn't call the - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response method before crash my app!
What's Wrong in my code how to fix!
Thanks in Advance!
i have done code for inapppurchase in my application using this code gets no issues
- (void)loadStore
{
// restarts any purchases if they were interrupted last time the app was open
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// get the product description (defined in early sections)
[self requestProUpgradeProductData];
}
- (void)requestProUpgradeProductData
{
NSSet *productIdentifiers = [NSSet setWithObject:kInAppPurchaseProUpgradeProductId];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
// we will release the request object in the delegate callback
}
After this it will call this method
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
proUpgradeProduct = [products count] == 1 ? [[products objectAtIndex:0] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
if ([self canMakePurchases]) {
if ([self respondsToSelector:#selector(purchaseProUpgrade)]) {
[self purchaseProUpgrade];
}
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[self languageSelectedStringForKey:#"Error"] message:#"Cannot connect to Store.\n Please Enable the Buying in settings" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
[SVProgressHUD dismiss];
[cancelButton setEnabled:YES];
[buyNowButton setEnabled:YES];
[restoreButton setEnabled:YES];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning" message:#"Error occured" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
NSLog(#"Invalid product id: %#" , invalidProductId);
}
// finally release the reqest we alloc/init’ed in requestProUpgradeProductData
[productsRequest release];
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
[self recordTransaction:transaction];
[self provideContent:transaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
- (void)purchaseProUpgrade
{
[SVProgressHUD showInView:self.view status:[self languageSelectedStringForKey:#"Connecting Store"] networkIndicator:YES];
SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseProUpgradeProductId])
{
// save the transaction receipt to disk
[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt" ];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
Finally this method
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, #"transaction" , nil];
if (wasSuccessful)
{
//Write your transaction complete statement required for your project
}
Follow any of this two Tutorieals:
Tutorial for in app purchase
OR follow RMStore Verification: Here.

Consumable product is acting strange and making duplicate requests.. What could be the cause?

I am doing in app purchase on two of my controllers say view1,view2.
when I buy a consumable product in view1 everything works fine.
but when I buy another consumable product on view2 after the transaction is complete it fires the old request I made on view1 and not the new view2 request. And vice versa
Also if I rebuy the same product from same controller after rebuying it makes the request which I made before it...
Example:
Controller1:
product1 > buy > someaction(say hit to the server with some params like "name= ABC")
user enters name and is dynamic
Again,
if I rebuy it
product1 > buy > someaction(say hit to the server with some params like "name= XYZ")
Now when product is bought it hits the server with old params "name=ABC" and not "name=XYZ" what user enters again..
product
**heres the code m using in my IAPhelper**
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = [productIdentifiers retain];
// Check for previously purchased products
NSMutableSet * purchasedProducts = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[purchasedProducts addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
}
NSLog(#"Not purchased: %#", productIdentifier);
}
self.purchasedProducts = purchasedProducts;
}
return self;
}
- (void)requestProducts {
self.request = [[[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers] autorelease];
_request.delegate = self;
[_request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Received products results...");
self.products = response.products;
self.request = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:_products];
}
- (void)recordTransaction:(SKPaymentTransaction *)transaction {
// TODO: Record the transaction on the server side...
}
- (void)provideContent:(NSString *)productIdentifier {
NSLog(#"Toggling flag for: %#", productIdentifier);
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
// [_purchasedProducts addObject:productIdentifier];
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchasedNotification object:productIdentifier];
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self recordTransaction: transaction];
[self provideContent: transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self recordTransaction: transaction];
[self provideContent: transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchaseFailedNotification object:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (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)buyProductIdentifier:(NSString *)productIdentifier {
NSLog(#"Buying in IAPHelper %#...", productIdentifier);
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)dealloc
{
[_productIdentifiers release];
_productIdentifiers = nil;
[_products release];
_products = nil;
[_purchasedProducts release];
_purchasedProducts = nil;
[_request release];
_request = nil;
[super dealloc];
}
#end
**heres the code m using in my view1controller**
-(void)viewDidAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:kProductPurchasedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector: #selector(productPurchaseFailed:) name:kProductPurchaseFailedNotification object: nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productsLoaded:) name:kProductsLoadedNotification object:nil];
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
} else {
if ([InAppRageIAPHelper sharedHelper].products == nil) {
[[InAppRageIAPHelper sharedHelper] requestProducts];
// self.hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
_hud.labelText = #"Loading comics...";
// [self performSelector:#selector(timeout:) withObject:nil afterDelay:30.0];
}
}
}
- (void)productPurchased:(NSNotification *)notification {
[self removeLoader];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
NSString *productIdentifier = (NSString *) notification.object;
NSLog(#"Purchased: %#", productIdentifier);
NSString *inaapstatus =[[NSUserDefaults standardUserDefaults] objectForKey:#"inaapstatus"];
if ([inaapstatus isEqualToString:#"addabeep"]) {
NSUserDefaults *inaapstatus = [NSUserDefaults standardUserDefaults];
[inaapstatus setValue:#"reset" forKey:#"inaapstatus"];
[[NSUserDefaults standardUserDefaults]synchronize];
if ([productIdentifier isEqualToString:#"com.beepbXXXXXXXX"]) {
duration = #"30";
NSUserDefaults *purchased = [NSUserDefaults standardUserDefaults];
[purchased setValue:#"YES" forKey:#"purchased"];
[[NSUserDefaults standardUserDefaults] synchronize];
[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:#selector(purchased:) userInfo:nil repeats:NO];
}
else {
duration = #"7";
NSUserDefaults *purchased = [NSUserDefaults standardUserDefaults];
[purchased setValue:#"YES" forKey:#"purchased"];
[[NSUserDefaults standardUserDefaults] synchronize];
[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:#selector(purchased:) userInfo:nil repeats:NO];
}
}
else
{
}
// [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(makeMyBeeps1Request:) userInfo:nil repeats:NO];
}
-(void)purchased:(NSTimer *)timer
{
NSString *purchased =[[NSUserDefaults standardUserDefaults] objectForKey:#"purchased" ];
if ([purchased isEqualToString:#"YES"]) {
[self doLogin];
NSUserDefaults *purchased1 = [NSUserDefaults standardUserDefaults];
[purchased1 setValue:#"NO" forKey:#"purchased"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
- (void)productPurchaseFailed:(NSNotification *)notification {
[self removeLoader];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
// [MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
SKPaymentTransaction * transaction = (SKPaymentTransaction *) notification.object;
if (transaction.error.code != SKErrorPaymentCancelled) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Oops error occcured!"
message:transaction.error.localizedDescription
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil] autorelease];
[alert show];
//[alert release];
}
}
- (void)dismissHUD:(id)arg {
NSLog(#"dismissHUD");
[self removeLoader];
}
- (void)productsLoaded:(NSNotification *)notification {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
// [MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
[self removeLoader];
}
- (void)timeout:(id)arg {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Timeout!"
message:#"Please try again later."
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil] autorelease];
[alert show];
// [alert release];
//[self performSelector:#selector(dismissHUD:) withObject:nil afterDelay:3.0];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
//self.hud = nil;
// self.table.hidden = TRUE;
inappObserver = [[InAppPurchaseObserver alloc] init];
if ([SKPaymentQueue canMakePayments]) {
// Yes, In-App Purchase is enabled on this device!
// Proceed to fetch available In-App Purchase items.
// Replace "Your IAP Product ID" with your actual In-App Purchase Product ID,
// fetched from either a remote server or stored locally within your app.
SKProductsRequest *prodRequest= [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObjects:#"com.beepccccceek1",#"com.beepbcccccnth1", nil ]];
prodRequest.delegate = self;
[prodRequest start];
} else {
// Notify user that In-App Purchase is disabled via button text.
[inappButton setTitle:#"In-App Purchase is Disabled" forState:UIControlStateNormal];
inappButton.enabled = NO;
}
[super viewDidLoad];
[self initializeView];
}
**view2 controller**
#pragma mark - View lifecycle
-(void)viewDidAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:kProductPurchasedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector: #selector(productPurchaseFailed:) name:kProductPurchaseFailedNotification object: nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productsLoaded:) name:kProductsLoadedNotification object:nil];
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
} else {
if ([InAppRageIAPHelper sharedHelper].products == nil) {
[[InAppRageIAPHelper sharedHelper] requestProducts];
// self.hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
[self performSelector:#selector(timeout:) withObject:nil afterDelay:30.0];
}
}
}
- (void)viewDidLoad
{
inappObserver = [[InAppPurchaseObserver alloc] init];
if ([SKPaymentQueue canMakePayments]) {
// Yes, In-App Purchase is enabled on this device!
// Proceed to fetch available In-App Purchase items.
// Replace "Your IAP Product ID" with your actual In-App Purchase Product ID,
// fetched from either a remote server or stored locally within your app.
SKProductsRequest *prodRequest= [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObjects:#"com.beepbccccecc1",#"com.beepcccp.month1",#"com.beeccccep.free", nil ]];
prodRequest.delegate = self;
[prodRequest start];
} else {
// Notify user that In-App Purchase is disabled via button text.
[inappButton setTitle:#"In-App Purchase is Disabled" forState:UIControlStateNormal];
inappButton.enabled = NO;
}
[super viewDidLoad];
[self initializeView];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
self.mTableView = nil;
self.numberofbeeps = nil;
self.segmentedControl = nil;
}
- (void)productPurchased:(NSNotification *)notification {
[self removeLoader];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
NSString *productIdentifier = (NSString *) notification.object;
NSLog(#"Purchased: %#", productIdentifier);
NSString *inaapstatus =[[NSUserDefaults standardUserDefaults] objectForKey:#"inaapstatus"];
if ([inaapstatus isEqualToString:#"expired"]) {
NSUserDefaults *inaapstatus = [NSUserDefaults standardUserDefaults];
[inaapstatus setValue:#"reset" forKey:#"inaapstatus"];
[[NSUserDefaults standardUserDefaults]synchronize];
if ([productIdentifier isEqualToString:#"com.beepbccccnth1"]) {
duration = #"30";
NSUserDefaults *purchased = [NSUserDefaults standardUserDefaults];
[purchased setValue:#"YES" forKey:#"purchased"];
[[NSUserDefaults standardUserDefaults] synchronize];
[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:#selector(purchased:) userInfo:nil repeats:NO];
}
else {
duration = #"7";
NSUserDefaults *purchased = [NSUserDefaults standardUserDefaults];
[purchased setValue:#"YES" forKey:#"purchased"];
[[NSUserDefaults standardUserDefaults] synchronize];
[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:#selector(purchased:) userInfo:nil repeats:NO];
}
}
else
{
}
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(makeMyBeeps1Request:) userInfo:nil repeats:NO];
}
-(void)purchased:(NSTimer *)timer
{
NSString *purchased =[[NSUserDefaults standardUserDefaults] objectForKey:#"purchased" ];
if ([purchased isEqualToString:#"YES"]) {
[self durationRequest];
NSUserDefaults *purchased = [NSUserDefaults standardUserDefaults];
[purchased setValue:#"NO" forKey:#"purchased"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
- (void)productPurchaseFailed:(NSNotification *)notification {
[self removeLoader];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
// [MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
SKPaymentTransaction * transaction = (SKPaymentTransaction *) notification.object;
if (transaction.error.code != SKErrorPaymentCancelled) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Oops error occcured!"
message:transaction.error.localizedDescription
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil] autorelease];
[alert show];
//[alert release];
}
}
- (void)dismissHUD:(id)arg {
NSLog(#"dismissHUD");
[self removeLoader];
}
- (void)productsLoaded:(NSNotification *)notification {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
// [MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
[self removeLoader];
}
- (void)timeout:(id)arg {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Timeout!"
message:#"Please try again later."
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil] autorelease];
[alert show];
// [alert release];
//[self performSelector:#selector(dismissHUD:) withObject:nil afterDelay:3.0];
}
Any help is appreciated thnx a lot guys
I don't recommend you having more than one active purchase delegate at a time. Try to deactivate the first delegate in view1 after all process succeeds, and then make the purchase in view2, see if that happens again
Sorry couldnt get time to update my answer.
I solved it by removingObserver of the notification posted just after the notification request completes...
Hope it helps someone..

iPhone In App Purchase issue

I use In App Purchase in my application, but I have problem when I'm testing that. I have four consumable products. Information about that products I show in tableview. Sometimes when I click a button to buy some product I get a transaction state SKPaymentTransactionStateFailed in updatedTransaction function but transaction.error localizedFailureReason is always null.
Once I noticed that one transaction was updated two times (in updatedTransaction function transaction with the same transactionIdentifier comes, state of transaction is SKPaymentTransactionStatePurchased) - is then that product two times purchased?.
So I have no idea where is the problem. Please help me.
I use that class to manage In App Purchase:
#implementation InAppPurchaseManager
#synthesize upgradeProducts;
#synthesize productsRequest;
#synthesize delegate;
- (id) init
{
self = [super init];
if (!self) return nil;
if ([SKPaymentQueue canMakePayments]) {
[self loadStore];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
upgradeProducts = [[NSMutableArray alloc] init];
delegate = nil;
return self;
}
+ (InAppPurchaseManager *) sharedInstance
{
static InAppPurchaseManager *myInstance = nil;
if (nil == myInstance) {
myInstance = [[[self class] alloc] init];
}
return myInstance;
}
- (void) loadStore
{
NSSet *productsIdentifiers = [[NSSet alloc] initWithObjects:PRODUCT_1_ID, PRODUCT_2_ID, PRODUCT_3_ID, PRODUCT_4_ID, nil];
[self requestUpgradeProductsData:productsIdentifiers];
[productsIdentifiers release];
}
- (void) requestUpgradeProductsData:(NSSet *) productIdentifiers
{
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
}
- (void) productsRequest:(SKProductsRequest *) request didReceiveResponse:(SKProductsResponse *) response
{
[upgradeProducts removeAllObjects];
for (int i = 0; i < [response.products count]; i++) {
SKProduct *product = [response.products objectAtIndex:i];
UpgradeProduct *upgradeProduct = [[UpgradeProduct alloc] initWithProductID:product.productIdentifier];
upgradeProduct.title = product.localizedTitle;
upgradeProduct.description = product.localizedDescription;
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *price = [numberFormatter stringFromNumber:product.price];
[numberFormatter release];
upgradeProduct.price = price;
[self.upgradeProducts addObject:upgradeProduct];
[upgradeProduct release];
}
[productsRequest release];
if ([self.delegate respondsToSelector:#selector(didLoadStore:)])
[self.delegate didLoadStore:self.upgradeProducts];
}
+ (BOOL) canMakePurchases
{
if ([SKPaymentQueue canMakePayments])
return YES;
else {
[Global showAlertViewWithTitle:NSLocalizedString(#"Payment Error", #"Payment Error Alert Title")
message:NSLocalizedString(#"You are not authorized to purchase from AppStore", #"Payment Error Alert Text when user cannot make payments from store")];
return NO;
}
}
- (void) purchaseUpgrade:(NSString *) productIdentifier
{
if ([InAppPurchaseManager canMakePurchases]) {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}
- (void) recordTransaction:(SKPaymentTransaction *) transaction
{
[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"upgradeTransactionReceipt" ];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void) finishTransaction:(SKPaymentTransaction *) transaction
{
[self paymentSucceeded:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void) paymentSucceeded:(SKPaymentTransaction *) transaction
{
// provide content here
if ([self.delegate respondsToSelector:#selector(didFinishPaymentTransaction)])
[self.delegate didFinishPaymentTransaction];
}
- (void) completeTransaction:(SKPaymentTransaction *) transaction
{
[self recordTransaction:transaction];
[self finishTransaction:transaction];
}
- (void) restoreTransaction:(SKPaymentTransaction *) transaction
{
[self recordTransaction:transaction.originalTransaction];
[self finishTransaction:transaction];
}
- (void) failedTransaction:(SKPaymentTransaction *) transaction
{
if (transaction.error.code != SKErrorPaymentCancelled) {
NSMutableString *messageToBeShown = [[NSMutableString alloc] init];
if ([transaction.error localizedFailureReason] != nil) {
[messageToBeShown setString:[NSString stringWithFormat:#"%# %#", NSLocalizedString(#"Reason:", #"Reason Text in alert when payment transaction failed"), [transaction.error localizedFailureReason]]];
if ([transaction.error localizedRecoverySuggestion] != nil)
[messageToBeShown appendFormat:#", %# %#", NSLocalizedString(#"You can try:", #"Text for sugesstion in alert when payment transaction failed"), [transaction.error localizedRecoverySuggestion]];
}
[Global showAlertViewWithTitle:NSLocalizedString(#"Unable to complete your purchase", #"Payment transaction failed alert title")
message:messageToBeShown];
[messageToBeShown release];
if ([self.delegate respondsToSelector:#selector(didFailedPaymentTransaction)])
[self.delegate didFailedPaymentTransaction];
} else {
if ([self.delegate respondsToSelector:#selector(didCancelPaymentTransaction)])
[self.delegate didCancelPaymentTransaction];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (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;
}
}
}
- (void) request:(SKRequest *) request didFailWithError:(NSError *) error
{
[Global showAlertViewWithTitle:NSLocalizedString(#"Payment Error", #"Payment Error Alert Title")
message:[NSString stringWithFormat:#"%#, %#", NSLocalizedString(#"Could not contact App Store properly", #"Alert text when request did fail"),
[error localizedDescription]]];
}
- (void) dealloc
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[upgradeProducts release];
if (productsRequest)
productsRequest = nil;
[super dealloc];
}
#end
In AppDelegate in function didFinishLaunchingWithOptions I make that:
[InAppPurchaseManager sharedInstance];
In Purchase View when I click a button I make:
UpgradeProduct *selectedProduct = [self.faxProducts objectAtIndex:[purchaseButton.identifier intValue]];
if (selectedProduct) {
[[InAppPurchaseManager sharedInstance] purchaseUpgrade:selectedProduct.productID];
}
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
This code is wrong, you can't use more than once. You know, if you add the observer for twice , you will find one transaction was updated two times.
I have the same problem, observer always has 2 transaction. Even i remove [[SKPaymentQueue defaultQueue] addPayment:payment], it still have 1 transaction. so i suspect the "canMakePayments"
After i remove [SKPaymentQueue canMakePayments], the problem seem to solve.. not sure why but may be it solve yours
If you're just testing, just make sure you sign out from your personal account and create a new test account in iTunesConnect.
Strangely it would fail if I don't use a test user. The API is full of black magic with little explanation.
Make sure you use the AppID correctly. I had the same issue "User cancelled" and I was not calling the transaction with the correct ID both times. I automatically append the com.mycompany.product" prefix to my code, but in some cases, the com.mycompany.product was already appended. Rookie bug...
I got this error because I had the wrong product identifier.