Skip to content

Commit

Permalink
[in_app_purchase_storekit] disallow ios versions lower than supported…
Browse files Browse the repository at this point in the history
… from enabling storekit (#8110)

Fixes flutter/flutter#158894

Checks device version before enabling storekit 2.
  • Loading branch information
LouiseHsu authored Nov 19, 2024
1 parent fc80137 commit e95f6d8
Show file tree
Hide file tree
Showing 14 changed files with 128 additions and 9 deletions.
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();

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()));
}
});
}
}
}
}

0 comments on commit e95f6d8

Please sign in to comment.