-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Convert startProductRequest(), finishTransaction(), restoreTransactions(), presentCodeRedemptionSheet() to pigeon #6032
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
cabdcf4
55f3524
751b034
50fe53a
bab76ff
214cc93
b0d16e1
7524ba8
1374433
a3a7439
f7e0c4a
569abc2
0aadf34
26681aa
7b011be
0cb73cf
2247065
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -307,6 +307,10 @@ + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: | |
| } | ||
|
|
||
| + (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error { | ||
| if (!error) { | ||
| return nil; | ||
| } | ||
|
|
||
| NSMutableDictionary *userInfo = [NSMutableDictionary new]; | ||
| for (NSErrorUserInfoKey key in error.userInfo) { | ||
| id value = error.userInfo[key]; | ||
|
|
@@ -376,4 +380,134 @@ + (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefro | |
| return msg; | ||
| } | ||
|
|
||
| + (nullable SKProductSubscriptionPeriodMessage *)convertSKProductSubscriptionPeriodToPigeon: | ||
| (nullable SKProductSubscriptionPeriod *)period API_AVAILABLE(ios(12.2)) { | ||
| if (!period) { | ||
| return nil; | ||
| } | ||
|
|
||
| SKSubscriptionPeriodUnitMessage unit; | ||
| switch (period.unit) { | ||
| case SKProductPeriodUnitDay: | ||
| unit = SKSubscriptionPeriodUnitMessageDay; | ||
| break; | ||
| case SKProductPeriodUnitWeek: | ||
| unit = SKSubscriptionPeriodUnitMessageWeek; | ||
| break; | ||
| case SKProductPeriodUnitMonth: | ||
| unit = SKSubscriptionPeriodUnitMessageMonth; | ||
| break; | ||
| case SKProductPeriodUnitYear: | ||
| unit = SKSubscriptionPeriodUnitMessageYear; | ||
| break; | ||
| } | ||
|
|
||
| SKProductSubscriptionPeriodMessage *msg = | ||
| [SKProductSubscriptionPeriodMessage makeWithNumberOfUnits:period.numberOfUnits unit:unit]; | ||
|
|
||
| return msg; | ||
| } | ||
|
|
||
| + (nullable SKProductDiscountMessage *)convertProductDiscountToPigeon: | ||
| (nullable SKProductDiscount *)productDiscount API_AVAILABLE(ios(12.2)) { | ||
| if (!productDiscount) { | ||
| return nil; | ||
| } | ||
|
|
||
| SKProductDiscountPaymentModeMessage paymentMode; | ||
| switch (productDiscount.paymentMode) { | ||
| case SKProductDiscountPaymentModeFreeTrial: | ||
| paymentMode = SKProductDiscountPaymentModeMessageFreeTrial; | ||
| break; | ||
| case SKProductDiscountPaymentModePayAsYouGo: | ||
| paymentMode = SKProductDiscountPaymentModeMessagePayAsYouGo; | ||
| break; | ||
| case SKProductDiscountPaymentModePayUpFront: | ||
| paymentMode = SKProductDiscountPaymentModeMessagePayUpFront; | ||
| break; | ||
| } | ||
|
|
||
| SKProductDiscountTypeMessage type; | ||
| switch (productDiscount.type) { | ||
| case SKProductDiscountTypeIntroductory: | ||
| type = SKProductDiscountTypeMessageIntroductory; | ||
| break; | ||
| case SKProductDiscountTypeSubscription: | ||
| type = SKProductDiscountTypeMessageSubscription; | ||
| break; | ||
| } | ||
|
|
||
| SKProductDiscountMessage *msg = [SKProductDiscountMessage | ||
| makeWithPrice:productDiscount.price.description | ||
| priceLocale:[self convertNSLocaleToPigeon:productDiscount.priceLocale] | ||
| numberOfPeriods:productDiscount.numberOfPeriods | ||
| paymentMode:paymentMode | ||
| subscriptionPeriod:[self convertSKProductSubscriptionPeriodToPigeon:productDiscount | ||
| .subscriptionPeriod] | ||
| identifier:productDiscount.identifier | ||
| type:type]; | ||
|
|
||
| return msg; | ||
| } | ||
|
|
||
| + (nullable SKPriceLocaleMessage *)convertNSLocaleToPigeon:(nullable NSLocale *)locale | ||
| API_AVAILABLE(ios(12.2)) { | ||
| if (!locale) { | ||
| return nil; | ||
| } | ||
| SKPriceLocaleMessage *msg = [SKPriceLocaleMessage makeWithCurrencySymbol:locale.currencySymbol | ||
| currencyCode:locale.currencyCode | ||
| countryCode:locale.countryCode]; | ||
|
|
||
| return msg; | ||
| } | ||
|
|
||
| + (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)product | ||
| API_AVAILABLE(ios(12.2)) { | ||
| if (!product) { | ||
| return nil; | ||
| } | ||
|
|
||
| NSArray<SKProductDiscount *> *skProductDiscounts = product.discounts; | ||
| NSMutableArray<SKProductDiscountMessage *> *pigeonProductDiscounts = | ||
| [[NSMutableArray arrayWithCapacity:skProductDiscounts.count] init]; | ||
|
||
|
|
||
| for (SKProductDiscount *productDiscount in skProductDiscounts) { | ||
| [pigeonProductDiscounts addObject:[self convertProductDiscountToPigeon:productDiscount]]; | ||
| }; | ||
|
|
||
| SKProductMessage *msg = [SKProductMessage | ||
| makeWithProductIdentifier:product.productIdentifier | ||
| localizedTitle:product.localizedTitle | ||
| localizedDescription:product.localizedDescription | ||
| priceLocale:[self convertNSLocaleToPigeon:product.priceLocale] | ||
| subscriptionGroupIdentifier:product.subscriptionGroupIdentifier | ||
| price:product.price.description | ||
| subscriptionPeriod: | ||
| [self convertSKProductSubscriptionPeriodToPigeon:product.subscriptionPeriod] | ||
| introductoryPrice:[self convertProductDiscountToPigeon:product.introductoryPrice] | ||
| discounts:pigeonProductDiscounts]; | ||
|
|
||
| return msg; | ||
| } | ||
|
|
||
| + (nullable SKProductsResponseMessage *)convertProductsResponseToPigeon: | ||
| (nullable SKProductsResponse *)productsResponse API_AVAILABLE(ios(12.2)) { | ||
| if (!productsResponse) { | ||
| return nil; | ||
| } | ||
| NSArray<SKProduct *> *skProducts = productsResponse.products; | ||
| NSMutableArray<SKProductMessage *> *pigeonProducts = | ||
| [NSMutableArray arrayWithCapacity:skProducts.count]; | ||
|
|
||
| for (SKProduct *product in skProducts) { | ||
| [pigeonProducts addObject:[self convertProductToPigeon:product]]; | ||
| }; | ||
|
|
||
| SKProductsResponseMessage *msg = | ||
| [SKProductsResponseMessage makeWithProducts:pigeonProducts | ||
| invalidProductIdentifiers:productsResponse.invalidProductIdentifiers]; | ||
| return msg; | ||
| } | ||
|
|
||
| @end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -88,18 +88,7 @@ - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar | |
| } | ||
|
|
||
| - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { | ||
| if ([@"-[InAppPurchasePlugin startProductRequest:result:]" isEqualToString:call.method]) { | ||
| [self handleProductRequestMethodCall:call result:result]; | ||
| } else if ([@"-[InAppPurchasePlugin finishTransaction:result:]" isEqualToString:call.method]) { | ||
| [self finishTransaction:call result:result]; | ||
| } else if ([@"-[InAppPurchasePlugin restoreTransactions:result:]" isEqualToString:call.method]) { | ||
| [self restoreTransactions:call result:result]; | ||
| #if TARGET_OS_IOS | ||
| } else if ([@"-[InAppPurchasePlugin presentCodeRedemptionSheet:result:]" | ||
| isEqualToString:call.method]) { | ||
| [self presentCodeRedemptionSheet:call result:result]; | ||
| #endif | ||
| } else if ([@"-[InAppPurchasePlugin retrieveReceiptData:result:]" isEqualToString:call.method]) { | ||
| if ([@"-[InAppPurchasePlugin retrieveReceiptData:result:]" isEqualToString:call.method]) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is probably not related to your PR - is this format iOS only? It's very objc-specific. do we intend to change that after migrating to swift?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this entire code block/fcn will be removed after the pigeon migration so it should be fine |
||
| [self retrieveReceiptData:call result:result]; | ||
| } else if ([@"-[InAppPurchasePlugin refreshReceipt:result:]" isEqualToString:call.method]) { | ||
| [self refreshReceipt:call result:result]; | ||
|
|
@@ -147,38 +136,38 @@ - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable * | |
| return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; | ||
| } | ||
|
|
||
| - (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { | ||
| if (![call.arguments isKindOfClass:[NSArray class]]) { | ||
| result([FlutterError errorWithCode:@"storekit_invalid_argument" | ||
| message:@"Argument type of startRequest is not array" | ||
| details:call.arguments]); | ||
| return; | ||
| } | ||
| NSArray *productIdentifiers = (NSArray *)call.arguments; | ||
| - (void)startProductRequestProductIdentifiers:(NSArray<NSString *> *)productIdentifiers | ||
| completion:(void (^)(SKProductsResponseMessage *_Nullable, | ||
| FlutterError *_Nullable))completion { | ||
| SKProductsRequest *request = | ||
| [self getProductRequestWithIdentifiers:[NSSet setWithArray:productIdentifiers]]; | ||
| FIAPRequestHandler *handler = [[FIAPRequestHandler alloc] initWithRequest:request]; | ||
| [self.requestHandlers addObject:handler]; | ||
| __weak typeof(self) weakSelf = self; | ||
|
|
||
| [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response, | ||
| NSError *_Nullable error) { | ||
| if (error) { | ||
| result([FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" | ||
| message:error.localizedDescription | ||
| details:error.description]); | ||
| NSError *_Nullable startProductRequestError) { | ||
| FlutterError *error = nil; | ||
| if (startProductRequestError != nil) { | ||
| error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" | ||
| message:startProductRequestError.localizedDescription | ||
| details:startProductRequestError.description]; | ||
| completion(nil, error); | ||
| return; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||
| } | ||
| if (!response) { | ||
| result([FlutterError errorWithCode:@"storekit_platform_no_response" | ||
| message:@"Failed to get SKProductResponse in startRequest " | ||
| @"call. Error occured on iOS platform" | ||
| details:call.arguments]); | ||
| error = [FlutterError errorWithCode:@"storekit_platform_no_response" | ||
| message:@"Failed to get SKProductResponse in startRequest " | ||
| @"call. Error occured on iOS platform" | ||
| details:productIdentifiers]; | ||
| completion(nil, error); | ||
| return; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
| } | ||
| for (SKProduct *product in response.products) { | ||
| [self.productsCache setObject:product forKey:product.productIdentifier]; | ||
| } | ||
| result([FIAObjectTranslator getMapFromSKProductsResponse:response]); | ||
|
|
||
| completion([FIAObjectTranslator convertProductsResponseToPigeon:response], error); | ||
| [weakSelf.requestHandlers removeObject:handler]; | ||
| }]; | ||
| } | ||
|
|
@@ -240,16 +229,10 @@ - (void)addPaymentPaymentMap:(nonnull NSDictionary *)paymentMap | |
| } | ||
| } | ||
|
|
||
| - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result { | ||
| if (![call.arguments isKindOfClass:[NSDictionary class]]) { | ||
| result([FlutterError errorWithCode:@"storekit_invalid_argument" | ||
| message:@"Argument type of finishTransaction is not a Dictionary" | ||
| details:call.arguments]); | ||
| return; | ||
| } | ||
| NSDictionary *paymentMap = (NSDictionary *)call.arguments; | ||
| NSString *transactionIdentifier = [paymentMap objectForKey:@"transactionIdentifier"]; | ||
| NSString *productIdentifier = [paymentMap objectForKey:@"productIdentifier"]; | ||
| - (void)finishTransactionFinishMap:(nonnull NSDictionary<NSString *, NSString *> *)finishMap | ||
| error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { | ||
| NSString *transactionIdentifier = [finishMap objectForKey:@"transactionIdentifier"]; | ||
| NSString *productIdentifier = [finishMap objectForKey:@"productIdentifier"]; | ||
|
|
||
| NSArray<SKPaymentTransaction *> *pendingTransactions = | ||
| [self.paymentQueueHandler getUnfinishedTransactions]; | ||
|
|
@@ -265,35 +248,27 @@ - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result | |
| @try { | ||
| [self.paymentQueueHandler finishTransaction:transaction]; | ||
| } @catch (NSException *e) { | ||
| result([FlutterError errorWithCode:@"storekit_finish_transaction_exception" | ||
| message:e.name | ||
| details:e.description]); | ||
| *error = [FlutterError errorWithCode:@"storekit_finish_transaction_exception" | ||
| message:e.name | ||
| details:e.description]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where is this error used?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all generated pigeon functions have like an extra "error" param, I think it gets passed back to the dart side automatically |
||
| return; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| result(nil); | ||
| } | ||
|
|
||
| - (void)restoreTransactions:(FlutterMethodCall *)call result:(FlutterResult)result { | ||
| if (call.arguments && ![call.arguments isKindOfClass:[NSString class]]) { | ||
| result([FlutterError | ||
| errorWithCode:@"storekit_invalid_argument" | ||
| message:@"Argument is not nil and the type of finishTransaction is not a string." | ||
| details:call.arguments]); | ||
| return; | ||
| } | ||
| [self.paymentQueueHandler restoreTransactions:call.arguments]; | ||
| result(nil); | ||
| - (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName | ||
| error:(FlutterError *_Nullable __autoreleasing *_Nonnull) | ||
| error { | ||
| [self.paymentQueueHandler restoreTransactions:applicationUserName]; | ||
| } | ||
|
|
||
| - (void)presentCodeRedemptionSheetWithError: | ||
| (FlutterError *_Nullable __autoreleasing *_Nonnull)error { | ||
| #if TARGET_OS_IOS | ||
| - (void)presentCodeRedemptionSheet:(FlutterMethodCall *)call result:(FlutterResult)result { | ||
| [self.paymentQueueHandler presentCodeRedemptionSheet]; | ||
| result(nil); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just want to call out how these calls have disappears now. In the past, failing to call these created bugs, you've eliminated that risk now 👍 |
||
| } | ||
| #endif | ||
| } | ||
|
|
||
| - (void)retrieveReceiptData:(FlutterMethodCall *)call result:(FlutterResult)result { | ||
| FlutterError *error = nil; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: mark
erroras nullable