Миграция Adapty React Native SDK на версию 3.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.

Обновление 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';
       };
     }
  3. 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.


 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 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 branch from 'react-native-branch';

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

Facebook Ads

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

 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';

 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 { 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 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`
 }