From 451365d6cceb971a89602d29d0389558635f1483 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 3 Jan 2024 11:22:35 -0800 Subject: [PATCH 01/31] wip --- .../pigeons/messages.dart | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart 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 new file mode 100644 index 00000000000..f99894e929f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -0,0 +1,57 @@ +// 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:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + objcHeaderOut: 'darwin/Classes/messages.g.h', + objcSourceOut: 'darwin/Classes/messages.g.m', + copyrightHeader: 'pigeons/copyright.txt', +)) + +/// Possible outcomes of launching a URL. +enum LaunchResult { + /// The URL was successfully launched (or could be, for `canLaunchUrl`). + success, + + /// There was no handler available for the URL. + failure, + + /// The URL could not be launched because it is invalid. + invalidUrl, +} + +/// Possible outcomes of handling a URL within the application. +enum InAppLoadResult { + /// The URL was successfully loaded. + success, + + /// The URL did not load successfully. + failedToLoad, + + /// The URL could not be launched because it is invalid. + invalidUrl, +} + +@HostApi() +abstract class UrlLauncherApi { + /// Checks whether a URL can be loaded. + @ObjCSelector('canLaunchURL:') + LaunchResult canLaunchUrl(String url); + + /// Opens the URL externally, returning the status of launching it. + @async + @ObjCSelector('launchURL:universalLinksOnly:') + LaunchResult launchUrl(String url, bool universalLinksOnly); + + /// Opens the URL in an in-app SFSafariViewController, returning the results + /// of loading it. + @async + @ObjCSelector('openSafariViewControllerWithURL:') + InAppLoadResult openUrlInSafariViewController(String url); + + /// Closes the view controller opened by [openUrlInSafariViewController]. + void closeSafariViewController(); +} \ No newline at end of file From 942a623c680aadb3c8e4220d566191bd2908bdd1 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 3 Jan 2024 14:18:28 -0800 Subject: [PATCH 02/31] wip --- .../ios/Runner.xcodeproj/project.pbxproj | 14 +- .../darwin/Classes/messages.g.h | 142 +++++++ .../darwin/Classes/messages.g.m | 376 ++++++++++++++++++ .../lib/src/messages.g.dart | 373 +++++++++++++++++ .../pigeons/copyright.txt | 0 .../pigeons/messages.dart | 161 ++++++-- .../in_app_purchase_storekit/pubspec.yaml | 1 + 7 files changed, 1028 insertions(+), 39 deletions(-) create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/pigeons/copyright.txt diff --git a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj index 8b83bba9670..b1639e4889f 100644 --- a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj @@ -418,14 +418,17 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -442,14 +445,17 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", 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 new file mode 100644 index 00000000000..e191d96d3c3 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -0,0 +1,142 @@ +// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#import + +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, PaymentTransactionStateWrapper) { + /// Indicates the transaction is being processed in App Store. + /// + /// You should update your UI to indicate that you are waiting for the + /// transaction to update to another state. Never complete a transaction that + /// is still in a purchasing state. + PaymentTransactionStateWrapperPurchasing = 0, + /// The user's payment has been succesfully processed. + /// + /// You should provide the user the content that they purchased. + PaymentTransactionStateWrapperPurchased = 1, + /// The transaction failed. + /// + /// Check the [PaymentTransactionWrapper.error] property from + /// [PaymentTransactionWrapper] for details. + PaymentTransactionStateWrapperFailed = 2, + /// This transaction is restoring content previously purchased by the user. + /// + /// The previous transaction information can be obtained in + /// [PaymentTransactionWrapper.originalTransaction] from + /// [PaymentTransactionWrapper]. + PaymentTransactionStateWrapperRestored = 3, + /// 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. + PaymentTransactionStateWrapperDeferred = 4, + /// Indicates the transaction is in an unspecified state. + PaymentTransactionStateWrapperUnspecified = 5, +}; + +/// Wrapper for PaymentTransactionStateWrapper to allow for nullability. +@interface PaymentTransactionStateWrapperBox : NSObject +@property(nonatomic, assign) PaymentTransactionStateWrapper value; +- (instancetype)initWithValue:(PaymentTransactionStateWrapper)value; +@end + +@class PaymentTransactionWrapper; +@class PaymentWrapper; +@class ErrorWrapper; +@class PaymentDiscountWrapper; +@class StorefrontWrapper; + +@interface PaymentTransactionWrapper : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPayment:(PaymentWrapper *)payment + transactionState:(PaymentTransactionStateWrapper)transactionState + originalTransaction:(nullable PaymentTransactionWrapper *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable ErrorWrapper *)error; +@property(nonatomic, strong) PaymentWrapper * payment; +@property(nonatomic, assign) PaymentTransactionStateWrapper transactionState; +@property(nonatomic, strong, nullable) PaymentTransactionWrapper * originalTransaction; +@property(nonatomic, strong, nullable) NSNumber * transactionTimeStamp; +@property(nonatomic, copy, nullable) NSString * transactionIdentifier; +@property(nonatomic, strong, nullable) ErrorWrapper * error; +@end + +@interface PaymentWrapper : 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:(NSNumber *)quantity + simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + paymentDiscount:(nullable PaymentDiscountWrapper *)paymentDiscount; +@property(nonatomic, copy) NSString * productIdentifier; +@property(nonatomic, copy, nullable) NSString * applicationUsername; +@property(nonatomic, copy, nullable) NSString * requestData; +@property(nonatomic, strong) NSNumber * quantity; +@property(nonatomic, strong) NSNumber * simulatesAskToBuyInSandbox; +@property(nonatomic, strong, nullable) PaymentDiscountWrapper * paymentDiscount; +@end + +@interface ErrorWrapper : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithCode:(NSNumber *)code + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo; +@property(nonatomic, strong) NSNumber * code; +@property(nonatomic, copy) NSString * domain; +@property(nonatomic, strong) NSDictionary * userInfo; +@end + +@interface PaymentDiscountWrapper : 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:(NSNumber *)timestamp; +@property(nonatomic, copy) NSString * identifier; +@property(nonatomic, copy) NSString * keyIdentifier; +@property(nonatomic, copy) NSString * nonce; +@property(nonatomic, copy) NSString * signature; +@property(nonatomic, strong) NSNumber * timestamp; +@end + +@interface StorefrontWrapper : 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; +@end + +/// The codec used by InAppPurchaseAPI. +NSObject *InAppPurchaseAPIGetCodec(void); + +@protocol InAppPurchaseAPI +/// Returns if the current device is able to make payments +/// +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)canMakePayments:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSArray *)transactions:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSArray *)storefront:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void InAppPurchaseAPISetup(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 new file mode 100644 index 00000000000..d44a019f9e0 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -0,0 +1,376 @@ +// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#import "messages.g.h" + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +@implementation PaymentTransactionStateWrapperBox +- (instancetype)initWithValue:(PaymentTransactionStateWrapper)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +static NSArray *wrapResult(id result, FlutterError *error) { + if (error) { + return @[ + error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] + ]; + } + return @[ result ?: [NSNull null] ]; +} +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface PaymentTransactionWrapper () ++ (PaymentTransactionWrapper *)fromList:(NSArray *)list; ++ (nullable PaymentTransactionWrapper *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface PaymentWrapper () ++ (PaymentWrapper *)fromList:(NSArray *)list; ++ (nullable PaymentWrapper *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface ErrorWrapper () ++ (ErrorWrapper *)fromList:(NSArray *)list; ++ (nullable ErrorWrapper *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface PaymentDiscountWrapper () ++ (PaymentDiscountWrapper *)fromList:(NSArray *)list; ++ (nullable PaymentDiscountWrapper *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface StorefrontWrapper () ++ (StorefrontWrapper *)fromList:(NSArray *)list; ++ (nullable StorefrontWrapper *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@implementation PaymentTransactionWrapper ++ (instancetype)makeWithPayment:(PaymentWrapper *)payment + transactionState:(PaymentTransactionStateWrapper)transactionState + originalTransaction:(nullable PaymentTransactionWrapper *)originalTransaction + transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp + transactionIdentifier:(nullable NSString *)transactionIdentifier + error:(nullable ErrorWrapper *)error { + PaymentTransactionWrapper* pigeonResult = [[PaymentTransactionWrapper alloc] init]; + pigeonResult.payment = payment; + pigeonResult.transactionState = transactionState; + pigeonResult.originalTransaction = originalTransaction; + pigeonResult.transactionTimeStamp = transactionTimeStamp; + pigeonResult.transactionIdentifier = transactionIdentifier; + pigeonResult.error = error; + return pigeonResult; +} ++ (PaymentTransactionWrapper *)fromList:(NSArray *)list { + PaymentTransactionWrapper *pigeonResult = [[PaymentTransactionWrapper alloc] init]; + pigeonResult.payment = [PaymentWrapper nullableFromList:(GetNullableObjectAtIndex(list, 0))]; + NSAssert(pigeonResult.payment != nil, @""); + pigeonResult.transactionState = [GetNullableObjectAtIndex(list, 1) integerValue]; + pigeonResult.originalTransaction = [PaymentTransactionWrapper nullableFromList:(GetNullableObjectAtIndex(list, 2))]; + pigeonResult.transactionTimeStamp = GetNullableObjectAtIndex(list, 3); + pigeonResult.transactionIdentifier = GetNullableObjectAtIndex(list, 4); + pigeonResult.error = [ErrorWrapper nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + return pigeonResult; +} ++ (nullable PaymentTransactionWrapper *)nullableFromList:(NSArray *)list { + return (list) ? [PaymentTransactionWrapper fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.payment ? [self.payment toList] : [NSNull null]), + @(self.transactionState), + (self.originalTransaction ? [self.originalTransaction toList] : [NSNull null]), + (self.transactionTimeStamp ?: [NSNull null]), + (self.transactionIdentifier ?: [NSNull null]), + (self.error ? [self.error toList] : [NSNull null]), + ]; +} +@end + +@implementation PaymentWrapper ++ (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSNumber *)quantity + simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + paymentDiscount:(nullable PaymentDiscountWrapper *)paymentDiscount { + PaymentWrapper* pigeonResult = [[PaymentWrapper alloc] init]; + pigeonResult.productIdentifier = productIdentifier; + pigeonResult.applicationUsername = applicationUsername; + pigeonResult.requestData = requestData; + pigeonResult.quantity = quantity; + pigeonResult.simulatesAskToBuyInSandbox = simulatesAskToBuyInSandbox; + pigeonResult.paymentDiscount = paymentDiscount; + return pigeonResult; +} ++ (PaymentWrapper *)fromList:(NSArray *)list { + PaymentWrapper *pigeonResult = [[PaymentWrapper alloc] init]; + pigeonResult.productIdentifier = GetNullableObjectAtIndex(list, 0); + NSAssert(pigeonResult.productIdentifier != nil, @""); + pigeonResult.applicationUsername = GetNullableObjectAtIndex(list, 1); + pigeonResult.requestData = GetNullableObjectAtIndex(list, 2); + pigeonResult.quantity = GetNullableObjectAtIndex(list, 3); + NSAssert(pigeonResult.quantity != nil, @""); + pigeonResult.simulatesAskToBuyInSandbox = GetNullableObjectAtIndex(list, 4); + NSAssert(pigeonResult.simulatesAskToBuyInSandbox != nil, @""); + pigeonResult.paymentDiscount = [PaymentDiscountWrapper nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + return pigeonResult; +} ++ (nullable PaymentWrapper *)nullableFromList:(NSArray *)list { + return (list) ? [PaymentWrapper fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.productIdentifier ?: [NSNull null]), + (self.applicationUsername ?: [NSNull null]), + (self.requestData ?: [NSNull null]), + (self.quantity ?: [NSNull null]), + (self.simulatesAskToBuyInSandbox ?: [NSNull null]), + (self.paymentDiscount ? [self.paymentDiscount toList] : [NSNull null]), + ]; +} +@end + +@implementation ErrorWrapper ++ (instancetype)makeWithCode:(NSNumber *)code + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo { + ErrorWrapper* pigeonResult = [[ErrorWrapper alloc] init]; + pigeonResult.code = code; + pigeonResult.domain = domain; + pigeonResult.userInfo = userInfo; + return pigeonResult; +} ++ (ErrorWrapper *)fromList:(NSArray *)list { + ErrorWrapper *pigeonResult = [[ErrorWrapper alloc] init]; + pigeonResult.code = GetNullableObjectAtIndex(list, 0); + NSAssert(pigeonResult.code != nil, @""); + pigeonResult.domain = GetNullableObjectAtIndex(list, 1); + NSAssert(pigeonResult.domain != nil, @""); + pigeonResult.userInfo = GetNullableObjectAtIndex(list, 2); + NSAssert(pigeonResult.userInfo != nil, @""); + return pigeonResult; +} ++ (nullable ErrorWrapper *)nullableFromList:(NSArray *)list { + return (list) ? [ErrorWrapper fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.code ?: [NSNull null]), + (self.domain ?: [NSNull null]), + (self.userInfo ?: [NSNull null]), + ]; +} +@end + +@implementation PaymentDiscountWrapper ++ (instancetype)makeWithIdentifier:(NSString *)identifier + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSNumber *)timestamp { + PaymentDiscountWrapper* pigeonResult = [[PaymentDiscountWrapper alloc] init]; + pigeonResult.identifier = identifier; + pigeonResult.keyIdentifier = keyIdentifier; + pigeonResult.nonce = nonce; + pigeonResult.signature = signature; + pigeonResult.timestamp = timestamp; + return pigeonResult; +} ++ (PaymentDiscountWrapper *)fromList:(NSArray *)list { + PaymentDiscountWrapper *pigeonResult = [[PaymentDiscountWrapper alloc] init]; + pigeonResult.identifier = GetNullableObjectAtIndex(list, 0); + NSAssert(pigeonResult.identifier != nil, @""); + pigeonResult.keyIdentifier = GetNullableObjectAtIndex(list, 1); + NSAssert(pigeonResult.keyIdentifier != nil, @""); + pigeonResult.nonce = GetNullableObjectAtIndex(list, 2); + NSAssert(pigeonResult.nonce != nil, @""); + pigeonResult.signature = GetNullableObjectAtIndex(list, 3); + NSAssert(pigeonResult.signature != nil, @""); + pigeonResult.timestamp = GetNullableObjectAtIndex(list, 4); + NSAssert(pigeonResult.timestamp != nil, @""); + return pigeonResult; +} ++ (nullable PaymentDiscountWrapper *)nullableFromList:(NSArray *)list { + return (list) ? [PaymentDiscountWrapper fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.identifier ?: [NSNull null]), + (self.keyIdentifier ?: [NSNull null]), + (self.nonce ?: [NSNull null]), + (self.signature ?: [NSNull null]), + (self.timestamp ?: [NSNull null]), + ]; +} +@end + +@implementation StorefrontWrapper ++ (instancetype)makeWithCountryCode:(NSString *)countryCode + identifier:(NSString *)identifier { + StorefrontWrapper* pigeonResult = [[StorefrontWrapper alloc] init]; + pigeonResult.countryCode = countryCode; + pigeonResult.identifier = identifier; + return pigeonResult; +} ++ (StorefrontWrapper *)fromList:(NSArray *)list { + StorefrontWrapper *pigeonResult = [[StorefrontWrapper alloc] init]; + pigeonResult.countryCode = GetNullableObjectAtIndex(list, 0); + NSAssert(pigeonResult.countryCode != nil, @""); + pigeonResult.identifier = GetNullableObjectAtIndex(list, 1); + NSAssert(pigeonResult.identifier != nil, @""); + return pigeonResult; +} ++ (nullable StorefrontWrapper *)nullableFromList:(NSArray *)list { + return (list) ? [StorefrontWrapper fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.countryCode ?: [NSNull null]), + (self.identifier ?: [NSNull null]), + ]; +} +@end + +@interface InAppPurchaseAPICodecReader : FlutterStandardReader +@end +@implementation InAppPurchaseAPICodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [ErrorWrapper fromList:[self readValue]]; + case 129: + return [PaymentDiscountWrapper fromList:[self readValue]]; + case 130: + return [PaymentTransactionWrapper fromList:[self readValue]]; + case 131: + return [PaymentWrapper fromList:[self readValue]]; + case 132: + return [StorefrontWrapper fromList:[self readValue]]; + default: + return [super readValueOfType:type]; + } +} +@end + +@interface InAppPurchaseAPICodecWriter : FlutterStandardWriter +@end +@implementation InAppPurchaseAPICodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[ErrorWrapper class]]) { + [self writeByte:128]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[PaymentDiscountWrapper class]]) { + [self writeByte:129]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[PaymentTransactionWrapper class]]) { + [self writeByte:130]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[PaymentWrapper class]]) { + [self writeByte:131]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[StorefrontWrapper class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface InAppPurchaseAPICodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation InAppPurchaseAPICodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[InAppPurchaseAPICodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[InAppPurchaseAPICodecReader alloc] initWithData:data]; +} +@end + +NSObject *InAppPurchaseAPIGetCodec(void) { + static FlutterStandardMessageCodec *sSharedObject = nil; + static dispatch_once_t sPred = 0; + dispatch_once(&sPred, ^{ + InAppPurchaseAPICodecReaderWriter *readerWriter = [[InAppPurchaseAPICodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void InAppPurchaseAPISetup(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" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(canMakePayments:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePayments:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSNumber *output = [api canMakePayments:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(transactions:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactions:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSArray *output = [api transactions:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(storefront:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefront:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSArray *output = [api storefront:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} 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 new file mode 100644 index 00000000000..b02408bb4fa --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart @@ -0,0 +1,373 @@ +// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +enum PaymentTransactionStateWrapper { + /// Indicates the transaction is being processed in App Store. + /// + /// You should update your UI to indicate that you are waiting for the + /// 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, +} + +class PaymentTransactionWrapper { + PaymentTransactionWrapper({ + required this.payment, + required this.transactionState, + this.originalTransaction, + this.transactionTimeStamp, + this.transactionIdentifier, + this.error, + }); + + PaymentWrapper payment; + + PaymentTransactionStateWrapper transactionState; + + PaymentTransactionWrapper? originalTransaction; + + double? transactionTimeStamp; + + String? transactionIdentifier; + + ErrorWrapper? error; + + Object encode() { + return [ + payment.encode(), + transactionState.index, + originalTransaction?.encode(), + transactionTimeStamp, + transactionIdentifier, + error?.encode(), + ]; + } + + static PaymentTransactionWrapper decode(Object result) { + result as List; + return PaymentTransactionWrapper( + payment: PaymentWrapper.decode(result[0]! as List), + transactionState: PaymentTransactionStateWrapper.values[result[1]! as int], + originalTransaction: result[2] != null + ? PaymentTransactionWrapper.decode(result[2]! as List) + : null, + transactionTimeStamp: result[3] as double?, + transactionIdentifier: result[4] as String?, + error: result[5] != null + ? ErrorWrapper.decode(result[5]! as List) + : null, + ); + } +} + +class PaymentWrapper { + PaymentWrapper({ + required this.productIdentifier, + this.applicationUsername, + this.requestData, + required this.quantity, + required this.simulatesAskToBuyInSandbox, + this.paymentDiscount, + }); + + String productIdentifier; + + String? applicationUsername; + + String? requestData; + + int quantity; + + bool simulatesAskToBuyInSandbox; + + PaymentDiscountWrapper? paymentDiscount; + + Object encode() { + return [ + productIdentifier, + applicationUsername, + requestData, + quantity, + simulatesAskToBuyInSandbox, + paymentDiscount?.encode(), + ]; + } + + static PaymentWrapper decode(Object result) { + result as List; + return PaymentWrapper( + productIdentifier: result[0]! as String, + applicationUsername: result[1] as String?, + requestData: result[2] as String?, + quantity: result[3]! as int, + simulatesAskToBuyInSandbox: result[4]! as bool, + paymentDiscount: result[5] != null + ? PaymentDiscountWrapper.decode(result[5]! as List) + : null, + ); + } +} + +class ErrorWrapper { + ErrorWrapper({ + required this.code, + required this.domain, + required this.userInfo, + }); + + int code; + + String domain; + + Map userInfo; + + Object encode() { + return [ + code, + domain, + userInfo, + ]; + } + + static ErrorWrapper decode(Object result) { + result as List; + return ErrorWrapper( + code: result[0]! as int, + domain: result[1]! as String, + userInfo: (result[2] as Map?)!.cast(), + ); + } +} + +class PaymentDiscountWrapper { + PaymentDiscountWrapper({ + required this.identifier, + required this.keyIdentifier, + required this.nonce, + required this.signature, + required this.timestamp, + }); + + String identifier; + + String keyIdentifier; + + String nonce; + + String signature; + + int timestamp; + + Object encode() { + return [ + identifier, + keyIdentifier, + nonce, + signature, + timestamp, + ]; + } + + static PaymentDiscountWrapper decode(Object result) { + result as List; + return PaymentDiscountWrapper( + identifier: result[0]! as String, + keyIdentifier: result[1]! as String, + nonce: result[2]! as String, + signature: result[3]! as String, + timestamp: result[4]! as int, + ); + } +} + +class StorefrontWrapper { + StorefrontWrapper({ + required this.countryCode, + required this.identifier, + }); + + String countryCode; + + String identifier; + + Object encode() { + return [ + countryCode, + identifier, + ]; + } + + static StorefrontWrapper decode(Object result) { + result as List; + return StorefrontWrapper( + countryCode: result[0]! as String, + identifier: result[1]! as String, + ); + } +} + +class _InAppPurchaseAPICodec extends StandardMessageCodec { + const _InAppPurchaseAPICodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is ErrorWrapper) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is PaymentDiscountWrapper) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is PaymentTransactionWrapper) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is PaymentWrapper) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is StorefrontWrapper) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return ErrorWrapper.decode(readValue(buffer)!); + case 129: + return PaymentDiscountWrapper.decode(readValue(buffer)!); + case 130: + return PaymentTransactionWrapper.decode(readValue(buffer)!); + case 131: + return PaymentWrapper.decode(readValue(buffer)!); + case 132: + return StorefrontWrapper.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class InAppPurchaseAPI { + /// Constructor for [InAppPurchaseAPI]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + InAppPurchaseAPI({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _InAppPurchaseAPICodec(); + + /// Returns if the current device is able to make payments + Future canMakePayments() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send(null) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as bool?)!; + } + } + + Future> transactions() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send(null) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as List?)!.cast(); + } + } + + Future> storefront() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send(null) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as List?)!.cast(); + } + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/copyright.txt b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/copyright.txt new file mode 100644 index 00000000000..e69de29bb2d 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 f99894e929f..12c6839341a 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 @@ -11,47 +11,138 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', )) -/// Possible outcomes of launching a URL. -enum LaunchResult { - /// The URL was successfully launched (or could be, for `canLaunchUrl`). - success, +class PaymentTransactionWrapper { + PaymentTransactionWrapper({ + required this.payment, + required this.transactionState, + this.originalTransaction, + this.transactionTimeStamp, + this.transactionIdentifier, + this.error, + }); - /// There was no handler available for the URL. - failure, + final PaymentWrapper payment; - /// The URL could not be launched because it is invalid. - invalidUrl, + final PaymentTransactionStateWrapper transactionState; + + final PaymentTransactionWrapper? originalTransaction; + + final double? transactionTimeStamp; + + final String? transactionIdentifier; + + final ErrorWrapper? error; } -/// Possible outcomes of handling a URL within the application. -enum InAppLoadResult { - /// The URL was successfully loaded. - success, +enum PaymentTransactionStateWrapper { + /// Indicates the transaction is being processed in App Store. + /// + /// You should update your UI to indicate that you are waiting for the + /// 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, +} + +class PaymentWrapper { + /// Creates a new [SKPaymentWrapper] with the provided information. + const PaymentWrapper({ + required this.productIdentifier, + this.applicationUsername, + this.requestData, + this.quantity = 1, + this.simulatesAskToBuyInSandbox = false, + this.paymentDiscount, + }); + + final String productIdentifier; + + final String? applicationUsername; + + final String? requestData; + + // default value is 0? + final int quantity; - /// The URL did not load successfully. - failedToLoad, + final bool simulatesAskToBuyInSandbox; - /// The URL could not be launched because it is invalid. - invalidUrl, + final PaymentDiscountWrapper? paymentDiscount; +} + +class ErrorWrapper { + // a lot of comparison operators are overriden in this class - do i add them here? + const ErrorWrapper( + {required this.code, required this.domain, required this.userInfo}); + + final int code; + final String domain; + final Map userInfo; +} + +class PaymentDiscountWrapper { + const PaymentDiscountWrapper({ + required this.identifier, + required this.keyIdentifier, + required this.nonce, + required this.signature, + required this.timestamp, + }); + + final String identifier; + final String keyIdentifier; + final String nonce; + final String signature; + final int timestamp; +} + +class StorefrontWrapper { + StorefrontWrapper({ + required this.countryCode, + required this.identifier, + }); + + final String countryCode; + final String identifier; } @HostApi() -abstract class UrlLauncherApi { - /// Checks whether a URL can be loaded. - @ObjCSelector('canLaunchURL:') - LaunchResult canLaunchUrl(String url); - - /// Opens the URL externally, returning the status of launching it. - @async - @ObjCSelector('launchURL:universalLinksOnly:') - LaunchResult launchUrl(String url, bool universalLinksOnly); - - /// Opens the URL in an in-app SFSafariViewController, returning the results - /// of loading it. - @async - @ObjCSelector('openSafariViewControllerWithURL:') - InAppLoadResult openUrlInSafariViewController(String url); - - /// Closes the view controller opened by [openUrlInSafariViewController]. - void closeSafariViewController(); -} \ No newline at end of file +abstract class InAppPurchaseAPI { + /// Returns if the current device is able to make payments + @ObjCSelector('canMakePayments') + bool canMakePayments(); + + @ObjCSelector('transactions') + List transactions(); + + @ObjCSelector('storefront') + List storefront(); +} + + 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 21801b3b05d..81f9177798f 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -32,6 +32,7 @@ dev_dependencies: sdk: flutter json_serializable: ^6.0.0 test: ^1.16.0 + pigeon: ^11.0.1 topics: - in-app-purchase From 90ff466ce2bd1ac531331b0e432e9ce00136a925 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 10 Jan 2024 15:46:03 -0800 Subject: [PATCH 03/31] Add pigeon converters --- .../darwin/Classes/messages.g.h | 68 ++++----- .../darwin/Classes/messages.g.m | 144 +++++++++--------- .../lib/src/messages.g.dart | 90 +++++------ .../sk_payment_queue_wrapper.dart | 57 +++++-- .../sk_payment_transaction_wrappers.dart | 32 +++- .../sk_storefront_wrapper.dart | 10 ++ .../pigeons/messages.dart | 42 ++--- 7 files changed, 258 insertions(+), 185 deletions(-) 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 e191d96d3c3..97a932128ca 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 @@ -10,68 +10,68 @@ NS_ASSUME_NONNULL_BEGIN -typedef NS_ENUM(NSUInteger, PaymentTransactionStateWrapper) { +typedef NS_ENUM(NSUInteger, SKPaymentTransactionStateMessage) { /// Indicates the transaction is being processed in App Store. /// /// You should update your UI to indicate that you are waiting for the /// transaction to update to another state. Never complete a transaction that /// is still in a purchasing state. - PaymentTransactionStateWrapperPurchasing = 0, + SKPaymentTransactionStateMessagePurchasing = 0, /// The user's payment has been succesfully processed. /// /// You should provide the user the content that they purchased. - PaymentTransactionStateWrapperPurchased = 1, + SKPaymentTransactionStateMessagePurchased = 1, /// The transaction failed. /// /// Check the [PaymentTransactionWrapper.error] property from /// [PaymentTransactionWrapper] for details. - PaymentTransactionStateWrapperFailed = 2, + SKPaymentTransactionStateMessageFailed = 2, /// This transaction is restoring content previously purchased by the user. /// /// The previous transaction information can be obtained in /// [PaymentTransactionWrapper.originalTransaction] from /// [PaymentTransactionWrapper]. - PaymentTransactionStateWrapperRestored = 3, + SKPaymentTransactionStateMessageRestored = 3, /// 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. - PaymentTransactionStateWrapperDeferred = 4, + SKPaymentTransactionStateMessageDeferred = 4, /// Indicates the transaction is in an unspecified state. - PaymentTransactionStateWrapperUnspecified = 5, + SKPaymentTransactionStateMessageUnspecified = 5, }; -/// Wrapper for PaymentTransactionStateWrapper to allow for nullability. -@interface PaymentTransactionStateWrapperBox : NSObject -@property(nonatomic, assign) PaymentTransactionStateWrapper value; -- (instancetype)initWithValue:(PaymentTransactionStateWrapper)value; +/// Wrapper for SKPaymentTransactionStateMessage to allow for nullability. +@interface SKPaymentTransactionStateMessageBox : NSObject +@property(nonatomic, assign) SKPaymentTransactionStateMessage value; +- (instancetype)initWithValue:(SKPaymentTransactionStateMessage)value; @end -@class PaymentTransactionWrapper; -@class PaymentWrapper; -@class ErrorWrapper; -@class PaymentDiscountWrapper; -@class StorefrontWrapper; +@class SKPaymentTransactionMessage; +@class SKPaymentMessage; +@class SKErrorMessage; +@class SKPaymentDiscountMessage; +@class SKStorefrontMessage; -@interface PaymentTransactionWrapper : NSObject +@interface SKPaymentTransactionMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithPayment:(PaymentWrapper *)payment - transactionState:(PaymentTransactionStateWrapper)transactionState - originalTransaction:(nullable PaymentTransactionWrapper *)originalTransaction ++ (instancetype)makeWithPayment:(SKPaymentMessage *)payment + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable ErrorWrapper *)error; -@property(nonatomic, strong) PaymentWrapper * payment; -@property(nonatomic, assign) PaymentTransactionStateWrapper transactionState; -@property(nonatomic, strong, nullable) PaymentTransactionWrapper * originalTransaction; + 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) ErrorWrapper * error; +@property(nonatomic, strong, nullable) SKErrorMessage * error; @end -@interface PaymentWrapper : NSObject +@interface SKPaymentMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier @@ -79,16 +79,16 @@ typedef NS_ENUM(NSUInteger, PaymentTransactionStateWrapper) { requestData:(nullable NSString *)requestData quantity:(NSNumber *)quantity simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox - paymentDiscount:(nullable PaymentDiscountWrapper *)paymentDiscount; + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; @property(nonatomic, copy) NSString * productIdentifier; @property(nonatomic, copy, nullable) NSString * applicationUsername; @property(nonatomic, copy, nullable) NSString * requestData; @property(nonatomic, strong) NSNumber * quantity; @property(nonatomic, strong) NSNumber * simulatesAskToBuyInSandbox; -@property(nonatomic, strong, nullable) PaymentDiscountWrapper * paymentDiscount; +@property(nonatomic, strong, nullable) SKPaymentDiscountMessage * paymentDiscount; @end -@interface ErrorWrapper : NSObject +@interface SKErrorMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithCode:(NSNumber *)code @@ -99,7 +99,7 @@ typedef NS_ENUM(NSUInteger, PaymentTransactionStateWrapper) { @property(nonatomic, strong) NSDictionary * userInfo; @end -@interface PaymentDiscountWrapper : NSObject +@interface SKPaymentDiscountMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithIdentifier:(NSString *)identifier @@ -114,7 +114,7 @@ typedef NS_ENUM(NSUInteger, PaymentTransactionStateWrapper) { @property(nonatomic, strong) NSNumber * timestamp; @end -@interface StorefrontWrapper : NSObject +@interface SKStorefrontMessage : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithCountryCode:(NSString *)countryCode @@ -130,11 +130,11 @@ NSObject *InAppPurchaseAPIGetCodec(void); /// Returns if the current device is able to make payments /// /// @return `nil` only when `error != nil`. -- (nullable NSNumber *)canMakePayments:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSNumber *)canMakePaymentsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSArray *)transactions:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSArray *)transactionsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSArray *)storefront:(FlutterError *_Nullable *_Nonnull)error; +- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error; @end extern void InAppPurchaseAPISetup(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 d44a019f9e0..cc620b579eb 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 @@ -13,8 +13,8 @@ #error File requires ARC to be enabled. #endif -@implementation PaymentTransactionStateWrapperBox -- (instancetype)initWithValue:(PaymentTransactionStateWrapper)value { +@implementation SKPaymentTransactionStateMessageBox +- (instancetype)initWithValue:(SKPaymentTransactionStateMessage)value { self = [super init]; if (self) { _value = value; @@ -36,44 +36,44 @@ static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { return (result == [NSNull null]) ? nil : result; } -@interface PaymentTransactionWrapper () -+ (PaymentTransactionWrapper *)fromList:(NSArray *)list; -+ (nullable PaymentTransactionWrapper *)nullableFromList:(NSArray *)list; +@interface SKPaymentTransactionMessage () ++ (SKPaymentTransactionMessage *)fromList:(NSArray *)list; ++ (nullable SKPaymentTransactionMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@interface PaymentWrapper () -+ (PaymentWrapper *)fromList:(NSArray *)list; -+ (nullable PaymentWrapper *)nullableFromList:(NSArray *)list; +@interface SKPaymentMessage () ++ (SKPaymentMessage *)fromList:(NSArray *)list; ++ (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@interface ErrorWrapper () -+ (ErrorWrapper *)fromList:(NSArray *)list; -+ (nullable ErrorWrapper *)nullableFromList:(NSArray *)list; +@interface SKErrorMessage () ++ (SKErrorMessage *)fromList:(NSArray *)list; ++ (nullable SKErrorMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@interface PaymentDiscountWrapper () -+ (PaymentDiscountWrapper *)fromList:(NSArray *)list; -+ (nullable PaymentDiscountWrapper *)nullableFromList:(NSArray *)list; +@interface SKPaymentDiscountMessage () ++ (SKPaymentDiscountMessage *)fromList:(NSArray *)list; ++ (nullable SKPaymentDiscountMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@interface StorefrontWrapper () -+ (StorefrontWrapper *)fromList:(NSArray *)list; -+ (nullable StorefrontWrapper *)nullableFromList:(NSArray *)list; +@interface SKStorefrontMessage () ++ (SKStorefrontMessage *)fromList:(NSArray *)list; ++ (nullable SKStorefrontMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@implementation PaymentTransactionWrapper -+ (instancetype)makeWithPayment:(PaymentWrapper *)payment - transactionState:(PaymentTransactionStateWrapper)transactionState - originalTransaction:(nullable PaymentTransactionWrapper *)originalTransaction +@implementation SKPaymentTransactionMessage ++ (instancetype)makeWithPayment:(SKPaymentMessage *)payment + transactionState:(SKPaymentTransactionStateMessage)transactionState + originalTransaction:(nullable SKPaymentTransactionMessage *)originalTransaction transactionTimeStamp:(nullable NSNumber *)transactionTimeStamp transactionIdentifier:(nullable NSString *)transactionIdentifier - error:(nullable ErrorWrapper *)error { - PaymentTransactionWrapper* pigeonResult = [[PaymentTransactionWrapper alloc] init]; + error:(nullable SKErrorMessage *)error { + SKPaymentTransactionMessage* pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = payment; pigeonResult.transactionState = transactionState; pigeonResult.originalTransaction = originalTransaction; @@ -82,19 +82,19 @@ + (instancetype)makeWithPayment:(PaymentWrapper *)payment pigeonResult.error = error; return pigeonResult; } -+ (PaymentTransactionWrapper *)fromList:(NSArray *)list { - PaymentTransactionWrapper *pigeonResult = [[PaymentTransactionWrapper alloc] init]; - pigeonResult.payment = [PaymentWrapper nullableFromList:(GetNullableObjectAtIndex(list, 0))]; ++ (SKPaymentTransactionMessage *)fromList:(NSArray *)list { + SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; + pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; NSAssert(pigeonResult.payment != nil, @""); pigeonResult.transactionState = [GetNullableObjectAtIndex(list, 1) integerValue]; - pigeonResult.originalTransaction = [PaymentTransactionWrapper nullableFromList:(GetNullableObjectAtIndex(list, 2))]; + pigeonResult.originalTransaction = [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; pigeonResult.transactionTimeStamp = GetNullableObjectAtIndex(list, 3); pigeonResult.transactionIdentifier = GetNullableObjectAtIndex(list, 4); - pigeonResult.error = [ErrorWrapper nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.error = [SKErrorMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } -+ (nullable PaymentTransactionWrapper *)nullableFromList:(NSArray *)list { - return (list) ? [PaymentTransactionWrapper fromList:list] : nil; ++ (nullable SKPaymentTransactionMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKPaymentTransactionMessage fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -108,14 +108,14 @@ - (NSArray *)toList { } @end -@implementation PaymentWrapper +@implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier applicationUsername:(nullable NSString *)applicationUsername requestData:(nullable NSString *)requestData quantity:(NSNumber *)quantity simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox - paymentDiscount:(nullable PaymentDiscountWrapper *)paymentDiscount { - PaymentWrapper* pigeonResult = [[PaymentWrapper alloc] init]; + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { + SKPaymentMessage* pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.applicationUsername = applicationUsername; pigeonResult.requestData = requestData; @@ -124,8 +124,8 @@ + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier pigeonResult.paymentDiscount = paymentDiscount; return pigeonResult; } -+ (PaymentWrapper *)fromList:(NSArray *)list { - PaymentWrapper *pigeonResult = [[PaymentWrapper alloc] init]; ++ (SKPaymentMessage *)fromList:(NSArray *)list { + SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = GetNullableObjectAtIndex(list, 0); NSAssert(pigeonResult.productIdentifier != nil, @""); pigeonResult.applicationUsername = GetNullableObjectAtIndex(list, 1); @@ -134,11 +134,11 @@ + (PaymentWrapper *)fromList:(NSArray *)list { NSAssert(pigeonResult.quantity != nil, @""); pigeonResult.simulatesAskToBuyInSandbox = GetNullableObjectAtIndex(list, 4); NSAssert(pigeonResult.simulatesAskToBuyInSandbox != nil, @""); - pigeonResult.paymentDiscount = [PaymentDiscountWrapper nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } -+ (nullable PaymentWrapper *)nullableFromList:(NSArray *)list { - return (list) ? [PaymentWrapper fromList:list] : nil; ++ (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKPaymentMessage fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -152,18 +152,18 @@ - (NSArray *)toList { } @end -@implementation ErrorWrapper +@implementation SKErrorMessage + (instancetype)makeWithCode:(NSNumber *)code domain:(NSString *)domain userInfo:(NSDictionary *)userInfo { - ErrorWrapper* pigeonResult = [[ErrorWrapper alloc] init]; + SKErrorMessage* pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; pigeonResult.userInfo = userInfo; return pigeonResult; } -+ (ErrorWrapper *)fromList:(NSArray *)list { - ErrorWrapper *pigeonResult = [[ErrorWrapper alloc] init]; ++ (SKErrorMessage *)fromList:(NSArray *)list { + SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = GetNullableObjectAtIndex(list, 0); NSAssert(pigeonResult.code != nil, @""); pigeonResult.domain = GetNullableObjectAtIndex(list, 1); @@ -172,8 +172,8 @@ + (ErrorWrapper *)fromList:(NSArray *)list { NSAssert(pigeonResult.userInfo != nil, @""); return pigeonResult; } -+ (nullable ErrorWrapper *)nullableFromList:(NSArray *)list { - return (list) ? [ErrorWrapper fromList:list] : nil; ++ (nullable SKErrorMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKErrorMessage fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -184,13 +184,13 @@ - (NSArray *)toList { } @end -@implementation PaymentDiscountWrapper +@implementation SKPaymentDiscountMessage + (instancetype)makeWithIdentifier:(NSString *)identifier keyIdentifier:(NSString *)keyIdentifier nonce:(NSString *)nonce signature:(NSString *)signature timestamp:(NSNumber *)timestamp { - PaymentDiscountWrapper* pigeonResult = [[PaymentDiscountWrapper alloc] init]; + SKPaymentDiscountMessage* pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; pigeonResult.nonce = nonce; @@ -198,8 +198,8 @@ + (instancetype)makeWithIdentifier:(NSString *)identifier pigeonResult.timestamp = timestamp; return pigeonResult; } -+ (PaymentDiscountWrapper *)fromList:(NSArray *)list { - PaymentDiscountWrapper *pigeonResult = [[PaymentDiscountWrapper alloc] init]; ++ (SKPaymentDiscountMessage *)fromList:(NSArray *)list { + SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = GetNullableObjectAtIndex(list, 0); NSAssert(pigeonResult.identifier != nil, @""); pigeonResult.keyIdentifier = GetNullableObjectAtIndex(list, 1); @@ -212,8 +212,8 @@ + (PaymentDiscountWrapper *)fromList:(NSArray *)list { NSAssert(pigeonResult.timestamp != nil, @""); return pigeonResult; } -+ (nullable PaymentDiscountWrapper *)nullableFromList:(NSArray *)list { - return (list) ? [PaymentDiscountWrapper fromList:list] : nil; ++ (nullable SKPaymentDiscountMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKPaymentDiscountMessage fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -226,24 +226,24 @@ - (NSArray *)toList { } @end -@implementation StorefrontWrapper +@implementation SKStorefrontMessage + (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString *)identifier { - StorefrontWrapper* pigeonResult = [[StorefrontWrapper alloc] init]; + SKStorefrontMessage* pigeonResult = [[SKStorefrontMessage alloc] init]; pigeonResult.countryCode = countryCode; pigeonResult.identifier = identifier; return pigeonResult; } -+ (StorefrontWrapper *)fromList:(NSArray *)list { - StorefrontWrapper *pigeonResult = [[StorefrontWrapper alloc] init]; ++ (SKStorefrontMessage *)fromList:(NSArray *)list { + SKStorefrontMessage *pigeonResult = [[SKStorefrontMessage alloc] init]; pigeonResult.countryCode = GetNullableObjectAtIndex(list, 0); NSAssert(pigeonResult.countryCode != nil, @""); pigeonResult.identifier = GetNullableObjectAtIndex(list, 1); NSAssert(pigeonResult.identifier != nil, @""); return pigeonResult; } -+ (nullable StorefrontWrapper *)nullableFromList:(NSArray *)list { - return (list) ? [StorefrontWrapper fromList:list] : nil; ++ (nullable SKStorefrontMessage *)nullableFromList:(NSArray *)list { + return (list) ? [SKStorefrontMessage fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -259,15 +259,15 @@ @implementation InAppPurchaseAPICodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 128: - return [ErrorWrapper fromList:[self readValue]]; + return [SKErrorMessage fromList:[self readValue]]; case 129: - return [PaymentDiscountWrapper fromList:[self readValue]]; + return [SKPaymentDiscountMessage fromList:[self readValue]]; case 130: - return [PaymentTransactionWrapper fromList:[self readValue]]; + return [SKPaymentMessage fromList:[self readValue]]; case 131: - return [PaymentWrapper fromList:[self readValue]]; + return [SKPaymentTransactionMessage fromList:[self readValue]]; case 132: - return [StorefrontWrapper fromList:[self readValue]]; + return [SKStorefrontMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; } @@ -278,19 +278,19 @@ @interface InAppPurchaseAPICodecWriter : FlutterStandardWriter @end @implementation InAppPurchaseAPICodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[ErrorWrapper class]]) { + if ([value isKindOfClass:[SKErrorMessage class]]) { [self writeByte:128]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PaymentDiscountWrapper class]]) { + } else if ([value isKindOfClass:[SKPaymentDiscountMessage class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PaymentTransactionWrapper class]]) { + } else if ([value isKindOfClass:[SKPaymentMessage class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PaymentWrapper class]]) { + } else if ([value isKindOfClass:[SKPaymentTransactionMessage class]]) { [self writeByte:131]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[StorefrontWrapper class]]) { + } else if ([value isKindOfClass:[SKStorefrontMessage class]]) { [self writeByte:132]; [self writeValue:[value toList]]; } else { @@ -329,10 +329,10 @@ void InAppPurchaseAPISetup(id binaryMessenger, NSObject< binaryMessenger:binaryMessenger codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(canMakePayments:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(canMakePayments:)", 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 canMakePayments:&error]; + NSNumber *output = [api canMakePaymentsWithError:&error]; callback(wrapResult(output, error)); }]; } else { @@ -346,10 +346,10 @@ void InAppPurchaseAPISetup(id binaryMessenger, NSObject< binaryMessenger:binaryMessenger codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(transactions:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(transactions:)", 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 transactions:&error]; + NSArray *output = [api transactionsWithError:&error]; callback(wrapResult(output, error)); }]; } else { @@ -363,10 +363,10 @@ void InAppPurchaseAPISetup(id binaryMessenger, NSObject< binaryMessenger:binaryMessenger codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(storefront:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefront:)", api); + NSCAssert([api respondsToSelector:@selector(storefrontWithError:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(storefrontWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; - NSArray *output = [api storefront:&error]; + SKStorefrontMessage *output = [api storefrontWithError:&error]; callback(wrapResult(output, error)); }]; } else { 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 b02408bb4fa..a12dbc26851 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 @@ -8,7 +8,7 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; -enum PaymentTransactionStateWrapper { +enum SKPaymentTransactionStateMessage { /// Indicates the transaction is being processed in App Store. /// /// You should update your UI to indicate that you are waiting for the @@ -40,8 +40,8 @@ enum PaymentTransactionStateWrapper { unspecified, } -class PaymentTransactionWrapper { - PaymentTransactionWrapper({ +class SKPaymentTransactionMessage { + SKPaymentTransactionMessage({ required this.payment, required this.transactionState, this.originalTransaction, @@ -50,17 +50,17 @@ class PaymentTransactionWrapper { this.error, }); - PaymentWrapper payment; + SKPaymentMessage payment; - PaymentTransactionStateWrapper transactionState; + SKPaymentTransactionStateMessage transactionState; - PaymentTransactionWrapper? originalTransaction; + SKPaymentTransactionMessage? originalTransaction; double? transactionTimeStamp; String? transactionIdentifier; - ErrorWrapper? error; + SKErrorMessage? error; Object encode() { return [ @@ -73,25 +73,25 @@ class PaymentTransactionWrapper { ]; } - static PaymentTransactionWrapper decode(Object result) { + static SKPaymentTransactionMessage decode(Object result) { result as List; - return PaymentTransactionWrapper( - payment: PaymentWrapper.decode(result[0]! as List), - transactionState: PaymentTransactionStateWrapper.values[result[1]! as int], + return SKPaymentTransactionMessage( + payment: SKPaymentMessage.decode(result[0]! as List), + transactionState: SKPaymentTransactionStateMessage.values[result[1]! as int], originalTransaction: result[2] != null - ? PaymentTransactionWrapper.decode(result[2]! as List) + ? SKPaymentTransactionMessage.decode(result[2]! as List) : null, transactionTimeStamp: result[3] as double?, transactionIdentifier: result[4] as String?, error: result[5] != null - ? ErrorWrapper.decode(result[5]! as List) + ? SKErrorMessage.decode(result[5]! as List) : null, ); } } -class PaymentWrapper { - PaymentWrapper({ +class SKPaymentMessage { + SKPaymentMessage({ required this.productIdentifier, this.applicationUsername, this.requestData, @@ -110,7 +110,7 @@ class PaymentWrapper { bool simulatesAskToBuyInSandbox; - PaymentDiscountWrapper? paymentDiscount; + SKPaymentDiscountMessage? paymentDiscount; Object encode() { return [ @@ -123,23 +123,23 @@ class PaymentWrapper { ]; } - static PaymentWrapper decode(Object result) { + static SKPaymentMessage decode(Object result) { result as List; - return PaymentWrapper( + return SKPaymentMessage( productIdentifier: result[0]! as String, applicationUsername: result[1] as String?, requestData: result[2] as String?, quantity: result[3]! as int, simulatesAskToBuyInSandbox: result[4]! as bool, paymentDiscount: result[5] != null - ? PaymentDiscountWrapper.decode(result[5]! as List) + ? SKPaymentDiscountMessage.decode(result[5]! as List) : null, ); } } -class ErrorWrapper { - ErrorWrapper({ +class SKErrorMessage { + SKErrorMessage({ required this.code, required this.domain, required this.userInfo, @@ -159,9 +159,9 @@ class ErrorWrapper { ]; } - static ErrorWrapper decode(Object result) { + static SKErrorMessage decode(Object result) { result as List; - return ErrorWrapper( + return SKErrorMessage( code: result[0]! as int, domain: result[1]! as String, userInfo: (result[2] as Map?)!.cast(), @@ -169,8 +169,8 @@ class ErrorWrapper { } } -class PaymentDiscountWrapper { - PaymentDiscountWrapper({ +class SKPaymentDiscountMessage { + SKPaymentDiscountMessage({ required this.identifier, required this.keyIdentifier, required this.nonce, @@ -198,9 +198,9 @@ class PaymentDiscountWrapper { ]; } - static PaymentDiscountWrapper decode(Object result) { + static SKPaymentDiscountMessage decode(Object result) { result as List; - return PaymentDiscountWrapper( + return SKPaymentDiscountMessage( identifier: result[0]! as String, keyIdentifier: result[1]! as String, nonce: result[2]! as String, @@ -210,8 +210,8 @@ class PaymentDiscountWrapper { } } -class StorefrontWrapper { - StorefrontWrapper({ +class SKStorefrontMessage { + SKStorefrontMessage({ required this.countryCode, required this.identifier, }); @@ -227,9 +227,9 @@ class StorefrontWrapper { ]; } - static StorefrontWrapper decode(Object result) { + static SKStorefrontMessage decode(Object result) { result as List; - return StorefrontWrapper( + return SKStorefrontMessage( countryCode: result[0]! as String, identifier: result[1]! as String, ); @@ -240,19 +240,19 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { const _InAppPurchaseAPICodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is ErrorWrapper) { + if (value is SKErrorMessage) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is PaymentDiscountWrapper) { + } else if (value is SKPaymentDiscountMessage) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is PaymentTransactionWrapper) { + } else if (value is SKPaymentMessage) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is PaymentWrapper) { + } else if (value is SKPaymentTransactionMessage) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is StorefrontWrapper) { + } else if (value is SKStorefrontMessage) { buffer.putUint8(132); writeValue(buffer, value.encode()); } else { @@ -264,15 +264,15 @@ class _InAppPurchaseAPICodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return ErrorWrapper.decode(readValue(buffer)!); + return SKErrorMessage.decode(readValue(buffer)!); case 129: - return PaymentDiscountWrapper.decode(readValue(buffer)!); + return SKPaymentDiscountMessage.decode(readValue(buffer)!); case 130: - return PaymentTransactionWrapper.decode(readValue(buffer)!); + return SKPaymentMessage.decode(readValue(buffer)!); case 131: - return PaymentWrapper.decode(readValue(buffer)!); + return SKPaymentTransactionMessage.decode(readValue(buffer)!); case 132: - return StorefrontWrapper.decode(readValue(buffer)!); + return SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -317,7 +317,7 @@ class InAppPurchaseAPI { } } - Future> transactions() async { + Future> transactions() async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', codec, binaryMessenger: _binaryMessenger); @@ -340,11 +340,11 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as List?)!.cast(); + return (replyList[0] as List?)!.cast(); } } - Future> storefront() async { + Future storefront() async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', codec, binaryMessenger: _binaryMessenger); @@ -367,7 +367,7 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as List?)!.cast(); + return (replyList[0] as SKStorefrontMessage?)!; } } } 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 7682714ab9c..d23b0f64dd7 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 @@ -12,6 +12,7 @@ import 'package:json_annotation/json_annotation.dart'; import '../../store_kit_wrappers.dart'; import '../channel.dart'; import '../in_app_purchase_storekit_platform.dart'; +import '../messages.g.dart'; part 'sk_payment_queue_wrapper.g.dart'; @@ -25,6 +26,14 @@ part 'sk_payment_queue_wrapper.g.dart'; /// Full information on using `SKPaymentQueue` and processing purchases is /// available at the [In-App Purchase Programming /// Guide](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html#//apple_ref/doc/uid/TP40008267). + +InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); + +@visibleForTesting +void setInAppPurchaseHostApi(InAppPurchaseAPI api) { + _hostApi = api; +} + class SKPaymentQueueWrapper { /// Returns the default payment queue. /// @@ -45,25 +54,18 @@ class SKPaymentQueueWrapper { /// /// Returns `null` if the user's device is below iOS 13.0 or macOS 10.15. Future storefront() async { - final Map? storefrontMap = await channel - .invokeMapMethod('-[SKPaymentQueue storefront]'); - if (storefrontMap == null) { - return null; - } - return SKStorefrontWrapper.fromJson(storefrontMap); + return SKStorefrontWrapper.convertFromPigeon(await _hostApi.storefront()); } /// Calls [`-[SKPaymentQueue transactions]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506026-transactions?language=objc). Future> transactions() async { - return _getTransactionList((await channel - .invokeListMethod('-[SKPaymentQueue transactions]'))!); + final List pigeonMsgs = await _hostApi.transactions(); + return pigeonMsgs.map((SKPaymentTransactionMessage? msg) => SKPaymentTransactionWrapper.convertFromPigeon(msg!)).toList(); } /// Calls [`-[SKPaymentQueue canMakePayments:]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506139-canmakepayments?language=objc). static Future canMakePayments() async => - (await channel - .invokeMethod('-[SKPaymentQueue canMakePayments:]')) ?? - false; + _hostApi.canMakePayments(); /// Sets an observer to listen to all incoming transaction events. /// @@ -359,7 +361,7 @@ class SKError { /// /// Any key of the map must be a valid [NSErrorUserInfoKey](https://developer.apple.com/documentation/foundation/nserroruserinfokey?language=objc). @JsonKey(defaultValue: {}) - final Map userInfo; + final Map userInfo; @override bool operator ==(Object other) { @@ -382,6 +384,14 @@ class SKError { domain, userInfo, ); + + static SKError convertFromPigeon(SKErrorMessage msg) { + return SKError( + code: msg.code, + domain: msg.domain, + userInfo: msg.userInfo + ); + } } /// Dart wrapper around StoreKit's @@ -499,6 +509,17 @@ class SKPaymentWrapper { @override String toString() => _$SKPaymentWrapperToJson(this).toString(); + + static SKPaymentWrapper convertFromPigeon (SKPaymentMessage msg) { + return SKPaymentWrapper( + productIdentifier: msg.productIdentifier, + applicationUsername: msg.applicationUsername, + quantity: msg.quantity, + simulatesAskToBuyInSandbox: msg.simulatesAskToBuyInSandbox, + requestData: msg.requestData, + paymentDiscount: SKPaymentDiscountWrapper.convertFromPigeon(msg.paymentDiscount) + ); + } } /// Dart wrapper around StoreKit's @@ -597,4 +618,16 @@ class SKPaymentDiscountWrapper { @override int get hashCode => Object.hash(identifier, keyIdentifier, nonce, signature, timestamp); + + static SKPaymentDiscountWrapper? convertFromPigeon(SKPaymentDiscountMessage? msg) { + if (msg == null) { + return null; + } + return SKPaymentDiscountWrapper( + identifier: msg.identifier, + keyIdentifier: msg.keyIdentifier, + nonce: msg.nonce, + signature: msg.signature, + timestamp: msg.timestamp); + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart index 3894721a1f8..0d9bab8ba91 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; +import '../messages.g.dart'; import 'enum_converters.dart'; import 'sk_payment_queue_wrapper.dart'; import 'sk_product_wrapper.dart'; @@ -90,7 +91,25 @@ enum SKPaymentTransactionStateWrapper { /// Indicates the transaction is in an unspecified state. @JsonValue(-1) - unspecified, + unspecified; + + // TODO(louisehsu): maybe remove this lol + static SKPaymentTransactionStateWrapper convertFromPigeon(SKPaymentTransactionStateMessage msg) { + switch (msg) { + case SKPaymentTransactionStateMessage.purchased: + return SKPaymentTransactionStateWrapper.purchased; + case SKPaymentTransactionStateMessage.purchasing: + return SKPaymentTransactionStateWrapper.purchasing; + case SKPaymentTransactionStateMessage.failed: + return SKPaymentTransactionStateWrapper.failed; + case SKPaymentTransactionStateMessage.restored: + return SKPaymentTransactionStateWrapper.restored; + case SKPaymentTransactionStateMessage.deferred: + return SKPaymentTransactionStateWrapper.deferred; + case SKPaymentTransactionStateMessage.unspecified: + return SKPaymentTransactionStateWrapper.unspecified; + } + } } /// Created when a payment is added to the [SKPaymentQueueWrapper]. @@ -199,4 +218,15 @@ class SKPaymentTransactionWrapper { 'transactionIdentifier': transactionIdentifier, 'productIdentifier': payment.productIdentifier, }; + + static SKPaymentTransactionWrapper convertFromPigeon(SKPaymentTransactionMessage msg) { + return SKPaymentTransactionWrapper( + payment: SKPaymentWrapper.convertFromPigeon(msg.payment), + transactionState: SKPaymentTransactionStateWrapper.convertFromPigeon(msg.transactionState), + originalTransaction: msg.originalTransaction == null ? null : convertFromPigeon(msg.originalTransaction!), + transactionTimeStamp: msg.transactionTimeStamp, + transactionIdentifier: msg.transactionIdentifier, + error: msg.error == null ? null : SKError.convertFromPigeon(msg.error!) + ); + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart index ff9e9b7db74..3064e1858cf 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart @@ -5,6 +5,8 @@ import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; +import '../messages.g.dart'; + part 'sk_storefront_wrapper.g.dart'; /// Contains the location and unique identifier of an Apple App Store storefront. @@ -65,4 +67,12 @@ class SKStorefrontWrapper { /// Converts the instance to a key value map which can be used to serialize /// to JSON format. Map toMap() => _$SKStorefrontWrapperToJson(this); + + /// Converts the pigeon equivalent to an instance of SKStorefrontWrapper + static SKStorefrontWrapper convertFromPigeon(SKStorefrontMessage msg) { + return SKStorefrontWrapper( + countryCode: msg.countryCode, + identifier: msg.identifier + ); + } } 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 12c6839341a..40e5d9607af 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 @@ -11,8 +11,8 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', )) -class PaymentTransactionWrapper { - PaymentTransactionWrapper({ +class SKPaymentTransactionMessage { + SKPaymentTransactionMessage({ required this.payment, required this.transactionState, this.originalTransaction, @@ -21,20 +21,20 @@ class PaymentTransactionWrapper { this.error, }); - final PaymentWrapper payment; + final SKPaymentMessage payment; - final PaymentTransactionStateWrapper transactionState; + final SKPaymentTransactionStateMessage transactionState; - final PaymentTransactionWrapper? originalTransaction; + final SKPaymentTransactionMessage? originalTransaction; final double? transactionTimeStamp; final String? transactionIdentifier; - final ErrorWrapper? error; + final SKErrorMessage? error; } -enum PaymentTransactionStateWrapper { +enum SKPaymentTransactionStateMessage { /// Indicates the transaction is being processed in App Store. /// /// You should update your UI to indicate that you are waiting for the @@ -71,9 +71,9 @@ enum PaymentTransactionStateWrapper { unspecified, } -class PaymentWrapper { +class SKPaymentMessage { /// Creates a new [SKPaymentWrapper] with the provided information. - const PaymentWrapper({ + const SKPaymentMessage({ required this.productIdentifier, this.applicationUsername, this.requestData, @@ -93,12 +93,12 @@ class PaymentWrapper { final bool simulatesAskToBuyInSandbox; - final PaymentDiscountWrapper? paymentDiscount; + final SKPaymentDiscountMessage? paymentDiscount; } -class ErrorWrapper { +class SKErrorMessage { // a lot of comparison operators are overriden in this class - do i add them here? - const ErrorWrapper( + const SKErrorMessage( {required this.code, required this.domain, required this.userInfo}); final int code; @@ -106,8 +106,8 @@ class ErrorWrapper { final Map userInfo; } -class PaymentDiscountWrapper { - const PaymentDiscountWrapper({ +class SKPaymentDiscountMessage { + const SKPaymentDiscountMessage({ required this.identifier, required this.keyIdentifier, required this.nonce, @@ -122,8 +122,8 @@ class PaymentDiscountWrapper { final int timestamp; } -class StorefrontWrapper { - StorefrontWrapper({ +class SKStorefrontMessage { + const SKStorefrontMessage({ required this.countryCode, required this.identifier, }); @@ -135,14 +135,14 @@ class StorefrontWrapper { @HostApi() abstract class InAppPurchaseAPI { /// Returns if the current device is able to make payments - @ObjCSelector('canMakePayments') + // @ObjCSelector('canMakePayments') bool canMakePayments(); - @ObjCSelector('transactions') - List transactions(); + // @ObjCSelector('transactions') + List transactions(); - @ObjCSelector('storefront') - List storefront(); + // @ObjCSelector('storefront') + SKStorefrontMessage storefront(); } From 020e024f844ac43c91e543d8bc4b85922f3e28da Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 11 Jan 2024 14:29:24 -0800 Subject: [PATCH 04/31] more converters yay --- .../darwin/Classes/InAppPurchasePlugin.h | 4 +- .../darwin/Classes/InAppPurchasePlugin.m | 51 ++++++++++++++++++- .../example/lib/main.dart | 1 + 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h index eeab0a70668..a2b9cb0eb93 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h @@ -7,10 +7,12 @@ #else #import #endif +#import "messages.g.h" + @class FIAPaymentQueueHandler; @class FIAPReceiptManager; -@interface InAppPurchasePlugin : NSObject +@interface InAppPurchasePlugin : NSObject @property(strong, nonatomic) FIAPaymentQueueHandler *paymentQueueHandler; 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 7086f611ace..5578cfed110 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 @@ -9,6 +9,7 @@ #import "FIAPReceiptManager.h" #import "FIAPRequestHandler.h" #import "FIAPaymentQueueHandler.h" +#import "messages.g.h" @interface InAppPurchasePlugin () @@ -40,8 +41,13 @@ + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase" binaryMessenger:[registrar messenger]]; - InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; - [registrar addMethodCallDelegate:instance channel:channel]; + // InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; + // SetUpInAppPurchaseAPI([registrar messenger], instance); + + InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; + [registrar addApplicationDelegate:instance]; + InAppPurchaseAPISetup([registrar messenger], instance); } - (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { @@ -466,4 +472,45 @@ - (SKReceiptRefreshRequest *)getRefreshReceiptRequest:(NSDictionary *)properties return [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:properties]; } +- (nullable NSNumber *)canMakePaymentsWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + return @([SKPaymentQueue canMakePayments]); +} + +- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + if (@available(iOS 13.0, macOS 10.15, *)) { + SKStorefront *storefront = self.paymentQueueHandler.storefront; + if (!storefront) { + return nil; + } + return NULL; +// return([self convertStorefrontToPigeon:storefront]); + } + + NSLog(@"storefront is not avaialbe in iOS below 13.0 or macOS below 10.15."); + return nil; +} + +- (nullable NSArray *)transactionsWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + NSArray *transactions = + [self.paymentQueueHandler getUnfinishedTransactions]; + NSMutableArray *transactionMaps = [[NSMutableArray alloc] init]; + for (SKPaymentTransaction *transaction in transactions) { + [transactionMaps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:transaction]]; + } + return(transactionMaps); + +} + +- (nullable SKPaymentTransactionMessage *) convertTransactionToPigeon:(SKPaymentTransactionMessage *) transaction { + SKPaymentTransactionMessage *msg = [SKPaymentTransactionMessage init]; + return NULL; +} + +- (nullable SKStorefrontMessage *) convertStorefrontToPigeon:(SKStorefront *)storefront API_AVAILABLE(ios(13.0)){ + SKStorefrontMessage *msg = [SKStorefrontMessage init]; + msg.identifier = storefront.identifier; + msg.countryCode = storefront.countryCode; + return msg; +} + @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index cab8ddf0c2d..47c7a2dd37e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -75,6 +75,7 @@ class _MyAppState extends State<_MyApp> { Future initStoreInfo() async { final bool isAvailable = await _iapStoreKitPlatform.isAvailable(); + if (!isAvailable) { setState(() { _isAvailable = isAvailable; From 82670b153f245674e22e7878c245a9e2818fe50f Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 11 Jan 2024 16:16:16 -0800 Subject: [PATCH 05/31] boop --- .../example/ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../src/store_kit_wrappers/sk_payment_queue_wrapper.dart | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj index b1639e4889f..097e280942e 100644 --- a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj @@ -176,7 +176,7 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Original; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 50a8cfc99f5..9c585c83b1d 100644 --- a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ storefront() async { + final Map? storefrontMap = await channel + .invokeMapMethod('-[SKPaymentQueue storefront]'); + if (storefrontMap == null) { + return null; + } + return SKStorefrontWrapper.fromJson(storefrontMap); return SKStorefrontWrapper.convertFromPigeon(await _hostApi.storefront()); } /// Calls [`-[SKPaymentQueue transactions]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506026-transactions?language=objc). Future> transactions() async { + return _getTransactionList((await channel + .invokeListMethod('-[SKPaymentQueue transactions]'))!); final List pigeonMsgs = await _hostApi.transactions(); return pigeonMsgs.map((SKPaymentTransactionMessage? msg) => SKPaymentTransactionWrapper.convertFromPigeon(msg!)).toList(); } From 0b110d736cc081b756f9b4a71db20d2747882520 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 17 Jan 2024 00:46:58 -0800 Subject: [PATCH 06/31] canMakePayment, transactions, storefront, addPayment --- .../darwin/Classes/FIAObjectTranslator.h | 4 + .../darwin/Classes/FIAObjectTranslator.m | 56 +++ .../darwin/Classes/InAppPurchasePlugin.m | 218 ++++++----- .../darwin/Classes/messages.g.h | 1 + .../darwin/Classes/messages.g.m | 19 + .../RunnerTests/InAppPurchasePluginTests.m | 369 ++++++++---------- .../lib/src/messages.g.dart | 22 ++ .../sk_payment_queue_wrapper.dart | 27 +- .../pigeons/messages.dart | 3 + 9 files changed, 398 insertions(+), 321 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 eb97ceb4475..622111208ed 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 @@ -4,6 +4,7 @@ #import #import +#import "messages.g.h" NS_ASSUME_NONNULL_BEGIN @@ -56,6 +57,9 @@ NS_ASSUME_NONNULL_BEGIN withError:(NSString *_Nullable *_Nullable)error API_AVAILABLE(ios(12.2)); ++ (nullable SKPaymentTransactionMessage *) convertTransactionToPigeon:(SKPaymentTransaction *) transaction; + ++ (nullable SKStorefrontMessage *) convertStorefrontToPigeon:(SKStorefront *)storefront API_AVAILABLE(ios(13.0)); @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 c656b58808b..e814f87c8e2 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 @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "FIAObjectTranslator.h" +#import "messages.g.h" #pragma mark - SKProduct Coders @@ -294,4 +295,59 @@ + (SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map return discount; } ++ (nullable SKPaymentTransactionMessage *) convertTransactionToPigeon:(SKPaymentTransaction *) transaction { + SKPaymentTransactionMessage *msg = [SKPaymentTransactionMessage makeWithPayment:[self convertPaymentToPigeon:transaction.payment] transactionState:[self convertTransactionStateToPigeon:transaction.transactionState] originalTransaction:transaction.originalTransaction ? [self convertTransactionToPigeon:transaction.originalTransaction] : nil transactionTimeStamp:[NSNumber numberWithDouble:[transaction.transactionDate timeIntervalSince1970]] transactionIdentifier:transaction.transactionIdentifier + error:[self convertSKErrorToPigeon:transaction.error]]; + return msg; +} + ++ (nullable SKErrorMessage *) convertSKErrorToPigeon:(NSError *) error { + SKErrorMessage *msg = [SKErrorMessage makeWithCode:@(error.code) domain:error.domain userInfo:error.userInfo]; + return msg; +} + ++ (SKPaymentTransactionStateMessage) convertTransactionStateToPigeon:(SKPaymentTransactionState) state { + switch (state) { + case SKPaymentTransactionStatePurchasing: + return SKPaymentTransactionStateMessagePurchasing; + break; + case SKPaymentTransactionStatePurchased: + return SKPaymentTransactionStateMessagePurchased; + break; + case SKPaymentTransactionStateFailed: + return SKPaymentTransactionStateMessageFailed; + break; + case SKPaymentTransactionStateRestored: + return SKPaymentTransactionStateMessageRestored; + break; + case SKPaymentTransactionStateDeferred: + return SKPaymentTransactionStateMessageDeferred; + break; + } +} + ++ (nullable SKPaymentMessage *) convertPaymentToPigeon:(SKPayment *) payment { + if (@available(iOS 12.2, *)) { + SKPaymentMessage *msg = [SKPaymentMessage makeWithProductIdentifier:payment.productIdentifier applicationUsername:payment.applicationUsername + requestData:[[NSString alloc] initWithData:payment.requestData encoding:NSUTF8StringEncoding] + quantity:@(payment.quantity) + simulatesAskToBuyInSandbox:@(payment.simulatesAskToBuyInSandbox) + paymentDiscount:[self convertPaymentDiscountToPigeon: payment.paymentDiscount]]; + return msg; + } + return nil; +} + ++ (nullable SKPaymentDiscountMessage *) convertPaymentDiscountToPigeon:(SKPaymentDiscount *) discount API_AVAILABLE(ios(12.2)){ + SKPaymentDiscountMessage *msg = [SKPaymentDiscountMessage makeWithIdentifier:discount.identifier keyIdentifier:discount.keyIdentifier nonce:[discount.nonce UUIDString] signature:discount.signature timestamp:discount.timestamp]; + + return msg; +} + ++ (nullable SKStorefrontMessage *) convertStorefrontToPigeon:(SKStorefront *)storefront API_AVAILABLE(ios(13.0)){ + SKStorefrontMessage *msg = [SKStorefrontMessage makeWithCountryCode:storefront.countryCode + identifier:storefront.identifier]; + 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 5578cfed110..b93c8293b7c 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 @@ -44,7 +44,7 @@ + (void)registerWithRegistrar:(NSObject *)registrar { // InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; // SetUpInAppPurchaseAPI([registrar messenger], instance); - InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] init]; + InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; [registrar addMethodCallDelegate:instance channel:channel]; [registrar addApplicationDelegate:instance]; InAppPurchaseAPISetup([registrar messenger], instance); @@ -91,16 +91,8 @@ - (instancetype)initWithRegistrar:(NSObject *)registrar } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([@"-[SKPaymentQueue canMakePayments:]" isEqualToString:call.method]) { - [self canMakePayments:result]; - } else if ([@"-[SKPaymentQueue transactions]" isEqualToString:call.method]) { - [self getPendingTransactions:result]; - } else if ([@"-[SKPaymentQueue storefront]" isEqualToString:call.method]) { - [self getStorefront:result]; - } else if ([@"-[InAppPurchasePlugin startProductRequest:result:]" isEqualToString:call.method]) { + if ([@"-[InAppPurchasePlugin startProductRequest:result:]" isEqualToString:call.method]) { [self handleProductRequestMethodCall:call result:result]; - } else if ([@"-[InAppPurchasePlugin addPayment:result:]" isEqualToString:call.method]) { - [self addPayment:call result:result]; } else if ([@"-[InAppPurchasePlugin finishTransaction:result:]" isEqualToString:call.method]) { [self finishTransaction:call result:result]; } else if ([@"-[InAppPurchasePlugin restoreTransactions:result:]" isEqualToString:call.method]) { @@ -199,69 +191,69 @@ - (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(Flutter }]; } -- (void)addPayment:(FlutterMethodCall *)call result:(FlutterResult)result { - if (![call.arguments isKindOfClass:[NSDictionary class]]) { - result([FlutterError errorWithCode:@"storekit_invalid_argument" - message:@"Argument type of addPayment is not a Dictionary" - details:call.arguments]); - return; - } - NSDictionary *paymentMap = (NSDictionary *)call.arguments; - NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; - // When a product is already fetched, we create a payment object with - // the product to process the payment. - SKProduct *product = [self getProduct:productID]; - if (!product) { - result([FlutterError - errorWithCode:@"storekit_invalid_payment_object" - message: - @"You have requested a payment for an invalid product. Either the " - @"`productIdentifier` of the payment is not valid or the product has not been " - @"fetched before adding the payment to the payment queue." - details:call.arguments]); - return; - } - SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; - payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; - NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; - payment.quantity = (quantity != nil) ? quantity.integerValue : 1; - NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; - payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] - ? NO - : [simulatesAskToBuyInSandbox boolValue]; - - if (@available(iOS 12.2, *)) { - NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap - forKey:@"paymentDiscount"]; - NSString *error = nil; - SKPaymentDiscount *paymentDiscount = - [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&error]; - - if (error) { - result([FlutterError - errorWithCode:@"storekit_invalid_payment_discount_object" - message:[NSString stringWithFormat:@"You have requested a payment and specified a " - @"payment discount with invalid properties. %@", - error] - details:call.arguments]); - return; - } - - payment.paymentDiscount = paymentDiscount; - } - - if (![self.paymentQueueHandler addPayment:payment]) { - result([FlutterError - errorWithCode:@"storekit_duplicate_product_object" - message:@"There is a pending transaction for the same product identifier. Please " - @"either wait for it to be finished or finish it manually using " - @"`completePurchase` to avoid edge cases." - - details:call.arguments]); - return; - } - result(nil); -} +//- (void)addPayment:(FlutterMethodCall *)call result:(FlutterResult)result { +// if (![call.arguments isKindOfClass:[NSDictionary class]]) { +// result([FlutterError errorWithCode:@"storekit_invalid_argument" +// message:@"Argument type of addPayment is not a Dictionary" +// details:call.arguments]); +// return; +// } +// NSDictionary *paymentMap = (NSDictionary *)call.arguments; +// NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; +// // When a product is already fetched, we create a payment object with +// // the product to process the payment. +// SKProduct *product = [self getProduct:productID]; +// if (!product) { +// result([FlutterError +// errorWithCode:@"storekit_invalid_payment_object" +// message: +// @"You have requested a payment for an invalid product. Either the " +// @"`productIdentifier` of the payment is not valid or the product has not been " +// @"fetched before adding the payment to the payment queue." +// details:call.arguments]); +// return; +// } +// SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; +// payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; +// NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; +// payment.quantity = (quantity != nil) ? quantity.integerValue : 1; +// NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; +// payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] +// ? NO +// : [simulatesAskToBuyInSandbox boolValue]; +// +// if (@available(iOS 12.2, *)) { +// NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap +// forKey:@"paymentDiscount"]; +// NSString *error = nil; +// SKPaymentDiscount *paymentDiscount = +// [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&error]; +// +// if (error) { +// result([FlutterError +// errorWithCode:@"storekit_invalid_payment_discount_object" +// message:[NSString stringWithFormat:@"You have requested a payment and specified a " +// @"payment discount with invalid properties. %@", +// error] +// details:call.arguments]); +// return; +// } +// +// payment.paymentDiscount = paymentDiscount; +// } +// +// if (![self.paymentQueueHandler addPayment:payment]) { +// result([FlutterError +// errorWithCode:@"storekit_duplicate_product_object" +// message:@"There is a pending transaction for the same product identifier. Please " +// @"either wait for it to be finished or finish it manually using " +// @"`completePurchase` to avoid edge cases." +// +// details:call.arguments]); +// return; +// } +// result(nil); +//} - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result { if (![call.arguments isKindOfClass:[NSDictionary class]]) { @@ -476,41 +468,83 @@ - (nullable NSNumber *)canMakePaymentsWithError:(FlutterError * _Nullable __auto return @([SKPaymentQueue canMakePayments]); } -- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error { if (@available(iOS 13.0, macOS 10.15, *)) { SKStorefront *storefront = self.paymentQueueHandler.storefront; if (!storefront) { return nil; } - return NULL; -// return([self convertStorefrontToPigeon:storefront]); + return[FIAObjectTranslator convertStorefrontToPigeon:storefront]; + } else { + NSLog(@"storefront is not avaialbe in iOS below 13.0 or macOS below 10.15."); + return nil; } - - NSLog(@"storefront is not avaialbe in iOS below 13.0 or macOS below 10.15."); - return nil; } -- (nullable NSArray *)transactionsWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (nullable NSArray *)transactionsWithError:(FlutterError *_Nullable *_Nonnull)error { NSArray *transactions = [self.paymentQueueHandler getUnfinishedTransactions]; NSMutableArray *transactionMaps = [[NSMutableArray alloc] init]; for (SKPaymentTransaction *transaction in transactions) { - [transactionMaps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:transaction]]; + [transactionMaps addObject:[FIAObjectTranslator convertTransactionToPigeon:transaction]]; } return(transactionMaps); - } -- (nullable SKPaymentTransactionMessage *) convertTransactionToPigeon:(SKPaymentTransactionMessage *) transaction { - SKPaymentTransactionMessage *msg = [SKPaymentTransactionMessage init]; - return NULL; -} +- (void)addPayment:(nonnull NSDictionary *)paymentMap error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + NSLog(@"Here"); + NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; + // When a product is already fetched, we create a payment object with + // the product to process the payment. + SKProduct *product = [self getProduct:productID]; + if (!product) { + *error = [FlutterError + errorWithCode:@"storekit_invalid_payment_object" + message: + @"You have requested a payment for an invalid product. Either the " + @"`productIdentifier` of the payment is not valid or the product has not been " + @"fetched before adding the payment to the payment queue." + details:paymentMap]; + return; + } + + SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; + payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; + NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; + payment.quantity = (quantity != nil) ? quantity.integerValue : 1; + NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; + payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] + ? NO + : [simulatesAskToBuyInSandbox boolValue]; + + if (@available(iOS 12.2, *)) { + NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap + forKey:@"paymentDiscount"]; + NSString *errorMsg = nil; + SKPaymentDiscount *paymentDiscount = + [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&errorMsg]; + + if (errorMsg) { + *error = [FlutterError + errorWithCode:@"storekit_invalid_payment_discount_object" + message:[NSString stringWithFormat:@"You have requested a payment and specified a " + @"payment discount with invalid properties. %@", + errorMsg] + details:paymentMap]; + return; + } + + payment.paymentDiscount = paymentDiscount; + } + if (![self.paymentQueueHandler addPayment:payment]) { + *error = [FlutterError + errorWithCode:@"storekit_duplicate_product_object" + message:@"There is a pending transaction for the same product identifier. Please " + @"either wait for it to be finished or finish it manually using " + @"`completePurchase` to avoid edge cases." -- (nullable SKStorefrontMessage *) convertStorefrontToPigeon:(SKStorefront *)storefront API_AVAILABLE(ios(13.0)){ - SKStorefrontMessage *msg = [SKStorefrontMessage init]; - msg.identifier = storefront.identifier; - msg.countryCode = storefront.countryCode; - return msg; + details:paymentMap]; + return; + } } - @end 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 97a932128ca..d9f19a1468a 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 @@ -135,6 +135,7 @@ NSObject *InAppPurchaseAPIGetCodec(void); - (nullable NSArray *)transactionsWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPayment:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; @end extern void InAppPurchaseAPISetup(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 cc620b579eb..1eb57663bfd 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 @@ -373,4 +373,23 @@ void InAppPurchaseAPISetup(id binaryMessenger, NSObject< [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(addPayment:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPayment:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSDictionary *arg_paymentMap = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api addPayment:arg_paymentMap error:&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 f8aa0e34afd..fb163da04bb 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 @@ -41,18 +41,67 @@ - (void)testInvalidMethodCall { } - (void)testCanMakePayments { - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result to be YES"]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue canMakePayments:]" - arguments:NULL]; - __block id result; - [self.plugin handleMethodCall:call - result:^(id r) { - [expectation fulfill]; - result = r; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(result, @YES); + FlutterError *error; + NSNumber *result = [self.plugin canMakePaymentsWithError:&error]; + XCTAssertTrue([result boolValue]); + XCTAssertNil(error); +} + +- (void)testPaymentQueueStorefront { + if (@available(iOS 13, macOS 10.15, *)) { + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + NSDictionary *storefrontMap = @{ + @"countryCode" : @"USA", + @"identifier" : @"unique_identifier", + }; + + OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); + + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + + FlutterError *error; + SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; + + XCTAssertEqualObjects(resultMap.countryCode, storefrontMap[@"countryCode"]); + XCTAssertEqualObjects(resultMap.identifier, storefrontMap[@"identifier"]); + XCTAssertNil(error); + } else { + NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); + } +} + +- (void)testPaymentQueueStorefrontReturnsNil { + if (@available(iOS 13, macOS 10.15, *)) { + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + + OCMStub(mockQueue.storefront).andReturn(nil); + + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + + FlutterError *error; + SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; + + XCTAssertNil(resultMap); + XCTAssertNil(error); + } else { + NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); + } } - (void)testGetProductResponse { @@ -74,134 +123,96 @@ - (void)testGetProductResponse { XCTAssertTrue([resultArray.firstObject[@"productIdentifier"] isEqualToString:@"123"]); } -- (void)testAddPaymentShouldReturnFlutterErrorWhenArgumentsAreInvalid { - XCTestExpectation *expectation = - [self expectationWithDescription: - @"Result should contain a FlutterError when invalid parameters are passed in."]; - NSString *argument = @"Invalid argument"; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:argument]; - [self.plugin handleMethodCall:call - result:^(id _Nullable result) { - FlutterError *error = result; - XCTAssertEqualObjects(@"storekit_invalid_argument", error.code); - XCTAssertEqualObjects(@"Argument type of addPayment is not a Dictionary", - error.message); - XCTAssertEqualObjects(argument, error.details); - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:5]; -} - - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { NSDictionary *arguments = @{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, }; - XCTestExpectation *expectation = - [self expectationWithDescription:@"Result should return failed state."]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:arguments]; FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); self.plugin.paymentQueueHandler = mockHandler; - [self.plugin handleMethodCall:call - result:^(id _Nullable result) { - FlutterError *error = result; - XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); - XCTAssertEqualObjects( - @"There is a pending transaction for the same product identifier. " - @"Please either wait for it to be finished or finish it manually " - @"using `completePurchase` to avoid edge cases.", - error.message); - XCTAssertEqualObjects(arguments, error.details); - [expectation fulfill]; - }]; + FlutterError *error; + + [self.plugin addPayment:arguments error:&error]; - [self waitForExpectations:@[ expectation ] timeout:5]; OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); + XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); + XCTAssertEqualObjects( + @"There is a pending transaction for the same product identifier. " + @"Please either wait for it to be finished or finish it manually " + @"using `completePurchase` to avoid edge cases.", + error.message); + XCTAssertEqualObjects(arguments, error.details); } - (void)testAddPaymentSuccessWithoutPaymentDiscount { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Result should return success state"]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:@{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }]; + NSDictionary *arguments = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); self.plugin.paymentQueueHandler = mockHandler; - [self.plugin handleMethodCall:call - result:^(id _Nullable result) { - XCTAssertNil(result); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; + + FlutterError *error; + + [self.plugin addPayment:arguments error:&error]; + XCTAssertNil(error); + OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); } - (void)testAddPaymentSuccessWithPaymentDiscount { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Result should return success state"]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:@{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - @"paymentDiscount" : @{ - @"identifier" : @"test_identifier", - @"keyIdentifier" : @"test_key_identifier", - @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", - @"signature" : @"test_signature", - @"timestamp" : @(1635847102), - } - }]; + NSDictionary *arguments = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + @"paymentDiscount" : @{ + @"identifier" : @"test_identifier", + @"keyIdentifier" : @"test_key_identifier", + @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", + @"signature" : @"test_signature", + @"timestamp" : @(1635847102), + } + }; FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); self.plugin.paymentQueueHandler = mockHandler; - [self.plugin handleMethodCall:call - result:^(id _Nullable result) { - XCTAssertNil(result); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; + + FlutterError *error; + + [self.plugin addPayment:arguments error:&error]; + XCTAssertNil(error); OCMVerify( - times(1), - [mockHandler - addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { - SKPayment *payment = obj; - if (@available(iOS 12.2, *)) { - SKPaymentDiscount *discount = payment.paymentDiscount; - - return [discount.identifier isEqual:@"test_identifier"] && - [discount.keyIdentifier isEqual:@"test_key_identifier"] && - [discount.nonce - isEqual:[[NSUUID alloc] - initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && - [discount.signature isEqual:@"test_signature"] && - [discount.timestamp isEqual:@(1635847102)]; - } - - return YES; - }]]); + times(1), + [mockHandler + addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + if (@available(iOS 12.2, *)) { + SKPaymentDiscount *discount = payment.paymentDiscount; + + return [discount.identifier isEqual:@"test_identifier"] && + [discount.keyIdentifier isEqual:@"test_key_identifier"] && + [discount.nonce + isEqual:[[NSUUID alloc] + initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && + [discount.signature isEqual:@"test_signature"] && + [discount.timestamp isEqual:@(1635847102)]; + } + + return YES; + }]]); } + - (void)testAddPaymentFailureWithInvalidPaymentDiscount { // Support for payment discount is only available on iOS 12.2 and higher. if (@available(iOS 12.2, *)) { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Result should return success state"]; NSDictionary *arguments = @{ @"productIdentifier" : @"123", @"quantity" : @(1), @@ -213,55 +224,43 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { @"timestamp" : @(1635847102), } }; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:arguments]; FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); id translator = OCMClassMock(FIAObjectTranslator.class); - NSString *error = @"Some error occurred"; + NSString *errorMsg = @"Some error occurred"; OCMStub(ClassMethod([translator getSKPaymentDiscountFromMap:[OCMArg any] - withError:(NSString __autoreleasing **)[OCMArg setTo:error]])) + withError:(NSString __autoreleasing **)[OCMArg setTo:errorMsg]])) .andReturn(nil); self.plugin.paymentQueueHandler = mockHandler; - [self.plugin - handleMethodCall:call - result:^(id _Nullable result) { - FlutterError *error = result; - XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); - XCTAssertEqualObjects( - @"You have requested a payment and specified a " - @"payment discount with invalid properties. Some error occurred", - error.message); - XCTAssertEqualObjects(arguments, error.details); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; + FlutterError *error; + + [self.plugin addPayment:arguments error:&error]; + + XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); + XCTAssertEqualObjects( + @"You have requested a payment and specified a " + @"payment discount with invalid properties. Some error occurred", + error.message); + XCTAssertEqualObjects(arguments, error.details); OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); } } - (void)testAddPaymentWithNullSandboxArgument { - XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return success state"]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:@{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : [NSNull null], - }]; + NSDictionary *arguments = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : [NSNull null], + }; + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); self.plugin.paymentQueueHandler = mockHandler; - [self.plugin handleMethodCall:call - result:^(id _Nullable result) { - XCTAssertNil(result); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; + FlutterError *error; + + [self.plugin addPayment:arguments error:&error]; OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { SKPayment *payment = obj; return !payment.simulatesAskToBuyInSandbox; @@ -383,9 +382,6 @@ - (void)testPresentCodeRedemptionSheet { } - (void)testGetPendingTransactions { - XCTestExpectation *expectation = [self expectationWithDescription:@"expect success"]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue transactions]" arguments:nil]; SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); NSDictionary *transactionMap = @{ @"transactionIdentifier" : [NSNull null], @@ -410,78 +406,19 @@ - (void)testGetPendingTransactions { shouldAddStorePayment:nil updatedDownloads:nil transactionCache:OCMClassMock(FIATransactionCache.class)]; - [self.plugin handleMethodCall:call - result:^(id r) { - resultArray = r; - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqualObjects(resultArray, @[ transactionMap ]); -} - -- (void)testPaymentQueueStorefront { - if (@available(iOS 13, macOS 10.15, *)) { - // storefront is not nil - XCTestExpectation *expectation = [self expectationWithDescription:@"expect success"]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue storefront]" arguments:nil]; - SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); - NSDictionary *storefrontMap = @{ - @"countryCode" : @"USA", - @"identifier" : @"unique_identifier", - }; - OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); - - __block NSDictionary *resultMap; - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - [self.plugin handleMethodCall:call - result:^(id r) { - resultMap = r; - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqualObjects(resultMap, storefrontMap); - } else { - NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); - } -} - -- (void)testPaymentQueueStorefrontReturnsNil { - if (@available(iOS 13, macOS 10.15, *)) { - XCTestExpectation *expectation = [self expectationWithDescription:@"expect success"]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue storefront]" arguments:nil]; - SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); - OCMStub(mockQueue.storefront).andReturn(nil); - - __block NSDictionary *resultMap; - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; - [self.plugin handleMethodCall:call - result:^(id r) { - resultMap = r; - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertNil(resultMap); - } else { - NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); - } + FlutterError *error; + SKPaymentTransactionStub *original = [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + + SKPaymentTransactionMessage *originalPigeon = [FIAObjectTranslator convertTransactionToPigeon:original]; + SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; + + // How should I test this nicely without overriding isEquals? +// XCTAssertEqualObjects(result.payment, originalPigeon.payment); + XCTAssertEqual(result.transactionState, originalPigeon.transactionState); + XCTAssertEqualObjects(result.originalTransaction, originalPigeon.originalTransaction); + XCTAssertEqualObjects(result.transactionTimeStamp, originalPigeon.transactionTimeStamp); + XCTAssertEqualObjects(result.transactionIdentifier, originalPigeon.transactionIdentifier); +// XCTAssertEqualObjects(result.error, originalPigeon.error); } - (void)testStartObservingPaymentQueue { 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 a12dbc26851..a7aa235ca5c 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 @@ -370,4 +370,26 @@ class InAppPurchaseAPI { return (replyList[0] as SKStorefrontMessage?)!; } } + + Future addPayment(Map arg_paymentMap) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_paymentMap]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: 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 eafe702ac56..9e72a506652 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 @@ -54,19 +54,19 @@ class SKPaymentQueueWrapper { /// /// Returns `null` if the user's device is below iOS 13.0 or macOS 10.15. Future storefront() async { - final Map? storefrontMap = await channel - .invokeMapMethod('-[SKPaymentQueue storefront]'); - if (storefrontMap == null) { - return null; - } - return SKStorefrontWrapper.fromJson(storefrontMap); + // final Map? storefrontMap = await channel + // .invokeMapMethod('-[SKPaymentQueue storefront]'); + // if (storefrontMap == null) { + // return null; + // } + // return SKStorefrontWrapper.fromJson(storefrontMap); return SKStorefrontWrapper.convertFromPigeon(await _hostApi.storefront()); } /// Calls [`-[SKPaymentQueue transactions]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506026-transactions?language=objc). Future> transactions() async { - return _getTransactionList((await channel - .invokeListMethod('-[SKPaymentQueue transactions]'))!); + // return _getTransactionList((await channel + // .invokeListMethod('-[SKPaymentQueue transactions]'))!); final List pigeonMsgs = await _hostApi.transactions(); return pigeonMsgs.map((SKPaymentTransactionMessage? msg) => SKPaymentTransactionWrapper.convertFromPigeon(msg!)).toList(); } @@ -148,11 +148,12 @@ class SKPaymentQueueWrapper { Future addPayment(SKPaymentWrapper payment) async { assert(_observer != null, '[in_app_purchase]: Trying to add a payment without an observer. One must be set using `SkPaymentQueueWrapper.setTransactionObserver` before the app launches.'); - final Map requestMap = payment.toMap(); - await channel.invokeMethod( - '-[InAppPurchasePlugin addPayment:result:]', - requestMap, - ); + + await _hostApi.addPayment(payment.toMap()); + // await channel.invokeMethod( + // '-[InAppPurchasePlugin addPayment:result:]', + // requestMap, + // ); } /// Finishes a transaction and removes it from the queue. 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 40e5d9607af..ed6e189bc13 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 @@ -143,6 +143,9 @@ abstract class InAppPurchaseAPI { // @ObjCSelector('storefront') SKStorefrontMessage storefront(); + + @ObjCSelector('addPayment:') + void addPayment(Map paymentMap); } From 2c9c5452726a2ed0159059a7d01b19fd7903e62e Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Wed, 17 Jan 2024 00:49:55 -0800 Subject: [PATCH 07/31] comments --- .../darwin/Classes/InAppPurchasePlugin.m | 64 ------------------- 1 file changed, 64 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 b93c8293b7c..169e7d362c1 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 @@ -191,70 +191,6 @@ - (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(Flutter }]; } -//- (void)addPayment:(FlutterMethodCall *)call result:(FlutterResult)result { -// if (![call.arguments isKindOfClass:[NSDictionary class]]) { -// result([FlutterError errorWithCode:@"storekit_invalid_argument" -// message:@"Argument type of addPayment is not a Dictionary" -// details:call.arguments]); -// return; -// } -// NSDictionary *paymentMap = (NSDictionary *)call.arguments; -// NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; -// // When a product is already fetched, we create a payment object with -// // the product to process the payment. -// SKProduct *product = [self getProduct:productID]; -// if (!product) { -// result([FlutterError -// errorWithCode:@"storekit_invalid_payment_object" -// message: -// @"You have requested a payment for an invalid product. Either the " -// @"`productIdentifier` of the payment is not valid or the product has not been " -// @"fetched before adding the payment to the payment queue." -// details:call.arguments]); -// return; -// } -// SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; -// payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; -// NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; -// payment.quantity = (quantity != nil) ? quantity.integerValue : 1; -// NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; -// payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] -// ? NO -// : [simulatesAskToBuyInSandbox boolValue]; -// -// if (@available(iOS 12.2, *)) { -// NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap -// forKey:@"paymentDiscount"]; -// NSString *error = nil; -// SKPaymentDiscount *paymentDiscount = -// [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&error]; -// -// if (error) { -// result([FlutterError -// errorWithCode:@"storekit_invalid_payment_discount_object" -// message:[NSString stringWithFormat:@"You have requested a payment and specified a " -// @"payment discount with invalid properties. %@", -// error] -// details:call.arguments]); -// return; -// } -// -// payment.paymentDiscount = paymentDiscount; -// } -// -// if (![self.paymentQueueHandler addPayment:payment]) { -// result([FlutterError -// errorWithCode:@"storekit_duplicate_product_object" -// message:@"There is a pending transaction for the same product identifier. Please " -// @"either wait for it to be finished or finish it manually using " -// @"`completePurchase` to avoid edge cases." -// -// details:call.arguments]); -// return; -// } -// result(nil); -//} - - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result { if (![call.arguments isKindOfClass:[NSDictionary class]]) { result([FlutterError errorWithCode:@"storekit_invalid_argument" From 9a085ba191c2a397ad46abc51fe6ff95f0359b66 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 18 Jan 2024 00:59:07 -0800 Subject: [PATCH 08/31] formatting --- .../ios/Classes/QueueUtils.h | 2 +- .../darwin/Classes/FIAObjectTranslator.h | 6 +- .../darwin/Classes/FIAObjectTranslator.m | 52 +++++--- .../darwin/Classes/InAppPurchasePlugin.m | 57 ++++----- .../darwin/Classes/messages.g.h | 86 +++++++------- .../darwin/Classes/messages.g.m | 112 ++++++++++-------- .../RunnerTests/InAppPurchasePluginTests.m | 99 ++++++++-------- .../lib/src/messages.g.dart | 42 ++++--- .../sk_payment_queue_wrapper.dart | 36 +++--- .../sk_payment_transaction_wrappers.dart | 23 ++-- .../sk_storefront_wrapper.dart | 4 +- .../pigeons/messages.dart | 3 - 12 files changed, 283 insertions(+), 239 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h index a7e22da716d..e230a53508f 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h +++ b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN /// Queue-specific context data to be associated with the capture session queue. -extern const char* FLTCaptureSessionQueueSpecific; +extern const char *FLTCaptureSessionQueueSpecific; /// Ensures the given block to be run on the main queue. /// If caller site is already on the main queue, the block will be run 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 622111208ed..da4f49c362f 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 @@ -57,9 +57,11 @@ NS_ASSUME_NONNULL_BEGIN withError:(NSString *_Nullable *_Nullable)error API_AVAILABLE(ios(12.2)); -+ (nullable SKPaymentTransactionMessage *) convertTransactionToPigeon:(SKPaymentTransaction *) transaction; ++ (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: + (SKPaymentTransaction *)transaction; -+ (nullable SKStorefrontMessage *) convertStorefrontToPigeon:(SKStorefront *)storefront API_AVAILABLE(ios(13.0)); ++ (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(SKStorefront *)storefront + API_AVAILABLE(ios(13.0)); @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 e814f87c8e2..63a4a79ffb1 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 @@ -295,18 +295,30 @@ + (SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map return discount; } -+ (nullable SKPaymentTransactionMessage *) convertTransactionToPigeon:(SKPaymentTransaction *) transaction { - SKPaymentTransactionMessage *msg = [SKPaymentTransactionMessage makeWithPayment:[self convertPaymentToPigeon:transaction.payment] transactionState:[self convertTransactionStateToPigeon:transaction.transactionState] originalTransaction:transaction.originalTransaction ? [self convertTransactionToPigeon:transaction.originalTransaction] : nil transactionTimeStamp:[NSNumber numberWithDouble:[transaction.transactionDate timeIntervalSince1970]] transactionIdentifier:transaction.transactionIdentifier - error:[self convertSKErrorToPigeon:transaction.error]]; ++ (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: + (SKPaymentTransaction *)transaction { + SKPaymentTransactionMessage *msg = [SKPaymentTransactionMessage + makeWithPayment:[self convertPaymentToPigeon:transaction.payment] + transactionState:[self convertTransactionStateToPigeon:transaction.transactionState] + originalTransaction:transaction.originalTransaction + ? [self convertTransactionToPigeon:transaction.originalTransaction] + : nil + transactionTimeStamp:[NSNumber numberWithDouble:[transaction.transactionDate + timeIntervalSince1970]] + transactionIdentifier:transaction.transactionIdentifier + error:[self convertSKErrorToPigeon:transaction.error]]; return msg; } -+ (nullable SKErrorMessage *) convertSKErrorToPigeon:(NSError *) error { - SKErrorMessage *msg = [SKErrorMessage makeWithCode:@(error.code) domain:error.domain userInfo:error.userInfo]; ++ (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error { + SKErrorMessage *msg = [SKErrorMessage makeWithCode:@(error.code) + domain:error.domain + userInfo:error.userInfo]; return msg; } -+ (SKPaymentTransactionStateMessage) convertTransactionStateToPigeon:(SKPaymentTransactionState) state { ++ (SKPaymentTransactionStateMessage)convertTransactionStateToPigeon: + (SKPaymentTransactionState)state { switch (state) { case SKPaymentTransactionStatePurchasing: return SKPaymentTransactionStateMessagePurchasing; @@ -326,27 +338,37 @@ + (SKPaymentTransactionStateMessage) convertTransactionStateToPigeon:(SKPaymentT } } -+ (nullable SKPaymentMessage *) convertPaymentToPigeon:(SKPayment *) payment { ++ (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment { if (@available(iOS 12.2, *)) { - SKPaymentMessage *msg = [SKPaymentMessage makeWithProductIdentifier:payment.productIdentifier applicationUsername:payment.applicationUsername - requestData:[[NSString alloc] initWithData:payment.requestData encoding:NSUTF8StringEncoding] - quantity:@(payment.quantity) + SKPaymentMessage *msg = [SKPaymentMessage + makeWithProductIdentifier:payment.productIdentifier + applicationUsername:payment.applicationUsername + requestData:[[NSString alloc] initWithData:payment.requestData + encoding:NSUTF8StringEncoding] + quantity:@(payment.quantity) simulatesAskToBuyInSandbox:@(payment.simulatesAskToBuyInSandbox) - paymentDiscount:[self convertPaymentDiscountToPigeon: payment.paymentDiscount]]; + paymentDiscount:[self convertPaymentDiscountToPigeon:payment.paymentDiscount]]; return msg; } return nil; } -+ (nullable SKPaymentDiscountMessage *) convertPaymentDiscountToPigeon:(SKPaymentDiscount *) discount API_AVAILABLE(ios(12.2)){ - SKPaymentDiscountMessage *msg = [SKPaymentDiscountMessage makeWithIdentifier:discount.identifier keyIdentifier:discount.keyIdentifier nonce:[discount.nonce UUIDString] signature:discount.signature timestamp:discount.timestamp]; ++ (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:(SKPaymentDiscount *)discount + API_AVAILABLE(ios(12.2)) { + SKPaymentDiscountMessage *msg = + [SKPaymentDiscountMessage makeWithIdentifier:discount.identifier + keyIdentifier:discount.keyIdentifier + nonce:[discount.nonce UUIDString] + signature:discount.signature + timestamp:discount.timestamp]; return msg; } -+ (nullable SKStorefrontMessage *) convertStorefrontToPigeon:(SKStorefront *)storefront API_AVAILABLE(ios(13.0)){ ++ (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(SKStorefront *)storefront + API_AVAILABLE(ios(13.0)) { SKStorefrontMessage *msg = [SKStorefrontMessage makeWithCountryCode:storefront.countryCode - identifier:storefront.identifier]; + identifier:storefront.identifier]; 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 169e7d362c1..97d24ef0327 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 @@ -44,10 +44,10 @@ + (void)registerWithRegistrar:(NSObject *)registrar { // InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; // SetUpInAppPurchaseAPI([registrar messenger], instance); - InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; - [registrar addMethodCallDelegate:instance channel:channel]; - [registrar addApplicationDelegate:instance]; - InAppPurchaseAPISetup([registrar messenger], instance); + InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; + [registrar addMethodCallDelegate:instance channel:channel]; + [registrar addApplicationDelegate:instance]; + InAppPurchaseAPISetup([registrar messenger], instance); } - (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { @@ -400,7 +400,8 @@ - (SKReceiptRefreshRequest *)getRefreshReceiptRequest:(NSDictionary *)properties return [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:properties]; } -- (nullable NSNumber *)canMakePaymentsWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (nullable NSNumber *)canMakePaymentsWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { return @([SKPaymentQueue canMakePayments]); } @@ -410,24 +411,26 @@ - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable * if (!storefront) { return nil; } - return[FIAObjectTranslator convertStorefrontToPigeon:storefront]; + return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; } else { NSLog(@"storefront is not avaialbe in iOS below 13.0 or macOS below 10.15."); return nil; } } -- (nullable NSArray *)transactionsWithError:(FlutterError *_Nullable *_Nonnull)error { +- (nullable NSArray *)transactionsWithError: + (FlutterError *_Nullable *_Nonnull)error { NSArray *transactions = [self.paymentQueueHandler getUnfinishedTransactions]; NSMutableArray *transactionMaps = [[NSMutableArray alloc] init]; for (SKPaymentTransaction *transaction in transactions) { [transactionMaps addObject:[FIAObjectTranslator convertTransactionToPigeon:transaction]]; } - return(transactionMaps); + return (transactionMaps); } -- (void)addPayment:(nonnull NSDictionary *)paymentMap error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (void)addPayment:(nonnull NSDictionary *)paymentMap + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { NSLog(@"Here"); NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; // When a product is already fetched, we create a payment object with @@ -435,41 +438,41 @@ - (void)addPayment:(nonnull NSDictionary *)paymentMap error:(FlutterError * _Nul SKProduct *product = [self getProduct:productID]; if (!product) { *error = [FlutterError - errorWithCode:@"storekit_invalid_payment_object" - message: - @"You have requested a payment for an invalid product. Either the " - @"`productIdentifier` of the payment is not valid or the product has not been " - @"fetched before adding the payment to the payment queue." - details:paymentMap]; + errorWithCode:@"storekit_invalid_payment_object" + message: + @"You have requested a payment for an invalid product. Either the " + @"`productIdentifier` of the payment is not valid or the product has not been " + @"fetched before adding the payment to the payment queue." + details:paymentMap]; return; } - + SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; payment.quantity = (quantity != nil) ? quantity.integerValue : 1; NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] - ? NO - : [simulatesAskToBuyInSandbox boolValue]; - + ? NO + : [simulatesAskToBuyInSandbox boolValue]; + if (@available(iOS 12.2, *)) { NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap forKey:@"paymentDiscount"]; NSString *errorMsg = nil; SKPaymentDiscount *paymentDiscount = - [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&errorMsg]; - + [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&errorMsg]; + if (errorMsg) { *error = [FlutterError - errorWithCode:@"storekit_invalid_payment_discount_object" - message:[NSString stringWithFormat:@"You have requested a payment and specified a " - @"payment discount with invalid properties. %@", - errorMsg] - details:paymentMap]; + errorWithCode:@"storekit_invalid_payment_discount_object" + message:[NSString stringWithFormat:@"You have requested a payment and specified a " + @"payment discount with invalid properties. %@", + errorMsg] + details:paymentMap]; return; } - + payment.paymentDiscount = paymentDiscount; } if (![self.paymentQueueHandler addPayment:payment]) { 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 d9f19a1468a..4fd9a21af34 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 @@ -58,69 +58,68 @@ typedef NS_ENUM(NSUInteger, SKPaymentTransactionStateMessage) { /// `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:(NSNumber *)quantity - simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; -@property(nonatomic, copy) NSString * productIdentifier; -@property(nonatomic, copy, nullable) NSString * applicationUsername; -@property(nonatomic, copy, nullable) NSString * requestData; -@property(nonatomic, strong) NSNumber * quantity; -@property(nonatomic, strong) NSNumber * simulatesAskToBuyInSandbox; -@property(nonatomic, strong, nullable) SKPaymentDiscountMessage * paymentDiscount; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSNumber *)quantity + simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; +@property(nonatomic, copy) NSString *productIdentifier; +@property(nonatomic, copy, nullable) NSString *applicationUsername; +@property(nonatomic, copy, nullable) NSString *requestData; +@property(nonatomic, strong) NSNumber *quantity; +@property(nonatomic, strong) NSNumber *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:(NSNumber *)code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo; -@property(nonatomic, strong) NSNumber * code; -@property(nonatomic, copy) NSString * domain; -@property(nonatomic, strong) NSDictionary * userInfo; + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo; +@property(nonatomic, strong) NSNumber *code; +@property(nonatomic, copy) NSString *domain; +@property(nonatomic, strong) 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:(NSNumber *)timestamp; -@property(nonatomic, copy) NSString * identifier; -@property(nonatomic, copy) NSString * keyIdentifier; -@property(nonatomic, copy) NSString * nonce; -@property(nonatomic, copy) NSString * signature; -@property(nonatomic, strong) NSNumber * timestamp; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSNumber *)timestamp; +@property(nonatomic, copy) NSString *identifier; +@property(nonatomic, copy) NSString *keyIdentifier; +@property(nonatomic, copy) NSString *nonce; +@property(nonatomic, copy) NSString *signature; +@property(nonatomic, strong) NSNumber *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 /// The codec used by InAppPurchaseAPI. @@ -132,12 +131,15 @@ 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)addPayment:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPayment:(NSDictionary *)paymentMap + error:(FlutterError *_Nullable *_Nonnull)error; @end -extern void InAppPurchaseAPISetup(id binaryMessenger, NSObject *_Nullable api); +extern void InAppPurchaseAPISetup(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 1eb57663bfd..8800d913886 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 @@ -68,12 +68,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; @@ -87,7 +87,8 @@ + (SKPaymentTransactionMessage *)fromList:(NSArray *)list { pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; NSAssert(pigeonResult.payment != nil, @""); 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))]; @@ -110,12 +111,12 @@ - (NSArray *)toList { @implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSNumber *)quantity - simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { - SKPaymentMessage* pigeonResult = [[SKPaymentMessage alloc] init]; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSNumber *)quantity + simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { + SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.applicationUsername = applicationUsername; pigeonResult.requestData = requestData; @@ -134,7 +135,8 @@ + (SKPaymentMessage *)fromList:(NSArray *)list { NSAssert(pigeonResult.quantity != nil, @""); pigeonResult.simulatesAskToBuyInSandbox = GetNullableObjectAtIndex(list, 4); NSAssert(pigeonResult.simulatesAskToBuyInSandbox != nil, @""); - pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.paymentDiscount = + [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } + (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { @@ -154,9 +156,9 @@ - (NSArray *)toList { @implementation SKErrorMessage + (instancetype)makeWithCode:(NSNumber *)code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo { - SKErrorMessage* pigeonResult = [[SKErrorMessage alloc] init]; + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo { + SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; pigeonResult.userInfo = userInfo; @@ -186,11 +188,11 @@ - (NSArray *)toList { @implementation SKPaymentDiscountMessage + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSNumber *)timestamp { - SKPaymentDiscountMessage* pigeonResult = [[SKPaymentDiscountMessage alloc] init]; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSNumber *)timestamp { + SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; pigeonResult.nonce = nonce; @@ -227,9 +229,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; @@ -258,15 +259,15 @@ @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 [SKStorefrontMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -314,22 +315,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 InAppPurchaseAPISetup(id binaryMessenger, NSObject *api) { +void InAppPurchaseAPISetup(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]; @@ -340,13 +346,14 @@ void InAppPurchaseAPISetup(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]; @@ -357,13 +364,14 @@ void InAppPurchaseAPISetup(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]; @@ -374,13 +382,13 @@ void InAppPurchaseAPISetup(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(addPayment:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPayment:error:)", api); + NSCAssert([api respondsToSelector:@selector(addPayment:error:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPayment:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_paymentMap = GetNullableObjectAtIndex(args, 0); 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 fb163da04bb..0115675abba 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 @@ -54,18 +54,18 @@ - (void)testPaymentQueueStorefront { @"countryCode" : @"USA", @"identifier" : @"unique_identifier", }; - + OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]); self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; FlutterError *error; SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; @@ -85,14 +85,14 @@ - (void)testPaymentQueueStorefrontReturnsNil { OCMStub(mockQueue.storefront).andReturn(nil); self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil - transactionCache:OCMClassMock(FIATransactionCache.class)]; + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; FlutterError *error; SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; @@ -140,11 +140,10 @@ - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); - XCTAssertEqualObjects( - @"There is a pending transaction for the same product identifier. " - @"Please either wait for it to be finished or finish it manually " - @"using `completePurchase` to avoid edge cases.", - error.message); + XCTAssertEqualObjects(@"There is a pending transaction for the same product identifier. " + @"Please either wait for it to be finished or finish it manually " + @"using `completePurchase` to avoid edge cases.", + error.message); XCTAssertEqualObjects(arguments, error.details); } @@ -189,27 +188,26 @@ - (void)testAddPaymentSuccessWithPaymentDiscount { [self.plugin addPayment:arguments error:&error]; XCTAssertNil(error); OCMVerify( - times(1), - [mockHandler - addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { - SKPayment *payment = obj; - if (@available(iOS 12.2, *)) { - SKPaymentDiscount *discount = payment.paymentDiscount; - - return [discount.identifier isEqual:@"test_identifier"] && - [discount.keyIdentifier isEqual:@"test_key_identifier"] && - [discount.nonce - isEqual:[[NSUUID alloc] - initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && - [discount.signature isEqual:@"test_signature"] && - [discount.timestamp isEqual:@(1635847102)]; - } - - return YES; - }]]); + times(1), + [mockHandler + addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + if (@available(iOS 12.2, *)) { + SKPaymentDiscount *discount = payment.paymentDiscount; + + return [discount.identifier isEqual:@"test_identifier"] && + [discount.keyIdentifier isEqual:@"test_key_identifier"] && + [discount.nonce + isEqual:[[NSUUID alloc] + initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && + [discount.signature isEqual:@"test_signature"] && + [discount.timestamp isEqual:@(1635847102)]; + } + + return YES; + }]]); } - - (void)testAddPaymentFailureWithInvalidPaymentDiscount { // Support for payment discount is only available on iOS 12.2 and higher. if (@available(iOS 12.2, *)) { @@ -239,10 +237,9 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { [self.plugin addPayment:arguments error:&error]; XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); - XCTAssertEqualObjects( - @"You have requested a payment and specified a " - @"payment discount with invalid properties. Some error occurred", - error.message); + XCTAssertEqualObjects(@"You have requested a payment and specified a " + @"payment discount with invalid properties. Some error occurred", + error.message); XCTAssertEqualObjects(arguments, error.details); OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); } @@ -407,18 +404,20 @@ - (void)testGetPendingTransactions { updatedDownloads:nil transactionCache:OCMClassMock(FIATransactionCache.class)]; FlutterError *error; - SKPaymentTransactionStub *original = [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; - - SKPaymentTransactionMessage *originalPigeon = [FIAObjectTranslator convertTransactionToPigeon:original]; + SKPaymentTransactionStub *original = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + + SKPaymentTransactionMessage *originalPigeon = + [FIAObjectTranslator convertTransactionToPigeon:original]; SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; // How should I test this nicely without overriding isEquals? -// XCTAssertEqualObjects(result.payment, originalPigeon.payment); + // XCTAssertEqualObjects(result.payment, originalPigeon.payment); XCTAssertEqual(result.transactionState, originalPigeon.transactionState); XCTAssertEqualObjects(result.originalTransaction, originalPigeon.originalTransaction); XCTAssertEqualObjects(result.transactionTimeStamp, originalPigeon.transactionTimeStamp); XCTAssertEqualObjects(result.transactionIdentifier, originalPigeon.transactionIdentifier); -// XCTAssertEqualObjects(result.error, originalPigeon.error); + // XCTAssertEqualObjects(result.error, originalPigeon.error); } - (void)testStartObservingPaymentQueue { 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 a7aa235ca5c..b4b1077fc98 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,27 +15,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, } @@ -77,7 +82,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, @@ -263,15 +269,15 @@ 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 SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -292,10 +298,10 @@ class InAppPurchaseAPI { /// Returns if the current device is able to make payments Future canMakePayments() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', + codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; + final List? replyList = await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -319,10 +325,10 @@ class InAppPurchaseAPI { Future> transactions() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', + codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; + final List? replyList = await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -340,16 +346,17 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as List?)!.cast(); + return (replyList[0] as List?)! + .cast(); } } Future storefront() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', + codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; + final List? replyList = await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -373,7 +380,8 @@ class InAppPurchaseAPI { Future addPayment(Map arg_paymentMap) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', + codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_paymentMap]) as List?; 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 9e72a506652..2fe04ff74e7 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 @@ -67,13 +67,16 @@ class SKPaymentQueueWrapper { Future> transactions() async { // return _getTransactionList((await channel // .invokeListMethod('-[SKPaymentQueue transactions]'))!); - final List pigeonMsgs = await _hostApi.transactions(); - return pigeonMsgs.map((SKPaymentTransactionMessage? msg) => SKPaymentTransactionWrapper.convertFromPigeon(msg!)).toList(); + final List pigeonMsgs = + await _hostApi.transactions(); + return pigeonMsgs + .map((SKPaymentTransactionMessage? msg) => + SKPaymentTransactionWrapper.convertFromPigeon(msg!)) + .toList(); } /// Calls [`-[SKPaymentQueue canMakePayments:]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506139-canmakepayments?language=objc). - static Future canMakePayments() async => - _hostApi.canMakePayments(); + static Future canMakePayments() async => _hostApi.canMakePayments(); /// Sets an observer to listen to all incoming transaction events. /// @@ -395,11 +398,7 @@ class SKError { ); static SKError convertFromPigeon(SKErrorMessage msg) { - return SKError( - code: msg.code, - domain: msg.domain, - userInfo: msg.userInfo - ); + return SKError(code: msg.code, domain: msg.domain, userInfo: msg.userInfo); } } @@ -519,15 +518,15 @@ class SKPaymentWrapper { @override String toString() => _$SKPaymentWrapperToJson(this).toString(); - static SKPaymentWrapper convertFromPigeon (SKPaymentMessage msg) { + static SKPaymentWrapper convertFromPigeon(SKPaymentMessage msg) { return SKPaymentWrapper( - productIdentifier: msg.productIdentifier, - applicationUsername: msg.applicationUsername, - quantity: msg.quantity, - simulatesAskToBuyInSandbox: msg.simulatesAskToBuyInSandbox, - requestData: msg.requestData, - paymentDiscount: SKPaymentDiscountWrapper.convertFromPigeon(msg.paymentDiscount) - ); + productIdentifier: msg.productIdentifier, + applicationUsername: msg.applicationUsername, + quantity: msg.quantity, + simulatesAskToBuyInSandbox: msg.simulatesAskToBuyInSandbox, + requestData: msg.requestData, + paymentDiscount: + SKPaymentDiscountWrapper.convertFromPigeon(msg.paymentDiscount)); } } @@ -628,7 +627,8 @@ class SKPaymentDiscountWrapper { int get hashCode => Object.hash(identifier, keyIdentifier, nonce, signature, timestamp); - static SKPaymentDiscountWrapper? convertFromPigeon(SKPaymentDiscountMessage? msg) { + static SKPaymentDiscountWrapper? convertFromPigeon( + SKPaymentDiscountMessage? msg) { if (msg == null) { return null; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart index 0d9bab8ba91..4843e734356 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart @@ -94,7 +94,8 @@ enum SKPaymentTransactionStateWrapper { unspecified; // TODO(louisehsu): maybe remove this lol - static SKPaymentTransactionStateWrapper convertFromPigeon(SKPaymentTransactionStateMessage msg) { + static SKPaymentTransactionStateWrapper convertFromPigeon( + SKPaymentTransactionStateMessage msg) { switch (msg) { case SKPaymentTransactionStateMessage.purchased: return SKPaymentTransactionStateWrapper.purchased; @@ -219,14 +220,18 @@ class SKPaymentTransactionWrapper { 'productIdentifier': payment.productIdentifier, }; - static SKPaymentTransactionWrapper convertFromPigeon(SKPaymentTransactionMessage msg) { + static SKPaymentTransactionWrapper convertFromPigeon( + SKPaymentTransactionMessage msg) { return SKPaymentTransactionWrapper( - payment: SKPaymentWrapper.convertFromPigeon(msg.payment), - transactionState: SKPaymentTransactionStateWrapper.convertFromPigeon(msg.transactionState), - originalTransaction: msg.originalTransaction == null ? null : convertFromPigeon(msg.originalTransaction!), - transactionTimeStamp: msg.transactionTimeStamp, - transactionIdentifier: msg.transactionIdentifier, - error: msg.error == null ? null : SKError.convertFromPigeon(msg.error!) - ); + payment: SKPaymentWrapper.convertFromPigeon(msg.payment), + transactionState: SKPaymentTransactionStateWrapper.convertFromPigeon( + msg.transactionState), + originalTransaction: msg.originalTransaction == null + ? null + : convertFromPigeon(msg.originalTransaction!), + transactionTimeStamp: msg.transactionTimeStamp, + transactionIdentifier: msg.transactionIdentifier, + error: + msg.error == null ? null : SKError.convertFromPigeon(msg.error!)); } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart index 3064e1858cf..2ba25e08616 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart @@ -71,8 +71,6 @@ class SKStorefrontWrapper { /// Converts the pigeon equivalent to an instance of SKStorefrontWrapper static SKStorefrontWrapper convertFromPigeon(SKStorefrontMessage msg) { return SKStorefrontWrapper( - countryCode: msg.countryCode, - identifier: msg.identifier - ); + countryCode: msg.countryCode, identifier: msg.identifier); } } 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 ed6e189bc13..fe17a649865 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 @@ -10,7 +10,6 @@ import 'package:pigeon/pigeon.dart'; objcSourceOut: 'darwin/Classes/messages.g.m', copyrightHeader: 'pigeons/copyright.txt', )) - class SKPaymentTransactionMessage { SKPaymentTransactionMessage({ required this.payment, @@ -147,5 +146,3 @@ abstract class InAppPurchaseAPI { @ObjCSelector('addPayment:') void addPayment(Map paymentMap); } - - From 5ddd517ab663b3730c6cdb0d637579881073f09a Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 18 Jan 2024 12:49:01 -0800 Subject: [PATCH 09/31] pr updates --- .../darwin/Classes/FIAObjectTranslator.m | 29 +++++--------- .../darwin/Classes/InAppPurchasePlugin.m | 40 +++++-------------- .../RunnerTests/InAppPurchasePluginTests.m | 33 +++++++-------- 3 files changed, 37 insertions(+), 65 deletions(-) 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 63a4a79ffb1..0e88f310686 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 @@ -322,35 +322,28 @@ + (SKPaymentTransactionStateMessage)convertTransactionStateToPigeon: switch (state) { case SKPaymentTransactionStatePurchasing: return SKPaymentTransactionStateMessagePurchasing; - break; case SKPaymentTransactionStatePurchased: return SKPaymentTransactionStateMessagePurchased; - break; case SKPaymentTransactionStateFailed: return SKPaymentTransactionStateMessageFailed; - break; case SKPaymentTransactionStateRestored: return SKPaymentTransactionStateMessageRestored; - break; case SKPaymentTransactionStateDeferred: return SKPaymentTransactionStateMessageDeferred; - break; } } -+ (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment { - if (@available(iOS 12.2, *)) { - SKPaymentMessage *msg = [SKPaymentMessage - makeWithProductIdentifier:payment.productIdentifier - applicationUsername:payment.applicationUsername - requestData:[[NSString alloc] initWithData:payment.requestData - encoding:NSUTF8StringEncoding] - quantity:@(payment.quantity) - simulatesAskToBuyInSandbox:@(payment.simulatesAskToBuyInSandbox) - paymentDiscount:[self convertPaymentDiscountToPigeon:payment.paymentDiscount]]; - return msg; - } - return nil; ++ (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment + API_AVAILABLE(ios(12.2)) { + SKPaymentMessage *msg = [SKPaymentMessage + makeWithProductIdentifier:payment.productIdentifier + applicationUsername:payment.applicationUsername + requestData:[[NSString alloc] initWithData:payment.requestData + encoding:NSUTF8StringEncoding] + quantity:@(payment.quantity) + simulatesAskToBuyInSandbox:@(payment.simulatesAskToBuyInSandbox) + paymentDiscount:[self convertPaymentDiscountToPigeon:payment.paymentDiscount]]; + return msg; } + (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:(SKPaymentDiscount *)discount 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 97d24ef0327..9410095a785 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 @@ -41,13 +41,11 @@ + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase" binaryMessenger:[registrar messenger]]; - // InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; - // SetUpInAppPurchaseAPI([registrar messenger], instance); InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; [registrar addMethodCallDelegate:instance channel:channel]; [registrar addApplicationDelegate:instance]; - InAppPurchaseAPISetup([registrar messenger], instance); + InAppPurchaseAPISetup(registrar.messenger, instance); } - (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { @@ -125,18 +123,20 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result } } -- (void)canMakePayments:(FlutterResult)result { - result(@([SKPaymentQueue canMakePayments])); +- (nullable NSNumber *)canMakePaymentsWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return @([SKPaymentQueue canMakePayments]); } -- (void)getPendingTransactions:(FlutterResult)result { +- (nullable NSArray *)transactionsWithError: + (FlutterError *_Nullable *_Nonnull)error { NSArray *transactions = [self.paymentQueueHandler getUnfinishedTransactions]; NSMutableArray *transactionMaps = [[NSMutableArray alloc] init]; for (SKPaymentTransaction *transaction in transactions) { - [transactionMaps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:transaction]]; + [transactionMaps addObject:[FIAObjectTranslator convertTransactionToPigeon:transaction]]; } - result(transactionMaps); + return (transactionMaps); } - (void)getStorefront:(FlutterResult)result { @@ -400,38 +400,16 @@ - (SKReceiptRefreshRequest *)getRefreshReceiptRequest:(NSDictionary *)properties return [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:properties]; } -- (nullable NSNumber *)canMakePaymentsWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - return @([SKPaymentQueue canMakePayments]); -} - -- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error { - if (@available(iOS 13.0, macOS 10.15, *)) { +- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error API_AVAILABLE(ios(13.0), macos(10.15)) { SKStorefront *storefront = self.paymentQueueHandler.storefront; if (!storefront) { return nil; } return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; - } else { - NSLog(@"storefront is not avaialbe in iOS below 13.0 or macOS below 10.15."); - return nil; } -} - -- (nullable NSArray *)transactionsWithError: - (FlutterError *_Nullable *_Nonnull)error { - NSArray *transactions = - [self.paymentQueueHandler getUnfinishedTransactions]; - NSMutableArray *transactionMaps = [[NSMutableArray alloc] init]; - for (SKPaymentTransaction *transaction in transactions) { - [transactionMaps addObject:[FIAObjectTranslator convertTransactionToPigeon:transaction]]; - } - return (transactionMaps); -} - (void)addPayment:(nonnull NSDictionary *)paymentMap error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - NSLog(@"Here"); NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; // When a product is already fetched, we create a payment object with // the product to process the payment. 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 0115675abba..09d7e22bfbc 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 @@ -68,10 +68,10 @@ - (void)testPaymentQueueStorefront { transactionCache:OCMClassMock(FIATransactionCache.class)]; FlutterError *error; - SKStorefrontMessage *resultMap = [self.plugin storefrontWithError:&error]; + SKStorefrontMessage *result = [self.plugin storefrontWithError:&error]; - XCTAssertEqualObjects(resultMap.countryCode, storefrontMap[@"countryCode"]); - XCTAssertEqualObjects(resultMap.identifier, storefrontMap[@"identifier"]); + XCTAssertEqualObjects(result.countryCode, storefrontMap[@"countryCode"]); + XCTAssertEqualObjects(result.identifier, storefrontMap[@"identifier"]); XCTAssertNil(error); } else { NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0 or macOS lower than 10.15."); @@ -124,7 +124,7 @@ - (void)testGetProductResponse { } - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { - NSDictionary *arguments = @{ + NSDictionary *argument = @{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, @@ -136,7 +136,7 @@ - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { FlutterError *error; - [self.plugin addPayment:arguments error:&error]; + [self.plugin addPayment:argument error:&error]; OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); @@ -144,11 +144,11 @@ - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { @"Please either wait for it to be finished or finish it manually " @"using `completePurchase` to avoid edge cases.", error.message); - XCTAssertEqualObjects(arguments, error.details); + XCTAssertEqualObjects(argument, error.details); } - (void)testAddPaymentSuccessWithoutPaymentDiscount { - NSDictionary *arguments = @{ + NSDictionary *argument = @{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, @@ -160,13 +160,13 @@ - (void)testAddPaymentSuccessWithoutPaymentDiscount { FlutterError *error; - [self.plugin addPayment:arguments error:&error]; + [self.plugin addPayment:argument error:&error]; XCTAssertNil(error); OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); } - (void)testAddPaymentSuccessWithPaymentDiscount { - NSDictionary *arguments = @{ + NSDictionary *argument = @{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, @@ -185,7 +185,7 @@ - (void)testAddPaymentSuccessWithPaymentDiscount { FlutterError *error; - [self.plugin addPayment:arguments error:&error]; + [self.plugin addPayment:argument error:&error]; XCTAssertNil(error); OCMVerify( times(1), @@ -211,7 +211,7 @@ - (void)testAddPaymentSuccessWithPaymentDiscount { - (void)testAddPaymentFailureWithInvalidPaymentDiscount { // Support for payment discount is only available on iOS 12.2 and higher. if (@available(iOS 12.2, *)) { - NSDictionary *arguments = @{ + NSDictionary *argument = @{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, @@ -234,19 +234,19 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { self.plugin.paymentQueueHandler = mockHandler; FlutterError *error; - [self.plugin addPayment:arguments error:&error]; + [self.plugin addPayment:argument error:&error]; XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); XCTAssertEqualObjects(@"You have requested a payment and specified a " @"payment discount with invalid properties. Some error occurred", error.message); - XCTAssertEqualObjects(arguments, error.details); + XCTAssertEqualObjects(argument, error.details); OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); } } - (void)testAddPaymentWithNullSandboxArgument { - NSDictionary *arguments = @{ + NSDictionary *argument = @{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : [NSNull null], @@ -257,7 +257,7 @@ - (void)testAddPaymentWithNullSandboxArgument { self.plugin.paymentQueueHandler = mockHandler; FlutterError *error; - [self.plugin addPayment:arguments error:&error]; + [self.plugin addPayment:argument error:&error]; OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { SKPayment *payment = obj; return !payment.simulatesAskToBuyInSandbox; @@ -393,7 +393,6 @@ - (void)testGetPendingTransactions { OCMStub(mockQueue.transactions).andReturn(@[ [[SKPaymentTransactionStub alloc] initWithMap:transactionMap] ]); - __block NSArray *resultArray; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue transactionsUpdated:nil @@ -411,6 +410,8 @@ - (void)testGetPendingTransactions { [FIAObjectTranslator convertTransactionToPigeon:original]; SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; + + XCTAssertEqualObjects([result.payment encode], [originalPigeon.payment encode]) // How should I test this nicely without overriding isEquals? // XCTAssertEqualObjects(result.payment, originalPigeon.payment); XCTAssertEqual(result.transactionState, originalPigeon.transactionState); From 7a47ab8c34c21920434d805e711934c314d5e388 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 18 Jan 2024 14:18:46 -0800 Subject: [PATCH 10/31] analyze --- .../RunnerTests/InAppPurchasePluginTests.m | 16 +++++++--------- .../sk_payment_queue_wrapper.dart | 13 +++++-------- .../sk_payment_transaction_wrappers.dart | 3 ++- .../in_app_purchase_storekit/pubspec.yaml | 2 +- 4 files changed, 15 insertions(+), 19 deletions(-) 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 09d7e22bfbc..46aa53673b3 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 @@ -16,6 +16,10 @@ @interface InAppPurchasePluginTest : XCTestCase @end +@interface SKPaymentTransactionMessage () +- (NSArray *)toList; +@end + @implementation InAppPurchasePluginTest - (void)setUp { @@ -410,15 +414,9 @@ - (void)testGetPendingTransactions { [FIAObjectTranslator convertTransactionToPigeon:original]; SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; - - XCTAssertEqualObjects([result.payment encode], [originalPigeon.payment encode]) - // How should I test this nicely without overriding isEquals? - // XCTAssertEqualObjects(result.payment, originalPigeon.payment); - XCTAssertEqual(result.transactionState, originalPigeon.transactionState); - XCTAssertEqualObjects(result.originalTransaction, originalPigeon.originalTransaction); - XCTAssertEqualObjects(result.transactionTimeStamp, originalPigeon.transactionTimeStamp); - XCTAssertEqualObjects(result.transactionIdentifier, originalPigeon.transactionIdentifier); - // XCTAssertEqualObjects(result.error, originalPigeon.error); + // Using a private pigeon method to avoid extraneous tests? + XCTAssertTrue([result respondsToSelector:@selector(toList)]); + XCTAssertEqualObjects([result toList], [originalPigeon toList]); } - (void)testStartObservingPaymentQueue { 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 2fe04ff74e7..6b63c00c8c9 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 @@ -30,10 +30,12 @@ part 'sk_payment_queue_wrapper.g.dart'; InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); @visibleForTesting +/// void setInAppPurchaseHostApi(InAppPurchaseAPI api) { _hostApi = api; } +/// class SKPaymentQueueWrapper { /// Returns the default payment queue. /// @@ -54,19 +56,11 @@ class SKPaymentQueueWrapper { /// /// Returns `null` if the user's device is below iOS 13.0 or macOS 10.15. Future storefront() async { - // final Map? storefrontMap = await channel - // .invokeMapMethod('-[SKPaymentQueue storefront]'); - // if (storefrontMap == null) { - // return null; - // } - // return SKStorefrontWrapper.fromJson(storefrontMap); return SKStorefrontWrapper.convertFromPigeon(await _hostApi.storefront()); } /// Calls [`-[SKPaymentQueue transactions]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506026-transactions?language=objc). Future> transactions() async { - // return _getTransactionList((await channel - // .invokeListMethod('-[SKPaymentQueue transactions]'))!); final List pigeonMsgs = await _hostApi.transactions(); return pigeonMsgs @@ -397,6 +391,7 @@ class SKError { userInfo, ); + /// Converts [SKErrorMessage] into the dart equivalent static SKError convertFromPigeon(SKErrorMessage msg) { return SKError(code: msg.code, domain: msg.domain, userInfo: msg.userInfo); } @@ -518,6 +513,7 @@ class SKPaymentWrapper { @override String toString() => _$SKPaymentWrapperToJson(this).toString(); + /// Converts [SKPaymentMessage] into the dart equivalent static SKPaymentWrapper convertFromPigeon(SKPaymentMessage msg) { return SKPaymentWrapper( productIdentifier: msg.productIdentifier, @@ -627,6 +623,7 @@ class SKPaymentDiscountWrapper { int get hashCode => Object.hash(identifier, keyIdentifier, nonce, signature, timestamp); + /// Converts [SKPaymentDiscountMessage] into the dart equivalent static SKPaymentDiscountWrapper? convertFromPigeon( SKPaymentDiscountMessage? msg) { if (msg == null) { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart index 4843e734356..7f16e1492a4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart @@ -93,7 +93,7 @@ enum SKPaymentTransactionStateWrapper { @JsonValue(-1) unspecified; - // TODO(louisehsu): maybe remove this lol + /// Converts [SKPaymentTransactionStateMessages] into the dart equivalent static SKPaymentTransactionStateWrapper convertFromPigeon( SKPaymentTransactionStateMessage msg) { switch (msg) { @@ -220,6 +220,7 @@ class SKPaymentTransactionWrapper { 'productIdentifier': payment.productIdentifier, }; + /// Converts [SKPaymentTransactionMessages] into the dart equivalent static SKPaymentTransactionWrapper convertFromPigeon( SKPaymentTransactionMessage msg) { return SKPaymentTransactionWrapper( 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 81f9177798f..011f96f3806 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -31,8 +31,8 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 - test: ^1.16.0 pigeon: ^11.0.1 + test: ^1.16.0 topics: - in-app-purchase From d434931d418c455adf4013b8db786e79bb9388fa Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Fri, 19 Jan 2024 01:59:50 -0800 Subject: [PATCH 11/31] fix tests --- .../darwin/Classes/messages.g.h | 86 ++++++------ .../darwin/Classes/messages.g.m | 112 +++++++-------- .../lib/src/messages.g.dart | 42 +++--- .../sk_payment_queue_wrapper.dart | 4 - .../pigeons/messages.dart | 3 +- .../in_app_purchase_storekit/pubspec.yaml | 1 + .../test/fakes/fake_storekit_platform.dart | 76 ++++++++-- ...rchase_storekit_platform_addtion_test.dart | 2 + ...n_app_purchase_storekit_platform_test.dart | 30 ++-- .../sk_methodchannel_apis_test.dart | 59 ++++---- .../sk_test_stub_objects.dart | 29 ++++ .../test/test_api.g.dart | 132 ++++++++++++++++++ 12 files changed, 386 insertions(+), 190 deletions(-) create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart 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 4fd9a21af34..d9f19a1468a 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 @@ -58,68 +58,69 @@ typedef NS_ENUM(NSUInteger, SKPaymentTransactionStateMessage) { /// `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:(NSNumber *)quantity - simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; -@property(nonatomic, copy) NSString *productIdentifier; -@property(nonatomic, copy, nullable) NSString *applicationUsername; -@property(nonatomic, copy, nullable) NSString *requestData; -@property(nonatomic, strong) NSNumber *quantity; -@property(nonatomic, strong) NSNumber *simulatesAskToBuyInSandbox; -@property(nonatomic, strong, nullable) SKPaymentDiscountMessage *paymentDiscount; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSNumber *)quantity + simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; +@property(nonatomic, copy) NSString * productIdentifier; +@property(nonatomic, copy, nullable) NSString * applicationUsername; +@property(nonatomic, copy, nullable) NSString * requestData; +@property(nonatomic, strong) NSNumber * quantity; +@property(nonatomic, strong) NSNumber * 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:(NSNumber *)code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo; -@property(nonatomic, strong) NSNumber *code; -@property(nonatomic, copy) NSString *domain; -@property(nonatomic, strong) NSDictionary *userInfo; + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo; +@property(nonatomic, strong) NSNumber * code; +@property(nonatomic, copy) NSString * domain; +@property(nonatomic, strong) 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:(NSNumber *)timestamp; -@property(nonatomic, copy) NSString *identifier; -@property(nonatomic, copy) NSString *keyIdentifier; -@property(nonatomic, copy) NSString *nonce; -@property(nonatomic, copy) NSString *signature; -@property(nonatomic, strong) NSNumber *timestamp; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSNumber *)timestamp; +@property(nonatomic, copy) NSString * identifier; +@property(nonatomic, copy) NSString * keyIdentifier; +@property(nonatomic, copy) NSString * nonce; +@property(nonatomic, copy) NSString * signature; +@property(nonatomic, strong) NSNumber * 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 /// The codec used by InAppPurchaseAPI. @@ -131,15 +132,12 @@ 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)addPayment:(NSDictionary *)paymentMap - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPayment:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; @end -extern void InAppPurchaseAPISetup(id binaryMessenger, - NSObject *_Nullable api); +extern void InAppPurchaseAPISetup(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 8800d913886..1eb57663bfd 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 @@ -68,12 +68,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; @@ -87,8 +87,7 @@ + (SKPaymentTransactionMessage *)fromList:(NSArray *)list { pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; NSAssert(pigeonResult.payment != nil, @""); 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))]; @@ -111,12 +110,12 @@ - (NSArray *)toList { @implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSNumber *)quantity - simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { - SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSNumber *)quantity + simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { + SKPaymentMessage* pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.applicationUsername = applicationUsername; pigeonResult.requestData = requestData; @@ -135,8 +134,7 @@ + (SKPaymentMessage *)fromList:(NSArray *)list { NSAssert(pigeonResult.quantity != nil, @""); pigeonResult.simulatesAskToBuyInSandbox = GetNullableObjectAtIndex(list, 4); NSAssert(pigeonResult.simulatesAskToBuyInSandbox != nil, @""); - pigeonResult.paymentDiscount = - [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } + (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { @@ -156,9 +154,9 @@ - (NSArray *)toList { @implementation SKErrorMessage + (instancetype)makeWithCode:(NSNumber *)code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo { - SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo { + SKErrorMessage* pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; pigeonResult.userInfo = userInfo; @@ -188,11 +186,11 @@ - (NSArray *)toList { @implementation SKPaymentDiscountMessage + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSNumber *)timestamp { - SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSNumber *)timestamp { + SKPaymentDiscountMessage* pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; pigeonResult.nonce = nonce; @@ -229,8 +227,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; @@ -259,15 +258,15 @@ @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 [SKStorefrontMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -315,27 +314,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 InAppPurchaseAPISetup(id binaryMessenger, - NSObject *api) { +void InAppPurchaseAPISetup(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]; @@ -346,14 +340,13 @@ void InAppPurchaseAPISetup(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]; @@ -364,14 +357,13 @@ void InAppPurchaseAPISetup(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]; @@ -382,13 +374,13 @@ void InAppPurchaseAPISetup(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(addPayment:error:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPayment:error:)", api); + NSCAssert([api respondsToSelector:@selector(addPayment:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPayment:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_paymentMap = GetNullableObjectAtIndex(args, 0); 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 b4b1077fc98..a7aa235ca5c 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,32 +15,27 @@ 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, } @@ -82,8 +77,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, @@ -269,15 +263,15 @@ 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 SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -298,10 +292,10 @@ class InAppPurchaseAPI { /// Returns if the current device is able to make payments Future canMakePayments() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', - codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send(null) as List?; + final List? replyList = + await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -325,10 +319,10 @@ class InAppPurchaseAPI { Future> transactions() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', - codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send(null) as List?; + final List? replyList = + await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -346,17 +340,16 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as List?)! - .cast(); + return (replyList[0] as List?)!.cast(); } } Future storefront() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', - codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send(null) as List?; + final List? replyList = + await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -380,8 +373,7 @@ class InAppPurchaseAPI { Future addPayment(Map arg_paymentMap) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', - codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_paymentMap]) as List?; 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 6b63c00c8c9..b892d1a104f 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 @@ -147,10 +147,6 @@ class SKPaymentQueueWrapper { '[in_app_purchase]: Trying to add a payment without an observer. One must be set using `SkPaymentQueueWrapper.setTransactionObserver` before the app launches.'); await _hostApi.addPayment(payment.toMap()); - // await channel.invokeMethod( - // '-[InAppPurchasePlugin addPayment:result:]', - // requestMap, - // ); } /// Finishes a transaction and removes it from the queue. 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 fe17a649865..89bba4fa899 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 @@ -6,6 +6,7 @@ import 'package:pigeon/pigeon.dart'; @ConfigurePigeon(PigeonOptions( dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.g.dart', objcHeaderOut: 'darwin/Classes/messages.g.h', objcSourceOut: 'darwin/Classes/messages.g.m', copyrightHeader: 'pigeons/copyright.txt', @@ -131,7 +132,7 @@ class SKStorefrontMessage { final String identifier; } -@HostApi() +@HostApi(dartHostTestHandler: 'TestInAppPurchaseApi') abstract class InAppPurchaseAPI { /// Returns if the current device is able to make payments // @ObjCSelector('canMakePayments') 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 011f96f3806..b2c2e9fc70b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -31,6 +31,7 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 + mockito: ^5.4.4 pigeon: ^11.0.1 test: ^1.16.0 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 6ae002c8251..778c652a155 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 @@ -8,11 +8,13 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'package:in_app_purchase_storekit/src/channel.dart'; +import 'package:in_app_purchase_storekit/src/messages.g.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; import '../store_kit_wrappers/sk_test_stub_objects.dart'; +import '../test_api.g.dart'; -class FakeStoreKitPlatform { +class FakeStoreKitPlatform implements TestInAppPurchaseApi{ FakeStoreKitPlatform() { _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger @@ -23,7 +25,7 @@ class FakeStoreKitPlatform { String? receiptData; late Set validProductIDs; late Map validProducts; - late List transactions; + late List transactionList; late List finishedTransactions; late bool testRestoredTransactionsNull; late bool testTransactionFail; @@ -35,7 +37,7 @@ class FakeStoreKitPlatform { Map discountReceived = {}; void reset() { - transactions = []; + transactionList = []; receiptData = 'dummy base64data'; validProductIDs = {'123', '456'}; validProducts = {}; @@ -123,8 +125,6 @@ class FakeStoreKitPlatform { Future onMethodCall(MethodCall call) { switch (call.method) { - case '-[SKPaymentQueue canMakePayments:]': - return Future.value(true); case '-[InAppPurchasePlugin startProductRequest:result:]': if (queryProductException != null) { throw queryProductException!; @@ -155,7 +155,7 @@ class FakeStoreKitPlatform { } if (!testRestoredTransactionsNull) { InAppPurchaseStoreKitPlatform.observer - .updatedTransactions(transactions: transactions); + .updatedTransactions(transactions: transactionList); } InAppPurchaseStoreKitPlatform.observer .paymentQueueRestoreCompletedTransactionsFinished(); @@ -188,7 +188,7 @@ class FakeStoreKitPlatform { final SKPaymentTransactionWrapper transaction = createPendingTransaction(id, quantity: quantity); - transactions.add(transaction); + transactionList.add(transaction); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transaction]); sleep(const Duration(milliseconds: 30)); @@ -217,7 +217,7 @@ class FakeStoreKitPlatform { finishedTransactions.add(createPurchasedTransaction( arguments['productIdentifier']! as String, arguments['transactionIdentifier']! as String, - quantity: transactions.first.payment.quantity)); + quantity: transactionList.first.payment.quantity)); break; case '-[SKPaymentQueue startObservingTransactionQueue]': queueIsActive = true; @@ -236,6 +236,66 @@ class FakeStoreKitPlatform { Map _getArgumentDictionary(MethodCall call) { return (call.arguments as Map).cast(); } + + @override + bool canMakePayments() { + return true; + } + + @override + void addPayment(Map paymentMap) { + final String id = paymentMap['productIdentifier']! as String; + final int quantity = paymentMap['quantity']! as int; + + // Keep the received paymentDiscount parameter when testing payment with discount. + if (paymentMap['applicationUsername']! == 'userWithDiscount') { + final Map? discountArgument = + paymentMap['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]); + } + } + + @override + SKStorefrontMessage storefront() { + // TODO(louisehsu): implement storefront + throw UnimplementedError(); + } + + @override + List transactions() { + // TODO(louisehsu): implement transactions + throw UnimplementedError(); + } } /// This allows a value of type T or T? to be treated as a value of type T?. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart index 2890e7542bb..286f8a05c6a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart @@ -8,6 +8,7 @@ import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_inte import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'fakes/fake_storekit_platform.dart'; +import 'test_api.g.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -15,6 +16,7 @@ void main() { final FakeStoreKitPlatform fakeStoreKitPlatform = FakeStoreKitPlatform(); setUpAll(() { + TestInAppPurchaseApi.setup(fakeStoreKitPlatform); _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .setMockMethodCallHandler( diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart index fbb37974a20..37e80cc8b16 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart @@ -13,6 +13,7 @@ import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; import 'fakes/fake_storekit_platform.dart'; import 'store_kit_wrappers/sk_test_stub_objects.dart'; +import 'test_api.g.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -21,6 +22,7 @@ void main() { late InAppPurchaseStoreKitPlatform iapStoreKitPlatform; setUpAll(() { + TestInAppPurchaseApi.setup(fakeStoreKitPlatform); _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .setMockMethodCallHandler( @@ -80,9 +82,9 @@ void main() { group('restore purchases', () { test('should emit restored transactions on purchase stream', () async { - fakeStoreKitPlatform.transactions.insert( + fakeStoreKitPlatform.transactionList.insert( 0, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT1')); - fakeStoreKitPlatform.transactions.insert( + fakeStoreKitPlatform.transactionList.insert( 1, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); final Completer> completer = Completer>(); @@ -101,9 +103,9 @@ void main() { final List details = await completer.future; expect(details.length, 2); - for (int i = 0; i < fakeStoreKitPlatform.transactions.length; i++) { + for (int i = 0; i < fakeStoreKitPlatform.transactionList.length; i++) { final SKPaymentTransactionWrapper expected = - fakeStoreKitPlatform.transactions[i]; + fakeStoreKitPlatform.transactionList[i]; final PurchaseDetails actual = details[i]; expect(actual.purchaseID, expected.transactionIdentifier); @@ -138,11 +140,11 @@ void main() { }); test('should not block transaction updates', () async { - fakeStoreKitPlatform.transactions.insert( + fakeStoreKitPlatform.transactionList.insert( 0, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT1')); - fakeStoreKitPlatform.transactions.insert( + fakeStoreKitPlatform.transactionList.insert( 1, fakeStoreKitPlatform.createPurchasedTransaction('foo', 'bar')); - fakeStoreKitPlatform.transactions.insert( + fakeStoreKitPlatform.transactionList.insert( 2, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); final Completer> completer = Completer>(); @@ -159,9 +161,9 @@ void main() { await iapStoreKitPlatform.restorePurchases(); final List details = await completer.future; expect(details.length, 3); - for (int i = 0; i < fakeStoreKitPlatform.transactions.length; i++) { + for (int i = 0; i < fakeStoreKitPlatform.transactionList.length; i++) { final SKPaymentTransactionWrapper expected = - fakeStoreKitPlatform.transactions[i]; + fakeStoreKitPlatform.transactionList[i]; final PurchaseDetails actual = details[i]; expect(actual.purchaseID, expected.transactionIdentifier); @@ -182,7 +184,7 @@ void main() { test( 'should emit empty transaction if transactions array does not contain a transaction with PurchaseStatus.restored status.', () async { - fakeStoreKitPlatform.transactions.insert( + fakeStoreKitPlatform.transactionList.insert( 0, fakeStoreKitPlatform.createPurchasedTransaction('foo', 'bar')); final Completer>> completer = Completer>>(); @@ -204,9 +206,9 @@ void main() { final List> details = await completer.future; expect(details.length, 2); expect(details[0], >[]); - for (int i = 0; i < fakeStoreKitPlatform.transactions.length; i++) { + for (int i = 0; i < fakeStoreKitPlatform.transactionList.length; i++) { final SKPaymentTransactionWrapper expected = - fakeStoreKitPlatform.transactions[i]; + fakeStoreKitPlatform.transactionList[i]; final PurchaseDetails actual = details[1][i]; expect(actual.purchaseID, expected.transactionIdentifier); @@ -226,9 +228,9 @@ void main() { test('receipt error should populate null to verificationData.data', () async { - fakeStoreKitPlatform.transactions.insert( + fakeStoreKitPlatform.transactionList.insert( 0, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT1')); - fakeStoreKitPlatform.transactions.insert( + fakeStoreKitPlatform.transactionList.insert( 1, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); fakeStoreKitPlatform.receiptData = null; final Completer> completer = 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 f04f4b3eb3a..a34114e6f8d 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 @@ -5,7 +5,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_storekit/src/channel.dart'; +import 'package:in_app_purchase_storekit/src/messages.g.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; +import '../test_api.g.dart'; import 'sk_test_stub_objects.dart'; void main() { @@ -14,6 +16,7 @@ void main() { final FakeStoreKitPlatform fakeStoreKitPlatform = FakeStoreKitPlatform(); setUpAll(() { + TestInAppPurchaseApi.setup(fakeStoreKitPlatform); _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .setMockMethodCallHandler( @@ -102,12 +105,6 @@ void main() { expect(await SKPaymentQueueWrapper.canMakePayments(), true); }); - test('canMakePayment returns false if method channel returns null', - () async { - fakeStoreKitPlatform.testReturnNull = true; - expect(await SKPaymentQueueWrapper.canMakePayments(), false); - }); - test('storefront returns valid SKStoreFrontWrapper object', () async { final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); expect( @@ -118,12 +115,6 @@ void main() { })); }); - test('storefront returns null', () async { - fakeStoreKitPlatform.testReturnNull = true; - final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); - expect(await queue.storefront(), isNull); - }); - test('transactions should return a valid list of transactions', () async { expect(await SKPaymentQueueWrapper().transactions(), isNotEmpty); }); @@ -201,7 +192,7 @@ void main() { }); } -class FakeStoreKitPlatform { +class FakeStoreKitPlatform implements TestInAppPurchaseApi { FakeStoreKitPlatform() { _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger @@ -257,27 +248,6 @@ class FakeStoreKitPlatform { throw Exception('some arbitrary error'); } return Future.value('receipt data'); - // payment queue - case '-[SKPaymentQueue canMakePayments:]': - if (testReturnNull) { - return Future.value(); - } - return Future.value(true); - case '-[SKPaymentQueue transactions]': - return Future>.value( - [buildTransactionMap(dummyTransaction)]); - case '-[SKPaymentQueue storefront]': - if (testReturnNull) { - return Future.value(); - } - return Future>.value(const { - 'countryCode': 'USA', - 'identifier': 'unique_identifier', - }); - case '-[InAppPurchasePlugin addPayment:result:]': - payments.add(SKPaymentWrapper.fromJson(Map.from( - call.arguments as Map))); - return Future.sync(() {}); case '-[InAppPurchasePlugin finishTransaction:result:]': transactionsFinished.add( Map.from(call.arguments as Map)); @@ -306,6 +276,27 @@ class FakeStoreKitPlatform { } return Future.error('method not mocked'); } + + @override + void addPayment(Map paymentMap) { + payments.add(SKPaymentWrapper.fromJson(Map.from( + paymentMap))); + } + + @override + bool canMakePayments() { + return true; + } + + @override + SKStorefrontMessage storefront() { + return SKStorefrontMessage(countryCode: 'USA', identifier: 'unique_identifier'); + } + + @override + List transactions() => + [dummyTransactionMessage]; + } 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 6601a21c4ee..5874f0d98b6 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 @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:in_app_purchase_storekit/src/messages.g.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; const SKPaymentWrapper dummyPayment = SKPaymentWrapper( @@ -11,6 +12,14 @@ const SKPaymentWrapper dummyPayment = SKPaymentWrapper( quantity: 2, simulatesAskToBuyInSandbox: true); +SKPaymentMessage dummyPaymentMessage = SKPaymentMessage( + productIdentifier: 'prod-id', + applicationUsername: 'app-user-name', + requestData: 'fake-data-utf8', + quantity: 2, + simulatesAskToBuyInSandbox: true +); + final SKPaymentWrapper dummyPaymentWithDiscount = SKPaymentWrapper( productIdentifier: 'prod-id', applicationUsername: 'app-user-name', @@ -43,6 +52,10 @@ final SKPaymentTransactionWrapper dummyTransaction = error: dummyError, ); +final SKPaymentTransactionMessage dummyTransactionMessage = + SKPaymentTransactionMessage(payment: dummyPaymentMessage, + transactionState: SKPaymentTransactionStateMessage.purchased); + final SKPriceLocaleWrapper dollarLocale = SKPriceLocaleWrapper( currencySymbol: r'$', currencyCode: 'USD', @@ -196,6 +209,22 @@ Map buildTransactionMap( return map; } +Map buildTransactionMessage( + SKPaymentTransactionWrapper transaction) { + final Map map = { + 'transactionState': SKPaymentTransactionStateWrapper.values + .indexOf(SKPaymentTransactionStateWrapper.purchased), + 'payment': transaction.payment.toMap(), + 'originalTransaction': transaction.originalTransaction == null + ? null + : buildTransactionMap(transaction.originalTransaction!), + 'transactionTimeStamp': transaction.transactionTimeStamp, + 'transactionIdentifier': transaction.transactionIdentifier, + 'error': buildErrorMap(transaction.error!), + }; + return map; +} + final SKPaymentDiscountWrapper dummyPaymentDiscountWrapper = SKPaymentDiscountWrapper.fromJson(const { 'identifier': 'dummy-discount-identifier', 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 new file mode 100644 index 00000000000..d977b026eb4 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart @@ -0,0 +1,132 @@ +// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: avoid_relative_lib_imports +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:in_app_purchase_storekit/src/messages.g.dart'; + +class _TestInAppPurchaseApiCodec extends StandardMessageCodec { + const _TestInAppPurchaseApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is SKErrorMessage) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is SKPaymentDiscountMessage) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is SKPaymentMessage) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is SKPaymentTransactionMessage) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is SKStorefrontMessage) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return SKErrorMessage.decode(readValue(buffer)!); + case 129: + return SKPaymentDiscountMessage.decode(readValue(buffer)!); + case 130: + return SKPaymentMessage.decode(readValue(buffer)!); + case 131: + return SKPaymentTransactionMessage.decode(readValue(buffer)!); + case 132: + return SKStorefrontMessage.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestInAppPurchaseApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = _TestInAppPurchaseApiCodec(); + + /// Returns if the current device is able to make payments + bool canMakePayments(); + + List transactions(); + + SKStorefrontMessage storefront(); + + void addPayment(Map paymentMap); + + static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + // ignore message + final bool output = api.canMakePayments(); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + // ignore message + final List output = api.transactions(); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + // ignore message + final SKStorefrontMessage output = api.storefront(); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != 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(); + assert(arg_paymentMap != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null, expected non-null Map.'); + api.addPayment(arg_paymentMap!); + return []; + }); + } + } + } +} From 427181aa5d0382ebd6739accb0e4dea5953e4941 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Fri, 19 Jan 2024 16:17:15 -0800 Subject: [PATCH 12/31] changelog, silence warnings --- packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h | 2 +- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../darwin/Classes/FIAObjectTranslator.m | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h index e230a53508f..a7e22da716d 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h +++ b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN /// Queue-specific context data to be associated with the capture session queue. -extern const char *FLTCaptureSessionQueueSpecific; +extern const char* FLTCaptureSessionQueueSpecific; /// Ensures the given block to be run on the main queue. /// If caller site is already on the main queue, the block will be run 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 671ab459c9a..ec0fff1aa98 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -2,6 +2,10 @@ * Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. +## 0.3.7+1 + +* Converted `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. + ## 0.3.7 * Adds `Future storefront()` in SKPaymentQueueWrapper class. 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 0e88f310686..bbe5f03b294 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 @@ -296,7 +296,7 @@ + (SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map } + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: - (SKPaymentTransaction *)transaction { + (SKPaymentTransaction *)transaction API_AVAILABLE(ios(12.2)){ SKPaymentTransactionMessage *msg = [SKPaymentTransactionMessage makeWithPayment:[self convertPaymentToPigeon:transaction.payment] transactionState:[self convertTransactionStateToPigeon:transaction.transactionState] From 1585d3a9999c0ac00b730e8d5d6c1634b5f13a56 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 00:53:47 -0800 Subject: [PATCH 13/31] format, more tests, fix converters --- .../darwin/Classes/FIAObjectTranslator.h | 8 +- .../darwin/Classes/FIAObjectTranslator.m | 16 ++- .../darwin/Classes/InAppPurchasePlugin.m | 13 +- .../darwin/Classes/messages.g.h | 86 +++++++------- .../darwin/Classes/messages.g.m | 112 ++++++++++-------- .../shared/RunnerTests/TranslatorTests.m | 58 ++++++++- .../lib/src/messages.g.dart | 42 ++++--- .../sk_payment_queue_wrapper.dart | 1 + .../pigeons/messages.dart | 5 - .../test/fakes/fake_storekit_platform.dart | 18 +-- .../sk_methodchannel_apis_test.dart | 8 +- .../sk_test_stub_objects.dart | 8 +- .../test/test_api.g.dart | 61 ++++++---- 13 files changed, 271 insertions(+), 165 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 da4f49c362f..af5b83e294d 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 @@ -62,7 +62,13 @@ NS_ASSUME_NONNULL_BEGIN + (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(SKStorefront *)storefront API_AVAILABLE(ios(13.0)); + ++ (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:(SKPaymentDiscount *)discount + API_AVAILABLE(ios(12.2)); + ++ (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment API_AVAILABLE(ios(12.2)); + ++ (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error; @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 bbe5f03b294..36414b4cd09 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 @@ -296,13 +296,16 @@ + (SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map } + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: - (SKPaymentTransaction *)transaction API_AVAILABLE(ios(12.2)){ + (SKPaymentTransaction *)transaction API_AVAILABLE(ios(12.2)) { + if (!transaction) { + return nil; + } SKPaymentTransactionMessage *msg = [SKPaymentTransactionMessage makeWithPayment:[self convertPaymentToPigeon:transaction.payment] transactionState:[self convertTransactionStateToPigeon:transaction.transactionState] originalTransaction:transaction.originalTransaction ? [self convertTransactionToPigeon:transaction.originalTransaction] - : nil + : NULL transactionTimeStamp:[NSNumber numberWithDouble:[transaction.transactionDate timeIntervalSince1970]] transactionIdentifier:transaction.transactionIdentifier @@ -335,6 +338,9 @@ + (SKPaymentTransactionStateMessage)convertTransactionStateToPigeon: + (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment API_AVAILABLE(ios(12.2)) { + if (!payment) { + return nil; + } SKPaymentMessage *msg = [SKPaymentMessage makeWithProductIdentifier:payment.productIdentifier applicationUsername:payment.applicationUsername @@ -348,6 +354,9 @@ + (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment + (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:(SKPaymentDiscount *)discount API_AVAILABLE(ios(12.2)) { + if (!discount) { + return nil; + } SKPaymentDiscountMessage *msg = [SKPaymentDiscountMessage makeWithIdentifier:discount.identifier keyIdentifier:discount.keyIdentifier @@ -360,6 +369,9 @@ + (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:(SKPayment + (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(SKStorefront *)storefront API_AVAILABLE(ios(13.0)) { + if (!storefront) { + return nil; + } SKStorefrontMessage *msg = [SKStorefrontMessage makeWithCountryCode:storefront.countryCode identifier:storefront.identifier]; 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 9410095a785..74975cf6252 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 @@ -400,13 +400,14 @@ - (SKReceiptRefreshRequest *)getRefreshReceiptRequest:(NSDictionary *)properties return [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:properties]; } -- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error API_AVAILABLE(ios(13.0), macos(10.15)) { - SKStorefront *storefront = self.paymentQueueHandler.storefront; - if (!storefront) { - return nil; - } - return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; +- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error + API_AVAILABLE(ios(13.0), macos(10.15)) { + SKStorefront *storefront = self.paymentQueueHandler.storefront; + if (!storefront) { + return nil; } + return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; +} - (void)addPayment:(nonnull NSDictionary *)paymentMap error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { 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 d9f19a1468a..4fd9a21af34 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 @@ -58,69 +58,68 @@ typedef NS_ENUM(NSUInteger, SKPaymentTransactionStateMessage) { /// `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:(NSNumber *)quantity - simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; -@property(nonatomic, copy) NSString * productIdentifier; -@property(nonatomic, copy, nullable) NSString * applicationUsername; -@property(nonatomic, copy, nullable) NSString * requestData; -@property(nonatomic, strong) NSNumber * quantity; -@property(nonatomic, strong) NSNumber * simulatesAskToBuyInSandbox; -@property(nonatomic, strong, nullable) SKPaymentDiscountMessage * paymentDiscount; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSNumber *)quantity + simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount; +@property(nonatomic, copy) NSString *productIdentifier; +@property(nonatomic, copy, nullable) NSString *applicationUsername; +@property(nonatomic, copy, nullable) NSString *requestData; +@property(nonatomic, strong) NSNumber *quantity; +@property(nonatomic, strong) NSNumber *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:(NSNumber *)code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo; -@property(nonatomic, strong) NSNumber * code; -@property(nonatomic, copy) NSString * domain; -@property(nonatomic, strong) NSDictionary * userInfo; + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo; +@property(nonatomic, strong) NSNumber *code; +@property(nonatomic, copy) NSString *domain; +@property(nonatomic, strong) 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:(NSNumber *)timestamp; -@property(nonatomic, copy) NSString * identifier; -@property(nonatomic, copy) NSString * keyIdentifier; -@property(nonatomic, copy) NSString * nonce; -@property(nonatomic, copy) NSString * signature; -@property(nonatomic, strong) NSNumber * timestamp; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSNumber *)timestamp; +@property(nonatomic, copy) NSString *identifier; +@property(nonatomic, copy) NSString *keyIdentifier; +@property(nonatomic, copy) NSString *nonce; +@property(nonatomic, copy) NSString *signature; +@property(nonatomic, strong) NSNumber *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 /// The codec used by InAppPurchaseAPI. @@ -132,12 +131,15 @@ 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)addPayment:(NSDictionary *)paymentMap error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPayment:(NSDictionary *)paymentMap + error:(FlutterError *_Nullable *_Nonnull)error; @end -extern void InAppPurchaseAPISetup(id binaryMessenger, NSObject *_Nullable api); +extern void InAppPurchaseAPISetup(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 1eb57663bfd..8800d913886 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 @@ -68,12 +68,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; @@ -87,7 +87,8 @@ + (SKPaymentTransactionMessage *)fromList:(NSArray *)list { pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; NSAssert(pigeonResult.payment != nil, @""); 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))]; @@ -110,12 +111,12 @@ - (NSArray *)toList { @implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier - applicationUsername:(nullable NSString *)applicationUsername - requestData:(nullable NSString *)requestData - quantity:(NSNumber *)quantity - simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox - paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { - SKPaymentMessage* pigeonResult = [[SKPaymentMessage alloc] init]; + applicationUsername:(nullable NSString *)applicationUsername + requestData:(nullable NSString *)requestData + quantity:(NSNumber *)quantity + simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { + SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; pigeonResult.applicationUsername = applicationUsername; pigeonResult.requestData = requestData; @@ -134,7 +135,8 @@ + (SKPaymentMessage *)fromList:(NSArray *)list { NSAssert(pigeonResult.quantity != nil, @""); pigeonResult.simulatesAskToBuyInSandbox = GetNullableObjectAtIndex(list, 4); NSAssert(pigeonResult.simulatesAskToBuyInSandbox != nil, @""); - pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; + pigeonResult.paymentDiscount = + [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; } + (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { @@ -154,9 +156,9 @@ - (NSArray *)toList { @implementation SKErrorMessage + (instancetype)makeWithCode:(NSNumber *)code - domain:(NSString *)domain - userInfo:(NSDictionary *)userInfo { - SKErrorMessage* pigeonResult = [[SKErrorMessage alloc] init]; + domain:(NSString *)domain + userInfo:(NSDictionary *)userInfo { + SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; pigeonResult.userInfo = userInfo; @@ -186,11 +188,11 @@ - (NSArray *)toList { @implementation SKPaymentDiscountMessage + (instancetype)makeWithIdentifier:(NSString *)identifier - keyIdentifier:(NSString *)keyIdentifier - nonce:(NSString *)nonce - signature:(NSString *)signature - timestamp:(NSNumber *)timestamp { - SKPaymentDiscountMessage* pigeonResult = [[SKPaymentDiscountMessage alloc] init]; + keyIdentifier:(NSString *)keyIdentifier + nonce:(NSString *)nonce + signature:(NSString *)signature + timestamp:(NSNumber *)timestamp { + SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; pigeonResult.nonce = nonce; @@ -227,9 +229,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; @@ -258,15 +259,15 @@ @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 [SKStorefrontMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -314,22 +315,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 InAppPurchaseAPISetup(id binaryMessenger, NSObject *api) { +void InAppPurchaseAPISetup(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]; @@ -340,13 +346,14 @@ void InAppPurchaseAPISetup(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]; @@ -357,13 +364,14 @@ void InAppPurchaseAPISetup(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]; @@ -374,13 +382,13 @@ void InAppPurchaseAPISetup(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(addPayment:error:)], @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPayment:error:)", api); + NSCAssert([api respondsToSelector:@selector(addPayment:error:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPayment:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_paymentMap = GetNullableObjectAtIndex(args, 0); 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 6f77fa72a63..3fd7597e952 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 @@ -21,11 +21,9 @@ @interface TranslatorTest : XCTestCase @property(strong, nonatomic) NSDictionary *localeMap; @property(strong, nonatomic) NSDictionary *storefrontMap; @property(strong, nonatomic) NSDictionary *storefrontAndPaymentTransactionMap; - @end @implementation TranslatorTest - - (void)setUp { self.periodMap = @{@"numberOfUnits" : @(0), @"unit" : @(0)}; @@ -411,4 +409,60 @@ - (void)testSKPaymentDiscountFromMapOverflowingTimestamp { } } +- (void)testSKPaymentDiscountConvertToPigeon { + if (@available(iOS 12.2, *)) { + NSString *error = nil; + SKPaymentDiscount *paymentDiscount = + [FIAObjectTranslator getSKPaymentDiscountFromMap:self.paymentDiscountMap withError:&error]; + SKPaymentDiscountMessage *paymentDiscountPigeon = + [FIAObjectTranslator convertPaymentDiscountToPigeon:paymentDiscount]; + + XCTAssertNotNil(paymentDiscountPigeon); + XCTAssertEqual(paymentDiscount.identifier, paymentDiscountPigeon.identifier); + XCTAssertEqual(paymentDiscount.keyIdentifier, paymentDiscount.keyIdentifier); + XCTAssertEqualObjects(paymentDiscount.nonce, + [[NSUUID alloc] initWithUUIDString:paymentDiscountPigeon.nonce]); + XCTAssertEqual(paymentDiscount.signature, paymentDiscountPigeon.signature); + XCTAssertEqual(paymentDiscount.timestamp, paymentDiscountPigeon.timestamp); + } +} + +- (void)testSKErrorConvertToPigeon { + NSError *error = [NSError errorWithDomain:SKErrorDomain code:3 userInfo:@{@"key" : @42}]; + SKErrorMessage *msg = [SKErrorMessage makeWithCode:@(3) + domain:SKErrorDomain + userInfo:@{@"key" : @42}]; + + SKErrorMessage *skerror = [FIAObjectTranslator convertSKErrorToPigeon:error]; + XCTAssertEqual(skerror.domain, msg.domain); + XCTAssertEqualObjects(skerror.code, msg.code); + XCTAssertEqualObjects(skerror.userInfo, msg.userInfo); +} + +- (void)testSKPaymentConvertToPigeon { + SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:self.paymentMap]; + SKPaymentMessage *msg = [FIAObjectTranslator convertPaymentToPigeon:payment]; + + XCTAssertEqual(payment.productIdentifier, msg.productIdentifier); + XCTAssertEqualObjects(payment.requestData, + [msg.requestData dataUsingEncoding:NSUTF8StringEncoding]); + XCTAssertEqual(payment.quantity, [msg.quantity integerValue]); + XCTAssertEqual(payment.applicationUsername, msg.applicationUsername); + XCTAssertEqual(payment.simulatesAskToBuyInSandbox, [msg.simulatesAskToBuyInSandbox boolValue]); +} + +- (void)testSKPaymentTransactionConvertToPigeon { + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:self.transactionMap]; + + SKPaymentTransactionMessage *msg = + [FIAObjectTranslator convertTransactionToPigeon:paymentTransaction]; + + XCTAssertEqual(paymentTransaction.payment, msg.payment); + XCTAssertEqual(paymentTransaction.transactionState, msg.transactionState); + XCTAssertEqual(paymentTransaction.transactionDate, + [NSDate dateWithTimeIntervalSince1970:[msg.transactionTimeStamp doubleValue]]); + XCTAssertEqual(paymentTransaction.transactionIdentifier, msg.transactionIdentifier); +} + @end 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 a7aa235ca5c..b4b1077fc98 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,27 +15,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, } @@ -77,7 +82,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, @@ -263,15 +269,15 @@ 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 SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -292,10 +298,10 @@ class InAppPurchaseAPI { /// Returns if the current device is able to make payments Future canMakePayments() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', + codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; + final List? replyList = await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -319,10 +325,10 @@ class InAppPurchaseAPI { Future> transactions() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', + codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; + final List? replyList = await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -340,16 +346,17 @@ class InAppPurchaseAPI { message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as List?)!.cast(); + return (replyList[0] as List?)! + .cast(); } } Future storefront() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', + codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; + final List? replyList = await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -373,7 +380,8 @@ class InAppPurchaseAPI { Future addPayment(Map arg_paymentMap) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', + codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_paymentMap]) as List?; 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 b892d1a104f..9772e286a62 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 @@ -30,6 +30,7 @@ part 'sk_payment_queue_wrapper.g.dart'; InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); @visibleForTesting + /// void setInAppPurchaseHostApi(InAppPurchaseAPI api) { _hostApi = api; 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 89bba4fa899..69d342d8448 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 @@ -88,7 +88,6 @@ class SKPaymentMessage { final String? requestData; - // default value is 0? final int quantity; final bool simulatesAskToBuyInSandbox; @@ -97,7 +96,6 @@ class SKPaymentMessage { } class SKErrorMessage { - // a lot of comparison operators are overriden in this class - do i add them here? const SKErrorMessage( {required this.code, required this.domain, required this.userInfo}); @@ -135,13 +133,10 @@ class SKStorefrontMessage { @HostApi(dartHostTestHandler: 'TestInAppPurchaseApi') abstract class InAppPurchaseAPI { /// Returns if the current device is able to make payments - // @ObjCSelector('canMakePayments') bool canMakePayments(); - // @ObjCSelector('transactions') List transactions(); - // @ObjCSelector('storefront') SKStorefrontMessage storefront(); @ObjCSelector('addPayment:') 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 778c652a155..ba3b6054ab4 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 @@ -14,7 +14,7 @@ import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; import '../store_kit_wrappers/sk_test_stub_objects.dart'; import '../test_api.g.dart'; -class FakeStoreKitPlatform implements TestInAppPurchaseApi{ +class FakeStoreKitPlatform implements TestInAppPurchaseApi { FakeStoreKitPlatform() { _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger @@ -250,7 +250,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi{ // Keep the received paymentDiscount parameter when testing payment with discount. if (paymentMap['applicationUsername']! == 'userWithDiscount') { final Map? discountArgument = - paymentMap['paymentDiscount'] as Map?; + paymentMap['paymentDiscount'] as Map?; if (discountArgument != null) { discountReceived = discountArgument.cast(); } else { @@ -259,27 +259,27 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi{ } final SKPaymentTransactionWrapper transaction = - createPendingTransaction(id, quantity: quantity); + 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); + createFailedTransaction(id, quantity: quantity); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transactionFailed]); } else if (testTransactionCancel > 0) { final SKPaymentTransactionWrapper transactionCanceled = - createCanceledTransaction(id, testTransactionCancel, - quantity: quantity); + createCanceledTransaction(id, testTransactionCancel, + quantity: quantity); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transactionCanceled]); } else { final SKPaymentTransactionWrapper transactionFinished = - createPurchasedTransaction( - id, transaction.transactionIdentifier ?? '', - quantity: quantity); + createPurchasedTransaction( + id, transaction.transactionIdentifier ?? '', + quantity: quantity); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transactionFinished]); } 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 a34114e6f8d..fc06db0b571 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 @@ -279,8 +279,8 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { @override void addPayment(Map paymentMap) { - payments.add(SKPaymentWrapper.fromJson(Map.from( - paymentMap))); + payments + .add(SKPaymentWrapper.fromJson(Map.from(paymentMap))); } @override @@ -290,13 +290,13 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { @override SKStorefrontMessage storefront() { - return SKStorefrontMessage(countryCode: 'USA', identifier: 'unique_identifier'); + return SKStorefrontMessage( + countryCode: 'USA', identifier: 'unique_identifier'); } @override List transactions() => [dummyTransactionMessage]; - } 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 5874f0d98b6..ad32a0a5c14 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 @@ -17,8 +17,7 @@ SKPaymentMessage dummyPaymentMessage = SKPaymentMessage( applicationUsername: 'app-user-name', requestData: 'fake-data-utf8', quantity: 2, - simulatesAskToBuyInSandbox: true -); + simulatesAskToBuyInSandbox: true); final SKPaymentWrapper dummyPaymentWithDiscount = SKPaymentWrapper( productIdentifier: 'prod-id', @@ -53,8 +52,9 @@ final SKPaymentTransactionWrapper dummyTransaction = ); final SKPaymentTransactionMessage dummyTransactionMessage = - SKPaymentTransactionMessage(payment: dummyPaymentMessage, - transactionState: SKPaymentTransactionStateMessage.purchased); + SKPaymentTransactionMessage( + payment: dummyPaymentMessage, + transactionState: SKPaymentTransactionStateMessage.purchased); final SKPriceLocaleWrapper dollarLocale = SKPriceLocaleWrapper( currencySymbol: r'$', 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 d977b026eb4..b816070dfe9 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 @@ -37,15 +37,15 @@ 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 SKStorefrontMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -54,7 +54,8 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { } abstract class TestInAppPurchaseApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; static const MessageCodec codec = _TestInAppPurchaseApiCodec(); /// Returns if the current device is able to make payments @@ -66,15 +67,20 @@ abstract class TestInAppPurchaseApi { void addPayment(Map paymentMap); - static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestInAppPurchaseApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', + codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { // ignore message final bool output = api.canMakePayments(); return [output]; @@ -83,12 +89,16 @@ abstract class TestInAppPurchaseApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', + codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { // ignore message final List output = api.transactions(); return [output]; @@ -97,12 +107,16 @@ abstract class TestInAppPurchaseApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', + codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { // ignore message final SKStorefrontMessage output = api.storefront(); return [output]; @@ -111,16 +125,21 @@ abstract class TestInAppPurchaseApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', codec, + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', + codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(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.'); api.addPayment(arg_paymentMap!); From c5610c724c9f470e9ff08d19e52c8f6f30f558ef Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 10:40:11 -0800 Subject: [PATCH 14/31] pubspec, failing tests --- .../example/shared/RunnerTests/TranslatorTests.m | 4 ++-- .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 3fd7597e952..a185428b3d4 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,8 +458,8 @@ - (void)testSKPaymentTransactionConvertToPigeon { SKPaymentTransactionMessage *msg = [FIAObjectTranslator convertTransactionToPigeon:paymentTransaction]; - XCTAssertEqual(paymentTransaction.payment, msg.payment); - XCTAssertEqual(paymentTransaction.transactionState, msg.transactionState); + XCTAssertEqual(msg.payment, NULL); + XCTAssertEqual(msg.transactionState, SKPaymentTransactionStateMessagePurchasing); XCTAssertEqual(paymentTransaction.transactionDate, [NSDate dateWithTimeIntervalSince1970:[msg.transactionTimeStamp doubleValue]]); XCTAssertEqual(paymentTransaction.transactionIdentifier, msg.transactionIdentifier); 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 b2c2e9fc70b..17d89190942 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.7 +version: 0.3.7+1 environment: sdk: ">=3.0.0 <4.0.0" From 4c0672cfc42a044e121e8e3441cfc5220029b336 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 11:03:43 -0800 Subject: [PATCH 15/31] pubspec? --- .../in_app_purchase_storekit/CHANGELOG.md | 3 --- .../shared/RunnerTests/TranslatorTests.m | 20 ++++++++++--------- .../in_app_purchase_storekit/pubspec.yaml | 2 +- 3 files changed, 12 insertions(+), 13 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 ec0fff1aa98..1097a72035f 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,9 +1,6 @@ ## NEXT * Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. - -## 0.3.7+1 - * Converted `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. ## 0.3.7 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 a185428b3d4..efd82af9538 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 @@ -440,15 +440,17 @@ - (void)testSKErrorConvertToPigeon { } - (void)testSKPaymentConvertToPigeon { - SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:self.paymentMap]; - SKPaymentMessage *msg = [FIAObjectTranslator convertPaymentToPigeon:payment]; - - XCTAssertEqual(payment.productIdentifier, msg.productIdentifier); - XCTAssertEqualObjects(payment.requestData, - [msg.requestData dataUsingEncoding:NSUTF8StringEncoding]); - XCTAssertEqual(payment.quantity, [msg.quantity integerValue]); - XCTAssertEqual(payment.applicationUsername, msg.applicationUsername); - XCTAssertEqual(payment.simulatesAskToBuyInSandbox, [msg.simulatesAskToBuyInSandbox boolValue]); + if (@available(iOS 12.2, *)) { + SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:self.paymentMap]; + SKPaymentMessage *msg = [FIAObjectTranslator convertPaymentToPigeon:payment]; + + XCTAssertEqual(payment.productIdentifier, msg.productIdentifier); + XCTAssertEqualObjects(payment.requestData, + [msg.requestData dataUsingEncoding:NSUTF8StringEncoding]); + XCTAssertEqual(payment.quantity, [msg.quantity integerValue]); + XCTAssertEqual(payment.applicationUsername, msg.applicationUsername); + XCTAssertEqual(payment.simulatesAskToBuyInSandbox, [msg.simulatesAskToBuyInSandbox boolValue]); + } } - (void)testSKPaymentTransactionConvertToPigeon { 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 17d89190942..b2c2e9fc70b 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.7+1 +version: 0.3.7 environment: sdk: ">=3.0.0 <4.0.0" From 7c68126dc3e8de2965e1fb7e829afaddb5d12fc6 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 12:17:24 -0800 Subject: [PATCH 16/31] pubspec? --- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 1097a72035f..f342c73b721 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,8 +1,11 @@ ## NEXT -* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. * Converted `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. +## 0.3.7+1 + +* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. + ## 0.3.7 * Adds `Future storefront()` in SKPaymentQueueWrapper class. From 56b8bc8b6a0fe547308f309fca33f1591bbe90cf Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 12:26:21 -0800 Subject: [PATCH 17/31] pubspec!! --- packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b2c2e9fc70b..17d89190942 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.7 +version: 0.3.7+1 environment: sdk: ">=3.0.0 <4.0.0" From 020623eb5c631043812bab44b8046ab6a7fb484a Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 13:44:43 -0800 Subject: [PATCH 18/31] pubspec :( --- packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 2 +- packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 2 files changed, 2 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 f342c73b721..116cec64912 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.7+2 * Converted `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to 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 17d89190942..a4d9e984c3e 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.7+1 +version: 0.3.7+2 environment: sdk: ">=3.0.0 <4.0.0" From 8fba6609c5bef4fef8f864e588cef0180d72e6d0 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 13:57:33 -0800 Subject: [PATCH 19/31] p u b s p e c --- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 5 +---- .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 5 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 116cec64912..457244b46af 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,9 +1,6 @@ -## 0.3.7+2 - -* Converted `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. - ## 0.3.7+1 +* Converted `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. * Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. ## 0.3.7 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 a4d9e984c3e..17d89190942 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.7+2 +version: 0.3.7+1 environment: sdk: ">=3.0.0 <4.0.0" From bc666b8029e24c1d40777f5ce03d324c01062654 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 14:36:15 -0800 Subject: [PATCH 20/31] analyze, shifted methods around for clearer diffs --- .../darwin/Classes/InAppPurchasePlugin.m | 142 ++++++++---------- .../example/lib/main.dart | 1 - .../pigeons/messages.dart | 1 - .../in_app_purchase_storekit/pubspec.yaml | 1 - 4 files changed, 63 insertions(+), 82 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 74975cf6252..7087b40899f 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 @@ -139,20 +139,13 @@ - (nullable NSNumber *)canMakePaymentsWithError: return (transactionMaps); } -- (void)getStorefront:(FlutterResult)result { - if (@available(iOS 13.0, macOS 10.15, *)) { - SKStorefront *storefront = self.paymentQueueHandler.storefront; - if (!storefront) { - result(nil); - return; - } - result([FIAObjectTranslator getMapFromSKStorefront:storefront]); - return; +- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error + API_AVAILABLE(ios(13.0), macos(10.15)) { + SKStorefront *storefront = self.paymentQueueHandler.storefront; + if (!storefront) { + return nil; } - - NSLog(@"storefront is not avaialbe in iOS below 13.0 or macOS below 10.15."); - result(nil); - return; + return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; } - (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { @@ -191,6 +184,63 @@ - (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(Flutter }]; } +- (void)addPayment:(nonnull NSDictionary *)paymentMap + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; + // When a product is already fetched, we create a payment object with + // the product to process the payment. + SKProduct *product = [self getProduct:productID]; + if (!product) { + *error = [FlutterError + errorWithCode:@"storekit_invalid_payment_object" + message: + @"You have requested a payment for an invalid product. Either the " + @"`productIdentifier` of the payment is not valid or the product has not been " + @"fetched before adding the payment to the payment queue." + details:paymentMap]; + return; + } + + SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; + payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; + NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; + payment.quantity = (quantity != nil) ? quantity.integerValue : 1; + NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; + payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] + ? NO + : [simulatesAskToBuyInSandbox boolValue]; + + if (@available(iOS 12.2, *)) { + NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap + forKey:@"paymentDiscount"]; + NSString *errorMsg = nil; + SKPaymentDiscount *paymentDiscount = + [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&errorMsg]; + + if (errorMsg) { + *error = [FlutterError + errorWithCode:@"storekit_invalid_payment_discount_object" + message:[NSString stringWithFormat:@"You have requested a payment and specified a " + @"payment discount with invalid properties. %@", + errorMsg] + details:paymentMap]; + return; + } + + payment.paymentDiscount = paymentDiscount; + } + if (![self.paymentQueueHandler addPayment:payment]) { + *error = [FlutterError + errorWithCode:@"storekit_duplicate_product_object" + message:@"There is a pending transaction for the same product identifier. Please " + @"either wait for it to be finished or finish it manually using " + @"`completePurchase` to avoid edge cases." + + details:paymentMap]; + return; + } +} + - (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result { if (![call.arguments isKindOfClass:[NSDictionary class]]) { result([FlutterError errorWithCode:@"storekit_invalid_argument" @@ -399,70 +449,4 @@ - (SKProduct *)getProduct:(NSString *)productID { - (SKReceiptRefreshRequest *)getRefreshReceiptRequest:(NSDictionary *)properties { return [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:properties]; } - -- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error - API_AVAILABLE(ios(13.0), macos(10.15)) { - SKStorefront *storefront = self.paymentQueueHandler.storefront; - if (!storefront) { - return nil; - } - return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; -} - -- (void)addPayment:(nonnull NSDictionary *)paymentMap - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; - // When a product is already fetched, we create a payment object with - // the product to process the payment. - SKProduct *product = [self getProduct:productID]; - if (!product) { - *error = [FlutterError - errorWithCode:@"storekit_invalid_payment_object" - message: - @"You have requested a payment for an invalid product. Either the " - @"`productIdentifier` of the payment is not valid or the product has not been " - @"fetched before adding the payment to the payment queue." - details:paymentMap]; - return; - } - - SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; - payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; - NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; - payment.quantity = (quantity != nil) ? quantity.integerValue : 1; - NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; - payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] - ? NO - : [simulatesAskToBuyInSandbox boolValue]; - - if (@available(iOS 12.2, *)) { - NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap - forKey:@"paymentDiscount"]; - NSString *errorMsg = nil; - SKPaymentDiscount *paymentDiscount = - [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&errorMsg]; - - if (errorMsg) { - *error = [FlutterError - errorWithCode:@"storekit_invalid_payment_discount_object" - message:[NSString stringWithFormat:@"You have requested a payment and specified a " - @"payment discount with invalid properties. %@", - errorMsg] - details:paymentMap]; - return; - } - - payment.paymentDiscount = paymentDiscount; - } - if (![self.paymentQueueHandler addPayment:payment]) { - *error = [FlutterError - errorWithCode:@"storekit_duplicate_product_object" - message:@"There is a pending transaction for the same product identifier. Please " - @"either wait for it to be finished or finish it manually using " - @"`completePurchase` to avoid edge cases." - - details:paymentMap]; - return; - } -} @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 47c7a2dd37e..cab8ddf0c2d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -75,7 +75,6 @@ class _MyAppState extends State<_MyApp> { Future initStoreInfo() async { final bool isAvailable = await _iapStoreKitPlatform.isAvailable(); - if (!isAvailable) { setState(() { _isAvailable = isAvailable; 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 69d342d8448..d9d43cd7eff 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 @@ -139,6 +139,5 @@ abstract class InAppPurchaseAPI { SKStorefrontMessage storefront(); - @ObjCSelector('addPayment:') void addPayment(Map paymentMap); } 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 17d89190942..9309bf91935 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -31,7 +31,6 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 - mockito: ^5.4.4 pigeon: ^11.0.1 test: ^1.16.0 From 3279b2b02b7d18c5c5980a505beeee1ff82705d2 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 14:43:23 -0800 Subject: [PATCH 21/31] license --- .../in_app_purchase_storekit/darwin/Classes/messages.g.h | 4 ++++ .../in_app_purchase_storekit/darwin/Classes/messages.g.m | 4 ++++ 2 files changed, 8 insertions(+) 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 4fd9a21af34..5dfe4b12401 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 (v11.0.1), 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 8800d913886..2caf70979b7 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 (v11.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon From 208844ede2328b3073360c2be6450985d5c58b9c Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 15:12:41 -0800 Subject: [PATCH 22/31] merge conflict --- .../test/fakes/fake_storekit_platform.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8800ee5caa0..de5df5721ca 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 @@ -216,7 +216,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { finishedTransactions.add(createPurchasedTransaction( arguments['productIdentifier']! as String, arguments['transactionIdentifier']! as String, - quantity: transactions.first.payment.quantity)); + quantity: transactionList.first.payment.quantity)); case '-[SKPaymentQueue startObservingTransactionQueue]': queueIsActive = true; case '-[SKPaymentQueue stopObservingTransactionQueue]': From eb3320cf12aca917f4fce50a2fd1096dc22bdc55 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Tue, 23 Jan 2024 15:21:22 -0800 Subject: [PATCH 23/31] changelog --- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 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 ae552242ffa..c36781ea27b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,10 +1,7 @@ -## NEXT - -* Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. - ## 0.3.9 * Converted `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. +* Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. ## 0.3.8+1 From 91554c654fea9e5c78c7acd947e7244bd37ae345 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 25 Jan 2024 14:12:57 -0800 Subject: [PATCH 24/31] pr comments --- .../ios/Runner.xcodeproj/project.pbxproj | 4 +- .../in_app_purchase_storekit/CHANGELOG.md | 2 +- .../darwin/Classes/FIAObjectTranslator.h | 11 +- .../darwin/Classes/FIAObjectTranslator.m | 21 ++- .../darwin/Classes/InAppPurchasePlugin.m | 9 +- .../darwin/Classes/messages.g.h | 30 ++-- .../darwin/Classes/messages.g.m | 98 +++++----- .../ios/Runner.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../RunnerTests/InAppPurchasePluginTests.m | 69 +++++-- .../shared/RunnerTests/TranslatorTests.m | 10 +- .../lib/src/messages.g.dart | 168 ++++++++++-------- .../sk_payment_queue_wrapper.dart | 21 +-- .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 11 +- .../test/test_api.g.dart | 91 ++++++---- 16 files changed, 317 insertions(+), 236 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj index 8b82385f6a5..c3302f6efa7 100644 --- a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj @@ -439,7 +439,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -466,7 +466,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", 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 c36781ea27b..044a1fef4fc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.3.9 -* Converted `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. +* Converts `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon. * Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. ## 0.3.8+1 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 af5b83e294d..bf32cc128fc 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 @@ -58,15 +58,16 @@ NS_ASSUME_NONNULL_BEGIN API_AVAILABLE(ios(12.2)); + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: - (SKPaymentTransaction *)transaction; + (nullable SKPaymentTransaction *)transaction; -+ (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(SKStorefront *)storefront ++ (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefront *)storefront API_AVAILABLE(ios(13.0)); -+ (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:(SKPaymentDiscount *)discount - API_AVAILABLE(ios(12.2)); ++ (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon: + (nullable SKPaymentDiscount *)discount API_AVAILABLE(ios(12.2)); -+ (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment API_AVAILABLE(ios(12.2)); ++ (nullable SKPaymentMessage *)convertPaymentToPigeon:(nullable SKPayment *)payment + API_AVAILABLE(ios(12.2)); + (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error; @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 25fc63e4fb0..99ded7c35df 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 @@ -3,7 +3,6 @@ // found in the LICENSE file. #import "FIAObjectTranslator.h" -#import "messages.g.h" #pragma mark - SKProduct Coders @@ -290,7 +289,7 @@ + (SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map } + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: - (SKPaymentTransaction *)transaction API_AVAILABLE(ios(12.2)) { + (nullable SKPaymentTransaction *)transaction API_AVAILABLE(ios(12.2)) { if (!transaction) { return nil; } @@ -299,7 +298,7 @@ + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: transactionState:[self convertTransactionStateToPigeon:transaction.transactionState] originalTransaction:transaction.originalTransaction ? [self convertTransactionToPigeon:transaction.originalTransaction] - : NULL + : nil transactionTimeStamp:[NSNumber numberWithDouble:[transaction.transactionDate timeIntervalSince1970]] transactionIdentifier:transaction.transactionIdentifier @@ -308,7 +307,7 @@ + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: } + (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error { - SKErrorMessage *msg = [SKErrorMessage makeWithCode:@(error.code) + SKErrorMessage *msg = [SKErrorMessage makeWithCode:error.code domain:error.domain userInfo:error.userInfo]; return msg; @@ -330,7 +329,7 @@ + (SKPaymentTransactionStateMessage)convertTransactionStateToPigeon: } } -+ (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment ++ (nullable SKPaymentMessage *)convertPaymentToPigeon:(nullable SKPayment *)payment API_AVAILABLE(ios(12.2)) { if (!payment) { return nil; @@ -340,14 +339,14 @@ + (nullable SKPaymentMessage *)convertPaymentToPigeon:(SKPayment *)payment applicationUsername:payment.applicationUsername requestData:[[NSString alloc] initWithData:payment.requestData encoding:NSUTF8StringEncoding] - quantity:@(payment.quantity) - simulatesAskToBuyInSandbox:@(payment.simulatesAskToBuyInSandbox) + quantity:payment.quantity + simulatesAskToBuyInSandbox:payment.simulatesAskToBuyInSandbox paymentDiscount:[self convertPaymentDiscountToPigeon:payment.paymentDiscount]]; return msg; } -+ (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:(SKPaymentDiscount *)discount - API_AVAILABLE(ios(12.2)) { ++ (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon: + (nullable SKPaymentDiscount *)discount API_AVAILABLE(ios(12.2)) { if (!discount) { return nil; } @@ -356,12 +355,12 @@ + (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:(SKPayment keyIdentifier:discount.keyIdentifier nonce:[discount.nonce UUIDString] signature:discount.signature - timestamp:discount.timestamp]; + timestamp:[discount.timestamp intValue]]; return msg; } -+ (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(SKStorefront *)storefront ++ (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefront *)storefront API_AVAILABLE(ios(13.0)) { if (!storefront) { return nil; 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 7087b40899f..88cc4e8a5d0 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 @@ -9,7 +9,6 @@ #import "FIAPReceiptManager.h" #import "FIAPRequestHandler.h" #import "FIAPaymentQueueHandler.h" -#import "messages.g.h" @interface InAppPurchasePlugin () @@ -45,7 +44,7 @@ + (void)registerWithRegistrar:(NSObject *)registrar { InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; [registrar addMethodCallDelegate:instance channel:channel]; [registrar addApplicationDelegate:instance]; - InAppPurchaseAPISetup(registrar.messenger, instance); + SetUpInAppPurchaseAPI(registrar.messenger, instance); } - (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { @@ -136,7 +135,7 @@ - (nullable NSNumber *)canMakePaymentsWithError: for (SKPaymentTransaction *transaction in transactions) { [transactionMaps addObject:[FIAObjectTranslator convertTransactionToPigeon:transaction]]; } - return (transactionMaps); + return transactionMaps; } - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error @@ -184,8 +183,8 @@ - (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(Flutter }]; } -- (void)addPayment:(nonnull NSDictionary *)paymentMap - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { +- (void)addPaymentPaymentMap:(nonnull NSDictionary *)paymentMap + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; // When a product is already fetched, we create a payment object with // the product to process the payment. 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 5dfe4b12401..0d3e967dc69 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,8 +1,4 @@ -// 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 (v11.0.1), do not edit directly. +// Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @@ -81,26 +77,26 @@ typedef NS_ENUM(NSUInteger, SKPaymentTransactionStateMessage) { + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier applicationUsername:(nullable NSString *)applicationUsername requestData:(nullable NSString *)requestData - quantity:(NSNumber *)quantity - simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + 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, strong) NSNumber *quantity; -@property(nonatomic, strong) NSNumber *simulatesAskToBuyInSandbox; +@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:(NSNumber *)code ++ (instancetype)makeWithCode:(NSInteger)code domain:(NSString *)domain userInfo:(NSDictionary *)userInfo; -@property(nonatomic, strong) NSNumber *code; +@property(nonatomic, assign) NSInteger code; @property(nonatomic, copy) NSString *domain; -@property(nonatomic, strong) NSDictionary *userInfo; +@property(nonatomic, copy) NSDictionary *userInfo; @end @interface SKPaymentDiscountMessage : NSObject @@ -110,12 +106,12 @@ typedef NS_ENUM(NSUInteger, SKPaymentTransactionStateMessage) { keyIdentifier:(NSString *)keyIdentifier nonce:(NSString *)nonce signature:(NSString *)signature - timestamp:(NSNumber *)timestamp; + 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, strong) NSNumber *timestamp; +@property(nonatomic, assign) NSInteger timestamp; @end @interface SKStorefrontMessage : NSObject @@ -139,11 +135,11 @@ NSObject *InAppPurchaseAPIGetCodec(void); (FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error; -- (void)addPayment:(NSDictionary *)paymentMap - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addPaymentPaymentMap:(NSDictionary *)paymentMap + error:(FlutterError *_Nullable *_Nonnull)error; @end -extern void InAppPurchaseAPISetup(id binaryMessenger, +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 2caf70979b7..6608b7a0002 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,8 +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 (v11.0.1), do not edit directly. +// Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" @@ -17,16 +16,6 @@ #error File requires ARC to be enabled. #endif -@implementation SKPaymentTransactionStateMessageBox -- (instancetype)initWithValue:(SKPaymentTransactionStateMessage)value { - self = [super init]; - if (self) { - _value = value; - } - return self; -} -@end - static NSArray *wrapResult(id result, FlutterError *error) { if (error) { return @[ @@ -35,11 +24,22 @@ - (instancetype)initWithValue:(SKPaymentTransactionStateMessage)value { } return @[ result ?: [NSNull null] ]; } + static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { id result = array[key]; return (result == [NSNull null]) ? nil : result; } +@implementation SKPaymentTransactionStateMessageBox +- (instancetype)initWithValue:(SKPaymentTransactionStateMessage)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + @interface SKPaymentTransactionMessage () + (SKPaymentTransactionMessage *)fromList:(NSArray *)list; + (nullable SKPaymentTransactionMessage *)nullableFromList:(NSArray *)list; @@ -89,7 +89,6 @@ + (instancetype)makeWithPayment:(SKPaymentMessage *)payment + (SKPaymentTransactionMessage *)fromList:(NSArray *)list { SKPaymentTransactionMessage *pigeonResult = [[SKPaymentTransactionMessage alloc] init]; pigeonResult.payment = [SKPaymentMessage nullableFromList:(GetNullableObjectAtIndex(list, 0))]; - NSAssert(pigeonResult.payment != nil, @""); pigeonResult.transactionState = [GetNullableObjectAtIndex(list, 1) integerValue]; pigeonResult.originalTransaction = [SKPaymentTransactionMessage nullableFromList:(GetNullableObjectAtIndex(list, 2))]; @@ -106,8 +105,8 @@ - (NSArray *)toList { (self.payment ? [self.payment toList] : [NSNull null]), @(self.transactionState), (self.originalTransaction ? [self.originalTransaction toList] : [NSNull null]), - (self.transactionTimeStamp ?: [NSNull null]), - (self.transactionIdentifier ?: [NSNull null]), + self.transactionTimeStamp ?: [NSNull null], + self.transactionIdentifier ?: [NSNull null], (self.error ? [self.error toList] : [NSNull null]), ]; } @@ -117,8 +116,8 @@ @implementation SKPaymentMessage + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier applicationUsername:(nullable NSString *)applicationUsername requestData:(nullable NSString *)requestData - quantity:(NSNumber *)quantity - simulatesAskToBuyInSandbox:(NSNumber *)simulatesAskToBuyInSandbox + quantity:(NSInteger)quantity + simulatesAskToBuyInSandbox:(BOOL)simulatesAskToBuyInSandbox paymentDiscount:(nullable SKPaymentDiscountMessage *)paymentDiscount { SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = productIdentifier; @@ -132,13 +131,10 @@ + (instancetype)makeWithProductIdentifier:(NSString *)productIdentifier + (SKPaymentMessage *)fromList:(NSArray *)list { SKPaymentMessage *pigeonResult = [[SKPaymentMessage alloc] init]; pigeonResult.productIdentifier = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.productIdentifier != nil, @""); pigeonResult.applicationUsername = GetNullableObjectAtIndex(list, 1); pigeonResult.requestData = GetNullableObjectAtIndex(list, 2); - pigeonResult.quantity = GetNullableObjectAtIndex(list, 3); - NSAssert(pigeonResult.quantity != nil, @""); - pigeonResult.simulatesAskToBuyInSandbox = GetNullableObjectAtIndex(list, 4); - NSAssert(pigeonResult.simulatesAskToBuyInSandbox != nil, @""); + pigeonResult.quantity = [GetNullableObjectAtIndex(list, 3) integerValue]; + pigeonResult.simulatesAskToBuyInSandbox = [GetNullableObjectAtIndex(list, 4) boolValue]; pigeonResult.paymentDiscount = [SKPaymentDiscountMessage nullableFromList:(GetNullableObjectAtIndex(list, 5))]; return pigeonResult; @@ -148,18 +144,18 @@ + (nullable SKPaymentMessage *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - (self.productIdentifier ?: [NSNull null]), - (self.applicationUsername ?: [NSNull null]), - (self.requestData ?: [NSNull null]), - (self.quantity ?: [NSNull null]), - (self.simulatesAskToBuyInSandbox ?: [NSNull null]), + self.productIdentifier ?: [NSNull null], + self.applicationUsername ?: [NSNull null], + self.requestData ?: [NSNull null], + @(self.quantity), + @(self.simulatesAskToBuyInSandbox), (self.paymentDiscount ? [self.paymentDiscount toList] : [NSNull null]), ]; } @end @implementation SKErrorMessage -+ (instancetype)makeWithCode:(NSNumber *)code ++ (instancetype)makeWithCode:(NSInteger)code domain:(NSString *)domain userInfo:(NSDictionary *)userInfo { SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; @@ -170,12 +166,9 @@ + (instancetype)makeWithCode:(NSNumber *)code } + (SKErrorMessage *)fromList:(NSArray *)list { SKErrorMessage *pigeonResult = [[SKErrorMessage alloc] init]; - pigeonResult.code = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.code != nil, @""); + pigeonResult.code = [GetNullableObjectAtIndex(list, 0) integerValue]; pigeonResult.domain = GetNullableObjectAtIndex(list, 1); - NSAssert(pigeonResult.domain != nil, @""); pigeonResult.userInfo = GetNullableObjectAtIndex(list, 2); - NSAssert(pigeonResult.userInfo != nil, @""); return pigeonResult; } + (nullable SKErrorMessage *)nullableFromList:(NSArray *)list { @@ -183,9 +176,9 @@ + (nullable SKErrorMessage *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - (self.code ?: [NSNull null]), - (self.domain ?: [NSNull null]), - (self.userInfo ?: [NSNull null]), + @(self.code), + self.domain ?: [NSNull null], + self.userInfo ?: [NSNull null], ]; } @end @@ -195,7 +188,7 @@ + (instancetype)makeWithIdentifier:(NSString *)identifier keyIdentifier:(NSString *)keyIdentifier nonce:(NSString *)nonce signature:(NSString *)signature - timestamp:(NSNumber *)timestamp { + timestamp:(NSInteger)timestamp { SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = identifier; pigeonResult.keyIdentifier = keyIdentifier; @@ -207,15 +200,10 @@ + (instancetype)makeWithIdentifier:(NSString *)identifier + (SKPaymentDiscountMessage *)fromList:(NSArray *)list { SKPaymentDiscountMessage *pigeonResult = [[SKPaymentDiscountMessage alloc] init]; pigeonResult.identifier = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.identifier != nil, @""); pigeonResult.keyIdentifier = GetNullableObjectAtIndex(list, 1); - NSAssert(pigeonResult.keyIdentifier != nil, @""); pigeonResult.nonce = GetNullableObjectAtIndex(list, 2); - NSAssert(pigeonResult.nonce != nil, @""); pigeonResult.signature = GetNullableObjectAtIndex(list, 3); - NSAssert(pigeonResult.signature != nil, @""); - pigeonResult.timestamp = GetNullableObjectAtIndex(list, 4); - NSAssert(pigeonResult.timestamp != nil, @""); + pigeonResult.timestamp = [GetNullableObjectAtIndex(list, 4) integerValue]; return pigeonResult; } + (nullable SKPaymentDiscountMessage *)nullableFromList:(NSArray *)list { @@ -223,11 +211,11 @@ + (nullable SKPaymentDiscountMessage *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - (self.identifier ?: [NSNull null]), - (self.keyIdentifier ?: [NSNull null]), - (self.nonce ?: [NSNull null]), - (self.signature ?: [NSNull null]), - (self.timestamp ?: [NSNull null]), + self.identifier ?: [NSNull null], + self.keyIdentifier ?: [NSNull null], + self.nonce ?: [NSNull null], + self.signature ?: [NSNull null], + @(self.timestamp), ]; } @end @@ -242,9 +230,7 @@ + (instancetype)makeWithCountryCode:(NSString *)countryCode identifier:(NSString + (SKStorefrontMessage *)fromList:(NSArray *)list { SKStorefrontMessage *pigeonResult = [[SKStorefrontMessage alloc] init]; pigeonResult.countryCode = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.countryCode != nil, @""); pigeonResult.identifier = GetNullableObjectAtIndex(list, 1); - NSAssert(pigeonResult.identifier != nil, @""); return pigeonResult; } + (nullable SKStorefrontMessage *)nullableFromList:(NSArray *)list { @@ -252,8 +238,8 @@ + (nullable SKStorefrontMessage *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - (self.countryCode ?: [NSNull null]), - (self.identifier ?: [NSNull null]), + self.countryCode ?: [NSNull null], + self.identifier ?: [NSNull null], ]; } @end @@ -326,7 +312,7 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { return sSharedObject; } -void InAppPurchaseAPISetup(id binaryMessenger, +void SetUpInAppPurchaseAPI(id binaryMessenger, NSObject *api) { /// Returns if the current device is able to make payments { @@ -391,13 +377,15 @@ void InAppPurchaseAPISetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:InAppPurchaseAPIGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(addPayment:error:)], - @"InAppPurchaseAPI api (%@) doesn't respond to @selector(addPayment: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); FlutterError *error; - [api addPayment:arg_paymentMap error:&error]; + [api addPaymentPaymentMap:arg_paymentMap error:&error]; callback(wrapResult(nil, error)); }]; } else { 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 1cfaebdf92f..06e0b3ac947 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 @@ -258,7 +258,7 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Original; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -341,12 +341,10 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/in_app_purchase_storekit/in_app_purchase_storekit_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/shared_preferences_foundation/shared_preferences_foundation_privacy.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/in_app_purchase_storekit_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/shared_preferences_foundation_privacy.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fc4170f3176..102a865d991 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + enum SKPaymentTransactionStateMessage { /// Indicates the transaction is being processed in App Store. /// @@ -101,8 +119,8 @@ class SKPaymentMessage { required this.productIdentifier, this.applicationUsername, this.requestData, - required this.quantity, - required this.simulatesAskToBuyInSandbox, + this.quantity = 1, + this.simulatesAskToBuyInSandbox = false, this.paymentDiscount, }); @@ -290,111 +308,119 @@ class InAppPurchaseAPI { /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. InAppPurchaseAPI({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec codec = _InAppPurchaseAPICodec(); + static const MessageCodec pigeonChannelCodec = + _InAppPurchaseAPICodec(); /// Returns if the current device is able to make payments Future canMakePayments() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send(null) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments'; + 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: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as bool?)!; } } Future> transactions() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send(null) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions'; + 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: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as List?)! + return (__pigeon_replyList[0] as List?)! .cast(); } } Future storefront() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send(null) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront'; + 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: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as SKStorefrontMessage?)!; + return (__pigeon_replyList[0] as SKStorefrontMessage?)!; } } - Future addPayment(Map arg_paymentMap) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_paymentMap]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + Future addPayment(Map paymentMap) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([paymentMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + 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 8d9272b00c9..5c2e3c395a3 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 @@ -16,6 +16,14 @@ import '../messages.g.dart'; part 'sk_payment_queue_wrapper.g.dart'; +InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); + +/// Set up pigeon API. +@visibleForTesting +void setInAppPurchaseHostApi(InAppPurchaseAPI api) { + _hostApi = api; +} + /// A wrapper around /// [`SKPaymentQueue`](https://developer.apple.com/documentation/storekit/skpaymentqueue?language=objc). /// @@ -25,18 +33,7 @@ part 'sk_payment_queue_wrapper.g.dart'; /// /// Full information on using `SKPaymentQueue` and processing purchases is /// available at the [In-App Purchase Programming -/// Guide](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html#//apple_ref/doc/uid/TP40008267). - -InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); - -@visibleForTesting - -/// -void setInAppPurchaseHostApi(InAppPurchaseAPI api) { - _hostApi = api; -} - -/// +/// Guide](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html#//apple_ref/doc/uid/TP40008267) class SKPaymentQueueWrapper { /// Returns the default payment queue. /// 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 f7018069563..fc7fde8d622 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -31,7 +31,7 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 - pigeon: ^11.0.1 + pigeon: ^16.0.4 test: ^1.16.0 topics: 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 de5df5721ca..f35efd7e511 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 @@ -245,12 +245,12 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { // Keep the received paymentDiscount parameter when testing payment with discount. if (paymentMap['applicationUsername']! == 'userWithDiscount') { - final Map? discountArgument = - paymentMap['paymentDiscount'] as Map?; + final Map? discountArgument = + paymentMap['paymentDiscount'] as Map?; if (discountArgument != null) { - discountReceived = discountArgument.cast(); + discountReceived = discountArgument.cast(); } else { - discountReceived = {}; + discountReceived = {}; } } @@ -259,7 +259,6 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { transactionList.add(transaction); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transaction]); - sleep(const Duration(milliseconds: 30)); if (testTransactionFail) { final SKPaymentTransactionWrapper transactionFailed = createFailedTransaction(id, quantity: quantity); @@ -283,13 +282,11 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { @override SKStorefrontMessage storefront() { - // TODO(louisehsu): implement storefront throw UnimplementedError(); } @override List transactions() { - // TODO(louisehsu): implement transactions throw UnimplementedError(); } } 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 b816070dfe9..a640a188c43 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 @@ -1,6 +1,6 @@ -// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -56,7 +56,8 @@ class _TestInAppPurchaseApiCodec extends StandardMessageCodec { abstract class TestInAppPurchaseApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec codec = _TestInAppPurchaseApiCodec(); + static const MessageCodec pigeonChannelCodec = + _TestInAppPurchaseApiCodec(); /// Returns if the current device is able to make payments bool canMakePayments(); @@ -70,70 +71,93 @@ abstract class TestInAppPurchaseApi { static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel channel = BasicMessageChannel( + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.canMakePayments', - codec, + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { - // ignore message - final bool output = api.canMakePayments(); - return [output]; + 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())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.transactions', - codec, + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { - // ignore message - final List output = api.transactions(); - return [output]; + try { + 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())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.storefront', - codec, + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { - // ignore message - final SKStorefrontMessage output = api.storefront(); - return [output]; + 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())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment', - codec, + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.addPayment was null.'); @@ -142,8 +166,15 @@ abstract class TestInAppPurchaseApi { (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.'); - api.addPayment(arg_paymentMap!); - return []; + try { + api.addPayment(arg_paymentMap!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } From 8f8e24220a9ab188a3b2419f3416781472852e38 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 25 Jan 2024 15:21:22 -0800 Subject: [PATCH 25/31] fix tests --- .../example/shared/RunnerTests/InAppPurchasePluginTests.m | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 ada17b6a46f..530537b143b 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 @@ -16,10 +16,6 @@ @interface InAppPurchasePluginTest : XCTestCase @end -@interface SKPaymentTransactionMessage () -- (NSArray *)toList; -@end - @implementation InAppPurchasePluginTest - (void)setUp { @@ -169,7 +165,7 @@ - (void)testAddPaymentSuccessWithoutPaymentDiscount { OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { SKPayment *payment = obj; XCTAssert(payment != nil); - XCTAssert(payment.productIdentifier == @"123"); + XCTAssertEquals(payment.productIdentifier, @"123"); XCTAssert(payment.quantity == 1); return YES; }]]); @@ -547,7 +543,7 @@ - (void)testShowPriceConsentIfNeeded { - (NSArray *)PaymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { return @[ - (paymentTransaction.payment ? [self PaymentToList:paymentTransaction] : [NSNull null]), + (paymentTransaction.payment ? [self PaymentToList:paymentTransaction.payment] : [NSNull null]), @(paymentTransaction.transactionState), (paymentTransaction.originalTransaction ? [self PaymentTransactionToList:paymentTransaction.originalTransaction] From 0442eb5a1fe6684a5e393340175ac9ba2da4007f Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 25 Jan 2024 15:34:47 -0800 Subject: [PATCH 26/31] oops --- .../example/shared/RunnerTests/InAppPurchasePluginTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 530537b143b..da209453fd0 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 @@ -165,7 +165,7 @@ - (void)testAddPaymentSuccessWithoutPaymentDiscount { OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { SKPayment *payment = obj; XCTAssert(payment != nil); - XCTAssertEquals(payment.productIdentifier, @"123"); + XCTAssertEqual(payment.productIdentifier, @"123"); XCTAssert(payment.quantity == 1); return YES; }]]); From d2f96fc169280c9c5494cd954163a1438ad0c38a Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Thu, 25 Jan 2024 15:58:52 -0800 Subject: [PATCH 27/31] license --- .../in_app_purchase_storekit/darwin/Classes/messages.g.h | 4 ++++ 1 file changed, 4 insertions(+) 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 0d3e967dc69..3874b7b20a8 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 From b3a831449ac19bcaaf64a985e7b1102e0526f5c4 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Fri, 26 Jan 2024 14:04:26 -0800 Subject: [PATCH 28/31] encode userInfo properly --- .../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 99ded7c35df..5c1921fa2e3 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 @@ -309,7 +309,7 @@ + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: + (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error { SKErrorMessage *msg = [SKErrorMessage makeWithCode:error.code domain:error.domain - userInfo:error.userInfo]; + userInfo:[FIAObjectTranslator encodeNSErrorUserInfo:error.userInfo]]; return msg; } From 4629236f0ab13489c01830956832ce170eeba2b5 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Fri, 26 Jan 2024 14:09:52 -0800 Subject: [PATCH 29/31] oops --- .../darwin/Classes/FIAObjectTranslator.m | 9 ++++++++- 1 file changed, 8 insertions(+), 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 5c1921fa2e3..deb70cbdfe1 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,9 +307,16 @@ + (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon: } + (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error { + NSMutableDictionary *userInfo = [NSMutableDictionary new]; + for (NSErrorUserInfoKey key in error.userInfo) { + id value = error.userInfo[key]; + userInfo[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value]; + } + + SKErrorMessage *msg = [SKErrorMessage makeWithCode:error.code domain:error.domain - userInfo:[FIAObjectTranslator encodeNSErrorUserInfo:error.userInfo]]; + userInfo:userInfo]; return msg; } From b4e1a5f0f6bd3d45115037cbf1071a93b4623400 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Mon, 29 Jan 2024 00:38:35 -0800 Subject: [PATCH 30/31] format --- .../darwin/Classes/FIAObjectTranslator.m | 1 - 1 file changed, 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 deb70cbdfe1..bfc5542af5e 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 @@ -313,7 +313,6 @@ + (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error { userInfo[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value]; } - SKErrorMessage *msg = [SKErrorMessage makeWithCode:error.code domain:error.domain userInfo:userInfo]; From 082ad703c92200d93b58f08e00fe7044546422e1 Mon Sep 17 00:00:00 2001 From: Louise Hsu Date: Mon, 29 Jan 2024 18:14:35 -0800 Subject: [PATCH 31/31] pr comments --- .../RunnerTests/InAppPurchasePluginTests.m | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) 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 da209453fd0..3b855991b8f 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 @@ -416,8 +416,8 @@ - (void)testGetPendingTransactions { [FIAObjectTranslator convertTransactionToPigeon:original]; SKPaymentTransactionMessage *result = [self.plugin transactionsWithError:&error][0]; - XCTAssertEqualObjects([self PaymentTransactionToList:result], - [self PaymentTransactionToList:originalPigeon]); + XCTAssertEqualObjects([self paymentTransactionToList:result], + [self paymentTransactionToList:originalPigeon]); } - (void)testStartObservingPaymentQueue { @@ -539,34 +539,34 @@ - (void)testShowPriceConsentIfNeeded { } #endif -// Pigeon message deserializers for easy comparison +// The following methods are deserializer copied from Pigeon's output. -- (NSArray *)PaymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { +- (NSArray *)paymentTransactionToList:(SKPaymentTransactionMessage *)paymentTransaction { return @[ - (paymentTransaction.payment ? [self PaymentToList:paymentTransaction.payment] : [NSNull null]), + (paymentTransaction.payment ? [self paymentToList:paymentTransaction.payment] : [NSNull null]), @(paymentTransaction.transactionState), (paymentTransaction.originalTransaction - ? [self PaymentTransactionToList:paymentTransaction.originalTransaction] + ? [self paymentTransactionToList:paymentTransaction.originalTransaction] : [NSNull null]), paymentTransaction.transactionTimeStamp ?: [NSNull null], paymentTransaction.transactionIdentifier ?: [NSNull null], - (paymentTransaction.error ? [self ErrorToList:paymentTransaction.error] : [NSNull null]), + (paymentTransaction.error ? [self errorToList:paymentTransaction.error] : [NSNull null]), ]; } -- (NSArray *)PaymentToList:(SKPaymentMessage *)payment { +- (NSArray *)paymentToList:(SKPaymentMessage *)payment { return @[ payment.productIdentifier ?: [NSNull null], payment.applicationUsername ?: [NSNull null], payment.requestData ?: [NSNull null], @(payment.quantity), @(payment.simulatesAskToBuyInSandbox), - (payment.paymentDiscount ? [self PaymentDiscountToList:payment.paymentDiscount] + (payment.paymentDiscount ? [self paymentDiscountToList:payment.paymentDiscount] : [NSNull null]), ]; } -- (NSArray *)PaymentDiscountToList:(SKPaymentDiscountMessage *)discount { +- (NSArray *)paymentDiscountToList:(SKPaymentDiscountMessage *)discount { return @[ discount.identifier ?: [NSNull null], discount.keyIdentifier ?: [NSNull null], @@ -576,7 +576,7 @@ - (NSArray *)PaymentDiscountToList:(SKPaymentDiscountMessage *)discount { ]; } -- (NSArray *)ErrorToList:(SKErrorMessage *)error { +- (NSArray *)errorToList:(SKErrorMessage *)error { return @[ @(error.code), error.domain ?: [NSNull null],