diff --git a/README.md b/README.md index 4c2a0ae6..62fa551d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ #RMStore -[![Join the chat at https://gitter.im/robotmedia/RMStore](https://badges.gitter.im/robotmedia/RMStore.svg)](https://gitter.im/robotmedia/RMStore?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Version](https://cocoapod-badges.herokuapp.com/v/RMStore/badge.png)](http://cocoadocs.org/docsets/RMStore) [![Platform](https://cocoapod-badges.herokuapp.com/p/RMStore/badge.png)](http://cocoadocs.org/docsets/RMStore) +[![CocoaPods Version](https://cocoapod-badges.herokuapp.com/v/RMStore/badge.png)](http://cocoadocs.org/docsets/RMStore) [![Platform](https://cocoapod-badges.herokuapp.com/p/RMStore/badge.png)](http://cocoadocs.org/docsets/RMStore) [![Build Status](https://travis-ci.org/robotmedia/RMStore.png)](https://travis-ci.org/robotmedia/RMStore) +[![Join the chat at https://gitter.im/robotmedia/RMStore](https://badges.gitter.im/robotmedia/RMStore.svg)](https://gitter.im/robotmedia/RMStore?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) A lightweight iOS library for In-App Purchases. diff --git a/RMStore/RMStore.m b/RMStore/RMStore.m index ece43bd9..7c9a9164 100755 --- a/RMStore/RMStore.m +++ b/RMStore/RMStore.m @@ -133,16 +133,15 @@ @implementation RMStore { NSMutableDictionary *_addPaymentParameters; // HACK: We use a dictionary of product identifiers because the returned SKPayment is different from the one we add to the queue. Bad Apple. NSMutableDictionary *_products; NSMutableSet *_productsRequestDelegates; - + NSMutableArray *_restoredTransactions; - - NSInteger _pendingRestoredTransactionsCount; + NSMutableSet *_pendingRestoredTransactionIds; BOOL _restoredCompletedTransactionsFinished; - + SKReceiptRefreshRequest *_refreshReceiptRequest; void (^_refreshReceiptFailureBlock)(NSError* error); void (^_refreshReceiptSuccessBlock)(); - + void (^_restoreTransactionsFailureBlock)(NSError* error); void (^_restoreTransactionsSuccessBlock)(NSArray* transactions); } @@ -155,6 +154,7 @@ - (instancetype) init _products = [NSMutableDictionary dictionary]; _productsRequestDelegates = [NSMutableSet set]; _restoredTransactions = [NSMutableArray array]; + _pendingRestoredTransactionIds = [NSMutableSet set]; [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; } return self; @@ -215,12 +215,12 @@ - (void)addPayment:(NSString*)productIdentifier { payment.applicationUsername = userIdentifier; } - + RMAddPaymentParameters *parameters = [[RMAddPaymentParameters alloc] init]; parameters.successBlock = successBlock; parameters.failureBlock = failureBlock; _addPaymentParameters[productIdentifier] = parameters; - + [[SKPaymentQueue defaultQueue] addPayment:payment]; } @@ -238,10 +238,10 @@ - (void)requestProducts:(NSSet*)identifiers delegate.successBlock = successBlock; delegate.failureBlock = failureBlock; [_productsRequestDelegates addObject:delegate]; - + SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:identifiers]; productsRequest.delegate = delegate; - + [productsRequest start]; } @@ -254,7 +254,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; @@ -267,7 +267,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]; @@ -392,7 +392,7 @@ - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { RMStoreLog(@"restore transactions finished"); _restoredCompletedTransactionsFinished = YES; - + [self notifyRestoreTransactionFinishedIfApplicableAfterTransaction:nil]; } @@ -478,7 +478,7 @@ - (void)didFinishDownload:(SKDownload*)download queue:(SKPaymentQueue*)queue { SKPaymentTransaction *transaction = download.transaction; RMStoreLog(@"download %@ for product %@ finished", download.contentIdentifier, transaction.payment.productIdentifier); - + [self postNotificationWithName:RMSKDownloadFinished download:download userInfoExtras:nil]; const BOOL hasPendingDownloads = [self.class hasPendingDownloadsInTransaction:transaction]; @@ -525,7 +525,7 @@ + (BOOL)hasPendingDownloadsInTransaction:(SKPaymentTransaction*)transaction - (void)didPurchaseTransaction:(SKPaymentTransaction *)transaction queue:(SKPaymentQueue*)queue { RMStoreLog(@"transaction purchased with product %@", transaction.payment.productIdentifier); - + if (self.receiptVerifier != nil) { [self.receiptVerifier verifyTransaction:transaction success:^{ @@ -544,23 +544,23 @@ - (void)didPurchaseTransaction:(SKPaymentTransaction *)transaction queue:(SKPaym - (void)didFailTransaction:(SKPaymentTransaction *)transaction queue:(SKPaymentQueue*)queue error:(NSError*)error { SKPayment *payment = transaction.payment; - NSString* productIdentifier = payment.productIdentifier; + NSString* productIdentifier = payment.productIdentifier; RMStoreLog(@"transaction failed with product %@ and error %@", productIdentifier, error.debugDescription); - + if (error.code != RMStoreErrorCodeUnableToCompleteVerification) { // If we were unable to complete the verification we want StoreKit to keep reminding us of the transaction [queue finishTransaction:transaction]; } - + RMAddPaymentParameters *parameters = [self popAddPaymentParametersForIdentifier:productIdentifier]; if (parameters.failureBlock != nil) { parameters.failureBlock(transaction, error); } - + NSDictionary *extras = error ? @{RMStoreNotificationStoreError : error} : nil; [self postNotificationWithName:RMSKPaymentTransactionFailed transaction:transaction userInfoExtras:extras]; - + if (transaction.transactionState == SKPaymentTransactionStateRestored) { [self notifyRestoreTransactionFinishedIfApplicableAfterTransaction:transaction]; @@ -569,9 +569,12 @@ - (void)didFailTransaction:(SKPaymentTransaction *)transaction queue:(SKPaymentQ - (void)didRestoreTransaction:(SKPaymentTransaction *)transaction queue:(SKPaymentQueue*)queue { - RMStoreLog(@"transaction restored with product %@", transaction.originalTransaction.payment.productIdentifier); - - _pendingRestoredTransactionsCount++; + NSString *productIdentifier = transaction.originalTransaction.payment.productIdentifier; + RMStoreLog(@"transaction restored with product %@", productIdentifier); + + if(productIdentifier) { + [_pendingRestoredTransactionIds addObject:productIdentifier]; + } if (self.receiptVerifier != nil) { [self.receiptVerifier verifyTransaction:transaction success:^{ @@ -634,15 +637,15 @@ - (void)finishTransaction:(SKPaymentTransaction *)transaction queue:(SKPaymentQu NSString* productIdentifier = payment.productIdentifier; [queue finishTransaction:transaction]; [self.transactionPersistor persistTransaction:transaction]; - + RMAddPaymentParameters *wrapper = [self popAddPaymentParametersForIdentifier:productIdentifier]; if (wrapper.successBlock != nil) { wrapper.successBlock(transaction); } - + [self postNotificationWithName:RMSKPaymentTransactionFinished transaction:transaction userInfoExtras:nil]; - + if (transaction.transactionState == SKPaymentTransactionStateRestored) { [self notifyRestoreTransactionFinishedIfApplicableAfterTransaction:transaction]; @@ -654,9 +657,9 @@ - (void)notifyRestoreTransactionFinishedIfApplicableAfterTransaction:(SKPaymentT if (transaction != nil) { [_restoredTransactions addObject:transaction]; - _pendingRestoredTransactionsCount--; + [_pendingRestoredTransactionIds removeObject:transaction.payment.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) @@ -711,7 +714,7 @@ - (void)request:(SKRequest *)request didFailWithError:(NSError *)error - (void)addProduct:(SKProduct*)product { - _products[product.productIdentifier] = product; + _products[product.productIdentifier] = product; } - (void)postNotificationWithName:(NSString*)notificationName download:(SKDownload*)download userInfoExtras:(NSDictionary*)extras @@ -748,17 +751,17 @@ - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProdu RMStoreLog(@"products request received response"); NSArray *products = [NSArray arrayWithArray:response.products]; NSArray *invalidProductIdentifiers = [NSArray arrayWithArray:response.invalidProductIdentifiers]; - + for (SKProduct *product in products) { RMStoreLog(@"received product with id %@", product.productIdentifier); [self.store addProduct:product]; } - + [invalidProductIdentifiers enumerateObjectsUsingBlock:^(NSString *invalid, NSUInteger idx, BOOL *stop) { RMStoreLog(@"invalid product with id %@", invalid); }]; - + if (self.successBlock) { self.successBlock(products, invalidProductIdentifiers);