React Native SDK 6.x to 7.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 package.json file to upgrade your Qonversion SDK to the latest

"react-native-qonversion": "^7.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

Speaking of the SDK side - the first big part of the changes is the addition of the new basePlanId field to Product which can be set to the product via Qonversion Dashboard.

We have deprecated the Product.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 ProductStoreDetails, 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 Product fields changes

Along 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 ProductType 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 SubscriptionPeriod 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.

Product updates summary

Below is the shortened summary of the Product 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. The INTRO and UNKNOWN values were added to ProductType 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 and trialDuration fields were replaced with subscriptionPeriod and trialPeriod fields of type SubscriptionPeriod with the values calculated from the store details both for Android and iOS;
  • the prettyPrice and prettyIntroductoryPrice fields on Android now prefers storeDetails 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 - PurchaseModel and PurchaseUpdateModel, 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 Product class.

Below is an example of how the purchase flow has changed:

// Old
const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().purchaseProduct(product);

// New
const purchaseModel: PurchaseModel = product.toPurchaseModel();
const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().purchase(purchaseModel);

And an updatePurchase flow example:

// Old
const entitlement: Map<string, Entitlement> = await Qonversion.getSharedInstance().updatePurchaseWithProduct(product, 'oldProductId');

// New
const purchaseUpdateModel: PurchaseUpdateModel = product.toPurchaseUpdateModel('oldProductId');
const entitlement: Map<string, Entitlement> = await Qonversion.getSharedInstance().updatePurchase(purchaseUpdateModel);

If you were using only product identifiers, you can create purchase models manually as follows:

// Old
const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().purchase('productId');

// New
const purchaseModel: PurchaseModel = new PurchaseModel('productId');
const entitlement: Map<string, Entitlement> = await Qonversion.getSharedInstance().updatePurchase(purchaseUpdateModel);
// Old
const entitlement: Map<string, Entitlement> = await Qonversion.getSharedInstance().updatePurchase('newProductId', 'oldProductId');

// New
const purchaseUpdateModel: PurchaseUpdateModel = new PurchaseUpdateModel('newProductId', 'oldProductId');
const entitlement: Map<string, Entitlement> = await Qonversion.getSharedInstance().updatePurchase(purchaseUpdateModel);

If necessary, you can provide a specific offer for subscription purchase.

// Specifying offer via the `toPurchaseModel` method:
const productOfferDetails = ...; // Choose an offer from `storeDetails`
const purchaseModel = product.toPurchaseModelWithOffer(productOfferDetails);

// Specifying offer ID via the `toPurchaseModel` method:
const purchaseModel = product.toPurchaseModel('offer_id');

// Specifying offer ID via the constructor:
const purchaseModel = new PurchaseModel('productId', 'offer_id');
const purchaseUpdateModel = new PurchaseUpdateModel('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

  • The checkTrialIntroEligibility method improved and now detects the eligibility based on store details on Android;
  • The ProductDuration and TrialDuration classes were removed;
  • The ProductType enum values were changed to meet guidelines;
  • The ProrationMode enum was removed and replaced with PurchaseUpdatePolicy (read more).