Миграция Adapty React Native SDK на v3.3

Adapty SDK 3.3.1 — это крупный релиз, который принёс ряд улучшений, требующих от вас выполнения нескольких шагов миграции.

  1. Обновите Adapty SDK до версии 3.3.x.
  2. Обновите модели.
  3. Удалите метод getProductsIntroductoryOfferEligibility.
  4. Обновите совершение покупки.
  5. Обновите отображение пейвола, созданного в Paywall Builder.
  6. Пересмотрите реализацию таймера, заданного разработчиком.
  7. Обновите обработку событий покупки в Paywall Builder.
  8. Обновите обработку событий пользовательских действий в Paywall Builder.
  9. Измените колбэк onProductSelected.
  10. Удалите параметры сторонних интеграций из метода updateProfile.
  11. Обновите настройки интеграций для Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase и Google Analytics, Mixpanel, OneSignal и Pushwoosh.
  12. Обновите реализацию режима Observer mode.

Обновление Adapty React Native SDK до версии 3.3.x

До версии 3.3.1 SDK react-native-adapty являлся основным и обязательным SDK для работы Adapty в вашем приложении. SDK @adapty/react-native-ui был необязательным и требовался только при использовании Adapty Paywall Builder.

Начиная с версии 3.3.1, SDK @adapty/react-native-ui считается устаревшим, а его функциональность перенесена в SDK react-native-adapty. Чтобы обновиться до версии 3.3.1, выполните следующие шаги:

  1. Обновите пакет react-native-adapty до версии 3.3.1.
  2. Удалите пакет @adapty/react-native-ui из зависимостей проекта.
  3. Синхронизируйте зависимости проекта, чтобы применить изменения.

Изменения в моделях

Новые модели

  1. AdaptySubscriptionOffer:

    export interface AdaptySubscriptionOffer {
      readonly identifier: AdaptySubscriptionOfferId;
    
      phases: AdaptyDiscountPhase[];
    
      android?: {
        offerTags?: string[];
      };
    }
  2. AdaptySubscriptionOfferId:

    export type AdaptySubscriptionOfferId =
      | { id?: string; type: 'introductory'; }
      | { id: string; type: 'promotional' | 'win_back'; };

Изменённые модели

  1. AdaptyPaywallProduct:

    • Переименовано свойство subscriptionDetails в subscription.

    -  subscriptionDetails?: AdaptySubscriptionDetails; 
    +  subscription?: AdaptySubscriptionDetails;
  2. AdaptySubscriptionDetails:

  • promotionalOffer удалён. Теперь promotional offer передаётся в свойстве offer только если он доступен. В этом случае offer?.identifier?.type будет равен 'promotional'.

    • introductoryOfferEligibility удалён (офферы возвращаются только если пользователь имеет право на них).

    • offerId удалён. ID оффера теперь хранится в AdaptySubscriptionOffer.identifier.

    • offerTags перемещён в AdaptySubscriptionOffer.android.

   -  introductoryOffers?: AdaptyDiscountPhase[];
   +  offer?: AdaptySubscriptionOffer;
   
      ios?: {
   -    promotionalOffer?: AdaptyDiscountPhase;
        subscriptionGroupIdentifier?: string;
      };
   
      android?: {
   -    offerId?: string;
        basePlanId: string;
   -    introductoryOfferEligibility: OfferEligibility;
   -    offerTags?: string[];
        renewalType?: 'prepaid' | 'autorenewable';
      };
    }
  1. AdaptyDiscountPhase:
  • Поле identifier удалено из модели AdaptyDiscountPhase. Идентификатор оффера теперь хранится в AdaptySubscriptionOffer.identifier.

    -  ios?: {
    -    readonly identifier?: string;
    -  };

Удалённые модели

  1. AttributionSource:
    • Вместо него теперь используется строка там, где ранее применялся AttributionSource.
  2. OfferEligibility:
    • Эта модель удалена, так как больше не нужна. Теперь оффер возвращается только если пользователь имеет право на его получение.

Удаление метода getProductsIntroductoryOfferEligibility

До Adapty SDK 3.3.1 объекты продуктов всегда включали офферы, даже если пользователь не имел права на их получение. Это требовало ручной проверки права на получение оффера перед его использованием.

Начиная с версии 3.3.1, объект продукта включает офферы только если пользователь имеет на них право. Это упрощает процесс: если оффер присутствует, можно считать, что пользователь имеет право на его получение.

Обновление процесса покупки

В более ранних версиях отменённые и ожидающие покупки считались ошибками и возвращали коды 2: 'paymentCancelled' и 25: 'pendingPurchase' соответственно.

Начиная с версии 3.3.1, отменённые и ожидающие покупки считаются успешными результатами и должны обрабатываться соответствующим образом:

try {
    const purchaseResult = await adapty.makePurchase(product);
    switch (purchaseResult.type) {
      case 'success':
        const isSubscribed = purchaseResult.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive;

        if (isSubscribed) {
          // Grant access to the paid features
        }
        break;
      case 'user_cancelled':
        // Handle the case where the user canceled the purchase
        break;
      case 'pending':
        // Handle deferred purchases (e.g., the user will pay offline with cash)
        break;
    }
} catch (error) {
    // Handle the error
}

