Skip to content

[in_app_purchase] add storefront in skpaymentqueuewrapper #5348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.3.7

* Adds `Future<SKStorefrontWrapper?> storefront()` in SKPaymentQueueWrapper class.

## 0.3.6+7

* Updates example code for current versions of Flutter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ typedef void (^UpdatedDownloads)(NSArray<SKDownload *> *downloads);

@property(NS_NONATOMIC_IOSONLY, weak, nullable) id<SKPaymentQueueDelegate> delegate API_AVAILABLE(
ios(13.0), macos(10.15), watchos(6.2));
@property(nonatomic, readonly, nullable)
SKStorefront *storefront API_AVAILABLE(ios(13.0), macos(10.15), watchos(6.2));

/// Creates a new FIAPaymentQueueHandler initialized with an empty
/// FIATransactionCache.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,8 @@ - (BOOL)paymentQueue:(SKPaymentQueue *)queue
return self.queue.transactions;
}

- (SKStorefront *)storefront {
return self.queue.storefront;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
[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]) {
[self handleProductRequestMethodCall:call result:result];
} else if ([@"-[InAppPurchasePlugin addPayment:result:]" isEqualToString:call.method]) {
Expand Down Expand Up @@ -139,6 +141,22 @@ - (void)getPendingTransactions:(FlutterResult)result {
result(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;
}

NSLog(@"storefront is not avaialbe in iOS below 13.0 or macOS below 10.15.");
result(nil);
return;
}

- (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
if (![call.arguments isKindOfClass:[NSArray class]]) {
result([FlutterError errorWithCode:@"storekit_invalid_argument"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,71 @@ - (void)testGetPendingTransactions {
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.");
}
}

- (void)testStartObservingPaymentQueue {
XCTestExpectation *expectation =
[self expectationWithDescription:@"Should return success result"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,19 @@ class SKPaymentQueueWrapper {
SKPaymentQueueDelegateWrapper? _paymentQueueDelegate;
SKTransactionObserverWrapper? _observer;

/// Calls [`-[SKPaymentQueue transactions]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506026-transactions?language=objc)
/// Calls [`[SKPaymentQueue storefront]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/3182430-storefront?language=objc).
///
/// Returns `null` if the user's device is below iOS 13.0 or macOS 10.15.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And a blank comment line before this one, per Effective Dart.

Future<SKStorefrontWrapper?> storefront() async {
final Map<String, dynamic>? storefrontMap = await channel
.invokeMapMethod<String, dynamic>('-[SKPaymentQueue storefront]');
if (storefrontMap == null) {
return null;
}
return SKStorefrontWrapper.fromJson(storefrontMap);
}

/// Calls [`-[SKPaymentQueue transactions]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506026-transactions?language=objc).
Future<List<SKPaymentTransactionWrapper>> transactions() async {
return _getTransactionList((await channel
.invokeListMethod<dynamic>('-[SKPaymentQueue transactions]'))!);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.6+7
version: 0.3.7

environment:
sdk: ">=2.19.0 <4.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@ void main() {
expect(await SKPaymentQueueWrapper.canMakePayments(), false);
});

test('storefront returns valid SKStoreFrontWrapper object', () async {
final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper();
expect(
await queue.storefront(),
SKStorefrontWrapper.fromJson(const <String, dynamic>{
'countryCode': 'USA',
'identifier': 'unique_identifier',
}));
});

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);
});
Expand Down Expand Up @@ -250,6 +266,14 @@ class FakeStoreKitPlatform {
case '-[SKPaymentQueue transactions]':
return Future<List<dynamic>>.value(
<dynamic>[buildTransactionMap(dummyTransaction)]);
case '-[SKPaymentQueue storefront]':
if (testReturnNull) {
return Future<dynamic>.value();
}
return Future<Map<String, dynamic>>.value(const <String, dynamic>{
'countryCode': 'USA',
'identifier': 'unique_identifier',
});
case '-[InAppPurchasePlugin addPayment:result:]':
payments.add(SKPaymentWrapper.fromJson(Map<String, dynamic>.from(
call.arguments as Map<dynamic, dynamic>)));
Expand Down