Flutter SDK 7.x to 8.x migration guide
We've upgraded the Google Play Billing Library dependency on Android to version 6 in this release, which has led to major changes in the product and subscription management parts of our SDK. These are described below.
Upgrading version
Increase the dependency version in your prodspec.yaml file to upgrade your Qonversion SDK to the latest
dependencies:
qonversion_flutter: ^8.0.0
Product updates
Qonversion and Google Play product link changes
Google Play has recently upgraded the structure of their subscription products. We've written about it in our blog. In short, they combined the same subscriptions with different durations into a single subscription with different base plans. In addition, they announced different kinds of offers to base plans, including free trials or introductory prices. In this release, we begin to support those changes.
Previously Qonversion subscription products required a Google Play subscription identifier. Now you can also set a specific base plan for the subscription. Different base plans of the same subscription can be linked to different Qonversion products. Let's have a look at an example.
Let's say you sell monthly and annual subscriptions in your app, both on Android and iOS. With the previous Qonversion SDK versions, you would specify two Qonversion products (premium_monthly
and premium_annual
), two App Store products (premium_monthly_ios
and premium_annual_ios
), and two Google Play products (premium_monthly_android
and premium_annual_android
). Then you would link those store products to Qonversion products as follows:
premium_monthly
- premium_monthly_ios
and premium_monthly_android
,
premium_annual
- premium_annual_ios
and premium_annual_android
.
With the new Google Play Subscriptions structure, you can create only one subscription for Android - premium_android
and two base plans for it (monthly
and annual
). With this done, you can now use a single subscription for both Qonversion products as follows:
premium_monthly
- premium_monthly_ios
and premium_android
with monthly
base plan,
premium_annual
- premium_annual_ios
and premium_android
with annual
base plan.
Store details for the QProduct
class
QProduct
classSpeaking of the SDK side - the first big part of the changes is the addition of the new basePlanId
field to QProduct
which can be set to the product via Qonversion Dashboard.
We have deprecated the QProduct.skuDetails
field, because SkuDetails
contain information only for backward compatible base plans and are now deprecated in the Google Play Billing Library. Instead, we have added the new storeDetails
field of type QProductStoreDetails
, which contains all the store information for the Google Play Product. You can find the specifications of that class on this page.
If basePlanId
is not specified for a product, it means one of the following:
- it is a one-time (in-app) product that can not have any base plans,
- it is a product that was not updated in the Qonversion dashboard after this version was released,
- it is an iOS-only product.
In the case of the in-app product, both skuDetails
and storeDetails
fields will contain information about that in-app. But in the second case, when there is a subscription product without a specified base plan, the skuDetails
field will contain information about the backward compatible base plan, while the storeDetails
field will only contain common subscription information without base-plan-specific data. It means that you should start using storeDetails
field for base-plan-specific data (like price, duration, and so on) for Android subscription products only if you've specified their base plan IDs in Qonversion product settings.
The other QProduct
fields changes
QProduct
fields changesAlong with the above changes, we have changed the type
field and removed the duration
and trialDuration
fields from the product class. The type
and duration
were previously being set via the dashboard, and the trialDuration
was calculated from the store details and was represented as an enumeration with several common durations. Now, the product type
is calculated from the store details both for Android and iOS. We've also added the intro
value to QProductType
to separate trial
and intro
products (Android only for now). An unknown
value was also added for the cases where we are unable to determine the product type. The duration
and trialDuration
fields were replaced with subscriptionPeriod
and trialPeriod
. Both properties are of type QSubscriptionPeriod
and the values are also calculated from the store details for both platforms.
The last thing to note is that we've also changed the prettyPrice
and prettyIntrodactoryPrice
fields calculation for Android. Earlier they were calculated from skuDetails
, while now we first try to use storeDetails
if possible and only then fall back to skuDetails
.
QProduct
updates summary
QProduct
updates summaryBelow is the shortened summary of the QProduct
changes:
- new
basePlanId
field, specifying concrete subscription base plan for Google Play subscriptions, added, skuDetails
field deprecated,- new
storeDetails
field, containing information about Google Play product, added; - the
type
field is now calculated based on store details both for Android and iOS, instead of the value set via the Qonversion dashboard. Theintro
andunknown
values were added toQProductType
to separate cases of trial and intro products (Android only for now) and also the case when we are unable to determine the product type; - the
duration
andtrialDuration
fields were replaced withsubscriptionPeriod
andtrialPeriod
fields of typeQSubscriptionPeriod
with the values calculated from the store details both for Android and iOS; - the
prettyIntroductoryPrice
field on Android now prefersstoreDetails
to get the price from, and only then -skuDetails
.
Purchase flow updates
In this release, we have changed the purchase
and updatePurchase
methods signatures. Previously there were several methods for different sets of arguments. Now we've arranged all those properties into classes - QPurchaseModel
and QPurchaseUpdateModel
, that should be instantiated and provided to the single purchase
or updatePurchase
method.
You can create instances of those classes either manually or by using the utility functions toPurchaseModel
and toPurchaseUpdateModel
added to the QProduct
class.
Below is an example of how the purchase
flow has changed:
// Old
final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchaseProduct(product);
// New
final QPurchaseModel purchaseModel = product.toPurchaseModel();
final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchase(purchaseModel);
And an updatePurchase
flow example:
// Old
final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchaseWithProduct(
newProduct: product,
oldProductId: 'oldProductId',
);
// New
final QPurchaseUpdateModel purchaseUpdateModel = product.toPurchaseUpdateModel('oldProductId');
final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchase(purchaseUpdateModel);
If you were using only product identifiers, you can create purchase models manually as follows:
// Old
final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchase('productId');
// New
final QPurchaseModel purchaseModel = new QPurchaseModel('productId');
final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchase(purchaseModel);
// Old
final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchase(
newProductId: 'newId',
oldProductId: 'oldId',
);
// New
final QPurchaseUpdateModel purchaseUpdateModel = new QPurchaseUpdateModel('newProductId', 'oldProductId');
final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchase(purchaseUpdateModel);
If necessary, you can provide a specific offer for subscription purchase.
// Specifying offer via the `toPurchaseModel` method:
final QProductOfferDetails productOfferDetails = ...; // Choose an offer from `storeDetails`
final QPurchaseModel purchaseModel = product.toPurchaseModelWithOffer(productOfferDetails);
// Specifying offer ID via the `toPurchaseModel` method:
final QPurchaseModel purchaseModel = product.toPurchaseModel('offer_id');
// Specifying offer ID via the constructor:
final QPurchaseModel purchaseModel = new QPurchaseModel('productId', 'offer_id');
final QPurchaseUpdateModel purchaseUpdateModel = new QPurchaseUpdateModel('newProductId', 'oldProductId', 'offer_id');
// Specifying offer ID after the purchase model creation:
purchaseModel.offerId = 'offer_id';
If provided, we will try to find and purchase the offer with the specified ID for the requested Qonversion product. If there is no offer with the specified ID, an error will be returned. If no offer ID is provided for the subscription purchase of Qonversion product with a specified base plan ID, then we will choose the most profitable offer for the client from all the available offers. We calculate the cheapest price for the client by comparing all the trial or intro phases and the base plan. For iOS, old Qonversion products (where the base plan ID is not specified), or in-app products, the offer ID is ignored.
You can also remove any intro/trial offer from the purchase (use only a bare base plan). For that purpose, you should call removeOffer
method of purchase model:
purchaseModel.removeOffer()
The other changes
- Gradle on Android was upgraded to the version 8;
- The
checkTrialIntroEligibility
method improved and now detects the eligibility based on store details on Android; - The
QProductDuration
andQTrialDuration
classes were removed; - The
QProrationMode
enum was removed and replaced withQPurchaseUpdatePolicy
(read more).
Updated 11 months ago