diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md index 65f7f86a16e6..d12fd807969b 100644 --- a/packages/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.4+6 + +* iOS: Fix the bug that prevent restored subscription transactions from being completed + ## 0.3.4+5 * Added necessary README docs for getting started with Android. diff --git a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h index ed1788186909..6f0c64bb85df 100644 --- a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h +++ b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h @@ -19,7 +19,8 @@ typedef void (^UpdatedDownloads)(NSArray *downloads); @interface FIAPaymentQueueHandler : NSObject // Unfinished transactions. -@property(nonatomic, readonly) NSDictionary *transactions; +@property(nonatomic, readonly) + NSDictionary *> *transactions; - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated diff --git a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m index 57370e16fcbb..ddf9b2736a77 100644 --- a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m +++ b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m @@ -16,7 +16,7 @@ @interface FIAPaymentQueueHandler () @property(nullable, copy, nonatomic) UpdatedDownloads updatedDownloads; @property(strong, nonatomic) - NSMutableDictionary *transactionsSetter; + NSMutableDictionary *> *transactionsSetter; @end @@ -81,7 +81,13 @@ - (void)paymentQueue:(SKPaymentQueue *)queue // will become impossible for clients to finish deferred transactions when needed. // 2. Using product identifiers can help prevent clients from purchasing the same // subscription more than once by accident. - self.transactionsSetter[transaction.payment.productIdentifier] = transaction; + NSMutableArray *transactionArray = + [self.transactionsSetter objectForKey:transaction.payment.productIdentifier]; + if (transactionArray == nil) { + transactionArray = [NSMutableArray array]; + } + [transactionArray addObject:transaction]; + self.transactionsSetter[transaction.payment.productIdentifier] = transactionArray; } } // notify dart through callbacks. @@ -92,7 +98,21 @@ - (void)paymentQueue:(SKPaymentQueue *)queue - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { - [self.transactionsSetter removeObjectForKey:transaction.payment.productIdentifier]; + NSString *productId = transaction.payment.productIdentifier; + + if ([self.transactionsSetter objectForKey:productId] == nil) { + continue; + } + + NSPredicate *predicate = [NSPredicate + predicateWithFormat:@"transactionIdentifier == %@", transaction.transactionIdentifier]; + NSArray *filteredTransactions = + [self.transactionsSetter[productId] filteredArrayUsingPredicate:predicate]; + [self.transactionsSetter[productId] removeObjectsInArray:filteredTransactions]; + + if (!self.transactionsSetter[productId] || !self.transactionsSetter[productId].count) { + [self.transactionsSetter removeObjectForKey:productId]; + } } self.transactionsRemoved(transactions); } @@ -128,7 +148,7 @@ - (BOOL)paymentQueue:(SKPaymentQueue *)queue #pragma mark - getter -- (NSDictionary *)transactions { +- (NSDictionary *> *)transactions { return [self.transactionsSetter copy]; } diff --git a/packages/in_app_purchase/ios/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/ios/Classes/InAppPurchasePlugin.m index 06fe74a613ae..62b86bf9d82e 100644 --- a/packages/in_app_purchase/ios/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/ios/Classes/InAppPurchasePlugin.m @@ -204,9 +204,8 @@ - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result return; } NSString *identifier = call.arguments; - SKPaymentTransaction *transaction = - [self.paymentQueueHandler.transactions objectForKey:identifier]; - if (!transaction) { + NSMutableArray *transactions = [self.paymentQueueHandler.transactions objectForKey:identifier]; + if (!transactions) { result([FlutterError errorWithCode:@"storekit_platform_invalid_transaction" message:[NSString @@ -214,14 +213,16 @@ - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result @"exist. Note that if the transactionState is " @"purchasing, the transactionIdentifier will be " @"nil(null).", - transaction.transactionIdentifier] + identifier] details:call.arguments]); return; } @try { + for (SKPaymentTransaction *transaction in transactions) { + [self.paymentQueueHandler finishTransaction:transaction]; + } // finish transaction will throw exception if the transaction type is purchasing. Notify dart // about this exception. - [self.paymentQueueHandler finishTransaction:transaction]; } @catch (NSException *e) { result([FlutterError errorWithCode:@"storekit_finish_transaction_exception" message:e.name diff --git a/packages/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/pubspec.yaml index 42bb0d8f4619..55ac120b30c6 100644 --- a/packages/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/pubspec.yaml @@ -1,7 +1,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. homepage: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase -version: 0.3.4+5 +version: 0.3.4+6 dependencies: async: ^2.0.8