Обновление отображения пейволов Paywall Builder

Актуальные примеры смотрите в документации Отображение новых пейволов Paywall Builder в React Native.

- import { createPaywallView } from '@adapty/react-native-ui';
+ import { createPaywallView } from 'react-native-adapty/dist/ui';

const view = await createPaywallView(paywall);

view.registerEventHandlers(); // handle close press, etc

try {
  await view.present();
} catch (error) {
  // handle the error
}

Обновление реализации таймеров, определяемых разработчиком

Переименуйте параметр timerInfo в customTimers:

- let timerInfo = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) }
+ let customTimers = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) }
 //and then you can pass it to createPaywallView as follows:
- view = await createPaywallView(paywall, { timerInfo })
+ view = await createPaywallView(paywall, { customTimers })

Изменение событий покупки в Paywall Builder

Раньше:

  • Отменённые покупки вызывали коллбэк onPurchaseCancelled.
  • Ожидающие покупки возвращали код ошибки 25: 'pendingPurchase'.

Теперь:

  • Оба случая обрабатываются коллбэком onPurchaseCompleted.

Шаги для миграции:

  1. Удалите коллбэк onPurchaseCancelled.
  2. Удалите обработку кода ошибки 25: 'pendingPurchase'.
  3. Обновите коллбэк onPurchaseCompleted:

const view = await createPaywallView(paywall);

const unsubscribe = view.registerEventHandlers({
  // ... other optional callbacks
  onPurchaseCompleted(purchaseResult, product) {
    switch (purchaseResult.type) {
      case 'success':
        const isSubscribed = purchaseResult.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive;

        if (isSubscribed) {
          // Grant access to the paid features
        }
        break;
      case 'user_cancelled':
        // Handle the case where the user canceled the purchase
        break;
      case 'pending':
        // Handle deferred purchases (e.g., the user will pay offline with cash)
        break;
    }
    return purchaseResult.type !== 'user_cancelled';
  },
});

Обновление событий кастомных действий в Paywall Builder

Удалённые коллбэки:

  • onAction
  • onCustomEvent

Добавленный коллбэк:

  • Новый коллбэк onCustomAction(actionId). Используйте его для кастомных действий.

Изменение коллбэка onProductSelected

Ранее onProductSelected принимал объект product. Теперь требуется productId в виде строки.

Удаление параметров сторонних интеграций из метода updateProfile

Идентификаторы сторонних интеграций теперь задаются с помощью метода setIntegrationIdentifier. Метод updateProfile больше не принимает их.

Обновление конфигурации SDK сторонних интеграций

Чтобы интеграции корректно работали с Adapty React Native SDK 3.3.1 и выше, обновите конфигурации SDK для следующих интеграций согласно разделам ниже.

Кроме того, если вы использовали AttributionSource для получения идентификатора атрибуции, измените код так, чтобы передавать нужный идентификатор в виде строки.

Adjust

Обновите код вашего мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Настройка SDK для интеграции с Adjust.

 import { Adjust, AdjustConfig } from "react-native-adjust";
 import { adapty } from "react-native-adapty";

 var adjustConfig = new AdjustConfig(appToken, environment);

 // Before submiting Adjust config...
 adjustConfig.setAttributionCallbackListener(attribution => {
   // Make sure Adapty SDK is activated at this point
   // You may want to lock this thread awaiting of `activate`
   adapty.updateAttribution(attribution, "adjust");
 });

 // ...
 Adjust.create(adjustConfig);

+ Adjust.getAdid((adid) => {
+   if (adid)
+     adapty.setIntegrationIdentifier("adjust_device_id", adid);
+ });

AirBridge

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Конфигурация SDK для интеграции AirBridge.

 import Airbridge from 'airbridge-react-native-sdk';
 import { adapty } from 'react-native-adapty';

 try {
   const deviceId = await Airbridge.state.deviceUUID();

-  await adapty.updateProfile({
-    airbridgeDeviceId: deviceId,
-  });
+  await adapty.setIntegrationIdentifier("airbridge_device_id", deviceId);
 } catch (error) {
   // handle `AdaptyError`
 }

Amplitude

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Конфигурация SDK для интеграции Amplitude.

  import { adapty } from 'react-native-adapty';

 try {
-   await adapty.updateProfile({
-     amplitudeDeviceId: deviceId,
-     amplitudeUserId: userId,
-   });
+   await adapty.setIntegrationIdentifier("amplitude_device_id", deviceId);
+   await adapty.setIntegrationIdentifier("amplitude_user_id", userId);
 } catch (error) {
   // handle `AdaptyError`
 }

AppMetrica

