-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[in_app_purchase] 0.3.0 Migration guide #4137
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
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
ffe6922
Add migration guide
JeroenWeener 318bddf
Add introductory price use case to migration guide
JeroenWeener 8ee0f94
Fix mention of two use cases instead of three
JeroenWeener 5af4608
Add anchor to billing client migration guide reference
JeroenWeener 09f7827
Update subscription use cases in migration guide
JeroenWeener 2a72802
Add unwrap comments
JeroenWeener 9aca27d
Extract code excerpts
JeroenWeener 916d9e5
Inject migration guide code excerpts
JeroenWeener 9d97949
Add missing license block
JeroenWeener 4c02cdf
Merge branch 'main' into iap/127844
stuartmorgan-g File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
packages/in_app_purchase/in_app_purchase_android/example/build.excerpt.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| targets: | ||
| $default: | ||
| sources: | ||
| include: | ||
| - lib/** | ||
| # Some default includes that aren't really used here but will prevent | ||
| # false-negative warnings: | ||
| - $package$ | ||
| - lib/$lib$ | ||
| exclude: | ||
| - '**/.*/**' | ||
| - '**/build/**' | ||
| builders: | ||
| code_excerpter|code_excerpter: | ||
| enabled: true |
62 changes: 62 additions & 0 deletions
62
packages/in_app_purchase/in_app_purchase_android/example/lib/migration_guide_examples.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| // 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. | ||
|
|
||
| // ignore_for_file: unused_local_variable | ||
|
|
||
| import 'package:in_app_purchase_android/billing_client_wrappers.dart'; | ||
| import 'package:in_app_purchase_android/in_app_purchase_android.dart'; | ||
| import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; | ||
|
|
||
| // #docregion one-time-purchase-price | ||
| /// Handles the one time purchase price of a product. | ||
| void handleOneTimePurchasePrice(ProductDetails productDetails) { | ||
| if (productDetails is GooglePlayProductDetails) { | ||
| final ProductDetailsWrapper product = productDetails.productDetails; | ||
| if (product.productType == ProductType.inapp) { | ||
| // Unwrapping is safe because the product is a one time purchase. | ||
| final OneTimePurchaseOfferDetailsWrapper offer = | ||
| product.oneTimePurchaseOfferDetails!; | ||
| final String price = offer.formattedPrice; | ||
| } | ||
| } | ||
| } | ||
| // #enddocregion one-time-purchase-price | ||
|
|
||
| // #docregion subscription-free-trial | ||
| /// Handles the free trial period of a subscription. | ||
| void handleFreeTrialPeriod(ProductDetails productDetails) { | ||
| if (productDetails is GooglePlayProductDetails) { | ||
| final ProductDetailsWrapper product = productDetails.productDetails; | ||
| if (product.productType == ProductType.subs) { | ||
| // Unwrapping is safe because the product is a subscription. | ||
| final SubscriptionOfferDetailsWrapper offer = | ||
| product.subscriptionOfferDetails![productDetails.subscriptionIndex!]; | ||
| final List<PricingPhaseWrapper> pricingPhases = offer.pricingPhases; | ||
| if (pricingPhases.first.priceAmountMicros == 0) { | ||
| // Free trial period logic. | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // #enddocregion subscription-free-trial | ||
|
|
||
| // #docregion subscription-introductory-price | ||
| /// Handles the introductory price period of a subscription. | ||
| void handleIntroductoryPricePeriod(ProductDetails productDetails) { | ||
| if (productDetails is GooglePlayProductDetails) { | ||
| final ProductDetailsWrapper product = productDetails.productDetails; | ||
| if (product.productType == ProductType.subs) { | ||
| // Unwrapping is safe because the product is a subscription. | ||
| final SubscriptionOfferDetailsWrapper offer = | ||
| product.subscriptionOfferDetails![productDetails.subscriptionIndex!]; | ||
| final List<PricingPhaseWrapper> pricingPhases = offer.pricingPhases; | ||
| if (pricingPhases.length >= 2 && | ||
| pricingPhases.first.priceAmountMicros < | ||
| pricingPhases[1].priceAmountMicros) { | ||
| // Introductory pricing period logic. | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // #enddocregion subscription-introductory-price |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
157 changes: 157 additions & 0 deletions
157
packages/in_app_purchase/in_app_purchase_android/migration_guide.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| <?code-excerpt path-base="excerpts/packages/in_app_purchase_android_example"?> | ||
| # Migration Guide from 0.2.x to 0.3.0 | ||
|
|
||
| Starting November 2023, Android Billing Client V4 is no longer supported, | ||
| requiring app developers using the plugin to migrate to 0.3.0. | ||
| Version 0.3.0 introduces breaking changes to the API as a result of changes in | ||
| the underlying Google Play Billing Library. | ||
| For context around the changes see the [recent changes to subscriptions in Play Console][1] | ||
| and the [Google Play Billing Library migration guide][2]. | ||
|
|
||
| ## SKUs become Products | ||
|
|
||
| SKUs have been replaced with products. For example, the `SkuDetailsWrapper` | ||
| class has been replaced by `ProductDetailsWrapper`, and the `SkuType` enum has | ||
| been replaced by `ProductType`. | ||
|
|
||
| Products are used to represent both in-app purchases (also referred to as | ||
| one-time purchases) and subscriptions. There are a few changes to the data model | ||
| that are important to note. | ||
|
|
||
| Previously, an SKU of `SkuType.subs` would contain a `freeTrialPeriod`, | ||
| `introductoryPrice` and some related properties. In the new model, this has been | ||
| replaced by a more generic approach. | ||
| A subscription product can have multiple pricing phases, each with its own | ||
| price, subscription period etc. This allows for more flexibility in the pricing | ||
| model. | ||
|
|
||
| In the next section we will look at how to migrate code that uses the old model | ||
| to the new model. Instead of listing all possible use cases, we will focus on | ||
| three: getting the price of a one time purchase, and handling both free trials | ||
| and introductory prices for subscriptions. Other use cases can be migrated in a | ||
| similar way. | ||
|
|
||
| ### Use case: getting the price of a one time purchase | ||
|
|
||
| Below code shows how to get the price of a one time purchase before and after | ||
| the migration. Other properties can be obtained in a similar way. | ||
|
|
||
| Code before migration: | ||
|
|
||
| ```dart | ||
| SkuDetailsWrapper sku; | ||
|
|
||
| if (sku.type == SkuType.inapp) { | ||
| String price = sku.price; | ||
| } | ||
| ``` | ||
|
|
||
| Code after migration: | ||
|
|
||
| <?code-excerpt "migration_guide_examples.dart (one-time-purchase-price)"?> | ||
| ```dart | ||
| /// Handles the one time purchase price of a product. | ||
| void handleOneTimePurchasePrice(ProductDetails productDetails) { | ||
| if (productDetails is GooglePlayProductDetails) { | ||
| final ProductDetailsWrapper product = productDetails.productDetails; | ||
| if (product.productType == ProductType.inapp) { | ||
| // Unwrapping is safe because the product is a one time purchase. | ||
| final OneTimePurchaseOfferDetailsWrapper offer = | ||
| product.oneTimePurchaseOfferDetails!; | ||
| final String price = offer.formattedPrice; | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Use case: free trials | ||
|
|
||
| Below code shows how to handle free trials for subscriptions before and after | ||
| the migration. As subscriptions can contain multiple offers, each containing | ||
| multiple price phases, the logic is more involved. | ||
|
|
||
| Code before migration: | ||
|
|
||
| ```dart | ||
| SkuDetailsWrapper sku; | ||
|
|
||
| if (sku.type == SkuType.subs) { | ||
| if (sku.freeTrialPeriod.isNotEmpty) { | ||
| // Free trial period logic. | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Code after migration: | ||
|
|
||
| <?code-excerpt "migration_guide_examples.dart (subscription-free-trial)"?> | ||
| ```dart | ||
| /// Handles the free trial period of a subscription. | ||
| void handleFreeTrialPeriod(ProductDetails productDetails) { | ||
| if (productDetails is GooglePlayProductDetails) { | ||
| final ProductDetailsWrapper product = productDetails.productDetails; | ||
| if (product.productType == ProductType.subs) { | ||
| // Unwrapping is safe because the product is a subscription. | ||
| final SubscriptionOfferDetailsWrapper offer = | ||
| product.subscriptionOfferDetails![productDetails.subscriptionIndex!]; | ||
| final List<PricingPhaseWrapper> pricingPhases = offer.pricingPhases; | ||
| if (pricingPhases.first.priceAmountMicros == 0) { | ||
| // Free trial period logic. | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Use case: introductory prices | ||
|
|
||
| Below code shows how to handle introductory prices for subscriptions before and | ||
| after the migration. As subscriptions can contain multiple offers, each | ||
| containing multiple price phases, the logic is more involved. | ||
|
|
||
| Code before migration: | ||
|
|
||
| ```dart | ||
| SkuDetailsWrapper sku; | ||
|
|
||
| if (sku.type == SkuType.subs) { | ||
| if (sku.introductoryPriceAmountMicros != 0) { | ||
| // Introductory price period logic. | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Code after migration: | ||
|
|
||
| <?code-excerpt "migration_guide_examples.dart (subscription-introductory-price)"?> | ||
| ```dart | ||
| /// Handles the introductory price period of a subscription. | ||
| void handleIntroductoryPricePeriod(ProductDetails productDetails) { | ||
| if (productDetails is GooglePlayProductDetails) { | ||
| final ProductDetailsWrapper product = productDetails.productDetails; | ||
| if (product.productType == ProductType.subs) { | ||
| // Unwrapping is safe because the product is a subscription. | ||
| final SubscriptionOfferDetailsWrapper offer = | ||
| product.subscriptionOfferDetails![productDetails.subscriptionIndex!]; | ||
| final List<PricingPhaseWrapper> pricingPhases = offer.pricingPhases; | ||
| if (pricingPhases.length >= 2 && | ||
| pricingPhases.first.priceAmountMicros < | ||
| pricingPhases[1].priceAmountMicros) { | ||
| // Introductory pricing period logic. | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Removal of `launchPriceChangeConfirmationFlow` | ||
|
|
||
| The method `launchPriceChangeConfirmationFlow` has been removed. Price changes | ||
| are no longer handled by the application but are instead handled by the Google | ||
| Play Store automatically. See [subscription price changes][3] for more | ||
| information. | ||
|
|
||
| <!-- References --> | ||
| [1]: https://support.google.com/googleplay/android-developer/answer/12124625 | ||
| [2]: https://developer.android.com/google/play/billing/migrate-gpblv6#5-or-6 | ||
| [3]: https://developer.android.com/google/play/billing/subscriptions#price-change | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.