Skip to content

[in_app_purchase_storekit] disallow ios versions lower than supported from enabling storekit #8110

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 19 commits into from
Nov 19, 2024
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.20+1

* Prevent devices below iOS 15 or macOS 15 from enabling StoreKit2.

## 0.3.20

* Fixes manual invocation of `finishTransaction` causing a fatal crash.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,15 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, FIAInAppPurchaseAPI {
NSLog("Received an updatedDownloads callback, but downloads are not supported.")
}

public func supportsStoreKit2WithError(_ error: AutoreleasingUnsafeMutablePointer<FlutterError?>)
-> NSNumber?
{
if #available(iOS 15.0, macOS 12.0, *) {
return true
}
return false
}

// MARK: - Methods exposed for testing
func getProduct(productID: String) -> SKProduct? {
return self.productsCache[productID] as? SKProduct
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +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 (v22.4.2), do not edit directly.
// Autogenerated from Pigeon (v22.6.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon

#import <Foundation/Foundation.h>
Expand Down Expand Up @@ -67,7 +67,8 @@ typedef NS_ENUM(NSUInteger, FIASKProductDiscountTypeMessage) {
typedef NS_ENUM(NSUInteger, FIASKProductDiscountPaymentModeMessage) {
/// Allows user to pay the discounted price at each payment period.
FIASKProductDiscountPaymentModeMessagePayAsYouGo = 0,
/// Allows user to pay the discounted price upfront and receive the product for the rest of time
/// Allows user to pay the discounted price upfront and receive the product
/// for the rest of time
/// that was paid for.
FIASKProductDiscountPaymentModeMessagePayUpFront = 1,
/// User pays nothing during the discounted period.
Expand Down Expand Up @@ -278,6 +279,8 @@ NSObject<FlutterMessageCodec> *FIAGetMessagesCodec(void);
- (void)registerPaymentQueueDelegateWithError:(FlutterError *_Nullable *_Nonnull)error;
- (void)removePaymentQueueDelegateWithError:(FlutterError *_Nullable *_Nonnull)error;
- (void)showPriceConsentIfNeededWithError:(FlutterError *_Nullable *_Nonnull)error;
/// @return `nil` only when `error != nil`.
- (nullable NSNumber *)supportsStoreKit2WithError:(FlutterError *_Nullable *_Nonnull)error;
@end

extern void SetUpFIAInAppPurchaseAPI(id<FlutterBinaryMessenger> binaryMessenger,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +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 (v22.4.2), do not edit directly.
// Autogenerated from Pigeon (v22.6.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon

#import "messages.g.h"
Expand Down Expand Up @@ -978,4 +978,26 @@ void SetUpFIAInAppPurchaseAPIWithSuffix(id<FlutterBinaryMessenger> binaryMesseng
[channel setMessageHandler:nil];
}
}
{
FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
initWithName:[NSString stringWithFormat:@"%@%@",
@"dev.flutter.pigeon.in_app_purchase_storekit."
@"InAppPurchaseAPI.supportsStoreKit2",
messageChannelSuffix]
binaryMessenger:binaryMessenger
codec:FIAGetMessagesCodec()];
if (api) {
NSCAssert(
[api respondsToSelector:@selector(supportsStoreKit2WithError:)],
@"FIAInAppPurchaseAPI api (%@) doesn't respond to @selector(supportsStoreKit2WithError:)",
api);
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
FlutterError *error;
NSNumber *output = [api supportsStoreKit2WithError:&error];
callback(wrapResult(output, error));
}];
} else {
[channel setMessageHandler:nil];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ void main() {
// When using the Android plugin directly it is mandatory to register
// the plugin as default instance as part of initializing the app.
InAppPurchaseStoreKitPlatform.registerPlatform();
InAppPurchaseStoreKitPlatform.enableStoreKit2();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it would be good to have Storekit 2 enabled inside the example project


runApp(_MyApp());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,10 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform {
Future<String?> getCountryCode() => countryCode();

/// Turns on StoreKit2. You cannot disable this after it is enabled.
void enableStoreKit2() {
_useStoreKit2 = true;
/// This can only be enabled if your device supports StoreKit 2.
static Future<bool> enableStoreKit2() async {
_useStoreKit2 = await SKRequestMaker.supportsStoreKit2();
return _useStoreKit2;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +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 (v22.4.2), do not edit directly.
// Autogenerated from Pigeon (v22.6.0), 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, no_leading_underscores_for_local_identifiers

Expand Down Expand Up @@ -984,4 +984,33 @@ class InAppPurchaseAPI {
return;
}
}

Future<bool> supportsStoreKit2() async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.supportsStoreKit2$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(null) as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as bool?)!;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@ class SKRequestMaker {
{Map<String, Object?>? receiptProperties}) {
return _hostApi.refreshReceipt(receiptProperties: receiptProperties);
}

/// Check if current device supports StoreKit 2.
static Future<bool> supportsStoreKit2() async {
return _hostApi.supportsStoreKit2();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,6 @@ abstract class InAppPurchaseAPI {
void removePaymentQueueDelegate();

void showPriceConsentIfNeeded();

bool supportsStoreKit2();
}
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.20
version: 0.3.20+1

environment:
sdk: ^3.3.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,11 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi {
void stopObservingPaymentQueue() {
queueIsActive = false;
}

@override
bool supportsStoreKit2() {
return true;
}
}

class FakeStoreKit2Platform implements TestInAppPurchase2Api {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:in_app_purchase_storekit/store_kit_2_wrappers.dart';

import 'fakes/fake_storekit_platform.dart';
import 'sk2_test_api.g.dart';
import 'test_api.g.dart';

void main() {
final SK2Product dummyProductWrapper = SK2Product(
Expand All @@ -26,17 +27,20 @@ void main() {
TestWidgetsFlutterBinding.ensureInitialized();

final FakeStoreKit2Platform fakeStoreKit2Platform = FakeStoreKit2Platform();
final FakeStoreKitPlatform fakeStoreKitPlatform = FakeStoreKitPlatform();

late InAppPurchaseStoreKitPlatform iapStoreKitPlatform;

setUpAll(() {
TestInAppPurchase2Api.setUp(fakeStoreKit2Platform);
TestInAppPurchaseApi.setUp(fakeStoreKitPlatform);
});

setUp(() {
InAppPurchaseStoreKitPlatform.registerPlatform();
iapStoreKitPlatform =
InAppPurchasePlatform.instance as InAppPurchaseStoreKitPlatform;
iapStoreKitPlatform.enableStoreKit2();
InAppPurchaseStoreKitPlatform.enableStoreKit2();
fakeStoreKit2Platform.reset();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi {
void showPriceConsentIfNeeded() {
showPriceConsent = true;
}

@override
bool supportsStoreKit2() {
return true;
}
}

class TestPaymentQueueDelegate extends SKPaymentQueueDelegateWrapper {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +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 (v22.4.2), do not edit directly.
// Autogenerated from Pigeon (v22.6.0), 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, no_leading_underscores_for_local_identifiers
// ignore_for_file: avoid_relative_lib_imports
Expand Down Expand Up @@ -153,6 +153,8 @@ abstract class TestInAppPurchaseApi {

void showPriceConsentIfNeeded();

bool supportsStoreKit2();

static void setUp(
TestInAppPurchaseApi? api, {
BinaryMessenger? binaryMessenger,
Expand Down Expand Up @@ -581,5 +583,31 @@ abstract class TestInAppPurchaseApi {
});
}
}
{
final BasicMessageChannel<
Object?> pigeonVar_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.supportsStoreKit2$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
_testBinaryMessengerBinding!.defaultBinaryMessenger
.setMockDecodedMessageHandler<Object?>(pigeonVar_channel, null);
} else {
_testBinaryMessengerBinding!.defaultBinaryMessenger
.setMockDecodedMessageHandler<Object?>(pigeonVar_channel,
(Object? message) async {
try {
final bool output = api.supportsStoreKit2();
return <Object?>[output];
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
}
}