From cabdcf4295f38707b4e253911fed01cfe7b5d095 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 1 Feb 2024 16:37:28 -0800 Subject: [PATCH 01/12] all dart pigeon types and converters created for startProductRequest --- .../darwin/Classes/InAppPurchasePlugin.m | 37 ++ .../darwin/Classes/messages.g.h | 214 +++++++--- .../darwin/Classes/messages.g.m | 402 +++++++++++++++--- .../ios/Runner.xcodeproj/project.pbxproj | 8 +- .../RunnerTests/InAppPurchasePluginTests.m | 2 +- .../lib/src/messages.g.dart | 332 +++++++++++++-- .../sk_product_wrapper.dart | 87 +++- .../store_kit_wrappers/sk_request_maker.dart | 24 +- .../pigeons/messages.dart | 111 +++++ .../test/fakes/fake_storekit_platform.dart | 6 + .../sk_methodchannel_apis_test.dart | 6 + .../test/test_api.g.dart | 153 ++++--- 12 files changed, 1151 insertions(+), 231 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m index 88cc4e8a5d03..4a57744eb7f8 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m @@ -147,6 +147,43 @@ - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable * return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; } +- (nullable NSDictionary *)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + + SKProductsRequest *request = + [self getProductRequestWithIdentifiers:[NSSet setWithArray:productIdentifiers]]; + FIAPRequestHandler *handler = [[FIAPRequestHandler alloc] initWithRequest:request]; + [self.requestHandlers addObject:handler]; + __weak typeof(self) weakSelf = self; + + NSError *startProductRequestError; + SKProductsResponse *result; + + [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable result, + NSError *_Nullable startProductRequestError) { + if (startProductRequestError) { + *error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" + message:startProductRequestError.localizedDescription + details:startProductRequestError.description]; + } + if (!result) { + *error = [FlutterError errorWithCode:@"storekit_platform_no_response" + message:@"Failed to get SKProductResponse in startRequest " + @"call. Error occured on iOS platform" + details:productIdentifiers]; + } + for (SKProduct *product in result.products) { + [self.productsCache setObject:product forKey:product.productIdentifier]; + } + + [weakSelf.requestHandlers removeObject:handler]; + }]; + + + + return [FIAObjectTranslator getMapFromSKProductsResponse:result]; + +} + - (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if (![call.arguments isKindOfClass:[NSArray class]]) { result([FlutterError errorWithCode:@"storekit_invalid_argument" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index 3874b7b20a88..9dbdfbe9da95 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -1,7 +1,3 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - // Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @@ -52,78 +48,199 @@ typedef NS_ENUM(NSUInteger, SKPaymentTransactionStateMessage) { - (instancetype)initWithValue:(SKPaymentTransactionStateMessage)value; @end +typedef NS_ENUM(NSUInteger, SKProductDiscountTypeMessage) { + /// A constant indicating the discount type is an introductory offer. + SKProductDiscountTypeMessageIntroductory = 0, + /// A constant indicating the discount type is a promotional offer. + SKProductDiscountTypeMessageSubscription = 1, +}; + +/// Wrapper for SKProductDiscountTypeMessage to allow for nullability. +@interface SKProductDiscountTypeMessageBox : NSObject +@property(nonatomic, assign) SKProductDiscountTypeMessage value; +- (instancetype)initWithValue:(SKProductDiscountTypeMessage)value; +@end + +typedef NS_ENUM(NSUInteger, SKProductDiscountPaymentModeMessage) { + /// Allows user to pay the discounted price at each payment period. + SKProductDiscountPaymentModeMessagePayAsYouGo = 0, + /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. + SKProductDiscountPaymentModeMessagePayUpFront = 1, + /// User pays nothing during the discounted period. + SKProductDiscountPaymentModeMessageFreeTrial = 2, + /// Unspecified mode. + SKProductDiscountPaymentModeMessageUnspecified = 3, +}; + +/// Wrapper for SKProductDiscountPaymentModeMessage to allow for nullability. +@interface SKProductDiscountPaymentModeMessageBox : NSObject +@property(nonatomic, assign) SKProductDiscountPaymentModeMessage value; +- (instancetype)initWithValue:(SKProductDiscountPaymentModeMessage)value; +@end + +typedef NS_ENUM(NSUInteger, SKSubscriptionPeriodUnitMessage) { + SKSubscriptionPeriodUnitMessageDay = 0, + SKSubscriptionPeriodUnitMessageWeek = 1, + SKSubscriptionPeriodUnitMessageMonth = 2, + SKSubscriptionPeriodUnitMessageYear = 3, +}; + +/// Wrapper for SKSubscriptionPeriodUnitMessage to allow for nullability. +@interface SKSubscriptionPeriodUnitMessageBox : NSObject +@property(nonatomic, assign) SKSubscriptionPeriodUnitMessage value; +- (instancetype)initWithValue:(SKSubscriptionPeriodUnitMessage)value; +@end + @class SKPaymentTransactionMessage; @class SKPaymentMessage; @class SKErrorMessage; @class SKPaymentDiscountMessage; @class SKStorefrontMessage; +@class SKProductResponseMessage; +@class SKProductMessage; +@class SKPriceLocaleMessage; +@class SKProductDiscountMessage; +@class SKProductSubscriptionPeriodMessage; @interface SKPaymentTransactionMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPayment:(SKPaymentMessage *)payment - transactionState:(SKPaymentTransactionStateMessage)transactionState - originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction - transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp - transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable SKErrorMessage *)error; -@property(nonatomic, strong) SKPaymentMessage *payment; + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable SKErrorMessage *)error; +@property(nonatomic, strong) SKPaymentMessage * payment; @property(nonatomic, assign) SKPaymentTransactionStateMessage transactionState; -@property(nonatomic, strong, nullable) SKPaymentTransactionMessage *originalTransaction; -@property(nonatomic, strong, nullable) NSNumber *transactionTimeStamp; -@property(nonatomic, copy, nullable) NSString *transactionIdentifier; -@property(nonatomic, strong, nullable) SKErrorMessage *error; +@property(nonatomic, strong, nullable) SKPaymentTransactionMessage * originalTransaction; +@property(nonatomic, strong, nullable) NSNumber * transactionTimeStamp; +@property(nonatomic, copy, nullable) NSString * transactionIdentifier; +@property(nonatomic, strong, nullable) SKErrorMessage * error; @end @interface SKPaymentMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSInteger)quantity - simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; -@property(nonatomic, copy) NSString *productIdentifier; -@property(nonatomic, copy, nullable) NSString *applicationUsername; -@property(nonatomic, copy, nullable) NSString *requestData; -@property(nonatomic, assign) NSInteger quantity; -@property(nonatomic, assign) BOOL simulatesAskToBuyInSandbox; -@property(nonatomic, strong, nullable) SKPaymentDiscountMessage *paymentDiscount; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSInteger )quantity + simulatesAskToBuyInSandbox:(BOOL )simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; +@property(nonatomic, copy) NSString * productIdentifier; +@property(nonatomic, copy, nullable) NSString * applicationUsername; +@property(nonatomic, copy, nullable) NSString * requestData; +@property(nonatomic, assign) NSInteger quantity; +@property(nonatomic, assign) BOOL simulatesAskToBuyInSandbox; +@property(nonatomic, strong, nullable) SKPaymentDiscountMessage * paymentDiscount; @end @interface SKErrorMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithCode:(NSInteger)code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo; -@property(nonatomic, assign) NSInteger code; -@property(nonatomic, copy) NSString *domain; -@property(nonatomic, copy) NSDictionary *userInfo; ++ (instancetype)makeWithCode:(NSInteger )code + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo; +@property(nonatomic, assign) NSInteger code; +@property(nonatomic, copy) NSString * domain; +@property(nonatomic, copy) NSDictionary * userInfo; @end @interface SKPaymentDiscountMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSInteger)timestamp; -@property(nonatomic, copy) NSString *identifier; -@property(nonatomic, copy) NSString *keyIdentifier; -@property(nonatomic, copy) NSString *nonce; -@property(nonatomic, copy) NSString *signature; -@property(nonatomic, assign) NSInteger timestamp; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSInteger )timestamp; +@property(nonatomic, copy) NSString * identifier; +@property(nonatomic, copy) NSString * keyIdentifier; +@property(nonatomic, copy) NSString * nonce; +@property(nonatomic, copy) NSString * signature; +@property(nonatomic, assign) NSInteger timestamp; @end @interface SKStorefrontMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier; -@property(nonatomic, copy) NSString *countryCode; -@property(nonatomic, copy) NSString *identifier; ++ (instancetype)makeWithCountryCode:(NSString *)countryCode + identifier:(NSString *)identifier; +@property(nonatomic, copy) NSString * countryCode; +@property(nonatomic, copy) NSString * identifier; +@end + +@interface SKProductResponseMessage : NSObject ++ (instancetype)makeWithProducts:(nullable NSArray *)products + invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers; +@property(nonatomic, copy, nullable) NSArray * products; +@property(nonatomic, copy, nullable) NSArray * invalidProductIdentifiers; +@end + +@interface SKProductMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier + localizedTitle:(NSString *)localizedTitle + localizedDescription:(NSString *)localizedDescription + priceLocale:(SKPriceLocaleMessage *)priceLocale + subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier + price:(NSString *)price + subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod + introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice + discounts:(nullable NSArray *)discounts; +@property(nonatomic, copy) NSString * productIdentifier; +@property(nonatomic, copy) NSString * localizedTitle; +@property(nonatomic, copy) NSString * localizedDescription; +@property(nonatomic, strong) SKPriceLocaleMessage * priceLocale; +@property(nonatomic, copy, nullable) NSString * subscriptionGroupIdentifier; +@property(nonatomic, copy) NSString * price; +@property(nonatomic, strong, nullable) SKProductSubscriptionPeriodMessage * subscriptionPeriod; +@property(nonatomic, strong, nullable) SKProductDiscountMessage * introductoryPrice; +@property(nonatomic, copy, nullable) NSArray * discounts; +@end + +@interface SKPriceLocaleMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithCurrencySymbol:(NSString *)currencySymbol + currencyCode:(NSString *)currencyCode + countryCode:(NSString *)countryCode; +///The currency symbol for the locale, e.g. $ for US locale. +@property(nonatomic, copy) NSString * currencySymbol; +///The currency code for the locale, e.g. USD for US locale. +@property(nonatomic, copy) NSString * currencyCode; +///The country code for the locale, e.g. US for US locale. +@property(nonatomic, copy) NSString * countryCode; +@end + +@interface SKProductDiscountMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPrice:(NSString *)price + priceLocale:(SKPriceLocaleMessage *)priceLocale + numberOfPeriods:(NSInteger )numberOfPeriods + paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode + subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod + identifier:(nullable NSString *)identifier + type:(SKProductDiscountTypeMessage)type; +@property(nonatomic, copy) NSString * price; +@property(nonatomic, strong) SKPriceLocaleMessage * priceLocale; +@property(nonatomic, assign) NSInteger numberOfPeriods; +@property(nonatomic, assign) SKProductDiscountPaymentModeMessage paymentMode; +@property(nonatomic, strong) SKProductSubscriptionPeriodMessage * subscriptionPeriod; +@property(nonatomic, copy, nullable) NSString * identifier; +@property(nonatomic, assign) SKProductDiscountTypeMessage type; +@end + +@interface SKProductSubscriptionPeriodMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithNumberOfUnits:(NSInteger )numberOfUnits + unit:(SKSubscriptionPeriodUnitMessage)unit; +@property(nonatomic, assign) NSInteger numberOfUnits; +@property(nonatomic, assign) SKSubscriptionPeriodUnitMessage unit; @end /// The codec used by InAppPurchaseAPI. @@ -135,15 +252,14 @@ NSObject *InAppPurchaseAPIGetCodec(void); /// @return `nil` only when `error != nil`. - (nullable NSNumber *)canMakePaymentsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSArray *)transactionsWithError: - (FlutterError *_Nullable *_Nonnull)error; +- (nullable NSArray *)transactionsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error; -- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable SKProductResponseMessage *)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers error:(FlutterError *_Nullable *_Nonnull)error; @end -extern void SetUpInAppPurchaseAPI(id binaryMessenger, - NSObject *_Nullable api); +extern void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 6608b7a0002b..8ac2f2fb071a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -1,6 +1,3 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. // Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @@ -40,6 +37,36 @@ - (instancetype)initWithValue:(SKPaymentTransactionStateMessage)value { } @end +@implementation SKProductDiscountTypeMessageBox +- (instancetype)initWithValue:(SKProductDiscountTypeMessage)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@implementation SKProductDiscountPaymentModeMessageBox +- (instancetype)initWithValue:(SKProductDiscountPaymentModeMessage)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@implementation SKSubscriptionPeriodUnitMessageBox +- (instancetype)initWithValue:(SKSubscriptionPeriodUnitMessage)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + @interface SKPaymentTransactionMessage () + (SKPaymentTransactionMessage *)fromList:(NSArray *)list; + (nullable SKPaymentTransactionMessage *)nullableFromList:(NSArray *)list; @@ -70,14 +97,44 @@ + (nullable SKStorefrontMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface SKProductResponseMessage () ++ (SKProductResponseMessage *)fromList:(NSArray *)list; ++ (nullable SKProductResponseMessage *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface SKProductMessage () ++ (SKProductMessage *)fromList:(NSArray *)list; ++ (nullable SKProductMessage *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface SKPriceLocaleMessage () ++ (SKPriceLocaleMessage *)fromList:(NSArray *)list; ++ (nullable SKPriceLocaleMessage *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface SKProductDiscountMessage () ++ (SKProductDiscountMessage *)fromList:(NSArray *)list; ++ (nullable SKProductDiscountMessage *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface SKProductSubscriptionPeriodMessage () ++ (SKProductSubscriptionPeriodMessage *)fromList:(NSArray *)list; ++ (nullable SKProductSubscriptionPeriodMessage *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @implementation SKPaymentTransactionMessage + (instancetype)makeWithPayment:(SKPaymentMessage *)payment - transactionState:(SKPaymentTransactionStateMessage)transactionState - originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction - transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp - transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable SKErrorMessage *)error { - SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable SKErrorMessage *)error { + SKPaymentTransactionMessage* pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = payment; pigeonResult.transactionState = transactionState; pigeonResult.originalTransaction = originalTransaction; @@ -90,8 +147,7 @@ + (SKPaymentTransactionMessage *)fromList:(NSArray *)list { SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; pigeonResult.transactionState = [GetNullableObjectAtIndex(list, 1) integerValue]; - pigeonResult.originalTransaction = - [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; + pigeonResult.originalTransaction = [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; pigeonResult.transactionTimeStamp = GetNullableObjectAtIndex(list, 3); pigeonResult.transactionIdentifier = GetNullableObjectAtIndex(list, 4); pigeonResult.error = [SKErrorMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; @@ -114,12 +170,12 @@ - (NSArray *)toList { @implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSInteger)quantity - simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { - SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSInteger )quantity + simulatesAskToBuyInSandbox:(BOOL )simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { + SKPaymentMessage* pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.applicationUsername = applicationUsername; pigeonResult.requestData = requestData; @@ -135,8 +191,7 @@ + (SKPaymentMessage *)fromList:(NSArray *)list { pigeonResult.requestData = GetNullableObjectAtIndex(list, 2); pigeonResult.quantity = [GetNullableObjectAtIndex(list, 3) integerValue]; pigeonResult.simulatesAskToBuyInSandbox = [GetNullableObjectAtIndex(list, 4) boolValue]; - pigeonResult.paymentDiscount = - [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } + (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { @@ -155,10 +210,10 @@ - (NSArray *)toList { @end @implementation SKErrorMessage -+ (instancetype)makeWithCode:(NSInteger)code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo { - SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; ++ (instancetype)makeWithCode:(NSInteger )code + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo { + SKErrorMessage* pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; pigeonResult.userInfo = userInfo; @@ -185,11 +240,11 @@ - (NSArray *)toList { @implementation SKPaymentDiscountMessage + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSInteger)timestamp { - SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSInteger )timestamp { + SKPaymentDiscountMessage* pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; pigeonResult.nonce = nonce; @@ -221,8 +276,9 @@ - (NSArray *)toList { @end @implementation SKStorefrontMessage -+ (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier { - SKStorefrontMessage *pigeonResult = [[SKStorefrontMessage alloc] init]; ++ (instancetype)makeWithCountryCode:(NSString *)countryCode + identifier:(NSString *)identifier { + SKStorefrontMessage* pigeonResult = [[SKStorefrontMessage alloc] init]; pigeonResult.countryCode = countryCode; pigeonResult.identifier = identifier; return pigeonResult; @@ -244,20 +300,207 @@ - (NSArray *)toList { } @end +@implementation SKProductResponseMessage ++ (instancetype)makeWithProducts:(nullable NSArray *)products + invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers { + SKProductResponseMessage* pigeonResult = [[SKProductResponseMessage alloc] init]; + pigeonResult.products = products; + pigeonResult.invalidProductIdentifiers = invalidProductIdentifiers; + return pigeonResult; +} ++ (SKProductResponseMessage *)fromList:(NSArray *)list { + SKProductResponseMessage *pigeonResult = [[SKProductResponseMessage alloc] init]; + pigeonResult.products = GetNullableObjectAtIndex(list, 0); + pigeonResult.invalidProductIdentifiers = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable SKProductResponseMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKProductResponseMessage fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.products ?: [NSNull null], + self.invalidProductIdentifiers ?: [NSNull null], + ]; +} +@end + +@implementation SKProductMessage ++ (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier + localizedTitle:(NSString *)localizedTitle + localizedDescription:(NSString *)localizedDescription + priceLocale:(SKPriceLocaleMessage *)priceLocale + subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier + price:(NSString *)price + subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod + introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice + discounts:(nullable NSArray *)discounts { + SKProductMessage* pigeonResult = [[SKProductMessage alloc] init]; + pigeonResult.productIdentifier = productIdentifier; + pigeonResult.localizedTitle = localizedTitle; + pigeonResult.localizedDescription = localizedDescription; + pigeonResult.priceLocale = priceLocale; + pigeonResult.subscriptionGroupIdentifier = subscriptionGroupIdentifier; + pigeonResult.price = price; + pigeonResult.subscriptionPeriod = subscriptionPeriod; + pigeonResult.introductoryPrice = introductoryPrice; + pigeonResult.discounts = discounts; + return pigeonResult; +} ++ (SKProductMessage *)fromList:(NSArray *)list { + SKProductMessage *pigeonResult = [[SKProductMessage alloc] init]; + pigeonResult.productIdentifier = GetNullableObjectAtIndex(list, 0); + pigeonResult.localizedTitle = GetNullableObjectAtIndex(list, 1); + pigeonResult.localizedDescription = GetNullableObjectAtIndex(list, 2); + pigeonResult.priceLocale = [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 3))]; + pigeonResult.subscriptionGroupIdentifier = GetNullableObjectAtIndex(list, 4); + pigeonResult.price = GetNullableObjectAtIndex(list, 5); + pigeonResult.subscriptionPeriod = [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 6))]; + pigeonResult.introductoryPrice = [SKProductDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 7))]; + pigeonResult.discounts = GetNullableObjectAtIndex(list, 8); + return pigeonResult; +} ++ (nullable SKProductMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKProductMessage fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.productIdentifier ?: [NSNull null], + self.localizedTitle ?: [NSNull null], + self.localizedDescription ?: [NSNull null], + (self.priceLocale ? [self.priceLocale toList] : [NSNull null]), + self.subscriptionGroupIdentifier ?: [NSNull null], + self.price ?: [NSNull null], + (self.subscriptionPeriod ? [self.subscriptionPeriod toList] : [NSNull null]), + (self.introductoryPrice ? [self.introductoryPrice toList] : [NSNull null]), + self.discounts ?: [NSNull null], + ]; +} +@end + +@implementation SKPriceLocaleMessage ++ (instancetype)makeWithCurrencySymbol:(NSString *)currencySymbol + currencyCode:(NSString *)currencyCode + countryCode:(NSString *)countryCode { + SKPriceLocaleMessage* pigeonResult = [[SKPriceLocaleMessage alloc] init]; + pigeonResult.currencySymbol = currencySymbol; + pigeonResult.currencyCode = currencyCode; + pigeonResult.countryCode = countryCode; + return pigeonResult; +} ++ (SKPriceLocaleMessage *)fromList:(NSArray *)list { + SKPriceLocaleMessage *pigeonResult = [[SKPriceLocaleMessage alloc] init]; + pigeonResult.currencySymbol = GetNullableObjectAtIndex(list, 0); + pigeonResult.currencyCode = GetNullableObjectAtIndex(list, 1); + pigeonResult.countryCode = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable SKPriceLocaleMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKPriceLocaleMessage fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.currencySymbol ?: [NSNull null], + self.currencyCode ?: [NSNull null], + self.countryCode ?: [NSNull null], + ]; +} +@end + +@implementation SKProductDiscountMessage ++ (instancetype)makeWithPrice:(NSString *)price + priceLocale:(SKPriceLocaleMessage *)priceLocale + numberOfPeriods:(NSInteger )numberOfPeriods + paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode + subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod + identifier:(nullable NSString *)identifier + type:(SKProductDiscountTypeMessage)type { + SKProductDiscountMessage* pigeonResult = [[SKProductDiscountMessage alloc] init]; + pigeonResult.price = price; + pigeonResult.priceLocale = priceLocale; + pigeonResult.numberOfPeriods = numberOfPeriods; + pigeonResult.paymentMode = paymentMode; + pigeonResult.subscriptionPeriod = subscriptionPeriod; + pigeonResult.identifier = identifier; + pigeonResult.type = type; + return pigeonResult; +} ++ (SKProductDiscountMessage *)fromList:(NSArray *)list { + SKProductDiscountMessage *pigeonResult = [[SKProductDiscountMessage alloc] init]; + pigeonResult.price = GetNullableObjectAtIndex(list, 0); + pigeonResult.priceLocale = [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + pigeonResult.numberOfPeriods = [GetNullableObjectAtIndex(list, 2) integerValue]; + pigeonResult.paymentMode = [GetNullableObjectAtIndex(list, 3) integerValue]; + pigeonResult.subscriptionPeriod = [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 4))]; + pigeonResult.identifier = GetNullableObjectAtIndex(list, 5); + pigeonResult.type = [GetNullableObjectAtIndex(list, 6) integerValue]; + return pigeonResult; +} ++ (nullable SKProductDiscountMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKProductDiscountMessage fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.price ?: [NSNull null], + (self.priceLocale ? [self.priceLocale toList] : [NSNull null]), + @(self.numberOfPeriods), + @(self.paymentMode), + (self.subscriptionPeriod ? [self.subscriptionPeriod toList] : [NSNull null]), + self.identifier ?: [NSNull null], + @(self.type), + ]; +} +@end + +@implementation SKProductSubscriptionPeriodMessage ++ (instancetype)makeWithNumberOfUnits:(NSInteger )numberOfUnits + unit:(SKSubscriptionPeriodUnitMessage)unit { + SKProductSubscriptionPeriodMessage* pigeonResult = [[SKProductSubscriptionPeriodMessage alloc] init]; + pigeonResult.numberOfUnits = numberOfUnits; + pigeonResult.unit = unit; + return pigeonResult; +} ++ (SKProductSubscriptionPeriodMessage *)fromList:(NSArray *)list { + SKProductSubscriptionPeriodMessage *pigeonResult = [[SKProductSubscriptionPeriodMessage alloc] init]; + pigeonResult.numberOfUnits = [GetNullableObjectAtIndex(list, 0) integerValue]; + pigeonResult.unit = [GetNullableObjectAtIndex(list, 1) integerValue]; + return pigeonResult; +} ++ (nullable SKProductSubscriptionPeriodMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKProductSubscriptionPeriodMessage fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.numberOfUnits), + @(self.unit), + ]; +} +@end + @interface InAppPurchaseAPICodecReader : FlutterStandardReader @end @implementation InAppPurchaseAPICodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 128: + case 128: return [SKErrorMessage fromList:[self readValue]]; - case 129: + case 129: return [SKPaymentDiscountMessage fromList:[self readValue]]; - case 130: + case 130: return [SKPaymentMessage fromList:[self readValue]]; - case 131: + case 131: return [SKPaymentTransactionMessage fromList:[self readValue]]; - case 132: + case 132: + return [SKPriceLocaleMessage fromList:[self readValue]]; + case 133: + return [SKProductDiscountMessage fromList:[self readValue]]; + case 134: + return [SKProductMessage fromList:[self readValue]]; + case 135: + return [SKProductResponseMessage fromList:[self readValue]]; + case 136: + return [SKProductSubscriptionPeriodMessage fromList:[self readValue]]; + case 137: return [SKStorefrontMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -281,9 +524,24 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[SKPaymentTransactionMessage class]]) { [self writeByte:131]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[SKStorefrontMessage class]]) { + } else if ([value isKindOfClass:[SKPriceLocaleMessage class]]) { [self writeByte:132]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[SKProductDiscountMessage class]]) { + [self writeByte:133]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[SKProductMessage class]]) { + [self writeByte:134]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[SKProductResponseMessage class]]) { + [self writeByte:135]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[SKProductSubscriptionPeriodMessage class]]) { + [self writeByte:136]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[SKStorefrontMessage class]]) { + [self writeByte:137]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -305,27 +563,22 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - InAppPurchaseAPICodecReaderWriter *readerWriter = - [[InAppPurchaseAPICodecReaderWriter alloc] init]; + InAppPurchaseAPICodecReaderWriter *readerWriter = [[InAppPurchaseAPICodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpInAppPurchaseAPI(id binaryMessenger, - NSObject *api) { +void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *api) { /// Returns if the current device is able to make payments { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(canMakePaymentsWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePaymentsWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(canMakePaymentsWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePaymentsWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSNumber *output = [api canMakePaymentsWithError:&error]; @@ -336,14 +589,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(transactionsWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactionsWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(transactionsWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactionsWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSArray *output = [api transactionsWithError:&error]; @@ -354,14 +606,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", - api); + NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; SKStorefrontMessage *output = [api storefrontWithError:&error]; @@ -372,15 +623,13 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(addPaymentPaymentMap:error:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPaymentPaymentMap:error:)", - api); + NSCAssert([api respondsToSelector:@selector(addPaymentPaymentMap:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPaymentPaymentMap:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_paymentMap = GetNullableObjectAtIndex(args, 0); @@ -392,4 +641,23 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(startProductRequestProductIdentifiers:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSArray *arg_productIdentifiers = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + SKProductResponseMessage *output = [api startProductRequestProductIdentifiers:arg_productIdentifiers error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index 06e0b3ac947d..5eef17d5f4a7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -579,7 +579,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -606,7 +606,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -639,7 +639,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = S8QB4VV633; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -666,7 +666,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = S8QB4VV633; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 3b855991b8f7..8fc43c144938 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -108,7 +108,7 @@ - (void)testGetProductResponse { XCTestExpectation *expectation = [self expectationWithDescription:@"expect response contains 1 item"]; FlutterMethodCall *call = [FlutterMethodCall - methodCallWithMethodName:@"-[InAppPurchasePlugin startProductRequest:result:]" + methodCallWithMethodName:@"-[`InAppPurchasePlugin startProductRequest`:result:]" arguments:@[ @"123" ]]; __block id result; [self.plugin handleMethodCall:call diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart index ba454380fbc9..88fd74727b6c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart @@ -15,8 +15,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse( - {Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -33,36 +32,56 @@ enum SKPaymentTransactionStateMessage { /// transaction to update to another state. Never complete a transaction that /// is still in a purchasing state. purchasing, - /// The user's payment has been succesfully processed. /// /// You should provide the user the content that they purchased. purchased, - /// The transaction failed. /// /// Check the [PaymentTransactionWrapper.error] property from /// [PaymentTransactionWrapper] for details. failed, - /// This transaction is restoring content previously purchased by the user. /// /// The previous transaction information can be obtained in /// [PaymentTransactionWrapper.originalTransaction] from /// [PaymentTransactionWrapper]. restored, - /// The transaction is in the queue but pending external action. Wait for /// another callback to get the final state. /// /// You should update your UI to indicate that you are waiting for the /// transaction to update to another state. deferred, - /// Indicates the transaction is in an unspecified state. unspecified, } +enum SKProductDiscountTypeMessage { + /// A constant indicating the discount type is an introductory offer. + introductory, + /// A constant indicating the discount type is a promotional offer. + subscription, +} + +enum SKProductDiscountPaymentModeMessage { + /// Allows user to pay the discounted price at each payment period. + payAsYouGo, + /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. + payUpFront, + /// User pays nothing during the discounted period. + freeTrial, + /// Unspecified mode. + unspecified, +} + +enum SKSubscriptionPeriodUnitMessage { + day, + week, + month, + year, +} + class SKPaymentTransactionMessage { SKPaymentTransactionMessage({ required this.payment, @@ -100,8 +119,7 @@ class SKPaymentTransactionMessage { result as List; return SKPaymentTransactionMessage( payment: SKPaymentMessage.decode(result[0]! as List), - transactionState: - SKPaymentTransactionStateMessage.values[result[1]! as int], + transactionState: SKPaymentTransactionStateMessage.values[result[1]! as int], originalTransaction: result[2] != null ? SKPaymentTransactionMessage.decode(result[2]! as List) : null, @@ -260,6 +278,208 @@ class SKStorefrontMessage { } } +class SKProductResponseMessage { + SKProductResponseMessage({ + this.products, + this.invalidProductIdentifiers, + }); + + List? products; + + List? invalidProductIdentifiers; + + Object encode() { + return [ + products, + invalidProductIdentifiers, + ]; + } + + static SKProductResponseMessage decode(Object result) { + result as List; + return SKProductResponseMessage( + products: (result[0] as List?)?.cast(), + invalidProductIdentifiers: (result[1] as List?)?.cast(), + ); + } +} + +class SKProductMessage { + SKProductMessage({ + required this.productIdentifier, + required this.localizedTitle, + required this.localizedDescription, + required this.priceLocale, + this.subscriptionGroupIdentifier, + required this.price, + this.subscriptionPeriod, + this.introductoryPrice, + this.discounts, + }); + + String productIdentifier; + + String localizedTitle; + + String localizedDescription; + + SKPriceLocaleMessage priceLocale; + + String? subscriptionGroupIdentifier; + + String price; + + SKProductSubscriptionPeriodMessage? subscriptionPeriod; + + SKProductDiscountMessage? introductoryPrice; + + List? discounts; + + Object encode() { + return [ + productIdentifier, + localizedTitle, + localizedDescription, + priceLocale.encode(), + subscriptionGroupIdentifier, + price, + subscriptionPeriod?.encode(), + introductoryPrice?.encode(), + discounts, + ]; + } + + static SKProductMessage decode(Object result) { + result as List; + return SKProductMessage( + productIdentifier: result[0]! as String, + localizedTitle: result[1]! as String, + localizedDescription: result[2]! as String, + priceLocale: SKPriceLocaleMessage.decode(result[3]! as List), + subscriptionGroupIdentifier: result[4] as String?, + price: result[5]! as String, + subscriptionPeriod: result[6] != null + ? SKProductSubscriptionPeriodMessage.decode(result[6]! as List) + : null, + introductoryPrice: result[7] != null + ? SKProductDiscountMessage.decode(result[7]! as List) + : null, + discounts: (result[8] as List?)?.cast(), + ); + } +} + +class SKPriceLocaleMessage { + SKPriceLocaleMessage({ + required this.currencySymbol, + required this.currencyCode, + required this.countryCode, + }); + + ///The currency symbol for the locale, e.g. $ for US locale. + String currencySymbol; + + ///The currency code for the locale, e.g. USD for US locale. + String currencyCode; + + ///The country code for the locale, e.g. US for US locale. + String countryCode; + + Object encode() { + return [ + currencySymbol, + currencyCode, + countryCode, + ]; + } + + static SKPriceLocaleMessage decode(Object result) { + result as List; + return SKPriceLocaleMessage( + currencySymbol: result[0]! as String, + currencyCode: result[1]! as String, + countryCode: result[2]! as String, + ); + } +} + +class SKProductDiscountMessage { + SKProductDiscountMessage({ + required this.price, + required this.priceLocale, + required this.numberOfPeriods, + required this.paymentMode, + required this.subscriptionPeriod, + this.identifier, + required this.type, + }); + + String price; + + SKPriceLocaleMessage priceLocale; + + int numberOfPeriods; + + SKProductDiscountPaymentModeMessage paymentMode; + + SKProductSubscriptionPeriodMessage subscriptionPeriod; + + String? identifier; + + SKProductDiscountTypeMessage type; + + Object encode() { + return [ + price, + priceLocale.encode(), + numberOfPeriods, + paymentMode.index, + subscriptionPeriod.encode(), + identifier, + type.index, + ]; + } + + static SKProductDiscountMessage decode(Object result) { + result as List; + return SKProductDiscountMessage( + price: result[0]! as String, + priceLocale: SKPriceLocaleMessage.decode(result[1]! as List), + numberOfPeriods: result[2]! as int, + paymentMode: SKProductDiscountPaymentModeMessage.values[result[3]! as int], + subscriptionPeriod: SKProductSubscriptionPeriodMessage.decode(result[4]! as List), + identifier: result[5] as String?, + type: SKProductDiscountTypeMessage.values[result[6]! as int], + ); + } +} + +class SKProductSubscriptionPeriodMessage { + SKProductSubscriptionPeriodMessage({ + required this.numberOfUnits, + required this.unit, + }); + + int numberOfUnits; + + SKSubscriptionPeriodUnitMessage unit; + + Object encode() { + return [ + numberOfUnits, + unit.index, + ]; + } + + static SKProductSubscriptionPeriodMessage decode(Object result) { + result as List; + return SKProductSubscriptionPeriodMessage( + numberOfUnits: result[0]! as int, + unit: SKSubscriptionPeriodUnitMessage.values[result[1]! as int], + ); + } +} + class _InAppPurchaseAPICodec extends StandardMessageCodec { const _InAppPurchaseAPICodec(); @override @@ -276,9 +496,24 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { } else if (value is SKPaymentTransactionMessage) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is SKStorefrontMessage) { + } else if (value is SKPriceLocaleMessage) { buffer.putUint8(132); writeValue(buffer, value.encode()); + } else if (value is SKProductDiscountMessage) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is SKProductMessage) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is SKProductResponseMessage) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is SKProductSubscriptionPeriodMessage) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is SKStorefrontMessage) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -287,15 +522,25 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return SKErrorMessage.decode(readValue(buffer)!); - case 129: + case 129: return SKPaymentDiscountMessage.decode(readValue(buffer)!); - case 130: + case 130: return SKPaymentMessage.decode(readValue(buffer)!); - case 131: + case 131: return SKPaymentTransactionMessage.decode(readValue(buffer)!); - case 132: + case 132: + return SKPriceLocaleMessage.decode(readValue(buffer)!); + case 133: + return SKProductDiscountMessage.decode(readValue(buffer)!); + case 134: + return SKProductMessage.decode(readValue(buffer)!); + case 135: + return SKProductResponseMessage.decode(readValue(buffer)!); + case 136: + return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); + case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -311,15 +556,12 @@ class InAppPurchaseAPI { : __pigeon_binaryMessenger = binaryMessenger; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _InAppPurchaseAPICodec(); + static const MessageCodec pigeonChannelCodec = _InAppPurchaseAPICodec(); /// Returns if the current device is able to make payments Future canMakePayments() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -345,10 +587,8 @@ class InAppPurchaseAPI { } Future> transactions() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -369,16 +609,13 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)! - .cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } Future storefront() async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -404,10 +641,8 @@ class InAppPurchaseAPI { } Future addPayment(Map paymentMap) async { - const String __pigeon_channelName = - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -426,4 +661,31 @@ class InAppPurchaseAPI { return; } } + + Future startProductRequest(List productIdentifiers) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([productIdentifiers]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as SKProductResponseMessage?)!; + } + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index 5eace6fda69e..093aa7a55038 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart @@ -6,6 +6,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; import 'enum_converters.dart'; +import '../messages.g.dart'; // WARNING: Changes to `@JsonSerializable` classes need to be reflected in the // below generated file. Run `flutter packages pub run build_runner watch` to @@ -64,6 +65,13 @@ class SkProductResponseWrapper { @override int get hashCode => Object.hash(products, invalidProductIdentifiers); + + static SkProductResponseWrapper convertFromPigeon(SKProductResponseMessage msg) { + return SkProductResponseWrapper( + products: msg.products!.map((SKProductMessage? e) => SKProductWrapper.convertFromPigeon(e!)).toList(), + invalidProductIdentifiers: msg.invalidProductIdentifiers != null ? msg.invalidProductIdentifiers!.cast() : [], + ); + } } /// Dart wrapper around StoreKit's [SKProductPeriodUnit](https://developer.apple.com/documentation/storekit/skproductperiodunit?language=objc). @@ -88,7 +96,20 @@ enum SKSubscriptionPeriodUnit { /// An interval lasting one year. @JsonValue(3) - year, + year; + + static SKSubscriptionPeriodUnit convertFromPigeon(SKSubscriptionPeriodUnitMessage msg) { + switch (msg) { + case SKSubscriptionPeriodUnitMessage.day: + return SKSubscriptionPeriodUnit.day; + case SKSubscriptionPeriodUnitMessage.week: + return SKSubscriptionPeriodUnit.week; + case SKSubscriptionPeriodUnitMessage.month: + return SKSubscriptionPeriodUnit.month; + case SKSubscriptionPeriodUnitMessage.year: + return SKSubscriptionPeriodUnit.year; + } + } } /// Dart wrapper around StoreKit's [SKProductSubscriptionPeriod](https://developer.apple.com/documentation/storekit/skproductsubscriptionperiod?language=objc). @@ -142,6 +163,12 @@ class SKProductSubscriptionPeriodWrapper { @override int get hashCode => Object.hash(numberOfUnits, unit); + + static SKProductSubscriptionPeriodWrapper convertFromPigeon(SKProductSubscriptionPeriodMessage msg) { + return SKProductSubscriptionPeriodWrapper( + numberOfUnits: msg.numberOfUnits, + unit: SKSubscriptionPeriodUnit.convertFromPigeon(msg.unit)); + } } /// Dart wrapper around StoreKit's [SKProductDiscountPaymentMode](https://developer.apple.com/documentation/storekit/skproductdiscountpaymentmode?language=objc). @@ -164,7 +191,20 @@ enum SKProductDiscountPaymentMode { /// Unspecified mode. @JsonValue(-1) - unspecified, + unspecified; + + static SKProductDiscountPaymentMode convertFromPigeon(SKProductDiscountPaymentModeMessage msg) { + switch (msg) { + case SKProductDiscountPaymentModeMessage.payAsYouGo: + return SKProductDiscountPaymentMode.payAsYouGo; + case SKProductDiscountPaymentModeMessage.payUpFront: + return SKProductDiscountPaymentMode.payUpFront; + case SKProductDiscountPaymentModeMessage.freeTrial: + return SKProductDiscountPaymentMode.freeTrail; + case SKProductDiscountPaymentModeMessage.unspecified: + return SKProductDiscountPaymentMode.unspecified; + } + } } /// Dart wrapper around StoreKit's [SKProductDiscountType] @@ -182,7 +222,16 @@ enum SKProductDiscountType { /// A constant indicating the discount type is a promotional offer. @JsonValue(1) - subscription, + subscription; + + static SKProductDiscountType convertFromPigeon(SKProductDiscountTypeMessage msg) { + switch (msg) { + case SKProductDiscountTypeMessage.introductory: + return SKProductDiscountType.introductory; + case SKProductDiscountTypeMessage.subscription: + return SKProductDiscountType.subscription; + } + } } /// Dart wrapper around StoreKit's [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc). @@ -265,6 +314,17 @@ class SKProductDiscountWrapper { @override int get hashCode => Object.hash(price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod, identifier, type); + + static SKProductDiscountWrapper convertFromPigeon(SKProductDiscountMessage msg) { + return SKProductDiscountWrapper( + price: msg.price, + priceLocale: SKPriceLocaleWrapper.convertFromPigeon(msg.priceLocale), + numberOfPeriods: msg.numberOfPeriods, + paymentMode: SKProductDiscountPaymentMode.convertFromPigeon(msg.paymentMode), + subscriptionPeriod: SKProductSubscriptionPeriodWrapper.convertFromPigeon(msg.subscriptionPeriod), + identifier: msg.identifier, + type: SKProductDiscountType.convertFromPigeon(msg.type)); + } } /// Dart wrapper around StoreKit's [SKProduct](https://developer.apple.com/documentation/storekit/skproduct?language=objc). @@ -383,6 +443,19 @@ class SKProductWrapper { subscriptionPeriod, introductoryPrice, discounts); + + static SKProductWrapper convertFromPigeon(SKProductMessage msg) { + return SKProductWrapper(productIdentifier: msg.productIdentifier, + localizedTitle: msg.localizedTitle, + localizedDescription: msg.localizedDescription, + priceLocale: SKPriceLocaleWrapper.convertFromPigeon(msg.priceLocale), + price: msg.price, + subscriptionGroupIdentifier: msg.subscriptionGroupIdentifier, + subscriptionPeriod: msg.subscriptionPeriod != null ? SKProductSubscriptionPeriodWrapper.convertFromPigeon(msg.subscriptionPeriod!) : null, + introductoryPrice: msg.introductoryPrice != null ? SKProductDiscountWrapper.convertFromPigeon(msg.introductoryPrice!) : null, + discounts: msg.discounts != null ? msg.discounts!.map((SKProductDiscountMessage? e) => SKProductDiscountWrapper.convertFromPigeon(e!)).toList() : [] + ); + } } /// Object that indicates the locale of the price @@ -442,4 +515,12 @@ class SKPriceLocaleWrapper { @override int get hashCode => Object.hash(currencySymbol, currencyCode); + + static SKPriceLocaleWrapper convertFromPigeon(SKPriceLocaleMessage msg) { + return SKPriceLocaleWrapper( + currencySymbol: msg.currencySymbol, + currencyCode: msg.currencyCode, + countryCode: msg.countryCode + ); + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart index d59f66fce2c9..2e57e3ad15df 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart @@ -5,16 +5,27 @@ import 'dart:async'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import '../channel.dart'; +import '../messages.g.dart'; import 'sk_product_wrapper.dart'; + + /// A request maker that handles all the requests made by SKRequest subclasses. /// /// There are multiple [SKRequest](https://developer.apple.com/documentation/storekit/skrequest?language=objc) subclasses handling different requests in the `StoreKit` with multiple delegate methods, /// we consolidated all the `SKRequest` subclasses into this class to make requests in a more straightforward way. /// The request maker will create a SKRequest object, immediately starting it, and completing the future successfully or throw an exception depending on what happened to the request. class SKRequestMaker { + InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); + + /// Set up pigeon API. + @visibleForTesting + void setInAppPurchaseHostApi(InAppPurchaseAPI api) { + _hostApi = api; + } /// Fetches product information for a list of given product identifiers. /// /// The `productIdentifiers` should contain legitimate product identifiers that you declared for the products in the iTunes Connect. Invalid identifiers @@ -26,18 +37,17 @@ class SKRequestMaker { /// A [PlatformException] is thrown if the platform code making the request fails. Future startProductRequest( List productIdentifiers) async { - final Map? productResponseMap = - await channel.invokeMapMethod( - '-[InAppPurchasePlugin startProductRequest:result:]', - productIdentifiers, - ); - if (productResponseMap == null) { + final SKProductResponseMessage productResponsePigeon = await _hostApi.startProductRequest(productIdentifiers); + + // should products be null or [] ? + if (productResponsePigeon.products == null) { throw PlatformException( code: 'storekit_no_response', message: 'StoreKit: Failed to get response from platform.', ); } - return SkProductResponseWrapper.fromJson(productResponseMap); + + return SkProductResponseWrapper.convertFromPigeon(productResponsePigeon); } /// Uses [SKReceiptRefreshRequest](https://developer.apple.com/documentation/storekit/skreceiptrefreshrequest?language=objc) to request a new receipt. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index d9d43cd7eff2..5c870698253d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -130,6 +130,115 @@ class SKStorefrontMessage { final String identifier; } +class SKProductResponseMessage { + const SKProductResponseMessage ({ + required this.products, + required this.invalidProductIdentifiers +}); + final List? products; + final List? invalidProductIdentifiers; +} + +class SKProductMessage { + const SKProductMessage({ + required this.productIdentifier, + required this.localizedTitle, + required this.localizedDescription, + required this.priceLocale, + required this.price, + this.subscriptionGroupIdentifier, + this.subscriptionPeriod, + this.introductoryPrice, + this.discounts + }); + + final String productIdentifier; + final String localizedTitle; + final String localizedDescription; + final SKPriceLocaleMessage priceLocale; + final String? subscriptionGroupIdentifier; + final String price; + final SKProductSubscriptionPeriodMessage? subscriptionPeriod; + final SKProductDiscountMessage? introductoryPrice; + final List? discounts; +} + +class SKPriceLocaleMessage { + SKPriceLocaleMessage({ + required this.currencySymbol, + required this.currencyCode, + required this.countryCode, + }); + + ///The currency symbol for the locale, e.g. $ for US locale. + final String currencySymbol; + + ///The currency code for the locale, e.g. USD for US locale. + final String currencyCode; + + ///The country code for the locale, e.g. US for US locale. + final String countryCode; +} + +class SKProductDiscountMessage { + const SKProductDiscountMessage( + {required this.price, + required this.priceLocale, + required this.numberOfPeriods, + required this.paymentMode, + required this.subscriptionPeriod, + required this.identifier, + required this.type}); + + final String price; + final SKPriceLocaleMessage priceLocale; + final int numberOfPeriods; + final SKProductDiscountPaymentModeMessage paymentMode; + final SKProductSubscriptionPeriodMessage subscriptionPeriod; + final String? identifier; + final SKProductDiscountTypeMessage type; +} + +enum SKProductDiscountTypeMessage { + /// A constant indicating the discount type is an introductory offer. + introductory, + + /// A constant indicating the discount type is a promotional offer. + subscription, +} + +enum SKProductDiscountPaymentModeMessage { + /// Allows user to pay the discounted price at each payment period. + payAsYouGo, + + /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. + payUpFront, + + /// User pays nothing during the discounted period. + freeTrial, + + /// Unspecified mode. + unspecified, +} + +class SKProductSubscriptionPeriodMessage { + SKProductSubscriptionPeriodMessage( + {required this.numberOfUnits, required this.unit}); + + final int numberOfUnits; + final SKSubscriptionPeriodUnitMessage unit; +} + +enum SKSubscriptionPeriodUnitMessage { + day, + + week, + + month, + + year, +} + @HostApi(dartHostTestHandler: 'TestInAppPurchaseApi') abstract class InAppPurchaseAPI { /// Returns if the current device is able to make payments @@ -140,4 +249,6 @@ abstract class InAppPurchaseAPI { SKStorefrontMessage storefront(); void addPayment(Map paymentMap); + + SKProductResponseMessage startProductRequest(List productIdentifiers); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index d26840ad8c07..8a3fbd56a955 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -288,4 +288,10 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { List transactions() { throw UnimplementedError(); } + + @override + Map? startProductRequest(List productIdentifiers) { + // TODO: implement startProductRequest + throw UnimplementedError(); + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart index 44228ebb523b..c5ad7d2b343f 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart @@ -295,6 +295,12 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { @override List transactions() => [dummyTransactionMessage]; + + @override + Map? startProductRequest(List productIdentifiers) { + // TODO: implement startProductRequest + throw UnimplementedError(); + } } class TestPaymentQueueDelegate extends SKPaymentQueueDelegateWrapper {} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart index a640a188c43c..2d319d5d0278 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart @@ -26,9 +26,24 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { } else if (value is SKPaymentTransactionMessage) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is SKStorefrontMessage) { + } else if (value is SKPriceLocaleMessage) { buffer.putUint8(132); writeValue(buffer, value.encode()); + } else if (value is SKProductDiscountMessage) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is SKProductMessage) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is SKProductResponseMessage) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is SKProductSubscriptionPeriodMessage) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is SKStorefrontMessage) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -37,15 +52,25 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return SKErrorMessage.decode(readValue(buffer)!); - case 129: + case 129: return SKPaymentDiscountMessage.decode(readValue(buffer)!); - case 130: + case 130: return SKPaymentMessage.decode(readValue(buffer)!); - case 131: + case 131: return SKPaymentTransactionMessage.decode(readValue(buffer)!); - case 132: + case 132: + return SKPriceLocaleMessage.decode(readValue(buffer)!); + case 133: + return SKProductDiscountMessage.decode(readValue(buffer)!); + case 134: + return SKProductMessage.decode(readValue(buffer)!); + case 135: + return SKProductResponseMessage.decode(readValue(buffer)!); + case 136: + return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); + case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -54,10 +79,8 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { } abstract class TestInAppPurchaseApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = - _TestInAppPurchaseApiCodec(); + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec pigeonChannelCodec = _TestInAppPurchaseApiCodec(); /// Returns if the current device is able to make payments bool canMakePayments(); @@ -68,102 +91,78 @@ abstract class TestInAppPurchaseApi { void addPayment(Map paymentMap); - static void setup(TestInAppPurchaseApi? api, - {BinaryMessenger? binaryMessenger}) { + SKProductResponseMessage startProductRequest(List productIdentifiers); + + static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { final bool output = api.canMakePayments(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { - final List output = - api.transactions(); + final List output = api.transactions(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { try { final SKStorefrontMessage output = api.storefront(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); final List args = (message as List?)!; - final Map? arg_paymentMap = - (args[0] as Map?)?.cast(); + final Map? arg_paymentMap = (args[0] as Map?)?.cast(); assert(arg_paymentMap != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null, expected non-null Map.'); try { @@ -171,9 +170,33 @@ abstract class TestInAppPurchaseApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null.'); + final List args = (message as List?)!; + final List? arg_productIdentifiers = (args[0] as List?)?.cast(); + assert(arg_productIdentifiers != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null, expected non-null List.'); + try { + final SKProductResponseMessage output = api.startProductRequest(arg_productIdentifiers!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } From 55f3524afac27ddedc2c396fd63564f17e7f73e1 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 7 Feb 2024 12:51:17 -0800 Subject: [PATCH 02/12] conversions working --- .../darwin/Classes/FIAObjectTranslator.h | 14 +++ .../darwin/Classes/FIAObjectTranslator.m | 118 ++++++++++++++++++ .../darwin/Classes/InAppPurchasePlugin.m | 109 +++------------- .../darwin/Classes/messages.g.h | 10 +- .../darwin/Classes/messages.g.m | 87 ++++++++++--- .../RunnerTests/InAppPurchasePluginTests.m | 23 ++-- .../lib/src/messages.g.dart | 86 +++++++++++-- .../sk_payment_queue_wrapper.dart | 12 +- .../sk_product_wrapper.dart | 2 +- .../store_kit_wrappers/sk_request_maker.dart | 2 +- .../pigeons/messages.dart | 16 ++- .../test/fakes/fake_storekit_platform.dart | 3 +- .../sk_methodchannel_apis_test.dart | 3 +- .../test/test_api.g.dart | 85 ++++++++++++- 14 files changed, 414 insertions(+), 156 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h index bf32cc128fc1..0ede2f4d46c5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h @@ -70,6 +70,20 @@ NS_ASSUME_NONNULL_BEGIN API_AVAILABLE(ios(12.2)); + (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error; + ++ (nullable SKProductsResponseMessage*)convertProductsResponseToPigeon:(nullable SKProductsResponse *)payment; + ++ (nullable SKProductMessage*)convertProductToPigeon:(nullable SKProduct *)product + API_AVAILABLE(ios(12.2)); + ++ (nullable SKProductDiscountMessage*)convertProductDiscountToPigeon:(nullable SKProductDiscount *)productDiscount + API_AVAILABLE(ios(12.2)); + ++ (nullable SKPriceLocaleMessage*)convertNSLocaleToPigeon:(nullable NSLocale *)locale + API_AVAILABLE(ios(12.2)); + ++ (nullable SKProductSubscriptionPeriodMessage*)convertSKProductSubscriptionPeriodToPigeon:(nullable SKProductSubscriptionPeriod *)period + API_AVAILABLE(ios(12.2)); @end NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m index bfc5542af5ec..753cff824e79 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m @@ -376,4 +376,122 @@ + (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)) { + NSArray *skProductDiscounts = product.discounts; + NSMutableArray *pigeonProductDiscounts = [[NSMutableArray alloc] 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)) { + + NSArray *skProducts = productsResponse.products; + NSMutableArray *pigeonProducts = [[NSMutableArray alloc] init]; + + for (SKProduct *product in skProducts) { + [pigeonProducts addObject:[self convertProductToPigeon:product]]; + }; + + SKProductsResponseMessage *msg = [SKProductsResponseMessage makeWithProducts:pigeonProducts + invalidProductIdentifiers:productsResponse.invalidProductIdentifiers]; + return msg; +} + @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m index 4a57744eb7f8..0141b21f0980 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m @@ -88,18 +88,7 @@ - (instancetype)initWithRegistrar:(NSObject *)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]) { [self retrieveReceiptData:call result:result]; } else if ([@"-[InAppPurchasePlugin refreshReceipt:result:]" isEqualToString:call.method]) { [self refreshReceipt:call result:result]; @@ -147,7 +136,7 @@ - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable * return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; } -- (nullable NSDictionary *)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { +- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers completion:(void (^)(SKProductsResponseMessage *_Nullable, FlutterError *_Nullable))completion { SKProductsRequest *request = [self getProductRequestWithIdentifiers:[NSSet setWithArray:productIdentifiers]]; @@ -155,67 +144,25 @@ - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable * [self.requestHandlers addObject:handler]; __weak typeof(self) weakSelf = self; - NSError *startProductRequestError; - SKProductsResponse *result; - - [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable result, + [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response, NSError *_Nullable startProductRequestError) { - if (startProductRequestError) { - *error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" + FlutterError *error = nil; + if (startProductRequestError != nil) { + error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" message:startProductRequestError.localizedDescription details:startProductRequestError.description]; } - if (!result) { - *error = [FlutterError errorWithCode:@"storekit_platform_no_response" - message:@"Failed to get SKProductResponse in startRequest " - @"call. Error occured on iOS platform" - details:productIdentifiers]; - } - for (SKProduct *product in result.products) { - [self.productsCache setObject:product forKey:product.productIdentifier]; - } - - [weakSelf.requestHandlers removeObject:handler]; - }]; - - - - return [FIAObjectTranslator getMapFromSKProductsResponse:result]; - -} - -- (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; - 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]); - return; - } if (!response) { - result([FlutterError errorWithCode:@"storekit_platform_no_response" + error = [FlutterError errorWithCode:@"storekit_platform_no_response" message:@"Failed to get SKProductResponse in startRequest " @"call. Error occured on iOS platform" - details:call.arguments]); - return; + details:productIdentifiers]; } 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]; }]; } @@ -277,16 +224,9 @@ - (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 *)finishMap error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + NSString *transactionIdentifier = [finishMap objectForKey:@"transactionIdentifier"]; + NSString *productIdentifier = [finishMap objectForKey:@"productIdentifier"]; NSArray *pendingTransactions = [self.paymentQueueHandler getUnfinishedTransactions]; @@ -302,35 +242,24 @@ - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result @try { [self.paymentQueueHandler finishTransaction:transaction]; } @catch (NSException *e) { - result([FlutterError errorWithCode:@"storekit_finish_transaction_exception" + *error = [FlutterError errorWithCode:@"storekit_finish_transaction_exception" message:e.name - details:e.description]); + details:e.description]; 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); -} #endif +} - (void)retrieveReceiptData:(FlutterMethodCall *)call result:(FlutterResult)result { FlutterError *error = nil; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index 9dbdfbe9da95..749b5e35a104 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -96,7 +96,7 @@ typedef NS_ENUM(NSUInteger, SKSubscriptionPeriodUnitMessage) { @class SKErrorMessage; @class SKPaymentDiscountMessage; @class SKStorefrontMessage; -@class SKProductResponseMessage; +@class SKProductsResponseMessage; @class SKProductMessage; @class SKPriceLocaleMessage; @class SKProductDiscountMessage; @@ -171,7 +171,7 @@ typedef NS_ENUM(NSUInteger, SKSubscriptionPeriodUnitMessage) { @property(nonatomic, copy) NSString * identifier; @end -@interface SKProductResponseMessage : NSObject +@interface SKProductsResponseMessage : NSObject + (instancetype)makeWithProducts:(nullable NSArray *)products invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers; @property(nonatomic, copy, nullable) NSArray * products; @@ -256,8 +256,10 @@ NSObject *InAppPurchaseAPIGetCodec(void); /// @return `nil` only when `error != nil`. - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error; - (void)addPaymentPaymentMap:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable SKProductResponseMessage *)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers error:(FlutterError *_Nullable *_Nonnull)error; +- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers completion:(void (^)(SKProductsResponseMessage *_Nullable, FlutterError *_Nullable))completion; +- (void)finishTransactionFinishMap:(NSDictionary *)finishMap error:(FlutterError *_Nullable *_Nonnull)error; +- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName error:(FlutterError *_Nullable *_Nonnull)error; +- (void)presentCodeRedemptionSheetWithError:(FlutterError *_Nullable *_Nonnull)error; @end extern void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *_Nullable api); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 8ac2f2fb071a..4b2867939504 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -97,9 +97,9 @@ + (nullable SKStorefrontMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@interface SKProductResponseMessage () -+ (SKProductResponseMessage *)fromList:(NSArray *)list; -+ (nullable SKProductResponseMessage *)nullableFromList:(NSArray *)list; +@interface SKProductsResponseMessage () ++ (SKProductsResponseMessage *)fromList:(NSArray *)list; ++ (nullable SKProductsResponseMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end @@ -300,22 +300,22 @@ - (NSArray *)toList { } @end -@implementation SKProductResponseMessage +@implementation SKProductsResponseMessage + (instancetype)makeWithProducts:(nullable NSArray *)products invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers { - SKProductResponseMessage* pigeonResult = [[SKProductResponseMessage alloc] init]; + SKProductsResponseMessage* pigeonResult = [[SKProductsResponseMessage alloc] init]; pigeonResult.products = products; pigeonResult.invalidProductIdentifiers = invalidProductIdentifiers; return pigeonResult; } -+ (SKProductResponseMessage *)fromList:(NSArray *)list { - SKProductResponseMessage *pigeonResult = [[SKProductResponseMessage alloc] init]; ++ (SKProductsResponseMessage *)fromList:(NSArray *)list { + SKProductsResponseMessage *pigeonResult = [[SKProductsResponseMessage alloc] init]; pigeonResult.products = GetNullableObjectAtIndex(list, 0); pigeonResult.invalidProductIdentifiers = GetNullableObjectAtIndex(list, 1); return pigeonResult; } -+ (nullable SKProductResponseMessage *)nullableFromList:(NSArray *)list { - return (list) ? [SKProductResponseMessage fromList:list] : nil; ++ (nullable SKProductsResponseMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKProductsResponseMessage fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -497,9 +497,9 @@ - (nullable id)readValueOfType:(UInt8)type { case 134: return [SKProductMessage fromList:[self readValue]]; case 135: - return [SKProductResponseMessage fromList:[self readValue]]; - case 136: return [SKProductSubscriptionPeriodMessage fromList:[self readValue]]; + case 136: + return [SKProductsResponseMessage fromList:[self readValue]]; case 137: return [SKStorefrontMessage fromList:[self readValue]]; default: @@ -533,10 +533,10 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[SKProductMessage class]]) { [self writeByte:134]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[SKProductResponseMessage class]]) { + } else if ([value isKindOfClass:[SKProductSubscriptionPeriodMessage class]]) { [self writeByte:135]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[SKProductSubscriptionPeriodMessage class]]) { + } else if ([value isKindOfClass:[SKProductsResponseMessage class]]) { [self writeByte:136]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[SKStorefrontMessage class]]) { @@ -648,13 +648,68 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< binaryMessenger:binaryMessenger codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(startProductRequestProductIdentifiers:error:)", api); + NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers:completion:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(startProductRequestProductIdentifiers:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSArray *arg_productIdentifiers = GetNullableObjectAtIndex(args, 0); + [api startProductRequestProductIdentifiers:arg_productIdentifiers completion:^(SKProductsResponseMessage *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(finishTransactionFinishMap:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(finishTransactionFinishMap:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSDictionary *arg_finishMap = GetNullableObjectAtIndex(args, 0); FlutterError *error; - SKProductResponseMessage *output = [api startProductRequestProductIdentifiers:arg_productIdentifiers error:&error]; - callback(wrapResult(output, error)); + [api finishTransactionFinishMap:arg_finishMap error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(restoreTransactionsApplicationUserName:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(restoreTransactionsApplicationUserName:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_applicationUserName = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api restoreTransactionsApplicationUserName:arg_applicationUserName error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(presentCodeRedemptionSheetWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(presentCodeRedemptionSheetWithError:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api presentCodeRedemptionSheetWithError:&error]; + callback(wrapResult(nil, error)); }]; } else { [channel setMessageHandler:nil]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 8fc43c144938..8264a42c20e4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -105,22 +105,13 @@ - (void)testPaymentQueueStorefrontReturnsNil { } - (void)testGetProductResponse { - XCTestExpectation *expectation = - [self expectationWithDescription:@"expect response contains 1 item"]; - FlutterMethodCall *call = [FlutterMethodCall - methodCallWithMethodName:@"-[`InAppPurchasePlugin startProductRequest`:result:]" - arguments:@[ @"123" ]]; - __block id result; - [self.plugin handleMethodCall:call - result:^(id r) { - [expectation fulfill]; - result = r; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssert([result isKindOfClass:[NSDictionary class]]); - NSArray *resultArray = [result objectForKey:@"products"]; - XCTAssertEqual(resultArray.count, 1); - XCTAssertTrue([resultArray.firstObject[@"productIdentifier"] isEqualToString:@"123"]); + NSArray *argument = @[@"123"]; + [self.plugin startProductRequestProductIdentifiers:argument completion:^(SKProductsResponseMessage * _Nullable response, FlutterError * _Nullable startProductRequestError) { + XCTAssert([response isKindOfClass:[SKProductsResponseMessage class]]); + XCTAssertEqual(response.products.count, 1); + XCTAssertEqual(response.invalidProductIdentifiers.count, 0); + XCTAssertEqual(response.products[0].productIdentifier, @"123"); + }]; } - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart index 88fd74727b6c..bc55a9d7ae55 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart @@ -278,8 +278,8 @@ class SKStorefrontMessage { } } -class SKProductResponseMessage { - SKProductResponseMessage({ +class SKProductsResponseMessage { + SKProductsResponseMessage({ this.products, this.invalidProductIdentifiers, }); @@ -295,9 +295,9 @@ class SKProductResponseMessage { ]; } - static SKProductResponseMessage decode(Object result) { + static SKProductsResponseMessage decode(Object result) { result as List; - return SKProductResponseMessage( + return SKProductsResponseMessage( products: (result[0] as List?)?.cast(), invalidProductIdentifiers: (result[1] as List?)?.cast(), ); @@ -505,10 +505,10 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { } else if (value is SKProductMessage) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is SKProductResponseMessage) { + } else if (value is SKProductSubscriptionPeriodMessage) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SKProductSubscriptionPeriodMessage) { + } else if (value is SKProductsResponseMessage) { buffer.putUint8(136); writeValue(buffer, value.encode()); } else if (value is SKStorefrontMessage) { @@ -537,9 +537,9 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { case 134: return SKProductMessage.decode(readValue(buffer)!); case 135: - return SKProductResponseMessage.decode(readValue(buffer)!); - case 136: return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); + case 136: + return SKProductsResponseMessage.decode(readValue(buffer)!); case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: @@ -662,7 +662,7 @@ class InAppPurchaseAPI { } } - Future startProductRequest(List productIdentifiers) async { + Future startProductRequest(List productIdentifiers) async { const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -685,7 +685,73 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as SKProductResponseMessage?)!; + return (__pigeon_replyList[0] as SKProductsResponseMessage?)!; + } + } + + Future finishTransaction(Map finishMap) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([finishMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future restoreTransactions(String? applicationUserName) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([applicationUserName]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future presentCodeRedemptionSheet() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; } } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart index 5c2e3c395a35..4a61eec0fce0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart @@ -161,10 +161,7 @@ class SKPaymentQueueWrapper { Future finishTransaction( SKPaymentTransactionWrapper transaction) async { final Map requestMap = transaction.toFinishMap(); - await channel.invokeMethod( - '-[InAppPurchasePlugin finishTransaction:result:]', - requestMap, - ); + await _hostApi.finishTransaction(requestMap); } /// Restore previously purchased transactions. @@ -188,9 +185,10 @@ class SKPaymentQueueWrapper { /// or [`-[SKPayment restoreCompletedTransactionsWithApplicationUsername:]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1505992-restorecompletedtransactionswith?language=objc) /// depending on whether the `applicationUserName` is set. Future restoreTransactions({String? applicationUserName}) async { - await channel.invokeMethod( - '-[InAppPurchasePlugin restoreTransactions:result:]', - applicationUserName); + // await channel.invokeMethod( + // '-[InAppPurchasePlugin restoreTransactions:result:]', + // applicationUserName); + await _hostApi.restoreTransactions(applicationUserName); } /// Present Code Redemption Sheet diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index 093aa7a55038..7da86e09a8fa 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart @@ -66,7 +66,7 @@ class SkProductResponseWrapper { @override int get hashCode => Object.hash(products, invalidProductIdentifiers); - static SkProductResponseWrapper convertFromPigeon(SKProductResponseMessage msg) { + static SkProductResponseWrapper convertFromPigeon(SKProductsResponseMessage msg) { return SkProductResponseWrapper( products: msg.products!.map((SKProductMessage? e) => SKProductWrapper.convertFromPigeon(e!)).toList(), invalidProductIdentifiers: msg.invalidProductIdentifiers != null ? msg.invalidProductIdentifiers!.cast() : [], diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart index 2e57e3ad15df..9432f4f97dd3 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart @@ -37,7 +37,7 @@ class SKRequestMaker { /// A [PlatformException] is thrown if the platform code making the request fails. Future startProductRequest( List productIdentifiers) async { - final SKProductResponseMessage productResponsePigeon = await _hostApi.startProductRequest(productIdentifiers); + final SKProductsResponseMessage productResponsePigeon = await _hostApi.startProductRequest(productIdentifiers); // should products be null or [] ? if (productResponsePigeon.products == null) { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index 5c870698253d..3e6db1c959b9 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -130,8 +130,8 @@ class SKStorefrontMessage { final String identifier; } -class SKProductResponseMessage { - const SKProductResponseMessage ({ +class SKProductsResponseMessage { + const SKProductsResponseMessage ({ required this.products, required this.invalidProductIdentifiers }); @@ -250,5 +250,15 @@ abstract class InAppPurchaseAPI { void addPayment(Map paymentMap); - SKProductResponseMessage startProductRequest(List productIdentifiers); + @async + SKProductsResponseMessage startProductRequest(List productIdentifiers); + + void finishTransaction(Map finishMap); + + void restoreTransactions(String? applicationUserName); + + void presentCodeRedemptionSheet(); + // + // void retrieveReceiptData(); + } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index 8a3fbd56a955..38129807638b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -290,8 +290,9 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { } @override - Map? startProductRequest(List productIdentifiers) { + SKProductsResponseMessage startProductRequest(List productIdentifiers) { // TODO: implement startProductRequest throw UnimplementedError(); } + } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart index c5ad7d2b343f..9236db49a898 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart @@ -297,10 +297,11 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { [dummyTransactionMessage]; @override - Map? startProductRequest(List productIdentifiers) { + SKProductsResponseMessage startProductRequest(List productIdentifiers) { // TODO: implement startProductRequest throw UnimplementedError(); } + } class TestPaymentQueueDelegate extends SKPaymentQueueDelegateWrapper {} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart index 2d319d5d0278..c871776694e1 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart @@ -35,10 +35,10 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { } else if (value is SKProductMessage) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is SKProductResponseMessage) { + } else if (value is SKProductSubscriptionPeriodMessage) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SKProductSubscriptionPeriodMessage) { + } else if (value is SKProductsResponseMessage) { buffer.putUint8(136); writeValue(buffer, value.encode()); } else if (value is SKStorefrontMessage) { @@ -67,9 +67,9 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { case 134: return SKProductMessage.decode(readValue(buffer)!); case 135: - return SKProductResponseMessage.decode(readValue(buffer)!); - case 136: return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); + case 136: + return SKProductsResponseMessage.decode(readValue(buffer)!); case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: @@ -91,7 +91,13 @@ abstract class TestInAppPurchaseApi { void addPayment(Map paymentMap); - SKProductResponseMessage startProductRequest(List productIdentifiers); + Future startProductRequest(List productIdentifiers); + + void finishTransaction(Map finishMap); + + void restoreTransactions(String? applicationUserName); + + void presentCodeRedemptionSheet(); static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -191,7 +197,7 @@ abstract class TestInAppPurchaseApi { assert(arg_productIdentifiers != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null, expected non-null List.'); try { - final SKProductResponseMessage output = api.startProductRequest(arg_productIdentifiers!); + final SKProductsResponseMessage output = await api.startProductRequest(arg_productIdentifiers!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -201,5 +207,72 @@ abstract class TestInAppPurchaseApi { }); } } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null.'); + final List args = (message as List?)!; + final Map? arg_finishMap = (args[0] as Map?)?.cast(); + assert(arg_finishMap != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null, expected non-null Map.'); + try { + api.finishTransaction(arg_finishMap!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions was null.'); + final List args = (message as List?)!; + final String? arg_applicationUserName = (args[0] as String?); + try { + api.restoreTransactions(arg_applicationUserName); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + try { + api.presentCodeRedemptionSheet(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } } } From 751b034c43d26e0aff06220a1960aa5952c594bb Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 7 Feb 2024 15:07:33 -0800 Subject: [PATCH 03/12] fix tests + formatting --- .../darwin/Classes/FIAObjectTranslator.h | 15 +- .../darwin/Classes/FIAObjectTranslator.m | 72 ++--- .../darwin/Classes/InAppPurchasePlugin.m | 29 +- .../darwin/Classes/messages.g.h | 186 +++++++------ .../darwin/Classes/messages.g.m | 255 ++++++++++-------- .../RunnerTests/InAppPurchasePluginTests.m | 30 ++- .../lib/src/messages.g.dart | 112 +++++--- .../sk_payment_queue_wrapper.dart | 6 +- .../sk_product_wrapper.dart | 60 +++-- .../store_kit_wrappers/sk_request_maker.dart | 6 +- .../pigeons/messages.dart | 43 ++- .../test/fakes/fake_storekit_platform.dart | 22 +- .../sk_methodchannel_apis_test.dart | 44 +-- .../sk_test_stub_objects.dart | 42 +++ .../test/test_api.g.dart | 199 +++++++++----- 15 files changed, 671 insertions(+), 450 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h index 0ede2f4d46c5..a0c4c0b58256 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h @@ -71,19 +71,20 @@ NS_ASSUME_NONNULL_BEGIN + (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error; -+ (nullable SKProductsResponseMessage*)convertProductsResponseToPigeon:(nullable SKProductsResponse *)payment; ++ (nullable SKProductsResponseMessage *)convertProductsResponseToPigeon: + (nullable SKProductsResponse *)payment; -+ (nullable SKProductMessage*)convertProductToPigeon:(nullable SKProduct *)product ++ (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)product API_AVAILABLE(ios(12.2)); -+ (nullable SKProductDiscountMessage*)convertProductDiscountToPigeon:(nullable SKProductDiscount *)productDiscount - API_AVAILABLE(ios(12.2)); ++ (nullable SKProductDiscountMessage *)convertProductDiscountToPigeon: + (nullable SKProductDiscount *)productDiscount API_AVAILABLE(ios(12.2)); -+ (nullable SKPriceLocaleMessage*)convertNSLocaleToPigeon:(nullable NSLocale *)locale ++ (nullable SKPriceLocaleMessage *)convertNSLocaleToPigeon:(nullable NSLocale *)locale API_AVAILABLE(ios(12.2)); -+ (nullable SKProductSubscriptionPeriodMessage*)convertSKProductSubscriptionPeriodToPigeon:(nullable SKProductSubscriptionPeriod *)period - API_AVAILABLE(ios(12.2)); ++ (nullable SKProductSubscriptionPeriodMessage *)convertSKProductSubscriptionPeriodToPigeon: + (nullable SKProductSubscriptionPeriod *)period API_AVAILABLE(ios(12.2)); @end NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m index 753cff824e79..bc40256ec4aa 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m @@ -376,9 +376,8 @@ + (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefro return msg; } - -+ (nullable SKProductSubscriptionPeriodMessage*)convertSKProductSubscriptionPeriodToPigeon:(nullable SKProductSubscriptionPeriod *)period -API_AVAILABLE(ios(12.2)) { ++ (nullable SKProductSubscriptionPeriodMessage *)convertSKProductSubscriptionPeriodToPigeon: + (nullable SKProductSubscriptionPeriod *)period API_AVAILABLE(ios(12.2)) { if (!period) { return nil; } @@ -399,13 +398,14 @@ + (nullable SKProductSubscriptionPeriodMessage*)convertSKProductSubscriptionPeri break; } - SKProductSubscriptionPeriodMessage *msg = [SKProductSubscriptionPeriodMessage makeWithNumberOfUnits:period.numberOfUnits unit:unit]; + SKProductSubscriptionPeriodMessage *msg = + [SKProductSubscriptionPeriodMessage makeWithNumberOfUnits:period.numberOfUnits unit:unit]; return msg; } -+ (nullable SKProductDiscountMessage*)convertProductDiscountToPigeon:(nullable SKProductDiscount *)productDiscount -API_AVAILABLE(ios(12.2)) { ++ (nullable SKProductDiscountMessage *)convertProductDiscountToPigeon: + (nullable SKProductDiscount *)productDiscount API_AVAILABLE(ios(12.2)) { if (!productDiscount) { return nil; } @@ -433,55 +433,58 @@ + (nullable SKProductDiscountMessage*)convertProductDiscountToPigeon:(nullable S 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]; + 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)) { ++ (nullable SKPriceLocaleMessage *)convertNSLocaleToPigeon:(nullable NSLocale *)locale + API_AVAILABLE(ios(12.2)) { if (!locale) { return nil; } - SKPriceLocaleMessage *msg = [SKPriceLocaleMessage makeWithCurrencySymbol:locale.currencySymbol + 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)) { ++ (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)product + API_AVAILABLE(ios(12.2)) { NSArray *skProductDiscounts = product.discounts; - NSMutableArray *pigeonProductDiscounts = [[NSMutableArray alloc] init]; + NSMutableArray *pigeonProductDiscounts = + [[NSMutableArray alloc] 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]; + 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)) { - ++ (nullable SKProductsResponseMessage *)convertProductsResponseToPigeon: + (nullable SKProductsResponse *)productsResponse API_AVAILABLE(ios(12.2)) { NSArray *skProducts = productsResponse.products; NSMutableArray *pigeonProducts = [[NSMutableArray alloc] init]; @@ -489,8 +492,9 @@ + (nullable SKProductsResponseMessage*)convertProductsResponseToPigeon:(nullable [pigeonProducts addObject:[self convertProductToPigeon:product]]; }; - SKProductsResponseMessage *msg = [SKProductsResponseMessage makeWithProducts:pigeonProducts - invalidProductIdentifiers:productsResponse.invalidProductIdentifiers]; + SKProductsResponseMessage *msg = + [SKProductsResponseMessage makeWithProducts:pigeonProducts + invalidProductIdentifiers:productsResponse.invalidProductIdentifiers]; return msg; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m index 0141b21f0980..e4a6b38f3cb7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m @@ -136,8 +136,9 @@ - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable * return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; } -- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers completion:(void (^)(SKProductsResponseMessage *_Nullable, FlutterError *_Nullable))completion { - +- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers + completion:(void (^)(SKProductsResponseMessage *_Nullable, + FlutterError *_Nullable))completion { SKProductsRequest *request = [self getProductRequestWithIdentifiers:[NSSet setWithArray:productIdentifiers]]; FIAPRequestHandler *handler = [[FIAPRequestHandler alloc] initWithRequest:request]; @@ -149,14 +150,14 @@ - (void)startProductRequestProductIdentifiers:(NSArray *)productIden FlutterError *error = nil; if (startProductRequestError != nil) { error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" - message:startProductRequestError.localizedDescription - details:startProductRequestError.description]; + message:startProductRequestError.localizedDescription + details:startProductRequestError.description]; } if (!response) { error = [FlutterError errorWithCode:@"storekit_platform_no_response" - message:@"Failed to get SKProductResponse in startRequest " - @"call. Error occured on iOS platform" - details:productIdentifiers]; + message:@"Failed to get SKProductResponse in startRequest " + @"call. Error occured on iOS platform" + details:productIdentifiers]; } for (SKProduct *product in response.products) { [self.productsCache setObject:product forKey:product.productIdentifier]; @@ -224,7 +225,8 @@ - (void)addPaymentPaymentMap:(nonnull NSDictionary *)paymentMap } } -- (void)finishTransactionFinishMap:(nonnull NSDictionary *)finishMap error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (void)finishTransactionFinishMap:(nonnull NSDictionary *)finishMap + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { NSString *transactionIdentifier = [finishMap objectForKey:@"transactionIdentifier"]; NSString *productIdentifier = [finishMap objectForKey:@"productIdentifier"]; @@ -243,19 +245,22 @@ - (void)finishTransactionFinishMap:(nonnull NSDictionary [self.paymentQueueHandler finishTransaction:transaction]; } @catch (NSException *e) { *error = [FlutterError errorWithCode:@"storekit_finish_transaction_exception" - message:e.name - details:e.description]; + message:e.name + details:e.description]; return; } } } } -- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { [self.paymentQueueHandler restoreTransactions:applicationUserName]; } -- (void)presentCodeRedemptionSheetWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (void)presentCodeRedemptionSheetWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { #if TARGET_OS_IOS [self.paymentQueueHandler presentCodeRedemptionSheet]; #endif diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index 749b5e35a104..05a1dc1e527e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -64,7 +64,8 @@ typedef NS_ENUM(NSUInteger, SKProductDiscountTypeMessage) { typedef NS_ENUM(NSUInteger, SKProductDiscountPaymentModeMessage) { /// Allows user to pay the discounted price at each payment period. SKProductDiscountPaymentModeMessagePayAsYouGo = 0, - /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. + /// Allows user to pay the discounted price upfront and receive the product for the rest of time + /// that was paid for. SKProductDiscountPaymentModeMessagePayUpFront = 1, /// User pays nothing during the discounted period. SKProductDiscountPaymentModeMessageFreeTrial = 2, @@ -106,140 +107,140 @@ typedef NS_ENUM(NSUInteger, SKSubscriptionPeriodUnitMessage) { /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPayment:(SKPaymentMessage *)payment - transactionState:(SKPaymentTransactionStateMessage)transactionState - originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction - transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp - transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable SKErrorMessage *)error; -@property(nonatomic, strong) SKPaymentMessage * payment; + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable SKErrorMessage *)error; +@property(nonatomic, strong) SKPaymentMessage *payment; @property(nonatomic, assign) SKPaymentTransactionStateMessage transactionState; -@property(nonatomic, strong, nullable) SKPaymentTransactionMessage * originalTransaction; -@property(nonatomic, strong, nullable) NSNumber * transactionTimeStamp; -@property(nonatomic, copy, nullable) NSString * transactionIdentifier; -@property(nonatomic, strong, nullable) SKErrorMessage * error; +@property(nonatomic, strong, nullable) SKPaymentTransactionMessage *originalTransaction; +@property(nonatomic, strong, nullable) NSNumber *transactionTimeStamp; +@property(nonatomic, copy, nullable) NSString *transactionIdentifier; +@property(nonatomic, strong, nullable) SKErrorMessage *error; @end @interface SKPaymentMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSInteger )quantity - simulatesAskToBuyInSandbox:(BOOL )simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; -@property(nonatomic, copy) NSString * productIdentifier; -@property(nonatomic, copy, nullable) NSString * applicationUsername; -@property(nonatomic, copy, nullable) NSString * requestData; -@property(nonatomic, assign) NSInteger quantity; -@property(nonatomic, assign) BOOL simulatesAskToBuyInSandbox; -@property(nonatomic, strong, nullable) SKPaymentDiscountMessage * paymentDiscount; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSInteger)quantity + simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; +@property(nonatomic, copy) NSString *productIdentifier; +@property(nonatomic, copy, nullable) NSString *applicationUsername; +@property(nonatomic, copy, nullable) NSString *requestData; +@property(nonatomic, assign) NSInteger quantity; +@property(nonatomic, assign) BOOL simulatesAskToBuyInSandbox; +@property(nonatomic, strong, nullable) SKPaymentDiscountMessage *paymentDiscount; @end @interface SKErrorMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithCode:(NSInteger )code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo; -@property(nonatomic, assign) NSInteger code; -@property(nonatomic, copy) NSString * domain; -@property(nonatomic, copy) NSDictionary * userInfo; ++ (instancetype)makeWithCode:(NSInteger)code + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo; +@property(nonatomic, assign) NSInteger code; +@property(nonatomic, copy) NSString *domain; +@property(nonatomic, copy) NSDictionary *userInfo; @end @interface SKPaymentDiscountMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSInteger )timestamp; -@property(nonatomic, copy) NSString * identifier; -@property(nonatomic, copy) NSString * keyIdentifier; -@property(nonatomic, copy) NSString * nonce; -@property(nonatomic, copy) NSString * signature; -@property(nonatomic, assign) NSInteger timestamp; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSInteger)timestamp; +@property(nonatomic, copy) NSString *identifier; +@property(nonatomic, copy) NSString *keyIdentifier; +@property(nonatomic, copy) NSString *nonce; +@property(nonatomic, copy) NSString *signature; +@property(nonatomic, assign) NSInteger timestamp; @end @interface SKStorefrontMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithCountryCode:(NSString *)countryCode - identifier:(NSString *)identifier; -@property(nonatomic, copy) NSString * countryCode; -@property(nonatomic, copy) NSString * identifier; ++ (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier; +@property(nonatomic, copy) NSString *countryCode; +@property(nonatomic, copy) NSString *identifier; @end @interface SKProductsResponseMessage : NSObject + (instancetype)makeWithProducts:(nullable NSArray *)products - invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers; -@property(nonatomic, copy, nullable) NSArray * products; -@property(nonatomic, copy, nullable) NSArray * invalidProductIdentifiers; + invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers; +@property(nonatomic, copy, nullable) NSArray *products; +@property(nonatomic, copy, nullable) NSArray *invalidProductIdentifiers; @end @interface SKProductMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - localizedTitle:(NSString *)localizedTitle - localizedDescription:(NSString *)localizedDescription - priceLocale:(SKPriceLocaleMessage *)priceLocale - subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier - price:(NSString *)price - subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod - introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice - discounts:(nullable NSArray *)discounts; -@property(nonatomic, copy) NSString * productIdentifier; -@property(nonatomic, copy) NSString * localizedTitle; -@property(nonatomic, copy) NSString * localizedDescription; -@property(nonatomic, strong) SKPriceLocaleMessage * priceLocale; -@property(nonatomic, copy, nullable) NSString * subscriptionGroupIdentifier; -@property(nonatomic, copy) NSString * price; -@property(nonatomic, strong, nullable) SKProductSubscriptionPeriodMessage * subscriptionPeriod; -@property(nonatomic, strong, nullable) SKProductDiscountMessage * introductoryPrice; -@property(nonatomic, copy, nullable) NSArray * discounts; + localizedTitle:(NSString *)localizedTitle + localizedDescription:(NSString *)localizedDescription + priceLocale:(SKPriceLocaleMessage *)priceLocale + subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier + price:(NSString *)price + subscriptionPeriod: + (nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod + introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice + discounts:(nullable NSArray *)discounts; +@property(nonatomic, copy) NSString *productIdentifier; +@property(nonatomic, copy) NSString *localizedTitle; +@property(nonatomic, copy) NSString *localizedDescription; +@property(nonatomic, strong) SKPriceLocaleMessage *priceLocale; +@property(nonatomic, copy, nullable) NSString *subscriptionGroupIdentifier; +@property(nonatomic, copy) NSString *price; +@property(nonatomic, strong, nullable) SKProductSubscriptionPeriodMessage *subscriptionPeriod; +@property(nonatomic, strong, nullable) SKProductDiscountMessage *introductoryPrice; +@property(nonatomic, copy, nullable) NSArray *discounts; @end @interface SKPriceLocaleMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithCurrencySymbol:(NSString *)currencySymbol - currencyCode:(NSString *)currencyCode - countryCode:(NSString *)countryCode; -///The currency symbol for the locale, e.g. $ for US locale. -@property(nonatomic, copy) NSString * currencySymbol; -///The currency code for the locale, e.g. USD for US locale. -@property(nonatomic, copy) NSString * currencyCode; -///The country code for the locale, e.g. US for US locale. -@property(nonatomic, copy) NSString * countryCode; + currencyCode:(NSString *)currencyCode + countryCode:(NSString *)countryCode; +/// The currency symbol for the locale, e.g. $ for US locale. +@property(nonatomic, copy) NSString *currencySymbol; +/// The currency code for the locale, e.g. USD for US locale. +@property(nonatomic, copy) NSString *currencyCode; +/// The country code for the locale, e.g. US for US locale. +@property(nonatomic, copy) NSString *countryCode; @end @interface SKProductDiscountMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPrice:(NSString *)price - priceLocale:(SKPriceLocaleMessage *)priceLocale - numberOfPeriods:(NSInteger )numberOfPeriods - paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode - subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod - identifier:(nullable NSString *)identifier - type:(SKProductDiscountTypeMessage)type; -@property(nonatomic, copy) NSString * price; -@property(nonatomic, strong) SKPriceLocaleMessage * priceLocale; -@property(nonatomic, assign) NSInteger numberOfPeriods; + priceLocale:(SKPriceLocaleMessage *)priceLocale + numberOfPeriods:(NSInteger)numberOfPeriods + paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode + subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod + identifier:(nullable NSString *)identifier + type:(SKProductDiscountTypeMessage)type; +@property(nonatomic, copy) NSString *price; +@property(nonatomic, strong) SKPriceLocaleMessage *priceLocale; +@property(nonatomic, assign) NSInteger numberOfPeriods; @property(nonatomic, assign) SKProductDiscountPaymentModeMessage paymentMode; -@property(nonatomic, strong) SKProductSubscriptionPeriodMessage * subscriptionPeriod; -@property(nonatomic, copy, nullable) NSString * identifier; +@property(nonatomic, strong) SKProductSubscriptionPeriodMessage *subscriptionPeriod; +@property(nonatomic, copy, nullable) NSString *identifier; @property(nonatomic, assign) SKProductDiscountTypeMessage type; @end @interface SKProductSubscriptionPeriodMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithNumberOfUnits:(NSInteger )numberOfUnits - unit:(SKSubscriptionPeriodUnitMessage)unit; -@property(nonatomic, assign) NSInteger numberOfUnits; ++ (instancetype)makeWithNumberOfUnits:(NSInteger)numberOfUnits + unit:(SKSubscriptionPeriodUnitMessage)unit; +@property(nonatomic, assign) NSInteger numberOfUnits; @property(nonatomic, assign) SKSubscriptionPeriodUnitMessage unit; @end @@ -252,16 +253,23 @@ NSObject *InAppPurchaseAPIGetCodec(void); /// @return `nil` only when `error != nil`. - (nullable NSNumber *)canMakePaymentsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSArray *)transactionsWithError:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSArray *)transactionsWithError: + (FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error; -- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; -- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers completion:(void (^)(SKProductsResponseMessage *_Nullable, FlutterError *_Nullable))completion; -- (void)finishTransactionFinishMap:(NSDictionary *)finishMap error:(FlutterError *_Nullable *_Nonnull)error; -- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers + completion:(void (^)(SKProductsResponseMessage *_Nullable, + FlutterError *_Nullable))completion; +- (void)finishTransactionFinishMap:(NSDictionary *)finishMap + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName + error:(FlutterError *_Nullable *_Nonnull)error; - (void)presentCodeRedemptionSheetWithError:(FlutterError *_Nullable *_Nonnull)error; @end -extern void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *_Nullable api); +extern void SetUpInAppPurchaseAPI(id binaryMessenger, + NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 4b2867939504..4eec24eb2c67 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -129,12 +129,12 @@ - (NSArray *)toList; @implementation SKPaymentTransactionMessage + (instancetype)makeWithPayment:(SKPaymentMessage *)payment - transactionState:(SKPaymentTransactionStateMessage)transactionState - originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction - transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp - transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable SKErrorMessage *)error { - SKPaymentTransactionMessage* pigeonResult = [[SKPaymentTransactionMessage alloc] init]; + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable SKErrorMessage *)error { + SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = payment; pigeonResult.transactionState = transactionState; pigeonResult.originalTransaction = originalTransaction; @@ -147,7 +147,8 @@ + (SKPaymentTransactionMessage *)fromList:(NSArray *)list { SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; pigeonResult.transactionState = [GetNullableObjectAtIndex(list, 1) integerValue]; - pigeonResult.originalTransaction = [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; + pigeonResult.originalTransaction = + [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; pigeonResult.transactionTimeStamp = GetNullableObjectAtIndex(list, 3); pigeonResult.transactionIdentifier = GetNullableObjectAtIndex(list, 4); pigeonResult.error = [SKErrorMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; @@ -170,12 +171,12 @@ - (NSArray *)toList { @implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSInteger )quantity - simulatesAskToBuyInSandbox:(BOOL )simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { - SKPaymentMessage* pigeonResult = [[SKPaymentMessage alloc] init]; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSInteger)quantity + simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { + SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.applicationUsername = applicationUsername; pigeonResult.requestData = requestData; @@ -191,7 +192,8 @@ + (SKPaymentMessage *)fromList:(NSArray *)list { pigeonResult.requestData = GetNullableObjectAtIndex(list, 2); pigeonResult.quantity = [GetNullableObjectAtIndex(list, 3) integerValue]; pigeonResult.simulatesAskToBuyInSandbox = [GetNullableObjectAtIndex(list, 4) boolValue]; - pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.paymentDiscount = + [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } + (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { @@ -210,10 +212,10 @@ - (NSArray *)toList { @end @implementation SKErrorMessage -+ (instancetype)makeWithCode:(NSInteger )code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo { - SKErrorMessage* pigeonResult = [[SKErrorMessage alloc] init]; ++ (instancetype)makeWithCode:(NSInteger)code + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo { + SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; pigeonResult.userInfo = userInfo; @@ -240,11 +242,11 @@ - (NSArray *)toList { @implementation SKPaymentDiscountMessage + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSInteger )timestamp { - SKPaymentDiscountMessage* pigeonResult = [[SKPaymentDiscountMessage alloc] init]; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSInteger)timestamp { + SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; pigeonResult.nonce = nonce; @@ -276,9 +278,8 @@ - (NSArray *)toList { @end @implementation SKStorefrontMessage -+ (instancetype)makeWithCountryCode:(NSString *)countryCode - identifier:(NSString *)identifier { - SKStorefrontMessage* pigeonResult = [[SKStorefrontMessage alloc] init]; ++ (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier { + SKStorefrontMessage *pigeonResult = [[SKStorefrontMessage alloc] init]; pigeonResult.countryCode = countryCode; pigeonResult.identifier = identifier; return pigeonResult; @@ -302,8 +303,8 @@ - (NSArray *)toList { @implementation SKProductsResponseMessage + (instancetype)makeWithProducts:(nullable NSArray *)products - invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers { - SKProductsResponseMessage* pigeonResult = [[SKProductsResponseMessage alloc] init]; + invalidProductIdentifiers:(nullable NSArray *)invalidProductIdentifiers { + SKProductsResponseMessage *pigeonResult = [[SKProductsResponseMessage alloc] init]; pigeonResult.products = products; pigeonResult.invalidProductIdentifiers = invalidProductIdentifiers; return pigeonResult; @@ -326,16 +327,17 @@ - (NSArray *)toList { @end @implementation SKProductMessage -+ (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - localizedTitle:(NSString *)localizedTitle - localizedDescription:(NSString *)localizedDescription - priceLocale:(SKPriceLocaleMessage *)priceLocale ++ (instancetype) + makeWithProductIdentifier:(NSString *)productIdentifier + localizedTitle:(NSString *)localizedTitle + localizedDescription:(NSString *)localizedDescription + priceLocale:(SKPriceLocaleMessage *)priceLocale subscriptionGroupIdentifier:(nullable NSString *)subscriptionGroupIdentifier - price:(NSString *)price - subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod - introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice - discounts:(nullable NSArray *)discounts { - SKProductMessage* pigeonResult = [[SKProductMessage alloc] init]; + price:(NSString *)price + subscriptionPeriod:(nullable SKProductSubscriptionPeriodMessage *)subscriptionPeriod + introductoryPrice:(nullable SKProductDiscountMessage *)introductoryPrice + discounts:(nullable NSArray *)discounts { + SKProductMessage *pigeonResult = [[SKProductMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.localizedTitle = localizedTitle; pigeonResult.localizedDescription = localizedDescription; @@ -352,11 +354,14 @@ + (SKProductMessage *)fromList:(NSArray *)list { pigeonResult.productIdentifier = GetNullableObjectAtIndex(list, 0); pigeonResult.localizedTitle = GetNullableObjectAtIndex(list, 1); pigeonResult.localizedDescription = GetNullableObjectAtIndex(list, 2); - pigeonResult.priceLocale = [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 3))]; + pigeonResult.priceLocale = + [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 3))]; pigeonResult.subscriptionGroupIdentifier = GetNullableObjectAtIndex(list, 4); pigeonResult.price = GetNullableObjectAtIndex(list, 5); - pigeonResult.subscriptionPeriod = [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 6))]; - pigeonResult.introductoryPrice = [SKProductDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 7))]; + pigeonResult.subscriptionPeriod = + [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 6))]; + pigeonResult.introductoryPrice = + [SKProductDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 7))]; pigeonResult.discounts = GetNullableObjectAtIndex(list, 8); return pigeonResult; } @@ -380,9 +385,9 @@ - (NSArray *)toList { @implementation SKPriceLocaleMessage + (instancetype)makeWithCurrencySymbol:(NSString *)currencySymbol - currencyCode:(NSString *)currencyCode - countryCode:(NSString *)countryCode { - SKPriceLocaleMessage* pigeonResult = [[SKPriceLocaleMessage alloc] init]; + currencyCode:(NSString *)currencyCode + countryCode:(NSString *)countryCode { + SKPriceLocaleMessage *pigeonResult = [[SKPriceLocaleMessage alloc] init]; pigeonResult.currencySymbol = currencySymbol; pigeonResult.currencyCode = currencyCode; pigeonResult.countryCode = countryCode; @@ -409,13 +414,13 @@ - (NSArray *)toList { @implementation SKProductDiscountMessage + (instancetype)makeWithPrice:(NSString *)price - priceLocale:(SKPriceLocaleMessage *)priceLocale - numberOfPeriods:(NSInteger )numberOfPeriods - paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode - subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod - identifier:(nullable NSString *)identifier - type:(SKProductDiscountTypeMessage)type { - SKProductDiscountMessage* pigeonResult = [[SKProductDiscountMessage alloc] init]; + priceLocale:(SKPriceLocaleMessage *)priceLocale + numberOfPeriods:(NSInteger)numberOfPeriods + paymentMode:(SKProductDiscountPaymentModeMessage)paymentMode + subscriptionPeriod:(SKProductSubscriptionPeriodMessage *)subscriptionPeriod + identifier:(nullable NSString *)identifier + type:(SKProductDiscountTypeMessage)type { + SKProductDiscountMessage *pigeonResult = [[SKProductDiscountMessage alloc] init]; pigeonResult.price = price; pigeonResult.priceLocale = priceLocale; pigeonResult.numberOfPeriods = numberOfPeriods; @@ -428,10 +433,12 @@ + (instancetype)makeWithPrice:(NSString *)price + (SKProductDiscountMessage *)fromList:(NSArray *)list { SKProductDiscountMessage *pigeonResult = [[SKProductDiscountMessage alloc] init]; pigeonResult.price = GetNullableObjectAtIndex(list, 0); - pigeonResult.priceLocale = [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + pigeonResult.priceLocale = + [SKPriceLocaleMessage nullableFromList:(GetNullableObjectAtIndex(list, 1))]; pigeonResult.numberOfPeriods = [GetNullableObjectAtIndex(list, 2) integerValue]; pigeonResult.paymentMode = [GetNullableObjectAtIndex(list, 3) integerValue]; - pigeonResult.subscriptionPeriod = [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 4))]; + pigeonResult.subscriptionPeriod = + [SKProductSubscriptionPeriodMessage nullableFromList:(GetNullableObjectAtIndex(list, 4))]; pigeonResult.identifier = GetNullableObjectAtIndex(list, 5); pigeonResult.type = [GetNullableObjectAtIndex(list, 6) integerValue]; return pigeonResult; @@ -453,15 +460,17 @@ - (NSArray *)toList { @end @implementation SKProductSubscriptionPeriodMessage -+ (instancetype)makeWithNumberOfUnits:(NSInteger )numberOfUnits - unit:(SKSubscriptionPeriodUnitMessage)unit { - SKProductSubscriptionPeriodMessage* pigeonResult = [[SKProductSubscriptionPeriodMessage alloc] init]; ++ (instancetype)makeWithNumberOfUnits:(NSInteger)numberOfUnits + unit:(SKSubscriptionPeriodUnitMessage)unit { + SKProductSubscriptionPeriodMessage *pigeonResult = + [[SKProductSubscriptionPeriodMessage alloc] init]; pigeonResult.numberOfUnits = numberOfUnits; pigeonResult.unit = unit; return pigeonResult; } + (SKProductSubscriptionPeriodMessage *)fromList:(NSArray *)list { - SKProductSubscriptionPeriodMessage *pigeonResult = [[SKProductSubscriptionPeriodMessage alloc] init]; + SKProductSubscriptionPeriodMessage *pigeonResult = + [[SKProductSubscriptionPeriodMessage alloc] init]; pigeonResult.numberOfUnits = [GetNullableObjectAtIndex(list, 0) integerValue]; pigeonResult.unit = [GetNullableObjectAtIndex(list, 1) integerValue]; return pigeonResult; @@ -482,25 +491,25 @@ @interface InAppPurchaseAPICodecReader : FlutterStandardReader @implementation InAppPurchaseAPICodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 128: + case 128: return [SKErrorMessage fromList:[self readValue]]; - case 129: + case 129: return [SKPaymentDiscountMessage fromList:[self readValue]]; - case 130: + case 130: return [SKPaymentMessage fromList:[self readValue]]; - case 131: + case 131: return [SKPaymentTransactionMessage fromList:[self readValue]]; - case 132: + case 132: return [SKPriceLocaleMessage fromList:[self readValue]]; - case 133: + case 133: return [SKProductDiscountMessage fromList:[self readValue]]; - case 134: + case 134: return [SKProductMessage fromList:[self readValue]]; - case 135: + case 135: return [SKProductSubscriptionPeriodMessage fromList:[self readValue]]; - case 136: + case 136: return [SKProductsResponseMessage fromList:[self readValue]]; - case 137: + case 137: return [SKStorefrontMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -563,22 +572,27 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - InAppPurchaseAPICodecReaderWriter *readerWriter = [[InAppPurchaseAPICodecReaderWriter alloc] init]; + InAppPurchaseAPICodecReaderWriter *readerWriter = + [[InAppPurchaseAPICodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *api) { +void SetUpInAppPurchaseAPI(id binaryMessenger, + NSObject *api) { /// Returns if the current device is able to make payments { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(canMakePaymentsWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePaymentsWithError:)", api); + NSCAssert( + [api respondsToSelector:@selector(canMakePaymentsWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePaymentsWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSNumber *output = [api canMakePaymentsWithError:&error]; @@ -589,13 +603,14 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(transactionsWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactionsWithError:)", api); + NSCAssert([api respondsToSelector:@selector(transactionsWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactionsWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; NSArray *output = [api transactionsWithError:&error]; @@ -606,13 +621,14 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", api); + NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; SKStorefrontMessage *output = [api storefrontWithError:&error]; @@ -623,13 +639,15 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(addPaymentPaymentMap:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPaymentPaymentMap:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(addPaymentPaymentMap:error:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPaymentPaymentMap:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_paymentMap = GetNullableObjectAtIndex(args, 0); @@ -642,32 +660,41 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers:completion:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(startProductRequestProductIdentifiers:completion:)", api); + NSCAssert([api respondsToSelector:@selector(startProductRequestProductIdentifiers: + completion:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(startProductRequestProductIdentifiers:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSArray *arg_productIdentifiers = GetNullableObjectAtIndex(args, 0); - [api startProductRequestProductIdentifiers:arg_productIdentifiers completion:^(SKProductsResponseMessage *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api startProductRequestProductIdentifiers:arg_productIdentifiers + completion:^(SKProductsResponseMessage *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(finishTransactionFinishMap:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(finishTransactionFinishMap:error:)", api); + NSCAssert([api respondsToSelector:@selector(finishTransactionFinishMap:error:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(finishTransactionFinishMap:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_finishMap = GetNullableObjectAtIndex(args, 0); @@ -680,13 +707,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(restoreTransactionsApplicationUserName:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(restoreTransactionsApplicationUserName:error:)", api); + NSCAssert([api respondsToSelector:@selector(restoreTransactionsApplicationUserName:error:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(restoreTransactionsApplicationUserName:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSString *arg_applicationUserName = GetNullableObjectAtIndex(args, 0); @@ -699,13 +729,16 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject< } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"presentCodeRedemptionSheet" binaryMessenger:binaryMessenger - codec:InAppPurchaseAPIGetCodec()]; + codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(presentCodeRedemptionSheetWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(presentCodeRedemptionSheetWithError:)", api); + NSCAssert([api respondsToSelector:@selector(presentCodeRedemptionSheetWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(presentCodeRedemptionSheetWithError:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api presentCodeRedemptionSheetWithError:&error]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 8264a42c20e4..3674d30ec427 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -105,13 +105,17 @@ - (void)testPaymentQueueStorefrontReturnsNil { } - (void)testGetProductResponse { - NSArray *argument = @[@"123"]; - [self.plugin startProductRequestProductIdentifiers:argument completion:^(SKProductsResponseMessage * _Nullable response, FlutterError * _Nullable startProductRequestError) { - XCTAssert([response isKindOfClass:[SKProductsResponseMessage class]]); - XCTAssertEqual(response.products.count, 1); - XCTAssertEqual(response.invalidProductIdentifiers.count, 0); - XCTAssertEqual(response.products[0].productIdentifier, @"123"); - }]; + NSArray *argument = @[ @"123" ]; + [self.plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + XCTAssert( + [response isKindOfClass:[SKProductsResponseMessage class]]); + XCTAssertEqual(response.products.count, 1); + XCTAssertEqual(response.invalidProductIdentifiers.count, 0); + XCTAssertEqual(response.products[0].productIdentifier, @"123"); + }]; } - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { @@ -264,11 +268,10 @@ - (void)testAddPaymentWithNullSandboxArgument { - (void)testRestoreTransactions { XCTestExpectation *expectation = [self expectationWithDescription:@"result successfully restore transactions"]; - FlutterMethodCall *call = [FlutterMethodCall - methodCallWithMethodName:@"-[InAppPurchasePlugin restoreTransactions:result:]" - arguments:nil]; + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; queue.testState = SKPaymentTransactionStatePurchased; + __block BOOL callbackInvoked = NO; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue transactionsUpdated:^(NSArray *_Nonnull transactions) { @@ -283,9 +286,10 @@ - (void)testRestoreTransactions { updatedDownloads:nil transactionCache:OCMClassMock(FIATransactionCache.class)]; [queue addTransactionObserver:self.plugin.paymentQueueHandler]; - [self.plugin handleMethodCall:call - result:^(id r){ - }]; + + FlutterError *error; + [self.plugin restoreTransactionsApplicationUserName:nil error:&error]; + [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertTrue(callbackInvoked); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart index bc55a9d7ae55..bfbd447de677 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart @@ -15,7 +15,8 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -32,27 +33,32 @@ enum SKPaymentTransactionStateMessage { /// transaction to update to another state. Never complete a transaction that /// is still in a purchasing state. purchasing, + /// The user's payment has been succesfully processed. /// /// You should provide the user the content that they purchased. purchased, + /// The transaction failed. /// /// Check the [PaymentTransactionWrapper.error] property from /// [PaymentTransactionWrapper] for details. failed, + /// This transaction is restoring content previously purchased by the user. /// /// The previous transaction information can be obtained in /// [PaymentTransactionWrapper.originalTransaction] from /// [PaymentTransactionWrapper]. restored, + /// The transaction is in the queue but pending external action. Wait for /// another callback to get the final state. /// /// You should update your UI to indicate that you are waiting for the /// transaction to update to another state. deferred, + /// Indicates the transaction is in an unspecified state. unspecified, } @@ -60,6 +66,7 @@ enum SKPaymentTransactionStateMessage { enum SKProductDiscountTypeMessage { /// A constant indicating the discount type is an introductory offer. introductory, + /// A constant indicating the discount type is a promotional offer. subscription, } @@ -67,10 +74,13 @@ enum SKProductDiscountTypeMessage { enum SKProductDiscountPaymentModeMessage { /// Allows user to pay the discounted price at each payment period. payAsYouGo, + /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. payUpFront, + /// User pays nothing during the discounted period. freeTrial, + /// Unspecified mode. unspecified, } @@ -119,7 +129,8 @@ class SKPaymentTransactionMessage { result as List; return SKPaymentTransactionMessage( payment: SKPaymentMessage.decode(result[0]! as List), - transactionState: SKPaymentTransactionStateMessage.values[result[1]! as int], + transactionState: + SKPaymentTransactionStateMessage.values[result[1]! as int], originalTransaction: result[2] != null ? SKPaymentTransactionMessage.decode(result[2]! as List) : null, @@ -359,12 +370,14 @@ class SKProductMessage { subscriptionGroupIdentifier: result[4] as String?, price: result[5]! as String, subscriptionPeriod: result[6] != null - ? SKProductSubscriptionPeriodMessage.decode(result[6]! as List) + ? SKProductSubscriptionPeriodMessage.decode( + result[6]! as List) : null, introductoryPrice: result[7] != null ? SKProductDiscountMessage.decode(result[7]! as List) : null, - discounts: (result[8] as List?)?.cast(), + discounts: + (result[8] as List?)?.cast(), ); } } @@ -446,8 +459,10 @@ class SKProductDiscountMessage { price: result[0]! as String, priceLocale: SKPriceLocaleMessage.decode(result[1]! as List), numberOfPeriods: result[2]! as int, - paymentMode: SKProductDiscountPaymentModeMessage.values[result[3]! as int], - subscriptionPeriod: SKProductSubscriptionPeriodMessage.decode(result[4]! as List), + paymentMode: + SKProductDiscountPaymentModeMessage.values[result[3]! as int], + subscriptionPeriod: SKProductSubscriptionPeriodMessage.decode( + result[4]! as List), identifier: result[5] as String?, type: SKProductDiscountTypeMessage.values[result[6]! as int], ); @@ -522,25 +537,25 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return SKErrorMessage.decode(readValue(buffer)!); - case 129: + case 129: return SKPaymentDiscountMessage.decode(readValue(buffer)!); - case 130: + case 130: return SKPaymentMessage.decode(readValue(buffer)!); - case 131: + case 131: return SKPaymentTransactionMessage.decode(readValue(buffer)!); - case 132: + case 132: return SKPriceLocaleMessage.decode(readValue(buffer)!); - case 133: + case 133: return SKProductDiscountMessage.decode(readValue(buffer)!); - case 134: + case 134: return SKProductMessage.decode(readValue(buffer)!); - case 135: + case 135: return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); - case 136: + case 136: return SKProductsResponseMessage.decode(readValue(buffer)!); - case 137: + case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -556,12 +571,15 @@ class InAppPurchaseAPI { : __pigeon_binaryMessenger = binaryMessenger; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = _InAppPurchaseAPICodec(); + static const MessageCodec pigeonChannelCodec = + _InAppPurchaseAPICodec(); /// Returns if the current device is able to make payments Future canMakePayments() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -587,8 +605,10 @@ class InAppPurchaseAPI { } Future> transactions() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -609,13 +629,16 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)! + .cast(); } } Future storefront() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -641,8 +664,10 @@ class InAppPurchaseAPI { } Future addPayment(Map paymentMap) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -662,15 +687,18 @@ class InAppPurchaseAPI { } } - Future startProductRequest(List productIdentifiers) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + Future startProductRequest( + List productIdentifiers) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([productIdentifiers]) as List?; + final List? __pigeon_replyList = await __pigeon_channel + .send([productIdentifiers]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -690,8 +718,10 @@ class InAppPurchaseAPI { } Future finishTransaction(Map finishMap) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -712,14 +742,16 @@ class InAppPurchaseAPI { } Future restoreTransactions(String? applicationUserName) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([applicationUserName]) as List?; + final List? __pigeon_replyList = await __pigeon_channel + .send([applicationUserName]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -734,8 +766,10 @@ class InAppPurchaseAPI { } Future presentCodeRedemptionSheet() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart index 4a61eec0fce0..f8fd85fcc6b0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart @@ -185,9 +185,6 @@ class SKPaymentQueueWrapper { /// or [`-[SKPayment restoreCompletedTransactionsWithApplicationUsername:]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1505992-restorecompletedtransactionswith?language=objc) /// depending on whether the `applicationUserName` is set. Future restoreTransactions({String? applicationUserName}) async { - // await channel.invokeMethod( - // '-[InAppPurchasePlugin restoreTransactions:result:]', - // applicationUserName); await _hostApi.restoreTransactions(applicationUserName); } @@ -198,8 +195,7 @@ class SKPaymentQueueWrapper { /// This method triggers [`-[SKPayment /// presentCodeRedemptionSheet]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/3566726-presentcoderedemptionsheet?language=objc) Future presentCodeRedemptionSheet() async { - await channel.invokeMethod( - '-[InAppPurchasePlugin presentCodeRedemptionSheet:result:]'); + await _hostApi.presentCodeRedemptionSheet(); } /// Shows the price consent sheet if the user has not yet responded to a diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index 7da86e09a8fa..82115222591d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart @@ -66,11 +66,16 @@ class SkProductResponseWrapper { @override int get hashCode => Object.hash(products, invalidProductIdentifiers); - static SkProductResponseWrapper convertFromPigeon(SKProductsResponseMessage msg) { - return SkProductResponseWrapper( - products: msg.products!.map((SKProductMessage? e) => SKProductWrapper.convertFromPigeon(e!)).toList(), - invalidProductIdentifiers: msg.invalidProductIdentifiers != null ? msg.invalidProductIdentifiers!.cast() : [], - ); + static SkProductResponseWrapper convertFromPigeon( + SKProductsResponseMessage msg) { + return SkProductResponseWrapper( + products: msg.products! + .map((SKProductMessage? e) => SKProductWrapper.convertFromPigeon(e!)) + .toList(), + invalidProductIdentifiers: msg.invalidProductIdentifiers != null + ? msg.invalidProductIdentifiers!.cast() + : [], + ); } } @@ -98,7 +103,8 @@ enum SKSubscriptionPeriodUnit { @JsonValue(3) year; - static SKSubscriptionPeriodUnit convertFromPigeon(SKSubscriptionPeriodUnitMessage msg) { + static SKSubscriptionPeriodUnit convertFromPigeon( + SKSubscriptionPeriodUnitMessage msg) { switch (msg) { case SKSubscriptionPeriodUnitMessage.day: return SKSubscriptionPeriodUnit.day; @@ -164,7 +170,8 @@ class SKProductSubscriptionPeriodWrapper { @override int get hashCode => Object.hash(numberOfUnits, unit); - static SKProductSubscriptionPeriodWrapper convertFromPigeon(SKProductSubscriptionPeriodMessage msg) { + static SKProductSubscriptionPeriodWrapper convertFromPigeon( + SKProductSubscriptionPeriodMessage msg) { return SKProductSubscriptionPeriodWrapper( numberOfUnits: msg.numberOfUnits, unit: SKSubscriptionPeriodUnit.convertFromPigeon(msg.unit)); @@ -193,7 +200,8 @@ enum SKProductDiscountPaymentMode { @JsonValue(-1) unspecified; - static SKProductDiscountPaymentMode convertFromPigeon(SKProductDiscountPaymentModeMessage msg) { + static SKProductDiscountPaymentMode convertFromPigeon( + SKProductDiscountPaymentModeMessage msg) { switch (msg) { case SKProductDiscountPaymentModeMessage.payAsYouGo: return SKProductDiscountPaymentMode.payAsYouGo; @@ -224,7 +232,8 @@ enum SKProductDiscountType { @JsonValue(1) subscription; - static SKProductDiscountType convertFromPigeon(SKProductDiscountTypeMessage msg) { + static SKProductDiscountType convertFromPigeon( + SKProductDiscountTypeMessage msg) { switch (msg) { case SKProductDiscountTypeMessage.introductory: return SKProductDiscountType.introductory; @@ -315,13 +324,17 @@ class SKProductDiscountWrapper { int get hashCode => Object.hash(price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod, identifier, type); - static SKProductDiscountWrapper convertFromPigeon(SKProductDiscountMessage msg) { + static SKProductDiscountWrapper convertFromPigeon( + SKProductDiscountMessage msg) { return SKProductDiscountWrapper( price: msg.price, priceLocale: SKPriceLocaleWrapper.convertFromPigeon(msg.priceLocale), numberOfPeriods: msg.numberOfPeriods, - paymentMode: SKProductDiscountPaymentMode.convertFromPigeon(msg.paymentMode), - subscriptionPeriod: SKProductSubscriptionPeriodWrapper.convertFromPigeon(msg.subscriptionPeriod), + paymentMode: + SKProductDiscountPaymentMode.convertFromPigeon(msg.paymentMode), + subscriptionPeriod: + SKProductSubscriptionPeriodWrapper.convertFromPigeon( + msg.subscriptionPeriod), identifier: msg.identifier, type: SKProductDiscountType.convertFromPigeon(msg.type)); } @@ -445,16 +458,26 @@ class SKProductWrapper { discounts); static SKProductWrapper convertFromPigeon(SKProductMessage msg) { - return SKProductWrapper(productIdentifier: msg.productIdentifier, + return SKProductWrapper( + productIdentifier: msg.productIdentifier, localizedTitle: msg.localizedTitle, localizedDescription: msg.localizedDescription, priceLocale: SKPriceLocaleWrapper.convertFromPigeon(msg.priceLocale), price: msg.price, subscriptionGroupIdentifier: msg.subscriptionGroupIdentifier, - subscriptionPeriod: msg.subscriptionPeriod != null ? SKProductSubscriptionPeriodWrapper.convertFromPigeon(msg.subscriptionPeriod!) : null, - introductoryPrice: msg.introductoryPrice != null ? SKProductDiscountWrapper.convertFromPigeon(msg.introductoryPrice!) : null, - discounts: msg.discounts != null ? msg.discounts!.map((SKProductDiscountMessage? e) => SKProductDiscountWrapper.convertFromPigeon(e!)).toList() : [] - ); + subscriptionPeriod: msg.subscriptionPeriod != null + ? SKProductSubscriptionPeriodWrapper.convertFromPigeon( + msg.subscriptionPeriod!) + : null, + introductoryPrice: msg.introductoryPrice != null + ? SKProductDiscountWrapper.convertFromPigeon(msg.introductoryPrice!) + : null, + discounts: msg.discounts != null + ? msg.discounts! + .map((SKProductDiscountMessage? e) => + SKProductDiscountWrapper.convertFromPigeon(e!)) + .toList() + : []); } } @@ -520,7 +543,6 @@ class SKPriceLocaleWrapper { return SKPriceLocaleWrapper( currencySymbol: msg.currencySymbol, currencyCode: msg.currencyCode, - countryCode: msg.countryCode - ); + countryCode: msg.countryCode); } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart index 9432f4f97dd3..13987a35c5e6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart @@ -11,8 +11,6 @@ import '../channel.dart'; import '../messages.g.dart'; import 'sk_product_wrapper.dart'; - - /// A request maker that handles all the requests made by SKRequest subclasses. /// /// There are multiple [SKRequest](https://developer.apple.com/documentation/storekit/skrequest?language=objc) subclasses handling different requests in the `StoreKit` with multiple delegate methods, @@ -26,6 +24,7 @@ class SKRequestMaker { void setInAppPurchaseHostApi(InAppPurchaseAPI api) { _hostApi = api; } + /// Fetches product information for a list of given product identifiers. /// /// The `productIdentifiers` should contain legitimate product identifiers that you declared for the products in the iTunes Connect. Invalid identifiers @@ -37,7 +36,8 @@ class SKRequestMaker { /// A [PlatformException] is thrown if the platform code making the request fails. Future startProductRequest( List productIdentifiers) async { - final SKProductsResponseMessage productResponsePigeon = await _hostApi.startProductRequest(productIdentifiers); + final SKProductsResponseMessage productResponsePigeon = + await _hostApi.startProductRequest(productIdentifiers); // should products be null or [] ? if (productResponsePigeon.products == null) { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index 3e6db1c959b9..54da48135223 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -131,26 +131,23 @@ class SKStorefrontMessage { } class SKProductsResponseMessage { - const SKProductsResponseMessage ({ - required this.products, - required this.invalidProductIdentifiers -}); + const SKProductsResponseMessage( + {required this.products, required this.invalidProductIdentifiers}); final List? products; final List? invalidProductIdentifiers; } class SKProductMessage { - const SKProductMessage({ - required this.productIdentifier, - required this.localizedTitle, - required this.localizedDescription, - required this.priceLocale, - required this.price, - this.subscriptionGroupIdentifier, - this.subscriptionPeriod, - this.introductoryPrice, - this.discounts - }); + const SKProductMessage( + {required this.productIdentifier, + required this.localizedTitle, + required this.localizedDescription, + required this.priceLocale, + required this.price, + this.subscriptionGroupIdentifier, + this.subscriptionPeriod, + this.introductoryPrice, + this.discounts}); final String productIdentifier; final String localizedTitle; @@ -183,12 +180,12 @@ class SKPriceLocaleMessage { class SKProductDiscountMessage { const SKProductDiscountMessage( {required this.price, - required this.priceLocale, - required this.numberOfPeriods, - required this.paymentMode, - required this.subscriptionPeriod, - required this.identifier, - required this.type}); + required this.priceLocale, + required this.numberOfPeriods, + required this.paymentMode, + required this.subscriptionPeriod, + required this.identifier, + required this.type}); final String price; final SKPriceLocaleMessage priceLocale; @@ -251,7 +248,8 @@ abstract class InAppPurchaseAPI { void addPayment(Map paymentMap); @async - SKProductsResponseMessage startProductRequest(List productIdentifiers); + SKProductsResponseMessage startProductRequest( + List productIdentifiers); void finishTransaction(Map finishMap); @@ -260,5 +258,4 @@ abstract class InAppPurchaseAPI { void presentCodeRedemptionSheet(); // // void retrieveReceiptData(); - } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index 38129807638b..b958b200d1c9 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -290,9 +290,25 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { } @override - SKProductsResponseMessage startProductRequest(List productIdentifiers) { - // TODO: implement startProductRequest + void finishTransaction(Map finishMap) { + // TODO: implement finishTransaction + } + + @override + void presentCodeRedemptionSheet() { + // TODO: implement presentCodeRedemptionSheet + } + + @override + void restoreTransactions(String? applicationUserName) { + // TODO: implement restoreTransactions + } + + @override + Future startProductRequest( + List productIdentifiers) { + print("hello 2"); + throw UnimplementedError(); } - } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart index 9236db49a898..704be83c8e25 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart @@ -228,13 +228,6 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { Future onMethodCall(MethodCall call) { switch (call.method) { // request makers - case '-[InAppPurchasePlugin startProductRequest:result:]': - startProductRequestParam = call.arguments as List; - if (getProductRequestFailTest) { - return Future.value(); - } - return Future>.value( - buildProductResponseMap(dummyProductResponseWrapper)); case '-[InAppPurchasePlugin refreshReceipt:result:]': refreshReceipt++; refreshReceiptParam = Map.castFrom( @@ -246,16 +239,6 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { throw Exception('some arbitrary error'); } return Future.value('receipt data'); - case '-[InAppPurchasePlugin finishTransaction:result:]': - transactionsFinished.add( - Map.from(call.arguments as Map)); - return Future.sync(() {}); - case '-[InAppPurchasePlugin restoreTransactions:result:]': - applicationNameHasTransactionRestored = call.arguments as String; - return Future.sync(() {}); - case '-[InAppPurchasePlugin presentCodeRedemptionSheet:result:]': - presentCodeRedemption = true; - return Future.sync(() {}); case '-[SKPaymentQueue startObservingTransactionQueue]': queueIsActive = true; return Future.sync(() {}); @@ -297,11 +280,30 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { [dummyTransactionMessage]; @override - SKProductsResponseMessage startProductRequest(List productIdentifiers) { - // TODO: implement startProductRequest - throw UnimplementedError(); + void finishTransaction(Map finishMap) { + transactionsFinished.add(Map.from(finishMap)); + } + + @override + void presentCodeRedemptionSheet() { + presentCodeRedemption = true; + } + + @override + void restoreTransactions(String? applicationUserName) { + applicationNameHasTransactionRestored = applicationUserName!; + } + + @override + Future startProductRequest( + List productIdentifiers) { + startProductRequestParam = productIdentifiers; + if (getProductRequestFailTest) { + return Future.value( + SKProductsResponseMessage()); + } + return Future.value(dummyProductResponseMessage); } - } class TestPaymentQueueDelegate extends SKPaymentQueueDelegateWrapper {} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart index ad32a0a5c14a..218c037ac599 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart @@ -62,6 +62,12 @@ final SKPriceLocaleWrapper dollarLocale = SKPriceLocaleWrapper( countryCode: 'US', ); +final SKPriceLocaleMessage dollarLocaleMessage = SKPriceLocaleMessage( + currencySymbol: r'$', + currencyCode: 'USD', + countryCode: 'US', +); + final SKPriceLocaleWrapper noSymbolLocale = SKPriceLocaleWrapper( currencySymbol: '', currencyCode: 'EUR', @@ -74,6 +80,12 @@ final SKProductSubscriptionPeriodWrapper dummySubscription = unit: SKSubscriptionPeriodUnit.month, ); +final SKProductSubscriptionPeriodMessage dummySubscriptionMessage = + SKProductSubscriptionPeriodMessage( + numberOfUnits: 1, + unit: SKSubscriptionPeriodUnitMessage.month, +); + final SKProductDiscountWrapper dummyDiscount = SKProductDiscountWrapper( price: '1.0', priceLocale: dollarLocale, @@ -84,6 +96,16 @@ final SKProductDiscountWrapper dummyDiscount = SKProductDiscountWrapper( type: SKProductDiscountType.subscription, ); +final SKProductDiscountMessage dummyDiscountMessage = SKProductDiscountMessage( + price: '1.0', + priceLocale: dollarLocaleMessage, + numberOfPeriods: 1, + paymentMode: SKProductDiscountPaymentModeMessage.payUpFront, + subscriptionPeriod: dummySubscriptionMessage, + identifier: 'id', + type: SKProductDiscountTypeMessage.subscription, +); + final SKProductDiscountWrapper dummyDiscountMissingIdentifierAndType = SKProductDiscountWrapper( price: '1.0', @@ -107,12 +129,32 @@ final SKProductWrapper dummyProductWrapper = SKProductWrapper( discounts: [dummyDiscount], ); +final SKProductMessage dummyProductMessage = SKProductMessage( + productIdentifier: 'id', + localizedTitle: 'title', + localizedDescription: 'description', + priceLocale: dollarLocaleMessage, + subscriptionGroupIdentifier: 'com.group', + price: '1.0', + subscriptionPeriod: dummySubscriptionMessage, + introductoryPrice: dummyDiscountMessage, + discounts: [dummyDiscountMessage], +); + final SkProductResponseWrapper dummyProductResponseWrapper = SkProductResponseWrapper( products: [dummyProductWrapper], invalidProductIdentifiers: const ['123'], ); +final SKProductsResponseMessage dummyProductResponseMessage = + SKProductsResponseMessage( + products: [dummyProductMessage], + invalidProductIdentifiers: const ['123'], +); + +class SkProductsResponseMessage {} + Map buildLocaleMap(SKPriceLocaleWrapper local) { return { 'currencySymbol': local.currencySymbol, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart index c871776694e1..80debcebb79d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart @@ -52,25 +52,25 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return SKErrorMessage.decode(readValue(buffer)!); - case 129: + case 129: return SKPaymentDiscountMessage.decode(readValue(buffer)!); - case 130: + case 130: return SKPaymentMessage.decode(readValue(buffer)!); - case 131: + case 131: return SKPaymentTransactionMessage.decode(readValue(buffer)!); - case 132: + case 132: return SKPriceLocaleMessage.decode(readValue(buffer)!); - case 133: + case 133: return SKProductDiscountMessage.decode(readValue(buffer)!); - case 134: + case 134: return SKProductMessage.decode(readValue(buffer)!); - case 135: + case 135: return SKProductSubscriptionPeriodMessage.decode(readValue(buffer)!); - case 136: + case 136: return SKProductsResponseMessage.decode(readValue(buffer)!); - case 137: + case 137: return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -79,8 +79,10 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { } abstract class TestInAppPurchaseApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = _TestInAppPurchaseApiCodec(); + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec pigeonChannelCodec = + _TestInAppPurchaseApiCodec(); /// Returns if the current device is able to make payments bool canMakePayments(); @@ -91,7 +93,8 @@ abstract class TestInAppPurchaseApi { void addPayment(Map paymentMap); - Future startProductRequest(List productIdentifiers); + Future startProductRequest( + List productIdentifiers); void finishTransaction(Map finishMap); @@ -99,76 +102,102 @@ abstract class TestInAppPurchaseApi { void presentCodeRedemptionSheet(); - static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestInAppPurchaseApi? api, + {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { final bool output = api.canMakePayments(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { - final List output = api.transactions(); + final List output = + api.transactions(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { final SKStorefrontMessage output = api.storefront(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); final List args = (message as List?)!; - final Map? arg_paymentMap = (args[0] as Map?)?.cast(); + final Map? arg_paymentMap = + (args[0] as Map?)?.cast(); assert(arg_paymentMap != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null, expected non-null Map.'); try { @@ -176,49 +205,64 @@ abstract class TestInAppPurchaseApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null.'); final List args = (message as List?)!; - final List? arg_productIdentifiers = (args[0] as List?)?.cast(); + final List? arg_productIdentifiers = + (args[0] as List?)?.cast(); assert(arg_productIdentifiers != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startProductRequest was null, expected non-null List.'); try { - final SKProductsResponseMessage output = await api.startProductRequest(arg_productIdentifiers!); + final SKProductsResponseMessage output = + await api.startProductRequest(arg_productIdentifiers!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null.'); final List args = (message as List?)!; - final Map? arg_finishMap = (args[0] as Map?)?.cast(); + final Map? arg_finishMap = + (args[0] as Map?)?.cast(); assert(arg_finishMap != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null, expected non-null Map.'); try { @@ -226,22 +270,28 @@ abstract class TestInAppPurchaseApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions was null.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.restoreTransactions was null.'); final List args = (message as List?)!; final String? arg_applicationUserName = (args[0] as String?); try { @@ -249,27 +299,34 @@ abstract class TestInAppPurchaseApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet', pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.presentCodeRedemptionSheet', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { try { api.presentCodeRedemptionSheet(); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } From 50fe53a117c7b67af95a5a84ba6ef9851c4d895d Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 7 Feb 2024 15:10:48 -0800 Subject: [PATCH 04/12] remove development team >:( --- .../example/ios/Runner.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index 5eef17d5f4a7..06e0b3ac947d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -579,7 +579,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -606,7 +606,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -639,7 +639,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -666,7 +666,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", From 214cc9346c53d2b612b1490e2bb9af88ed7e90f3 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 7 Feb 2024 16:22:51 -0800 Subject: [PATCH 05/12] adding converters TO pigeon for test, fix dart tests --- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../sk_product_wrapper.dart | 131 +++++++++++++++++- .../store_kit_wrappers/sk_request_maker.dart | 11 +- .../pigeons/messages.dart | 2 - .../test/fakes/fake_storekit_platform.dart | 123 +++++----------- .../sk_test_stub_objects.dart | 2 - 6 files changed, 171 insertions(+), 102 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 044a1fef4fc5..802b4687da97 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Converts `startProductRequest()`, `finishTransaction()`, `restoreTransactions()`, `presentCodeRedemptionSheet()` to pigeon. + ## 0.3.9 * Converts `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index 82115222591d..b4e4707af0f7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart @@ -5,8 +5,9 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'enum_converters.dart'; + import '../messages.g.dart'; +import 'enum_converters.dart'; // WARNING: Changes to `@JsonSerializable` classes need to be reflected in the // below generated file. Run `flutter packages pub run build_runner watch` to @@ -66,6 +67,7 @@ class SkProductResponseWrapper { @override int get hashCode => Object.hash(products, invalidProductIdentifiers); + /// Convert from [SkProductResponseMessage] to [SkProductResponseWrapper] static SkProductResponseWrapper convertFromPigeon( SKProductsResponseMessage msg) { return SkProductResponseWrapper( @@ -77,6 +79,19 @@ class SkProductResponseWrapper { : [], ); } + + @visibleForTesting + + /// Convert from [SkProductResponseWrapper] to [SkProductResponseWrapper] + static SKProductsResponseMessage convertToPigeon( + SkProductResponseWrapper wrapper) { + return SKProductsResponseMessage( + products: wrapper.products + .map((SKProductWrapper? e) => SKProductWrapper.convertToPigeon(e!)) + .toList(), + invalidProductIdentifiers: + wrapper.invalidProductIdentifiers.cast()); + } } /// Dart wrapper around StoreKit's [SKProductPeriodUnit](https://developer.apple.com/documentation/storekit/skproductperiodunit?language=objc). @@ -103,6 +118,7 @@ enum SKSubscriptionPeriodUnit { @JsonValue(3) year; + /// Convert from [SKSubscriptionPeriodUnitMessage] to [SKSubscriptionPeriodUnit] static SKSubscriptionPeriodUnit convertFromPigeon( SKSubscriptionPeriodUnitMessage msg) { switch (msg) { @@ -116,6 +132,23 @@ enum SKSubscriptionPeriodUnit { return SKSubscriptionPeriodUnit.year; } } + + @visibleForTesting + + /// Convert from [SKSubscriptionPeriodUnit] to [SKSubscriptionPeriodUnitMessage] + static SKSubscriptionPeriodUnitMessage convertToPigeon( + SKSubscriptionPeriodUnit msg) { + switch (msg) { + case SKSubscriptionPeriodUnit.day: + return SKSubscriptionPeriodUnitMessage.day; + case SKSubscriptionPeriodUnit.week: + return SKSubscriptionPeriodUnitMessage.week; + case SKSubscriptionPeriodUnit.month: + return SKSubscriptionPeriodUnitMessage.month; + case SKSubscriptionPeriodUnit.year: + return SKSubscriptionPeriodUnitMessage.year; + } + } } /// Dart wrapper around StoreKit's [SKProductSubscriptionPeriod](https://developer.apple.com/documentation/storekit/skproductsubscriptionperiod?language=objc). @@ -170,12 +203,23 @@ class SKProductSubscriptionPeriodWrapper { @override int get hashCode => Object.hash(numberOfUnits, unit); + /// Convert from [SKProductSubscriptionPeriodMessage] to [SKProductSubscriptionPeriodWrapper] static SKProductSubscriptionPeriodWrapper convertFromPigeon( SKProductSubscriptionPeriodMessage msg) { return SKProductSubscriptionPeriodWrapper( numberOfUnits: msg.numberOfUnits, unit: SKSubscriptionPeriodUnit.convertFromPigeon(msg.unit)); } + + @visibleForTesting + + /// Convert from [SKProductSubscriptionPeriodWrapper] to [SKProductSubscriptionPeriodMessage] + static SKProductSubscriptionPeriodMessage convertToPigeon( + SKProductSubscriptionPeriodWrapper wrapper) { + return SKProductSubscriptionPeriodMessage( + numberOfUnits: wrapper.numberOfUnits, + unit: SKSubscriptionPeriodUnit.convertToPigeon(wrapper.unit)); + } } /// Dart wrapper around StoreKit's [SKProductDiscountPaymentMode](https://developer.apple.com/documentation/storekit/skproductdiscountpaymentmode?language=objc). @@ -200,6 +244,7 @@ enum SKProductDiscountPaymentMode { @JsonValue(-1) unspecified; + /// Convert from [SKProductDiscountPaymentModeMessage] to [SKProductDiscountPaymentModeWrapper] static SKProductDiscountPaymentMode convertFromPigeon( SKProductDiscountPaymentModeMessage msg) { switch (msg) { @@ -213,6 +258,23 @@ enum SKProductDiscountPaymentMode { return SKProductDiscountPaymentMode.unspecified; } } + + @visibleForTesting + + /// Convert from [SKProductDiscountPaymentModeMessage] to [SKProductDiscountPaymentMode] + static SKProductDiscountPaymentModeMessage convertToPigeon( + SKProductDiscountPaymentMode wrapper) { + switch (wrapper) { + case SKProductDiscountPaymentMode.payAsYouGo: + return SKProductDiscountPaymentModeMessage.payAsYouGo; + case SKProductDiscountPaymentMode.payUpFront: + return SKProductDiscountPaymentModeMessage.payUpFront; + case SKProductDiscountPaymentMode.freeTrail: + return SKProductDiscountPaymentModeMessage.freeTrial; + case SKProductDiscountPaymentMode.unspecified: + return SKProductDiscountPaymentModeMessage.unspecified; + } + } } /// Dart wrapper around StoreKit's [SKProductDiscountType] @@ -232,6 +294,7 @@ enum SKProductDiscountType { @JsonValue(1) subscription; + /// Convert from [SKProductDiscountTypeMessage] to [SKProductDiscountType] static SKProductDiscountType convertFromPigeon( SKProductDiscountTypeMessage msg) { switch (msg) { @@ -241,6 +304,19 @@ enum SKProductDiscountType { return SKProductDiscountType.subscription; } } + + @visibleForTesting + + /// Convert from [SKProductDiscountType] to [SKProductDiscountTypeMessage] + static SKProductDiscountTypeMessage convertToPigeon( + SKProductDiscountType wrapper) { + switch (wrapper) { + case SKProductDiscountType.introductory: + return SKProductDiscountTypeMessage.introductory; + case SKProductDiscountType.subscription: + return SKProductDiscountTypeMessage.subscription; + } + } } /// Dart wrapper around StoreKit's [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc). @@ -324,6 +400,7 @@ class SKProductDiscountWrapper { int get hashCode => Object.hash(price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod, identifier, type); + /// Convert from [SKProductDiscountMessage] to [SKProductDiscountWrapper] static SKProductDiscountWrapper convertFromPigeon( SKProductDiscountMessage msg) { return SKProductDiscountWrapper( @@ -338,6 +415,23 @@ class SKProductDiscountWrapper { identifier: msg.identifier, type: SKProductDiscountType.convertFromPigeon(msg.type)); } + + @visibleForTesting + + /// Convert from [SKProductDiscountWrapper] to [SKProductDiscountMessage] + static SKProductDiscountMessage convertToPigeon( + SKProductDiscountWrapper wrapper) { + return SKProductDiscountMessage( + price: wrapper.price, + priceLocale: SKPriceLocaleWrapper.convertToPigeon(wrapper.priceLocale), + numberOfPeriods: wrapper.numberOfPeriods, + paymentMode: + SKProductDiscountPaymentMode.convertToPigeon(wrapper.paymentMode), + subscriptionPeriod: SKProductSubscriptionPeriodWrapper.convertToPigeon( + wrapper.subscriptionPeriod), + identifier: wrapper.identifier, + type: SKProductDiscountType.convertToPigeon(wrapper.type)); + } } /// Dart wrapper around StoreKit's [SKProduct](https://developer.apple.com/documentation/storekit/skproduct?language=objc). @@ -457,6 +551,7 @@ class SKProductWrapper { introductoryPrice, discounts); + /// Convert from [SKProductMessage] to [SKProductWrapper] static SKProductWrapper convertFromPigeon(SKProductMessage msg) { return SKProductWrapper( productIdentifier: msg.productIdentifier, @@ -479,6 +574,29 @@ class SKProductWrapper { .toList() : []); } + + /// Convert from [SKProductWrapper] to [SKProductMessage] + static SKProductMessage convertToPigeon(SKProductWrapper wrapper) { + return SKProductMessage( + productIdentifier: wrapper.productIdentifier, + localizedTitle: wrapper.localizedTitle, + localizedDescription: wrapper.localizedDescription, + priceLocale: SKPriceLocaleWrapper.convertToPigeon(wrapper.priceLocale), + price: wrapper.price, + subscriptionGroupIdentifier: wrapper.subscriptionGroupIdentifier, + subscriptionPeriod: wrapper.subscriptionPeriod != null + ? SKProductSubscriptionPeriodWrapper.convertToPigeon( + wrapper.subscriptionPeriod!) + : null, + introductoryPrice: wrapper.introductoryPrice != null + ? SKProductDiscountWrapper.convertToPigeon( + wrapper.introductoryPrice!) + : null, + discounts: wrapper.discounts + .map((SKProductDiscountWrapper? e) => + SKProductDiscountWrapper.convertToPigeon(e!)) + .toList()); + } } /// Object that indicates the locale of the price @@ -539,10 +657,21 @@ class SKPriceLocaleWrapper { @override int get hashCode => Object.hash(currencySymbol, currencyCode); + /// Convert from [SKPriceLocaleMessage] to [SKPriceLocaleWrapper] static SKPriceLocaleWrapper convertFromPigeon(SKPriceLocaleMessage msg) { return SKPriceLocaleWrapper( currencySymbol: msg.currencySymbol, currencyCode: msg.currencyCode, countryCode: msg.countryCode); } + + @visibleForTesting + + /// Convert from [SKPriceLocaleWrapper] to [SKPriceLocaleMessage] + static SKPriceLocaleMessage convertToPigeon(SKPriceLocaleWrapper msg) { + return SKPriceLocaleMessage( + currencySymbol: msg.currencySymbol, + currencyCode: msg.currencyCode, + countryCode: msg.countryCode); + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart index 13987a35c5e6..2dc2ad7baef2 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart @@ -5,26 +5,19 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import '../channel.dart'; import '../messages.g.dart'; import 'sk_product_wrapper.dart'; +InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); + /// A request maker that handles all the requests made by SKRequest subclasses. /// /// There are multiple [SKRequest](https://developer.apple.com/documentation/storekit/skrequest?language=objc) subclasses handling different requests in the `StoreKit` with multiple delegate methods, /// we consolidated all the `SKRequest` subclasses into this class to make requests in a more straightforward way. /// The request maker will create a SKRequest object, immediately starting it, and completing the future successfully or throw an exception depending on what happened to the request. class SKRequestMaker { - InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); - - /// Set up pigeon API. - @visibleForTesting - void setInAppPurchaseHostApi(InAppPurchaseAPI api) { - _hostApi = api; - } - /// Fetches product information for a list of given product identifiers. /// /// The `productIdentifiers` should contain legitimate product identifiers that you declared for the products in the iTunes Connect. Invalid identifiers diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index 54da48135223..f20a111f0dbf 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -256,6 +256,4 @@ abstract class InAppPurchaseAPI { void restoreTransactions(String? applicationUserName); void presentCodeRedemptionSheet(); - // - // void retrieveReceiptData(); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index b958b200d1c9..3525df005aed 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -124,42 +124,6 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { Future onMethodCall(MethodCall call) { switch (call.method) { - case '-[InAppPurchasePlugin startProductRequest:result:]': - if (queryProductException != null) { - throw queryProductException!; - } - final List productIDS = - List.castFrom(call.arguments as List); - final List invalidFound = []; - final List products = []; - for (final String productID in productIDS) { - if (!validProductIDs.contains(productID)) { - invalidFound.add(productID); - } else { - products.add(validProducts[productID]!); - } - } - final SkProductResponseWrapper response = SkProductResponseWrapper( - products: products, invalidProductIdentifiers: invalidFound); - return Future>.value( - buildProductResponseMap(response)); - case '-[InAppPurchasePlugin restoreTransactions:result:]': - if (restoreException != null) { - throw restoreException!; - } - if (testRestoredError != null) { - InAppPurchaseStoreKitPlatform.observer - .restoreCompletedTransactionsFailed(error: testRestoredError!); - return Future.sync(() {}); - } - if (!testRestoredTransactionsNull) { - InAppPurchaseStoreKitPlatform.observer - .updatedTransactions(transactions: transactionList); - } - InAppPurchaseStoreKitPlatform.observer - .paymentQueueRestoreCompletedTransactionsFinished(); - - return Future.sync(() {}); case '-[InAppPurchasePlugin retrieveReceiptData:result:]': if (receiptData != null) { return Future.value(receiptData!); @@ -169,53 +133,6 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { case '-[InAppPurchasePlugin refreshReceipt:result:]': receiptData = 'refreshed receipt data'; return Future.sync(() {}); - case '-[InAppPurchasePlugin addPayment:result:]': - final Map arguments = _getArgumentDictionary(call); - final String id = arguments['productIdentifier']! as String; - final int quantity = arguments['quantity']! as int; - - // Keep the received paymentDiscount parameter when testing payment with discount. - if (arguments['applicationUsername']! == 'userWithDiscount') { - final Map? discountArgument = - arguments['paymentDiscount'] as Map?; - if (discountArgument != null) { - discountReceived = discountArgument.cast(); - } else { - discountReceived = {}; - } - } - - final SKPaymentTransactionWrapper transaction = - createPendingTransaction(id, quantity: quantity); - transactionList.add(transaction); - InAppPurchaseStoreKitPlatform.observer.updatedTransactions( - transactions: [transaction]); - sleep(const Duration(milliseconds: 30)); - if (testTransactionFail) { - final SKPaymentTransactionWrapper transactionFailed = - createFailedTransaction(id, quantity: quantity); - InAppPurchaseStoreKitPlatform.observer.updatedTransactions( - transactions: [transactionFailed]); - } else if (testTransactionCancel > 0) { - final SKPaymentTransactionWrapper transactionCanceled = - createCanceledTransaction(id, testTransactionCancel, - quantity: quantity); - InAppPurchaseStoreKitPlatform.observer.updatedTransactions( - transactions: [transactionCanceled]); - } else { - final SKPaymentTransactionWrapper transactionFinished = - createPurchasedTransaction( - id, transaction.transactionIdentifier ?? '', - quantity: quantity); - InAppPurchaseStoreKitPlatform.observer.updatedTransactions( - transactions: [transactionFinished]); - } - case '-[InAppPurchasePlugin finishTransaction:result:]': - final Map arguments = _getArgumentDictionary(call); - finishedTransactions.add(createPurchasedTransaction( - arguments['productIdentifier']! as String, - arguments['transactionIdentifier']! as String, - quantity: transactionList.first.payment.quantity)); case '-[SKPaymentQueue startObservingTransactionQueue]': queueIsActive = true; case '-[SKPaymentQueue stopObservingTransactionQueue]': @@ -291,24 +208,54 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { @override void finishTransaction(Map finishMap) { - // TODO: implement finishTransaction + finishedTransactions.add(createPurchasedTransaction( + finishMap['productIdentifier']!, finishMap['transactionIdentifier']!, + quantity: transactionList.first.payment.quantity)); } @override void presentCodeRedemptionSheet() { - // TODO: implement presentCodeRedemptionSheet + return; } @override void restoreTransactions(String? applicationUserName) { - // TODO: implement restoreTransactions + if (restoreException != null) { + throw restoreException!; + } + if (testRestoredError != null) { + InAppPurchaseStoreKitPlatform.observer + .restoreCompletedTransactionsFailed(error: testRestoredError!); + return; + } + if (!testRestoredTransactionsNull) { + InAppPurchaseStoreKitPlatform.observer + .updatedTransactions(transactions: transactionList); + } + InAppPurchaseStoreKitPlatform.observer + .paymentQueueRestoreCompletedTransactionsFinished(); } @override Future startProductRequest( List productIdentifiers) { - print("hello 2"); + if (queryProductException != null) { + throw queryProductException!; + } + final List productIDS = productIdentifiers; + final List invalidFound = []; + final List products = []; + for (final String? productID in productIDS) { + if (!validProductIDs.contains(productID)) { + invalidFound.add(productID!); + } else { + products.add(validProducts[productID]!); + } + } + final SkProductResponseWrapper response = SkProductResponseWrapper( + products: products, invalidProductIdentifiers: invalidFound); - throw UnimplementedError(); + return Future.value( + SkProductResponseWrapper.convertToPigeon(response)); } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart index 218c037ac599..59022aa2fd24 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart @@ -153,8 +153,6 @@ final SKProductsResponseMessage dummyProductResponseMessage = invalidProductIdentifiers: const ['123'], ); -class SkProductsResponseMessage {} - Map buildLocaleMap(SKPriceLocaleWrapper local) { return { 'currencySymbol': local.currencySymbol, From 7524ba8d10509a29780034a95645195561cad09c Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 7 Feb 2024 16:26:53 -0800 Subject: [PATCH 06/12] remove unused imports and fcns. --- .../test/fakes/fake_storekit_platform.dart | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index 3525df005aed..ba1f7acc978e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io'; - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; @@ -141,14 +139,6 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { return Future.sync(() {}); } - /// Returns the arguments of [call] as typed string-keyed Map. - /// - /// This does not do any type validation, so is only safe to call if the - /// arguments are known to be a map. - Map _getArgumentDictionary(MethodCall call) { - return (call.arguments as Map).cast(); - } - @override bool canMakePayments() { return true; From 1374433b846dd9dd4dc9082c9783d81ccd1c25bb Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 7 Feb 2024 16:42:22 -0800 Subject: [PATCH 07/12] pubspec/license --- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 2 +- .../in_app_purchase_storekit/darwin/Classes/messages.g.h | 4 ++++ .../in_app_purchase_storekit/darwin/Classes/messages.g.m | 4 ++++ .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 802b4687da97..67ff21186e7b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 0.3.10 * Converts `startProductRequest()`, `finishTransaction()`, `restoreTransactions()`, `presentCodeRedemptionSheet()` to pigeon. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index 05a1dc1e527e..87a197212504 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + // Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 4eec24eb2c67..79599d932e0e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + // Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index fc7fde8d6220..97fa1dbd1869 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.9 +version: 0.3.10 environment: sdk: ^3.2.3 From a3a7439b707d9178be56b719d50343e69d5e8971 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 8 Feb 2024 13:13:18 -0800 Subject: [PATCH 08/12] add translator tests, add null check for coverters --- .../darwin/Classes/FIAObjectTranslator.m | 11 ++++++ .../shared/RunnerTests/TranslatorTests.m | 35 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m index bc40256ec4aa..637d3d1e83bc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m @@ -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]; @@ -460,6 +464,10 @@ + (nullable SKPriceLocaleMessage *)convertNSLocaleToPigeon:(nullable NSLocale *) + (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)product API_AVAILABLE(ios(12.2)) { + if (!product) { + return nil; + } + NSArray *skProductDiscounts = product.discounts; NSMutableArray *pigeonProductDiscounts = [[NSMutableArray alloc] init]; @@ -485,6 +493,9 @@ + (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)prod + (nullable SKProductsResponseMessage *)convertProductsResponseToPigeon: (nullable SKProductsResponse *)productsResponse API_AVAILABLE(ios(12.2)) { + if (!productsResponse) { + return nil; + } NSArray *skProducts = productsResponse.products; NSMutableArray *pigeonProducts = [[NSMutableArray alloc] init]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m index 23597321de9a..b27917d3d645 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m @@ -458,4 +458,39 @@ - (void)testSKPaymentTransactionConvertToPigeon { XCTAssertEqual(paymentTransaction.transactionIdentifier, msg.transactionIdentifier); } +- (void)testSKProductResponseCovertToPigeon { + SKProductsResponseStub *response = + [[SKProductsResponseStub alloc] initWithMap:self.productResponseMap]; + SKProductsResponseMessage *responseMsg = [FIAObjectTranslator convertProductsResponseToPigeon:response]; + + XCTAssertEqual(responseMsg.products.count, 1); + XCTAssertEqual(responseMsg.invalidProductIdentifiers.count, 0); + + SKProductMessage *productMsg = responseMsg.products[0]; + + XCTAssertEqualObjects(productMsg.price, @"1"); + XCTAssertEqualObjects(productMsg.productIdentifier, @"123"); + XCTAssertEqualObjects(productMsg.localizedTitle, @"title"); + XCTAssertEqualObjects(productMsg.localizedDescription, @"des"); + XCTAssertEqualObjects(productMsg.subscriptionGroupIdentifier, @"com.group"); + + SKPriceLocaleMessage *localeMsg = productMsg.priceLocale; + SKProductSubscriptionPeriodMessage *subPeriod = productMsg.subscriptionPeriod; + SKProductDiscountMessage *introDiscount = productMsg.introductoryPrice; + NSArray *discounts = productMsg.discounts; + + XCTAssertEqualObjects(localeMsg.countryCode, nil); + XCTAssertEqualObjects(localeMsg.currencyCode, nil); + XCTAssertEqualObjects(localeMsg.currencySymbol, @"\u00a4"); + + XCTAssertEqual(subPeriod.unit, SKSubscriptionPeriodUnitMessageDay); + XCTAssertEqual(subPeriod.numberOfUnits, 0); + + XCTAssertEqualObjects(introDiscount.price, @"1"); + XCTAssertEqual(introDiscount.numberOfPeriods, 1); + XCTAssertEqual(introDiscount.paymentMode, SKProductDiscountPaymentModeMessagePayUpFront); + + XCTAssertEqual(discounts.count, 1); +} + @end From 569abc2a1baa6af290364a6bceea2f78b76215fb Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 8 Feb 2024 13:56:01 -0800 Subject: [PATCH 09/12] format --- .../example/shared/RunnerTests/TranslatorTests.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m index b27917d3d645..833e2c931349 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m @@ -461,7 +461,8 @@ - (void)testSKPaymentTransactionConvertToPigeon { - (void)testSKProductResponseCovertToPigeon { SKProductsResponseStub *response = [[SKProductsResponseStub alloc] initWithMap:self.productResponseMap]; - SKProductsResponseMessage *responseMsg = [FIAObjectTranslator convertProductsResponseToPigeon:response]; + SKProductsResponseMessage *responseMsg = + [FIAObjectTranslator convertProductsResponseToPigeon:response]; XCTAssertEqual(responseMsg.products.count, 1); XCTAssertEqual(responseMsg.invalidProductIdentifiers.count, 0); From 0aadf34b705040993cacf16d4b5ae82b9a402029 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Fri, 9 Feb 2024 14:12:38 -0800 Subject: [PATCH 10/12] PR comments --- .../darwin/Classes/FIAObjectTranslator.m | 5 +- .../darwin/Classes/InAppPurchasePlugin.m | 4 + .../RunnerTests/InAppPurchasePluginTests.m | 4 + .../shared/RunnerTests/TranslatorTests.m | 1 + .../sk_product_wrapper.dart | 21 ++-- .../pigeons/messages.dart | 13 --- .../test/fakes/fake_storekit_platform.dart | 4 +- .../pigeon_converter_test.dart | 98 +++++++++++++++++++ 8 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/pigeon_converter_test.dart diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m index 637d3d1e83bc..5af57671c2ce 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m @@ -470,7 +470,7 @@ + (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)prod NSArray *skProductDiscounts = product.discounts; NSMutableArray *pigeonProductDiscounts = - [[NSMutableArray alloc] init]; + [[NSMutableArray arrayWithCapacity:skProductDiscounts.count] init]; for (SKProductDiscount *productDiscount in skProductDiscounts) { [pigeonProductDiscounts addObject:[self convertProductDiscountToPigeon:productDiscount]]; @@ -497,7 +497,8 @@ + (nullable SKProductsResponseMessage *)convertProductsResponseToPigeon: return nil; } NSArray *skProducts = productsResponse.products; - NSMutableArray *pigeonProducts = [[NSMutableArray alloc] init]; + NSMutableArray *pigeonProducts = + [NSMutableArray arrayWithCapacity:skProducts.count]; for (SKProduct *product in skProducts) { [pigeonProducts addObject:[self convertProductToPigeon:product]]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m index e4a6b38f3cb7..82366537404b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m @@ -152,12 +152,16 @@ - (void)startProductRequestProductIdentifiers:(NSArray *)productIden error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" message:startProductRequestError.localizedDescription details:startProductRequestError.description]; + completion(nil, error); + return; } if (!response) { 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; } for (SKProduct *product in response.products) { [self.productsCache setObject:product forKey:product.productIdentifier]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 3674d30ec427..d53301e54c39 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -106,6 +106,8 @@ - (void)testPaymentQueueStorefrontReturnsNil { - (void)testGetProductResponse { NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; [self.plugin startProductRequestProductIdentifiers:argument completion:^(SKProductsResponseMessage *_Nullable response, @@ -115,7 +117,9 @@ - (void)testGetProductResponse { XCTAssertEqual(response.products.count, 1); XCTAssertEqual(response.invalidProductIdentifiers.count, 0); XCTAssertEqual(response.products[0].productIdentifier, @"123"); + [expectation fulfill]; }]; + [self waitForExpectations:@[ expectation ] timeout:5]; } - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m index 833e2c931349..0060051dad6a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m @@ -469,6 +469,7 @@ - (void)testSKProductResponseCovertToPigeon { SKProductMessage *productMsg = responseMsg.products[0]; + // These values are being set in productResponseMap in setUp() XCTAssertEqualObjects(productMsg.price, @"1"); XCTAssertEqualObjects(productMsg.productIdentifier, @"123"); XCTAssertEqualObjects(productMsg.localizedTitle, @"title"); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index b4e4707af0f7..dfd8c3083fac 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart @@ -80,9 +80,8 @@ class SkProductResponseWrapper { ); } - @visibleForTesting - /// Convert from [SkProductResponseWrapper] to [SkProductResponseWrapper] + @visibleForTesting static SKProductsResponseMessage convertToPigeon( SkProductResponseWrapper wrapper) { return SKProductsResponseMessage( @@ -133,9 +132,8 @@ enum SKSubscriptionPeriodUnit { } } - @visibleForTesting - /// Convert from [SKSubscriptionPeriodUnit] to [SKSubscriptionPeriodUnitMessage] + @visibleForTesting static SKSubscriptionPeriodUnitMessage convertToPigeon( SKSubscriptionPeriodUnit msg) { switch (msg) { @@ -211,9 +209,8 @@ class SKProductSubscriptionPeriodWrapper { unit: SKSubscriptionPeriodUnit.convertFromPigeon(msg.unit)); } - @visibleForTesting - /// Convert from [SKProductSubscriptionPeriodWrapper] to [SKProductSubscriptionPeriodMessage] + @visibleForTesting static SKProductSubscriptionPeriodMessage convertToPigeon( SKProductSubscriptionPeriodWrapper wrapper) { return SKProductSubscriptionPeriodMessage( @@ -259,9 +256,8 @@ enum SKProductDiscountPaymentMode { } } - @visibleForTesting - /// Convert from [SKProductDiscountPaymentModeMessage] to [SKProductDiscountPaymentMode] + @visibleForTesting static SKProductDiscountPaymentModeMessage convertToPigeon( SKProductDiscountPaymentMode wrapper) { switch (wrapper) { @@ -305,9 +301,8 @@ enum SKProductDiscountType { } } - @visibleForTesting - /// Convert from [SKProductDiscountType] to [SKProductDiscountTypeMessage] + @visibleForTesting static SKProductDiscountTypeMessage convertToPigeon( SKProductDiscountType wrapper) { switch (wrapper) { @@ -416,9 +411,8 @@ class SKProductDiscountWrapper { type: SKProductDiscountType.convertFromPigeon(msg.type)); } - @visibleForTesting - /// Convert from [SKProductDiscountWrapper] to [SKProductDiscountMessage] + @visibleForTesting static SKProductDiscountMessage convertToPigeon( SKProductDiscountWrapper wrapper) { return SKProductDiscountMessage( @@ -665,9 +659,8 @@ class SKPriceLocaleWrapper { countryCode: msg.countryCode); } - @visibleForTesting - /// Convert from [SKPriceLocaleWrapper] to [SKPriceLocaleMessage] + @visibleForTesting static SKPriceLocaleMessage convertToPigeon(SKPriceLocaleWrapper msg) { return SKPriceLocaleMessage( currencySymbol: msg.currencySymbol, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index f20a111f0dbf..9ada32ed2e0d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -22,15 +22,10 @@ class SKPaymentTransactionMessage { }); final SKPaymentMessage payment; - final SKPaymentTransactionStateMessage transactionState; - final SKPaymentTransactionMessage? originalTransaction; - final double? transactionTimeStamp; - final String? transactionIdentifier; - final SKErrorMessage? error; } @@ -83,15 +78,10 @@ class SKPaymentMessage { }); final String productIdentifier; - final String? applicationUsername; - final String? requestData; - final int quantity; - final bool simulatesAskToBuyInSandbox; - final SKPaymentDiscountMessage? paymentDiscount; } @@ -228,11 +218,8 @@ class SKProductSubscriptionPeriodMessage { enum SKSubscriptionPeriodUnitMessage { day, - week, - month, - year, } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index ba1f7acc978e..48db0847bf30 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -204,9 +204,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { } @override - void presentCodeRedemptionSheet() { - return; - } + void presentCodeRedemptionSheet() {} @override void restoreTransactions(String? applicationUserName) { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/pigeon_converter_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/pigeon_converter_test.dart new file mode 100644 index 000000000000..36881eb07986 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/pigeon_converter_test.dart @@ -0,0 +1,98 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:in_app_purchase_storekit/src/messages.g.dart'; +import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; + +void main() { + final SKPriceLocaleWrapper locale = SKPriceLocaleWrapper( + currencySymbol: r'$', currencyCode: 'USD', countryCode: 'USA'); + + final SKProductSubscriptionPeriodWrapper subPeriod = + SKProductSubscriptionPeriodWrapper( + numberOfUnits: 1, unit: SKSubscriptionPeriodUnit.month); + + final SKProductDiscountWrapper discount = SKProductDiscountWrapper( + price: '0.99', + priceLocale: locale, + numberOfPeriods: 1, + paymentMode: SKProductDiscountPaymentMode.payUpFront, + subscriptionPeriod: subPeriod, + identifier: 'discount', + type: SKProductDiscountType.subscription); + + final SKProductWrapper product = SKProductWrapper( + productIdentifier: 'fake_product', + localizedTitle: 'title', + localizedDescription: 'description', + priceLocale: locale, + price: '3.99', + subscriptionGroupIdentifier: 'sub_group', + discounts: [discount]); + + final SkProductResponseWrapper productResponse = SkProductResponseWrapper( + products: [product], + invalidProductIdentifiers: const ['invalid_identifier']); + + test('test SKPriceLocale pigeon converters', () { + final SKPriceLocaleMessage msg = + SKPriceLocaleWrapper.convertToPigeon(locale); + expect(msg.currencySymbol, r'$'); + expect(msg.currencyCode, 'USD'); + expect(msg.countryCode, 'USA'); + + final SKPriceLocaleWrapper convertedWrapper = + SKPriceLocaleWrapper.convertFromPigeon(msg); + expect(convertedWrapper, locale); + }); + + test('test SKProductSubscription pigeon converters', () { + final SKProductSubscriptionPeriodMessage msg = + SKProductSubscriptionPeriodWrapper.convertToPigeon(subPeriod); + expect(msg.unit, SKSubscriptionPeriodUnitMessage.month); + expect(msg.numberOfUnits, 1); + final SKProductSubscriptionPeriodWrapper convertedWrapper = + SKProductSubscriptionPeriodWrapper.convertFromPigeon(msg); + expect(convertedWrapper, subPeriod); + }); + + test('test SKProductDiscount pigeon converters', () { + final SKProductDiscountMessage msg = + SKProductDiscountWrapper.convertToPigeon(discount); + expect(msg.price, '0.99'); + expect(msg.numberOfPeriods, 1); + expect(msg.paymentMode, SKProductDiscountPaymentModeMessage.payUpFront); + expect(msg.identifier, 'discount'); + expect(msg.type, SKProductDiscountTypeMessage.subscription); + + final SKProductDiscountWrapper convertedWrapper = + SKProductDiscountWrapper.convertFromPigeon(msg); + expect(convertedWrapper, discount); + }); + + test('test SKProduct pigeon converters', () { + final SKProductMessage msg = SKProductWrapper.convertToPigeon(product); + expect(msg.productIdentifier, 'fake_product'); + expect(msg.localizedTitle, 'title'); + expect(msg.localizedDescription, 'description'); + expect(msg.price, '3.99'); + expect(msg.discounts?.length, 1); + + final SKProductWrapper convertedWrapper = + SKProductWrapper.convertFromPigeon(msg); + expect(convertedWrapper, product); + }); + + test('test SKProductResponse pigeon converters', () { + final SKProductsResponseMessage msg = + SkProductResponseWrapper.convertToPigeon(productResponse); + expect(msg.products?.length, 1); + expect(msg.invalidProductIdentifiers, ['invalid_identifier']); + + final SkProductResponseWrapper convertedWrapper = + SkProductResponseWrapper.convertFromPigeon(msg); + expect(convertedWrapper, productResponse); + }); +} From 26681aa7225c9b003ddfd47b6923ec1824dd3a01 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Fri, 9 Feb 2024 14:47:13 -0800 Subject: [PATCH 11/12] remove init --- .../darwin/Classes/FIAObjectTranslator.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m index 5af57671c2ce..e2795b3a55ba 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m @@ -470,7 +470,7 @@ + (nullable SKProductMessage *)convertProductToPigeon:(nullable SKProduct *)prod NSArray *skProductDiscounts = product.discounts; NSMutableArray *pigeonProductDiscounts = - [[NSMutableArray arrayWithCapacity:skProductDiscounts.count] init]; + [NSMutableArray arrayWithCapacity:skProductDiscounts.count]; for (SKProductDiscount *productDiscount in skProductDiscounts) { [pigeonProductDiscounts addObject:[self convertProductDiscountToPigeon:productDiscount]]; From 0cb73cf2c53873d6e2fcec3594b8e29fef80303a Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Mon, 12 Feb 2024 18:15:44 -0800 Subject: [PATCH 12/12] add nullable to skerror --- .../darwin/Classes/FIAObjectTranslator.h | 2 +- .../darwin/Classes/FIAObjectTranslator.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h index a0c4c0b58256..ffb0f1fe9588 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h @@ -69,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN + (nullable SKPaymentMessage *)convertPaymentToPigeon:(nullable SKPayment *)payment API_AVAILABLE(ios(12.2)); -+ (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error; ++ (nullable SKErrorMessage *)convertSKErrorToPigeon:(nullable NSError *)error; + (nullable SKProductsResponseMessage *)convertProductsResponseToPigeon: (nullable SKProductsResponse *)payment; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m index e2795b3a55ba..b8c6a269b8ce 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m @@ -306,7 +306,7 @@ + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: return msg; } -+ (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error { ++ (nullable SKErrorMessage *)convertSKErrorToPigeon:(nullable NSError *)error { if (!error) { return nil; }