Обновите код своего мобильного приложения, как показано ниже. Полный пример кода можно найти в разделе Настройка SDK для интеграции с AppMetrica.

 import { adapty } from 'react-native-adapty';
 import AppMetrica, { DEVICE_ID_KEY, StartupParams, StartupParamsReason } from '@appmetrica/react-native-analytics';

 // ...
 const startupParamsCallback = async (
   params?: StartupParams,
   reason?: StartupParamsReason
 ) => {
   const deviceId = params?.deviceId
   if (deviceId) {
     try {
-       await adapty.updateProfile({
-         appmetricaProfileId: 'YOUR_ADAPTY_CUSTOMER_USER_ID',
-         appmetricaDeviceId: deviceId,
-       });
+       await adapty.setIntegrationIdentifier("appmetrica_profile_id", 'YOUR_ADAPTY_CUSTOMER_USER_ID');
+       await adapty.setIntegrationIdentifier("appmetrica_device_id", deviceId);
     } catch (error) {
       // handle `AdaptyError`
     }
   }
 }

 AppMetrica.requestStartupParams(startupParamsCallback, [DEVICE_ID_KEY])

AppsFlyer

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Настройка SDK для интеграции с AppsFlyer.

 import { adapty, AttributionSource } from 'react-native-adapty';
 import appsFlyer from 'react-native-appsflyer';

 appsFlyer.onInstallConversionData(installData => {
     try {
-        const networkUserId = appsFlyer.getAppsFlyerUID();
-        adapty.updateAttribution(installData, AttributionSource.AppsFlyer, networkUserId);
+        const uid = appsFlyer.getAppsFlyerUID();
+        adapty.setIntegrationIdentifier("appsflyer_id", uid);
+        adapty.updateAttribution(installData, "appsflyer");
     } catch (error) {
         // handle the error
     }
 });

 // ...
 appsFlyer.initSdk(/*...*/);

Branch

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Конфигурация SDK для интеграции Branch.

 import { adapty, AttributionSource } from 'react-native-adapty';
 import branch from 'react-native-branch';

 branch.subscribe({
   enComplete: ({
     params,
   }) => {
-    adapty.updateAttribution(params, AttributionSource.Branch);
+    adapty.updateAttribution(params, "branch");
   },
 });

Facebook Ads

Обновите код вашего мобильного приложения, как показано ниже. Полный пример кода можно найти в разделе Настройка SDK для интеграции с Facebook Ads.

 import { adapty } from 'react-native-adapty';
 import { AppEventsLogger } from 'react-native-fbsdk-next';

 try {
   const anonymousId = await AppEventsLogger.getAnonymousID();

-  await adapty.updateProfile({
-    facebookAnonymousId: anonymousId,
-  });
+  await adapty.setIntegrationIdentifier("facebook_anonymous_id", anonymousId);
 } catch (error) {
   // handle `AdaptyError`
 }

Firebase и Google Analytics

Обновите код мобильного приложения, как показано ниже. Полный пример кода можно найти в разделе настройка SDK для интеграции с Firebase и Google Analytics.

 import analytics from '@react-native-firebase/analytics';
 import { adapty } from 'react-native-adapty';

 try {
   const appInstanceId = await analytics().getAppInstanceId();

-   await adapty.updateProfile({
-     firebaseAppInstanceId: appInstanceId,
-   });
+   await adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId);
 } catch (error) {
   // handle `AdaptyError`
 }

Mixpanel

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Конфигурация SDK для интеграции Mixpanel.

 import { adapty } from 'react-native-adapty';
 import { Mixpanel } from 'mixpanel-react-native';

 // ...
 try {
-   await adapty.updateProfile({
-     mixpanelUserId: mixpanelUserId,
-   });
+   await adapty.setIntegrationIdentifier("mixpanel_user_id", mixpanelUserId);
 } catch (error) {
   // handle `AdaptyError`
 }

OneSignal

Обновите код мобильного приложения, как показано ниже. Полный пример кода можно найти в разделе Настройка SDK для интеграции с OneSignal.

Pushwoosh

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Конфигурация SDK для интеграции Pushwoosh.

 import { adapty } from 'react-native-adapty';
 import Pushwoosh from 'pushwoosh-react-native-plugin';

 // ...
 try {
-  await adapty.updateProfile({
-    pushwooshHWID: hwid,
-  });
+  await adapty.setIntegrationIdentifier("pushwoosh_hwid", hwid);
 } catch (error) {
   // handle `AdaptyError`
 }

Обновление реализации режима Observer

Обновите способ привязки пейволов к транзакциям. Раньше для назначения variationId использовался метод setVariationId. Теперь можно передавать variationId напрямую при записи транзакции с помощью нового метода reportTransaction. Итоговый пример кода смотрите в разделе Привязка пейволов к транзакциям покупок в режиме Observer.

Не забывайте фиксировать транзакцию с помощью метода reportTransaction. Если пропустить этот шаг, Adapty не распознает транзакцию, не предоставит уровни доступа, не включит её в аналитику и не отправит в интеграции. Этот шаг обязателен!

Обратите внимание, что порядок параметров метода reportTransaction отличается от порядка параметров метода setVariationId.

  const variationId = paywall.variationId;

 try {
-    await adapty.setVariationId(variationId, transactionId);
+    await adapty.reportTransaction(transactionId, variationId);
 } catch (error) {
     // handle the `AdaptyError`
 }