Error on some devices when restoring InApp purchases.

I have the following problem and I'm stuck.

For most users, buying and restoring works without problems.


Still, I have a problem with about one in a hundred users that the purchase was not recognized, but was billed. For these users, the purchase was not unlocked. Users can not restore the purchase to the device where the purchase was made. However, these users can use the "restore purchases" feature on a second device without any problem.


The restoration works for me currently as follows:


1. The user call's:

- (void)restore{
    wantBuy = YES;
   
    [self productRequestStop];

    SKReceiptRefreshRequest *produkt = [[SKReceiptRefreshRequest alloc] init];
    [produkt setDelegate:self];
    [produkt start];
}


Note:


- (void)productRequestStop{
    [productsRequest setDelegate:nil];
    [productsRequest cancel];
    productsRequest = nil;
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

// SKProductsRequest *productsRequest; ** at the interface **


2.


- (void)requestDidFinish:(SKRequest *)request{

    if([request isKindOfClass:[SKReceiptRefreshRequest class]]){
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

    }else if([request isKindOfClass:[SKProductsRequest class]]){
    }else{
        NSLog(@"unkown requestDidFinish %@ ", request.class);
    }
}



3.

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
    for(SKPaymentTransaction *transaction in transactions){
        switch([transaction transactionState]){
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"Transaction state -> Purchasing");
                break;
            case SKPaymentTransactionStatePurchased:
                NSLog(@"Transaction state -> Purchased: %@",[[transaction payment] productIdentifier]);
                if ([[[transaction payment] productIdentifier]  isEqual: inAppID_ProVersion]) {
                    [self unlook_ProVersion];
                }
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                NSLog(@"Transaction state -> Restored");
                [self unlookRestore:[[transaction payment] productIdentifier]];
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:{
                if(transaction.error.code == SKErrorPaymentCancelled){
                    NSLog(@"Transaction state -> Cancelled");
                }else{
                    NSLog(@"SKErrorPaymentCancelled %@", transaction.error.description);
                }
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            }
            case SKPaymentTransactionStateDeferred:
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
        }
    }
}



4.


- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue{
    for(SKPaymentTransaction *transaction in queue.transactions){
        if(transaction.transactionState == SKPaymentTransactionStateRestored && wantBuy){
            [self unlookRestore:[[transaction payment] productIdentifier]];
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        }
    }
}


I have now developed an ONLINE log extension via JSON Script and found out what happens if one of these problem users wants to restore purchases.


1. The User calls [self restore];

2. The User reenter his login data.

3. The requestDidFinish was called with SKReceiptRefreshRequest

4. Now it calls: [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];


5. At normal user it calls paymentQueue:updatedTransactions: but at problem users this function is not called.

6. At problem user it calls after restoreCompletedTransactions the function paymentQueueRestoreCompletedTransactionsFinished with a empty queue.transactions array (Zero SKPaymentTransaction).



Why does this work for users only on the 2nd device? The code is the same! A re-instalation and a log-out in the AppStore were also unsuccessful.

Speculation ------ One difference is the device used to make the purchase most likely received two calls to updatedTransactions - the first was a 'failed' transaction and the second was a purchase transaction. The first transaction caused the transaction observer to turn off. The second purchased transaction has not been finished. It is hanging up the queue on that device. Do you have some way of launching the app and turning on the transactionObserver to clear the queue?

I have a special UIViewController for the InApp purchases.

I have changed the following:


1.

Removed: [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; from paymentQueueRestoreCompletedTransactionsFinished:


2.

add [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; to viewDidLoad:

Note: only at viewDidLoad: exists addTransactionObserver


3.

add [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; to viewDidDisappear:

Note: only at viewDidDisappear: exists removeTransactionObserver


4.

[[SKPaymentQueue defaultQueue] finishTransaction:transaction] was added in updatedTransactions: to SKPaymentTransactionStatePurchased, SKPaymentTransactionStateRestored, SKPaymentTransactionStateFailed, SKPaymentTransactionStateDeferred.


If the restoration works on the customer's iPad, it should work on the iPhone, right?

How can i clear the transactionObserver?


Did you mean this lines with "clear the transactionObserver"?

- (void)viewDidLoad {
    [super viewDidLoad];
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    for (SKPaymentTransaction *t in [[SKPaymentQueue defaultQueue] transactions]) {
        [[SKPaymentQueue defaultQueue] finishTransaction:t];
    }
}

your step #1 seems correct in that you already finished the transaction in updatedTransactions.

Yes, this will clear the transaction queue; it's a bit extreme - I was expecting the transactions to flow into updatedTransactions.

It seems like you remove the transaction observer in "productRequestStop" which is called from "restore".

Yes, in the old version the transaction observer was removed in "restore". However, an SKReceiptRefreshRequest was executed. Here, the "transaction observer" was set and called "restoreCompletedTransactions".

I am now waiting for the report from my "problem customer". I will tell if it works like this. Is it ok if you clean the transactionObserver with every "viewdidload" in the future?

No, that was not the mistake.

I have received the following LOG:


1.

// viewDidLoad
[[SKPaymentQueue defaultQueue] transactions].count

>> returned 0.


2.

// requestDidFinish:
SKProductsRequest

>> The product information has been loaded.


3.

// restore
SKReceiptRefreshRequest *produkt = [[SKReceiptRefreshRequest alloc] init];
[produkt setDelegate:self];
[produkt start];

>> Restore button was pressed.


4.

// requestDidFinish:
SKReceiptRefreshRequest

>> Got a new receipt...and call:

[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];


>> paymentQueue:updatedTransactions: was not called!!


5.

// paymentQueueRestoreCompletedTransactionsFinished:
queue.description >> "<SKPaymentQueue: 0x1c10029f0>"
queue.debugDescription >> "<SKPaymentQueue: 0x1c10029f0>"
queue.transactions.description >> "()"
queue.transactions.debugDescription "<__NSArray0 0x1c401c060>()"

>> queue.transactions is empty


Please further ideas. The user has purchased the items and restored them on his second device via the restore function. Only on the device where the items have purchased, it does not work.

Error on some devices when restoring InApp purchases.
 
 
Q