completeTransaction:(SKPaymentTransaction *)transaction runs multiple times - iphone

I am using below typed code to handle in app purchases in my app. In completeTransaction I unlock premium content by calling provideContentForProductIdentifier. Mostly it runs only one time but, some times it runs multiple times.
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
Please help!

I've had this same problem. In my case I forgot to removeTransactionObserver. So it still contained the earlier product id. Add this to remove the purchased product id after finishing the transaction.
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions
{
NSLog(#"Purchase removedTransactions");
// Release the transaction observer since transaction is finished/removed.
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

Related

Obj-C, Storekit restoreCompletedTransactions returns zero transactions?

I'm having some problems restoring completed transactions.
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
I've added the observer mentioned in several examples, I've tried adding paymentQueueRestoreCompletedTransactionsFinished and already have updatedTransactions. paymentQueueRestoreCompletedTransactionsFinished says I have zero transactions.
I can buy a product and if I try to buy again, it stops me and says I've already bought the product, using this code.
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
I thought maybe I had a problem with my bundle identifier, but that seems fine and the buy wouldn't work if it wasn't.
I have been trying this on the device as well as the simulator, but this has the same result. Also, it doesn't make a difference If I'm using UK or US store.
I'm really grasping at straws to find out why this doesn't work for me ?
try to do it like this and check the array count is it return zero also ?
- (void) checkPurchasedItems
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}//You Call This Function
//Then this delegate Function Will be fired
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
purchasedItemIDs = [[NSMutableArray alloc] init];
NSLog(#"received restored transactions: %i", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
[purchasedItemIDs addObject:productID];
}
}
According to the docs:
When you create a new product to be sold in your store, you choose whether that product can be restored or not.
So the question is, is your product configured to allow restores?

Consumable In-App Purchase

Hi I'm trying to make an in-app store in my game for players to buy virtual currencies, which is consumables. But I am a bit unsure if my approach is correct or not. So I think it might be good to ask for help here.
After pressing the Buy Button, buyProductIdentifier is called
- (void)buyProductIdentifier:(NSString *)productIdentifier {
NSLog(#"Buying %#...", productIdentifier);
SKPayment *payment = [SKPayment paymentWithProductIdentifier: productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
and then followed up by paymentQueue
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
CCLOG(#"PAYMENT QUEUE CALLED!");
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 tried by commenting out the SKPaymentTransactionStateRestored case in order to make the player complete a purchase every time instead of popping up a message saying that "You've already purchased this but it hasn't been downloaded".
Is this approach correct? if not, can you guys give me some hints?
Many thanks.
I don't think it is a code problem.
Are you sure that you you have created the In-App Purchases in iTunes Connect as "Consumable" ?
If you have chosen "Non-Consumable" it is only possible to buy the item once.

How to pass messages from one class to another through NSNotification

Ha-ii ,am doing an in-app purchase in my application,i need to disable a button when the in-app content purchase is success,so i put the NSuserdefault to my code to identify weather its success or not,but NSUserDefault works only when i refresh the page,i didnt get the button enabled when the content success message is come ,i get enabled when i go back to another page and came back,my need is to pass instance message to the viewcontroller through notification.my code is
- (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
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];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"Transsucess"];
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.
if (transaction.error.code != SKErrorPaymentCancelled) {
// A transaction error occurred, so notify user.
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"Transsucess"];
}
// Finished transactions should be removed from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:
transaction];
break;
}
}
}
in my anothervierwcontroller i want to pass the Notification value insted of Nsuserdefault value.
How to do this.please help me.
in your first view vieDidLoad add the line ..
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ContentPurchased) name:#"Purchased" object:nil];
( do remove the observer in dealloc)
then when you have successful purchase in your question code..
Post the notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"Purchased" object:nil userInfo:nil];
You can pass an object..but i believe you should store the purchase transaction detail in NSUserdefautls..since it will be needed in the next runs of the app..
When you post the notification ContentPurchased method is called in first view ( you have to add that in the view controller)

Turning off an in-app purchase?

We currently have a live app that features episodic content in the App store.
We're re-working our pricing, and instead of offering individual episodes for purchase, we want to have it as simply an entire pack of episodes.
My question is this: If I set my old identifiers to NOT cleared for sale, would a user who previously purchased that content still be allowed access to it? (Meaning if I query whether they've purchased it, will it return true)
I'm new to the in-app purchase side of apps, and I'm not entirely sure how that works.
Also, if I delete the identifier from iTunesConnect, what happens? Should this even be done?
Thanks in advance for any insight
When using [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];, Apple will return all completed transactions in a SKPaymentQueue which is a collection of transactions. The transaction will contain the the payment object. The payment object will contain the productIdentifier. This info is available despite your deletion. Thus you may honor past purchases that are no longer for purchase.
Here is some example code:
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
#pragma mark SKPayment Observer Delegate methods
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(#"received restored transactions: %i", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSLog(#"tran for product: %# of state: %i", [[transaction payment] productIdentifier], [transaction transactionState]);
switch ([transaction transactionState])
{
case SKPaymentTransactionStateRestored:
NSLog(#"found restored transaction: %# productIdentifier: %#", transaction.transactionIdentifier, transaction.payment.productIdentifier);
[self yourRestoreProcessSelector:transaction];
break;
default:
break;
}
}
}

differentiate between different in app purchases iphone paymentQueue

I have just managed to integrate in app purchases into my ios application however i have know run into a problem that i can't really ask google about.
I have an app with 2 products to purchase but once payment is complete i need to differentiate between the 2 products purchased to then fire another method.
please see the paymentQueue method below:
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for(SKPaymentTransaction *transaction in transactions)
{
switch(transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStatePurchased:
//do crediting here
NSLog(#"transactionid = %#",transaction);
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
if(transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Error Encountered");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}
When the SKPaymentStatePurchased is hit it needs to find the initial product that was purchased to then fire the method for the product to credit an account set up on a remote server.
I hope someone can help!
Thanks in advance :)
You can get that from the transaction itself
transaction.payment.productIdentifier
This will give you the product identifier that the user have purchased