From 2e166de09348f73f207648528c98d6f8a1c42683 Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Mon, 12 May 2025 14:34:19 -0700 Subject: [PATCH] [in_app_purchase_storekit] Make Storekit 2 the default (#9178) Make Storekit 2 the default. Fixes https://github.com/flutter/flutter/issues/159871 ## Pre-Review Checklist --- .../in_app_purchase/CHANGELOG.md | 4 +++- .../in_app_purchase/in_app_purchase/README.md | 19 ++++++++++++++++++- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 5 +++++ .../example/lib/main.dart | 1 - .../in_app_purchase_storekit_platform.dart | 15 +++++++++++---- .../sk2_transaction_wrapper.dart | 9 +++++++-- .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 3 ++- ...app_purchase_storekit_2_platform_test.dart | 1 - ...n_app_purchase_storekit_platform_test.dart | 1 + 11 files changed, 49 insertions(+), 13 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 02d883c551..33108fbe94 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 3.2.2 +* Updates `in_app_purchase_storekit` to 0.4.0 +* Updates README with Storekit 2 examples. * Updates README to indicate that Andoid SDK <21 is no longer supported. ## 3.2.1 diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index 1a10714154..a9ce1c98a7 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -356,6 +356,14 @@ if (productDetails is AppStoreProductDetails) { SKProductWrapper skProduct = (productDetails as AppStoreProductDetails).skProduct; print(skProduct.subscriptionGroupIdentifier); } + +// With StoreKit 2 +import 'package:in_app_purchase_storekit/store_kit_2_wrappers.dart'; + +if (productDetails is AppStoreProduct2Details) { + SK2Product product = (productDetails as AppStoreProduct2Details).sk2Product; + print(product.subscription?.subscriptionGroupID); +} ``` The `purchaseStream` provides objects of type `PurchaseDetails`. PurchaseDetails' provides all @@ -377,7 +385,7 @@ if (purchaseDetails is GooglePlayPurchaseDetails) { } ``` -How to get the `transactionState` of a purchase in iOS: +How to get the `transactionState` of a purchase in iOS, using the original StoreKit API: ```dart //import for AppStorePurchaseDetails import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; @@ -390,6 +398,15 @@ if (purchaseDetails is AppStorePurchaseDetails) { } ``` +How to get the `jsonRepresentation` of a transaction in iOS, using StoreKit 2: +```dart +//import for SK2TransactionWrapper +import 'package:in_app_purchase_storekit/store_kit_2_wrappers.dart'; + +List transactions = await SK2Transaction.transactions(); +print(transactions[0].jsonRepresentation); +``` + Please note that it is required to import `in_app_purchase_android` and/or `in_app_purchase_storekit`. ### Presenting a code redemption sheet (iOS 14) diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index b9cdb234c1..2fc1f6bed4 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.2.1 +version: 3.2.2 environment: sdk: ^3.5.0 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 c9b2240ef3..cf6651bbf2 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0 + +* **BREAKING CHANGE:** StoreKit 2 is now the default for all devices that support it. +* To revert to StoreKit1 for devices below iOS 15, call `enableStoreKit1` + ## 0.3.22+1 * Fix a channel thread-safety issue when StoreKit2 is enabled. 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 a6beb31f90..74ea473467 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 @@ -12,7 +12,6 @@ import 'consumable_store.dart'; import 'example_payment_queue_delegate.dart'; void main() { - InAppPurchaseStoreKitPlatform.enableStoreKit2(); WidgetsFlutterBinding.ensureInitialized(); // When using the Android plugin directly it is mandatory to register // the plugin as default instance as part of initializing the app. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index 6272dcd583..720607978b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -31,7 +31,7 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { InAppPurchaseStoreKitPlatform(); /// Experimental flag for StoreKit2. - static bool _useStoreKit2 = false; + static bool _useStoreKit2 = true; /// StoreKit1 static late SKPaymentQueueWrapper _skPaymentQueueWrapper; @@ -251,10 +251,17 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { @Deprecated('Use countryCode') Future getCountryCode() => countryCode(); - /// Turns on StoreKit2. You cannot disable this after it is enabled. - /// This can only be enabled if your device supports StoreKit 2. + /// StoreKit 2 is now the default. + @Deprecated('StoreKit 2 is now the default') static Future enableStoreKit2() async { - _useStoreKit2 = await SKRequestMaker.supportsStoreKit2(); + _useStoreKit2 = true; + return true; + } + + /// Call this before `registerPlatform` to re-enable StoreKit1 + @Deprecated('Please note that StoreKit 1 will be removed in the future.') + static Future enableStoreKit1() async { + _useStoreKit2 = !(await SKRequestMaker.supportsStoreKit2()); return _useStoreKit2; } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_transaction_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_transaction_wrapper.dart index ae0728f3c9..5fb6cee896 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_transaction_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_2_wrappers/sk2_transaction_wrapper.dart @@ -27,7 +27,8 @@ class SK2Transaction { required this.appAccountToken, this.subscriptionGroupID, this.price, - this.error}); + this.error, + this.jsonRepresentation}); /// The unique identifier for the transaction. final String id; @@ -62,6 +63,9 @@ class SK2Transaction { /// Any error returned from StoreKit final SKError? error; + /// The json representation of a transaction + final String? jsonRepresentation; + /// Wrapper around [Transaction.finish] /// https://developer.apple.com/documentation/storekit/transaction/3749694-finish /// Indicates to the App Store that the app delivered the purchased content @@ -105,7 +109,8 @@ extension on SK2TransactionMessage { productId: productId, purchaseDate: purchaseDate, expirationDate: expirationDate, - appAccountToken: appAccountToken); + appAccountToken: appAccountToken, + jsonRepresentation: jsonRepresentation); } PurchaseDetails convertToDetails() { 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 1e7349f770..c615f5eb4d 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.22+1 +version: 0.4.0 environment: sdk: ^3.4.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 bf8773e8bd..f854e78f61 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 @@ -33,6 +33,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { bool isPaymentQueueDelegateRegistered = false; String _countryCode = 'USA'; String _countryIdentifier = 'LL'; + bool shouldStoreKit2BeEnabled = true; void reset() { transactionList = []; @@ -283,7 +284,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { @override bool supportsStoreKit2() { - return true; + return shouldStoreKit2BeEnabled; } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart index d9a40562f5..9277127d60 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart @@ -40,7 +40,6 @@ void main() { InAppPurchaseStoreKitPlatform.registerPlatform(); iapStoreKitPlatform = InAppPurchasePlatform.instance as InAppPurchaseStoreKitPlatform; - InAppPurchaseStoreKitPlatform.enableStoreKit2(); fakeStoreKit2Platform.reset(); }); 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 a255b4fa3b..eb32ceb9b3 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 @@ -26,6 +26,7 @@ void main() { }); setUp(() { + InAppPurchaseStoreKitPlatform.enableStoreKit1(); InAppPurchaseStoreKitPlatform.registerPlatform(); iapStoreKitPlatform = InAppPurchasePlatform.instance as InAppPurchaseStoreKitPlatform;