repetition of methods while purchasing - iphone

My control reaches to method:
IAPHelper.m
- (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;
}
}
}
after notification method in
viewcontroller.m
- (void)productPurchased:(NSNotification *)notification {
NSLog(#"product purchased notification");
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
NSString *productIdentifier = (NSString *) notification.object;
NSLog(#"Purchased: %#", productIdentifier);
}
how could i stop this ? any bunch line of code or something else that could stop going at transaction method in IAPHelper.m class after purchasing second product?

Yes, i experienced similar behaviour some time ago. I managed to work around it by keeping track (locally) of already delivered products and i ignore the subsequent callbacks from StoreKit for the delivered products.

Related

displaying dialogue after in-app purchase complete

I have implemented the SKPaymentTransactionObserver in my apps AppDelegate the way Apple recommended:
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
// Optionally, display an error here.
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
self.products = response.products;
}
- (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;
}
}
}
I'd like for my app to send a dialogue message to the user when the following happens: purchase successful, purchase failed, restore successful, restore failed. I'm having a little trouble wrapping my head around how I can do this with my design set up. I have a couple of questions:
1) The alert needs to be posted in the view controller where the transaction is initialized. How can I make the AppDelegate communicate with this view controller to let it know when an event has happened? Do I set up a delegate for the AppDelegate? This seems kind of funny to me...is there a better way?
2) Where do I send the message? Should it be in finishTransaction (do I need to override?) or somewhere else?
Passing a notification will be the best way for it. The view controller which invokes the payment procedure should register for notification.
On the completion of a transaction, app delegate will post the notification which controller will receive and it will show the appropriate message.

In-App purchasing: Listen for the "Cancel' button?

I'm trying to figure out how I can listen to the "Cancel" button that appears in the "confirmation" alert shown when a user tries to purchase something. You know, the official one done by Apple, looks something like: "Confirm Your In App Purchase. Do you want to buy one $product for $price? [Cancel] [Buy]"
If I understand my code correctly, the alert initiated by something like this:
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
So basically I'd like to do something if they hit Cancel. Thanks
implement the paymentQueue:updatedTransactions: method from the SKPaymentTransactionObserver Protocol. There you can check the transactionState and the error of each transaction object.
I used something like that:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code == SKErrorPaymentCancelled) {
/// user has cancelled
[self finishTransaction:transaction wasSuccessful:NO];
}
else if (transaction.error.code == SKErrorPaymentNotAllowed) {
// payment not allowed
[self finishTransaction:transaction wasSuccessful:NO];
}
else {
// real error
[self finishTransaction:transaction wasSuccessful:NO];
// show error
}
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
default:
break;
}
}
}
Use something like this:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code == SKErrorPaymentCancelled) {
/// user has cancelled
[self finishTransaction:transaction wasSuccessful:NO];
}
else if (transaction.error.code == SKErrorPaymentNotAllowed) {
// payment not allowed
[self finishTransaction:transaction wasSuccessful:NO];
}
else {
// real error
[self finishTransaction:transaction wasSuccessful:NO];
// show error
}
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
default:
break;
}
}
}

How to stop uiactivityindicator after waiting for in-app purchase store to load

I thought one of these would do it, none of them are getting called -_-
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"flag paymentQueue");
// spinner.hidden=YES; //where does this go?
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchasing:
// [self stillPurchasing]; // this creates an alertView and shows
NSLog(#"flag SKPaymentTransactionStatePurchasing");
break;
case SKPaymentTransactionStatePurchased:
NSLog(#"flag SKPaymentTransactionStatePurchased");
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"flag SKPaymentTransactionStateFailed");
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"flag SKPaymentTransactionStateRestored");
[self restoreTransaction:transaction];
// spinner.hidden=YES;
break;
thanks!!!
You can hide spinner in following method-
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
[target hideSpinner];
}
it should be good. You should hide your spinner in the 3 cases :
- SKPaymentTransactionStatePurchased
- SKPaymentTransactionStateFailed
- SKPaymentTransactionStateRestored
Have you checked your delegates ?
your class should implement the delegate method of :
-> SKPaymentTransactionObserver
Mine is declared like that :
#interface InAppPurchaseStoreManager : NSObject

iPhone App : In App Purchase

In iPhone App for in App Purchase: when is SKPaymentTransactionStateRestored: called?
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}
And how to get confirmation from apple that the app is purchased successfully means I want to Print that information in NSlog.
what should I write for that?
As far as i'm concerned, transactions is purchased successfully only if Apple responses the updatedTransactions state as SKPaymentTransactionStateRestored or SKPaymentTransactionStatePurchased.
When the method:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for (SKPaymentTransaction * transaction in transactions) {
//process the transaction
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
default:
break;
}
}
}
is called it means that you have finished your transaction with apple and you handle three cases:
SKPaymentTransactionStatePurchased: it means that your product was bought.
SKPaymentTransactionStateFailed: your product couldn't be bought.
SKPaymentTransactionStatePurchased: This is your question. As long as your SKPaymentTransactionObserver is alive the transaction is persistant, meaning that if your client intended to buy you product but something wrong happened while delivering the product (server error or something else), when the app starts again the transaction will return to this method to finish the purchase.
I hope the info helps.

Problems with iPhone SDK StoreKit

In my iPhone app Im using StoreKit to make it possible for users to buy subscriptions in the app. The problem I'm having is that suddenly everytime I start the app SKPaymentTransactionStatePurchased is sent to the observer so the app tries to buy the subscription again and again. And if I try to buy the subscription again from the list of subscriptions in the app I get a message saying "You've already purchased this In App Purchase but it hasn't been downloaded." then failedTransaction is called with SKErrorPaymentCancelled.
EDIT: I have now found a lot of threads about this in the Apple Developer Forum, for example: https://devforums.apple.com/thread/73818 and /thread/73572, it seems like a lot of developers have the same problem..
This is the code I'm using, can you see something wrong with it?
-(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) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Error");
} else {
NSLog(#"Cancel");
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
-(void) restoreTransaction: (SKPaymentTransaction *)transaction
{
[self subscribe:transaction];
}
-(void) completeTransaction: (SKPaymentTransaction *)transaction
{
[self subscribe:transaction];
}
-(void)subscribe: (SKPaymentTransaction*)transaction {
NSInteger errorCode = //Connects to my server that verifies receipt with Apple server etc..
if (errorCode==0) {
[self provideContent];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
I had the exact problem when using the StoreKit the first time and it happened because as I was implemented the code I was leaving transactions unfinished.
So, when you start the app, you'll need to loop through the queue and finish all transactions. You shouldn't need to do this is you cover all outcomes (which, based on the code above, you did).
After talking to Apple support I was able to solve this issue. It seems to be a problem from Apple that has now been fixed.