# REACT-NATIVE - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-07-24T13:01:55.751Z Total files: 45 --- # File: sdk-installation-react-native-expo --- --- title: "Install & configure Adapty React Native SDK in an Expo project" description: "Step-by-step guide on installing Adapty React Native SDK in an Expo project for subscription-based apps." --- :::important Esta guía cubre la instalación y configuración del SDK de Adapty para React Native **en un proyecto Expo**. Si usas **React Native puro (sin Expo)**, sigue la [guía de instalación de React Native](sdk-installation-react-native-pure) en su lugar. ::: El SDK de Adapty incluye dos módulos clave para una integración fluida en tu app de React Native: - **Core Adapty**: Este módulo es necesario para que Adapty funcione correctamente en tu app. - **AdaptyUI**: Este módulo es necesario si utilizas el [Adapty Paywall Builder](adapty-paywall-builder), una herramienta visual sin código para crear paywalls multiplataforma fácilmente. AdaptyUI se activa automáticamente junto con el módulo principal. Si necesitas un tutorial completo sobre cómo implementar compras in-app en tu app de React Native, consulta [este artículo](https://adapty.io/blog/react-native-in-app-purchases-tutorial/). :::tip ¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app de Expo? Echa un vistazo a nuestras apps de muestra: - [Ejemplo de Expo dev build](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/FocusJournalExpo) para funcionalidad completa, incluyendo compras reales y Paywall Builder - [Ejemplo de Expo Go y Web](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/ExpoGoWebMock) para pruebas en modo mock ::: Para una guía completa de implementación, también puedes ver el vídeo:
## Requisitos \{#requirements\} El SDK de Adapty para React Native requiere iOS 15.0+. Para compilar para iOS se necesita **Swift 6.0** o posterior. El [Modo Infantil](kids-mode-react-native) requiere **Swift 6.1** o posterior. :::info A partir del SDK v3.17, Adapty SDK utiliza Google Play Billing Library v8.0.0 por defecto. ::: :::info Instalar el SDK es el paso 5 de la configuración de Adapty. Para que las compras funcionen en tu app, también necesitas conectar tu app a los stores, y luego crear productos, un paywall y un placement en el Adapty Dashboard. La [guía de inicio rápido](quickstart) explica todos los pasos necesarios. ::: ## Instalar el SDK de Adapty \{#install-adapty-sdk\} :::important A partir de la v4, el SDK de Adapty para React Native ya no admite la instalación de sus dependencias nativas mediante CocoaPods. Si necesitas la v4 o posterior (para el [Flow Builder](adapty-flow-builder)), sigue los pasos de [SDK de Adapty 4.0: activar Swift Package Manager](#adapty-sdk-40-enable-swift-package-manager) que se indican más abajo. ::: [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-React-Native.svg?style=flat&logo=react)](https://github.com/adaptyteam/AdaptySDK-React-Native/releases) :::important [Expo Dev Client](https://docs.expo.dev/versions/latest/sdk/dev-client/) (una compilación de desarrollo personalizada) es necesario para usar Adapty en un proyecto Expo. Expo Go no admite módulos nativos personalizados, por lo que solo puedes usarlo con el [**modo mock**](#set-up-mock-mode-for-expo-go--expo-web) para el desarrollo de UI/lógica (sin compras reales ni renderizado de AdaptyUI/Paywall Builder). ::: 1. Instala el SDK de Adapty (esto también instala `@adapty/core` automáticamente): ```sh npx expo install react-native-adapty npx expo prebuild ``` 2. Compila tu app para desarrollo usando EAS o una compilación local: ```sh # For iOS eas build --profile development --platform ios # For Android eas build --profile development --platform android ``` ```sh # For iOS npx expo run:ios # For Android npx expo run:android ``` 3. Inicia el servidor de desarrollo: ```sh npx expo start --dev-client ``` ### SDK de Adapty 4.0: habilitar Swift Package Manager \{#adapty-sdk-40-enable-swift-package-manager\} El SDK de React Native 4.0 — que añade compatibilidad con [Flow Builder](adapty-flow-builder) — requiere **React Native 0.75 o posterior**. Instala el SDK: ```sh npx expo install react-native-adapty@^4.0.0 ``` v4 descarga los SDKs nativos de iOS (`Adapty`, `AdaptyUI`, `AdaptyPlugin`) a través de Swift Package Manager en lugar de sub-dependencias de CocoaPods ([el repositorio de specs de CocoaPods pasará a ser de solo lectura en diciembre de 2026](https://blog.cocoapods.org/CocoaPods-Specs-Repo/)). SPM requiere frameworks dinámicos, que en Expo se habilitan con el plugin [`expo-build-properties`](https://docs.expo.dev/versions/latest/sdk/build-properties/). Añádelo a `app.json` (o `app.config.js`): ```json showLineNumbers title="app.json" { "expo": { "plugins": [ [ "expo-build-properties", { "ios": { "useFrameworks": "dynamic" } } ] ] } } ``` Luego instala el plugin y regenera el proyecto nativo: ```sh npx expo install expo-build-properties npx expo prebuild --clean ``` Consulta [Migrar el SDK de Adapty para React Native a v4](migration-to-react-native-sdk-v4) para ver la migración completa. ## Activa el módulo Adapty del SDK \{#activate-adapty-module-of-adapty-sdk\} Para obtener tu **Public SDK Key**: 1. Ve al Adapty Dashboard y navega a [**App settings → General**](https://app.adapty.io/settings/general). 2. En la sección **Api keys**, copia la **Public SDK Key** (NO la Secret Key). 3. Reemplaza `"YOUR_PUBLIC_SDK_KEY"` en el código. O bien, obtenla de forma programática usando el [Adapty CLI](developer-cli): ``` npm install -g adapty adapty auth login adapty apps list ``` O directamente: ``` npx adapty auth login adapty apps list ``` - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API del lado del servidor](getting-started-with-server-side-api). - Las **SDK keys** son únicas para cada app, así que si tienes varias apps asegúrate de elegir la correcta. Copia el siguiente código en `App.tsx` para activar Adapty: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY'); ``` :::important Espera a que `activate` se resuelva antes de llamar a cualquier otro método del SDK de Adapty. Consulta [Orden de llamadas en el SDK de React Native](react-native-sdk-call-order) para ver la secuencia completa. ::: Ahora configura los paywalls en tu app: - Si usas [Adapty Paywall Builder](adapty-paywall-builder), sigue la [guía de inicio rápido con Paywall Builder](react-native-quickstart-paywalls). - Si construyes tu propia interfaz de paywall, consulta la [guía de inicio rápido para paywalls personalizados](react-native-quickstart-manual). :::tip Para evitar errores de activación en el entorno de desarrollo, consulta los [consejos](#development-environment-tips). ::: ## Activar el módulo AdaptyUI del SDK de Adapty \{#activate-adaptyui-module-of-adapty-sdk\} Si planeas usar el [Paywall Builder](adapty-paywall-builder), necesitas el módulo AdaptyUI. Se activa automáticamente al activar el módulo principal; no necesitas hacer nada más. ## Configuración opcional \{#optional-setup\} ### Registro #### Configurar el sistema de registro \{#set-up-the-logging-system\} Adapty registra errores y otra información importante para ayudarte a entender qué está pasando. Hay los siguientes niveles disponibles: | Level | Description | | ---------- | ------------------------------------------------------------ | | `error` | Solo se registrarán errores | | `warn` | Se registrarán errores y mensajes del SDK que no causan errores críticos, pero que conviene tener en cuenta | | `info` | Se registrarán errores, advertencias y varios mensajes informativos | | `verbose` | Se registrará cualquier información adicional que pueda ser útil durante la depuración, como llamadas a funciones, consultas a la API, etc. | Puedes establecer el nivel de log en tu app antes o durante la configuración de Adapty: ```typescript showLineNumbers title="App.tsx" // Set log level before activation // 'verbose' is recommended for development and the first production release adapty.setLogLevel('verbose'); // Or set it during configuration adapty.activate('YOUR_PUBLIC_SDK_KEY', { logLevel: 'verbose', }); ``` ### Políticas de datos \{#data-policies\} Adapty no almacena datos personales de tus usuarios a menos que los envíes explícitamente, pero puedes implementar políticas de seguridad de datos adicionales para cumplir con las directrices del store o del país. #### Deshabilitar la recopilación y el intercambio de direcciones IP \{#disable-ip-address-collection-and-sharing\} Al activar el módulo de Adapty, establece `ipAddressCollectionDisabled` en `true` para deshabilitar la recopilación y el intercambio de la dirección IP del usuario. El valor predeterminado es `false`. Usa este parámetro para mejorar la privacidad del usuario, cumplir con normativas regionales de protección de datos (como GDPR o CCPA) o reducir la recopilación innecesaria de datos cuando las funciones basadas en IP no son necesarias para tu aplicación. ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { ipAddressCollectionDisabled: true, }); ``` #### Desactivar la recopilación y el uso compartido del ID publicitario \{#disable-advertising-id-collection-and-sharing\} Al activar el módulo de Adapty, establece `ios.idfaCollectionDisabled` (iOS) o `android.adIdCollectionDisabled` (Android) en `true` para desactivar la recopilación de identificadores publicitarios. El valor predeterminado es `false`. Usa este parámetro para cumplir con las políticas de App Store/Play Store, evitar que aparezca el prompt de App Tracking Transparency, o si tu app no necesita atribución publicitaria ni análisis basados en IDs publicitarios. ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { ios: { idfaCollectionDisabled: true, }, android: { adIdCollectionDisabled: true, }, }); ``` #### Configurar la caché de medios para AdaptyUI \{#set-up-media-cache-configuration-for-adaptyui\} Por defecto, AdaptyUI almacena en caché los medios (como imágenes y vídeos) para mejorar el rendimiento y reducir el consumo de red. Puedes personalizar la configuración de la caché proporcionando una configuración personalizada. Usa `mediaCache` para cambiar la configuración de caché predeterminada: ```typescript adapty.activate('YOUR_PUBLIC_SDK_KEY', { mediaCache: { memoryStorageTotalCostLimit: 200 * 1024 * 1024, // Optional: memory cache size in bytes memoryStorageCountLimit: 2147483647, // Optional: max number of items in memory diskStorageSizeLimit: 200 * 1024 * 1024, // Optional: disk cache size in bytes }, }); ``` | Parámetro | Obligatorio | Descripción | |-----------|-------------|-------------| | memoryStorageTotalCostLimit | opcional | Tamaño total de la caché en memoria en bytes. El valor predeterminado depende de la plataforma. | | memoryStorageCountLimit | opcional | Límite de elementos en el almacenamiento en memoria. El valor predeterminado depende de la plataforma. | | diskStorageSizeLimit | opcional | Límite del tamaño del archivo en disco en bytes. El valor predeterminado depende de la plataforma. | ### Habilitar niveles de acceso locales (Android) \{#enable-local-access-levels-android\} Por defecto, los [niveles de acceso locales](local-access-levels) están habilitados en iOS y deshabilitados en Android. Para habilitarlos también en Android, establece `localAccessLevelAllowed` en `true`: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { android: { localAccessLevelAllowed: true, }, }); ``` ### Borrar datos al restaurar desde copia de seguridad \{#clear-data-on-backup-restore\} Cuando `clearDataOnBackup` se establece en `true`, el SDK detecta cuándo la app se restaura desde una copia de seguridad de iCloud y elimina todos los datos del SDK almacenados localmente, incluida la información de perfil en caché, los detalles de productos y los paywalls. El SDK se inicializa entonces con un estado limpio. El valor predeterminado es `false`. :::note Solo se elimina la caché local del SDK. El historial de transacciones con Apple y los datos de usuario en los servidores de Adapty no se modifican. ::: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { ios: { clearDataOnBackup: true }, }); ``` ## Consejos para el entorno de desarrollo \{#development-environment-tips\} #### Configura el modo mock para Expo Go / Expo Web \{#set-up-mock-mode-for-expo-go--expo-web\} Los entornos de Expo Go y Expo Web no tienen acceso a los módulos nativos de Adapty. Para evitar errores en tiempo de ejecución sin dejar de poder construir y probar la UI y la lógica de paywall de tu app, Adapty ofrece el **modo mock**. ::::important El modo mock **no** es una herramienta para probar compras reales: - **No abre** los flujos de compra de App Store / Google Play y **no crea** transacciones reales. - **No renderiza** paywalls/onboardings creados con **Adapty Paywall Builder (AdaptyUI)**. - Los módulos nativos de Adapty se **omiten por completo**: incluso si faltan archivos del SDK nativo en la compilación de Xcode/Android o la clave de API no es válida, no se producirán errores. Para probar compras reales y paywalls del Paywall Builder, usa un Expo Dev Client / build de producción, donde el modo mock se desactiva automáticamente. :::: **De forma predeterminada**, el SDK detecta automáticamente los entornos Expo Go y web y activa el modo simulado. No necesitas configurar nada a menos que quieras personalizar los datos simulados. Cuando el modo simulado está activo: - Todos los métodos de Adapty devuelven datos simulados sin realizar solicitudes de red a los servidores de Adapty. - Por defecto, el perfil simulado inicial no tiene suscripciones activas. - Por defecto, `makePurchase(...)` simula una compra exitosa y otorga acceso premium. Puedes personalizar los datos de prueba usando `mockConfig` durante la activación. Consulta el formato de configuración y los parámetros disponibles [aquí](https://react-native.adapty.io/interfaces/adaptymockconfig). ```typescript showLineNumbers title="App.tsx" try { await adapty.activate('YOUR_PUBLIC_SDK_KEY', { mockConfig: { // Customize the initial mock profile (optional) }, }); } catch (error) { console.error('Failed to activate Adapty SDK:', error); } ``` Si necesitas llamar a métodos del SDK antes de la activación (como `isActivated()` o `setLogLevel()`), usa `enableMock()` antes de `activate()`. Si el puente ya está inicializado, este método no hace nada. ```typescript showLineNumbers title="App.tsx" adapty.enableMock(); // Optional: pass mockConfig to customize mock data // Now you can call methods before activation await adapty.activate('YOUR_PUBLIC_SDK_KEY'); ``` #### Retrasar la activación del SDK con fines de desarrollo \{#delay-sdk-activation-for-development-purposes\} Adapty pre-carga todos los datos de usuario necesarios al activar el SDK, lo que permite un acceso más rápido a datos actualizados. Sin embargo, esto puede ser un problema en el simulador de iOS, que con frecuencia solicita autenticación durante el desarrollo. Aunque Adapty no puede controlar el flujo de autenticación de StoreKit, sí puede aplazar las solicitudes que el SDK realiza para obtener datos de usuario actualizados. Al activar la propiedad `__debugDeferActivation`, la llamada de activación queda en espera hasta que realizas la siguiente llamada al SDK de Adapty. Esto evita solicitudes de autenticación innecesarias cuando no son necesarias. Es importante tener en cuenta que **esta función está pensada únicamente para uso en desarrollo**, ya que no cubre todos los escenarios de usuario posibles. En producción, la activación no debe retrasarse, ya que los dispositivos reales suelen recordar los datos de autenticación y no solicitan las credenciales repetidamente. Este es el enfoque recomendado para su uso: ```typescript showLineNumbers title="Typescript" try { adapty.activate('PUBLIC_SDK_KEY', { __debugDeferActivation: isSimulator(), // 'isSimulator' from any 3rd party library }); } catch (error) { console.error('Failed to activate Adapty SDK:', error); // Handle the error appropriately for your app } ``` #### Solucionar errores de activación del SDK con Fast Refresh de React Native \{#troubleshoot-sdk-activation-errors-on-react-natives-fast-refresh\} Al desarrollar con el SDK de Adapty en React Native, es posible que encuentres el error: `Adapty can only be activated once. Ensure that the SDK activation call is not made more than once.` Esto ocurre porque la función de actualización rápida (fast refresh) de React Native activa múltiples llamadas de activación durante el desarrollo. Para evitarlo, usa la opción `__ignoreActivationOnFastRefresh` con el valor `__DEV__` (el indicador del modo de desarrollo de React Native). ```typescript showLineNumbers title="Typescript" try { adapty.activate('PUBLIC_SDK_KEY', { __ignoreActivationOnFastRefresh: __DEV__, }); } catch (error) { console.error('Failed to activate Adapty SDK:', error); // Handle the error appropriately for your app } ``` ## Solución de problemas \{#troubleshooting\} #### Error de versión mínima de iOS \{#minimum-ios-version-error\} Al compilar para iOS, puede aparecer un error relacionado con la **versión mínima de iOS** o el deployment target. Adapty requiere **iOS 15.0+**. Como Expo genera el proyecto de iOS (incluido el `Podfile`) durante `expo prebuild`, **no debes editar el `Podfile` directamente**. En su lugar, configura el deployment target mediante el plugin de configuración `expo-build-properties`. 1. Instala el plugin: ```sh npx expo install expo-build-properties ``` 2. Actualiza tu configuración de Expo (`app.json` o `app.config.js`) para establecer el deployment target de iOS: ``` { "expo": { // ...other Expo config... "plugins": [ [ "expo-build-properties", { "ios": { // Adapty requires iOS 15.0+. "deploymentTarget": "15.0" } } ], ] } } ``` 3. Regenera el proyecto nativo de iOS y reconstrúyelo: ``` npx expo prebuild --clean npx expo run:ios # or `eas build -p ios` on your CI ``` #### Conflicto del manifiesto de Android Auto Backup \{#android-auto-backup-manifest-conflict\} Cuando usas Expo con varios SDKs que configuran Android Auto Backup (como Adapty, AppsFlyer o expo-secure-store), puede aparecer un conflicto en el fusionador de manifiestos. Un error típico tiene este aspecto: `Manifest merger failed : Attribute application@fullBackupContent value=(@xml/secure_store_backup_rules) from AndroidManifest.xml:24:248-306 is also present at [io.adapty:android-sdk:3.12.0] AndroidManifest.xml:9:18-70 value=(@xml/adapty_backup_rules).` Para resolver este conflicto, debes permitir que el plugin de Adapty gestione la configuración de copia de seguridad de Android. Si tu proyecto también usa `expo-secure-store`, desactiva su propia configuración de copia de seguridad para evitar conflictos. Así es como debes configurar tu `app.json`: ```json title="app.json" { "expo": { "plugins": [ ["react-native-adapty", { "replaceAndroidBackupConfig": true }], ["expo-secure-store", { "configureAndroidBackup": false }] ] } } ``` La opción `replaceAndroidBackupConfig` está en `false` por defecto. Cuando se activa, permite que el plugin de Adapty controle las reglas de copia de seguridad de Android. Incluye `"configureAndroidBackup": false` si usas `expo-secure-store` para evitar advertencias, ya que la configuración de copia de seguridad de SecureStore pasará a gestionarla Adapty. :::important Esta configuración solo respeta los requisitos de copia de seguridad de Adapty, AppsFlyer y expo-secure-store. Si otras bibliotecas de tu proyecto definen reglas de copia de seguridad personalizadas, tendrás que configurarlas manualmente. ::: --- # File: sdk-installation-react-native-pure --- --- title: "Install & configure Adapty SDK in a pure React Native project" description: "Step-by-step guide on installing Adapty SDK on React Native for subscription-based apps." --- :::important Esta guía aplica únicamente a **proyectos de React Native puro (sin Expo)**. Si usas **Expo**, sigue la [guía de instalación para Expo](sdk-installation-react-native-expo) en su lugar. ::: El SDK de Adapty incluye dos módulos clave para una integración fluida en tu app de React Native: - **Core Adapty**: Este módulo es necesario para que Adapty funcione correctamente en tu app. - **AdaptyUI**: Este módulo es necesario si usas el [Adapty Paywall Builder](adapty-paywall-builder), una herramienta sin código y fácil de usar para crear paywalls multiplataforma. AdaptyUI se activa automáticamente junto con el módulo principal. :::tip ¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funciones básicas. ::: ## Requisitos \{#requirements\} El SDK de Adapty para React Native requiere iOS 15.0+. Para compilar en iOS se necesita **Swift 6.0** o posterior. El [Modo Infantil](kids-mode-react-native) requiere **Swift 6.1** o posterior. :::info A partir del SDK v3.17, el SDK de Adapty usa Google Play Billing Library v8.0.0 por defecto. ::: :::info Instalar el SDK es el paso 5 de la configuración de Adapty. Para que las compras funcionen en tu app, también necesitas conectar tu app a los stores, y luego crear productos, un paywall y un placement en el Adapty Dashboard. La [guía de inicio rápido](quickstart) explica todos los pasos necesarios. ::: ## Instalar el SDK de Adapty \{#install-adapty-sdk\} :::important A partir de la v4, el SDK de Adapty para React Native ya no admite la instalación de sus dependencias nativas mediante CocoaPods. Si necesitas la v4 o una versión posterior (para el [Flow Builder](adapty-flow-builder)), sigue los pasos de [Adapty SDK 4.0: habilitar Swift Package Manager](#adapty-sdk-40-enable-swift-package-manager) a continuación. ::: [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-React-Native.svg?style=flat&logo=react)](https://github.com/adaptyteam/AdaptySDK-React-Native/releases) 1. Instala el SDK de Adapty (esto también instala `@adapty/core` automáticamente): ```sh showLineNumbers title="Shell" # using npm npm install react-native-adapty # or using yarn yarn add react-native-adapty ``` 2. Para iOS, instala los pods: ```sh showLineNumbers title="Shell" cd ios && pod install ```
Para Android, si tu versión de React Native es anterior a 0.73.0 (haz clic para expandir) Actualiza el archivo `/android/build.gradle`. Asegúrate de que existe la dependencia `kotlin-gradle-plugin:1.8.0` o una versión más reciente: ```groovy showLineNumbers title="/android/build.gradle" ... buildscript { ... dependencies { ... classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0" } } ... ```
### Adapty SDK 4.0: habilitar Swift Package Manager \{#adapty-sdk-40-enable-swift-package-manager\} React Native SDK 4.0 — que añade soporte para [Flow Builder](adapty-flow-builder) — requiere **React Native 0.75 o posterior**. Instala el SDK: ```sh showLineNumbers title="Shell" npm install react-native-adapty@^4.0.0 # or using yarn yarn add react-native-adapty@^4.0.0 ``` v4 incorpora los SDK nativos de iOS (`Adapty`, `AdaptyUI`, `AdaptyPlugin`) a través de Swift Package Manager en lugar de las sub-dependencias de CocoaPods ([el repositorio de specs de CocoaPods pasará a ser de solo lectura en diciembre de 2026](https://blog.cocoapods.org/CocoaPods-Specs-Repo/)). SPM requiere frameworks dinámicos — añade lo siguiente al target de tu `ios/Podfile` y luego reinstala los pods: ```ruby showLineNumbers title="ios/Podfile" use_frameworks! :linkage => :dynamic ``` ```sh showLineNumbers title="Shell" cd ios && pod install --repo-update ``` Si anteriamente tenías `Adapty`, `AdaptyUI` o `AdaptyPlugin` como sub-dependencias de CocoaPods, elimina primero cualquier línea `pod 'Adapty'`, `pod 'AdaptyUI'` o `pod 'AdaptyPlugin'` de tu `Podfile`. :::warning Cambiar del enlace estático predeterminado a frameworks dinámicos puede entrar en conflicto con bibliotecas que aún no soportan cabeceras modulares, y es incompatible con Flipper. Consulta [Migrar el SDK de Adapty React Native a v4](migration-to-react-native-sdk-v4) para más detalles. ::: ## Activa el módulo Adapty del SDK \{#activate-adapty-module-of-adapty-sdk\} Para obtener tu **Public SDK Key**: 1. Ve al Adapty Dashboard y navega a [**App settings → General**](https://app.adapty.io/settings/general). 2. En la sección **Api keys**, copia la **Public SDK Key** (NO la Secret Key). 3. Reemplaza `"YOUR_PUBLIC_SDK_KEY"` en el código. O bien, obtenla de forma programática usando el [Adapty CLI](developer-cli): ``` npm install -g adapty adapty auth login adapty apps list ``` O directamente: ``` npx adapty auth login adapty apps list ``` - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API del lado del servidor](getting-started-with-server-side-api). - Las **SDK keys** son únicas para cada app, así que si tienes varias apps asegúrate de elegir la correcta. Copia el siguiente código en `App.tsx` para activar Adapty: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY'); ``` :::important Espera a que `activate` se resuelva antes de llamar a cualquier otro método del SDK de Adapty. Consulta [Orden de llamadas en el SDK de React Native](react-native-sdk-call-order) para ver la secuencia completa. ::: Ahora configura los paywalls en tu app: - Si usas [Adapty Paywall Builder](adapty-paywall-builder), sigue el [inicio rápido con Paywall Builder](react-native-quickstart-paywalls). - Si construyes tu propia UI de paywall, consulta el [inicio rápido para paywalls personalizados](react-native-quickstart-manual). :::tip Para evitar errores de activación en el entorno de desarrollo, consulta los [consejos](#development-environment-tips). ::: ## Activar el módulo AdaptyUI del SDK de Adapty \{#activate-adaptui-module-of-adapty-sdk\} Si planeas usar [Paywall Builder](adapty-paywall-builder), necesitas el módulo AdaptyUI. Se activa automáticamente al activar el módulo principal; no es necesario hacer nada más. ## Configuración opcional \{#optional-setup\} ### Registro #### Configura el sistema de registro \{#set-up-the-logging-system\} Adapty registra errores y otra información importante para ayudarte a entender qué está ocurriendo. Hay los siguientes niveles disponibles: | Level | Description | | ---------- | ------------------------------------------------------------ | | `error` | Solo se registrarán los errores | | `warn` | Se registrarán los errores y los mensajes del SDK que no causan errores críticos pero que merecen atención | | `info` | Se registrarán los errores, las advertencias y varios mensajes informativos | | `verbose` | Se registrará cualquier información adicional que pueda ser útil durante la depuración, como llamadas a funciones, consultas a la API, etc. | Puedes establecer el nivel de registro en tu app antes o durante la configuración de Adapty: ```typescript showLineNumbers title="App.tsx" // Set log level before activation // 'verbose' is recommended for development and the first production release adapty.setLogLevel('verbose'); // Or set it during configuration adapty.activate('YOUR_PUBLIC_SDK_KEY', { logLevel: 'verbose', }); ``` ### Políticas de datos \{#data-policies\} Adapty no almacena datos personales de tus usuarios a menos que los envíes explícitamente, pero puedes implementar políticas de seguridad de datos adicionales para cumplir con las directrices de la store o del país. #### Desactivar la recopilación y el uso compartido de direcciones IP \{#disable-ip-address-collection-and-sharing\} Al activar el módulo de Adapty, establece `ipAddressCollectionDisabled` en `true` para desactivar la recopilación y el uso compartido de la dirección IP del usuario. El valor predeterminado es `false`. Usa este parámetro para mejorar la privacidad del usuario, cumplir con normativas regionales de protección de datos (como GDPR o CCPA), o reducir la recopilación de datos innecesaria cuando las funciones basadas en IP no son necesarias para tu app. ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { ipAddressCollectionDisabled: true, }); ``` #### Desactivar la recopilación y el uso compartido del ID publicitario \{#disable-advertising-id-collection-and-sharing\} Al activar el módulo de Adapty, establece `ios.idfaCollectionDisabled` (iOS) o `android.adIdCollectionDisabled` (Android) en `true` para deshabilitar la recopilación de identificadores publicitarios. El valor predeterminado es `false`. Usa este parámetro para cumplir con las políticas de App Store/Play Store, evitar que aparezca el aviso de App Tracking Transparency, o si tu app no necesita atribución publicitaria ni analíticas basadas en IDs de publicidad. ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { ios: { idfaCollectionDisabled: true, }, android: { adIdCollectionDisabled: true, }, }); ``` #### Configurar la caché de medios para AdaptyUI \{#set-up-media-cache-configuration-for-adaptyui\} Por defecto, AdaptyUI almacena en caché los medios (como imágenes y vídeos) para mejorar el rendimiento y reducir el uso de red. Puedes personalizar la configuración de caché proporcionando una configuración personalizada. Usa `mediaCache` para sobrescribir la configuración de caché predeterminada: ```typescript adapty.activate('YOUR_PUBLIC_SDK_KEY', { mediaCache: { memoryStorageTotalCostLimit: 200 * 1024 * 1024, // Optional: memory cache size in bytes memoryStorageCountLimit: 2147483647, // Optional: max number of items in memory diskStorageSizeLimit: 200 * 1024 * 1024, // Optional: disk cache size in bytes }, }); ``` | Parámetro | Obligatorio | Descripción | |-----------|-------------|-------------| | memoryStorageTotalCostLimit | opcional | Tamaño total de la caché en memoria en bytes. El valor por defecto depende de la plataforma. | | memoryStorageCountLimit | opcional | Límite de elementos en el almacenamiento en memoria. El valor por defecto depende de la plataforma. | | diskStorageSizeLimit | opcional | Límite del tamaño de archivo en disco en bytes. El valor por defecto depende de la plataforma. | ### Habilitar niveles de acceso locales (Android) \{#enable-local-access-levels-android\} Por defecto, los [niveles de acceso locales](local-access-levels) están habilitados en iOS y deshabilitados en Android. Para habilitarlos también en Android, establece `localAccessLevelAllowed` en `true`: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { android: { localAccessLevelAllowed: true, }, }); ``` ### Borrar datos al restaurar desde copia de seguridad \{#clear-data-on-backup-restore\} Cuando `clearDataOnBackup` se establece en `true`, el SDK detecta cuándo la app se restaura desde una copia de seguridad de iCloud y elimina todos los datos del SDK almacenados localmente, incluida la información de perfil en caché, los detalles de productos y los paywalls. A continuación, el SDK se inicializa con un estado limpio. El valor predeterminado es `false`. :::note Solo se elimina la caché local del SDK. El historial de transacciones con Apple y los datos de usuario en los servidores de Adapty permanecen sin cambios. ::: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { ios: { clearDataOnBackup: true }, }); ``` ## Consejos para el entorno de desarrollo \{#development-environment-tips\} #### Retrasar la activación del SDK con fines de desarrollo \{#delay-sdk-activation-for-development-purposes\} Adapty obtiene de antemano todos los datos de usuario necesarios al activar el SDK, lo que permite un acceso más rápido a datos actualizados. Sin embargo, esto puede ser un problema en el simulador de iOS, que durante el desarrollo suele pedir autenticación con frecuencia. Aunque Adapty no puede controlar el flujo de autenticación de StoreKit, sí puede aplazar las solicitudes que realiza el SDK para obtener datos de usuario actualizados. Al activar la propiedad `__debugDeferActivation`, la llamada de activación se retiene hasta que realizas la siguiente llamada al SDK de Adapty. Esto evita solicitudes innecesarias de datos de autenticación cuando no son necesarias. Es importante tener en cuenta que **esta función está pensada solo para uso en desarrollo**, ya que no cubre todos los escenarios de usuario posibles. En producción, la activación no debe retrasarse, ya que los dispositivos reales suelen recordar los datos de autenticación y no solicitan las credenciales repetidamente. Este es el enfoque recomendado para su uso: ```typescript showLineNumbers title="Typescript" try { adapty.activate('PUBLIC_SDK_KEY', { __debugDeferActivation: isSimulator(), // 'isSimulator' from any 3rd party library }); } catch (error) { console.error('Failed to activate Adapty SDK:', error); // Handle the error appropriately for your app } ``` #### Solucionar errores de activación del SDK con Fast Refresh de React Native \{#troubleshoot-sdk-activation-errors-on-react-natives-fast-refresh\} Al desarrollar con el SDK de Adapty en React Native, es posible que encuentres el error: `Adapty can only be activated once. Ensure that the SDK activation call is not made more than once.` Esto ocurre porque la función de fast refresh de React Native activa múltiples llamadas de activación durante el desarrollo. Para evitarlo, usa la opción `__ignoreActivationOnFastRefresh` con el valor `__DEV__` (el indicador del modo de desarrollo de React Native). ```typescript showLineNumbers title="Typescript" try { adapty.activate('PUBLIC_SDK_KEY', { __ignoreActivationOnFastRefresh: __DEV__, }); } catch (error) { console.error('Failed to activate Adapty SDK:', error); // Handle the error appropriately for your app } ``` #### Configura el modo mock para pruebas locales \{#set-up-mock-mode-for-local-testing\} Para el desarrollo local y las pruebas, puedes activar el modo mock para evitar necesitar cuentas de sandbox de App Store/Google Play y agilizar las iteraciones. El modo mock omite completamente los módulos nativos de Adapty y devuelve datos simulados. :::important El modo mock **no** es una herramienta para probar compras reales: - **No abre** los flujos de compra de App Store / Google Play y **no crea** transacciones reales. - **No renderiza** paywalls/onboardings creados con **Adapty Paywall Builder (AdaptyUI)**. - Los módulos nativos de Adapty se **omiten por completo**—incluso la ausencia de archivos del SDK nativo en la compilación de Xcode/Android o una API key inválida no generarán errores. - No se envían datos a los servidores de Adapty. Para probar compras reales y paywalls del Paywall Builder, desactiva el modo mock y usa cuentas sandbox. ::: Para activar el modo mock, establece `enableMock` en `true`: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { enableMock: true, }); ``` Cuando el modo mock está activo: - Todos los métodos de Adapty devuelven datos de prueba sin realizar solicitudes de red a los servidores de Adapty. - Por defecto, el perfil mock inicial no tiene suscripciones activas. - Por defecto, `makePurchase(...)` simula una compra exitosa y concede acceso premium. Puedes personalizar los datos mock usando `mockConfig` durante la activación. Consulta el formato de configuración y los parámetros disponibles [aquí](https://react-native.adapty.io/interfaces/adaptymockconfig). ```typescript showLineNumbers title="App.tsx" try { await adapty.activate('YOUR_PUBLIC_SDK_KEY', { mockConfig: { // Customize the initial mock profile (optional) }, }); } catch (error) { console.error('Failed to activate Adapty SDK:', error); } ``` Si necesitas llamar a métodos del SDK antes de la activación (como `isActivated()` o `setLogLevel()`), usa `enableMock()` antes de `activate()`. Si el puente ya está inicializado, este método no hace nada. ```typescript showLineNumbers title="App.tsx" adapty.enableMock(); // Optional: pass mockConfig to customize mock data // Now you can call methods before activation await adapty.activate('YOUR_PUBLIC_SDK_KEY'); ``` ## Solución de problemas \{#troubleshooting\} #### Error de versión mínima de iOS \{#minimum-ios-version-error\} Si obtienes un error de versión mínima de iOS, actualiza tu Podfile: ```diff -platform :ios, min_ios_version_supported +platform :ios, '15.0' ``` #### Conflicto en el manifiesto de Android Auto Backup \{#android-auto-backup-manifest-conflict\} Algunos SDKs (incluido Adapty) incluyen su propia configuración de Android Auto Backup. Si utilizas varios SDKs que definen reglas de copia de seguridad, el fusionador de manifiestos de Android puede fallar con un error relacionado con `android:fullBackupContent`, `android:dataExtractionRules` o `android:allowBackup`. Síntomas típicos del error: `Manifest merger failed: Attribute application@dataExtractionRules value=(@xml/your_data_extraction_rules) is also present at [com.other.sdk:library:1.0.0] value=(@xml/other_sdk_data_extraction_rules)` :::note Estos cambios deben realizarse en el directorio de la plataforma Android (normalmente en la carpeta `android/` de tu proyecto). ::: Para resolverlo, necesitas: - Indicar al fusionador de manifiestos que use los valores de tu app para los atributos relacionados con la copia de seguridad. - Crear archivos de reglas de copia de seguridad que combinen las reglas de Adapty con las de otros SDKs. #### 1. Añade el namespace `tools` a tu manifiesto \{#1-add-the-tools-namespace-to-your-manifest\} En tu archivo `AndroidManifest.xml`, asegúrate de que la etiqueta raíz `` incluya tools: ```xml ... ``` #### 2. Sobreescribe los atributos de copia de seguridad en `` \{#2-override-backup-attributes-in-application\} En el mismo archivo `AndroidManifest.xml`, actualiza la etiqueta `` para que tu app proporcione los valores definitivos e indique al fusionador de manifiestos que reemplace los valores de las librerías: ```xml ... ``` Si algún SDK también define `android:allowBackup`, inclúyelo en `tools:replace`: ```xml tools:replace="android:allowBackup,android:fullBackupContent,android:dataExtractionRules" ``` #### 3. Crea los archivos de reglas de copia de seguridad combinadas \{#3-create-merged-backup-rules-files\} Crea archivos XML en el directorio `res/xml/` de tu proyecto Android que combinen las reglas de Adapty con las de otros SDKs. Android utiliza distintos formatos de reglas de copia de seguridad según la versión del sistema operativo, por lo que crear ambos archivos garantiza la compatibilidad con todas las versiones de Android que admite tu app. :::note Los ejemplos a continuación usan AppsFlyer como SDK de terceros de muestra. Reemplaza o añade reglas para cualquier otro SDK que uses en tu app. ::: **Para Android 12 y superior** (usa el nuevo formato de reglas de extracción de datos): ```xml title="sample_data_extraction_rules.xml" ``` **Para Android 11 e inferior** (usa el formato legado de contenido de copia de seguridad completa): ```xml title="sample_backup_rules.xml" #### Las compras fallan al volver desde otra app en Android \{#purchases-fail-after-returning-from-another-app-in-android\} Si la Activity que inicia el flow de compra usa un `launchMode` distinto al predeterminado, Android puede recrearla o reutilizarla de forma incorrecta cuando el usuario regresa desde Google Play, una app bancaria o un navegador. Esto puede provocar que el resultado de la compra se pierda o se trate como cancelado. Para garantizar que las compras funcionen correctamente, utiliza solo los modos de inicio `standard` o `singleTop` para la Activity que inicia el flujo de compra, y evita cualquier otro modo. En tu `AndroidManifest.xml`, asegúrate de que la Activity que inicia el flujo de compra esté configurada como `standard` o `singleTop`: ```xml ``` #### Errores de compilación de Swift 6 causados por la anulación de SWIFT_VERSION en el Podfile \{#swift-6-build-errors-caused-by-podfile-swift-version-override\} Al compilar tu app de React Native para iOS, puede que veas errores de compilación de Swift 6 en los targets de los pods de Adapty. Los síntomas típicos incluyen discrepancias con `@Sendable` en `AdaptyUIBuilderLogic`, falta de conformidad con `Sendable` en tipos de Adapty, o errores de aislamiento de actores. Los pods de Adapty declaran `s.swift_version = '6.0'` y requieren Swift 6 para compilar. Tu propio código puede quedarse en Swift 5 — solo los targets de los pods de Adapty (`Adapty`, `AdaptyUI`, `AdaptyUIBuilder`, `AdaptyLogger`, `AdaptyPlugin`) necesitan compilarse con Swift 6. La causa más común es un hook `post_install` en `ios/Podfile` que sobreescribe `SWIFT_VERSION` para todos los targets del pod: ```ruby showLineNumbers title="ios/Podfile" post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = '5.9' end end end ``` **Solución**: Excluye los targets del pod de Adapty del override: ```ruby showLineNumbers title="ios/Podfile" post_install do |installer| installer.pods_project.targets.each do |target| next if %w[Adapty AdaptyUI AdaptyUIBuilder AdaptyLogger AdaptyPlugin].include?(target.name) target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = '5.9' end end end ``` Luego ejecuta `pod install` desde el directorio `ios/` y vuelve a compilar. Para verificarlo, abre `ios/Pods/Pods.xcodeproj`, selecciona el target del pod `Adapty` → **Build Settings** → **Swift Language Version**. Debería mostrar **Swift 6**. --- # File: react-native-quickstart-paywalls --- --- title: "Habilitar compras con Flow Builder en React Native SDK" description: "Guía de inicio rápido para habilitar compras in-app con Adapty Flow Builder." --- Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – todo lo que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - [**Flows**](adapty-flow-builder) – secuencias de pantallas que presentan productos a los usuarios, creadas en el Flow Builder sin código. El SDK las recupera mediante `getFlow`. Si prefieres construir la interfaz en tu propio código, usa un paywall en su lugar — consulta [Implementar paywalls manualmente](react-native-quickstart-manual). - [**Placements**](placements) – dónde y cuándo mostrar los flows en tu app (como `main`, `onboarding`, `settings`). Asocias los flows a los placements en el dashboard y luego los solicitas por ID de placement en tu código. Esto facilita ejecutar pruebas A/B y mostrar flows distintos a diferentes usuarios. Adapty te ofrece tres formas de habilitar compras en tu aplicación. Elige la que mejor se adapte a tus necesidades: | Implementación | Complejidad | Cuándo usar | |---|---|---| | Adapty Flow Builder | ✅ Fácil | [Creas un flow completo y listo para compras en el builder sin código](quickstart-paywalls). Adapty lo renderiza automáticamente y gestiona todo el flujo de compra, la validación de recibos y la gestión de suscripciones en segundo plano. | | Paywalls creados manualmente | 🟡 Media | Implementas la UI de tu paywall en el código de tu app, pero obtienes el objeto flow de Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](react-native-quickstart-manual). | | Modo observador | 🔴 Difícil | Ya tienes tu propia infraestructura de gestión de compras y quieres seguir usándola. Ten en cuenta que el modo observador tiene sus limitaciones en Adapty. Consulta el [artículo](observer-vs-full-mode). | :::important **Los pasos a continuación muestran cómo implementar un flow creado en el Adapty Flow Builder.** Si prefieres construir la UI del paywall tú mismo, consulta [Implementar paywalls manualmente](react-native-quickstart-manual). ::: Para mostrar un flow creado en el Adapty Flow Builder, en el código de tu app solo necesitas: 1. **Obtener el flow**: Obténlo desde Adapty. 2. **Mostrarlo y Adapty gestionará las compras por ti**: Muestra la vista en tu app. 3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con la respuesta de tu app. Por ejemplo, abrir enlaces o cerrar el flow cuando los usuarios pulsen botones. ## Antes de empezar \{#before-you-start\} Antes de empezar, completa estos pasos: 1. Conecta tu app al [App Store](initial_ios) y/o [Google Play](initial-android) en el Adapty Dashboard. 2. [Crea tus productos](create-product) en Adapty. 3. [Crea un paywall y añade productos](create-paywall). 4. [Crea un placement y añade tu paywall](create-placement). 5. [Instala y activa el SDK de Adapty](sdk-installation-reactnative) en el código de tu app. :::tip La forma más rápida de completar estos pasos es seguir la [guía de inicio rápido](quickstart) o crear paywalls y placements usando el [CLI para desarrolladores](developer-cli-quickstart). ::: ## 1. Obtén el flow \{#1-get-the-flow\} Tus flows están asociados con placements configurados en el dashboard. Los placements te permiten ejecutar distintos flows para diferentes audiencias o realizar [pruebas A/B](ab-tests). Para obtener un flow creado en el Adapty Flow Builder, obtén el objeto `flow` mediante el ID del [placement](placements) usando el método `getFlow`. El flow contiene los elementos de UI y el estilo necesarios para mostrarlo. ```typescript showLineNumbers title="React Native" try { const flow = await adapty.getFlow('YOUR_PLACEMENT_ID'); // the requested flow } catch (error) { // handle the error } ``` ## 2. Mostrar el flow \{#display-the-flow\} Ahora que tienes el flow, basta con añadir unas pocas líneas para mostrarlo. Para insertar un flow dentro de tu árbol de componentes existente, usa el componente `AdaptyFlowView` directamente en la jerarquía de componentes de React Native: ```typescript showLineNumbers title="React Native (TSX)" function MyFlow({ flow }) { const onPurchaseCompleted = useCallback( (result, product) => result.type !== 'user_cancelled', [], ); return ( ); } ``` Para mostrar el flow como una pantalla independiente, crea un `view` con el método `createFlowView`, define sus manejadores de eventos y llama a `view.present()`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el flow de nuevo, llama a `createFlowView` otra vez para crear una nueva instancia de `view`. ```typescript showLineNumbers title="React Native" try { const view = await createFlowView(flow); view.setEventHandlers({ onPurchaseCompleted(result, product) { return result.type !== 'user_cancelled'; }, }); await view.present(); } catch (error) { // handle the error } ``` :::tip Para más detalles sobre cómo mostrar un flow, consulta nuestra [guía](react-native-present-paywalls). ::: ## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\} Cuando los usuarios pulsan botones en el flow, el SDK de React Native gestiona automáticamente las compras, la restauración, el cierre del flow y la apertura de URLs. Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren gestionar las acciones en tu código. O puede que quieras sobreescribir su comportamiento predeterminado. Por ejemplo, aquí tienes el comportamiento predeterminado del botón de cerrar. No necesitas añadirlo en el código, pero aquí puedes ver cómo se hace si fuera necesario. Para el componente React, gestiona las acciones directamente en el componente `AdaptyFlowView`: ```typescript showLineNumbers title="React Native (TSX)" function MyFlow({ flow }) { const onCloseButtonPress = useCallback( () => true, // allow the flow to close [], ); const onCustomAction = useCallback( (actionId) => false, [], ); return ( ); } ``` Para la presentación modal, implementa los manejadores de eventos usando `setEventHandlers`: ```typescript showLineNumbers title="React Native" const unsubscribe = view.setEventHandlers({ onCloseButtonPress() { return true; // allow the flow to close }, }); ``` :::tip Lee nuestras guías sobre cómo gestionar [acciones](react-native-handle-paywall-actions) y [eventos](react-native-handling-events-1) de botones. ::: ## Próximos pasos \{#next-steps\} :::tip ¿Tienes preguntas o estás teniendo algún problema? Consulta nuestro [foro de soporte](https://adapty.featurebase.app/) donde encontrarás respuestas a preguntas frecuentes o podrás plantear las tuyas. ¡Nuestro equipo y la comunidad están aquí para ayudarte! ::: Tu paywall está listo para mostrarse en la app. Prueba tus compras en el [sandbox del App Store](test-purchases-in-sandbox) o en [Google Play Store](testing-on-android) para asegurarte de que puedes completar una compra de prueba desde el paywall. Ahora necesitas [comprobar el nivel de acceso de los usuarios](react-native-check-subscription-status) para asegurarte de mostrar un paywall o dar acceso a las funciones de pago a los usuarios correctos. ## Ejemplo completo \{#full-example\} Aquí se muestra cómo integrar todos los pasos de esta guía en tu aplicación. ```javascript showLineNumbers title="React Native (TSX)" export default function FlowScreen() { const [flow, setFlow] = useState(null); const loadFlow = async () => { try { const flowData = await adapty.getFlow('YOUR_PLACEMENT_ID'); setFlow(flowData); } catch (error) { console.warn('Error loading flow:', error); } }; const onCloseButtonPress = useCallback( () => true, [], ); const onPurchaseCompleted = useCallback( (result, product) => result.type !== 'user_cancelled', [], ); useEffect(() => { loadFlow(); }, []); return ( {flow ? ( ) : (