mirror of
https://github.com/flutter/packages.git
synced 2025-06-30 23:03:11 +08:00
[in_app_purchase] 0.3.0 Migration guide (#4137)
The API changes introduced in 0.3.0 turned out to be bigger and more complicated than anticipated. This migration guide aims to help developers upgrade to 0.3.0 by giving context to the changes and treating some exemplary use cases. The need for a migration guide was brought to my attention in flutter/flutter#127844. A follow-up PR will update the README in the app-facing package to align with the breaking changes introduced in 0.3.0, as the current one is out-of-date. Fixes flutter/flutter#127844
This commit is contained in:
@ -1,3 +1,7 @@
|
||||
## 0.3.0+8
|
||||
|
||||
* Adds a [guide for migrating](migration_guide.md) to [0.3.0](#0.3.0).
|
||||
|
||||
## 0.3.0+7
|
||||
|
||||
* Bumps org.mockito:mockito-core from 4.7.0 to 5.3.1.
|
||||
|
@ -11,6 +11,9 @@ so you do not need to add it to your `pubspec.yaml`.
|
||||
However, if you `import` this package to use any of its APIs directly, you
|
||||
should [add it to your `pubspec.yaml` as usual][3].
|
||||
|
||||
## Migrating to 0.3.0
|
||||
To migrate to version 0.3.0 from 0.2.x, have a look at the [migration guide](migration_guide.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
This plugin uses
|
||||
|
@ -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
|
@ -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
|
@ -20,6 +20,7 @@ dependencies:
|
||||
shared_preferences: ^2.0.0
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.5
|
||||
flutter_driver:
|
||||
sdk: flutter
|
||||
flutter_test:
|
||||
|
@ -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
|
@ -2,7 +2,7 @@ name: in_app_purchase_android
|
||||
description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs.
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_android
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
|
||||
version: 0.3.0+7
|
||||
version: 0.3.0+8
|
||||
|
||||
environment:
|
||||
sdk: ">=2.18.0 <4.0.0"
|
||||
|
Reference in New Issue
Block a user