What's new in Adapty SDK 2.6
Google Billing Library v5 and v6 support and more!
If you are migrating from Adapty SDK 1.x.x, we recommend you to read What's new in Adapty SDK 2.0 article first.
Android SDK
With the release of Adapty SDK version 2.6.1, we are excited to announce support for Google Billing Library v5 and v6. This update includes several improvements and additions to the public API for the native Android, Flutter, and React Native SDKs.
Here are the key changes and enhancements:
-
Unified subscription details: We have removed separate properties for free trials on Android to provide a more streamlined experience. Instead, we have introduced an optional
subscriptionDetails
property that consolidates all subscription-related properties. It includes anintroductoryOfferPhases
property, which is a list that can contain up to two phases: the free trial phase and the introductory price phase.Before (Kotlin) After (Kotlin) Checking discount offer eligibility product.introductoryOfferEligibility == AdaptyEligibility.ELIGIBLE
product.subscriptionDetails?.introductoryOfferEligibility == AdaptyEligibility.ELIGIBLE
Introductory price info product.introductoryDiscount
product.subscriptionDetails?.introductoryOfferPhases?.firstOrNull { it.paymentMode in listOf(PaymentMode.PAY_UPFRONT, PaymentMode.PAY_AS_YOU_GO) }
Free trial info product.freeTrialPeriod
product.subscriptionDetails?.introductoryOfferPhases?.firstOrNull { it.paymentMode == PaymentMode.FREE_TRIAL }?.subscriptionPeriod
Free trial info (localized) product.localizedFreeTrialPeriod
product.subscriptionDetails?.introductoryOfferPhases?.firstOrNull { it.paymentMode == PaymentMode.FREE_TRIAL }?.localizedSubscriptionPeriod
-
Payment mode for discounts: To align with iOS, we have added the
paymentMode
property to theAdaptyProductDiscountPhase
entity. -
Renewal type for subscriptions: We have introduced the
renewalType
property in theAdaptyProductSubscriptionDetails
entity to accommodate the two types of subscriptions available on Google Play: auto-renewable and prepaid. -
Price entity updates: The
price
,localizedPrice
,currencyCode
andcurrencySymbol
properties have been moved fromAdaptyPaywallProduct
to a new entity calledPrice
. -
SKU details update: The
skuDetails
property inAdaptyPaywallProduct
has been renamed toproductDetails
to reflect the use of original product entities from Google. -
Eligibility status update: In
AdaptyEligibility
, we have replaced theUNKNOWN
value withNOT_APPLICABLE
. The latter is used for products that cannot contain offers, such asprepaid
products in the Google Play console. -
Personalized offers: We have added a boolean parameter
isOfferPersonalized
tomakePurchase()
, with a default value of false. For more information, refer to the following documentation. -
Offer identification: The
offerId
property has been added to theAccessLevel
andSubscription
entities inAdaptyProfile
. It is an optional field that represents the discount offer ID from Google Play. Additionally, we want to draw your attention to the fact that thevendorProductId
in these entities may contain eitherproductId
only orproductId:basePlanId
. -
Replacement mode: We have renamed
ProrationMode
toReplacementMode
, and the constants have been adjusted to align with Google's standards.
For more detailed information and step-by-step guides on adding products and base plans to the Google Play Store, refer to our documentation.
We hope these updates enhance your experience with Adapty SDK and the integration with Google's new billing system.
Cross-platform SDKs migration
Determine introductory offer eligibility
Adapty SDK 2.4.x and older:
// Adapty 2.4.x and older
try {
final products = await Adapty().getPaywallProducts(paywall: paywall);
final product = products[0]; // don't forget to check products count before accessing
if (product.introductoryOfferEligibility == AdaptyEligibility.eligible) {
// display offer
} else if (product.introductoryOfferEligibility == AdaptyEligibility.ineligible) {
// user is not eligible for this offer
} else {
// Adapty SDK wasn't able to determine eligibility at this step
// Refetch products with .waitForReceiptValidation policy:
final products = await adapty.getPaywallProducts(
paywall: paywall,
fetchPolicy: AdaptyIOSProductsFetchPolicy.waitForReceiptValidation,
);
// if there wasn't error, elegibility should be eligible or ineligible.
}
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle the error
}
// Adapty 2.4.x and older
import {adapty,OfferEligibility} from 'react-native-adapty';
const products = await adapty.getPaywallProducts(paywall);
const product = products[0]; // or any other product
switch (product.introductoryOfferEligibility) {
case OfferEligibility.Eligible:
// display offer
case OfferEligibility.Ineligible:
// user is not eligible for this offer
case OfferEligibility.Unknown:
// Adapty SDK wasn't able to determine eligibility at this step
// Refetch products with 'waitForReceiptValidation' policy:
}
Adapty SDK 2.6.0 and newer:
// Adapty 2.6.0+
try {
final products = await Adapty().getPaywallProducts(paywall: paywall);
// at this step you can display products
// but you shouldn't display offers as eligibilities are not determined yet
final eligibilities = await Adapty().getProductsIntroductoryOfferEligibility(products: products);
final introEligibility = eligibilities["your_product_id"];
switch (introEligibility) {
case AdaptyEligibility.eligible:
// display offer
break;
default:
// don't display offer
break;
}
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle the error
}
// Adapty 2.6.0+
import {adapty,OfferEligibility} from 'react-native-adapty';
const products = await adapty.getPaywallProducts(paywall);
const eligibilityMap = await adapty.getProductsIntroductoryOfferEligibility(products);
const introEligibility = eligibilityMap["your_product_id"];
if (intoEligibility === OfferEligibility.Eligible) {
// display offer
return;
}
// user is not eligible
Displaying product and offers
Adapty SDK 2.4.x and older:
// Adapty 2.4.x and older
try {
final products = await Adapty().getPaywallProducts(paywall: paywall);
final product = products[0];
final titleString = product.localizedTitle;
final priceString = product.localizedPrice;
// Introductory Offer
final introductoryDiscount = product.introductoryDiscount;
if (introductoryDiscount != null) {
final introPrice = introductoryDiscount.localizedPrice;
final introPeriod = introductoryDiscount.localizedSubscriptionPeriod;
final introNumberOfPeriods = introductoryDiscount.localizedNumberOfPeriods;
}
// Free Trial for Android
final freeTrialPeriod = product.freeTrialPeriod;
final localizedFreeTrialPeriod = product.localizedFreeTrialPeriod;
// Promo Offer for iOS
final promoOfferId = product.promotionalOfferId;
final promoDisount = product.discounts.firstWhere((element) => element.identifier == promoOfferId);
if (promoDisount != null) {
final promoPrice = promoDisount.localizedPrice;
final promoPeriod = promoDisount.localizedSubscriptionPeriod;
final promoNumberOfPeriods = promoDisount.localizedNumberOfPeriods;
}
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle the error
}
Adapty SDK 2.6.0 and newer:
// Adapty 2.6.0+
try {
final products = await Adapty().getPaywallProducts(paywall: paywall);
final product = products[0];
final titleString = product.localizedTitle;
final priceString = product.localizedPrice;
// It is possible to have more than one introductory offer (e.g. on Android)
final introductoryOffer = product.subscriptionDetails?.introductoryOffer.first;
if (introductoryOffer != null) {
final introPrice = introductoryOffer.price.localizedString;
final introPeriod = introductoryOffer.localizedSubscriptionPeriod;
final introNumberOfPeriods = introductoryOffer.localizedNumberOfPeriods;
}
// Promo Offer
final promotionalOfferEligibility = product.subscriptionDetails?.promotionalOfferEligibility ?? false;
final promotionalOffer = product.subscriptionDetails?.promotionalOffer;
if (promotionalOfferEligibility && promotionalOffer != null) {
final promoPrice = promotionalOffer.price.localizedString;
final promoPeriod = promotionalOffer.localizedSubscriptionPeriod;
final promoNumberOfPeriods = promotionalOffer.localizedNumberOfPeriods;
}
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle the error
}
Updated 16 days ago