Inapppurchase SKProductsRequest Crash my application in ios5 - iphone

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.

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!

How to display an activity indicator while loading in app purchase?

I have a button that calls the in app purchase feature. I would to display a loading wheel while it is loading the product and the dismiss it when the UIAlertView to confirm the purchase appears. I am using the MBAlertView to show other messages in my app and I would to use it also here.
How can I do it? I want to display it when the user presses the button and dismiss it when it receives a response.
Here is my code at the moment!
- (IBAction)buyCoffeeInAppPurchase:(id)sender {
SKProductsRequest *request= [[SKProductsRequest alloc]
initWithProductIdentifiers: [NSSet setWithObject: #"com.giovannibalestra.emergencycall.Thankyoudeveloper"]];
request.delegate = self;
[request start];
// I should add something like this line of code to show the activity indicator but I can only set hidesAfter some seconds
// [MBHUDView hudWithBody:#"Wait." type:MBAlertViewHUDTypeActivityIndicator hidesAfter:4.0 show:YES];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
NSArray *myProduct = response.products;
NSLog(#"%#",[[myProduct objectAtIndex:0] productIdentifier]);
SKPayment *newPayment = [SKPayment paymentWithProduct:[myProduct objectAtIndex:0]];
[[SKPaymentQueue defaultQueue] addPayment:newPayment];
}
- (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 {
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Purchase Unsuccessful"
message:#"Your purchase failed. Please try again."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
For show loading you can also use MBProgressHUD. Its a easy way to show loading process. Download MBProgressHUD.h & .m file from internet & just copy in xcode project.
HOW TO USE: Import this #import "MBProgressHUD.h" in your .h & .m files & also this MBProgressHUD *HUD; in .h file.
Then in .m file your code looks like:
- (IBAction)buyCoffeeInAppPurchase:(id)sender {
HUD = [[MBProgressHUD alloc] initWithFrame:CGRectMake(0, 0, 320, 460) ];
HUD.labelText = #"Fetching...";
[self.view addSubview:HUD];
[HUD show:YES];
SKProductsRequest *request= [[SKProductsRequest alloc]
initWithProductIdentifiers: [NSSet setWithObject: #"com.giovannibalestra.emergencycall.Thankyoudeveloper"]];
request.delegate = self;
[request start];
// I should add something like this line of code to show the activity indicator but I can only set hidesAfter some seconds
// [MBHUDView hudWithBody:#"Wait." type:MBAlertViewHUDTypeActivityIndicator hidesAfter:4.0 show:YES];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
NSArray *myProduct = response.products;
NSLog(#"%#",[[myProduct objectAtIndex:0] productIdentifier]);
SKPayment *newPayment = [SKPayment paymentWithProduct:[myProduct objectAtIndex:0]];
[[SKPaymentQueue defaultQueue] addPayment:newPayment];
[HUD hide:YES];
[HUD removeFromSuperViewOnHide];
}
- (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 {
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Purchase Unsuccessful"
message:#"Your purchase failed. Please try again."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
[self completeTransaction:transaction];
}
if (transaction.transactionState == SKPaymentTransactionStateFailed) {
[self failedTransaction:transaction];
}
if (transaction.transactionState == SKPaymentTransactionStateRestored) {
[self restoreTransaction:transaction];
}
else {
// Do something.
}
// You can dismiss your Activity Indicator (MBHUDView) here.
}
}
Hi Simply apply the activity indicator when user clicks on purchase button and stop the activity indicator after this method - (void)completeTransaction:(SKPaymentTransaction *)transaction. Don't forget to stop in failed transaction too, other wise activity indicator start animating. Some times activity indicator needs to calls through threads is they not visible. So please try that too.

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
}

Inapp Purchase is succeeded but content is not unlock in iPhone app.. Why?

In My iphone app i have used In App purchase. All the things are working and the contents are in Resource folder. After successful transaction i have used a Sql Query to insert data in Sqlite database. I have Uploaded this app in App Store and find that after successful transaction the payment is taken from users but the content is not inserted in database and not Showed in app. Please help me. i am stressed on this Point. Due to this i have removed my app form App Store.
In this code after successful finding list of In App Purchases i am using method
For better Understanding i am putting my Some code here:
- (void)insertNewObject {
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
// NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
-(void)updateObject {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
abort();
}
}
#pragma mark -
#pragma mark Store request
- (void) requestProductData {
request= [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:[arrayProductID objectAtIndex:b-2]]];//#"Scaringmirror11"
request.delegate = self;
[request start];
}
- (void) startPurchase {
//int newB=b-2;
SKPayment *payment = [SKPayment paymentWithProductIdentifier:[arrayProductID objectAtIndex:b-2]];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma mark Store delegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *myProduct = response.products;
if (myProduct == nil || [myProduct count] == 0)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:ALERT_TITLE message:#"Missing product from App store.\n"
delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
SKProduct *product;
BOOL existProduct = NO;
for (int i=0; i<[myProduct count]; i++) {
product = (SKProduct*)[myProduct objectAtIndex:0];
if ([product.productIdentifier isEqualToString:[arrayProductID objectAtIndex:b-2]]) {
existProduct = YES;
//[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt" ];
//[[NSUserDefaults standardUserDefaults] synchronize];
break;
}
}
if (existProduct == NO) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:ALERT_TITLE message:#"Missing product from App store.No match Identifier found.\n"
delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
[request autorelease];
[self startPurchase];
}
#pragma mark Store delegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *myProduct = response.products;
if (myProduct == nil || [myProduct count] == 0)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:ALERT_TITLE message:#"Missing product from App store.\n"
delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
SKProduct *product;
BOOL existProduct = NO;
for (int i=0; i<[myProduct count]; i++) {
product = (SKProduct*)[myProduct objectAtIndex:0];
if ([product.productIdentifier isEqualToString:[arrayProductID objectAtIndex:b-2]]) {
existProduct = YES;
//[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt" ];
//[[NSUserDefaults standardUserDefaults] synchronize];
break;
}
}
if (existProduct == NO) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:ALERT_TITLE message:#"Missing product from App store.No match Identifier found.\n"
delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
[request autorelease];
[self startPurchase];
}
#pragma mark observer delegate
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
NSLog(#"Success");
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
NSLog(#"Failed");
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction {
//[[NSUserDefaults standardUserDefaults] setBool:YES forKey:REGISTRATION_KEY];
registered = YES;
//NSData *receiptData = [transaction transactionReceipt];
//NSString *str =[NSString string
//[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt" ];
//[self registeredEnable];
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[sqlite insertIntoScaryImage:[arrayGetMoreScaryImage objectAtIndex:b] add:[arrayGetMoreScarySound objectAtIndex:b]];
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction {
//[[NSUserDefaults standardUserDefaults] setBool:YES forKey:REGISTRATION_KEY];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:ALERT_TITLE message:#"Purchase success."
delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
//[sq];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled)
{
// Optionally, display an error here.
NSString *stringError = [NSString stringWithFormat:#"Payment Cancelled\n\n%#", [transaction.error localizedDescription]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:ALERT_TITLE message:stringError
delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
Please have a look and tell me where should i insert data in database so that i will able to provide data to users after Successful in app Purchase.
Thanks in Advance
You should implement your unlocking functionality into the payment observer, right after you receive confirmation that the payment is all right.
Your implementation of the observer is kinda strange, I guess it's not working.
EDIT:
your observer class
#import Foundation/Foundation.h
#import StoreKit/StoreKit.h
#interface InAppPurchaseObserver : NSObject {
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;
#end
#import "InAppPurchaseObserver.h"
#implementation InAppPurchaseObserver
// The transaction status of the SKPaymentQueue is sent here.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for(SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
// Item is still in the process of being purchased
NSLog(#"Processing!!!");
break;
case SKPaymentTransactionStatePurchased:
// Item was successfully purchased!
// --- UNLOCK FEATURE OR DOWNLOAD CONTENT HERE ---
// The purchased item ID is accessible via
// transaction.payment.productIdentifier
// After customer has successfully received purchased content,
// remove the finished transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
break;
case SKPaymentTransactionStateRestored:
// Verified that user has already paid for this item.
// Ideal for restoring item across all devices of this customer.
// --- UNLOCK FEATURE OR DOWNLOAD CONTENT HERE ---
// The purchased item ID is accessible via
// transaction.payment.productIdentifier
// After customer has restored purchased content on this device,
// remove the finished transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
break;
case SKPaymentTransactionStateFailed:
// Purchase was either cancelled by user or an error occurred.
NSLog(#"Failed!!!");
if (transaction.error.code != SKErrorPaymentCancelled) {
// A transaction error occurred, so notify user.
NSLog(#"Cancelled!!!");
}
// Finished transactions should be removed from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
break;
}
}
}
#end
implementation
- (void) requestProductData {
inappObserver = [[InAppPurchaseObserver alloc] init];
request= [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:[arrayProductID objectAtIndex:b-2]]];//#"Scaringmirror11"
request.delegate = self;
[request start];
}
- (void) startPurchase {
//int newB=b-2;
SKPayment *payment = [SKPayment paymentWithProductIdentifier:[arrayProductID objectAtIndex:b-2]];
[[SKPaymentQueue defaultQueue] addTransactionObserver:inappObserver];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}

Problem in In-App purchase-consumable model

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.