Skip to content

Commit 9385bbb

Browse files
authored
Convert startProductRequest(), finishTransaction(), restoreTransactions(), presentCodeRedemptionSheet() to pigeon (flutter#6032)
Part 2 of flutter#117910 This PR converts startProductRequest(), finishTransaction(), restoreTransactions(), presentCodeRedemptionSheet() to pigeon, as well as add all remaining converts to and from pigeons for SK objects. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing.
1 parent 0d9fea6 commit 9385bbb

19 files changed

+1822
-223
lines changed

packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.3.10
2+
3+
* Converts `startProductRequest()`, `finishTransaction()`, `restoreTransactions()`, `presentCodeRedemptionSheet()` to pigeon.
4+
15
## 0.3.9
26

37
* Converts `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon.

packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,22 @@ NS_ASSUME_NONNULL_BEGIN
6969
+ (nullable SKPaymentMessage *)convertPaymentToPigeon:(nullable SKPayment *)payment
7070
API_AVAILABLE(ios(12.2));
7171

72-
+ (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error;
72+
+ (nullable SKErrorMessage *)convertSKErrorToPigeon:(nullable NSError *)error;
73+
74+
+ (nullable SKProductsResponseMessage *)convertProductsResponseToPigeon:
75+
(nullable SKProductsResponse *)payment;
76+
77+
+ (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)product
78+
API_AVAILABLE(ios(12.2));
79+
80+
+ (nullable SKProductDiscountMessage *)convertProductDiscountToPigeon:
81+
(nullable SKProductDiscount *)productDiscount API_AVAILABLE(ios(12.2));
82+
83+
+ (nullable SKPriceLocaleMessage *)convertNSLocaleToPigeon:(nullable NSLocale *)locale
84+
API_AVAILABLE(ios(12.2));
85+
86+
+ (nullable SKProductSubscriptionPeriodMessage *)convertSKProductSubscriptionPeriodToPigeon:
87+
(nullable SKProductSubscriptionPeriod *)period API_AVAILABLE(ios(12.2));
7388
@end
7489

7590
NS_ASSUME_NONNULL_END

packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,11 @@ + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon:
306306
return msg;
307307
}
308308

309-
+ (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error {
309+
+ (nullable SKErrorMessage *)convertSKErrorToPigeon:(nullable NSError *)error {
310+
if (!error) {
311+
return nil;
312+
}
313+
310314
NSMutableDictionary *userInfo = [NSMutableDictionary new];
311315
for (NSErrorUserInfoKey key in error.userInfo) {
312316
id value = error.userInfo[key];
@@ -376,4 +380,134 @@ + (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefro
376380
return msg;
377381
}
378382

383+
+ (nullable SKProductSubscriptionPeriodMessage *)convertSKProductSubscriptionPeriodToPigeon:
384+
(nullable SKProductSubscriptionPeriod *)period API_AVAILABLE(ios(12.2)) {
385+
if (!period) {
386+
return nil;
387+
}
388+
389+
SKSubscriptionPeriodUnitMessage unit;
390+
switch (period.unit) {
391+
case SKProductPeriodUnitDay:
392+
unit = SKSubscriptionPeriodUnitMessageDay;
393+
break;
394+
case SKProductPeriodUnitWeek:
395+
unit = SKSubscriptionPeriodUnitMessageWeek;
396+
break;
397+
case SKProductPeriodUnitMonth:
398+
unit = SKSubscriptionPeriodUnitMessageMonth;
399+
break;
400+
case SKProductPeriodUnitYear:
401+
unit = SKSubscriptionPeriodUnitMessageYear;
402+
break;
403+
}
404+
405+
SKProductSubscriptionPeriodMessage *msg =
406+
[SKProductSubscriptionPeriodMessage makeWithNumberOfUnits:period.numberOfUnits unit:unit];
407+
408+
return msg;
409+
}
410+
411+
+ (nullable SKProductDiscountMessage *)convertProductDiscountToPigeon:
412+
(nullable SKProductDiscount *)productDiscount API_AVAILABLE(ios(12.2)) {
413+
if (!productDiscount) {
414+
return nil;
415+
}
416+
417+
SKProductDiscountPaymentModeMessage paymentMode;
418+
switch (productDiscount.paymentMode) {
419+
case SKProductDiscountPaymentModeFreeTrial:
420+
paymentMode = SKProductDiscountPaymentModeMessageFreeTrial;
421+
break;
422+
case SKProductDiscountPaymentModePayAsYouGo:
423+
paymentMode = SKProductDiscountPaymentModeMessagePayAsYouGo;
424+
break;
425+
case SKProductDiscountPaymentModePayUpFront:
426+
paymentMode = SKProductDiscountPaymentModeMessagePayUpFront;
427+
break;
428+
}
429+
430+
SKProductDiscountTypeMessage type;
431+
switch (productDiscount.type) {
432+
case SKProductDiscountTypeIntroductory:
433+
type = SKProductDiscountTypeMessageIntroductory;
434+
break;
435+
case SKProductDiscountTypeSubscription:
436+
type = SKProductDiscountTypeMessageSubscription;
437+
break;
438+
}
439+
440+
SKProductDiscountMessage *msg = [SKProductDiscountMessage
441+
makeWithPrice:productDiscount.price.description
442+
priceLocale:[self convertNSLocaleToPigeon:productDiscount.priceLocale]
443+
numberOfPeriods:productDiscount.numberOfPeriods
444+
paymentMode:paymentMode
445+
subscriptionPeriod:[self convertSKProductSubscriptionPeriodToPigeon:productDiscount
446+
.subscriptionPeriod]
447+
identifier:productDiscount.identifier
448+
type:type];
449+
450+
return msg;
451+
}
452+
453+
+ (nullable SKPriceLocaleMessage *)convertNSLocaleToPigeon:(nullable NSLocale *)locale
454+
API_AVAILABLE(ios(12.2)) {
455+
if (!locale) {
456+
return nil;
457+
}
458+
SKPriceLocaleMessage *msg = [SKPriceLocaleMessage makeWithCurrencySymbol:locale.currencySymbol
459+
currencyCode:locale.currencyCode
460+
countryCode:locale.countryCode];
461+
462+
return msg;
463+
}
464+
465+
+ (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)product
466+
API_AVAILABLE(ios(12.2)) {
467+
if (!product) {
468+
return nil;
469+
}
470+
471+
NSArray<SKProductDiscount *> *skProductDiscounts = product.discounts;
472+
NSMutableArray<SKProductDiscountMessage *> *pigeonProductDiscounts =
473+
[NSMutableArray arrayWithCapacity:skProductDiscounts.count];
474+
475+
for (SKProductDiscount *productDiscount in skProductDiscounts) {
476+
[pigeonProductDiscounts addObject:[self convertProductDiscountToPigeon:productDiscount]];
477+
};
478+
479+
SKProductMessage *msg = [SKProductMessage
480+
makeWithProductIdentifier:product.productIdentifier
481+
localizedTitle:product.localizedTitle
482+
localizedDescription:product.localizedDescription
483+
priceLocale:[self convertNSLocaleToPigeon:product.priceLocale]
484+
subscriptionGroupIdentifier:product.subscriptionGroupIdentifier
485+
price:product.price.description
486+
subscriptionPeriod:
487+
[self convertSKProductSubscriptionPeriodToPigeon:product.subscriptionPeriod]
488+
introductoryPrice:[self convertProductDiscountToPigeon:product.introductoryPrice]
489+
discounts:pigeonProductDiscounts];
490+
491+
return msg;
492+
}
493+
494+
+ (nullable SKProductsResponseMessage *)convertProductsResponseToPigeon:
495+
(nullable SKProductsResponse *)productsResponse API_AVAILABLE(ios(12.2)) {
496+
if (!productsResponse) {
497+
return nil;
498+
}
499+
NSArray<SKProduct *> *skProducts = productsResponse.products;
500+
NSMutableArray<SKProductMessage *> *pigeonProducts =
501+
[NSMutableArray arrayWithCapacity:skProducts.count];
502+
503+
for (SKProduct *product in skProducts) {
504+
[pigeonProducts addObject:[self convertProductToPigeon:product]];
505+
};
506+
507+
SKProductsResponseMessage *msg =
508+
[SKProductsResponseMessage makeWithProducts:pigeonProducts
509+
invalidProductIdentifiers:productsResponse.invalidProductIdentifiers];
510+
return msg;
511+
}
512+
379513
@end

packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m

Lines changed: 33 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,7 @@ - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar
8888
}
8989

9090
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
91-
if ([@"-[InAppPurchasePlugin startProductRequest:result:]" isEqualToString:call.method]) {
92-
[self handleProductRequestMethodCall:call result:result];
93-
} else if ([@"-[InAppPurchasePlugin finishTransaction:result:]" isEqualToString:call.method]) {
94-
[self finishTransaction:call result:result];
95-
} else if ([@"-[InAppPurchasePlugin restoreTransactions:result:]" isEqualToString:call.method]) {
96-
[self restoreTransactions:call result:result];
97-
#if TARGET_OS_IOS
98-
} else if ([@"-[InAppPurchasePlugin presentCodeRedemptionSheet:result:]"
99-
isEqualToString:call.method]) {
100-
[self presentCodeRedemptionSheet:call result:result];
101-
#endif
102-
} else if ([@"-[InAppPurchasePlugin retrieveReceiptData:result:]" isEqualToString:call.method]) {
91+
if ([@"-[InAppPurchasePlugin retrieveReceiptData:result:]" isEqualToString:call.method]) {
10392
[self retrieveReceiptData:call result:result];
10493
} else if ([@"-[InAppPurchasePlugin refreshReceipt:result:]" isEqualToString:call.method]) {
10594
[self refreshReceipt:call result:result];
@@ -147,38 +136,38 @@ - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *
147136
return [FIAObjectTranslator convertStorefrontToPigeon:storefront];
148137
}
149138

150-
- (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
151-
if (![call.arguments isKindOfClass:[NSArray class]]) {
152-
result([FlutterError errorWithCode:@"storekit_invalid_argument"
153-
message:@"Argument type of startRequest is not array"
154-
details:call.arguments]);
155-
return;
156-
}
157-
NSArray *productIdentifiers = (NSArray *)call.arguments;
139+
- (void)startProductRequestProductIdentifiers:(NSArray<NSString *> *)productIdentifiers
140+
completion:(void (^)(SKProductsResponseMessage *_Nullable,
141+
FlutterError *_Nullable))completion {
158142
SKProductsRequest *request =
159143
[self getProductRequestWithIdentifiers:[NSSet setWithArray:productIdentifiers]];
160144
FIAPRequestHandler *handler = [[FIAPRequestHandler alloc] initWithRequest:request];
161145
[self.requestHandlers addObject:handler];
162146
__weak typeof(self) weakSelf = self;
147+
163148
[handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response,
164-
NSError *_Nullable error) {
165-
if (error) {
166-
result([FlutterError errorWithCode:@"storekit_getproductrequest_platform_error"
167-
message:error.localizedDescription
168-
details:error.description]);
149+
NSError *_Nullable startProductRequestError) {
150+
FlutterError *error = nil;
151+
if (startProductRequestError != nil) {
152+
error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error"
153+
message:startProductRequestError.localizedDescription
154+
details:startProductRequestError.description];
155+
completion(nil, error);
169156
return;
170157
}
171158
if (!response) {
172-
result([FlutterError errorWithCode:@"storekit_platform_no_response"
173-
message:@"Failed to get SKProductResponse in startRequest "
174-
@"call. Error occured on iOS platform"
175-
details:call.arguments]);
159+
error = [FlutterError errorWithCode:@"storekit_platform_no_response"
160+
message:@"Failed to get SKProductResponse in startRequest "
161+
@"call. Error occured on iOS platform"
162+
details:productIdentifiers];
163+
completion(nil, error);
176164
return;
177165
}
178166
for (SKProduct *product in response.products) {
179167
[self.productsCache setObject:product forKey:product.productIdentifier];
180168
}
181-
result([FIAObjectTranslator getMapFromSKProductsResponse:response]);
169+
170+
completion([FIAObjectTranslator convertProductsResponseToPigeon:response], error);
182171
[weakSelf.requestHandlers removeObject:handler];
183172
}];
184173
}
@@ -240,16 +229,10 @@ - (void)addPaymentPaymentMap:(nonnull NSDictionary *)paymentMap
240229
}
241230
}
242231

243-
- (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result {
244-
if (![call.arguments isKindOfClass:[NSDictionary class]]) {
245-
result([FlutterError errorWithCode:@"storekit_invalid_argument"
246-
message:@"Argument type of finishTransaction is not a Dictionary"
247-
details:call.arguments]);
248-
return;
249-
}
250-
NSDictionary *paymentMap = (NSDictionary *)call.arguments;
251-
NSString *transactionIdentifier = [paymentMap objectForKey:@"transactionIdentifier"];
252-
NSString *productIdentifier = [paymentMap objectForKey:@"productIdentifier"];
232+
- (void)finishTransactionFinishMap:(nonnull NSDictionary<NSString *, NSString *> *)finishMap
233+
error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
234+
NSString *transactionIdentifier = [finishMap objectForKey:@"transactionIdentifier"];
235+
NSString *productIdentifier = [finishMap objectForKey:@"productIdentifier"];
253236

254237
NSArray<SKPaymentTransaction *> *pendingTransactions =
255238
[self.paymentQueueHandler getUnfinishedTransactions];
@@ -265,35 +248,27 @@ - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result
265248
@try {
266249
[self.paymentQueueHandler finishTransaction:transaction];
267250
} @catch (NSException *e) {
268-
result([FlutterError errorWithCode:@"storekit_finish_transaction_exception"
269-
message:e.name
270-
details:e.description]);
251+
*error = [FlutterError errorWithCode:@"storekit_finish_transaction_exception"
252+
message:e.name
253+
details:e.description];
271254
return;
272255
}
273256
}
274257
}
275-
276-
result(nil);
277258
}
278259

279-
- (void)restoreTransactions:(FlutterMethodCall *)call result:(FlutterResult)result {
280-
if (call.arguments && ![call.arguments isKindOfClass:[NSString class]]) {
281-
result([FlutterError
282-
errorWithCode:@"storekit_invalid_argument"
283-
message:@"Argument is not nil and the type of finishTransaction is not a string."
284-
details:call.arguments]);
285-
return;
286-
}
287-
[self.paymentQueueHandler restoreTransactions:call.arguments];
288-
result(nil);
260+
- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName
261+
error:(FlutterError *_Nullable __autoreleasing *_Nonnull)
262+
error {
263+
[self.paymentQueueHandler restoreTransactions:applicationUserName];
289264
}
290265

266+
- (void)presentCodeRedemptionSheetWithError:
267+
(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
291268
#if TARGET_OS_IOS
292-
- (void)presentCodeRedemptionSheet:(FlutterMethodCall *)call result:(FlutterResult)result {
293269
[self.paymentQueueHandler presentCodeRedemptionSheet];
294-
result(nil);
295-
}
296270
#endif
271+
}
297272

298273
- (void)retrieveReceiptData:(FlutterMethodCall *)call result:(FlutterResult)result {
299274
FlutterError *error = nil;

0 commit comments

Comments
 (0)