updatedTransactions first time, call old queue - ios

Firs time i tried to make a subscription this function call for all transactions, but after then if i tried to subscribe it calls just 1 state.
This code same as in apple developer documentation
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"purchasing in progress");
break;
case SKPaymentTransactionStateDeferred:
NSLog(#"Stade deferred");
break;
case SKPaymentTransactionStateFailed:
NSLog(#"State failed");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStatePurchased:
NSLog(#"Sucsess purchased");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"restored");
break;
default:
// For debugging
NSLog(#"Unexpected transaction state %#",#(transaction.transactionState));
break;
}
}
}

updatedTransactions is called only for updated transactions (see Apple docs https://developer.apple.com/library/prerelease/ios/documentation/StoreKit/Reference/SKPaymentTransactionObserver_Protocol/)
transactions - An array of the transactions that were updated.
But on application start or first purchase iOS sends every transaction from SKPaymentQueue: defaultQueue.
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/DeliverProduct.html:
Terminate and relaunch your app. Store Kit calls the paymentQueue:updatedTransactions: method again shortly after launch; this time, let your app respond normally. Verify that your app correctly delivers the product and completes the transaction
So if app should view all transactions, it should request SKPaymentQueue: defaultQueue.

Related

iOS In-App Purchases handling when app goes to background (Unfinished interrupted transactions)

In my app In-App Purchases are working fine.
The issue I am facing is that;
if I initiate a subscription process and send app to background by pressing Home button on iPhone.
then In-App purchases API keeps on working and prompts user for iTunes credentials, while app is in background and user completes the process of purchasing the subscription successfully.
Now user has purchased the subscription through In-App API while app is in background but I am not getting that how to handle this scenario as, if user kills app without taking it back to foreground then the subscription purchase information will never be forwarded to our server and we will not be able to update user account in our server and user will not be able to use the special features of the app.
To send the latest receipt and In-App Purchase info to server, I get notified through below method:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"updated transaction");
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
NSLog(#"transationStatePurchased");
// here I send data to server, but it never runs if app is in background.
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"transationStateFailed");
[self failedTransaction:transaction];
break;
default:
break;
}
}
}
But the above method always works when app is in foreground so the issue is how can I get notified about the complete transactions even if above method does not executes.
OK, So I have got the answer.
For others in easy words.
If you have an interrupted In-App Purchase which was due to any reason was completed but the receipt/information of that transaction of In-App Purchase, could not be sent to your server. Simply do this
Most Important: NEVER CALL [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; BEFORE SAVING THE RECEIPT ON YOUR SERVER, CALL IT WHEN PROCESS OF SAVING THE RECEIPT COMPLETES SUCCESSFULLY, SO THAT IF TRANSACTION GETS INTERRUPTED YOU WOULD HAVE INFORMATION REGARDING IT, NEXT TIME WHEN USER WILL START THE APP.
In your app delegate or in your first view controller or wherever you want, add this: (In my scenario I need to update user data before login so that user could use the special features of the app)
Add #import <StoreKit/StoreKit.h> to your .h file.
Add Delegate <SKPaymentTransactionObserver> to .h file.
Add observer
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
to - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
And delegate Method
-(void) paymentQueue:(SKPaymentQueue *) queue updatedTransactions:(NSArray *) transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"Purchasing");
break;
case SKPaymentTransactionStatePurchased:
if ([transaction.payment.productIdentifier isEqualToString:kProductID]) {
NSLog(#"Purchased ");
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Purchase is completed succesfully" message:nil delegate:
self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}
[self sendReceiptDataToServer:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"Restored ");
[self methodAfterTransactionSuccessful:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"Purchase failed ");
break;
default:
break;
}
}}
-(void) sendReceiptDataToServer:(SKPaymentTransaction*)transaction {
NSLog(#"transaction successful");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

In App Purchases won't allow me to purchase consumable product

I'm testing out my In App Purchases in sandbox mode and I keep getting the message "This in-app purchaes has already been bought" when I try to purchase it, wherein the product is supposed to be consumable that can be bought unlimited times. I tried kiling my app and restarting it, but it continues to give me this message. Is this an Xcode Objective C bug, or is this an iOS bug? I'd be most grateful for any answers or guidance in resolving this matter, thanks in advance.
Update: Code for SKPayment
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for(SKPaymentTransaction *transaction in transactions){
switch(transaction.transactionState){
case SKPaymentTransactionStatePurchasing: NSLog(#"Transaction state -> Purchasing");
//called when the user is in the process of purchasing, do not add any of your own code here.
break;
case SKPaymentTransactionStatePurchased:
//this is called when the user has successfully purchased the package (Cha-Ching!)
[self doRemoveAds:validProduct];
//you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(#"Transaction state -> Purchased");
break;
case SKPaymentTransactionStateRestored:
NSLog(#"Transaction state -> Restored");
[self doRemoveAds:validProduct];
//add the same code as you did from SKPaymentTransactionStatePurchased here
break;
case SKPaymentTransactionStateFailed:
//called when the transaction does not finish
if(transaction.error.code == SKErrorPaymentCancelled){
NSLog(#"Transaction state -> Cancelled");
//the user cancelled the payment ;
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}

In app purchase unfinished transaction in a particular device

Im doing a in app purchase non consumable,the issue is when I start the app there comes a alert view filled with a mail address already and asking password.This particular issue happens only in the same device which I test where else it never happened when I tested in other device.
Somewhere I have to place this [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; to get that unfinished transaction done but I'm completely confused because I've placed in necessary places .This is my existing code
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self UnlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"Restored ");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:NSLog(#"Transaction Failed");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:
break;
}
}
- (IBAction)Restore:(id)sender {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
[self UnlockPurchase];
}
- (IBAction)BuyProduct:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
Similar problem I had previously on another device just because I left out [[SKPaymentQueue defaultQueue] finishTransaction:transaction]in SKPaymentTransactionStateRestoredthen my problem was solved.When I used this test user to restore the purchase,it restores without entering into SKPaymentTransactionStateRestored case.When I try to purchase again it says you've already purchased item but hasn't been downloaded.Now I need is to get rid of that login containing a email address filled already pops up when I start? HELP !
You are missing the methods that handle the response of the app store to your application. They should be in your app delegate:
Follow apples documentation for delivering products:
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/DeliverProduct.html#//apple_ref/doc/uid/TP40008267-CH5-SW3
Alternatively you can use ray wenderlichs tutorial:
http://www.raywenderlich.com/21081/
Take notice that these methods are the ones that should control the content delivery, for the purchase being successful or not.

In-App Purchase: separate download of purchased items from transaction

I'm implementing In-App purchase with my server delivering the purchases in the form of zip files (not Apple server).
As downloadable files are quite big, 50Mb-500Mb I would like to let the user buy the items
and only when they want download and install the purchases.
So my implementation would be
By - Close Apple Transaction and Mark Item as Purchased - Trigger Download when you Want
Apple docs suggest you close transaction only after download completed:
I've been reading all Apple docs and I cannot see any specific comment about this saying that you must
close transaction only after download.
Am I going toward an Apple rejection?
Here the code: in case "SKPaymentTransactionStatePurchased" I call the method
"completeTransaction" which is closing the transaction itself. My question is about the
method:
[self provideContent:transaction.payment.productIdentifier];
Can I move this download from here in later action ofter "finishTransaction"?
- (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
{
[self recordTransaction:transaction];
[self provideContent:transaction.payment.productIdentifier];
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

Multiple receipt count for restoreCompletedTransaction inapp purchasing

I have an autorenewable subscription. When the app is installed on a new device, Apple returns ALL previous purchase receipts, in this case since it is sandbox I get 6 receipts every time I install. The observer then sends the queue for restoredCompleted transactions. I have a method to send the transaction to my server for Apple verification, but it runs 6 times because of the 6 receipts. I really only want to deal with the LAST receipt sent.
So I am trying to count the transactions in the queue and ONLY verify the receipt when the count reaches 1.
Here is what I have so far:
- (void)paymentQueue: (SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
if (myQueue == nil) {
myQueue = [SKPaymentQueue defaultQueue];
}
NSLog(#"Transactions in Array in My Store %#", [queue transactions]);
tCount =myQueue.transactions.count;
NSString *transCount = [NSString stringWithFormat:#"%d",tCount];
for (SKPaymentTransaction *transaction in transactions)
{ switch (transaction.transactionState)
{ case SKPaymentTransactionStatePurchased:
[self completeTransaction: transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction: transaction];
break;
case SKPaymentTransactionStateRestored:
if ([transCount isEqualToString:#"1"]) {
[self restoreTransaction: transaction];
}
else {
tCount--;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
});
}
default:
break;
}
}
return;
}
The restore cycles through, but the count does not decrement. This is probably something simple and dumb. Can someone show me how to decrement this count?
Thanks!
I wait until restoreCompletedTransactionsFinished before sending a receipt to my server to verify with Apple. This eliminates sending every receipt, which after a few months could get onerous. So, no need to decrement to transaction cout.

Resources