Skip to content

Commit

Permalink
Fix to ensure restoreTransactions completion callback is called robot…
Browse files Browse the repository at this point in the history
…media#150

Inside RMStore, when the paymentQueue:updatedTransactions is called after a restore, a pendingRestoredTransactions counter was incremented/decremented as transactions were processed. However, in our case updateTransactions callback was called twice, so 2x the amount of transactions were added to the pendingRestoredTransactions counter. The problem was the counter was incremented twice but only decremented once for each transaction. WHen the code reached notifyRestoreTransactionFinishedIfApplicableAfterTransaction, the counter was checked but was not equal to zero, so the callback wasn't called, even though all the transactions were processed.

The fix was to use a MutableSet to store transaction identifiers that were pending restore, which prevents duplicate transactions to be handled. The callback is now always called after transactions are restored.
  • Loading branch information
everappz committed Mar 24, 2024
1 parent 9b4f37c commit 19ab18e
Showing 1 changed file with 17 additions and 7 deletions.
24 changes: 17 additions & 7 deletions RMStore/RMStore.m
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ @implementation RMStore {

NSMutableArray *_storedStorePayments;

NSInteger _pendingRestoredTransactionsCount;
NSMutableSet *_pendingRestoredTransactionIds;
BOOL _restoredCompletedTransactionsFinished;

SKReceiptRefreshRequest *_refreshReceiptRequest;
Expand All @@ -158,6 +158,7 @@ - (instancetype) init
_productsRequestDelegates = [NSMutableSet set];
_restoredTransactions = [NSMutableArray array];
_storedStorePayments = [NSMutableArray array];
_pendingRestoredTransactionIds = [NSMutableSet set];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
Expand Down Expand Up @@ -265,7 +266,7 @@ - (void)restoreTransactionsOnSuccess:(void (^)(NSArray *transactions))successBlo
failure:(void (^)(NSError *error))failureBlock
{
_restoredCompletedTransactionsFinished = NO;
_pendingRestoredTransactionsCount = 0;
[_pendingRestoredTransactionIds removeAllObjects];
_restoredTransactions = [NSMutableArray array];
_restoreTransactionsSuccessBlock = successBlock;
_restoreTransactionsFailureBlock = failureBlock;
Expand All @@ -278,7 +279,7 @@ - (void)restoreTransactionsOfUser:(NSString*)userIdentifier
{
NSAssert([[SKPaymentQueue defaultQueue] respondsToSelector:@selector(restoreCompletedTransactionsWithApplicationUsername:)], @"restoreCompletedTransactionsWithApplicationUsername: not supported in this iOS version. Use restoreTransactionsOnSuccess:failure: instead.");
_restoredCompletedTransactionsFinished = NO;
_pendingRestoredTransactionsCount = 0;
[_pendingRestoredTransactionIds removeAllObjects];
_restoreTransactionsSuccessBlock = successBlock;
_restoreTransactionsFailureBlock = failureBlock;
[[SKPaymentQueue defaultQueue] restoreCompletedTransactionsWithApplicationUsername:userIdentifier];
Expand Down Expand Up @@ -593,9 +594,13 @@ - (void)didFailTransaction:(SKPaymentTransaction *)transaction queue:(SKPaymentQ

- (void)didRestoreTransaction:(SKPaymentTransaction *)transaction queue:(SKPaymentQueue*)queue
{
RMStoreLog(@"transaction restored with product %@", transaction.originalTransaction.payment.productIdentifier);
NSString *productIdentifier = transaction.originalTransaction.payment.productIdentifier;
RMStoreLog(@"transaction restored with product %@", productIdentifier);

_pendingRestoredTransactionsCount++;
if (productIdentifier)
{
[_pendingRestoredTransactionIds addObject:productIdentifier];
}
if (self.receiptVerifier != nil)
{
[self.receiptVerifier verifyTransaction:transaction success:^{
Expand Down Expand Up @@ -695,9 +700,14 @@ - (void)notifyRestoreTransactionFinishedIfApplicableAfterTransaction:(SKPaymentT
if (transaction != nil)
{
[_restoredTransactions addObject:transaction];
_pendingRestoredTransactionsCount--;

NSString *productIdentifier = transaction.payment.productIdentifier;
if (productIdentifier)
{
[_pendingRestoredTransactionIds removeObject:productIdentifier];
}
}
if (_restoredCompletedTransactionsFinished && _pendingRestoredTransactionsCount == 0)
if (_restoredCompletedTransactionsFinished && _pendingRestoredTransactionIds.count == 0)
{ // Wait until all restored transations have been verified
NSArray *restoredTransactions = [_restoredTransactions copy];
if (_restoreTransactionsSuccessBlock != nil)
Expand Down

0 comments on commit 19ab18e

Please sign in to comment.