From 300f223734d9ab5aaf107af80a139eba2e9556d3 Mon Sep 17 00:00:00 2001 From: Ziggy Crane Date: Mon, 13 Jul 2020 14:04:39 +0300 Subject: [PATCH 1/7] Fix the bug that prevent restored subscription transactions from being completed --- .../ios/Classes/FIAPaymentQueueHandler.h | 2 +- .../ios/Classes/FIAPaymentQueueHandler.m | 16 ++++++++++++---- .../ios/Classes/InAppPurchasePlugin.m | 11 ++++++----- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h index ed1788186909..f516c7e27f7d 100644 --- a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h +++ b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h @@ -19,7 +19,7 @@ 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..430caac088e0 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,11 @@ - (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; + if ([self.transactionsSetter objectForKey:transaction.payment.productIdentifier] == nil) { + self.transactionsSetter[transaction.payment.productIdentifier] = [NSMutableArray array]; + } + + [self.transactionsSetter[transaction.payment.productIdentifier] addObject:transaction]; } } // notify dart through callbacks. @@ -92,7 +96,11 @@ - (void)paymentQueue:(SKPaymentQueue *)queue - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { - [self.transactionsSetter removeObjectForKey:transaction.payment.productIdentifier]; + if ([self.transactionsSetter objectForKey:transaction.payment.productIdentifier] == nil) { + continue; + } + + [self.transactionsSetter[transaction.payment.productIdentifier] removeObject:transaction]; } self.transactionsRemoved(transactions); } @@ -128,7 +136,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 bfe29f981396..a9f965435935 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 From 17af9f9bfb1e07e706ab4dc8cf29547728c41a46 Mon Sep 17 00:00:00 2001 From: Ziggy Crane Date: Mon, 13 Jul 2020 14:05:35 +0300 Subject: [PATCH 2/7] Update changelog --- packages/in_app_purchase/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md index c159b094fb47..cd8ee84c6385 100644 --- a/packages/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.4+2 + +* iOS: Fix the bug that prevent restored subscription transactions from being completed + ## 0.3.4+1 * iOS: Fix the bug that `SKPaymentQueueWrapper.transactions` doesn't return all transactions. From b90c6c125992faadb3f0198390f85126f41cadc7 Mon Sep 17 00:00:00 2001 From: Ziggy Crane Date: Mon, 13 Jul 2020 14:55:39 +0300 Subject: [PATCH 3/7] increased version --- packages/in_app_purchase/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/pubspec.yaml index 58f8e1174618..96432507a7af 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+1 +version: 0.3.4+2 dependencies: async: ^2.0.8 From b52fca17ba08e109102f19f1d05ec04cbac414d0 Mon Sep 17 00:00:00 2001 From: Ziggy Crane Date: Tue, 14 Jul 2020 18:52:06 +0300 Subject: [PATCH 4/7] fixed removing transactions from transactionsSetter --- .../ios/Classes/FIAPaymentQueueHandler.m | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m index 430caac088e0..3977e60ab69b 100644 --- a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m +++ b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m @@ -81,11 +81,11 @@ - (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. - if ([self.transactionsSetter objectForKey:transaction.payment.productIdentifier] == nil) { - self.transactionsSetter[transaction.payment.productIdentifier] = [NSMutableArray array]; - } + if ([self.transactionsSetter objectForKey:transaction.payment.productIdentifier] == nil) { + self.transactionsSetter[transaction.payment.productIdentifier] = [NSMutableArray array]; + } - [self.transactionsSetter[transaction.payment.productIdentifier] addObject:transaction]; + [self.transactionsSetter[transaction.payment.productIdentifier] addObject:transaction]; } } // notify dart through callbacks. @@ -96,11 +96,17 @@ - (void)paymentQueue:(SKPaymentQueue *)queue - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { - if ([self.transactionsSetter objectForKey:transaction.payment.productIdentifier] == nil) { + NSString *productId = transaction.payment.productIdentifier; + + if ([self.transactionsSetter objectForKey:productId] == nil) { continue; } - [self.transactionsSetter[transaction.payment.productIdentifier] removeObject:transaction]; + [self.transactionsSetter[productId] removeObjectsInArray:[self.transactionsSetter[productId] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"transactionIdentifier == %@", transaction.transactionIdentifier]]]; + + if (!self.transactionsSetter[productId] || !self.transactionsSetter[productId].count) { + [self.transactionsSetter removeObjectForKey:productId]; + } } self.transactionsRemoved(transactions); } From 6611eff025a3306df256e61c38ca3ab7e7c12717 Mon Sep 17 00:00:00 2001 From: Ziggy Crane Date: Sat, 8 Aug 2020 10:58:55 +0300 Subject: [PATCH 5/7] Fixed CHANGELOGS conflicts --- packages/in_app_purchase/CHANGELOG.md | 4 ++++ packages/in_app_purchase/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md index 7e2eb3079396..5109a9b4ea1f 100644 --- a/packages/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.4+4 + +* iOS: Fix the bug that prevent restored subscription transactions from being completed + ## 0.3.4+3 * Fixed typo 'manuelly' for 'manually'. diff --git a/packages/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/pubspec.yaml index 4b50721bf22b..0d2f5785f8a6 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+3 +version: 0.3.4+4 dependencies: async: ^2.0.8 From cd52a972ef74c45b7a53e0ae42b13d257616e138 Mon Sep 17 00:00:00 2001 From: Ziggy Crane Date: Sat, 8 Aug 2020 11:23:38 +0300 Subject: [PATCH 6/7] transactionsSetter code formating updates --- .../ios/Classes/FIAPaymentQueueHandler.m | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m index 3977e60ab69b..4385856dc620 100644 --- a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m +++ b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m @@ -81,11 +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. - if ([self.transactionsSetter objectForKey:transaction.payment.productIdentifier] == nil) { - self.transactionsSetter[transaction.payment.productIdentifier] = [NSMutableArray array]; + NSMutableArray *transactionArray = [self.transactionsSetter + objectForKey:transaction.payment.productIdentifier]; + if (transactionArray == nil) { + transactionArray = [NSMutableArray array]; } - - [self.transactionsSetter[transaction.payment.productIdentifier] addObject:transaction]; + [transactionArray addObject:transaction]; + self.transactionsSetter[transaction.payment.productIdentifier] = transactionArray; } } // notify dart through callbacks. @@ -102,7 +104,11 @@ - (void)paymentQueue:(SKPaymentQueue *)queue continue; } - [self.transactionsSetter[productId] removeObjectsInArray:[self.transactionsSetter[productId] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"transactionIdentifier == %@", transaction.transactionIdentifier]]]; + 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]; From 6b4b8b181c834f27e84313da670e8a7c5fc4ac73 Mon Sep 17 00:00:00 2001 From: Ziggy Crane Date: Sat, 8 Aug 2020 11:51:30 +0300 Subject: [PATCH 7/7] fixed formating --- .../ios/Classes/FIAPaymentQueueHandler.h | 3 ++- .../ios/Classes/FIAPaymentQueueHandler.m | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.h index f516c7e27f7d..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 4385856dc620..ddf9b2736a77 100644 --- a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m +++ b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m @@ -81,8 +81,8 @@ - (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. - NSMutableArray *transactionArray = [self.transactionsSetter - objectForKey:transaction.payment.productIdentifier]; + NSMutableArray *transactionArray = + [self.transactionsSetter objectForKey:transaction.payment.productIdentifier]; if (transactionArray == nil) { transactionArray = [NSMutableArray array]; } @@ -104,10 +104,10 @@ - (void)paymentQueue:(SKPaymentQueue *)queue continue; } - NSPredicate *predicate = [NSPredicate predicateWithFormat: - @"transactionIdentifier == %@", transaction.transactionIdentifier]; - NSArray *filteredTransactions = [self.transactionsSetter[productId] - filteredArrayUsingPredicate:predicate]; + 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) {