# Adapty Documentation (Full Content) > Complete documentation content across all platforms. Locale: es Generated on: 2026-05-15T20:13:57.815Z --- # ANDROID - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.433Z Total files: 39 --- # File: sdk-installation-android --- --- title: "Instalar y configurar el SDK de Android" description: "Guía paso a paso para instalar el SDK de Adapty en Android para apps con suscripciones." --- El SDK de Adapty incluye dos módulos clave para una integración sin fricciones en tu aplicación móvil: - **Core Adapty**: Este SDK esencial 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. :::tip ¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestra [app de ejemplo](https://github.com/adaptyteam/AdaptySDK-Android/tree/master/app), que muestra la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Requisitos \{#requirements\} Requisito mínimo del SDK: `minSdkVersion 21` :::info Adapty es compatible con Google Play Billing Library hasta la versión 8.x. Por defecto, Adapty trabaja con Google Play Billing Library v.7.0.0, pero si quieres forzar una versión posterior, puedes añadir la dependencia manualmente [aquí](https://developer.android.com/google/play/billing/integrate#dependency). ::: --- no_index: true --- import Callout from '../../../components/Callout.astro'; 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\} Elige el método de configuración de dependencias: - Gradle estándar: añade las dependencias en tu `build.gradle` **a nivel de módulo** - Si tu proyecto usa archivos `.gradle.kts`, añade las dependencias en tu `build.gradle.kts` a nivel de módulo - Si usas catálogos de versiones, añade las dependencias en tu archivo `libs.versions.toml` y luego referéncialas en `build.gradle.kts` [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-Android.svg?style=flat&logo=android)](https://github.com/adaptyteam/AdaptySDK-Android/releases) ```groovy showLineNumbers dependencies { ... implementation platform('io.adapty:adapty-bom:') implementation 'io.adapty:android-sdk' // Only add this line if you plan to use Paywall Builder implementation 'io.adapty:android-ui' } ``` ```kotlin showLineNumbers dependencies { ... implementation(platform("io.adapty:adapty-bom:")) implementation("io.adapty:android-sdk") // Solo añade esta línea si planeas usar Paywall Builder: implementation("io.adapty:android-ui") } ``` ```toml showLineNumbers //libs.versions.toml [versions] .. adaptyBom = "" [libraries] .. adapty-bom = { module = "io.adapty:adapty-bom", version.ref = "adaptyBom" } adapty = { module = "io.adapty:android-sdk" } // Only add this line if you plan to use Paywall Builder: adapty-ui = { module = "io.adapty:android-ui" } //module-level build.gradle.kts dependencies { ... implementation(platform(libs.adapty.bom)) implementation(libs.adapty) // Only add this line if you plan to use Paywall Builder: implementation(libs.adapty.ui) } ``` Si la dependencia no se resuelve, asegúrate de tener `mavenCentral()` en tus scripts de Gradle.
Instrucciones para añadirlo Si tu proyecto no tiene `dependencyResolutionManagement` en el archivo `settings.gradle`, añade lo siguiente a tu `build.gradle` de nivel superior al final de los repositorios: ```groovy showLineNumbers title="top-level build.gradle" allprojects { repositories { ... mavenCentral() } } ``` De lo contrario, añade lo siguiente en `settings.gradle` en `repositories` de la sección `dependencyResolutionManagement`: ```groovy showLineNumbers title="settings.gradle" dependencyResolutionManagement { ... repositories { ... mavenCentral() } } ```
## Activar el módulo de Adapty del SDK \{#activate-adapty-module-of-adapty-sdk\} ### Configuración básica \{#basic-setup\} Activa el SDK de Adapty en el código de tu aplicación. :::note El SDK de Adapty solo necesita activarse una vez en tu aplicación. ::: 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. :::important - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API 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. ::: ```kotlin showLineNumbers // In your Application class class MyApplication : Application() { override fun onCreate() { super.onCreate() Adapty.activate( applicationContext, AdaptyConfig.Builder("PUBLIC_SDK_KEY") .build() ) } } ``` ```java showLineNumbers // In your Application class public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Adapty.activate( getApplicationContext(), new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .build() ); } } ``` :::important Espera a que `Adapty.activate` finalice antes de llamar a cualquier otro método del SDK. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order) para ver la secuencia completa. ::: Ahora configura los paywalls en tu aplicación: - Si usas [Adapty Paywall Builder](adapty-paywall-builder), sigue la [guía de inicio rápido de Paywall Builder](android-quickstart-paywalls). - Si construyes tu propia interfaz de paywall, consulta la [guía de inicio rápido para paywalls personalizados](android-quickstart-manual). ## Activar el módulo AdaptyUI del SDK de Adapty \{#activate-adaptyui-module-of-adapty-sdk\} Si tienes pensado usar [Paywall Builder](adapty-paywall-builder), necesitas el módulo AdaptyUI. Se activa automáticamente al activar el módulo principal; no tienes que hacer nada más. ## Configurar Proguard \{#configure-proguard\} Antes de lanzar tu app en producción, añade `-keep class com.adapty.** { *; }` a tu configuración de Proguard. ## 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. Están disponibles los siguientes niveles: | Nivel | Descripción | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------ | | `AdaptyLogLevel.NONE` | No se registrará nada. Valor predeterminado | | `AdaptyLogLevel.ERROR` | Solo se registrarán errores | | `AdaptyLogLevel.WARN` | Se registrarán errores y mensajes del SDK que no causan errores críticos, pero que conviene tener en cuenta. | | `AdaptyLogLevel.INFO` | Se registrarán errores, advertencias y diversos mensajes informativos. | | `AdaptyLogLevel.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 configurar el nivel de log en tu app antes de configurar Adapty. ```kotlin showLineNumbers Adapty.logLevel = AdaptyLogLevel.VERBOSE //recommended for development and the first production release ``` ```java showLineNumbers Adapty.setLogLevel(AdaptyLogLevel.VERBOSE); //recommended for development and the first production release ``` #### Redirigir los mensajes del sistema de logging \{#redirect-the-logging-system-messages\} Si por alguna razón necesitas enviar los mensajes de Adapty a tu propio sistema o guardarlos en un archivo, puedes sobrescribir el comportamiento predeterminado: ```kotlin showLineNumbers Adapty.setLogHandler { level, message -> //handle the log } ``` ```java showLineNumbers Adapty.setLogHandler((level, message) -> { //handle the log }); ``` ### Políticas de datos \{#data-policies\} Adapty no almacena datos personales de tus usuarios a menos que los envíes explícitamente, pero puedes aplicar políticas adicionales de seguridad de datos 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 proteger la privacidad de los usuarios, 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 aplicación. ```kotlin showLineNumbers AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withIpAddressCollectionDisabled(true) .build() ``` ```java showLineNumbers new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withIpAddressCollectionDisabled(true) .build(); ``` #### Deshabilitar la recopilación y el uso compartido del ID de publicidad (Ad ID) \{#disable-advertising-id-ad-id-collection-and-sharing\} Al activar el módulo de Adapty, establece `adIdCollectionDisabled` en `true` para deshabilitar la recopilación del [ID de publicidad](https://support.google.com/googleplay/android-developer/answer/6048248) del usuario. El valor predeterminado es `false`. Utiliza este parámetro para cumplir con las políticas de Play Store, evitar que aparezca el aviso de permiso de ID publicitario, o si tu app no requiere atribución publicitaria ni análisis basados en Ad ID. ```kotlin showLineNumbers AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withAdIdCollectionDisabled(true) .build() ``` ```java showLineNumbers new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withAdIdCollectionDisabled(true) .build(); ``` #### 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. Utiliza `AdaptyUI.configureMediaCache` para modificar el tamaño de caché y el período de validez predeterminados. Es opcional: si no llamas a este método, se usarán los valores por defecto (100 MB de tamaño en disco, 7 días de validez). ```kotlin showLineNumbers val cacheConfig = MediaCacheConfiguration.Builder() .overrideDiskStorageSizeLimit(200L * 1024 * 1024) // 200 MB .overrideDiskCacheValidityTime(3.days) .build() AdaptyUI.configureMediaCache(cacheConfig) ``` ```java showLineNumbers MediaCacheConfiguration cacheConfig = new MediaCacheConfiguration.Builder() .overrideDiskStorageSizeLimit(200L * 1024 * 1024) // 200 MB .overrideDiskCacheValidityTime(TimeInterval.days(3)) .build(); AdaptyUI.configureMediaCache(cacheConfig); ``` **Parámetros:** | Parámetro | Presencia | Descripción | |-------------------------|-----------|-----------------------------------------------------------------------------| | diskStorageSizeLimit | opcional | Tamaño total de la caché en disco en bytes. El valor predeterminado es 100 MB. | | diskCacheValidityTime | opcional | Tiempo durante el cual los archivos en caché se consideran válidos. El valor predeterminado es 7 días. | :::tip Puedes limpiar la caché de medios en tiempo de ejecución usando `AdaptyUI.clearMediaCache(strategy)`, donde `strategy` puede ser `CLEAR_ALL` o `CLEAR_EXPIRED_ONLY`. ::: ### Establecer IDs de cuenta ofuscados \{#set-obfuscated-account-ids\} Google Play requiere IDs de cuenta ofuscados en ciertos casos de uso para mejorar la privacidad y la seguridad del usuario. Estos IDs ayudan a Google Play a identificar las compras manteniendo el anonimato de la información del usuario, lo cual es especialmente importante para la prevención del fraude y el análisis. Es posible que necesites establecer estos IDs si tu aplicación maneja datos sensibles de usuarios o si debes cumplir con normativas de privacidad específicas. Los IDs ofuscados permiten a Google Play rastrear las compras sin exponer los identificadores reales de los usuarios. ```kotlin showLineNumbers AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withObfuscatedAccountId("YOUR_OBFUSCATED_ACCOUNT_ID") .build() ``` ```java showLineNumbers new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withObfuscatedAccountId("YOUR_OBFUSCATED_ACCOUNT_ID") .build(); ``` ### Ejecutar Adapty en un proceso personalizado \{#run-adapty-in-a-custom-process\} Por defecto, Adapty solo puede ejecutarse en el proceso principal de tu aplicación. Si tu app utiliza múltiples procesos, inicializa Adapty una sola vez; de lo contrario, pueden producirse comportamientos inesperados. Si necesitas ejecutar Adapty en un proceso diferente, especifícalo en tu configuración: ```kotlin showLineNumbers AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withProcessName(":custom") .build() ``` ```java showLineNumbers new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withProcessName(":custom") .build(); ``` Si intentas activar Adapty en otro proceso sin establecer este valor, el SDK registrará una advertencia y omitirá la activación. ### Habilitar niveles de acceso locales \{#enable-local-access-levels\} De forma predeterminada, los [niveles de acceso locales](local-access-levels) están deshabilitados en Android. Para habilitarlos, establece `withLocalAccessLevelAllowed` en `true`: ```kotlin showLineNumbers AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withLocalAccessLevelAllowed(true) .build() ``` ```java showLineNumbers new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withLocalAccessLevelAllowed(true) .build(); ``` ## Solución de problemas \{#troubleshooting\} #### Reglas de copia de seguridad en Android (configuración de Auto Backup) \{#android-backup-rules-auto-backup-configuration\} 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 habituales del error: `Manifest merger failed: Attribute application@dataExtractionRules value=(@xml/sample_data_extraction_rules) is also present at [com.other.sdk:library:1.0.0] value=(@xml/other_sdk_data_extraction_rules)` Para resolver esto, necesitas: - Indicar al fusionador de manifiestos que use los valores de tu aplicación para los atributos relacionados con la copia de seguridad. - Fusionar las reglas de copia de seguridad de Adapty y otros SDKs en un único archivo XML (o un par de archivos para Android 12+). #### 1. Añade el espacio de nombres `tools` a tu manifiesto \{#add-the-tools-namespace-to-your-manifest\} Si aún no está presente, añade el espacio de nombres `tools` a la etiqueta raíz ``: ```xml ... ``` #### 2. Sobreescribe los atributos de copia de seguridad en `` \{#override-backup-attributes-in-application\} En el archivo `AndroidManifest.xml` de tu app, actualiza la etiqueta `` para que tu app proporcione los valores finales e indique al fusionador de manifiestos que reemplace los valores de la biblioteca: ```xml ... ``` Si algún SDK también establece `android:allowBackup`, inclúyelo en `tools:replace`: ```xml tools:replace="android:allowBackup,android:fullBackupContent,android:dataExtractionRules" ``` #### 3. Crear archivos de reglas de copia de seguridad combinados \{#3-create-merged-backup-rules-files\} Crea archivos XML en `app/src/main/res/xml/` que combinen las reglas de Adapty con las reglas de otros SDKs. Android utiliza distintos formatos de reglas de copia de seguridad según la versión del SO, 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 muestran AppsFlyer como SDK de terceros de muestra. Reemplaza o añade reglas para cualquier otro SDK que estés utilizando 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 y versiones anteriores** (usa el formato heredado de copia de seguridad completa): ```xml title="sample_backup_rules.xml" ``` Con esta configuración: - Las exclusiones de copia de seguridad de Adapty (`AdaptySDKPrefs.xml`) se conservan. - Las exclusiones de otros SDKs (por ejemplo, `appsflyer-data`) también se aplican. - El combinador de manifiestos usa la configuración de tu app y ya no falla con atributos de copia de seguridad en conflicto. #### Las compras fallan al volver desde otra app \{#purchases-fail-after-returning-from-another-app\} Si la Activity que inicia el flujo de compra usa un `launchMode` distinto al predeterminado, Android puede recrearla o reutilizarla incorrectamente cuando el usuario vuelve 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 que las compras funcionen correctamente, usa únicamente los modos de lanzamiento `standard` o `singleTop` en 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 ``` --- # File: android-quickstart-paywalls --- --- title: "Habilitar compras usando paywalls en el SDK de Android" description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app." --- 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) - Los [**paywalls**](paywalls) son configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar ofertas, precios y combinaciones de productos sin tocar el código de tu app. - Los [**placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 diferentes paywalls a distintos usuarios. Adapty te ofrece tres formas de habilitar las compras en tu app. Selecciona una según los requisitos de tu aplicación: | Implementación | Complejidad | Cuándo usarla | |------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Paywall Builder | ✅ Fácil | [Creas un paywall completo y listo para compras en el editor 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 | 🟡 Medio | Implementas la UI de tu paywall en el código de tu app, pero sigues obteniendo el objeto paywall de Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](android-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 paywall creado en el Paywall Builder de Adapty.** Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](android-making-purchases). ::: Para mostrar un paywall creado en el Paywall Builder de Adapty, en el código de tu app solo necesitas: 1. **Obtener el paywall**: Obtén el paywall desde Adapty. 2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app. 3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con el paywall a las respuestas de tu app. Por ejemplo, abrir enlaces o cerrar el paywall cuando los usuarios pulsan botones. ## Antes de empezar \{#before-you-start\} Antes de empezar, completa estos pasos: 1. [Conecta tu app a 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-android) 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. Obtener el paywall \{#1-get-the-paywall\} Tus paywalls están asociados a placements configurados en el dashboard. Los placements te permiten ejecutar diferentes paywalls para distintas audiencias o para correr [pruebas A/B](ab-tests). Para obtener un paywall creado en el Paywall Builder de Adapty, debes: 1. Obtener el objeto `paywall` por el ID del [placement](placements) usando el método `getPaywall` y verificar si se trata de un paywall creado en el builder. 2. Obtener la configuración de vista del paywall usando el método `getViewConfiguration`. La configuración de vista contiene los elementos de UI y el estilo necesarios para mostrar el paywall. :::important Para obtener la configuración de vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará. ::: ```kotlin showLineNumbers Adapty.getPaywall("YOUR_PLACEMENT_ID") { result -> if (result is AdaptyResult.Success) { val paywall = result.value if (!paywall.hasViewConfiguration) { return@getPaywall } AdaptyUI.getViewConfiguration(paywall) { configResult -> if (configResult is AdaptyResult.Success) { val viewConfiguration = configResult.value } } } } ``` ```java showLineNumbers Adapty.getPaywall("YOUR_PLACEMENT_ID", result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue(); if (!paywall.hasViewConfiguration()) { return; } AdaptyUI.getViewConfiguration(paywall, configResult -> { if (configResult instanceof AdaptyResult.Success) { AdaptyUI.LocalizedViewConfiguration viewConfiguration = ((AdaptyResult.Success) configResult).getValue(); // use loaded configuration } }); } }); ``` ## 2. Mostrar el paywall \{#2-display-the-paywall\} Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo. Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, llama al método `AdaptyUI.getPaywallView()` o crea el `AdaptyPaywallView` directamente: ```kotlin showLineNumbers val paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, null, // products = null means auto-fetch eventListener, ) ``` ```kotlin showLineNumbers val paywallView = AdaptyPaywallView(activity) // or retrieve it from xml ... with(paywallView) { showPaywall( viewConfiguration, null, // products = null means auto-fetch eventListener, ) } ``` ```java showLineNumbers AdaptyPaywallView paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, null, // products = null means auto-fetch eventListener, ); ``` ```java showLineNumbers AdaptyPaywallView paywallView = new AdaptyPaywallView(activity); //add to the view hierarchy if needed, or you receive it from xml ... paywallView.showPaywall(viewConfiguration, products, eventListener); ``` ```xml showLineNumbers ``` Una vez creada la vista correctamente, puedes añadirla a la jerarquía de vistas y mostrarla en la pantalla del dispositivo. :::tip Para más detalles sobre cómo mostrar un paywall, consulta nuestra [guía](android-present-paywalls). ::: ## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\} Cuando los usuarios pulsan botones en el paywall, el SDK de Android gestiona automáticamente las compras, la restauración, el cierre del paywall y la apertura de enlaces. Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren gestionar sus acciones en tu código. También puedes querer sobrescribir su comportamiento predeterminado. Por ejemplo, aquí está el comportamiento predeterminado del botón de cierre. No necesitas añadirlo en el código, pero aquí puedes ver cómo hacerlo si fuera necesario. :::tip Lee nuestras guías sobre cómo gestionar [acciones](android-handle-paywall-actions) y [eventos](android-handling-events) de los botones. ::: ```kotlin showLineNumbers title="Kotlin" override fun onActionPerformed(action: AdaptyUI.Action, context: Context) { when (action) { AdaptyUI.Action.Close -> (context as? Activity)?.onBackPressed() // default behavior } } ``` ```java showLineNumbers @Override public void onActionPerformed(@NonNull AdaptyUI.Action action, @NonNull Context context) { if (action instanceof AdaptyUI.Action.Close) { if (context instanceof Activity) { ((Activity) context).onBackPressed(); } } } ``` ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; ¿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 Google Play Store](testing-on-android) para asegurarte de que puedes completar una compra de prueba desde el paywall. A continuación, debes [comprobar el nivel de acceso de los usuarios](android-check-subscription-status) para asegurarte de que muestras un paywall o das acceso a las funciones de pago a los usuarios correctos. ## Ejemplo completo \{#full-example\} Aquí se muestra cómo integrar todos estos pasos en tu app. ```kotlin showLineNumbers title="Kotlin" class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Adapty.getPaywall("YOUR_PLACEMENT_ID") { paywallResult -> if (paywallResult is AdaptyResult.Success) { val paywall = paywallResult.value if (!paywall.hasViewConfiguration) { // Use custom logic return@getPaywall } AdaptyUI.getViewConfiguration(paywall) { configResult -> if (configResult is AdaptyResult.Success) { val viewConfiguration = configResult.value val paywallView = AdaptyUI.getPaywallView( this, viewConfiguration, null, // products = null means auto-fetch object : AdaptyUIEventListener { override fun onActionPerformed(action: AdaptyUI.Action, context: Context) { when (action) { is AdaptyUI.Action.Close -> { (context as? Activity)?.onBackPressed() } } } } ) setContentView(paywallView) } } } } } } ``` ```java showLineNumbers public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Adapty.getPaywall("YOUR_PLACEMENT_ID", paywallResult -> { if (paywallResult instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success) paywallResult).getValue(); if (!paywall.hasViewConfiguration()) { // Use custom logic return; } AdaptyUI.getViewConfiguration(paywall, configResult -> { if (configResult instanceof AdaptyResult.Success) { AdaptyUI.LocalizedViewConfiguration viewConfiguration = ((AdaptyResult.Success) configResult).getValue(); AdaptyPaywallView paywallView = AdaptyUI.getPaywallView( this, viewConfiguration, null, // products = null means auto-fetch new AdaptyUIEventListener() { @Override public void onActionPerformed(@NonNull AdaptyUI.Action action, @NonNull Context context) { if (action instanceof AdaptyUI.Action.Close) { if (context instanceof Activity) { ((Activity) context).onBackPressed(); } } } } ); setContentView(paywallView); } }); } }); } } ``` --- # File: android-check-subscription-status --- --- title: "Comprobar el estado de la suscripción en el SDK de Android" description: "Aprende cómo comprobar el estado de la suscripción en tu app de Android con Adapty." --- Para decidir si los usuarios pueden acceder al contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil. Este artículo muestra cómo acceder al estado del perfil para decidir qué deben ver los usuarios: si mostrarles un paywall o darles acceso a las funciones de pago. ## Obtener el estado de la suscripción \{#get-subscription-status\} Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones: - Llama a `getProfile` si necesitas los datos más recientes del perfil de inmediato (por ejemplo, al iniciar la app) o quieres forzar una actualización. - Configura **actualizaciones automáticas del perfil** para mantener una copia local que se refresca automáticamente cada vez que cambia el estado de la suscripción. ### Obtener el perfil \{#get-profile\} La forma más sencilla de obtener el estado de la suscripción es usar el método `getProfile` para acceder al perfil: ```kotlin showLineNumbers Adapty.getProfile { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value // check the access } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.getProfile(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success) result).getValue(); // check the access } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` ### Escuchar actualizaciones de la suscripción \{#listen-to-subscription-updates\} Para recibir automáticamente actualizaciones del perfil en tu app: 1. Usa `Adapty.setOnProfileUpdatedListener()` para escuchar los cambios en el perfil: Adapty llamará automáticamente a este método cada vez que cambie el estado de la suscripción del usuario. 2. Guarda los datos del perfil actualizado cuando se llame a este método, para poder usarlos en toda tu app sin realizar peticiones de red adicionales. ```kotlin class SubscriptionManager { private var currentProfile: AdaptyProfile? = null init { // Listen for profile updates Adapty.setOnProfileUpdatedListener { profile -> currentProfile = profile // Update UI, unlock content, etc. } } // Use stored profile instead of calling getProfile() fun hasAccess(): Boolean { return currentProfile?.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true } } ``` ```java public class SubscriptionManager { private AdaptyProfile currentProfile; public SubscriptionManager() { // Listen for profile updates Adapty.setOnProfileUpdatedListener(profile -> { this.currentProfile = profile; // Update UI, unlock content, etc. }); } // Use stored profile instead of calling getProfile() public boolean hasAccess() { if (currentProfile == null) { return false; } AdaptyAccessLevel premiumAccess = currentProfile.getAccessLevels().get("YOUR_ACCESS_LEVEL"); return premiumAccess != null && premiumAccess.isActive(); } } ``` :::note Adapty llama automáticamente al listener de actualización del perfil cuando tu app arranca, proporcionando datos de suscripción en caché incluso si el dispositivo está sin conexión. ::: ## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\} Cuando necesitas tomar decisiones inmediatas sobre mostrar paywalls o conceder acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en situaciones como el inicio de la app, al entrar en secciones premium o antes de mostrar contenido específico. ```kotlin private fun initializePaywall() { loadPaywall { paywallView -> checkAccessLevel { result -> when (result) { is AdaptyResult.Success -> { if (!result.value && paywallView != null) { setContentView(paywallView) // Show paywall if no access } } is AdaptyResult.Error -> { if (paywallView != null) { setContentView(paywallView) // Show paywall if access check fails } } } } } } private fun checkAccessLevel(callback: ResultCallback) { Adapty.getProfile { result -> when (result) { is AdaptyResult.Success -> { val hasAccess = result.value.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true callback.onResult(AdaptyResult.Success(hasAccess)) } is AdaptyResult.Error -> { callback.onResult(AdaptyResult.Error(result.error)) } } } } ``` ```java private void initializePaywall() { loadPaywall(paywallView -> { checkAccessLevel(result -> { if (result instanceof AdaptyResult.Success) { boolean hasAccess = ((AdaptyResult.Success) result).getValue(); if (!hasAccess && paywallView != null) { setContentView(paywallView); // Show paywall if no access } } else if (result instanceof AdaptyResult.Error) { if (paywallView != null) { setContentView(paywallView); // Show paywall if access check fails } } }); }); } private void checkAccessLevel(ResultCallback callback) { Adapty.getProfile(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success) result).getValue(); AdaptyAccessLevel premiumAccess = profile.getAccessLevels().get("YOUR_ACCESS_LEVEL"); boolean hasAccess = premiumAccess != null && premiumAccess.isActive(); callback.onResult(AdaptyResult.success(hasAccess)); } else if (result instanceof AdaptyResult.Error) { callback.onResult(AdaptyResult.error(((AdaptyResult.Error) result).getError())); } }); } ``` ## Pasos siguientes \{#next-steps\} Ahora que sabes cómo hacer seguimiento del estado de la suscripción, aprende a [trabajar con perfiles de usuario](android-quickstart-identify) para asegurarte de que los usuarios pueden acceder a lo que han pagado. --- # File: android-quickstart-identify --- --- title: "Identificar usuarios en el SDK de Android" description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app en Android." --- :::important Esta guía es para ti si tienes tu propio sistema de autenticación. Aquí aprenderás a trabajar con perfiles de usuario en Adapty para asegurarte de que se alinee con tu sistema de autenticación existente. ::: La forma en que gestionas las compras de los usuarios depende del modelo de autenticación de tu app: - Si tu app no usa autenticación de backend y no almacena datos de usuario, consulta la [sección sobre usuarios anónimos](#anonymous-users). - Si tu app tiene (o tendrá) autenticación de backend, consulta la [sección sobre usuarios identificados](#identified-users). **Conceptos clave**: - Los **perfiles** son las entidades necesarias para que funcione el SDK. Adapty los crea automáticamente. - Pueden ser anónimos **(sin customer user ID)** o identificados **(con customer user ID)**. - Tú proporcionas el **customer user ID** para cruzar los perfiles de Adapty con tu sistema de autenticación interno. Estas son las diferencias entre usuarios anónimos e identificados: | | Usuarios anónimos | Usuarios identificados | |------------------------------|------------------------------------------------------------|--------------------------------------------------------------------------------------| | **Gestión de compras** | Restauración de compras a nivel de store | Mantienen el historial de compras entre dispositivos gracias a su customer user ID | | **Gestión de perfiles** | Nuevos perfiles en cada reinstalación | El mismo perfil en todas las sesiones y dispositivos | | **Persistencia de datos** | Los datos de usuarios anónimos están ligados a la instalación de la app | Los datos de usuarios identificados persisten entre instalaciones de la app | ## Usuarios anónimos \{#anonymous-users\} Si no tienes autenticación de backend, **no necesitas gestionar la autenticación en el código de la app**: 1. Cuando el SDK se activa en el primer arranque de la app, Adapty **crea un nuevo perfil para el usuario**. 2. Cuando el usuario compra algo en la app, esa compra queda **asociada a su perfil de Adapty y a su cuenta de la store**. 3. Cuando el usuario **reinstala** la app o la instala en un **nuevo dispositivo**, Adapty **crea un nuevo perfil anónimo al activarse**. 4. Si el usuario ya había realizado compras en tu app, por defecto se sincronizan automáticamente desde el App Store al activar el SDK. Con usuarios anónimos se crean nuevos perfiles en cada instalación, pero eso no es un problema porque, en los análisis de Adapty, puedes [configurar qué se considera una nueva instalación](general#4-installs-definition-for-analytics). Para usuarios anónimos, debes contar las instalaciones por **ID de dispositivo**. En este caso, cada instalación de la app en un dispositivo se cuenta como una instalación, incluidas las reinstalaciones. ## Usuarios identificados \{#identified-users\} Tienes dos opciones para identificar usuarios en la app: - [**Durante el login/registro:**](#during-loginsignup) Si los usuarios inician sesión después de que arranca tu app, llama a `identify()` con un customer user ID cuando se autentiquen. - [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando se lanza la app, envíalo al llamar a `activate()`. :::important Por defecto, cuando Adapty recibe una compra de un Customer User ID que en ese momento está asociado a otro Customer User ID, el nivel de acceso se comparte, de modo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o desactivar completamente el uso compartido. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles. ::: ### Durante el login/registro \{#during-loginsignup\} Si identificas a los usuarios después del arranque de la app (por ejemplo, tras iniciar sesión o registrarse), usa el método `identify` para establecer su customer user ID. - Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual. - Si **ya has usado este customer user ID para identificar al usuario antes**, Adapty pasará a trabajar con el perfil asociado a ese customer user ID. :::important Los customer user IDs deben ser únicos para cada usuario. Si escribes el valor del parámetro directamente en el código, todos los usuarios se considerarán como uno solo. ::: Espera a que se ejecute el callback de finalización de `identify` antes de llamar a otros métodos del SDK. Las llamadas concurrentes pueden acabar en el perfil anónimo en lugar del identificado. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order). ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") { error -> // Unique for each user if (error == null) { // successful identify } } ``` ```java showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID", error -> { if (error == null) { // successful identify } }); ``` ### Durante la activación del SDK \{#during-the-sdk-activation\} Si ya conoces el customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado. Si conoces el customer user ID pero lo estableces solo después de la activación, eso significa que, al activarse, Adapty creará un nuevo perfil anónimo y cambiará al existente solo cuando llames a `identify`. Puedes pasar un customer user ID existente (uno que hayas usado antes) o uno nuevo. Si pasas uno nuevo, el perfil creado al activarse se vinculará automáticamente a ese customer user ID. :::note Por defecto, crear perfiles anónimos no afecta a los dashboards de análisis, porque las instalaciones se cuentan por ID de dispositivo. Un ID de dispositivo representa una única instalación de la app desde la store en un dispositivo y se regenera solo cuando la app se reinstala. No depende de si es una primera o repetida instalación, ni de si se usa un customer user ID existente. Crear un perfil (al activar el SDK o al cerrar sesión), iniciar sesión o actualizar la app sin reinstalarla no genera eventos de instalación adicionales. Si quieres contar instalaciones por usuarios únicos en lugar de por dispositivos, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics). ::: ```kotlin showLineNumbers AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId("user123") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one. .build() ``` ```java showLineNumbers new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId("user123") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one. .build(); ``` ### Cerrar sesión de usuarios \{#log-users-out\} Si tienes un botón para cerrar la sesión de los usuarios, usa el método `logout`. :::important Cerrar la sesión de un usuario crea un nuevo perfil anónimo para ese usuario. ::: ```kotlin showLineNumbers Adapty.logout { error -> if (error == null) { // successful logout } } ``` ```java showLineNumbers Adapty.logout(error -> { if (error == null) { // successful logout } }); ``` :::info Para volver a iniciar sesión de los usuarios en la app, usa el método `identify`. ::: ### Permitir compras sin iniciar sesión \{#allow-purchases-without-login\} Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, debes asegurarte de que mantengan el acceso después de iniciar sesión: 1. Cuando un usuario sin sesión realiza una compra, Adapty la vincula a su ID de perfil anónimo. 2. Cuando el usuario inicia sesión en su cuenta, Adapty pasa a trabajar con su perfil identificado. - Si es un customer user ID nuevo (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que se mantiene todo el historial de compras. - Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), debes obtener el nivel de acceso actual después del cambio de perfil. Puedes llamar a [`getProfile`](android-check-subscription-status) justo después de la identificación, o [escuchar las actualizaciones de perfil](android-check-subscription-status) para que los datos se sincronicen automáticamente. ## Próximos pasos \{#next-steps\} ¡Enhorabuena! Has implementado la lógica de pago in-app en tu app. ¡Te deseamos mucho éxito con la monetización de tu app! Para sacar aún más partido a Adapty, puedes explorar estos temas: - [**Pruebas**](troubleshooting-test-purchases): Asegúrate de que todo funciona como se espera - [**Onboardings**](android-onboardings): Engancha a los usuarios con onboardings e impulsa la retención - [**Integraciones**](configuration): Integra con servicios de atribución de marketing y análisis con una sola línea de código - [**Establecer atributos de perfil personalizados**](android-setting-user-attributes): Añade atributos personalizados a los perfiles de usuario y crea segmentos para lanzar pruebas A/B o mostrar distintos paywalls a diferentes usuarios --- # File: adapty-cursor-android --- --- title: "Integra Adapty en tu app Android con ayuda de IA" description: "Una guía paso a paso para integrar Adapty en tu app Android usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA." --- Esta página explica dos formas de integrar Adapty en tu app Android. Usa la skill de integración del SDK que encontrarás a continuación para un flujo automatizado de principio a fin, o sigue el recorrido manual más adelante. ## Usa la skill de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\} La [skill adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza la integración completa: configuración del dashboard, instalación del SDK, paywall y verificación por etapas. El recorrido manual que aparece más abajo es la alternativa si tu herramienta no admite el formato Claude Skills. **Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI. ### Instalación \{#install\} Elige el formato para tu herramienta. La lista completa está en el [README de la skill](https://github.com/adaptyteam/adapty-sdk-integration-skill). - **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y luego `claude plugin install adapty-sdk-integration@adapty` desde tu terminal. - **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`. - **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`. - **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de skills de tu herramienta. ### Ejecución \{#run\} En tu proyecto, ejecuta `/adapty-sdk-integration`. La skill detecta tu plataforma y hace algunas preguntas de configuración. Luego guía el proceso de configuración del dashboard, instalación del SDK, paywall y verificación — consultando la documentación de Adapty relevante en cada etapa. :::note La skill está en beta. Si se detiene o se comporta de forma inesperada, el recorrido manual que aparece a continuación cubre cada etapa paso a paso. ::: ## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\} Adapty requiere cierta configuración en el dashboard antes de escribir código con el SDK. Puedes hacerlo con una skill de LLM interactiva o manualmente desde el Dashboard. ### Con la skill (recomendado) \{#skill-approach-recommended\} La skill de la CLI de Adapty permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin necesidad de abrir el Dashboard en cada paso. Solo necesitas [conectar tu store](integrate-payments) desde el Dashboard. ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` Una vez añadida la skill, ejecuta `/adapty-cli` en tu agente. Te guiará en cada paso, incluido cuándo abrir el Dashboard para conectar tu store. ### Con el Dashboard \{#dashboard-approach\} Si prefieres configurarlo todo manualmente, esto es lo que necesitas antes de escribir código. Tu LLM no puede consultar los valores del dashboard por ti — tendrás que proporcionárselos. 1. **Conecta tu store**: En el Adapty Dashboard, ve a **App settings → General**. Es obligatorio para que las compras funcionen. [Conectar Google Play](integrate-payments) 2. **Copia tu Public SDK key**: En el Adapty Dashboard, ve a **App settings → General** y busca la sección **API keys**. En el código, es la cadena que pasas al constructor de configuración de Adapty. 3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No referenciarás los productos directamente en el código — Adapty los entrega a través de los paywalls. [Añadir productos](quickstart-products) 4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `Adapty.getPaywall("YOUR_PLACEMENT_ID")`. [Crear paywall](quickstart-paywalls) 5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena se comprueba en `profile.accessLevels["premium"]?.isActive`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago tienen acceso a diferentes funciones según el producto (por ejemplo, un plan `basic` frente a un plan `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar. :::tip Una vez que tengas los cinco, estás listo para escribir código. Dile a tu LLM: "Mi Public SDK key es X, mi placement ID es Y" para que pueda generar el código correcto de inicialización y obtención del paywall. ::: ### Configura cuando estés listo \{#set-up-when-ready\} No son obligatorios para empezar a programar, pero los necesitarás a medida que tu integración madure: - **Pruebas A/B**: Configúralas en la página **Placements**. No requieren cambios en el código. [Pruebas A/B](ab-tests) - **Paywalls y placements adicionales**: Añade más llamadas a `getPaywall` con diferentes IDs de placement. - **Integraciones de analíticas**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta [integraciones de analíticas](analytics-integration) e [integraciones de atribución](attribution-integration). ## Dale documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\} ### Usa Context7 (recomendado) \{#use-context7-recommended\} [Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. El LLM obtiene automáticamente la documentación correcta según lo que le preguntes — sin necesidad de pegar URLs manualmente. Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta: ``` npx ctx7 setup ``` Esto detecta tu editor y configura el servidor Context7. Para la configuración manual, consulta el [repositorio de Context7 en GitHub](https://github.com/upstash/context7). Una vez configurado, referencia la biblioteca de Adapty en tus prompts: ``` Use the adaptyteam/adapty-docs library to look up how to install the Android SDK ``` :::warning Aunque Context7 elimina la necesidad de pegar enlaces a la documentación manualmente, el orden de implementación importa. Sigue el [recorrido de implementación](#implementation-walkthrough) que aparece a continuación paso a paso para asegurarte de que todo funcione. ::: ### Usa la documentación en texto plano \{#use-plain-text-docs\} Puedes acceder a cualquier artículo de Adapty en texto plano Markdown. Añade `.md` al final de su URL o haz clic en **Copy for LLM** debajo del título del artículo. Por ejemplo: [adapty-cursor-android.md](https://adapty.io/docs/es/adapty-cursor-android.md). Cada etapa del [recorrido de implementación](#implementation-walkthrough) incluye un bloque "Envía esto a tu LLM" con enlaces `.md` para pegar. Para obtener más documentación a la vez, consulta los [archivos de índice y subconjuntos específicos por plataforma](#plain-text-doc-index-files) que aparecen más abajo. ## Recorrido de implementación \{#implementation-walkthrough\} El resto de esta guía recorre la integración de Adapty en el orden de implementación. Cada etapa incluye la documentación que debes enviar a tu LLM, qué deberías ver al terminar y los problemas más comunes. ### Planifica tu integración \{#plan-your-integration\} Antes de escribir código, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA admite un modo de planificación (como el modo plan de Cursor o Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir cualquier código. Indícale a tu LLM qué enfoque usas para las compras — esto afecta a las guías que debe seguir: - [**Adapty Paywall Builder**](adapty-paywall-builder): Creas los paywalls en el editor no-code de Adapty y el SDK los renderiza automáticamente. - [**Paywalls creados manualmente**](android-making-purchases): Construyes tu propia interfaz de paywall en el código, pero sigues usando Adapty para obtener los productos y gestionar las compras. - [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analíticas e integraciones. ¿No sabes cuál elegir? Lee la [tabla comparativa en la guía de inicio rápido](android-quickstart-paywalls). ### Instala y configura el SDK \{#install-and-configure-the-sdk\} Añade la dependencia del SDK de Adapty mediante Gradle en Android Studio y actívalo con tu Public SDK key. Esta es la base — sin ella nada más funciona. **Guía:** [Instalar y configurar el SDK de Adapty](sdk-installation-android) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/sdk-installation-android.md ``` :::tip[Punto de control] - **Esperado:** La app compila y se ejecuta. Logcat muestra el log de activación de Adapty. - **Problema frecuente:** "Public API key is missing" → comprueba que has reemplazado el placeholder con tu clave real de App settings. ::: ### Muestra los paywalls y gestiona las compras \{#show-paywalls-and-handle-purchases\} Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras. Prueba cada compra en sandbox a medida que avanzas — no esperes al final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración. **Guías:** - [Habilitar compras con paywalls (inicio rápido)](android-quickstart-paywalls) - [Obtener paywalls del Paywall Builder y su configuración](android-get-pb-paywalls) - [Mostrar paywalls](android-present-paywalls) - [Gestionar eventos del paywall](android-handling-events) - [Responder a acciones de botones](android-handle-paywall-actions) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/android-quickstart-paywalls.md - https://adapty.io/docs/es/android-get-pb-paywalls.md - https://adapty.io/docs/es/android-present-paywalls.md - https://adapty.io/docs/es/android-handling-events.md - https://adapty.io/docs/es/android-handle-paywall-actions.md ``` :::tip[Punto de control] - **Esperado:** El paywall aparece con los productos configurados. Al pulsar un producto se activa el diálogo de compra en sandbox. - **Problema frecuente:** Paywall vacío o error en `getPaywall` → verifica que el ID del placement coincide exactamente con el del dashboard y que el placement tiene una audiencia asignada. ::: **Guías:** - [Habilitar compras en tu paywall personalizado (inicio rápido)](android-quickstart-manual) - [Obtener paywalls y productos](fetch-paywalls-and-products-android) - [Renderizar un paywall diseñado con Remote Config](present-remote-config-paywalls-android) - [Realizar compras](android-making-purchases) - [Restaurar compras](android-restore-purchase) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/android-quickstart-manual.md - https://adapty.io/docs/es/fetch-paywalls-and-products-android.md - https://adapty.io/docs/es/present-remote-config-paywalls-android.md - https://adapty.io/docs/es/android-making-purchases.md - https://adapty.io/docs/es/android-restore-purchase.md ``` :::tip[Punto de control] - **Esperado:** Tu paywall personalizado muestra los productos obtenidos de Adapty. Al pulsar un producto se activa el diálogo de compra en sandbox. - **Problema frecuente:** Array de productos vacío → verifica que el paywall tiene productos asignados en el dashboard y que el placement tiene una audiencia. ::: **Guías:** - [Descripción general del modo Observer](observer-vs-full-mode) - [Implementar el modo Observer](implement-observer-mode-android) - [Informar transacciones en el modo Observer](report-transactions-observer-mode-android) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/observer-vs-full-mode.md - https://adapty.io/docs/es/implement-observer-mode-android.md - https://adapty.io/docs/es/report-transactions-observer-mode-android.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox usando tu flujo de compras existente, la transacción aparece en el **Event Feed** del dashboard de Adapty. - **Problema frecuente:** Sin eventos → verifica que estás reportando las transacciones a Adapty y que las notificaciones en tiempo real de Google Play Developer están configuradas. ::: ### Comprueba el estado de la suscripción \{#check-subscription-status\} Tras una compra, comprueba el perfil del usuario para verificar si tiene un nivel de acceso activo y así restringir el contenido premium. **Guía:** [Comprobar el estado de la suscripción](android-check-subscription-status) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/android-check-subscription-status.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox, `profile.accessLevels["premium"]?.isActive` devuelve `true`. - **Problema frecuente:** `accessLevels` vacío tras la compra → comprueba que el producto tiene un nivel de acceso asignado en el dashboard. ::: ### Identifica a los usuarios \{#identify-users\} Vincula las cuentas de usuario de tu app con los perfiles de Adapty para que las compras persistan entre dispositivos. :::important Omite este paso si tu app no tiene autenticación. ::: **Guía:** [Identificar usuarios](android-quickstart-identify) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/android-quickstart-identify.md ``` :::tip[Punto de control] - **Esperado:** Tras llamar a `Adapty.identify("your-user-id")`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado. - **Problema frecuente:** Llama a `identify` después de la activación pero antes de obtener los paywalls para evitar la atribución a perfiles anónimos. ::: ### Prepárate para el lanzamiento \{#prepare-for-release\} Una vez que tu integración funcione en sandbox, revisa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción. **Guía:** [Lista de verificación de lanzamiento](release-checklist) Envía esto a tu LLM: ``` Read these Adapty docs before releasing: - https://adapty.io/docs/es/release-checklist.md ``` :::tip[Punto de control] - **Esperado:** Todos los elementos de la lista confirmados: conexión al store, notificaciones del servidor, flujo de compras, comprobaciones del nivel de acceso y requisitos de privacidad. - **Problema frecuente:** Faltan las notificaciones en tiempo real de Google Play → configúralas en **App settings → Android SDK** o los eventos no aparecerán en el dashboard. ::: ## Archivos de índice de documentación en texto plano \{#plain-text-doc-index-files\} Si necesitas darle a tu LLM un contexto más amplio más allá de páginas individuales, ofrecemos archivos de índice que listan o combinan toda la documentación de Adapty: - [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con enlaces `.md`. Un [estándar emergente](https://llmstxt.org/) para hacer los sitios web accesibles a los LLMs. Ten en cuenta que para algunos agentes de IA (p. ej., ChatGPT) necesitarás descargar `llms.txt` y subirlo al chat como archivo. - [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación de Adapty combinada en un único archivo. Muy grande — úsalo solo cuando necesites el panorama completo. - Subconjuntos específicos de Android: [`android-llms.txt`](https://adapty.io/docs/es/android-llms.txt) y [`android-llms-full.txt`](https://adapty.io/docs/es/android-llms-full.txt): Subconjuntos específicos de la plataforma que ahorran tokens en comparación con el sitio completo. --- # File: android-get-pb-paywalls --- --- title: "Obtener paywalls del Paywall Builder y su configuración en el SDK de Android" description: "Aprende a recuperar paywalls de PB en Adapty para un mejor control de suscripciones en tu app de Android." --- Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación. :::warning El nuevo Paywall Builder funciona con el SDK de Android versión 3.0 o superior. ::: Ten en cuenta que este tema hace referencia a paywalls personalizados con Paywall Builder. Si estás implementando tus paywalls manualmente, consulta el tema [Obtener paywalls y productos para paywalls con Remote Config en tu app móvil](fetch-paywalls-and-products-android). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. :::
Antes de empezar a mostrar paywalls en tu app móvil (haz clic para expandir) 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en ellos](create-placement) en el Adapty Dashboard. 4. Instala el [SDK de Adapty](sdk-installation-android) en tu app móvil.
## Obtener el paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\} Si has [diseñado un paywall usando el Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Este tipo de paywall contiene tanto lo que se debe mostrar como la forma en que debe mostrarse. Aun así, necesitas obtener su ID a través del placement, su configuración de vista, y luego presentarlo en tu app móvil. Para garantizar un rendimiento óptimo, es fundamental recuperar el paywall y su [configuración de vista](android-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dando tiempo suficiente para que las imágenes se descarguen antes de presentárselas al usuario. Para obtener un paywall, usa el método `getPaywall`: ```kotlin showLineNumbers ... Adapty.getPaywall("YOUR_PLACEMENT_ID", locale = "en", loadTimeout = 10.seconds) { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value // the requested paywall } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers ... Adapty.getPaywall("YOUR_PLACEMENT_ID", "en", TimeInterval.seconds(10), result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue(); // the requested paywall } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** |

opcional

por defecto: `en`

|

El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.

Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.

Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.

| | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |

Por defecto, el SDK intentará cargar datos del servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.

Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo irregular que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.

Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante limpieza manual.

El SDK de Adapty almacena paywalls localmente en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN sea inaccesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.

| | **loadTimeout** | por defecto: 5 seg |

Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local.

Ten en cuenta que en casos excepcionales este método puede exceder ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.

Para Android: puedes crear `TimeInterval` con funciones de extensión (como `5.seconds`, donde `.seconds` es de `import com.adapty.utils.seconds`), o `TimeInterval.seconds(5)`. Para no establecer ningún límite, usa `TimeInterval.INFINITE`.

| Parámetros de respuesta: | Parámetro | Descripción | | :-------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------| | Paywall | Un objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/) con una lista de IDs de productos, el identificador del paywall, Remote Config y otras propiedades. | ## Obtener la configuración de vista del paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important Asegúrate de activar el toggle **Show on device** en el Paywall Builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperar. ::: Después de obtener el paywall, comprueba si incluye una `ViewConfiguration`, lo que indica que fue creado con Paywall Builder. Esto te indicará cómo mostrar el paywall. Si la `ViewConfiguration` está presente, trátalo como un paywall de Paywall Builder; si no, [manéjalo como un paywall de Remote Config](present-remote-config-paywalls). Usa el método `getViewConfiguration` para cargar la configuración de vista. ```kotlin showLineNumbers if (!paywall.hasViewConfiguration) { // use your custom logic return } AdaptyUI.getViewConfiguration(paywall, loadTimeout = 10.seconds) { result -> when(result) { is AdaptyResult.Success -> { val viewConfiguration = result.value // use loaded configuration } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` | Parámetro | Presencia | Descripción | | :-------------- | :------------- | :----------------------------------------------------------- | | **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. | | **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local. Ten en cuenta que en casos excepcionales este método puede exceder ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente. | Usa el método `getViewConfiguration` para cargar la configuración de vista. ```java showLineNumbers if (!paywall.hasViewConfiguration()) { // use your custom logic return; } AdaptyUI.getViewConfiguration(paywall, TimeInterval.seconds(10), result -> { if (result instanceof AdaptyResult.Success) { AdaptyUI.LocalizedViewConfiguration viewConfiguration = ((AdaptyResult.Success) result).getValue(); // use loaded configuration } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` | Parámetro | Presencia | Descripción | | :----------------------- | :------------- | :----------------------------------------------------------- | | **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. | | **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local. Ten en cuenta que en casos excepcionales este método puede exceder ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente. | :::note Si usas varios idiomas, aprende cómo añadir una [localización en Paywall Builder](add-paywall-locale-in-adapty-paywall-builder) y cómo usar los códigos de idioma correctamente [aquí](android-localizations-and-locale-codes). ::: Una vez cargado, [presenta el paywall](android-present-paywalls). ## Obtener un paywall para la audiencia predeterminada y cargarlo más rápido \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} Normalmente, los paywalls se obtienen casi de forma instantánea, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, si tienes muchas audiencias y paywalls y tus usuarios tienen una conexión a internet lenta, obtener un paywall puede tardar más de lo deseado. En esos casos, puede que quieras mostrar un paywall por defecto para garantizar una buena experiencia de usuario en lugar de no mostrar ninguno. Para resolver esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es importante entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, tal como se detalla en la sección [Obtener el paywall diseñado con Paywall Builder](#fetch-paywall-designed-with-paywall-builder) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad hacia atrás**: si necesitas mostrar paywalls diferentes para distintas versiones de la app (actual y futuras), puedes encontrarte con dificultades. Tendrás que diseñar paywalls que sean compatibles con la versión actual (legacy) o asumir que los usuarios con esa versión pueden tener problemas con paywalls que no se renderizan. - **Pérdida de segmentación**: todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluyendo la basada en países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes a cambio de una carga más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, utiliza `getPaywall` como se describe [arriba](#fetch-paywall-designed-with-paywall-builder). ::: ```kotlin showLineNumbers Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", locale = "en") { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value // the requested paywall } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue(); // the requested paywall } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` :::note El método `getPaywallForDefaultAudience` está disponible a partir del SDK de Android 2.11.3 ::: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** |

opcional

por defecto: `en`

|

El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.

Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.

Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.

| | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |

Por defecto, el SDK intentará cargar datos del servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.

Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo irregular que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.

Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante limpieza manual.

| ## Personalizar assets \{#customize-assets\} Para personalizar imágenes y vídeos en tu paywall, implementa los assets personalizados. Las imágenes y vídeos hero tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de assets personalizado, apuntas a estos elementos por sus IDs y personalizas su comportamiento. Para otras imágenes y vídeos, necesitas [establecer un ID personalizado](custom-media) en el dashboard de Adapty. Por ejemplo, puedes: - Mostrar una imagen o vídeo diferente a algunos usuarios. - Mostrar una imagen de vista previa local mientras se carga la imagen principal remota. - Mostrar una imagen de vista previa antes de reproducir un vídeo. :::important Para usar esta funcionalidad, actualiza el SDK de Android de Adapty a la versión 3.7.0 o superior. ::: Aquí tienes un ejemplo de cómo puedes proporcionar assets personalizados mediante un diccionario simple: ```kotlin showLineNumbers val customAssets = AdaptyCustomAssets.of( "hero_image" to AdaptyCustomImageAsset.remote( url = "https://example.com/image.jpg", preview = AdaptyCustomImageAsset.file( FileLocation.fromAsset("images/hero_image_preview.png"), ) ), "hero_video" to AdaptyCustomVideoAsset.file( FileLocation.fromResId(requireContext(), R.raw.custom_video), preview = AdaptyCustomImageAsset.file( FileLocation.fromResId(requireContext(), R.drawable.video_preview), ), ), ) val paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, products, eventListener, insets, customAssets, ) ``` :::note Si un asset no se encuentra, el paywall volverá a su apariencia predeterminada. ::: --- # File: android-present-paywalls --- --- title: "Android - Presentar paywalls con el nuevo Paywall Builder" description: "Aprende a presentar paywalls en Android para una monetización efectiva." --- Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese paywall ya contiene tanto lo que se muestra como la forma en que se muestra. :::warning Esta guía es solo para **paywalls del nuevo Paywall Builder** que requieren SDK v3.0. El proceso para mostrar paywalls es diferente según si fueron diseñados con versiones anteriores del Paywall Builder, si son paywalls con Remote Config, o si usas el [modo Observer](observer-vs-full-mode). - Para presentar **paywalls con Remote Config**, consulta [Renderizar paywalls diseñados con Remote Config](present-remote-config-paywalls). - Para presentar **paywalls en modo Observer**, consulta [Android - Presentar paywalls de Paywall Builder en modo Observer](android-present-paywall-builder-paywalls-in-observer-mode) ::: Para obtener el objeto `viewConfiguration` utilizado a continuación, consulta [Obtener paywalls de Paywall Builder y su configuración](android-get-pb-paywalls). Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, llama al método `AdaptyUI.getPaywallView()` o crea el `AdaptyPaywallView` directamente: ```kotlin showLineNumbers val paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, products, eventListener, insets, personalizedOfferResolver, tagResolver, timerResolver, ) ``` ```kotlin showLineNumbers val paywallView = AdaptyPaywallView(activity) // or retrieve it from xml ... with(paywallView) { showPaywall( viewConfiguration, products, eventListener, insets, personalizedOfferResolver, tagResolver, timerResolver, ) } ``` ```java showLineNumbers AdaptyPaywallView paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, products, eventListener, insets, personalizedOfferResolver, tagResolver, timerResolver ); ``` ```java showLineNumbers AdaptyPaywallView paywallView = new AdaptyPaywallView(activity); //añade a la jerarquía de vistas si es necesario, o recíbela desde xml ... paywallView.showPaywall(viewConfiguration, products, eventListener, insets, personalizedOfferResolver, tagResolver, timerResolver); ``` ```xml showLineNumbers ``` Después de crear la vista correctamente, puedes añadirla a la jerarquía de vistas y mostrarla en la pantalla del dispositivo. Si obtienes `AdaptyPaywallView` _sin_ llamar a `AdaptyUI.getPaywallView()`, también tendrás que llamar al método `.showPaywall()`. Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, utiliza esta función composable: ```kotlin showLineNumbers AdaptyPaywallScreen( viewConfiguration, products, eventListener, insets, personalizedOfferResolver, tagResolver, timerResolver, ) ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------------------------- | :------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **viewConfiguration** | obligatorio | Proporciona un objeto `AdaptyUI.LocalizedViewConfiguration` con los detalles visuales del paywall. Usa el método `Adapty.getViewConfiguration(paywall)` para cargarlo. Consulta el tema [Fetch the visual configuration of paywall](android-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) para más detalles. | | **products** | opcional | Proporciona un array de `AdaptyPaywallProduct` para optimizar el momento en que se muestran los productos en pantalla. Si se pasa `null`, AdaptyUI obtendrá automáticamente los productos necesarios. | | **eventListener** | opcional | Proporciona un `AdaptyUiEventListener` para observar los eventos del paywall. Se recomienda extender `AdaptyUiDefaultEventListener` para facilitar su uso. Consulta el tema [Handling paywall events](android-handling-events) para más detalles. | | **insets** | opcional |

Los insets son los espacios alrededor del paywall que evitan que los elementos interactivos queden ocultos detrás de las barras del sistema.

Por defecto: `UNSPECIFIED`, lo que significa que Adapty ajustará automáticamente los insets. Esto funciona bien para paywalls de borde a borde.

Si tu paywall no es de borde a borde, puede que quieras definir insets personalizados. Consulta la sección [Change paywall insets](android-present-paywalls#change-paywall-insets) más abajo para saber cómo hacerlo.

| | **personalizedOfferResolver** | opcional | Para indicar precios personalizados ([más información](https://developer.android.com/google/play/billing/integrate#personalized-price)), implementa `AdaptyUiPersonalizedOfferResolver` y proporciona tu propia lógica que mapee `AdaptyPaywallProduct` a `true` si el precio del producto es personalizado, o a `false` en caso contrario. | | **tagResolver** | opcional | Usa `AdaptyUiTagResolver` para resolver etiquetas personalizadas dentro del texto del paywall. Este resolver recibe un parámetro de etiqueta y lo resuelve a la cadena correspondiente. Consulta el tema [Custom tags in Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | | **timerResolver** | opcional | Pasa el resolver aquí si vas a usar funcionalidad de temporizador personalizado. | :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Cambiar los márgenes del paywall \{#change-paywall-insets\} Los márgenes son los espacios alrededor del paywall que evitan que los elementos interactivos queden ocultos detrás de las barras del sistema. Por defecto, Adapty ajusta automáticamente estos márgenes, lo que funciona perfectamente para paywalls de borde a borde. Si tu paywall no es de borde a borde, puede que quieras definir márgenes personalizados: - Si ni la barra de estado ni la barra de navegación se superponen con `AdaptyPaywallView`, usa `AdaptyPaywallInsets.NONE`. - Para configuraciones más personalizadas, como cuando tu paywall se superpone con la barra de estado superior pero no con la inferior, puedes establecer solo el `bottomInset` a `0`, como se muestra en el ejemplo a continuación: ```kotlin showLineNumbers //create extension function fun View.onReceiveSystemBarsInsets(action: (insets: Insets) -> Unit) { ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets -> val systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()) ViewCompat.setOnApplyWindowInsetsListener(this, null) action(systemBarInsets) insets } } //and then use it with the view paywallView.onReceiveSystemBarsInsets { insets -> val paywallInsets = AdaptyPaywallInsets.vertical(insets.top, 0) paywallView.showPaywall( viewConfiguration, products, eventListener, paywallInsets, personalizedOfferResolver, tagResolver, timerResolver, ) } ``` ```java showLineNumbers ... ViewCompat.setOnApplyWindowInsetsListener(paywallView, (view, insets) -> { Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()); ViewCompat.setOnApplyWindowInsetsListener(paywallView, null); AdaptyPaywallInsets paywallInsets = AdaptyPaywallInsets.of(systemBarInsets.top, 0); paywallView.showPaywall(paywall, products, viewConfiguration, paywallInsets, productTitleResolver); return insets; }); ``` ## Usar temporizador definido por el desarrollador \{#use-developer-defined-timer\} Para usar temporizadores definidos por el desarrollador en tu app, crea un objeto `timerResolver` —un diccionario o mapa que asocia temporizadores personalizados con los valores de cadena que los reemplazarán cuando se renderice el paywall. Aquí tienes un ejemplo: ```kotlin showLineNumbers ... val customTimers = mapOf( "CUSTOM_TIMER_NY" to Calendar.getInstance(TimeZone.getDefault()).apply { set(2025, 0, 1) }.time, // New Year 2025 ) val timerResolver = AdaptyUiTimerResolver { timerId -> customTimers.getOrElse(timerId, { Date(System.currentTimeMillis() + 3600 * 1000L) /* in 1 hour */ } ) } ``` ```java showLineNumbers ... Map customTimers = new HashMap<>(); customTimers.put( "CUSTOM_TIMER_NY", new Calendar.Builder().setTimeZone(TimeZone.getDefault()).setDate(2025, 0, 1).build().getTime() ); AdaptyUiTimerResolver timerResolver = new AdaptyUiTimerResolver() { @NonNull @Override public Date timerEndAtDate(@NonNull String timerId) { Date date = customTimers.get(timerId); return date != null ? date : new Date(System.currentTimeMillis() + 3600 * 1000L); /* en 1 hora */ } }; ``` En este ejemplo, `CUSTOM_TIMER_NY` es el **Timer ID** del temporizador definido por el desarrollador que configuraste en el Adapty dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente el temporizador con el valor correcto, como `13d 09h 03m 34s` (calculado como la hora de fin del temporizador, por ejemplo el Año Nuevo, menos la hora actual). ## Usa etiquetas personalizadas \{#use-custom-tags\} Para usar etiquetas personalizadas en tu app, crea un objeto `tagResolver`—un diccionario o mapa que asocia etiquetas personalizadas con los valores de cadena que las reemplazarán cuando se renderice el paywall. Aquí tienes un ejemplo: ```kotlin showLineNumbers val customTags = mapOf("USERNAME" to "John") val tagResolver = AdaptyUiTagResolver { tag -> customTags[tag] } ``` ```java showLineNumbers Map customTags = new HashMap<>(); customTags.put("USERNAME", "John"); AdaptyUiTagResolver tagResolver = customTags::get; ``` En este ejemplo, `USERNAME` es una etiqueta personalizada que introdujiste en el Adapty dashboard como ``. El `tagResolver` se encarga de que tu app reemplace dinámicamente esta etiqueta personalizada con el valor especificado, como `John`. Te recomendamos crear y rellenar el `tagResolver` justo antes de mostrar tu paywall. Una vez listo, pásalo al método de AdaptyUI que uses para presentar el paywall. ## Cambiar el color del indicador de carga del paywall \{#change-paywall-loading-indicator-color\} Puedes sobrescribir el color predeterminado del indicador de carga de la siguiente manera: ```xml showLineNumbers title = "XML" ``` --- # File: android-handle-paywall-actions --- --- title: "Responder a acciones de botones en el SDK de Android" description: "Gestiona las acciones de los botones del paywall en Android usando Adapty para una mejor monetización de la app." --- Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente: 1. Añade un [botón en el Paywall Builder](paywall-buttons) y asígnale una acción predefinida o crea un ID de acción personalizado. 2. Escribe código en tu app para gestionar cada acción que hayas asignado. Esta guía explica cómo gestionar acciones personalizadas y predefinidas en tu código. :::warning **Solo las compras, restauraciones, cierres de paywall y apertura de URLs se gestionan automáticamente.** Todas las demás acciones de botones requieren una implementación de respuesta adecuada en el código de la app. ::: ## Cerrar paywalls \{#close-paywalls\} Para añadir un botón que cierre tu paywall: 1. En el Paywall Builder, añade un botón y asígnale la acción **Close**. 2. En el código de tu app, implementa un manejador para la acción `close` que descarte el paywall. :::info En el SDK de Android, la acción `close` cierra el paywall por defecto. Sin embargo, puedes sobreescribir este comportamiento en tu código si lo necesitas. Por ejemplo, cerrar un paywall podría abrir otro. ::: ```kotlin override fun onActionPerformed(action: AdaptyUI.Action, context: Context) { when (action) { AdaptyUI.Action.Close -> (context as? Activity)?.onBackPressed() // default behavior } } ``` ## Abrir URLs desde paywalls \{#open-urls-from-paywalls\} :::tip Si quieres añadir un grupo de enlaces (por ejemplo, términos de uso y restauración de compras), añade un elemento **Link** en el Paywall Builder y gestiónalo de la misma forma que los botones con la acción **Open URL**. ::: Para añadir un botón que abra un enlace desde tu paywall (por ejemplo, **Términos de uso** o **Política de privacidad**): 1. En el Paywall Builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir. 2. En el código de tu app, implementa un manejador para la acción `openUrl` que abra la URL recibida en un navegador. :::info En el SDK de Android, la acción `openUrl` abre la URL por defecto. Sin embargo, puedes sobreescribir este comportamiento en tu código si lo necesitas. ::: ```kotlin override fun onActionPerformed(action: AdaptyUI.Action, context: Context) { when (action) { is AdaptyUI.Action.OpenUrl -> { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(action.url)) // default behavior context.startActivity(intent) } } } ``` ## Iniciar sesión en la app \{#log-into-the-app\} Para añadir un botón que permita a los usuarios iniciar sesión en tu app: 1. En el Paywall Builder, añade un botón y asígnale la acción **Login**. 2. En el código de tu app, implementa un manejador para la acción `login` que identifique a tu usuario. ```kotlin override fun onActionPerformed(action: AdaptyUI.Action, context: Context) { when (action) { AdaptyUI.Action.Login -> { val intent = Intent(context, LoginActivity::class.java) context.startActivity(intent) } } } ``` ## Gestionar acciones personalizadas \{#handle-custom-actions\} Para añadir un botón que gestione cualquier otra acción: 1. En el Paywall Builder, añade un botón, asígnale la acción **Custom** y asígnale un ID. 2. En el código de tu app, implementa un manejador para el ID de acción que hayas creado. Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall: ```kotlin override fun onActionPerformed(action: AdaptyUI.Action, context: Context) { when (action) { is AdaptyUI.Action.Custom -> { if (action.customId == "openNewPaywall") { // Display another paywall } } } } ``` --- # File: android-handling-events --- --- title: "Android - Gestionar eventos del paywall" description: "Gestiona eventos de suscripción en Android de forma eficiente con las herramientas de seguimiento de eventos de Adapty." --- :::important Esta guía cubre la gestión de eventos para compras, restauraciones, selección de productos y renderizado del paywall. También debes implementar la gestión de botones (cerrar el paywall, abrir enlaces, etc.). Consulta nuestra [guía sobre la gestión de acciones de botones](android-handle-paywall-actions) para más detalles. ::: Los paywalls configurados con el [Paywall Builder](adapty-paywall-builder) no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan algunos eventos a los que tu app puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.), así como notificaciones sobre acciones relacionadas con compras en el paywall. A continuación, aprende cómo responder a estos eventos. :::warning Esta guía es **exclusivamente para paywalls del nuevo Paywall Builder** que requieren el SDK de Adapty v3.0 o posterior. ::: :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: Si necesitas controlar o monitorizar los procesos que tienen lugar en la pantalla de compra, implementa los métodos de `AdaptyUiEventListener`. Si quieres mantener el comportamiento predeterminado en algunos casos, puedes extender `AdaptyUiDefaultEventListener` y sobreescribir solo los métodos que desees cambiar. A continuación se muestran los valores predeterminados de `AdaptyUiDefaultEventListener`. ### Eventos generados por el usuario \{#user-generated-events\} #### Selección de producto \{#product-selection\} Si se selecciona un producto para la compra (por el usuario o por el sistema), se invocará este método: ```kotlin showLineNumbers title="Kotlin" public override fun onProductSelected( product: AdaptyPaywallProduct, context: Context, ) {} ```
Ejemplo de evento (haz clic para expandir) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ```
#### Compra iniciada \{#started-purchase\} Si un usuario inicia el proceso de compra, se invocará este método: ```kotlin showLineNumbers title="Kotlin" public override fun onPurchaseStarted( product: AdaptyPaywallProduct, context: Context, ) {} ```
Ejemplo de evento (haz clic para expandir) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ```
Este método no se invocará en el modo Observer. Consulta el tema [Android - Presentar paywalls del Paywall Builder en modo Observer](android-present-paywall-builder-paywalls-in-observer-mode) para más detalles. #### Compra exitosa, cancelada o pendiente \{#successful-canceled-or-pending-purchase\} Si la compra se realiza correctamente, se invocará este método: ```kotlin showLineNumbers title="Kotlin" public override fun onPurchaseFinished( purchaseResult: AdaptyPurchaseResult, product: AdaptyPaywallProduct, context: Context, ) { if (purchaseResult !is AdaptyPurchaseResult.UserCanceled) context.getActivityOrNull()?.onBackPressed() } ```
Ejemplos de eventos (haz clic para expandir) ```javascript // Successful purchase { "purchaseResult": { "type": "Success", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } // Cancelled purchase { "purchaseResult": { "type": "UserCanceled" }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } // Pending purchase { "purchaseResult": { "type": "Pending" }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ```
En ese caso, recomendamos cerrar la pantalla. Este método no se invocará en el modo Observer. Consulta el tema [Android - Presentar paywalls del Paywall Builder en modo Observer](android-present-paywall-builder-paywalls-in-observer-mode) para más detalles. #### Compra fallida \{#failed-purchase\} Si una compra falla debido a un error, se invocará este método. Esto incluye errores de Google Play Billing (restricciones de pago, productos no válidos, fallos de red), fallos en la verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan `onPurchaseFinished` con un resultado cancelado, y los pagos pendientes no activan este método. ```kotlin showLineNumbers title="Kotlin" public override fun onPurchaseFailure( error: AdaptyError, product: AdaptyPaywallProduct, context: Context, ) {} ```
Ejemplo de evento (haz clic para expandir) ```javascript { "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" } }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ```
Este método no se invocará en el modo Observer. Consulta el tema [Android - Presentar paywalls del Paywall Builder en modo Observer](android-present-paywall-builder-paywalls-in-observer-mode) para más detalles. #### Navegación web de pago finalizada \{#finished-web-payment-navigation\} Este método se invoca tras un intento de abrir un [web paywall](web-paywall) para un producto específico. Esto incluye tanto intentos de navegación exitosos como fallidos: ```kotlin showLineNumbers title="Kotlin" public override fun onFinishWebPaymentNavigation( product: AdaptyPaywallProduct?, error: AdaptyError?, context: Context, ) {} ``` **Parámetros:** | Parámetro | Descripción | |:------------|:---------------------------------------------------------------------------------------------------| | **product** | Un `AdaptyPaywallProduct` para el que se abrió el web paywall. Puede ser `null`. | | **error** | Un objeto `AdaptyError` si la navegación al web paywall falló; `null` si la navegación fue exitosa. |
Ejemplos de eventos (haz clic para expandir) ```javascript // Successful navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": null } // Failed navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "web_navigation_failed", "message": "Failed to open web paywall", "details": { "underlyingError": "Browser unavailable" } } } ```
#### Restauración exitosa \{#successful-restore\} Si la restauración de una compra se realiza correctamente, se invocará este método: ```kotlin showLineNumbers title="Kotlin" public override fun onRestoreSuccess( profile: AdaptyProfile, context: Context, ) {} ```
Ejemplo de evento (haz clic para expandir) ```javascript { "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } ```
Recomendamos cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de la suscripción](android-listen-subscription-changes) para saber cómo comprobarlo. #### Restauración fallida \{#failed-restore\} Si `Adapty.restorePurchases()` falla, se invocará este método: ```kotlin showLineNumbers title="Kotlin" public override fun onRestoreFailure( error: AdaptyError, context: Context, ) {} ```
Ejemplo de evento (haz clic para expandir) ```javascript { "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } ```
#### Actualizar suscripción \{#upgrade-subscription\} Cuando un usuario intenta comprar una nueva suscripción mientras otra está activa, puedes controlar cómo debe gestionarse la nueva compra sobreescribiendo este método. Tienes dos opciones: 1. **Reemplazar la suscripción actual** por la nueva: ```kotlin showLineNumbers title="Kotlin" public override fun onAwaitingPurchaseParams( product: AdaptyPaywallProduct, context: Context, onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback, ): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked { onPurchaseParamsReceived( AdaptyPurchaseParameters.Builder() .withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...)) .build() ) return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked } ``` 2. **Mantener ambas suscripciones** (añadir la nueva por separado): ```kotlin showLineNumbers title="Kotlin" public override fun onAwaitingPurchaseParams( product: AdaptyPaywallProduct, context: Context, onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback, ): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked { onPurchaseParamsReceived(AdaptyPurchaseParameters.Empty) return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked } ``` :::note Si no sobreescribes este método, el comportamiento predeterminado es mantener ambas suscripciones activas (equivalente a usar `AdaptyPurchaseParameters.Empty`). ::: También puedes establecer parámetros de compra adicionales si es necesario: ```kotlin AdaptyPurchaseParameters.Builder() .withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...)) // opcional - para reemplazar la suscripción actual .withOfferPersonalized(true) // opcional - si se usan precios personalizados .build() ``` Si se compra una nueva suscripción mientras otra sigue activa, sobreescribe este método para reemplazar la actual por la nueva. Si la suscripción activa debe permanecer activa y la nueva se añade por separado, llama a `onSubscriptionUpdateParamsReceived(null)`: ```kotlin showLineNumbers title="Kotlin" public override fun onAwaitingSubscriptionUpdateParams( product: AdaptyPaywallProduct, context: Context, onSubscriptionUpdateParamsReceived: SubscriptionUpdateParamsCallback, ) { onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters(...)) } ```
Ejemplo de evento (haz clic para expandir) ```javascript { "product": { "vendorProductId": "premium_yearly", "localizedTitle": "Premium Yearly", "localizedDescription": "Premium subscription for 1 year", "localizedPrice": "$99.99", "price": 99.99, "currencyCode": "USD" }, "subscriptionUpdateParams": { "replacementMode": "with_time_proration" } } ```
### Obtención de datos y renderizado \{#data-fetching-and-rendering\} #### Errores al cargar productos \{#product-loading-errors\} Si no pasas los productos durante la inicialización, AdaptyUI recuperará los objetos necesarios del servidor por sí solo. Si esta operación falla, AdaptyUI notificará el error invocando este método: ```kotlin showLineNumbers title="Kotlin" public override fun onLoadingProductsFailure( error: AdaptyError, context: Context, ): Boolean = false ```
Ejemplo de evento (haz clic para expandir) ```javascript { "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } ```
Si devuelves `true`, AdaptyUI repetirá la solicitud en 2 segundos. #### Errores de renderizado \{#rendering-errors\} Si se produce un error durante el renderizado de la interfaz, se notificará llamando a este método: ```kotlin showLineNumbers title="Kotlin" public override fun onRenderingError( error: AdaptyError, context: Context, ) {} ```
Ejemplo de evento (haz clic para expandir) ```javascript { "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } ```
En una situación normal, estos errores no deberían producirse, así que si te encuentras con alguno, comunícanoslo. --- # File: android-use-fallback-paywalls --- --- title: "Android - Usar paywalls de respaldo" description: "Gestiona los casos en que los usuarios están sin conexión o los servidores de Adapty no están disponibles." --- :::warning Los paywalls de respaldo son compatibles con Android SDK v2.11 y versiones posteriores. ::: Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet. * **Si la aplicación no puede acceder a los servidores de Adapty:** Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding. * **Si la aplicación no puede acceder a internet:** Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar. :::important Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty. ::: ## Configuración \{#configuration\} 1. Mueve el archivo de configuración de respaldo al directorio `assets` o `res/raw` de tu proyecto Android. 2. Llama al método `.setFallback` **antes** de obtener el paywall o el onboarding de destino. ```kotlin showLineNumbers //if you put the 'android_fallback.json' file to the 'assets' directory val location = FileLocation.fromAsset("android_fallback.json") //or `FileLocation.fromAsset("/android_fallback.json")` if you placed it in a child folder of 'assets') //if you put the 'android_fallback.json' file to the 'res/raw' directory val location = FileLocation.fromResId(context, R.raw.android_fallback) //you can also pass a file URI val fileUri: Uri = //get Uri for the file with fallback paywalls val location = FileLocation.fromFileUri(fileUri) //pass the file location Adapty.setFallback(location, callback) ``` ```java showLineNumbers //if you put the 'android_fallback.json' file to the 'assets' directory FileLocation location = FileLocation.fromAsset("android_fallback.json"); //or `FileLocation.fromAsset("/android_fallback.json");` if you placed it in a child folder of 'assets') //if you put the 'android_fallback.json' file to the 'res/raw' directory FileLocation location = FileLocation.fromResId(context, R.raw.android_fallback); //you can also pass a file URI Uri fileUri = //get Uri for the file with fallback paywalls FileLocation location = FileLocation.fromFileUri(fileUri); //pass the file location Adapty.setFallback(location, callback); ``` Parámetros: | Parámetro | Descripción | | :----------- | :----------------------------------------------------------- | | **location** | El objeto [FileLocation](https://android.adapty.io/adapty/com.adapty.utils/-file-location/-companion/) para el archivo de configuración de respaldo | --- # File: android-localizations-and-locale-codes --- --- title: "Usar localizaciones y códigos de idioma en el SDK de Android" description: "Gestiona las localizaciones de tu app y los códigos de idioma para llegar a una audiencia global (Android)." --- ## Por qué es importante \{#why-this-is-important\} Hay varios escenarios en los que los códigos de idioma entran en juego; por ejemplo, cuando intentas obtener el paywall correcto para la localización actual de tu app. Como los códigos de idioma son complejos y pueden variar de una plataforma a otra, nos basamos en un estándar interno para todas las plataformas que admitimos. Sin embargo, precisamente por esa complejidad, es muy importante que entiendas exactamente qué le estás enviando a nuestro servidor para obtener la localización correcta y qué ocurre después, de modo que siempre recibas lo que esperas. ## Estándar de códigos de idioma en Adapty \{#locale-code-standard-at-adapty\} Para los códigos de idioma, Adapty utiliza una versión ligeramente modificada del [estándar BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag): cada código se compone de subetiquetas en minúsculas separadas por guiones. Algunos ejemplos: `en` (inglés), `pt-br` (portugués de Brasil), `zh` (chino simplificado), `zh-hant` (chino tradicional). ## Coincidencia de códigos de idioma \{#locale-code-matching\} Cuando Adapty recibe una llamada del SDK con el código de idioma y empieza a buscar la localización correspondiente de un paywall, ocurre lo siguiente: 1. La cadena de idioma entrante se convierte a minúsculas y todos los guiones bajos (`_`) se reemplazan por guiones (`-`). 2. A continuación, buscamos la localización cuyo código de idioma coincida exactamente. 3. Si no se encuentra ninguna coincidencia, tomamos la subcadena antes del primer guion (`pt` para `pt-br`) y buscamos la localización que coincida. 4. Si tampoco se encuentra ninguna coincidencia, devolvemos la localización predeterminada `en`. De este modo, un dispositivo iOS que envíe `'pt_BR'`, un dispositivo Android que envíe `pt-BR` y otro dispositivo que envíe `pt-br` obtendrán el mismo resultado. ## Implementar localizaciones: la forma recomendada \{#implementing-localizations-recommended-way\} Si te estás preguntando sobre las localizaciones, probablemente ya estés trabajando con los archivos de cadenas localizadas de tu proyecto. En ese caso, te recomendamos añadir un par clave-valor con el código de idioma de Adapty correspondiente en cada uno de esos archivos. Luego, extrae el valor de esa clave al llamar a nuestro SDK, así: ```kotlin showLineNumbers // 1. Modify your strings.xml files /* strings.xml - Spanish */ es /* strings.xml - Portuguese (Brazil) */ pt-br // 2. Extract and use the locale code val localeCode = context.getString(R.string.adapty_paywalls_locale) // pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method ``` Así te aseguras de tener un control total sobre qué localización se recuperará para cada usuario de tu app. ## Implementar localizaciones: la otra forma \{#implementing-localizations-the-other-way\} Puedes obtener resultados similares (aunque no idénticos) sin definir explícitamente códigos de idioma para cada localización. Esto implica extraer el código de idioma de otros objetos que proporciona tu plataforma, así: ```kotlin showLineNumbers val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) context.resources.configuration.locales[0] else context.resources.configuration.locale val localeCode = locale.toLanguageTag() // pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method ``` Ten en cuenta que no recomendamos este enfoque, ya que es difícil predecir exactamente qué recibirá el servidor de Adapty. Si aun así decides usarlo, asegúrate de haber cubierto todos los casos de uso relevantes. --- # File: android-web-paywall --- --- title: "Implementar web paywalls en Android SDK" description: "Configura un web paywall para cobrar sin las comisiones y revisiones de Play Store." --- :::important Antes de empezar, asegúrate de haber [configurado tu web paywall en el dashboard](web-paywall) e instalado Adapty SDK versión 3.15 o posterior. ::: ## Abrir web paywalls \{#open-web-paywalls\} Si estás trabajando con un paywall que desarrollaste tú mismo, necesitas gestionar los web paywalls mediante el método del SDK. El método `.openWebPaywall`: 1. Genera una URL única que permite a Adapty vincular un paywall concreto mostrado a un usuario específico con la página web a la que es redirigido. 2. Detecta cuando tus usuarios vuelven a la app y luego solicita `.getProfile` en intervalos cortos para determinar si los derechos de acceso del perfil se han actualizado. De esta forma, si el pago fue exitoso y los derechos de acceso se actualizaron, la suscripción se activa en la app casi de inmediato. :::note Cuando los usuarios vuelvan a la app, actualiza la interfaz para reflejar los cambios del perfil. Adapty recibirá y procesará los eventos de actualización del perfil. ::: ```kotlin showLineNumbers Adapty.openWebPaywall( activity = activity, product = product, ) { error -> if (error == null) { // the web paywall was opened successfully } else { // handle the error } } ``` :::note Existen dos versiones del método `openWebPaywall`: 1. `openWebPaywall(product)`, que genera las URLs por paywall y también añade los datos del producto a las URLs. 2. `openWebPaywall(paywall)`, que genera las URLs por paywall sin añadir los datos del producto a las URLs. Úsalo cuando tus productos en el paywall de Adapty sean distintos a los del web paywall. ::: ## Abrir web paywalls en un navegador in-app \{#open-web-paywalls-in-an-in-app-browser\} Por defecto, los web paywalls se abren en el navegador externo. Para ofrecer una experiencia de usuario fluida, puedes abrir los web paywalls en un navegador in-app. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin cambiar de app. Para habilitarlo, establece el parámetro `presentation` en `AdaptyWebPresentation.InAppBrowser`: ```kotlin showLineNumbers Adapty.openWebPaywall( activity = activity, product = product, presentation = AdaptyWebPresentation.InAppBrowser, ) { error -> if (error == null) { // the web paywall was opened successfully } else { // handle the error val adaptyError = error } } ``` --- # File: android-troubleshoot-paywall-builder --- --- title: "Solucionar problemas del Paywall Builder en el SDK de Android" description: "Solucionar problemas del Paywall Builder en el SDK de Android" --- Esta guía te ayuda a resolver problemas comunes al usar paywalls diseñados en el Paywall Builder de Adapty en el SDK de Android. ## La obtención de la configuración del paywall falla \{#getting-a-paywall-configuration-fails\} **Problema**: El método `getViewConfiguration` no puede recuperar la configuración del paywall. **Causa**: El paywall no está habilitado para mostrarse en el dispositivo en el Paywall Builder. **Solución**: Activa el interruptor **Show on device** en el Paywall Builder. ## El número de vistas del paywall es demasiado alto \{#the-paywall-view-number-is-too-big\} **Problema**: El contador de vistas del paywall muestra el doble del número esperado. **Causa**: Es posible que estés llamando a `logShowPaywall` en tu código, lo que duplica el contador de vistas si estás usando el Paywall Builder. Para los paywalls diseñados con el Paywall Builder, las analíticas se registran automáticamente, por lo que no necesitas usar este método. **Solución**: Asegúrate de no llamar a `logShowPaywall` en tu código si estás usando el Paywall Builder. ## Otros problemas \{#other-issues\} **Problema**: Estás experimentando otros problemas relacionados con el Paywall Builder que no se tratan más arriba. **Solución**: Migra el SDK a la versión más reciente usando las [guías de migración](android-sdk-migration-guides) si es necesario. Muchos problemas se resuelven en versiones más recientes del SDK. --- # File: android-quickstart-manual --- --- title: "Habilitar compras en tu paywall personalizado en Android SDK" description: "Integra el SDK de Adapty en tus paywalls personalizados de Android para habilitar las compras in-app." --- Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall, mientras el SDK de Adapty obtiene los productos, gestiona las nuevas compras y restaura las anteriores. :::important **Esta guía es para desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Adapty Paywall Builder](android-quickstart-paywalls). Con Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compras automáticamente y puedes probar distintos diseños sin volver a publicar tu app. ::: ## Antes de empezar \{#before-you-start\} ### Configurar productos \{#set-up-products\} 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) - [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de obtener productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 diferentes paywalls a distintos usuarios. Asegúrate de entender estos conceptos aunque trabajes con tu paywall personalizado. Básicamente, son tu forma de gestionar los productos que vendes en tu app. Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite obtener tus productos. Para entender qué debes hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart). ### Gestionar usuarios \{#manage-users\} Puedes trabajar con o sin autenticación de backend en tu lado. Sin embargo, el SDK de Adapty gestiona de forma diferente a los usuarios anónimos e identificados. Lee la [guía de inicio rápido de identificación](android-quickstart-identify) para entender las particularidades y asegurarte de trabajar correctamente con los usuarios. ## Paso 1. Obtener productos \{#step-1-get-products\} Para obtener los productos de tu paywall personalizado, necesitas: 1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`. 2. Obtener el array de productos para este paywall usando el método `getPaywallProducts`. ```kotlin showLineNumbers fun loadPaywall() { Adapty.getPaywall("YOUR_PLACEMENT_ID") { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value Adapty.getPaywallProducts(paywall) { productResult -> when (productResult) { is AdaptyResult.Success -> { val products = productResult.value // Use products to build your custom paywall UI } is AdaptyResult.Error -> { val error = productResult.error // Handle the error } } } } is AdaptyResult.Error -> { val error = result.error // Handle the error } } } } ``` ```java showLineNumbers public void loadPaywall() { Adapty.getPaywall("YOUR_PLACEMENT_ID", result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue(); Adapty.getPaywallProducts(paywall, productResult -> { if (productResult instanceof AdaptyResult.Success) { List products = ((AdaptyResult.Success>) productResult).getValue(); // Use products to build your custom paywall UI } else if (productResult instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) productResult).getError(); // Handle the error } }); } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // Handle the error } }); } ``` ## Paso 2. Aceptar compras \{#step-2-accept-purchases\} Cuando un usuario pulsa un producto en tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado. ```kotlin showLineNumbers fun purchaseProduct(activity: Activity, product: AdaptyPaywallProduct) { Adapty.makePurchase(activity, product) { result -> when (result) { is AdaptyResult.Success -> { when (val purchaseResult = result.value) { is AdaptyPurchaseResult.Success -> { val profile = purchaseResult.profile // Purchase successful, profile updated } is AdaptyPurchaseResult.UserCanceled -> { // User canceled the purchase } is AdaptyPurchaseResult.Pending -> { // Purchase is pending (e.g., user will pay offline with cash) } } } is AdaptyResult.Error -> { val error = result.error // Handle the error } } } } ``` ```java showLineNumbers public void purchaseProduct(Activity activity, AdaptyPaywallProduct product) { Adapty.makePurchase(activity, product, null, result -> { if (result instanceof AdaptyResult.Success) { AdaptyPurchaseResult purchaseResult = ((AdaptyResult.Success) result).getValue(); if (purchaseResult instanceof AdaptyPurchaseResult.Success) { AdaptyProfile profile = ((AdaptyPurchaseResult.Success) purchaseResult).getProfile(); // Purchase successful, profile updated } else if (purchaseResult instanceof AdaptyPurchaseResult.UserCanceled) { // User canceled the purchase } else if (purchaseResult instanceof AdaptyPurchaseResult.Pending) { // Purchase is pending (e.g., user will pay offline with cash) } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // Handle the error } }); } ``` ## Paso 3. Restaurar compras \{#step-3-restore-purchases\} Google Play y otras tiendas de aplicaciones exigen que todas las apps con suscripciones ofrezcan una forma de que los usuarios puedan restaurar sus compras. Llama al método `restorePurchases` cuando el usuario pulse el botón de restaurar. Esto sincronizará su historial de compras con Adapty y devolverá el perfil actualizado. ```kotlin showLineNumbers fun restorePurchases() { Adapty.restorePurchases { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value // Restore successful, profile updated } is AdaptyResult.Error -> { val error = result.error // Handle the error } } } } ``` ```java showLineNumbers public void restorePurchases() { Adapty.restorePurchases(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success) result).getValue(); // Restore successful, profile updated } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // Handle the error } }); } ``` ## Siguientes pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; ¿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 ya está listo para mostrarse en la app. [Prueba tus compras en Google Play Store](testing-on-android) para asegurarte de que puedes completar una compra de prueba desde el paywall. Para ver cómo funciona en una implementación lista para producción, consulta [ProductListFragment.kt](https://github.com/adaptyteam/AdaptySDK-Android/blob/master/app/src/main/java/com/adapty/example/ProductListFragment.kt) en nuestra app de ejemplo, que muestra el manejo de compras con gestión adecuada de errores, retroalimentación en la interfaz y administración de suscripciones. A continuación, [comprueba si los usuarios han completado su compra](android-check-subscription-status) para determinar si mostrar el paywall o conceder acceso a las funciones de pago. --- # File: fetch-paywalls-and-products-android --- --- title: "Obtener paywalls y productos para paywalls de Remote Config en el SDK de Android" description: "Obtén paywalls y productos en el SDK de Android de Adapty para mejorar la monetización de usuarios." --- Antes de mostrar paywalls de Remote Config y personalizadas, necesitas obtener la información sobre ellas. Ten en cuenta que este tema hace referencia a paywalls de Remote Config y personalizadas. Para obtener orientación sobre cómo obtener paywalls configuradas con Paywall Builder, consulta [Obtener paywalls del Paywall Builder y su configuración](android-get-pb-paywalls). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. :::
Antes de empezar a obtener paywalls y productos en tu app (haz clic para expandir) 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en tu paywall](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en el placement](create-placement) en el Adapty Dashboard. 4. [Instala el SDK de Adapty](sdk-installation-android) en tu app.
## Obtener información del paywall \{#fetch-paywall-information\} En Adapty, un [producto](product) es una combinación de productos tanto de App Store como de Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos dentro de placements específicos de tu app. Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`. :::important **No escribas los IDs de producto en el código.** El único ID que debes escribir directamente en el código es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe gestionar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana devuelve tres, debes mostrarlos todos sin modificar el código. ::: ```kotlin showLineNumbers Adapty.getPaywall("YOUR_PLACEMENT_ID", locale = "en") { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value // the requested paywall } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.getPaywall("YOUR_PLACEMENT_ID", "en", result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue(); // the requested paywall } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** |

opcional

por defecto: `en`

|

El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto de una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.

Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.

Consulta [Localizaciones y códigos de idioma](android-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos utilizarlos.

| | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |

Por defecto, el SDK intentará cargar datos del servidor y devolverá los datos en caché en caso de error. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.

Sin embargo, si crees que tus usuarios tienen una conexión inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.

Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.

El SDK de Adapty almacena los paywalls en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](android-use-fallback-paywalls). También usamos CDN para obtener paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.

| | **loadTimeout** | por defecto: 5 seg |

Este valor limita el tiempo de espera de este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el respaldo local.

Ten en cuenta que en casos excepcionales este método puede agotar el tiempo de espera un poco más tarde de lo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.

| ¡No escribas los IDs de producto en el código! Como los paywalls se configuran de forma remota, los productos disponibles, el número de productos y las ofertas especiales (como pruebas gratuitas) pueden cambiar con el tiempo. Asegúrate de que tu código gestione estos escenarios. Por ejemplo, si inicialmente obtienes 2 productos, tu app debería mostrar esos 2 productos. Sin embargo, si más adelante obtienes 3 productos, tu app debería mostrar los 3 sin necesidad de cambios en el código. Lo único que debes escribir directamente en el código es el ID del placement. Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :----------- | | Paywall | Un objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/) con: una lista de IDs de producto, el identificador del paywall, Remote Config y varias otras propiedades. | ## Obtener productos \{#fetch-products\} Una vez que tienes el paywall, puedes consultar el array de productos que le corresponde: ```kotlin showLineNumbers Adapty.getPaywallProducts(paywall) { result -> when (result) { is AdaptyResult.Success -> { val products = result.value // the requested products } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.getPaywallProducts(paywall, result -> { if (result instanceof AdaptyResult.Success) { List products = ((AdaptyResult.Success>) result).getValue(); // the requested products } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :----------- | | Products | Lista de objetos [`AdaptyPaywallProduct`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall-product/) con: identificador del producto, nombre del producto, precio, moneda, duración de la suscripción y varias otras propiedades. | Al implementar tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall-product/). A continuación se muestran las propiedades más utilizadas, pero consulta el documento enlazado para obtener detalles completos sobre todas las propiedades disponibles. | Propiedad | Descripción | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | Para mostrar el título del producto, usa `product.localizedTitle`. Ten en cuenta que la localización se basa en el país de la store seleccionado por el usuario, no en el idioma del dispositivo. | | **Price** | Para mostrar una versión localizada del precio, usa `product.price.localizedString`. Esta localización se basa en la información de idioma del dispositivo. También puedes acceder al precio como número usando `product.price.amount`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda asociado, usa `product.price.currencySymbol`. | | **Subscription Period** | Para mostrar el período (p. ej., semana, mes, año, etc.), usa `product.subscriptionDetails?.localizedSubscriptionPeriod`. Esta localización se basa en el idioma del dispositivo. Para obtener el período de suscripción de forma programática, usa `product.subscriptionDetails?.subscriptionPeriod`. Desde ahí puedes acceder al enum `unit` para obtener la duración (es decir, DAY, WEEK, MONTH, YEAR, o UNKNOWN). El valor `numberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral, verás `MONTH` en la propiedad unit y `3` en la propiedad numberOfUnits. | | **Introductory Offer** | Para mostrar un distintivo u otro indicador de que una suscripción incluye una oferta introductoria, consulta la propiedad `product.subscriptionDetails?.introductoryOfferPhases`. Es una lista que puede contener hasta dos fases de descuento: la fase de prueba gratuita y la fase de precio introductorio. Dentro de cada objeto de fase están las siguientes propiedades útiles:
• `paymentMode`: un enum con los valores `FREE_TRIAL`, `PAY_AS_YOU_GO`, `PAY_UPFRONT` y `UNKNOWN`. Las pruebas gratuitas serán del tipo `FREE_TRIAL`.
• `price`: el precio con descuento como número. Para las pruebas gratuitas, busca `0` aquí.
• `localizedNumberOfPeriods`: una cadena localizada usando el idioma del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `3 days` en este campo.
• `subscriptionPeriod`: alternativamente, puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que la sección anterior describe.
• `localizedSubscriptionPeriod`: un período de suscripción formateado del descuento para el idioma del usuario. | ## Acelera la obtención de paywalls con el paywall de audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\} Normalmente, los paywalls se obtienen casi al instante, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos donde tienes numerosas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseado. En estas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún paywall. Para solucionar esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener información del paywall](fetch-paywalls-and-products-android#fetch-paywall-information) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar diferentes paywalls para distintas versiones de la app (actual y futuras), puede que encuentres problemas. Tendrás que diseñar paywalls que sean compatibles con la versión actual (legacy) o aceptar que los usuarios con la versión actual (legacy) puedan encontrar problemas con paywalls que no se renderizan. - **Pérdida de targeting**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes el targeting personalizado (incluido el basado en países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención de paywalls más rápida, usa el método `getPaywallForDefaultAudience` tal como se indica a continuación. De lo contrario, sigue usando el método `getPaywall` descrito [anteriormente](fetch-paywalls-and-products-android#fetch-paywall-information). ::: ```kotlin showLineNumbers Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", locale = "en") { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value // the requested paywall } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue(); // the requested paywall } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` :::note El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.3 del SDK de Android. ::: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** |

opcional

por defecto: `en`

|

El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto de una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.

Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.

Consulta [Localizaciones y códigos de idioma](android-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos utilizarlos.

| | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |

Por defecto, el SDK intentará cargar datos del servidor y devolverá los datos en caché en caso de error. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.

Sin embargo, si crees que tus usuarios tienen una conexión inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.

Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.

| --- # File: present-remote-config-paywalls-android --- --- title: "Renderizar un paywall diseñado con Remote Config en Android SDK" description: "Descubre cómo presentar paywalls de Remote Config en el SDK de Android de Adapty para personalizar la experiencia del usuario." --- Si has personalizado un paywall usando Remote Config, tendrás que implementar el renderizado en el código de tu app móvil para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú controlas qué se incluye y cómo se ve tu paywall. Te proporcionamos un método para obtener la configuración remota, dándote autonomía para mostrar tu paywall personalizado configurado mediante Remote Config. ## Obtener el Remote Config del paywall y presentarlo \{#get-paywall-remote-config-and-present-it\} Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores necesarios. ```kotlin showLineNumbers Adapty.getPaywall("YOUR_PLACEMENT_ID") { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value val headerText = paywall.remoteConfig?.dataMap?.get("header_text") as? String } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.getPaywall("YOUR_PLACEMENT_ID", result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue(); AdaptyPaywall.RemoteConfig remoteConfig = paywall.getRemoteConfig(); if (remoteConfig != null) { if (remoteConfig.getDataMap().get("header_text") instanceof String) { String headerText = (String) remoteConfig.getDataMap().get("header_text"); } } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` En este punto, una vez que hayas recibido todos los valores necesarios, es el momento de renderizarlos y ensamblarlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a distintas pantallas y orientaciones de teléfonos móviles, ofreciendo una experiencia fluida y cómoda en todos los dispositivos. :::warning Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls-android#track-paywall-view-events) tal como se describe a continuación, para que Adapty Analytics capture la información de embudos y pruebas A/B. ::: Una vez que hayas terminado de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.makePurchase()` con el producto de tu paywall. Para más detalles sobre el método `.makePurchase()`, consulta [Realizar compras](android-making-purchases). Te recomendamos [crear un paywall de respaldo llamado fallback paywall](android-use-fallback-paywalls). Este paywall de respaldo se mostrará al usuario cuando no haya conexión a internet ni caché disponible, garantizando una experiencia fluida incluso en esas situaciones. ## Registrar eventos de visualización del paywall \{#track-paywall-view-events\} Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque los datos de compras se recopilan automáticamente, registrar las visualizaciones de paywalls requiere tu intervención, ya que solo tú sabes cuándo un usuario ve un paywall. Para registrar un evento de visualización de paywall, simplemente llama a `.logShowPaywall(paywall)`, y se reflejará en las métricas de tu paywall en embudos y pruebas A/B. :::important No es necesario llamar a `.logShowPaywall(paywall)` si estás mostrando paywalls creados en el [Paywall Builder](adapty-paywall-builder). ::: ```kotlin showLineNumbers Adapty.logShowPaywall(paywall) ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :------- |:------------------------------------------------------------------------------------------------------------| | **paywall** | obligatorio | Un objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | --- # File: android-making-purchases --- --- title: "Realizar compras in-app en la app para Android SDK" description: "Guía para gestionar compras in-app y suscripciones con Adapty." --- Mostrar paywalls en tu app es un paso esencial para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, con solo presentar estos paywalls es suficiente para gestionar las compras únicamente si usas [Paywall Builder](adapty-paywall-builder) para personalizar tus paywalls. Si no usas el Paywall Builder, debes utilizar un método independiente llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método es la puerta de entrada para que los usuarios interactúen con los paywalls y procedan con sus transacciones. Si tu paywall tiene una oferta promocional activa para el producto que el usuario intenta comprar, Adapty la aplicará automáticamente en el momento de la compra. :::warning Ten en cuenta que la oferta introductoria solo se aplicará automáticamente si usas paywalls configurados con el Paywall Builder. En otros casos, deberás [verificar la elegibilidad del usuario para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Saltarte este paso puede provocar que tu app sea rechazada durante la publicación. Además, podría generar cargos al precio completo a usuarios que son elegibles para una oferta introductoria. ::: Asegúrate de haber completado la [configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras. ## Realizar una compra \{#make-purchase\} :::note **¿Usas [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente: puedes saltarte este paso. **¿Buscas una guía paso a paso?** Consulta la [guía de inicio rápido](android-implement-paywalls-manually) para instrucciones de implementación completas con todo el contexto. ::: ```kotlin showLineNumbers Adapty.makePurchase(activity, product, null) { result -> when (result) { is AdaptyResult.Success -> { when (val purchaseResult = result.value) { is AdaptyPurchaseResult.Success -> { val profile = purchaseResult.profile if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { // Grant access to the paid features } } is AdaptyPurchaseResult.UserCanceled -> { // Handle the case where the user canceled the purchase } is AdaptyPurchaseResult.Pending -> { // Handle deferred purchases (e.g., the user will pay offline with cash) } } } is AdaptyResult.Error -> { val error = result.error // Handle the error } } } ``` ```java showLineNumbers Adapty.makePurchase(activity, product, null, result -> { if (result instanceof AdaptyResult.Success) { AdaptyPurchaseResult purchaseResult = ((AdaptyResult.Success) result).getValue(); if (purchaseResult instanceof AdaptyPurchaseResult.Success) { AdaptyProfile profile = ((AdaptyPurchaseResult.Success) purchaseResult).getProfile(); AdaptyProfile.AccessLevel premium = profile.getAccessLevels().get("YOUR_ACCESS_LEVEL"); if (premium != null && premium.isActive()) { // Grant access to the paid features } } else if (purchaseResult instanceof AdaptyPurchaseResult.UserCanceled) { // Handle the case where the user canceled the purchase } else if (purchaseResult instanceof AdaptyPurchaseResult.Pending) { // Handle deferred purchases (e.g., the user will pay offline with cash) } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // Handle the error } }); ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :-------- | :-------------------------------------------------------------------------------------------------- | | **Product** | requerido | Un objeto [`AdaptyPaywallProduct`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall-product/) obtenido del paywall. | Parámetros de la respuesta: | Parámetro | Descripción | |-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** |

Si la solicitud se realizó correctamente, la respuesta contiene este objeto. Un objeto [AdaptyProfile](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/) proporciona información completa sobre los niveles de acceso, suscripciones y compras no recurrentes de un usuario dentro de la app.

Comprueba el estado del nivel de acceso para determinar si el usuario tiene el acceso requerido a la app.

| :::warning **Nota:** si aún usas una versión de StoreKit de Apple inferior a v2.0 y una versión del SDK de Adapty inferior a v.2.9.0, debes proporcionar el [secreto compartido de App Store de Apple](app-store-connection-configuration#step-5-enter-app-store-shared-secret). Apple ha deprecado este método. ::: ## Cambiar la suscripción al realizar una compra \{#change-subscription-when-making-a-purchase\} Cuando un usuario elige una nueva suscripción en lugar de renovar la actual, el funcionamiento depende del store. En Google Play, la suscripción no se actualiza automáticamente. Tendrás que gestionar el cambio en el código de tu app tal como se describe a continuación. Para reemplazar una suscripción por otra en Android, llama al método `.makePurchase()` con el parámetro adicional: ```kotlin showLineNumbers Adapty.makePurchase( activity, product, AdaptyPurchaseParameters.Builder() .withSubscriptionUpdateParams(subscriptionUpdateParams) .build() ) { result -> when (result) { is AdaptyResult.Success -> { when (val purchaseResult = result.value) { is AdaptyPurchaseResult.Success -> { val profile = purchaseResult.profile // successful cross-grade } is AdaptyPurchaseResult.UserCanceled -> { // user canceled the purchase flow } is AdaptyPurchaseResult.Pending -> { // the purchase has not been finished yet, e.g. user will pay offline by cash } } } is AdaptyResult.Error -> { val error = result.error // Handle the error } } } ``` Parámetro adicional de la solicitud: | Parámetro | Presencia | Descripción | | :--------------------------- | :-------- | :----------------------------------------------------------- | | **subscriptionUpdateParams** | requerido | un objeto [`AdaptySubscriptionUpdateParameters`](https://android.adapty.io/adapty/com.adapty.models/-adapty-subscription-update-parameters/). | ```java showLineNumbers Adapty.makePurchase( activity, product, new AdaptyPurchaseParameters.Builder() .withSubscriptionUpdateParams(subscriptionUpdateParams) .build(), result -> { if (result instanceof AdaptyResult.Success) { AdaptyPurchaseResult purchaseResult = ((AdaptyResult.Success) result).getValue(); if (purchaseResult instanceof AdaptyPurchaseResult.Success) { AdaptyProfile profile = ((AdaptyPurchaseResult.Success) purchaseResult).getProfile(); // successful cross-grade } else if (purchaseResult instanceof AdaptyPurchaseResult.UserCanceled) { // user canceled the purchase flow } else if (purchaseResult instanceof AdaptyPurchaseResult.Pending) { // the purchase has not been finished yet, e.g. user will pay offline by cash } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // Handle the error } }); ``` Parámetro adicional de la solicitud: | Parámetro | Presencia | Descripción | | :--------------------------- | :-------- | :----------------------------------------------------------- | | **subscriptionUpdateParams** | requerido | un objeto [`AdaptySubscriptionUpdateParameters`](https://android.adapty.io/adapty/com.adapty.models/-adapty-subscription-update-parameters/). | Puedes leer más sobre las suscripciones y los modos de reemplazo en la documentación para desarrolladores de Google: - [Acerca de los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-modes) - [Recomendaciones de Google sobre los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-recommendations) - Modo de reemplazo [`CHARGE_PRORATED_PRICE`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#CHARGE_PRORATED_PRICE()). Nota: este método solo está disponible para actualizaciones de suscripción. No se admiten degradaciones. - Modo de reemplazo [`DEFERRED`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#DEFERRED()). Nota: el cambio real de suscripción solo se producirá cuando finalice el período de facturación actual. ### Gestionar planes prepagados \{#manage-prepaid-plans\} Si los usuarios de tu app pueden adquirir [planes prepagados](https://developer.android.com/google/play/billing/subscriptions#prepaid-plans) (por ejemplo, comprar una suscripción no renovable por varios meses), puedes habilitar las [transacciones pendientes](https://developer.android.com/google/play/billing/subscriptions#pending) para planes prepagados. ```kotlin showLineNumbers AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withEnablePendingPrepaidPlans(true) .build() ``` ```java showLineNumbers new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withEnablePendingPrepaidPlans(true) .build(); ``` --- # File: android-restore-purchase --- --- title: "Restaurar compras en una app para Android con el SDK" description: "Aprende cómo restaurar compras en Adapty para garantizar una experiencia de usuario fluida." --- Restaurar compras es una función que permite a los usuarios recuperar el acceso a contenido adquirido previamente, como suscripciones o compras in-app, sin que se les vuelva a cobrar. Esta función es especialmente útil para usuarios que han desinstalado y reinstalado la app o que han cambiado de dispositivo y quieren acceder a su contenido sin pagar de nuevo. :::note En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin que tengas que añadir código adicional. Si es tu caso, puedes saltarte este paso. ::: Para restaurar una compra cuando no usas [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`: ```kotlin showLineNumbers Adapty.restorePurchases { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { // successful access restore } } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.restorePurchases(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success) result).getValue(); if (profile != null) { AdaptyProfile.AccessLevel premium = profile.getAccessLevels().get("YOUR_ACCESS_LEVEL"); if (premium != null && premium.isActive()) { // successful access restore } } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` Parámetros de respuesta: | Parámetro | Descripción | |---------|-----------| | **Profile** |

Un objeto [`AdaptyProfile`](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/). Este modelo contiene información sobre los niveles de acceso, suscripciones y compras únicas.

Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app.

| :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: implement-observer-mode-android --- --- title: "Implementar el modo Observer en el SDK de Android" description: "Implementa el modo Observer en Adapty para rastrear eventos de suscripción de usuarios en el SDK de Android." --- Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analíticas. Si esto cubre tus necesidades, solo tienes que: 1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. Sigue las instrucciones de configuración para [Android](sdk-installation-android#activate-adapty-module-of-adapty-sdk). 2. [Reportar transacciones](report-transactions-observer-mode-android) desde tu infraestructura de compras existente a Adapty. ## Configuración del modo Observer \{#observer-mode-setup\} Activa el modo Observer si gestionas las compras y el estado de la suscripción por tu cuenta y usas Adapty para enviar eventos de suscripción y analíticas. :::important Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlas tú mismo. ::: ```kotlin showLineNumbers class MyApplication : Application() { override fun onCreate() { super.onCreate() Adapty.activate( applicationContext, AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withObserverMode(true) //default false .build() ) } ``` ```java showLineNumbers public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Adapty.activate( applicationContext, new AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withObserverMode(true) //default false .build() ); } ``` Parámetros: | Parámetro | Descripción | | --------------------------- | ------------------------------------------------------------ | | observerMode | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor por defecto es `false`. | ## Usar los paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\} Si también quieres usar los paywalls y las funciones de pruebas A/B de Adapty, puedes hacerlo, pero requiere una configuración adicional en el modo Observer. Esto es lo que necesitas hacer además de los pasos anteriores: 1. Muestra los paywalls de la forma habitual para [paywalls con Remote Config](present-remote-config-paywalls-android). Para los paywalls del Paywall Builder, sigue las guías de configuración específicas para [Android](android-present-paywall-builder-paywalls-in-observer-mode). 3. [Asocia los paywalls](report-transactions-observer-mode-android) con las transacciones de compra. --- # File: report-transactions-observer-mode-android --- --- title: "Reportar transacciones en Observer Mode en el SDK de Android" description: "Reporta transacciones de compra en el Observer Mode de Adapty para obtener información de usuario y seguimiento de ingresos en el SDK de Android." --- En Observer Mode, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Debes reportar las transacciones desde tu app store. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` para reportar explícitamente cada transacción y que Adapty la reconozca. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra con el paywall que la generó, garantizando análisis precisos del paywall. ```kotlin showLineNumbers val transactionInfo = TransactionInfo.fromPurchase(purchase) Adapty.reportTransaction(transactionInfo, variationId) { result -> if (result is AdaptyResult.Success) { // success } } ``` Parámetros: | Parámetro | Presencia | Descripción | | --------------- | --------- | ------------------------------------------------------------ | | transactionInfo | requerido | El TransactionInfo de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | ```java showLineNumbers TransactionInfo transactionInfo = TransactionInfo.fromPurchase(purchase); Adapty.reportTransaction(transactionInfo, variationId, result -> { if (result instanceof AdaptyResult.Success) { // success } }); ``` Parámetros: | Parámetro | Presencia | Descripción | | --------------- | --------- | ------------------------------------------------------------ | | transactionInfo | requerido | El TransactionInfo de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | En Observer Mode, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Debes reportar las transacciones desde tu app store o restaurarlas. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `restorePurchases` para reportar la transacción a Adapty. :::warning **¡No omitas la restauración de compras!** Si no llamas a `restorePurchases`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, vincula tu transacción con el paywall que originó la compra mediante el método `setVariationId`. Esto garantiza que la compra se atribuya correctamente al paywall que la desencadenó para un análisis preciso. Este paso solo es necesario si usas paywalls de Adapty. ```kotlin showLineNumbers Adapty.restorePurchases { result -> if (result is AdaptyResult.Success) { // success } } Adapty.setVariationId(transactionId, variationId) { error -> if (error == null) { // success } } ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | transactionId | requerido | Identificador de cadena (`purchase.getOrderId`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. | | variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | ```java showLineNumbers Adapty.restorePurchases(result -> { if (result instanceof AdaptyResult.Success) { // success } }); Adapty.setVariationId(transactionId, variationId, error -> { if (error == null) { // success } }); ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | transactionId | requerido | Identificador de cadena (`purchase.getOrderId`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. | | variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | **Reporte de transacciones** Usa `restorePurchases` para reportar una transacción a Adapty en Observer Mode, tal como se explica en la página [Restaurar compras en el código móvil](android-restore-purchase). :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `restorePurchases`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: **Asociar paywalls a transacciones** El SDK de Adapty no puede determinar el origen de las compras, ya que eres tú quien las procesa. Por lo tanto, si tienes intención de usar paywalls y/o pruebas A/B en Observer Mode, debes asociar la transacción proveniente de tu app store con el paywall correspondiente en el código de tu app móvil. Es importante hacerlo bien antes de publicar tu app, de lo contrario generará errores en los análisis. ```kotlin Adapty.setVariationId(transactionId, variationId) { error -> if (error == null) { // success } } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | transactionId | requerido | Identificador de cadena (purchase.getOrderId de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. | | variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | ```java Adapty.setVariationId(transactionId, variationId, error -> { if (error == null) { // success } }); ``` | Parámetro | Presencia | Descripción | | ------------------------------------------------- | --------- | ------------------------------------------------------------ | | transactionId | requerido | Identificador de cadena (purchase.getOrderId de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. | | variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | --- # File: android-present-paywall-builder-paywalls-in-observer-mode --- --- title: "Mostrar paywalls de Paywall Builder en modo Observer en el SDK de Android" description: "Aprende cómo mostrar paywalls en modo observer usando el Paywall Builder de Adapty." --- Si has personalizado un paywall con el Paywall Builder, no tienes que preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Este tipo de paywall incluye tanto qué mostrar como cómo mostrarlo. :::warning Esta sección es exclusivamente para el [modo Observer](observer-vs-full-mode). Si no trabajas en modo Observer, consulta el tema [Android - Mostrar paywalls creados con Paywall Builder](android-present-paywalls). :::
Antes de empezar a mostrar paywalls (haz clic para expandir) 1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios). 2. Instala y configura el SDK de Adapty. Asegúrate de establecer el parámetro `observerMode` en `true`. Consulta nuestras instrucciones específicas por framework [para Android](sdk-installation-android). 3. [Crea productos](create-product) en el Adapty Dashboard. 4. [Configura los paywalls, asígnales productos](create-paywall) y personalízalos usando el Paywall Builder en el Adapty Dashboard. 5. [Crea placements y asígnales tus paywalls](create-placement) en el Adapty Dashboard. 6. [Obtén los paywalls del Paywall Builder y su configuración](android-get-pb-paywalls) en el código de tu aplicación móvil.

1. Implementa el `AdaptyUiObserverModeHandler`. El evento `onPurchaseInitiated` te informará de que el usuario ha iniciado una compra. Puedes activar tu flujo de compra personalizado en respuesta a este callback: ```kotlin showLineNumbers val observerModeHandler = AdaptyUiObserverModeHandler { product, paywall, paywallView, onStartPurchase, onFinishPurchase -> onStartPurchase() yourBillingClient.makePurchase( product, onSuccess = { purchase -> onFinishPurchase() //handle success }, onError = { onFinishPurchase() //handle error }, onCancel = { onFinishPurchase() //handle cancel } ) } ``` ```java showLineNumbers AdaptyUiObserverModeHandler observerModeHandler = (product, paywall, paywallView, onStartPurchase, onFinishPurchase) -> { onStartPurchase.invoke(); yourBillingClient.makePurchase( product, purchase -> { onFinishPurchase.invoke(); //handle success }, error -> { onFinishPurchase.invoke(); //handle error }, () -> { //cancellation onFinishPurchase.invoke(); //handle cancel } ); }; ``` Para manejar las restauraciones en el modo Observer, sobreescribe `getRestoreHandler()`. Por defecto devuelve `null`, lo que utiliza el flujo integrado de `Adapty.restorePurchases()` de Adapty. Para proporcionar tu propia implementación de restauración: ```kotlin showLineNumbers val observerModeHandler = object : AdaptyUiObserverModeHandler { // onPurchaseInitiated implementation (see above) override fun getRestoreHandler() = AdaptyUiObserverModeHandler.RestoreHandler { onStartRestore, onFinishRestore -> onStartRestore() yourBillingClient.restorePurchases( onSuccess = { restoredPurchases -> onFinishRestore() //handle successful restore }, onError = { onFinishRestore() //handle error } ) } } ``` ```java showLineNumbers AdaptyUiObserverModeHandler observerModeHandler = new AdaptyUiObserverModeHandler() { // onPurchaseInitiated implementation (see above) @Override public RestoreHandler getRestoreHandler() { return (onStartRestore, onFinishRestore) -> { onStartRestore.invoke(); yourBillingClient.restorePurchases( restoredPurchases -> { onFinishRestore.invoke(); //handle successful restore }, error -> { onFinishRestore.invoke(); //handle error } ); }; } }; ``` Recuerda invocar los siguientes callbacks para notificar a AdaptyUI sobre el proceso de compra o restauración. Esto es necesario para que el paywall funcione correctamente, por ejemplo, para mostrar el loader: | Callback | Descripción | | :----------------- |:-----------------------------------------------------------------------------------------------------| | onStartPurchase() | El callback debe invocarse para notificar a AdaptyUI que la compra ha comenzado. | | onFinishPurchase() | El callback debe invocarse para notificar a AdaptyUI que la compra ha finalizado. | | onStartRestore() | Opcional. El callback puede invocarse para notificar a AdaptyUI que la restauración ha comenzado. | | onFinishRestore() | Opcional. El callback puede invocarse para notificar a AdaptyUI que la restauración ha finalizado. | 2. Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, llama al método `AdaptyUI.getPaywallView()` o crea el `AdaptyPaywallView` directamente: ```kotlin showLineNumbers val paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, products, eventListener, personalizedOfferResolver, tagResolver, timerResolver, observerModeHandler, ) ``` ```kotlin showLineNumbers val paywallView = AdaptyPaywallView(activity) // or retrieve it from xml ... with(paywallView) { showPaywall( viewConfiguration, products, eventListener, personalizedOfferResolver, tagResolver, timerResolver, observerModeHandler, ) } ``` ```java showLineNumbers AdaptyPaywallView paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, products, eventListener, personalizedOfferResolver, tagResolver, timerResolver, observerModeHandler ); ``` ```java showLineNumbers AdaptyPaywallView paywallView = new AdaptyPaywallView(activity); //añadir a la jerarquía de vistas si es necesario, o recibirlo desde xml ... paywallView.showPaywall(viewConfiguration, products, eventListener, personalizedOfferResolver, tagResolver, timerResolver, observerModeHandler); ``` ```xml showLineNumbers ``` Después de crear la vista correctamente, puedes añadirla a la jerarquía de vistas y mostrarla. Para hacerlo, utiliza esta función composable: ```kotlin showLineNumbers AdaptyPaywallScreen( viewConfiguration, products, eventListener, personalizedOfferResolver, tagResolver, timerResolver, ) ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **Products** | opcional | Proporciona un array de `AdaptyPaywallProduct` para optimizar el momento de visualización de los productos en pantalla. Si se pasa `null`, AdaptyUI obtendrá automáticamente los productos necesarios. | | **ViewConfiguration** | obligatorio | Proporciona un objeto `AdaptyViewConfiguration` que contiene los detalles visuales del paywall. Usa el método `Adapty.getViewConfiguration(paywall)` para cargarlo. Consulta el tema [Obtener la configuración visual del paywall](#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) para más detalles. | | **EventListener** | opcional | Proporciona un `AdaptyUiEventListener` para observar los eventos del paywall. Se recomienda extender `AdaptyUiDefaultEventListener` para mayor comodidad. Consulta el tema [Gestionar eventos del paywall](android-handling-events) para más detalles. | | **PersonalizedOfferResolver** | opcional | Para indicar precios personalizados ([leer más](https://developer.android.com/google/play/billing/integrate#personalized-price)), implementa `AdaptyUiPersonalizedOfferResolver` y añade tu propia lógica que mapee `AdaptyPaywallProduct` a `true` si el precio del producto es personalizado, o a `false` en caso contrario. | | **TagResolver** | opcional | Usa `AdaptyUiTagResolver` para resolver etiquetas personalizadas dentro del texto del paywall. Este resolver recibe un parámetro de etiqueta y lo resuelve en la cadena de texto correspondiente. Consulta el tema [Etiquetas personalizadas en Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | | **ObserverModeHandler** | obligatorio en modo Observer | El `AdaptyUiObserverModeHandler` que implementaste en el paso anterior. | | **variationId** | obligatorio | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | | **transaction** | obligatorio |

Para iOS, StoreKit 1: un objeto [`SKPaymentTransaction`](https://developer.apple.com/documentation/storekit/skpaymenttransaction).

Para iOS, StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).

Para Android: identificador de cadena (`purchase.getOrderId()`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.

|
Antes de empezar a mostrar paywalls (Haz clic para ampliar) 1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios). 2. Instala y configura el SDK de Adapty. Asegúrate de establecer el parámetro `observerMode` en `true`. Consulta las instrucciones específicas para cada framework: [Android](sdk-installation-android), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk) y [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk). 3. [Crea productos](create-product) en el Adapty Dashboard. 4. [Configura los paywalls, asígnales productos](create-paywall) y personalízalos con el Paywall Builder en el Adapty Dashboard. 5. [Crea placements y asígnales tus paywalls](create-placement) en el Adapty Dashboard. 6. [Obtén los paywalls del Paywall Builder y su configuración](android-get-pb-paywalls) en el código de tu aplicación móvil.
1. Implementa el `AdaptyUiObserverModeHandler`. El callback de `AdaptyUiObserverModeHandler` (`onPurchaseInitiated`) te avisa cuando el usuario inicia una compra. Puedes activar tu flujo de compra personalizado en respuesta a este callback así: ```kotlin showLineNumbers val observerModeHandler = AdaptyUiObserverModeHandler { product, paywall, paywallView, onStartPurchase, onFinishPurchase -> onStartPurchase() yourBillingClient.makePurchase( product, onSuccess = { purchase -> onFinishPurchase() //handle success }, onError = { onFinishPurchase() //handle error }, onCancel = { onFinishPurchase() //handle cancel } ) } ``` ```java showLineNumbers AdaptyUiObserverModeHandler observerModeHandler = (product, paywall, paywallView, onStartPurchase, onFinishPurchase) -> { onStartPurchase.invoke(); yourBillingClient.makePurchase( product, purchase -> { onFinishPurchase.invoke(); //handle success }, error -> { onFinishPurchase.invoke(); //handle error }, () -> { //cancellation onFinishPurchase.invoke(); //handle cancel } ); }; ``` También recuerda invocar estos callbacks en AdaptyUI. Esto es necesario para el correcto funcionamiento del paywall, como mostrar el loader, entre otras cosas: | Callback en Kotlin | Callback en Java | Descripción | | :----------------- | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- | | onStartPurchase() | onStartPurchase.invoke() | Este callback debe invocarse para notificar a AdaptyUI que la compra ha comenzado. | | onFinishPurchase() | onFinishPurchase.invoke() | Este callback debe invocarse para notificar a AdaptyUI que la compra ha finalizado correctamente, que ha fallado, o que ha sido cancelada. | 2. Para mostrar el paywall visual, primero debes inicializarlo. Para ello, llama al método `AdaptyUI.getPaywallView()` o crea el `AdaptyPaywallView` directamente: ```kotlin showLineNumbers val paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, products, AdaptyPaywallInsets.of(topInset, bottomInset), eventListener, personalizedOfferResolver, tagResolver, observerModeHandler, ) //======= OR ======= val paywallView = AdaptyPaywallView(activity) // or retrieve it from xml ... with(paywallView) { setEventListener(eventListener) setObserverModeHandler(observerModeHandler) showPaywall( viewConfiguration, products, AdaptyPaywallInsets.of(topInset, bottomInset), personalizedOfferResolver, tagResolver, ) } ``` ```java showLineNumbers AdaptyPaywallView paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, products, AdaptyPaywallInsets.of(topInset, bottomInset), eventListener, personalizedOfferResolver, tagResolver, observerModeHandler ); //======= OR ======= AdaptyPaywallView paywallView = new AdaptyPaywallView(activity); //add to the view hierarchy if needed, or you receive it from xml ... paywallView.setEventListener(eventListener); paywallView.setObserverModeHandler(observerModeHandler); paywallView.showPaywall(viewConfiguration, products, AdaptyPaywallInsets.of(topInset, bottomInset), personalizedOfferResolver); ``` ```xml showLineNumbers ``` Tras crear la vista correctamente, puedes añadirla a la jerarquía de vistas y mostrarla. Parámetros de la solicitud: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Products** | opcional | Proporciona un array de `AdaptyPaywallProduct` para optimizar el tiempo de visualización de los productos en pantalla. Si se pasa `null`, AdaptyUI obtendrá automáticamente los productos necesarios. | | **ViewConfiguration** | requerido | Suministra un objeto `AdaptyViewConfiguration` que contiene los detalles visuales del paywall. Usa el método `Adapty.getViewConfiguration(paywall)` para cargarlo. Consulta el tema [Obtener la configuración visual del paywall](android-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) para más detalles. | | **Insets** | requerido | Define un objeto `AdaptyPaywallInsets` con información sobre el área solapada por las barras del sistema, creando márgenes verticales para el contenido. Si ni la barra de estado ni la barra de navegación solapan el `AdaptyPaywallView`, pasa `AdaptyPaywallInsets.NONE`. En modo pantalla completa donde las barras del sistema solapan parte de tu interfaz, obtén los insets como se muestra debajo de la tabla. | | **EventListener** | opcional | Proporciona un `AdaptyUiEventListener` para observar los eventos del paywall. Se recomienda extender `AdaptyUiDefaultEventListener` para facilitar su uso. Consulta el tema [Gestión de eventos del paywall](android-handling-events) para más detalles. | | **PersonalizedOfferResolver** | opcional | Para indicar precios personalizados ([leer más](https://developer.android.com/google/play/billing/integrate#personalized-price)), implementa `AdaptyUiPersonalizedOfferResolver` y pasa tu propia lógica que mapea `AdaptyPaywallProduct` a `true` si el precio del producto es personalizado, o `false` en caso contrario. | | **TagResolver** | opcional | Usa `AdaptyUiTagResolver` para resolver etiquetas personalizadas dentro del texto del paywall. Este resolver toma un parámetro de etiqueta y lo resuelve en la cadena correspondiente. Consulta el tema [Etiquetas personalizadas en Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | | **ObserverModeHandler** | requerido en modo Observer | El `AdaptyUiObserverModeHandler` que implementaste en el paso anterior. | | **variationId** | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). | | **transaction** | requerido |

Para iOS, StoreKit 1: un objeto [`SKPaymentTransaction`](https://developer.apple.com/documentation/storekit/skpaymenttransaction).

Para iOS, StoreKit 2: un objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).

Para Android: el identificador de cadena (`purchase.getOrderId()`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.

| Para el modo de pantalla completa, donde las barras del sistema se superponen a parte de tu UI, obtén los insets de la siguiente manera: ```kotlin showLineNumbers import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat //create extension function fun View.onReceiveSystemBarsInsets(action: (insets: Insets) -> Unit) { ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets -> val systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()) ViewCompat.setOnApplyWindowInsetsListener(this, null) action(systemBarInsets) insets } } //and then use it with the view paywallView.onReceiveSystemBarsInsets { insets -> val paywallInsets = AdaptyPaywallInsets.of(insets.top, insets.bottom) paywallView.setEventListener(eventListener) paywallView.setObserverModeHandler(observerModeHandler) paywallView.showPaywall(viewConfig, products, paywallInsets, personalizedOfferResolver, tagResolver) } ``` ```java showLineNumbers import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; ... ViewCompat.setOnApplyWindowInsetsListener(paywallView, (view, insets) -> { Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()); ViewCompat.setOnApplyWindowInsetsListener(paywallView, null); AdaptyPaywallInsets paywallInsets = AdaptyPaywallInsets.of(systemBarInsets.top, systemBarInsets.bottom); paywallView.setEventListener(eventListener); paywallView.setObserverModeHandler(observerModeHandler); paywallView.showPaywall(viewConfiguration, products, paywallInsets, personalizedOfferResolver, tagResolver); return insets; }); ``` Devuelve: | Objeto | Descripción | | :------------------ | :------------------------------------------------- | | `AdaptyPaywallView` | objeto que representa la pantalla del paywall solicitado. | :::warning No olvides [Asociar paywalls a transacciones de compra](report-transactions-observer-mode-android). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra. :::
--- # File: android-troubleshoot-purchases --- --- title: "Solucionar problemas de compras en el SDK de Android" description: "Solucionar problemas de compras en el SDK de Android" --- Esta guía te ayuda a resolver los problemas más comunes al implementar compras manualmente en el SDK de Android. ## makePurchase se llama correctamente, pero el perfil no se actualiza \{#makepurchase-is-called-successfully-but-the-profile-is-not-being-updated\} **Problema**: El método `makePurchase` se completa correctamente, pero el perfil del usuario y el estado de la suscripción no se actualizan en Adapty. **Causa**: Esto generalmente indica una configuración incompleta de Google Play Store. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## makePurchase se invoca dos veces \{#makepurchase-is-invoked-twice\} **Problema**: El método `makePurchase` se llama varias veces para la misma compra. **Causa**: Esto suele ocurrir cuando el flujo de compra se activa varias veces debido a problemas en la gestión del estado de la interfaz o a interacciones muy rápidas del usuario. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## AdaptyError.cantMakePayments en el modo observador \{#adaptyerrorcantmakepayments-in-observer-mode\} **Problema**: Recibes `AdaptyError.cantMakePayments` al usar `makePurchase` en el modo observador. **Causa**: En el modo observador, debes gestionar las compras por tu cuenta y no usar el método `makePurchase` de Adapty. **Solución**: Si usas `makePurchase` para las compras, desactiva el modo observador. Tienes que elegir entre usar `makePurchase` o gestionar las compras por tu cuenta en el modo observador. Consulta [Implementar el modo observador](implement-observer-mode-android) para más detalles. ## Error de Adapty: (code: 103, message: Play Market request failed on purchases updated: responseCode=3, debugMessage=Billing Unavailable, detail: null) \{#adapty-error-code-103-message-play-market-request-failed-on-purchases-updated-responsecode3-debugmessagebilling-unavailable-detail-null\} **Problema**: Recibes un error de facturación no disponible de Google Play Store. **Causa**: Este error no está relacionado con Adapty. Es un error de la biblioteca de facturación de Google Play que indica que la facturación no está disponible en el dispositivo. **Solución**: Este error no está relacionado con Adapty. Puedes encontrar más información en la documentación de Play Store: [Handle BillingResult response codes](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) | Play Billing | Android Developers. ## No se encuentran makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\} **Problema**: Tienes problemas porque no se encuentran los `makePurchasesCompletionHandlers`. **Causa**: Esto suele estar relacionado con problemas en las pruebas en sandbox. **Solución**: Crea un nuevo usuario de sandbox e inténtalo de nuevo. Esto suele resolver los problemas con los manejadores de finalización de compras en entornos sandbox. ## Otros problemas \{#other-issues\} **Problema**: Experimentas otros problemas relacionados con compras que no se tratan anteriormente. **Solución**: Si es necesario, migra el SDK a la versión más reciente siguiendo las [guías de migración](android-sdk-migration-guides). Muchos problemas se resuelven en versiones más nuevas del SDK. --- # File: android-identifying-users --- --- title: "Identificar usuarios en el SDK de Android" description: "Identifica usuarios en Adapty para mejorar las experiencias de suscripción personalizadas (Android)." --- Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, deberías definir tu propio Customer User ID. Puedes buscar usuarios por su Customer User ID en la sección [Perfiles](profiles-crm) y usarlo en la [API server-side](getting-started-with-server-side-api), que se enviará a todas las integraciones. ### Configurar el ID de usuario en la configuración \{#setting-customer-user-id-on-configuration\} Si tienes un ID de usuario durante la configuración, pásalo directamente como parámetro `customerUserId` al método `.activate()`: ```kotlin showLineNumbers Adapty.activate(applicationContext, "PUBLIC_SDK_KEY", customerUserId = "YOUR_USER_ID") ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Establecer el ID de usuario personalizado tras la configuración \{#setting-customer-user-id-after-configuration\} Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo más tarde en cualquier momento con el método `.identify()`. Los casos más habituales para usar este método son después del registro o la autenticación, cuando el usuario pasa de ser anónimo a estar autenticado. ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") { error -> if (error == null) { // successful identify } } ``` ```java showLineNumbers Adapty.identify("YOUR_USER_ID", error -> { if (error == null) { // successful identify } }); ``` Parámetros de la solicitud: - **Customer User ID** (obligatorio): un identificador de usuario de tipo string. :::warning Reenvío de datos significativos del usuario En algunos casos, como cuando un usuario inicia sesión en su cuenta de nuevo, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si pasaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, debes volver a enviar esos datos para el usuario identificado. También es importante tener en cuenta que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes. ::: ### Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\} Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`: ```kotlin showLineNumbers Adapty.logout { error -> if (error == null) { // successful logout } } ``` ```java showLineNumbers Adapty.logout(error -> { if (error == null) { // successful logout } }); ``` Luego puedes iniciar sesión con el usuario usando el método `.identify()`. ### Detectar usuarios en varios dispositivos \{#detect-users-across-devices\} --- no_index: true --- Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`. Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID. **Lo que Adapty puede detectar entre dispositivos** | Tu configuración | Lo que Adapty detecta | Lo que debes hacer | | --- | --- | --- | | Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. | | Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. | | Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. | | Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. | **Restaurar compras en un dispositivo nuevo** Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK. No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado. --- # File: android-setting-user-attributes --- --- title: "Establecer atributos de usuario en el SDK de Android" description: "Aprende a establecer atributos de usuario en Adapty para mejorar la segmentación de audiencias." --- Puedes añadir atributos opcionales como email, número de teléfono, etc., al usuario de tu app. Luego puedes usar esos atributos para crear [segmentos](segments) de usuarios o simplemente consultarlos en el CRM. ### Configurar atributos de usuario \{#setting-user-attributes\} Para establecer atributos de usuario, llama al método `.updateProfile()`: ```kotlin showLineNumbers val builder = AdaptyProfileParameters.Builder() .withEmail("email@email.com") .withPhoneNumber("+18888888888") .withFirstName("John") .withLastName("Appleseed") .withGender(AdaptyProfile.Gender.OTHER) .withBirthday(AdaptyProfile.Date(1970, 1, 3)) Adapty.updateProfile(builder.build()) { error -> if (error != null) { // handle the error } } ``` ```java showLineNumbers AdaptyProfileParameters.Builder builder = new AdaptyProfileParameters.Builder() .withEmail("email@email.com") .withPhoneNumber("+18888888888") .withFirstName("John") .withLastName("Appleseed") .withGender(AdaptyProfile.Gender.OTHER) .withBirthday(new AdaptyProfile.Date(1970, 1, 3)); Adapty.updateProfile(builder.build(), error -> { if (error != null) { // handle the error } }); ``` Ten en cuenta que los atributos que hayas establecido previamente con el método `updateProfile` no se restablecerán. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Lista de claves permitidas \{#the-allowed-keys-list\} Las claves `` permitidas de `AdaptyProfileParameters.Builder` y sus valores `` son los siguientes: | Clave | Valor | |---|-----| |

email

phoneNumber

firstName

lastName

| String | | gender | Enum, los valores permitidos son: `female`, `male`, `other` | | birthday | Date | ### Atributos personalizados de usuario \{#custom-user-attributes\} Puedes definir tus propios atributos personalizados, que normalmente están relacionados con el uso de tu app. Por ejemplo, en aplicaciones de fitness pueden ser el número de ejercicios por semana; en apps de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes usarlos en segmentos para crear paywalls y ofertas segmentadas, y también en analíticas para identificar qué métricas de producto influyen más en los ingresos. ```kotlin showLineNumbers builder.withCustomAttribute("key1", "value1") ``` ```java showLineNumbers builder.withCustomAttribute("key1", "value1"); ``` Para eliminar una clave existente, usa el método `.withRemoved(customAttributeForKey:)`: ```kotlin showLineNumbers builder.withRemovedCustomAttribute("key2") ``` ```java showLineNumbers builder.withRemovedCustomAttribute("key2"); ``` A veces necesitas saber qué atributos personalizados ya se han establecido. Para ello, usa el campo `customAttributes` del objeto `AdaptyProfile`. :::warning Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor podrían haber cambiado desde la última sincronización. ::: ### Límites \{#limits\} - Hasta 30 atributos personalizados por usuario - Los nombres de las claves tienen un máximo de 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.` - El valor puede ser una cadena de texto o un número decimal con un máximo de 50 caracteres. --- # File: android-listen-subscription-changes --- --- title: "Comprobar el estado de la suscripción en el SDK de Android" description: "Rastrea y gestiona el estado de la suscripción del usuario en Adapty para mejorar la retención de clientes en tu app de Android." --- Con Adapty, hacer seguimiento del estado de la suscripción es muy sencillo. No necesitas insertar manualmente los IDs de producto en tu código. En su lugar, puedes confirmar fácilmente el estado de la suscripción de un usuario comprobando si tiene un [nivel de acceso](access-level) activo. Antes de empezar a comprobar el estado de la suscripción, configura las [notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn). ## Nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptyprofile-object\} Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/). Te recomendamos recuperar el perfil cuando tu app arranque, por ejemplo al [identificar a un usuario](android-identifying-users#setting-customer-user-id-on-configuration), y luego actualizarlo cada vez que se produzcan cambios. Así podrás usar el objeto de perfil sin tener que solicitarlo repetidamente. Para recibir notificaciones sobre actualizaciones del perfil, escucha los cambios tal como se describe en la sección [Escuchar actualizaciones del perfil, incluidos los niveles de acceso](android-listen-subscription-changes) más abajo. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\} Para obtener el nivel de acceso desde el servidor, usa el método `.getProfile()`: ```kotlin showLineNumbers Adapty.getProfile { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value // check the access } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.getProfile(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success) result).getValue(); // check the access } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` Parámetros de respuesta: | Parámetro | Descripción | | --------- | ------------------------------------------------------------ | | Profile |

Un objeto [AdaptyProfile](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/). En general, solo necesitas comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app.

El método `.getProfile` devuelve el resultado más actualizado posible, ya que siempre intenta consultar la API. Si por algún motivo (p. ej., sin conexión a internet) el SDK de Adapty no puede obtener información del servidor, se devolverán los datos de la caché. También es importante tener en cuenta que el SDK de Adapty actualiza la caché de `AdaptyProfile` con regularidad para mantener esta información lo más actualizada posible.

| El método `.getProfile()` te proporciona el perfil del usuario, a partir del cual puedes obtener el estado del nivel de acceso. Puedes tener varios niveles de acceso por app. Por ejemplo, si tienes una app de periódico y vendes suscripciones a distintos temas de forma independiente, puedes crear los niveles de acceso "sports" y "science". Sin embargo, la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes utilizar simplemente el nivel de acceso "premium" predeterminado. A continuación se muestra un ejemplo para comprobar el nivel de acceso "premium" predeterminado: ```kotlin showLineNumbers Adapty.getProfile { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value if (profile.accessLevels["premium"]?.isActive == true) { // grant access to premium features } } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` ```java showLineNumbers Adapty.getProfile(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success) result).getValue(); AdaptyProfile.AccessLevel premium = profile.getAccessLevels().get("premium"); if (premium != null && premium.isActive()) { // grant access to premium features } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` ### Escuchar actualizaciones del estado de la suscripción \{#listening-for-subscription-status-updates\} Cada vez que cambia la suscripción del usuario, Adapty lanza un evento. Para recibir mensajes de Adapty, necesitas hacer alguna configuración adicional: ```kotlin showLineNumbers Adapty.setOnProfileUpdatedListener { profile -> // handle any changes to subscription state } ``` ```java showLineNumbers t Adapty.setOnProfileUpdatedListener(profile -> { // handle any changes to subscription state }); ``` Adapty también lanza un evento al iniciar la aplicación. En ese caso, se pasará el estado de la suscripción en caché. ### Caché del estado de la suscripción \{#subscription-status-cache\} La caché implementada en el SDK de Adapty almacena el estado de la suscripción del perfil. Esto significa que, aunque el servidor no esté disponible, se puede acceder a los datos en caché para obtener información sobre el estado de la suscripción del perfil. No obstante, hay que tener en cuenta que no es posible solicitar datos directamente desde la caché. El SDK consulta periódicamente el servidor cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si existen modificaciones, como nuevas transacciones u otras actualizaciones, se enviarán a los datos en caché para mantenerlos sincronizados con el servidor. --- # File: kids-mode-android --- --- title: "Modo Kids en Android SDK" description: "Activa fácilmente el Modo Kids para cumplir las políticas de Google. Sin GAID ni datos publicitarios recopilados en Android SDK." --- Si tu aplicación Android está destinada a niños, debes seguir las políticas de [Google](https://support.google.com/googleplay/android-developer/answer/9893335). Si usas el SDK de Adapty, unos pocos pasos sencillos te ayudarán a configurarlo para cumplir estas políticas y superar las revisiones de la tienda. ## ¿Qué se requiere? \{#whats-required\} Necesitas configurar el SDK de Adapty para deshabilitar la recopilación de: - [Android Advertising ID (AAID/GAID)](https://support.google.com/googleplay/android-developer/answer/6048248) - [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) Además, te recomendamos usar el ID de usuario personalizado con cuidado. Un ID de usuario con formato `` se considerará claramente como recopilación de datos personales, al igual que usar un correo electrónico. Para el Modo Kids, la práctica recomendada es usar identificadores aleatorios o anonimizados (por ejemplo, IDs hasheados o UUIDs generados por el dispositivo) para garantizar el cumplimiento. ## Activar el Modo Kids \{#enabling-kids-mode\} ### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\} En el Adapty Dashboard, necesitas deshabilitar la recopilación de direcciones IP. Para ello, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** en **Collect users' IP address**. ### Cambios en el código de tu aplicación \{#updates-in-your-mobile-app-code\} Para cumplir con las políticas, debes deshabilitar la recopilación del Android Advertising ID (AAID/GAID) y la dirección IP al inicializar el SDK de Adapty: **Kotlin:** ```kotlin showLineNumbers override fun onCreate() { super.onCreate() Adapty.activate( applicationContext, AdaptyConfig.Builder("PUBLIC_SDK_KEY") // highlight-start .withAdIdCollectionDisabled(true) // set to `true` .withIpAddressCollectionDisabled(true) // set to `true` // highlight-end .build() ) } ``` **Java:** ```java showLineNumbers @Override public void onCreate() { super.onCreate(); Adapty.activate( applicationContext, new AdaptyConfig.Builder("PUBLIC_SDK_KEY") // highlight-start .withAdIdCollectionDisabled(true) // set to `true` .withIpAddressCollectionDisabled(true) // set to `true` // highlight-end .build() ); } ``` --- # File: android-get-onboardings --- --- title: "Obtener onboardings en el SDK de Android" description: "Aprende cómo recuperar onboardings en Adapty para Android." --- Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el builder en el Adapty Dashboard, puedes mostrarlo en tu app de Android. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, tal como se describe a continuación. Antes de empezar, asegúrate de que: 1. Tienes instalado el [SDK de Adapty para Android](sdk-installation-android) en la versión 3.8.0 o superior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). ## Obtener el onboarding \{#fetch-onboarding\} Cuando creas un [onboarding](onboardings) con nuestro builder sin código, se almacena como un contenedor con una configuración que tu app necesita obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a cuestionarios o entradas de formularios). El contenedor también registra automáticamente los eventos de analítica, por lo que no necesitas implementar un seguimiento de vistas por separado. Para un mejor rendimiento, obtén la configuración del onboarding con antelación para que las imágenes tengan tiempo suficiente de descargarse antes de mostrárselas a los usuarios. Para obtener un onboarding, usa el método `getOnboarding`: ```kotlin showLineNumbers Adapty.getOnboarding("YOUR_PLACEMENT_ID") { result -> when (result) { is AdaptyResult.Success -> { val onboarding = result.value // the requested onboarding } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** |

opcional

por defecto: `en`

|

El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.

Ejemplo: `en` significa inglés; `pt-br` representa el portugués de Brasil.

Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.

| | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |

Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.

Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.

Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra al desinstalarla o mediante limpieza manual.

El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no esté disponible. Este sistema está diseñado para asegurarse de que siempre obtengas la versión más reciente de tus onboardings garantizando la fiabilidad incluso cuando la conexión a internet es limitada.

| | **loadTimeout** | por defecto: 5 seg |

Este valor limita el tiempo de espera de este método. Si se alcanza el timeout, se devolverán los datos en caché o el fallback local.

Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes solicitudes internamente.

Para Android: puedes crear `TimeInterval` con funciones de extensión (como `5.seconds`, donde `.seconds` es de `import com.adapty.utils.seconds`), o `TimeInterval.seconds(5)`. Para no establecer ningún límite, usa `TimeInterval.INFINITE`.

| Parámetros de respuesta: | Parámetro | Descripción | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | Un objeto [`AdaptyOnboarding`](https://android.adapty.io/adapty/com.adapty.models/-adapty-onboarding/) con: el identificador y la configuración del onboarding, Remote Config y varias otras propiedades. | ## Acelerar la obtención del onboarding con el onboarding de la audiencia predeterminada \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} Normalmente, los onboardings se obtienen casi al instante, por lo que no tienes que preocuparte por acelerar este proceso. Sin embargo, cuando tienes muchas audiencias y onboardings y tus usuarios tienen una conexión a internet débil, obtener un onboarding puede tardar más de lo deseable. En esas situaciones, puede que quieras mostrar un onboarding por defecto para garantizar una experiencia fluida en lugar de no mostrar ninguno. Para abordar esto, puedes usar el método `getOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, tal como se detalla en la sección [Obtener onboarding](#fetch-onboarding) anterior. :::warning Considera usar `getOnboarding` en lugar de `getOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: puede crear problemas al dar soporte a múltiples versiones de la app, lo que requiere diseños compatibles con versiones anteriores o asumir que las versiones más antiguas podrían mostrarse incorrectamente. - **Sin personalización**: solo muestra contenido para la audiencia "All Users", eliminando la segmentación por país, atribución o atributos personalizados. Si la mayor velocidad de obtención compensa estos inconvenientes para tu caso de uso, utiliza `getOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getOnboarding` como se describe [arriba](#fetch-onboarding). ::: ```kotlin Adapty.getOnboardingForDefaultAudience("YOUR_PLACEMENT_ID") { result -> when (result) { is AdaptyResult.Success -> { val onboarding = result.value // Handle successful onboarding retrieval } is AdaptyResult.Error -> { val error = result.error // Handle error case } } } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** |

opcional

por defecto: `en`

|

El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.

Ejemplo: `en` significa inglés; `pt-br` representa el portugués de Brasil.

Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.

| | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |

Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.

Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.

Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra al desinstalarla o mediante limpieza manual.

El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no esté disponible. Este sistema está diseñado para asegurarse de que siempre obtengas la versión más reciente de tus onboardings garantizando la fiabilidad incluso cuando la conexión a internet es limitada.

| --- # File: android-present-onboardings --- --- title: "Presentar onboardings en Android SDK" description: "Aprende cómo presentar onboardings en Android para una interacción efectiva con el usuario." --- Antes de comenzar, asegúrate de que: 1. Has instalado el [SDK de Adapty para Android](sdk-installation-android) 3.8.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). Si has personalizado un onboarding con el Onboarding Builder, no necesitas preocuparte por renderizarlo en el código de tu app móvil para mostrárselo al usuario. Ese onboarding ya contiene tanto qué mostrar como cómo mostrarlo. Para mostrar el onboarding visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, llama al método `AdaptyUI.getOnboardingView()` o crea el `OnboardingView` directamente: ```kotlin val onboardingView = AdaptyUI.getOnboardingView( activity = this, viewConfig = onboardingConfig, eventListener = eventListener ) ``` ```kotlin val onboardingView = AdaptyOnboardingView(activity) onboardingView.show( viewConfig = onboardingConfig, delegate = eventListener ) ``` ```java AdaptyOnboardingView onboardingView = AdaptyUI.getOnboardingView( activity, onboardingConfig, eventListener ); ``` ```java AdaptyOnboardingView onboardingView = new AdaptyOnboardingView(activity); onboardingView.show(onboardingConfig, eventListener); ``` ```xml ``` Una vez que la vista se haya creado correctamente, puedes añadirla a la jerarquía de vistas y mostrarla en la pantalla del dispositivo. Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :-------- | :------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **viewConfig** | requerido | La configuración del onboarding obtenida de `AdaptyUI.getOnboardingConfiguration()` | | **eventListener** | requerido | Una implementación de `AdaptyOnboardingEventListener` para gestionar los eventos del onboarding. Consulta [Gestión de eventos del onboarding](android-handle-onboarding-events) para más detalles. | ## Cambiar el color del indicador de carga \{#change-loading-indicator-color\} Puedes sobreescribir el color por defecto del indicador de carga de la siguiente manera: ```xml ``` ## Añadir transiciones suaves entre la pantalla de inicio y el onboarding \{#add-smooth-transitions-between-the-splash-screen-and-onboarding\} Por defecto, entre la pantalla de inicio y el onboarding verás la pantalla de carga hasta que el onboarding esté completamente cargado. Sin embargo, si quieres que la transición sea más fluida, puedes personalizarla y bien extender la pantalla de inicio o mostrar algo diferente. Para ello, crea `adapty_onboarding_placeholder_view.xml` en `res/layout` y define ahí un placeholder (lo que se mostrará mientras se carga el onboarding). Si defines un placeholder, el onboarding se cargará en segundo plano y se mostrará automáticamente cuando esté listo. ## Desactivar los márgenes de área segura \{#disable-safe-area-paddings\} Por defecto, la vista del onboarding aplica automáticamente márgenes de área segura para evitar elementos del sistema como la barra de estado y la barra de navegación. Sin embargo, si quieres desactivar este comportamiento y tener control total sobre el diseño, puedes hacerlo estableciendo el parámetro `safeAreaPaddings` en `false`. ```kotlin val onboardingView = AdaptyUI.getOnboardingView( activity = this, viewConfig = onboardingConfig, eventListener = eventListener, safeAreaPaddings = false ) ``` ```kotlin val onboardingView = AdaptyOnboardingView(activity) onboardingView.show( viewConfig = onboardingConfig, delegate = eventListener, safeAreaPaddings = false ) ``` ```java AdaptyOnboardingView onboardingView = AdaptyUI.getOnboardingView( activity, onboardingConfig, eventListener, false ); ``` ```java AdaptyOnboardingView onboardingView = new AdaptyOnboardingView(activity); onboardingView.show(onboardingConfig, eventListener, false); ``` También puedes controlar este comportamiento de forma global añadiendo un recurso booleano a tu app: ```xml false ``` Cuando `safeAreaPaddings` está en `false`, el onboarding se extenderá a pantalla completa sin ajustes automáticos de márgenes, dándote control total sobre el diseño y permitiendo que el contenido del onboarding use todo el espacio de la pantalla. ## Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\} :::important La personalización de cómo se abren los enlaces en los onboardings está disponible a partir del SDK de Adapty v. 3.15.1. ::: Por defecto, los enlaces en los onboardings se abren en un navegador integrado en la app. Esto ofrece una experiencia fluida al mostrar páginas web dentro de tu aplicación, sin que el usuario tenga que cambiar de app. Si prefieres abrir los enlaces en un navegador externo, puedes personalizar este comportamiento estableciendo el parámetro `externalUrlsPresentation` en `AdaptyWebPresentation.ExternalBrowser`: ```kotlin val onboardingConfig = AdaptyUI.getOnboardingConfiguration( onboarding = onboarding, externalUrlsPresentation = AdaptyWebPresentation.ExternalBrowser // default – InAppBrowser ) ``` ```java AdaptyOnboardingConfiguration onboardingConfig = AdaptyUI.getOnboardingConfiguration( onboarding, AdaptyWebPresentation.ExternalBrowser // default – InAppBrowser ); ``` --- # File: android-handle-onboarding-events --- --- title: "Gestionar eventos de onboarding en el SDK de Android" description: "Gestiona los eventos relacionados con el onboarding en Android usando Adapty." --- Antes de empezar, asegúrate de que: 1. Has instalado el [SDK de Adapty para Android](sdk-installation-android) 3.8.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). Los onboardings configurados con el builder generan eventos a los que tu app puede responder. A continuación se explica cómo reaccionar ante estos eventos. Para controlar o monitorizar los procesos que ocurren en la pantalla de onboarding dentro de tu app Android, implementa la interfaz `AdaptyOnboardingEventListener`. ## Acciones personalizadas \{#custom-actions\} En el builder, puedes añadir una acción **personalizada** a un botón y asignarle un ID. Luego puedes usar ese ID en tu código y gestionarlo como una acción personalizada. Por ejemplo, si un usuario toca un botón personalizado como **Login** o **Allow notifications**, se activará el método delegado `onCustomAction` con el ID de acción definido en el builder. Puedes crear tus propios IDs, como "allowNotifications". ```kotlin showLineNumbers class YourActivity : AppCompatActivity() { private val eventListener = object : AdaptyOnboardingEventListener { override fun onCustomAction(action: AdaptyOnboardingCustomAction, context: Context) { when (action.actionId) { "allowNotifications" -> { // Request notification permissions } } } override fun onError(error: AdaptyOnboardingError, context: Context) { // Handle errors } // ... other required delegate methods } } ```
Ejemplo de evento (haz clic para expandir) ```json { "actionId": "allowNotifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ```
## Cerrar el onboarding \{#closing-onboarding\} El onboarding se considera cerrado cuando el usuario toca un botón con la acción **Close** asignada. Debes gestionar qué ocurre cuando el usuario cierra el onboarding. Por ejemplo: :::important Debes gestionar qué ocurre cuando el usuario cierra el onboarding. Por ejemplo, debes dejar de mostrar el onboarding. ::: Por ejemplo: ```kotlin override fun onCloseAction(action: AdaptyOnboardingCloseAction, context: Context) { // Dismiss the onboarding screen (context as? Activity)?.onBackPressed() } ```
Ejemplo de evento (haz clic para expandir) ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ```
## Abrir un paywall \{#opening-a-paywall\} :::tip Gestiona este evento para abrir un paywall si quieres mostrarlo dentro del onboarding. Si prefieres abrir el paywall una vez que el onboarding se haya cerrado, hay una forma más sencilla: gestiona [`AdaptyOnboardingCloseAction`](#closing-onboarding) y abre el paywall sin depender de los datos del evento. ::: Si un usuario hace clic en un botón que abre un paywall, recibirás el ID de la acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más fluida de trabajar con paywalls en onboardings es hacer que el ID de acción coincida con el ID del placement del paywall. Así, tras el evento `AdaptyOnboardingOpenPaywallAction`, puedes usar el ID del placement para obtener y abrir el paywall directamente: ```kotlin override fun onOpenPaywallAction(action: AdaptyOnboardingOpenPaywallAction, context: Context) { // Get the paywall using the placement ID from the action Adapty.getPaywall(placementId = action.actionId) { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value // Get the paywall configuration AdaptyUI.getViewConfiguration(paywall) { result -> when(result) { is AdaptyResult.Success -> { val paywallConfig = result.value // Create and present the paywall val paywallView = AdaptyUI.getPaywallView( activity = this, viewConfig = paywallConfig, products, eventListener = paywallEventListener ) // Add the paywall view to your layout binding.container.addView(paywallView) } is AdaptyResult.Error -> { val error = result.error // handle the error } } } is AdaptyResult.Error -> { val error = result.error // handle the error } } } } ```
Ejemplo de evento (haz clic para expandir) ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ```
## Finalización de la carga del onboarding \{#finishing-loading-onboarding\} Cuando el onboarding termina de cargarse, se invocará este método: ```kotlin override fun onFinishLoading(action: AdaptyOnboardingLoadedAction, context: Context) { // Handle loading completion } ```
Ejemplo de evento (haz clic para expandir) ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ```
## Eventos de navegación \{#navigation-events\} El método `onAnalyticsEvent` se llama cuando ocurren distintos eventos de analítica durante el flujo del onboarding. El objeto `event` puede ser de uno de los siguientes tipos: | Tipo | Descripción | |------------|-------------| | `OnboardingStarted` | Cuando el onboarding ha terminado de cargarse | | `ScreenPresented` | Cuando se muestra cualquier pantalla | | `ScreenCompleted` | Cuando se completa una pantalla. Incluye un `elementId` opcional (identificador del elemento completado) y un `reply` opcional (respuesta del usuario). Se activa cuando el usuario realiza cualquier acción para salir de la pantalla. | | `SecondScreenPresented` | Cuando se muestra la segunda pantalla | | `UserEmailCollected` | Se activa cuando se recoge el email del usuario mediante el campo de entrada | | `OnboardingCompleted` | Se activa cuando el usuario llega a una pantalla con el ID `final`. Si necesitas este evento, asigna el ID `final` a la última pantalla. | | `Unknown` | Para cualquier tipo de evento no reconocido. Incluye `name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) | Cada evento incluye información `meta` con los siguientes campos: | Campo | Descripción | |------------|-------------| | `onboardingId` | Identificador único del flujo de onboarding | | `screenClientId` | Identificador de la pantalla actual | | `screenIndex` | Posición de la pantalla actual en el flujo | | `totalScreens` | Número total de pantallas en el flujo | A continuación se muestra un ejemplo de cómo puedes usar los eventos de analítica para el seguimiento: ```kotlin override fun onAnalyticsEvent(event: AdaptyOnboardingAnalyticsEvent, context: Context) { when (event) { is AdaptyOnboardingAnalyticsEvent.OnboardingStarted -> { // Track onboarding start trackEvent("onboarding_started", event.meta) } is AdaptyOnboardingAnalyticsEvent.ScreenPresented -> { // Track screen presentation trackEvent("screen_presented", event.meta) } is AdaptyOnboardingAnalyticsEvent.ScreenCompleted -> { // Track screen completion with user response trackEvent("screen_completed", event.meta, event.elementId, event.reply) } is AdaptyOnboardingAnalyticsEvent.OnboardingCompleted -> { // Track successful onboarding completion trackEvent("onboarding_completed", event.meta) } is AdaptyOnboardingAnalyticsEvent.Unknown -> { // Handle unknown events trackEvent(event.name, event.meta) } // Handle other cases as needed } } ```
Ejemplos de eventos (haz clic para expandir) ```javascript // OnboardingStarted { "name": "onboarding_started", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } // ScreenPresented { "name": "screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 4 } } // ScreenCompleted { "name": "screen_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 }, "params": { "element_id": "profile_form", "reply": "success" } } // SecondScreenPresented { "name": "second_screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // UserEmailCollected { "name": "user_email_collected", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // OnboardingCompleted { "name": "onboarding_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ```
--- # File: android-onboarding-input --- --- title: "Procesar datos de onboardings en el SDK de Android" description: "Guarda y usa datos de onboardings en tu app de Android con el SDK de Adapty." --- Cuando tus usuarios responden a una pregunta de quiz o introducen datos en un campo de texto, se invocará el método `onStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código. Por ejemplo: ```kotlin override fun onStateUpdatedAction(action: AdaptyOnboardingStateUpdatedAction, context: Context) { // Store user preferences or responses when (val params = action.params) { is AdaptyOnboardingStateUpdatedParams.Select -> { // Handle single selection } is AdaptyOnboardingStateUpdatedParams.MultiSelect -> { // Handle multiple selections } is AdaptyOnboardingStateUpdatedParams.Input -> { // Handle text input } is AdaptyOnboardingStateUpdatedParams.DatePicker -> { // Handle date selection } } } ``` Consulta el formato de la acción [aquí](https://android.adapty.io/adapty-ui/com.adapty.ui.onboardings.actions/-adapty-onboarding-state-updated-action/).
Ejemplos de datos guardados (el formato puede variar según tu implementación) ```javascript // Example of a saved select action { "elementId": "preference_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "preferences_screen", "screenIndex": 1, "screensTotal": 3 }, "params": { "type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Example of a saved multi-select action { "elementId": "interests_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 3 }, "params": { "type": "multiSelect", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Example of a saved input action { "elementId": "name_input", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "input", "value": { "type": "text", "value": "John Doe" } } } // Example of a saved date picker action { "elementId": "birthday_picker", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "datePicker", "value": { "day": 15, "month": 6, "year": 1990 } } } ```
## Casos de uso \{#use-cases\} ### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\} Si quieres vincular inmediatamente los datos introducidos con el perfil del usuario y evitar pedirle la misma información dos veces, necesitas [actualizar el perfil del usuario](android-setting-user-attributes) con los datos del campo al gestionar la acción. Por ejemplo, pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name`, y quieres establecer el valor de ese campo como el nombre de pila del usuario. También les pides que introduzcan su correo electrónico en el campo `email`. En el código de tu app, podría verse así: ```kotlin showLineNumbers override fun onStateUpdatedAction(action: AdaptyOnboardingStateUpdatedAction, context: Context) { // Store user preferences or responses when (val params = action.params) { is AdaptyOnboardingStateUpdatedParams.Input -> { // Handle text input val builder = AdaptyProfileParameters.Builder() // Map elementId to appropriate profile field when (action.elementId) { "name" -> { when (val inputParams = params.params) { is AdaptyOnboardingInputParams.Text -> { builder.withFirstName(inputParams.value) } } } "email" -> { when (val inputParams = params.params) { is AdaptyOnboardingInputParams.Email -> { builder.withEmail(inputParams.value) } } } } Adapty.updateProfile(builder.build()) { error -> if (error != null) { // handle the error } } } } } ``` ### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\} Con los quizzes en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios tras completar el onboarding. Por ejemplo, puedes preguntarles sobre su experiencia deportiva y mostrar diferentes CTAs y productos a distintos grupos de usuarios. 1. [Añade un quiz](onboarding-quizzes) en el constructor de onboarding y asigna IDs significativos a sus opciones. 2. Gestiona las respuestas del quiz según sus IDs y [establece atributos personalizados](android-setting-user-attributes) para los usuarios. ```kotlin showLineNumbers override fun onStateUpdatedAction(action: AdaptyOnboardingStateUpdatedAction, context: Context) { // Handle quiz responses and set custom attributes when (val params = action.params) { is AdaptyOnboardingStateUpdatedParams.Select -> { // Handle quiz selection val builder = AdaptyProfileParameters.Builder() // Map quiz responses to custom attributes when (action.elementId) { "experience" -> { // Set custom attribute 'experience' with the selected value (beginner, amateur, pro) builder.withCustomAttribute("experience", params.params.value) } } Adapty.updateProfile(builder.build()) { error -> if (error != null) { // handle the error } } } } } ``` 3. [Crea segmentos](segments) para cada valor de atributo personalizado. 4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado. 5. [Muestra un paywall](android-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](android-handle-onboarding-events#opening-a-paywall). --- # File: android-sdk-call-order --- --- title: "Orden de llamadas en el SDK de Android" description: "Evita perder el acceso premium, que falten datos de atribución y errores intermitentes de ADAPTY_NOT_INITIALIZED llamando a los métodos del SDK de Adapty en el orden correcto." --- `Adapty.activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que finalice, el SDK no tiene estado. Cualquier llamada emitida antes o en paralelo con `activate()` falla con [`ADAPTY_NOT_INITIALIZED`](android-sdk-error-handling). Si tu app autentica usuarios y recoges un ID de usuario personalizado después del lanzamiento, llama a `Adapty.identify()` en ese momento. No llames a métodos de acción de usuario hasta que se ejecute el callback de finalización de `identify`. Las llamadas que compiten con él devuelven un error en su callback, o recaen sobre el perfil anónimo creado en la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la propiedad de la instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `identify` y sigue trabajando con el perfil anónimo. Los SDKs de MMP y analíticas (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera sus callbacks de UID antes de llamar a `Adapty.activate`. De lo contrario, el ID del MMP queda en un perfil anónimo temporal y no siempre se transfiere al perfil identificado. Para los detalles específicos de AppsFlyer, consulta [AppsFlyer](appsflyer). ## El orden correcto \{#the-correct-order\} Tu camino depende de dos cosas: cuándo conoces el ID de usuario personalizado y si usas un SDK de MMP o analíticas. - **Pasos 2 y 5**: Obligatorios para toda app. Activa el SDK y luego llama a los métodos del SDK. - **Pasos 1 y 3**: Necesarios solo si integras un SDK de MMP o analíticas (AppsFlyer, Adjust, Branch, PostHog). - **Paso 4**: Necesario solo si tu app autentica usuarios y recoge el ID de usuario personalizado después del lanzamiento. Si tienes el ID de usuario personalizado al lanzar la app, pásalo en el `AdaptyConfig.Builder` antes de llamar a `activate()` (paso 2a). Esta ruta nunca crea un perfil anónimo, por lo que el paso 4 no es necesario. | Paso | Llamada | Cuándo | Notas | |------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | Inicializa tu SDK de MMP o analíticas (AppsFlyer, Adjust, PostHog, Branch) | Al lanzar la app, primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerUID`. | | 2a | `Adapty.activate(context, AdaptyConfig.Builder("KEY").withCustomerUserId(...).build())` | Al lanzar la app, tras el paso 1, si tienes el ID de usuario personalizado | Recomendado. Nunca se crea un perfil anónimo. | | 2b | `Adapty.activate(context, AdaptyConfig.Builder("KEY").build())` sin `customerUserId` | Al lanzar la app, tras el paso 1, si no tienes el ID de usuario personalizado (o nunca lo recoges) | Adapty crea un perfil anónimo. | | 3 | `Adapty.setIntegrationIdentifier("appsflyer_id", uid)` para cada MMP | Tras el paso 2, antes de cualquier llamada de acción de usuario | Necesario para que los IDs de MMP queden en el perfil correcto. | | 4 | `Adapty.identify("YOUR_USER_ID") { error -> ... }` | Tras el paso 3 (o el paso 2 si no hay MMP), antes del paso 5 — solo en la ruta 2b con autenticación | Usa el callback de finalización. Las llamadas concurrentes durante `identify` pueden recaer en el perfil anónimo. | | 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Tras el paso 4 si llamas a `identify`; si no, tras el paso 3 (o el paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. | :::important Saltarse estos pasos provoca pérdida de acceso premium para usuarios que regresan, que falte `appsflyer_id` en los perfiles, y que se devuelvan paywalls para la audiencia equivocada. ::: ## Instalaciones web2app y de embudo web \{#web2app-and-web-funnel-installs\} Si los usuarios compran en un checkout web (Stripe, Paddle) y luego instalan la app nativa, el primer `activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el ID de usuario personalizado antes del lanzamiento de la app (desde tu flujo de autenticación o el referrer de instalación), pásalo directamente en el `AdaptyConfig.Builder`. De lo contrario, la compra web será invisible en el dispositivo hasta que llames a `identify("YOUR_USER_ID")` y luego a `restorePurchases`. Para los metadatos que enviar con cada checkout web, consulta: - [Stripe](stripe) - [Paddle](paddle) --- # File: android-optimize-paywall-fetching --- --- title: "Optimizar la obtención de paywalls en el SDK de Android" description: "Obtén paywalls de Adapty de forma fiable: sincronización, caché y patrones de respaldo para Android." --- Una obtención fiable de paywalls en Android hace tres cosas: renderiza rápido, devuelve el paywall dirigido a la audiencia correcta y tiene un respaldo elegante cuando la red es lenta. Las reglas a continuación cubren los patrones de sincronización, caché y respaldo para conseguirlo. :::tip Las reglas asumen que `Adapty.activate()` y `Adapty.identify()` ya se han resuelto. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order). ::: ## Reglas y errores comunes \{#rules-and-pitfalls\} | Haz esto | No hagas esto | Por qué | |---|---|---| | Obtén el placement que vas a mostrar. | No precargues todos los placements de forma concurrente al iniciar. | La precarga masiva bloquea el hilo principal y produce una pantalla en negro durante la ráfaga. | | Llama a `getPaywall` después de que la atribución haya tenido oportunidad de resolverse — por ejemplo, 1–2 segundos después de `activate` o tras que se dispare `setOnProfileUpdatedListener`. | No llames a `getPaywall` en `Application.onCreate()`. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia por defecto y omite silenciosamente los segmentos y la personalización de ASA. | | Establece un `loadTimeout` y configura un [paywall de respaldo](fallback-paywalls) para cada placement. | No esperes a `getPaywall` indefinidamente. | Sin un tiempo de espera, los usuarios con conectividad deficiente ven una pantalla en blanco hasta que la red responde — o cierran la app. | Consulta [Obtener paywalls y productos](fetch-paywalls-and-products-android) para la referencia de parámetros `fetchPolicy` y `loadTimeout`, y [Placements](placements) para elegir el placement adecuado. ## Ajustar para conectividad deficiente \{#tune-for-poor-connectivity\} Para mercados con conectividad consistentemente deficiente (zonas rurales, transporte, regiones afectadas por enrutamiento): - Establece `fetchPolicy` en `AdaptyPlacementFetchPolicy.ReturnCacheDataElseLoad` en cada obtención excepto la primera. - Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard. - Establece `loadTimeout` entre 3 y 5 segundos y acepta el respaldo cuando se agote el tiempo. - No condicionales la visualización del paywall a `getProfile`. Llama a `getPaywall` de forma independiente para que un perfil lento no bloquee la interfaz. --- # File: android-test --- --- title: "Prueba y lanzamiento en Android SDK" description: "Aprende a verificar el estado de suscripción en tu app Android con Adapty." --- Si ya has implementado el SDK de Adapty en tu app Android, querrás comprobar que todo está configurado correctamente y que las compras funcionan como se espera. Esto implica probar tanto la integración del SDK como el flujo de compra real con el entorno sandbox de Google Play. ## Prueba tu app \{#test-your-app\} Para realizar pruebas exhaustivas de tus compras in-app, incluyendo pruebas en sandbox y validación en closed track, consulta nuestra [guía de pruebas](testing-on-android). ## Prepárate para el lanzamiento \{#prepare-for-release\} Antes de enviar tu app al store, sigue la [Lista de verificación para el lanzamiento](release-checklist) para confirmar que: - La conexión al store y las notificaciones del servidor están configuradas - Las compras se completan y se reportan a Adapty - El acceso se desbloquea y se restaura correctamente - Se cumplen los requisitos de privacidad y revisión --- # File: android-sdk-error-handling --- --- title: "Gestionar errores en el SDK de Android" description: "Gestiona los errores del SDK de Android de forma eficaz con la guía de solución de problemas de Adapty." --- Cada error devuelto por el SDK es de tipo `AdaptyError`. :::tip **Activa los logs detallados antes de depurar.** La mayoría de los `AdaptyError` envuelven un error subyacente de Play Billing, de red o del backend. Con los logs detallados activados (`Adapty.logLevel = AdaptyLogLevel.VERBOSE` — consulta [Logging](sdk-installation-android#logging)), ese error envuelto se imprime en la consola, lo que normalmente te indica la causa real. ::: :::important Si estas soluciones no resuelven tu problema, consulta [Otros problemas](#other-issues) para ver los pasos que debes seguir antes de contactar con el soporte y así ayudarnos a asistirte de forma más eficiente. ::: | Error | Solución | |----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | UNKNOWN | Este error indica que ocurrió un error desconocido o inesperado. | | [ITEM_UNAVAILABLE](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#ITEM_UNAVAILABLE()) | Este error ocurre principalmente durante la fase de pruebas. Puede significar que los productos no están en producción o que el usuario no pertenece al grupo de Testers en Google Play. | | ADAPTY_NOT_INITIALIZED | El SDK de Adapty no está activado.
Lo más habitual es que ocurra cuando una pantalla de inicio u otro hook de UI temprano llama a métodos de Adapty antes de que `Adapty.activate` haya terminado. El síntoma es intermitente y puede no reproducirse en un emulador porque los tiempos en dispositivos reales son distintos. Espera a que `Adapty.activate` finalice antes de ejecutar cualquier otra llamada al SDK. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order) para ver la secuencia completa. También debes [configurar el SDK de Adapty](sdk-installation-android#activate-adapty-module-of-adapty-sdk) correctamente usando el método `Adapty.activate`. | | PROFILE_WAS_CHANGED | El perfil del usuario cambió durante la operación.
Esto ocurre cuando se llama a un método mientras `Adapty.identify` todavía está en curso: la llamada en vuelo aterriza en un perfil que está a punto de ser reemplazado y el SDK la rechaza. Espera a que `Adapty.identify` finalice antes de ejecutar otras llamadas al SDK. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order). | | PRODUCT_NOT_FOUND | Este error indica que el producto solicitado para la compra no está disponible en el store. | | INVALID_JSON |

El JSON del paywall de respaldo local no es válido.

Corrige tu paywall en inglés predeterminado y luego reemplaza los paywalls locales no válidos. Consulta el tema [Personalizar el paywall con Remote Config](customize-paywall-with-remote-config) para saber cómo corregir un paywall, y [Definir paywalls de respaldo locales](fallback-paywalls) para saber cómo reemplazar los paywalls locales.

| |

CURRENT_SUBSCRIPTION_TO_UPDATE

\_NOT_FOUND_IN_HISTORY

| La suscripción original que debe reemplazarse no se encontró en las suscripciones activas. | | [BILLING_SERVICE_TIMEOUT](https://developer.android.com/google/play/billing/errors#service_timeout_error_code_-3) | Este error indica que la solicitud alcanzó el tiempo de espera máximo antes de que Google Play pudiera responder. Puede deberse, por ejemplo, a un retraso en la ejecución de la acción solicitada por la llamada a la Play Billing Library. | | [FEATURE_NOT_SUPPORTED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#FEATURE_NOT_SUPPORTED()) | La función solicitada no es compatible con la Play Store en el dispositivo actual. | | [BILLING_SERVICE_DISCONNECTED](https://developer.android.com/google/play/billing/errors#service_disconnected_error_code_-1) | Este error indica que la conexión de la app cliente con el servicio de Google Play Store a través del `BillingClient` se ha interrumpido. | | [BILLING_SERVICE_UNAVAILABLE](https://developer.android.com/google/play/billing/errors#service_unavailable_error_code_2) | Este error indica que el servicio de facturación de Google Play no está disponible en este momento. En la mayoría de los casos significa que hay un problema de conexión de red entre el dispositivo cliente y los servicios de Google Play Billing. | | [BILLING_UNAVAILABLE](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) |

Este error indica que ocurrió un problema de facturación durante el proceso de compra. Las posibles causas son:

1. La app de Play Store en el dispositivo del usuario no está instalada o está desactualizada.

2. El usuario se encuentra en un país no compatible.

3. El usuario forma parte de una cuenta empresarial cuyo administrador ha desactivado las compras.

4. Google Play no pudo cargar el método de pago del usuario (por ejemplo, una tarjeta de crédito caducada).

5. El usuario no ha iniciado sesión en la app de Play Store.

| | [DEVELOPER_ERROR](https://developer.android.com/google/play/billing/errors#developer_error) | Este error indica que estás usando una API de forma incorrecta. | | [BILLING_ERROR](https://developer.android.com/google/play/billing/errors#error_error_code_6) | Este error indica un problema interno del propio Google Play. | | [ITEM_ALREADY_OWNED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#ITEM_ALREADY_OWNED()) | El producto ya fue comprado anteriormente. | | [ITEM_NOT_OWNED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#ITEM_NOT_OWNED()) | Este error indica que la acción solicitada sobre el ítem falló porque el usuario no es su propietario. | | [BILLING_NETWORK_ERROR](https://developer.android.com/google/play/billing/errors#network_error_error_code_12) | Este error indica que hubo un problema con la conexión de red entre el dispositivo y los sistemas de Play. | | NO_PRODUCT_IDS_FOUND |

Este error indica que ninguno de los productos del paywall está disponible en el store.

Si encuentras este error, sigue los pasos a continuación para resolverlo:

  1. Comprueba que todos los productos se hayan añadido al Adapty Dashboard.
  2. Asegúrate de que el **Package name** de tu app coincide con el de Google Play Console.
  3. Verifica que los identificadores de producto de los stores coincidan con los que has añadido al Dashboard. Ten en cuenta que los identificadores no deben incluir el Bundle ID, a menos que ya esté incluido en el store.
  4. Confirma que el estado de pago de la app sea **Active** en la configuración fiscal de Google. Asegúrate de que tu información fiscal esté actualizada y que tus certificados sean válidos.
  5. Comprueba que haya una cuenta bancaria vinculada a la app para que sea elegible para la monetización.
  6. Verifica si los productos están disponibles en tu región.
  7. Asegúrate de que tu app esté en uno de los tracks de prueba. El track **Internal testing** es la opción más sencilla, ya que no requiere revisión y mantiene la app oculta para los clientes.
| | NO_PURCHASES_TO_RESTORE | Este error indica que Google Play no encontró ninguna compra para restaurar. | | AUTHENTICATION_ERROR | Debes [configurar el SDK de Adapty](sdk-installation-android#activate-adapty-module-of-adapty-sdk) correctamente usando el método `Adapty.activate`. | | BAD_REQUEST | Solicitud incorrecta.
Asegúrate de haber completado todos los pasos necesarios para [integrarte con Google Play](google-play-store-connection-configuration). | | SERVER_ERROR | Error del servidor. | | REQUEST_FAILED | Este error indica un problema de red que no se puede definir con precisión. | | DECODING_FAILED | No fue posible decodificar la respuesta.
Revisa tu código y asegúrate de que los parámetros que envías son válidos. Por ejemplo, este error puede indicar que estás usando una clave API no válida. | | ANALYTICS_DISABLED | No podemos gestionar eventos de análisis porque los has [desactivado](analytics-integration#disabling-external-analytics-for-a-specific-customer). | | WRONG_PARAMETER | Este error indica que alguno de tus parámetros no es correcto: está en blanco cuando no puede estarlo, es del tipo incorrecto, etc. | ## Otros problemas \{#other-issues\} Si todavía no has encontrado una solución, los siguientes pasos pueden ser: - **Actualizar el SDK a la última versión**: Siempre recomendamos actualizar a las últimas versiones del SDK, ya que son más estables e incluyen correcciones de problemas conocidos. - **Contactar al equipo de soporte u obtener ayuda de otros desarrolladores** en el [foro de soporte](https://adapty.featurebase.app/). - **Contactar al equipo de soporte a través de [support@adapty.io](mailto:support@adapty.io) o por el chat**: Si no puedes actualizar el SDK o la actualización no resolvió el problema, contacta a nuestro equipo de soporte. Ten en cuenta que tu problema se resolverá más rápido si [activas el registro detallado](sdk-installation-android#logging) y compartes los logs con el equipo. También puedes adjuntar fragmentos de código relevantes. --- # File: migration-to-android-312 --- --- title: "Migrar el SDK de Adapty Android a v. 3.12" description: "Migra al SDK de Adapty Android v3.12 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- En el SDK de Adapty 3.12.0, hemos eliminado el método `logShowOnboarding` del SDK. Si has estado usando este método, dejará de estar disponible al actualizar el SDK a la versión 3.12 o posterior. En su lugar, puedes [crear onboardings en el constructor de onboardings sin código de Adapty](onboardings). El análisis de estos onboardings se registra automáticamente y dispones de numerosas opciones de personalización. --- # File: migration-to-android-310 --- --- title: "Guía de migración al SDK de Adapty para Android 3.10.0" description: "" --- El SDK de Adapty 3.10.0 es una versión mayor que introduce mejoras que pueden requerir algunos pasos de migración de tu parte: 1. `AdaptyUiPersonalizedOfferResolver` ha sido eliminado. Si lo estás usando, pásalo en el callback `onAwaitingPurchaseParams`. 2. Actualiza la firma del método `onAwaitingSubscriptionUpdateParams` para los paywalls del Paywall Builder. ## Actualizar el callback de parámetros de compra \{#update-purchase-parameters-callback\} El método `onAwaitingSubscriptionUpdateParams` ha sido renombrado a `onAwaitingPurchaseParams` y ahora usa `AdaptyPurchaseParameters` en lugar de `AdaptySubscriptionUpdateParameters`. Esto te permite especificar parámetros de reemplazo de suscripción (crossgrade) e indicar si el precio es personalizado ([más información](https://developer.android.com/google/play/billing/integrate#personalized-price)), junto con otros parámetros de compra. ```diff showLineNumbers - override fun onAwaitingSubscriptionUpdateParams( - product: AdaptyPaywallProduct, - context: Context, - onSubscriptionUpdateParamsReceived: SubscriptionUpdateParamsCallback, - ) { - onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters(...)) - } + override fun onAwaitingPurchaseParams( + product: AdaptyPaywallProduct, + context: Context, + onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback, + ): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked { + onPurchaseParamsReceived( + AdaptyPurchaseParameters.Builder() + .withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...)) + .withOfferPersonalized(true) + .build() + ) + return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked + } ``` Si no se necesitan parámetros adicionales, puedes usar simplemente: ```kotlin showLineNumbers + override fun onAwaitingPurchaseParams( product: AdaptyPaywallProduct, context: Context, onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback, ): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked { onPurchaseParamsReceived(AdaptyPurchaseParameters.Empty) return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked } ``` --- # File: migration-to-android-sdk-34 --- --- title: "Migrar el SDK de Adapty para Android a la versión 3.4" description: "Migra al SDK de Adapty para Android v3.4 para obtener mejor rendimiento y nuevas funciones de monetización." --- El SDK de Adapty 3.4.0 es una versión principal que introduce mejoras que requieren pasos de migración por tu parte. ## Actualizar los archivos de paywall de respaldo \{#update-fallback-paywall-files\} Actualiza los archivos de paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK: 1. [Descarga los archivos de paywall de respaldo actualizados](fallback-paywalls) desde el Adapty Dashboard. 2. [Reemplaza los paywalls de respaldo existentes en tu app](android-use-fallback-paywalls) con los nuevos archivos. ## Actualizar la implementación del Modo Observador \{#update-implementation-of-observer-mode\} Si usas el Modo Observador, asegúrate de actualizar su implementación. En versiones anteriores, tenías que restaurar las compras para que Adapty pudiera reconocer las transacciones realizadas a través de tu propia infraestructura, ya que Adapty no tenía acceso directo a ellas en el Modo Observador. Si usabas paywalls, también debías asociar manualmente cada transacción con el paywall que la había iniciado. En la nueva versión, debes reportar explícitamente cada transacción para que Adapty pueda reconocerla. Si usas paywalls, también necesitas pasar el ID de variación para vincular la transacción con el paywall utilizado. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: ```diff showLineNumbers - Adapty.restorePurchases { result -> - if (result is AdaptyResult.Success) { - // success - } - } - - Adapty.setVariationId(transactionId, variationId) { error -> - if (error == null) { - // success - } - } + val transactionInfo = TransactionInfo.fromPurchase(purchase) + + Adapty.reportTransaction(transactionInfo, variationId) { result -> + if (result is AdaptyResult.Success) { + // success + } + } ``` ```diff showLineNumbers - Adapty.restorePurchases(result -> { - if (result instanceof AdaptyResult.Success) { - // success - } - }); - - Adapty.setVariationId(transactionId, variationId, error -> { - if (error == null) { - // success - } - }); + TransactionInfo transactionInfo = TransactionInfo.fromPurchase(purchase); + + Adapty.reportTransaction(transactionInfo, variationId, result -> { + if (result instanceof AdaptyResult.Success) { + // success + } + }); ``` --- # File: migration-to-android330 --- --- title: "Migrar el SDK de Adapty para Android a la v. 3.3" description: "Migra al SDK de Adapty para Android v3.3 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK de Adapty 3.3.0 es una versión mayor que incluye mejoras que, no obstante, pueden requerir algunos pasos de migración por tu parte. 1. Actualiza cómo gestionas las compras en paywalls que no se han creado con Paywall Builder. Deja de procesar los códigos de error `USER_CANCELED` y `PENDING_PURCHASE`. Una compra cancelada ya no se considera un error y ahora aparecerá en los resultados de compra sin error. 2. Sustituye los eventos `onPurchaseCanceled` y `onPurchaseSuccess` por el nuevo evento `onPurchaseFinished` para paywalls creados con Paywall Builder. Este cambio se debe al mismo motivo: las compras canceladas ya no se tratan como errores y se incluirán en los resultados de compra sin error. 3. Cambia la firma del método `onAwaitingSubscriptionUpdateParams` para paywalls de Paywall Builder. 4. Actualiza el método utilizado para proporcionar paywalls de respaldo si pasas el URI del archivo directamente. 5. Actualiza las configuraciones de integración para Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase y Google Analytics, Mixpanel, OneSignal, Pushwoosh. ## Actualizar la compra \{#update-making-purchase\} Las compras canceladas y pendientes anteriormente se consideraban errores y devolvían los códigos `USER_CANCELED` y `PENDING_PURCHASE`, respectivamente. Ahora se usa una nueva clase `AdaptyPurchaseResult` para indicar compras canceladas, exitosas y pendientes. Actualiza el código de compra de la siguiente manera: ```diff Adapty.makePurchase(activity, product) { result -> when (result) { is AdaptyResult.Success -> { - val info = result.value - val profile = info?.profile - - if (profile?.accessLevels?.get("YOUR_ACCESS_LEVEL")?.isActive == true) { - // Grant access to the paid features - } + when (val purchaseResult = result.value) { + is AdaptyPurchaseResult.Success -> { + val profile = purchaseResult.profile + if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { + // Grant access to the paid features + } + } + + is AdaptyPurchaseResult.UserCanceled -> { + // Handle the case where the user canceled the purchase + } + + is AdaptyPurchaseResult.Pending -> { + // Handle deferred purchases (e.g., the user will pay offline with cash + } + } } is AdaptyResult.Error -> { val error = result.error // Handle the error } } } ``` Para ver el ejemplo de código completo, consulta la página [Realizar compras en la app móvil](android-making-purchases#make-purchase). ## Modificar eventos de compra en el Paywall Builder \{#modify-paywall-builder-purchase-events\} 1. Añade el evento `onPurchaseFinished`: ```diff showLineNumbers + public override fun onPurchaseFinished( + purchaseResult: AdaptyPurchaseResult, + product: AdaptyPaywallProduct, + context: Context, + ) { + when (purchaseResult) { + is AdaptyPurchaseResult.Success -> { + // Otorgar acceso a las funciones de pago + } + is AdaptyPurchaseResult.UserCanceled -> { + // Manejar el caso en que el usuario canceló la compra + } + is AdaptyPurchaseResult.Pending -> { + // Manejar compras diferidas (p. ej., el usuario pagará en efectivo fuera de línea) + } + } + } ``` Para ver el ejemplo de código completo, consulta [Compra exitosa, cancelada o pendiente](android-handling-events#successful-canceled-or-pending-purchase) y la descripción del evento. 2. Elimina el procesamiento del evento `onPurchaseCancelled`: ```diff showLineNumbers - public override fun onPurchaseCanceled( - product: AdaptyPaywallProduct, - context: Context, - ) {} ``` 3. Elimina `onPurchaseSuccess`: ```diff showLineNumbers - public override fun onPurchaseSuccess( - profile: AdaptyProfile?, - product: AdaptyPaywallProduct, - context: Context, - ) { - // Your logic on successful purchase - } ``` ## Cambiar la firma del método onAwaitingSubscriptionUpdateParams \{#change-the-signature-of-on-awaiting-subscription-update-params-method\} Ahora, si se compra una nueva suscripción mientras otra sigue activa, llama a `onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters...))` si la nueva suscripción debe reemplazar a la que está activa, o a `onSubscriptionUpdateParamsReceived(null)` si la suscripción activa debe permanecer activa y la nueva debe añadirse por separado: ```diff showLineNumbers - public override fun onAwaitingSubscriptionUpdateParams( - product: AdaptyPaywallProduct, - context: Context, - ): AdaptySubscriptionUpdateParameters? { - return AdaptySubscriptionUpdateParameters(...) - } + public override fun onAwaitingSubscriptionUpdateParams( + product: AdaptyPaywallProduct, + context: Context, + onSubscriptionUpdateParamsReceived: SubscriptionUpdateParamsCallback, + ) { + onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters(...)) + } ``` Consulta la sección [Actualizar suscripción](android-handling-events#upgrade-subscription) de la documentación para ver el ejemplo de código completo. ## Actualización del método para proporcionar paywalls de respaldo \{#update-providing-fallback-paywalls\} Si pasas una URI de archivo para proporcionar paywalls de respaldo, actualiza la forma en que lo haces de la siguiente manera: ```diff showLineNumbers val fileUri: Uri = // Get the URI for the file with fallback paywalls - Adapty.setFallbackPaywalls(fileUri, callback) + Adapty.setFallbackPaywalls(FileLocation.fromFileUri(fileUri), callback) ``` ```diff showLineNumbers Uri fileUri = // Obtén el URI del archivo con los paywalls de respaldo - Adapty.setFallbackPaywalls(fileUri, callback); + Adapty.setFallbackPaywalls(FileLocation.fromFileUri(fileUri), callback); ``` ## Actualizar la configuración del SDK de integraciones de terceros \{#update-third-party-integration-sdk-configuration\} Para garantizar que las integraciones funcionen correctamente con el SDK de Android de Adapty 3.3.0 y versiones posteriores, actualiza las configuraciones del SDK para las siguientes integraciones tal como se describe en las secciones a continuación. ### Adjust Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Adjust](adjust#connect-your-app-to-adjust). ```diff showLineNumbers - Adjust.getAttribution { attribution -> - if (attribution == null) return@getAttribution - - Adjust.getAdid { adid -> - if (adid == null) return@getAdid - - Adapty.updateAttribution(attribution, AdaptyAttributionSource.ADJUST, adid) { error -> - // Handle the error - } - } - } + Adjust.getAdid { adid -> + if (adid == null) return@getAdid + + Adapty.setIntegrationIdentifier("adjust_device_id", adid) { error -> + if (error != null) { + // Handle the error + } + } + } + + Adjust.getAttribution { attribution -> + if (attribution == null) return@getAttribution + + Adapty.updateAttribution(attribution, "adjust") { error -> + if (error != null) { + // Handle the error + } + } + } ``` ```diff showLineNumbers val config = AdjustConfig(context, adjustAppToken, environment) config.setOnAttributionChangedListener { attribution -> attribution?.let { attribution -> - Adapty.updateAttribution(attribution, AdaptyAttributionSource.ADJUST) { error -> + Adapty.updateAttribution(attribution, "adjust") { error -> if (error != null) { // Handle the error } } } } Adjust.onCreate(config) ``` ### AirBridge Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AirBridge](airbridge#connect-your-app-to-airbridge). ```diff showLineNumbers Airbridge.getDeviceInfo().getUUID(object: AirbridgeCallback.SimpleCallback() { override fun onSuccess(result: String) { - val params = AdaptyProfileParameters.Builder() - .withAirbridgeDeviceId(result) - .build() - Adapty.updateProfile(params) { error -> - if (error != null) { - // Handle the error - } - } + Adapty.setIntegrationIdentifier("airbridge_device_id", result) { error -> + if (error != null) { + // Handle the error + } + } } override fun onFailure(throwable: Throwable) { } }) ``` ### Amplitude Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Amplitude](amplitude#sdk-configuration). ```diff showLineNumbers // For Amplitude maintenance SDK (obsolete) val amplitude = Amplitude.getInstance() val amplitudeDeviceId = amplitude.getDeviceId() val amplitudeUserId = amplitude.getUserId() //for actual Amplitude Kotlin SDK val amplitude = Amplitude( Configuration( apiKey = AMPLITUDE_API_KEY, context = applicationContext ) ) val amplitudeDeviceId = amplitude.store.deviceId val amplitudeUserId = amplitude.store.userId // - val params = AdaptyProfileParameters.Builder() - .withAmplitudeDeviceId(amplitudeDeviceId) - .withAmplitudeUserId(amplitudeUserId) - .build() - Adapty.updateProfile(params) { error -> - if (error != null) { - // Handle the error - } - } + Adapty.setIntegrationIdentifier("amplitude_user_id", amplitudeUserId) { error -> + if (error != null) { + // Handle the error + } + } + Adapty.setIntegrationIdentifier("amplitude_device_id", amplitudeDeviceId) { error -> + if (error != null) { + // Handle the error + } + } ``` ### AppMetrica Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AppMetrica](appmetrica#sdk-configuration). ```diff showLineNumbers val startupParamsCallback = object: StartupParamsCallback { override fun onReceive(result: StartupParamsCallback.Result?) { val deviceId = result?.deviceId ?: return - val params = AdaptyProfileParameters.Builder() - .withAppmetricaDeviceId(deviceId) - .withAppmetricaProfileId("YOUR_ADAPTY_CUSTOMER_USER_ID") - .build() - Adapty.updateProfile(params) { error -> - if (error != null) { - // Handle the error - } - } + Adapty.setIntegrationIdentifier("appmetrica_device_id", deviceId) { error -> + if (error != null) { + // Handle the error + } + } + + Adapty.setIntegrationIdentifier("appmetrica_profile_id", "YOUR_ADAPTY_CUSTOMER_USER_ID") { error -> + if (error != null) { + // Handle the error + } + } } override fun onRequestError( reason: StartupParamsCallback.Reason, result: StartupParamsCallback.Result? ) { // Handle the error } } AppMetrica.requestStartupParams(context, startupParamsCallback, listOf(StartupParamsCallback.APPMETRICA_DEVICE_ID)) ``` ### AppsFlyer Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AppsFlyer](appsflyer#connect-your-app-to-appsflyer). ```diff showLineNumbers val conversionListener: AppsFlyerConversionListener = object : AppsFlyerConversionListener { override fun onConversionDataSuccess(conversionData: Map) { - Adapty.updateAttribution( - conversionData, - AdaptyAttributionSource.APPSFLYER, - AppsFlyerLib.getInstance().getAppsFlyerUID(context) - ) { error -> - if (error != null) { - // Handle the error - } - } + val uid = AppsFlyerLib.getInstance().getAppsFlyerUID(context) + Adapty.setIntegrationIdentifier("appsflyer_id", uid) { error -> + if (error != null) { + // Handle the error + } + } + Adapty.updateAttribution(conversionData, "appsflyer") { error -> + if (error != null) { + // Handle the error + } + } } } ``` ### Branch Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Branch](branch#connect-your-app-to-branch). ```diff showLineNumbers // Login and update attribution Branch.getAutoInstance(this) .setIdentity("YOUR_USER_ID") { referringParams, error -> referringParams?.let { data -> - Adapty.updateAttribution(data, AdaptyAttributionSource.BRANCH) { error -> - if (error != null) { - // Handle the error - } - } + Adapty.updateAttribution(data, "branch") { error -> + if (error != null) { + // Handle the error + } + } } } // Logout Branch.getAutoInstance(context).logout() ``` ### Facebook Ads Actualiza el código de tu aplicación móvil como se indica a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Facebook Ads](facebook-ads#connect-your-app-to-facebook-ads). ```diff showLineNumbers - val builder = AdaptyProfileParameters.Builder() - .withFacebookAnonymousId(AppEventsLogger.getAnonymousAppDeviceGUID(context)) - - Adapty.updateProfile(builder.build()) { error -> - if (error != null) { - // Handle the error - } - } + Adapty.setIntegrationIdentifier( + "facebook_anonymous_id", + AppEventsLogger.getAnonymousAppDeviceGUID(context) + ) { error -> + if (error != null) { + // Handle the error + } + } ``` ### Firebase y Google Analytics \{#firebase-and-google-analytics\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Firebase y Google Analytics](firebase-and-google-analytics). ```diff showLineNumbers // After Adapty.activate() FirebaseAnalytics.getInstance(context).appInstanceId.addOnSuccessListener { appInstanceId -> - Adapty.updateProfile( - AdaptyProfileParameters.Builder() - .withFirebaseAppInstanceId(appInstanceId) - .build() - ) { error -> - if (error != null) { - // Handle the error - } - } + Adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId) { error -> + if (error != null) { + // Handle the error + } + } } ``` ```diff showLineNumbers // Después de Adapty.activate() - FirebaseAnalytics.getInstance(context).getAppInstanceId().addOnSuccessListener(appInstanceId -> { - AdaptyProfileParameters params = new AdaptyProfileParameters.Builder() - .withFirebaseAppInstanceId(appInstanceId) - .build(); - - Adapty.updateProfile(params, error -> { - if (error != null) { - // Handle the error - } - }); - }); + FirebaseAnalytics.getInstance(context).getAppInstanceId().addOnSuccessListener(appInstanceId -> { + Adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId, error -> { + if (error != null) { + // Handle the error + } + }); + }); ``` ### Mixpanel Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Mixpanel](mixpanel#sdk-configuration). ```diff showLineNumbers - val params = AdaptyProfileParameters.Builder() - .withMixpanelUserId(mixpanelAPI.distinctId) - .build() - - Adapty.updateProfile(params) { error -> - if (error != null) { - // Handle the error - } - } + Adapty.setIntegrationIdentifier("mixpanel_user_id", mixpanelAPI.distinctId) { error -> + if (error != null) { + // Handle the error + } + } ``` ### OneSignal Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con OneSignal](onesignal#sdk-configuration). ```diff showLineNumbers // SubscriptionID val oneSignalSubscriptionObserver = object: IPushSubscriptionObserver { override fun onPushSubscriptionChange(state: PushSubscriptionChangedState) { - val params = AdaptyProfileParameters.Builder() - .withOneSignalSubscriptionId(state.current.id) - .build() - - Adapty.updateProfile(params) { error -> + Adapty.setIntegrationIdentifier("one_signal_subscription_id", state.current.id) { error -> if (error != null) { // Handle the error } } } } ``` ```diff showLineNumbers // SubscriptionID IPushSubscriptionObserver oneSignalSubscriptionObserver = state -> { - AdaptyProfileParameters params = new AdaptyProfileParameters.Builder() - .withOneSignalSubscriptionId(state.getCurrent().getId()) - .build(); - Adapty.updateProfile(params, error -> { + Adapty.setIntegrationIdentifier("one_signal_subscription_id", state.getCurrent().getId(), error -> { if (error != null) { // Handle the error } }); }; ``` ```diff showLineNumbers // PlayerID val osSubscriptionObserver = OSSubscriptionObserver { stateChanges -> stateChanges?.to?.userId?.let { playerId -> - val params = AdaptyProfileParameters.Builder() - .withOneSignalPlayerId(playerId) - .build() - - Adapty.updateProfile(params) { error -> + Adapty.setIntegrationIdentifier("one_signal_player_id", playerId) { error -> if (error != null) { // Handle the error } - } } } ``` ```diff showLineNumbers // PlayerID OSSubscriptionObserver osSubscriptionObserver = stateChanges -> { OSSubscriptionState to = stateChanges != null ? stateChanges.getTo() : null; String playerId = to != null ? to.getUserId() : null; if (playerId != null) { - AdaptyProfileParameters params1 = new AdaptyProfileParameters.Builder() - .withOneSignalPlayerId(playerId) - .build(); - - Adapty.updateProfile(params1, error -> { + Adapty.setIntegrationIdentifier("one_signal_player_id", playerId, error -> { if (error != null) { // Handle the error } - }); } }; ``` ### Pushwoosh Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo completo de código, consulta la [configuración del SDK para la integración con Pushwoosh](pushwoosh#sdk-configuration). ```diff showLineNumbers - val params = AdaptyProfileParameters.Builder() - .withPushwooshHwid(Pushwoosh.getInstance().hwid) - .build() - Adapty.updateProfile(params) { error -> + Adapty.setIntegrationIdentifier("pushwoosh_hwid", Pushwoosh.getInstance().hwid) { error -> if (error != null) { // Handle the error } } ``` ```diff showLineNumbers - AdaptyProfileParameters params = new AdaptyProfileParameters.Builder() - .withPushwooshHwid(Pushwoosh.getInstance().getHwid()) - .build(); - - Adapty.updateProfile(params, error -> { + Adapty.setIntegrationIdentifier("pushwoosh_hwid", Pushwoosh.getInstance().getHwid(), error -> { if (error != null) { // Handle the error } }); ``` --- # File: migration-to-android-sdk-v3 --- --- title: "Migrar el SDK de Adapty para Android a la v. 3.0" description: "Migra al SDK de Adapty para Android v3.0 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK de Adapty v.3.0 incluye soporte para el nuevo y emocionante [Adapty Paywall Builder](adapty-paywall-builder), la nueva versión de la herramienta visual sin código para crear paywalls. Con su máxima flexibilidad y ricas capacidades de diseño, tus paywalls serán más efectivos y rentables. Los SDKs de Adapty se distribuyen como BoM (Bill of Materials), lo que garantiza que las versiones del SDK de Adapty y del SDK de AdaptyUI en tu aplicación se mantengan sincronizadas. Para migrar a la v3.0, actualiza tu código de la siguiente manera: ```diff showLineNumbers dependencies { ... - implementation 'io.adapty:android-sdk:2.11.5' - implementation 'io.adapty:android-ui:2.11.3' + implementation platform('io.adapty:adapty-bom:3.0.4') + implementation 'io.adapty:android-sdk' + implementation 'io.adapty:android-ui' } ``` ```diff showLineNumbers dependencies { ... - implementation("io.adapty:android-sdk:2.11.5") - implementation("io.adapty:android-ui:2.11.3") + implementation(platform("io.adapty:adapty-bom:3.0.4")) + implementation("io.adapty:android-sdk") + implementation("io.adapty:android-ui") } ``` ```diff showLineNumbers //libs.versions.toml [versions] .. - adapty = "2.11.5" - adaptyUi = "2.11.3" + adaptyBom = "3.0.4" [libraries] .. - adapty = { group = "io.adapty", name = "android-sdk", version.ref = "adapty" } - adapty-ui = { group = "io.adapty", name = "android-ui", version.ref = "adaptyUi" } + adapty-bom = { module = "io.adapty:adapty-bom", version.ref = "adaptyBom" } + adapty = { module = "io.adapty:android-sdk" } + adapty-ui = { module = "io.adapty:android-ui" } //module-level build.gradle.kts dependencies { ... + implementation(libs.adapty.bom) implementation(libs.adapty) implementation(libs.adapty.ui) } ``` --- # End of Documentation _Generated on: 2026-05-15T20:13:57.456Z_ _Successfully processed: 39/39 files_ # API - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.458Z Total files: 18 --- # File: developer-cli --- --- title: "Developer CLI" description: "Overview of the Adapty Developer CLI." --- The **Adapty Developer CLI** is a command-line tool for managing your Adapty account without opening the Dashboard. It provides the main configuration capabilities, accessible from your terminal or automated environments. **What you can do with the CLI:** - Create and configure iOS and Android apps in your Adapty account - Define access levels — the subscription tiers your app checks at runtime - Set up products and map them to App Store and Google Play store IDs - Create paywalls and assign products to them - Configure placements to fetch paywalls via the SDK :::link Using an AI assistant or MCP client? An [Adapty CLI skill](https://github.com/adaptyteam/adapty-cli/tree/main/skills/adapty-cli) is available to help LLMs work with the CLI. ::: --- # File: developer-cli-quickstart --- --- title: "Guía de inicio rápido para el Developer CLI de Adapty" description: "Configura tu cuenta de Adapty de principio a fin usando el Developer CLI: desde la creación de la app hasta un placement activo, en unos pocos comandos." --- :::link ¿Usas un asistente de IA? Hay una [skill del CLI de Adapty](https://github.com/adaptyteam/adapty-cli/tree/main/skills/adapty-cli) disponible para ayudar a los LLMs a trabajar con el CLI. ::: El CLI de Adapty te permite configurar tu app por completo desde la línea de comandos. Úsalo como alternativa al [inicio rápido desde el dashboard](integrate-payments) si prefieres trabajar en la terminal o con [clientes MCP](https://github.com/adaptyteam/adapty-cli/tree/main/skills/adapty-cli). :::note Conectar Adapty a App Store Connect y Google Play todavía requiere una configuración puntual en el dashboard, que se explica en el paso 3. ::: Al terminar, tu app, nivel de acceso, producto, paywall y placement estarán todos visibles en el [Adapty Dashboard](https://app.adapty.io). ## 1. Instala el CLI \{#1-install-the-cli\} Requiere [Node.js](https://nodejs.org/en/download) 18 o posterior. Para instalar el CLI, ejecuta el comando: ```bash npm install -g adapty ``` O directamente: ```bash npx adapty auth login ``` ## 2. Autentícate \{#2-authenticate\} Ejecuta el comando de inicio de sesión para conectar el CLI con tu cuenta de Adapty. ```bash adapty auth login ``` El CLI abre una pestaña del navegador. Verifica que el código que aparece en la terminal coincida con el del navegador y haz clic en **Authorize**. La terminal confirmará cuando la autenticación se haya completado. ## 3. Crea tu app \{#3-create-your-app\} Una app en Adapty representa tu aplicación móvil. Una misma app de Adapty se conecta tanto a App Store como a Google Play, por lo que solo necesitas crear una, independientemente de en cuántos stores publiques. ```bash adapty apps create --title "My App" --platform ios --platform android --apple-bundle-id com.example.app --google-bundle-id com.example.app ``` ```bash adapty apps create --title "My App" --platform ios --apple-bundle-id com.example.app ``` ```bash adapty apps create --title "My App" --platform android --google-bundle-id com.example.app ``` El comando devuelve un ``. Usa este ID en todos los comandos siguientes. :::important Antes de continuar, conecta tu app a App Store Connect y Google Play en el Adapty Dashboard. Los IDs de producto de ambos stores son necesarios en el paso 5. - [Conectar App Store Connect](app-store-connection-configuration) - [Conectar Google Play](google-play-store-connection-configuration) ::: ## 4. Crea un nivel de acceso (opcional) \{#4-create-an-access-level-optional\} Los [niveles de acceso](access-level) controlan a qué puede acceder el usuario tras una compra. En lugar de comprobar si un usuario compró un producto concreto, tu app verifica si el usuario tiene un determinado nivel de acceso. Esto desacopla la lógica de tu app de los IDs de producto específicos. Con cada nueva app se crea automáticamente un nivel de acceso `premium`. **Para la mayoría de las apps puedes saltarte este paso.** Usa `premium` como ID de nivel de acceso en el paso 5. Solo ejecuta este comando si distintos productos desbloquean distintas funciones para diferentes grupos de usuarios; por ejemplo, si un suscriptor "Basic" y uno "Pro" tienen acceso a partes diferentes de la app. ```bash adapty access-levels create --app --sdk-id "pro" --title "Pro" ``` - `--sdk-id` es el identificador que usarás en el código de tu app para comprobar si una función debe estar disponible para el usuario (por ejemplo, `if user.hasAccessLevel("pro")`). Si te saltas este paso y usas el nivel de acceso por defecto, su `--sdk-id` es `premium`. - `--title` es una etiqueta de visualización para tu referencia en el Adapty Dashboard. El comando devuelve un ``. ## 5. Crea un producto \{#5-create-a-product\} En Adapty, un [producto](product) representa cualquier cosa que tu app vende: una suscripción o una compra única. Los elementos de App Store Connect y Google Play se pueden agrupar en un único producto de Adapty y gestionarse desde un solo lugar. Necesitarás los IDs de producto de cada store: el Apple product ID de App Store Connect, y el Android product ID y el base plan ID de Google Play Console. Consulta [Productos](quickstart-products) para saber dónde encontrarlos. Si te saltaste el paso 4, usa el `default_access_level.id` que devolvió el comando `apps create` en el paso 3 como tu ``. :::important Los IDs de producto del store que vinculas aquí (`--ios-product-id`, `--android-product-id`) no pueden modificarse una vez creados. Para usar IDs de producto del store diferentes, crea un nuevo producto. ::: ```bash adapty products create --app --title "My Product" --access-level-id --period monthly --ios-product-id --android-product-id --android-base-plan-id ``` ```bash adapty products create --app --title "My Product" --access-level-id --period monthly --ios-product-id ``` ```bash adapty products create --app --title "My Product" --access-level-id --period monthly --android-product-id --android-base-plan-id ``` El comando devuelve un ``. ## 6. Crea un paywall \{#6-create-a-paywall\} Un [paywall](paywalls) es el contenedor que alberga tus productos. En Adapty, los paywalls son la única forma de entregar productos a los usuarios. Todo producto debe estar en un paywall antes de poder aparecer en tu app. :::important Una vez que un paywall está vinculado a un placement, sus productos no se pueden cambiar. Para usar productos diferentes, crea un nuevo paywall y actualiza el placement para que apunte a él. ::: ```bash adapty paywalls create --app --title "My Paywall" --product-id ``` ```bash adapty paywalls create --app --title "My Paywall" --product-id --product-id ``` El comando devuelve un ``. ## 7. Crea un placement \{#7-create-a-placement\} Un [placement](placements) es el punto de tu app donde muestras un paywall. Lo único que tienes que escribir directamente en el código de tu app es el ID del placement. Todo lo demás —qué paywall mostrar y a qué usuarios— se gestiona desde el dashboard sin publicar una nueva versión de la app. `--developer-id` es la cadena que usarás más adelante en el código de tu app cuando le pidas a Adapty qué paywall mostrar en ese punto. Elige algo que describa la ubicación, como `"main"`, `"onboarding"` o `"settings"`. ```bash adapty placements create --app --title "Main" --developer-id "main" --audiences '[{"segment_ids":[],"paywall_id":"","priority":0}]' ``` El flag `--audiences` controla qué paywall se muestra a qué usuarios. El ejemplo anterior establece una audiencia por defecto única: todos los usuarios en este placement ven el mismo paywall. ## ¿Qué viene ahora? \{#whats-next\} Todas las entidades ya son visibles en el [Adapty Dashboard](https://app.adapty.io). A continuación: - [Diseña tu paywall](adapty-paywall-builder): usa el Paywall Builder sin código para añadir imágenes, diseño y textos al paywall que acabas de crear. - [Integra el SDK de Adapty](quickstart-sdk): añade el SDK a tu app para obtener y mostrar el placement. - Dirige distintos [segmentos](segments) de usuarios a diferentes paywalls: consulta [`placements update`](developer-cli-reference#adapty-placements-update) y [`segments list`](developer-cli-reference#adapty-segments-list) en la referencia completa. --- # File: developer-cli-authentication --- --- title: "Autenticación en el CLI para desarrolladores de Adapty" description: "Cómo autenticarse con el CLI para desarrolladores de Adapty." --- :::link ¿Usas un asistente de IA? Hay una [skill de Adapty CLI](https://github.com/adaptyteam/adapty-cli/tree/main/skills/adapty-cli) disponible para ayudar a los LLMs a trabajar con el CLI. ::: El CLI requiere autenticación para llamar a la API de Adapty. ## Iniciar sesión \{#log-in\} Para iniciar sesión: 1. En tu terminal, ejecuta: ```bash adapty auth login ``` 2. El CLI muestra un código de verificación en formato `XXXX-XXXX` y abre el Adapty Dashboard en tu navegador. 3. En la página de autorización, confirma que el código coincide con lo que aparece en tu terminal. 4. Haz clic en **Authorize**. El navegador mostrará "CLI authorized! You can close this tab." 5. De vuelta en la terminal, el CLI confirma que estás autenticado. Si el código expira antes de que autorices, o si haces clic en **Deny**, ejecuta el siguiente comando de nuevo para reiniciar el proceso: ```bash adapty auth login ``` ## Gestionar la autenticación \{#manage-authentication\} ### Comprobar el estado de autenticación \{#check-authentication-status\} Para ver tu estado de autenticación actual, ejecuta: ```bash adapty auth status ``` Cuando estás autenticado, la salida muestra tu correo electrónico, un prefijo de token enmascarado y la ruta al archivo de configuración local: ``` Email: you@example.com Token: abcd1234**** Config: ~/.config/adapty/config.json ``` Cuando no estás autenticado: ``` Not authenticated. Run `adapty auth login`. ``` ### Verificar tu token \{#verify-your-token\} Para confirmar que tu token es válido y ver los detalles de tu cuenta, ejecuta: ```bash adapty auth whoami ``` A diferencia de `adapty auth status`, este comando realiza una solicitud en tiempo real al servidor para verificar el token. ### Cerrar sesión \{#log-out\} Para eliminar tus credenciales almacenadas localmente, ejecuta: ```bash adapty auth logout ``` Esto borra `~/.config/adapty/config.json`. El token sigue siendo válido en el servidor hasta que expira; si necesitas invalidarlo de inmediato, usa `adapty auth revoke` en su lugar. ### Revocar tu token \{#revoke-your-token\} Para invalidar el token en el servidor y eliminarlo localmente, ejecuta: ```bash adapty auth revoke ``` Úsalo cuando quieras invalidar un token por completo, por ejemplo, si crees que tus credenciales pueden haberse visto comprometidas. Tras revocarlo, ejecuta `adapty auth login` para volver a autenticarte. ## Errores de token \{#token-errors\} Si un token se revoca o deja de ser válido, los comandos del CLI devuelven un error 401. Para volver a autenticarte, ejecuta: ```bash adapty auth login ``` --- # File: developer-cli-reference --- --- title: "Referencia completa de la CLI para desarrolladores de Adapty" description: "Referencia completa de todos los comandos de la CLI para desarrolladores de Adapty." --- :::link ¿Usas un asistente de IA? Hay disponible una [skill de Adapty CLI](https://github.com/adaptyteam/adapty-cli/tree/main/skills/adapty-cli) para ayudar a los LLMs a trabajar con la CLI. ::: Este artículo lista todos los comandos de la CLI de Adapty con sus argumentos, flags y valores aceptados. :::link Para configurar la autenticación y gestionar tokens, consulta [Autenticación](developer-cli-authentication). ::: ## Flags globales \{#global-flags\} Estos flags están disponibles en todos los comandos. | Flag | Descripción | |---|---| | `--json` | Mostrar la salida en JSON en lugar de texto formateado | | `--help` | Mostrar la ayuda del comando | Todos los comandos `list` también aceptan flags de paginación: | Flag | Por defecto | Descripción | |---|---|---| | `--page` | `1` | Número de página | | `--page-size` | `20` | Elementos por página (máx.: 100) | ## Apps \{#apps\} Gestiona las apps de tu cuenta de Adapty. Para la configuración desde el dashboard, consulta [App settings](general). ### adapty apps list \{#adapty-apps-list\} Lista todas las apps de tu cuenta de Adapty. ```bash adapty apps list ``` Acepta [flags de paginación](#global-flags). ### adapty apps get \{#adapty-apps-get\} Obtén los detalles de una app específica. ```bash adapty apps get ``` | Argumento | Descripción | |---|---| | `app-id` | ID de la app (UUID) | ### adapty apps create \{#adapty-apps-create\} Crea una nueva app. ```bash adapty apps create --title "My App" --platform ios --apple-bundle-id com.example.app ``` | Flag | Requerido | Descripción | |---|---|---| | `--title` | Sí | Título de la app | | `--platform` | Sí | Plataforma: `ios` o `android`. Repite para ambas: `--platform ios --platform android` | | `--apple-bundle-id` | Requerido con `--platform ios` | Bundle ID de Apple | | `--google-bundle-id` | Requerido con `--platform android` | Bundle ID de Google | ### adapty apps update \{#adapty-apps-update\} Actualiza una app existente. ```bash adapty apps update --title "New Name" ``` | Argumento | Descripción | |---|---| | `app-id` | ID de la app (UUID) | | Flag | Descripción | |---|---| | `--title` | Nuevo título de la app | | `--apple-bundle-id` | Nuevo bundle ID de Apple | | `--google-bundle-id` | Nuevo bundle ID de Google | Se requiere al menos un flag. `--platform` no se puede cambiar después de la creación. ## Niveles de acceso \{#access-levels\} ### adapty access-levels list \{#adapty-access-levels-list\} Lista todos los [niveles de acceso](access-level) de una app. ```bash adapty access-levels list --app ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | Acepta [flags de paginación](#global-flags). ### adapty access-levels get \{#adapty-access-levels-get\} Obtén los detalles de un [nivel de acceso](access-level) específico. ```bash adapty access-levels get --app ``` | Argumento | Descripción | |---|---| | `access-level-id` | ID del nivel de acceso (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | ### adapty access-levels create \{#adapty-access-levels-create\} Crea un nuevo [nivel de acceso](access-level). ```bash adapty access-levels create --app --sdk-id "pro" --title "Pro" ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | | `--sdk-id` | Sí | Identificador utilizado en el código de la app para comprobar el acceso (por ejemplo, `"pro"` o `"premium"`) | | `--title` | Sí | Etiqueta de visualización en el Adapty Dashboard | ### adapty access-levels update \{#adapty-access-levels-update\} Actualiza un [nivel de acceso](access-level) existente. ```bash adapty access-levels update --app --title "Pro Access" ``` | Argumento | Descripción | |---|---| | `access-level-id` | ID del nivel de acceso (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | | `--title` | Sí | Nueva etiqueta de visualización | `--sdk-id` no se puede cambiar después de la creación. ## Productos \{#products\} ### adapty products list \{#adapty-products-list\} Lista todos los [productos](product) de una app. ```bash adapty products list --app ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | Acepta [flags de paginación](#global-flags). ### adapty products get \{#adapty-products-get\} Obtén los detalles de un [producto](product) específico. ```bash adapty products get --app ``` | Argumento | Descripción | |---|---| | `product-id` | ID del producto (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | ### adapty products create \{#adapty-products-create\} Crea un nuevo [producto](product). :::important Los IDs de producto de la store (`--ios-product-id`, `--android-product-id`, `--android-base-plan-id`) no se pueden cambiar después de la creación. Para usar IDs de producto de la store distintos, crea un nuevo producto. ::: ```bash adapty products create --app --title "Monthly" --access-level-id --period monthly --ios-product-id com.example.monthly ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | | `--title` | Sí | Título del producto | | `--access-level-id` | Sí | ID (UUID) del [nivel de acceso](access-level) que desbloquea este producto | | `--period` | Sí | Período de suscripción: `weekly`, `monthly`, `2_months`, `3_months`, `6_months`, `yearly`, `lifetime` | | `--ios-product-id` | Se requiere al menos una plataforma | ID del producto en App Store Connect | | `--android-product-id` | Se requiere al menos una plataforma | ID del producto en Google Play Console | | `--android-base-plan-id` | Requerido con `--android-product-id` salvo que `--period lifetime` | ID del plan base en Google Play Console | ### adapty products update \{#adapty-products-update\} Actualiza un [producto](product) existente. Los IDs de producto de la store (`--ios-product-id`, `--android-product-id`) no se pueden cambiar después de la creación y no están disponibles en este comando. Para usar IDs de producto de la store distintos, crea un nuevo producto. ```bash adapty products update --app --title "Monthly" --access-level-id ``` | Argumento | Descripción | |---|---| | `product-id` | ID del producto (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | | `--title` | No | Título del producto | | `--access-level-id` | No | ID (UUID) del [nivel de acceso](access-level) que desbloquea este producto | ## Paywalls \{#paywalls\} ### adapty paywalls list \{#adapty-paywalls-list\} Lista todos los [paywalls](paywalls) de una app. ```bash adapty paywalls list --app ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | Acepta [flags de paginación](#global-flags). ### adapty paywalls get \{#adapty-paywalls-get\} Obtén los detalles de un [paywall](paywalls) específico. ```bash adapty paywalls get --app ``` | Argumento | Descripción | |---|---| | `paywall-id` | ID del paywall (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | ### adapty paywalls create \{#adapty-paywalls-create\} Crea un nuevo [paywall](paywalls). ```bash adapty paywalls create --app --title "Default Paywall" --product-id ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | | `--title` | Sí | Título del paywall | | `--product-id` | Sí | ID (UUID) del [producto](product). Repite para varios productos: `--product-id --product-id ` | ### adapty paywalls update \{#adapty-paywalls-update\} Reemplaza todos los campos de un [paywall](paywalls) existente. :::important Una vez que un paywall está vinculado a un placement, sus productos no se pueden cambiar. Para usar productos distintos en un paywall en producción, crea un nuevo paywall y actualiza el placement para que apunte a él. ::: ```bash adapty paywalls update --app --title "Default Paywall" --product-id ``` Este comando reemplaza todos los campos del paywall, incluida la lista completa de productos. | Argumento | Descripción | |---|---| | `paywall-id` | ID del paywall (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | | `--title` | Sí | Título del paywall | | `--product-id` | Sí | ID (UUID) del [producto](product). Repite para varios productos: `--product-id --product-id ` | ### adapty paywalls placements \{#adapty-paywalls-placements\} Lista todos los [placements](placements) que actualmente usan un [paywall](paywalls) determinado. ```bash adapty paywalls placements --app ``` | Argumento | Descripción | |---|---| | `paywall-id` | ID del paywall (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | Usa este comando antes de reemplazar un paywall para ver qué placements se verían afectados. ## Placements \{#placements\} ### adapty placements list \{#adapty-placements-list\} Lista todos los [placements](placements) de una app. ```bash adapty placements list --app ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | Acepta [flags de paginación](#global-flags). ### adapty placements get \{#adapty-placements-get\} Obtén los detalles de un [placement](placements) específico. ```bash adapty placements get --app ``` | Argumento | Descripción | |---|---| | `placement-id` | ID del placement (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | La respuesta contiene un array `audiences`. Cada entrada es `{segment_ids, paywall_id, priority}`. La audiencia por defecto tiene `segment_ids: []` y el valor de prioridad más alto (se evalúa en último lugar). La salida formateada en texto también muestra un `Paywall ID` de nivel superior derivado de la audiencia por defecto, por comodidad. Con `--json` se devuelve la forma de la API sin modificar. ### adapty placements create \{#adapty-placements-create\} Crea un nuevo [placement](placements). ```bash adapty placements create --app --title "Main" --developer-id "main" --audiences '[{"segment_ids":[],"paywall_id":"","priority":0}]' ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | | `--title` | Sí | Título del placement | | `--developer-id` | Sí | Identificador de cadena utilizado en el código de la app para solicitar este [placement](placements) | | `--audiences` | Uno de los dos | Array JSON de entradas `{segment_ids, paywall_id, priority}`. Consulta [Forma de las audiencias](#audiences-shape) | | `--paywall-id` | Uno de los dos | **Obsoleto.** ID (UUID) del [paywall](paywalls). Se convierte en el cliente en una única audiencia por defecto | Pasa exactamente uno de `--audiences` o `--paywall-id`. Si se pasan ambos o ninguno, se produce un error. :::warning `--paywall-id` está obsoleto y se eliminará. Al usarlo, la CLI muestra una advertencia en stderr y convierte el valor en una audiencia por defecto. Para nuevas automatizaciones, usa `--audiences`. ::: ### adapty placements update \{#adapty-placements-update\} Reemplaza todos los campos de un [placement](placements) existente. ```bash adapty placements update --app --title "Main" --developer-id "main" --audiences '[{"segment_ids":[],"paywall_id":"","priority":0}]' ``` Este comando reemplaza todos los campos del placement, incluida la lista completa de audiencias. | Argumento | Descripción | |---|---| | `placement-id` | ID del placement (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | | `--title` | Sí | Título del placement | | `--developer-id` | Sí | Identificador de cadena utilizado en el código de la app para solicitar este [placement](placements) | | `--audiences` | Uno de los dos | Array JSON de entradas `{segment_ids, paywall_id, priority}`. Consulta [Forma de las audiencias](#audiences-shape) | | `--paywall-id` | Uno de los dos | **Obsoleto.** ID (UUID) del [paywall](paywalls). Reemplaza todas las audiencias con una única audiencia por defecto | :::warning Al usar `--paywall-id` se sobreescriben todas las audiencias del placement. Las audiencias específicas de segmento se eliminan. Para conservarlas, usa `--audiences` e incluye todas las entradas que quieras mantener. ::: #### Forma de las audiencias \{#audiences-shape\} El flag `--audiences` acepta un array JSON. Cada entrada tiene: | Campo | Tipo | Descripción | |---|---|---| | `segment_ids` | `string[]` | IDs de [segmento](segments) a los que se dirige esta audiencia. Longitud 0 o 1. Un array vacío marca la **audiencia por defecto**: el fallback para usuarios que no coinciden con ningún otro segmento | | `paywall_id` | `string` | ID (UUID) del [paywall](paywalls) que se muestra a los usuarios de esta audiencia | | `priority` | `number` | Basado en 0, único dentro del placement. Las audiencias se evalúan de menor a mayor; la audiencia por defecto debe tener el valor más alto | Un placement debe tener exactamente una audiencia por defecto. Ejemplo con una audiencia segmentada y una por defecto: ```bash adapty placements update --app --title "Main" --developer-id "main" \ --audiences '[{"segment_ids":[""],"paywall_id":"","priority":0},{"segment_ids":[],"paywall_id":"","priority":1}]' ``` Para reemplazar un paywall en varios placements sin perder el enrutamiento por segmento: 1. Encuentra los placements afectados: ```bash adapty paywalls placements --app ``` 2. Para cada uno, lee el array completo de `audiences`: ```bash adapty placements get --app --json ``` 3. Reemplaza los valores de `paywall_id` correspondientes en el cliente. 4. Escribe el payload modificado: ```bash adapty placements update --app --title "" --developer-id "<developer-id>" --audiences '<modified-payload>' ``` ## Segmentos \{#segments\} Los [segmentos](segments) son de solo lectura a través de la CLI. Créalos y edítalos en el [Adapty dashboard](https://app.adapty.io). Usa estos comandos para buscar los IDs de segmento al componer audiencias de placements. ### adapty segments list \{#adapty-segments-list\} Lista todos los [segmentos](segments) de una app. ```bash adapty segments list --app <app-id> ``` | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | Acepta [flags de paginación](#global-flags). ### adapty segments get \{#adapty-segments-get\} Obtén los detalles de un [segmento](segments) específico. ```bash adapty segments get --app <app-id> <segment-id> ``` | Argumento | Descripción | |---|---| | `segment-id` | ID del segmento (UUID) | | Flag | Requerido | Descripción | |---|---|---| | `--app` | Sí | ID de la app (UUID) | La respuesta contiene `id`, `title` y `description`. Las reglas de filtro no están expuestas a través de esta API. ## Auth \{#auth\} | Comando | Descripción | |---|---| | `adapty auth login` | Autenticarse mediante el navegador usando el flujo de dispositivo | | `adapty auth logout` | Borrar las credenciales almacenadas localmente | | `adapty auth whoami` | Verificar el token con el servidor y mostrar información del usuario | | `adapty auth status` | Mostrar el estado de autenticación local sin hacer una llamada al servidor | | `adapty auth revoke` | Revocar el token en el servidor y borrarlo localmente | Consulta [Autenticación](developer-cli-authentication) para ver todos los detalles de cada comando. --- # File: getting-started-with-server-side-api --- --- title: "API del servidor" description: "Primeros pasos con la API del servidor de Adapty para la gestión de suscripciones." --- Con la API puedes: 1. Consultar el estado de suscripción de un usuario. 2. Activar la suscripción de un usuario con un nivel de acceso. 3. Recuperar los atributos de un usuario. 4. Establecer los atributos de un usuario. 5. Obtener y actualizar configuraciones de paywalls. <img src="/assets/shared/img/server.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <p> </p> :::note Para hacer seguimiento de eventos de suscripción, usa la integración de [Webhook](webhook) en Adapty o intégralo directamente con tu servicio existente. ::: ## Caso 1: Sincronizar suscriptores entre web y móvil \{#case-1-sync-subscribers-between-web-and-mobile\} Si usas proveedores de pago web como Stripe, ChargeBee u otros, puedes sincronizar tus suscriptores fácilmente. Así es cómo: 1. <InlineTooltip tooltip="Asigna un ID único a cada usuario">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip>. 2. [Consulta su estado de suscripción](api-adapty/operations/getProfile) usando la API. 3. Si un usuario está en un plan freemium, muestra un paywall en tu sitio web. 4. Tras un pago exitoso, [actualiza el estado de la suscripción](api-adapty/operations/setTransaction) en Adapty a través de la API. 5. Tus suscriptores se mantendrán automáticamente sincronizados con tu app móvil. ## Caso 2: Conceder una suscripción \{#case-2-grant-a-subscription\} :::note Por razones de seguridad, no es posible conceder una suscripción a través del SDK. ::: Si vendes a través de tu propia tienda online, Amazon Appstore, Microsoft Store u cualquier otra plataforma distinta de Google Play y App Store, necesitarás sincronizar esas transacciones con Adapty para otorgar acceso y registrar la transacción en los análisis. 1. <InlineTooltip tooltip="Asigna un ID único a cada usuario">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip>. 2. [Configura una tienda personalizada para tus productos en el Adapty Dashboard](custom-store). 3. Sincroniza la transacción con Adapty usando la petición de API [Set transaction](api-adapty/operations/setTransaction). ## Caso 3: Conceder un nivel de acceso \{#case-3-grant-an-access-level\} Supongamos que estás ejecutando una promoción que ofrece una prueba gratuita de 7 días y quieres que la experiencia sea coherente en todas las plataformas. Para sincronizarlo con la app móvil: 1. <InlineTooltip tooltip="Asigna un ID único a cada usuario">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip>. 2. Usa la API para [conceder acceso premium](api-adapty/operations/grantAccessLevel) durante 7 días. Transcurridos los 7 días, los usuarios que no se suscriban pasarán al nivel gratuito. ## Caso 4: Sincronizar propiedades y atributos personalizados de usuarios \{#case-4-sync-users-properties-and-custom-attributes\} Si tienes atributos personalizados para tus usuarios —como el número de palabras aprendidas en una app de idiomas— también puedes sincronizarlos. 1. <InlineTooltip tooltip="Asigna un ID único a cada usuario">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip>. 2. [Actualiza el atributo](api-adapty/operations/updateProfile) a través de la API o el SDK. Estos atributos personalizados se pueden usar para crear segmentos y ejecutar pruebas A/B. ## Caso 5: Gestionar configuraciones de paywalls \{#case-5-manage-paywall-configurations\} Puedes [actualizar los Remote Configs en los paywalls](api-adapty/operations/updatePaywall) para ajustar dinámicamente la apariencia y el comportamiento de tu paywall sin redesplegar tu app. --- **A continuación:** - Continúa con la [autorización para la API del servidor](ss-authorization) - Peticiones: - [Obtener perfil](api-adapty/operations/getProfile) - [Crear perfil](api-adapty/operations/createProfile) - [Actualizar perfil](api-adapty/operations/updateProfile) - [Eliminar perfil](api-adapty/operations/deleteProfile) - [Conceder nivel de acceso](api-adapty/operations/grantAccessLevel) - [Revocar nivel de acceso](api-adapty/operations/revokeAccessLevel) - [Establecer transacción](api-adapty/operations/setTransaction) - [Validar compra, proporcionar nivel de acceso al cliente e importar su historial de transacciones](api-adapty/operations/validateStripePurchase) - [Añadir identificadores de integración](api-adapty/operations/setIntegrationIdentifiers) - [Obtener paywall](api-adapty/operations/getPaywall) - [Listar paywalls](api-adapty/operations/listPaywalls) - [Actualizar paywall](api-adapty/operations/updatePaywall) --- # File: ss-authorization --- --- title: "Autorización y formato de solicitud de la API del servidor" description: "" --- ## Autorización \{#authorization\} Las solicitudes a la API deben autenticarse con tu clave de API secreta o pública como cabecera de autorización. Puedes encontrarlas en [**App Settings**](https://app.adapty.io/settings/general). El formato del valor es `Api-Key {your-secret-api-key}`, por ejemplo, `Api-Key secret_live_...`. :::important Las claves de API son específicas de cada app. Si tienes varias apps, asegúrate de usar claves distintas para cada una. ::: ## Formato de solicitud \{#request-format\} **Cabeceras** Las solicitudes a la API del servidor requieren cabeceras específicas y un cuerpo en JSON. Usa los detalles a continuación para estructurar tus solicitudes. | **Cabecera** | **Descripción** | | --------------------------- | ------------------------------------------------------------ | | **adapty-profile-id** | <p>El ID de perfil de Adapty del usuario. Visible en el campo **Adapty ID** en [Adapty Dashboard -> **Profiles**](https://app.adapty.io/profiles/users) -> página del perfil específico. </p><p>Es intercambiable con **adapty-customer-user-id**; usa cualquiera de los dos.</p> | | **adapty-customer-user-id** | <p>El ID del usuario en tu sistema. Visible en el campo **Customer user ID** en [Adapty Dashboard -> **Profiles**](https://app.adapty.io/profiles/users) -> página del perfil específico. </p><p>Es intercambiable con **adapty-profile-id**; usa cualquiera de los dos.</p><p> ⚠️ Solo funciona si <InlineTooltip tooltip="identificas usuarios en tu app">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip> en el código de tu app usando el SDK de Adapty.</p> | | **adapty-platform** | (opcional) Especifica la plataforma del dispositivo en el que está instalada la app. Recomendamos establecer este parámetro en las solicitudes [Crear perfil](api-adapty/operations/createProfile) y [Actualizar perfil](api-adapty/operations/updateProfile) al modificar el objeto [Installation Meta](server-side-api-objects#installation-meta), ya que depende del dispositivo que utilice el usuario y un mismo usuario puede tener varios dispositivos. Valores posibles: `iOS`, `macOS`, `iPadOS`, `visionOS`, `Android` o `web`. | | **Content-Type** | Establécelo en `application/json` para que la API procese la solicitud. | **Cuerpo** La API espera un cuerpo en formato JSON con los datos necesarios para la solicitud. ## Límites de frecuencia \{#rate-limits\} Para evitar la limitación de velocidad, asegúrate de que el número de solicitudes (por app) se mantenga por debajo de 40 000 por minuto. Si se supera este límite, el sistema puede ralentizarse o bloquear temporalmente más solicitudes para mantener un rendimiento óptimo para todos los usuarios. ## Rotar claves de API \{#rotate-api-keys\} Si necesitas rotar las claves de API secretas: 1. En **Settings → General**, haz clic en **Generate new key** y luego en el icono de papelera junto a la clave antigua. 2. Actualiza la clave utilizada en tu app. --- **Próximos pasos — solicitudes:** - [Obtener perfil](api-adapty/operations/getProfile) - [Crear perfil](api-adapty/operations/createProfile) - [Actualizar perfil](api-adapty/operations/updateProfile) - [Eliminar perfil](api-adapty/operations/deleteProfile) - [Conceder nivel de acceso](api-adapty/operations/grantAccessLevel) - [Revocar nivel de acceso](api-adapty/operations/revokeAccessLevel) - [Establecer transacción](api-adapty/operations/setTransaction) - [Validar compra, proporcionar nivel de acceso al cliente e importar su historial de transacciones](api-adapty/operations/validateStripePurchase) - [Obtener paywall](api-adapty/operations/getPaywall) - [Listar paywalls](api-adapty/operations/listPaywalls) - [Actualizar paywall](api-adapty/operations/updatePaywall) --- # File: server-side-api-specs --- --- title: "Solicitudes a la API del lado del servidor" description: "Explora las especificaciones de la API del lado del servidor de Adapty para una integración avanzada." --- La API del lado del servidor de Adapty te permite acceder y gestionar tus datos de suscripción de forma programática, facilitando la integración con tus servicios e infraestructura existentes. Ya sea que estés sincronizando datos entre plataformas, otorgando niveles de acceso o validando compras en Stripe, esta API te proporciona las herramientas para mantener tus sistemas sincronizados y a tus usuarios activos. ## Colección y entorno de Postman \{#postman-collection-and-environment\} Para simplificar el uso de nuestra API del lado del servidor, hemos preparado una colección de Postman y un archivo de entorno que puedes descargar e importar en Postman. - **Colección de solicitudes**: Incluye todas las solicitudes disponibles en la API del lado del servidor de Adapty. Ten en cuenta que usa variables que puedes definir en el entorno. - **Entorno**: Contiene una lista de variables donde puedes definir los valores una sola vez. Hemos preparado un entorno unificado para la API del lado del servidor, la API web y la API de exportación de analíticas para facilitarte las cosas. Una vez que actives este entorno, Postman sustituirá automáticamente los valores de las variables definidas en tus solicitudes. :::tip [Descarga la colección y el entorno](https://raw.githubusercontent.com/adaptyteam/adapty-docs/refs/heads/main/Downloads/Adapty_server_side_API_postman_collection.zip) ::: Para más información sobre cómo importar una colección y un entorno en Postman, consulta la [documentación de Postman](https://learning.postman.com/docs/getting-started/importing-and-exporting/importing-data/). ### Variables utilizadas \{#variables-used\} Hemos creado un entorno unificado para la API del lado del servidor, la API web y la API de exportación de analíticas para simplificar tu flujo de trabajo. A continuación se muestran las variables específicas de la API del lado del servidor: | Variable | Descripción | Valor de ejemplo | | ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | | secret_api_key | Puedes encontrarla en el campo **Secret key** en [**App settings**](https://app.adapty.io/settings/general). | `secret_live_Pj1P1xzM.2CvSvE1IalQRFjsWy6csBVNpH33atnod` | | adapty-customer-user-id | El ID de usuario utilizado en tu sistema. En el Adapty Dashboard, puedes encontrarlo en el campo **Customer user ID** del perfil. | `john.doe@example.com` | | adapty-profile-id | El ID de usuario asignado en Adapty. En el Adapty Dashboard, puedes encontrarlo en el campo **Adapty ID** del perfil. | `3286abd3-48b0-4e9c-a5f6-ac0a006333a6` | | Adapty-platform | La plataforma utilizada por el usuario en tu app. Valores posibles: `iOS`, `macOS`, `iPadOS`, `visionOS`, `Android`, `web`. | `iOS` | | stripe_token | Token de un objeto de Stripe que representa una compra única, como una suscripción (`sub_XXX`) o un Payment Intent (`pi_XXX`). | `sub_1JY8xLLy6P12345a` | **Próximos pasos — Solicitudes:** - [Obtener perfil](api-adapty/operations/getProfile) - [Crear perfil](api-adapty/operations/createProfile) - [Actualizar perfil](api-adapty/operations/updateProfile) - [Eliminar perfil](api-adapty/operations/deleteProfile) - [Otorgar nivel de acceso](api-adapty/operations/grantAccessLevel) - [Revocar nivel de acceso](api-adapty/operations/revokeAccessLevel) - [Establecer transacción](api-adapty/operations/setTransaction) - [Validar compra, proporcionar nivel de acceso al cliente e importar su historial de transacciones](api-adapty/operations/validateStripePurchase) - [Añadir identificadores de integración](api-adapty/operations/setIntegrationIdentifiers) - [Obtener paywall](api-adapty/operations/getPaywall) - [Listar paywalls](api-adapty/operations/listPaywalls) - [Actualizar paywall](api-adapty/operations/updatePaywall) --- # File: api-guides --- --- title: "API guides" description: "Learn how to perform specific tasks using the server-side API." --- In this section, you can find guides that cover different use cases and help you perform specific tasks using the server-side API and the Adapty SDK. <CustomDocCardList /> --- # File: sync-subscribers-from-web --- --- title: "Sincronizar compras entre web y móvil" description: "Sincroniza suscriptores en web y móvil." --- Si tus usuarios pueden comprar un producto en tu **sitio web**, puedes mantener sus niveles de acceso sincronizados automáticamente con tu **aplicación móvil**. En esta guía aprenderás cómo hacerlo usando la API y el SDK de Adapty. #### Caso de uso de ejemplo \{#sample-use-case\} Digamos que en tu app, los usuarios pueden registrarse con un plan freemium tanto en móvil como en web. Les permites actualizar al plan Premium en tu web a través de Stripe o Chargebee. Una vez que un usuario se suscribe en la web, quieres que obtenga acceso Premium en la app móvil de inmediato, sin esperar ni volver a iniciar sesión. De eso se encarga Adapty. ## Paso 1. Identificar usuarios \{#step-1-identify-users\} Adapty usa `customer_user_id` para identificar usuarios en todas las plataformas. Debes crear este ID una sola vez y pasarlo tanto al SDK móvil como al backend web. ### Registro desde la web \{#sign-up-from-web\} Cuando tus usuarios se registren en tu sitio web, necesitas crear un perfil para ellos en Adapty usando la API del lado del servidor. Consulta la referencia del método [aquí](api-adapty/operations/createProfile). ```bash curl --request POST \ --url https://api.adapty.io/api/v2/server-side-api/profile/ \ --header 'Accept: application/json' \ --header 'Authorization: Api-Key YOUR_SECRET_API_KEY' \ --header 'Content-Type: application/json' \ --header 'adapty-customer-user-id: YOUR_CUSTOMER_USER_ID' ``` ### Registrarse desde la app \{#sign-up-from-app\} Cuando tus usuarios se registran por primera vez desde la app, puedes pasar su customer user ID durante la activación del SDK, o si ya activaste el SDK antes de la etapa de registro, usa el método `identify` para crear un nuevo perfil y asignarle un customer user ID. :::important Si identificas a nuevos usuarios después de la activación del SDK, primero el SDK creará un perfil anónimo, ya que no puede funcionar sin ningún perfil. Luego, cuando identifiques al usuario y le asignes un nuevo customer user ID, se creará un nuevo perfil. Este comportamiento es completamente normal y no afectará a la precisión de los análisis. Lee más [aquí](ios-quickstart-identify). ::: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> ```swift showLineNumbers do { try await Adapty.identify("YOUR_USER_ID") // Unique for each user } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="iOS (Swift-Callback)" default> ```swift showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID") { error in if let error { // handle the error } } ``` </TabItem> <TabItem value="android" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") { error -> // Unique for each user if (error == null) { // successful identify } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID", error -> { if (error == null) { // successful identify } }); ``` </TabItem> <TabItem value="react-native" label="React Native" default> ```typescript showLineNumbers try { await adapty.identify("YOUR_USER_ID"); // Unique for each user // successfully identified } catch (error) { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```dart showLineNumbers try { await Adapty().identify(customerUserId); // Unique for each user } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` </TabItem> <TabItem value="unity" label="Unity" default> ```csharp showLineNumbers Adapty.Identify("YOUR_USER_ID", (error) => { // Unique for each user if(error == null) { // successful identify } }); ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform" default> ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") // Unique for each user .onSuccess { // successful identify } .onError { error -> // handle the error } ``` </TabItem> <TabItem value="capacitor" label="Capacitor" default> ```typescript showLineNumbers try { await adapty.identify({ customerUserId: "YOUR_USER_ID" }); // successfully identified } catch (error) { // handle the error } ``` </TabItem> </Tabs> ## Paso 2. Verificar el estado de la suscripción mediante la API \{#step-2-check-subscription-status-via-api\} Cuando un usuario inicia sesión en tu sitio web, obtén su perfil de Adapty usando la API. Si el usuario no tiene una suscripción activa, puedes mostrarle un paywall. Consulta la referencia del método [aquí](api-adapty/operations/getProfile). ```bash curl --request GET \ --url https://api.adapty.io/api/v2/server-side-api/profile/ \ --header 'Accept: application/json' \ --header 'Authorization: Api-Key YOUR_SECRET_API_KEY' \ --header 'adapty-customer-user-id: YOUR_USER_ID' \ ``` ## Paso 3. Mostrar un paywall en tu sitio web \{#step-3-display-a-paywall-on-your-website\} En tu sitio web, muestra un paywall a los usuarios freemium. Puedes usar cualquier proveedor de pagos (Stripe, Chargebee, LemonSqueezy, etc.). ## Paso 4. Actualizar el estado de la suscripción en Adapty \{#step-4-update-subscription-status-in-adapty\} Una vez completado el pago en tu sitio web, llama a la API de Adapty para actualizar el nivel de acceso del usuario según el producto que haya comprado. Consulta la referencia del método [aquí](api-adapty/operations/grantAccessLevel). ```bash curl --request POST \ --url https://api.adapty.io/api/v2/server-side-api/purchase/profile/grant/access-level/ \ --header 'Accept: application/json' \ --header 'Authorization: Api-Key YOUR_SECRET_API_KEY' \ --header 'Content-Type: application/json' \ --header 'adapty-customer-user-id: YOUR_USER_ID' \ --data '{ "access_level_id": "YOUR_ACCESS_LEVEL" }' ``` ## Paso 5. Sincronizar el estado en la app \{#step-5-sync-status-in-the-app\} Cuando el usuario abra tu app, recupera el perfil actualizado y desbloquea las funciones de pago. Necesitas obtener su perfil o sincronizarlo automáticamente. Luego, obtén el nivel de acceso a partir de él. A continuación puedes ver cómo obtener el perfil y comprobar su estado. Para más detalles, ve [aquí](ios-check-subscription-status). <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> ```swift showLineNumbers do { let profile = try await Adapty.getProfile() if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="iOS (Swift-Callback)" default> ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get() { // check the access if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } } ``` </TabItem> <TabItem value="android" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.getProfile { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { // grant access to premium features } } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers Adapty.getProfile(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success<AdaptyProfile>) result).getValue(); // check the access if (profile.getAccessLevels().get("YOUR_ACCESS_LEVEL") != null && profile.getAccessLevels().get("YOUR_ACCESS_LEVEL").getIsActive()) { // grant access to premium features } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` </TabItem> <TabItem value="react-native" label="React Native" default> ```typescript showLineNumbers try { const profile = await adapty.getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive) { // grant access to premium features } } catch (error) { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```dart showLineNumbers try { final profile = await Adapty().getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false) { // grant access to premium features } } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` </TabItem> <TabItem value="unity" label="Unity" default> ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // check the access if (profile.AccessLevels["YOUR_ACCESS_LEVEL"]?.IsActive ?? false) { // grant access to premium features } }); ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform" default> ```kotlin showLineNumbers Adapty.getProfile() .onSuccess { profile -> // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { // grant access to premium features } } .onError { error -> // handle the error } ``` </TabItem> <TabItem value="capacitor" label="Capacitor" default> ```typescript showLineNumbers try { const profile = await adapty.getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive) { // grant access to premium features } } catch (error) { // handle the error } ``` </TabItem> </Tabs> --- # File: sync-purchases-from-custom-stores --- --- title: "Sincronizar transacciones de stores personalizados" description: "Sincroniza transacciones de stores personalizados con Adapty para gestionar accesos y hacer seguimiento de ingresos." --- Si vendes suscripciones o compras in-app a través de **stores personalizadas** como Amazon Appstore, Microsoft Store o tu propia plataforma de pago, puedes sincronizar esas transacciones con Adapty para gestionar automáticamente los niveles de acceso y hacer seguimiento de los ingresos en tu analítica. En esta guía aprenderás a conectar las compras de stores personalizadas con Adapty usando el SDK y la API. #### Ejemplo de uso \{#sample-use-case\} Supongamos que distribuyes tu app en Amazon Appstore, o que tienes tu propia tienda web para ventas directas. Cuando un usuario completa una compra en estas plataformas, quieres: - Concederle acceso automáticamente a las funciones premium de tu app móvil - Registrar la transacción en los análisis de Adapty junto con los ingresos de App Store y Google Play - Activar integraciones y webhooks igual que con cualquier otra suscripción Eso es exactamente lo que esta integración te permite conseguir. ## Paso 1. Identifica a los usuarios \{#step-1-identify-users\} Adapty usa `customer_user_id` para identificar usuarios entre plataformas. Necesitas crear este ID una sola vez y pasárselo tanto al SDK como a tu backend web. Cuando tus usuarios se registren por primera vez desde la app, puedes pasar su customer user ID durante la activación del SDK, o si ya activaste el SDK de Adapty antes del registro, usa el método `identify` para crear un nuevo perfil y asignarle un customer user ID. :::important Si identificas nuevos usuarios después de la activación del SDK, el SDK creará primero un perfil anónimo (no puede funcionar sin uno). Cuando llames a `identify` con un customer user ID, se creará un nuevo perfil. Este comportamiento es normal y no afectará a la precisión de los análisis. Lee más [aquí](ios-quickstart-identify). ::: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> ```swift showLineNumbers do { try await Adapty.identify("YOUR_USER_ID") // Unique for each user } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="iOS (Swift-Callback)" default> ```swift showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID") { error in if let error { // handle the error } } ``` </TabItem> <TabItem value="android" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") { error -> // Unique for each user if (error == null) { // successful identify } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID", error -> { if (error == null) { // successful identify } }); ``` </TabItem> <TabItem value="react-native" label="React Native" default> ```typescript showLineNumbers try { await adapty.identify("YOUR_USER_ID"); // Unique for each user // successfully identified } catch (error) { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```dart showLineNumbers try { await Adapty().identify(customerUserId); // Unique for each user } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` </TabItem> <TabItem value="unity" label="Unity" default> ```csharp showLineNumbers Adapty.Identify("YOUR_USER_ID", (error) => { // Unique for each user if(error == null) { // successful identify } }); ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform" default> ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") // Unique for each user .onSuccess { // successful identify } .onError { error -> // handle the error } ``` </TabItem> <TabItem value="capacitor" label="Capacitor" default> ```typescript showLineNumbers try { await adapty.identify({ customerUserId: "YOUR_USER_ID" }); // successfully identified } catch (error) { // handle the error } ``` </TabItem> </Tabs> ## Paso 2. Crea productos en una store personalizada en el Adapty Dashboard \{#step-2-create-products-in-a-custom-store-in-adapty-dashboard\} Para que Adapty relacione las transacciones de la store personalizada con tus productos, debes añadir los productos y configurar los detalles de la store personalizada para cada uno. 1. Ve a [**Products**](https://app.adapty.io/settings/general) desde el menú lateral del Adapty Dashboard y haz clic en **Create product**. O haz clic en un producto existente para editarlo. 2. Asegúrate de haber seleccionado el [nivel de acceso](access-level) que quieres conceder a los usuarios que compren el producto. 3. Haz clic en **+** y selecciona **Add a custom store**. 4. Haz clic en **Create new custom store**. 5. Dale un nombre a tu store (por ejemplo, "Amazon Appstore", "Microsoft Store" o "Web Store") y un ID. Haz clic en **Create custom store**. 6. Luego, haz clic en **Save changes** para vincular el producto al store personalizado. 7. Introduce el **Store product ID** del producto para asociarlo con algún producto de ese store. Después, haz clic en **Save**. ## Paso 3. Sincroniza las transacciones mediante la API \{#step-3-sync-transactions-via-api\} Cuando se completa una compra en tu store personalizado, debes sincronizarla con Adapty mediante la API de servidor. Esta llamada a la API: - Registra la transacción en Adapty - Otorga el nivel de acceso correspondiente al usuario - Activa las integraciones y webhooks que hayas configurado - Hace que la transacción aparezca en tus analíticas Consulta la referencia completa del método [aquí](api-adapty/operations/setTransaction). ```bash curl --request POST \ --url https://api.adapty.io/api/v2/server-side-api/purchase/set/transaction/ \ --header 'Accept: application/json' \ --header 'Authorization: Api-Key YOUR_SECRET_API_KEY' \ --header 'Content-Type: application/json' \ --header 'adapty-customer-user-id: YOUR_CUSTOMER_USER_ID' \ --data '{ "purchase_type": "PRODUCT_PERIOD", "store": "YOUR_CUSTOM_STORE", "environment": "production", "store_product_id": "YOUR_STORE_PRODUCT_ID", "store_transaction_id": "STORE_TRANSACTION_ID", "store_original_transaction_id": "ORIGINAL_TRANSACTION_ID", "price": { "country": "COUNTRY_CODE", "currency": "CURRENCY_CODE", "value": "YOUR_PRICE" }, "purchased_at": "2024-01-15T10:30:00Z" }' ``` :::important Parámetros importantes: - **store**: El ID de tu store personalizado del Paso 2 - **store_product_id**: El ID de producto del store del Paso 2 - **store_transaction_id**: Un identificador único para esta transacción - **purchased_at**: Marca de tiempo en formato ISO 8601 de cuándo se realizó la compra - **price**: El importe pagado por el usuario ::: ## Paso 4. Verificar el acceso en la app \{#step-4-verify-access-in-the-app\} Una vez sincronizada la transacción, el perfil del usuario se actualizará automáticamente con el nuevo nivel de acceso. Cuando el usuario abra tu app, obtén su perfil para comprobar el estado de su suscripción y desbloquear las funciones premium. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> ```swift showLineNumbers do { let profile = try await Adapty.getProfile() if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="iOS (Swift-Callback)" default> ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get() { // check the access if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } } ``` </TabItem> <TabItem value="android" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.getProfile { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { // grant access to premium features } } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers Adapty.getProfile(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success<AdaptyProfile>) result).getValue(); // check the access if (profile.getAccessLevels().get("YOUR_ACCESS_LEVEL") != null && profile.getAccessLevels().get("YOUR_ACCESS_LEVEL").getIsActive()) { // grant access to premium features } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` </TabItem> <TabItem value="react-native" label="React Native" default> ```typescript showLineNumbers try { const profile = await adapty.getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive) { // grant access to premium features } } catch (error) { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```dart showLineNumbers try { final profile = await Adapty().getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false) { // grant access to premium features } } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` </TabItem> <TabItem value="unity" label="Unity" default> ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // check the access if (profile.AccessLevels["YOUR_ACCESS_LEVEL"]?.IsActive ?? false) { // grant access to premium features } }); ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform" default> ```kotlin showLineNumbers Adapty.getProfile() .onSuccess { profile -> // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { // grant access to premium features } } .onError { error -> // handle the error } ``` </TabItem> <TabItem value="capacitor" label="Capacitor" default> ```typescript showLineNumbers try { const profile = await adapty.getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive) { // grant access to premium features } } catch (error) { // handle the error } ``` </TabItem> </Tabs> --- # File: grant-access-level --- --- title: "Otorgar niveles de acceso manualmente" description: "Desbloquea funciones de pago manualmente para usuarios o grupos de usuarios específicos" --- Si necesitas **desbloquear manualmente funciones premium** para usuarios o grupos de usuarios específicos, puedes hacerlo mediante la API de Adapty. Esto resulta útil para campañas promocionales, acceso para inversores o casos especiales de soporte al cliente. En esta guía aprenderás a identificar usuarios y concederles niveles de acceso de forma programática. #### Casos de uso - **Códigos promocionales**: Cuando un usuario introduce un código promocional válido en tu app, concédele acceso automático a las funciones premium. - **Acceso para inversores/beta testers**: Proporciona acceso premium a inversores o beta testers comprobando sus atributos personalizados. ## Paso 1. Identifica a los usuarios \{#step-1-identify-users\} Adapty usa `customer_user_id` para identificar a los usuarios en todas las plataformas y dispositivos. Esto es fundamental para garantizar que los usuarios conserven su acceso después de reinstalar la app o cambiar de dispositivo. Solo necesitas crear este ID una vez. Cuando los usuarios se registran desde la app, puedes pasarles el customer user ID durante la activación del SDK, o usar el método `identify` si el SDK se activó antes del registro. :::important Si identificas nuevos usuarios después de la activación del SDK, el SDK primero creará un perfil anónimo (no puede funcionar sin uno). Cuando llames a `identify` con un customer user ID, se creará un nuevo perfil. Este comportamiento es normal y no afectará la precisión de las métricas. Lee más [aquí](ios-quickstart-identify). ::: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> ```swift showLineNumbers do { try await Adapty.identify("YOUR_USER_ID") // Unique for each user } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="iOS (Swift-Callback)" default> ```swift showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID") { error in if let error { // handle the error } } ``` </TabItem> <TabItem value="android" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") { error -> // Unique for each user if (error == null) { // successful identify } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID", error -> { if (error == null) { // successful identify } }); ``` </TabItem> <TabItem value="react-native" label="React Native" default> ```typescript showLineNumbers try { await adapty.identify("YOUR_USER_ID"); // Unique for each user // successfully identified } catch (error) { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```dart showLineNumbers try { await Adapty().identify(customerUserId); // Unique for each user } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` </TabItem> <TabItem value="unity" label="Unity" default> ```csharp showLineNumbers Adapty.Identify("YOUR_USER_ID", (error) => { // Unique for each user if(error == null) { // successful identify } }); ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform" default> ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") // Unique for each user .onSuccess { // successful identify } .onError { error -> // handle the error } ``` </TabItem> <TabItem value="capacitor" label="Capacitor" default> ```typescript showLineNumbers try { await adapty.identify({ customerUserId: "YOUR_USER_ID" }); // successfully identified } catch (error) { // handle the error } ``` </TabItem> </Tabs> ## Paso 2. Otorgar nivel de acceso mediante la API \{#step-2-grant-access-level-via-api\} Una vez que el usuario está identificado con un `customer_user_id`, puedes otorgarle niveles de acceso mediante la API del servidor. Esta llamada a la API concede el nivel de acceso al usuario para que pueda acceder a las funciones de pago sin necesidad de realizar un pago real. Consulta la referencia completa del método [aquí](api-adapty/operations/grantAccessLevel). :::tip Puedes controlar el acceso de los usuarios añadiendo un atributo personalizado (por ejemplo, Beta tester o Investor) en el Adapty Dashboard. Cuando se inicie tu app, [comprueba este atributo en el perfil del usuario](subscription-status) para conceder acceso automáticamente. Para actualizar el acceso, simplemente cambia el atributo en el dashboard. ::: ```bash curl --request POST \ --url https://api.adapty.io/api/v2/server-side-api/purchase/profile/grant/access-level/ \ --header 'Accept: application/json' \ --header 'Authorization: Api-Key YOUR_SECRET_API_KEY' \ --header 'Content-Type: application/json' \ --header 'adapty-customer-user-id: CUSTOMER_USER_ID' \ --data '{ "access_level_id": "YOUR_ACCESS_LEVEL" }' ``` ## Paso 3. Verifica el acceso en la app \{#step-3-verify-access-in-the-app\} Tras conceder el acceso mediante la API, el perfil del usuario se actualizará automáticamente. Obtén su perfil para comprobar el estado de su suscripción y desbloquear las funciones premium. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> ```swift showLineNumbers do { let profile = try await Adapty.getProfile() if profile.accessLevels["YOUR_ACCESS_LEVEL_ID"]?.isActive ?? false { // grant access to premium features } } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="iOS (Swift-Callback)" default> ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get() { // check the access if profile.accessLevels["YOUR_ACCESS_LEVEL_ID"]?.isActive ?? false { // grant access to premium features } } } ``` </TabItem> <TabItem value="android" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.getProfile { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL_ID"]?.isActive == true) { // grant access to premium features } } is AdaptyResult.Error -> { val error = result.error // handle the error } } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers Adapty.getProfile(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success<AdaptyProfile>) result).getValue(); // check the access if (profile.getAccessLevels().get("YOUR_ACCESS_LEVEL_ID") != null && profile.getAccessLevels().get("YOUR_ACCESS_LEVEL_ID").getIsActive()) { // grant access to premium features } } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // handle the error } }); ``` </TabItem> <TabItem value="react-native" label="React Native" default> ```typescript showLineNumbers try { const profile = await adapty.getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL_ID"]?.isActive) { // grant access to premium features } } catch (error) { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```dart showLineNumbers try { final profile = await Adapty().getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL_ID"]?.isActive ?? false) { // grant access to premium features } } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` </TabItem> <TabItem value="unity" label="Unity" default> ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // check the access if (profile.AccessLevels["YOUR_ACCESS_LEVEL_ID"]?.IsActive ?? false) { // grant access to premium features } }); ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform" default> ```kotlin showLineNumbers Adapty.getProfile() .onSuccess { profile -> // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL_ID"]?.isActive == true) { // grant access to premium features } } .onError { error -> // handle the error } ``` </TabItem> <TabItem value="capacitor" label="Capacitor" default> ```typescript showLineNumbers try { const profile = await adapty.getProfile(); // check the access if (profile.accessLevels["YOUR_ACCESS_LEVEL_ID"]?.isActive) { // grant access to premium features } } catch (error) { // handle the error } ``` </TabItem> </Tabs> --- # File: web-api --- --- title: Adapty Web API description: "" --- La Web API es una extensión de la API del lado del servidor diseñada para usarse con aplicaciones web. Te permite recuperar el paywall correcto mediante su placement ID asociado y registrar las visualizaciones de paywall para un seguimiento preciso de las conversiones. Esto te ayuda a aprovechar las pruebas A/B y la personalización de paywalls disponibles en Adapty, además de identificar qué paywalls funcionan mejor. ## Caso de uso: Registrar una transacción desde tu app web y vincularla al paywall utilizado \{#use-case-record-a-transaction-from-your-web-app-and-link-it-to-the-used-paywall\} Supongamos que vendes productos en tu app web. Necesitas mostrar un paywall a tus usuarios, permitirles comprar un producto y luego añadir los detalles de la transacción a Adapty. Es fundamental vincular estas transacciones a los paywalls específicos a través de los cuales el usuario realizó la compra, para que tus análisis reflejen datos precisos. Esto se puede lograr fácilmente usando la API de Adapty. ### Requisitos previos \{#prerequisites\} 1. [Crea los productos](create-product) que usarás en el paywall dentro del Adapty Dashboard. 2. [Crea el paywall](create-paywall) en el Adapty Dashboard. [Usa Remote Config](customize-paywall-with-remote-config) para diseñar tu paywall web. 3. [Configura un placement](create-placement) y vincúlalo al paywall en el Adapty Dashboard. ### Pasos con la API de Adapty \{#steps-with-adapty-api\} 1. **Crear un perfil de usuario:** Adapty necesita tener un perfil antes de solicitar un paywall para poder personalizarlo según el usuario que lo solicita. Usa la solicitud [Create profile](api-adapty/operations/createProfile) para crear un perfil de usuario. 2. **Obtener y mostrar el paywall:** Cuando el usuario llegue al placement de tu app web donde debe mostrarse el paywall, usa la solicitud [Get paywall](api-web/operations/getPaywall) para recuperarlo mediante el [placement ID](placements). Como resultado, obtendrás un paywall para la [audiencia](audience) correspondiente a tu usuario. Muestra el paywall con tu código, usando los productos devueltos y (opcionalmente) el [Remote Config](customize-paywall-with-remote-config) de ese paywall. 3. **Registrar la visualización del paywall:** Usa [Record paywall view](api-web/operations/recordPaywallView) para registrar la visualización del paywall en Adapty y garantizar que tus análisis reflejen el evento con precisión. Esto es fundamental para hacer un seguimiento correcto de las conversiones. 4. **Registrar la compra:** Si el usuario completa una compra, envía los detalles de la transacción a Adapty mediante la API de Adapty. Incluye el **variation ID** en esta solicitud para vincular la transacción al paywall específico que se mostró. Como referencia, consulta nuestra página sobre [cómo asociar paywalls con transacciones en apps móviles](report-transactions-observer-mode): el mismo enfoque se aplica a las apps web. 5. **Añadir datos de atribución de marketing (si aplica):** Si dispones de datos de atribución de marketing (por ejemplo, detalles de campaña o anuncio), usa [Add attribution](api-web/operations/addAttribution) para incorporarlos al perfil de usuario y enriquecer los análisis y conocer mejor el rendimiento de tus anuncios en Adapty. --- **A continuación:** - Continúa con [Autorización de la Web API](web-api-authorization) - Solicitudes: - [Add attribution](api-web/operations/addAttribution) - [Get paywall](api-web/operations/getPaywall) - [Record paywall view](api-web/operations/recordPaywallView) --- # File: web-api-authorization --- --- title: Autorización y formato de solicitud para la Web API description: "" --- ## Autorización \{#authorization\} Las solicitudes a la API deben autenticarse con tu clave pública de API como cabecera **Authorization** con el valor `Api-Key {your_public_api_key}`, por ejemplo, `Api-Key public_live_...`. Puedes encontrar esta clave en el [Adapty Dashboard -> **App Settings** -> pestaña **General** -> sección **API keys**](https://app.adapty.io/settings/general). :::important Las claves de API son específicas de cada aplicación. Si tienes varias aplicaciones, asegúrate de usar claves distintas para cada una de ellas. ::: ## Formato de solicitud \{#request-format\} - **Cabecera Content-Type**: Establece la cabecera **Content-Type** en `application/json` para que la API procese tu solicitud. - **Body**: La API espera que la solicitud utilice el body en formato JSON. --- # File: web-api-requests --- --- title: " Solicitudes de Web API" description: "" --- La API del lado del servidor de Adapty te permite acceder y gestionar tus datos de suscripción de forma programática, facilitando la integración con tus servicios e infraestructura existentes. Ya sea que estés sincronizando datos entre plataformas, otorgando niveles de acceso o validando compras en Stripe, esta API ofrece las herramientas para mantener tus sistemas sincronizados y a tus usuarios activos. ## Colección y entorno de Postman \{#postman-collection-and-environment\} Para simplificar el uso de nuestra web API, hemos preparado una colección de Postman y un archivo de entorno que puedes descargar e importar en Postman. - **Colección de solicitudes**: Incluye todas las solicitudes disponibles en la web API de Adapty. Ten en cuenta que utiliza variables que puedes definir en el entorno. - **Entorno**: Contiene una lista de variables donde puedes definir los valores una sola vez. Hemos preparado un entorno unificado para la API del lado del servidor, la web API y la API de exportación de analíticas para facilitarte el trabajo. Una vez que actives este entorno, Postman sustituirá automáticamente los valores de las variables definidas en tus solicitudes. :::tip [Descarga la colección y el entorno](https://raw.githubusercontent.com/adaptyteam/adapty-docs/refs/heads/main/Downloads/Adapty_Web_API_postman_collection.zip) ::: Para más información sobre cómo importar una colección y un entorno en Postman, consulta la [documentación de Postman](https://learning.postman.com/docs/getting-started/importing-and-exporting/importing-data/). ## Variables utilizadas \{#variables-used\} Hemos creado un entorno unificado para la API del lado del servidor, la web API y la API de exportación de analíticas para simplificar tu flujo de trabajo. A continuación se muestran las variables específicas de la web API: | Variable | Descripción | Valor de ejemplo | | ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | | public_api_key | Puedes encontrarla en el campo **Public SDK key** en [**App settings**](https://app.adapty.io/settings/general). | `public_live_Pj1P1xzM.2CvSvE1IalQRFjsWy6csBVNpH33atnod` | | adapty-customer-user-id | El ID de usuario utilizado en tu sistema. En el Adapty Dashboard, puedes encontrarlo en el campo **Customer user ID** del perfil. | `john.doe@example.com` | | adapty-profile-id | El ID de usuario asignado en Adapty. En el Adapty Dashboard, puedes encontrarlo en el campo **Adapty ID** del perfil. | `3286abd3-48b0-4e9c-a5f6-ac0a006333a6` | **Siguientes pasos: Solicitudes:** - [Obtener paywall](api-web/operations/getPaywall) - [Registrar vista de paywall](api-web/operations/recordPaywallView) - [Añadir atribución](api-web/operations/addAttribution) --- # File: export-analytics-api --- --- title: Exportar análisis con API --- Exportar tus datos de análisis a CSV te da la flexibilidad de profundizar en las métricas de rendimiento de tu app, personalizar informes y analizar tendencias a lo largo del tiempo. Con la API de Adapty, puedes extraer fácilmente análisis detallados en formato CSV, lo que facilita el seguimiento, el intercambio y el refinamiento de tus datos según necesites. ## Primeros pasos con la API para exportar análisis \{#getting-started-with-the-api-for-analytics-export\} Con la API de exportación de análisis, puedes, por ejemplo: 1. **Analizar el MRR de campañas de marketing**: Mide el impacto de las campañas de marketing del año pasado en un país específico para ver cuáles generaron los mayores ingresos, con seguimiento semanal. Usa el método [Recuperar datos de análisis](api-export-analytics/operations/retrieveAnalyticsData) para esto. 2. **Rastrear la retención por cohorte a lo largo del tiempo**: Sigue la retención por cohorte para identificar puntos de abandono y comparar cohortes en el tiempo, revelando tendencias y momentos clave en los que las estrategias de engagement podrían mejorar la retención. Limitado a un store específico, un país concreto y un producto particular. Usa el método [Recuperar datos de cohorte](api-export-analytics/operations/retrieveCohortData) para esto. 3. **Evaluar las tasas de conversión por canal**: Analiza las tasas de conversión de los principales canales de adquisición para ver cuáles son más eficaces a la hora de impulsar las primeras compras. Esto ayuda a priorizar el gasto en marketing en los canales de mayor rendimiento. Usa el método [Recuperar datos de conversión](api-export-analytics/operations/retrieveConversionData) para esto. 4. **Revisar la tasa de abandono**: Monitoriza la rapidez con la que los usuarios se dan de baja para descubrir patrones de abandono o medir el éxito de los esfuerzos de retención, centrándote en un país y un producto específicos. Usa el método [Recuperar datos de embudo](api-export-analytics/operations/retrieveFunnelData) para esto. 5. **Evaluar el LTV por segmento de usuario**: Identifica el valor de por vida de los distintos segmentos de usuarios para entender qué grupos generan los mayores ingresos a lo largo del tiempo. Céntrate en los segmentos de mayor valor, como los suscriptores a largo plazo, y usa los resultados para refinar las estrategias de adquisición. Usa el método [Recuperar datos de LTV](api-export-analytics/operations/retrieveLTVData) para esto. 6. **Comprobar la retención por país**: Analiza las tasas de retención por región para encontrar mercados con alto engagement y orientar las estrategias de localización o regionales. Usa el método [Recuperar datos de retención](api-export-analytics/operations/retrieveRetentionData) para esto. --- **Siguientes pasos**: - [Autorización y formato de solicitud](export-analytics-api-authorization) - [Solicitudes de la API de exportación de análisis](export-analytics-api-requests) --- # File: export-analytics-api-authorization --- --- title: Autorización y formato de solicitud para la API de exportación de analíticas --- ## Autorización \{#authorization\} Necesitas autenticar tus solicitudes a la API con tu clave API secreta como cabecera de autorización. Puedes encontrarla en [App Settings](https://app.adapty.io/settings/general). El formato es `Api-Key {YOUR_SECRET_API_KEY}`, por ejemplo: `Api-Key secret_live_...`. :::important Las claves API son específicas de cada app. Si tienes varias apps, asegúrate de usar claves distintas para cada una. ::: ## Formato de solicitud \{#request-format\} **Cabeceras** Las solicitudes a la API del lado del servidor requieren cabeceras específicas y un cuerpo JSON. Usa los detalles a continuación para estructurar tus solicitudes: | Cabecera | Descripción | | ------------ | ------------------------------------------------------------ | | Content-Type | (Obligatorio) Establece `application/json` para que la API procese la solicitud. | | Adapty-Tz | (Opcional) Define la zona horaria para determinar cómo se agrupan y muestran los datos. Usa el [formato de la base de datos de zonas horarias IANA](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) (p. ej., `Europe/Berlin`). | ## Cuerpo \{#body\} La API espera un cuerpo en formato JSON con los datos necesarios para la solicitud. ## Límites de velocidad \{#rate-limits\} El máximo es 2 solicitudes por segundo por clave API. Superar este límite devuelve un error `429 Too Many Requests`. ## Rotar claves API \{#rotate-api-keys\} Si necesitas rotar las claves API secretas: 1. En **Settings → General**, haz clic en **Generate new key** y luego en el icono de papelera junto a la clave antigua. 2. Actualiza la clave utilizada en tu app. --- **Próximos pasos: Solicitudes:** - [Recuperar datos de analíticas](api-export-analytics/operations/retrieveAnalyticsData) - [Recuperar datos de cohorte](api-export-analytics/operations/retrieveCohortData) - [Recuperar datos de conversión](api-export-analytics/operations/retrieveConversionData) - [Recuperar datos de embudo](api-export-analytics/operations/retrieveFunnelData) - [Recuperar datos de Valor de por Vida (LTV)](api-export-analytics/operations/retrieveLTVData) - [Recuperar datos de retención](api-export-analytics/operations/retrieveRetentionData) --- # File: export-analytics-api-requests --- --- title: Solicitudes de API para exportar análisis --- Exportar tus datos de análisis a CSV te da la flexibilidad de profundizar en las métricas de rendimiento de tu app, personalizar informes y analizar tendencias a lo largo del tiempo. Con la API de Adapty, puedes extraer análisis detallados en formato CSV fácilmente, lo que resulta práctico para hacer seguimiento, compartir y refinar tus datos según necesites. ## Colección y entorno de Postman \{#postman-collection-and-environment\} Para simplificar el uso de nuestra API al exportar datos de análisis, hemos preparado una colección de Postman y un archivo de entorno que puedes descargar e importar en Postman. - **Colección de solicitudes**: Incluye todas las solicitudes disponibles en la API de exportación de análisis de Adapty. Ten en cuenta que utiliza variables que puedes definir en el entorno. - **Entorno**: Contiene una lista de variables donde puedes definir valores una sola vez. Hemos preparado un entorno unificado para la API del lado del servidor, la API web y la API de exportación de análisis para facilitarte el trabajo. Una vez que actives este entorno, Postman sustituirá automáticamente los valores de las variables definidas en tus solicitudes. :::tip [Descarga la colección y el entorno](https://raw.githubusercontent.com/adaptyteam/adapty-docs/refs/heads/main/Downloads/Adapty_export_analytics_API_postman_collection.zip) ::: Para más información sobre cómo importar una colección y un entorno en Postman, consulta la [documentación de Postman](https://learning.postman.com/docs/getting-started/importing-and-exporting/importing-data/). ### Variables utilizadas \{#variables-used\} Hemos creado un entorno unificado para la API del lado del servidor, la API web y la API de exportación de análisis para simplificar tu flujo de trabajo. A continuación se muestran las variables específicas de la API de exportación de análisis: | Variable | Descripción | Valor de ejemplo | | ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | | secret_api_key | Puedes encontrarla en el campo **Secret key** en [**App settings**](https://app.adapty.io/settings/general). | `secret_live_Pj1P1xzM.2CvSvE1IalQRFjsWy6csBVNpH33atnod` | **Solicitudes:** - [Recuperar datos de análisis](api-export-analytics/operations/retrieveAnalyticsData) - [Recuperar datos de cohorte](api-export-analytics/operations/retrieveCohortData) - [Recuperar datos de conversión](api-export-analytics/operations/retrieveConversionData) - [Recuperar datos de embudo](api-export-analytics/operations/retrieveFunnelData) - [Recuperar datos de Lifetime Value (LTV)](api-export-analytics/operations/retrieveLTVData) - [Recuperar datos de retención](api-export-analytics/operations/retrieveRetentionData) --- # End of Documentation _Generated on: 2026-05-15T20:13:57.477Z_ _Successfully processed: 17/18 files_ # CAPACITOR - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.477Z Total files: 40 --- # File: capacitor-sdk-overview --- --- title: "Capacitor SDK overview" description: "Learn about Adapty Capacitor SDK and its key features." --- [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-Capacitor.svg?style=flat&logo=capacitor)](https://github.com/adaptyteam/AdaptySDK-Capacitor/releases) Welcome! We're here to make in-app purchases a breeze 🚀 We've built the [Adapty Capacitor SDK](https://github.com/adaptyteam/AdaptySDK-Capacitor/) to take the headache out of in-app purchases so you can focus on what you do best – building amazing apps. Here's what we handle for you: - Handle purchases, receipt validation, and subscription management out of the box - Create and test paywalls without app updates - Get detailed purchase analytics with zero setup - cohorts, LTV, churn, and funnel analysis included - Keep the user subscription status always up to date across app sessions and devices - Integrate your app with marketing attribution and analytics services using just one line of code :::note Before diving into the code, you'll need to integrate Adapty with App Store Connect and Google Play Console, then set up products in the dashboard. Check out our [quickstart guide](quickstart) to get everything configured first. ::: ## Get started :::tip Our docs are optimized for use with LLMs. Check out [this article](adapty-cursor-capacitor) to learn how to get the best results when integrating the Adapty SDK using AI with our docs. ::: Here's what we'll cover in the integration guide: 1. [Install & configure SDK](sdk-installation-capacitor): Add the SDK as a [dependency](https://www.npmjs.com/package/@adapty/capacitor) to your project and activate it in the code. 2. [Enable purchases through paywalls](capacitor-quickstart-paywalls): Set up the purchase flow so users can buy products. 3. [Check the subscription status](capacitor-check-subscription-status): Automatically check the user's subscription state and control their access to paid content. 4. [Identify users (optional)](capacitor-quickstart-identify): Associate users with their Adapty profiles to ensure their data is stored consistently across devices. ### See it in action Want to see how it all comes together? We've got you covered: **Sample apps**: Check out our complete examples that demonstrate the full setup: - [React](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-react-example) - [Vue.js](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-vue-example) - [Angular](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-angular-example) - [Advanced development tools](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/adapty-devtools) ## Main concepts Before diving into the code, let's get familiar with the key concepts that make Adapty work. The beauty of Adapty's approach is that only placements are hardcoded in your app. Everything else – products, paywall designs, pricing, and offers – can be managed flexibly from the Adapty dashboard without app updates: 1. **Product** - Anything available for purchase in your app – subscription, consumable product, or lifetime access. 2. **Paywall** - The only way to retrieve products from Adapty and use it to its full power. We've designed it this way to make it easier to track how different product combinations affect your monetization metrics. A paywall in Adapty serves as both a specific set of your products and the visual configuration that accompanies them. 3. **Placement** - A strategic point in your user journey where you want to show a paywall. Think of placements as the "where" and "when" of your monetization strategy. Common placements include: - `main` - Your primary paywall location - `onboarding` - Shown during the user onboarding flow - `settings` - Accessible from your app's settings Start with the basics like `main` or `onboarding` for your first integration, then think about where else in your app users might be ready to purchase. 4. **Profile** - When users purchase a product, their profile is assigned an **access level** which you use to define access to paid features. --- # File: sdk-installation-capacitor --- --- title: "Capacitor - Instalación y configuración del SDK de Adapty" description: "Guía paso a paso para instalar el SDK de Adapty en Capacitor para apps con suscripciones." --- El SDK de Adapty incluye dos módulos clave para una integración fluida en tu app de Capacitor: - **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-Capacitor/tree/master/examples), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Requisitos \{#requirements\} El [SDK de Adapty para Capacitor](https://github.com/adaptyteam/AdaptySDK-Capacitor/) tiene los siguientes requisitos de versión: | Versión del SDK de Adapty | Versión de Capacitor | Versión de iOS | |---------------------------|----------------------|----------------| | 3.16.0+ | 8 | 15.0+ | | 3.15 | 7 | 14.0+ | Las versiones 6 e inferiores de Capacitor no están soportadas. :::info Adapty es compatible con Google Play Billing Library hasta la versión 8.x. Por defecto, Adapty trabaja con Google Play Billing Library v.7.0.0, pero si quieres forzar una versión posterior, puedes [añadir la dependencia](https://developer.android.com/google/play/billing/integrate#dependency) manualmente. ::: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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. </Callout> ## Instalar el SDK de Adapty \{#install-adapty-sdk\} [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-Capacitor.svg?style=flat&logo=capacitor)](https://github.com/adaptyteam/AdaptySDK-Capacitor/releases) Instala el SDK de Adapty: ```sh npm install @adapty/capacitor npx cap sync ``` ## Activar el módulo Adapty del SDK de Adapty \{#activate-adapty-module-of-adapty-sdk\} :::note El SDK de Adapty solo necesita activarse una vez en tu app. ::: 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. :::important - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API 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 cualquier archivo de tu app para activar Adapty: ```typescript showLineNumbers try { await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { // verbose logging is recommended for the development purposes and for the first production release logLevel: 'verbose', // in the development environment, use this variable to avoid multiple activation errors. Set it to your development environment variable __ignoreActivationOnFastRefresh: true, } }); console.log('Adapty activated successfully!'); } catch (error) { console.error('Failed to activate Adapty SDK:', error); } ``` :::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 Capacitor](capacitor-sdk-call-order) para ver la secuencia completa. ::: :::tip Para evitar errores de activación en el entorno de desarrollo, usa los [consejos](#development-environment-tips). ::: Ahora configura los paywalls en tu app: - Si usas el [Adapty Paywall Builder](adapty-paywall-builder), sigue el [inicio rápido con Paywall Builder](capacitor-quickstart-paywalls). - Si construyes tu propia UI de paywall, consulta el [inicio rápido para paywalls personalizados](capacitor-quickstart-manual). ## 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 cuando activas el módulo principal; no necesitas hacer nada más. ## Configuración opcional \{#optional-setup\} ### Logging \{#logging\} #### Configurar el sistema de logging \{#set-up-the-logging-system\} Adapty registra errores y otra información importante para ayudarte a entender qué está pasando. Los niveles disponibles son los siguientes: | Nivel | Descripción | | ---------- | ------------------------------------------------------------ | | `error` | Solo se registrarán errores | | `warn` | Se registrarán errores y mensajes del SDK que no causan errores críticos, pero que vale la pena 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 // Set log level before activation adapty.setLogLevel({ logLevel: 'verbose' }); // Or set it during configuration await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { 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 adicionales de seguridad de datos para cumplir con las directrices del store o del país. #### Deshabilitar la recopilación y el uso compartido de la dirección IP \{#disable-ip-address-collection-and-sharing\} Al activar el módulo Adapty, establece `ipAddressCollectionDisabled` en `true` para deshabilitar 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 las 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 await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { ipAddressCollectionDisabled: true, } }); ``` #### Deshabilitar la recopilación y el uso compartido del ID publicitario \{#disable-advertising-id-collection-and-sharing\} Al activar el módulo 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 se active la solicitud de App Tracking Transparency, o si tu app no requiere atribución publicitaria ni analíticas basadas en IDs publicitarios. ```typescript showLineNumbers await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { 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 sobreescribir la configuración de caché predeterminada: ```typescript showLineNumbers await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { 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ámetros: | Parámetro | Requerido | Descripción | |-----------|-----------|-------------| | memoryStorageTotalCostLimit | opcional | Tamaño total de la caché en memoria en bytes. El valor predeterminado es específico de la plataforma. | | memoryStorageCountLimit | opcional | El límite de elementos en el almacenamiento en memoria. El valor predeterminado es específico de la plataforma. | | diskStorageSizeLimit | opcional | El límite de tamaño de archivo en disco en bytes. El valor predeterminado es específico 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 await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { 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, incluyendo la información del perfil en caché, los detalles de los 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 del usuario en los servidores de Adapty no se ven afectados. ::: ```swift showLineNumbers await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { ios: { clearDataOnBackup: true, }, } }); ``` ## Consejos para el entorno de desarrollo \{#development-environment-tips\} #### Solucionar errores de activación del SDK en la recarga en vivo de Capacitor \{#troubleshoot-sdk-activation-errors-on-capacitors-live-reload\} Al desarrollar con el SDK de Adapty en Capacitor, puede 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 recarga en vivo de Capacitor activa múltiples llamadas de activación durante el desarrollo. Para evitarlo, usa la opción `__ignoreActivationOnFastRefresh` con el indicador del modo de desarrollo de Capacitor — diferirá según el bundle que estés usando. ```typescript showLineNumbers try { await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { // Set your development environment variable __ignoreActivationOnFastRefresh: true, } }); } 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\} Si obtienes un error de versión mínima de iOS, actualiza tu Podfile: ```diff -platform :ios, min_ios_version_supported +platform :ios, '14.0' # For core features only # OR +platform :ios, '15.0' # If using paywalls created in the paywall builder ``` #### Reglas de copia de seguridad de Android (configuración de Auto Backup) \{#android-backup-rules-auto-backup-configuration\} 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 `<manifest>` incluya tools: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.app"> ... </manifest> ``` #### 2. Sobreescribe los atributos de copia de seguridad en `<application>` \{#2-override-backup-attributes-in-application\} En el mismo archivo `AndroidManifest.xml`, actualiza la etiqueta `<application>` para que tu app proporcione los valores definitivos e indique al fusionador de manifiestos que reemplace los valores de las librerías: ```xml <application android:name=".App" android:allowBackup="true" android:fullBackupContent="@xml/sample_backup_rules" android:dataExtractionRules="@xml/sample_data_extraction_rules" tools:replace="android:fullBackupContent,android:dataExtractionRules"> ... </application> ``` 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" <?xml version="1.0" encoding="utf-8"?> <data-extraction-rules> <cloud-backup> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </cloud-backup> <device-transfer> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </device-transfer> </data-extraction-rules> ``` **Para Android 11 e inferior** (usa el formato legado de contenido de copia de seguridad completa): ```xml title="sample_backup_rules.xml" <?xml version="1.0" encoding="utf-8"?> <full-backup-content> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> :::tip Tras modificar archivos nativos de Android, ejecuta `npx cap sync android` para que Capacitor recoja los recursos actualizados si regeneras la plataforma. ::: #### 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 flujo de compra usa un `launchMode` no predeterminado, Android puede recrearla o reutilizarla incorrectamente 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, usa solo los modos de lanzamiento `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 <activity android:name=".MainActivity" android:launchMode="standard" /> ``` #### Errores de compilación de Swift 6 causados por la sobreescritura de SWIFT_VERSION en el Podfile \{#swift-6-build-errors-caused-by-podfile-swift_version-override\} Al compilar tu app de Capacitor para iOS, puede que veas errores de compilación de Swift 6 en los targets de pods de Adapty. Los síntomas típicos incluyen incompatibilidades de `@Sendable` en `AdaptyUIBuilderLogic`, conformidad faltante de `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. El código de tu propia app puede permanecer en Swift 5 — solo los targets de 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/App/Podfile` que reescribe `SWIFT_VERSION` para cada target de pod: ```ruby showLineNumbers title="ios/App/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 de pods de Adapty de la sobreescritura: ```ruby showLineNumbers title="ios/App/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 `npx cap sync ios` y vuelve a compilar. Para verificarlo, abre `ios/App/Pods/Pods.xcodeproj`, selecciona el target del pod `Adapty` → **Build Settings** → **Swift Language Version**. Debería ser **Swift 6**. --- # File: capacitor-quickstart-paywalls --- --- title: "Habilitar compras usando paywalls en el SDK de Capacitor" description: "Aprende cómo mostrar paywalls en tu app de Capacitor con el SDK de Adapty." --- Para habilitar las compras in-app, necesitas entender tres conceptos clave: - **Productos** – todo lo que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - **Paywalls**: son configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única manera de obtener productos, pero este diseño te permite modificar las ofertas, los precios y las combinaciones de productos sin tocar el código de tu app. - **Placements** – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 paywalls distintos a diferentes usuarios. Adapty te ofrece tres formas de habilitar compras en tu app. Elige la que mejor se adapte a los requisitos de tu app: | Implementación | Complejidad | Cuándo usarla | |----------------------------|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Adapty Paywall Builder** | ✅ Fácil | [Creas un paywall completo y listo para compras en el editor sin código](quickstart-paywalls). Adapty lo renderiza automáticamente y gestiona todo el flujo de compra, la validación de recibos y la administración de suscripciones. | | `makePurchase` | 🟡 Media | Implementas la UI del paywall en el código de tu app, pero igual obtienes el objeto paywall desde Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](capacitor-quickstart-manual). | | Modo Observer | 🔴 Difícil | Implementas el flujo de compra completamente por tu cuenta. Consulta la [guía](implement-observer-mode-capacitor). | :::important **Los pasos a continuación muestran cómo implementar un paywall creado en el Paywall Builder de Adapty.** Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](capacitor-making-purchases). ::: Para mostrar un paywall creado en el Paywall Builder de Adapty, en el código de tu app solo necesitas: 1. **Obtener el paywall**: Obtén el paywall desde Adapty. 2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app. 3. **Gestionar las acciones de los botones**: Asocia las interacciones de los usuarios con el paywall a las respuestas de tu app. Por ejemplo, abrir enlaces o cerrar el paywall cuando los usuarios pulsan 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-capacitor) 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. Obtener el paywall \{#1-get-the-paywall\} Tus paywalls están asociados a placements configurados en el dashboard. Los placements te permiten mostrar paywalls diferentes a distintas audiencias o ejecutar [pruebas A/B](ab-tests). Para obtener un paywall creado en el Paywall Builder de Adapty, necesitas: 1. Obtener el objeto `paywall` por el ID del [placement](placements) usando el método `getPaywall` y comprobar si es un paywall creado en el builder usando la propiedad `hasViewConfiguration`. 2. Crear la vista del paywall con el método `createPaywallView`. La vista contiene los elementos de UI y los estilos necesarios para mostrar el paywall. :::important Para obtener la configuración de la vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará. ::: ```typescript showLineNumbers title="Capacitor" try { const paywall = await adapty.getPaywall({ placementId: 'YOUR_PLACEMENT_ID', }); // the requested paywall } catch (error) { // handle the error } if (paywall.hasViewConfiguration) { try { const view = await createPaywallView(paywall); } catch (error) { // handle the error } } else { // use your custom logic } ``` ## 2. Mostrar el paywall \{#2-display-the-paywall\} Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo. Para mostrar el paywall, usa el método `view.present()` sobre el `view` creado por `createPaywallView`. Cada `view` solo se puede usar una vez. Si necesitas mostrar el paywall de nuevo, llama a `createPaywallView` otra vez para crear una nueva instancia de `view`. ```typescript showLineNumbers title="Capacitor" try { await view.present(); } catch (error) { // handle the error } ``` :::tip Para más detalles sobre cómo mostrar un paywall, consulta nuestra [guía](capacitor-present-paywalls). ::: ## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\} Cuando los usuarios pulsan botones en el paywall, el SDK de Capacitor gestiona automáticamente las compras, la restauración y el cierre del paywall. Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren que gestiones sus acciones en tu código. O puede que quieras anular su comportamiento predeterminado. Por ejemplo, puede que quieras mantener el paywall abierto cuando los usuarios abran un enlace web. Veamos cómo puedes gestionarlo en tu implementación. :::tip Lee nuestras guías sobre cómo gestionar [acciones](capacitor-handle-paywall-actions) y [eventos](capacitor-handling-events) de botones. ::: ```typescript showLineNumbers title="Capacitor" const unsubscribe = view.setEventHandlers({ onUrlPress(url) { window.open(url, '_blank'); return false; }, }); ``` ## Siguientes pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> 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. A continuación, necesitas [comprobar el nivel de acceso de los usuarios](capacitor-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í tienes cómo integrar todos estos pasos en tu app. ```typescript showLineNumbers title="Capacitor" export default function PaywallScreen() { const showPaywall = async () => { try { const paywall = await adapty.getPaywall({ placementId: 'YOUR_PLACEMENT_ID', }); if (!paywall.hasViewConfiguration) { // use your custom logic return; } const view = await createPaywallView(paywall); view.setEventHandlers({ onUrlPress(url) { window.open(url, '_blank'); return false; }, }); await view.present(); } catch (error) { // handle any error that may occur during the process console.warn('Error showing paywall:', error); } }; return ( <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}> <button onClick={showPaywall}>Show Paywall</button> </div> ); } ``` --- # File: capacitor-check-subscription-status --- --- title: "Comprobar el estado de la suscripción en el SDK de Capacitor" description: "Aprende a comprobar el estado de la suscripción en tu app de Capacitor con Adapty." --- Para decidir si los usuarios pueden acceder al contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil. Este artículo te muestra cómo acceder al estado del perfil para decidir qué deben ver los usuarios: si mostrarles un paywall o darles acceso a las funciones de pago. ## Obtener el estado de la suscripción \{#get-subscription-status\} Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones: - Llama a `getProfile` si necesitas los datos del perfil más recientes de inmediato (por ejemplo, al iniciar la app) o quieres forzar una actualización. - Configura **actualizaciones automáticas del perfil** para mantener una copia local que se actualice automáticamente cada vez que cambie el estado de la suscripción. ### Obtener el perfil \{#get-profile\} La forma más sencilla de obtener el estado de la suscripción es usar el método `getProfile` para acceder al perfil: ```typescript showLineNumbers try { const profile = await adapty.getProfile(); } catch (error) { // handle the error } ``` ### Escuchar actualizaciones de la suscripción \{#listen-to-subscription-updates\} Para recibir actualizaciones del perfil automáticamente en tu app: 1. Usa `adapty.addListener('onLatestProfileLoad')` para escuchar cambios en el perfil. Adapty llamará a este método automáticamente cada vez que cambie el estado de la suscripción del usuario. 2. Almacena los datos del perfil actualizados cuando se invoque este método, para poder usarlos en toda la app sin hacer peticiones de red adicionales. ```typescript showLineNumbers class SubscriptionManager { private currentProfile: any = null; constructor() { // Listen for profile updates adapty.addListener('onLatestProfileLoad', (data) => { this.currentProfile = data.profile; // Update UI, unlock content, etc. }); } // Use stored profile instead of calling getProfile() hasAccess(): boolean { return this.currentProfile?.accessLevels?.['YOUR_ACCESS_LEVEL']?.isActive ?? false; } } ``` :::note Adapty llama automáticamente al listener del evento `onLatestProfileLoad` cuando se inicia tu app, proporcionando datos de suscripción en caché aunque el dispositivo esté sin conexión. ::: ## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\} Cuando necesitas tomar decisiones inmediatas sobre si mostrar paywalls o dar acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en situaciones como el inicio de la app, al entrar en secciones premium o antes de mostrar contenido específico. ```typescript showLineNumbers const checkAccessLevel = async () => { try { const profile = await adapty.getProfile(); return profile?.accessLevels?.['YOUR_ACCESS_LEVEL']?.isActive === true; } catch (error) { console.warn('Error checking access level:', error); return false; // Show paywall if access check fails } }; const getAccessLevel = () => { return profile?.accessLevels?.['YOUR_ACCESS_LEVEL']; }; const initializePaywall = async () => { try { await loadPaywall(); const hasAccess = await checkAccessLevel(); if (!hasAccess) { // Show paywall if no access } } catch (error) { console.warn('Error initializing paywall:', error); } }; ``` ## Próximos pasos \{#next-steps\} Ahora que sabes cómo hacer seguimiento del estado de la suscripción, aprende a [trabajar con perfiles de usuario](capacitor-quickstart-identify) para garantizar que puedan acceder a lo que han pagado. --- # File: capacitor-quickstart-identify --- --- title: "Identificar usuarios en el SDK de Capacitor" description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app en Capacitor." --- La forma en que gestionas las compras de tus usuarios depende del modelo de autenticación de tu app: - Si tu app no usa autenticación de backend ni almacena datos de usuarios, consulta la [sección sobre usuarios anónimos](#anonymous-users). - Si tu app tiene (o tendrá) autenticación de backend, consulta la [sección sobre usuarios identificados](#identified-users). :::tip **Conceptos clave**: - Los **perfiles** son las entidades necesarias para que el SDK funcione. Adapty los crea automáticamente. Pueden ser anónimos (sin customer user ID) o identificados (con customer user ID). - Los **customer user IDs** son identificadores opcionales que **tú creas** para que Adapty vincule a tus usuarios con sus perfiles de Adapty. ::: Estas son las diferencias entre usuarios anónimos e identificados: | | Usuarios anónimos | Usuarios identificados | |------------------------------|----------------------------------------------------------------|-------------------------------------------------------------------------------------| | **Gestión de compras** | Restauración de compras a nivel de store | Mantienen el historial de compras en todos los dispositivos mediante su customer user ID | | **Gestión de perfiles** | Nuevo perfil en cada reinstalación | El mismo perfil en todas las sesiones y dispositivos | | **Persistencia de datos** | Los datos de usuarios anónimos están vinculados al dispositivo/instalación | Los datos de usuarios identificados persisten en todos los dispositivos y sesiones | ## Usuarios anónimos \{#anonymous-users\} Si no tienes autenticación de backend, **no necesitas gestionar la autenticación en el código de la app**: 1. Cuando el SDK se activa en el primer inicio de la app, Adapty **crea un nuevo perfil para el usuario**. 2. Cuando el usuario realiza cualquier compra en la app, esta compra queda **asociada a su perfil de Adapty y a su cuenta en el store**. 3. Cuando el usuario **reinstala** la app o la instala en un **nuevo dispositivo**, Adapty **crea un nuevo perfil vacío al activarse**. 4. Si el usuario ha realizado compras anteriormente en tu app, por defecto sus compras se sincronizan automáticamente desde el App Store al activar el SDK. :::note Las restauraciones desde copia de seguridad se comportan de forma diferente a las reinstalaciones. Por defecto, cuando un usuario restaura desde una copia de seguridad, el SDK conserva los datos en caché y no crea un nuevo perfil. Puedes configurar este comportamiento mediante el parámetro `clearDataOnBackup`. [Más información](sdk-installation-capacitor#clear-data-on-backup-restore). ::: ## Usuarios identificados \{#identified-users\} - Si un perfil todavía no tiene customer user ID (es decir, **el usuario no ha iniciado sesión**), cuando envías un customer user ID este queda asociado a ese perfil. - Si es una **reinstalación, un inicio de sesión o una instalación desde un nuevo dispositivo**, y ya has enviado su customer user ID anteriormente, no se crea un nuevo perfil. En su lugar, se cambia al perfil existente asociado al customer user ID. Tienes dos opciones para identificar usuarios en la app: - [**Durante el inicio/registro de sesión:**](#during-loginsignup) Si los usuarios inician sesión después de que tu app arranque, llama a `identify()` con un customer user ID cuando se autentiquen. - [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando la app se inicia, envíalo al llamar a `activate()`. :::important Por defecto, cuando Adapty recibe una compra de un Customer User ID que está actualmente asociado a otro Customer User ID, el nivel de acceso se comparte, de modo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o deshabilitar el uso compartido por completo. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles. ::: <img src="/assets/shared/img/identify-diagram.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Durante el inicio/registro de sesión \{#during-loginsignup\} Si identificas a los usuarios después de que la app se inicie (por ejemplo, tras iniciar sesión o registrarse), usa el método `identify` para establecer su customer user ID. - Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual. - Si **ya has usado este customer user ID para identificar al usuario antes**, Adapty cambiará a trabajar con el perfil asociado a ese customer user ID. :::tip Al crear un customer user ID, guárdalo junto con los datos de tu usuario para poder enviar el mismo ID cuando inicie sesión desde nuevos dispositivos o reinstale tu app. ::: Siempre usa `await` con `identify` antes de llamar a otros métodos del SDK. Las llamadas concurrentes producen `#3006 profileWasChanged` o recaen en el perfil anónimo. Consulta [Orden de llamadas en el SDK de Capacitor](capacitor-sdk-call-order). ```typescript showLineNumbers try { await adapty.identify({ customerUserId: "YOUR_USER_ID" }); // successfully identified } catch (error) { // handle the error } ``` ### Durante la activación del SDK \{#during-the-sdk-activation\} Si ya conoces el customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado. Si conoces el customer user ID pero lo estableces solo después de la activación, eso significa que, al activarse, Adapty creará un nuevo perfil vacío y cambiará al existente únicamente cuando llames a `identify`. Puedes pasar un customer user ID existente (uno que hayas usado antes) o uno nuevo. Si pasas uno nuevo, el nuevo perfil creado en la activación se vinculará automáticamente al customer user ID. :::tip Para excluir los perfiles vacíos creados de las [analíticas](analytics-charts) del dashboard, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics). ::: ```typescript showLineNumbers await adapty.activate({ apiKey: "YOUR_PUBLIC_SDK_KEY", params: { customerUserId: "YOUR_USER_ID" } }); ``` ### Cerrar sesión de usuarios \{#log-users-out\} Si tienes un botón para cerrar la sesión de los usuarios, usa el método `logout`. Esto crea un nuevo ID de perfil anónimo para el usuario. ```typescript showLineNumbers try { await adapty.logout(); // successful logout } catch (error) { // handle the error } ``` :::info Para volver a iniciar sesión en la app, usa el método `identify`. ::: ### Permitir compras sin inicio de sesión \{#allow-purchases-without-login\} Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, no necesitas realizar ninguna configuración adicional: Así es como funciona: 1. Cuando un usuario sin sesión iniciada realiza una compra, Adapty la asocia a su ID de perfil anónimo. 2. Cuando el usuario inicia sesión en su cuenta, Adapty cambia a trabajar con su perfil identificado. - Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), Adapty sincroniza sus transacciones automáticamente. - Si es un customer user ID nuevo (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que se mantiene todo el historial de compras. --- # File: adapty-cursor-capacitor --- --- title: "Integra Adapty en tu app de Capacitor con ayuda de IA" description: "Una guía paso a paso para integrar Adapty en tu app de Capacitor usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA." --- Esta página cubre dos formas de integrar Adapty en tu app de Capacitor. Usa la skill de integración del SDK para un flujo automatizado de principio a fin, o sigue el recorrido manual más abajo. ## Usa la skill de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\} La [skill adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza toda la integración: configuración del dashboard, instalación del SDK, paywall y verificación en cada etapa. El recorrido manual más abajo es la alternativa si tu herramienta no admite el formato Claude Skills. **Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI. ### Instalar \{#install\} Elige el formato para tu herramienta. La lista completa está en el [README de la skill](https://github.com/adaptyteam/adapty-sdk-integration-skill). - **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y luego `claude plugin install adapty-sdk-integration@adapty` desde tu terminal. - **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`. - **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`. - **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de skills de tu herramienta. ### Ejecutar \{#run\} En tu proyecto, ejecuta `/adapty-sdk-integration`. La skill detecta tu plataforma y hace algunas preguntas de configuración. Después recorre la configuración del dashboard, la instalación del SDK, el paywall y la verificación, obteniendo la documentación relevante de Adapty en cada etapa. :::note La skill está en beta. Si se detiene o se comporta de forma inesperada, el recorrido manual a continuación cubre cada etapa paso a paso. ::: ## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\} Adapty requiere cierta configuración en el dashboard antes de escribir cualquier código del SDK. Puedes hacerlo con una skill interactiva de LLM o manualmente desde el Dashboard. ### Enfoque con skill (recomendado) \{#skill-approach-recommended\} La skill de Adapty CLI permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin tener que abrir el Dashboard en cada paso. Solo necesitas [conectar tus stores](integrate-payments) en el Dashboard. ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` Una vez añadida la skill, ejecuta `/adapty-cli` en tu agente. Te guiará por cada paso, incluyendo cuándo abrir el Dashboard para conectar tus stores. ### Enfoque manual con el Dashboard \{#dashboard-approach\} Si prefieres configurarlo todo manualmente, esto es lo que necesitas antes de escribir código. Tu LLM no puede buscar los valores del dashboard por ti — tendrás que proporcionárselos. 1. **Conecta tus stores**: En el Adapty Dashboard, ve a **App settings → General**. Conecta tanto App Store como Google Play si tu app de Capacitor apunta a ambas plataformas. Esto es obligatorio para que funcionen las compras. [Conectar stores](integrate-payments) 2. **Copia tu Public SDK key**: En el Adapty Dashboard, ve a **App settings → General** y busca la sección **API keys**. En el código, es la cadena que pasas a `adapty.activate()`. 3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No referenciarás los productos directamente en el código — Adapty los entrega a través de paywalls. [Añadir productos](quickstart-products) 4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `adapty.getPaywall()`. [Crear paywall](quickstart-paywalls) 5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena que se verifica es `profile.accessLevels['premium']?.isActive`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago acceden a funciones distintas según el producto (por ejemplo, un plan `basic` vs. un plan `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar. :::tip Una vez que tengas los cinco, estás listo para escribir código. Dile a tu LLM: "Mi Public SDK key es X, mi placement ID es Y" para que pueda generar el código correcto de inicialización y obtención de paywalls. ::: ### Configura cuando estés listo \{#set-up-when-ready\} Estos pasos no son necesarios para empezar a programar, pero los necesitarás a medida que tu integración madure: - **Pruebas A/B**: Configúralas en la página **Placements**. No se necesita ningún cambio en el código. [Pruebas A/B](ab-tests) - **Paywalls y placements adicionales**: Añade más llamadas a `getPaywall` con diferentes IDs de placement. - **Integraciones de analíticas**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta [integraciones de analíticas](analytics-integration) e [integraciones de atribución](attribution-integration). ## Proporciona la documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\} ### Usa Context7 (recomendado) \{#use-context7-recommended\} [Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. Tu LLM obtiene los documentos adecuados automáticamente según lo que le preguntes — sin necesidad de pegar URLs manualmente. Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta: ``` npx ctx7 setup ``` Esto detecta tu editor y configura el servidor de Context7. Para la configuración manual, consulta el [repositorio de Context7 en GitHub](https://github.com/upstash/context7). Una vez configurado, referencia la librería de Adapty en tus prompts: ``` Use the adaptyteam/adapty-docs library to look up how to install the Capacitor SDK ``` :::warning Aunque Context7 elimina la necesidad de pegar links de documentación manualmente, el orden de implementación es importante. Sigue el [recorrido de implementación](#implementation-walkthrough) paso a paso para asegurarte de que todo funcione. ::: ### Usa documentación en texto plano \{#use-plain-text-docs\} Puedes acceder a cualquier documento de Adapty como Markdown en texto plano. Añade `.md` al final de su URL, o haz clic en **Copy for LLM** bajo el título del artículo. Por ejemplo: [adapty-cursor-capacitor.md](https://adapty.io/docs/es/adapty-cursor-capacitor.md). Cada etapa del [recorrido de implementación](#implementation-walkthrough) incluye un bloque "Envía esto a tu LLM" con links `.md` para pegar. Para acceder a más documentación a la vez, consulta los [archivos de índice y subconjuntos por plataforma](#plain-text-doc-index-files) más abajo. ## Recorrido de implementación \{#implementation-walkthrough\} El resto de esta guía recorre la integración de Adapty en orden de implementación. Cada etapa incluye los documentos que debes enviar a tu LLM, lo que deberías ver cuando esté listo y los problemas más comunes. ### Planifica tu integración \{#plan-your-integration\} Antes de ponerte a escribir código, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA admite un modo de planificación (como el de Cursor o el de Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir cualquier código. Dile a tu LLM qué enfoque usas para las compras — esto afecta a las guías que debe seguir: - [**Adapty Paywall Builder**](adapty-paywall-builder): Creas paywalls en el editor sin código de Adapty y el SDK los renderiza automáticamente. - [**Paywalls creados manualmente**](capacitor-making-purchases): Construyes tu propia UI de paywall en código, pero sigues usando Adapty para obtener productos y gestionar compras. - [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analíticas e integraciones. ¿No sabes cuál elegir? Lee la [tabla comparativa en el quickstart](capacitor-quickstart-paywalls). ### Instala y configura el SDK \{#install-and-configure-the-sdk\} Añade la dependencia del SDK de Adapty usando npm y actívalo con tu Public SDK key. Esto es la base — sin ello nada más funciona. **Guía:** [Instalar y configurar el SDK de Adapty](sdk-installation-capacitor) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/sdk-installation-capacitor.md ``` :::tip[Punto de control] - **Esperado:** La app se compila y ejecuta tanto en iOS como en Android. La consola muestra el log de activación de Adapty. - **Truco:** "Public API key is missing" → comprueba que hayas reemplazado el placeholder con tu clave real de App settings. ::: ### Muestra paywalls y gestiona compras \{#show-paywalls-and-handle-purchases\} Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras. Prueba cada compra en el sandbox mientras avanzas — no esperes al final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración. <Tabs groupId="paywall-approach"> <TabItem value="builder" label="Paywall Builder" default> **Guías:** - [Habilitar compras usando paywalls (quickstart)](capacitor-quickstart-paywalls) - [Obtener paywalls del Paywall Builder y su configuración](capacitor-get-pb-paywalls) - [Mostrar paywalls](capacitor-present-paywalls) - [Gestionar eventos del paywall](capacitor-handling-events) - [Responder a las acciones de los botones](capacitor-handle-paywall-actions) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/capacitor-quickstart-paywalls.md - https://adapty.io/docs/es/capacitor-get-pb-paywalls.md - https://adapty.io/docs/es/capacitor-present-paywalls.md - https://adapty.io/docs/es/capacitor-handling-events.md - https://adapty.io/docs/es/capacitor-handle-paywall-actions.md ``` :::tip[Punto de control] - **Esperado:** El paywall aparece con tus productos configurados. Al tocar un producto se activa el diálogo de compra en sandbox. - **Truco:** Paywall vacío o error en `getPaywall` → verifica que el ID del placement coincida exactamente con el del dashboard y que el placement tenga una audiencia asignada. ::: </TabItem> <TabItem value="manual" label="Manual paywalls"> **Guías:** - [Habilitar compras en tu paywall personalizado (quickstart)](capacitor-quickstart-manual) - [Obtener paywalls y productos](fetch-paywalls-and-products-capacitor) - [Renderizar paywall diseñado con Remote Config](present-remote-config-paywalls-capacitor) - [Realizar compras](capacitor-making-purchases) - [Restaurar compras](capacitor-restore-purchase) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/capacitor-quickstart-manual.md - https://adapty.io/docs/es/fetch-paywalls-and-products-capacitor.md - https://adapty.io/docs/es/present-remote-config-paywalls-capacitor.md - https://adapty.io/docs/es/capacitor-making-purchases.md - https://adapty.io/docs/es/capacitor-restore-purchase.md ``` :::tip[Punto de control] - **Esperado:** Tu paywall personalizado muestra los productos obtenidos desde Adapty. Al tocar un producto se activa el diálogo de compra en sandbox. - **Truco:** Array de productos vacío → verifica que el paywall tenga productos asignados en el dashboard y que el placement tenga una audiencia. ::: </TabItem> <TabItem value="observer" label="Observer mode"> **Guías:** - [Descripción general del modo Observer](observer-vs-full-mode) - [Implementar el modo Observer](implement-observer-mode-capacitor) - [Reportar transacciones en el modo Observer](report-transactions-observer-mode-capacitor) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/observer-vs-full-mode.md - https://adapty.io/docs/es/implement-observer-mode-capacitor.md - https://adapty.io/docs/es/report-transactions-observer-mode-capacitor.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox usando tu flujo de compra existente, la transacción aparece en el **Event Feed** del dashboard de Adapty. - **Truco:** Sin eventos → verifica que estás reportando las transacciones a Adapty y que las notificaciones del servidor están configuradas para ambos stores. ::: </TabItem> </Tabs> ### Comprueba el estado de la suscripción \{#check-subscription-status\} Tras una compra, verifica el perfil de usuario para detectar un nivel de acceso activo y proteger el contenido premium. **Guía:** [Comprobar el estado de la suscripción](capacitor-check-subscription-status) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/capacitor-check-subscription-status.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox, `profile.accessLevels['premium']?.isActive` devuelve `true`. - **Truco:** `accessLevels` vacío tras la compra → comprueba que el producto tenga un nivel de acceso asignado en el dashboard. ::: ### Identifica a los usuarios \{#identify-users\} Vincula las cuentas de usuario de tu app con los perfiles de Adapty para que las compras persistan entre dispositivos. :::important Omite este paso si tu app no tiene autenticación. ::: **Guía:** [Identificar usuarios](capacitor-quickstart-identify) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/capacitor-quickstart-identify.md ``` :::tip[Punto de control] - **Esperado:** Tras llamar a `adapty.identify()`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado. - **Truco:** Llama a `identify` después de la activación pero antes de obtener los paywalls para evitar problemas de atribución del perfil anónimo. ::: ### Prepárate para el lanzamiento \{#prepare-for-release\} Una vez que tu integración funcione en el sandbox, repasa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción. **Guía:** [Lista de verificación de lanzamiento](release-checklist) Envía esto a tu LLM: ``` Read these Adapty docs before releasing: - https://adapty.io/docs/es/release-checklist.md ``` :::tip[Punto de control] - **Esperado:** Todos los elementos de la lista confirmados: conexiones de store, notificaciones del servidor, flujo de compra, comprobaciones de nivel de acceso y requisitos de privacidad. - **Truco:** Notificaciones del servidor ausentes → configura las App Store Server Notifications en **App settings → iOS SDK** y las Google Play Real-Time Developer Notifications en **App settings → Android SDK**. ::: ## Archivos de índice de documentación en texto plano \{#plain-text-doc-index-files\} Si necesitas dar a tu LLM un contexto más amplio más allá de páginas individuales, disponemos de archivos de índice que listan o combinan toda la documentación de Adapty: - [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con links `.md`. Un [estándar emergente](https://llmstxt.org/) para hacer los sitios web accesibles a los LLMs. Ten en cuenta que para algunos agentes de IA (p. ej., ChatGPT) tendrás que descargar `llms.txt` y subirlo al chat como archivo. - [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación del sitio de Adapty combinada en un único archivo. Muy grande — úsalo solo cuando necesites el panorama completo. - Subconjuntos específicos de Capacitor: [`capacitor-llms.txt`](https://adapty.io/docs/es/capacitor-llms.txt) y [`capacitor-llms-full.txt`](https://adapty.io/docs/es/capacitor-llms-full.txt): Subconjuntos por plataforma que ahorran tokens en comparación con el sitio completo. --- # File: capacitor-paywalls --- --- title: "Paywalls" description: "Learn how to work with paywalls in your Capacitor app with Adapty SDK." --- ## Display paywalls ### Adapty Paywall Builder <CustomDocCardList ids={['capacitor-get-pb-paywalls', 'capacitor-present-paywalls', 'capacitor-handling-events', 'capacitor-handle-paywall-actions']} /> :::tip To get started with the Adapty Paywall Builder paywalls quickly, see our [quickstart guide](capacitor-quickstart-paywalls). ::: ### Implement paywalls manually <CustomDocCardList ids={['capacitor-quickstart-manual', 'fetch-paywalls-and-products-capacitor', 'present-remote-config-paywalls-capacitor', 'capacitor-making-purchases']} /> For more guides on implementing paywalls and handling purchases manually, see the [category](capacitor-implement-paywalls-manually). ## Useful features <CustomDocCardList ids={['capacitor-use-fallback-paywalls', 'capacitor-web-paywall']} /> --- # File: capacitor-get-pb-paywalls --- --- title: "Obtener paywalls de Paywall Builder y su configuración en el SDK de Capacitor" description: "Aprende a recuperar paywalls de PB en Adapty para un mejor control de las suscripciones en tu app de Capacitor." --- Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación. Ten en cuenta que este tema hace referencia a paywalls personalizados con Paywall Builder. Para obtener información sobre cómo recuperar paywalls con Remote Config, consulta el tema [Obtener paywalls y productos para paywalls con Remote Config en tu app móvil](fetch-paywalls-and-products-capacitor). <details> <summary>Antes de empezar a mostrar paywalls en tu app móvil (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en ellos](create-placement) en el Adapty Dashboard. 4. Instala el [SDK de Adapty](sdk-installation-capacitor) en tu app móvil. </details> ## Obtener un paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\} Si has [diseñado un paywall con Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app móvil para mostrárselo al usuario. Ese tipo de paywall contiene tanto lo que debe mostrarse como la forma en que debe mostrarse. Sin embargo, necesitas obtener su ID a través del placement, su configuración de vista, y luego presentarlo en tu app móvil. Para garantizar un rendimiento óptimo, es fundamental recuperar el paywall y su [configuración de vista](capacitor-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dejando tiempo suficiente para que las imágenes se descarguen antes de presentarlas al usuario. Para obtener un paywall, usa el método `getPaywall`: ```typescript showLineNumbers try { const paywall = await adapty.getPaywall({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en', }); // the requested paywall } catch (error) { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto de uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma, la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **params** | opcional | Parámetros adicionales para obtener el paywall. | **No escribas IDs de productos en el código.** El único ID que debes escribir directamente en el código es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe gestionar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana tres, muéstralos todos sin necesidad de modificar el código. Parámetros de respuesta: | Parámetro | Descripción | | :-------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Paywall | Un objeto [`AdaptyPaywall`](https://capacitor.adapty.io/interfaces/adaptypaywall) con una lista de IDs de productos, el identificador del paywall, el Remote Config y otras propiedades. | ## Obtener la configuración de vista del paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important Asegúrate de activar el botón **Show on device** en el Paywall Builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperar. ::: Después de obtener el paywall, comprueba si incluye un `ViewConfiguration`, lo que indica que fue creado con Paywall Builder. Esto te orientará sobre cómo mostrar el paywall. Si el `ViewConfiguration` está presente, trátalo como un paywall de Paywall Builder; si no lo está, [trátalo como un paywall de Remote Config](present-remote-config-paywalls-capacitor). En el SDK de Capacitor, llama directamente al método `createPaywallView` sin necesidad de obtener primero la configuración de vista manualmente. :::warning El resultado del método `createPaywallView` solo puede usarse una vez. Si necesitas usarlo de nuevo, vuelve a llamar al método `createPaywallView`. ::: ```typescript showLineNumbers if (paywall.hasViewConfiguration) { try { const view = await createPaywallView(paywall); } catch (error) { // handle the error } } else { // use your custom logic } ``` Parámetros: | Parámetro | Presencia | Descripción | | :------------------- | :------- | :----------------------------------------------------------- | | **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. | | **customTags** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas actúan como marcadores de posición en el contenido del paywall y se sustituyen dinámicamente por cadenas específicas para personalizar el contenido. Consulta el tema [Etiquetas personalizadas en el Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | | **prefetchProducts** | opcional | Actívalo para optimizar el momento de visualización de los productos en pantalla. Cuando es `true`, AdaptyUI obtiene automáticamente los productos necesarios. Por defecto: `false`. | :::note Si usas varios idiomas, aprende a añadir una [localización en Paywall Builder](add-paywall-locale-in-adapty-paywall-builder) y cómo usar correctamente los códigos de idioma [aquí](capacitor-localizations-and-locale-codes). ::: Una vez que tengas la vista, [presenta el paywall](capacitor-present-paywalls). ## Obtener un paywall para la audiencia predeterminada y recuperarlo más rápido \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} Por lo general, los paywalls se obtienen casi de forma instantánea, por lo que no necesitas preocuparte por agilizar este proceso. Sin embargo, si tienes muchas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseado. En esas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia fluida al usuario en lugar de no mostrar ningún paywall. Para resolver esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, tal como se detalla en la sección [Obtener el paywall](#fetch-paywall-designed-with-paywall-builder) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls diferentes para distintas versiones de la app (actual y futuras), tendrás que diseñar paywalls que sean compatibles con la versión actual (heredada) o asumir que los usuarios con esa versión podrían encontrarse con paywalls que no se renderizan correctamente. - **Pérdida de segmentación**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluida la basada en países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, usa `getPaywall` como se describe [arriba](#fetch-paywall-designed-with-paywall-builder). ::: ```typescript showLineNumbers try { const paywall = await adapty.getPaywallForDefaultAudience({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en', }); // the requested paywall } catch (error) { // handle the error } ``` :::note El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.2 del SDK de Capacitor. ::: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto de una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma, la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](capacitor-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **params** | opcional | Parámetros adicionales para obtener el paywall. | ## Personalizar recursos \{#customize-assets\} Para personalizar imágenes y vídeos en tu paywall, implementa los recursos personalizados. Las imágenes y vídeos principales tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de recursos personalizados, seleccionas estos elementos por sus IDs y personalizas su comportamiento. Para otras imágenes y vídeos, debes [establecer un ID personalizado](custom-media) en el dashboard de Adapty. Por ejemplo, puedes: - Mostrar una imagen o vídeo diferente a algunos usuarios. - Mostrar una imagen de vista previa local mientras se carga la imagen principal remota. - Mostrar una imagen de vista previa antes de reproducir un vídeo. :::important Para usar esta función, actualiza el SDK de Capacitor de Adapty a la versión 3.8.0 o superior. ::: Aquí tienes un ejemplo de cómo puedes proporcionar recursos personalizados mediante un diccionario sencillo: ```typescript showLineNumbers const customAssets: Record<string, AdaptyCustomAsset> = { 'custom_image': { type: 'image', relativeAssetPath: 'custom_image.png' }, 'hero_video': { type: 'video', fileLocation: { ios: { fileName: 'custom_video.mp4' }, android: { relativeAssetPath: 'videos/custom_video.mp4' } } } }; view = await createPaywallView(paywall, { customAssets }); ``` :::note Si no se encuentra un recurso, el paywall mostrará su apariencia predeterminada. ::: --- # File: capacitor-present-paywalls --- --- title: "Presentar paywalls de Paywall Builder en Capacitor SDK" description: "Presenta paywalls en aplicaciones Capacitor usando Adapty." --- Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu aplicación móvil para mostrárselo al usuario. Ese paywall contiene tanto lo que debe mostrarse como la forma en que debe mostrarse. :::warning Esta guía es solo para **paywalls de Paywall Builder**. El proceso para presentar paywalls difiere en el caso de los paywalls de Remote Config. Para presentar **paywalls de Remote Config**, consulta [Renderizar un paywall diseñado con Remote Config](present-remote-config-paywalls). ::: Para mostrar un paywall, usa el método `view.present()` sobre el `view` creado por el método [`createPaywallView`](capacitor-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `createPaywallView` otra vez para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo puede provocar un error. ::: ```typescript showLineNumbers const view = await createPaywallView(paywall); view.setEventHandlers({ onUrlPress(url) { window.open(url, '_blank'); return false; }, }); try { await view.present(); } catch (error) { // handle the error } ``` ## Usar un temporizador definido por el desarrollador \{#use-developer-defined-timer\} Para usar temporizadores definidos por el desarrollador en tu aplicación móvil, utiliza el `timerId`; en este ejemplo, `CUSTOM_TIMER_NY`, que es el **Timer ID** del temporizador definido por el desarrollador que estableciste en el Adapty dashboard. Esto garantiza que tu aplicación actualice dinámicamente el temporizador con el valor correcto, como `13d 09h 03m 34s` (calculado como la hora de fin del temporizador, por ejemplo el Año Nuevo, menos la hora actual). ```typescript showLineNumbers const customTimers = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) }; const view = await createPaywallView(paywall, { customTimers }); ``` En este ejemplo, `CUSTOM_TIMER_NY` es el **Timer ID** del temporizador definido por el desarrollador que estableciste en el Adapty dashboard. El temporizador garantiza que tu aplicación actualice dinámicamente el valor con el resultado correcto, como `13d 09h 03m 34s` (calculado como la hora de fin del temporizador, por ejemplo el Año Nuevo, menos la hora actual). ## Mostrar un diálogo \{#show-dialog\} Usa este método en lugar de los diálogos de alerta nativos cuando se presenta una vista de paywall en Android. En Android, las alertas normales aparecen detrás de la vista del paywall, lo que las hace invisibles para los usuarios. Este método garantiza que el diálogo se muestre correctamente por encima del paywall en todas las plataformas. ```typescript showLineNumbers title="Capacitor" try { const action = await view.showDialog({ title: 'Close paywall?', content: 'You will lose access to exclusive offers.', primaryActionTitle: 'Stay', secondaryActionTitle: 'Close', }); if (action === 'secondary') { // User confirmed - close the paywall await view.dismiss(); } // If primary - do nothing, user stays } catch (error) { // handle error } ``` ## Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el paywall en iOS pasando el parámetro `iosPresentationStyle` al método `present()`. El parámetro acepta los valores `'full_screen'` (predeterminado) o `'page_sheet'`. ```typescript showLineNumbers await view.present({ iosPresentationStyle: 'page_sheet' }); ``` --- # File: capacitor-handle-paywall-actions --- --- title: "Responder a acciones de botones en el SDK de Capacitor" description: "Gestiona las acciones de botones del paywall en Capacitor usando Adapty para una mejor monetización de tu app." --- Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente: 1. Añade un [botón en el Paywall Builder](paywall-buttons) y asígnale una acción predefinida o crea un ID de acción personalizado. 2. Escribe código en tu app para gestionar cada acción que hayas asignado. Esta guía muestra cómo gestionar acciones personalizadas y predefinidas en tu código. ## Cerrar paywalls \{#close-paywalls\} Para añadir un botón que cierre tu paywall: 1. En el Paywall Builder, añade un botón y asígnale la acción **Close**. 2. En el código de tu app, implementa un manejador para la acción `close` que cierre el paywall. :::info En el SDK de Capacitor, la acción `close` activa el cierre del paywall por defecto. Sin embargo, puedes modificar este comportamiento en tu código si lo necesitas. Por ejemplo, cerrar un paywall podría activar la apertura de otro. ::: ```typescript showLineNumbers const view = await createPaywallView(paywall); const unsubscribe = view.setEventHandlers({ onCloseButtonPress() { console.log('User closed paywall'); return true; // Allow the paywall to close } }); ``` ## Abrir URLs desde paywalls \{#open-urls-from-paywalls\} :::tip Si quieres añadir un grupo de enlaces (por ejemplo, términos de uso y restauración de compras), añade un elemento **Link** en el Paywall Builder y gestiónalo igual que los botones con la acción **Open URL**. ::: Para añadir un botón que abra un enlace desde tu paywall (por ejemplo, **Terms of use** o **Privacy policy**): 1. En el Paywall Builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir. 2. En el código de tu app, implementa un manejador para la acción `openUrl` que abra la URL recibida en un navegador. :::info En el SDK de Capacitor, la acción `window.open` activa la apertura de la URL por defecto. Sin embargo, puedes modificar este comportamiento en tu código si lo necesitas. ::: ```typescript showLineNumbers const view = await createPaywallView(paywall); const unsubscribe = view.setEventHandlers({ onUrlPress(url) { window.open(url, '_blank'); return false; // Don't close the paywall }, }); ``` ## Iniciar sesión en la app \{#log-into-the-app\} Para añadir un botón que permita a los usuarios iniciar sesión en tu app: 1. En el Paywall Builder, añade un botón y asígnale la acción **Login**. 2. En el código de tu app, implementa un manejador para la acción `login` que identifique a tu usuario. ```typescript showLineNumbers const view = await createPaywallView(paywall); const unsubscribe = view.setEventHandlers({ onCustomAction(actionId) { if (actionId === 'login') { // Navigate to login screen console.log('User requested login'); } } }); ``` ## Gestionar acciones personalizadas \{#handle-custom-actions\} Para añadir un botón que gestione cualquier otra acción: 1. En el Paywall Builder, añade un botón, asígnale la acción **Custom** y dale un ID. 2. En el código de tu app, implementa un manejador para el ID de acción que hayas creado. Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall: ```typescript showLineNumbers const unsubscribe = view.setEventHandlers({ onCustomAction(actionId) { if (actionId === 'openNewPaywall') { // Display another paywall console.log('User requested new paywall'); } }, }); ``` --- # File: capacitor-handling-events --- --- title: "Capacitor - Gestionar eventos del paywall" description: "Gestiona eventos de suscripción en Capacitor con el SDK de Adapty." --- :::important Esta guía cubre la gestión de eventos para compras, restauraciones, selección de productos y renderizado del paywall. También debes implementar la gestión de botones (cerrar el paywall, abrir enlaces, etc.). Consulta nuestra [guía sobre la gestión de acciones de botones](capacitor-handle-paywall-actions) para más detalles. ::: Los paywalls configurados con el [Paywall Builder](adapty-paywall-builder) no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan ciertos eventos a los que tu aplicación puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.), así como notificaciones sobre acciones relacionadas con compras realizadas en el paywall. A continuación aprenderás cómo responder a estos eventos. Para controlar o monitorizar los procesos que ocurren en la pantalla del paywall dentro de tu aplicación, implementa el método `view.setEventHandlers`: ```typescript showLineNumbers const view = await createPaywallView(paywall); const unsubscribe = view.setEventHandlers({ onCloseButtonPress() { console.log('User closed paywall'); return true; // Allow the paywall to close }, onAndroidSystemBack() { console.log('User pressed back button'); return true; // Allow the paywall to close }, onAppeared() { console.log('Paywall appeared'); return false; // Don't close the paywall }, onDisappeared() { console.log('Paywall disappeared'); }, onPurchaseCompleted(purchaseResult, product) { console.log('Purchase completed:', purchaseResult); return purchaseResult.type !== 'user_cancelled'; // Close if not cancelled }, onPurchaseStarted(product) { console.log('Purchase started:', product); return false; // Don't close the paywall }, onPurchaseFailed(error, product) { console.error('Purchase failed:', error); return false; // Don't close the paywall }, onRestoreCompleted(profile) { console.log('Restore completed:', profile); return true; // Close the paywall after successful restore }, onRestoreFailed(error) { console.error('Restore failed:', error); return false; // Don't close the paywall }, onProductSelected(productId) { console.log('Product selected:', productId); return false; // Don't close the paywall }, onRenderingFailed(error) { console.error('Rendering failed:', error); return false; // Don't close the paywall }, onLoadingProductsFailed(error) { console.error('Loading products failed:', error); return false; // Don't close the paywall }, onUrlPress(url) { window.open(url, '_blank'); return false; // Don't close the paywall }, }); ``` <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```typescript // onCloseButtonPress { "event": "close_button_press" } // onAndroidSystemBack { "event": "android_system_back" } // onAppeared { "event": "paywall_shown" } // onDisappeared { "event": "paywall_closed" } // onUrlPress { "event": "url_press", "url": "https://example.com/terms" } // onCustomAction { "event": "custom_action", "actionId": "login" } // onProductSelected { "event": "product_selected", "productId": "premium_monthly" } // onPurchaseStarted { "event": "purchase_started", "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } // onPurchaseCompleted - Success { "event": "purchase_completed", "purchaseResult": { "type": "success", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } // onPurchaseCompleted - Cancelled { "event": "purchase_completed", "purchaseResult": { "type": "user_cancelled" }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } // onPurchaseFailed { "event": "purchase_failed", "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" } } } // onRestoreCompleted { "event": "restore_completed", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } // onRestoreFailed { "event": "restore_failed", "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } // onRenderingFailed { "event": "rendering_failed", "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } // onLoadingProductsFailed { "event": "loading_products_failed", "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } ``` </Details> Puedes registrar solo los manejadores de eventos que necesites y omitir los que no uses. En ese caso, no se crearán los listeners de eventos no utilizados. No hay manejadores de eventos obligatorios. Los manejadores de eventos devuelven un booleano. Si se devuelve `true`, el proceso de visualización se considera completado, por lo que la pantalla del paywall se cierra y se eliminan los listeners de eventos para esa vista. Algunos manejadores de eventos tienen un comportamiento predeterminado que puedes sobrescribir si lo necesitas: - `onCloseButtonPress`: cierra el paywall al pulsar el botón de cierre. - `onAndroidSystemBack`: cierra el paywall al pulsar el botón **Back**. - `onRestoreCompleted`: cierra el paywall tras una restauración exitosa. - `onPurchaseCompleted`: cierra el paywall a menos que el usuario haya cancelado. - `onRenderingFailed`: cierra el paywall si falla su renderizado. - `onUrlPress`: abre las URLs en el navegador del sistema y mantiene el paywall abierto. ### Manejadores de eventos \{#event-handlers\} | Manejador de evento | Descripción | |:----------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **onCustomAction** | Se invoca cuando el usuario realiza una acción personalizada, p. ej., hace clic en un [botón personalizado](paywall-buttons). | | **onUrlPress** | Se invoca cuando el usuario hace clic en una URL del paywall. | | **onAndroidSystemBack** | Se invoca cuando el usuario pulsa el botón **Back** del sistema Android. | | **onCloseButtonPress** | Se invoca cuando el botón de cierre es visible y el usuario lo pulsa. Se recomienda cerrar la pantalla del paywall en este manejador. | | **onPurchaseCompleted** | Se invoca cuando la compra se completa, ya sea con éxito, cancelada por el usuario o pendiente de aprobación. En caso de compra exitosa, proporciona un `AdaptyProfile` actualizado. Las cancelaciones del usuario y los pagos pendientes (p. ej., aprobación parental requerida) disparan este evento, no `onPurchaseFailed`. | | **onPurchaseStarted** | Se invoca cuando el usuario pulsa el botón de acción "Comprar" para iniciar el proceso de compra. | | **onPurchaseCancelled** | Se invoca cuando el usuario inicia el proceso de compra y lo interrumpe manualmente (cancela el diálogo de pago). | | **onPurchaseFailed** | Se invoca cuando una compra falla debido a errores (p. ej., restricciones de pago, productos no válidos, fallos de red, errores de verificación de transacciones). No se invoca por cancelaciones del usuario ni pagos pendientes, que disparan `onPurchaseCompleted` en su lugar. | | **onRestoreStarted** | Se invoca cuando el usuario inicia un proceso de restauración de compras. | | **onRestoreCompleted** | Se invoca cuando la restauración de compras tiene éxito y proporciona un `AdaptyProfile` actualizado. Se recomienda cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de la suscripción](capacitor-listen-subscription-changes) para saber cómo comprobarlo. | | **onRestoreFailed** | Se invoca cuando el proceso de restauración falla y proporciona un `AdaptyError`. | | **onProductSelected** | Se invoca cuando se selecciona cualquier producto en la vista del paywall, lo que te permite monitorizar qué elige el usuario antes de la compra. | | **onAppeared** | Se invoca cuando la vista del paywall aparece en pantalla. En iOS, también se invoca cuando el usuario pulsa el [botón de paywall web](web-paywall#step-2a-add-a-web-purchase-button) dentro de un paywall y se abre un paywall web en un navegador in-app. | | **onDisappeared** | Se invoca cuando la vista del paywall desaparece de la pantalla. En iOS, también se invoca cuando un [paywall web](web-paywall#step-2a-add-a-web-purchase-button) abierto desde un paywall en un navegador in-app desaparece de la pantalla. | | **onRenderingFailed** | Se invoca cuando ocurre un error durante el renderizado de la vista y proporciona un `AdaptyError`. Estos errores no deberían ocurrir, así que si te encuentras con uno, comunícanoslo. | | **onLoadingProductsFailed** | Se invoca cuando la carga de productos falla y proporciona un `AdaptyError`. Si no has establecido `prefetchProducts: true` al crear la vista, AdaptyUI recuperará los objetos necesarios del servidor por sí mismo. | --- # File: capacitor-use-fallback-paywalls --- --- title: "Capacitor - Usar paywalls de respaldo" description: "Gestiona los casos en que los usuarios están sin conexión o los servidores de Adapty no están disponibles" --- Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet. * **Si la aplicación no puede acceder a los servidores de Adapty:** Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding. * **Si la aplicación no puede acceder a internet:** Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar. :::important Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty. ::: ## Configuración \{#configuration\} ### Android \{#android\} 1. Añade el archivo de configuración de respaldo a tu aplicación. Elige uno de los siguientes directorios: * **android/app/src/main/assets/** * **android/app/src/main/res/raw/** Nota: La carpeta `res/raw` tiene una convención especial para los nombres de archivo (deben comenzar con una letra, sin mayúsculas, sin caracteres especiales salvo el guion bajo y sin espacios en los nombres). 2. Actualiza la propiedad `android` de la constante `FileLocation`: * Si el archivo está en el directorio `assets`, pasa la ruta del archivo relativa a ese directorio. * Si el archivo está en el directorio `res/raw`, pasa el nombre del archivo sin la extensión. ### iOS \{#ios\} 1. Añade el archivo JSON de respaldo al bundle de tu proyecto: abre el menú **File** en XCode y selecciona la opción **Add Files to "YourProjectName"**. 2. Pasa el nombre de tu archivo de configuración a la propiedad `ios` de la constante `FileLocation`. ## Ejemplo \{#example\} ```typescript showLineNumbers const fileLocation = { ios: { fileName: 'ios_fallback.json' }, android: { //if the file is located in 'android/app/src/main/assets/' relativeAssetPath: 'android_fallback.json' } }; await adapty.setFallback({ fileLocation }); ``` Parámetros: | Parámetro | Descripción | | :------------------- | :------------------------------------------------------- | | **fileLocation** | Objeto que representa la ubicación del archivo de configuración de respaldo. | --- # File: capacitor-web-paywall --- --- title: "Implementar paywalls web" description: "Aprende a implementar paywalls web en tu app de Capacitor con el SDK de Adapty." --- :::important Antes de comenzar, asegúrate de haber [configurado tu paywall web en el dashboard](web-paywall) y de tener instalada la versión 3.6.1 o posterior del SDK de Adapty. ::: ## Abrir paywalls web \{#open-web-paywalls\} Si estás trabajando con un paywall que desarrollaste tú mismo, necesitas gestionar los paywalls web usando el método del SDK. El método `.openWebPaywall`: 1. Genera una URL única que permite a Adapty vincular un paywall concreto mostrado a un usuario específico con la página web a la que es redirigido. 2. Detecta cuando tus usuarios vuelven a la app y luego solicita `.getProfile` a intervalos cortos para determinar si los derechos de acceso del perfil se han actualizado. De esta forma, si el pago fue exitoso y los derechos de acceso se han actualizado, la suscripción se activa en la app casi de inmediato. ```typescript showLineNumbers try { await adapty.openWebPaywall({ paywallOrProduct: product }); } catch (error) { console.error('Failed to open web paywall:', error); } ``` :::note Existen dos versiones del método `openWebPaywall`: 1. `openWebPaywall({ paywallOrProduct: product })` que genera URLs por paywall y también añade los datos del producto a las URLs. 2. `openWebPaywall({ paywallOrProduct: paywall })` que genera URLs por paywall sin añadir los datos del producto a las URLs. Úsalo cuando tus productos en el paywall de Adapty difieran de los del paywall web. ::: #### Gestionar errores \{#handle-errors\} | Error | Descripción | Acción recomendada | |-----------------------------------------|----------------------------------------------------------------|----------------------------------------------------------------------------------------| | AdaptyError.paywallWithoutPurchaseUrl | El paywall no tiene configurada una URL de compra web | Comprueba si el paywall está correctamente configurado en el Adapty Dashboard | | AdaptyError.productWithoutPurchaseUrl | El producto no tiene una URL de compra web | Verifica la configuración del producto en el Adapty Dashboard | | AdaptyError.failedOpeningWebPaywallUrl | No se pudo abrir la URL en el navegador | Comprueba la configuración del dispositivo o proporciona un método de compra alternativo | | AdaptyError.failedDecodingWebPaywallUrl | No se pudieron codificar correctamente los parámetros en la URL | Verifica que los parámetros de la URL sean válidos y estén correctamente formateados | ## Abrir paywalls web en un navegador in-app \{#open-web-paywalls-in-an-in-app-browser\} :::important La apertura de paywalls web en un navegador in-app está disponible a partir del SDK de Adapty v. 3.15. ::: Por defecto, los paywalls web se abren en el navegador externo. Para ofrecer una experiencia de usuario fluida, puedes abrir los paywalls web en un navegador in-app. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin cambiar de app. Para habilitarlo, establece `openIn` como `WebPresentation.BrowserInApp` en `openWebPaywall`: ```typescript showLineNumbers try { await adapty.openWebPaywall({ paywallOrProduct: product, openIn: WebPresentation.BrowserInApp, // default – WebPresentation.BrowserOutApp }); } catch (error) { console.error('Failed to open web paywall:', error); } ``` --- # File: capacitor-implement-paywalls-manually --- --- title: "Implement paywalls manually" description: "Learn how to implement paywalls manually in your Capacitor app with Adapty SDK." --- ## Accept purchases If you are working with paywalls you've implemented yourself, you can delegate handling purchases to Adapty, using the `makePurchase` method. This way, we will handle all the user scenarios, and you will only need to handle the purchase results. :::important `makePurchase` works with products created in the Adapty dashboard. Make sure you configure products and ways to retrieve them in the dashboard by following the [quickstart guide](quickstart). ::: <CustomDocCardList ids={['capacitor-quickstart-manual', 'fetch-paywalls-and-products-capacitor', 'present-remote-config-paywalls-capacitor', 'capacitor-making-purchases', 'capacitor-restore-purchase']} /> ## Observer mode If you want to implement your own purchase handling logic from scratch but still want to benefit from the advanced analytics in Adapty, you can use the observer mode. :::important Consider the observer mode limitations [here](observer-vs-full-mode). ::: <CustomDocCardList ids={['implement-observer-mode-capacitor', 'report-transactions-observer-mode-capacitor']} /> --- # File: capacitor-quickstart-manual --- --- title: "Habilitar compras en tu paywall personalizado en Capacitor SDK" description: "Integra el SDK de Adapty en tus paywalls personalizados de Capacitor para habilitar compras in-app." --- Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall, mientras el SDK de Adapty obtiene productos, gestiona nuevas compras y restaura las anteriores. :::important **Esta guía es para desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Adapty Paywall Builder](capacitor-quickstart-paywalls). Con Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compras automáticamente y puedes probar diferentes diseños sin volver a publicar tu app. ::: ## Antes de empezar \{#before-you-start\} ### Configura los productos \{#set-up-products\} Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras paywalls para placements en el dashboard y luego los solicitas por ID de placement en tu código. Esto facilita ejecutar pruebas A/B y mostrar diferentes paywalls a diferentes usuarios. Asegúrate de entender estos conceptos aunque trabajes con tu paywall personalizado. Básicamente, son solo tu forma de gestionar los productos que vendes en tu app. Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite recuperar tus productos. Para entender qué debes hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart). ### Gestiona usuarios \{#manage-users\} Puedes trabajar con o sin autenticación de backend en tu lado. Sin embargo, el SDK de Adapty gestiona los usuarios anónimos e identificados de forma diferente. Lee la [guía de inicio rápido de identificación](capacitor-quickstart-identify) para entender los detalles y asegurarte de que trabajas con los usuarios correctamente. ## Paso 1. Obtener productos \{#step-1-get-products\} Para recuperar productos para tu paywall personalizado, necesitas: 1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`. 2. Obtener el array de productos para este paywall usando el método `getPaywallProducts`. ```typescript showLineNumbers async function loadPaywall() { try { const paywall: AdaptyPaywall = await adapty.getPaywall({ placementId: 'YOUR_PLACEMENT_ID' }); const products: AdaptyPaywallProduct[] = await adapty.getPaywallProducts({ paywall }); // Use products to build your custom paywall UI } catch (error) { // Handle the error } } ``` ## Paso 2. Aceptar compras \{#step-2-accept-purchases\} Cuando un usuario pulsa sobre un producto en tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado. ```typescript showLineNumbers async function purchaseProduct(product: AdaptyPaywallProduct) { try { const result: AdaptyPurchaseResult = await adapty.makePurchase({ product }); if (result.type === 'success') { // Purchase successful, profile updated } else if (result.type === 'user_cancelled') { // User canceled the purchase } else if (result.type === 'pending') { // Purchase is pending (e.g., user will pay offline with cash) } } catch (error) { // Handle the error } } ``` ## Paso 3. Restaurar compras \{#step-3-restore-purchases\} Los stores de aplicaciones requieren que todas las apps con suscripciones ofrezcan una forma de que los usuarios puedan restaurar sus compras. Llama al método `restorePurchases` cuando el usuario pulse el botón de restaurar. Esto sincronizará su historial de compras con Adapty y devolverá el perfil actualizado. ```typescript showLineNumbers async function restorePurchases() { try { const profile: AdaptyProfile = await adapty.restorePurchases(); // Restore successful, profile updated } catch (error) { // Handle the error } } ``` ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Tu paywall está listo para mostrarse en la app. Prueba tus compras en el [sandbox de 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. Para ver cómo funciona esto en una implementación lista para producción, consulta el [App.tsx](https://github.com/adaptyteam/AdaptySDK-Capacitor/blob/master/examples/adapty-devtools/src/screens/app/App.tsx) en nuestra app de ejemplo, que muestra el manejo de compras con gestión de errores adecuada, estados de carga e integración completa del SDK. A continuación, [comprueba si los usuarios han completado su compra](capacitor-check-subscription-status) para determinar si mostrar el paywall o conceder acceso a las funciones de pago. --- # File: fetch-paywalls-and-products-capacitor --- --- title: "Obtener paywalls y productos para paywalls con Remote Config en Capacitor SDK" description: "Obtén paywalls y productos en Adapty Capacitor SDK para mejorar la monetización de los usuarios." --- Antes de mostrar paywalls con Remote Config o paywalls personalizados, necesitas obtener la información sobre ellos. Ten en cuenta que este tema se refiere a paywalls con Remote Config y paywalls personalizados. Para obtener información sobre cómo obtener paywalls creados con Paywall Builder, consulta [Obtener paywalls del Paywall Builder y su configuración](capacitor-get-pb-paywalls). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a obtener paywalls y productos en tu app (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en el placement](create-placement) en el Adapty Dashboard. 4. [Instala el SDK de Adapty](sdk-installation-capacitor) en tu app. </details> ## Obtener información del paywall \{#fetch-paywall-information\} En Adapty, un [producto](product) es una combinación de productos tanto de App Store como de Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos en placements específicos de tu app. Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`. ```typescript showLineNumbers try { const paywall = await adapty.getPaywall({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en', params: { fetchPolicy: 'reload_revalidating_cache_data', // Load from server, fallback to cache loadTimeoutMs: 5000 // 5 second timeout } }); // the requested paywall } catch (error) { console.error('Failed to fetch paywall:', error); } ``` | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](capacitor-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **params.fetchPolicy** | <p>opcional</p><p>por defecto: `'reload_revalidating_cache_data'`</p> | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `'return_cache_data_else_load'` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché se mantiene al reiniciar la app y solo se borra al desinstalarla o mediante limpieza manual.</p> | | **params.loadTimeoutMs** | <p>opcional</p><p>por defecto: 5000 ms</p> | <p>Este valor limita el tiempo de espera (en milisegundos) para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local.</p><p></p><p>Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeoutMs`, ya que la operación puede consistir en diferentes solicitudes internamente.</p> | **No escribas los IDs de productos directamente en el código.** El único ID que debes incluir en el código es el del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe gestionar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana tres, muéstralos todos sin modificar el código. Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://capacitor.adapty.io/interfaces/adaptypaywall) con: una lista de IDs de productos, el identificador del paywall, Remote Config y otras propiedades. | ## Obtener productos \{#fetch-products\} Una vez que tienes el paywall, puedes consultar el array de productos que le corresponde: ```typescript showLineNumbers try { const products = await adapty.getPaywallProducts({ paywall }); // the requested products list } catch (error) { console.error('Failed to fetch products:', error); } ``` Parámetros de respuesta: | Parámetro | Descripción | | :-------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | Lista de objetos [`AdaptyPaywallProduct`](https://capacitor.adapty.io/interfaces/adaptypaywallproduct) con: identificador del producto, nombre, precio, moneda, duración de la suscripción y otras propiedades. | Al implementar tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://capacitor.adapty.io/interfaces/adaptypaywallproduct). A continuación se muestran las propiedades más utilizadas, pero consulta el documento enlazado para ver todos los detalles disponibles. | Propiedad | Descripción | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Título** | Para mostrar el título del producto, usa `product.localizedTitle`. Ten en cuenta que la localización se basa en el país de la store seleccionado por el usuario, no en el idioma del dispositivo. | | **Precio** | Para mostrar el precio localizado, usa `product.price?.localizedString`. Esta localización se basa en la configuración regional del dispositivo. También puedes acceder al precio como número con `product.price?.amount`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda correspondiente, usa `product.price?.currencySymbol`. | | **Período de suscripción** | Para mostrar el período (p. ej., semana, mes, año, etc.), usa `product.subscription?.localizedSubscriptionPeriod`. Esta localización se basa en el idioma del dispositivo. Para obtener el período de suscripción de forma programática, usa `product.subscription?.subscriptionPeriod`. Desde ahí puedes acceder a la propiedad `unit` para obtener la duración (es decir, `'day'`, `'week'`, `'month'`, `'year'` o `'unknown'`). El valor `numberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral verías `'month'` en la propiedad unit y `3` en numberOfUnits. | | **Oferta introductoria** | Para mostrar una insignia u otro indicador de que una suscripción incluye una oferta introductoria, revisa la propiedad `product.subscription?.offer?.phases`. Esta es una lista que puede contener hasta dos fases de descuento: la fase de prueba gratuita y la fase de precio introductorio. Dentro de cada objeto de fase están las siguientes propiedades útiles:<br/>• `paymentMode`: una cadena con los valores `'free_trial'`, `'pay_as_you_go'`, `'pay_up_front'` y `'unknown'`. Las pruebas gratuitas son del tipo `'free_trial'`.<br/>• `price`: El precio con descuento como número. Para pruebas gratuitas, busca `0` aquí.<br/>• `localizedNumberOfPeriods`: una cadena localizada según el idioma del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `'3 days'` en este campo.<br/>• `subscriptionPeriod`: Alternativamente, puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que lo descrito en la sección anterior.<br/>• `localizedSubscriptionPeriod`: Un período de suscripción formateado del descuento para el idioma del usuario. | ## Acelera la obtención de paywalls con el paywall de audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\} Normalmente, los paywalls se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, si tienes muchas audiencias y paywalls y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseado. En esas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia fluida en lugar de no mostrar ninguno. Para resolver esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es importante entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener información del paywall](fetch-paywalls-and-products-capacitor#fetch-paywall-information) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls diferentes para distintas versiones de la app (actual y futuras), puede que tengas dificultades. Tendrás que diseñar paywalls que soporten la versión actual (legacy) o aceptar que los usuarios con la versión actual puedan encontrar problemas con paywalls que no se renderizan. - **Pérdida de segmentación**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluyendo la basada en países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención más rápida de paywalls, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, usa `getPaywall` como se describe [más arriba](fetch-paywalls-and-products-capacitor#fetch-paywall-information). ::: ```typescript showLineNumbers try { const paywall = await adapty.getPaywallForDefaultAudience({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en', params: { fetchPolicy: 'reload_revalidating_cache_data' // Load from server, fallback to cache } }); // the requested paywall } catch (error) { console.error('Failed to fetch default audience paywall:', error); } ``` :::note El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.2 del Capacitor SDK. ::: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](capacitor-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **params.fetchPolicy** | <p>opcional</p><p>por defecto: `'reload_revalidating_cache_data'`</p> | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `'return_cache_data_else_load'` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché se mantiene al reiniciar la app y solo se borra al desinstalarla o mediante limpieza manual.</p> | --- # File: present-remote-config-paywalls-capacitor --- --- title: "Renderizar un paywall diseñado con Remote Config en Capacitor SDK" description: "Descubre cómo presentar paywalls de Remote Config en el SDK de Adapty para Capacitor y personalizar la experiencia del usuario." --- Si has personalizado un paywall usando Remote Config, tendrás que implementar el renderizado en el código de tu app para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú decides qué se incluye y cómo se ve tu paywall. Proporcionamos un método para obtener la configuración remota y así puedas mostrar tu paywall personalizado configurado mediante Remote Config. ## Obtener el Remote Config del paywall y presentarlo \{#get-paywall-remote-config-and-present-it\} Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores que necesites. ```typescript showLineNumbers try { const paywall = await adapty.getPaywall({ placementId: 'YOUR_PLACEMENT_ID', params: { fetchPolicy: 'reload_revalidating_cache_data', // Load from server, fallback to cache loadTimeoutMs: 5000 // 5 second timeout } }); const headerText = paywall.remoteConfig?.data?.['header_text']; } catch (error) { console.error('Failed to fetch paywall:', error); } ``` En este punto, una vez que hayas recibido todos los valores necesarios, es momento de renderizarlos y componerlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a distintas pantallas y orientaciones de dispositivos móviles, ofreciendo una experiencia fluida y agradable en todos ellos. :::warning Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls-capacitor#track-paywall-view-events) tal como se describe a continuación, para que los análisis de Adapty puedan capturar información para los embudos y las pruebas A/B. ::: Cuando termines de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.makePurchase()` con el producto de tu paywall. Para más detalles sobre el método `.makePurchase()`, consulta [Realizar compras](capacitor-making-purchases). Te recomendamos [crear un paywall de respaldo](capacitor-use-fallback-paywalls). Este paywall de respaldo se mostrará al usuario cuando no haya conexión a internet ni caché disponible, garantizando una experiencia fluida incluso en esas situaciones. ## Registrar eventos de visualización del paywall \{#track-paywall-view-events\} Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque los datos de compras se recopilan automáticamente, registrar las visualizaciones del paywall requiere tu intervención, ya que solo tú sabes cuándo un usuario ve un paywall. Para registrar un evento de visualización del paywall, simplemente llama a `.logShowPaywall(paywall)` y esto quedará reflejado en las métricas de tu paywall en los embudos y las pruebas A/B. :::important No es necesario llamar a `.logShowPaywall(paywall)` si estás mostrando paywalls creados en el [Paywall Builder](adapty-paywall-builder). ::: ```typescript showLineNumbers try { await adapty.logShowPaywall({ paywall }); } catch (error) { console.error('Failed to log paywall view:', error); } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :-------- | :--------------------------------------------------------- | | **paywall** | requerido | Un objeto [`AdaptyPaywall`](https://capacitor.adapty.io/interfaces/adaptypaywall). | --- # File: capacitor-making-purchases --- --- title: "Realizar compras en una app móvil con Capacitor SDK" description: "Guía para gestionar compras in-app y suscripciones con Adapty." --- Mostrar paywalls en tu app móvil es un paso fundamental para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, simplemente mostrarlos basta para gestionar compras solo si usas el [Paywall Builder](adapty-paywall-builder) para personalizar tus paywalls. Si no usas el Paywall Builder, debes utilizar un método separado llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método es la puerta de entrada para que los usuarios interactúen con los paywalls y procedan con sus transacciones. Si tu paywall tiene una oferta promocional activa para el producto que el usuario intenta comprar, Adapty la aplicará automáticamente en el momento de la compra. Asegúrate de haber completado la [configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras. ## Realizar una compra \{#make-purchase\} :::note **¿Usas el [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente; puedes saltarte este paso. **¿Buscas instrucciones paso a paso?** Consulta la [guía de inicio rápido](capacitor-implement-paywalls-manually) para obtener instrucciones de implementación completas con todo el contexto. ::: ```typescript showLineNumbers try { const result = await adapty.makePurchase({ product }); if (result.type === 'success') { const isSubscribed = result.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive; if (isSubscribed) { // Grant access to the paid features console.log('User is now subscribed!'); } } else if (result.type === 'user_cancelled') { console.log('Purchase cancelled by user'); } else if (result.type === 'pending') { console.log('Purchase is pending'); } } catch (error) { console.error('Purchase failed:', error); } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :--------- |:--------------------------------------------------------------------------------------------------------------------------------------| | **product** | obligatorio | Un objeto [`AdaptyPaywallProduct`](https://capacitor.adapty.io/interfaces/adaptypaywallproduct) obtenido del paywall. | Parámetros de la respuesta: | Parámetro | Descripción | |------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **result** | Un objeto [`AdaptyPurchaseResult`](https://capacitor.adapty.io/types/adaptypurchaseresult) con un campo `type` que indica el resultado de la compra (`'success'`, `'user_cancelled'` o `'pending'`) y un campo `profile` que contiene el [`AdaptyProfile`](https://capacitor.adapty.io/interfaces/adaptyprofile) actualizado cuando la compra es exitosa. | ## Cambiar la suscripción al realizar una compra \{#change-subscription-when-making-a-purchase\} Cuando un usuario opta por una nueva suscripción en lugar de renovar la actual, el comportamiento depende del store: - En el App Store, la suscripción se actualiza automáticamente dentro del grupo de suscripciones. Si un usuario compra una suscripción de un grupo mientras ya tiene una activa de otro, ambas estarán activas al mismo tiempo. - En Google Play, la suscripción no se actualiza automáticamente. Deberás gestionar el cambio en el código de tu app tal como se describe a continuación. Para reemplazar una suscripción por otra en Android, llama al método `.makePurchase()` con el parámetro adicional: ```typescript showLineNumbers try { const result = await adapty.makePurchase({ product, params: { android: { subscriptionUpdateParams: { oldSubVendorProductId: 'old_product_id', prorationMode: 'charge_prorated_price' }, isOfferPersonalized: true } } }); if (result.type === 'success') { const isSubscribed = result.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive; if (isSubscribed) { // Grant access to the paid features console.log('Subscription updated successfully!'); } } else if (result.type === 'user_cancelled') { console.log('Purchase cancelled by user'); } else if (result.type === 'pending') { console.log('Purchase is pending'); } } catch (error) { console.error('Purchase failed:', error); } ``` Parámetro de solicitud adicional: | Parámetro | Presencia | Descripción | | :--------- | :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **params** | opcional | Un objeto de tipo [`MakePurchaseParamsInput`](https://capacitor.adapty.io/types/makepurchaseparamsinput) que contiene parámetros de compra específicos de cada plataforma. | La estructura de `MakePurchaseParamsInput` es: ```typescript { android: { subscriptionUpdateParams: { oldSubVendorProductId: 'old_product_id', prorationMode: 'charge_prorated_price' }, isOfferPersonalized: true } } ``` Puedes obtener más información sobre suscripciones y modos de reemplazo en la documentación para desarrolladores de Google: - [Acerca de los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-modes) - [Recomendaciones de Google sobre modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-recommendations) - Modo de reemplazo [`CHARGE_PRORATED_PRICE`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#CHARGE_PRORATED_PRICE()). Nota: este método solo está disponible para actualizaciones de suscripción. No se admiten las degradaciones. - Modo de reemplazo [`DEFERRED`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#DEFERRED()). Nota: el cambio real de suscripción solo ocurre cuando finaliza el período de facturación actual. ### Gestionar planes prepagos (Android) \{#manage-prepaid-plans-android\} Si los usuarios de tu app pueden adquirir [planes prepagos](https://developer.android.com/google/play/billing/subscriptions#prepaid-plans) (por ejemplo, comprar una suscripción no renovable por varios meses), puedes habilitar las [transacciones pendientes](https://developer.android.com/google/play/billing/subscriptions#pending) para dichos planes. ```typescript showLineNumbers await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { android: { enablePendingPrepaidPlans: true, }, } }); ``` ## Canjear códigos de oferta en iOS \{#redeem-offer-codes-in-ios\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Details> <summary>Sobre los códigos de oferta</summary> Los códigos de oferta te permiten dar descuentos o períodos de prueba gratuitos a usuarios concretos. A diferencia de las ofertas habituales, que se aplican de forma automática, los códigos de oferta se distribuyen fuera de la app — por email, redes sociales o materiales impresos. Los usuarios los canjean introduciendo el código en el App Store, accediendo a una URL de canje o a través de un diálogo dentro de la app. Para configurar códigos de oferta, abre una suscripción en App Store Connect y ve a su sección **Offer Codes**. Puedes crear [tres tipos](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes) de códigos de oferta: - **Free** — la suscripción es gratuita durante un período determinado y la siguiente renovación se cobra al precio completo. - **Pay as you go** — el usuario paga un precio reducido en cada ciclo de facturación durante un período determinado y, después, la suscripción se renueva al precio completo. - **Pay up front** — el usuario paga un precio único reducido por toda la duración de la oferta y, después, la suscripción se renueva al precio completo. No es necesario añadir los códigos de oferta a Adapty. Apple etiqueta cada transacción durante el período de la oferta con la categoría del código de oferta. Esto incluye el canje inicial y todas las renovaciones con descuento posteriores. Adapty detecta la etiqueta y registra cada transacción con la categoría de oferta `offer_code`. Cuando termina el período de oferta y la suscripción se renueva al precio completo, la etiqueta deja de estar presente. Puedes filtrar los análisis por el tipo de oferta **Offer Code** en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds). #### Resolución de discrepancias en los ingresos \{#revenue-discrepancy-troubleshooting\} Si observas que una transacción con código de oferta aparece en Adapty al precio completo del producto en lugar del precio reducido, verifica lo siguiente en App Store Connect: - El código de oferta tiene los precios correctos configurados para todas las regiones donde los usuarios pueden canjearlo. - El precio de la oferta está configurado para el país o región específica del usuario. Apple envía el precio regional en la transacción. Si no hay ningún precio regional configurado para la oferta, Apple puede enviar el precio completo del producto. Puedes filtrar y verificar las transacciones con código de oferta en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds) mediante los filtros de tipo de oferta **Offer Code** y **Offer Discount Type**. #### Códigos promocionales heredados (obsoletos) \{#legacy-promo-codes-deprecated\} <Callout type="warning"> Apple dejó obsoletos los códigos promocionales para compras in-app en marzo de 2026. Los códigos de oferta los sustituyen con más funcionalidades: elegibilidad configurable, fechas de expiración y hasta 1 millón de códigos por trimestre. Si antes usabas códigos promocionales para compras in-app, migra a los códigos de oferta en App Store Connect. </Callout> Los códigos promocionales heredados (limitados a 100 por app y versión) daban acceso gratuito a una suscripción. A diferencia de los códigos de oferta, Apple no incluía información de descuento en las transacciones con código promocional — enviaba el precio completo del producto en el recibo. Por ello, Adapty registraba estas transacciones al precio completo, lo que generaba discrepancias entre los análisis de Adapty y App Store Connect. Si ves transacciones históricas al precio completo que deberían haber sido gratuitas, es probable que provengan de códigos promocionales heredados. Como estos códigos ya están obsoletos, migra a los códigos de oferta para un seguimiento preciso de los ingresos. </Details> Para mostrar la pantalla de canje de códigos en tu app: ```typescript showLineNumbers try { await adapty.presentCodeRedemptionSheet(); } catch (error) { console.error('Failed to present code redemption sheet:', error); } ``` :::danger Según nuestras observaciones, la pantalla de canje de códigos de oferta puede no funcionar de forma fiable en algunas apps. Recomendamos redirigir al usuario directamente al App Store. Para hacerlo, debes abrir una URL con el siguiente formato: `https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}` ::: --- # File: capacitor-restore-purchase --- --- title: "Restaurar compras en app móvil con el SDK de Capacitor" description: "Aprende a restaurar compras en Adapty para garantizar una experiencia de usuario fluida." --- Restaurar compras tanto en iOS como en Android es una función que permite a los usuarios recuperar el acceso a contenido comprado anteriormente, como suscripciones o compras in-app, sin que se les cobre de nuevo. Esta función es especialmente útil para usuarios que hayan desinstalado y reinstalado la app, o que hayan cambiado a un dispositivo nuevo y quieran acceder a su contenido comprado anteriormente sin volver a pagar. :::note En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin necesidad de código adicional por tu parte. Si es tu caso, puedes omitir este paso. ::: Para restaurar una compra si no usas el [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`: ```typescript showLineNumbers try { const profile = await adapty.restorePurchases(); const isSubscribed = profile.accessLevels['YOUR_ACCESS_LEVEL']?.isActive; if (isSubscribed) { // Restore access to paid features console.log('Access restored successfully!'); } else { console.log('No active subscriptions found'); } } catch (error) { console.error('Failed to restore purchases:', error); } ``` Parámetros de respuesta: | Parámetro | Descripción | |-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **profile** | Un objeto [`AdaptyProfile`](https://capacitor.adapty.io/interfaces/adaptyprofile). Este modelo contiene información sobre los niveles de acceso, suscripciones y compras únicas. Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app. | --- # File: implement-observer-mode-capacitor --- --- title: "Implementar el modo Observer en el SDK de Capacitor" description: "Implementa el modo observer en Adapty para rastrear eventos de suscripción de usuarios en el SDK de Capacitor." --- Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analíticas. Si esto se ajusta a tus necesidades, solo tienes que: 1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. Sigue las instrucciones de configuración para [Capacitor](sdk-installation-capacitor#activate-adapty-module-of-adapty-sdk). 2. [Reportar transacciones](report-transactions-observer-mode-capacitor) desde tu infraestructura de compras existente a Adapty. ### Configuración del modo Observer \{#observer-mode-setup\} Activa el modo Observer si gestionas las compras y el estado de las suscripciones tú mismo y usas Adapty solo para enviar eventos de suscripción y analíticas. :::important Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlo tú mismo. ::: ```typescript showLineNumbers try { await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { observerMode: true // Enable observer mode } }); } catch (error) { console.error('Failed to activate Adapty:', error); } ``` Parámetros: | Parámetro | Descripción | | --------------------------- | ------------------------------------------------------------ | | **observerMode** | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor predeterminado es `false`. | ## Usar paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\} Si también quieres usar los paywalls y las funciones de pruebas A/B de Adapty, puedes hacerlo, pero requiere una configuración adicional en el modo Observer. Esto es lo que necesitarás hacer además de los pasos anteriores: 1. Muestra los paywalls de la forma habitual para [paywalls con Remote Config](present-remote-config-paywalls-capacitor). 2. [Asocia los paywalls](report-transactions-observer-mode-capacitor) con las transacciones de compra. --- # File: report-transactions-observer-mode-capacitor --- --- title: "Reportar transacciones en Observer Mode en el SDK de Capacitor" description: "Reporta transacciones de compra en el Observer Mode de Adapty para obtener información sobre usuarios y realizar seguimiento de ingresos en el SDK de Capacitor." --- En Observer Mode, el SDK de Adapty no puede rastrear de forma autónoma las compras realizadas a través de tu sistema de compras existente. Necesitas reportar las transacciones desde tu store. Es fundamental configurar esto **antes** de lanzar tu app para evitar errores en los análisis. Usa `reportTransaction` para reportar explícitamente cada transacción y que Adapty pueda reconocerla. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra con el paywall que la originó, garantizando un análisis preciso del paywall. ```typescript showLineNumbers const variationId = paywall.variationId; try { await adapty.reportTransaction({ transactionId: 'your_transaction_id', variationId: variationId }); } catch (error) { console.error('Failed to report transaction:', error); } ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | **transactionId** | obligatorio | <ul><li> Para iOS: Identificador de la transacción.</li><li> Para Android: Identificador de cadena (`purchase.getOrderId`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.</li></ul> | | **variationId** | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://capacitor.adapty.io/interfaces/adaptypaywall). | --- # File: capacitor-user --- --- title: "Users & access" description: "Learn how to work with users and access levels in your Capacitor app with Adapty SDK." --- <CustomDocCardList /> --- # File: capacitor-identifying-users --- --- title: "Identificar usuarios en el SDK de Capacitor" description: "Aprende cómo identificar usuarios en tu app de Capacitor con el SDK de Adapty." --- Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, deberías establecer tu propio Customer User ID. Puedes encontrar usuarios por su Customer User ID en la sección de [Perfiles](profiles-crm) y utilizarlo en la [API del lado del servidor](getting-started-with-server-side-api), que se enviará a todas las integraciones. ### Establecer el ID de usuario del cliente en la configuración \{#setting-customer-user-id-on-configuration\} Si tienes un ID de usuario durante la configuración, pásalo como parámetro `customerUserId` al método `.activate()`: ```typescript showLineNumbers try { await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { customerUserId: 'YOUR_USER_ID' } }); } catch (error) { console.error('Failed to activate Adapty:', error); } ``` ### Establecer el ID de usuario tras la configuración \{#setting-customer-user-id-after-configuration\} Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo más adelante en cualquier momento con el método `.identify()`. Los casos más habituales para usar este método son tras el registro o la autenticación, cuando el usuario pasa de ser anónimo a estar autenticado. ```typescript showLineNumbers try { await adapty.identify({ customerUserId: 'YOUR_USER_ID' }); console.log('User identified successfully'); } catch (error) { console.error('Failed to identify user:', error); } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **customerUserId** | obligatorio | Identificador de usuario en formato string. | :::warning Reenvío de datos significativos del usuario En algunos casos, como cuando un usuario vuelve a iniciar sesión en su cuenta, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si enviaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, deberás reenviar esos datos para el usuario identificado. Es importante destacar que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes. ::: ### Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\} Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`: ```typescript showLineNumbers try { await adapty.logout(); console.log('User logged out successfully'); } catch (error) { console.error('Failed to logout user:', error); } ``` Luego puedes iniciar sesión con el usuario usando el método `.identify()`. ## Asignar `appAccountToken` (iOS) \{#assign-appaccounttoken-ios\} [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un **UUID** que te permite vincular las transacciones del App Store con la identidad interna de tus usuarios. StoreKit asocia este token a cada transacción, de modo que tu backend puede relacionar los datos del App Store con tus usuarios. Usa un UUID estable generado por usuario y reutilízalo para la misma cuenta en todos los dispositivos. Así te aseguras de que las compras y las notificaciones del App Store queden correctamente vinculadas. Puedes establecer el token de dos maneras: durante la activación del SDK o al identificar al usuario. :::important Siempre debes pasar `appAccountToken` junto con `customerUserId`. Si solo pasas el token, no se incluirá en la transacción. ::: ```typescript showLineNumbers // Durante la configuración: await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { customerUserId: 'YOUR_USER_ID', ios: { appAccountToken: "YOUR_APP_ACCOUNT_TOKEN" }, } }); // O al identificar usuarios await adapty.identify({ customerUserId: 'YOUR_USER_ID', params: { ios: { appAccountToken: 'YOUR_APP_ACCOUNT_TOKEN' }, } }); ``` ### Establecer IDs de cuenta ofuscados (Android) \{#set-obfuscated-account-ids-android\} Google Play requiere IDs de cuenta ofuscados en ciertos casos de uso para mejorar la privacidad y seguridad de los usuarios. Estos IDs ayudan a Google Play a identificar las compras manteniendo el anonimato de la información del usuario, lo que resulta especialmente importante para la prevención del fraude y el análisis. Es posible que necesites configurar estos IDs si tu aplicación maneja datos sensibles de los usuarios o si debes cumplir con normativas de privacidad específicas. Los IDs ofuscados permiten a Google Play rastrear las compras sin exponer los identificadores reales de los usuarios. ```typescript showLineNumbers // Durante la configuración: await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { android: { obfuscatedAccountId: 'YOUR_OBFUSCATED_ACCOUNT_ID' }, } }); // O al identificar usuarios await adapty.identify({ customerUserId: 'YOUR_USER_ID', params: { android: { obfuscatedAccountId: 'YOUR_OBFUSCATED_ACCOUNT_ID' }, } }); ``` ## Detectar usuarios en varios dispositivos \{#detect-users-across-devices\} --- no_index: true --- Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`. Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID. **Lo que Adapty puede detectar entre dispositivos** | Tu configuración | Lo que Adapty detecta | Lo que debes hacer | | --- | --- | --- | | Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. | | Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. | | Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. | | Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. | **Restaurar compras en un dispositivo nuevo** Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK. No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado. --- # File: capacitor-setting-user-attributes --- --- title: "Establecer atributos de usuario en el SDK de Capacitor" description: "Aprende cómo actualizar los atributos de usuario y los datos del perfil en tu app de Capacitor con el SDK de Adapty." --- Puedes establecer atributos opcionales como email, número de teléfono, etc., para el usuario de tu app. Luego puedes usar estos atributos para crear [segmentos](segments) de usuarios o simplemente verlos en el CRM. ### Establecer atributos de usuario \{#setting-user-attributes\} Para establecer atributos de usuario, llama al método `.updateProfile()`: ```typescript showLineNumbers const params = { email: 'email@email.com', phoneNumber: '+18888888888', firstName: 'John', lastName: 'Appleseed', gender: 'other', birthday: new Date().toISOString(), }; try { await adapty.updateProfile(params); console.log('Profile updated successfully'); } catch (error) { console.error('Failed to update profile:', error); } ``` Ten en cuenta que los atributos que hayas establecido previamente con el método `updateProfile` no se restablecerán. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Lista de claves permitidas \{#the-allowed-keys-list\} A continuación se muestran las claves permitidas de `AdaptyProfileParameters` y sus valores: | Clave | Valor | |---|-----| | **email** | String | | **phoneNumber** | String | | **firstName** | String | | **lastName** | String | | **gender** | Enum, los valores permitidos son: `'female'`, `'male'`, `'other'` | | **birthday** | Cadena de fecha en formato ISO | ### Atributos de usuario personalizados \{#custom-user-attributes\} Puedes definir tus propios atributos personalizados. Generalmente están relacionados con el uso de tu app. Por ejemplo, en aplicaciones de fitness podrían ser el número de ejercicios por semana; en apps de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes usarlos en segmentos para crear paywalls y ofertas personalizadas, y también en análisis para identificar qué métricas de producto influyen más en los ingresos. ```typescript showLineNumbers try { await adapty.updateProfile({ codableCustomAttributes: { key_1: 'value_1', key_2: 2, }, }); console.log('Custom attributes updated successfully'); } catch (error) { console.error('Failed to update custom attributes:', error); } ``` Para eliminar claves existentes, pasa `null` como sus valores: ```typescript showLineNumbers try { // to remove keys, pass null as their values await adapty.updateProfile({ codableCustomAttributes: { key_1: null, key_2: null, }, }); console.log('Custom attributes removed successfully'); } catch (error) { console.error('Failed to remove custom attributes:', error); } ``` En ocasiones necesitas saber qué atributos personalizados ya están configurados. Para ello, utiliza el campo `customAttributes` del objeto `AdaptyProfile`. :::warning Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor podrían haber cambiado desde la última sincronización. ::: ### Límites \{#limits\} - Hasta 30 atributos personalizados por usuario - Los nombres de clave tienen un máximo de 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.` - El valor puede ser una cadena o un número flotante con un máximo de 50 caracteres. --- # File: capacitor-listen-subscription-changes --- --- title: "Verificar el estado de suscripción en el SDK de Capacitor" description: "Rastrea y gestiona el estado de suscripción de usuarios en Adapty para mejorar la retención de clientes en tu app de Capacitor." --- Con Adapty, controlar el estado de la suscripción es muy sencillo. No tienes que insertar manualmente los ID de producto en tu código. En cambio, puedes confirmar fácilmente el estado de suscripción de un usuario comprobando si tiene un [nivel de acceso](access-level) activo. <details> <summary>Antes de empezar a comprobar el estado de suscripción (haz clic para expandir)</summary> - Para iOS, configura las [notificaciones del servidor de App Store](enable-app-store-server-notifications) - Para Android, configura las [notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn) </details> ## El nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptyprofile-object\} Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://capacitor.adapty.io/interfaces/adaptyprofile). Te recomendamos recuperar el perfil cuando arranca la app, por ejemplo al [identificar un usuario](capacitor-identifying-users#setting-customer-user-id-on-configuration), y actualizarlo cada vez que haya cambios. Así podrás usar el objeto del perfil sin tener que solicitarlo repetidamente. Para recibir notificaciones de las actualizaciones del perfil, escucha los cambios en el perfil tal como se describe en la sección [Escuchar actualizaciones del perfil, incluidos los niveles de acceso](capacitor-listen-subscription-changes) más abajo. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\} Para obtener el nivel de acceso desde el servidor, usa el método `.getProfile()`: ```typescript showLineNumbers try { const profile = await adapty.getProfile(); console.log('Profile retrieved successfully'); } catch (error) { console.error('Failed to get profile:', error); } ``` Parámetros de respuesta: | Parámetro | Descripción | | --------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **profile** | Un objeto [AdaptyProfile](https://capacitor.adapty.io/interfaces/adaptyprofile). En general, solo necesitas comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app. El método `.getProfile` devuelve el resultado más actualizado, ya que siempre intenta consultar la API. Si por alguna razón (por ejemplo, sin conexión a internet) el SDK de Adapty no puede obtener información del servidor, se devolverán los datos de la caché. También es importante tener en cuenta que el SDK de Adapty actualiza la caché de `AdaptyProfile` de forma regular para mantener esta información lo más actualizada posible. | El método `.getProfile()` te proporciona el perfil de usuario desde el que puedes obtener el estado del nivel de acceso. Puedes tener múltiples niveles de acceso por app. Por ejemplo, si tienes una app de noticias y vendes suscripciones a diferentes temáticas de forma independiente, puedes crear los niveles de acceso "sports" y "science". Sin embargo, la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes usar simplemente el nivel de acceso "premium" por defecto. A continuación se muestra un ejemplo para comprobar el nivel de acceso "premium" por defecto: ```typescript showLineNumbers try { const profile = await adapty.getProfile(); const isActive = profile.accessLevels['premium']?.isActive; if (isActive) { // Grant access to premium features console.log('User has premium access'); } else { console.log('User does not have premium access'); } } catch (error) { console.error('Failed to check subscription status:', error); } ``` ### Escuchar actualizaciones del estado de suscripción \{#listening-for-subscription-status-updates\} Cada vez que cambia la suscripción del usuario, Adapty lanza un evento. Para recibir mensajes de Adapty, necesitas realizar alguna configuración adicional: ```typescript showLineNumbers // Create an "onLatestProfileLoad" event listener adapty.addListener('onLatestProfileLoad', (data) => { const profile = data.profile; const isActive = profile.accessLevels['premium']?.isActive; if (isActive) { console.log('Subscription status updated: User has premium access'); } else { console.log('Subscription status updated: User does not have premium access'); } }); ``` Adapty también lanza un evento al iniciar la aplicación. En ese caso, se pasará el estado de suscripción almacenado en caché. ### Caché del estado de suscripción \{#subscription-status-cache\} La caché implementada en el SDK de Adapty almacena el estado de suscripción del perfil. Esto significa que, aunque el servidor no esté disponible, se puede acceder a los datos en caché para obtener información sobre el estado de suscripción del perfil. Sin embargo, es importante tener en cuenta que no es posible realizar solicitudes de datos directamente desde la caché. El SDK consulta el servidor periódicamente cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si hay modificaciones, como nuevas transacciones u otras actualizaciones, se enviarán a los datos en caché para mantenerlos sincronizados con el servidor. --- # File: capacitor-deal-with-att --- --- title: "Gestionar ATT en el SDK de Capacitor" description: "Empieza a usar Adapty en Capacitor para simplificar la configuración y gestión de suscripciones." --- Si tu aplicación usa el framework AppTrackingTransparency y muestra al usuario una solicitud de autorización para el seguimiento de la app, debes enviar el [estado de autorización](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/) a Adapty. ```typescript showLineNumbers try { await adapty.updateProfile({ appTrackingTransparencyStatus: AppTrackingTransparencyStatus.Authorized, }); console.log('ATT status updated successfully'); } catch (error) { console.error('Failed to update ATT status:', error); } ``` :::warning Te recomendamos encarecidamente que envíes este valor lo antes posible cuando cambie; solo así los datos se enviarán a tiempo a las integraciones que hayas configurado. ::: --- # File: capacitor-onboardings --- --- title: "Onboardings" description: "Learn how to work with onboardings in your Capacitor app with Adapty SDK." --- <CustomDocCardList /> --- # File: capacitor-get-onboardings --- --- title: "Obtener onboardings en el SDK de Capacitor" description: "Aprende a obtener onboardings en Adapty para Capacitor." --- Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el builder en el Adapty Dashboard, puedes mostrarlo en tu app de Capacitor. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, tal como se describe a continuación. Antes de empezar, asegúrate de que: 1. Has [creado un onboarding](create-onboarding). 2. Has añadido el onboarding a un [placement](placements). ## Obtener el onboarding \{#fetch-onboarding\} Cuando creas un [onboarding](onboardings) con nuestro builder sin código, se almacena como un contenedor con configuración que tu app necesita obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a cuestionarios o entradas de formularios). El contenedor también registra automáticamente los eventos de analítica, por lo que no necesitas implementar un seguimiento de vistas por separado. Para un mejor rendimiento, obtén la configuración del onboarding con antelación para que las imágenes tengan tiempo suficiente de descargarse antes de mostrárselas a los usuarios. Para obtener un onboarding, usa el método `getOnboarding`: ```typescript showLineNumbers try { const onboarding = await adapty.getOnboarding({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en', params: { fetchPolicy: 'reload_revalidating_cache_data', // Load from server, fallback to cache loadTimeoutMs: 5000 // 5 second timeout } }); console.log('Onboarding fetched successfully'); } catch (error) { console.error('Failed to fetch onboarding:', error); } ``` Luego, llama al método `createOnboardingView` para crear una instancia de vista. :::warning El resultado del método `createOnboardingView` solo se puede usar una vez. Si necesitas usarlo de nuevo, vuelve a llamar al método `createOnboardingView`. ::: ```typescript showLineNumbers if (onboarding.hasViewConfiguration) { try { const view = await createOnboardingView(onboarding); console.log('Onboarding view created successfully'); } catch (error) { console.error('Failed to create onboarding view:', error); } } else { // Use your custom logic console.log('Onboarding does not have view configuration'); } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>predeterminado: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto de uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de configuración regional](localizations-and-locale-codes) para más información sobre los códigos de configuración regional y cómo recomendamos usarlos.</p> | | **params.fetchPolicy** | <p>opcional</p><p>predeterminado: `'reload_revalidating_cache_data'`</p> | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `'return_cache_data_else_load'` para devolver los datos en caché si existen. En este caso, es posible que los usuarios no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché se conserva al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p> | | **params.loadTimeoutMs** | <p>opcional</p><p>predeterminado: 5000 ms</p> | <p>Este valor limita el tiempo de espera (en milisegundos) para este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el fallback local.</p><p>Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeoutMs`, ya que la operación puede estar compuesta de diferentes solicitudes internamente.</p> | Parámetros de respuesta: | Parámetro | Descripción | |:----------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **onboarding** | Un objeto [`AdaptyOnboarding`](https://capacitor.adapty.io/interfaces/adaptyonboarding) con: el identificador y la configuración del onboarding, Remote Config y varias otras propiedades. | ## Acelera la obtención del onboarding con el onboarding de audiencia predeterminada \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} Por lo general, los onboardings se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos en los que tengas numerosas audiencias y onboardings, y tus usuarios tengan una conexión a internet lenta, obtener un onboarding puede tardar más de lo deseado. En estas situaciones, puede que quieras mostrar un onboarding predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún onboarding. Para ello, puedes usar el método `getOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es importante entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, tal como se detalla en la sección [Obtener el onboarding](#fetch-onboarding) anterior. :::warning Considera usar `getOnboarding` en lugar de `getOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: Puede generar problemas al dar soporte a varias versiones de la app, lo que requiere diseños compatibles con versiones anteriores o aceptar que las versiones más antiguas puedan mostrarse incorrectamente. - **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación por país, atribución o atributos personalizados. Si para tu caso de uso la mayor velocidad de obtención compensa estos inconvenientes, usa `getOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getOnboarding` como se describe [arriba](#fetch-onboarding). ::: ```typescript showLineNumbers try { const onboarding = await adapty.getOnboardingForDefaultAudience({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en', params: { fetchPolicy: 'reload_revalidating_cache_data' // Load from server, fallback to cache } }); console.log('Default audience onboarding fetched successfully'); } catch (error) { console.error('Failed to fetch default audience onboarding:', error); } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>predeterminado: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto de uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de configuración regional](localizations-and-locale-codes) para más información sobre los códigos de configuración regional y cómo recomendamos usarlos.</p> | | **params.fetchPolicy** | <p>opcional</p><p>predeterminado: `'reload_revalidating_cache_data'`</p> | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `'return_cache_data_else_load'` para devolver los datos en caché si existen. En este caso, es posible que los usuarios no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché se conserva al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p> | --- # File: capacitor-present-onboardings --- --- title: "Mostrar onboardings en el SDK de Capacitor" description: "Descubre cómo mostrar onboardings en Capacitor para mejorar las conversiones y los ingresos." --- Si has personalizado un onboarding con el builder, no necesitas preocuparte por renderizarlo en el código de tu app móvil para mostrárselo al usuario. Ese onboarding ya incluye tanto el contenido que debe mostrarse como la forma en que debe hacerlo. Antes de empezar, asegúrate de que: 1. Has [creado un onboarding](create-onboarding). 2. Has añadido el onboarding a un [placement](placements). ## Mostrar el onboarding \{#present-onboarding\} Para mostrar un onboarding, usa el método `view.present()` sobre el `view` creado por el método `createOnboardingView`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el onboarding de nuevo, llama a `createOnboardingView` otra vez para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo puede provocar un error. ::: ```typescript showLineNumbers try { const view = await createOnboardingView(onboarding); view.setEventHandlers({ onClose: (actionId, meta) => { console.log('Onboarding closed:', actionId); return true; // Allow the onboarding to close }, onCustom: (actionId, meta) => { console.log('Custom action:', actionId); return false; // Don't close the onboarding } }); await view.present(); console.log('Onboarding presented successfully'); } catch (error) { console.error('Failed to present onboarding:', error); } ``` ## Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el onboarding en iOS pasando el parámetro `iosPresentationStyle` al método `present()`. El parámetro acepta los valores `'full_screen'` (predeterminado) o `'page_sheet'`. ```typescript showLineNumbers await view.present({ iosPresentationStyle: 'page_sheet' }); ``` ## Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\} :::important La personalización de cómo se abren los enlaces en los onboardings está disponible a partir del SDK de Adapty v.3.15. ::: Por defecto, los enlaces en los onboardings se abren en un navegador integrado en la app. Esto ofrece una experiencia fluida al mostrar las páginas web dentro de tu aplicación, sin que el usuario tenga que cambiar de app. Si prefieres abrir los enlaces en un navegador externo, puedes personalizar este comportamiento estableciendo el parámetro `openIn` en `browser_out_app`: ```typescript showLineNumbers await view.present({ openIn: 'browser_out_app' }); // default — browser_in_app ``` ## Próximos pasos \{#next-steps\} Una vez que hayas mostrado tu onboarding, querrás [gestionar las interacciones y eventos del usuario](capacitor-handling-onboarding-events). Aprende a manejar los eventos del onboarding para responder a las acciones del usuario y hacer seguimiento de los datos analíticos. --- # File: capacitor-handling-onboarding-events --- --- title: "Gestionar eventos de onboarding en el SDK de Capacitor" description: "Gestiona eventos relacionados con el onboarding en Capacitor usando Adapty." --- Los onboardings configurados con el builder generan eventos a los que tu app puede responder. Usa el método `setEventHandlers` para gestionar estos eventos en la presentación de pantallas independientes. Antes de empezar, asegúrate de que: 1. Has [creado un onboarding](create-onboarding). 2. Has añadido el onboarding a un [placement](placements). ## Configurar los manejadores de eventos \{#set-up-event-handlers\} Para gestionar eventos de onboardings, usa el método `view.setEventHandlers`: ```typescript showLineNumbers try { const view = await createOnboardingView(onboarding); view.setEventHandlers({ onAnalytics(event, meta) { console.log('Analytics event:', event); }, onClose(actionId, meta) { console.log('Onboarding closed:', actionId); return true; // Allow the onboarding to close }, onCustom(actionId, meta) { console.log('Custom action:', actionId); return false; // Don't close the onboarding }, onPaywall(actionId, meta) { console.log('Paywall action:', actionId); view.dismiss().then(() => { openPaywall(actionId); }); }, onStateUpdated(action, meta) { console.log('State updated:', action); }, onFinishedLoading(meta) { console.log('Onboarding finished loading'); }, onError(error) { console.error('Onboarding error:', error); }, }); await view.present(); } catch (error) { console.error('Failed to present onboarding:', error); } ``` ## Tipos de eventos \{#event-types\} Las siguientes secciones describen los distintos tipos de eventos que puedes gestionar. ### Gestionar acciones personalizadas \{#handle-custom-actions\} En el builder, puedes añadir una acción **custom** a un botón y asignarle un ID. <img src="/assets/shared/img/ios-events-1.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Luego puedes usar ese ID en tu código y gestionarlo como una acción personalizada. Por ejemplo, si el usuario pulsa un botón personalizado como **Login** o **Allow notifications**, el manejador de eventos se activará con el parámetro `actionId` que coincide con el **Action ID** del builder. Puedes crear tus propios IDs, como "allowNotifications". ```typescript showLineNumbers view.setEventHandlers({ onCustom(actionId, meta) { switch (actionId) { case 'login': console.log('Login action triggered'); break; case 'allow_notifications': console.log('Allow notifications action triggered'); break; } return false; // Don't close the onboarding }, }); ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "actionId": "allow_notifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ``` </Details> ### Finalización de la carga del onboarding \{#finishing-loading-onboarding\} Cuando un onboarding termina de cargarse, se activa este evento: ```typescript showLineNumbers view.setEventHandlers({ onFinishedLoading(meta) { console.log('Onboarding loaded:', meta.onboardingId); }, }); ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ``` </Details> ### Cerrar el onboarding \{#closing-onboarding\} El onboarding se considera cerrado cuando el usuario pulsa un botón con la acción **Close** asignada. <img src="/assets/shared/img/ios-events-2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::important Ten en cuenta que debes gestionar qué ocurre cuando el usuario cierra el onboarding. Por ejemplo, necesitas dejar de mostrar el propio onboarding. ::: ```typescript showLineNumbers view.setEventHandlers({ onClose(actionId, meta) { console.log('Onboarding closed:', actionId); return true; // Allow the onboarding to close }, }); ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> ### Abrir un paywall \{#opening-a-paywall\} :::tip Gestiona este evento para abrir un paywall si quieres abrirlo dentro del onboarding. Si prefieres abrirlo después de que se cierre, hay una forma más sencilla: gestiona la acción de cierre y abre el paywall sin depender de los datos del evento. ::: Si el usuario pulsa un botón que abre un paywall, recibirás el ID de acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más fluida de trabajar con paywalls en onboardings es que el ID de acción coincida con el ID del placement del paywall. Ten en cuenta que, en iOS, solo se puede mostrar una vista (paywall u onboarding) en pantalla a la vez. Si presentas un paywall sobre un onboarding, no podrás controlar el onboarding programáticamente en segundo plano. Si intentas cerrar el onboarding, se cerrará el paywall en su lugar y el onboarding quedará visible. Para evitarlo, cierra siempre la vista del onboarding antes de presentar el paywall. ```typescript showLineNumbers view.setEventHandlers({ onPaywall(actionId, meta) { // Dismiss onboarding before presenting paywall view.dismiss().then(() => { openPaywall(actionId); }); }, }); async function openPaywall(placementId: string) { // Implement your paywall opening logic here } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ``` </Details> ### Seguimiento de la navegación \{#tracking-navigation\} Recibes un evento de analítica cuando ocurren distintos eventos relacionados con la navegación durante el flujo del onboarding: ```typescript showLineNumbers view.setEventHandlers({ onAnalytics(event, meta) { console.log('Analytics event:', event.type, meta.onboardingId); }, }); ``` El objeto `event` puede ser de uno de los siguientes tipos: |Tipo | Descripción | |------------|-------------| | `onboardingStarted` | Cuando el onboarding se ha cargado | | `screenPresented` | Cuando se muestra cualquier pantalla | | `screenCompleted` | Cuando se completa una pantalla. Incluye un `elementId` opcional (identificador del elemento completado) y un `reply` opcional (respuesta del usuario). Se activa cuando el usuario realiza cualquier acción para salir de la pantalla. | | `secondScreenPresented` | Cuando se muestra la segunda pantalla | | `userEmailCollected` | Se activa cuando se recoge el correo electrónico del usuario a través del campo de entrada | | `onboardingCompleted` | Se activa cuando el usuario llega a una pantalla con el ID `final`. Si necesitas este evento, [asigna el ID `final` a la última pantalla](design-onboarding). | | `unknown` | Para cualquier tipo de evento no reconocido. Incluye `name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) | Cada evento incluye información `meta` con los siguientes campos: | Campo | Descripción | |------------|-------------| | `onboardingId` | Identificador único del flujo del onboarding | | `screenClientId` | Identificador de la pantalla actual | | `screenIndex` | Posición de la pantalla actual en el flujo | | `screensTotal` | Número total de pantallas en el flujo | <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // onboardingStarted { "name": "onboarding_started", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } // screenPresented { "name": "screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 4 } } // screenCompleted { "name": "screen_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 }, "params": { "element_id": "profile_form", "reply": "success" } } // secondScreenPresented { "name": "second_screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // userEmailCollected { "name": "user_email_collected", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // onboardingCompleted { "name": "onboarding_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> --- # File: capacitor-onboarding-input --- --- title: "Procesar datos de onboardings en Capacitor SDK" description: "Guarda y usa datos de onboardings en tu app de Capacitor con Adapty SDK." --- Cuando tus usuarios responden a una pregunta de un cuestionario o introducen datos en un campo de texto, se invocará el método `onStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código. Por ejemplo: ```typescript view.setEventHandlers({ onStateUpdated(action, meta) { // Process data }, }); ``` Consulta el formato de acción [aquí](https://capacitor.adapty.io/types/onboardingstateupdatedaction). <Details> <summary>Ejemplos de datos guardados (el formato puede variar según tu implementación)</summary> ```javascript // Example of a saved select action { "elementId": "preference_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "preferences_screen", "screenIndex": 1, "screensTotal": 3 }, "params": { "type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Example of a saved multi-select action { "elementId": "interests_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 3 }, "params": { "type": "multiSelect", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Example of a saved input action { "elementId": "name_input", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "input", "value": { "type": "text", "value": "John Doe" } } } // Example of a saved date picker action { "elementId": "birthday_picker", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "datePicker", "value": { "day": 15, "month": 6, "year": 1990 } } } ``` </Details> ## Casos de uso \{#use-cases\} ### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\} Si quieres vincular inmediatamente los datos introducidos con el perfil del usuario y evitar pedirle la misma información dos veces, necesitas [actualizar el perfil del usuario](capacitor-setting-user-attributes) con los datos introducidos al gestionar la acción. Por ejemplo, si pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name` y quieres usar ese valor como nombre de pila del usuario, y además les pides su correo en el campo `email`, en el código de tu app quedaría así: ```typescript showLineNumbers view.setEventHandlers({ onStateUpdated(action, meta) { // Store user preferences or responses if (action.elementType === 'input') { const profileParams: any = {}; // Map elementId to appropriate profile field switch (action.elementId) { case 'name': if (action.value.type === 'text') { profileParams.firstName = action.value.value; } break; case 'email': if (action.value.type === 'email') { profileParams.email = action.value.value; } break; } // Update profile if we have data to update if (Object.keys(profileParams).length > 0) { adapty.updateProfile({ params: profileParams }).catch((error) => { // handle the error }); } } }, }); ``` ### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\} Usando cuestionarios en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios cuando completan el onboarding. Por ejemplo, puedes preguntarles sobre su experiencia con el deporte y mostrar diferentes CTAs y productos a distintos grupos de usuarios. 1. [Añade un cuestionario](onboarding-quizzes) en el editor de onboardings y asigna IDs significativos a sus opciones. 2. Gestiona las respuestas del cuestionario según sus IDs y [establece atributos personalizados](capacitor-setting-user-attributes) para los usuarios. ```typescript showLineNumbers view.setEventHandlers({ onStateUpdated(action, meta) { // Handle quiz responses and set custom attributes if (action.elementType === 'select') { const profileParams: any = {}; // Map quiz responses to custom attributes switch (action.elementId) { case 'experience': // Set custom attribute 'experience' with the selected value (beginner, amateur, pro) profileParams.codableCustomAttributes = { experience: action.value.value }; break; } // Update profile if we have data to update if (Object.keys(profileParams).length > 0) { adapty.updateProfile({ params: profileParams }).catch((error) => { // handle the error }); } } }, }); ``` 3. [Crea segmentos](segments) para cada valor de atributo personalizado. 4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado. 5. [Muestra un paywall](capacitor-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](capacitor-handling-onboarding-events#opening-a-paywall). --- # File: capacitor-best-practices --- --- title: "Best practices in Capacitor SDK" description: "Reference patterns for integrating Adapty SDK on Capacitor — call order, error handling, and other production-readiness rules." --- <CustomDocCardList /> --- # File: capacitor-sdk-call-order --- --- title: "Orden de llamadas en el SDK de Capacitor" description: "Evita perder acceso premium, atribución faltante y errores intermitentes #2002 llamando a los métodos del SDK de Adapty en el orden correcto." --- `adapty.activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que se resuelva, el SDK no tiene estado. Cualquier llamada realizada antes o en paralelo con `activate()` falla con [`#2002 notActivated`](capacitor-handle-errors#custom-network-codes). Si tu app autentica usuarios y recoges un customer user ID después del lanzamiento, llama a `adapty.identify()` en ese momento. No llames a métodos que dependan de acciones del usuario hasta que `identify` se resuelva. Las llamadas que compiten con él fallan con [`#3006 profileWasChanged`](capacitor-handle-errors#custom-network-codes) o caen sobre el perfil anónimo creado en la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la titularidad de la instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `identify` y sigue trabajando con el perfil anónimo. Los SDKs de MMP y analítica (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera sus callbacks de UID antes de llamar a `adapty.activate`. De lo contrario, el ID de MMP cae sobre un perfil anónimo temporal y no siempre se transfiere al identificado. Para más detalles sobre AppsFlyer, consulta [AppsFlyer](appsflyer). ## El orden correcto \{#the-correct-order\} Tu camino depende de dos cosas: cuándo conoces el customer user ID y si usas un SDK de MMP o analítica. - **Pasos 2 y 5**: Obligatorios para cualquier app. Activa el SDK y luego llama a los métodos del SDK. - **Pasos 1 y 3**: Necesarios solo si integras un SDK de MMP o analítica (AppsFlyer, Adjust, Branch, PostHog). - **Paso 4**: Necesario solo si tu app autentica usuarios y recoge el customer user ID después del lanzamiento. Si tienes el customer user ID en el lanzamiento de la app, pásalo directamente a `activate()` (paso 2a). Esta ruta nunca crea un perfil anónimo, por lo que el paso 4 es innecesario. | Paso | Llamada | Cuándo | Notas | |------|---------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | Inicializa tu SDK de MMP o analítica (AppsFlyer, Adjust, PostHog, Branch) | Lanzamiento de la app, primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerUID`. | | 2a | `adapty.activate({ apiKey: '...', params: { customerUserId: '...' } })` | Lanzamiento de la app, después del paso 1, si tienes el customer user ID | Recomendado. Nunca se crea un perfil anónimo. | | 2b | `adapty.activate({ apiKey: '...' })` sin `customerUserId` | Lanzamiento de la app, después del paso 1, si no tienes el customer user ID (o nunca lo recoges) | Adapty crea un perfil anónimo. | | 3 | `adapty.setIntegrationIdentifier({ key: '...', value: '...' })` para cada MMP | Después del paso 2, antes de cualquier llamada de acción de usuario | Necesario para que los IDs de MMP caigan en el perfil correcto. | | 4 | `await adapty.identify({ customerUserId: 'YOUR_USER_ID' })` | Después del paso 3 (o paso 2 si no hay MMP), antes del paso 5 — solo en la ruta 2b con autenticación | Usa siempre `await`. Las llamadas concurrentes durante `identify` producen `#3006 profileWasChanged`. | | 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Después del paso 4 si llamas a `identify`; en caso contrario, después del paso 3 (o paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. | :::important Omitir estos pasos provoca pérdida de acceso premium para usuarios que regresan, `appsflyer_id` faltante en los perfiles y paywalls devueltos para la audiencia incorrecta. ::: ## Instalaciones web2app y de embudo web \{#web2app-and-web-funnel-installs\} Si los usuarios compran en un checkout web (Stripe, Paddle) e instalan luego la app nativa, el primer `activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el customer user ID antes del lanzamiento de la app (desde tu flujo de autenticación o el referrer de instalación), pásalo directamente a `activate()`. De lo contrario, la compra web es invisible en el dispositivo hasta que llames a `identify({ customerUserId: 'YOUR_USER_ID' })` y luego a `restorePurchases`. Para los metadatos que debes enviar con cada checkout web, consulta: - [Stripe](stripe) - [Paddle](paddle) --- # File: capacitor-optimize-paywall-fetching --- --- title: "Optimizar la obtención de paywalls en el SDK de Capacitor" description: "Obtén paywalls de Adapty de forma fiable: temporización, caché y patrones de respaldo para Capacitor." --- Una obtención de paywall fiable en Capacitor hace tres cosas: renderiza rápido, devuelve el paywall dirigido a la audiencia y recurre al respaldo sin problemas cuando la red es lenta. Las reglas siguientes cubren los patrones de temporización, caché y respaldo para conseguirlo. :::tip Las reglas asumen que `adapty.activate()` y `adapty.identify()` ya se han resuelto. Consulta [Orden de llamadas en el SDK de Capacitor](capacitor-sdk-call-order). ::: ## Reglas y errores comunes \{#rules-and-pitfalls\} | Haz esto | No hagas esto | Por qué | |---|---|---| | Obtén el placement que estás a punto de mostrar. | No hagas prefetch de todos los placements de forma concurrente al iniciar. | El prefetch masivo bloquea el hilo principal y produce una pantalla en negro durante la ráfaga. | | Llama a `getPaywall` después de que la atribución haya tenido oportunidad de resolverse — por ejemplo, 1–2 segundos después de `activate` o tras dispararse el listener `onLatestProfileLoad`. | No llames a `getPaywall` al iniciar la app en `App.tsx`. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia predeterminada y omite silenciosamente los segmentos y la personalización de ASA. | | Establece un `loadTimeoutMs` y configura un [paywall de respaldo](fallback-paywalls) para cada placement. | No esperes `getPaywall` indefinidamente. | Sin tiempo de espera, los usuarios con conectividad deficiente ven una pantalla en blanco hasta que la red se resuelve — o cierran la app. | Consulta [Obtener paywalls y productos](fetch-paywalls-and-products-capacitor) para la referencia de parámetros `fetchPolicy` y `loadTimeoutMs`, y [Placements](placements) para elegir el placement adecuado. ## Ajuste para conectividad deficiente \{#tune-for-poor-connectivity\} Para mercados con conectividad consistentemente deficiente (zonas rurales, transporte, regiones afectadas por enrutamiento): - Establece `fetchPolicy: 'return_cache_data_else_load'` en cada obtención excepto la primera. - Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard. - Establece `loadTimeoutMs` entre 3000 y 5000 milisegundos y acepta el paywall de respaldo cuando se agote el tiempo. - No condicionales la visualización del paywall a `adapty.getProfile()`. Llama a `getPaywall` de forma independiente para que un perfil lento no bloquee la interfaz. --- # File: capacitor-test --- --- title: "Test & release in Capacitor SDK" description: "Learn how to test and release your Capacitor app with Adapty SDK." --- If you've already implemented the Adapty SDK in your Capacitor app, you'll want to test that everything is set up correctly and that purchases work as expected across both iOS and Android platforms. This involves testing both the SDK integration and the actual purchase flow with Apple's sandbox environment and Google Play's testing environment. ## Test your app For comprehensive testing of your in-app purchases, see our platform-specific testing guides: [iOS testing guide](test-purchases-in-sandbox) and [Android testing guide](testing-on-android). ## Prepare for release Before submitting your app to the store, follow the [Release checklist](release-checklist) to confirm: - Store connection and server notifications are configured - Purchases complete and are reported to Adapty - Access unlocks and restores correctly - Privacy and review requirements are met --- # File: kids-mode-capacitor --- --- title: "Modo Infantil en Capacitor SDK" description: "Activa fácilmente el Modo Infantil para cumplir con las políticas de Apple y Google. No se recopilan IDFA, GAID ni datos publicitarios en Capacitor SDK." --- Si tu aplicación de Capacitor está destinada a niños, debes seguir las políticas de [Apple](https://developer.apple.com/kids/) y [Google](https://support.google.com/googleplay/android-developer/answer/9893335). Si usas el SDK de Adapty, unos pocos pasos sencillos te ayudarán a configurarlo para cumplir con estas políticas y superar las revisiones de las stores. ## ¿Qué se requiere? \{#whats-required\} Debes configurar el SDK de Adapty para deshabilitar la recopilación de: - [IDFA (Identifier for Advertisers)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers) (iOS) - [Android Advertising ID (AAID/GAID)](https://support.google.com/googleplay/android-developer/answer/6048248) (Android) - [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) Además, te recomendamos usar el ID de usuario del cliente con cuidado. Un ID con el formato `<Nombre.Apellido>` será tratado como recopilación de datos personales, al igual que el uso del correo electrónico. Para el Modo Infantil, la mejor práctica es usar identificadores aleatorios o anonimizados (por ejemplo, IDs hasheados o UUIDs generados por el dispositivo) para garantizar el cumplimiento. ## Activar el Modo Infantil \{#enabling-kids-mode\} ### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\} En el Adapty Dashboard, debes deshabilitar la recopilación de direcciones IP. Para ello, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** en **Collect users' IP address**. ### Cambios en el código de tu app \{#updates-in-your-mobile-app-code\} Para cumplir con las políticas, deshabilita la recopilación del IDFA, GAID y la dirección IP del usuario: ```typescript showLineNumbers try { await adapty.activate({ apiKey: 'YOUR_PUBLIC_SDK_KEY', params: { // Disable IP address collection ipAddressCollectionDisabled: true, // Disable IDFA collection on iOS ios: { idfaCollectionDisabled: true }, // Disable Google Advertising ID collection on Android android: { adIdCollectionDisabled: true } } }); console.log('Adapty activated with Kids Mode enabled'); } catch (error) { console.error('Failed to activate Adapty with Kids Mode:', error); } ``` ### Configuraciones específicas por plataforma \{#platform-specific-configurations\} #### iOS: Activar el Modo Infantil con CocoaPods \{#ios-enable-kids-mode-using-cocoapods\} Si usas CocoaPods para iOS, también puedes activar el Modo Infantil a nivel nativo: 1. Actualiza tu Podfile: - Si **no** tienes una sección `post_install`, añade el bloque de código completo a continuación. - Si **sí** tienes una sección `post_install`, combina las líneas destacadas con la tuya. ```ruby showLineNumbers title="Podfile" post_install do |installer| installer.pods_project.targets.each do |target| # highlight-start if target.name == 'Adapty' target.build_configurations.each do |config| config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)'] config.build_settings['OTHER_SWIFT_FLAGS'] << '-DADAPTY_KIDS_MODE' end end # highlight-end end end ``` 2. Ejecuta el siguiente comando para aplicar los cambios: ```sh showLineNumbers title="Shell" pod install ``` #### Android: Activar el Modo Infantil con Gradle \{#android-enable-kids-mode-using-gradle\} Para Android, también puedes activar el Modo Infantil a nivel nativo añadiendo lo siguiente al `build.gradle` de tu app: ```groovy showLineNumbers title="android/app/build.gradle" android { defaultConfig { // ... existing config ... // Enable Kids Mode buildConfigField "boolean", "ADAPTY_KIDS_MODE", "true" } } ``` ## Próximos pasos \{#next-steps\} Una vez que hayas activado el Modo Infantil, asegúrate de: 1. Probar tu app a fondo para garantizar que toda la funcionalidad funciona correctamente. 2. Revisar la política de privacidad de tu app para reflejar la recopilación de datos deshabilitada. 3. Enviar tu app a revisión con documentación clara sobre el cumplimiento del Modo Infantil. Para más información sobre los requisitos específicos de cada plataforma: - [Modo Infantil en iOS SDK](kids-mode) para detalles adicionales de configuración en iOS - [Modo Infantil en Android SDK](kids-mode-android) para detalles adicionales de configuración en Android --- # File: capacitor-reference --- --- title: "Reference" description: "Reference documentation for Adapty Capacitor SDK." --- This page contains reference documentation for Adapty Capacitor SDK. Choose the topic you need: - **[SDK models](https://capacitor.adapty.io/)** - Data models and structures used by the SDK - **[Handle errors](capacitor-handle-errors)** - Error handling and troubleshooting --- # File: capacitor-handle-errors --- --- title: "Gestionar errores en el SDK de Capacitor" description: "Gestionar errores en el SDK de Capacitor." --- Cada error que devuelve el SDK es una instancia de `AdaptyError`. Aquí tienes un ejemplo: :::tip **Activa los logs detallados antes de depurar.** La mayoría de los `AdaptyError` envuelven un error subyacente de StoreKit, Play Billing, red o backend. Con los logs detallados activados (`adapty.setLogLevel({ logLevel: 'verbose' })` — consulta [Logging](sdk-installation-capacitor#logging)), ese error se muestra en la consola, lo que generalmente indica la causa real. La propiedad `detail` de `AdaptyError` se rellena independientemente del nivel de log — los logs detallados simplemente la muestran en la consola. ::: ```typescript showLineNumbers try { const result = await adapty.makePurchase({ product }); // Manejar el resultado de la compra if (result.type === 'success') { console.log('Compra exitosa:', result.profile); } else if (result.type === 'user_cancelled') { console.log('El usuario canceló la compra'); } else if (result.type === 'pending') { console.log('La compra está pendiente'); } } catch (error) { if (error instanceof AdaptyError) { console.error('Error de Adapty:', error.adaptyCode, error.localizedDescription); // Manejar códigos de error específicos switch (error.adaptyCode) { case ErrorCodeName.cantMakePayments: console.log('Las compras in-app no están permitidas en este dispositivo'); break; case ErrorCodeName.notActivated: console.log('El SDK de Adapty no está activado'); break; case ErrorCodeName.productPurchaseFailed: console.log('La compra falló:', error.detail); break; default: console.log('Se produjo otro error:', error.detail); } } else { console.error('Error no relacionado con Adapty:', error); } } ``` ## Propiedades del error \{#error-properties\} La clase `AdaptyError` expone las siguientes propiedades: | Propiedad | Tipo | Descripción | |----------|------|-------------| | `adaptyCode` | `number` | Código de error numérico (p. ej., `1003` para cantMakePayments) | | `localizedDescription` | `string` | Mensaje de error legible por el usuario | | `detail` | `string \| undefined` | Detalles adicionales del error (opcional) | | `message` | `string` | Mensaje de error completo con código y descripción | ## Códigos de error \{#error-codes\} El SDK exporta constantes y utilidades para trabajar con códigos de error: ### Constante ErrorCodeName \{#errorcodename-constant\} Mapea identificadores de cadena a códigos numéricos: ```typescript ErrorCodeName.cantMakePayments // 1003 ErrorCodeName.notActivated // 2002 ErrorCodeName.networkFailed // 2005 ``` ### Constante ErrorCode \{#errorcode-constant\} Mapea códigos numéricos a identificadores de cadena: ```typescript ErrorCode[1003] // 'cantMakePayments' ErrorCode[2002] // 'notActivated' ErrorCode[2005] // 'networkFailed' ``` ### Funciones de ayuda \{#helper-functions\} ```typescript // Get numeric code from string name: getErrorCode('cantMakePayments') // 1003 // Get string name from numeric code: getErrorPrompt(1003) // 'cantMakePayments' ``` ### Comparar códigos de error \{#comparing-error-codes\} **Importante:** `error.adaptyCode` es un **número**, así que compáralo directamente con códigos numéricos: ```typescript // Option 1: Use ErrorCodeName constant (recommended) ✅ if (error.adaptyCode === ErrorCodeName.cantMakePayments) { console.log('Cannot make payments'); } // Option 2: Compare with numeric literal ✅ if (error.adaptyCode === 1003) { console.log('Cannot make payments'); } // NOT like this ❌ - compares number to string and will never match if (error.adaptyCode === ErrorCode[1003]) { } ``` ## Manejador de errores global \{#global-error-handler\} Puedes configurar un manejador de errores global para capturar todos los errores de Adapty: ```typescript showLineNumbers // Configura el manejador de errores global AdaptyError.onError = (error: AdaptyError) => { console.error('Error global de Adapty:', { code: error.adaptyCode, message: error.localizedDescription, detail: error.detail }); // Gestiona tipos de error específicos de forma global if (error.adaptyCode === ErrorCodeName.notActivated) { // SDK no activado - puede que sea necesario reintentar la activación console.log('SDK no activado, intentando reactivar...'); } }; ``` ## Patrones comunes de manejo de errores \{#common-error-handling-patterns\} ### Gestión de errores en las compras \{#handle-purchase-errors\} ```typescript showLineNumbers async function handlePurchase(product: AdaptyPaywallProduct) { try { const result = await adapty.makePurchase({ product }); if (result.type === 'success') { console.log('Compra exitosa:', result.profile); } else if (result.type === 'user_cancelled') { console.log('El usuario canceló la compra'); } else if (result.type === 'pending') { console.log('La compra está pendiente'); } } catch (error) { if (error instanceof AdaptyError) { switch (error.adaptyCode) { case ErrorCodeName.cantMakePayments: console.log('Las compras in-app no están permitidas'); break; case ErrorCodeName.productPurchaseFailed: console.log('La compra falló:', error.detail); break; default: console.error('Error en la compra:', error.localizedDescription); } } } } ``` ### Gestión de errores de red \{#handle-network-errors\} ```typescript showLineNumbers async function fetchPaywall(placementId: string) { try { const paywall = await adapty.getPaywall({ placementId }); return paywall; } catch (error) { if (error instanceof AdaptyError) { switch (error.adaptyCode) { case ErrorCodeName.networkFailed: console.log('Network error, retrying...'); // Implement retry logic break; case ErrorCodeName.serverError: console.log('Server error:', error.detail); break; case ErrorCodeName.notActivated: console.log('SDK not activated'); break; default: console.error('Paywall fetch error:', error.localizedDescription); } } throw error; } } ``` ## Códigos de sistema de StoreKit \{#system-storekit-codes\} | Error | Código | Descripción | |-----|----|-----------| | unknown | 0 | Este error indica que ocurrió un error desconocido o inesperado. | | clientInvalid | 1 | Este código de error indica que el cliente no tiene permiso para realizar la acción solicitada. | | paymentCancelled | 2 | <p>Este código de error indica que el usuario canceló una solicitud de pago.</p><p>No se requiere ninguna acción, pero en términos de lógica de negocio, puedes ofrecer un descuento al usuario o recordárselo más adelante.</p> | | paymentInvalid | 3 | Este error indica que uno de los parámetros de pago no fue reconocido por la store. | | paymentNotAllowed | 4 | <p>Este código de error indica que el usuario no tiene permiso para autorizar pagos. Posibles razones:</p><p></p><p>- Los pagos no están disponibles en el país del usuario.</p><p>- El usuario es menor de edad.</p> | | storeProductNotAvailable | 5 | Este código de error indica que el producto solicitado no está disponible en la App Store. Asegúrate de que el producto esté disponible en el país correspondiente. | | cloudServicePermissionDenied | 6 | Este código de error indica que el usuario no ha permitido el acceso a la información del servicio en la nube. | | cloudServiceNetworkConnectionFailed | 7 | Este código de error indica que el dispositivo no pudo conectarse a la red. | | cloudServiceRevoked | 8 | Este código de error indica que el usuario ha revocado el permiso para usar este servicio en la nube. | | privacyAcknowledgementRequired | 9 | Este código de error indica que el usuario aún no ha aceptado la política de privacidad de la store. | | unauthorizedRequestData | 10 | Este código de error indica que la solicitud está construida de forma incorrecta. | | invalidOfferIdentifier | 11 | <p>El identificador de oferta no es válido. Posibles razones:</p><p></p><p>- No has configurado una oferta con ese identificador en la App Store.</p><p>- Has revocado la oferta.</p><p>- Hay un error tipográfico en el ID de la oferta.</p> | | invalidSignature | 12 | Este código de error indica que la firma en un descuento de pago no es válida. Asegúrate de haber rellenado el campo **In-app purchase Key ID** y de haber subido el archivo **In-App Purchase Private Key**. Consulta el tema [Configure App Store integration](app-store-connection-configuration) para más detalles. | | missingOfferParams | 13 | <p>Este error indica problemas con la integración de Adapty o con las ofertas.</p><p>Consulta [Configure App Store integration](app-store-connection-configuration) y [Offers](offers) para más detalles sobre cómo configurarlas.</p> | | invalidOfferPrice | 14 | Este código de error indica que el precio que especificaste en la store ya no es válido. Las ofertas siempre deben representar un precio con descuento. | ## Códigos personalizados de Android \{#custom-android-codes\} | Error | Código | Descripción | |-----|----|-----------| | adaptyNotInitialized | 20 | Necesitas configurar correctamente el SDK de Adapty mediante el método `Adapty.activate`. Aprende cómo hacerlo [para React Native](sdk-installation-reactnative). | | productNotFound | 22 | Este error indica que el producto solicitado para la compra no está disponible en la store. | | invalidJson | 23 | El JSON del paywall no es válido. Corrígelo en el Adapty Dashboard. Consulta el tema [Customize paywall with remote config](customize-paywall-with-remote-config) para más detalles sobre cómo corregirlo. | | currentSubscriptionToUpdateNotFoundInHistory | 24 | No se encontró la suscripción original que debe renovarse. | | pendingPurchase | 25 | Este error indica que el estado de la compra está pendiente en lugar de completado. Consulta la página [Handling pending transactions](https://developer.android.com/google/play/billing/integrate#pending) en la documentación para desarrolladores de Android para más detalles. | | billingServiceTimeout | 97 | Este error indica que la solicitud alcanzó el tiempo de espera máximo antes de que Google Play pudiera responder. Esto puede deberse, por ejemplo, a un retraso en la ejecución de la acción solicitada por la llamada a la Play Billing Library. | | featureNotSupported | 98 | La función solicitada no es compatible con la Play Store en el dispositivo actual. | | billingServiceDisconnected | 99 | Este error fatal indica que la conexión de la app cliente al servicio de Google Play Store a través del `BillingClient` se ha interrumpido. | | billingServiceUnavailable | 102 | Este error transitorio indica que el servicio de facturación de Google Play no está disponible en este momento. En la mayoría de los casos, significa que hay un problema de conexión de red entre el dispositivo cliente y los servicios de facturación de Google Play. | | billingUnavailable | 103 | <p>Este error indica que ocurrió un error de facturación del usuario durante el proceso de compra. Algunos ejemplos de cuándo puede ocurrir:</p><p></p><p>1\. La app de Play Store en el dispositivo del usuario está desactualizada.</p><p>2. El usuario está en un país no compatible.</p><p>3. El usuario es un empleado de empresa y su administrador ha deshabilitado las compras.</p><p>4. Google Play no puede cargar el método de pago del usuario. Por ejemplo, la tarjeta de crédito del usuario puede haber caducado.</p><p>5. El usuario no ha iniciado sesión en la app de Play Store.</p> | | developerError | 105 | Este es un error fatal que indica que estás usando una API de forma incorrecta. | | billingError | 106 | Este es un error fatal que indica un problema interno en Google Play. | | itemAlreadyOwned | 107 | El producto consumible ya ha sido comprado. | | itemNotOwned | 108 | Este error indica que la acción solicitada sobre el artículo falló. | ## Códigos personalizados de StoreKit \{#custom-storekit-codes\} | Error | Código | Descripción | |-----|----|-----------| | noProductIDsFound | 1000 | <p>Este error indica que ninguno de los productos del paywall está disponible en la store.</p><p>Si encuentras este error, sigue los pasos a continuación para resolverlo:</p><p></p><p>1. Comprueba que todos los productos se han añadido al Adapty Dashboard.</p><p>2. Asegúrate de que el Bundle ID de tu app coincide con el de Apple Connect.</p><p>3. Verifica que los identificadores de producto de las app stores coincidan con los que has añadido al Dashboard. Ten en cuenta que los identificadores no deben contener el Bundle ID, a menos que ya esté incluido en la store.</p><p>4. Confirma que el estado de pago de la app está activo en tu configuración fiscal de Apple. Asegúrate de que tu información fiscal está actualizada y que tus certificados son válidos.</p><p>5. Comprueba que hay una cuenta bancaria vinculada a la app para que pueda ser elegible para la monetización.</p><p>6. Verifica que los productos estén disponibles en todas las regiones. Además, asegúrate de que tus productos estén en estado **"Ready to Submit"**.</p> | | productRequestFailed | 1002 | <p>No se pueden obtener los productos disponibles en este momento. Posible razón:</p><p></p><p>- Aún no se ha creado ninguna caché y no hay conexión a internet al mismo tiempo.</p> | | cantMakePayments | 1003 | Las compras in-app no están permitidas en este dispositivo. | | noPurchasesToRestore | 1004 | Este error indica que Google Play no encontró ninguna compra que restaurar. | | cantReadReceipt | 1005 | <p>No hay ningún recibo válido disponible en el dispositivo. Esto puede ser un problema durante las pruebas en sandbox.</p><p>No se requiere ninguna acción, pero en términos de lógica de negocio, puedes ofrecer un descuento al usuario o recordárselo más adelante.</p> | | productPurchaseFailed | 1006 | La compra del producto falló. Esto envuelve un error subyacente de StoreKit: lee el error interno (o activa los registros detallados para verlo en la consola) para conocer la razón exacta. El error interno suele ser uno de los códigos de StoreKit 0–14 de la tabla anterior, siendo los más comunes `paymentCancelled`, `paymentInvalid`, `paymentNotAllowed` o `invalidOfferPrice`. Si no puedes identificar una razón concreta, prueba con un nuevo [perfil de sandbox](test-purchases-in-sandbox); si sigue fallando, contacta con el soporte de Apple. | | refreshReceiptFailed | 1010 | Este error indica que no se recibió el recibo. Solo aplica a StoreKit 1. | | receiveRestoredTransactionsFailed | 1011 | La restauración de la compra falló. | ## Códigos de red personalizados \{#custom-network-codes\} | Error | Código | Descripción | | :------------------- | :----- | :----------------------------------------------------------- | | notActivated | 2002 | Necesitas configurar correctamente el SDK de Adapty mediante el método `Adapty.activate`. Aprende cómo hacerlo [para React Native](sdk-installation-reactnative). | | badRequest | 2003 | Solicitud incorrecta. | | serverError | 2004 | Error del servidor. | | networkFailed | 2005 | La solicitud de red falló. | | decodingFailed | 2006 | Este error indica que falló la decodificación de la respuesta. | | encodingFailed | 2009 | Este error indica que falló la codificación de la solicitud. | | analyticsDisabled | 3000 | No podemos gestionar eventos de análisis porque los has desactivado. Consulta el tema [Analytics integration](analytics-integration) para más detalles. | | wrongParam | 3001 | Este error indica que alguno de tus parámetros no es correcto: está en blanco cuando no puede estarlo, tiene un tipo incorrecto, etc. | | activateOnceError | 3005 | No es posible llamar al método `.activate` más de una vez. | | profileWasChanged | 3006 | El perfil de usuario cambió durante la operación. | | fetchTimeoutError | 3101 | Este error significa que el paywall no se pudo obtener dentro del límite establecido. Para evitar esta situación, [configura los fallbacks locales](fetch-paywalls-and-products). | | operationInterrupted | 9000 | Esta operación fue interrumpida por el sistema. | --- # File: capacitor-sdk-migration-guides --- --- title: "Capacitor SDK Migration Guides" description: "Migration guides for Adapty Capacitor SDK versions." --- This page contains all migration guides for Adapty Capacitor SDK. Choose the version you want to migrate to for detailed instructions: - [**Migrate to v. 3.16**](migration-to-capacitor-316) --- # File: migration-to-capacitor-316 --- --- title: "Migrar el SDK de Adapty Capacitor a la versión 3.16" description: "Migra al SDK de Adapty Capacitor v3.16 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- A partir de la versión 3.16.0 del SDK de Adapty, se requiere Capacitor 8. Si necesitas Capacitor 7, usa la versión 3.15 del SDK de Adapty. Para actualizar al SDK de Capacitor v.3.16, asegúrate de que tu proyecto use Capacitor 8. Si todavía usas Capacitor 7, tienes dos opciones: 1. **Actualizar a Capacitor 8**: Sigue la [guía oficial de migración de Capacitor](https://capacitorjs.com/docs/updating/8-0) para actualizar tu proyecto y luego instala el SDK de Adapty v.3.16. 2. **Quedarte en el SDK de Adapty v.3.15**: Si actualizar a Capacitor 8 no es viable, sigue usando el SDK de Adapty v.3.15, que es compatible con Capacitor 7. --- # End of Documentation _Generated on: 2026-05-15T20:13:57.503Z_ _Successfully processed: 40/40 files_ # FLUTTER - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.504Z Total files: 39 --- # File: sdk-installation-flutter --- --- title: "Instalar y configurar el SDK de Flutter" description: "Guía paso a paso para instalar el SDK de Adapty en Flutter en apps con suscripciones." --- El SDK de Adapty incluye dos módulos clave para una integración fluida en tu app de Flutter: - **Core Adapty**: el SDK esencial, necesario para que Adapty funcione correctamente en tu app. - **AdaptyUI**: necesario si usas el [Adapty Paywall Builder](adapty-paywall-builder), una herramienta visual sin código para crear paywalls multiplataforma fácilmente. :::tip ¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Consulta nuestra [app de ejemplo](https://github.com/adaptyteam/AdaptySDK-Flutter/tree/master/example), que muestra la configuración completa: visualización de paywalls, compras y otras funciones básicas. ::: ## Requisitos \{#requirements\} El SDK de Adapty es compatible con iOS 13.0+, pero requiere iOS 15.0+ para funcionar correctamente con paywalls creados en el Paywall Builder. :::info Adapty es compatible con Google Play Billing Library hasta la versión 8.x. Por defecto, Adapty trabaja con Google Play Billing Library v.7.0.0, pero si quieres forzar una versión posterior, puedes [añadir la dependencia](https://developer.android.com/google/play/billing/integrate#dependency) manualmente. ::: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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. </Callout> ## Instalar el SDK de Adapty \{#install-adapty-sdk\} [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-Flutter.svg?style=flat&logo=flutter)](https://github.com/adaptyteam/AdaptySDK-Flutter/releases) 1. Añade Adapty a tu archivo `pubspec.yaml`: ```yaml showLineNumbers title="pubspec.yaml" dependencies: adapty_flutter: ^<the latest SDK version> ``` 2. Ejecuta el siguiente comando para instalar las dependencias: ```bash showLineNumbers title="Terminal" flutter pub get ``` 3. Importa los SDKs de Adapty en tu aplicación: ```dart showLineNumbers title="main.dart" import 'package:adapty_flutter/adapty_flutter.dart'; ``` ## Activar el módulo Adapty del SDK de Adapty \{#activate-adapty-module-of-adapty-sdk\} Activa el SDK de Adapty en el código de tu app. :::note El SDK de Adapty solo necesita activarse una vez en tu app. ::: 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. :::important - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API 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. ::: ```dart showLineNumbers title="main.dart" void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override void initState() { _initializeAdapty(); super.initState(); } Future<void> _initializeAdapty() async { try { await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY'), ); } catch (e) { // handle the error } } Widget build(BuildContext context) { return Text("Hello"); } } ``` :::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 Flutter](flutter-sdk-call-order) para ver la secuencia completa. ::: Ahora configura los paywalls en tu app: - Si usas [Adapty Paywall Builder](adapty-paywall-builder), primero [activa el módulo AdaptyUI](#activate-adaptyui-module-of-adapty-sdk) a continuación y luego sigue la [guía de inicio rápido de Paywall Builder](flutter-quickstart-paywalls). - Si construyes tu propia UI de paywall, consulta la [guía de inicio rápido para paywalls personalizados](flutter-quickstart-manual). ## Activar el módulo AdaptyUI del SDK de Adapty \{#activate-adaptyui-module-of-adapty-sdk\} Si tienes previsto usar el [Paywall Builder](adapty-paywall-builder) y has [instalado el módulo AdaptyUI](sdk-installation-flutter#install-adapty-sdk), también necesitas activar AdaptyUI: :::note Las dependencias relacionadas con AdaptyUI se vinculan a tu app independientemente de si AdaptyUI está activado. ::: :::important En tu código, debes activar el módulo principal de Adapty antes de activar AdaptyUI. ::: ```dart showLineNumbers title="main.dart" await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withActivateUI(true), // This automatically activates AdaptyUI ); ``` ## Configuración opcional \{#optional-setup\} ### Registro de actividad \{#logging\} #### Configurar el sistema de registro \{#set-up-the-logging-system\} Adapty registra errores y otra información importante para ayudarte a entender qué está ocurriendo. Los niveles disponibles son los siguientes: | Nivel | Descripción | | :----------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | | `AdaptyLogLevel.none` | No se registrará nada. Valor por defecto | | `AdaptyLogLevel.error` | Solo se registrarán los errores | | `AdaptyLogLevel.warn` | Se registrarán los errores y los mensajes del SDK que no causan errores críticos pero merecen atención. | | `AdaptyLogLevel.info` | Se registrarán errores, advertencias y varios mensajes informativos. | | `AdaptyLogLevel.verbose` | Se registrará cualquier información adicional que pueda resultar útil durante la depuración, como llamadas a funciones, consultas a la API, etc. | Puedes establecer el nivel de registro en tu app antes de configurar Adapty: ```dart showLineNumbers title="main.dart" // Set log level before activation. // 'verbose' is recommended for development and the first production release await Adapty().setLogLevel(AdaptyLogLevel.verbose); // Or set it during configuration await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withLogLevel(AdaptyLogLevel.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. #### 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 por defecto es `false`. Usa este parámetro para mejorar la privacidad del usuario, cumplir con las normativas regionales de protección de datos (como el RGPD o la CCPA), o reducir la recopilación innecesaria de datos cuando las funciones basadas en IP no son necesarias para tu app. ```dart showLineNumbers title="main.dart" await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withIpAddressCollectionDisabled(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 `appleIdfaCollectionDisabled` (iOS) o `googleAdvertisingIdCollectionDisabled` (Android) en `true` para desactivar la recopilación de identificadores publicitarios. El valor por defecto es `false`. Usa este parámetro para cumplir con las políticas de App Store/Play Store, evitar que se active la solicitud de App Tracking Transparency, o si tu app no requiere atribución publicitaria ni analíticas basadas en IDs publicitarios. ```dart showLineNumbers title="main.dart" await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withAppleIdfaCollectionDisabled(true) // iOS ..withGoogleAdvertisingIdCollectionDisabled(true), // Android ); ``` #### Configurar la caché de medios para AdaptyUI \{#set-up-media-cache-configuration-for-adaptyui\} El módulo se activa automáticamente con el SDK de Adapty. Si no usas el Paywall Builder y quieres desactivar el módulo AdaptyUI, pasa `withActivateUI(false)` durante la activación. 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 la caché proporcionando una configuración personalizada. Usa `withMediaCacheConfiguration` para cambiar el tamaño predeterminado de la caché y el período de validez. Es opcional: si no llamas a este método, se usarán los valores por defecto (100 MB en disco, sin límite de elementos en memoria). Sin embargo, si usas la configuración, debes incluir todos los parámetros. ```dart showLineNumbers title="main.dart" final mediaCacheConfig = AdaptyUIMediaCacheConfiguration( memoryStorageTotalCostLimit: 200 * 1024 * 1024, // 200 MB memoryStorageCountLimit: 2147483647, // max int value diskStorageSizeLimit: 200 * 1024 * 1024, // 200 MB ); await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withMediaCacheConfiguration(mediaCacheConfig), ); ``` **Parámetros:** | Parámetro | Presencia | Descripción | |-----------------------------|-----------|-----------------------------------------------------------------------------------------| | memoryStorageTotalCostLimit | opcional | Tamaño total de la caché en memoria en bytes. El valor por defecto es 100 MB. | | memoryStorageCountLimit | opcional | Límite de elementos en el almacenamiento en memoria. El valor por defecto es el máximo entero. | | diskStorageSizeLimit | opcional | Límite del tamaño de archivo en disco en bytes. El valor por defecto es 100 MB. | ### Activar 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 `withGoogleLocalAccessLevelAllowed` en `true`: ```dart showLineNumbers title="main.dart" await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withGoogleLocalAccessLevelAllowed(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, incluyendo 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 por defecto es `false`. :::note Solo se elimina la caché local del SDK. El historial de transacciones con Apple y los datos del usuario en los servidores de Adapty permanecen sin cambios. ::: ```dart showLineNumbers title="main.dart" await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withClearDataOnBackup(true) // default – false ); ``` ## Solución de problemas \{#troubleshooting\} #### Reglas de copia de seguridad en Android (configuración de Auto Backup) \{#android-backup-rules-auto-backup-configuration\} 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 `<manifest>` incluya tools: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.app"> ... </manifest> ``` #### 2. Sobreescribe los atributos de copia de seguridad en `<application>` \{#2-override-backup-attributes-in-application\} En el mismo archivo `AndroidManifest.xml`, actualiza la etiqueta `<application>` para que tu app proporcione los valores definitivos e indique al fusionador de manifiestos que reemplace los valores de las librerías: ```xml <application android:name=".App" android:allowBackup="true" android:fullBackupContent="@xml/sample_backup_rules" android:dataExtractionRules="@xml/sample_data_extraction_rules" tools:replace="android:fullBackupContent,android:dataExtractionRules"> ... </application> ``` 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" <?xml version="1.0" encoding="utf-8"?> <data-extraction-rules> <cloud-backup> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </cloud-backup> <device-transfer> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </device-transfer> </data-extraction-rules> ``` **Para Android 11 e inferior** (usa el formato legado de contenido de copia de seguridad completa): ```xml title="sample_backup_rules.xml" <?xml version="1.0" encoding="utf-8"?> <full-backup-content> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.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 flujo de compra utiliza un `launchMode` distinto al predeterminado, Android puede recrearla o reutilizarla de forma incorrecta cuando el usuario vuelve 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, usa solo los modos de lanzamiento `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 tenga el modo `standard` o `singleTop`: ```xml <activity android:name=".MainActivity" android:launchMode="standard" /> ``` #### Errores de compilación de Swift 6 causados por la sobreescritura de SWIFT_VERSION en el Podfile \{#swift-6-build-errors-caused-by-podfile-swift_version-override\} Al compilar tu app de Flutter para iOS, es posible que veas errores de compilación de Swift 6 en los targets de los pods de Adapty. Los síntomas habituales incluyen incompatibilidades de `@Sendable` en `AdaptyUIBuilderLogic`, falta de conformidad `Sendable` en los 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. El código de tu propia app puede seguir usando 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 de pods: ```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 de los pods de Adapty de la sobreescritura: ```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**. Debe aparecer como **Swift 6**. --- # File: flutter-quickstart-paywalls --- --- title: "Habilitar compras usando paywalls en Flutter SDK" description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app." --- Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - [**Paywalls**](paywalls) son configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar ofertas, precios y combinaciones de productos sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 diferentes paywalls a distintos usuarios. Adapty te ofrece tres formas de habilitar compras en tu app. Elige la que mejor se adapte a los requisitos de tu app: | Implementación | Complejidad | Cuándo usarla | |------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Paywall Builder | ✅ Fácil | [Creas un paywall completo y listo para compras en el editor sin código](quickstart-paywalls). Adapty lo renderiza automáticamente y gestiona todo el flujo de compra complejo, la validación de recibos y la administración de suscripciones en segundo plano. | | Paywalls creados manualmente | 🟡 Medio | Implementas la UI de tu paywall en el código de tu app, pero sigues obteniendo el objeto paywall de Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](flutter-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 paywall creado en el Adapty Paywall Builder.** Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](flutter-making-purchases). ::: Para mostrar un paywall creado en el Adapty Paywall Builder, en el código de tu app solo necesitas: 1. **Obtener el paywall**: Obtén el paywall desde Adapty. 2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app. 3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con el paywall con la respuesta de tu app. Por ejemplo, abrir enlaces o cerrar el paywall cuando los usuarios pulsen botones. ## Antes de comenzar \{#before-you-start\} Antes de comenzar, 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-flutter) 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. Obtener el paywall \{#1-get-the-paywall\} Tus paywalls están asociados a los placements configurados en el dashboard. Los placements te permiten mostrar diferentes paywalls para distintas audiencias o ejecutar [pruebas A/B](ab-tests). Para obtener un paywall creado en el Adapty Paywall Builder, necesitas: 1. Obtener el objeto `paywall` por el ID del [placement](placements) usando el método `getPaywall` y verificar si es un paywall creado en el builder usando la propiedad `hasViewConfiguration`. 2. Crear la vista del paywall usando el método `createPaywallView`. La vista contiene los elementos de UI y el estilo necesarios para mostrar el paywall. :::important Para obtener la configuración de la vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará. ::: ```dart showLineNumbers try { final paywall = await Adapty().getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en"); // the requested paywall } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } try { final view = await AdaptyUI().createPaywallView( paywall: paywall, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ## 2. Mostrar el paywall \{#2-display-the-paywall\} Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo. Para mostrar el paywall, usa el método `view.present()` sobre el `view` creado por el método `createPaywallView`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `createPaywallView` otra vez para crear una nueva instancia de `view`. ```dart showLineNumbers title="Flutter" try { await view.present(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` :::tip Para más detalles sobre cómo mostrar un paywall, consulta nuestra [guía](flutter-present-paywalls). ::: ## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\} Cuando los usuarios pulsan botones en el paywall, el SDK de Flutter gestiona automáticamente las compras y la restauración. Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren gestionar las acciones en tu código. Para controlar o monitorizar los procesos en la pantalla del paywall, implementa los métodos de `AdaptyUIPaywallsEventsObserver` y establece el observer antes de presentar cualquier pantalla. Si un usuario ha realizado alguna acción, se invocará `paywallViewDidPerformAction` y tu app deberá responder según el ID de la acción. Por ejemplo, tu paywall probablemente tenga un botón de cerrar y URLs que abrir (como los términos de uso y la política de privacidad). Por lo tanto, debes responder a las acciones con los IDs `Close` y `OpenUrl`. :::tip Lee nuestras guías sobre cómo gestionar [acciones](flutter-handle-paywall-actions) y [eventos](flutter-handling-events) de botones. ::: ```dart showLineNumbers title="Flutter" class _PaywallScreenState extends State<PaywallScreen> implements AdaptyUIPaywallsEventsObserver { @override void initState() { super.initState(); // Register this class as the paywalls event observer AdaptyUI().setPaywallsEventsObserver(this); } // This method is called when user performs an action on the paywall UI @override void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action) { switch (action) { case const CloseAction(): case const AndroidSystemBackAction(): view.dismiss(); break; case OpenUrlAction(url: final url): // Open the URL using url_launcher package _launchUrl(url); break; } } // Helper method to launch URLs Future<void> _launchUrl(String url) async { try { final Uri uri = Uri.parse(url); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } else { // Handle case where URL cannot be launched print('Could not launch $url'); } } catch (e) { // Handle any errors print('Error launching URL: $e'); } } } ``` ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Tu paywall está listo para mostrarse en la app. Prueba tus compras en el [sandbox de 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](flutter-check-subscription-status) para asegurarte de que muestras un paywall o concedes acceso a las funciones de pago a los usuarios correctos. ## Ejemplo completo \{#full-example\} Así es como se pueden integrar todos estos pasos en tu app. ```dart void main() async { runApp(MaterialApp(home: PaywallScreen())); } class PaywallScreen extends StatefulWidget { @override State<PaywallScreen> createState() => _PaywallScreenState(); } class _PaywallScreenState extends State<PaywallScreen> implements AdaptyUIPaywallsEventsObserver { @override void initState() { super.initState(); // Register this class as the paywalls event observer AdaptyUI().setPaywallsEventsObserver(this); _showPaywallIfNeeded(); } Future<void> _showPaywallIfNeeded() async { try { final paywall = await Adapty().getPaywall( placementId: 'YOUR_PLACEMENT_ID', ); if (!paywall.hasViewConfiguration) return; final view = await AdaptyUI().createPaywallView(paywall: paywall); await view.present(); } catch (_) { // Handle any errors (network, SDK issues, etc.) } } // This method is called when user performs an action on the paywall UI @override void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action) { switch (action) { case const CloseAction(): case const AndroidSystemBackAction(): view.dismiss(); break; case OpenUrlAction(url: final url): // Open the URL using url_launcher package _launchUrl(url); break; } } // Helper method to launch URLs Future<void> _launchUrl(String url) async { try { final Uri uri = Uri.parse(url); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } else { // Handle case where URL cannot be launched print('Could not launch $url'); } } catch (e) { // Handle any errors print('Error launching URL: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Adapty Paywall Example')), body: Center( // Add a button to re-trigger the paywall for testing purposes child: ElevatedButton( onPressed: _showPaywallIfNeeded, child: Text('Show Paywall'), ), ), ); } } ``` --- # File: flutter-check-subscription-status --- --- title: "Verificar el estado de la suscripción en el SDK de Flutter" description: "Aprende cómo verificar el estado de la suscripción en tu app de Flutter con Adapty." --- Para decidir si los usuarios pueden acceder al contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil. Este artículo te muestra cómo acceder al estado del perfil para decidir qué deben ver los usuarios: si mostrarles un paywall o darles acceso a las funciones de pago. ## Obtener el estado de la suscripción \{#get-subscription-status\} Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones: - Llama a `getProfile` si necesitas los datos más recientes del perfil de inmediato (por ejemplo, al lanzar la app) o quieres forzar una actualización. - Configura **actualizaciones automáticas del perfil** para mantener una copia local que se actualice automáticamente cada vez que cambie el estado de la suscripción. ### Obtener el perfil \{#get-profile\} La forma más sencilla de obtener el estado de la suscripción es usar el método `getProfile` para acceder al perfil: ```javascript showLineNumbers try { final profile = await Adapty().getProfile(); // check the access } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` ### Escuchar actualizaciones de la suscripción \{#listen-to-subscription-updates\} Para recibir actualizaciones del perfil automáticamente en tu app: 1. Usa `Adapty().didUpdateProfileStream.listen()` para escuchar los cambios del perfil: Adapty llamará a este método automáticamente cada vez que cambie el estado de la suscripción del usuario. 2. Guarda los datos del perfil actualizado cuando se invoque este método, para poder usarlos en toda tu app sin realizar peticiones de red adicionales. ```dart class SubscriptionManager { AdaptyProfile? _currentProfile; SubscriptionManager() { // Listen for profile updates Adapty().didUpdateProfileStream.listen((profile) { _currentProfile = profile; // Update UI, unlock content, etc. }); } // Use stored profile instead of calling getProfile() bool hasAccess() { return _currentProfile?.accessLevels['premium']?.isActive ?? false; } } ``` :::note Adapty llama automáticamente al listener del stream de actualización del perfil cuando se inicia tu app, proporcionando datos de suscripción en caché incluso si el dispositivo está sin conexión. ::: ## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\} Cuando necesitas tomar decisiones inmediatas sobre si mostrar paywalls o dar acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en situaciones como el lanzamiento de la app, al acceder a secciones premium o antes de mostrar contenido específico. ```dart Future<bool> _checkAccessLevel() async { try { final profile = await Adapty().getProfile(); return profile.accessLevels['YOUR_ACCESS_LEVEL']?.isActive ?? false; } catch (e) { print('Error checking access level: $e'); return false; // Show paywall if access check fails } } Future<void> _initializePaywall() async { await _loadPaywall(); final hasAccess = await _checkAccessLevel(); if (!hasAccess) { // Show paywall if no access } } ``` ## Próximos pasos \{#next-steps\} Ahora que sabes cómo hacer seguimiento del estado de la suscripción, aprende a [trabajar con perfiles de usuario](flutter-quickstart-identify) para asegurarte de que pueden acceder a lo que han pagado. --- # File: flutter-quickstart-identify --- --- title: "Identificar usuarios en el SDK de Flutter" description: "Guía de inicio rápido para configurar Adapty para la gestión de suscripciones in-app en Flutter." --- :::important Esta guía es para ti si tienes tu propio sistema de autenticación. Aquí aprenderás a trabajar con perfiles de usuario en Adapty para que se alinee con tu sistema de autenticación existente. ::: La forma en que gestionas las compras de los usuarios depende del modelo de autenticación de tu app: - Si tu app no usa autenticación de backend y no almacena datos de usuario, consulta la [sección sobre usuarios anónimos](#anonymous-users). - Si tu app tiene (o tendrá) autenticación de backend, consulta la [sección sobre usuarios identificados](#identified-users). **Conceptos clave**: - Los **perfiles** son las entidades necesarias para que funcione el SDK. Adapty los crea automáticamente. - Pueden ser anónimos **(sin customer user ID)** o identificados **(con customer user ID)**. - Tú proporcionas el **customer user ID** para relacionar los perfiles de Adapty con tu sistema de autenticación interno. Esto es lo que diferencia a los usuarios anónimos de los identificados: | | Usuarios anónimos | Usuarios identificados | |------------------------------|-------------------------------------------------------------|----------------------------------------------------------------------------------| | **Gestión de compras** | Restauración de compras a nivel de store | Mantienen el historial de compras en todos los dispositivos mediante su customer user ID | | **Gestión de perfiles** | Nuevo perfil en cada reinstalación | El mismo perfil en todas las sesiones y dispositivos | | **Persistencia de datos** | Los datos de usuarios anónimos están vinculados a la instalación | Los datos de usuarios identificados persisten entre instalaciones | ## Usuarios anónimos \{#anonymous-users\} Si no tienes autenticación de backend, **no necesitas gestionar la autenticación en el código de la app**: 1. Cuando se activa el SDK en el primer lanzamiento de la app, Adapty **crea un nuevo perfil para el usuario**. 2. Cuando el usuario realiza una compra en la app, esta compra se **asocia a su perfil de Adapty y a su cuenta del store**. 3. Cuando el usuario **reinstala** la app o la instala en un **dispositivo nuevo**, Adapty **crea un nuevo perfil anónimo al activarse**. 4. Si el usuario había realizado compras anteriormente en tu app, de forma predeterminada sus compras se sincronizan automáticamente desde el App Store al activarse el SDK. Con usuarios anónimos se crearán nuevos perfiles en cada instalación, pero no es un problema porque en los análisis de Adapty puedes [configurar qué se considerará una nueva instalación](general#4-installs-definition-for-analytics). Para usuarios anónimos, debes contar las instalaciones por **ID de dispositivo**. En este caso, cada instalación de la app en un dispositivo se cuenta como una instalación, incluidas las reinstalaciones. ## Usuarios identificados \{#identified-users\} Tienes dos opciones para identificar usuarios en la app: - [**Durante el login/registro:**](#during-loginsignup) Si los usuarios inician sesión después de que se inicia tu app, llama a `identify()` con un customer user ID cuando se autentiquen. - [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando se inicia la app, envíalo al llamar a `activate()`. :::important De forma predeterminada, cuando Adapty recibe una compra de un Customer User ID que está actualmente asociado a otro Customer User ID, el nivel de acceso se comparte, de modo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o desactivar el uso compartido por completo. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles. ::: <img src="/assets/shared/img/identify-diagram.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Durante el login/registro \{#during-loginsignup\} Si identificas a los usuarios después del lanzamiento de la app (por ejemplo, después de que inicien sesión o se registren), usa el método `identify` para establecer su customer user ID. - Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual. - Si **ya has usado este customer user ID para identificar al usuario**, Adapty cambiará al perfil asociado a ese customer user ID. :::important Los customer user IDs deben ser únicos para cada usuario. Si defines el valor del parámetro de forma fija en el código, todos los usuarios se considerarán como uno solo. ::: Siempre usa `await` con `identify` antes de llamar a otros métodos del SDK. Las llamadas concurrentes generan `#3006 profileWasChanged` o se aplican al perfil anónimo. Consulta [Orden de llamadas en el SDK de Flutter](flutter-sdk-call-order). ```dart showLineNumbers try { await Adapty().identify(customerUserId); // Unique for each user } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` ### Durante la activación del SDK \{#during-the-sdk-activation\} Si ya conoces el customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado. Si conoces el customer user ID pero lo defines solo después de la activación, Adapty creará un nuevo perfil anónimo al activarse y cambiará al existente solo después de que llames a `identify`. Puedes pasar un customer user ID existente (uno que hayas usado antes) o uno nuevo. Si pasas uno nuevo, el perfil creado al activarse se vinculará automáticamente al customer user ID. :::note De forma predeterminada, la creación de perfiles anónimos no afecta a los dashboards de análisis, porque las instalaciones se cuentan según los IDs de dispositivo. Un ID de dispositivo representa una única instalación de la app desde el store en un dispositivo y solo se regenera después de reinstalar la app. No depende de si es una primera o posterior instalación, ni de si se usa un customer user ID existente. Crear un perfil (al activar el SDK o al cerrar sesión), iniciar sesión o actualizar la app sin reinstalarla no genera eventos de instalación adicionales. Si quieres contar las instalaciones por usuarios únicos en lugar de por dispositivos, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics). ::: ```dart showLineNumbers" try { await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_API_KEY') ..withCustomerUserId(YOUR_CUSTOMER_USER_ID) // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one. ); } catch (e) { // handle the error } ``` ### Cerrar sesión de usuarios \{#log-users-out\} Si tienes un botón para cerrar sesión de los usuarios, usa el método `logout`. :::important Cerrar la sesión de un usuario crea un nuevo perfil anónimo para ese usuario. ::: ```dart showLineNumbers try { await Adapty().logout(); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle unknown error } ``` :::info Para volver a iniciar sesión en la app, usa el método `identify`. ::: ### Permitir compras sin inicio de sesión \{#allow-purchases-without-login\} Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, debes asegurarte de que conserven el acceso después de iniciar sesión: 1. Cuando un usuario sin sesión iniciada realiza una compra, Adapty la vincula a su ID de perfil anónimo. 2. Cuando el usuario inicia sesión en su cuenta, Adapty cambia a trabajar con su perfil identificado. - Si es un nuevo customer user ID (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que se mantiene todo el historial de compras. - Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), debes obtener el nivel de acceso actual después del cambio de perfil. Puedes llamar a [`getProfile`](flutter-check-subscription-status) justo después de la identificación, o [escuchar las actualizaciones del perfil](flutter-check-subscription-status) para que los datos se sincronicen automáticamente. ## Siguientes pasos \{#next-steps\} ¡Enhorabuena! Has implementado la lógica de pagos in-app en tu app. ¡Te deseamos mucho éxito con la monetización de tu app! Para sacar aún más partido a Adapty, puedes explorar estos temas: - [**Pruebas**](troubleshooting-test-purchases): Verifica que todo funciona como se espera - [**Onboardings**](flutter-onboardings): Engancha a los usuarios con onboardings y mejora la retención - [**Integraciones**](configuration): Integra con servicios de atribución de marketing y análisis con una sola línea de código - [**Establecer atributos de perfil personalizados**](flutter-setting-user-attributes): Añade atributos personalizados a los perfiles de usuario y crea segmentos para lanzar pruebas A/B o mostrar diferentes paywalls a distintos usuarios --- # File: adapty-cursor-flutter --- --- title: "Integra Adapty en tu app de Flutter con asistencia de IA" description: "Una guía paso a paso para integrar Adapty en tu app de Flutter usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA." --- Esta página explica dos formas de integrar Adapty en tu app de Flutter. Usa la habilidad de integración del SDK que se describe a continuación para un flujo automatizado de extremo a extremo, o sigue el recorrido manual más adelante. ## Usa la habilidad de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\} La [habilidad adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza la integración de principio a fin: configuración del dashboard, instalación del SDK, paywall y verificación por etapas. El recorrido manual que aparece más abajo es la alternativa si tu herramienta no admite el formato Claude Skills. **Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI. ### Instalación \{#install\} Elige el formato para tu herramienta. La lista completa está en el [README de la habilidad](https://github.com/adaptyteam/adapty-sdk-integration-skill). - **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y después `claude plugin install adapty-sdk-integration@adapty` desde tu shell. - **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`. - **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`. - **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de habilidades de tu herramienta. ### Ejecución \{#run\} Desde tu proyecto, ejecuta `/adapty-sdk-integration`. La habilidad detecta tu plataforma y hace algunas preguntas de configuración. Luego recorre la configuración del dashboard, la instalación del SDK, el paywall y la verificación, consultando la documentación de Adapty en cada etapa. :::note La habilidad está en beta. Si se detiene o se comporta de forma inesperada, el recorrido manual que aparece a continuación cubre cada etapa paso a paso. ::: ## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\} Adapty requiere cierta configuración en el dashboard antes de escribir código del SDK. Puedes hacerlo con una habilidad de LLM interactiva o manualmente desde el Dashboard. ### Enfoque con habilidad (recomendado) \{#skill-approach-recommended\} La habilidad CLI de Adapty permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin necesidad de abrir el Dashboard en cada paso. Solo necesitas [conectar tus stores](integrate-payments) desde el Dashboard. ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` Una vez añadida la habilidad, ejecuta `/adapty-cli` en tu agente. Te guiará por cada paso, incluido cuándo abrir el Dashboard para conectar tus stores. ### Enfoque manual desde el dashboard \{#dashboard-approach\} Si prefieres configurarlo todo manualmente, esto es lo que necesitas antes de escribir código. Tu LLM no puede buscar los valores del dashboard por ti, así que tendrás que proporcionarlos. 1. **Conecta tus stores**: En el Adapty Dashboard, ve a **App settings → General**. Conecta App Store y Google Play si tu app de Flutter se dirige a ambas plataformas. Esto es obligatorio para que las compras funcionen. [Conectar stores](integrate-payments) 2. **Copia tu clave SDK pública**: En el Adapty Dashboard, ve a **App settings → General** y localiza la sección **API keys**. En el código, esta es la cadena que pasas a la configuración de Adapty. 3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No referenciarás los productos directamente en el código: Adapty los entrega a través de los paywalls. [Añadir productos](quickstart-products) 4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `Adapty().getPaywall()`. [Crear paywall](quickstart-paywalls) 5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena que se comprueba es `profile.accessLevels['premium']?.isActive`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago acceden a distintas funciones según el producto (por ejemplo, un plan `basic` frente a uno `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar. :::tip Una vez que tengas los cinco, estás listo para escribir código. Dile a tu LLM: "Mi clave SDK pública es X, mi ID de placement es Y" para que pueda generar el código correcto de inicialización y obtención del paywall. ::: ### Configura cuando estés listo \{#set-up-when-ready\} Estos pasos no son necesarios para empezar a programar, pero los querrás a medida que tu integración madure: - **Pruebas A/B**: Configúralas en la página **Placements**. No requieren cambios en el código. [Pruebas A/B](ab-tests) - **Paywalls y placements adicionales**: Añade más llamadas a `getPaywall` con distintos IDs de placement. - **Integraciones de analíticas**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta [integraciones de analíticas](analytics-integration) e [integraciones de atribución](attribution-integration). ## Proporciona documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\} ### Usa Context7 (recomendado) \{#use-context7-recommended\} [Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. El LLM obtiene automáticamente la documentación adecuada según lo que preguntes, sin necesidad de pegar URLs manualmente. Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta: ``` npx ctx7 setup ``` Esto detecta tu editor y configura el servidor Context7. Para la configuración manual, consulta el [repositorio GitHub de Context7](https://github.com/upstash/context7). Una vez configurado, referencia la biblioteca de Adapty en tus prompts: ``` Use the adaptyteam/adapty-docs library to look up how to install the Flutter SDK ``` :::warning Aunque Context7 elimina la necesidad de pegar enlaces de documentación manualmente, el orden de implementación importa. Sigue el [recorrido de implementación](#implementation-walkthrough) paso a paso para asegurarte de que todo funciona correctamente. ::: ### Usa documentación en texto plano \{#use-plain-text-docs\} Puedes acceder a cualquier documento de Adapty como Markdown en texto plano. Añade `.md` al final de su URL o haz clic en **Copy for LLM** debajo del título del artículo. Por ejemplo: [adapty-cursor-flutter.md](https://adapty.io/docs/es/adapty-cursor-flutter.md). Cada etapa del [recorrido de implementación](#implementation-walkthrough) que aparece a continuación incluye un bloque "Envía esto a tu LLM" con enlaces `.md` listos para pegar. Para obtener más documentación a la vez, consulta los [archivos de índice y subconjuntos por plataforma](#plain-text-doc-index-files) que aparecen más abajo. ## Recorrido de implementación \{#implementation-walkthrough\} El resto de esta guía recorre la integración de Adapty en el orden de implementación recomendado. Cada etapa incluye la documentación que debes enviar a tu LLM, qué deberías ver al terminar y los problemas más comunes. ### Planifica tu integración \{#plan-your-integration\} Antes de escribir código, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA admite un modo de planificación (como el modo plan de Cursor o Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir código. Dile a tu LLM qué enfoque usas para las compras, ya que esto afecta a las guías que debe seguir: - [**Adapty Paywall Builder**](adapty-paywall-builder): Creas los paywalls en el editor no-code de Adapty y el SDK los renderiza automáticamente. - [**Paywalls creados manualmente**](flutter-making-purchases): Construyes tu propia interfaz de paywall en código, pero sigues usando Adapty para obtener productos y gestionar compras. - [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analíticas e integraciones. ¿No sabes cuál elegir? Lee la [tabla comparativa en la guía de inicio rápido](flutter-quickstart-paywalls). ### Instala y configura el SDK \{#install-and-configure-the-sdk\} Añade la dependencia del SDK de Adapty usando `flutter pub add` y actívalo con tu clave SDK pública. Esta es la base: nada más funciona sin ella. **Guía:** [Instalar y configurar el SDK de Adapty](sdk-installation-flutter) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/sdk-installation-flutter.md ``` :::tip[Punto de control] - **Esperado:** La app se compila y ejecuta en iOS y Android. La consola de depuración muestra el log de activación de Adapty. - **Problema frecuente:** "Public API key is missing" → comprueba que reemplazaste el marcador de posición con tu clave real desde App settings. ::: ### Muestra paywalls y gestiona compras \{#show-paywalls-and-handle-purchases\} Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras. Prueba cada compra en el sandbox a medida que avanzas; no esperes hasta el final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración. <Tabs groupId="paywall-approach"> <TabItem value="builder" label="Paywall Builder" default> **Guías:** - [Habilitar compras con paywalls (inicio rápido)](flutter-quickstart-paywalls) - [Obtener paywalls del Paywall Builder y su configuración](flutter-get-pb-paywalls) - [Mostrar paywalls](flutter-present-paywalls) - [Gestionar eventos de paywall](flutter-handling-events) - [Responder a acciones de botones](flutter-handle-paywall-actions) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/flutter-quickstart-paywalls.md - https://adapty.io/docs/es/flutter-get-pb-paywalls.md - https://adapty.io/docs/es/flutter-present-paywalls.md - https://adapty.io/docs/es/flutter-handling-events.md - https://adapty.io/docs/es/flutter-handle-paywall-actions.md ``` :::tip[Punto de control] - **Esperado:** El paywall aparece con tus productos configurados. Al pulsar un producto se activa el diálogo de compra en sandbox. - **Problema frecuente:** Paywall vacío o error en `getPaywall` → verifica que el ID de placement coincide exactamente con el del dashboard y que el placement tiene una audiencia asignada. ::: </TabItem> <TabItem value="manual" label="Manual paywalls"> **Guías:** - [Habilitar compras en tu paywall personalizado (inicio rápido)](flutter-quickstart-manual) - [Obtener paywalls y productos](fetch-paywalls-and-products-flutter) - [Renderizar el paywall diseñado con Remote Config](present-remote-config-paywalls-flutter) - [Realizar compras](flutter-making-purchases) - [Restaurar compras](flutter-restore-purchase) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/flutter-quickstart-manual.md - https://adapty.io/docs/es/fetch-paywalls-and-products-flutter.md - https://adapty.io/docs/es/present-remote-config-paywalls-flutter.md - https://adapty.io/docs/es/flutter-making-purchases.md - https://adapty.io/docs/es/flutter-restore-purchase.md ``` :::tip[Punto de control] - **Esperado:** Tu paywall personalizado muestra los productos obtenidos desde Adapty. Al pulsar un producto se activa el diálogo de compra en sandbox. - **Problema frecuente:** Array de productos vacío → verifica que el paywall tiene productos asignados en el dashboard y que el placement tiene una audiencia. ::: </TabItem> <TabItem value="observer" label="Observer mode"> **Guías:** - [Descripción general del modo Observer](observer-vs-full-mode) - [Implementar el modo Observer](implement-observer-mode-flutter) - [Reportar transacciones en el modo Observer](report-transactions-observer-mode-flutter) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/observer-vs-full-mode.md - https://adapty.io/docs/es/implement-observer-mode-flutter.md - https://adapty.io/docs/es/report-transactions-observer-mode-flutter.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox con tu flujo de compras existente, la transacción aparece en el **Event Feed** del dashboard de Adapty. - **Problema frecuente:** Sin eventos → verifica que estás reportando las transacciones a Adapty y que las notificaciones del servidor están configuradas para ambos stores. ::: </TabItem> </Tabs> ### Comprueba el estado de la suscripción \{#check-subscription-status\} Tras una compra, comprueba el perfil del usuario para verificar un nivel de acceso activo y restringir el contenido premium. **Guía:** [Comprobar el estado de la suscripción](flutter-check-subscription-status) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/flutter-check-subscription-status.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox, `profile.accessLevels['premium']?.isActive` devuelve `true`. - **Problema frecuente:** `accessLevels` vacío después de la compra → comprueba que el producto tiene un nivel de acceso asignado en el dashboard. ::: ### Identifica a los usuarios \{#identify-users\} Vincula las cuentas de usuario de tu app con los perfiles de Adapty para que las compras persistan entre dispositivos. :::important Omite este paso si tu app no tiene autenticación. ::: **Guía:** [Identificar usuarios](flutter-quickstart-identify) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/flutter-quickstart-identify.md ``` :::tip[Punto de control] - **Esperado:** Tras llamar a `Adapty().identify()`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado. - **Problema frecuente:** Llama a `identify` después de la activación pero antes de obtener los paywalls para evitar problemas de atribución en perfiles anónimos. ::: ### Prepárate para el lanzamiento \{#prepare-for-release\} Una vez que tu integración funcione en el sandbox, repasa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción. **Guía:** [Lista de verificación de lanzamiento](release-checklist) Envía esto a tu LLM: ``` Read these Adapty docs before releasing: - https://adapty.io/docs/es/release-checklist.md ``` :::tip[Punto de control] - **Esperado:** Todos los elementos de la lista confirmados: conexiones de stores, notificaciones del servidor, flujo de compras, comprobaciones de nivel de acceso y requisitos de privacidad. - **Problema frecuente:** Notificaciones del servidor ausentes → configura las App Store Server Notifications en **App settings → iOS SDK** y las Google Play Real-Time Developer Notifications en **App settings → Android SDK**. ::: ## Archivos de índice de documentación en texto plano \{#plain-text-doc-index-files\} Si necesitas dar a tu LLM un contexto más amplio más allá de páginas individuales, ofrecemos archivos de índice que listan o combinan toda la documentación de Adapty: - [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con enlaces `.md`. Es un [estándar emergente](https://llmstxt.org/) para hacer los sitios web accesibles a los LLMs. Ten en cuenta que para algunos agentes de IA (p. ej., ChatGPT) necesitarás descargar `llms.txt` y subirlo al chat como archivo. - [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación del sitio de Adapty combinada en un único archivo. Es muy grande; úsalo solo cuando necesites el panorama completo. - Subconjuntos específicos de Flutter: [`flutter-llms.txt`](https://adapty.io/docs/es/flutter-llms.txt) y [`flutter-llms-full.txt`](https://adapty.io/docs/es/flutter-llms-full.txt), que ahorran tokens en comparación con el sitio completo. --- # File: flutter-get-pb-paywalls --- --- title: "Obtener paywalls de Paywall Builder y su configuración en Flutter SDK" description: "Aprende cómo recuperar paywalls de PB en Adapty para un mejor control de suscripciones en Flutter." --- Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación. :::warning El nuevo Paywall Builder requiere Flutter SDK versión 3.3.0 o superior. ::: Ten en cuenta que este tema hace referencia a paywalls personalizados con Paywall Builder. Si implementas tus paywalls de forma manual, consulta el tema [Obtener paywalls y productos para paywalls con Remote Config en tu app móvil](fetch-paywalls-and-products-flutter). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a mostrar paywalls en tu app móvil (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en ellos](create-placement) en el Adapty Dashboard. 4. Instala el [SDK de Adapty](sdk-installation-flutter) en tu app móvil. </details> ## Obtener el paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\} Si has [diseñado un paywall con el Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese paywall contiene tanto qué mostrar como cómo mostrarlo. Aun así, necesitas obtener su ID a través del placement, su configuración de vista y luego presentarlo en tu app móvil. Para garantizar un rendimiento óptimo, es fundamental obtener el paywall y su [configuración de vista](flutter-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dejando tiempo suficiente para que las imágenes se descarguen antes de mostrárselas al usuario. Para obtener un paywall, usa el método `getPaywall`: ```dart showLineNumbers try { final paywall = await Adapty().getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en"); // the requested paywall } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | requerido | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de idioma](flutter-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar peticiones de red.</p><p></p><p>Ten en cuenta que la caché se mantiene al reiniciar la app y solo se borra al desinstalarla o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls localmente en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que la CDN no esté disponible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local.</p><p>Ten en cuenta que en casos excepcionales este método puede exceder ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por distintas peticiones internamente.</p><p>Para Android: puedes crear `TimeInterval` con funciones de extensión (como `5.seconds`, donde `.seconds` es de `import com.adapty.utils.seconds`), o `TimeInterval.seconds(5)`. Para no establecer límite, usa `TimeInterval.INFINITE`.</p> | Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html) con una lista de IDs de productos, el identificador del paywall, el Remote Config y varias otras propiedades. | ## Obtener la configuración de vista del paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important Asegúrate de activar el toggle **Show on device** en el Paywall Builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperar. ::: Después de obtener el paywall, comprueba si incluye una `ViewConfiguration`, lo que indica que fue creado con el Paywall Builder. Esto te indicará cómo mostrar el paywall. Si la `ViewConfiguration` está presente, trátalo como un paywall de Paywall Builder; si no, [manéjalo como un paywall de Remote Config](present-remote-config-paywalls-flutter). ```dart showLineNumbers try { final view = await AdaptyUI().createPaywallView( paywall: paywall, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` Una vez que tengas la vista, [presenta el paywall](flutter-present-paywalls). ## Obtener un paywall para la audiencia por defecto y cargarlo más rápido \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} Normalmente, los paywalls se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, si tienes muchas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseado. En esos casos, puede que quieras mostrar un paywall por defecto para garantizar una experiencia de usuario fluida en lugar de no mostrar ninguno. Para resolver esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener el paywall diseñado con Paywall Builder](flutter-get-pb-paywalls#fetch-paywall-designed-with-paywall-builder) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls distintos para diferentes versiones de la app (actual y futuras), puedes encontrar dificultades. Tendrás que diseñar paywalls que soporten la versión actual (legacy) o asumir que los usuarios con esa versión podrían encontrar problemas con paywalls que no se renderizan. - **Pérdida de targeting**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes el targeting personalizado (incluido el basado en países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a asumir estos inconvenientes para beneficiarte de una carga más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, usa `getPaywall` descrito [arriba](#fetch-paywall-designed-with-paywall-builder). ::: ```dart showLineNumbers try { final paywall = await Adapty().getPaywallForDefaultAudience(placementId: 'YOUR_PLACEMENT_ID'); } on AdaptyError catch (adaptyError) { // handle error } catch (e) { // handle unknown error } ``` :::note El método `getPaywallForDefaultAudience` está disponible a partir de la versión 3.2.0 del Flutter SDK. ::: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | requerido | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar peticiones de red.</p><p></p><p>Ten en cuenta que la caché se mantiene al reiniciar la app y solo se borra al desinstalarla o mediante una limpieza manual.</p> | ## Personalizar recursos \{#customize-assets\} Para personalizar imágenes y vídeos en tu paywall, implementa los recursos personalizados. Las imágenes y vídeos principales tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de recursos personalizados, apuntas a estos elementos por sus IDs y personalizas su comportamiento. Para otras imágenes y vídeos, necesitas [establecer un ID personalizado](custom-media) en el dashboard de Adapty. Por ejemplo, puedes: - Mostrar una imagen o vídeo diferente a algunos usuarios. - Mostrar una imagen de vista previa local mientras se carga la imagen principal remota. - Mostrar una imagen de vista previa antes de reproducir un vídeo. :::important Para usar esta funcionalidad, actualiza el Flutter SDK de Adapty a la versión 3.8.0 o superior. ::: Aquí tienes un ejemplo de cómo proporcionar recursos personalizados mediante un diccionario simple: ```dart final customAssets = { // Show a local image using a custom ID 'custom_image': AdaptyCustomAsset.localImageAsset( assetId: 'assets/images/image_name.png', ), // Show a local video with a preview image 'hero_video': AdaptyCustomAsset.localVideoAsset( assetId: 'assets/videos/custom_video.mp4', ), }; try { final view = await AdaptyUI().createPaywallView( paywall: paywall, customAssets: <CUSTOM_ASSETS>, preloadProducts: preloadProducts, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` :::note Si no se encuentra un recurso, el paywall usará su apariencia por defecto. ::: ## Configurar temporizadores definidos por el desarrollador \{#set-up-developer-defined-timers\} Para usar temporizadores personalizados en tu app móvil, crea un objeto que siga el protocolo `AdaptyTimerResolver`. Este objeto define cómo debe renderizarse cada temporizador personalizado. Si lo prefieres, puedes usar directamente un diccionario `[String: Date]`, ya que ya cumple con este protocolo. Aquí tienes un ejemplo: ```dart showLineNumbers try { final view = await AdaptyUI().createPaywallView( paywall: paywall, customTimers: { 'CUSTOM_TIMER_6H': DateTime.now().add(const Duration(seconds: 3600 * 6)), 'CUSTOM_TIMER_NY': DateTime(2025, 1, 1), // New Year 2025 }, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` En este ejemplo, `CUSTOM_TIMER_NY` y `CUSTOM_TIMER_6H` son los **Timer ID** de los temporizadores definidos por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente cada temporizador con el valor correcto. Por ejemplo: - `CUSTOM_TIMER_NY`: El tiempo restante hasta el fin del temporizador, como el día de Año Nuevo. - `CUSTOM_TIMER_6H`: El tiempo restante en un período de 6 horas que comenzó cuando el usuario abrió el paywall. --- # File: flutter-present-paywalls --- --- title: "Flutter - Presenta paywalls del nuevo Paywall Builder" description: "Presenta paywalls en apps de Flutter usando las funciones de monetización de Adapty." --- Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu app móvil para mostrárselo al usuario. Ese paywall contiene tanto lo que se debe mostrar como la forma en que se debe mostrar. :::warning Esta guía es exclusivamente para **paywalls del nuevo Paywall Builder**, que requieren SDK v3.2.0 o posterior. El proceso para presentar paywalls varía según la versión del Paywall Builder con la que se hayan diseñado y para los paywalls de Remote Config. - Para presentar **paywalls de Remote Config**, consulta [Renderizar un paywall diseñado con Remote Config](present-remote-config-paywalls). ::: El SDK de Adapty para Flutter ofrece dos formas de presentar paywalls: - **Pantalla independiente** - **Widget embebido** ## Presentar como pantalla independiente \{#present-as-standalone-screen\} Para mostrar un paywall como pantalla independiente, usa el método `view.present()` en el `view` creado por el método [`createPaywallView`](flutter-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `createPaywallView` otra vez para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo puede generar un error `AdaptyUIError.viewAlreadyPresented`. ::: ```dart showLineNumbers title="Flutter" try { await view.present(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Cerrar el paywall \{#dismiss-the-paywall\} Cuando necesites cerrar el paywall mediante código, usa el método `dismiss()`: ```dart showLineNumbers title="Flutter" try { await view.dismiss(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ### Mostrar diálogo \{#show-dialog\} Usa este método en lugar de los diálogos de alerta nativos cuando haya un paywall presentado en Android. En Android, las alertas normales aparecen detrás del paywall y, por tanto, son invisibles para el usuario. Este método garantiza que el diálogo se muestre correctamente encima del paywall en todas las plataformas. ```dart showLineNumbers title="Flutter" try { final action = await view.showDialog( title: 'Close paywall?', content: 'You will lose access to exclusive offers.', primaryActionTitle: 'Stay', secondaryActionTitle: 'Close', ); if (action == AdaptyUIDialogActionType.secondary) { // User confirmed - close the paywall await view.dismiss(); } // If primary - do nothing, user stays } catch (e) { // handle error } ``` ### Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el paywall en iOS pasando el parámetro `iosPresentationStyle` al método `present()`. El parámetro acepta los valores `AdaptyUIIOSPresentationStyle.fullScreen` (predeterminado) o `AdaptyUIIOSPresentationStyle.pageSheet`. ```dart showLineNumbers try { await view.present(iosPresentationStyle: AdaptyUIIOSPresentationStyle.pageSheet); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ## Embeber en la jerarquía de widgets \{#embed-in-widget-hierarchy\} Para embeber un paywall dentro de tu árbol de widgets existente, usa el widget `AdaptyUIPaywallPlatformView` directamente en la jerarquía de widgets de Flutter. ```dart showLineNumbers title="Flutter" AdaptyUIPaywallPlatformView( paywall: paywall, // The paywall object you fetched onDidAppear: (view) { }, onDidDisappear: (view) { }, onDidPerformAction: (view, action) { }, onDidSelectProduct: (view, productId) { }, onDidStartPurchase: (view, product) { }, onDidFinishPurchase: (view, product, purchaseResult) { }, onDidFailPurchase: (view, product, error) { }, onDidStartRestore: (view) { }, onDidFinishRestore: (view, profile) { }, onDidFailRestore: (view, error) { }, onDidFailRendering: (view, error) { }, onDidFailLoadingProducts: (view, error) { }, onDidFinishWebPaymentNavigation: (view, product, error) { }, ) ``` :::note Para que la vista de plataforma en Android funcione, asegúrate de que tu `MainActivity` extienda `FlutterFragmentActivity`: ```kotlin showLineNumbers title="Kotlin" class MainActivity : FlutterFragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } } ``` ::: --- # File: flutter-handle-paywall-actions --- --- title: "Responder a las acciones de botones en el SDK de Flutter" description: "Gestiona las acciones de botones en paywalls de Flutter con Adapty para mejorar la monetización de tu app." --- Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente: 1. Añade un [botón en el Paywall Builder](paywall-buttons) y asígnale una acción predefinida o crea un ID de acción personalizado. 2. Escribe código en tu app para gestionar cada acción que hayas asignado. Esta guía muestra cómo gestionar acciones personalizadas y predefinidas en tu código. :::warning **Solo las compras y restauraciones se gestionan automáticamente.** El resto de acciones de botones, como cerrar paywalls o abrir enlaces, requieren implementar las respuestas correspondientes en el código de la app. ::: ## Cerrar paywalls \{#close-paywalls\} Para añadir un botón que cierre tu paywall: 1. En el Paywall Builder, añade un botón y asígnale la acción **Close**. 2. En el código de tu app, implementa un manejador para las acciones `CloseAction` y `AndroidSystemBackAction`. :::info En el SDK de Flutter, las acciones `CloseAction` y `AndroidSystemBackAction` cierran el paywall por defecto. Sin embargo, puedes sobrescribir este comportamiento en tu código si lo necesitas. Por ejemplo, cerrar un paywall podría disparar la apertura de otro. ::: ```dart void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action) { switch (action) { case const CloseAction(): case const AndroidSystemBackAction(): view.dismiss(); break; default: break; } } ``` ## Abrir URLs desde paywalls \{#open-urls-from-paywalls\} :::tip Si quieres añadir un grupo de enlaces (por ejemplo, términos de uso y restauración de compras), añade un elemento **Link** en el Paywall Builder y gestiónalo de la misma manera que los botones con la acción **Open URL**. ::: Para añadir un botón que abra un enlace desde tu paywall (por ejemplo, **Terms of use** o **Privacy policy**): 1. En el Paywall Builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir. 2. En el código de tu app, implementa un manejador para la acción `openUrl` que abra la URL recibida en un navegador. ```dart // You have to install url_launcher plugin in order to handle urls: // https://pub.dev/packages/url_launcher void paywallViewDidPerformAction(AdaptyUIView view, AdaptyUIAction action) { switch (action) { case OpenUrlAction(url: final url): final Uri uri = Uri.parse(url); launchUrl(uri, mode: LaunchMode.inAppBrowserView); break; default: break; } } ``` ## Iniciar sesión en la app \{#log-into-the-app\} Para añadir un botón que permita a los usuarios iniciar sesión en tu app: 1. En el Paywall Builder, añade un botón y asígnale la acción **Login**. 2. En el código de tu app, implementa un manejador para la acción `login` que identifique a tu usuario. ```dart void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action) { switch (action) { case CustomAction(action: 'login'): // Handle login action Navigator.of(context).push(MaterialPageRoute(builder: (context) => LoginScreen())); break; default: break; } } ``` ## Gestionar acciones personalizadas \{#handle-custom-actions\} Para añadir un botón que gestione cualquier otra acción: 1. En el Paywall Builder, añade un botón, asígnale la acción **Custom** y asígnale un ID. 2. En el código de tu app, implementa un manejador para el ID de acción que hayas creado. Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall: ```dart void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action) { switch (action) { case CustomAction(action: 'openNewPaywall'): // Display another paywall break; default: break; } } ``` --- # File: flutter-handling-events --- --- title: "Flutter - Gestionar eventos del paywall" description: "Descubre cómo gestionar eventos relacionados con suscripciones en Flutter usando Adapty para rastrear las interacciones de los usuarios de manera efectiva." --- :::important Esta guía cubre el manejo de eventos para compras, restauraciones, selección de productos y renderizado de paywalls. También debes implementar el manejo de botones (cerrar paywall, abrir enlaces, etc.). Consulta nuestra [guía sobre el manejo de acciones de botones](flutter-handle-paywall-actions) para más detalles. ::: Los paywalls configurados con el [Paywall Builder](adapty-paywall-builder-legacy) no necesitan código adicional para realizar ni restaurar compras. Sin embargo, generan ciertos eventos a los que tu app puede responder. Esos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.), así como notificaciones sobre acciones relacionadas con compras realizadas en el paywall. A continuación aprenderás cómo responder a estos eventos. :::warning Esta guía es exclusivamente para **paywalls del nuevo Paywall Builder**, que requieren el SDK de Adapty v3.0 o posterior. ::: Para controlar o monitorizar los procesos que ocurren en la pantalla del paywall dentro de tu app, implementa los métodos de `AdaptyUIPaywallsEventsObserver` y establece el observer antes de presentar cualquier pantalla: ```javascript showLineNumbers title="Flutter" AdaptyUI().setPaywallsEventsObserver(this); ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Eventos generados por el usuario \{#user-generated-events\} #### Paywall mostrado \{#paywall-appeared\} Este método se invoca cuando la vista del paywall aparece en pantalla. :::note En iOS, también se invoca cuando el usuario pulsa el [botón de web paywall](web-paywall#step-2a-add-a-web-purchase-button) dentro de un paywall y se abre un web paywall en un navegador integrado. ::: ```javascript showLineNumbers title="Flutter" void paywallViewDidAppear(AdaptyUIPaywallView view) { } ``` #### Paywall ocultado \{#paywall-disappeared\} Este método se invoca cuando la vista del paywall desaparece de la pantalla. :::note En iOS, también se invoca cuando un [web paywall](web-paywall#step-2a-add-a-web-purchase-button) abierto desde un paywall en un navegador integrado desaparece de la pantalla. ::: ```javascript showLineNumbers title="Flutter" void paywallViewDidDisappear(AdaptyUIPaywallView view) { } ``` #### Selección de producto \{#product-selection\} Si se selecciona un producto para comprar (por el usuario o por el sistema), se invocará este método: ```javascript showLineNumbers title="Flutter" void paywallViewDidSelectProduct(AdaptyUIPaywallView view, String productId) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "productId": "premium_monthly" } ``` </Details> #### Compra iniciada \{#started-purchase\} Si el usuario inicia el proceso de compra, se invocará este método: ```javascript showLineNumbers title="Flutter" void paywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ``` </Details> #### Compra finalizada \{#finished-purchase\} Este método se invoca cuando una compra tiene éxito, el usuario cancela su compra o la compra queda pendiente: ```javascript showLineNumbers title="Flutter" void paywallViewDidFinishPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyPurchaseResult purchaseResult) { switch (purchaseResult) { case AdaptyPurchaseResultSuccess(profile: final profile): // successful purchase break; case AdaptyPurchaseResultPending(): // purchase is pending break; case AdaptyPurchaseResultUserCancelled(): // user cancelled the purchase break; default: break; } } ``` <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // Successful purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "AdaptyPurchaseResultSuccess", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } } } // Pending purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "AdaptyPurchaseResultPending" } } // User cancelled purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "AdaptyPurchaseResultUserCancelled" } } ``` </Details> Recomendamos cerrar la pantalla en ese caso. Consulta [Responder a acciones de botones](flutter-handle-paywall-actions) para más detalles sobre cómo cerrar una pantalla de paywall. #### Navegación de pago web finalizada \{#finished-web-payment-navigation\} Este método se invoca después de un intento de abrir un [web paywall](web-paywall) para un producto específico. Esto incluye tanto los intentos de navegación exitosos como los fallidos: ```javascript showLineNumbers title="Flutter" void paywallViewDidFinishWebPaymentNavigation(AdaptyUIPaywallView view, AdaptyPaywallProduct? product, AdaptyError? error) { } ``` **Parámetros:** | Parámetro | Descripción | |:------------|:---------------------------------------------------------------------------------------------------------------------| | **product** | Un `AdaptyPaywallProduct` para el que se abrió el web paywall. Puede ser `null`. | | **error** | Un objeto `AdaptyError` si la navegación al web paywall falló; `null` si la navegación fue exitosa. | <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // Successful navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": null } // Failed navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "web_navigation_failed", "message": "Failed to open web paywall", "details": { "underlyingError": "Browser unavailable" } } } ``` </Details> #### Compra fallida \{#failed-purchase\} Este método se invoca cuando una compra falla (por ejemplo, debido a problemas de pago o errores de red). **No** se activa para cancelaciones iniciadas por el usuario ni para transacciones pendientes — esos casos los gestiona `paywallViewDidFinishPurchase`: ```javascript showLineNumbers title="Flutter" void paywallViewDidFailPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" } } } ``` </Details> #### Restauración iniciada \{#started-restore\} Si el usuario inicia el proceso de restauración, se invocará este método: ```javascript showLineNumbers title="Flutter" void paywallViewDidStartRestore(AdaptyUIPaywallView view) { } ``` #### Restauración exitosa \{#successful-restore\} Si la restauración de una compra tiene éxito, se invocará este método: ```javascript showLineNumbers title="Flutter" void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } ``` </Details> Recomendamos cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de suscripción](flutter-listen-subscription-changes) para aprender cómo verificarlo y el tema [Responder a acciones de botones](flutter-handle-paywall-actions) para aprender cómo cerrar una pantalla de paywall. #### Restauración fallida \{#failed-restore\} Si la restauración de una compra falla, se invocará este método: ```javascript showLineNumbers title="Flutter" void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } ``` </Details> ### Carga de datos y renderizado \{#data-fetching-and-rendering\} #### Errores al cargar productos \{#product-loading-errors\} Si no pasas el array de productos durante la inicialización, AdaptyUI recuperará los objetos necesarios del servidor por sí mismo. Si esta operación falla, AdaptyUI reportará el error invocando este método: ```javascript showLineNumbers title="Flutter" void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } ``` </Details> #### Errores de renderizado \{#rendering-errors\} Si se produce un error durante el renderizado de la interfaz, se reportará llamando a este método. Por defecto (desde v3.15.2), el paywall se cierra automáticamente cuando ocurre un error de renderizado, pero puedes cambiar este comportamiento si lo necesitas. ```javascript showLineNumbers title="Flutter" void paywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) { // Default behavior: view.dismiss() // Override with custom logic if needed, for example: // - Log the error // - Show an error message to the user } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } ``` </Details> En condiciones normales, estos errores no deberían ocurrir, así que si te encuentras con alguno, por favor comunícanoslo. --- # File: flutter-use-fallback-paywalls --- --- title: "Flutter - Usar paywalls de respaldo" description: "Gestiona los casos en que los usuarios están sin conexión o los servidores de Adapty no están disponibles" --- :::warning Los paywalls de respaldo son compatibles con Flutter SDK v2.11 y versiones posteriores. ::: Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet. * **Si la aplicación no puede acceder a los servidores de Adapty:** Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding. * **Si la aplicación no puede acceder a internet:** Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar. :::important Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty. ::: ## Configuración \{#configuration\} 1. Añade los archivos de configuración de respaldo al directorio `assets` de la aplicación en la raíz del proyecto. 2. Llama al método `.setFallback` **antes** de obtener el paywall o el onboarding de destino. ```javascript showLineNumbers title="javascript" final assetId = Platform.isIOS ? 'assets/ios_fallback.json' : 'assets/android_fallback.json'; try { await Adapty.setFallback(assetId); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Parámetros: | Parámetro | Descripción | | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **assetId** | Ruta al archivo de configuración de respaldo. | :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: flutter-web-paywall --- --- title: "Implementar paywalls web en Flutter SDK" description: "Configura un paywall web para cobrar sin las comisiones ni las revisiones de la App Store." --- :::important Antes de empezar, asegúrate de haber [configurado tu paywall web en el dashboard](web-paywall) y de tener instalada la versión 3.6.1 o posterior del SDK de Adapty. ::: Si estás trabajando con un paywall que has desarrollado tú mismo, necesitas gestionar los paywalls web mediante el método del SDK. El método `.openWebPaywall`: 1. Genera una URL única que permite a Adapty vincular el paywall concreto mostrado a un usuario determinado con la página web a la que es redirigido. 2. Detecta cuándo los usuarios vuelven a la app y luego solicita `.getProfile` a intervalos cortos para determinar si los derechos de acceso del perfil se han actualizado. De esta forma, si el pago se ha completado correctamente y los derechos de acceso se han actualizado, la suscripción se activa en la app casi de inmediato. ```dart showLineNumbers title="Flutter" try { await Adapty().openWebPaywall(product: <YOUR_PRODUCT>); // The web paywall will be opened } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle other errors } ``` :::note Existen dos versiones del método `openWebPaywall`: 1. `openWebPaywall(product)`, que genera URLs por paywall y añade también los datos del producto a las URLs. 2. `openWebPaywall(paywall)`, que genera URLs por paywall sin añadir los datos del producto a las URLs. Úsalo cuando los productos de tu paywall en Adapty sean diferentes a los del paywall web. ::: #### Gestionar errores \{#handle-errors\} | Error | Descripción | Acción recomendada | |-----------------------------------------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------| | AdaptyError.paywallWithoutPurchaseUrl | El paywall no tiene configurada una URL de compra web | Comprueba si el paywall se ha configurado correctamente en el Adapty Dashboard | | AdaptyError.productWithoutPurchaseUrl | El producto no tiene una URL de compra web | Verifica la configuración del producto en el Adapty Dashboard | | AdaptyError.failedOpeningWebPaywallUrl | No se pudo abrir la URL en el navegador | Revisa la configuración del dispositivo o proporciona un método de compra alternativo | | AdaptyError.failedDecodingWebPaywallUrl | No se pudieron codificar correctamente los parámetros en la URL | Verifica que los parámetros de la URL sean válidos y estén correctamente formateados | ## Abrir paywalls web en un navegador in-app \{#open-web-paywalls-in-an-in-app-browser\} :::important La apertura de paywalls web en un navegador in-app está disponible a partir de la versión 3.15 del SDK de Adapty. ::: Por defecto, los paywalls web se abren en el navegador externo. Para ofrecer una experiencia de usuario fluida, puedes abrir los paywalls web en un navegador in-app. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin cambiar de app. Para habilitarlo, establece el parámetro `in` en `.inAppBrowser`: ```dart showLineNumbers try { await Adapty().openWebPaywall( product: <YOUR_PRODUCT>, openIn: AdaptyWebPresentation.inAppBrowser, ); // The web paywall will be opened in the in-app browser } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle other errors } ``` --- # File: flutter-troubleshoot-paywall-builder --- --- title: "Solucionar problemas del Paywall Builder en el SDK de Flutter" description: "Solucionar problemas del Paywall Builder en el SDK de Flutter" --- Esta guía te ayuda a resolver los problemas más comunes al usar paywalls diseñados con el Adapty Paywall Builder en el SDK de Flutter. ## Fallo al obtener la configuración del paywall \{#getting-a-paywall-configuration-fails\} **Problema**: El método `createPaywallView` no puede recuperar la configuración del paywall. **Causa**: El paywall no está habilitado para mostrarse en el dispositivo en el Paywall Builder. **Solución**: Activa el interruptor **Show on device** en el Paywall Builder. <img src="/assets/shared/img/show-on-device.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## El número de vistas del paywall es demasiado alto \{#the-paywall-view-number-is-too-big\} **Problema**: El recuento de vistas del paywall muestra el doble del número esperado. **Causa**: Es posible que estés llamando a `logShowPaywall` en tu código, lo que duplica el recuento de vistas si usas el Paywall Builder. En los paywalls diseñados con el Paywall Builder, el seguimiento de analytics es automático, por lo que no es necesario usar este método. **Solución**: Asegúrate de no llamar a `logShowPaywall` en tu código si estás usando el Paywall Builder. ## Otros problemas \{#other-issues\} **Problema**: Experimentas otros problemas relacionados con el Paywall Builder que no se han tratado anteriormente. **Solución**: Si es necesario, migra el SDK a la versión más reciente siguiendo las [guías de migración](flutter-sdk-migration-guides). Muchos problemas se resuelven en versiones más nuevas del SDK. --- # File: flutter-quickstart-manual --- --- title: "Habilitar compras en tu paywall personalizado en Flutter SDK" description: "Integra el SDK de Adapty en tus paywalls personalizados de Flutter para habilitar compras in-app." --- Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall, mientras el SDK de Adapty obtiene los productos, gestiona las nuevas compras y restaura las anteriores. :::important **Esta guía es para desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Paywall Builder de Adapty](flutter-quickstart-paywalls). Con Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compras automáticamente y puedes probar distintos diseños sin volver a publicar tu app. ::: ## Antes de empezar \{#before-you-start\} ### Configura los productos \{#set-up-products\} 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) - [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 paywalls distintos a diferentes usuarios. Asegúrate de entender estos conceptos aunque trabajes con tu paywall personalizado. Básicamente, son tu forma de gestionar los productos que vendes en tu app. Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite recuperar tus productos. Para entender qué debes hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart). ### Gestiona usuarios \{#manage-users\} Puedes trabajar con o sin autenticación de backend en tu lado. Sin embargo, el SDK de Adapty gestiona de forma diferente a los usuarios anónimos e identificados. Lee la [guía de inicio rápido de identificación](flutter-quickstart-identify) para entender las particularidades y asegurarte de que trabajas con los usuarios correctamente. ## Paso 1. Obtén los productos \{#step-1-get-products\} Para recuperar los productos de tu paywall personalizado, necesitas: 1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`. 2. Obtener el array de productos para ese paywall usando el método `getPaywallProducts`. ```dart showLineNumbers Future<void> loadPaywall() async { try { final paywall = await Adapty().getPaywall(placementId: 'YOUR_PLACEMENT_ID'); final products = await Adapty().getPaywallProducts(paywall: paywall); // Use products to build your custom paywall UI } on AdaptyError catch (adaptyError) { // Handle the error } catch (e) { // Handle the error } } ``` ## Paso 2. Acepta compras \{#step-2-accept-purchases\} Cuando un usuario toca un producto en tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado. ```dart showLineNumbers Future<void> purchaseProduct(AdaptyPaywallProduct product) async { try { final purchaseResult = await Adapty().makePurchase(product: product); switch (purchaseResult) { case AdaptyPurchaseResultSuccess(profile: final profile): // Purchase successful, profile updated break; case AdaptyPurchaseResultUserCancelled(): // User canceled the purchase break; case AdaptyPurchaseResultPending(): // Purchase is pending (e.g., user will pay offline with cash) break; } } on AdaptyError catch (adaptyError) { // Handle the error } catch (e) { // Handle the error } } ``` ## Paso 3. Restaura compras \{#step-3-restore-purchases\} Los stores de aplicaciones exigen que todas las apps con suscripciones ofrezcan una forma de que los usuarios puedan restaurar sus compras. Llama al método `restorePurchases` cuando el usuario toque el botón de restaurar. Esto sincronizará el historial de compras con Adapty y devolverá el perfil actualizado. ```dart showLineNumbers Future<void> restorePurchases() async { try { final profile = await Adapty().restorePurchases(); // Restore successful, profile updated } on AdaptyError catch (adaptyError) { // Handle the error } catch (e) { // Handle the error } } ``` ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Tu paywall está listo para mostrarse en la app. Prueba tus compras en el [sandbox de 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. Para ver cómo funciona en una implementación lista para producción, consulta el [PurchasesObserver](https://github.com/adaptyteam/AdaptySDK-Flutter/blob/master/example/lib/purchase_observer.dart) en nuestra app de ejemplo, que muestra el manejo de compras con gestión de errores adecuada, observadores de UI e integración completa del SDK. A continuación, [comprueba si los usuarios han completado su compra](flutter-check-subscription-status) para determinar si mostrar el paywall o conceder acceso a las funciones de pago. --- # File: fetch-paywalls-and-products-flutter --- --- title: "Obtener paywalls y productos para paywalls con Remote Config en Flutter SDK" description: "Obtén paywalls y productos en el SDK de Flutter de Adapty para mejorar la monetización de tus usuarios." --- Antes de mostrar paywalls con Remote Config o personalizados, necesitas obtener información sobre ellos. Ten en cuenta que este tema hace referencia a paywalls con Remote Config y personalizados. Si necesitas orientación sobre cómo obtener paywalls del Paywall Builder, consulta [Obtener paywalls del Paywall Builder y su configuración](flutter-get-pb-paywalls). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a obtener paywalls y productos en tu app (haz clic para ampliar)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos a tu paywall](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall al placement](create-placement) en el Adapty Dashboard. 4. [Instala el SDK de Adapty](sdk-installation-flutter) en tu app. </details> ## Obtener información del paywall \{#fetch-paywall-information\} En Adapty, un [producto](product) es una combinación de productos tanto de App Store como de Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos dentro de placements específicos de tu app. Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`. :::important **No escribas los IDs de producto en el código.** El único ID que debes escribir en el código es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe gestionar estos cambios de forma dinámica: si un paywall devuelve dos productos hoy y tres mañana, muéstralos todos sin necesidad de cambiar el código. ::: ```dart showLineNumbers try { final paywall = await Adapty().getPaywall(id: "YOUR_PLACEMENT_ID", locale: "en"); // the requested paywall } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de configuración regional](flutter-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos del servidor y devolverá datos en caché en caso de error. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este caso, puede que los usuarios no reciban los datos más recientes, pero experimentarán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar peticiones de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls en dos capas: la caché actualizada regularmente descrita arriba y los [paywalls de respaldo](flutter-use-fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo de espera, se devolverán datos en caché o el fallback local.</p><p></p><p>Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo de espera especificado en `loadTimeout`, ya que la operación puede incluir diferentes solicitudes internamente.</p> | ¡No escribas los IDs de producto en el código! Dado que los paywalls se configuran de forma remota, los productos disponibles, el número de productos y las ofertas especiales (como períodos de prueba gratuitos) pueden cambiar con el tiempo. Asegúrate de que tu código gestione estos escenarios. Por ejemplo, si inicialmente obtienes 2 productos, tu app debería mostrar esos 2 productos. Sin embargo, si luego obtienes 3 productos, tu app debería mostrar los 3 sin necesidad de cambios en el código. Lo único que debes escribir en el código es el ID del placement. Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html) con: una lista de IDs de productos, el identificador del paywall, el Remote Config y varias otras propiedades. | ## Obtener productos \{#fetch-products\} Una vez que tengas el paywall, puedes consultar el array de productos que le corresponde: ```dart showLineNumbers try { final products = await Adapty().getPaywallProducts(paywall: paywall); // the requested products array } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Products | Lista de objetos [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html) con: identificador del producto, nombre del producto, precio, moneda, duración de la suscripción y varias otras propiedades. | Cuando implementes tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html). A continuación se muestran las propiedades más utilizadas, pero consulta el documento enlazado para ver todos los detalles de las propiedades disponibles. | Propiedad | Descripción | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | Para mostrar el título del producto, usa `product.localizedTitle`. Ten en cuenta que la localización se basa en el país del store seleccionado por el usuario, no en la configuración regional del dispositivo. | | **Price** | Para mostrar una versión localizada del precio, usa `product.price.localizedString`. Esta localización se basa en la información de configuración regional del dispositivo. También puedes acceder al precio como número con `product.price.amount`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda asociado, usa `product.price.currencySymbol`. | | **Subscription Period** | Para mostrar el período (por ejemplo, semana, mes, año, etc.), usa `product.subscription?.localizedPeriod`. Esta localización se basa en la configuración regional del dispositivo. Para obtener el período de suscripción de forma programática, usa `product.subscription?.period`. Desde ahí puedes acceder al enum `unit` para obtener la duración (es decir, día, semana, mes, año o desconocido). El valor `numberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral, verías `AdaptyPeriodUnit.month` en la propiedad unit y `3` en la propiedad numberOfUnits. | | **Introductory Offer** | Para mostrar un distintivo u otro indicador de que una suscripción contiene una oferta introductoria, consulta la propiedad `product.subscription?.offer?.phases`. Es una lista que puede contener hasta dos fases de descuento: la fase de prueba gratuita y la fase de precio introductorio. Dentro de cada objeto de fase se encuentran las siguientes propiedades útiles:<br/>• `paymentMode`: un enum con los valores `AdaptyPaymentMode.freeTrial`, `AdaptyPaymentMode.payAsYouGo`, `AdaptyPaymentMode.payUpFront` y `AdaptyPaymentMode.unknown`. Las pruebas gratuitas serán del tipo `AdaptyPaymentMode.freeTrial`.<br/>• `price`: el precio con descuento como número. Para las pruebas gratuitas, busca `0` aquí.<br/>• `localizedNumberOfPeriods`: una cadena localizada según la configuración regional del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `3 days` en este campo.<br/>• `subscriptionPeriod`: como alternativa, puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que la sección anterior.<br/>• `localizedSubscriptionPeriod`: un período de suscripción formateado del descuento para la configuración regional del usuario. | ## Acelerar la obtención del paywall con el paywall de la audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\} Normalmente, los paywalls se obtienen casi al instante, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos donde tienes numerosas audiencias y paywalls y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseable. En estas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia fluida en lugar de no mostrar ningún paywall. Para solucionar esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, tal como se detalla en la sección [Obtener información del paywall](fetch-paywalls-and-products-flutter#fetch-paywall-information) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls diferentes para distintas versiones de la app (actual y futuras), puedes encontrarte con dificultades. Tendrás que diseñar paywalls compatibles con la versión actual (legacy) o aceptar que los usuarios con esa versión puedan tener problemas con paywalls que no se renderizan. - **Pérdida de targeting**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes el targeting personalizado (incluyendo por países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. En caso contrario, sigue usando el método `getPaywall` descrito [arriba](fetch-paywalls-and-products-flutter#fetch-paywall-information). ::: :::note El método `getPaywallForDefaultAudience` aún no es compatible con el SDK de Flutter, pero el soporte se añadirá próximamente. ::: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de configuración regional](flutter-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos del servidor y devolverá datos en caché en caso de error. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este caso, puede que los usuarios no reciban los datos más recientes, pero experimentarán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar peticiones de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p> | --- # File: present-remote-config-paywalls-flutter --- --- title: "Renderizar un paywall diseñado con Remote Config en el SDK de Flutter" description: "Descubre cómo presentar paywalls de Remote Config en el SDK de Flutter de Adapty para personalizar la experiencia del usuario." --- Si has personalizado un paywall usando Remote Config, necesitarás implementar el renderizado en el código de tu app para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú decides qué incluir y cómo se muestra la vista de tu paywall. Proporcionamos un método para obtener la configuración remota, dándote autonomía para mostrar tu paywall personalizado configurado a través de Remote Config. ## Obtener el Remote Config del paywall y presentarlo \{#get-paywall-remote-config-and-present-it\} Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores necesarios. ```dart showLineNumbers try { final paywall = await Adapty().getPaywall(id: "YOUR_PLACEMENT_ID"); final String? headerText = paywall.remoteConfig?.dictionary?['header_text'] as String?; } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` En este punto, una vez que hayas recibido todos los valores necesarios, es momento de renderizarlos y ensamblarlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a las distintas pantallas y orientaciones de los móviles, ofreciendo una experiencia fluida y amigable en todos los dispositivos. :::warning Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls-flutter#track-paywall-view-events) tal como se describe a continuación, para que Adapty Analytics pueda capturar información para los embudos y las pruebas A/B. ::: Una vez que hayas terminado de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.makePurchase()` con el producto de tu paywall. Para más detalles sobre el método `.makePurchase()`, consulta [Realizar compras](flutter-making-purchases). Te recomendamos [crear un paywall de respaldo llamado fallback paywall](flutter-use-fallback-paywalls). Este respaldo se mostrará al usuario cuando no haya conexión a internet ni caché disponible, garantizando una experiencia fluida incluso en esas situaciones. ## Registrar eventos de visualización del paywall \{#track-paywall-view-events\} Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque los datos de compras se recopilan automáticamente, el registro de las visualizaciones del paywall requiere tu intervención, ya que solo tú sabes cuándo un usuario ve un paywall. Para registrar un evento de visualización del paywall, simplemente llama a `.logShowPaywall(paywall)`, y se reflejará en las métricas de tu paywall en los embudos y las pruebas A/B. :::important No es necesario llamar a `.logShowPaywall(paywall)` si estás mostrando paywalls creados en el [Paywall Builder](adapty-paywall-builder). ::: ```dart showLineNumbers try { final result = await Adapty().logShowPaywall(paywall: paywall); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :------- |:----------------------------------------------------------------------| | **paywall** | obligatorio | Un objeto [`AdaptyPaywall`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html). | --- # File: flutter-making-purchases --- --- title: "Realizar compras en una aplicación móvil con Flutter SDK" description: "Guía sobre cómo gestionar compras in-app y suscripciones con Adapty." --- Mostrar paywalls en tu aplicación móvil es un paso esencial para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, simplemente mostrar esos paywalls es suficiente para gestionar las compras solo si usas el [Paywall Builder](adapty-paywall-builder) para personalizarlos. Si no usas el Paywall Builder, debes utilizar un método específico llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método es la puerta de entrada para que los usuarios interactúen con los paywalls y realicen sus transacciones. Si tu paywall tiene una oferta promocional activa para el producto que el usuario intenta comprar, Adapty la aplicará automáticamente en el momento de la compra. :::warning Ten en cuenta que la oferta introductoria solo se aplicará automáticamente si usas paywalls configurados con el Paywall Builder. En otros casos, tendrás que [verificar la elegibilidad del usuario para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Omitir este paso puede provocar que tu app sea rechazada durante la revisión. Además, podría resultar en que se cobre el precio completo a usuarios que son elegibles para una oferta introductoria. ::: Asegúrate de haber completado la [configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras. ## Realizar una compra \{#make-purchase\} :::note **¿Usas el [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente; puedes omitir este paso. **¿Buscas instrucciones paso a paso?** Consulta la [guía de inicio rápido](flutter-implement-paywalls-manually) para obtener instrucciones de implementación completas con todo el contexto. ::: ```dart showLineNumbers try { final purchaseResult = await Adapty().makePurchase(product: product); switch (purchaseResult) { case AdaptyPurchaseResultSuccess(profile: final profile): if (profile.accessLevels['premium']?.isActive ?? false) { // Grant access to the paid features } break; case AdaptyPurchaseResultPending(): break; case AdaptyPurchaseResultUserCancelled(): break; default: break; } } on AdaptyError catch (adaptyError) { // Handle the error } catch (e) { // Handle the error } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :-------- | :-------------------------------------------------------------------------------------------------- | | **Product** | obligatorio | Un objeto [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html) obtenido del paywall. | Parámetros de la respuesta: | Parámetro | Descripción | |---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** | <p>Si la solicitud se ha realizado correctamente, la respuesta contiene este objeto. Un objeto [AdaptyProfile](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyProfile-class.html) proporciona información completa sobre los niveles de acceso, suscripciones y compras únicas de un usuario dentro de la app.</p><p>Comprueba el estado del nivel de acceso para determinar si el usuario tiene el acceso requerido en la app.</p> | :::warning **Nota:** si aún utilizas una versión de StoreKit de Apple inferior a v2.0 y una versión del SDK de Adapty inferior a v.2.9.0, debes proporcionar el [secreto compartido de la App Store de Apple](app-store-connection-configuration#step-5-enter-app-store-shared-secret). Este método está actualmente deprecado por Apple. ::: ## Cambiar la suscripción al realizar una compra \{#change-subscription-when-making-a-purchase\} Cuando un usuario opta por una nueva suscripción en lugar de renovar la actual, el comportamiento depende del store: - En la App Store, la suscripción se actualiza automáticamente dentro del grupo de suscripciones. Si un usuario compra una suscripción de un grupo mientras ya tiene activa una de otro grupo, ambas suscripciones estarán activas al mismo tiempo. - En Google Play, la suscripción no se actualiza automáticamente. Tendrás que gestionar el cambio en el código de tu aplicación móvil tal como se describe a continuación. Para reemplazar la suscripción por otra en Android, llama al método `.makePurchase()` con el parámetro adicional: ```dart showLineNumbers try { final result = await adapty.makePurchase( product: product, subscriptionUpdateParams: subscriptionUpdateParams, ); // successful cross-grade } on AdaptyError catch (adaptyError) { // Handle the error } catch (e) { // Handle the error } ``` Parámetro adicional de la solicitud: | Parámetro | Presencia | Descripción | | :--------------------------- | :-------- |:--------------------------------------------------------------------------------------------------------| | **subscriptionUpdateParams** | obligatorio | Un objeto [`AdaptyAndroidSubscriptionUpdateParameters`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyAndroidSubscriptionUpdateParameters-class.html). | Puedes obtener más información sobre las suscripciones y los modos de reemplazo en la documentación para desarrolladores de Google: - [Acerca de los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-modes) - [Recomendaciones de Google para los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-recommendations) - Modo de reemplazo [`CHARGE_PRORATED_PRICE`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#CHARGE_PRORATED_PRICE()). Nota: este método solo está disponible para actualizaciones de suscripción. Las degradaciones no son compatibles. - Modo de reemplazo [`DEFERRED`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#DEFERRED()). Nota: el cambio de suscripción real solo ocurrirá cuando finalice el período de facturación de la suscripción actual. ## Canjear códigos de oferta en iOS \{#redeem-offer-codes-in-ios\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Details> <summary>Sobre los códigos de oferta</summary> Los códigos de oferta te permiten dar descuentos o períodos de prueba gratuitos a usuarios concretos. A diferencia de las ofertas habituales, que se aplican de forma automática, los códigos de oferta se distribuyen fuera de la app — por email, redes sociales o materiales impresos. Los usuarios los canjean introduciendo el código en el App Store, accediendo a una URL de canje o a través de un diálogo dentro de la app. Para configurar códigos de oferta, abre una suscripción en App Store Connect y ve a su sección **Offer Codes**. Puedes crear [tres tipos](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes) de códigos de oferta: - **Free** — la suscripción es gratuita durante un período determinado y la siguiente renovación se cobra al precio completo. - **Pay as you go** — el usuario paga un precio reducido en cada ciclo de facturación durante un período determinado y, después, la suscripción se renueva al precio completo. - **Pay up front** — el usuario paga un precio único reducido por toda la duración de la oferta y, después, la suscripción se renueva al precio completo. No es necesario añadir los códigos de oferta a Adapty. Apple etiqueta cada transacción durante el período de la oferta con la categoría del código de oferta. Esto incluye el canje inicial y todas las renovaciones con descuento posteriores. Adapty detecta la etiqueta y registra cada transacción con la categoría de oferta `offer_code`. Cuando termina el período de oferta y la suscripción se renueva al precio completo, la etiqueta deja de estar presente. Puedes filtrar los análisis por el tipo de oferta **Offer Code** en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds). #### Resolución de discrepancias en los ingresos \{#revenue-discrepancy-troubleshooting\} Si observas que una transacción con código de oferta aparece en Adapty al precio completo del producto en lugar del precio reducido, verifica lo siguiente en App Store Connect: - El código de oferta tiene los precios correctos configurados para todas las regiones donde los usuarios pueden canjearlo. - El precio de la oferta está configurado para el país o región específica del usuario. Apple envía el precio regional en la transacción. Si no hay ningún precio regional configurado para la oferta, Apple puede enviar el precio completo del producto. Puedes filtrar y verificar las transacciones con código de oferta en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds) mediante los filtros de tipo de oferta **Offer Code** y **Offer Discount Type**. #### Códigos promocionales heredados (obsoletos) \{#legacy-promo-codes-deprecated\} <Callout type="warning"> Apple dejó obsoletos los códigos promocionales para compras in-app en marzo de 2026. Los códigos de oferta los sustituyen con más funcionalidades: elegibilidad configurable, fechas de expiración y hasta 1 millón de códigos por trimestre. Si antes usabas códigos promocionales para compras in-app, migra a los códigos de oferta en App Store Connect. </Callout> Los códigos promocionales heredados (limitados a 100 por app y versión) daban acceso gratuito a una suscripción. A diferencia de los códigos de oferta, Apple no incluía información de descuento en las transacciones con código promocional — enviaba el precio completo del producto en el recibo. Por ello, Adapty registraba estas transacciones al precio completo, lo que generaba discrepancias entre los análisis de Adapty y App Store Connect. Si ves transacciones históricas al precio completo que deberían haber sido gratuitas, es probable que provengan de códigos promocionales heredados. Como estos códigos ya están obsoletos, migra a los códigos de oferta para un seguimiento preciso de los ingresos. </Details> Para mostrar la hoja de canje de códigos en tu app: ```dart showLineNumbers try { await Adapty().presentCodeRedemptionSheet(); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` :::danger Según nuestras observaciones, la hoja de canje de códigos de oferta puede no funcionar de forma fiable en algunas apps. Recomendamos redirigir al usuario directamente a la App Store. Para ello, abre una URL con el siguiente formato: `https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}` ::: ### Gestionar planes de prepago (Android) \{#manage-prepaid-plans-android\} Si los usuarios de tu app pueden adquirir [planes de prepago](https://developer.android.com/google/play/billing/subscriptions#prepaid-plans) (por ejemplo, comprar una suscripción no renovable por varios meses), puedes habilitar las [transacciones pendientes](https://developer.android.com/google/play/billing/subscriptions#pending) para dichos planes. ```dart showLineNumbers title="main.dart" await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withGoogleEnablePendingPrepaidPlans(true), ); ``` --- # File: flutter-restore-purchase --- --- title: "Restaurar compras en una app móvil con Flutter SDK" description: "Aprende cómo restaurar compras en Adapty para garantizar una experiencia de usuario fluida." --- Restaurar compras tanto en iOS como en Android es una funcionalidad que permite a los usuarios recuperar el acceso a contenido comprado anteriormente, como suscripciones o compras in-app, sin que se les cobre de nuevo. Esta funcionalidad es especialmente útil para usuarios que hayan desinstalado y vuelto a instalar la app, o que hayan cambiado de dispositivo y quieran acceder a su contenido previamente adquirido sin pagar otra vez. :::note En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin necesidad de código adicional por tu parte. Si ese es tu caso, puedes saltarte este paso. ::: Para restaurar una compra cuando no usas [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`: ```javascript showLineNumbers try { final profile = await Adapty().restorePurchases(); if (profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive ?? false) { // successful access restore } } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Parámetros de respuesta: | Parámetro | Descripción | |---------|-----------| | **Profile** | <p>Un objeto [`AdaptyProfile`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyProfile-class.html). Este modelo contiene información sobre niveles de acceso, suscripciones y compras únicas.</p><p>Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app.</p> | :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: implement-observer-mode-flutter --- --- title: "Implementar el modo Observer en Flutter SDK" description: "Implementa el modo observer en Adapty para rastrear eventos de suscripción de usuarios en Flutter SDK." --- Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analítica. Si esto cubre tus necesidades, solo tienes que: 1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. Sigue las instrucciones de configuración para [Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk). 2. [Reportar transacciones](report-transactions-observer-mode-flutter) desde tu infraestructura de compras existente a Adapty. ## Configuración del modo Observer \{#observer-mode-setup\} Activa el modo Observer si gestionas las compras y el estado de la suscripción tú mismo y usas Adapty para enviar eventos de suscripción y analíticas. :::important Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlo tú mismo. ::: ```dart showLineNumbers title="main.dart" await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withObserverMode(true) // Enable observer mode ..withLogLevel(AdaptyLogLevel.verbose), ); ``` Parámetros: | Parámetro | Descripción | | --------------------------- | ------------------------------------------------------------ | | observerMode | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor predeterminado es `false`. | ## Usar paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\} Si también quieres usar los paywalls y las funciones de pruebas A/B de Adapty, puedes hacerlo, pero requiere algo de configuración adicional en el modo Observer. Esto es lo que necesitas hacer además de los pasos anteriores: 1. Muestra los paywalls como de costumbre para [paywalls con Remote Config](present-remote-config-paywalls-flutter). 3. [Asocia los paywalls](report-transactions-observer-mode-flutter) con las transacciones de compra. --- # File: report-transactions-observer-mode-flutter --- --- title: "Reportar transacciones en Observer Mode en Flutter SDK" description: "Reporta transacciones de compra en el Observer Mode de Adapty para obtener información sobre usuarios y seguimiento de ingresos en Flutter SDK." --- <Tabs groupId="sdk-version" queryString> <TabItem value="current" label="Adapty SDK v3.4+ (current)" default> En el modo Observer, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas reportar las transacciones desde tu app store. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` para reportar explícitamente cada transacción y que Adapty pueda reconocerla. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra con el paywall que la originó, garantizando análisis precisos del paywall. ```javascript showLineNumbers try { // every time when calling transaction.finish() await Adapty().reportTransaction( "YOUR_TRANSACTION_ID", variationId: "PAYWALL_VARIATION_ID", // optional ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | transactionId | requerido | <ul><li> Para iOS: Identificador de la transacción.</li><li> Para Android: Identificador de cadena `purchase.getOrderId` de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.</li></ul> | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html). | </TabItem> <TabItem value="old" label="Adapty SDK 3.3.x (legacy)" default> En el modo Observer, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas reportar las transacciones desde tu app store o restaurarlas. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` en ambas plataformas para reportar explícitamente cada transacción, y usa `restorePurchases` en Android como paso adicional para asegurarte de que Adapty la reconozca. :::warning **¡No omitas el reporte de transacciones ni la restauración de compras!** Si no llamas a estos métodos, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra con el paywall que la originó, garantizando análisis precisos del paywall. ```javascript showLineNumbers // every time when calling transaction.finish() if (Platform.isAndroid) { try { await Adapty().restorePurchases(); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } } try { // every time when calling transaction.finish() await Adapty().reportTransaction( "YOUR_TRANSACTION_ID", variationId: "PAYWALL_VARIATION_ID", // optional ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | transactionId | requerido | <ul><li> Para iOS, StoreKit 1: un objeto [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction).</li><li> Para iOS, StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).</li><li> Para Android: Identificador de cadena (purchase.getOrderId de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación).</li></ul> | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html). | </TabItem> <TabItem value="old2" label="Adapty SDK up to 3.2.x (legacy)" default> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> **Reporte de transacciones** - Las versiones hasta 3.1.x escuchan automáticamente las transacciones en el App Store, por lo que no se requiere reporte manual. - La versión 3.2 no admite el Observer Mode. </TabItem> <TabItem value="kotlin" label="Android and Android-based cross-platforms" default> **Reporte de transacciones** Usa `restorePurchases` para reportar una transacción a Adapty en el Observer Mode, tal como se explica en la página [Restaurar compras en código móvil](flutter-restore-purchase). :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `restorePurchases`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: </TabItem> </Tabs> **Asociar paywalls a transacciones** El SDK de Adapty no puede determinar el origen de las compras, ya que eres tú quien las procesa. Por lo tanto, si tienes intención de usar paywalls y/o pruebas A/B en el modo Observer, necesitas asociar la transacción proveniente de tu app store con el paywall correspondiente en el código de tu app móvil. Es importante hacerlo correctamente antes de publicar tu app; de lo contrario, generará errores en los análisis. ```javascript final transactionId = transaction.transactionIdentifier final variationId = paywall.variationId try { await Adapty().setVariationId('transactionId', variationId); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` </TabItem> </Tabs> --- # File: flutter-troubleshoot-purchases --- --- title: "Solucionar problemas de compras en Flutter SDK" description: "Solucionar problemas de compras en Flutter SDK" --- Esta guía te ayuda a resolver los problemas más comunes al implementar compras manualmente en el Flutter SDK. ## makePurchase se ejecuta correctamente, pero el perfil no se actualiza \{#makepurchase-is-called-successfully-but-the-profile-is-not-being-updated\} **Problema**: El método `makePurchase` se completa correctamente, pero el perfil del usuario y el estado de la suscripción no se actualizan en Adapty. **Causa**: Esto normalmente indica una configuración incompleta de Google Play Store. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## makePurchase se invoca dos veces \{#makepurchase-is-invoked-twice\} **Problema**: El método `makePurchase` se está llamando varias veces para la misma compra. **Causa**: Esto suele ocurrir cuando el flujo de compra se dispara varias veces debido a problemas de gestión del estado de la UI o interacciones rápidas del usuario. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## AdaptyError.cantMakePayments en modo observer \{#adaptyerror-cantmakepayments-in-observer-mode\} **Problema**: Recibes `AdaptyError.cantMakePayments` al usar `makePurchase` en modo observer. **Causa**: En modo observer, debes gestionar las compras por tu cuenta, no usar el método `makePurchase` de Adapty. **Solución**: Si usas `makePurchase` para las compras, desactiva el modo observer. Tienes que elegir entre usar `makePurchase` o gestionar las compras por tu cuenta en modo observer. Consulta [Implementar el modo Observer](implement-observer-mode-flutter) para más detalles. ## Error de Adapty: (code: 103, message: Play Market request failed on purchases updated: responseCode=3, debugMessage=Billing Unavailable, detail: null) \{#adapty-error-code-103-message-play-market-request-failed-on-purchases-updated-responsecode3-debugmessagebilling-unavailable-detail-null\} **Problema**: Recibes un error de facturación no disponible de Google Play Store. **Causa**: Este error no está relacionado con Adapty. Es un error de la biblioteca de facturación de Google Play que indica que la facturación no está disponible en el dispositivo. **Solución**: Este error no está relacionado con Adapty. Puedes consultarlo en la documentación de Play Store: [Handle BillingResult response codes](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) | Play Billing | Android Developers. ## No se encuentran makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\} **Problema**: Tienes problemas porque no se encuentran los `makePurchasesCompletionHandlers`. **Causa**: Esto suele estar relacionado con problemas durante las pruebas en sandbox. **Solución**: Crea un nuevo usuario sandbox e inténtalo de nuevo. Esto resuelve con frecuencia los problemas del completion handler de compras en sandbox. ## Otros problemas \{#other-issues\} **Problema**: Tienes otros problemas relacionados con compras que no están cubiertos arriba. **Solución**: Si es necesario, migra el SDK a la versión más reciente siguiendo las [guías de migración](flutter-sdk-migration-guides). Muchos problemas se resuelven en versiones más recientes del SDK. --- # File: flutter-identifying-users --- --- title: "Identificar usuarios en el SDK de Flutter" description: "Identifica usuarios en Adapty para mejorar las experiencias personalizadas de suscripción." --- Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, debes establecer tu propio Customer User ID. Puedes encontrar usuarios por su Customer User ID en la sección [Perfiles](profiles-crm) y usarlo en la [API de servidor](getting-started-with-server-side-api), que se enviará a todas las integraciones. ### Configurar el ID de usuario del cliente durante la inicialización \{#setting-customer-user-id-on-configuration\} Si tienes un ID de usuario en el momento de la configuración, simplemente pásalo como parámetro `customerUserId` al método `.activate()`: ```dart showLineNumbers title="Dart" try { await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_API_KEY') ..withCustomerUserId(YOUR_CUSTOMER_USER_ID) ); } catch (e) { // handle the error } ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Configurar el ID de usuario del cliente después de la configuración \{#setting-customer-user-id-after-configuration\} Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo más adelante en cualquier momento con el método `.identify()`. Los casos más habituales para usar este método son tras el registro o la autenticación, cuando el usuario pasa de ser anónimo a estar autenticado. ```dart showLineNumbers try { await Adapty().identify(customerUserId); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Parámetros de la solicitud: - **Customer User ID** (obligatorio): un identificador de usuario de tipo string. :::warning Reenvío de datos significativos del usuario En algunos casos, como cuando un usuario vuelve a iniciar sesión en su cuenta, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si enviaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, deberás reenviar esos datos para el usuario identificado. También es importante tener en cuenta que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes. ::: ### Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\} Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`: ```dart showLineNumbers try { await Adapty().logout(); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle unknown error } ``` Luego puedes iniciar sesión con el usuario usando el método `.identify()`. ## Asignar `appAccountToken` (iOS) [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un **UUID** que te permite vincular las transacciones de App Store con la identidad interna de tus usuarios. StoreKit asocia este token con cada transacción, de modo que tu backend puede relacionar los datos de App Store con tus usuarios. Usa un UUID estable generado por usuario y reutilízalo para la misma cuenta en todos los dispositivos. Esto garantiza que las compras y las notificaciones de App Store queden correctamente vinculadas. Puedes establecer el token de dos formas: durante la activación del SDK o al identificar al usuario. :::important Siempre debes pasar `appAccountToken` junto con `customerUserId`. Si solo pasas el token, no se incluirá en la transacción. ::: ```dart showLineNumbers // Durante la configuración: try { await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_API_KEY') ..withCustomerUserId(YOUR_CUSTOMER_USER_ID, iosAppAccountToken: "YOUR_APP_ACCOUNT_TOKEN") ); } catch (e) { // handle the error } // O al identificar usuarios try { await Adapty().identify(customerUserId, iosAppAccountToken: "YOUR_APP_ACCOUNT_TOKEN"); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` ### Establecer IDs de cuenta ofuscados (Android) \{#set-obfuscated-account-ids-android\} Google Play requiere IDs de cuenta ofuscados en ciertos casos de uso para mejorar la privacidad y seguridad de los usuarios. Estos IDs ayudan a Google Play a identificar las compras manteniendo el anonimato de la información del usuario, lo que resulta especialmente importante para la prevención del fraude y el análisis de datos. Es posible que necesites establecer estos IDs si tu app maneja datos sensibles de los usuarios o si debes cumplir con normativas de privacidad específicas. Los IDs ofuscados permiten a Google Play rastrear las compras sin exponer los identificadores reales de los usuarios. ```dart showLineNumbers // Durante la configuración: try { await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_API_KEY') ..withCustomerUserId(YOUR_CUSTOMER_USER_ID, androidObfuscatedAccountId: "OBFUSCATED_ACCOUNT_ID") ); } catch (e) { // handle the error } // O al identificar usuarios try { await Adapty().identify(customerUserId, androidObfuscatedAccountId: "OBFUSCATED_ACCOUNT_ID"); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` ## Detectar usuarios en varios dispositivos \{#detect-users-across-devices\} --- no_index: true --- Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`. Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID. **Lo que Adapty puede detectar entre dispositivos** | Tu configuración | Lo que Adapty detecta | Lo que debes hacer | | --- | --- | --- | | Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. | | Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. | | Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. | | Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. | **Restaurar compras en un dispositivo nuevo** Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK. No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado. --- # File: flutter-setting-user-attributes --- --- title: "Establecer atributos de usuario en Flutter SDK" description: "Aprende a establecer atributos de usuario en Adapty para mejorar la segmentación de audiencias." --- Puedes añadir atributos opcionales como correo electrónico, número de teléfono, etc., al perfil del usuario de tu app. Después puedes usar esos atributos para crear [segmentos](segments) de usuarios o simplemente consultarlos en el CRM. ### Establecer atributos de usuario \{#setting-user-attributes\} Para establecer atributos de usuario, llama al método `.updateProfile()`: ```dart showLineNumbers final builder = AdaptyProfileParametersBuilder() ..setEmail("email@email.com") ..setPhoneNumber("+18888888888") ..setFirstName('John') ..setLastName('Appleseed') ..setGender(AdaptyProfileGender.other) ..setBirthday(DateTime(1970, 1, 3)); try { await Adapty().updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Ten en cuenta que los atributos que hayas establecido anteriormente con el método `updateProfile` no se restablecerán. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Lista de claves permitidas \{#the-allowed-keys-list\} A continuación se muestran las claves `<Key>` permitidas de `AdaptyProfileParameters.Builder` y sus valores `<Value>`: | Clave | Valor | |---|-----| | <p>email</p><p>phoneNumber</p><p>firstName</p><p>lastName</p> | String | | gender | Enum, los valores permitidos son: `female`, `male`, `other` | | birthday | Date | ### Atributos de usuario personalizados \{#custom-user-attributes\} Puedes definir tus propios atributos personalizados, que habitualmente están relacionados con el uso de tu app. Por ejemplo, en aplicaciones de fitness podrían ser el número de ejercicios por semana; en apps de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes usarlos en segmentos para crear paywalls y ofertas dirigidas, y también en analítica para descubrir qué métricas de producto tienen más impacto en los ingresos. ```javascript showLineNumbers try { final builder = AdaptyProfileParametersBuilder() ..setCustomStringAttribute('value1', 'key1') ..setCustomDoubleAttribute(1.0, 'key2'); await Adapty().updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Para eliminar una clave existente, usa el método `.withRemoved(customAttributeForKey:)`: ```javascript showLineNumbers try { final builder = AdaptyProfileParametersBuilder() ..removeCustomAttribute('key1') ..removeCustomAttribute('key2'); await Adapty().updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` A veces necesitas saber qué atributos personalizados se han establecido previamente. Para ello, utiliza el campo `customAttributes` del objeto `AdaptyProfile`. :::warning Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor podrían haber cambiado tras la última sincronización. ::: ### Límites \{#limits\} - Hasta 30 atributos personalizados por usuario - Los nombres de clave pueden tener hasta 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.` - El valor puede ser una cadena de texto o un número decimal con un máximo de 50 caracteres. --- # File: flutter-listen-subscription-changes --- --- title: "Comprobar el estado de suscripción en el SDK de Flutter" description: "Rastrea y gestiona el estado de suscripción de los usuarios en Adapty para mejorar la retención en tu app de Flutter." --- Con Adapty, llevar el control del estado de suscripción es muy sencillo. No tienes que insertar manualmente los IDs de producto en tu código. En su lugar, puedes confirmar fácilmente el estado de suscripción de un usuario comprobando si tiene un [nivel de acceso](access-level) activo. <details> <summary>Antes de empezar a comprobar el estado de suscripción (haz clic para expandir)</summary> - Para iOS, configura las [notificaciones del servidor de App Store](enable-app-store-server-notifications) - Para Android, configura las [notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn) </details> ## Nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptypro file-object\} Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyProfile-class.html). Te recomendamos obtener el perfil cuando tu app arranque, por ejemplo al [identificar a un usuario](flutter-identifying-users#setting-customer-user-id-on-configuration), y actualizarlo cada vez que se produzcan cambios. Así podrás usar el objeto de perfil sin tener que solicitarlo repetidamente. Para recibir notificaciones sobre actualizaciones del perfil, escucha los cambios tal como se describe en la sección [Escuchar actualizaciones del perfil, incluidos los niveles de acceso](flutter-listen-subscription-changes) más abajo. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\} Para obtener el nivel de acceso desde el servidor, usa el método `.getProfile()`: ```javascript showLineNumbers try { final profile = await Adapty().getProfile(); // check the access } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Parámetros de respuesta: | Parámetro | Descripción | | --------- | ------------------------------------------------------------ | | Profile | <p>Un objeto [AdaptyProfile](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyProfile-class.html). En general, solo tienes que comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app.</p><p></p><p>El método `.getProfile` devuelve el resultado más actualizado posible, ya que siempre intenta consultar la API. Si por algún motivo (por ejemplo, sin conexión a internet) el SDK de Adapty no puede obtener la información del servidor, se devolverán los datos de la caché. También es importante destacar que el SDK de Adapty actualiza la caché de `AdaptyProfile` periódicamente para mantener esta información lo más actualizada posible.</p> | El método `.getProfile()` te proporciona el perfil del usuario desde el que puedes obtener el estado del nivel de acceso. Puedes tener múltiples niveles de acceso por app. Por ejemplo, si tienes una app de noticias y vendes suscripciones a diferentes temáticas de forma independiente, puedes crear niveles de acceso "sports" y "science". Pero la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes usar simplemente el nivel de acceso "premium" por defecto. Aquí tienes un ejemplo para comprobar el nivel de acceso "premium" por defecto: ```javascript showLineNumbers try { final profile = await Adapty().getProfile(); if (profile?.accessLevels['premium']?.isActive ?? false) { // grant access to premium features } } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` ### Escuchar actualizaciones del estado de suscripción \{#listening-for-subscription-status-updates\} Cada vez que cambia la suscripción del usuario, Adapty lanza un evento. Para recibir mensajes de Adapty, necesitas realizar una configuración adicional: ```javascript showLineNumbers Adapty().didUpdateProfileStream.listen((profile) { // handle any changes to subscription state }); ``` Adapty también lanza un evento al iniciar la aplicación. En ese caso, se pasará el estado de suscripción almacenado en caché. ### Caché del estado de suscripción \{#subscription-status-cache\} La caché implementada en el SDK de Adapty almacena el estado de suscripción del perfil. Esto significa que, aunque el servidor no esté disponible, se puede acceder a los datos en caché para obtener información sobre el estado de suscripción del perfil. No obstante, hay que tener en cuenta que no es posible solicitar datos directamente desde la caché. El SDK consulta periódicamente el servidor cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si hay alguna modificación, como nuevas transacciones u otras actualizaciones, se enviarán a los datos en caché para mantenerlos sincronizados con el servidor. --- # File: flutter-deal-with-att --- --- title: "Gestionar ATT en Flutter SDK" description: "Empieza con Adapty en Flutter para simplificar la configuración y gestión de suscripciones." --- Si tu aplicación utiliza el framework AppTrackingTransparency y muestra al usuario una solicitud de autorización de seguimiento, debes enviar el [estado de autorización](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/) a Adapty. ```dart showLineNumbers final builder = AdaptyProfileParametersBuilder() ..setAppTrackingTransparencyStatus(AdaptyIOSAppTrackingTransparencyStatus.authorized); try { await Adapty().updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle unknown error } ``` :::warning Recomendamos encarecidamente que envíes este valor lo antes posible cuando cambie; solo así los datos se transmitirán a tiempo a las integraciones que hayas configurado. ::: --- # File: kids-mode-flutter --- --- title: "Modo Kids en Flutter SDK" description: "Activa fácilmente el Modo Kids para cumplir con las políticas de Apple y Google. Sin recopilación de IDFA, GAID ni datos de publicidad en Flutter SDK." --- Si tu aplicación Flutter está destinada a niños, debes seguir las políticas de [Apple](https://developer.apple.com/kids/) y [Google](https://support.google.com/googleplay/android-developer/answer/9893335). Si usas el SDK de Adapty, unos pocos pasos sencillos te ayudarán a configurarlo para cumplir con estas políticas y superar las revisiones de las tiendas. ## ¿Qué se requiere? \{#whats-required\} Debes configurar el SDK de Adapty para deshabilitar la recopilación de: - [IDFA (Identificador para Anunciantes)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers) (iOS) - [Android Advertising ID (AAID/GAID)](https://support.google.com/googleplay/android-developer/answer/6048248) (Android) - [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) Además, te recomendamos usar el ID de usuario con precaución. Un ID de usuario con formato `<NombreApellido>` se considerará sin duda como recopilación de datos personales, al igual que el uso del correo electrónico. Para el Modo Kids, la mejor práctica es usar identificadores aleatorios o anonimizados (por ejemplo, IDs con hash o UUIDs generados por el dispositivo) para garantizar el cumplimiento. ## Habilitar el Modo Kids \{#enabling-kids-mode\} ### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\} En el Adapty Dashboard, debes deshabilitar la recopilación de direcciones IP. Para hacerlo, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** bajo **Collect users' IP address**. ### Cambios en el código de tu aplicación móvil \{#updates-in-your-mobile-app-code\} Para cumplir con las políticas, deshabilita la recopilación del IDFA del usuario (para iOS), GAID/AAID (para Android) y la dirección IP. **Android: Actualiza la configuración de tu SDK** ```dart showLineNumbers title="Dart" try { await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_API_KEY') // highlight-start ..withGoogleAdvertisingIdCollectionDisabled(true), // set to `true` ..withIpAddressCollectionDisabled(true), // set to `true` // highlight-end ); } catch (e) { // handle the error } ``` **iOS: Habilitar el Modo Kids con CocoaPods** 1. Actualiza tu Podfile: - Si **no** tienes una sección `post_install`, añade el bloque de código completo a continuación. - Si **ya tienes** una sección `post_install`, fusiona las líneas resaltadas en ella. ```ruby showLineNumbers title="Podfile" post_install do |installer| installer.pods_project.targets.each do |target| // highlight-start if target.name == 'Adapty' target.build_configurations.each do |config| config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)'] config.build_settings['OTHER_SWIFT_FLAGS'] << '-DADAPTY_KIDS_MODE' end end // highlight-end end end ``` 2. Aplica los cambios ejecutando ```sh showLineNumbers title="Shell" pod install ``` --- # File: flutter-get-onboardings --- --- title: "Obtener onboardings en Flutter SDK" description: "Aprende cómo recuperar onboardings en Adapty para Flutter." --- Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el builder en el Adapty Dashboard, puedes mostrarlo en tu app Flutter. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, como se describe a continuación. Antes de empezar, asegúrate de que: 1. Tienes instalado el [SDK de Adapty para Flutter](sdk-installation-flutter) en la versión 3.8.0 o superior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). ## Obtener el onboarding \{#fetch-onboarding\} Cuando creas un [onboarding](onboardings) con nuestro builder sin código, se almacena como un contenedor con la configuración que tu app necesita obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a cuestionarios o entradas de formularios). El contenedor también registra automáticamente los eventos de analíticas, por lo que no necesitas implementar un seguimiento de vistas por separado. Para un mejor rendimiento, obtén la configuración del onboarding con antelación para dar tiempo suficiente a que las imágenes se descarguen antes de mostrárselas a los usuarios. Para obtener un onboarding, utiliza el método `getOnboarding`: ```dart showLineNumbers try { final onboarding = await Adapty().getOnboarding(placementId: "YOUR_PLACEMENT_ID"); } on AdaptyError catch (e) { //handle error } catch (e) { //handle error } ``` A continuación, llama al método `createOnboardingView` para obtener la vista que vas a mostrar. :::warning El resultado del método `createOnboardingView` solo se puede usar una vez. Si necesitas usarlo de nuevo, vuelve a llamar al método `createOnboardingView`. Llamarlo dos veces sin recrearlo puede producir el error `AdaptyUIError.viewAlreadyPresented`. ::: ```dart showLineNumbers try { final onboardingView = await Adapty().createOnboardingView(onboarding: onboarding); } on AdaptyError catch (e) { //handle error } catch (e) { //handle error } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen conexiones inestables a internet, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, puede que los usuarios no reciban los últimos datos, pero experimentarán tiempos de carga más rápidos independientemente de lo inestable que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la última versión de tus onboardings y asegurar la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera de este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el respaldo local.</p><p>Ten en cuenta que en casos excepcionales este método puede agotar el tiempo de espera ligeramente después de lo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.</p> | Parámetros de respuesta: | Parámetro | Descripción | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | Un objeto [`AdaptyOnboarding`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyOnboarding-class.html) con: el identificador y la configuración del onboarding, el Remote Config y otras propiedades. | ## Acelerar la obtención del onboarding con el onboarding de audiencia predeterminada \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} Por lo general, los onboardings se obtienen casi de forma instantánea, por lo que no es necesario preocuparse por acelerar este proceso. Sin embargo, en casos donde tienes muchas audiencias y onboardings, y tus usuarios tienen una conexión a internet débil, obtener un onboarding puede tardar más de lo deseable. En esas situaciones, puede que quieras mostrar un onboarding predeterminado para garantizar una experiencia fluida en lugar de no mostrar ninguno. Para esto, puedes usar el método `getOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, como se detalla en la sección [Obtener el onboarding](#fetch-onboarding) anterior. :::warning Considera usar `getOnboarding` en lugar de `getOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: Puede crear problemas al admitir varias versiones de la app, lo que requiere diseños compatibles hacia atrás o aceptar que las versiones más antiguas puedan mostrarse incorrectamente. - **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación basada en país, atribución o atributos personalizados. Si una obtención más rápida supera estos inconvenientes para tu caso de uso, utiliza `getOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getOnboarding` como se describe [arriba](#fetch-onboarding). ::: ```dart showLineNumbers try { final onboarding = await Adapty().getOnboardingForDefaultAudience(placementId: 'YOUR_PLACEMENT_ID'); } on AdaptyError catch (adaptyError) { // handle error } catch (e) { // handle unknown error } ``` Parámetros: | Parámetro | Presencia | Descripción | |-----------------|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen conexiones inestables a internet, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, puede que los usuarios no reciban los últimos datos, pero experimentarán tiempos de carga más rápidos independientemente de lo inestable que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la última versión de tus onboardings y asegurar la fiabilidad incluso cuando la conexión a internet es escasa.</p> | --- # File: flutter-present-onboardings --- --- title: "Presentar onboardings en Flutter SDK" description: "Aprende a presentar onboardings de forma efectiva para impulsar más conversiones." --- Si has personalizado un onboarding con el builder, no necesitas preocuparte por renderizarlo en el código de tu app Flutter para mostrárselo al usuario. Ese onboarding ya contiene tanto lo que debe mostrarse como la forma en que debe hacerse. Antes de empezar, asegúrate de que: 1. Tienes instalado el [SDK de Flutter de Adapty](sdk-installation-flutter) versión 3.8.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). El SDK de Flutter de Adapty ofrece dos formas de presentar onboardings: - **Pantalla independiente** - **Widget embebido** ## Presentar como pantalla independiente \{#present-as-standalone-screen\} Para mostrar un onboarding como pantalla independiente, usa el método `onboardingView.present()` en el `onboardingView` creado por el método `createOnboardingView`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el onboarding de nuevo, llama a `createOnboardingView` otra vez para crear una nueva instancia de `onboardingView`. :::warning Reutilizar el mismo `onboardingView` sin recrearlo puede provocar un error `AdaptyUIError.viewAlreadyPresented`. ::: ```javascript showLineNumbers title="Flutter" try { await onboardingView.present(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ### Cerrar el onboarding \{#dismiss-the-onboarding\} Cuando necesites cerrar el onboarding por código, usa el método `dismiss()`: ```dart showLineNumbers title="Flutter" try { await onboardingView.dismiss(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ### Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el onboarding en iOS pasando el parámetro `iosPresentationStyle` al método `present()`. El parámetro acepta los valores `AdaptyUIIOSPresentationStyle.fullScreen` (predeterminado) o `AdaptyUIIOSPresentationStyle.pageSheet`. ```dart showLineNumbers try { await onboardingView.present(iosPresentationStyle: AdaptyUIIOSPresentationStyle.pageSheet); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ## Embeber en la jerarquía de widgets \{#embed-in-widget-hierarchy\} Para embeber un onboarding dentro de tu árbol de widgets existente, usa el widget `AdaptyUIOnboardingPlatformView` directamente en la jerarquía de widgets de Flutter. ```javascript showLineNumbers title="Flutter" AdaptyUIOnboardingPlatformView( onboarding: onboarding, // The onboarding object you fetched onDidFinishLoading: (meta) { }, onDidFailWithError: (error) { }, onCloseAction: (meta, actionId) { }, onPaywallAction: (meta, actionId) { }, onCustomAction: (meta, actionId) { }, onStateUpdatedAction: (meta, elementId, params) { }, onAnalyticsEvent: (meta, event) { }, ) ``` :::note Para que la platform view de Android funcione, asegúrate de que tu `MainActivity` extiende `FlutterFragmentActivity`: ```kotlin showLineNumbers title="Kotlin" class MainActivity : FlutterFragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } } ``` ::: ## Loader durante el onboarding \{#loader-during-onboarding\} Al presentar un onboarding, puede que notes una breve pantalla de carga entre tu splash screen y el onboarding mientras se inicializa la vista subyacente. Puedes gestionar esto de distintas formas según tus necesidades. #### Controlar el splash screen con onDidFinishLoading \{#control-splash-screen-using-ondidfinishloading\} :::note Este enfoque solo está disponible cuando el onboarding se embebe como widget. No está disponible para la presentación como pantalla independiente. ::: El enfoque multiplataforma recomendado es mantener visible tu splash screen o superposición personalizada hasta que el onboarding se haya cargado por completo y luego ocultarla manualmente. Al usar el widget embebido, superpón tu propio widget sobre él y oculta la superposición cuando se dispare `onDidFinishLoading`: ```dart showLineNumbers title="Flutter" AdaptyUIOnboardingPlatformView( onboarding: onboarding, onDidFinishLoading: (meta) { // Hide your custom splash screen or overlay here }, // ... other callbacks ) ``` ### Personalizar el loader nativo \{#customize-native-loader\} :::important Este enfoque es específico de plataforma y requiere mantener código de UI nativo. No se recomienda a menos que ya mantengas capas nativas separadas en tu app. ::: Si necesitas personalizar el loader predeterminado, puedes reemplazarlo con layouts específicos de cada plataforma. Este enfoque requiere implementaciones separadas para Android e iOS: - **iOS**: Añade `AdaptyOnboardingPlaceholderView.xib` a tu proyecto de Xcode - **Android**: Crea `adapty_onboarding_placeholder_view.xml` en `res/layout` y define ahí un placeholder ## Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\} :::important La personalización de cómo se abren los enlaces en los onboardings está disponible a partir del SDK de Adapty v.3.15.1. ::: Por defecto, los enlaces en los onboardings se abren en un navegador in-app. Esto ofrece una experiencia fluida al mostrar las páginas web dentro de tu aplicación, sin que el usuario tenga que cambiar de app. Si prefieres abrir los enlaces en un navegador externo, puedes personalizar este comportamiento estableciendo el parámetro `externalUrlsPresentation` en `AdaptyWebPresentation.externalBrowser`: <Tabs> <TabItem value="standalone" label="Pantalla independiente" default> ```dart showLineNumbers title="Flutter" final onboardingView = await AdaptyUI().createOnboardingView( onboarding: onboarding, externalUrlsPresentation: AdaptyWebPresentation.externalBrowser, // default – AdaptyWebPresentation.inAppBrowser ); try { await onboardingView.present(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="embedded" label="Widget embebido"> ```dart showLineNumbers title="Flutter" AdaptyUIOnboardingPlatformView( onboarding: onboarding, externalUrlsPresentation: AdaptyWebPresentation.externalBrowser, // default – AdaptyWebPresentation.inAppBrowser onDidFinishLoading: (meta) { }, onDidFailWithError: (error) { }, onCloseAction: (meta, actionId) { }, onPaywallAction: (meta, actionId) { }, onCustomAction: (meta, actionId) { }, onStateUpdatedAction: (meta, elementId, params) { }, onAnalyticsEvent: (meta, event) { }, ) ``` </TabItem> </Tabs> ## Desactivar los rellenos de área segura (Android) \{#disable-safe-area-paddings-android\} Por defecto, en dispositivos Android, la vista del onboarding aplica automáticamente rellenos de área segura para evitar los elementos de la UI del sistema, como la barra de estado y la barra de navegación. Sin embargo, si quieres desactivar este comportamiento y tener control total sobre el layout, puedes hacerlo añadiendo un recurso booleano a tu app: 1. Ve a `android/app/src/main/res/values`. Si no existe el archivo `bools.xml`, créalo. 2. Añade el siguiente recurso: ```xml <resources> <bool name="adapty_onboarding_enable_safe_area_paddings">false</bool> </resources> ``` Ten en cuenta que los cambios se aplican de forma global a todos los onboardings de tu app. --- # File: flutter-handling-onboarding-events --- --- title: "Gestionar eventos de onboarding en Flutter SDK" description: "Gestiona eventos relacionados con el onboarding en Flutter usando Adapty." --- Los onboardings configurados con el builder generan eventos a los que tu app puede responder. La forma de gestionar estos eventos depende del enfoque de presentación que estés usando: - **Presentación en pantalla completa**: Requiere configurar un observador de eventos global que gestione los eventos de todas las vistas de onboarding - **Widget embebido**: Gestiona los eventos a través de parámetros de callback inline directamente en el widget Antes de empezar, asegúrate de que: 1. Has instalado el [SDK de Flutter de Adapty](sdk-installation-flutter) 3.8.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). ## Eventos de presentación en pantalla completa \{#full-screen-presentation-events\} ### Configurar el observador de eventos \{#set-up-event-observer\} Para gestionar eventos de onboardings en pantalla completa, implementa `AdaptyUIOnboardingsEventsObserver` y configúralo antes de presentarlo: ```javascript showLineNumbers title="Flutter" AdaptyUI().setOnboardingsEventsObserver(this); try { await onboardingView.present(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ### Gestionar eventos \{#handle-events\} Implementa estos métodos en tu observador: ```javascript showLineNumbers title="Flutter" void onboardingViewDidFinishLoading( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, ) { // Onboarding finished loading } void onboardingViewDidFailWithError( AdaptyUIOnboardingView view, AdaptyError error, ) { // Handle loading errors } void onboardingViewOnCloseAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String actionId, ) { // Handle close action view.dismiss(); } void onboardingViewOnPaywallAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String actionId, ) { // Dismiss onboarding before presenting paywall view.dismiss().then((_) { _openPaywall(actionId); }); } void onboardingViewOnCustomAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String actionId, ) { // Handle custom actions } void onboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String elementId, AdaptyOnboardingsStateUpdatedParams params, ) { // Handle user input updates } void onboardingViewOnAnalyticsEvent( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, AdaptyOnboardingsAnalyticsEvent event, ) { // Track analytics events } ``` ## Eventos del widget embebido \{#embedded-widget-events\} Al usar `AdaptyUIOnboardingPlatformView`, puedes gestionar eventos a través de parámetros de callback inline directamente en el widget. Ten en cuenta que los eventos se enviarán tanto a los callbacks del widget como al observador global (si está configurado), pero el observador global es opcional: ```javascript showLineNumbers title="Flutter" AdaptyUIOnboardingPlatformView( onboarding: onboarding, onDidFinishLoading: (meta) { // Onboarding finished loading }, onDidFailWithError: (error) { // Handle loading errors }, onCloseAction: (meta, actionId) { // Handle close action }, onPaywallAction: (meta, actionId) { _openPaywall(actionId); }, onCustomAction: (meta, actionId) { // Handle custom actions }, onStateUpdatedAction: (meta, elementId, params) { // Handle user input updates }, onAnalyticsEvent: (meta, event) { // Track analytics events }, ) ``` ## Tipos de eventos \{#event-types\} Las siguientes secciones describen los distintos tipos de eventos que puedes gestionar, independientemente del enfoque de presentación que estés usando. ### Gestionar acciones personalizadas \{#handle-custom-actions\} En el builder, puedes añadir una acción **custom** a un botón y asignarle un ID. <img src="/assets/shared/img/ios-events-1.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Después puedes usar este ID en tu código y gestionarlo como una acción personalizada. Por ejemplo, si un usuario pulsa un botón personalizado como **Login** o **Allow notifications**, el método delegado `onboardingController` se activará con el caso `.custom(id:)` y el parámetro `actionId` corresponde al **Action ID** del builder. Puedes crear tus propios IDs, como "allowNotifications". ```javascript // Full-screen presentation void onboardingViewOnCustomAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String actionId, ) { switch (actionId) { case 'login': _login(); break; case 'allow_notifications': _allowNotifications(); break; } } // Embedded widget onCustomAction: (meta, actionId) { _handleCustomAction(actionId); } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "actionId": "allowNotifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ``` </Details> ### Finalización de carga del onboarding \{#finishing-loading-onboarding\} Cuando un onboarding termina de cargarse, se activará este evento: ```javascript showLineNumbers title="Flutter" // Full-screen presentation void onboardingViewDidFinishLoading( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, ) { print('Onboarding loaded: ${meta.onboardingId}'); } // Embedded widget onDidFinishLoading: (meta) { print('Onboarding loaded: ${meta.onboardingId}'); } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ``` </Details> ### Cerrar el onboarding \{#closing-onboarding\} El onboarding se considera cerrado cuando el usuario pulsa un botón con la acción **Close** asignada. <img src="/assets/shared/img/ios-events-2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::important Ten en cuenta que debes gestionar qué ocurre cuando un usuario cierra el onboarding. Por ejemplo, debes dejar de mostrar el propio onboarding. ::: ```javascript showLineNumbers title="Flutter" // Full-screen presentation void onboardingViewOnCloseAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String actionId, ) { await view.dismiss(); } // Embedded widget onCloseAction: (meta, actionId) { Navigator.of(context).pop(); } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> ### Abrir un paywall \{#opening-a-paywall\} :::tip Gestiona este evento para abrir un paywall si quieres abrirlo dentro del onboarding. Si quieres abrirlo después de que el onboarding se cierre, hay una forma más directa: gestiona la acción de cierre y abre el paywall sin depender de los datos del evento. ::: Si el usuario pulsa un botón que abre un paywall, recibirás el ID de la acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más fluida de trabajar con paywalls en onboardings es que el ID de acción sea igual al ID del placement del paywall: Ten en cuenta que, en iOS, solo se puede mostrar una vista (paywall o onboarding) en pantalla al mismo tiempo. Si presentas un paywall encima de un onboarding, no puedes controlar el onboarding en segundo plano de forma programática. Intentar cerrar el onboarding cerrará el paywall en su lugar, dejando el onboarding visible. Para evitarlo, cierra siempre la vista del onboarding antes de presentar el paywall. ```javascript showLineNumbers title="Flutter" // Full-screen presentation void onboardingViewOnPaywallAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String actionId, ) { // Dismiss onboarding before presenting paywall view.dismiss().then((_) { _openPaywall(actionId); }); } Future<void> _openPaywall(String actionId) async { // Implement your paywall opening logic here } // Embedded widget onPaywallAction: (meta, actionId) { _openPaywall(actionId); } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ``` </Details> ### Seguimiento de navegación \{#tracking-navigation\} Recibes un evento de analítica cuando ocurren distintos eventos relacionados con la navegación durante el flujo de onboarding: ```javascript showLineNumbers title="Flutter" // Full-screen presentation void onboardingViewOnAnalyticsEvent( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, AdaptyOnboardingsAnalyticsEvent event, ) { trackEvent(event.type, meta.onboardingId); } // Embedded widget onAnalyticsEvent: (meta, event) { trackEvent(event.type, meta.onboardingId); } ``` El objeto `event` puede ser uno de los siguientes tipos: | Tipo | Descripción | |------------|-------------| | `onboardingStarted` | Cuando el onboarding se ha cargado | | `screenPresented` | Cuando se muestra cualquier pantalla | | `screenCompleted` | Cuando se completa una pantalla. Incluye `elementId` opcional (identificador del elemento completado) y `reply` opcional (respuesta del usuario). Se activa cuando el usuario realiza cualquier acción para salir de la pantalla. | | `secondScreenPresented` | Cuando se muestra la segunda pantalla | | `userEmailCollected` | Se activa cuando se recopila el email del usuario a través del campo de entrada | | `onboardingCompleted` | Se activa cuando el usuario llega a una pantalla con el ID `final`. Si necesitas este evento, [asigna el ID `final` a la última pantalla](design-onboarding). | | `unknown` | Para cualquier tipo de evento no reconocido. Incluye `name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) | Cada evento incluye información `meta` que contiene: | Campo | Descripción | |------------|-------------| | `onboardingId` | Identificador único del flujo de onboarding | | `screenClientId` | Identificador de la pantalla actual | | `screenIndex` | Posición de la pantalla actual en el flujo | | `screensTotal` | Número total de pantallas en el flujo | <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // onboardingStarted { "name": "onboarding_started", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } // screenPresented { "name": "screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 4 } } // screenCompleted { "name": "screen_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 }, "params": { "element_id": "profile_form", "reply": "success" } } // secondScreenPresented { "name": "second_screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // userEmailCollected { "name": "user_email_collected", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // onboardingCompleted { "name": "onboarding_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> --- # File: flutter-onboarding-input --- --- title: "Procesar datos de onboardings en Flutter SDK" description: "Guarda y usa datos de onboardings en tu app Flutter con el SDK de Adapty." --- Cuando tus usuarios responden a una pregunta de un cuestionario o introducen datos en un campo de texto, se invocará el método `onStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código. Por ejemplo: ```dart // Presentación a pantalla completa void onboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String elementId, AdaptyOnboardingsStateUpdatedParams params, ) { // Process data } // Widget embebido onStateUpdatedAction: (meta, elementId, params) { // Process data } ``` Consulta el formato de la acción [aquí](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyUIOnboardingPlatformView/onStateUpdatedAction.html). <Details> <summary>Ejemplos de datos guardados (el formato puede variar según tu implementación)</summary> ```javascript // Example of a saved select action { "elementId": "preference_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "preferences_screen", "screenIndex": 1, "screensTotal": 3 }, "params": { "type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Example of a saved multi-select action { "elementId": "interests_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 3 }, "params": { "type": "multiSelect", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Example of a saved input action { "elementId": "name_input", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "input", "value": { "type": "text", "value": "John Doe" } } } // Example of a saved date picker action { "elementId": "birthday_picker", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "datePicker", "value": { "day": 15, "month": 6, "year": 1990 } } } ``` </Details> ## Casos de uso \{#use-cases\} ### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\} Si quieres vincular inmediatamente los datos introducidos con el perfil del usuario y evitar pedirle la misma información dos veces, debes [actualizar el perfil del usuario](flutter-setting-user-attributes) con los datos del campo al gestionar la acción. Por ejemplo, si pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name` y quieres establecer el valor de ese campo como nombre del usuario, y también les pides que introduzcan su correo electrónico en el campo `email`, en el código de tu app podría verse así: ```dart showLineNumbers // Presentación a pantalla completa void onboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String elementId, AdaptyOnboardingsStateUpdatedParams params, ) { // Store user preferences or responses if (params is AdaptyOnboardingsInputParams) { final builder = AdaptyProfileParametersBuilder(); // Map elementId to appropriate profile field switch (elementId) { case 'name': if (params.input is AdaptyOnboardingsTextInput) { builder.setFirstName((params.input as AdaptyOnboardingsTextInput).value); } break; case 'email': if (params.input is AdaptyOnboardingsEmailInput) { builder.setEmail((params.input as AdaptyOnboardingsEmailInput).value); } break; } // Update profile Adapty().updateProfile(builder.build()).catchError((error) { // handle the error }); } } // Widget embebido onStateUpdatedAction: (meta, elementId, params) { // Store user preferences or responses if (params is AdaptyOnboardingsInputParams) { final builder = AdaptyProfileParametersBuilder(); // Map elementId to appropriate profile field switch (elementId) { case 'name': if (params.input is AdaptyOnboardingsTextInput) { builder.setFirstName((params.input as AdaptyOnboardingsTextInput).value); } break; case 'email': if (params.input is AdaptyOnboardingsEmailInput) { builder.setEmail((params.input as AdaptyOnboardingsEmailInput).value); } break; } // Update profile Adapty().updateProfile(builder.build()).catchError((error) { // handle the error }); } } ``` ### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\} Usando cuestionarios en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios una vez que completan el onboarding. Por ejemplo, puedes preguntarles sobre su experiencia con el deporte y mostrar distintos CTAs y productos a diferentes grupos de usuarios. 1. [Añade un cuestionario](onboarding-quizzes) en el constructor de onboarding y asigna IDs significativos a sus opciones. 2. Gestiona las respuestas del cuestionario según sus IDs y [establece atributos personalizados](flutter-setting-user-attributes) para los usuarios. ```dart showLineNumbers // Presentación a pantalla completa void onboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, String elementId, AdaptyOnboardingsStateUpdatedParams params, ) { // Handle quiz responses and set custom attributes if (params is AdaptyOnboardingsSelectParams) { final builder = AdaptyProfileParametersBuilder(); // Map quiz responses to custom attributes switch (elementId) { case 'experience': // Set custom attribute 'experience' with the selected value (beginner, amateur, pro) builder.setCustomStringAttribute(params.value, 'experience'); break; } // Update profile Adapty().updateProfile(builder.build()).catchError((error) { // handle the error }); } } // Widget embebido onStateUpdatedAction: (meta, elementId, params) { // Handle quiz responses and set custom attributes if (params is AdaptyOnboardingsSelectParams) { final builder = AdaptyProfileParametersBuilder(); // Map quiz responses to custom attributes switch (elementId) { case 'experience': // Set custom attribute 'experience' with the selected value (beginner, amateur, pro) builder.setCustomStringAttribute(params.value, 'experience'); break; } // Update profile Adapty().updateProfile(builder.build()).catchError((error) { // handle the error }); } } ``` 3. [Crea segmentos](segments) para cada valor de atributo personalizado. 4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado. 5. [Muestra un paywall](flutter-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](flutter-handling-onboarding-events#opening-a-paywall). --- # File: flutter-sdk-call-order --- --- title: "Orden de llamadas en el SDK de Flutter" description: "Evita perder el acceso premium, la atribución y los errores intermitentes #2002 llamando a los métodos del SDK de Adapty en el orden correcto." --- `Adapty().activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que se resuelva, el SDK no tiene estado. Cualquier llamada realizada antes o en paralelo con `activate()` falla con [`#2002 notActivated`](error-handling-on-flutter-react-native-unity#custom-network-codes). Si tu app autentica usuarios y recopilas un customer user ID después del lanzamiento, llama a `Adapty().identify()` en ese momento. No llames a métodos de acción de usuario hasta que `identify` se resuelva. Las llamadas que compiten con él fallan con [`#3006 profileWasChanged`](error-handling-on-flutter-react-native-unity#custom-network-codes), o aterrizan en el perfil anónimo creado en la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la propiedad de la instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `identify` y sigue trabajando con el perfil anónimo. Los SDKs de MMP y analítica (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera sus callbacks de UID antes de llamar a `Adapty().activate`. De lo contrario, el ID de MMP aterriza en un perfil anónimo temporal y no siempre se transfiere al identificado. Para más detalles sobre AppsFlyer, consulta [AppsFlyer](appsflyer). ## El orden correcto \{#the-correct-order\} Tu camino depende de dos factores: cuándo conoces el customer user ID y si usas un SDK de MMP o analítica. - **Pasos 2 y 5**: Obligatorios para todas las apps. Activa el SDK y luego llama a los métodos del SDK. - **Pasos 1 y 3**: Solo son necesarios si integras un SDK de MMP o analítica (AppsFlyer, Adjust, Branch, PostHog). - **Paso 4**: Solo es necesario si tu app autentica usuarios y recopila el customer user ID después del lanzamiento. Si tienes el customer user ID en el lanzamiento de la app, pásalo directamente a `activate()` (paso 2a). Este camino nunca crea un perfil anónimo, por lo que el paso 4 es innecesario. | Paso | Llamada | Cuándo | Notas | |------|----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | Inicializa tu SDK de MMP o analítica (AppsFlyer, Adjust, PostHog, Branch) | Al lanzar la app, lo primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerUID`. | | 2a | `Adapty().activate(configuration: ...)` con `withCustomerUserId` configurado en la configuración | Al lanzar la app, después del paso 1, si tienes el customer user ID | Recomendado. Nunca se crea un perfil anónimo. | | 2b | `Adapty().activate(configuration: ...)` sin `withCustomerUserId` | Al lanzar la app, después del paso 1, si no tienes el customer user ID (o nunca lo recopilas) | Adapty crea un perfil anónimo. | | 3 | `Adapty().setIntegrationIdentifier(key: ..., value: ...)` para cada MMP | Después del paso 2, antes de cualquier llamada de acción de usuario | Necesario para que los IDs de MMP aterricen en el perfil correcto. | | 4 | `await Adapty().identify(customerUserId)` | Después del paso 3 (o paso 2 si no hay MMP), antes del paso 5 — solo en el camino 2b con autenticación | Siempre usa `await`. Las llamadas concurrentes durante `identify` producen `#3006 profileWasChanged`. | | 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Después del paso 4 si llamas a `identify`; si no, después del paso 3 (o paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. | :::important Omitir estos pasos provoca la pérdida del acceso premium para usuarios que regresan, la ausencia de `appsflyer_id` en los perfiles y paywalls devueltos contra la audiencia incorrecta. ::: ## Instalaciones web2app y embudo web \{#web2app-and-web-funnel-installs\} Si los usuarios compran en un checkout web (Stripe, Paddle) e instalan después la app nativa, el primer `activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el customer user ID antes del lanzamiento de la app (desde tu flujo de autenticación o el referrer de instalación), pásalo directamente a `activate()`. De lo contrario, la compra web será invisible en el dispositivo hasta que llames a `identify("YOUR_USER_ID")` y luego a `restorePurchases`. Para los metadatos que debes enviar con cada checkout web, consulta: - [Stripe](stripe) - [Paddle](paddle) --- # File: flutter-optimize-paywall-fetching --- --- title: "Optimizar la carga de paywalls en Flutter SDK" description: "Carga paywalls de Adapty de forma fiable: tiempos, caché y patrones de respaldo para Flutter." --- Una carga fiable de paywalls en Flutter hace tres cosas: renderiza rápido, devuelve el paywall con la audiencia correcta y recurre a un respaldo cuando la red es lenta. Las reglas a continuación cubren los tiempos, la caché y los patrones de respaldo para lograrlo. :::tip Las reglas asumen que `Adapty().activate()` y `Adapty().identify()` ya se han resuelto. Consulta [Orden de llamadas en Flutter SDK](flutter-sdk-call-order). ::: ## Reglas y errores comunes \{#rules-and-pitfalls\} | Haz esto | No hagas esto | Por qué | |---|---|---| | Carga el placement que vas a mostrar. | Precarga todos los placements de forma concurrente al arrancar. | La precarga masiva bloquea el hilo principal y produce una pantalla en negro durante la ráfaga. | | Llama a `getPaywall` después de que la atribución haya tenido tiempo de resolverse — por ejemplo, 1–2 segundos después de `activate` o cuando se dispare `didUpdateProfileStream`. | Llama a `getPaywall` en `main()` antes de `runApp`. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia predeterminada y omite silenciosamente los segmentos y la personalización de ASA. | | Establece un `loadTimeout` y configura un [paywall de respaldo](fallback-paywalls) para cada placement. | Esperar indefinidamente a `getPaywall`. | Sin un tiempo de espera, los usuarios con mala conectividad ven una pantalla en blanco hasta que la red se resuelva — o cierran la app. | Consulta [Obtener paywalls y productos](fetch-paywalls-and-products-flutter) para la referencia de los parámetros `fetchPolicy` y `loadTimeout`, y [Placements](placements) para elegir el placement adecuado. ## Ajuste para mala conectividad \{#tune-for-poor-connectivity\} Para mercados con conectividad consistentemente deficiente (zonas rurales, transporte, regiones afectadas por enrutamiento): - Establece `fetchPolicy: AdaptyPaywallFetchPolicy.returnCacheDataElseLoad` en cada carga excepto la primera. - Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard. - Establece `loadTimeout` en 3–5 segundos y acepta el respaldo cuando se agote el tiempo. - No condicionales la visualización del paywall a `getProfile()`. Llama a `getPaywall` de forma independiente para que un perfil lento no bloquee la interfaz. --- # File: flutter-test --- --- title: "Test & release in Flutter SDK" description: "Aprende cómo comprobar el estado de suscripción en tu app Flutter con Adapty." --- Si ya has implementado el SDK de Adapty en tu app Flutter, querrás comprobar que todo está configurado correctamente y que las compras funcionan como se espera en las plataformas iOS y Android. Esto implica probar tanto la integración del SDK como el flujo de compra real con el entorno sandbox de Apple y el entorno de pruebas de Google Play. ## Prueba tu app \{#test-your-app\} Para realizar pruebas exhaustivas de tus compras in-app, consulta nuestras guías de pruebas por plataforma: [guía de pruebas para iOS](test-purchases-in-sandbox) y [guía de pruebas para Android](testing-on-android). ## Prepárate para el lanzamiento \{#prepare-for-release\} Antes de enviar tu app al store, sigue la [lista de verificación para el lanzamiento](release-checklist) para confirmar que: - La conexión con el store y las notificaciones del servidor están configuradas - Las compras se completan y se notifican a Adapty - El acceso se desbloquea y se restaura correctamente - Se cumplen los requisitos de privacidad y revisión --- # File: InvalidProductIdentifiers-flutter --- --- title: "Solución para el error Code-1000 noProductIDsFound en Flutter SDK" description: "Resuelve errores de identificador de producto inválido al gestionar suscripciones en Adapty." --- El error con código 1000, `noProductIDsFound`, indica que ninguno de los productos que solicitaste en el paywall está disponible para su compra en la App Store, aunque estén listados allí. Este error puede aparecer en ocasiones acompañado de una advertencia `InvalidProductIdentifiers`. Si la advertencia aparece sin un error, puedes ignorarla sin problema. Si encuentras el error `noProductIDsFound`, sigue estos pasos para resolverlo: ## Paso 1. Comprueba el Bundle ID \{#step-2-check-bundle-id\} --- no_index: true --- 1. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu app y ve a la sección **General** → **App Information**. 2. Copia el **Bundle ID** en la subsección **General Information**. <Zoom> <img src="/docs/img/afd5012-bundle_id_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 3. Abre la pestaña [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) desde el menú superior de Adapty y pega el valor copiado en el campo **Bundle ID**. <Zoom> <img src="/docs/img/2d64163-bundle_id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 4. Vuelve a la página **App information** en App Store Connect y copia el **Apple ID** desde allí. 5. En la página [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) del Adapty Dashboard, pega el ID en el campo **Apple app ID**. ## Paso 2. Comprueba los productos \{#step-3-check-products\} 1. Ve a **App Store Connect** y navega a [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) en el menú de la izquierda. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripciones. Verás tus productos listados en la sección **Subscriptions**. 3. Asegúrate de que el producto que estás probando esté marcado como **Ready to Submit**. <img src="/assets/shared/img/ready-to-submit.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Compara el ID del producto de la tabla con el que aparece en la pestaña [**Products**](https://app.adapty.io/products) del Adapty Dashboard. Si los IDs no coinciden, copia el ID del producto de la tabla y [crea un producto](create-product) con ese ID en el Adapty Dashboard. <img src="/assets/shared/img/product-id-copy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 3. Comprueba la disponibilidad del producto \{#step-4-check-product-availability\} 1. Vuelve a **App Store Connect** y abre la misma sección **Subscriptions**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripciones para ver tus productos. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hasta la sección **Availability** y comprueba que todos los países y regiones requeridos estén listados. <img src="/assets/shared/img/product-availability.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 4. Comprueba los precios del producto \{#step-5-check-product-prices\} 1. De nuevo, ve a la sección **Monetization** → **Subscriptions** en **App Store Connect**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripciones. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hacia abajo hasta **Subscription Pricing** y despliega la sección **Current Pricing for New Subscribers**. <img src="/assets/shared/img/check-prices.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Asegúrate de que todos los precios requeridos estén listados. <img src="/assets/shared/img/product-pricing.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 5. Comprueba que el estado de pago de la app, la cuenta bancaria y los formularios fiscales estén activos \{#step-5-check-app-paid-status-bank-account-and-tax-forms-are-active\} 1. En la página de inicio de [**App Store Connect**](https://appstoreconnect.apple.com/), haz clic en **Business**. <img src="/assets/shared/img/business.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Selecciona el nombre de tu empresa. <img src="/assets/shared/img/business-name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Desplázate hacia abajo y comprueba que tu **Paid Apps Agreement**, tu **Bank Account** y tus **Tax forms** aparezcan como **Active**. <img src="/assets/shared/img/appstore-connect-status.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Siguiendo estos pasos, deberías poder resolver la advertencia `InvalidProductIdentifiers` y tener tus productos disponibles en la store. ## Paso 6. Recrea el producto si está bloqueado \{#step-6-recreate-the-product-if-its-stuck\} Es posible que los pasos 1–5 pasen todos correctamente —estado `Approved`, Bundle ID coincidente, API key válida— y aun así el SDK devuelva `1000 noProductIDsFound`. En ese caso, puede que el producto esté bloqueado en el registro de Apple. El registro de productos de Apple entra ocasionalmente en un estado en el que un producto existe en la interfaz de App Store Connect pero no está expuesto en la ruta de búsqueda de StoreKit. Elimina el producto en App Store Connect y vuelve a crearlo con el mismo ID de producto. Espera hasta 24 horas tras la recreación para que los cambios se propaguen. --- # File: cantMakePayments-flutter --- --- title: "Solución al error Code-1003 cantMakePayment en Flutter SDK" description: "Resuelve el error de realización de pagos al gestionar suscripciones en Adapty." --- El error 1003, `cantMakePayments`, indica que no es posible realizar compras in-app en este dispositivo. Si encuentras el error `cantMakePayments`, normalmente se debe a una de estas razones: - Restricciones del dispositivo: El error no está relacionado con Adapty. Consulta las soluciones más abajo. - Configuración del modo Observer: El método `makePurchase` y el modo Observer no pueden usarse al mismo tiempo. Consulta la sección más abajo. ## Problema: Restricciones del dispositivo \{#issue-device-restrictions\} | Problema | Solución | |-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| | Restricciones de Screen Time | Desactiva las restricciones de compras in-app en [Screen Time](https://support.apple.com/en-us/102470) | | Cuenta suspendida | Contacta con el soporte de Apple para resolver problemas con la cuenta | | Restricciones regionales | Usa una cuenta de App Store de una región compatible | ## Problema: Usar el modo Observer y makePurchase a la vez \{#issue-using-both-observer-mode-and-makepurchase\} Si usas `makePurchases` para gestionar las compras, no necesitas el modo Observer. El [modo Observer](observer-vs-full-mode) solo es necesario si implementas la lógica de compra tú mismo. Por lo tanto, si usas `makePurchase`, puedes eliminar sin problema la activación del modo Observer del código de inicialización del SDK. --- # File: flutter-migration-guide-310 --- --- title: "Guía de migración al SDK de Adapty para Flutter 3.10.0" description: "" --- El SDK de Adapty 3.10.0 es una versión principal que incorpora mejoras que, sin embargo, pueden requerir algunos pasos de migración por tu parte: 1. Actualiza el método `makePurchase` para usar `AdaptyPurchaseParameters` en lugar de parámetros individuales. 2. Reemplaza `vendorProductIds` por `productIdentifiers` en el modelo `AdaptyPaywall`. ## Actualizar el método makePurchase \{#update-makepurchase-method\} El método `makePurchase` ahora usa `AdaptyPurchaseParameters` en lugar de los argumentos individuales `subscriptionUpdateParams` e `isOfferPersonalized`. Esto proporciona mayor seguridad de tipos y permite una mayor extensibilidad de los parámetros de compra en el futuro. ```diff showLineNumbers - final purchaseResult = await adapty.makePurchase( - product: product, - subscriptionUpdateParams: subscriptionUpdateParams, - isOfferPersonalized: true, - ); + final parameters = AdaptyPurchaseParametersBuilder() + ..setSubscriptionUpdateParams(subscriptionUpdateParams) + ..setIsOfferPersonalized(true) + ..setObfuscatedAccountId('your-account-id') + ..setObfuscatedProfileId('your-profile-id'); + final purchaseResult = await adapty.makePurchase( + product: product, + parameters: parameters.build(), + ); ``` Si no necesitas parámetros adicionales, puedes usar simplemente: ```dart showLineNumbers final purchaseResult = await adapty.makePurchase( product: product, ); ``` ## Actualizar el uso del modelo AdaptyPaywall \{#update-adaptyp-aywall-model-usage\} La propiedad `vendorProductIds` ha quedado obsoleta en favor de `productIdentifiers`. La nueva propiedad devuelve objetos `AdaptyProductIdentifier` en lugar de cadenas de texto simples, lo que ofrece información de producto con una estructura más organizada. ```diff showLineNumbers - paywall.vendorProductIds.map((vendorId) => - ListTextTile(title: vendorId) - ).toList() + paywall.productIdentifiers.map((productId) => + ListTextTile(title: productId.vendorProductId) + ).toList() ``` El objeto `AdaptyProductIdentifier` proporciona acceso al ID del producto del proveedor a través de la propiedad `vendorProductId`, manteniendo la misma funcionalidad y ofreciendo una mejor estructura para mejoras futuras. ## Compatibilidad con versiones anteriores \{#backward-compatibility\} Ambos cambios mantienen la compatibilidad con versiones anteriores: - Los parámetros antiguos en `makePurchase` están obsoletos, pero siguen funcionando - La propiedad `vendorProductIds` está obsoleta, pero sigue siendo accesible - El código existente seguirá funcionando, aunque verás advertencias de obsolescencia Te recomendamos actualizar tu código para usar las nuevas API y garantizar la compatibilidad futura, además de aprovechar la mayor seguridad de tipos y extensibilidad. --- # File: flutter-migration-guide-38 --- --- title: "Migrar el SDK de Adapty para Flutter a v. 3.8" description: "Migra al SDK de Adapty para Flutter v3.8 para obtener mejor rendimiento y nuevas funciones de monetización." --- El SDK 3.8.0 de Adapty es una versión mayor que incluye mejoras que pueden requerir algunos pasos de migración por tu parte. 1. Actualiza los nombres de la clase observadora y sus métodos. 2. Actualiza el nombre del método de paywalls de respaldo. 3. Actualiza el nombre de la clase de vista en los métodos de manejo de eventos. ## Actualiza los nombres de la clase observadora y sus métodos \{#update-observer-class-and-method-names\} La clase observadora y su método de registro han sido renombrados: ```diff showLineNumbers - class MyObserver extends AdaptyUIObserver { + class MyObserver extends AdaptyUIPaywallsEventsObserver { @override void paywallViewDidPerformAction(AdaptyUIView view, AdaptyUIAction action) { // Handle action } } // Register observer - AdaptyUI().setObserver(this); + AdaptyUI().setPaywallsEventsObserver(this); ``` ## Actualiza el nombre del método de paywalls de respaldo \{#update-fallback-paywalls-method-name\} El método para configurar los paywalls de respaldo ha sido simplificado: ```diff showLineNumbers try { - await Adapty.setFallbackPaywalls(assetId); + await Adapty.setFallback(assetId); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` ## Actualiza el nombre de la clase de vista en los métodos de manejo de eventos \{#update-view-class-name-in-event-handling-methods\} Todos los métodos de manejo de eventos ahora usan la nueva clase `AdaptyUIPaywallView` en lugar de `AdaptyUIView`: ```diff showLineNumbers - void paywallViewDidPerformAction(AdaptyUIView view, AdaptyUIAction action) + void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action) - void paywallViewDidSelectProduct(AdaptyUIView view, AdaptyPaywallProduct product) + void paywallViewDidSelectProduct(AdaptyUIPaywallView view, AdaptyPaywallProduct product) - void paywallViewDidStartPurchase(AdaptyUIView view, AdaptyPaywallProduct product) + void paywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) - void paywallViewDidFinishPurchase(AdaptyUIView view, AdaptyPaywallProduct product, AdaptyProfile profile) + void paywallViewDidFinishPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyProfile profile) - void paywallViewDidFailPurchase(AdaptyUIView view, AdaptyPaywallProduct product, AdaptyError error) + void paywallViewDidFailPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error) - void paywallViewDidFinishRestore(AdaptyUIView view, AdaptyProfile profile) + void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) - void paywallViewDidFailRestore(AdaptyUIView view, AdaptyError error) + void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) - void paywallViewDidFailLoadingProducts(AdaptyUIView view, AdaptyIOSProductsFetchPolicy? fetchPolicy, AdaptyError error) + void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyIOSProductsFetchPolicy? fetchPolicy, AdaptyError error) - void paywallViewDidFailRendering(AdaptyUIView view, AdaptyError error) + void paywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) ``` --- # File: migration-to-flutter-sdk-34 --- --- title: "Migrar Adapty Flutter SDK a la v. 3.4" description: "Migra al Adapty Flutter SDK v3.4 para mejor rendimiento y nuevas funciones de monetización." --- Adapty SDK 3.4.0 es una versión mayor que introduce mejoras que requieren pasos de migración por tu parte. ## Actualizar los archivos de paywall de respaldo \{#update-fallback-paywall-files\} Actualiza tus archivos de paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK: 1. [Descarga los archivos de paywall de respaldo actualizados](fallback-paywalls) desde el Adapty Dashboard. 2. [Reemplaza los paywalls de respaldo existentes en tu app](flutter-use-fallback-paywalls) con los nuevos archivos. ## Actualizar la implementación del modo Observer \{#update-implementation-of-observer-mode\} Si usas el modo Observer, asegúrate de actualizar su implementación. Anteriormente, se usaban distintos métodos para reportar transacciones a Adapty. En la nueva versión, el método `reportTransaction` debe usarse de forma consistente tanto en Android como en iOS. Este método reporta explícitamente cada transacción a Adapty, asegurando que sea reconocida. Si se usó un paywall, pasa el ID de variación para vincular la transacción con él. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: ```diff showLineNumbers - // every time when calling transaction.finish() - if (Platform.isAndroid) { - try { - await Adapty().restorePurchases(); - } on AdaptyError catch (adaptyError) { - // handle the error - } catch (e) { - } - } try { // every time when calling transaction.finish() await Adapty().reportTransaction( "YOUR_TRANSACTION_ID", variationId: "PAYWALL_VARIATION_ID", // optional ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` --- # File: migration-to-flutter330 --- --- title: "Migrar el SDK de Adapty Flutter a v. 3.3" description: "Migra al SDK de Adapty Flutter v3.3 para mejor rendimiento y nuevas funcionalidades de monetización." --- Adapty SDK 3.3.0 es una versión mayor que incluye mejoras que pueden requerir algunos pasos de migración de tu parte. 1. Actualiza el método para proporcionar paywalls de respaldo. 2. Elimina el método `getProductsIntroductoryOfferEligibility`. 3. Actualiza las configuraciones de integración para Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase y Google Analytics, Mixpanel, OneSignal, Pushwoosh. 4. Actualiza la implementación del modo Observer. ## Actualiza el método para proporcionar paywalls de respaldo \{#update-method-for-providing-fallback-paywalls\} Anteriormente, el método requería el paywall de respaldo como una cadena JSON (`jsonString`), pero ahora recibe la ruta al archivo de respaldo local (`assetId`). ```diff showLineNumbers import 'dart:async' show Future; import 'dart:io' show Platform; -import 'package:flutter/services.dart' show rootBundle; -final filePath = Platform.isIOS ? 'assets/ios_fallback.json' : 'assets/android_fallback.json'; -final jsonString = await rootBundle.loadString(filePath); +final assetId = Platform.isIOS ? 'assets/ios_fallback.json' : 'assets/android_fallback.json'; try { - await adapty.setFallbackPaywalls(jsonString); + await adapty.setFallbackPaywalls(assetId); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` Para ver el ejemplo de código completo, consulta la página [Usar paywalls de respaldo](flutter-use-fallback-paywalls). ## Elimina el método `getProductsIntroductoryOfferEligibility` \{#remove-getproductsintroductoryoffereligibility-method\} Antes del SDK de Adapty iOS 3.3.0, el objeto de producto siempre incluía las ofertas, independientemente de si el usuario era elegible. Tenías que verificar la elegibilidad manualmente antes de usar la oferta. Ahora, el objeto de producto solo incluye una oferta si el usuario es elegible. Esto significa que ya no necesitas verificar la elegibilidad: si hay una oferta presente, el usuario es elegible. ## Actualiza la configuración del SDK de integraciones de terceros \{#update-third-party-integration-sdk-configuration\} Para garantizar que las integraciones funcionen correctamente con el SDK de Adapty Flutter 3.3.0 y versiones posteriores, actualiza las configuraciones de tu SDK para las siguientes integraciones tal como se describe en las secciones a continuación. ### Adjust \{#adjust\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Adjust](adjust#connect-your-app-to-adjust). ```diff showLineNumbers import 'package:adjust_sdk/adjust.dart'; import 'package:adjust_sdk/adjust_config.dart'; try { final adid = await Adjust.getAdid(); if (adid == null) { // handle the error } + await Adapty().setIntegrationIdentifier( + key: "adjust_device_id", + value: adid, + ); final attributionData = await Adjust.getAttribution(); var attribution = Map<String, String>(); if (attributionData.trackerToken != null) attribution['trackerToken'] = attributionData.trackerToken!; if (attributionData.trackerName != null) attribution['trackerName'] = attributionData.trackerName!; if (attributionData.network != null) attribution['network'] = attributionData.network!; if (attributionData.adgroup != null) attribution['adgroup'] = attributionData.adgroup!; if (attributionData.creative != null) attribution['creative'] = attributionData.creative!; if (attributionData.clickLabel != null) attribution['clickLabel'] = attributionData.clickLabel!; if (attributionData.costType != null) attribution['costType'] = attributionData.costType!; if (attributionData.costAmount != null) attribution['costAmount'] = attributionData.costAmount!.toString(); if (attributionData.costCurrency != null) attribution['costCurrency'] = attributionData.costCurrency!; if (attributionData.fbInstallReferrer != null) attribution['fbInstallReferrer'] = attributionData.fbInstallReferrer!; - Adapty().updateAttribution( - attribution, - source: AdaptyAttributionSource.adjust, - networkUserId: adid, - ); + await Adapty().updateAttribution(attribution, source: "adjust"); } catch (e) { // handle the error } on AdaptyError catch (adaptyError) { // handle the error } ``` ### AirBridge \{#airbridge\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AirBridge](airbridge#connect-your-app-to-airbridge). ```diff showLineNumbers import 'package:airbridge_flutter_sdk/airbridge_flutter_sdk.dart'; final deviceUUID = await Airbridge.state.deviceUUID; try { - final builder = AdaptyProfileParametersBuilder() - ..setAirbridgeDeviceId(deviceUUID); - await Adapty().updateProfile(builder.build()); + await Adapty().setIntegrationIdentifier( + key: "airbridge_device_id", + value: deviceUUID, + ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` ### Amplitude \{#amplitude\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Amplitude](amplitude#sdk-configuration). ```diff showLineNumbers import 'package:amplitude_flutter/amplitude.dart'; final Amplitude amplitude = Amplitude.getInstance(instanceName: "YOUR_INSTANCE_NAME"); final deviceId = await amplitude.getDeviceId(); final userId = await amplitude.getUserId(); try { - final builder = AdaptyProfileParametersBuilder() - ..setAmplitudeDeviceId(deviceId) - ..setAmplitudeUserId(userId); - await adapty.updateProfile(builder.build()); + await Adapty().setIntegrationIdentifier( + key: "amplitude_user_id", + value: userId, + ); + await Adapty().setIntegrationIdentifier( + key: "amplitude_device_id", + value: deviceId, + ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` ### AppMetrica \{#appmetrica\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AppMetrica](appmetrica#sdk-configuration). ```diff showLineNumbers import 'package:appmetrica_plugin/appmetrica_plugin.dart'; final deviceId = await AppMetrica.deviceId; if (deviceId != null) { try { - final builder = AdaptyProfileParametersBuilder() - ..setAppmetricaDeviceId(deviceId) - ..setAppmetricaProfileId("YOUR_ADAPTY_CUSTOMER_USER_ID"); - - await adapty.updateProfile(builder.build()); + await Adapty().setIntegrationIdentifier( + key: "appmetrica_device_id", + value: deviceId, + ); + await Adapty().setIntegrationIdentifier( + key: "appmetrica_profile_id", + value: "YOUR_ADAPTY_CUSTOMER_USER_ID", + ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } } ``` ### AppsFlyer \{#appsflyer\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AppsFlyer](appsflyer#connect-your-app-to-appsflyer). ```diff showLineNumbers import 'package:appsflyer_sdk/appsflyer_sdk.dart'; AppsflyerSdk appsflyerSdk = AppsflyerSdk(<YOUR_OPTIONS>); appsflyerSdk.onInstallConversionData((data) async { try { final appsFlyerUID = await appsFlyerSdk.getAppsFlyerUID(); - await Adapty().updateAttribution( - data, - source: AdaptyAttributionSource.appsflyer, - networkUserId: appsFlyerUID, - ); + await Adapty().setIntegrationIdentifier( + key: "appsflyer_id", + value: appsFlyerUID, + ); + + await Adapty().updateAttribution(data, source: "appsflyer"); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } }); appsflyerSdk.initSdk( registerConversionDataCallback: true, registerOnAppOpenAttributionCallback: true, registerOnDeepLinkingCallback: true, ); ``` ### Branch \{#branch\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Branch](branch#connect-your-app-to-branch). ```diff showLineNumbers FlutterBranchSdk.initSession().listen((data) async { try { + await Adapty().setIntegrationIdentifier( + key: "branch_id", + value: <BRANCH_IDENTITY_ID>, + ); - await Adapty().updateAttribution(data, source: AdaptyAttributionSource.branch); + await Adapty().updateAttribution(data, source: "branch"); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ); ``` ### Firebase y Google Analytics \{#firebase-and-google-analytics\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Firebase y Google Analytics](firebase-and-google-analytics). ```diff showLineNumbers final appInstanceId = await FirebaseAnalytics.instance.appInstanceId; try { - final builder = AdaptyProfileParametersBuilder() - ..setFirebaseAppInstanceId(appInstanceId); - await adapty.updateProfile(builder.build()); + await Adapty().setIntegrationIdentifier( + key: "firebase_app_instance_id", + value: appInstanceId, + ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` ### Mixpanel \{#mixpanel\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Mixpanel](mixpanel#sdk-configuration). ```diff showLineNumbers final mixpanel = await Mixpanel.init("Your Token", trackAutomaticEvents: true); final distinctId = await mixpanel.getDistinctId(); try { - final builder = AdaptyProfileParametersBuilder() - ..setMixpanelUserId(distinctId); - await Adapty().updateProfile(builder.build()); + await Adapty().setIntegrationIdentifier( + key: "mixpanel_user_id", + value: distinctId, + ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` ### OneSignal \{#onesignal\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con OneSignal](onesignal#sdk-configuration). ```diff showLineNumbers OneSignal.shared.setSubscriptionObserver((changes) { final playerId = changes.to.userId; if (playerId != null) { - final builder = - AdaptyProfileParametersBuilder() - ..setOneSignalPlayerId(playerId); - // ..setOneSignalSubscriptionId(playerId); try { - Adapty().updateProfile(builder.build()); + await Adapty().setIntegrationIdentifier( + key: "one_signal_player_id", + value: playerId, + ); } on AdaptyError catch (adaptyError) { // handle error } catch (e) { // handle error } } }); ``` ### Pushwoosh \{#pushwoosh\} Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Pushwoosh](pushwoosh#sdk-configuration). ```diff showLineNumbers final hwid = await Pushwoosh.getInstance.getHWID; - final builder = AdaptyProfileParametersBuilder() - ..setPushwooshHWID(hwid); try { - await adapty.updateProfile(builder.build()); + await Adapty().setIntegrationIdentifier( + key: "pushwoosh_hwid", + value: hwid, + ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` ## Actualiza la implementación del modo Observer \{#update-observer-mode-implementation\} Actualiza la forma en que vinculas los paywalls a las transacciones. Anteriormente, usabas el método `setVariationId` para asignar el `variationId`. Ahora puedes incluir el `variationId` directamente al registrar la transacción usando el nuevo método `reportTransaction`. Consulta el ejemplo de código final en [Asociar paywalls con transacciones de compra en el modo Observer](report-transactions-observer-mode-flutter). :::warning No olvides registrar la transacción usando el método `reportTransaction`. Si omites este paso, Adapty no reconocerá la transacción, no otorgará niveles de acceso, no la incluirá en los análisis ni la enviará a las integraciones. ¡Este paso es esencial! ::: ```diff showLineNumbers try { - await Adapty().setVariationId("YOUR_TRANSACTION_ID", "PAYWALL_VARIATION_ID"); + // every time when calling transaction.finish() + await Adapty().reportTransaction( + "YOUR_TRANSACTION_ID", + variationId: "PAYWALL_VARIATION_ID", // optional + ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` --- # File: migration-to-flutter-sdk-v3 --- --- title: "Migrar el SDK de Adapty Flutter a la v. 3.0" description: "Migra al SDK de Adapty Flutter v3.0 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK de Adapty v.3.0 incorpora soporte para el nuevo e innovador [Adapty Paywall Builder](adapty-paywall-builder), la nueva versión de la herramienta sin código y fácil de usar para crear paywalls. Con su máxima flexibilidad y ricas capacidades de diseño, tus paywalls serán más efectivos y rentables. :::info Ten en cuenta que la librería AdaptyUI ha quedado obsoleta y ahora está incluida como parte del SDK de Adapty. ::: ## Eliminar el SDK de AdaptyUI \{#remove-adaptyui-sdk\} 1. AdaptyUI pasa a ser un módulo dentro del SDK de Adapty, así que elimina `adapty_ui_flutter` de tu archivo `pubspec.yaml`: ```diff showLineNumbers dependencies: + adapty_flutter: ^3.2.1 - adapty_flutter: ^2.10.3 - adapty_ui_flutter: ^2.1.3 ``` 2. Ejecuta: ```bash showLineNumbers title="Bash" flutter pub get ``` ## Configurar los SDK de Adapty \{#configure-adapty-sdks\} Antes era necesario usar los archivos `Adapty-Info.plist` y `AndroidManifest.xml` para configurar el SDK de Adapty. Ahora ya no es necesario usar archivos adicionales. En su lugar, puedes proporcionar todos los parámetros requeridos durante la activación. Solo tienes que configurar el SDK de Adapty una vez, normalmente al inicio del ciclo de vida de tu app. ### Activar el módulo Adapty del SDK de Adapty \{#activate-adapty-module-of-adapty-sdk\} 1. Elimina la importación del SDK de AdaptyUI de tu aplicación de la siguiente manera: ```diff showLineNumbers import 'package:adapty_flutter/adapty_flutter.dart'; - import 'package:adapty_ui_flutter/adapty_ui_flutter.dart'; ``` 2. Actualiza la activación del SDK de Adapty así: ```diff showLineNumbers try { - Adapty().activate(); + await Adapty().activate( + configuration: AdaptyConfiguration(apiKey: 'YOUR_API_KEY') + ..withLogLevel(AdaptyLogLevel.debug) + ..withObserverMode(false) + ..withCustomerUserId(null) + ..withIdfaCollectionDisabled(false) + ..withIpAddressCollectionDisabled(false), + ); } catch (e) { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | | ----------------------------------- | --------- | ------------------------------------------------------------ | | **PUBLIC_SDK_KEY** | requerido | La clave que puedes encontrar en el campo **Public SDK key** de la configuración de tu app en Adapty: [**App settings**-> pestaña **General** -> subsección **API keys**](https://app.adapty.io/settings/general) | | **withLogLevel** | opcional | Adapty registra errores y otra información relevante para dar visibilidad sobre el funcionamiento de tu app. Los niveles disponibles son:<ul><li> error: Solo se registran errores.</li><li> warn: Se registran errores y mensajes del SDK que no causan errores críticos pero merecen atención.</li><li> info: Se registran errores, advertencias y mensajes informativos importantes, como los del ciclo de vida de los distintos módulos.</li><li> verbose: Se registra cualquier información adicional que pueda ser útil durante la depuración, como llamadas a funciones, consultas a la API, etc.</li></ul> | | **withObserverMode** | opcional | <p>Un valor booleano que controla el [modo Observer](observer-vs-full-mode). Actívalo si gestionas las compras y el estado de las suscripciones tú mismo y usas Adapty solo para enviar eventos de suscripción y analíticas.</p><p>El valor predeterminado es `false`.</p><p></p><p>🚧 Al ejecutarse en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlas tú.</p> | | **withCustomerUserId** | opcional | Un identificador del usuario en tu sistema. Lo enviamos en eventos de suscripción y analíticos para atribuir los eventos al perfil correcto. También puedes buscar clientes por `customerUserId` en el menú [**Profiles and Segments**](https://app.adapty.io/profiles/users). | | **withIdfaCollectionDisabled** | opcional | <p>Ponlo en `true` para deshabilitar la recopilación y el uso compartido del IDFA.</p><p>la dirección IP del usuario.</p><p>El valor predeterminado es `false`.</p><p>Para más detalles sobre la recopilación del IDFA, consulta la sección [Integración de analíticas](analytics-integration#disable-collection-of-advertising-identifiers).</p> | | **withIpAddressCollectionDisabled** | opcional | <p>Ponlo en `true` para deshabilitar la recopilación y el uso compartido de la dirección IP del usuario.</p><p>El valor predeterminado es `false`.</p> | ### Activar el módulo AdaptyUI del SDK de Adapty \{#activate-adaptyui-module-of-adapty-sdk\} Solo necesitas configurar el módulo AdaptyUI si planeas usar el [Paywall Builder](adapty-paywall-builder): ```dart showLineNumbers title="Dart" try { final mediaCache = AdaptyUIMediaCacheConfiguration( memoryStorageTotalCostLimit: 100 * 1024 * 1024, // 100MB memoryStorageCountLimit: 2147483647, // 2^31 - 1, max int value in Dart diskStorageSizeLimit: 100 * 1024 * 1024, // 100MB ); await AdaptyUI().activate( configuration: AdaptyUIConfiguration(mediaCache: mediaCache), observer: <AdaptyUIObserver Implementation>, ); } catch (e) { // handle the error } ``` Ten en cuenta que la configuración de AdaptyUI es opcional; puedes activar el módulo AdaptyUI sin su configuración. Sin embargo, si usas la configuración, todos los parámetros son obligatorios. Parámetros: | Parámetro | Presencia | Descripción | | :------------------------------ | :-------- | :----------------------------------------------------------- | | **memoryStorageTotalCostLimit** | requerido | Límite de coste total del almacenamiento en bytes. | | **memoryStorageCountLimit** | requerido | Límite de número de elementos del almacenamiento en memoria. | | **diskStorageSizeLimit** | requerido | Límite de tamaño de archivo en disco del almacenamiento en bytes. 0 significa sin límite. | --- # End of Documentation _Generated on: 2026-05-15T20:13:57.528Z_ _Successfully processed: 39/39 files_ # IOS - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.530Z Total files: 41 --- # File: sdk-installation-ios --- --- title: "Instalar y configurar el SDK de iOS" description: "Guía paso a paso para instalar el SDK de Adapty en iOS para apps con suscripciones." --- El SDK de Adapty incluye dos módulos clave para integrarse fácilmente en tu app: - **Core Adapty**: El SDK esencial, necesario para que Adapty funcione correctamente en tu app. - **AdaptyUI**: Módulo opcional que necesitas si usas el [Adapty Paywall Builder](adapty-paywall-builder), una herramienta visual sin código para crear paywalls multiplataforma de forma sencilla. :::tip ¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app? Consulta nuestras [apps de ejemplo](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples), que muestran la configuración completa: cómo mostrar paywalls, realizar compras y otras funciones básicas. ::: Para ver una implementación completa paso a paso, también puedes ver los vídeos: <Tabs groupId="current-os" queryString> <TabItem value="swiftui" label="iOS (SwiftUI)" default> <div style={{ textAlign: 'center' }}> <iframe width="560" height="315" src="https://www.youtube.com/embed/cSChHc8k2zA?si=KhNFhqXccIzYwTcm" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> </div> </TabItem> <TabItem value="uikit" label="iOS (UIKit)" default> <div style={{ textAlign: 'center' }}> <iframe width="560" height="315" src="https://www.youtube.com/embed/WEUnlaAjSI0?si=sjXKVVb56tEHDKzJ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> </div> </TabItem> </Tabs> ## Requisitos \{#requirements\} Aunque el SDK es técnicamente compatible con iOS 13.0+ para el módulo principal, en la práctica se requiere iOS 15.0+ porque: - Todas las funciones de StoreKit 2 requieren iOS 15.0+ - El módulo AdaptyUI solo es compatible con iOS 15.0+ :::important Se requiere Adapty SDK 3.15.7+ al compilar con Xcode 26.4 o posterior. ::: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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. </Callout> ## Instalar el SDK de Adapty \{#install-adapty-sdk\} [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-iOS.svg?style=flat&logo=apple)](https://github.com/adaptyteam/AdaptySDK-iOS/releases) <Tabs> <TabItem value="spm" label="Swift Package Manager (recomendado)" default> En Xcode, ve a **File** -> **Add Package Dependency...**. Ten en cuenta que los pasos para añadir dependencias pueden variar según la versión de Xcode, así que consulta la documentación de Xcode si es necesario. 1. Introduce la URL del repositorio: ``` https://github.com/adaptyteam/AdaptySDK-iOS.git ``` 2. Selecciona la versión (se recomienda la última versión estable) y haz clic en **Add Package**. 3. En la ventana **Choose Package Products**, selecciona los módulos que necesitas: - **Adapty** (módulo principal) - **AdaptyUI** (opcional - solo si planeas usar el Paywall Builder) :::note Nota: - Para activar el [modo Kids](kids-mode), selecciona **Adapty_KidsMode** en lugar de **Adapty**. - No selecciones ningún otro paquete de la lista, no los necesitas. ::: 4. Haz clic en **Add Package** para completar la instalación. 5. **Verifica la instalación:** En el navegador de tu proyecto deberías ver "Adapty" (y "AdaptyUI" si lo seleccionaste) bajo **Package Dependencies**. </TabItem> <TabItem value="cocoapods" label="CocoaPods (heredado)" default> :::info CocoaPods está ahora en modo de mantenimiento y su desarrollo se ha detenido oficialmente. Recomendamos cambiar a [Swift Package Manager](sdk-installation-ios#install-adapty-sdk). ::: 1. Añade Adapty a tu `Podfile`. Elige los módulos que necesitas: 1. **Adapty** es el módulo obligatorio. 2. **AdaptyUI** es un módulo opcional que necesitas si planeas usar el [Adapty Paywall Builder](adapty-paywall-builder). ```shell showLineNumbers title="Podfile" pod 'Adapty' pod 'AdaptyUI' # optional module needed only for Paywall Builder ``` 2. Ejecuta: ```sh showLineNumbers title="Shell" pod install ``` Esto creará un archivo `.xcworkspace` para tu app. Usa este archivo para todo el desarrollo posterior. </TabItem> </Tabs> ## Activar el módulo Adapty del SDK de Adapty \{#activate-adapty-module-of-adapty-sdk\} Activa el SDK de Adapty en el código de tu app. :::note El SDK de Adapty solo necesita activarse una vez en tu app. ::: 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. :::important - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API 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. ::: <Tabs groupId="current-os" queryString> <TabItem value="swiftui" label="SwiftUI"> ```swift showLineNumbers @main struct YourApp: App { init() { // Configure Adapty SDK let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard Adapty.logLevel = .verbose // recommended for development and the first production release let config = configurationBuilder.build() // Activate Adapty SDK asynchronously Task { do { try await Adapty.activate(with: config) } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } var body: some Scene { WindowGroup { // Your content view } } } } ``` </TabItem> <TabItem value="swift" label="UIKit" default> ```swift showLineNumbers // In your AppDelegate class: // If you only use an AppDelegate, place the following code in the // application(_:didFinishLaunchingWithOptions:) method. // If you use a SceneDelegate, place the following code in the // scene(_:willConnectTo:options:) method. Task { do { let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard .with(logLevel: .verbose) // recommended for development and the first production release let config = configurationBuilder.build() try await Adapty.activate(with: config) } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } ``` </TabItem> </Tabs> :::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 iOS](ios-sdk-call-order) para ver la secuencia completa. ::: Ahora configura los paywalls en tu app: - Si usas el [Adapty Paywall Builder](adapty-paywall-builder), primero [activa el módulo AdaptyUI](#activate-adaptyui-module-of-adapty-sdk) a continuación, y luego sigue la [guía de inicio rápido del Paywall Builder](ios-quickstart-paywalls). - Si construyes tu propia interfaz de paywall, consulta la [guía de inicio rápido para paywalls personalizados](ios-quickstart-manual). ## Activar el módulo AdaptyUI del SDK de Adapty \{#activate-adaptyui-module-of-adapty-sdk\} Si planeas usar el [Paywall Builder](adapty-paywall-builder) y has [instalado el módulo AdaptyUI](sdk-installation-ios#install-adapty-sdk), también necesitas activar AdaptyUI. :::important En tu código, debes activar el módulo principal de Adapty antes de activar AdaptyUI. ::: <Tabs groupId="current-os" queryString> <TabItem value="swiftui" label="SwiftUI"> ```swift showLineNumbers title="Swift" @main struct YourApp: App { init() { // ...ConfigurationBuilder steps // Activate Adapty SDK asynchronously Task { do { try await Adapty.activate(with: config) try await AdaptyUI.activate() } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } // main body... } } ``` </TabItem> <TabItem value="uikit" label="UIKit" default> ```swift showLineNumbers title="UIKit" // If you only use an AppDelegate, place the following code in the // application(_:didFinishLaunchingWithOptions:) method. // If you use a SceneDelegate, place the following code in the // scene(_:willConnectTo:options:) method. Task { do { let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard .with(logLevel: .verbose) // recommended for development let config = configurationBuilder.build() try await Adapty.activate(with: config) try await AdaptyUI.activate() } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } ``` </TabItem> </Tabs> :::tip De forma opcional, al activar AdaptyUI puedes [personalizar la configuración de caché para los paywalls](#set-up-media-cache-configuration-for-adaptyui). ::: ## Configuración opcional \{#optional-setup\} ### Registro de eventos \{#logging\} #### Configurar el sistema de registro \{#set-up-the-logging-system\} Adapty registra errores e información importante para ayudarte a entender qué está ocurriendo. Los niveles disponibles son: | Nivel | Descripción | | ---------- | ------------------------------------------------------------ | | `error` | Solo se registran errores | | `warn` | Se registran errores y mensajes del SDK que no causan errores críticos pero merecen atención | | `info` | Se registran errores, advertencias y varios mensajes informativos | | `verbose` | Se registra cualquier información adicional que pueda ser útil durante la depuración, como llamadas a funciones, consultas a la API, etc. | ```swift showLineNumbers let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(logLevel: .verbose) // recommended for development ``` #### Redirigir los mensajes del sistema de registro \{#redirect-the-logging-system-messages\} Si necesitas enviar los mensajes de registro de Adapty a tu sistema o guardarlos en un archivo, usa el método `setLogHandler` e implementa tu lógica de registro personalizada dentro de él. Este handler recibe registros con el contenido del mensaje y su nivel de severidad. ```swift showLineNumbers title="Swift" Adapty.setLogHandler { record in writeToLocalFile("Adapty \(record.level): \(record.message)") } ``` ### 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 adicionales de seguridad de datos para cumplir con las directrices de la store o de tu país. #### Deshabilitar la recopilación y el uso compartido del IDFA \{#disable-idfa-collection-and-sharing\} Al activar el módulo Adapty, establece `idfaCollectionDisabled` en `true` para deshabilitar la recopilación y el uso compartido del IDFA. Usa este parámetro para cumplir con las directrices de revisión del App Store o para evitar activar el prompt de App Tracking Transparency cuando el IDFA no es necesario para tu app. El valor predeterminado es `false`. Para más detalles sobre la recopilación del IDFA, consulta la sección de [Integración de analíticas](analytics-integration#disable-collection-of-advertising-identifiers). ```swift showLineNumbers let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(idfaCollectionDisabled: true) ``` #### Deshabilitar la recopilación y el uso compartido de la IP \{#disable-ip-collection-and-sharing\} Al activar el módulo Adapty, establece `ipAddressCollectionDisabled` en `true` para deshabilitar 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 el RGPD o el CCPA) o reducir la recopilación de datos innecesaria cuando las funciones basadas en IP no son necesarias para tu app. ```swift showLineNumbers let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(ipAddressCollectionDisabled: true) ``` #### Configuración de caché de medios para paywalls en AdaptyUI \{#set-up-media-cache-configuration-for-adaptyui\} Ten en cuenta que la configuración de AdaptyUI es opcional. Puedes activar el módulo AdaptyUI sin su configuración. Sin embargo, si usas la configuración, todos los parámetros son obligatorios. ```swift showLineNumbers title="Swift" // Configure AdaptyUI let adaptyUIConfiguration = AdaptyUI.Configuration( mediaCacheConfiguration: .init( memoryStorageTotalCostLimit: 100 * 1024 * 1024, memoryStorageCountLimit: .max, diskStorageSizeLimit: 100 * 1024 * 1024 ) ) // Activate AdaptyUI AdaptyUI.activate(configuration: adaptyUIConfiguration) ``` Parámetros: | Parámetro | Presencia | Descripción | | :-------------------------- | :-------- | :----------------------------------------------------------- | | memoryStorageTotalCostLimit | obligatorio | Límite de coste total del almacenamiento en bytes. | | memoryStorageCountLimit | obligatorio | Límite de elementos del almacenamiento en memoria. | | diskStorageSizeLimit | obligatorio | Límite de tamaño del archivo en disco en bytes. 0 significa sin límite. | ### Comportamiento de finalización de transacciones \{#transaction-finishing-behavior\} :::info Esta función está disponible a partir de la versión 3.12.0 del SDK. ::: Por defecto, Adapty finaliza las transacciones automáticamente tras una validación exitosa. Sin embargo, si necesitas una validación avanzada de transacciones (como validación de recibos en el servidor, detección de fraude o lógica de negocio personalizada), puedes configurar el SDK para usar la finalización manual de transacciones. ```swift showLineNumbers title="Swift" let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(transactionsFinishBehavior: .manual) // .auto is the default ``` Consulta más detalles sobre cómo finalizar transacciones en la [guía](ios-transaction-management). ### Borrar datos al restaurar desde copia de seguridad \{#clear-data-on-backup-restore\} Cuando `clearDataOnBackup` está establecido 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 del usuario en los servidores de Adapty no se modifican. ::: ```swift showLineNumbers let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(clearDataOnBackup: true) // default – false ``` ## Resolución de problemas \{#troubleshooting\} #### Error de concurrencia de Swift 6 con Tuist \{#swift-6-concurrency-error-with-tuist\} Al compilar con [Tuist](https://tuist.dev/), puede que veas errores de compilación de concurrencia estricta de Swift 6. Los síntomas habituales incluyen errores de compatibilidad con el atributo `@Sendable` en `AdaptyUIBuilderLogic` u otros errores de Sendability entre módulos. Esto ocurre porque Tuist genera proyectos Xcode a partir de paquetes SPM pero no conserva la configuración `swift-tools-version: 6.0`. Como resultado, algunos targets de Adapty (`Adapty`, `AdaptyUI`, `AdaptyUIBuilder`) se compilan con las reglas de Swift 5 mientras que otros usan Swift 6, lo que genera errores de `@Sendable` entre módulos. **Solución**: Actualiza al SDK de Adapty **3.15.5** o posterior, que resuelve el problema independientemente de las versiones mixtas de Swift. **Solución alternativa**: Si no puedes actualizar, establece explícitamente Swift 6 para los tres targets de Adapty en tu configuración de Tuist: ```swift showLineNumbers targetSettings: [ "Adapty": .init().swiftVersion("6"), "AdaptyUI": .init().swiftVersion("6"), "AdaptyUIBuilder": .init().swiftVersion("6"), ] ``` #### Errores de compilación de Swift 6 causados por la sobreescritura de SWIFT_VERSION en el Podfile \{#swift-6-build-errors-caused-by-podfile-swift_version-override\} Al compilar para iOS con [CocoaPods](sdk-installation-ios#install-adapty-sdk), puede que veas errores de compilación de Swift 6 en los targets de los pods de Adapty. Los síntomas habituales incluyen errores de `@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 de la app puede permanecer en Swift 5 — solo los targets de los pods de Adapty (`Adapty`, `AdaptyUI`, `AdaptyUIBuilder`, `AdaptyLogger`) necesitan compilarse con Swift 6. La causa más común es un hook `post_install` en tu `Podfile` que sobreescribe `SWIFT_VERSION` para cada target de pod: ```ruby showLineNumbers title="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 de los pods de Adapty de la sobreescritura: ```ruby showLineNumbers title="Podfile" post_install do |installer| installer.pods_project.targets.each do |target| next if %w[Adapty AdaptyUI AdaptyUIBuilder AdaptyLogger].include?(target.name) target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = '5.9' end end end ``` Luego ejecuta `pod install` y vuelve a compilar. Para verificarlo, abre `Pods.xcodeproj`, selecciona el target del pod `Adapty` → **Build Settings** → **Swift Language Version**. Debería ser **Swift 6**. --- # File: ios-quickstart-paywalls --- --- title: "Activar compras usando paywalls en el SDK de iOS" description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app." --- Para activar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - [**Paywalls**](paywalls): configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única manera de recuperar productos, pero este diseño te permite modificar ofertas, precios y combinaciones de productos sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 distintos paywalls a diferentes usuarios. Adapty te ofrece tres formas de activar las compras en tu app. Elige la que mejor se adapte a los requisitos de tu aplicación: | Implementación | Complejidad | Cuándo usar | |----------------------------|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Paywall Builder | ✅ Fácil | [Creas un paywall completo y listo para compras en el editor 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 de forma transparente. | | Paywalls creados manualmente | 🟡 Medio | Implementas la UI del paywall en el código de tu app, pero aun así obtienes el objeto paywall desde Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](ios-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 paywall creado en el Paywall Builder de Adapty.** Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](making-purchases). ::: Para mostrar un paywall creado en el Paywall Builder de Adapty, solo necesitas hacer lo siguiente en el código de tu app: 1. **Obtener el paywall**: Obtén el paywall desde Adapty. 2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app. 3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con el paywall a la respuesta de tu app. Por ejemplo, abre enlaces o cierra el paywall cuando los usuarios pulsen botones. ## Antes de empezar \{#before-you-start\} Antes de comenzar, completa estos pasos: 1. [Conecta tu app al App Store](initial_ios) 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-ios) 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 la [CLI para desarrolladores](developer-cli-quickstart). ::: ## 1. Obtener el paywall creado en el Paywall Builder \{#1-get-the-paywall-created-in-the-paywall-builder\} Tus paywalls están asociados a placements configurados en el dashboard. Los placements te permiten mostrar distintos paywalls a diferentes audiencias o ejecutar [pruebas A/B](ab-tests). Para obtener un paywall creado en el Paywall Builder de Adapty, debes: 1. Obtener el objeto `paywall` por el ID de [placement](placements) usando el método `getPaywall` y comprobar si es un paywall creado en el builder. 2. Obtener la configuración de vista del paywall usando el método `getPaywallConfiguration`. La configuración de vista contiene los elementos de UI y el estilo necesarios para mostrar el paywall. :::important Para obtener la configuración de vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará. ::: ```swift func loadPaywall() async { let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID") guard paywall.hasViewConfiguration else { print("Paywall doesn't have view configuration") return } paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall) } ``` ## 2. Mostrar el paywall \{#2-display-the-paywall\} Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo. <Tabs groupId="current-os" queryString> <TabItem value="swiftui" label="SwiftUI" default> En SwiftUI, al mostrar el paywall también necesitas gestionar eventos. Algunos son opcionales, pero `didFailPurchase`, `didFinishRestore`, `didFailRestore` y `didFailRendering` son obligatorios. Para hacer pruebas, puedes copiar el código del fragmento de abajo para registrar estos errores. :::tip Gestionar `didFinishPurchase` no es obligatorio, pero es útil cuando quieres realizar acciones tras una compra exitosa. Si no implementas ese callback, el paywall se cerrará automáticamente. ::: ```swift .paywall( isPresented: $paywallPresented, paywallConfiguration: paywallConfiguration, didFailPurchase: { product, error in print("Purchase failed: \(error)") }, didFinishRestore: { profile in print("Restore finished successfully") }, didFailRestore: { error in print("Restore failed: \(error)") }, didFailRendering: { error in paywallPresented = false print("Rendering failed: \(error)") }, showAlertItem: $alertItem ) ``` </TabItem> <TabItem value="uikit" label="UIKit" default> ```swift func presentPaywall(with config: AdaptyUI.PaywallConfiguration) { let paywallController = AdaptyUI.paywallController( with: config, delegate: self ) present(paywallController, animated: true) } ``` </TabItem> </Tabs> :::info Para más detalles sobre cómo mostrar un paywall, consulta nuestra [guía](ios-present-paywalls). ::: ## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\} Cuando los usuarios pulsan botones en el paywall, el SDK de iOS gestiona automáticamente las compras, la restauración, el cierre del paywall y la apertura de enlaces. 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í se muestra el comportamiento predeterminado del botón de cierre. No necesitas añadirlo al código, pero aquí puedes ver cómo hacerlo si fuera necesario. :::tip Lee nuestras guías sobre cómo gestionar [acciones](handle-paywall-actions) y [eventos](ios-handling-events) de botones. ::: <Tabs groupId="current-os" queryString> <TabItem value="swiftui" label="SwiftUI" default> ```swift didPerformAction: { action in switch action { case let .close: paywallPresented = false // default behavior default: break } } ``` </TabItem> <TabItem value="uikit" label="UIKit" default> ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case let .close: controller.dismiss(animated: true) // default behavior break } } ``` </TabItem> </Tabs> ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Tu paywall está listo para mostrarse en la app. [Prueba tus compras en modo sandbox](test-purchases-in-sandbox) para asegurarte de que puedes completar una compra de prueba desde el paywall. Ahora necesitas [comprobar el nivel de acceso de los usuarios](ios-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í puedes ver cómo integrar todos los pasos de esta guía en tu app. <Tabs groupId="current-os" queryString> <TabItem value="swiftui" label="SwiftUI" default> ```swift struct ContentView: View { @State private var paywallPresented = false @State private var alertItem: AlertItem? @State private var paywallConfiguration: AdaptyUI.PaywallConfiguration? @State private var isLoading = false @State private var hasInitialized = false var body: some View { VStack { if isLoading { ProgressView("Loading...") } else { Text("Your App Content") } } .task { guard !hasInitialized else { return } await initializePaywall() hasInitialized = true } .paywall( isPresented: $paywallPresented, configuration: paywallConfiguration, didPerformAction: { action in switch action.type { case let .close: paywallPresented = false default: break } }, didFailPurchase: { product, error in print("Purchase failed: \(error)") }, didFinishRestore: { profile in print("Restore finished successfully") }, didFailRestore: { error in print("Restore failed: \(error)") }, didFailRendering: { error in print("Rendering failed: \(error)") }, showAlertItem: $alertItem ) } private func initializePaywall() async { isLoading = true defer { isLoading = false } await loadPaywall() paywallPresented = true } } private func loadPaywall() async { do { let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID") guard paywall.hasViewConfiguration else { print("Paywall doesn't have view configuration") return } paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall) } catch { print("Failed to load paywall: \(error)") } } } ``` </TabItem> <TabItem value="uikit" label="UIKit" default> ```swift class ViewController: UIViewController { private var paywallConfiguration: AdaptyUI.PaywallConfiguration? override func viewDidLoad() { super.viewDidLoad() Task { await initializePaywall() } } private func initializePaywall() async { do { paywallConfiguration = try await loadPaywall() if let paywallConfiguration { await MainActor.run { presentPaywall(with: paywallConfiguration) } } } catch { print("Error initializing paywall: \(error)") } } private func loadPaywall() async throws -> AdaptyUI.PaywallConfiguration? { let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID") guard paywall.hasViewConfiguration else { print("Paywall doesn't have view configuration") return nil } return try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall) } private func presentPaywall(with config: AdaptyUI.PaywallConfiguration) { let paywallController = AdaptyUI.paywallController(with: config, delegate: self) present(paywallController, animated: true) } } // MARK: - AdaptyPaywallControllerDelegate extension ViewController: AdaptyPaywallControllerDelegate { func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case let .close: controller.dismiss(animated: true) break } } func paywallController(_ controller: AdaptyUI.PaywallController, didFailPurchase product: AdaptyPaywallProduct, error: AdaptyError) { print("Purchase failed for \(product.vendorProductId): \(error)") guard error.adaptyErrorCode != .paymentCancelled else { return // Don't show alert for user cancellation } let message = switch error.adaptyErrorCode { case .paymentNotAllowed: "Purchases are not allowed on this device." default: "Purchase failed. Please try again." } let alert = UIAlertController(title: "Purchase Error", message: message, preferredStyle: .alert) let okAction = UIAlertAction(title: "OK", style: .default) { _ in } alert.addAction(okAction) present(alert, animated: true) } func paywallController(_ controller: AdaptyUI.PaywallController, didFinishRestore profile: AdaptyProfile) { print("Restore finished successfully") controller.dismiss(animated: true) } func paywallController(_ controller: AdaptyUI.PaywallController, didFailRestore error: AdaptyError) { print("Restore failed: \(error)") } func paywallController(_ controller: AdaptyUI.PaywallController, didFailRendering error: AdaptyError) { print("Rendering failed: \(error)") controller.dismiss(animated: true) } } ``` </TabItem> </Tabs> --- # File: ios-check-subscription-status --- --- title: "Comprobar el estado de suscripción en el SDK de iOS" description: "Aprende a comprobar el estado de suscripción en tu app de iOS con Adapty." --- Para decidir si los usuarios pueden acceder al contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil. Este artículo te muestra cómo acceder al estado del perfil para decidir qué deben ver los usuarios: si mostrarles un paywall o concederles acceso a las funciones de pago. ## Obtener el estado de suscripción \{#get-subscription-status\} Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones: - Llamar a `getProfile` si necesitas los datos más recientes del perfil de inmediato (como al iniciar la app) o quieres forzar una actualización. - Configurar **actualizaciones automáticas del perfil** para mantener una copia local que se actualice automáticamente cada vez que cambie el estado de la suscripción. :::important Por defecto, el nivel de acceso `premium` ya existe en Adapty. Si no necesitas configurar más de un nivel de acceso, puedes usar simplemente `premium`. ::: ### Obtener el perfil \{#get-profile\} La forma más sencilla de obtener el estado de suscripción es usar el método `getProfile` para acceder al perfil: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { let profile = try await Adapty.getProfile() if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get() { // check the access profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } } ``` </TabItem> </Tabs> ### Escuchar actualizaciones de suscripción \{#listen-to-subscription-updates\} Si quieres recibir actualizaciones del perfil automáticamente en tu app: 1. Conforma el protocolo `AdaptyDelegate` en el tipo que prefieras e implementa el método `didLoadLatestProfile`. Adapty llamará a este método automáticamente cada vez que cambie el estado de suscripción del usuario. En el ejemplo siguiente usamos un tipo `SubscriptionManager` para gestionar los flujos de suscripción y el perfil del usuario. Este tipo puede inyectarse como dependencia o configurarse como singleton en una app UIKit, o añadirse al entorno SwiftUI desde la estructura principal de la app. 2. Guarda los datos del perfil actualizado cuando se llame a este método, para poder usarlos en toda tu app sin necesidad de realizar peticiones de red adicionales. ```swift class SubscriptionManager: AdaptyDelegate { nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) { let hasAccess = profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false // Update UI, unlock content, etc. } } // Set delegate after Adapty activation Adapty.delegate = subscriptionManager ``` :::note Adapty llama automáticamente a `didLoadLatestProfile` cuando se inicia tu app, proporcionando datos de suscripción en caché aunque el dispositivo esté sin conexión. ::: ## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\} Cuando necesitas tomar decisiones inmediatas sobre mostrar paywalls o conceder acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en situaciones como el inicio de la app, al entrar en secciones premium o antes de mostrar contenido específico. <Tabs> <TabItem value="swiftui" label="SwiftUI" default> ```swift private func checkAccessLevel() async -> Bool { do { let profile = try await Adapty.getProfile() return profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false } catch { print("Error checking access level: \(error)") return false } } // In your initialization logic: let hasAccess = await checkAccessLevel() if !hasAccess { paywallPresented = true // Show paywall if no access } ``` </TabItem> <TabItem value="uikit" label="UIKit"> ```swift private func checkAccessLevel() async throws -> Bool { let profile = try await Adapty.getProfile() return profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false } // In your initialization logic: let hasAccess = try await checkAccessLevel() if !hasAccess { presentPaywall(with: paywallConfiguration) } ``` </TabItem> </Tabs> ## Próximos pasos \{#next-steps\} Ahora que sabes cómo seguir el estado de suscripción, [aprende a trabajar con perfiles de usuario](ios-quickstart-identify) para asegurarte de que se integra con tu sistema de autenticación existente y los permisos de acceso compartido de pago. Si no tienes tu propio sistema de autenticación, no es ningún problema: Adapty gestionará los usuarios por ti, aunque puedes leer la [guía](ios-quickstart-identify) para entender cómo funciona Adapty con usuarios anónimos. --- # File: ios-quickstart-identify --- --- title: "Identificar usuarios en el SDK de iOS" description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app." --- :::important Esta guía es para ti si tienes tu propio sistema de autenticación. Aquí aprenderás a trabajar con perfiles de usuario en Adapty para que se alinee con tu sistema de autenticación existente. ::: La forma de gestionar las compras de los usuarios depende del modelo de autenticación de tu app: - Si tu app no usa autenticación backend y no almacena datos de usuario, consulta la [sección sobre usuarios anónimos](#anonymous-users). - Si tu app tiene (o tendrá) autenticación backend, consulta la [sección sobre usuarios identificados](#identified-users). **Conceptos clave**: - Los **perfiles** son las entidades que necesita el SDK para funcionar. Adapty los crea automáticamente. - Pueden ser anónimos **(sin customer user ID)** o identificados **(con customer user ID)**. - Proporcionas el **customer user ID** para cruzar los perfiles de Adapty con tu sistema de autenticación interno. Estas son las diferencias entre usuarios anónimos e identificados: | | Usuarios anónimos | Usuarios identificados | |------------------------------|----------------------------------------------------------------|-------------------------------------------------------------------------------------| | **Gestión de compras** | Restauración de compras a nivel de store | Mantiene el historial de compras entre dispositivos mediante su customer user ID | | **Gestión de perfiles** | Nuevos perfiles en cada reinstalación | El mismo perfil entre sesiones y dispositivos | | **Persistencia de datos** | Los datos de usuarios anónimos están ligados a la instalación | Los datos de usuarios identificados persisten entre instalaciones de la app | ## Usuarios anónimos \{#anonymous-users\} Si no tienes autenticación backend, **no necesitas gestionar la autenticación en el código de la app**: 1. Cuando el SDK se activa en el primer inicio de la app, Adapty **crea un nuevo perfil para el usuario**. 2. Cuando el usuario realiza una compra en la app, esta se **asocia con su perfil de Adapty y su cuenta del store**. 3. Cuando el usuario **reinstala** la app o la instala en un **nuevo dispositivo**, Adapty **crea un nuevo perfil anónimo al activarse**. 4. Si el usuario ya ha realizado compras en tu app, por defecto, sus compras se sincronizan automáticamente desde el App Store al activarse el SDK. :::note Las restauraciones desde copia de seguridad se comportan de forma diferente a las reinstalaciones. Por defecto, cuando un usuario restaura desde una copia de seguridad, el SDK conserva los datos en caché y no crea un nuevo perfil. Puedes configurar este comportamiento con el ajuste `clearDataOnBackup`. [Más información](sdk-installation-ios#clear-data-on-backup-restore). ::: Con usuarios anónimos se crearán nuevos perfiles en cada instalación, pero eso no es un problema porque, en los análisis de Adapty, puedes [configurar qué se considerará una nueva instalación](general#4-installs-definition-for-analytics). Para los usuarios anónimos, debes contar las instalaciones por **ID de dispositivo**. En este caso, cada instalación de la app en un dispositivo se cuenta como una instalación, incluidas las reinstalaciones. ## Usuarios identificados \{#identified-users\} Tienes dos opciones para identificar usuarios en la app: - [**Durante el inicio de sesión/registro:**](#during-loginsignup) Si los usuarios inician sesión después de que arranque tu app, llama a `identify()` con un customer user ID cuando se autentiquen. - [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando se inicia la app, envíalo al llamar a `activate()`. :::important Por defecto, cuando Adapty recibe una compra de un Customer User ID que actualmente está asociado a otro Customer User ID, el nivel de acceso se comparte, de modo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o desactivar el uso compartido por completo. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles. ::: <img src="/assets/shared/img/identify-diagram.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Durante el inicio de sesión/registro \{#during-loginsignup\} Si identificas a los usuarios después del inicio de la app (por ejemplo, tras iniciar sesión o registrarse), usa el método `identify` para establecer su customer user ID. - Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual. - Si **ya has usado este customer user ID para identificar al usuario**, Adapty pasará a trabajar con el perfil asociado a ese customer user ID. :::important Los customer user IDs deben ser únicos para cada usuario. Si defines el valor del parámetro de forma fija, todos los usuarios se considerarán como uno solo. ::: Siempre usa `await` con `identify` antes de llamar a otros métodos del SDK. Las llamadas concurrentes producen `#3006 profileWasChanged` o recaen sobre el perfil anónimo. Consulta [Orden de llamadas en el SDK de iOS](ios-sdk-call-order). <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { try await Adapty.identify("YOUR_USER_ID") // Unique for each user } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID") { error in if let error { // handle the error } } ``` </TabItem> </Tabs> ### Durante la activación del SDK \{#during-the-sdk-activation\} Si ya conoces un customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado. Si conoces un customer user ID pero lo estableces solo después de la activación, eso significará que, al activarse, Adapty creará un nuevo perfil anónimo y pasará al existente solo después de que llames a `identify`. Puedes pasar un customer user ID existente (uno que hayas usado antes) o uno nuevo. Si pasas uno nuevo, el nuevo perfil creado al activarse se vinculará automáticamente al customer user ID. :::note Por defecto, la creación de perfiles anónimos no afecta a los dashboards de análisis, porque las instalaciones se cuentan según los IDs de dispositivo. Un ID de dispositivo representa una única instalación de la app desde el store en un dispositivo y se regenera solo después de que la app se reinstale. No depende de si es una primera instalación o una repetida, ni de si se usa un customer user ID existente. Crear un perfil (al activar el SDK o cerrar sesión), iniciar sesión o actualizar la app sin reinstalarla no genera eventos de instalación adicionales. Si quieres contar instalaciones basándote en usuarios únicos en lugar de dispositivos, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics). ::: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers // Place in the app main struct for SwiftUI or in AppDelegate for UIKit let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one. do { try await Adapty.activate(with: configurationBuilder.build()) } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers // Place in the app main struct for SwiftUI or in AppDelegate for UIKit let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one. Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } ``` </TabItem> </Tabs> ### Cerrar sesión de usuarios \{#log-users-out\} Si tienes un botón para que los usuarios cierren sesión, usa el método `logout`. :::important Cerrar la sesión de un usuario crea un nuevo perfil anónimo para ese usuario. ::: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { try await Adapty.logout() } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.logout { error in if error == nil { // successful logout } } ``` </TabItem> </Tabs> :::info Para volver a iniciar sesión en la app, usa el método `identify`. ::: ### Permitir compras sin inicio de sesión \{#allow-purchases-without-login\} Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, debes asegurarte de que mantendrán el acceso después de iniciar sesión: 1. Cuando un usuario sin sesión iniciada realiza una compra, Adapty la vincula a su ID de perfil anónimo. 2. Cuando el usuario inicia sesión en su cuenta, Adapty pasa a trabajar con su perfil identificado. - Si es un customer user ID nuevo (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que se mantiene todo el historial de compras. - Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), debes obtener el nivel de acceso actual después del cambio de perfil. Puedes llamar a [`getProfile`](ios-check-subscription-status) justo después de la identificación, o [escuchar las actualizaciones del perfil](ios-check-subscription-status) para que los datos se sincronicen automáticamente. ## Próximos pasos \{#next-steps\} ¡Enhorabuena! Has implementado la lógica de pago in-app en tu app. ¡Te deseamos todo lo mejor con la monetización! Para sacar aún más partido a Adapty, puedes explorar estos temas: - [**Testing**](test-purchases-in-sandbox): Comprueba que todo funciona como se espera - [**Onboardings**](ios-onboardings): Engancha a los usuarios con onboardings e impulsa la retención - [**Integraciones**](configuration): Integra con servicios de atribución de marketing y análisis con una sola línea de código - [**Establecer atributos de perfil personalizados**](setting-user-attributes): Añade atributos personalizados a los perfiles de usuario y crea segmentos para lanzar pruebas A/B o mostrar diferentes paywalls a distintos usuarios --- # File: adapty-cursor --- --- title: "Integra Adapty en tu app iOS con ayuda de IA" description: "Una guía paso a paso para integrar Adapty en tu app iOS usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA." --- Esta página cubre dos formas de integrar Adapty en tu app iOS. Usa la skill de integración del SDK que encontrarás a continuación para un flujo automatizado de extremo a extremo, o sigue el tutorial manual más abajo. ## Usa la skill de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\} La [skill adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza la integración de principio a fin: configuración del dashboard, instalación del SDK, paywall y verificación por etapas. El tutorial manual que encontrarás más abajo es la alternativa si tu herramienta no admite el formato Claude Skills. **Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI. ### Instalar \{#install\} Elige el comando para tu herramienta. La lista completa está en el [README de la skill](https://github.com/adaptyteam/adapty-sdk-integration-skill). - **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y luego `claude plugin install adapty-sdk-integration@adapty` desde tu shell. - **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`. - **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`. - **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de skills de tu herramienta. ### Ejecutar \{#run\} En tu proyecto, ejecuta `/adapty-sdk-integration`. La skill detecta tu plataforma y hace algunas preguntas de configuración. A continuación, guía la configuración del dashboard, la instalación del SDK, el paywall y la verificación — obteniendo la documentación relevante de Adapty en cada etapa. :::note La skill está en beta. Si se detiene o se comporta de forma inesperada, el tutorial manual de más abajo cubre cada etapa paso a paso. ::: ## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\} Adapty requiere algo de configuración en el dashboard antes de escribir cualquier código del SDK. Puedes hacerlo con una skill de LLM interactiva o manualmente desde el Dashboard. ### Enfoque con skill (recomendado) \{#skill-approach-recommended\} La skill de la CLI de Adapty permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin necesidad de abrir el Dashboard en cada paso. Solo necesitas [conectar tu store](integrate-payments) en el Dashboard. ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` Una vez añadida la skill, ejecuta `/adapty-cli` en tu agente. Te guiará por cada paso, incluido cuándo abrir el Dashboard para conectar tu store. ### Enfoque manual desde el Dashboard \{#dashboard-approach\} Si prefieres configurarlo todo manualmente, esto es lo que necesitas antes de escribir cualquier código. Tu LLM no puede consultar los valores del dashboard por ti — tendrás que proporcionárselos. 1. **Conecta tu store**: En el Adapty Dashboard, ve a **App settings → General**. Esto es obligatorio para que las compras funcionen. [Conectar App Store](integrate-payments) 2. **Copia tu clave pública del SDK**: En el Adapty Dashboard, ve a **App settings → General** y localiza la sección **API keys**. En el código, es la cadena que pasas a `Adapty.activate("YOUR_PUBLIC_SDK_KEY")`. 3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No referenciarás los productos directamente en el código — Adapty los entrega a través de paywalls. [Añadir productos](quickstart-products) 4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `Adapty.getPaywall("YOUR_PLACEMENT_ID")`. [Crear paywall](quickstart-paywalls) 5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena que se comprueba en `profile.accessLevels["premium"]`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago acceden a funciones diferentes según el producto (por ejemplo, un plan `basic` frente a un plan `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar. :::tip Una vez que tengas los cinco, estás listo para escribir código. Dile a tu LLM: "Mi clave pública del SDK es X, mi ID de placement es Y" para que pueda generar el código correcto de inicialización y obtención del paywall. ::: ### Configura cuando estés listo \{#set-up-when-ready\} Esto no es obligatorio para empezar a programar, pero lo necesitarás a medida que tu integración madure: - **Pruebas A/B**: Configúralas en la página **Placements**. No requieren cambios en el código. [Pruebas A/B](ab-tests) - **Paywalls y placements adicionales**: Añade más llamadas a `getPaywall` con diferentes IDs de placement. - **Integraciones de analíticas**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta las [integraciones de analíticas](analytics-integration) y las [integraciones de atribución](attribution-integration). ## Proporciona documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\} ### Usa Context7 (recomendado) \{#use-context7-recommended\} [Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. Tu LLM obtiene automáticamente la documentación adecuada según lo que le preguntes — sin necesidad de pegar URLs manualmente. Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta: ``` npx ctx7 setup ``` Esto detecta tu editor y configura el servidor Context7. Para la configuración manual, consulta el [repositorio de Context7 en GitHub](https://github.com/upstash/context7). Una vez configurado, haz referencia a la librería de Adapty en tus prompts: ``` Use the adaptyteam/adapty-docs library to look up how to install the iOS SDK ``` :::warning Aunque Context7 elimina la necesidad de pegar enlaces de documentación manualmente, el orden de implementación es importante. Sigue el [tutorial de implementación](#implementation-walkthrough) que encontrarás más abajo paso a paso para asegurarte de que todo funciona correctamente. ::: ### Usa la documentación en texto plano \{#use-plain-text-docs\} Puedes acceder a cualquier página de la documentación de Adapty como Markdown en texto plano. Añade `.md` al final de su URL o haz clic en **Copy for LLM** bajo el título del artículo. Por ejemplo: [adapty-cursor.md](https://adapty.io/docs/es/adapty-cursor.md). Cada etapa del [tutorial de implementación](#implementation-walkthrough) incluye un bloque "Envía esto a tu LLM" con enlaces `.md` para pegar. Para obtener más documentación a la vez, consulta los [archivos índice y los subconjuntos por plataforma](#plain-text-doc-index-files) más abajo. ## Tutorial de implementación \{#implementation-walkthrough\} El resto de esta guía recorre la integración de Adapty en orden de implementación. Cada etapa incluye la documentación que debes enviar a tu LLM, qué deberías ver al terminar y los problemas más frecuentes. ### Planifica tu integración \{#plan-your-integration\} Antes de empezar a escribir código, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA tiene un modo de planificación (como el modo plan de Cursor o Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir cualquier código. Dile a tu LLM qué enfoque usas para las compras — esto afecta a las guías que debe seguir: - [**Paywall Builder de Adapty**](adapty-paywall-builder): Creas paywalls en el editor sin código de Adapty y el SDK los renderiza automáticamente. - [**Paywalls creados manualmente**](making-purchases): Construyes tu propia UI del paywall en el código, pero sigues usando Adapty para obtener productos y gestionar compras. - [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analíticas e integraciones. ¿No sabes cuál elegir? Lee la [tabla comparativa en el inicio rápido](ios-quickstart-paywalls). ### Instala y configura el SDK \{#install-and-configure-the-sdk\} Instala el paquete del SDK de Adapty a través de Swift Package Manager en Xcode y actívalo con tu clave pública del SDK. Esta es la base — nada más funciona sin esto. **Guía:** [Instalar y configurar el SDK de Adapty](sdk-installation-ios) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/sdk-installation-ios.md ``` :::tip[Punto de control] - **Resultado esperado:** La app compila y se ejecuta. La consola de Xcode muestra el log de activación de Adapty. - **Problema frecuente:** "Public API key is missing" → comprueba que has reemplazado el marcador de posición con tu clave real de App settings. ::: ### Muestra paywalls y gestiona compras \{#show-paywalls-and-handle-purchases\} Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras. Prueba cada compra en el sandbox a medida que avanzas — no esperes al final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración. <Tabs groupId="paywall-approach"> <TabItem value="builder" label="Paywall Builder" default> **Guías:** - [Habilitar compras con paywalls (inicio rápido)](ios-quickstart-paywalls) - [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) - [Mostrar paywalls](ios-present-paywalls) - [Gestionar eventos del paywall](ios-handling-events) - [Responder a acciones de botones](handle-paywall-actions) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/ios-quickstart-paywalls.md - https://adapty.io/docs/es/get-pb-paywalls.md - https://adapty.io/docs/es/ios-present-paywalls.md - https://adapty.io/docs/es/ios-handling-events.md - https://adapty.io/docs/es/handle-paywall-actions.md ``` :::tip[Punto de control] - **Resultado esperado:** El paywall aparece con los productos configurados. Al pulsar un producto se activa el diálogo de compra en sandbox. - **Problema frecuente:** Paywall vacío o error en `getPaywall` → verifica que el ID del placement coincide exactamente con el del dashboard y que el placement tiene una audiencia asignada. ::: </TabItem> <TabItem value="manual" label="Paywalls manuales"> **Guías:** - [Habilitar compras en tu paywall personalizado (inicio rápido)](ios-quickstart-manual) - [Obtener paywalls y productos](fetch-paywalls-and-products) - [Renderizar un paywall diseñado con Remote Config](present-remote-config-paywalls) - [Realizar compras](making-purchases) - [Restaurar compras](restore-purchase) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/ios-quickstart-manual.md - https://adapty.io/docs/es/fetch-paywalls-and-products.md - https://adapty.io/docs/es/present-remote-config-paywalls.md - https://adapty.io/docs/es/making-purchases.md - https://adapty.io/docs/es/restore-purchase.md ``` :::tip[Punto de control] - **Resultado esperado:** Tu paywall personalizado muestra los productos obtenidos desde Adapty. Al pulsar un producto se activa el diálogo de compra en sandbox. - **Problema frecuente:** Array de productos vacío → verifica que el paywall tiene productos asignados en el dashboard y que el placement tiene una audiencia. ::: </TabItem> <TabItem value="observer" label="Observer mode"> **Guías:** - [Descripción general del modo Observer](observer-vs-full-mode) - [Implementar el modo Observer](implement-observer-mode) - [Reportar transacciones en modo Observer](report-transactions-observer-mode) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/observer-vs-full-mode.md - https://adapty.io/docs/es/implement-observer-mode.md - https://adapty.io/docs/es/report-transactions-observer-mode.md ``` :::tip[Punto de control] - **Resultado esperado:** Tras una compra en sandbox usando tu flujo de compra existente, la transacción aparece en el **Event Feed** del dashboard de Adapty. - **Problema frecuente:** Sin eventos → verifica que estás reportando las transacciones a Adapty y que las notificaciones del servidor de App Store están configuradas. ::: </TabItem> </Tabs> ### Comprueba el estado de la suscripción \{#check-subscription-status\} Tras una compra, comprueba el perfil del usuario para detectar un nivel de acceso activo y restringir el contenido premium. **Guía:** [Comprobar el estado de la suscripción](ios-check-subscription-status) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/ios-check-subscription-status.md ``` :::tip[Punto de control] - **Resultado esperado:** Tras una compra en sandbox, `profile.accessLevels["premium"]?.isActive` devuelve `true`. - **Problema frecuente:** `accessLevels` vacío tras la compra → comprueba que el producto tiene un nivel de acceso asignado en el dashboard. ::: ### Identifica a los usuarios \{#identify-users\} Vincula las cuentas de usuario de tu app a los perfiles de Adapty para que las compras persistan entre dispositivos. :::important Omite este paso si tu app no tiene autenticación. ::: **Guía:** [Identificar usuarios](ios-quickstart-identify) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/ios-quickstart-identify.md ``` :::tip[Punto de control] - **Resultado esperado:** Tras llamar a `Adapty.identify("your-user-id")`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado. - **Problema frecuente:** Llama a `identify` después de la activación pero antes de obtener paywalls para evitar una atribución anónima del perfil. ::: ### Prepárate para el lanzamiento \{#prepare-for-release\} Una vez que tu integración funcione en el sandbox, repasa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción. **Guía:** [Lista de verificación de lanzamiento](release-checklist) Envía esto a tu LLM: ``` Read these Adapty docs before releasing: - https://adapty.io/docs/es/release-checklist.md ``` :::tip[Punto de control] - **Resultado esperado:** Todos los elementos de la lista confirmados: conexión al store, notificaciones del servidor, flujo de compra, comprobaciones del nivel de acceso y requisitos de privacidad. - **Problema frecuente:** Notificaciones del servidor de App Store no configuradas → configúralas en **App settings → iOS SDK** o los eventos no aparecerán en el dashboard. ::: ## Archivos índice de documentación en texto plano \{#plain-text-doc-index-files\} Si necesitas dar a tu LLM un contexto más amplio más allá de páginas individuales, disponemos de archivos índice que listan o combinan toda la documentación de Adapty: - [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con enlaces `.md`. Un [estándar emergente](https://llmstxt.org/) para hacer los sitios web accesibles a los LLMs. Ten en cuenta que para algunos agentes de IA (p. ej., ChatGPT) tendrás que descargar `llms.txt` y subirlo al chat como archivo. - [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación del sitio de Adapty combinada en un único archivo. Muy grande — úsalo solo cuando necesites el panorama completo. - Subconjuntos específicos para iOS: [`ios-llms.txt`](https://adapty.io/docs/es/ios-llms.txt) e [`ios-llms-full.txt`](https://adapty.io/docs/es/ios-llms-full.txt): subconjuntos por plataforma que ahorran tokens en comparación con el sitio completo. --- # File: get-pb-paywalls --- --- title: "Obtener paywalls del Paywall Builder y su configuración en el SDK de iOS" description: "Aprende cómo recuperar paywalls de PB en Adapty para un mejor control de suscripciones en tu app de iOS." --- Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación. :::warning El nuevo Paywall Builder funciona con la versión 3.0 o superior del SDK de iOS. Para mostrar paywalls en Adapty SDK v2 diseñados con el Paywall Builder heredado, consulta [Mostrar paywalls diseñados con el Paywall Builder heredado](adapty-paywall-builder). ::: Ten en cuenta que este tema hace referencia a los paywalls personalizados con Paywall Builder. Si estás implementando tus paywalls de forma manual, consulta el tema [Obtener paywalls y productos para paywalls con Remote Config en tu app móvil](fetch-paywalls-and-products). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a mostrar paywalls en tu app móvil (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en ellos](create-placement) en el Adapty Dashboard. 4. Instala el [SDK de Adapty](sdk-installation-ios) en tu app móvil. </details> ## Obtener un paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\} Si has [diseñado un paywall con Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese tipo de paywall contiene tanto qué se debe mostrar dentro del paywall como cómo debe mostrarse. Aun así, necesitas obtener su ID a través del placement, su configuración de vista y luego presentarlo en tu app móvil. Para garantizar un rendimiento óptimo, es fundamental obtener el paywall y su [configuración de vista](get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dejando tiempo suficiente para que las imágenes se descarguen antes de mostrárselas al usuario. Para obtener un paywall, usa el método `getPaywall`: <Tabs> <TabItem value="swift" label="Swift"> ```swift showLineNumbers do { let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID") // the requested paywall } catch { // handle the error } ``` </TabItem> <TabItem value="callback" label="Swift-Callback"> ```swift showLineNumbers Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` </TabItem> </Tabs> Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués brasileño.</p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos utilizarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En ese caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo inestable que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls localmente en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que la CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el respaldo local.</p><p>Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes solicitudes internamente.</p> | Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall) con una lista de IDs de producto, el identificador del paywall, Remote Config y otras propiedades. | ## Obtener la configuración de vista de un paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important Asegúrate de activar el toggle **Show on device** en el paywall builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperarse. ::: Después de obtener el paywall, comprueba si incluye una configuración de vista, lo que indica que fue creado con Paywall Builder. Esto te guiará sobre cómo mostrar el paywall. Si la configuración de vista está presente, trátalo como un paywall de Paywall Builder; si no, [trátalo como un paywall con Remote Config](present-remote-config-paywalls). Usa el método `getPaywallConfiguration` para cargar la configuración de vista. ```swift showLineNumbers guard paywall.hasViewConfiguration else { // use your custom logic return } do { let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall, products: products ) // use loaded configuration } catch { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | | :----------------------- | :------------- | :----------------------------------------------------------- | | **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador para el paywall deseado. | | **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el respaldo local. Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes solicitudes internamente. | | **products** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el tiempo de visualización de los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. | :::note Si usas varios idiomas, aprende cómo añadir una [localización de Paywall Builder](add-paywall-locale-in-adapty-paywall-builder) y cómo usar los códigos de idioma correctamente [aquí](localizations-and-locale-codes). ::: Una vez cargado, [presenta el paywall](ios-present-paywalls). ## Obtener un paywall para la audiencia predeterminada para cargarlo más rápido \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} Normalmente, los paywalls se obtienen casi al instante, así que no necesitas preocuparte por acelerar este proceso. Sin embargo, si tienes muchas audiencias y paywalls y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseable. En esos casos, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún paywall. Para solucionar esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener información del paywall](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls diferentes para distintas versiones de la app (actual y futuras), puedes encontrarte con dificultades. Tendrás que diseñar paywalls compatibles con la versión actual (heredada) o aceptar que los usuarios con la versión actual (heredada) puedan tener problemas con paywalls que no se renderizan. - **Pérdida de targeting**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que perderás el targeting personalizado (incluyendo por países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, usa `getPaywall` descrito [anteriormente](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder). ::: ```swift showLineNumbers Adapty.getPaywallForDefaultAudience(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` :::note El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.2 del SDK de iOS. ::: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués brasileño.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos utilizarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En ese caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo inestable que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p> | ## Personalizar recursos \{#customize-assets\} Para personalizar imágenes y vídeos en tu paywall, implementa los recursos personalizados. Las imágenes y vídeos principales tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de recursos personalizados, puedes apuntar a estos elementos por sus IDs y personalizar su comportamiento. Para otras imágenes y vídeos, necesitas [establecer un ID personalizado](custom-media) en el dashboard de Adapty. Por ejemplo, puedes: - Mostrar una imagen o vídeo diferente a algunos usuarios. - Mostrar una imagen de vista previa local mientras se carga la imagen principal remota. - Mostrar una imagen de vista previa antes de reproducir un vídeo. :::important Para usar esta función, actualiza el SDK de Adapty para iOS a la versión 3.7.0 o superior. ::: Aquí tienes un ejemplo de cómo proporcionar recursos personalizados mediante un diccionario simple: ```swift showLineNumbers let customAssets: [String: AdaptyCustomAsset] = [ // Show a local image using a custom ID "custom_image": .image( .uiImage(value: UIImage(named: "image_name")!) ), // Show a local preview image while a remote main image is loading "hero_image": .image( .remote( url: URL(string: "https://example.com/image.jpg")!, preview: UIImage(named: "preview_image") ) ), // Show a local video with a preview image "hero_video": .video( .file( url: Bundle.main.url(forResource: "custom_video", withExtension: "mp4")!, preview: .uiImage(value: UIImage(named: "video_preview")!) ) ), ] let paywallConfig = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall, assetsResolver: customAssets ) ``` :::note Si no se encuentra un recurso, el paywall usará su apariencia predeterminada. ::: ## Configurar temporizadores definidos por el desarrollador \{#set-up-developer-defined-timers\} Para usar temporizadores personalizados en tu app móvil, crea un objeto que siga el protocolo `AdaptyTimerResolver`. Este objeto define cómo debe renderizarse cada temporizador personalizado. Si lo prefieres, puedes usar directamente un diccionario `[String: Date]`, ya que ya cumple con este protocolo. Aquí tienes un ejemplo: ```swift showLineNumbers @MainActor struct AdaptyTimerResolverImpl: AdaptyTimerResolver { func timerEndAtDate(for timerId: String) -> Date { switch timerId { case "CUSTOM_TIMER_6H": Date(timeIntervalSinceNow: 3600.0 * 6.0) // 6 hours case "CUSTOM_TIMER_NY": Calendar.current.date(from: DateComponents(year: 2025, month: 1, day: 1)) ?? Date(timeIntervalSinceNow: 3600.0) default: Date(timeIntervalSinceNow: 3600.0) // 1 hour } } } ``` En este ejemplo, `CUSTOM_TIMER_NY` y `CUSTOM_TIMER_6H` son los **Timer ID** de los temporizadores definidos por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente cada temporizador con el valor correcto. Por ejemplo: - `CUSTOM_TIMER_NY`: El tiempo restante hasta el final del temporizador, como el día de Año Nuevo. - `CUSTOM_TIMER_6H`: El tiempo restante en un período de 6 horas que comenzó cuando el usuario abrió el paywall. --- # File: ios-present-paywalls --- --- title: "Presentar paywalls del nuevo Paywall Builder en el SDK de iOS" description: "Descubre cómo presentar paywalls en iOS para aumentar las conversiones y los ingresos." --- Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese paywall ya contiene tanto lo que debe mostrarse como la forma en que debe hacerlo. :::warning Esta guía es para **[paywalls del nuevo Paywall Builder](adapty-paywall-builder)**. El proceso para presentar paywalls varía según la versión del Paywall Builder con la que se diseñaron, si son paywalls de Remote Config o si usas el [modo Observer](observer-vs-full-mode). - Para presentar **paywalls de Remote Config**, consulta [Renderizar paywall diseñado con Remote Config](present-remote-config-paywalls). - Para presentar **paywalls en modo Observer**, consulta [iOS - Presentar paywalls del Paywall Builder en modo Observer](ios-present-paywall-builder-paywalls-in-observer-mode) ::: Para obtener el objeto `AdaptyUI.PaywallConfiguration` que se usa a continuación, consulta [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls). ## Presentar paywalls en SwiftUI \{#present-paywalls-in-swiftui\} ### Presentar como vista modal \{#present-as-a-modal-view\} Para mostrar el paywall visual en la pantalla del dispositivo como vista modal, usa el modificador `.paywall` en SwiftUI: ```swift showLineNumbers title="SwiftUI" @State var paywallPresented = false // ensure that you manage this variable state and set it to `true` at the moment you want to show the paywall var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, paywallConfiguration: <AdaptyUI.PaywallConfiguration>, didPerformAction: { action in switch action { case .close: paywallPresented = false default: // Handle other actions break } }, didFinishPurchase: { product, profile in paywallPresented = false }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didFailRendering: { error in paywallPresented = false } ) } ``` Parámetros: | Parámetro | Requerido | Descripción | |:----------------------------------|:---------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **isPresented** | requerido | Un binding que controla si la pantalla del paywall está visible. | | **paywallConfiguration** | requerido | Un objeto `AdaptyUI.PaywallConfiguration` con los detalles visuales del paywall. Usa el método `AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:)`. Consulta [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. | | **didFailPurchase** | requerido | Se invoca cuando `Adapty.makePurchase()` falla. | | **didFinishRestore** | requerido | Se invoca cuando `Adapty.restorePurchases()` finaliza correctamente. | | **didFailRestore** | requerido | Se invoca cuando `Adapty.restorePurchases()` falla. | | **didFailRendering** | requerido | Se invoca si ocurre un error al renderizar la interfaz. En ese caso, [contacta con el soporte de Adapty](mailto:support@adapty.io). | | **fullScreen** | opcional | Determina si el paywall se muestra en pantalla completa o como modal. Por defecto es `true`. | | **didAppear** | opcional | Se invoca cuando la vista del paywall se presenta. | | **didDisappear** | opcional | Se invoca cuando la vista del paywall se descarta. | | **didPerformAction** | opcional | Se invoca cuando el usuario pulsa un botón. Cada botón tiene un ID de acción distinto. Hay dos IDs predefinidos: `close` y `openURL`; los demás son personalizados y se pueden configurar en el builder. | | **didSelectProduct** | opcional | Se invoca cuando se selecciona un producto para la compra (por el usuario o por el sistema). | | **didStartPurchase** | opcional | Se invoca cuando el usuario inicia el proceso de compra. | | **didFinishPurchase** | opcional | Se invoca cuando `Adapty.makePurchase()` finaliza correctamente. | | **didFinishWebPaymentNavigation** | opcional | Se invoca cuando finaliza la navegación del pago web. | | **didStartRestore** | opcional | Se invoca cuando el usuario inicia el proceso de restauración. | | **didFailLoadingProducts** | opcional | Se invoca cuando ocurren errores al cargar los productos. Devuelve `true` para reintentar la carga. | | **didPartiallyLoadProducts** | opcional | Se invoca cuando los productos se cargan de forma parcial. | | **showAlertItem** | opcional | Un binding que gestiona la visualización de alertas sobre el paywall. | | **showAlertBuilder** | opcional | Una función para renderizar la vista de alerta. | | **placeholderBuilder** | opcional | Una función para renderizar la vista de marcador de posición mientras se carga el paywall. | Consulta el tema [iOS - Gestión de eventos](ios-handling-events) para más detalles sobre los parámetros. ### Presentar como vista no modal \{#present-as-a-non-modal-view\} También puedes presentar paywalls como destinos de navegación o vistas en línea dentro del flujo de navegación de tu app. Usa `AdaptyPaywallView` directamente en tus vistas SwiftUI: ```swift showLineNumbers title="SwiftUI" AdaptyPaywallView( paywallConfiguration: <AdaptyUI.PaywallConfiguration>, didFailPurchase: { product, error in // Handle purchase failure }, didFinishRestore: { profile in // Handle successful restore }, didFailRestore: { error in // Handle restore failure }, didFailRendering: { error in // Handle rendering error } ) ``` ## Presentar paywalls en UIKit \{#present-paywalls-in-uikit\} Para mostrar el paywall visual en la pantalla del dispositivo, sigue estos pasos: 1. Inicializa el paywall visual que quieres mostrar usando el método `.paywallController(for:products:viewConfiguration:delegate:)`: ```swift showLineNumbers title="Swift" import Adapty import AdaptyUI let visualPaywall = AdaptyUI.paywallController( with: <paywall configuration object>, delegate: <AdaptyPaywallControllerDelegate> ) ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :----------------------- | :------- | :---------- | | **paywall configuration** | requerido | Un objeto `AdaptyUI.PaywallConfiguration` con los detalles visuales del paywall. Usa el método `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consulta [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. | | **delegate** | requerido | Un `AdaptyPaywallControllerDelegate` para escuchar los eventos del paywall. Consulta el tema [Gestión de eventos del paywall](ios-handling-events) para más detalles. Devuelve: | Objeto | Descripción | | :---------------------- | :--------------------------------------------------- | | **AdaptyPaywallController** | Un objeto que representa la pantalla del paywall solicitado | 2. Una vez creado el objeto correctamente, puedes mostrarlo en la pantalla del dispositivo: ```swift showLineNumbers title="Swift" present(visualPaywall, animated: true) ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: handle-paywall-actions --- --- title: "Responder a acciones de botones en el SDK de iOS" description: "Gestiona las acciones de botones de paywall en iOS usando Adapty para una mejor monetización de tu app." --- Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente: 1. Añade un [botón en el paywall builder](paywall-buttons) y asígnale una acción predefinida o crea un ID de acción personalizado. 2. Escribe código en tu app para gestionar cada acción que hayas asignado. Esta guía muestra cómo gestionar acciones personalizadas y predefinidas en tu código. :::warning **Solo las compras, restauraciones, cierres de paywall y apertura de URLs se gestionan automáticamente.** Todas las demás acciones de botones requieren una implementación adecuada en el código de la app. ::: ## Cerrar paywalls \{#close-paywalls\} Para añadir un botón que cierre tu paywall: 1. En el paywall builder, añade un botón y asígnale la acción **Close**. 2. En el código de tu app, implementa un handler para la acción `close` que descarte el paywall. :::info En el SDK de iOS, la acción `close` cierra el paywall por defecto. Sin embargo, puedes sobrescribir este comportamiento en tu código si lo necesitas. Por ejemplo, cerrar un paywall podría desencadenar la apertura de otro. ::: ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case .close: controller.dismiss(animated: true) // default behavior break } } ``` ## Abrir URLs desde paywalls \{#open-urls-from-paywalls\} :::tip Si quieres añadir un grupo de enlaces (por ejemplo, términos de uso y restauración de compras), añade un elemento **Link** en el paywall builder y trátalo igual que los botones con la acción **Open URL**. ::: Para añadir un botón que abra un enlace desde tu paywall (por ejemplo, **Terms of use** o **Privacy policy**): 1. En el paywall builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir. 2. En el código de tu app, implementa un handler para la acción `openUrl` que abra la URL recibida en un navegador. :::info En el SDK de iOS, la acción `openUrl` abre la URL por defecto. Sin embargo, puedes sobrescribir este comportamiento en tu código si lo necesitas. ::: ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case let .openURL(url): UIApplication.shared.open(url, options: [:]) // default behavior break } } ``` ## Iniciar sesión en la app \{#log-into-the-app\} Para añadir un botón que permita a los usuarios iniciar sesión en tu app: 1. En el paywall builder, añade un botón y asígnale la acción **Login**. 2. En el código de tu app, implementa un handler para la acción `login` que identifique a tu usuario. ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case .login: // Show a login screen let loginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginViewController") controller.present(loginVC, animated: true) } } ``` ## Gestionar acciones personalizadas \{#handle-custom-actions\} Para añadir un botón que gestione cualquier otra acción: 1. En el paywall builder, añade un botón, asígnale la acción **Custom** y dale un ID. 2. En el código de tu app, implementa un handler para el ID de acción que hayas creado. Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall: ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case let .custom(id): if id == "openNewPaywall" { // Display another paywall } } break } } ``` --- # File: ios-handling-events --- --- title: "Gestionar eventos del paywall en el SDK de iOS" description: "Gestiona eventos relacionados con suscripciones en iOS usando Adapty para una mejor monetización de la app." --- :::important Esta guía cubre el manejo de eventos para compras, restauraciones, selección de productos y renderizado de paywalls. También debes implementar el manejo de botones (cerrar el paywall, abrir enlaces, etc.). Consulta nuestra [guía sobre el manejo de acciones de botones](handle-paywall-actions) para más detalles. ::: Los paywalls configurados con el [Paywall Builder](adapty-paywall-builder) no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan algunos eventos a los que tu app puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selección de productos, etc.) así como notificaciones sobre acciones relacionadas con compras realizadas en el paywall. A continuación aprenderás cómo responder a estos eventos. Esta guía es únicamente para paywalls del **nuevo Paywall Builder**, que requieren Adapty SDK v3.0 o posterior. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Gestionar eventos en SwiftUI \{#handling-events-in-swiftui\} Para controlar o supervisar los procesos que ocurren en la pantalla del paywall dentro de tu app, usa el modificador `.paywall` en SwiftUI: ```swift showLineNumbers title="Swift" @State var paywallPresented = false var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, paywall: paywall, viewConfiguration: viewConfig, didPerformAction: { action in switch action { case .close: paywallPresented = false case let .openURL(url): // handle opening the URL (incl. for terms and privacy) default: // handle other actions } }, didSelectProduct: { /* Handle the event */ }, didStartPurchase: { /* Handle the event */ }, didFinishPurchase: { product, info in /* Handle the event */ }, didFailPurchase: { product, error in /* Handle the event */ }, didStartRestore: { /* Handle the event */ }, didFinishRestore: { /* Handle the event */ }, didFailRestore: { /* Handle the event */ }, didFailRendering: { error in paywallPresented = false }, didFailLoadingProducts: { error in return false } ) } ``` Puedes registrar solo los parámetros de closure que necesites y omitir los que no uses. En ese caso, los parámetros de closure no utilizados no se crearán. | Parámetro | Requerido | Descripción | |:----------------------------------|:-----------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **isPresented** | requerido | Un binding que gestiona si la pantalla del paywall se muestra o no. | | **paywallConfiguration** | requerido | Un objeto `AdaptyUI.PaywallConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:)`. Consulta el tema [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. | | **didFailPurchase** | requerido | Se invoca cuando una compra falla por errores (p. ej., pago no permitido, problemas de red, producto inválido). No se invoca por cancelaciones del usuario ni pagos pendientes. | | **didFinishRestore** | requerido | Se invoca cuando la compra se completa correctamente. | | **didFailRestore** | requerido | Se invoca cuando falla la restauración de una compra. | | **didFailRendering** | requerido | Se invoca si ocurre un error al renderizar la interfaz. En ese caso, [contacta con el soporte de Adapty](mailto:support@adapty.io). | | **fullScreen** | opcional | Determina si el paywall aparece en pantalla completa o como modal. Por defecto es `true`. | | **didAppear** | opcional | Se invoca cuando la vista del paywall aparece en pantalla. También se invoca cuando el usuario pulsa el [botón de web paywall](web-paywall#step-2a-add-a-web-purchase-button) dentro de un paywall y se abre un web paywall en un navegador in-app. | | **didDisappear** | opcional | Se invoca cuando la vista del paywall se cierra. También se invoca cuando un [web paywall](web-paywall#step-2a-add-a-web-purchase-button) abierto desde un paywall en un navegador in-app desaparece de la pantalla. | | **didPerformAction** | opcional | Se invoca cuando el usuario pulsa un botón. Distintos botones tienen distintos IDs de acción. Dos IDs están predefinidos: `close` y `openURL`, mientras que el resto son personalizados y se pueden configurar en el builder. | | **didSelectProduct** | opcional | Si se selecciona un producto para comprar (por el usuario o por el sistema), se invocará este callback. | | **didStartPurchase** | opcional | Se invoca cuando el usuario inicia el proceso de compra. | | **didFinishPurchase** | opcional | Se invoca cuando la compra se completa correctamente. | | **didFinishWebPaymentNavigation** | opcional | Se invoca tras intentar abrir un [web paywall](web-paywall) para realizar una compra, tanto si tiene éxito como si falla. | | **didStartRestore** | opcional | Se invoca cuando el usuario inicia el proceso de restauración. | | **didFailLoadingProducts** | opcional | Se invoca cuando ocurren errores durante la carga de productos. Devuelve `true` para reintentar la carga. | | **didPartiallyLoadProducts** | opcional | Se invoca cuando los productos se cargan parcialmente. | | **showAlertItem** | opcional | Un binding que gestiona la visualización de elementos de alerta sobre el paywall. | | **showAlertBuilder** | opcional | Una función para renderizar la vista de alerta. | | **placeholderBuilder** | opcional | Una función para renderizar la vista de marcador de posición mientras se carga el paywall. | ## Gestionar eventos en UIKit \{#handling-events-in-uikit\} Para controlar o supervisar los procesos que ocurren en la pantalla del paywall dentro de tu app, implementa los métodos de `AdaptyPaywallControllerDelegate`. ### Eventos generados por el usuario \{#user-generated-events\} #### Selección de producto \{#product-selection\} Si el usuario selecciona un producto para comprar, se invocará este método: ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer ) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ``` </Details> #### Compra iniciada \{#started-purchase\} Si el usuario inicia el proceso de compra, se invocará este método: ```swift showLineNumbers title="Swift" func paywallController(_ controller: AdaptyPaywallController, didStartPurchase product: AdaptyPaywallProduct) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ``` </Details> No se invocará en el modo Observer. Consulta el tema [iOS - Presentar paywalls del Paywall Builder en modo Observer](ios-present-paywall-builder-paywalls-in-observer-mode) para más detalles. #### Compra iniciada mediante un web paywall \{#started-purchase-using-a-web-paywall\} Si el usuario inicia el proceso de compra usando un [web paywall](web-paywall), se invocará este método: ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, shouldContinueWebPaymentNavigation product: AdaptyPaywallProduct ) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ``` </Details> #### Compra completada o cancelada \{#successful-or-canceled-purchase\} Si la compra tiene éxito, se invocará este método: ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didFinishPurchase product: AdaptyPaywallProductWithoutDeterminingOffer, purchaseResult: AdaptyPurchaseResult ) { } } ``` <Details> <summary>Ejemplos de evento (haz clic para expandir)</summary> ```javascript // Successful purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "success", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } } } // Cancelled purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "cancelled" } } ``` </Details> En ese caso, te recomendamos cerrar la pantalla del paywall. No se invocará en el modo Observer. Consulta el tema [iOS - Presentar paywalls del Paywall Builder en modo Observer](ios-present-paywall-builder-paywalls-in-observer-mode) para más detalles. #### Compra fallida \{#failed-purchase\} Si una compra falla por un error, se invocará este método. Esto incluye errores de StoreKit (restricciones de pago, productos inválidos, fallos de red), fallos en la verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan `didFinishPurchase` con un resultado cancelado, y los pagos pendientes no activan este método. ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didFailPurchase product: AdaptyPaywallProduct, error: AdaptyError ) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" } } } ``` </Details> No se invocará en el modo Observer. Consulta el tema [iOS - Presentar paywalls del Paywall Builder en modo Observer](ios-present-paywall-builder-paywalls-in-observer-mode) para más detalles. #### Compra fallida mediante un web paywall \{#failed-purchase-using-a-web-paywall\} Si `Adapty.openWebPaywall()` falla, se invocará este método: ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didFailWebPaymentNavigation product: AdaptyPaywallProduct, error: AdaptyError ) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "web_payment_failed", "message": "Web payment navigation failed", "details": { "underlyingError": "Network connection error" } } } ``` </Details> #### Restauración completada \{#successful-restore\} Si la restauración de una compra tiene éxito, se invocará este método: ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didFinishRestoreWith profile: AdaptyProfile ) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } ``` </Details> Te recomendamos cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de la suscripción](subscription-status) para aprender a comprobarlo. #### Restauración fallida \{#failed-restore\} Si la restauración de una compra falla, se invocará este método: ```swift showLineNumbers title="Swift" public func paywallController( _ controller: AdaptyPaywallController, didFailRestoreWith error: AdaptyError ) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } ``` </Details> ### Obtención de datos y renderizado \{#data-fetching-and-rendering\} #### Errores al cargar productos \{#product-loading-errors\} Si no pasas el array de productos durante la inicialización, AdaptyUI recuperará los objetos necesarios del servidor por sí solo. Si esta operación falla, AdaptyUI notificará el error llamando a este método: ```swift showLineNumbers title="Swift" public func paywallController( _ controller: AdaptyPaywallController, didFailLoadingProductsWith error: AdaptyError ) -> Bool { return true } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } ``` </Details> Si devuelves `true`, AdaptyUI repetirá la solicitud después de 2 segundos. #### Errores de renderizado \{#rendering-errors\} Si ocurre un error durante el renderizado de la interfaz, se notificará mediante este método: ```swift showLineNumbers title="Swift" public func paywallController( _ controller: AdaptyPaywallController, didFailRenderingWith error: AdaptyError ) { } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } ``` </Details> En condiciones normales, estos errores no deberían producirse, así que si te encuentras con alguno, comunícanoslo. --- # File: ios-use-fallback-paywalls --- --- title: "iOS - Usar paywalls de respaldo" description: "Gestiona los casos en que los usuarios están sin conexión o los servidores de Adapty no están disponibles" --- :::warning Los paywalls de respaldo son compatibles con el SDK de iOS v2.11 o posterior. ::: Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet. * **Si la aplicación no puede acceder a los servidores de Adapty:** Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding. * **Si la aplicación no puede acceder a internet:** Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar. :::important Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty. ::: ## Configuración \{#configuration\} 1. Añade el archivo JSON de respaldo al bundle de tu proyecto: abre el menú **File** en XCode y selecciona la opción **Add Files to "YourProjectName"**. 2. Llama al método `.setFallback` **antes** de obtener el paywall o onboarding de destino. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { if let urlPath = Bundle.main.url(forResource: fileName, withExtension: "json") { try await Adapty.setFallback(fileURL: urlPath) } } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers if let url = Bundle.main.url(forResource: "ios_fallback", withExtension: "json") { Adapty.setFallback(fileURL: url) } ``` </TabItem> </Tabs> Parámetros: | Parámetro | Descripción | | :---------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **fileURL** | Ruta al archivo de configuración de respaldo. | --- # File: localizations-and-locale-codes --- --- title: "Usar localizaciones y códigos de idioma en el SDK de iOS" description: "Gestiona las localizaciones de la app y los códigos de idioma para llegar a una audiencia global en tu app de iOS." --- ## Por qué esto es importante \{#why-this-is-important\} Hay algunos escenarios en los que los códigos de idioma entran en juego, por ejemplo, cuando intentas obtener el paywall correcto para la localización actual de tu app. Como los códigos de idioma son complejos y pueden variar de una plataforma a otra, nos basamos en un estándar interno para todas las plataformas que soportamos. Sin embargo, precisamente por esa complejidad, es muy importante que entiendas exactamente qué estás enviando a nuestro servidor para obtener la localización correcta y qué ocurre a continuación, de modo que siempre recibas lo que esperas. ## Estándar de códigos de idioma en Adapty \{#locale-code-standard-at-adapty\} Para los códigos de idioma, Adapty utiliza una versión ligeramente modificada del [estándar BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag): cada código está formado por subetiquetas en minúsculas separadas por guiones. Algunos ejemplos: `en` (inglés), `pt-br` (portugués de Brasil), `zh` (chino simplificado), `zh-hant` (chino tradicional). ## Coincidencia de códigos de idioma \{#locale-code-matching\} Cuando Adapty recibe una llamada del SDK con el código de idioma y busca la localización correspondiente de un paywall, ocurre lo siguiente: 1. La cadena de idioma entrante se convierte a minúsculas y todos los guiones bajos (`_`) se reemplazan con guiones (`-`). 2. Buscamos la localización con el código de idioma que coincida exactamente. 3. Si no se encuentra ninguna coincidencia, tomamos la subcadena antes del primer guion (`pt` para `pt-br`) y buscamos la localización correspondiente. 4. Si tampoco se encuentra coincidencia, devolvemos la localización por defecto `en`. De este modo, un dispositivo iOS que envíe `'pt_BR'`, un dispositivo Android que envíe `pt-BR` y otro dispositivo que envíe `pt-br` obtendrán el mismo resultado. ## Implementar localizaciones: la forma recomendada \{#implementing-localizations-recommended-way\} Si te estás preguntando por las localizaciones, lo más probable es que ya estés trabajando con archivos de cadenas localizadas en tu proyecto. En ese caso, te recomendamos añadir un par clave-valor con el código de idioma de Adapty correspondiente en cada uno de tus archivos para las localizaciones respectivas, y luego extraer el valor de esa clave al llamar a nuestro SDK, como se muestra aquí: ```swift showLineNumbers // 1. Modify your Localizable.strings files /* Localizable.strings - Spanish */ adapty_paywalls_locale = "es"; /* Localizable.strings - Portuguese (Brazil) */ adapty_paywalls_locale = "pt-br"; // 2. Extract and use the locale code let locale = NSLocalizedString("adapty_paywalls_locale", comment: "") // pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method ``` Así tienes el control total sobre qué localización se obtendrá para cada usuario de tu app. ## Implementar localizaciones: la otra forma \{#implementing-localizations-the-other-way\} Puedes obtener resultados similares (aunque no idénticos) sin definir explícitamente los códigos de idioma para cada localización. Esto implica extraer un código de idioma de otros objetos que proporciona tu plataforma, como en este ejemplo: ```swift showLineNumbers let locale = Locale.current.identifier // pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method ``` No recomendamos este enfoque por varios motivos: 1. En iOS, los idiomas preferidos y el idioma actual no son idénticos. Si quieres que la localización se seleccione correctamente, tendrás que apoyarte en la lógica de Apple, que funciona de manera automática si usas el enfoque recomendado con archivos de cadenas localizadas, o bien recrearla tú mismo. 2. Es difícil predecir exactamente qué recibirá el servidor de Adapty. Por ejemplo, en iOS es posible obtener un código como `ar_OM@numbers='latn'` en un dispositivo y enviarlo a nuestro servidor. Para esa llamada no obtendrás la localización `ar-om` que buscabas, sino `ar`, lo cual probablemente no es lo que esperabas. Si aun así decides utilizar este enfoque, asegúrate de haber cubierto todos los casos de uso relevantes. --- # File: ios-web-paywall --- --- title: "Implementar web paywalls en el SDK de iOS" description: "Configura un web paywall para cobrar sin las comisiones y revisiones de la App Store." --- :::important Antes de empezar, asegúrate de haber [configurado tu web paywall en el dashboard](web-paywall) y de tener instalada la versión 3.6.1 o posterior del SDK de Adapty. ::: ## Abrir web paywalls \{#open-web-paywalls\} Si estás trabajando con un paywall que desarrollaste tú mismo, debes gestionar los web paywalls usando el método del SDK. El método `.openWebPaywall`: 1. Genera una URL única que permite a Adapty vincular un paywall específico mostrado a un usuario concreto con la página web a la que es redirigido. 2. Detecta cuando tus usuarios vuelven a la app y luego solicita `.getProfile` a intervalos cortos para determinar si los derechos de acceso del perfil se han actualizado. De este modo, si el pago se ha completado correctamente y los derechos de acceso se han actualizado, la suscripción se activa en la app casi de inmediato. ```swift showLineNumbers title="Swift" do { try await Adapty.openWebPaywall(for: product) } catch { print("Failed to open web paywall: \(error)") } ``` :::note Existen dos versiones del método `openWebPaywall`: 1. `openWebPaywall(product)`, que genera URLs a partir del paywall y también añade los datos del producto a las URLs. 2. `openWebPaywall(paywall)`, que genera URLs a partir del paywall sin añadir los datos del producto. Úsalo cuando tus productos en el paywall de Adapty sean distintos de los del web paywall. ::: ## Gestionar errores \{#handle-errors\} | Error | Descripción | Acción recomendada | |-----------------------------------------|------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| | AdaptyError.paywallWithoutPurchaseUrl | El paywall no tiene configurada una URL de compra web | Comprueba si el paywall está correctamente configurado en el Adapty Dashboard | | AdaptyError.productWithoutPurchaseUrl | El producto no tiene una URL de compra web | Verifica la configuración del producto en el Adapty Dashboard | | AdaptyError.failedOpeningWebPaywallUrl | No se pudo abrir la URL en el navegador | Revisa la configuración del dispositivo o proporciona un método de compra alternativo | | AdaptyError.failedDecodingWebPaywallUrl | No se pudieron codificar correctamente los parámetros de la URL | Verifica que los parámetros de la URL sean válidos y estén correctamente formateados | ## Ejemplo de implementación \{#implementation-example\} ```swift showLineNumbers title="Swift" class SubscriptionViewController: UIViewController { var paywall: AdaptyPaywall? @IBAction func purchaseButtonTapped(_ sender: UIButton) { guard let paywall = paywall, let product = paywall.products.first else { return } Task { await offerWebPurchase(for: product) } } func offerWebPurchase(for paywallProduct: AdaptyPaywallProduct) async { do { // Attempt to open web paywall try await Adapty.openWebPaywall(for: paywallProduct) } catch let error as AdaptyError { switch error { case .paywallWithoutPurchaseUrl, .productWithoutPurchaseUrl: showAlert(message: "Web purchase is not available for this product.") case .failedOpeningWebPaywallUrl: showAlert(message: "Could not open web browser. Please try again.") default: showAlert(message: "An error occurred: \(error.localizedDescription)") } } catch { showAlert(message: "An unexpected error occurred.") } } // Helper methods private func showAlert(message: String) { /* ... */ } } ``` :::note Cuando los usuarios vuelvan a la app, actualiza la interfaz para reflejar los cambios del perfil. `AdaptyDelegate` recibirá y procesará los eventos de actualización del perfil. ::: ## Abrir web paywalls en un navegador in-app \{#open-web-paywalls-in-an-in-app-browser\} :::important La apertura de web paywalls en un navegador in-app es compatible a partir del SDK de Adapty v. 3.15. ::: Por defecto, los web paywalls se abren en el navegador externo. Para ofrecer una experiencia de usuario más fluida, puedes abrir los web paywalls en un navegador in-app. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin cambiar de app. Para activarlo, establece el parámetro `in` en `.inAppBrowser`: ```swift showLineNumbers title="Swift" do { try await Adapty.openWebPaywall(for: product, in: .inAppBrowser) // default – .externalBrowser } catch { print("Failed to open web paywall: \(error)") } ``` --- # File: ios-troubleshoot-paywall-builder --- --- title: "Solucionar problemas del Paywall Builder en el SDK de iOS" description: "Solucionar problemas del Paywall Builder en el SDK de iOS" --- Esta guía te ayuda a resolver los problemas más comunes al usar paywalls diseñados en el Adapty Paywall Builder con el SDK de iOS. ## Fallo al obtener la configuración del paywall \{#getting-a-paywall-configuration-fails\} **Problema**: El método `getPaywallConfiguration` no consigue recuperar la configuración del paywall. **Causa**: El paywall no está habilitado para mostrarse en el dispositivo en el Paywall Builder. **Solución**: Activa el interruptor **Show on device** en el Paywall Builder. <img src="/assets/shared/img/show-on-device.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## El número de vistas del paywall es demasiado alto \{#the-paywall-view-number-is-too-big\} **Problema**: El contador de vistas del paywall muestra el doble del número esperado. **Causa**: Es posible que estés llamando a `logShowPaywall` en tu código, lo que duplica el contador de vistas si usas el Paywall Builder. En los paywalls diseñados con el Paywall Builder, el seguimiento de analíticas es automático, por lo que no necesitas usar este método. **Solución**: Asegúrate de no llamar a `logShowPaywall` en tu código si usas el Paywall Builder. ## Otros problemas \{#other-issues\} **Problema**: Estás experimentando otros problemas relacionados con el Paywall Builder que no se tratan arriba. **Solución**: Migra el SDK a la versión más reciente siguiendo las [guías de migración](ios-sdk-migration-guides) si es necesario. Muchos problemas se resuelven en versiones más nuevas del SDK. --- # File: ios-quickstart-manual --- --- title: "Habilitar compras en tu paywall personalizado en iOS SDK" description: "Integra el SDK de Adapty en tus paywalls personalizados de iOS para habilitar compras in-app." --- Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall, mientras el SDK de Adapty obtiene los productos, gestiona las nuevas compras y restaura las anteriores. :::important **Esta guía es para desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Adapty Paywall Builder](ios-quickstart-paywalls). Con Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compras automáticamente y puedes probar distintos diseños sin volver a publicar tu app. ::: ## Antes de empezar \{#before-you-start\} ### Configura los productos \{#set-up-products\} Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo mostrar los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 diferentes paywalls a distintos usuarios. Asegúrate de entender estos conceptos aunque trabajes con tu paywall personalizado. Básicamente, son tu forma de gestionar los productos que vendes en tu app. Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite recuperar tus productos. Para entender qué tienes que hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart). ### Gestión de usuarios \{#manage-users\} Puedes trabajar con o sin autenticación backend en tu lado. Sin embargo, el SDK de Adapty gestiona los usuarios anónimos e identificados de forma diferente. Lee la [guía de inicio rápido de identificación](ios-quickstart-identify) para entender los detalles y asegurarte de que estás trabajando con los usuarios correctamente. ## Paso 1. Obtener productos \{#step-1-get-products\} Para recuperar productos para tu paywall personalizado, necesitas: 1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`. 2. Obtener el array de productos para este paywall usando el método `getPaywallProducts`. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift func loadPaywall() async { do { let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID") let products = try await Adapty.getPaywallProducts(paywall: paywall) // Use products to build your custom paywall UI } catch { // Handle the error } } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift func loadPaywall() { Adapty.getPaywall("YOUR_PLACEMENT_ID") { result in switch result { case let .success(paywall): Adapty.getPaywallProducts(paywall: paywall) { result in switch result { case let .success(products): // Use products to build your custom paywall UI case let .failure(error): // Handle the error } } case let .failure(error): // Handle the error } } } ``` </TabItem> </Tabs> ## Paso 2. Aceptar compras \{#step-2-accept-purchases\} Cuando un usuario pulsa en un producto de tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift func purchaseProduct(_ product: AdaptyPaywallProduct) async { do { let purchaseResult = try await Adapty.makePurchase(product: product) switch purchaseResult { case .userCancelled: // User canceled the purchase break case .pending: // Purchase is pending (e.g., awaiting parental approval) break case let .success(profile, transaction): // Purchase successful, profile updated break } } catch { // Handle the error } } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift func purchaseProduct(_ product: AdaptyPaywallProduct) { Adapty.makePurchase(product: product) { result in switch result { case let .success(purchaseResult): switch purchaseResult { case .userCancelled: // User canceled the purchase break case .pending: // Purchase is pending (e.g., awaiting parental approval) break case let .success(profile, transaction): // Purchase successful, profile updated break } case let .failure(error): // Handle the error } } } ``` </TabItem> </Tabs> ## Paso 3. Restaurar compras \{#step-3-restore-purchases\} Apple exige que todas las apps con suscripciones ofrezcan una forma de que los usuarios puedan restaurar sus compras. Aunque las compras se restauran automáticamente cuando un usuario inicia sesión con su Apple ID, debes implementar igualmente un botón de restauración en tu app. Llama al método `restorePurchases` cuando el usuario pulse el botón de restaurar. Esto sincronizará su historial de compras con Adapty y devolverá el perfil actualizado. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift func restorePurchases() async { do { let profile = try await Adapty.restorePurchases() // Restore successful, profile updated } catch { // Handle the error } } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift func restorePurchases() { Adapty.restorePurchases { result in switch result { case let .success(profile): // Restore successful, profile updated case let .failure(error): // Handle the error } } } ``` </TabItem> </Tabs> ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Tu paywall está listo para mostrarse en la app. [Prueba tus compras en modo sandbox](test-purchases-in-sandbox) para asegurarte de que puedes completar una compra de prueba desde el paywall. A continuación, [comprueba si los usuarios han completado su compra](ios-check-subscription-status) para determinar si mostrar el paywall o conceder acceso a las funciones de pago. --- # File: fetch-paywalls-and-products --- --- title: "Obtener paywalls y productos para paywalls con Remote Config en iOS SDK" description: "Obtén paywalls y productos en el SDK de Adapty para iOS y mejora la monetización." --- Antes de mostrar paywalls con Remote Config y personalizados, necesitas obtener la información sobre ellos. Ten en cuenta que este tema se refiere a paywalls con Remote Config y personalizados. Para obtener información sobre cómo obtener paywalls creados con Paywall Builder, consulta las <InlineTooltip tooltip="guías sobre cómo obtener paywalls del Paywall Builder en tu app">[iOS](get-pb-paywalls), [Android](android-get-pb-paywalls), [React Native](react-native-get-pb-paywalls), [Flutter](flutter-get-pb-paywalls), y [Unity](unity-get-pb-paywalls)</InlineTooltip>. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a obtener paywalls y productos en tu app (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos a tu paywall](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall al placement](create-placement) en el Adapty Dashboard. 4. [Instala el SDK de Adapty](sdk-installation-ios) en tu app. </details> ## Obtener información del paywall \{#fetch-paywall-information\} En Adapty, un [producto](product) es una combinación de productos de App Store y Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos en placements específicos de tu app. Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`. :::important **No escribas los IDs de producto en el código.** El único ID que debes incluir directamente en el código es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe manejar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana tres, muéstralos todos sin necesidad de cambiar el código. ::: <Tabs group="current-os"> <TabItem value="swift" label="Swift"> ```swift showLineNumbers do { let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID") // the requested paywall } catch { // handle the error } ``` </TabItem> <TabItem value="callback" label="Swift-Callback"> ```swift showLineNumbers Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` </TabItem> </Tabs> | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos del servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este escenario, los usuarios podrían no obtener los datos más recientes, pero disfrutarán de tiempos de carga más rápidos, sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar peticiones de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el respaldo local.</p><p></p><p>Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes peticiones internamente.</p> | ¡No escribas los IDs de producto directamente en el código! Como los paywalls se configuran de forma remota, los productos disponibles, su número y las ofertas especiales (como pruebas gratuitas) pueden cambiar con el tiempo. Asegúrate de que tu código gestione estos escenarios. Por ejemplo, si inicialmente obtienes 2 productos, tu app debería mostrar esos 2 productos. Sin embargo, si más adelante obtienes 3 productos, tu app debería mostrar los 3 sin necesidad de cambios en el código. Lo único que debes incluir directamente en el código es el ID del placement. Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall) con: una lista de IDs de producto, el identificador del paywall, Remote Config y varias otras propiedades. | ## Obtener productos \{#fetch-products\} Una vez que tienes el paywall, puedes consultar el array de productos que le corresponde: <Tabs group="current-os"> <TabItem value="swift" label="Swift"> ```swift showLineNumbers do { let products = try await Adapty.getPaywallProducts(paywall: paywall) // the requested products array } catch { // handle the error } ``` </TabItem> <TabItem value="callback" label="Swift-Callback"> ```swift showLineNumbers Adapty.getPaywallProducts(paywall: paywall) { result in switch result { case let .success(products): // the requested products array case let .failure(error): // handle the error } } ``` </TabItem> </Tabs> Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Products | Lista de objetos [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct) con: identificador del producto, nombre del producto, precio, moneda, duración de la suscripción y varias otras propiedades. | Al implementar tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct). A continuación se muestran las propiedades más utilizadas, pero consulta el documento enlazado para ver todos los detalles sobre las propiedades disponibles. | Propiedad | Descripción | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | Para mostrar el título del producto, usa `product.localizedTitle`. Ten en cuenta que la localización se basa en el país de la store seleccionado por el usuario, no en el idioma del dispositivo. | | **Price** | Para mostrar una versión localizada del precio, usa `product.localizedPrice`. Esta localización se basa en la información de idioma del dispositivo. También puedes acceder al precio como número con `product.price`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda asociado, usa `product.currencySymbol`. | | **Subscription Period** | Para mostrar el período (por ejemplo, semana, mes, año, etc.), usa `product.localizedSubscriptionPeriod`. Esta localización se basa en el idioma del dispositivo. Para obtener el período de suscripción de forma programática, usa `product.subscriptionPeriod`. Desde ahí puedes acceder al enum `unit` para obtener la duración (es decir, día, semana, mes, año o desconocido). El valor `numberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral, verías `.month` en la propiedad unit y `3` en la propiedad numberOfUnits. | | **Introductory Offer** | Para mostrar una insignia u otro indicador de que una suscripción incluye una oferta introductoria, consulta la propiedad `product.subscriptionOffer`. Dentro de este objeto se encuentran las siguientes propiedades útiles:<br/>• `offerType`: un enum con los valores `introductory`, `promotional` y `winBack`. Las pruebas gratuitas y las suscripciones con descuento inicial serán del tipo `introductory`.<br/>• `price`: El precio con descuento como número. Para las pruebas gratuitas, busca `0` aquí.<br/>• `localizedPrice`: Un precio formateado del descuento para el idioma del usuario.<br/>• `localizedNumberOfPeriods`: una cadena localizada según el idioma del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `3 days` en este campo.<br/>• `subscriptionPeriod`: También puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que la sección anterior.<br/>• `localizedSubscriptionPeriod`: Un período de suscripción formateado del descuento para el idioma del usuario. | ## Comprobar la elegibilidad para la oferta introductoria en iOS \{#check-intro-offer-eligibility-on-ios\} Por defecto, el método `getPaywallProducts` comprueba la elegibilidad para las ofertas introductorias, promocionales y de recuperación. Si necesitas mostrar productos antes de que el SDK determine la elegibilidad para las ofertas, usa el método `getPaywallProductsWithoutDeterminingOffer` en su lugar. :::note Después de mostrar los productos iniciales, asegúrate de llamar al método habitual `getPaywallProducts` para actualizar los productos con información precisa sobre la elegibilidad de las ofertas. ::: <Tabs group="current-os"> <TabItem value="swift" label="Swift"> ```swift showLineNumbers do { let products = try await Adapty.getPaywallProductsWithoutDeterminingOffer(paywall: paywall) // the requested products array without subscriptionOffer } catch { // handle the error } ``` </TabItem> <TabItem value="callback" label="Swift-Callback"> ```swift showLineNumbers Adapty.getPaywallProductsWithoutDeterminingOffer(paywall: paywall) { result in switch result { case let .success(products): // the requested products array without subscriptionOffer case let .failure(error): // handle the error } } ``` </TabItem> </Tabs> ## Acelera la obtención del paywall con el paywall de audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\} Normalmente, los paywalls se obtienen casi de forma instantánea, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, cuando tienes muchas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseable. En esas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia fluida en lugar de no mostrar ninguno. Para resolver esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, tal como se detalla en la sección [Obtener información del paywall](fetch-paywalls-and-products#fetch-paywall-information) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls diferentes para distintas versiones de la app (actuales y futuras), podrías enfrentarte a dificultades. Tendrás que diseñar paywalls que sean compatibles con la versión actual (legacy) o aceptar que los usuarios con esa versión puedan encontrarse con paywalls que no se renderizan correctamente. - **Pérdida de segmentación**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluyendo por países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, utiliza el `getPaywall` descrito [anteriormente](fetch-paywalls-and-products#fetch-paywall-information). ::: <Tabs group="current-os"> <TabItem value="swift" label="Swift"> ```swift showLineNumbers do { let paywall = try await Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID") // the requested paywall } catch { // handle the error } ``` </TabItem> <TabItem value="callback" label="Swift-Callback"> ```swift showLineNumbers Adapty.getPaywallForDefaultAudience(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` </TabItem> </Tabs> :::note El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.2 del SDK de iOS. ::: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos del servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este escenario, los usuarios podrían no obtener los datos más recientes, pero disfrutarán de tiempos de carga más rápidos, sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar peticiones de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.</p> | --- # File: present-remote-config-paywalls --- --- title: "Mostrar un paywall diseñado con Remote Config en iOS SDK" description: "Descubre cómo presentar paywalls de Remote Config en Adapty para personalizar la experiencia del usuario." --- Si has personalizado un paywall usando Remote Config, necesitarás implementar el renderizado en el código de tu app para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú decides qué incluir y cómo se ve tu paywall. Te proporcionamos un método para obtener la configuración remota, dándote autonomía para mostrar tu paywall personalizado configurado mediante Remote Config. No olvides [comprobar si un usuario es elegible para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios) y adaptar la vista del paywall para gestionar el caso en que sea elegible. ## Obtener el Remote Config del paywall y mostrarlo \{#get-paywall-remote-config-and-present-it\} Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores necesarios. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID") let headerText = paywall.remoteConfig?.dictionary?["header_text"] as? String } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID") { result in let paywall = try? result.get() let headerText = paywall?.remoteConfig?.dictionary?["header_text"] as? String } ``` </TabItem> </Tabs> En este punto, una vez que hayas recibido todos los valores necesarios, es momento de renderizarlos y ensamblarlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a las distintas pantallas y orientaciones de los móviles, ofreciendo una experiencia fluida y amigable en todos los dispositivos. :::warning Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls#track-paywall-view-events) como se describe a continuación, para que los análisis de Adapty capturen la información necesaria para los embudos y las pruebas A/B. ::: Una vez que hayas terminado de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.makePurchase()` con el producto de tu paywall. Para más información sobre el método `.makePurchase()`, consulta [Realizar compras](making-purchases). Te recomendamos [crear un paywall de respaldo llamado fallback paywall](fallback-paywalls). Este respaldo se mostrará al usuario cuando no haya conexión a internet o caché disponible, garantizando una experiencia fluida incluso en esas situaciones. ## Registrar eventos de visualización del paywall \{#track-paywall-view-events\} Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque los datos de compras se recopilan automáticamente, registrar las visualizaciones del paywall requiere tu intervención, ya que solo tú sabes cuándo un usuario ve un paywall. Para registrar un evento de visualización del paywall, simplemente llama a `.logShowPaywall(paywall)`, y quedará reflejado en las métricas de tu paywall en los embudos y las pruebas A/B. :::important No es necesario llamar a `.logShowPaywall(paywall)` si estás mostrando paywalls creados con el [Paywall Builder](adapty-paywall-builder). ::: ```swift showLineNumbers Adapty.logShowPaywall(paywall) ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :------- |:-----------------------------------------------------------------------------------------| | **paywall** | obligatorio | Un objeto [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall). | --- # File: making-purchases --- --- title: "Realizar compras in-app en iOS SDK" description: "Guía para gestionar compras in-app y suscripciones con Adapty." --- Mostrar paywalls en tu aplicación móvil es un paso esencial para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, con solo mostrar estos paywalls es suficiente para admitir compras únicamente si usas el [Paywall Builder](adapty-paywall-builder) para personalizar tus paywalls. Si no usas el Paywall Builder, debes utilizar un método independiente llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método actúa como punto de entrada para que los usuarios interactúen con los paywalls y realicen sus transacciones. Si tu paywall tiene una oferta promocional activa para el producto que el usuario intenta comprar, Adapty la aplicará automáticamente en el momento de la compra. :::warning Ten en cuenta que la oferta introductoria se aplicará automáticamente solo si usas paywalls configurados con el Paywall Builder. En otros casos, tendrás que [verificar la elegibilidad del usuario para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Omitir este paso puede provocar que tu app sea rechazada durante la publicación. Además, podría suponer cobrar el precio completo a usuarios que sí son elegibles para una oferta introductoria. ::: Asegúrate de haber completado la [configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras. ## Realizar una compra \{#make-purchase\} :::note **¿Usas el [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente: puedes saltarte este paso. **¿Buscas instrucciones paso a paso?** Consulta la [guía de inicio rápido](ios-implement-paywalls-manually) para ver instrucciones de implementación completas con todo el contexto. ::: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { let purchaseResult = try await Adapty.makePurchase(product: product) switch purchaseResult { case .userCancelled: // Handle the case where the user canceled the purchase case .pending: // Handle deferred purchases (e.g., the user will pay offline with cash) case let .success(profile, transaction): if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // Grant access to the paid features } } } catch { // Handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.makePurchase(product: product) { result in switch result { case let .success(purchaseResult): switch purchaseResult { case .userCancelled: // Handle the case where the user canceled the purchase case .pending: // Handle deferred purchases (e.g., the user will pay offline with cash) case let .success(profile, transaction): if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // Grant access to the paid features } } case let .failure(error): // Handle the error } } ``` </TabItem> </Tabs> Parámetros de la solicitud: | Parámetro | Obligatorio | Descripción | | :---------- | :---------- | :-------------------------------------------------------------------------------------------------- | | **Product** | obligatorio | Un objeto [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct) obtenido del paywall. | Parámetros de la respuesta: | Parámetro | Descripción | |---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** | <p>Si la solicitud fue exitosa, la respuesta contiene este objeto. Un objeto [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile) proporciona información completa sobre los niveles de acceso, suscripciones y compras únicas de un usuario dentro de la app.</p><p>Comprueba el estado del nivel de acceso para verificar si el usuario tiene el acceso requerido a la app.</p> | :::warning **Nota:** si todavía usas una versión de StoreKit de Apple inferior a v2.0 y una versión de Adapty SDK inferior a v.2.9.0, debes proporcionar el [secreto compartido de Apple App Store](app-store-connection-configuration#step-5-enter-app-store-shared-secret). Este método está actualmente deprecado por Apple. ::: ## Compras in-app desde el App Store \{#in-app-purchases-from-the-app-store\} Cuando un usuario inicia una compra en el App Store y la transacción se traslada a tu app, tienes dos opciones: - **Procesar la transacción de inmediato:** Devuelve `true` en `shouldAddStorePayment`. Esto mostrará la pantalla del sistema de compra de Apple de inmediato. - **Guardar el objeto del producto para procesarlo más tarde:** Devuelve `false` en `shouldAddStorePayment` y después llama a `makePurchase` con el producto guardado cuando sea oportuno. Esto puede ser útil si necesitas mostrar algo personalizado al usuario antes de iniciar la compra. Aquí tienes el código completo: ```swift showLineNumbers title="Swift" final class YourAdaptyDelegateImplementation: AdaptyDelegate { nonisolated func shouldAddStorePayment(for product: AdaptyDeferredProduct) -> Bool { // 1a. // Return `true` to continue the transaction in your app. The Apple purchase system screen will show automatically. // 1b. // Store the product object and return `false` to defer or cancel the transaction. false } // 2. Continue the deferred purchase later on by passing the product to `makePurchase` when the timing is appropriate func continueDeferredPurchase() async { let storedProduct: AdaptyDeferredProduct = // get the product object from 1b. do { try await Adapty.makePurchase(product: storedProduct) } catch { // handle the error } } } ``` ## Canjear códigos de oferta en iOS \{#redeem-offer-codes-in-ios\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Details> <summary>Sobre los códigos de oferta</summary> Los códigos de oferta te permiten dar descuentos o períodos de prueba gratuitos a usuarios concretos. A diferencia de las ofertas habituales, que se aplican de forma automática, los códigos de oferta se distribuyen fuera de la app — por email, redes sociales o materiales impresos. Los usuarios los canjean introduciendo el código en el App Store, accediendo a una URL de canje o a través de un diálogo dentro de la app. Para configurar códigos de oferta, abre una suscripción en App Store Connect y ve a su sección **Offer Codes**. Puedes crear [tres tipos](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes) de códigos de oferta: - **Free** — la suscripción es gratuita durante un período determinado y la siguiente renovación se cobra al precio completo. - **Pay as you go** — el usuario paga un precio reducido en cada ciclo de facturación durante un período determinado y, después, la suscripción se renueva al precio completo. - **Pay up front** — el usuario paga un precio único reducido por toda la duración de la oferta y, después, la suscripción se renueva al precio completo. No es necesario añadir los códigos de oferta a Adapty. Apple etiqueta cada transacción durante el período de la oferta con la categoría del código de oferta. Esto incluye el canje inicial y todas las renovaciones con descuento posteriores. Adapty detecta la etiqueta y registra cada transacción con la categoría de oferta `offer_code`. Cuando termina el período de oferta y la suscripción se renueva al precio completo, la etiqueta deja de estar presente. Puedes filtrar los análisis por el tipo de oferta **Offer Code** en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds). #### Resolución de discrepancias en los ingresos \{#revenue-discrepancy-troubleshooting\} Si observas que una transacción con código de oferta aparece en Adapty al precio completo del producto en lugar del precio reducido, verifica lo siguiente en App Store Connect: - El código de oferta tiene los precios correctos configurados para todas las regiones donde los usuarios pueden canjearlo. - El precio de la oferta está configurado para el país o región específica del usuario. Apple envía el precio regional en la transacción. Si no hay ningún precio regional configurado para la oferta, Apple puede enviar el precio completo del producto. Puedes filtrar y verificar las transacciones con código de oferta en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds) mediante los filtros de tipo de oferta **Offer Code** y **Offer Discount Type**. #### Códigos promocionales heredados (obsoletos) \{#legacy-promo-codes-deprecated\} <Callout type="warning"> Apple dejó obsoletos los códigos promocionales para compras in-app en marzo de 2026. Los códigos de oferta los sustituyen con más funcionalidades: elegibilidad configurable, fechas de expiración y hasta 1 millón de códigos por trimestre. Si antes usabas códigos promocionales para compras in-app, migra a los códigos de oferta en App Store Connect. </Callout> Los códigos promocionales heredados (limitados a 100 por app y versión) daban acceso gratuito a una suscripción. A diferencia de los códigos de oferta, Apple no incluía información de descuento en las transacciones con código promocional — enviaba el precio completo del producto en el recibo. Por ello, Adapty registraba estas transacciones al precio completo, lo que generaba discrepancias entre los análisis de Adapty y App Store Connect. Si ves transacciones históricas al precio completo que deberían haber sido gratuitas, es probable que provengan de códigos promocionales heredados. Como estos códigos ya están obsoletos, migra a los códigos de oferta para un seguimiento preciso de los ingresos. </Details> Para mostrar la pantalla de canje de códigos en tu app: ```swift showLineNumbers Adapty.presentCodeRedemptionSheet() ``` :::danger Según nuestras observaciones, la pantalla de canje de códigos de oferta puede no funcionar de forma fiable en algunas apps. Te recomendamos redirigir al usuario directamente al App Store. Para ello, debes abrir una URL con el siguiente formato: `https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}` ::: --- # File: restore-purchase --- --- title: "Restaurar compras en aplicaciones móviles con iOS SDK" description: "Aprende cómo restaurar compras en Adapty para garantizar una experiencia de usuario fluida." --- Restaurar compras es una función que permite a los usuarios recuperar el acceso a contenido previamente adquirido, como suscripciones o compras in-app, sin que se les cobre de nuevo. Esta función es especialmente útil para quienes hayan desinstalado y reinstalado la app, o hayan cambiado de dispositivo y quieran acceder a su contenido sin volver a pagar. :::note En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin que necesites añadir código adicional. Si es tu caso, puedes saltarte este paso. ::: Para restaurar una compra cuando no usas [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { let profile = try await Adapty.restorePurchases() if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // successful access restore } } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.restorePurchases { [weak self] result in switch result { case let .success(profile): if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // successful access restore } case let .failure(error): // handle the error } } ``` </TabItem> </Tabs> Parámetros de respuesta: | Parámetro | Descripción | |---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** | <p>Un objeto [`AdaptyProfile`](https://swift.adapty.io/documentation/adapty/adaptyprofile). Este modelo contiene información sobre niveles de acceso, suscripciones y compras únicas.</p><p>Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app.</p> | :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: ios-transaction-management --- --- title: "Gestión avanzada de transacciones en el SDK de iOS" description: "Finaliza transacciones manualmente en tu app de iOS con el SDK de Adapty." --- :::note La gestión avanzada de transacciones está disponible en el SDK de Adapty para iOS a partir de la versión 3.12. ::: La gestión avanzada de transacciones en Adapty te da mayor control sobre cómo se procesan, verifican y finalizan las transacciones. Esta funcionalidad introduce tres características opcionales que trabajan en conjunto: | Característica | Propósito | |-------------------------------------------------------------|----------| | [`appAccountToken`](#assign-appaccounttoken) | Vincula las transacciones de Apple con tu ID de usuario interno | | [`jwsTransaction`](#access-the-jws-representation) | Proporciona el payload de transacción firmado por Apple para validación | | [Finalización manual](#control-transaction-finishing-behavior) | Permite finalizar transacciones solo después de que tu backend confirme el éxito | En conjunto, estas herramientas te permiten construir flujos de validación personalizados robustos mientras Adapty sigue sincronizando las transacciones con su backend. :::important La mayoría de las apps no necesitan esto. Por defecto, Adapty valida y finaliza automáticamente las transacciones de StoreKit. Usa esta guía solo si ejecutas tu propia validación en el backend o quieres controlar por completo el ciclo de vida de las compras. ::: ## Asignar `appAccountToken` \{#assign-appaccounttoken\} [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un **UUID** que te permite vincular las transacciones de la App Store con la identidad interna de tus usuarios. StoreKit asocia este token con cada transacción, de modo que tu backend puede relacionar los datos de la App Store con tus usuarios. Usa un UUID estable generado por usuario y reutilízalo para la misma cuenta en todos los dispositivos. Esto garantiza que las compras y las notificaciones de la App Store queden correctamente vinculadas. Puedes establecer el token de dos formas: durante la activación del SDK o al identificar al usuario. :::important Siempre debes pasar `appAccountToken` junto con `customerUserId`. Si solo pasas el token, no se incluirá en la transacción. ::: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers // During configuration: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID()) do { try await Adapty.activate(with: configurationBuilder.build()) } catch { // handle the error } // Or when identifying a user: do { try await Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID()) } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers // During configuration: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID", withAppAccountToken: <APP_ACCOUNT_TOKEN>) Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } // Or when identifying a user: Adapty.identify("YOUR_USER_ID", withAppAccountToken: <APP_ACCOUNT_TOKEN>) { error in if let error { // handle the error } } ``` </TabItem> </Tabs> ## Acceder a la representación JWS \{#access-the-jws-representation\} Al realizar una compra, el resultado incluye la transacción de Apple en [formato JWS Compact Serialization](https://developer.apple.com/documentation/storekit/verificationresult/jwsrepresentation-21vgo). Puedes reenviar este valor a tu backend para validación independiente o registro. ```swift let result = try await Adapty.makePurchase(product: paywallProduct) let jwsRepresentation = result.jwsTransaction ``` ## Controlar el comportamiento de finalización de transacciones \{#control-transaction-finishing-behavior\} Por defecto, Adapty finaliza automáticamente las transacciones de StoreKit tras la validación. Si necesitas retrasar la finalización hasta que tu backend confirme el éxito, establece el comportamiento de finalización en manual. En este modo: - Adapty sigue validando las compras y sincronizándolas con su backend. - Las transacciones permanecen sin finalizar hasta que llames explícitamente a `finish()`. ```swift var configBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_API_KEY") .with(transactionFinishBehavior: .manual) try await Adapty.activate(with: configBuilder.build()) ``` Al usar la finalización manual de transacciones, necesitas implementar el método delegado `onUnfinishedTransaction` para gestionar las transacciones sin finalizar: ```swift showLineNumbers title="Swift" extension YourApp: AdaptyDelegate { func onUnfinishedTransaction(_ transaction: AdaptyUnfinishedTransaction) async { // Perform your custom validation logic here // When ready, finish the transaction await transaction.finish() } } ``` Para obtener todas las transacciones sin finalizar actuales, usa el método `getUnfinishedTransactions()`: ```swift let unfinishedTransactions = try await Adapty.getUnfinishedTransactions() ``` --- # File: implement-observer-mode --- --- title: "Implementar el modo Observer en el SDK de iOS" description: "Implementa el modo Observer en Adapty para rastrear eventos de suscripción de usuarios en el SDK de iOS." --- Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analíticas. Si esto cubre tus necesidades, solo tienes que: 1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. 2. [Reportar las transacciones](report-transactions-observer-mode) desde tu infraestructura de compras existente a Adapty. Si además necesitas paywalls y pruebas A/B, se requiere una configuración adicional, como se describe a continuación. ## Configuración del modo Observer \{#observer-mode-setup\} Activa el modo Observer si gestionas las compras y el estado de las suscripciones por tu cuenta y usas Adapty para enviar eventos de suscripción y analíticas. :::important Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlo tú mismo. ::: <Tabs groupId="current-os" queryString> <TabItem value="swiftui" label="SwiftUI"> ```swift showLineNumbers @main struct YourApp: App { init() { // Configure Adapty SDK let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard .with(observerMode: true) let config = configurationBuilder.build() // Activate Adapty SDK asynchronously Task { do { try await Adapty.activate(with: configurationBuilder) } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } var body: some Scene { WindowGroup { // Your content view } } } } ``` </TabItem> <TabItem value="swift" label="UIKit" default> ```swift showLineNumbers Task { do { let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard .with(observerMode: true) let config = configurationBuilder.build() try await Adapty.activate(with: config) } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } ``` </TabItem> </Tabs> Parámetros: | Parámetro | Descripción | | --------------------------- | ------------------------------------------------------------ | | observerMode | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor predeterminado es `false`. | ## Usar los paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\} Si también quieres usar los paywalls y las funcionalidades de pruebas A/B de Adapty, puedes hacerlo, pero requiere una configuración adicional en el modo Observer. Esto es lo que necesitas hacer además de los pasos anteriores: 1. Muestra los paywalls de la forma habitual para los [paywalls con Remote Config](present-remote-config-paywalls). Para los paywalls con Paywall Builder, sigue las guías de configuración específicas para [iOS](ios-present-paywall-builder-paywalls-in-observer-mode). 3. [Asocia los paywalls](report-transactions-observer-mode) con las transacciones de compra. --- # File: report-transactions-observer-mode --- --- title: "Informar transacciones en Observer Mode en el SDK de iOS" description: "Informa transacciones de compra en el Observer Mode de Adapty para obtener información sobre usuarios y realizar un seguimiento de ingresos en el SDK de iOS." --- <Tabs groupId="sdk-version" queryString> <TabItem value="current" label="Adapty SDK v3.4+ (current)" default> En el modo Observer, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas informar las transacciones desde tu app store. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` para informar explícitamente cada transacción y que Adapty pueda reconocerla. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al informar una transacción. Esto vincula la compra con el paywall que la originó, garantizando un análisis preciso del paywall. ```swift showLineNumbers do { // every time when calling transasction.finish() try await Adapty.reportTransaction(transaction, withVariationId: <YOUR_PAYWALL_VARIATION_ID>) } catch { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | | --------------- | --------- | ------------------------------------------------------------ | | **transaction** | requerido | <ul><li> Para StoreKit 1: SKPaymentTransaction.</li><li> Para StoreKit 2: Transaction.</li></ul> | | **variationId** | opcional | El ID único de la variante del paywall. Obtenlo desde la propiedad `variationId` del objeto [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). | </TabItem> <TabItem value="old" label="Adapty SDK 3.3.x (legacy)" default> En el modo Observer, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas informar las transacciones desde tu app store o restaurarlas. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` para enviar los datos de la transacción a Adapty. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `withVariationId` al informar una transacción. Esto vincula la compra con el paywall que la originó, garantizando un análisis preciso del paywall. ```swift showLineNumbers do { // every time when calling transasction.finish() try await Adapty.reportTransaction(transaction, withVariationId: <YOUR_PAYWALL_VARIATION_ID>) } catch { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | | --------------- | --------- | ------------------------------------------------------------ | | **transaction** | requerido | <ul><li> Para StoreKit 1: SKPaymentTransaction.</li><li> Para StoreKit 2: Transaction.</li></ul> | | **variationId** | opcional | El ID único de la variante del paywall. Obtenlo desde la propiedad `variationId` del objeto [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). | </TabItem> <TabItem value="old2" label="Adapty SDK up to 3.2.x (legacy)" default> **Reporte de transacciones** - Las versiones hasta la 3.1.x escuchan automáticamente las transacciones en el App Store, por lo que no se requiere reporte manual. - La versión 3.2 no es compatible con el Observer Mode. **Asociar paywalls a transacciones** El SDK de Adapty no puede determinar el origen de las compras, ya que eres tú quien las procesa. Por ello, si planeas usar paywalls y/o pruebas A/B en el modo Observer, debes asociar la transacción proveniente de tu app store con el paywall correspondiente en el código de tu app. Es importante hacerlo correctamente antes de publicar tu app; de lo contrario, generará errores en los análisis. ```swift let variationId = paywall.variationId // There are two overloads: for StoreKit 1 and StoreKit 2 Adapty.setVariationId(variationId, forPurchasedTransaction: transactionId) { error in if error == nil { // successful binding } } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). | | transactionId | requerido | <p>Para StoreKit 1: un objeto [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction).</p><p>Para StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).</p> | </TabItem> </Tabs> --- # File: ios-present-paywall-builder-paywalls-in-observer-mode --- --- title: "Presentar paywalls del Paywall Builder en modo Observer en el SDK de iOS" description: "Aprende a presentar paywalls de PB en modo observer para obtener mejores insights." --- Si has personalizado un paywall con el Paywall Builder, no tienes que preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Este tipo de paywall incluye tanto lo que se muestra como la forma en que se muestra. :::warning Esta sección se refiere únicamente al [modo Observer](observer-vs-full-mode). Si no trabajas en el modo Observer, consulta [iOS - Mostrar paywalls con Paywall Builder](ios-present-paywalls). ::: <Tabs groupId="current-os" queryString> <TabItem value="sdk3" label="New Paywall Builder (SDK 3.0+)" default> <details> <summary>Antes de comenzar a mostrar paywalls (haz clic para expandir)</summary> 1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios). 2. Instala y configura el SDK. Asegúrate de establecer el parámetro `observerMode` en `true`. Consulta las instrucciones específicas para cada framework: [iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk). 3. [Crea productos](create-product) en el Adapty Dashboard. 4. [Configura los paywalls, asígnales productos](create-paywall) y personalízalos con Paywall Builder en el Adapty Dashboard. 5. [Crea placements y asígnales tus paywalls](create-placement) en el Adapty Dashboard. 6. [Obtén los paywalls de Paywall Builder y su configuración](get-pb-paywalls) en el código de tu aplicación. </details> <p> </p> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> 1. Implementa el objeto `AdaptyObserverModeResolver`: ```swift showLineNumbers title="Swift" func observerMode(didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void) { // use the product object to handle the purchase // use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase } El evento `observerMode(didInitiatePurchase:onStartPurchase:onFinishPurchase:)` te informará de que el usuario ha iniciado una compra. Puedes activar tu flujo de compra personalizado en respuesta a este callback. El evento `observerModeDidInitiateRestorePurchases(onStartRestore:onFinishRestore:)` te informará de que el usuario ha iniciado una restauración. Puedes activar tu flujo de restauración personalizado en respuesta a este callback. Además, recuerda invocar los siguientes callbacks para notificar a AdaptyUI sobre el proceso de compra o restauración. Esto es necesario para que el paywall funcione correctamente, por ejemplo, para mostrar el loader, entre otras cosas: | Callback | Descripción | | :----------------- | :--------------------------------------------------------------------------------------------------- | | onStartPurchase() | Este callback debe invocarse para notificar a AdaptyUI que la compra ha comenzado. | | onFinishPurchase() | Este callback debe invocarse para notificar a AdaptyUI que la compra ha finalizado. | | onStartRestore() | Este callback debe invocarse para notificar a AdaptyUI que la restauración ha comenzado. | | onFinishRestore() | Este callback debe invocarse para notificar a AdaptyUI que la restauración ha finalizado. | 2. Crea un objeto de configuración de paywall: ```swift showLineNumbers title="Swift" do { let paywallConfiguration = try AdaptyUI.getPaywallConfiguration( forPaywall: <paywall object>, observerModeResolver: <AdaptyObserverModeResolver> ) } catch { // handle the error } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :----------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador para el paywall deseado. | | **ObserverModeResolver** | obligatorio | El objeto `AdaptyObserverModeResolver` que implementaste en el paso anterior. | 3. Inicializa el paywall visual que quieres mostrar usando el método `.paywallController(for:products:viewConfiguration:delegate:)`: ```swift showLineNumbers title="Swift" import Adapty import AdaptyUI let visualPaywall = AdaptyUI.paywallController( with: <paywall configuration object>, delegate: <AdaptyPaywallControllerDelegate> ) ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :----------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Paywall Configuration** | requerido | Un objeto `AdaptyUI.PaywallConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consulta el tema [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. | | **Delegate** | requerido | Un `AdaptyPaywallControllerDelegate` para escuchar los eventos del paywall. Consulta el tema [Gestión de eventos del paywall](ios-handling-events) para más detalles. | Retorna: | Objeto | Descripción | | :---------------------- | :--------------------------------------------------- | | AdaptyPaywallController | Un objeto que representa la pantalla del paywall solicitado | Una vez creado el objeto correctamente, puedes mostrarlo así: ```swift showLineNumbers title="Swift" present(visualPaywall, animated: true) ``` :::warning No olvides [asociar los paywalls a las transacciones de compra](report-transactions-observer-mode). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra. ::: </TabItem> <TabItem value="swiftui" label="SwiftUI" default> Para mostrar el paywall visual en la pantalla del dispositivo, usa el modificador `.paywall` en SwiftUI: ```swift showLineNumbers title="SwiftUI" @State var paywallPresented = false var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, paywallConfiguration: <paywall configuration object>, didPerformAction: { action in switch action { case .close: paywallPresented = false default: // Handle other actions break } }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didFailRendering: { error in paywallPresented = false } ) } ``` The input appears to be empty or contains only a heading without any actual content to translate. Please provide the full MDX documentation you'd like translated from English to Spanish (es-ES). | Parámetro | Presencia | Descripción | | :----------------------- | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Paywall Configuration** | obligatorio | Un objeto `AdaptyUI.PaywallConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consulta el tema [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. | | **Products** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el momento de visualización de los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. | | **TagResolver** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas actúan como marcadores de posición en el contenido del paywall y se reemplazan dinámicamente con cadenas específicas para ofrecer contenido personalizado. Consulta el tema [Etiquetas personalizadas en Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | | **ObserverModeResolver** | opcional | El objeto `AdaptyObserverModeResolver` que implementaste en el paso anterior | Parámetros de closure: | Parámetro de closure | Descripción | | :------------------- | :--------------------------------------------------------------------------------------------------- | | **didFinishRestore** | Si Adapty.restorePurchases() tiene éxito, se invocará este callback. | | **didFailRestore** | Si Adapty.restorePurchases() falla, se invocará este callback. | | **didFailRendering** | Si ocurre un error durante el renderizado de la interfaz, se invocará este callback. | Consulta el tema [iOS - Manejo de eventos](ios-handling-events) para ver otros parámetros de closure. :::warning No olvides [asociar los paywalls a las transacciones de compra](report-transactions-observer-mode). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra. ::: </TabItem> </Tabs> </TabItem> <TabItem value="sdk2" label="Legacy Paywall Builder (SDK up to 2.x)" default> <details> <summary>Antes de empezar a mostrar paywalls (haz clic para expandir)</summary> 1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios). 1. Instala y configura el SDK de Adapty. Asegúrate de establecer el parámetro `observerMode` en `true`. Consulta las instrucciones específicas para cada framework: [iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk) y [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk). 2. [Crea productos](create-product) en el Adapty Dashboard. 3. [Configura paywalls, asígnales productos](create-paywall) y personalízalos con Paywall Builder en el Adapty Dashboard. 4. [Crea placements y asígnales tus paywalls](create-placement) en el Adapty Dashboard. 5. [Obtén los paywalls de Paywall Builder y su configuración](get-pb-paywalls) en el código de tu app. </details> <p> </p> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> 1. Implementa el objeto `AdaptyObserverModeDelegate`: ```swift showLineNumbers title="Swift" func paywallController(_ controller: AdaptyPaywallController, didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void) { // use the product object to handle the purchase // use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase } ``` El evento `paywallController(_:didInitiatePurchase:onStartPurchase:onFinishPurchase:)` te informará de que el usuario ha iniciado una compra. Puedes activar tu flujo de compra personalizado en respuesta a este evento. Además, recuerda invocar los siguientes callbacks para notificar a AdaptyUI sobre el proceso de compra. Esto es necesario para el comportamiento correcto del paywall, como mostrar el loader, entre otras cosas: | Callback | Descripción | | :--------------- | :-------------------------------------------------------------------------------------------------- | | onStartPurchase | Este callback debe invocarse para notificar a AdaptyUI que la compra ha comenzado. | | onFinishPurchase | Este callback debe invocarse para notificar a AdaptyUI que la compra ha finalizado. | 2. Inicializa el paywall visual que quieres mostrar usando el método `.paywallController(for:products:viewConfiguration:delegate:observerModeDelegate:)`: ```swift showLineNumbers title="Swift" import Adapty import AdaptyUI let visualPaywall = AdaptyUI.paywallController( for: <paywall object>, products: <paywall products array>, viewConfiguration: <LocalizedViewConfiguration>, delegate: <AdaptyPaywallControllerDelegate> observerModeDelegate: <AdaptyObserverModeDelegate> ) ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :----------------------- | :-------- | :----------------------------------------------------------- | | **Paywall** | requerido | Un objeto `AdaptyPaywall` para obtener un controlador para el paywall deseado. | | **Products** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el tiempo de visualización de los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. | | **ViewConfiguration** | requerido | Un objeto `AdaptyUI.LocalizedViewConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.getViewConfiguration(paywall:locale:)`. Consulta el tema [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. | | **Delegate** | requerido | Un `AdaptyPaywallControllerDelegate` para escuchar los eventos del paywall. Consulta el tema [Gestión de eventos del paywall](ios-handling-events) para más detalles. | | **ObserverModeDelegate** | requerido | El objeto `AdaptyObserverModeDelegate` que implementaste en el paso anterior. | | **TagResolver** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas actúan como marcadores de posición en el contenido del paywall y se reemplazan dinámicamente con cadenas específicas para ofrecer contenido personalizado. Consulta el tema [Etiquetas personalizadas en el Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | Devuelve: | Objeto | Descripción | | :---------------------- | :--------------------------------------------------- | | AdaptyPaywallController | Un objeto que representa la pantalla del paywall solicitado | Una vez que el objeto se haya creado correctamente, puedes mostrarlo así: ```swift showLineNumbers title="Swift" present(visualPaywall, animated: true) ``` :::warning No olvides [asociar los paywalls a las transacciones de compra](report-transactions-observer-mode). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra. ::: </TabItem> <TabItem value="swiftui" label="SwiftUI" default> Para mostrar el paywall visual en la pantalla del dispositivo, utiliza el modificador `.paywall` en SwiftUI: ```swift showLineNumbers title="SwiftUI" @State var paywallPresented = false var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, paywall: <paywall object>, configuration: <LocalizedViewConfiguration>, didPerformAction: { action in switch action { case .close: paywallPresented = false default: // Handle other actions break } }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didFailRendering: { error in paywallPresented = false }, observerModeDidInitiatePurchase: { product, onStartPurchase, onFinishPurchase in // use the product object to handle the purchase // use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase }, ) } ``` The input is empty — there is no MDX content to translate. Please provide the MDX documentation you'd like translated from English to Spanish (es-ES). | Parámetro | Presencia | Descripción | | :---------------- | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. | | **Product** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el momento en que se muestran los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. | | **Configuration** | obligatorio | Un objeto `AdaptyUI.LocalizedViewConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.getViewConfiguration(paywall:locale:)`. Consulta el artículo [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. | | **TagResolver** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas funcionan como marcadores de posición en el contenido del paywall y se reemplazan dinámicamente con cadenas específicas para personalizar el contenido. Consulta el artículo [Etiquetas personalizadas en el Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | The input appears to be just the text "Closure parameters:" with no actual MDX documentation content to translate. Please provide the full MDX documentation you'd like translated from English to Spanish (es-ES). | Parámetro de closure | Descripción | | :---------------------------------- | :------------------------------------------------------------------------------------------------- | | **didFinishRestore** | Si Adapty.restorePurchases() tiene éxito, se invocará este callback. | | **didFailRestore** | Si Adapty.restorePurchases() falla, se invocará este callback. | | **didFailRendering** | Si ocurre un error durante el renderizado de la interfaz, se invocará este callback. | | **observerModeDidInitiatePurchase** | Este callback se invoca cuando un usuario inicia una compra. | Consulta el tema [iOS - Manejo de eventos](ios-handling-events) para conocer otros parámetros de closure. :::warning No olvides [asociar paywalls a las transacciones de compra](report-transactions-observer-mode). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra. ::: </TabItem> </Tabs> </TabItem> </Tabs> --- # File: ios-troubleshoot-purchases --- --- title: "Solucionar problemas de compras en el SDK de iOS" description: "Solucionar problemas de compras en el SDK de iOS" --- Esta guía te ayuda a resolver los problemas más comunes al implementar compras manualmente en el SDK de iOS. ## AdaptyError.cantMakePayments en el modo observador \{#adaptyerrorcantmakepayments-in-observer-mode\} **Problema**: Estás recibiendo `AdaptyError.cantMakePayments` al usar `makePurchase` en el modo observador. **Causa**: En el modo observador, debes gestionar las compras por tu cuenta, no usar el método `makePurchase` de Adapty. **Solución**: Si usas `makePurchase` para las compras, desactiva el modo observador. Puedes usar `makePurchase` o gestionar las compras por tu cuenta en el modo observador, pero no ambas opciones a la vez. Consulta [Implementar el modo observador](implement-observer-mode) para más detalles. ## No se encuentran makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\} **Problema**: Estás experimentando problemas porque no se encuentran los `makePurchasesCompletionHandlers`. **Causa**: Esto suele estar relacionado con problemas en las pruebas de sandbox. **Solución**: Crea un nuevo usuario de sandbox e inténtalo de nuevo. Esto generalmente resuelve los problemas con el manejador de finalización de compras en sandbox. ## Otros problemas \{#other-issues\} **Problema**: Estás experimentando otros problemas relacionados con las compras que no se tratan más arriba. **Solución**: Si es necesario, migra el SDK a la versión más reciente siguiendo las [guías de migración](ios-sdk-migration-guides). Muchos problemas se resuelven en las versiones más nuevas del SDK. --- # File: identifying-users --- --- title: "Identificar usuarios en iOS SDK" description: "Identifica usuarios en Adapty para mejorar las experiencias de suscripción personalizadas." --- Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, deberías establecer tu propio Customer User ID. Puedes encontrar usuarios por su Customer User ID en la sección [Perfiles](profiles-crm) y usarlo en la [API del servidor](getting-started-with-server-side-api), que se enviará a todas las integraciones. ## Establecer el ID de usuario en la configuración \{#set-customer-user-id-on-configuration\} Si tienes un ID de usuario durante la configuración, pásalo como parámetro `customerUserId` al método `.activate()`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers // In your AppDelegate class: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID") do { try await Adapty.activate(with: configurationBuilder.build()) } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers // In your AppDelegate class: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID") Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } ``` </TabItem> </Tabs> :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Establecer el ID de usuario después de la configuración \{#set-customer-user-id-after-configuration\} Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo más adelante en cualquier momento con el método `.identify()`. Los casos más habituales para usar este método son después del registro o la autenticación, cuando el usuario pasa de ser un usuario anónimo a un usuario autenticado. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { try await Adapty.identify("YOUR_USER_ID") } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.identify("YOUR_USER_ID") { error in if let error { // handle the error } } ``` </TabItem> </Tabs> Parámetros de la solicitud: - **Customer User ID** (obligatorio): un identificador de usuario en formato string. :::warning Reenvío de datos significativos del usuario En algunos casos, como cuando un usuario vuelve a iniciar sesión en su cuenta, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si pasaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, debes volver a enviar esos datos para el usuario identificado. También es importante tener en cuenta que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes. ::: ## Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\} Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { try await Adapty.logout() } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.logout { error in if error == nil { // successful logout } } ``` </TabItem> </Tabs> Después puedes iniciar sesión con el usuario usando el método `.identify()`. ## Configurar appAccountToken \{#set-appaccounttoken\} El [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un UUID que ayuda a StoreKit 2 de Apple a identificar a los usuarios entre instalaciones de la app y dispositivos. A partir del SDK de Adapty para iOS 3.10.2, puedes pasar el `appAccountToken` al configurar el SDK o al identificar a un usuario: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers // During configuration: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID()) do { try await Adapty.activate(with: configurationBuilder.build()) } catch { // handle the error } // Or when identifying a user: do { try await Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID()) } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers // Durante la configuración: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID()) Adapty.activate(with: configurationBuilder.build()) { error in // maneja el error } // O al identificar a un usuario: Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID()) { error in if let error { // maneja el error } } ``` </TabItem> </Tabs> A continuación, puedes iniciar sesión del usuario con el método `.identify()`. ## Detectar usuarios en varios dispositivos \{#detect-users-across-devices\} --- no_index: true --- Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`. Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID. **Lo que Adapty puede detectar entre dispositivos** | Tu configuración | Lo que Adapty detecta | Lo que debes hacer | | --- | --- | --- | | Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. | | Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. | | Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. | | Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. | **Restaurar compras en un dispositivo nuevo** Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK. No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado. --- # File: setting-user-attributes --- --- title: "Definir atributos de usuario en el SDK de iOS" description: "Aprende a definir atributos de usuario en Adapty para mejorar la segmentación de audiencias." --- Puedes añadir atributos opcionales como el email, número de teléfono, etc., al usuario de tu app. Luego puedes usar esos atributos para crear [segmentos](segments) de usuarios o simplemente consultarlos en el CRM. ### Definir atributos de usuario \{#setting-user-attributes\} Para definir atributos de usuario, llama al método `.updateProfile()`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers let builder = AdaptyProfileParameters.Builder() .with(email: "email@email.com") .with(phoneNumber: "+18888888888") .with(firstName: "John") .with(lastName: "Appleseed") .with(gender: .other) .with(birthday: Date()) do { try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers let builder = AdaptyProfileParameters.Builder() .with(email: "email@email.com") .with(phoneNumber: "+18888888888") .with(firstName: "John") .with(lastName: "Appleseed") .with(gender: .other) .with(birthday: Date()) Adapty.updateProfile(params: builder.build()) { error in if error != nil { // handle the error } } ``` </TabItem> </Tabs> Ten en cuenta que los atributos que hayas definido previamente con el método `updateProfile` no se restablecerán. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Lista de claves permitidas \{#the-allowed-keys-list\} Las claves `<Key>` permitidas de `AdaptyProfileParameters.Builder` y sus valores `<Value>` correspondientes son: | Clave | Valor | |---|-----| | <p>email</p><p>phoneNumber</p><p>firstName</p><p>lastName</p> | String | | gender | Enum, los valores permitidos son: `female`, `male`, `other` | | birthday | Date | ### Atributos de usuario personalizados \{#custom-user-attributes\} Puedes definir tus propios atributos personalizados, que normalmente están relacionados con el uso de tu app. Por ejemplo, en apps de fitness pueden ser el número de entrenamientos por semana; en apps de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes usarlos en segmentos para crear paywalls y ofertas dirigidas, y también en analíticas para identificar qué métricas de producto influyen más en los ingresos. ```swift showLineNumbers do { builder = try builder.with(customAttribute: "value1", forKey: "key1") } catch { // handle key/value validation error } ``` Para eliminar una clave existente, usa el método `.withRemoved(customAttributeForKey:)`: ```swift showLineNumbers do { builder = try builder.withRemoved(customAttributeForKey: "key2") } catch { // handle error } ``` A veces necesitas saber qué atributos personalizados ya están definidos. Para ello, usa el campo `customAttributes` del objeto `AdaptyProfile`. :::warning Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor podrían haber cambiado desde la última sincronización. ::: ### Límites \{#limits\} - Hasta 30 atributos personalizados por usuario. - Los nombres de clave tienen hasta 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.` - El valor puede ser una cadena de texto o un número decimal con un máximo de 50 caracteres. --- # File: subscription-status --- --- title: "Comprobar el estado de suscripción en iOS SDK" description: "Rastrea y gestiona el estado de suscripción de los usuarios en Adapty para mejorar la retención de clientes." --- Con Adapty, hacer seguimiento del estado de suscripción es muy sencillo. No tienes que insertar manualmente los IDs de producto en tu código. En su lugar, puedes confirmar fácilmente el estado de suscripción de un usuario comprobando si tiene un [nivel de acceso](access-level) activo. Antes de empezar a comprobar el estado de suscripción, configura las [Notificaciones del servidor de App Store](enable-app-store-server-notifications). ## Nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptyprofile-object\} Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile). Te recomendamos recuperar el perfil cuando tu app arranque, por ejemplo al [identificar a un usuario](identifying-users#set-customer-user-id-on-configuration), y actualizarlo cada vez que se produzcan cambios. Así podrás usar el objeto de perfil sin tener que solicitarlo repetidamente. Para recibir notificaciones de las actualizaciones del perfil, escucha los cambios tal como se describe en la sección [Escuchar actualizaciones del estado de suscripción](subscription-status#listening-for-subscription-status-updates) más abajo. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\} Para obtener el nivel de acceso desde el servidor, usa el método `.getProfile()`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { let profile = try await Adapty.getProfile() if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get() { // check the access profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } } ``` </TabItem> </Tabs> Parámetros de respuesta: | Parámetro | Descripción | | --------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Profile | <p>Un objeto [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile). En general, solo necesitas comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app.</p><p></p><p>El método `.getProfile` devuelve el resultado más actualizado, ya que siempre intenta consultar la API. Si por algún motivo (por ejemplo, sin conexión a internet) el SDK de Adapty no puede obtener información del servidor, se devolverán los datos de la caché. También es importante destacar que el SDK de Adapty actualiza la caché de `AdaptyProfile` periódicamente para mantener esta información lo más actualizada posible.</p> | El método `.getProfile()` te proporciona el perfil del usuario a partir del cual puedes obtener el estado del nivel de acceso. Puedes tener múltiples niveles de acceso por app. Por ejemplo, si tienes una app de noticias y vendes suscripciones a diferentes temáticas de forma independiente, puedes crear los niveles de acceso "sports" y "science". Pero la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes usar simplemente el nivel de acceso predeterminado "premium". A continuación tienes un ejemplo para comprobar el nivel de acceso predeterminado "premium": <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { let profile = try await Adapty.getProfile() let isPremium = profile.accessLevels["premium"]?.isActive ?? false // grant access to premium features } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get(), profile.accessLevels["premium"]?.isActive ?? false { // grant access to premium features } } ``` </TabItem> </Tabs> ### Escuchar actualizaciones del estado de suscripción \{#listening-for-subscription-status-updates\} Cada vez que cambia la suscripción del usuario, Adapty lanza un evento. Para recibir mensajes de Adapty, necesitas realizar una configuración adicional: ```swift showLineNumbers Adapty.delegate = self // To receive subscription updates, extend `AdaptyDelegate` with this method: nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) { // handle any changes to subscription state } ``` Adapty también lanza un evento al inicio de la aplicación. En ese caso, se pasará el estado de suscripción almacenado en caché. ### Caché del estado de suscripción \{#subscription-status-cache\} La caché implementada en el SDK de Adapty almacena el estado de suscripción del perfil. Esto significa que, aunque el servidor no esté disponible, se puede acceder a los datos almacenados en caché para obtener información sobre el estado de suscripción del perfil. No obstante, hay que tener en cuenta que no es posible solicitar datos directamente desde la caché. El SDK consulta periódicamente el servidor cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si hay alguna modificación, como nuevas transacciones u otras actualizaciones, se enviarán a los datos almacenados en caché para mantenerlos sincronizados con el servidor. --- # File: ios-deal-with-att --- --- title: "Gestión de ATT en el SDK de iOS" description: "Comienza a usar Adapty en iOS para simplificar la configuración y gestión de suscripciones." --- Si tu aplicación utiliza el framework AppTrackingTransparency y presenta una solicitud de autorización de seguimiento al usuario, debes enviar el [estado de autorización](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/) a Adapty. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers let builder = AdaptyProfileParameters.Builder() .with(appTrackingTransparencyStatus: .authorized) do { try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="Swift-Callback" default> ```swift showLineNumbers if #available(iOS 14, macOS 11.0, *) { let builder = AdaptyProfileParameters.Builder() .with(appTrackingTransparencyStatus: .authorized) Adapty.updateProfile(params: builder.build()) { [weak self] error in if error != nil { // handle the error } } } ``` </TabItem> </Tabs> :::warning Recomendamos encarecidamente que envíes este valor lo antes posible cuando cambie; solo así los datos se enviarán a tiempo a las integraciones que hayas configurado. ::: --- # File: kids-mode --- --- title: "Modo Kids en iOS SDK" description: "Activa el Modo Kids fácilmente para cumplir con las políticas de Apple. No se recopila IDFA ni datos publicitarios en iOS SDK." --- Si tu aplicación iOS está destinada a niños, debes seguir las políticas de [Apple](https://developer.apple.com/kids/). Si usas el SDK de Adapty, unos pocos pasos sencillos te ayudarán a configurarlo para cumplir con estas políticas y superar las revisiones de la app store. ## ¿Qué se necesita? \{#whats-required\} Debes configurar el SDK de Adapty para deshabilitar la recopilación de: - [IDFA (Identificador para anunciantes)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers) - [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) Además, te recomendamos usar el ID de usuario del cliente con cuidado. Un ID de usuario con el formato `<Nombre.Apellido>` se considerará claramente como recopilación de datos personales, al igual que el uso de un correo electrónico. En el Modo Kids, la mejor práctica es utilizar identificadores aleatorios o anonimizados (por ejemplo, IDs con hash o UUIDs generados por el dispositivo) para garantizar el cumplimiento. ## Activar el Modo Kids \{#enabling-kids-mode\} ### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\} En el Adapty Dashboard, debes deshabilitar la recopilación de direcciones IP. Para ello, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** bajo **Collect users' IP address**. ### Cambios en el código de tu aplicación \{#updates-in-your-mobile-app-code\} Para cumplir con las políticas, deshabilita la recopilación del IDFA y la dirección IP del usuario. <Tabs> <TabItem value="spm" label="Swift Package Manager" default> Si usas Swift Package Manager, puedes activar el Modo Kids seleccionando el módulo **Adapty_KidsMode** en Xcode al instalar el SDK. En Xcode, ve a **File** -> **Add Package Dependency...**. Ten en cuenta que los pasos para agregar dependencias de paquetes pueden variar entre versiones de Xcode, así que consulta la documentación de Xcode si es necesario. 1. Introduce la URL del repositorio: ``` https://github.com/adaptyteam/AdaptySDK-iOS.git ``` 2. Selecciona la versión (se recomienda la última versión estable) y haz clic en **Add Package**. 3. En la ventana **Choose Package Products**, selecciona los módulos que necesites: - **Adapty_KidsMode** (módulo principal) - **AdaptyUI** (opcional: solo si planeas usar Paywall Builder) No necesitarás ningún otro paquete. 4. Haz clic en **Add Package** para completar la instalación. </TabItem> <TabItem value="cocoapods" label="CocoaPods"> 1. Actualiza tu Podfile: - Si **no** tienes una sección `post_install`, añade el bloque de código completo a continuación. - Si **sí** tienes una sección `post_install`, incorpora las líneas resaltadas en ella. ```ruby showLineNumbers title="Podfile" post_install do |installer| installer.pods_project.targets.each do |target| // highlight-start if target.name == 'Adapty' target.build_configurations.each do |config| config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)'] config.build_settings['OTHER_SWIFT_FLAGS'] << '-DADAPTY_KIDS_MODE' end end // highlight-end end end ``` 2. Ejecuta el siguiente comando para aplicar los cambios: ```sh showLineNumbers title="Shell" pod install ``` </TabItem> </Tabs> --- # File: get-onboardings --- --- title: "Obtener onboardings y su configuración" description: "Aprende cómo recuperar onboardings en Adapty." --- Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, tal como se describe a continuación. Antes de empezar, asegúrate de que: 1. Has instalado el [SDK de Adapty para iOS, Android, React Native o Flutter](installation-of-adapty-sdks) en la versión 3.8.0 o superior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). ## Obtener un onboarding \{#fetch-onboarding\} Cuando creas un [onboarding](onboardings) con nuestro builder sin código, se almacena como un contenedor con la configuración que tu app necesita obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a cuestionarios o entradas de formularios). El contenedor también registra automáticamente los eventos de analíticas, por lo que no necesitas implementar un seguimiento de vistas por separado. Para un mejor rendimiento, obtén la configuración del onboarding con antelación para que las imágenes tengan tiempo suficiente de descargarse antes de mostrárselas a los usuarios. Para obtener un onboarding, usa el método `getOnboarding`: ```swift showLineNumbers do { let onboarding = try await Adapty.getOnboarding(placementId: "YOUR_PLACEMENT_ID") // the requested onboarding } catch { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, puede que los usuarios no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita arriba y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el límite, se devolverán los datos en caché o el respaldo local.</p><p>Ten en cuenta que, en casos excepcionales, este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede implicar distintas solicitudes internamente.</p> | Parámetros de respuesta: | Parámetro | Descripción | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | Un objeto [`AdaptyOnboarding`](https://swift.adapty.io/documentation/adapty/adaptyonboarding) con: el identificador y la configuración del onboarding, Remote Config y varias otras propiedades. | ## Acelerar la obtención del onboarding con el onboarding de audiencia predeterminada \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} Normalmente, los onboardings se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos en los que tienes muchas audiencias y onboardings, y tus usuarios tienen una conexión a internet débil, la obtención de un onboarding puede tardar más de lo deseado. En esas situaciones, puede que quieras mostrar un onboarding predeterminado para garantizar una experiencia fluida en lugar de no mostrar ninguno. Para abordar esto, puedes usar el método `getOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, como se detalla en la sección [Obtener un onboarding](#fetch-onboarding) anterior. :::warning Considera usar `getOnboarding` en lugar de `getOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: Puede generar problemas al soportar múltiples versiones de la app, lo que requiere diseños compatibles con versiones anteriores o aceptar que las versiones más antiguas puedan mostrarse incorrectamente. - **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación basada en país, atribución o atributos personalizados. Si la velocidad de obtención supera estos inconvenientes para tu caso de uso, utiliza `getOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getOnboarding` como se describe [arriba](#fetch-onboarding). ::: ```swift showLineNumbers Adapty.getOnboardingForDefaultAudience(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(onboarding): // the requested onboarding case let .failure(error): // handle the error } } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, puede que los usuarios no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita arriba y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | --- # File: ios-present-onboardings --- --- title: "Mostrar onboardings en el SDK de iOS" description: "Descubre cómo mostrar onboardings en iOS para aumentar las conversiones y los ingresos." --- Si has personalizado un onboarding con el builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese onboarding ya incluye tanto lo que debe mostrarse como la forma en que debe hacerlo. Antes de empezar, asegúrate de que: 1. Tienes instalado el [SDK de Adapty para iOS](sdk-installation-ios) 3.8.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). ## Mostrar onboardings en Swift \{#present-onboardings-in-swift\} Para mostrar el onboarding visual en la pantalla del dispositivo, sigue estos pasos: 1. Obtén la configuración de la vista del onboarding con el método `.getOnboardingConfiguration`. 2. Inicializa el onboarding visual que quieres mostrar usando el método `.onboardingController`: Parámetros de la solicitud: | Parámetro | Presencia | Descripción | |:-------------------------------|:-----------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **onboarding configuration** | obligatorio | Un objeto `AdaptyUI.OnboardingConfiguration` con todas las propiedades del onboarding. Usa el método `AdaptyUI.getOnboardingConfiguration` para obtenerlo. | | **delegate** | obligatorio | Un `AdaptyOnboardingControllerDelegate` para escuchar los eventos del onboarding. | Devuelve: | Objeto | Descripción | |:-------------------------------|:---------------------------------------------------------------| | **AdaptyOnboardingController** | Un objeto que representa la pantalla del onboarding solicitado | 3. Una vez creado el objeto correctamente, puedes mostrarlo en la pantalla del dispositivo: ```swift showLineNumbers title="Swift" import Adapty import AdaptyUI // 0. Get an onboarding if you haven't done it yet let onboarding = try await Adapty.getOnboarding(placementId: "YOUR_PLACEMENT_ID") // 1. Obtain the onboarding view configuration: let configuration = try AdaptyUI.getOnboardingConfiguration(forOnboarding: onboarding) // 2. Create Onboarding View Controller let onboardingController = try AdaptyUI.onboardingController( with: configuration, delegate: <AdaptyOnboardingControllerDelegate> ) // 3. Present it to the user present(onboardingController, animated: true) ``` ## Mostrar onboardings en SwiftUI \{#present-onboardings-in-swiftui\} Para mostrar el onboarding visual en la pantalla del dispositivo con SwiftUI: ```swift showLineNumbers title="SwiftUI" // 1. Obtain the onboarding view configuration: let configuration = try AdaptyUI.getOnboardingConfiguration(forOnboarding: onboarding) // 2. Display the Onboarding View within your view hierarchy AdaptyOnboardingView( configuration: configuration, placeholder: { Text("Your Placeholder View") }, onCloseAction: { action in // hide the onboarding view }, onError: { error in // handle the error } ) ``` ## Añadir transiciones suaves entre la pantalla de carga y el onboarding \{#add-smooth-transitions-between-the-splash-screen-and-onboarding\} Por defecto, entre la pantalla de carga y el onboarding verás una pantalla de carga hasta que el onboarding esté completamente listo. Sin embargo, si quieres que la transición sea más fluida, puedes personalizarla y prolongar la pantalla de carga o mostrar otra cosa en su lugar. Para hacerlo, define un placeholder (qué se mostrará mientras se carga el onboarding). Si defines un placeholder, el onboarding se cargará en segundo plano y se mostrará automáticamente cuando esté listo. <Tabs> <TabItem value="swift" label="UIKit"> ```swift showLineNumbers extension YourOnboardingManagerClass: AdaptyOnboardingControllerDelegate { func onboardingsControllerLoadingPlaceholder( _ controller: AdaptyOnboardingController ) -> UIView? { // instantiate and return the UIView which will be presented while onboarding is being loaded } } ``` </TabItem> <TabItem value="swiftui" label="SwiftUI"> ```swift showLineNumbers AdaptyOnboardingView( configuration: configuration, placeholder: { // define your placeholder view, which will be presented while onboarding is being loaded }, // the rest of the implementation ) ``` </TabItem> </Tabs> ## Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\} :::important La personalización de cómo se abren los enlaces en los onboardings está disponible a partir de la versión 3.15.1 del SDK de Adapty. ::: Por defecto, los enlaces de los onboardings se abren en un navegador dentro de la app. Esto ofrece una experiencia de usuario fluida al mostrar las páginas web dentro de tu aplicación, sin necesidad de cambiar de app. Si prefieres abrir los enlaces en un navegador externo, puedes personalizar este comportamiento estableciendo el parámetro `externalUrlsPresentation` en `.externalBrowser`: ```swift showLineNumbers let configuration = try AdaptyUI.getOnboardingConfiguration( forOnboarding: onboarding, externalUrlsPresentation: .externalBrowser // default – .inAppBrowser ) ``` --- # File: ios-handling-onboarding-events --- --- title: "Gestionar eventos de onboarding en el SDK de iOS" description: "Gestiona eventos relacionados con el onboarding en iOS usando Adapty." --- Antes de empezar, asegúrate de que: 1. Tienes instalado el [SDK de Adapty para iOS](sdk-installation-ios) 3.8.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). Los onboardings configurados con el builder generan eventos a los que tu app puede responder. Aprende cómo responder a estos eventos a continuación. Para controlar o monitorizar los procesos que ocurren en la pantalla de onboarding dentro de tu app, implementa los métodos de `AdaptyOnboardingControllerDelegate`. ## Acciones personalizadas \{#custom-actions\} En el builder, puedes añadir una acción **personalizada** a un botón y asignarle un ID. <img src="/assets/shared/img/ios-events-1.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Luego puedes usar este ID en tu código y gestionarlo como una acción personalizada. Por ejemplo, si un usuario pulsa un botón personalizado como **Login** o **Allow notifications**, se activará el método delegado `onboardingController` con el caso `.custom(id:)` y el parámetro `actionId` corresponde al **Action ID** del builder. Puedes crear tus propios IDs, como "allowNotifications". ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onCustomAction action: AdaptyOnboardingsCustomAction) { if action.actionId == "allowNotifications" { // Request notification permissions } } func onboardingController(_ controller: AdaptyOnboardingController, didFailWithError error: AdaptyUIError) { // Handle errors } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "actionId": "allowNotifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ``` </Details> ## Cerrar el onboarding \{#closing-onboarding\} El onboarding se considera cerrado cuando el usuario pulsa un botón con la acción **Close** asignada. <img src="/assets/shared/img/ios-events-2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::important Ten en cuenta que debes gestionar lo que ocurre cuando el usuario cierra el onboarding. Por ejemplo, debes dejar de mostrar el onboarding. ::: Por ejemplo: ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onCloseAction action: AdaptyOnboardingsCloseAction) { controller.dismiss(animated: true) } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> ## Abrir un paywall \{#opening-a-paywall\} :::tip Gestiona este evento para abrir un paywall si quieres abrirlo dentro del onboarding. Si quieres abrir un paywall después de cerrarlo, hay una forma más directa: gestiona [`AdaptyOnboardingsCloseAction`](#closing-onboarding) y abre el paywall sin depender de los datos del evento. ::: Si un usuario pulsa un botón que abre un paywall, recibirás el ID de acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más fluida de trabajar con paywalls en onboardings es hacer que el ID de acción sea igual al ID del placement del paywall. Así, tras el `AdaptyOnboardingsOpenPaywallAction`, puedes usar el ID del placement para obtener y abrir el paywall directamente. Ten en cuenta que solo se puede mostrar una vista (paywall u onboarding) en pantalla al mismo tiempo. Si presentas un paywall encima de un onboarding, no puedes controlar el onboarding en segundo plano de forma programática. Intentar descartar el onboarding cerrará el paywall en su lugar, dejando el onboarding visible. Para evitar esto, descarta siempre la vista del onboarding antes de presentar el paywall. ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onPaywallAction action: AdaptyOnboardingsOpenPaywallAction) { // Dismiss onboarding before presenting paywall controller.dismiss(animated: true) { Task { do { // Get the paywall using the placement ID from the action let paywall = try await Adapty.getPaywall(placementId: action.actionId) // Get the paywall configuration let paywallConfig = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall ) // Create and present the paywall controller let paywallController = try AdaptyUI.paywallController( with: paywallConfig, delegate: self ) // Present the paywall from the root view controller if let rootVC = UIApplication.shared.windows.first?.rootViewController { rootVC.present(paywallController, animated: true) } } catch { // Handle any errors that occur during paywall loading print("Failed to present paywall: \(error)") } } } } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ``` </Details> ## Finalización de carga del onboarding \{#finishing-loading-onboarding\} Cuando el onboarding termina de cargarse, se invocará este método: ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, didFinishLoading action: OnboardingsDidFinishLoadingAction) { // Handle loading completion } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ``` </Details> ## Seguimiento de navegación \{#tracking-navigation\} El método `onAnalyticsEvent` se llama cuando ocurren varios eventos de analíticas durante el flujo del onboarding. El objeto `event` puede ser uno de los siguientes tipos: | Tipo | Descripción | |------------|-------------| | `onboardingStarted` | Cuando el onboarding se ha cargado | | `screenPresented` | Cuando se muestra cualquier pantalla | | `screenCompleted` | Cuando se completa una pantalla. Incluye el `elementId` opcional (identificador del elemento completado) y la `reply` opcional (respuesta del usuario). Se activa cuando los usuarios realizan cualquier acción para salir de la pantalla. | | `secondScreenPresented` | Cuando se muestra la segunda pantalla | | `userEmailCollected` | Se activa cuando se recoge el email del usuario a través del campo de entrada | | `onboardingCompleted` | Se activa cuando el usuario llega a una pantalla con el ID `final`. Si necesitas este evento, [asigna el ID `final` a la última pantalla](design-onboarding). | | `unknown` | Para cualquier tipo de evento no reconocido. Incluye `name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) | Cada evento incluye información `meta` con los siguientes campos: | Campo | Descripción | |------------|-------------| | `onboardingId` | Identificador único del flujo de onboarding | | `screenClientId` | Identificador de la pantalla actual | | `screenIndex` | Posición de la pantalla actual en el flujo | | `screensTotal` | Número total de pantallas en el flujo | Aquí tienes un ejemplo de cómo puedes usar los eventos de analíticas para el seguimiento: ```swift func onboardingController(_ controller: AdaptyOnboardingController, onAnalyticsEvent event: AdaptyOnboardingsAnalyticsEvent) { switch event { case .onboardingStarted(let meta): // Track onboarding start trackEvent("onboarding_started", meta: meta) case .screenPresented(let meta): // Track screen presentation trackEvent("screen_presented", meta: meta) case .screenCompleted(let meta, let elementId, let reply): // Track screen completion with user response trackEvent("screen_completed", meta: meta, elementId: elementId, reply: reply) case .onboardingCompleted(let meta): // Track successful onboarding completion trackEvent("onboarding_completed", meta: meta) case .unknown(let meta, let name): // Handle unknown events trackEvent(name, meta: meta) // Handle other cases as needed } } ``` <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // onboardingStarted { "name": "onboarding_started", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } // screenPresented { "name": "screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 4 } } // screenCompleted { "name": "screen_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 }, "params": { "element_id": "profile_form", "reply": "success" } } // secondScreenPresented { "name": "second_screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // userEmailCollected { "name": "user_email_collected", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // onboardingCompleted { "name": "onboarding_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> --- # File: ios-onboarding-input --- --- title: "Procesar datos de onboardings en iOS SDK" description: "Guarda y utiliza datos de onboardings en tu app iOS con Adapty SDK." --- Cuando tus usuarios responden a una pregunta de un cuestionario o introducen datos en un campo de texto, se invocará el método `onStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código. Por ejemplo: ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) { // Store user preferences or responses switch action.params { case .select(let params): // Handle single selection case .multiSelect(let params): // Handle multiple selections case .input(let params): // Handle text input case .datePicker(let params): // Handle date selection } } ``` El objeto `action` contiene: | Parámetro | Descripción | |----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `elementId` | Un identificador único para el elemento de entrada. Puedes usarlo para asociar preguntas con respuestas al guardarlas. | | `params` | El objeto de datos de entrada del usuario que contiene las propiedades de tipo y valor. | | `params.type` | El tipo de elemento de entrada. Puede ser:<br/>• `"select"` - Selección única entre opciones<br/>• `"multiSelect"` - Selección múltiple entre opciones<br/>• `"input"` - Campo de texto<br/>• `"datePicker"` - Selección de fecha | | `params.value` | El o los valores seleccionados o introducidos por el usuario. La estructura depende del tipo:<br/>• `select`: Objeto con `id`, `value`, `label`<br/>• `multiSelect`: Array de objetos con `id`, `value`, `label`<br/>• `input`: Objeto con `type`, `value`<br/>• `datePicker`: Objeto con `day`, `month`, `year` | <Details> <summary>Ejemplos de datos guardados (pueden variar según tu implementación)</summary> ```javascript // Example of a saved select action { "elementId": "preference_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "preferences_screen", "screenIndex": 1, "screensTotal": 3 }, "params": { "type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Example of a saved multi-select action { "elementId": "interests_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 3 }, "params": { "type": "multiSelect", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Example of a saved input action { "elementId": "name_input", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "input", "value": { "type": "text", "value": "John Doe" } } } // Example of a saved date picker action { "elementId": "birthday_picker", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "datePicker", "value": { "day": 15, "month": 6, "year": 1990 } } } ``` </Details> ## Casos de uso \{#use-cases\} ### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\} Si quieres vincular de inmediato los datos de entrada con el perfil del usuario y evitar pedirle la misma información dos veces, debes [actualizar el perfil del usuario](setting-user-attributes) con los datos de entrada al gestionar la acción. Por ejemplo, pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name` y quieres establecer el valor de ese campo como nombre de pila del usuario. Además, les pides que introduzcan su email en el campo `email`. En el código de tu app, puede quedar así: ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) { // Store user preferences or responses switch action.params { case .input(let params): // Handle text input let builder = AdaptyProfileParameters.Builder() // Map elementId to appropriate profile field switch action.elementId { case "name": builder.with(firstName: params.value.value) case "email": builder.with(email: params.value.value) } do { try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } } ``` ### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\} Usando cuestionarios en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios tras completar el onboarding. Por ejemplo, puedes preguntarles sobre su experiencia con el deporte y mostrar diferentes CTAs y productos a distintos grupos de usuarios. 1. [Añade un cuestionario](onboarding-quizzes) en el constructor de onboarding y asigna IDs significativos a sus opciones. 2. Gestiona las respuestas del cuestionario según sus IDs y [establece atributos personalizados](setting-user-attributes) para los usuarios. ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) { // Handle quiz responses and set custom attributes switch action.params { case .select(let params): // Handle quiz selection let builder = AdaptyProfileParameters.Builder() // Map quiz responses to custom attributes switch action.elementId { case "experience": // Set custom attribute 'experience' with the selected value (beginner, amateur, pro) try? builder.with(customAttribute: params.value.value, forKey: "experience") } do { try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } } } ``` 3. [Crea segmentos](segments) para cada valor de atributo personalizado. 4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado. 5. [Muestra un paywall](ios-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](ios-handling-onboarding-events#opening-a-paywall). --- # File: ios-sdk-call-order --- --- title: "Orden de llamadas en iOS SDK" description: "Evita pérdidas de acceso premium, atribuciones faltantes y errores intermitentes #2002 llamando a los métodos del SDK de Adapty en el orden correcto." --- `Adapty.activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que se resuelva, el SDK no tiene estado. Cualquier llamada realizada antes o en paralelo con `activate()` falla con [`#2002 notActivated`](ios-sdk-error-handling#network-errors). Si tu app autentica usuarios y recoges el customer user ID después del lanzamiento, llama a `Adapty.identify()` en ese momento. No llames a métodos de acción del usuario hasta que `identify` se resuelva. Las llamadas que compiten con él fallan con [`#3006 profileWasChanged`](ios-sdk-error-handling#general-errors), o aterrizan en el perfil anónimo creado en la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la propiedad de la instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `identify` y sigue trabajando con el perfil anónimo. Los SDKs de MMP y analíticas (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera sus callbacks de UID antes de llamar a `Adapty.activate`. De lo contrario, el ID del MMP cae en un perfil anónimo breve y no siempre se transfiere al identificado. Para los detalles específicos de AppsFlyer, consulta [AppsFlyer](appsflyer). ## El orden correcto \{#the-correct-order\} Tu camino depende de dos cosas: cuándo conoces el customer user ID y si usas un SDK de MMP o analíticas. - **Pasos 2 y 5**: Obligatorios para cada app. Activa el SDK y luego llama a los métodos del SDK. - **Pasos 1 y 3**: Necesarios solo si integras un SDK de MMP o analíticas (AppsFlyer, Adjust, Branch, PostHog). - **Paso 4**: Necesario solo si tu app autentica usuarios y recoge el customer user ID después del lanzamiento. Si tienes el customer user ID en el lanzamiento de la app, pásalo directamente a `activate()` (paso 2a). Esta ruta nunca crea un perfil anónimo, por lo que el paso 4 no es necesario. | Paso | Llamada | Cuándo | Notas | |------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | Inicializa tu SDK de MMP o analíticas (AppsFlyer, Adjust, PostHog, Branch) | Lanzamiento de la app, primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerUID`. | | 2a | `Adapty.activate(with: config)` con `customerUserId` establecido en la config | Lanzamiento de la app, después del paso 1, si tienes el customer user ID | Recomendado. Nunca se crea un perfil anónimo. | | 2b | `Adapty.activate(with: config)` sin `customerUserId` | Lanzamiento de la app, después del paso 1, si no tienes el customer user ID (o nunca lo recoges) | Adapty crea un perfil anónimo. | | 3 | `Adapty.setIntegrationIdentifier(key:value:)` para cada MMP | Después del paso 2, antes de cualquier llamada de acción del usuario | Necesario para que los IDs de MMP caigan en el perfil correcto. | | 4 | `try await Adapty.identify("YOUR_USER_ID")` | Después del paso 3 (o paso 2 si no hay MMP), antes del paso 5 — solo en la ruta 2b con autenticación | Siempre `await`. Las llamadas concurrentes durante `identify` producen `#3006 profileWasChanged`. | | 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Después del paso 4 si llamas a `identify`; de lo contrario, después del paso 3 (o paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. | :::important Saltarse estos pasos provoca pérdida de acceso premium para usuarios que regresan, `appsflyer_id` faltante en los perfiles, y paywalls devueltos contra la audiencia equivocada. ::: ## Instalaciones web2app y de embudo web \{#web2app-and-web-funnel-installs\} Si los usuarios compran en un checkout web (Stripe, Paddle, FunnelFox) e instalan después la app nativa, el primer `activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el customer user ID antes del lanzamiento de la app (desde tu flujo de autenticación o referrer de instalación), pásalo directamente a `activate()`. De lo contrario, la compra web será invisible en el dispositivo hasta que llames a `identify("YOUR_USER_ID")` y luego a `restorePurchases`. Para los metadatos que debes enviar con cada checkout web, consulta: - [Stripe](stripe) - [Paddle](paddle) --- # File: ios-optimize-paywall-fetching --- --- title: "Optimizar la carga del paywall en el SDK de iOS" description: "Carga paywalls de Adapty de forma fiable: temporización, caché y patrones de respaldo para iOS." --- Una carga fiable del paywall en iOS hace tres cosas: renderiza rápido, devuelve el paywall orientado a la audiencia y recurre al respaldo sin problemas cuando la red es lenta. Las reglas siguientes cubren los patrones de temporización, caché y respaldo para conseguirlo. :::tip Las reglas asumen que `Adapty.activate()` y `Adapty.identify()` ya han resuelto. Consulta [Orden de llamadas en el SDK de iOS](ios-sdk-call-order). ::: ## Reglas y errores comunes \{#rules-and-pitfalls\} | Haz esto | No hagas esto | Por qué | |---|---|---| | Carga el placement que vas a mostrar. | Precarga todos los placements de forma concurrente al iniciar. | La precarga masiva bloquea el hilo principal y produce una pantalla en negro durante la ráfaga. | | Llama a `getPaywall` después de que la atribución haya tenido tiempo de resolverse — por ejemplo, 1–2 segundos después de `activate` o cuando se dispare `onProfileUpdate`. | Llama a `getPaywall` en `App.init()`. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia predeterminada y omite silenciosamente los segmentos y la personalización de ASA. | | Establece un `loadTimeout` y configura un [paywall de respaldo](fallback-paywalls) para cada placement. | Esperes a `getPaywall` indefinidamente. | Sin un tiempo límite, los usuarios con mala conectividad ven una pantalla en blanco hasta que la red responde — o cierran la app. | Consulta [Cargar paywalls y productos](fetch-paywalls-and-products) para la referencia de los parámetros `fetchPolicy` y `loadTimeout`, y [Placements](placements) para elegir el placement adecuado. ## Ajustar para conectividad deficiente \{#tune-for-poor-connectivity\} Para mercados con conectividad consistentemente deficiente (zonas rurales, transporte, regiones afectadas por el enrutamiento): - Establece `fetchPolicy: .returnCacheDataElseLoad` en cada carga excepto la primera. - Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard. - Establece `loadTimeout` en 3–5 segundos y acepta el respaldo cuando se agote el tiempo. - No condicionar la visualización del paywall a `getProfile()`. Llama a `getPaywall` de forma independiente para que un perfil lento no bloquee la UI. --- # File: ios-test --- --- title: "Prueba y lanzamiento en el SDK de iOS" description: "Aprende a comprobar el estado de la suscripción en tu app de iOS con Adapty." --- Si ya has implementado el SDK de Adapty en tu app de iOS, querrás comprobar que todo está configurado correctamente y que las compras funcionan como se espera. Esto implica probar tanto la integración del SDK como las compras en sí. ## Prueba tu app \{#test-your-app\} Para realizar pruebas exhaustivas de tus compras in-app, incluyendo pruebas en sandbox y validación en TestFlight, consulta nuestra [guía de pruebas](test-purchases-in-sandbox). ## Prepárate para el lanzamiento \{#prepare-for-release\} Antes de enviar tu app al store, sigue el [checklist de lanzamiento](release-checklist) para confirmar que: - La conexión con el store y las notificaciones del servidor están configuradas - Las compras se completan y se reportan a Adapty - El acceso se desbloquea y se restaura correctamente - Se cumplen los requisitos de privacidad y revisión --- # File: InvalidProductIdentifiers --- --- title: "Solución al error Code-1000 noProductIDsFound" description: "Resuelve los errores de identificador de producto no válido al gestionar suscripciones en Adapty." --- El error con código 1000, `noProductIDsFound`, indica que ninguno de los productos que solicitaste en el paywall está disponible para su compra en el App Store, aunque aparezcan listados allí. Este error puede ir acompañado a veces de una advertencia `InvalidProductIdentifiers`. Si la advertencia aparece sin error, puedes ignorarla sin problema. Si te encuentras con el error `noProductIDsFound`, sigue estos pasos para resolverlo: ## Paso 1. Verifica el bundle ID \{#step-2-check-bundle-id\} --- no_index: true --- 1. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu app y ve a la sección **General** → **App Information**. 2. Copia el **Bundle ID** en la subsección **General Information**. <Zoom> <img src="/docs/img/afd5012-bundle_id_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 3. Abre la pestaña [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) desde el menú superior de Adapty y pega el valor copiado en el campo **Bundle ID**. <Zoom> <img src="/docs/img/2d64163-bundle_id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 4. Vuelve a la página **App information** en App Store Connect y copia el **Apple ID** desde allí. 5. En la página [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) del Adapty Dashboard, pega el ID en el campo **Apple app ID**. ## Paso 2. Verifica los productos 1. Ve a **App Store Connect** y navega a [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) en el menú de la izquierda. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripciones. Verás tus productos listados en la sección **Subscriptions**. 3. Asegúrate de que el producto que estás probando figure como **Ready to Submit**. Si no es así, sigue las instrucciones de la página [Producto en App Store](app-store-products). <img src="/assets/shared/img/ready-to-submit.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Compara el ID del producto de la tabla con el que aparece en la pestaña [**Products**](https://app.adapty.io/products) del Adapty Dashboard. Si los IDs no coinciden, copia el ID del producto de la tabla y [crea un producto](create-product) con ese ID en el Adapty Dashboard. <img src="/assets/shared/img/product-id-copy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 3. Verifica la disponibilidad del producto \{#step-4-check-product-availability\} 1. Vuelve a **App Store Connect** y abre la misma sección **Subscriptions**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripciones para ver tus productos. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hasta la sección **Availability** y comprueba que todos los países y regiones necesarios están listados. <img src="/assets/shared/img/product-availability.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 4. Verifica los precios del producto \{#step-5-check-product-prices\} 1. De nuevo, ve a la sección **Monetization** → **Subscriptions** en **App Store Connect**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripciones. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hacia abajo hasta **Subscription Pricing** y expande la sección **Current Pricing for New Subscribers**. <img src="/assets/shared/img/check-prices.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Asegúrate de que todos los precios necesarios están listados. <img src="/assets/shared/img/product-pricing.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 5. Verifica que el estado de pago de la app, la cuenta bancaria y los formularios fiscales estén activos 1. En la página de inicio de [**App Store Connect**](https://appstoreconnect.apple.com/), haz clic en **Business**. <img src="/assets/shared/img/business.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Selecciona el nombre de tu empresa. <img src="/assets/shared/img/business-name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Desplázate hacia abajo y comprueba que tu **Paid Apps Agreement**, **Bank Account** y **Tax forms** aparecen todos como **Active**. <img src="/assets/shared/img/appstore-connect-status.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Siguiendo estos pasos, deberías poder resolver la advertencia `InvalidProductIdentifiers` y publicar tus productos en el store. ## Paso 6. Vuelve a crear el producto si está bloqueado Los pasos 1–5 pueden superarse correctamente — estado `Approved`, Bundle ID coincidente, API key válida — y aun así el SDK devuelve `1000 noProductIDsFound`. En ese caso, puede que el producto esté bloqueado en el registro de Apple. El registro de productos de Apple entra ocasionalmente en un estado en el que un producto existe en la interfaz de App Store Connect pero no queda expuesto en la ruta de consulta de StoreKit. Elimina el producto en App Store Connect y vuelve a crearlo con el mismo ID de producto. Espera hasta 24 horas tras la recreación para que se propague. --- # File: cantMakePayments --- --- title: "Solución para el error Code-1003 cantMakePayment" description: "Resuelve el error de realización de pagos al gestionar suscripciones en Adapty." --- El error 1003, `cantMakePayments`, indica que no es posible realizar compras in-app en este dispositivo. Si encuentras el error `cantMakePayments`, normalmente se debe a una de estas razones: - Restricciones del dispositivo: El error no está relacionado con Adapty. Consulta las soluciones más abajo. - Configuración del modo Observer: El método `makePurchase` y el modo Observer no pueden usarse al mismo tiempo. Consulta la sección más abajo. ## Problema: Restricciones del dispositivo \{#issue-device-restrictions\} | Problema | Solución | |-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| | Restricciones de Screen Time | Desactiva las restricciones de compras in-app en [Screen Time](https://support.apple.com/en-us/102470) | | Cuenta suspendida | Contacta con el soporte de Apple para resolver problemas con la cuenta | | Restricciones regionales | Usa una cuenta de App Store de una región compatible | ## Problema: Usar el modo Observer y makePurchase a la vez \{#issue-using-both-observer-mode-and-makepurchase\} Si usas `makePurchases` para gestionar las compras, no necesitas el modo Observer. El [modo Observer](observer-vs-full-mode) solo es necesario si implementas la lógica de compra tú mismo. Por lo tanto, si usas `makePurchase`, puedes eliminar sin problema la activación del modo Observer del código de inicialización del SDK. --- # File: migration-to-ios-315 --- --- title: "Migrar Adapty iOS SDK a la v. 3.15" description: "Migra al Adapty iOS SDK v3.15 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- Si usas [Paywall Builder](adapty-paywall-builder) en [modo Observer](observer-vs-full-mode), a partir del iOS SDK 3.15 necesitas implementar un nuevo método `observerModeDidInitiateRestorePurchases(onStartRestore:onFinishRestore:)`. Este método ofrece mayor control sobre la lógica de restauración, permitiéndote gestionar las restauraciones de compras en tu propio flujo personalizado. Para ver todos los detalles de implementación, consulta [Presentar paywalls de Paywall Builder en modo Observer](ios-present-paywall-builder-paywalls-in-observer-mode). ```diff showLineNumbers func observerMode(didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void) { // use the product object to handle the purchase // use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase } + func observerModeDidInitiateRestorePurchases(onStartRestore: @escaping () -> Void, + onFinishRestore: @escaping () -> Void) { + // use the onStartRestore and onFinishRestore callbacks to notify AdaptyUI about the process of the restore + } ``` --- # File: migration-to-ios-sdk-34 --- --- title: "Migrar el SDK de Adapty para iOS a la v. 3.4" description: "Migra al SDK de Adapty para iOS v3.4 para obtener mejor rendimiento y nuevas funciones de monetización." --- El SDK de Adapty 3.4.0 es una versión principal que introduce mejoras que requieren pasos de migración por tu parte. ## Actualizar la activación del SDK de Adapty \{#update-adapty-sdk-activation\} <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```diff showLineNumbers // In your AppDelegate class: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") - Adapty.activate(with: configurationBuilder) { error in + Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } ``` **Actualizar los archivos de paywall de respaldo** Actualiza tus archivos de paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK: 1. [Descarga los archivos de paywall de respaldo actualizados](fallback-paywalls) desde el Adapty Dashboard. 2. [Sustituye los paywalls de respaldo existentes en tu aplicación móvil](ios-use-fallback-paywalls) por los nuevos archivos. </TabItem> <TabItem value="swiftui" label="SwiftUI" default> ```diff showLineNumbers @main struct SampleApp: App { init() { let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") Task { - try await Adapty.activate(with: configurationBuilder) + try await Adapty.activate(with: configurationBuilder.build()) } } var body: some Scene { WindowGroup { ContentView() } } } ``` **Actualizar los archivos de paywall de respaldo** Actualiza tus archivos de paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK: 1. [Descarga los archivos de paywall de respaldo actualizados](fallback-paywalls) desde el Adapty Dashboard. 2. [Sustituye los paywalls de respaldo existentes en tu aplicación móvil](ios-use-fallback-paywalls) por los nuevos archivos. </TabItem> </Tabs> --- # File: migration-to-ios330 --- --- title: "Migrar el SDK de Adapty iOS a la v. 3.3" description: "Migra al SDK de Adapty iOS v3.3 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK de Adapty 3.3.0 es una versión mayor que incluye mejoras que pueden requerir algunos pasos de migración de tu parte. 1. Renombra `Adapty.Configuration` a `AdaptyConfiguration`. 2. Renombra el método `getViewConfiguration` a `getPaywallConfiguration`. 3. Elimina los parámetros `didCancelPurchase` y `paywall` de SwiftUI, y renombra el parámetro `viewConfiguration` a `paywallConfiguration`. 4. Actualiza cómo procesas las compras in-app promocionales desde el App Store eliminando el parámetro `defermentCompletion` del método `AdaptyDelegate`. 5. Elimina el método `getProductsIntroductoryOfferEligibility`. 6. Actualiza las configuraciones de integración para Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase y Google Analytics, Mixpanel, OneSignal, Pushwoosh. 7. Actualiza la implementación del modo Observer. <div style={{ textAlign: 'center' }}> <iframe width="560" height="315" src="https://www.youtube.com/embed/9Xs8d0lt_RY?si=xvWhUO2tlG1tKP5f" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen> </iframe> </div> ## Renombrar Adapty.Configuration a AdaptyConfiguration \{#rename-adaptyconfiguration-to-adaptyconfiguration\} Actualiza el código de activación del SDK de Adapty iOS de la siguiente manera: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```diff showLineNumbers // In your AppDelegate class: let configurationBuilder = - Adapty.Configuration + AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(observerMode: false) .with(customerUserId: "YOUR_USER_ID") .with(idfaCollectionDisabled: false) .with(ipAddressCollectionDisabled: false) Adapty.activate(with: configurationBuilder) { error in // handle the error } ``` </TabItem> <TabItem value="swiftui" label="SwiftUI" default> ```diff showLineNumbers @main struct SampleApp: App { init() let configurationBuilder = - Adapty.Configuration + AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(observerMode: false) // optional .with(customerUserId: "YOUR_USER_ID") // optional .with(idfaCollectionDisabled: false) // optional .with(ipAddressCollectionDisabled: false) // optional Task { try await Adapty.activate(with: configurationBuilder) } } var body: some Scene { WindowGroup { ContentView() } } } ``` </TabItem> </Tabs> ## Renombrar el método getViewConfiguration a getPaywallConfiguration \{#rename-getviewconfiguration-method-to-getpaywallconfiguration\} Actualiza el nombre del método para obtener el `viewConfiguration` del paywall: ```diff showLineNumbers guard paywall.hasViewConfiguration else { // use your custom logic return } do { - let paywallConfiguration = try await AdaptyUI.getViewConfiguration( + let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall ) // use loaded configuration } catch { // handle the error } ``` Para más detalles sobre el método, consulta [Obtener la configuración de vista del paywall diseñado con Paywall Builder](get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). ## Cambiar parámetros en SwiftUI \{#change-parameters-in-swiftui\} Se han realizado las siguientes actualizaciones en SwiftUI: 1. Se ha eliminado el parámetro `didCancelPurchase`. Usa `didFinishPurchase` en su lugar. 2. El método `.paywall()` ya no acepta un objeto paywall. 3. El parámetro `paywallConfiguration` ha reemplazado al parámetro `viewConfiguration`. Actualiza tu código de la siguiente manera: ```diff showLineNumbers @State var paywallPresented = false var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, - paywall: <paywall object>, - viewConfiguration: <LocalizedViewConfiguration>, + paywallConfiguration: <AdaptyUI.PaywallConfiguration>, didPerformAction: { action in switch action { case .close: paywallPresented = false default: // Handle other actions break } }, - didFinishPurchase: { product, profile in paywallPresented = false }, + didFinishPurchase: { product, purchaseResult in /* handle the result*/ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didFailRendering: { error in paywallPresented = false } - didCancelPurchase: { product in /* handle the result*/} ) } ``` ## Actualizar el manejo de compras in-app promocionales desde el App Store \{#update-handling-of-promotional-in-app-purchases-from-app-store\} Actualiza cómo gestionas las compras in-app promocionales desde el App Store eliminando el parámetro `defermentCompletion` del método `AdaptyDelegate`, como se muestra en el siguiente ejemplo: ```swift showLineNumbers title="Swift" final class YourAdaptyDelegateImplementation: AdaptyDelegate { nonisolated func shouldAddStorePayment(for product: AdaptyDeferredProduct) -> Bool { // 1a. // Return `true` to continue the transaction in your app. // 1b. // Store the product object and return `false` to defer or cancel the transaction. false } // 2. Continue the deferred purchase later on by passing the product to `makePurchase` func continueDeferredPurchase() async { let storedProduct: AdaptyDeferredProduct = // get the product object from the 1b. do { try await Adapty.makePurchase(product: storedProduct) } catch { // handle the error } } } ``` ## Eliminar el método getProductsIntroductoryOfferEligibility \{#remove-getproductsintroductoryoffereligibility-method\} Antes del SDK de Adapty iOS 3.3.0, el objeto de producto siempre incluía las ofertas, independientemente de si el usuario era elegible. Tenías que verificar manualmente la elegibilidad antes de usar la oferta. Ahora, el objeto de producto solo incluye una oferta si el usuario es elegible. Esto significa que ya no necesitas verificar la elegibilidad: si hay una oferta presente, el usuario es elegible. Si aún quieres ver las ofertas para usuarios que no son elegibles, consulta `sk1Product` y `sk2Product`. ## Actualizar la configuración del SDK de integraciones de terceros \{#update-third-party-integration-sdk-configuration\} A partir del SDK de Adapty iOS 3.3.0, hemos actualizado la API pública del método `updateAttribution`. Anteriormente aceptaba un diccionario `[AnyHashable: Any]`, lo que te permitía pasar objetos de atribución directamente desde varios servicios. Ahora requiere un `[String: any Sendable]`, por lo que deberás convertir los objetos de atribución antes de pasarlos. Para garantizar que las integraciones funcionen correctamente con el SDK de Adapty iOS 3.3.0 y versiones posteriores, actualiza las configuraciones de tu SDK para las siguientes integraciones tal como se describe en las secciones a continuación. ### Adjust \{#adjust\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Adjust](adjust#connect-your-app-to-adjust). <Tabs groupId="current-os" queryString> <TabItem value="v5" label="Adjust 5.x+" default> ```diff showLineNumbers class AdjustModuleImplementation { - func updateAdjustAttribution() { - Adjust.attribution { attribution in - guard let attributionDictionary = attribution?.dictionary()?.toSendableDict() else { return } - - Adjust.adid { adid in - guard let adid else { return } - - Adapty.updateAttribution(attributionDictionary, source: .adjust, networkUserId: adid) { error in - // handle the error - } - } - } - } + func updateAdjustAdid() { + Adjust.adid { adid in + guard let adid else { return } + + Adapty.setIntegrationIdentifier(key: "adjust_device_id", value: adid) + } + } + + func updateAdjustAttribution() { + Adjust.attribution { attribution in + guard let attribution = attribution?.dictionary() else { + return + } + + Adapty.updateAttribution(attribution, source: "adjust") + } + } } ``` </TabItem> <TabItem value="v4" label="Adjust 4.x" default> ```diff showLineNumbers class YourAdjustDelegateImplementation { // Find your implementation of AdjustDelegate // and update adjustAttributionChanged method: func adjustAttributionChanged(_ attribution: ADJAttribution?) { - if let attribution = attribution?.dictionary()?.toSendableDict() { - Adapty.updateAttribution(attribution, source: .adjust) + if let attribution = attribution?.dictionary() { + Adapty.updateAttribution(attribution, source: "adjust") } } } ``` </TabItem> </Tabs> ### AirBridge \{#airbridge\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con AirBridge](airbridge#connect-your-app-to-airbridge). ```diff showLineNumbers import AirBridge - let builder = AdaptyProfileParameters.Builder() - .with(airbridgeDeviceId: AirBridge.deviceUUID()) - - Adapty.updateProfile(params: builder.build()) + do { + try await Adapty.setIntegrationIdentifier( + key: "airbridge_device_id", + value: AirBridge.deviceUUID() + ) + } catch { + // handle the error + } ``` ### Amplitude \{#amplitude\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Amplitude](amplitude#sdk-configuration). ```diff showLineNumbers import Amplitude - let builder = AdaptyProfileParameters.Builder() - .with(amplitudeUserId: Amplitude.instance().userId) - .with(amplitudeDeviceId: Amplitude.instance().deviceId) - - Adapty.updateProfile(params: builder.build()) + do { + try await Adapty.setIntegrationIdentifier( + key: "amplitude_user_id", + value: Amplitude.instance().userId + ) + try await Adapty.setIntegrationIdentifier( + key: "amplitude_device_id", + value: Amplitude.instance().deviceId + ) + } catch { + // handle the error + } ``` ### AppMetrica \{#appmetrica\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con AppMetrica](appmetrica#sdk-configuration). ```diff showLineNumbers import AppMetricaCore - if let deviceID = AppMetrica.deviceID { - let builder = AdaptyProfileParameters.Builder() - .with(appmetricaDeviceId: deviceID) - .with(appmetricaProfileId: "YOUR_ADAPTY_CUSTOMER_USER_ID") - - Adapty.updateProfile(params: builder.build()) - } + if let deviceID = AppMetrica.deviceID { + do { + try await Adapty.setIntegrationIdentifier( + key: "appmetrica_device_id", + value: deviceID + ) + try await Adapty.setIntegrationIdentifier( + key: "appmetrica_profile_id", + value: "YOUR_ADAPTY_CUSTOMER_USER_ID" + ) + } catch { + // handle the error + } + } ``` ### AppsFlyer \{#appsflyer\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con AppsFlyer](appsflyer#connect-your-app-to-appsflyer). ```diff showLineNumbers class YourAppsFlyerLibDelegateImplementation { // Find your implementation of AppsFlyerLibDelegate // and update onConversionDataSuccess method: func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) { let uid = AppsFlyerLib.shared().getAppsFlyerUID() - Adapty.updateAttribution( - conversionInfo.toSendableDict(), - source: .appsflyer, - networkUserId: uid - ) + Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid) + Adapty.updateAttribution(conversionInfo, source: "appsflyer") } } ``` ### Branch \{#branch\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Branch](branch#connect-your-app-to-branch). ```diff showLineNumbers class YourBranchImplementation { func initializeBranch() { // Pass the attribution you receive from the initializing method of Branch iOS SDK to Adapty. Branch.getInstance().initSession(launchOptions: launchOptions) { (data, error) in - if let data = data?.toSendableDict() { - Adapty.updateAttribution(data, source: .branch) - } + if let data { + Adapty.updateAttribution(data, source: "branch") + } } } } ``` ### Facebook Ads \{#facebook-ads\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Facebook Ads](facebook-ads#connect-your-app-to-facebook-ads). ```diff showLineNumbers import FacebookCore - let builder = AdaptyProfileParameters.Builder() - .with(facebookAnonymousId: AppEvents.shared.anonymousID) - - do { - try Adapty.updateProfile(params: builder.build()) - } catch { - // handle the error - } + do { + try await Adapty.setIntegrationIdentifier( + key: "facebook_anonymous_id", + value: AppEvents.shared.anonymousID + ) + } catch { + // handle the error + } ``` ### Firebase y Google Analytics \{#firebase-and-google-analytics\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Firebase y Google Analytics](firebase-and-google-analytics). ```diff showLineNumbers import FirebaseCore import FirebaseAnalytics FirebaseApp.configure() - if let appInstanceId = Analytics.appInstanceID() { - let builder = AdaptyProfileParameters.Builder() - .with(firebaseAppInstanceId: appInstanceId) - Adapty.updateProfile(params: builder.build()) { error in - // handle error - } - } + if let appInstanceId = Analytics.appInstanceID() { + do { + try await Adapty.setIntegrationIdentifier( + key: "firebase_app_instance_id", + value: appInstanceId + ) + } catch { + // handle the error + } + } ``` ### Mixpanel \{#mixpanel\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Mixpanel](mixpanel#sdk-configuration). ```diff showLineNumbers import Mixpanel - let builder = AdaptyProfileParameters.Builder() - .with(mixpanelUserId: Mixpanel.mainInstance().distinctId) - - do { - try await Adapty.updateProfile(params: builder.build()) - } catch { - // handle the error - } + do { + try await Adapty.setIntegrationIdentifier( + key: "mixpanel_user_id", + value: Mixpanel.mainInstance().distinctId + ) + } catch { + // handle the error + } ``` ### OneSignal \{#onesignal\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con OneSignal](onesignal#sdk-configuration). ```diff showLineNumbers // PlayerID (pre-v5 OneSignal SDK) // in your OSSubscriptionObserver implementation func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges) { if let playerId = stateChanges.to.userId { - let params = AdaptyProfileParameters.Builder() - .with(oneSignalPlayerId: playerId) - .build() - - Adapty.updateProfile(params:params) { error in - // check error - } + Task { + try await Adapty.setIntegrationIdentifier( + key: "one_signal_player_id", + value: playerId + ) + } } } // SubscriptionID (v5+ OneSignal SDK) OneSignal.Notifications.requestPermission({ accepted in - let id = OneSignal.User.pushSubscription.id - - let builder = AdaptyProfileParameters.Builder() - .with(oneSignalSubscriptionId: id) - - Adapty.updateProfile(params: builder.build()) + Task { + try await Adapty.setIntegrationIdentifier( + key: "one_signal_subscription_id", + value: OneSignal.User.pushSubscription.id + ) + } }, fallbackToSettings: true) ``` ### Pushwoosh \{#pushwoosh\} Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Pushwoosh](pushwoosh#sdk-configuration). ```diff showLineNumbers - let params = AdaptyProfileParameters.Builder() - .with(pushwooshHWID: Pushwoosh.sharedInstance().getHWID()) - .build() - - Adapty.updateProfile(params: params) { error in - // handle the error - } + do { + try await Adapty.setIntegrationIdentifier( + key: "pushwoosh_hwid", + value: Pushwoosh.sharedInstance().getHWID() + ) + } catch { + // handle the error + } ``` ## Actualizar la implementación del modo Observer \{#update-observer-mode-implementation\} Actualiza cómo vinculas los paywalls a las transacciones. Anteriormente, usabas el método `setVariationId` para asignar el `variationId`. Ahora puedes incluir el `variationId` directamente al registrar la transacción usando el nuevo método `reportTransaction`. Consulta el ejemplo de código final en [Asociar paywalls con transacciones de compra en el modo Observer](report-transactions-observer-mode). :::warning Recuerda registrar la transacción usando el método `reportTransaction`. Si omites este paso, Adapty no reconocerá la transacción, no otorgará niveles de acceso, no la incluirá en los análisis ni la enviará a las integraciones. ¡Este paso es esencial! ::: ```diff showLineNumbers - let variationId = paywall.variationId - - // There are two overloads: for StoreKit 1 and StoreKit 2 - Adapty.setVariationId(variationId, forPurchasedTransaction: transaction) { error in - if error == nil { - // successful binding - } - } + do { + // every time when calling transaction.finish() + try await Adapty.reportTransaction(transaction, withVariationId: <YOUR_PAYWALL_VARIATION_ID>) + } catch { + // handle the error + } ``` --- # File: migration-to-ios-sdk-v3 --- --- title: "Migrar el SDK de iOS de Adapty a la v. 3.0" description: "Migra al SDK de iOS de Adapty v3.0 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK de Adapty v.3.0 incorpora compatibilidad con el nuevo y emocionante [Adapty Paywall Builder](adapty-paywall-builder), la nueva versión de la herramienta no-code y fácil de usar para crear paywalls. Con su máxima flexibilidad y amplias capacidades de diseño, tus paywalls serán más efectivos y rentables. :::info Ten en cuenta que la librería AdaptyUI está obsoleta y ahora se incluye como parte del AdaptySDK. ::: ## Reinstalar el SDK de Adapty v3.x mediante Swift Package Manager \{#reinstall-adapty-sdk-v3x-via-swift-package-manager\} 1. Elimina la dependencia del paquete AdaptyUI SDK de tu proyecto, ya no la necesitarás. 2. Aunque ya lo tengas, deberás volver a añadir la dependencia del SDK de Adapty. Para ello, en Xcode, abre **File** -> **Add Package Dependency...**. Ten en cuenta que la forma de añadir dependencias de paquetes puede variar según la versión de Xcode. Consulta la documentación de Xcode si es necesario. 3. Introduce la URL del repositorio `https://github.com/adaptyteam/AdaptySDK-iOS.git` 4. Elige la versión y haz clic en el botón **Add package**. 5. Elige los módulos que necesitas: 1. **Adapty** es el módulo obligatorio. 2. **AdaptyUI** es un módulo opcional que necesitas si piensas usar el [Adapty Paywall Builder](adapty-paywall-builder). 6. Xcode añadirá la dependencia del paquete a tu proyecto y podrás importarla. Para ello, en la ventana **Choose Package Products**, haz clic de nuevo en el botón **Add package**. El paquete aparecerá en la lista **Packages**. ## Reinstalar el SDK de Adapty v3.x mediante CocoaPods \{#reinstall-adapty-sdk-v3x-via-cocoapods\} 1. Añade Adapty a tu `Podfile`. Elige los módulos que necesitas: 1. **Adapty** es el módulo obligatorio. 2. **AdaptyUI** es un módulo opcional que necesitas si piensas usar el [Adapty Paywall Builder](adapty-paywall-builder). 2. ```shell showLineNumbers title="Podfile" pod 'Adapty', '~> 3.2.0' pod 'AdaptyUI', '~> 3.2.0' # optional module needed only for Paywall Builder ``` 3. Ejecuta: ```sh showLineNumbers title="Shell" pod install ``` Esto crea un archivo `.xcworkspace` para tu app. Usa este archivo para todo el desarrollo futuro de tu aplicación. Activa los módulos del SDK de Adapty y AdaptyUI. Antes de la v3.0 no se activaba AdaptyUI, así que recuerda **añadir la activación de AdaptyUI**. Los parámetros no cambian, así que déjalos tal como están. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers // In your AppDelegate class: let configurationBuilder = AdaptyConfiguration .Builder(withAPIKey: "PUBLIC_SDK_KEY") .with(observerMode: false) .with(customerUserId: "YOUR_USER_ID") .with(idfaCollectionDisabled: false) .with(ipAddressCollectionDisabled: false) Adapty.activate(with: configurationBuilder) { error in // handle the error } // Only if you are going to use AdaptyUI AdaptyUI.activate() ``` </TabItem> <TabItem value="swiftui" label="SwiftUI" default> ```swift title="" showLineNumbers @main struct SampleApp: App { init() let configurationBuilder = AdaptyConfiguration .Builder(withAPIKey: "PUBLIC_SDK_KEY") .with(observerMode: false) // optional .with(customerUserId: "YOUR_USER_ID") // optional .with(idfaCollectionDisabled: false) // optional .with(ipAddressCollectionDisabled: false) // optional Adapty.activate(with: configurationBuilder) { error in // handle the error } // Only if you are going to use AdaptyUI AdaptyUI.activate() } var body: some Scene { WindowGroup { ContentView() } } } ``` </TabItem> </Tabs> --- # End of Documentation _Generated on: 2026-05-15T20:13:57.556Z_ _Successfully processed: 41/41 files_ # KMP - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.558Z Total files: 44 --- # File: kmp-sdk-overview --- --- title: "Kotlin Multiplatform SDK overview" description: "Learn about Adapty Kotlin Multiplatform SDK and its key features." --- [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-KMP.svg?style=flat&logo=kotlin)](https://github.com/adaptyteam/AdaptySDK-KMP/releases) Welcome! We're here to make in-app purchases a breeze 🚀 We've built the Adapty Kotlin Multiplatform SDK to take the headache out of in-app purchases so you can focus on what you do best – building amazing apps. Here's what we handle for you: - Handle purchases, receipt validation, and subscription management out of the box - Create and test paywalls without app updates - Get detailed purchase analytics with zero setup - cohorts, LTV, churn, and funnel analysis included - Keep the user subscription status always up to date across app sessions and devices - Integrate your app with marketing attribution and analytics services using just one line of code :::note Before diving into the code, you'll need to integrate Adapty with Google Play Console and set up products in the dashboard. Check out our [quickstart guide](quickstart) to get everything configured first. ::: ## Get started :::tip Our docs are optimized for use with LLMs. Check out [this article](adapty-cursor-kmp) to learn how to get the best results when integrating the Adapty SDK using AI with our docs. ::: Here's what we'll cover in the integration guide: 1. [Install & configure SDK](sdk-installation-kotlin-multiplatform): Add the SDK as a dependency to your project and activate it in the code. 2. [Enable purchases through paywalls](kmp-quickstart-paywalls): Set up the purchase flow so users can buy products. 3. [Check the subscription status](kmp-check-subscription-status): Automatically check the user's subscription state and control their access to paid content. 4. [Identify users (optional)](kmp-quickstart-identify): Associate users with their Adapty profiles to ensure their data is stored consistently across devices. ### See it in action Want to see how it all comes together? We've got you covered: - **Sample app**: Check out our [complete example](https://github.com/adaptyteam/AdaptySDK-KMP/tree/main/example) that demonstrates the full setup - **Video tutorial**: Follow along with our step-by-step implementation video below <iframe width="560" height="315" src="https://www.youtube.com/embed/JfwJvwnloNw?si=HskPxRk4WGkF_u9s" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> ## Main concepts Before diving into the code, let's get familiar with the key concepts that make Adapty work. The beauty of Adapty's approach is that only placements are hardcoded in your app. Everything else – products, paywall designs, pricing, and offers – can be managed flexibly from the Adapty dashboard without app updates: 1. [**Product**](product) - Anything available for purchase in your app – subscription, consumable product, or lifetime access. 2. [**Paywall**](paywalls) - The only way to retrieve products from Adapty and use it to its full power. We've designed it this way to make it easier to track how different product combinations affect your monetization metrics. A paywall in Adapty serves as both a specific set of your products and the visual configuration that accompanies them. 3. [**Placement**](placements) - A strategic point in your user journey where you want to show a paywall. Think of placements as the "where" and "when" of your monetization strategy. Common placements include: - `main` - Your primary paywall location - `onboarding` - Shown during the user onboarding flow - `settings` - Accessible from your app's settings Start with the basics like `main` or `onboarding` for your first integration, then [think about where else in your app users might be ready to purchase](choose-meaningful-placements). 4. [**Profile**](profiles-crm) - When users purchase a product, their profile is assigned an **access level** which you use to define access to paid features. --- # File: sdk-installation-kotlin-multiplatform --- --- title: "Instalar y configurar el SDK de Adapty para Kotlin Multiplatform" description: "Instala y configura el SDK de Adapty para apps de Kotlin Multiplatform." --- El SDK de Adapty incluye dos módulos clave para una integración fluida en tu app móvil: - **Core Adapty**: Este SDK esencial es necesario para que Adapty funcione correctamente en tu aplicación. - **AdaptyUI** (`io.adapty:adapty-kmp-ui`): Este módulo es necesario si usas el [Adapty Paywall Builder](adapty-paywall-builder) con la capa de renderizado Compose Multiplatform (`view.present()`). Si tu proyecto no usa Compose Multiplatform, puedes usar [`createNativePaywallView`](kmp-present-paywalls#without-compose-multiplatform) y [`createNativeOnboardingView`](kmp-present-onboardings#without-compose-multiplatform) del módulo principal en su lugar. :::tip ¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una aplicación móvil? Echa un vistazo a nuestra [aplicación de ejemplo](https://github.com/adaptyteam/AdaptySDK-KMP/tree/main/example), que muestra la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funciones básicas. ::: Para una guía completa de implementación, también puedes ver el vídeo: <iframe width="560" height="315" src="https://www.youtube.com/embed/JfwJvwnloNw?si=HskPxRk4WGkF_u9s" title="Reproductor de vídeo de YouTube" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> ## Requisitos \{#requirements\} El SDK de Adapty para Kotlin Multiplatform es compatible con Xcode 16.2 y versiones posteriores. :::info Adapty es compatible con Google Play Billing Library hasta la versión 8.x. Por defecto, Adapty funciona con Google Play Billing Library v.7.0.0, pero si quieres forzar una versión posterior, puedes [añadir la dependencia](https://developer.android.com/google/play/billing/integrate#dependency) manualmente. ::: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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. </Callout> ## Instalar el SDK de Adapty mediante Gradle \{#install-adapty-sdk-via-gradle\} La instalación del SDK de Adapty con Gradle es necesaria tanto para apps Android como iOS. Elige el método de configuración de dependencias: - Gradle estándar: añade las dependencias a tu `build.gradle` **a nivel de módulo** - Si tu proyecto usa archivos `.gradle.kts`, añade las dependencias a tu `build.gradle.kts` **a nivel de módulo** - Si usas catálogos de versiones, añade las dependencias a tu archivo `libs.versions.toml` y luego referencíalas en `build.gradle.kts` <Tabs> <TabItem value="module-level build.gradle" label="module-level build.gradle" default> ```kotlin showLineNumbers kotlin { sourceSets { commonMain { dependencies { implementation libs.adapty.kmp } } } } ``` </TabItem> <TabItem value="module-level build.gradle.kts" label="module-level build.gradle.kts" default> ```kotlin showLineNumbers kotlin { sourceSets { val commonMain by getting { dependencies { implementation(libs.adapty.kmp) } } } } ``` </TabItem> <TabItem value="version-catalog" label="Biblioteca de versiones" default> ```toml showLineNumbers // libs.versions.toml [versions] .. adapty-kmp = "<the latest SDK version>" [libraries] .. adapty-kmp = { module = "io.adapty:adapty-kmp", version.ref = "adapty-kmp" } // build.gradle.kts kotlin { sourceSets { val commonMain by getting { dependencies { implementation(libs.adapty.kmp) } } } } ``` </TabItem> </Tabs> :::note Si obtienes un error relacionado con Maven, asegúrate de tener `mavenCentral()` en tus scripts de Gradle. <details> <summary>Instrucciones para añadirlo</summary> Si tu proyecto no tiene `dependencyResolutionManagement` en tu `settings.gradle`, añade lo siguiente a tu `build.gradle` de nivel superior al final de repositories: ```groovy showLineNumbers title="top-level build.gradle" allprojects { repositories { ... mavenCentral() } } ``` De lo contrario, añade lo siguiente a tu `settings.gradle` en `repositories` de la sección `dependencyResolutionManagement`: ```groovy showLineNumbers title="settings.gradle" dependencyResolutionManagement { ... repositories { ... google() mavenCentral() } } ``` </details> ::: ## Activar el SDK de Adapty \{#activate-adapty-sdk\} ### Configuración básica \{#basic-setup\} Añade la inicialización lo antes posible, normalmente en tu código Kotlin compartido para ambas plataformas. :::note El SDK de Adapty solo necesita activarse una vez en tu app. ::: ```kotlin title="Kotlin" showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .build() Adapty.activate(configuration = config) .onSuccess { Log.d("Adapty", "SDK initialised") } .onError { error -> Log.e("Adapty", "Adapty init error: ${error.message}") } ``` :::important Espera a que `activate` termine antes de llamar a cualquier otro método del SDK. Consulta [Orden de llamadas en el SDK de Kotlin Multiplatform](kmp-sdk-call-order) para ver la secuencia completa. ::: 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. :::info - Asegúrate de usar la clave SDK pública para inicializar Adapty; la clave secreta solo debe usarse para la [API del lado del servidor](getting-started-with-server-side-api). - Las claves SDK son únicas para cada aplicación, así que si tienes varias apps asegúrate de elegir la correcta. ::: Ahora configura los paywalls en tu app: - Si usas [Adapty Paywall Builder](adapty-paywall-builder), primero [activa el módulo AdaptyUI](#activate-adaptyui-module-of-adapty-sdk) a continuación y luego sigue la [guía de inicio rápido del Paywall Builder](kmp-quickstart-paywalls). - Si construyes tu propia interfaz de paywall, consulta la [guía de inicio rápido para paywalls personalizados](kmp-quickstart-manual). ## Activar el módulo AdaptyUI del SDK \{#activate-adaptyui-module-of-adapty-sdk\} Si tienes previsto activar el módulo **AdaptyUI** para usar el [Adapty Paywall Builder](kmp-present-paywalls), asegúrate de establecer `.withActivateUI(true)` en tu configuración. :::info importante En tu código, debes activar el módulo principal de Adapty antes de activar AdaptyUI. ::: ```kotlin title="Kotlin" showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withActivateUI(true) // true for activating the AdaptyUI module .build() Adapty.activate(configuration = config) .onSuccess { Log.d("Adapty", "SDK initialised") } .onError { error -> Log.e("Adapty", "Adapty init error: ${error.message}") } ``` ## Configurar Proguard (Android) \{#configure-proguard-android\} Antes de lanzar tu app en producción, es posible que necesites añadir `-keep class com.adapty.** { *; }` a tu configuración de Proguard. ## 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. Los niveles disponibles son los siguientes: | Nivel | Descripción | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------ | | `AdaptyLogLevel.NONE` | No se registrará nada. Valor predeterminado | | `AdaptyLogLevel.ERROR` | Solo se registrarán los errores | | `AdaptyLogLevel.WARN` | Se registrarán los errores y los mensajes del SDK que no causan errores críticos pero que merecen atención. | | `AdaptyLogLevel.INFO` | Se registrarán errores, advertencias y varios mensajes informativos. | | `AdaptyLogLevel.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 aplicación antes de configurar Adapty: ```kotlin title="Kotlin" showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withLogLevel(AdaptyLogLevel.VERBOSE) // recommended for development .build() ``` ### Políticas de datos \{#data-policies\} #### Desactivar la recopilación y el uso compartido de la dirección 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 las normativas regionales de protección de datos (como GDPR o CCPA) o reducir la recopilación de datos innecesarios cuando las funciones basadas en IP no son necesarias para tu app. ```kotlin title="Kotlin" showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withIpAddressCollectionDisabled(true) .build() ``` #### Deshabilitar la recopilación y el uso compartido del ID de publicidad \{#disable-advertising-id-collection-and-sharing\} Al activar el módulo de Adapty, establece `appleIdfaCollectionDisabled` (iOS) o `googleAdvertisingIdCollectionDisabled` (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 análisis basados en IDs publicitarios. ```kotlin title="Kotlin" showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withGoogleAdvertisingIdCollectionDisabled(true) // Android only .withAppleIdfaCollectionDisabled(true) // iOS only .build() ``` #### Configurar la caché de medios para AdaptyUI \{#set-up-media-cache-configuration-for-adaptyui\} Por defecto, AdaptyUI almacena en caché los archivos multimedia (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 sobreescribir la configuración de caché predeterminada: ```kotlin val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withMediaCacheConfiguration( AdaptyConfig.MediaCacheConfiguration( memoryStorageTotalCostLimit = 200 * 1024 * 1024, // 200 MB memoryStorageCountLimit = Int.MAX_VALUE, diskStorageSizeLimit = 200 * 1024 * 1024 // 200 MB ) ) .build() ``` ### Habilitar niveles de acceso locales (Android) \{#enable-local-access-levels-android\} Por defecto, los [niveles de acceso locales](local-access-levels) están desactivados para Android. Para habilitarlos, establece `withLocalAccessLevelAllowed` en `true`: ```kotlin title="Kotlin" showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withGoogleLocalAccessLevelAllowed(true) .build() ``` ### Borrar datos al restaurar desde copia de seguridad \{#clear-data-on-backup-restore\} Cuando `withAppleClearDataOnBackup` está configurado 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. Después, 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 no se ven afectados. ::: ```swift showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withAppleClearDataOnBackup(true) .build() ``` ## Solución de problemas \{#troubleshooting\} #### Reglas de copia de seguridad de Android (configuración de Auto Backup) \{#android-backup-rules-auto-backup-configuration\} 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 `<manifest>` incluya tools: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.app"> ... </manifest> ``` #### 2. Sobreescribe los atributos de copia de seguridad en `<application>` \{#2-override-backup-attributes-in-application\} En el mismo archivo `AndroidManifest.xml`, actualiza la etiqueta `<application>` para que tu app proporcione los valores definitivos e indique al fusionador de manifiestos que reemplace los valores de las librerías: ```xml <application android:name=".App" android:allowBackup="true" android:fullBackupContent="@xml/sample_backup_rules" android:dataExtractionRules="@xml/sample_data_extraction_rules" tools:replace="android:fullBackupContent,android:dataExtractionRules"> ... </application> ``` 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" <?xml version="1.0" encoding="utf-8"?> <data-extraction-rules> <cloud-backup> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </cloud-backup> <device-transfer> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </device-transfer> </data-extraction-rules> ``` **Para Android 11 e inferior** (usa el formato legado de contenido de copia de seguridad completa): ```xml title="sample_backup_rules.xml" <?xml version="1.0" encoding="utf-8"?> <full-backup-content> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> :::important En un proyecto Kotlin Multiplatform, aplica estos cambios en el módulo de la aplicación Android (el que genera el APK/AAB), por ejemplo, `androidApp` o `app`: - Manifiesto: `androidApp/src/main/AndroidManifest.xml` - XML de reglas de copia de seguridad: `androidApp/src/main/res/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 flujo de compra usa un `launchMode` no predeterminado, Android puede recrearla o reutilizarla incorrectamente cuando el usuario regresa desde Google Play, una aplicación 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, usa únicamente los modos de lanzamiento `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 <activity android:name=".MainActivity" android:launchMode="standard" /> ``` --- # File: kmp-quickstart-paywalls --- --- title: "Habilitar compras usando paywalls en el SDK de Kotlin Multiplatform" description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app." --- Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios puedan comprar (suscripciones, consumibles, acceso de por vida) - [**Paywalls**](paywalls) son configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar ofertas, precios y combinaciones de productos sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 distintos paywalls a distintos usuarios. Adapty te ofrece tres formas de habilitar compras en tu app. Elige la que mejor se adapte a los requisitos de tu aplicación: | Implementación | Complejidad | Cuándo usarla | |---------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Paywall Builder | ✅ Fácil | [Creas un paywall completo y listo para compras en el editor 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 sigues obteniendo el objeto paywall de Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](kmp-quickstart-manual). | | Modo observer | 🔴 Difícil | Ya tienes tu propia infraestructura de gestión de compras y quieres seguir usándola. Ten en cuenta que el modo observer tiene sus limitaciones en Adapty. Consulta el [artículo](observer-vs-full-mode). | :::important **Los pasos que se muestran a continuación explican cómo implementar un paywall creado en el Adapty Paywall Builder.** Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](kmp-making-purchases). ::: Para mostrar un paywall creado en el Adapty Paywall Builder, en el código de tu app solo necesitas: 1. **Obtener el paywall**: Obtén el paywall desde Adapty. 2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app. 3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con el paywall con la respuesta de tu app a ellas. Por ejemplo, abrir enlaces o cerrar el paywall cuando los usuarios pulsan botones. ## Antes de empezar \{#before-you-start\} Antes de comenzar, 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 a él](create-paywall). 4. [Crea un placement y añade tu paywall a él](create-placement). 5. [Instala y activa el SDK de Adapty](sdk-installation-kotlin-multiplatform) 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 la [CLI para desarrolladores](developer-cli-quickstart). ::: ## 1. Obtener el paywall \{#1-get-the-paywall\} Tus paywalls están asociados con placements configurados en el dashboard. Los placements te permiten ejecutar diferentes paywalls para distintas audiencias o realizar [pruebas A/B](ab-tests). Para obtener un paywall creado en el Adapty Paywall Builder, necesitas: 1. Obtener el objeto `paywall` por el ID del [placement](placements) usando el método `getPaywall` y comprobar si es un paywall creado en el builder. 2. Obtener la configuración de vista del paywall usando el método `createPaywallView`. La configuración de vista contiene los elementos de UI y el estilo necesarios para mostrar el paywall. :::important Para obtener la configuración de vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará. ::: ```kotlin showLineNumbers Adapty.getPaywall("YOUR_PLACEMENT_ID") .onSuccess { paywall -> if (!paywall.hasViewConfiguration) { return@onSuccess } val paywallView = AdaptyUI.createPaywallView(paywall = paywall) paywallView?.present() } .onError { error -> // handle the error } ``` ## 2. Mostrar el paywall \{#2-display-the-paywall\} Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo. Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, llama al método `AdaptyUI.createPaywallView()`: ```kotlin showLineNumbers val paywallView = AdaptyUI.createPaywallView(paywall = paywall) paywallView?.present() ``` Una vez que la vista se haya creado correctamente, puedes presentarla en la pantalla del dispositivo. :::tip Para más detalles sobre cómo mostrar un paywall, consulta nuestra [guía](kmp-present-paywalls). ::: ## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\} Cuando los usuarios pulsan botones en el paywall, el SDK de Kotlin Multiplatform gestiona automáticamente las compras, la restauración, el cierre del paywall y la apertura de enlaces. Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren gestionar las acciones en tu código. O puede que quieras sobrescribir su comportamiento predeterminado. Por ejemplo, aquí está el comportamiento predeterminado del botón de cierre. No necesitas añadirlo en el código, pero aquí puedes ver cómo se hace si fuera necesario. :::tip Lee nuestras guías sobre cómo gestionar [acciones](kmp-handle-paywall-actions) y [eventos](kmp-handling-events) de los botones. ::: ```kotlin showLineNumbers AdaptyUI.setPaywallsEventsObserver(object : AdaptyUIPaywallsEventsObserver { override fun paywallViewDidPerformAction(view: AdaptyUIPaywallView, action: AdaptyUIAction) { when (action) { AdaptyUIAction.CloseAction, AdaptyUIAction.AndroidSystemBackAction -> view.dismiss() } } }) ``` ## Próximos pasos \{#next-steps\} 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](kmp-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í puedes ver cómo integrar todos estos pasos en tu app. ```kotlin showLineNumbers // Set up the observer for handling paywall actions AdaptyUI.setPaywallsEventsObserver(object : AdaptyUIPaywallsEventsObserver { override fun paywallViewDidPerformAction(view: AdaptyUIPaywallView, action: AdaptyUIAction) { when (action) { is AdaptyUIAction.CloseAction -> view.dismiss() } } }) // Get and display the paywall Adapty.getPaywall("YOUR_PLACEMENT_ID") .onSuccess { paywall -> if (!paywall.hasViewConfiguration) { // Use custom logic return@onSuccess } val paywallView = AdaptyUI.createPaywallView(paywall = paywall) paywallView?.present() } .onError { error -> // handle the error } ``` --- # File: kmp-check-subscription-status --- --- title: "Comprobar el estado de la suscripción en el SDK de Kotlin Multiplatform" description: "Aprende a comprobar el estado de la suscripción en tu app de Kotlin Multiplatform con Adapty." --- Para decidir si los usuarios pueden acceder a contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil. Este artículo muestra cómo acceder al estado del perfil para decidir qué deben ver los usuarios: si mostrarles un paywall o darles acceso a las funciones de pago. ## Obtener el estado de la suscripción \{#get-subscription-status\} Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones: - Llama a `getProfile` si necesitas los datos más recientes del perfil de inmediato (como al iniciar la app) o quieres forzar una actualización. - Configura **actualizaciones automáticas del perfil** para mantener una copia local que se refresca automáticamente cada vez que cambia el estado de la suscripción. ### Obtener el perfil \{#get-profile\} La forma más sencilla de obtener el estado de la suscripción es usar el método `getProfile` para acceder al perfil: ```kotlin showLineNumbers Adapty.getProfile() .onSuccess { profile -> // check the access } .onError { error -> // handle the error } ``` ### Escuchar actualizaciones de la suscripción \{#listen-to-subscription-updates\} Para recibir actualizaciones del perfil automáticamente en tu app: 1. Usa `Adapty.setOnProfileUpdatedListener()` para escuchar cambios en el perfil: Adapty llamará a este método automáticamente cada vez que cambie el estado de la suscripción del usuario. 2. Almacena los datos del perfil actualizado cuando se llame a este método, para poder usarlos en toda tu app sin hacer peticiones de red adicionales. ```kotlin showLineNumbers class SubscriptionManager { private var currentProfile: AdaptyProfile? = null init { // Listen for profile updates Adapty.setOnProfileUpdatedListener { profile -> currentProfile = profile // Update UI, unlock content, etc. } } // Use stored profile instead of calling getProfile() fun hasAccess(): Boolean { return currentProfile?.accessLevels?.get("YOUR_ACCESS_LEVEL")?.isActive == true } } ``` :::note Adapty llama automáticamente al listener de actualización del perfil cuando se inicia tu app, proporcionando datos de suscripción en caché incluso si el dispositivo está sin conexión. ::: ## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\} Cuando necesitas tomar decisiones inmediatas sobre si mostrar paywalls o dar acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en escenarios como el inicio de la app, al entrar en secciones premium o antes de mostrar contenido específico. ```kotlin showLineNumbers private fun checkAccessAndShowPaywall() { // First, check if user has access Adapty.getProfile() .onSuccess { profile -> val hasAccess = profile.accessLevels?.get("YOUR_ACCESS_LEVEL")?.isActive == true if (!hasAccess) { // User doesn't have access, show paywall showPaywall() } else { // User has access, show premium content showPremiumContent() } } .onError { error -> // If we can't check access, show paywall as fallback showPaywall() } } private fun showPaywall() { // Get and display paywall using the KMP SDK Adapty.getPaywall("YOUR_PLACEMENT_ID") .onSuccess { paywall -> if (paywall.hasViewConfiguration) { val paywallView = AdaptyUI.createPaywallView(paywall = paywall) paywallView?.present() } else { // Handle remote config paywall or show custom UI handleRemoteConfigPaywall(paywall) } } .onError { error -> // Handle paywall loading error showError("Unable to load paywall") } } private fun showPremiumContent() { // Show your premium content here // This is where you unlock paid features } ``` ## Próximos pasos \{#next-steps\} Ahora que sabes cómo hacer seguimiento del estado de la suscripción, aprende a [trabajar con perfiles de usuario](kmp-quickstart-identify) para asegurarte de que pueden acceder a lo que han pagado. --- # File: kmp-quickstart-identify --- --- title: "Identificar usuarios en el SDK de Kotlin Multiplatform" description: "Guía de inicio rápido para configurar Adapty para la gestión de suscripciones in-app en KMP." --- :::important Esta guía es para ti si tienes tu propio sistema de autenticación. Aquí aprenderás a trabajar con perfiles de usuario en Adapty para que se alineen con tu sistema de autenticación existente. ::: La forma en que gestionas las compras de los usuarios depende del modelo de autenticación de tu app: - Si tu app no usa autenticación de backend ni almacena datos de usuario, consulta la [sección sobre usuarios anónimos](#anonymous-users). - Si tu app tiene (o tendrá) autenticación de backend, consulta la [sección sobre usuarios identificados](#identified-users). **Conceptos clave**: - Los **perfiles** son las entidades necesarias para que el SDK funcione. Adapty los crea automáticamente. - Pueden ser anónimos **(sin customer user ID)** o identificados **(con customer user ID)**. - Proporcionas el **customer user ID** para cruzar los perfiles de Adapty con tu sistema de autenticación interno. Esto es lo que difiere entre usuarios anónimos e identificados: | | Usuarios anónimos | Usuarios identificados | |-------------------------|-------------------------------------------------------|-------------------------------------------------------------------------------------| | **Gestión de compras** | Restauración de compras a nivel de store | Mantienen el historial de compras entre dispositivos mediante su customer user ID | | **Gestión de perfiles** | Nuevos perfiles en cada reinstalación | El mismo perfil entre sesiones y dispositivos | | **Persistencia de datos** | Los datos de usuarios anónimos están ligados a la instalación de la app | Los datos de usuarios identificados persisten entre instalaciones | ## Usuarios anónimos \{#anonymous-users\} Si no tienes autenticación de backend, **no necesitas gestionar la autenticación en el código de la app**: 1. Cuando el SDK se activa en el primer inicio de la app, Adapty **crea un nuevo perfil para el usuario**. 2. Cuando el usuario compra algo en la app, esa compra se **asocia a su perfil de Adapty y a su cuenta en el store**. 3. Cuando el usuario **reinstala** la app o la instala desde un **nuevo dispositivo**, Adapty **crea un nuevo perfil anónimo al activarse**. 4. Si el usuario ya había realizado compras en tu app, por defecto sus compras se sincronizan automáticamente desde el App Store al activar el SDK. Con usuarios anónimos, se crearán nuevos perfiles en cada instalación, pero eso no es un problema porque en los análisis de Adapty puedes [configurar qué se considerará una nueva instalación](general#4-installs-definition-for-analytics). Para los usuarios anónimos, debes contar las instalaciones por **ID de dispositivo**. En este caso, cada instalación de la app en un dispositivo se cuenta como una instalación, incluidas las reinstalaciones. :::note Las restauraciones de copia de seguridad se comportan de forma diferente a las reinstalaciones. Por defecto, cuando un usuario restaura desde una copia de seguridad, el SDK conserva los datos en caché y no crea un nuevo perfil. Puedes configurar este comportamiento con el parámetro `withAppleClearDataOnBackup`. [Más información](sdk-installation-kotlin-multiplatform#clear-data-on-backup-restore). ::: ## Usuarios identificados \{#identified-users\} Tienes dos opciones para identificar usuarios en la app: - [**Durante el login/registro:**](#during-loginsignup) Si los usuarios inician sesión después de que tu app arranque, llama a `identify()` con un customer user ID cuando se autentiquen. - [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando la app se inicia, envíalo al llamar a `activate()`. :::important Por defecto, cuando Adapty recibe una compra de un Customer User ID que ya está asociado a otro Customer User ID, el nivel de acceso se comparte, por lo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o deshabilitar el uso compartido completamente. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles. ::: <img src="/assets/shared/img/identify-diagram.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Durante el login/registro \{#during-loginsignup\} Si identificas a los usuarios después de que arranque la app (por ejemplo, tras iniciar sesión o registrarse), usa el método `identify` para establecer su customer user ID. - Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual. - Si **ya has usado este customer user ID para identificar al usuario antes**, Adapty cambiará a trabajar con el perfil asociado a ese customer user ID. :::important Los customer user IDs deben ser únicos para cada usuario. Si hardcodeas el valor del parámetro, todos los usuarios se considerarán como uno solo. ::: Espera a que `identify` se complete (en su callback `onSuccess`) antes de llamar a otros métodos del SDK. Las llamadas concurrentes pueden acabar en el perfil anónimo. Consulta [Orden de llamadas en el SDK de Kotlin Multiplatform](kmp-sdk-call-order). ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID") // Único para cada usuario .onSuccess { // identify exitoso } .onError { error -> // gestionar el error } ``` ### Durante la activación del SDK \{#during-the-sdk-activation\} Si ya conoces el customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado. Si conoces un customer user ID pero solo lo estableces después de la activación, eso significa que, al activarse, Adapty creará un nuevo perfil anónimo y cambiará al existente solo cuando llames a `identify`. Puedes pasar un customer user ID existente (uno que hayas usado antes) o uno nuevo. Si pasas uno nuevo, el nuevo perfil creado en la activación se vinculará automáticamente al customer user ID. :::note Por defecto, la creación de perfiles anónimos no afecta a los dashboards de análisis, porque las instalaciones se cuentan en función de los ID de dispositivo. Un ID de dispositivo representa una única instalación de la app desde el store en un dispositivo y solo se regenera tras reinstalar la app. No depende de si es una primera instalación o una repetida, ni de si se usa un customer user ID existente. Crear un perfil (al activar el SDK o al cerrar sesión), iniciar sesión o actualizar la app sin reinstalarla no genera eventos de instalación adicionales. Si quieres contar las instalaciones en función de usuarios únicos en lugar de dispositivos, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics). ::: ```kotlin showLineNumbers AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId("user123") // Los customer user IDs deben ser únicos para cada usuario. Si hardcodeas el valor del parámetro, todos los usuarios se considerarán como uno solo. .build() ``` ### Cerrar sesión de usuarios \{#log-users-out\} Si tienes un botón para cerrar la sesión de los usuarios, usa el método `logout`. :::important Cerrar la sesión de los usuarios crea un nuevo perfil anónimo para el usuario. ::: ```kotlin showLineNumbers Adapty.logout() .onSuccess { // cierre de sesión exitoso } .onError { error -> // gestionar el error } ``` :::info Para volver a iniciar sesión en la app, usa el método `identify`. ::: ### Permitir compras sin inicio de sesión \{#allow-purchases-without-login\} Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, debes asegurarte de que mantendrán el acceso una vez que inicien sesión: 1. Cuando un usuario sin sesión iniciada realiza una compra, Adapty la vincula a su ID de perfil anónimo. 2. Cuando el usuario inicia sesión en su cuenta, Adapty pasa a trabajar con su perfil identificado. - Si es un nuevo customer user ID (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que todo el historial de compras se mantiene. - Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), debes obtener el nivel de acceso real tras el cambio de perfil. Puedes llamar a [`getProfile`](kmp-check-subscription-status) justo después de la identificación, o [escuchar las actualizaciones del perfil](kmp-check-subscription-status) para que los datos se sincronicen automáticamente. ## Próximos pasos \{#next-steps\} ¡Enhorabuena! Has implementado la lógica de pago in-app en tu app. ¡Te deseamos lo mejor con la monetización de tu app! Para sacar aún más partido a Adapty, puedes explorar estos temas: - [**Pruebas**](troubleshooting-test-purchases): Asegúrate de que todo funciona correctamente - [**Integraciones**](configuration): Integra con servicios de atribución de marketing y análisis en una sola línea de código - [**Establecer atributos de perfil personalizados**](kmp-setting-user-attributes): Añade atributos personalizados a los perfiles de usuario y crea segmentos para lanzar pruebas A/B o mostrar diferentes paywalls a distintos usuarios --- # File: adapty-cursor-kmp --- --- title: "Integra Adapty en tu app de Kotlin Multiplatform con ayuda de IA" description: "Una guía paso a paso para integrar Adapty en tu app de Kotlin Multiplatform usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA." --- Esta página cubre dos formas de integrar Adapty en tu app de Kotlin Multiplatform. Usa la skill de integración del SDK que se describe a continuación para un flujo automatizado de principio a fin, o sigue el recorrido manual más adelante. ## Usa la skill de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\} La [skill adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza la integración de extremo a extremo: configuración del dashboard, instalación del SDK, paywall y verificación por etapas. El recorrido manual más adelante es la alternativa si tu herramienta no admite el formato Claude Skills. **Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI. ### Instalación \{#install\} Elige el formato para tu herramienta. La lista completa está en el [README de la skill](https://github.com/adaptyteam/adapty-sdk-integration-skill). - **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y luego `claude plugin install adapty-sdk-integration@adapty` desde tu terminal. - **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`. - **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`. - **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de skills de tu herramienta. ### Ejecución \{#run\} En tu proyecto, ejecuta `/adapty-sdk-integration`. La skill detecta tu plataforma y hace algunas preguntas de configuración. Luego recorre la configuración del dashboard, la instalación del SDK, el paywall y la verificación, consultando la documentación de Adapty relevante en cada etapa. :::note La skill está en beta. Si se detiene o se comporta de forma inesperada, el recorrido manual que aparece más abajo cubre cada etapa paso a paso. ::: ## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\} Adapty requiere cierta configuración en el dashboard antes de escribir código con el SDK. Puedes hacerlo con una skill de LLM interactiva o manualmente desde el Dashboard. ### Con la skill (recomendado) \{#skill-approach-recommended\} La skill de la CLI de Adapty permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin necesidad de abrir el Dashboard en cada paso. Solo necesitas [conectar tus stores](integrate-payments) en el Dashboard. ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` Una vez añadida la skill, ejecuta `/adapty-cli` en tu agente. Te guiará por cada paso, incluyendo cuándo abrir el Dashboard para conectar tus stores. ### Con el Dashboard \{#dashboard-approach\} Si prefieres configurarlo todo manualmente, esto es lo que necesitas antes de escribir código. Tu LLM no puede consultar los valores del dashboard por ti — tendrás que proporcionárselos. 1. **Conecta tus stores**: En el Adapty Dashboard, ve a **App settings → General**. Conecta tanto App Store como Google Play si tu app KMP tiene como objetivo ambas plataformas. Esto es necesario para que las compras funcionen. [Conectar stores](integrate-payments) 2. **Copia tu clave pública del SDK**: En el Adapty Dashboard, ve a **App settings → General** y busca la sección **API keys**. En el código, esta es la cadena que pasas al constructor de configuración de Adapty. 3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No haces referencia a los productos directamente en el código — Adapty los entrega a través de los paywalls. [Añadir productos](quickstart-products) 4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `Adapty.getPaywall("YOUR_PLACEMENT_ID")`. [Crear paywall](quickstart-paywalls) 5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena se comprueba en `profile.accessLevels["premium"]?.isActive`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago tienen acceso a funciones diferentes según el producto (por ejemplo, un plan `basic` frente a un plan `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar. :::tip Una vez que tengas los cinco, estás listo para escribir código. Dile a tu LLM: "Mi clave pública del SDK es X, mi ID de placement es Y" para que pueda generar código correcto de inicialización y obtención del paywall. ::: ### Configura cuando estés listo \{#set-up-when-ready\} Esto no es necesario para empezar a programar, pero lo necesitarás a medida que tu integración madure: - **Pruebas A/B**: Configúralas en la página **Placements**. No se necesita ningún cambio de código. [Pruebas A/B](ab-tests) - **Paywalls y placements adicionales**: Añade más llamadas a `getPaywall` con diferentes IDs de placement. - **Integraciones de analíticas**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta [integraciones de analíticas](analytics-integration) e [integraciones de atribución](attribution-integration). ## Dale documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\} ### Usa Context7 (recomendado) \{#use-context7-recommended\} [Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. Tu LLM obtiene los documentos correctos automáticamente según lo que preguntes — sin necesidad de pegar URLs manualmente. Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta: ``` npx ctx7 setup ``` Esto detecta tu editor y configura el servidor Context7. Para la configuración manual, consulta el [repositorio de Context7 en GitHub](https://github.com/upstash/context7). Una vez configurado, haz referencia a la librería de Adapty en tus prompts: ``` Use the adaptyteam/adapty-docs library to look up how to install the Kotlin Multiplatform SDK ``` :::warning Aunque Context7 elimina la necesidad de pegar enlaces a la documentación manualmente, el orden de implementación es importante. Sigue el [recorrido de implementación](#implementation-walkthrough) que aparece a continuación paso a paso para asegurarte de que todo funciona. ::: ### Usa documentación en texto plano \{#use-plain-text-docs\} Puedes acceder a cualquier artículo de Adapty en texto plano Markdown. Añade `.md` al final de su URL o haz clic en **Copy for LLM** bajo el título del artículo. Por ejemplo: [adapty-cursor-kmp.md](https://adapty.io/docs/es/adapty-cursor-kmp.md). Cada etapa del [recorrido de implementación](#implementation-walkthrough) que aparece a continuación incluye un bloque "Envía esto a tu LLM" con enlaces `.md` para pegar. Para obtener más documentación a la vez, consulta los [archivos de índice y subconjuntos por plataforma](#plain-text-doc-index-files) que aparecen más abajo. ## Recorrido de implementación \{#implementation-walkthrough\} El resto de esta guía recorre la integración de Adapty en orden de implementación. Cada etapa incluye los documentos que debes enviar a tu LLM, lo que deberías ver cuando termines y los problemas más comunes. ### Planifica tu integración \{#plan-your-integration\} Antes de lanzarte al código, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA admite un modo de planificación (como el modo plan de Cursor o Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir código. Dile a tu LLM qué enfoque usas para las compras — esto afecta a las guías que debe seguir: - [**Adapty Paywall Builder**](adapty-paywall-builder): Creas los paywalls en el constructor no-code de Adapty y el SDK los renderiza automáticamente. - [**Paywalls creados manualmente**](kmp-making-purchases): Construyes tu propia interfaz de paywall en código pero sigues usando Adapty para obtener productos y gestionar compras. - [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analíticas e integraciones. ¿No sabes cuál elegir? Lee la [tabla comparativa del inicio rápido](kmp-quickstart-paywalls). ### Instala y configura el SDK \{#install-and-configure-the-sdk\} Añade la dependencia del SDK de Adapty mediante Gradle y actívalo con tu clave pública del SDK. Esta es la base — todo lo demás depende de esto. **Guía:** [Instalar y configurar el SDK de Adapty](sdk-installation-kotlin-multiplatform) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/sdk-installation-kotlin-multiplatform.md ``` :::tip[Punto de control] - **Esperado:** La app compila y se ejecuta. El Logcat (Android) o la consola de Xcode (iOS) muestra el log de activación de Adapty. - **Problema frecuente:** "Public API key is missing" → comprueba que hayas sustituido el marcador de posición por tu clave real de App settings. ::: ### Muestra paywalls y gestiona compras \{#show-paywalls-and-handle-purchases\} Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras. Prueba cada compra en el sandbox a medida que avanzas — no esperes hasta el final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración. <Tabs groupId="paywall-approach"> <TabItem value="builder" label="Paywall Builder" default> **Guías:** - [Habilitar compras con paywalls (inicio rápido)](kmp-quickstart-paywalls) - [Obtener paywalls del Paywall Builder y su configuración](kmp-get-pb-paywalls) - [Mostrar paywalls](kmp-present-paywalls) - [Gestionar eventos del paywall](kmp-handling-events) - [Responder a acciones de botones](kmp-handle-paywall-actions) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/kmp-quickstart-paywalls.md - https://adapty.io/docs/es/kmp-get-pb-paywalls.md - https://adapty.io/docs/es/kmp-present-paywalls.md - https://adapty.io/docs/es/kmp-handling-events.md - https://adapty.io/docs/es/kmp-handle-paywall-actions.md ``` :::tip[Punto de control] - **Esperado:** El paywall aparece con los productos que has configurado. Al pulsar un producto se activa el diálogo de compra en sandbox. - **Problema frecuente:** Paywall vacío o error en `getPaywall` → verifica que el ID del placement coincida exactamente con el del dashboard y que el placement tenga una audiencia asignada. ::: </TabItem> <TabItem value="manual" label="Paywalls manuales"> **Guías:** - [Habilitar compras en tu paywall personalizado (inicio rápido)](kmp-quickstart-manual) - [Obtener paywalls y productos](fetch-paywalls-and-products-kmp) - [Renderizar paywall diseñado con Remote Config](present-remote-config-paywalls-kmp) - [Realizar compras](kmp-making-purchases) - [Restaurar compras](kmp-restore-purchase) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/kmp-quickstart-manual.md - https://adapty.io/docs/es/fetch-paywalls-and-products-kmp.md - https://adapty.io/docs/es/present-remote-config-paywalls-kmp.md - https://adapty.io/docs/es/kmp-making-purchases.md - https://adapty.io/docs/es/kmp-restore-purchase.md ``` :::tip[Punto de control] - **Esperado:** Tu paywall personalizado muestra los productos obtenidos de Adapty. Al pulsar un producto se activa el diálogo de compra en sandbox. - **Problema frecuente:** Array de productos vacío → verifica que el paywall tenga productos asignados en el dashboard y que el placement tenga una audiencia. ::: </TabItem> <TabItem value="observer" label="Observer mode"> **Guías:** - [Descripción general del modo Observer](observer-vs-full-mode) - [Implementar el modo Observer](implement-observer-mode-kmp) - [Reportar transacciones en modo Observer](report-transactions-observer-mode-kmp) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/observer-vs-full-mode.md - https://adapty.io/docs/es/implement-observer-mode-kmp.md - https://adapty.io/docs/es/report-transactions-observer-mode-kmp.md ``` :::tip[Punto de control] - **Esperado:** Después de una compra en sandbox usando tu flujo de compras existente, la transacción aparece en el **Event Feed** del dashboard de Adapty. - **Problema frecuente:** Sin eventos → verifica que estás reportando las transacciones a Adapty y que las notificaciones del servidor están configuradas para ambos stores. ::: </TabItem> </Tabs> ### Comprueba el estado de la suscripción \{#check-subscription-status\} Tras una compra, comprueba el perfil del usuario para ver si hay un nivel de acceso activo y restringir el contenido premium. **Guía:** [Comprobar el estado de la suscripción](kmp-check-subscription-status) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/kmp-check-subscription-status.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox, `profile.accessLevels["premium"]?.isActive` devuelve `true`. - **Problema frecuente:** `accessLevels` vacío tras la compra → comprueba que el producto tiene un nivel de acceso asignado en el dashboard. ::: ### Identifica a los usuarios \{#identify-users\} Vincula las cuentas de usuario de tu app con los perfiles de Adapty para que las compras persistan entre dispositivos. :::important Omite este paso si tu app no tiene autenticación. ::: **Guía:** [Identificar usuarios](kmp-quickstart-identify) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/kmp-quickstart-identify.md ``` :::tip[Punto de control] - **Esperado:** Tras llamar a `Adapty.identify("your-user-id")`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado. - **Problema frecuente:** Llama a `identify` después de la activación pero antes de obtener los paywalls para evitar la atribución a perfiles anónimos. ::: ### Prepárate para el lanzamiento \{#prepare-for-release\} Una vez que tu integración funcione en el sandbox, repasa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción. **Guía:** [Lista de verificación de lanzamiento](release-checklist) Envía esto a tu LLM: ``` Read these Adapty docs before releasing: - https://adapty.io/docs/es/release-checklist.md ``` :::tip[Punto de control] - **Esperado:** Todos los elementos de la lista confirmados: conexiones de stores, notificaciones del servidor, flujo de compras, comprobaciones del nivel de acceso y requisitos de privacidad. - **Problema frecuente:** Notificaciones del servidor faltantes → configura las notificaciones del servidor de App Store en **App settings → iOS SDK** y las notificaciones en tiempo real para desarrolladores de Google Play en **App settings → Android SDK**. ::: ## Archivos de índice de documentación en texto plano \{#plain-text-doc-index-files\} Si necesitas dar a tu LLM un contexto más amplio más allá de páginas individuales, ofrecemos archivos de índice que listan o combinan toda la documentación de Adapty: - [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con enlaces `.md`. Un [estándar emergente](https://llmstxt.org/) para hacer los sitios web accesibles a los LLMs. Ten en cuenta que para algunos agentes de IA (por ejemplo, ChatGPT) necesitarás descargar `llms.txt` y subirlo al chat como archivo. - [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación del sitio de Adapty combinada en un único archivo. Muy grande — úsalo solo cuando necesites el panorama completo. - [`kmp-llms.txt`](https://adapty.io/docs/es/kmp-llms.txt) y [`kmp-llms-full.txt`](https://adapty.io/docs/es/kmp-llms-full.txt) específicos de Kotlin Multiplatform: Subconjuntos por plataforma que ahorran tokens en comparación con el sitio completo. --- # File: kmp-paywalls --- --- title: "Paywalls in Kotlin Multiplatform SDK" description: "Learn how to work with paywalls in your Kotlin Multiplatform app with Adapty SDK." --- ## Display paywalls ### Adapty Paywall Builder <CustomDocCardList ids={['kmp-get-pb-paywalls', 'kmp-present-paywalls', 'kmp-handling-events', 'kmp-handle-paywall-actions']} /> :::tip To get started with the Adapty Paywall Builder paywalls quickly, see our [quickstart guide](kmp-quickstart-paywalls). ::: ### Implement paywalls manually <CustomDocCardList ids={['kmp-quickstart-manual', 'fetch-paywalls-and-products-kmp', 'present-remote-config-paywalls-kmp', 'kmp-making-purchases']} /> For more guides on implementing paywalls and handling purchases manually, see the [category](kmp-implement-paywalls-manually). ## Useful features <CustomDocCardList ids={['kmp-use-fallback-paywalls', 'kmp-web-paywalls']} /> --- # File: kmp-get-pb-paywalls --- --- title: "Obtener paywalls del Paywall Builder y su configuración en el SDK de Kotlin Multiplatform" description: "Aprende a recuperar paywalls de PB en Adapty para un mejor control de suscripciones en tu app Kotlin Multiplatform." --- Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación. Ten en cuenta que este tema hace referencia a paywalls personalizados con Paywall Builder. Si estás implementando tus paywalls de forma manual, consulta el tema [Obtener paywalls y productos para paywalls con Remote Config en tu app móvil](fetch-paywalls-and-products-kmp). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de comenzar a mostrar paywalls en tu app móvil (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en ellos](create-placement) en el Adapty Dashboard. 4. Instala el [SDK de Adapty](sdk-installation-kotlin-multiplatform) en tu app móvil. </details> ## Obtener un paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\} Si [diseñaste un paywall con el Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese tipo de paywall contiene tanto lo que debe mostrarse como la forma en que debe presentarse. Aun así, necesitas obtener su ID a través del placement, su configuración de vista y luego presentarlo en tu app. Para garantizar un rendimiento óptimo, es fundamental recuperar el paywall y su [configuración de vista](kmp-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dejando tiempo suficiente para que las imágenes se descarguen antes de mostrárselas al usuario. Para obtener un paywall, usa el método `getPaywall`: ```kotlin showLineNumbers Adapty.getPaywall( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default, loadTimeout = 5.seconds ).onSuccess { paywall -> // the requested paywall }.onError { error -> // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda, a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `AdaptyPaywallFetchPolicy.Default` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban la información más actualizada.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad` para devolver datos en caché si existen. En ese caso, los usuarios podrían no recibir los datos más recientes, pero tendrán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché se mantiene al reiniciar la app y solo se borra al desinstalarla o mediante limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls localmente en dos capas: la caché actualizada regularmente descrita arriba y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para que siempre obtengas la versión más reciente de tus paywalls, garantizando fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el límite, se devolverán datos en caché o el fallback local.</p><p>Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo indicado en `loadTimeout`, ya que la operación puede estar compuesta de distintas solicitudes internas.</p><p>Para Kotlin Multiplatform: puedes crear `TimeInterval` con funciones de extensión (como `5.seconds`, donde `.seconds` proviene de `import com.adapty.utils.seconds`), o `TimeInterval.seconds(5)`. Para no establecer límite, usa `TimeInterval.INFINITE`.</p> | Parámetros de respuesta: | Parámetro | Descripción | | :-------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------| | Paywall | Un objeto [`AdaptyPaywall`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall/) con una lista de IDs de productos, el identificador del paywall, Remote Config y otras propiedades. | ## Obtener la configuración de vista de un paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important Asegúrate de activar el toggle **Show on device** en el Paywall Builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperarse. ::: Tras obtener el paywall, comprueba si incluye un `ViewConfiguration`, lo que indica que fue creado con Paywall Builder. Esto te orientará sobre cómo mostrar el paywall. Si el `ViewConfiguration` está presente, trátalo como un paywall de Paywall Builder; si no lo está, [manéjalo como un paywall de Remote Config](present-remote-config-paywalls-kmp). Usa el método `createPaywallView` para cargar la configuración de vista. ```kotlin showLineNumbers if (paywall.hasViewConfiguration) { AdaptyUI.createPaywallView( paywall = paywall, loadTimeout = 5.seconds, preloadProducts = true ).onSuccess { paywallView -> // use paywallView }.onError { error -> // handle the error } } else { // use your custom logic } ``` | Parámetro | Presencia | Descripción | | :--------------------------- | :------------- | :----------------------------------------------------------- | | **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. | | **loadTimeout** | opcional | Este valor limita el tiempo de espera para este método. Si se alcanza el límite, se devolverán datos en caché o el fallback local. Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo indicado en `loadTimeout`, ya que la operación puede estar compuesta de distintas solicitudes internas. Puedes usar funciones de extensión como `5.seconds` de `kotlin.time.Duration.Companion`. | | **preloadProducts** | opcional | Establécelo en `true` para precargar productos y mejorar el rendimiento. Cuando está activado, los productos se cargan con antelación, reduciendo el tiempo necesario para mostrar el paywall. | | **productPurchaseParams** | opcional | Un mapa de [`AdaptyProductIdentifier`](https://kmp.adapty.io/adapty/com.adapty.kmp.models/-adapty-product-identifier/) a [`AdaptyPurchaseParameters`](https://kmp.adapty.io/adapty/com.adapty.kmp.models/-adapty-purchase-parameters/). Úsalo para configurar parámetros específicos de compra, como ofertas personalizadas o parámetros de actualización de suscripción para productos individuales del paywall. | :::note Si usas varios idiomas, aprende a añadir una [localización al Paywall Builder](add-paywall-locale-in-adapty-paywall-builder). ::: Una vez cargado, [presenta el paywall](kmp-present-paywalls). ## Obtener un paywall para la audiencia por defecto y cargarlo más rápido \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} Normalmente, los paywalls se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, si tienes muchas audiencias y paywalls y tus usuarios tienen una conexión a internet débil, puede que obtener un paywall tarde más de lo deseable. En esas situaciones, puede que prefieras mostrar un paywall por defecto para garantizar una buena experiencia de usuario en lugar de no mostrar ninguno. Para resolver esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement indicado para la audiencia **All Users**. No obstante, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, tal como se detalla en la sección [Obtener un paywall diseñado con Paywall Builder](#fetch-paywall-designed-with-paywall-builder) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunas desventajas importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: si necesitas mostrar paywalls distintos para diferentes versiones de la app (actual y futuras), pueden surgir complicaciones. Tendrás que diseñar paywalls que sean compatibles con la versión actual (antigua) o asumir que los usuarios con esa versión puedan encontrar problemas con paywalls que no se renderizan correctamente. - **Pérdida de segmentación**: todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluyendo por países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estas desventajas para beneficiarte de una carga más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getPaywall` descrito [arriba](#fetch-paywall-designed-with-paywall-builder). ::: ```kotlin showLineNumbers Adapty.getPaywallForDefaultAudience( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default, ).onSuccess { paywall -> // the requested paywall }.onError { error -> // handle the error } ``` | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda, a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `AdaptyPaywallFetchPolicy.Default` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban la información más actualizada.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad` para devolver datos en caché si existen. En ese caso, los usuarios podrían no recibir los datos más recientes, pero tendrán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché se mantiene al reiniciar la app y solo se borra al desinstalarla o mediante limpieza manual.</p> | ## Personalizar assets \{#customize-assets\} Para personalizar imágenes y vídeos en tu paywall, implementa los assets personalizados. Las imágenes y vídeos destacados tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de assets personalizados, puedes apuntar a estos elementos por sus IDs y personalizar su comportamiento. Para otras imágenes y vídeos, necesitas [establecer un ID personalizado](custom-media) en el dashboard de Adapty. Por ejemplo, puedes: - Mostrar una imagen o vídeo diferente a algunos usuarios. - Mostrar una imagen de vista previa local mientras se carga la imagen principal remota. - Mostrar una imagen de vista previa antes de reproducir un vídeo. :::important Para usar esta funcionalidad, actualiza el SDK de Adapty a la versión 3.7.0 o superior. ::: A continuación se muestra un ejemplo de cómo proporcionar assets personalizados a través de un mapa: :::info El SDK de Kotlin Multiplatform solo admite assets locales. Para contenido remoto, debes descargar y almacenar en caché los assets localmente antes de usarlos como assets personalizados. ::: ```kotlin showLineNumbers // Import generated Res class for accessing resources viewModelScope.launch { // Get URIs for bundled resources using Res.getUri() val heroImagePath = Res.getUri("files/images/hero_image.png") val demoVideoPath = Res.getUri("files/videos/demo_video.mp4") // Or read image as byte data val imageByteData = Res.readBytes("files/images/avatar.png") // Create custom assets map val customAssets: Map<String, AdaptyCustomAsset> = mapOf( // Load image from app resources (bundled with the app) // Files should be placed in commonMain/composeResources/files/ "hero_image" to AdaptyCustomAsset.localImageResource( path = heroImagePath ), // Or use image byte data "avatar" to AdaptyCustomAsset.localImageData( data = imageByteData ), // Load video from app resources "demo_video" to AdaptyCustomAsset.localVideoResource( path = demoVideoPath ), // Or use a video file from device storage "intro_video" to AdaptyCustomAsset.localVideoFile( path = "/path/to/local/video.mp4" ), // Apply custom brand colors "brand_primary" to AdaptyCustomAsset.color( colorHex = "#FF6B35" ), // Create gradient background "card_gradient" to AdaptyCustomAsset.linearGradient( colors = listOf("#1E3A8A", "#3B82F6", "#60A5FA"), stops = listOf(0.0f, 0.5f, 1.0f) ) ) // Use custom assets when creating paywall view AdaptyUI.createPaywallView( paywall = paywall, customAssets = customAssets ).onSuccess { paywallView -> // Present the paywall with custom assets paywallView.present() }.onError { error -> // Handle the error - paywall will fall back to default appearance } } ``` :::note Si un asset no se encuentra o falla al cargarse, el paywall volverá a su apariencia predeterminada configurada en el Paywall Builder. ::: --- # File: kmp-present-paywalls --- --- title: "Kotlin Multiplatform - Presentar paywalls del nuevo Paywall Builder" description: "Aprende a presentar paywalls en Kotlin Multiplatform para una monetización efectiva." --- Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese paywall ya contiene tanto lo que se debe mostrar como la forma en que debe mostrarse. :::warning Esta guía es exclusivamente para **paywalls del nuevo Paywall Builder**. El proceso de presentación de paywalls es diferente para los paywalls diseñados con Remote Config y para el [modo Observer](observer-vs-full-mode). Para presentar **paywalls con Remote Config**, consulta [Renderizar paywalls diseñados con Remote Config](present-remote-config-paywalls-kmp). ::: El SDK de Adapty para Kotlin Multiplatform ofrece dos formas de presentar paywalls: - **Con Compose Multiplatform** - **Sin Compose Multiplatform** ## Con Compose Multiplatform \{#with-compose-multiplatform\} Para mostrar un paywall, usa el método `view.present()` sobre el `view` creado por el método [`createPaywallView`](kmp-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `createPaywallView` otra vez para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo puede provocar un error. ::: ```kotlin showLineNumbers title="Kotlin Multiplatform" viewModelScope.launch { AdaptyUI.createPaywallView(paywall = paywall).onSuccess { view -> view.present() }.onError { error -> // handle the error } } ``` ### Mostrar diálogo \{#show-dialog\} Usa este método en lugar de los diálogos de alerta nativos cuando se presenta una vista de paywall en Android. En Android, las alertas normales aparecen detrás de la vista del paywall, lo que las hace invisibles para los usuarios. Este método garantiza que el diálogo se muestre correctamente por encima del paywall en todas las plataformas. ```kotlin showLineNumbers title="Kotlin Multiplatform" viewModelScope.launch { view.showDialog( title = "Close paywall?", content = "You will lose access to exclusive offers.", primaryActionTitle = "Stay", secondaryActionTitle = "Close" ).onSuccess { action -> if (action == AdaptyUIDialogActionType.SECONDARY) { // User confirmed - close the paywall view.dismiss() } // If primary - do nothing, user stays }.onError { error -> // handle the error } } ``` ### Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el paywall en iOS pasando el parámetro `iosPresentationStyle` al método `present()`. El parámetro acepta los valores `AdaptyUIIOSPresentationStyle.FULLSCREEN` (por defecto) o `AdaptyUIIOSPresentationStyle.PAGESHEET`. ```kotlin showLineNumbers viewModelScope.launch { val view = AdaptyUI.createPaywallView(paywall = paywall).getOrNull() view?.present(iosPresentationStyle = AdaptyUIIOSPresentationStyle.PAGESHEET) } ``` ## Sin Compose Multiplatform \{#without-compose-multiplatform\} :::note `createNativePaywallView` forma parte del módulo principal `io.adapty:adapty-kmp`. Si tu proyecto no usa Compose Multiplatform, no necesitas la dependencia `io.adapty:adapty-kmp-ui`. ::: Para embeber un paywall sin Compose Multiplatform, llama a `createNativePaywallView`. Devuelve un `AdaptyNativePaywallView` que puedes añadir a tu layout: <Tabs> <TabItem value="android" label="Android"> ```kotlin showLineNumbers title="Kotlin Multiplatform (Android)" val nativeView = AdaptyUI.createNativePaywallView( context = context, viewModelStoreOwner = activity, paywall = paywall, observer = myPaywallObserver, ) // Embed in your Compose layout: AndroidView( factory = { nativeView.view }, modifier = Modifier.fillMaxSize() ) ``` </TabItem> <TabItem value="ios" label="iOS"> Dado que los métodos por defecto de la interfaz KMP se vuelven `@required` en Swift, no puedes implementar `AdaptyUIPaywallsEventsObserver` directamente desde Swift. Primero declara una clase base abierta en `iosMain`: ```kotlin showLineNumbers title="iosMain (Kotlin)" open class BasePaywallObserver : AdaptyUIPaywallsEventsObserver ``` Luego crea una subclase en Swift, sobreescribiendo solo lo que necesitas: ```swift showLineNumbers title="Swift" class MyPaywallObserver: BasePaywallObserver { override func paywallViewDidPerformAction(view: AdaptyUIPaywallView, action: any AdaptyUIAction) { if action is AdaptyUIActionCloseAction { // remove nativeView from your view hierarchy } } } let nativeView = AdaptyUI.shared.createNativePaywallView( paywall: paywall, observer: MyPaywallObserver() ) // nativeView.viewController is a UIViewController. // Add it to your SwiftUI view or UIKit hierarchy. ``` </TabItem> </Tabs> ### Eliminar la vista \{#dispose-the-view\} Llama a `dispose()` cuando vayas a retirar la vista de tu layout. Esto cancela el registro del listener de eventos y libera los recursos internos. ```kotlin showLineNumbers title="Kotlin Multiplatform" nativeView.dispose() ``` ## Etiquetas personalizadas \{#custom-tags\} Las etiquetas personalizadas te permiten evitar crear paywalls separados para distintos escenarios. Imagina un único paywall que se adapta dinámicamente según los datos del usuario. Por ejemplo, en lugar de un genérico "¡Hola!", podrías saludar al usuario por su nombre: "¡Hola, Juan!" o "¡Hola, Ana!". Algunos casos de uso de las etiquetas personalizadas: - Mostrar el nombre o email del usuario en el paywall. - Mostrar el día de la semana para impulsar las ventas (p. ej., "Feliz jueves"). - Añadir detalles personalizados sobre los productos que vendes (como el nombre de un programa de fitness o un número de teléfono en una app VoIP). Las etiquetas personalizadas te ayudan a crear un paywall flexible que se adapta a distintas situaciones, haciendo la interfaz de tu app más personalizada y atractiva. :::warning En algunos casos, tu app puede no saber con qué reemplazar una etiqueta personalizada, especialmente si los usuarios están en una versión antigua del SDK de AdaptyUI. Para evitarlo, añade siempre un texto de respaldo que sustituya las líneas que contengan etiquetas personalizadas desconocidas. Sin esto, los usuarios podrían ver las etiquetas como código (`<USERNAME/>`). ::: Para usar etiquetas personalizadas en tu paywall, pásalas al crear la vista del paywall: <Tabs> <TabItem value="standalone" label="Con Compose Multiplatform" default> ```kotlin showLineNumbers title="Kotlin Multiplatform" viewModelScope.launch { val customTags = mapOf( "USERNAME" to "John", "DAY_OF_WEEK" to "Thursday" ) AdaptyUI.createPaywallView( paywall = paywall, customTags = customTags ).onSuccess { view -> view.present() }.onError { error -> // handle the error } } ``` </TabItem> <TabItem value="native" label="Sin Compose Multiplatform"> ```kotlin showLineNumbers title="Kotlin Multiplatform (Android)" val customTags = mapOf( "USERNAME" to "John", "DAY_OF_WEEK" to "Thursday" ) val nativeView = AdaptyUI.createNativePaywallView( context = context, viewModelStoreOwner = activity, paywall = paywall, observer = myPaywallObserver, customTags = customTags, ) ``` ```kotlin showLineNumbers title="Kotlin Multiplatform (iOS)" val customTags = mapOf( "USERNAME" to "John", "DAY_OF_WEEK" to "Thursday" ) val nativeView = AdaptyUI.createNativePaywallView( paywall = paywall, observer = myPaywallObserver, customTags = customTags, ) ``` </TabItem> </Tabs> ## Temporizadores personalizados \{#custom-timers\} El temporizador del paywall es una herramienta excelente para promocionar ofertas especiales y de temporada con un límite de tiempo. Sin embargo, es importante tener en cuenta que este temporizador no está vinculado a la validez de la oferta ni a la duración de la campaña. Es simplemente una cuenta atrás independiente que comienza desde el valor que establezcas y disminuye hasta cero. Cuando el temporizador llega a cero, no ocurre nada: simplemente se queda en cero. Puedes personalizar el texto antes y después del temporizador para crear el mensaje deseado, por ejemplo: "La oferta termina en: 10:00 seg." Para usar temporizadores personalizados en tu paywall, pásalos al crear la vista del paywall: <Tabs> <TabItem value="standalone" label="Con Compose Multiplatform" default> ```kotlin showLineNumbers title="Kotlin Multiplatform" viewModelScope.launch { val customTimers = mapOf( "CUSTOM_TIMER_NY" to LocalDateTime(2025, 1, 1, 0, 0, 0), "CUSTOM_TIMER_SALE" to LocalDateTime(2024, 12, 31, 23, 59, 59) ) AdaptyUI.createPaywallView( paywall = paywall, customTimers = customTimers ).onSuccess { view -> view.present() }.onError { error -> // handle the error } } ``` </TabItem> <TabItem value="native" label="Sin Compose Multiplatform"> ```kotlin showLineNumbers title="Kotlin Multiplatform (Android)" val customTimers = mapOf( "CUSTOM_TIMER_NY" to LocalDateTime(2025, 1, 1, 0, 0, 0), "CUSTOM_TIMER_SALE" to LocalDateTime(2024, 12, 31, 23, 59, 59) ) val nativeView = AdaptyUI.createNativePaywallView( context = context, viewModelStoreOwner = activity, paywall = paywall, observer = myPaywallObserver, customTimers = customTimers, ) ``` ```kotlin showLineNumbers title="Kotlin Multiplatform (iOS)" val customTimers = mapOf( "CUSTOM_TIMER_NY" to LocalDateTime(2025, 1, 1, 0, 0, 0), "CUSTOM_TIMER_SALE" to LocalDateTime(2024, 12, 31, 23, 59, 59) ) val nativeView = AdaptyUI.createNativePaywallView( paywall = paywall, observer = myPaywallObserver, customTimers = customTimers, ) ``` </TabItem> </Tabs> --- # File: kmp-handle-paywall-actions --- --- title: "Responder a acciones de botones en el SDK de Kotlin Multiplatform" description: "Gestiona las acciones de botones del paywall en Kotlin Multiplatform usando Adapty para una mejor monetización de tu app." --- :::warning **Solo las compras y restauraciones se gestionan automáticamente.** El resto de acciones de botones, como cerrar paywalls o abrir enlaces, requieren implementar las respuestas correspondientes en el código de la app. ::: Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente: 1. Añade un [botón en el Paywall Builder](paywall-buttons) y asígnale una acción existente o crea un ID de acción personalizado. 2. Escribe código en tu app para gestionar cada acción que hayas asignado. Esta guía muestra cómo gestionar acciones personalizadas y predefinidas en tu código. ## Configurar AdaptyUIPaywallsEventsObserver \{#set-up-the-adaptyuipaywallseventsob server\} Para gestionar las acciones del paywall, necesitas implementar la interfaz `AdaptyUIPaywallsEventsObserver` y configurarla con `AdaptyUI.setPaywallsEventsObserver()`. Esto debe hacerse al inicio del ciclo de vida de tu app, normalmente en tu actividad principal o en la inicialización de la app. ```kotlin // In your app initialization AdaptyUI.setPaywallsEventsObserver(MyAdaptyUIPaywallsEventsObserver()) ``` ## Cerrar paywalls \{#close-paywalls\} Para añadir un botón que cierre tu paywall: 1. En el Paywall Builder, añade un botón y asígnale la acción **Close**. 2. En el código de tu app, implementa un manejador para la acción `close` que descarte el paywall. :::info En el SDK de Kotlin Multiplatform, `CloseAction` y `AndroidSystemBackAction` cierran el paywall por defecto. Sin embargo, puedes sobreescribir este comportamiento en tu código si lo necesitas. Por ejemplo, cerrar un paywall podría abrir otro. ::: ```kotlin class MyAdaptyUIPaywallsEventsObserver : AdaptyUIPaywallsEventsObserver { override fun paywallViewDidPerformAction(view: AdaptyUIPaywallView, action: AdaptyUIAction) { when (action) { AdaptyUIAction.CloseAction, AdaptyUIAction.AndroidSystemBackAction -> view.dismiss() } } } // Set up the observer AdaptyUI.setPaywallsEventsObserver(MyAdaptyUIPaywallsEventsObserver()) ``` Si estás usando [`createNativePaywallView`](kmp-present-paywalls#without-compose-multiplatform), llamar a `view.dismiss()` no tiene ningún efecto — la vista está integrada en tu layout, no presentada a través del stack de KMP. En su lugar, elimina la vista de tu layout y llama a `dispose()` sobre ella. ## Abrir URLs desde paywalls \{#open-urls-from-paywalls\} :::tip Si quieres añadir un grupo de enlaces (por ejemplo, términos de uso y restauración de compras), añade un elemento **Link** en el Paywall Builder y gestiónalo igual que los botones con la acción **Open URL**. ::: Para añadir un botón que abra un enlace desde tu paywall (por ejemplo, **Términos de uso** o **Política de privacidad**): 1. En el Paywall Builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir. 2. En el código de tu app, implementa un manejador para la acción `openUrl` que abra la URL recibida en un navegador. :::info En el SDK de Kotlin Multiplatform, `OpenUrlAction` proporciona la URL que debe abrirse. Puedes implementar lógica personalizada para gestionar la apertura de URLs, como mostrar un diálogo de confirmación o usar el método de gestión de URLs preferido de tu app. ::: ```kotlin class MyAdaptyUIPaywallsEventsObserver( private val uriHandler: UriHandler ) : AdaptyUIPaywallsEventsObserver { override fun paywallViewDidPerformAction(view: AdaptyUIPaywallView, action: AdaptyUIAction) { when (action) { is AdaptyUIAction.OpenUrlAction -> { // Show confirmation dialog before opening URL mainUiScope.launch { val selectedAction = view.showDialog( title = "Open URL?", content = action.url, primaryActionTitle = "Cancel", secondaryActionTitle = "Open" ).getOrNull() when (selectedAction) { AdaptyUIDialogActionType.PRIMARY -> { // User cancelled } AdaptyUIDialogActionType.SECONDARY -> { // User confirmed - open URL uriHandler.openUri(action.url) } else -> Unit } } } } } } // Set up the observer with UriHandler AdaptyUI.setPaywallsEventsObserver(MyAdaptyUIPaywallsEventsObserver(uriHandler)) ``` ## Iniciar sesión en la app \{#log-into-the-app\} Para añadir un botón que permita a los usuarios iniciar sesión en tu app: 1. En el Paywall Builder, añade un botón y asígnale una acción **Custom** con el ID "login". 2. En el código de tu app, implementa un manejador para la acción personalizada que identifique a tu usuario. ```kotlin class MyAdaptyUIObserver : AdaptyUIObserver { override fun paywallViewDidPerformAction(view: AdaptyUIView, action: AdaptyUIAction) { when (action) { is AdaptyUIAction.CustomAction -> { if (action.action == "login") { // Handle login action - navigate to login screen // This depends on your app's navigation system // For example, in Compose Multiplatform: // navController.navigate("login") } } } } } ``` ## Gestionar acciones personalizadas \{#handle-custom-actions\} Para añadir un botón que gestione cualquier otra acción: 1. En el Paywall Builder, añade un botón, asígnale la acción **Custom** y asígnale un ID. 2. En el código de tu app, implementa un manejador para el ID de acción que hayas creado. Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall: ```kotlin class MyAdaptyUIPaywallsEventsObserver : AdaptyUIPaywallsEventsObserver { override fun paywallViewDidPerformAction(view: AdaptyUIPaywallView, action: AdaptyUIAction) { when (action) { is AdaptyUIAction.CustomAction -> { when (action.action) { "login" -> { // Handle login action - navigate to login screen // This depends on your app's navigation system // For example, in Compose Multiplatform: // navController.navigate("login") } } } } } } // Set up the observer AdaptyUI.setPaywallsEventsObserver(MyAdaptyUIPaywallsEventsObserver()) ``` --- # File: kmp-handling-events --- --- title: "Kotlin Multiplatform - Gestionar eventos del paywall" description: "Gestiona eficientemente los eventos de suscripción en Kotlin Multiplatform con las herramientas de seguimiento de eventos de Adapty." --- Los paywalls configurados con el [Paywall Builder](adapty-paywall-builder) no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan ciertos eventos a los que tu app puede reaccionar. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de producto, etc.) y notificaciones sobre acciones relacionadas con compras en el paywall. A continuación aprenderás cómo responder a estos eventos. :::warning Esta guía es solo para paywalls del **nuevo Paywall Builder**. ::: Para controlar o monitorizar los procesos que ocurren en la pantalla del paywall dentro de tu app, implementa los métodos de la interfaz `AdaptyUIPaywallsEventsObserver`. Algunos métodos tienen implementaciones por defecto que gestionan automáticamente los escenarios más comunes. :::note En estos métodos es donde añades tu lógica personalizada para responder a los eventos del paywall. Puedes usar `view.dismiss()` para cerrar el paywall o implementar cualquier otro comportamiento personalizado que necesites. ::: ## Eventos generados por el usuario \{#user-generated-events\} ### Aparición y desaparición del paywall \{#paywall-appearance-and-disappearance\} Cuando un paywall aparece o desaparece, se invocan estos métodos: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidAppear(view: AdaptyUIPaywallView) { // Handle paywall appearance // You can track analytics or update UI here } override fun paywallViewDidDisappear(view: AdaptyUIPaywallView) { // Handle paywall disappearance // You can track analytics or update UI here } ``` :::note - En iOS, `paywallViewDidAppear` también se invoca cuando el usuario pulsa el [botón de web paywall](web-paywall#step-2a-add-a-web-purchase-button) dentro de un paywall y se abre un web paywall en un navegador in-app. - En iOS, `paywallViewDidDisappear` también se invoca cuando un [web paywall](web-paywall#step-2a-add-a-web-purchase-button) abierto desde un paywall en un navegador in-app desaparece de la pantalla. ::: <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // Paywall appeared { // No additional data } // Paywall disappeared { // No additional data } ``` </Details> ### Selección de producto \{#product-selection\} Si el usuario selecciona un producto para comprarlo, se invoca este método: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidSelectProduct(view: AdaptyUIPaywallView, productId: String) { // Handle product selection // You can update UI or track analytics here } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "productId": "premium_monthly" } ``` </Details> ### Inicio de compra \{#started-purchase\} Si el usuario inicia el proceso de compra, se invoca este método: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidStartPurchase(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct) { // Handle purchase start // You can show loading indicators or track analytics here } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ``` </Details> ### Compra exitosa, cancelada o pendiente \{#successful-canceled-or-pending-purchase\} Si una compra tiene éxito, se invoca este método. Por defecto, cierra automáticamente el paywall a menos que el usuario haya cancelado la compra: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidFinishPurchase( view: AdaptyUIPaywallView, product: AdaptyPaywallProduct, purchaseResult: AdaptyPurchaseResult ) { when (purchaseResult) { is AdaptyPurchaseResult.Success -> { // Check if user has access to premium features if (purchaseResult.profile.accessLevels["premium"]?.isActive == true) { view.dismiss() } } AdaptyPurchaseResult.Pending -> { // Handle pending purchase (e.g., user will pay offline with cash) } AdaptyPurchaseResult.UserCanceled -> { // Handle user cancellation } } } ``` <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // Successful purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "Success", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } } } // Pending purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "Pending" } } // User canceled purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "UserCanceled" } } ``` </Details> Te recomendamos cerrar la pantalla del paywall cuando la compra sea exitosa. ### Compra fallida \{#failed-purchase\} Si una compra falla por un error, se invoca este método. Esto incluye errores de StoreKit/Google Play Billing (restricciones de pago, productos inválidos, fallos de red), errores de verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan `paywallViewDidFinishPurchase` con un resultado de cancelación, y los pagos pendientes no activan este método. ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidFailPurchase( view: AdaptyUIPaywallView, product: AdaptyPaywallProduct, error: AdaptyError ) { // Add your purchase failure handling logic here // For example: show error message, retry option, or custom error handling } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" } } } ``` </Details> ### Inicio de restauración \{#started-restore\} Si el usuario inicia el proceso de restauración, se invoca este método: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidStartRestore(view: AdaptyUIPaywallView) { // Handle restore start // You can show loading indicators or track analytics here } ``` ### Restauración exitosa \{#successful-restore\} Si la restauración de una compra tiene éxito, se invoca este método: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidFinishRestore(view: AdaptyUIPaywallView, profile: AdaptyProfile) { // Add your successful restore handling logic here // For example: show success message, update UI, or dismiss paywall // Check if user has access to premium features if (profile.accessLevels["premium"]?.isActive == true) { view.dismiss() } } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } ``` </Details> Te recomendamos cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de la suscripción](subscription-status) para aprender cómo comprobarlo. ### Restauración fallida \{#failed-restore\} Si `Adapty.restorePurchases()` falla, se invoca este método: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidFailRestore(view: AdaptyUIPaywallView, error: AdaptyError) { // Add your restore failure handling logic here // For example: show error message, retry option, or custom error handling } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } ``` </Details> ### Finalización de la navegación de pago web \{#web-payment-navigation-completion\} Si el usuario inicia el proceso de compra mediante un [web paywall](web-paywall), se invoca este método: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidFinishWebPaymentNavigation( view: AdaptyUIPaywallView, product: AdaptyPaywallProduct?, error: AdaptyError? ) { if (error != null) { // Handle web payment navigation error } else { // Handle successful web payment navigation } } ``` <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // Successful web payment navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": null } // Failed web payment navigation { "product": null, "error": { "code": "web_payment_failed", "message": "Web payment navigation failed", "details": { "underlyingError": "Network connection error" } } } ``` </Details> ## Carga de datos y renderizado \{#data-fetching-and-rendering\} ### Errores de carga de productos \{#product-loading-errors\} Si no pasas los productos durante la inicialización, AdaptyUI obtendrá los objetos necesarios del servidor por sí mismo. Si esta operación falla, AdaptyUI notificará el error llamando a este método: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidFailLoadingProducts(view: AdaptyUIPaywallView, error: AdaptyError) { // Add your product loading failure handling logic here // For example: show error message, retry option, or custom error handling } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } ``` </Details> ### Errores de renderizado \{#rendering-errors\} Si ocurre un error durante el renderizado de la interfaz, se notificará mediante este método: ```kotlin showLineNumbers title="Kotlin" override fun paywallViewDidFailRendering(view: AdaptyUIPaywallView, error: AdaptyError) { // Handle rendering error // In a normal situation, such errors should not occur // If you come across one, please let us know } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```javascript { "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } ``` </Details> En una situación normal, estos errores no deberían producirse, así que si te encuentras con alguno, por favor comunícanoslo. --- # File: kmp-use-fallback-paywalls --- --- title: "Kotlin Multiplatform - Usar paywalls de respaldo" description: "Gestiona los casos en que los usuarios están sin conexión o los servidores de Adapty no están disponibles" --- Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet. * **Si la aplicación no puede acceder a los servidores de Adapty:** Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding. * **Si la aplicación no puede acceder a internet:** Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar. :::important Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty. ::: ## Configuración \{#configuration\} 1. Añade el archivo de configuración de respaldo a tu aplicación. * Si tu plataforma de destino es Android, mueve el archivo de configuración de respaldo a la carpeta `android/app/src/main/assets/`. * Si tu plataforma de destino es iOS, añade el archivo JSON de respaldo al bundle de tu proyecto. (**File** -> **Add Files to YourProjectName**) 2. Llama al método `.setFallback` **antes** de obtener el paywall o onboarding de destino. 3. Establece el parámetro `assetId` según tu plataforma de destino. * Android: usa la ruta del archivo relativa al directorio `assets`. * iOS: usa el nombre completo del archivo. ```kotlin showLineNumbers Adapty.setFallback(assetId = "fallback.json") .onSuccess { // Fallback paywalls loaded successfully } .onError { error -> // Handle the error } ``` Parámetros: | Parámetro | Descripción | | :---------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **assetId** | Nombre del archivo de configuración de respaldo (iOS). <br /> Ruta del archivo de configuración de respaldo, relativa al directorio `assets` (Android). | :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: kmp-web-paywalls --- --- title: "Implementar web paywalls en el SDK de Kotlin Multiplatform" description: "Configura un web paywall para recibir pagos sin las comisiones ni las revisiones de la store." --- :::important Antes de comenzar, asegúrate de haber [configurado tu web paywall en el dashboard](web-paywall) e instalado la versión 3.15 o posterior del SDK de Adapty. ::: ## Abrir web paywalls \{#open-web-paywalls\} Si estás trabajando con un paywall que desarrollaste tú mismo, necesitas gestionar los web paywalls usando el método del SDK. El método `openWebPaywall`: 1. Genera una URL única que permite a Adapty vincular un paywall específico mostrado a un usuario concreto con la página web a la que es redirigido. 2. Detecta cuándo los usuarios regresan a la app y luego solicita `getProfile` a intervalos cortos para determinar si los derechos de acceso del perfil han sido actualizados. De esta forma, si el pago fue exitoso y los derechos de acceso han sido actualizados, la suscripción se activa en la app casi de inmediato. :::note Cuando los usuarios regresen a la app, actualiza la interfaz para reflejar los cambios del perfil. Adapty recibirá y procesará los eventos de actualización del perfil. ::: ```kotlin showLineNumbers viewModelScope.launch { Adapty.openWebPaywall(product = product).onSuccess { // the web paywall was opened successfully }.onError { error -> // handle the error } } ``` :::note Existen dos versiones del método `openWebPaywall`: 1. `openWebPaywall(product = product)`, que genera URLs a partir del paywall y también añade los datos del producto a las URLs. 2. `openWebPaywall(paywall = paywall)`, que genera URLs a partir del paywall sin añadir los datos del producto a las URLs. Úsalo cuando los productos de tu paywall en Adapty sean diferentes a los del web paywall. ::: ## Abrir web paywalls en un navegador integrado \{#open-web-paywalls-in-an-in-app-browser\} Por defecto, los web paywalls se abren en el navegador externo. Para ofrecer una experiencia de usuario más fluida, puedes abrir los web paywalls en un navegador integrado. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin salir de la app. Para habilitarlo, establece el parámetro `openIn` en `AdaptyWebPresentation.IN_APP_BROWSER`: ```kotlin showLineNumbers viewModelScope.launch { Adapty.openWebPaywall( product = product, openIn = AdaptyWebPresentation.IN_APP_BROWSER // default – EXTERNAL_BROWSER ).onSuccess { // the web paywall was opened successfully }.onError { error -> // handle the error } } ``` --- # File: kmp-troubleshoot-paywall-builder --- --- title: "Solucionar problemas del Paywall Builder en el SDK de Kotlin Multiplatform" description: "Solucionar problemas del Paywall Builder en el SDK de Kotlin Multiplatform" --- Esta guía te ayuda a resolver problemas comunes al usar paywalls diseñados en el Adapty Paywall Builder en el SDK de Kotlin Multiplatform. ## Falla al obtener la configuración de un paywall \{#getting-a-paywall-configuration-fails\} **Problema**: El método `createPaywallView` no consigue crear una vista del paywall, o el paywall no tiene una configuración de vista. **Motivo**: El paywall no está habilitado para mostrarse en el dispositivo en el Paywall Builder. **Solución**: Activa el botón **Show on device** en el Paywall Builder. También puedes comprobar si un paywall tiene configuración de vista usando la propiedad `hasViewConfiguration` en el objeto `AdaptyPaywall`. <img src="/assets/shared/img/show-on-device.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## El número de vistas del paywall es demasiado alto \{#the-paywall-view-number-is-too-big\} **Problema**: El contador de vistas del paywall muestra el doble del número esperado. **Motivo**: Es posible que estés llamando a `logShowPaywall` en tu código, lo que duplica el contador de vistas si estás usando el Paywall Builder. Para paywalls diseñados con el Paywall Builder, las analíticas se registran automáticamente, por lo que no es necesario usar este método. **Solución**: Asegúrate de no estar llamando a `logShowPaywall` en tu código si estás usando el Paywall Builder. --- # File: kmp-implement-paywalls-manually --- --- title: "Implement paywalls manually in Kotlin Multiplatform SDK" description: "Learn how to implement paywalls manually in your Kotlin Multiplatform app with Adapty SDK." --- ## Accept purchases If you are working with paywalls you've implemented yourself, you can delegate handling purchases to Adapty, using the `makePurchase` method. This way, we will handle all the user scenarios, and you will only need to handle the purchase results. :::important `makePurchase` works with products created in the Adapty dashboard. Make sure you configure products and ways to retrieve them in the dashboard by following the [quickstart guide](quickstart). ::: <CustomDocCardList ids={['kmp-quickstart-manual', 'fetch-paywalls-and-products-kmp', 'present-remote-config-paywalls-kmp', 'kmp-making-purchases', 'kmp-restore-purchase', 'kmp-troubleshoot-purchases']} /> ## Observer mode If you want to implement your own purchase handling logic from scratch, but still want to benefit from the advanced analytics in Adapty, you can use the observer mode. :::important Consider the observer mode limitations [here](observer-vs-full-mode). ::: <CustomDocCardList ids={['implement-observer-mode-kmp', 'report-transactions-observer-mode-kmp', 'kmp-troubleshoot-purchases']} /> --- # File: kmp-quickstart-manual --- --- title: "Habilitar compras en tu paywall personalizado con el SDK de Kotlin Multiplatform" description: "Integra el SDK de Adapty en tus paywalls personalizados de Kotlin Multiplatform para habilitar compras in-app." --- Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall mientras el SDK de Adapty obtiene los productos, gestiona las nuevas compras y restaura las anteriores. :::important **Esta guía es para desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Paywall Builder de Adapty](kmp-quickstart-paywalls). Con el Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compra automáticamente y puedes probar distintos diseños sin volver a publicar tu app. ::: ## Antes de empezar \{#before-you-start\} ### Configura los productos \{#set-up-products\} Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras paywalls para placements en el dashboard y luego los solicitas por ID de placement en tu código. Esto facilita ejecutar pruebas A/B y mostrar distintos paywalls a diferentes usuarios. Asegúrate de entender estos conceptos aunque trabajes con tu paywall personalizado. En esencia, son solo tu forma de gestionar los productos que vendes en tu app. Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite recuperar tus productos. Para entender qué debes hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart). ### Gestiona los usuarios \{#manage-users\} Puedes trabajar con o sin autenticación de backend por tu parte. Sin embargo, el SDK de Adapty gestiona de forma diferente los usuarios anónimos y los identificados. Lee la [guía de inicio rápido de identificación](kmp-quickstart-identify) para entender los detalles y asegurarte de que trabajas con los usuarios correctamente. ## Paso 1. Obtén los productos \{#step-1-get-products\} Para recuperar los productos de tu paywall personalizado, necesitas: 1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`. 2. Obtener el array de productos para este paywall usando el método `getPaywallProducts`. ```kotlin showLineNumbers fun loadPaywall() { Adapty.getPaywall(placementId = "YOUR_PLACEMENT_ID") .onSuccess { paywall -> Adapty.getPaywallProducts(paywall = paywall) .onSuccess { products -> // Use products to build your custom paywall UI } .onError { error -> // Handle the error } } .onError { error -> // Handle the error } } ``` ## Paso 2. Acepta las compras \{#step-2-accept-purchases\} Cuando un usuario toca un producto en tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado. ```kotlin showLineNumbers fun purchaseProduct(product: AdaptyPaywallProduct) { Adapty.makePurchase(product = product) .onSuccess { purchaseResult -> when (purchaseResult) { is AdaptyPurchaseResult.Success -> { val profile = purchaseResult.profile // Purchase successful, profile updated } is AdaptyPurchaseResult.UserCanceled -> { // User canceled the purchase } is AdaptyPurchaseResult.Pending -> { // Purchase is pending (e.g., user will pay offline with cash) } } } .onError { error -> // Handle the error } } ``` ## Paso 3. Restaura las compras \{#step-3-restore-purchases\} Los stores de apps exigen que todas las apps con suscripciones ofrezcan una forma de restaurar las compras. Llama al método `restorePurchases` cuando el usuario toque el botón de restaurar. Esto sincronizará su historial de compras con Adapty y devolverá el perfil actualizado. ```kotlin showLineNumbers fun restorePurchases() { Adapty.restorePurchases() .onSuccess { profile -> // Restore successful, profile updated } .onError { error -> // Handle the error } } ``` ## Pasos siguientes \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Tu paywall está listo para mostrarse en la app. Prueba tus compras en el [sandbox de 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. Para ver cómo funciona esto en una implementación lista para producción, consulta el [AppViewModel.kt](https://github.com/adaptyteam/AdaptySDK-KMP/blob/main/example/composeMultiplatformApp/composeApp/src/commonMain/kotlin/com/adapty/exampleapp/AppViewModel.kt) en nuestra app de ejemplo, que demuestra la gestión de compras con manejo adecuado de errores y gestión de estados. A continuación, [comprueba si los usuarios han completado su compra](kmp-check-subscription-status) para determinar si mostrar el paywall o conceder acceso a las funciones de pago. --- # File: fetch-paywalls-and-products-kmp --- --- title: "Obtener paywalls y productos para paywalls de Remote Config en el SDK de Kotlin Multiplatform" description: "Obtén paywalls y productos en el SDK de Kotlin Multiplatform de Adapty para mejorar la monetización de los usuarios." --- Antes de mostrar Remote Config y paywalls personalizados, necesitas obtener la información sobre ellos. Ten en cuenta que este tema hace referencia a paywalls de Remote Config y personalizados. Para obtener orientación sobre cómo obtener paywalls creados con Paywall Builder, consulta [Obtener paywalls del Paywall Builder y su configuración](kmp-get-pb-paywalls). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a obtener paywalls y productos en tu app móvil (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en el placement](create-placement) en el Adapty Dashboard. 4. [Instala el SDK de Adapty](sdk-installation-kotlin-multiplatform) en tu app móvil. </details> ## Obtener información del paywall \{#fetch-paywall-information\} En Adapty, un [producto](product) es una combinación de productos tanto de App Store como de Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos en placements específicos de la app móvil. Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`. :::important **No hardcodees los IDs de productos.** El único ID que debes hardcodear es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe gestionar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana tres, muéstralos todos sin necesidad de cambios en el código. ::: ```kotlin showLineNumbers Adapty.getPaywall( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default, loadTimeout = 5.seconds ).onSuccess { paywall -> // the requested paywall }.onError { error -> // handle the error } ``` | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p> | | **fetchPolicy** | por defecto: `AdaptyPaywallFetchPolicy.Default` | <p>Por defecto, el SDK intentará cargar datos del servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que los usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad` para devolver datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero tendrán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza con regularidad, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](kmp-use-fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que la CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls mientras se asegura la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 s | <p>Este valor limita el tiempo de espera de este método. Si se alcanza el tiempo de espera, se devolverán datos en caché o el fallback local.</p><p></p><p>Ten en cuenta que en casos raros este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes solicitudes internamente.</p> | ¡No hardcodees los IDs de productos! Dado que los paywalls se configuran de forma remota, los productos disponibles, el número de productos y las ofertas especiales (como pruebas gratuitas) pueden cambiar con el tiempo. Asegúrate de que tu código gestione estos escenarios. Por ejemplo, si inicialmente obtienes 2 productos, tu app debería mostrar esos 2 productos. Sin embargo, si más tarde obtienes 3 productos, tu app debería mostrar los 3 sin requerir ningún cambio en el código. Lo único que debes hardcodear es el ID del placement. Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall/) con: una lista de IDs de productos, el identificador del paywall, Remote Config y varias otras propiedades. | ## Obtener productos \{#fetch-products\} Una vez que tienes el paywall, puedes consultar el array de productos que le corresponde: ```kotlin showLineNumbers Adapty.getPaywallProducts(paywall).onSuccess { products -> // the requested products }.onError { error -> // handle the error } ``` Parámetros de respuesta: | Parámetro | Descripción | | :-------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | Lista de objetos [`AdaptyPaywallProduct`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall-product/) con: identificador del producto, nombre del producto, precio, moneda, duración de la suscripción y varias otras propiedades. | Al implementar tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall-product/). A continuación se muestran las propiedades más usadas, pero consulta el documento enlazado para obtener información completa sobre todas las propiedades disponibles. | Propiedad | Descripción | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | Para mostrar el título del producto, usa `product.localizedTitle`. Ten en cuenta que la localización se basa en el país de la store seleccionado por el usuario, no en el idioma del dispositivo. | | **Price** | Para mostrar una versión localizada del precio, usa `product.price.localizedString`. Esta localización se basa en la información de idioma del dispositivo. También puedes acceder al precio como número usando `product.price.amount`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda correspondiente, usa `product.price.currencySymbol`. | | **Subscription Period** | Para mostrar el período (por ejemplo, semana, mes, año, etc.), usa `product.subscriptionDetails?.localizedSubscriptionPeriod`. Esta localización se basa en el idioma del dispositivo. Para obtener el período de suscripción de forma programática, usa `product.subscriptionDetails?.subscriptionPeriod`. Desde ahí puedes acceder al enum `unit` para obtener la duración (es decir, DAY, WEEK, MONTH, YEAR o UNKNOWN). El valor `numberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral, verías `MONTH` en la propiedad unit y `3` en la propiedad numberOfUnits. | | **Introductory Offer** | Para mostrar un distintivo u otro indicador de que una suscripción incluye una oferta introductoria, consulta la propiedad `product.subscriptionDetails?.introductoryOfferPhases`. Esta es una lista que puede contener hasta dos fases de descuento: la fase de prueba gratuita y la fase de precio introductorio. Dentro de cada objeto de fase encontrarás las siguientes propiedades útiles:<br/>• `paymentMode`: un enum con los valores `FREE_TRIAL`, `PAY_AS_YOU_GO`, `PAY_UPFRONT` y `UNKNOWN`. Las pruebas gratuitas serán del tipo `FREE_TRIAL`.<br/>• `price`: el precio con descuento como número. Para las pruebas gratuitas, busca `0` aquí.<br/>• `localizedNumberOfPeriods`: una cadena localizada con el idioma del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `3 days` en este campo.<br/>• `subscriptionPeriod`: como alternativa, puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que la sección anterior.<br/>• `localizedSubscriptionPeriod`: un período de suscripción formateado del descuento para el idioma del usuario. | ## Acelerar la obtención del paywall con el paywall de la audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\} Normalmente, los paywalls se obtienen casi al instante, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos donde tienes numerosas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseado. En estas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún paywall. Para solucionar esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener información del paywall](fetch-paywalls-and-products-kmp#fetch-paywall-information) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunas desventajas importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls diferentes para distintas versiones de la app (la actual y las futuras), puedes encontrar dificultades. Tendrás que diseñar paywalls que sean compatibles con la versión actual (legacy) o aceptar que los usuarios con la versión actual (legacy) puedan encontrarse con paywalls que no se renderizan correctamente. - **Pérdida de targeting**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes el targeting personalizado (incluido el basado en países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estas desventajas para beneficiarte de una obtención de paywalls más rápida, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, utiliza el `getPaywall` descrito [arriba](fetch-paywalls-and-products-kmp#fetch-paywall-information). ::: ```kotlin showLineNumbers Adapty.getPaywallForDefaultAudience( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default ).onSuccess { paywall -> // the requested paywall }.onError { error -> // handle the error } ``` | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p> | | **fetchPolicy** | por defecto: `AdaptyPaywallFetchPolicy.Default` | <p>Por defecto, el SDK intentará cargar datos del servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que los usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad` para devolver datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero tendrán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza con regularidad, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante una limpieza manual.</p> | --- # File: present-remote-config-paywalls-kmp --- --- title: "Renderizar un paywall diseñado con Remote Config en el SDK de Kotlin Multiplatform" description: "Descubre cómo presentar paywalls de Remote Config en el SDK de Adapty para Kotlin Multiplatform y personalizar la experiencia del usuario." --- Si has personalizado un paywall usando Remote Config, necesitarás implementar el renderizado en el código de tu aplicación móvil para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú decides qué incluir y cómo se ve tu paywall. Te proporcionamos un método para obtener la configuración remota, dándote la autonomía para mostrar tu paywall personalizado configurado mediante Remote Config. ## Obtener el Remote Config del paywall y presentarlo \{#get-paywall-remote-config-and-present-it\} Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores necesarios. ```kotlin showLineNumbers Adapty.getPaywall( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default, loadTimeout = 5.seconds ).onSuccess { paywall -> val headerText = paywall.remoteConfig?.dataMap?.get("header_text") as? String // use the remote config values }.onError { error -> // handle the error } ``` En este punto, una vez que hayas recibido todos los valores necesarios, es el momento de renderizarlos y ensamblarlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a las distintas pantallas y orientaciones de dispositivos móviles, ofreciendo una experiencia fluida y amigable en todos los dispositivos. :::warning Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls-kmp#track-paywall-view-events) como se describe a continuación, para que los análisis de Adapty puedan capturar información para los embudos y las pruebas A/B. ::: Cuando hayas terminado de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.makePurchase()` con el producto de tu paywall. Para más detalles sobre el método `.makePurchase()`, consulta [Realizar compras](kmp-making-purchases). Te recomendamos [crear un paywall de respaldo llamado fallback paywall](kmp-use-fallback-paywalls). Este respaldo se mostrará al usuario cuando no haya conexión a internet ni caché disponible, garantizando una experiencia fluida incluso en esas situaciones. ## Registrar eventos de visualización del paywall \{#track-paywall-view-events\} Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque los datos de compras se recopilan automáticamente, el registro de las visualizaciones de paywalls requiere tu intervención, ya que solo tú sabes cuándo un cliente ve un paywall. Para registrar un evento de visualización de paywall, simplemente llama a `.logShowPaywall(paywall)` y se reflejará en las métricas de tu paywall en los embudos y las pruebas A/B. :::important No es necesario llamar a `.logShowPaywall(paywall)` si estás mostrando paywalls creados en el [Paywall Builder](adapty-paywall-builder). ::: ```kotlin showLineNumbers Adapty.logShowPaywall(paywall = paywall) .onSuccess { // paywall view logged successfully } .onError { error -> // handle the error } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :------- |:-------------------------------------------------------------------------------------------------------| | **paywall** | obligatorio | Un objeto [`AdaptyPaywall`](https://kmp.adapty.io//////adapty/com.adapty.kmp.models/-adapty-paywall/). | --- # File: kmp-making-purchases --- --- title: "Realizar compras en app móvil con el SDK de Kotlin Multiplatform" description: "Guía para gestionar compras in-app y suscripciones con Adapty." --- Mostrar paywalls dentro de tu app es un paso fundamental para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, simplemente mostrar estos paywalls es suficiente para gestionar las compras solo si usas el [Paywall Builder](adapty-paywall-builder) para personalizar tus paywalls. Si no usas el Paywall Builder, debes utilizar un método aparte llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método actúa como la puerta de entrada para que los usuarios interactúen con los paywalls y procedan con sus transacciones. Si tu paywall tiene una oferta promocional activa para el producto que el usuario quiere comprar, Adapty la aplicará automáticamente en el momento de la compra. :::warning Ten en cuenta que la oferta introductoria solo se aplicará automáticamente si usas los paywalls configurados con el Paywall Builder. En otros casos, necesitarás [verificar la elegibilidad del usuario para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Saltarte este paso puede provocar que tu app sea rechazada durante la revisión. Además, podría dar lugar a cobrar el precio completo a usuarios que sí son elegibles para una oferta introductoria. ::: Asegúrate de haber [completado la configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras. ## Realizar una compra \{#make-purchase\} :::note **¿Usas el [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente; puedes saltarte este paso. **¿Buscas una guía paso a paso?** Consulta la [guía de inicio rápido](kmp-implement-paywalls-manually) para instrucciones de implementación completas con todo el contexto. ::: ```kotlin showLineNumbers Adapty.makePurchase(product = product).onSuccess { purchaseResult -> when (purchaseResult) { is AdaptyPurchaseResult.Success -> { val profile = purchaseResult.profile if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { // Grant access to the paid features } } is AdaptyPurchaseResult.UserCanceled -> { // Handle the case where the user canceled the purchase } is AdaptyPurchaseResult.Pending -> { // Handle deferred purchases (e.g., the user will pay offline with cash) } } }.onError { error -> // Handle the error } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :-------- |:----------------------------------------------------------------------------------------------------------------------------------------------| | **Product** | obligatorio | Un objeto [`AdaptyPaywallProduct`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall-product/) obtenido del paywall. | Parámetros de la respuesta: | Parámetro | Descripción | |---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** | <p>Si la solicitud fue exitosa, la respuesta contiene este objeto. Un objeto [AdaptyProfile](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-profile/) proporciona información completa sobre los niveles de acceso, suscripciones y compras únicas de un usuario dentro de la app.</p><p>Comprueba el estado del nivel de acceso para verificar si el usuario tiene el acceso requerido a la app.</p> | :::warning **Nota:** si aún usas una versión de StoreKit de Apple inferior a v2.0 y una versión del SDK de Adapty inferior a v.2.9.0, necesitas proporcionar el [secreto compartido de App Store de Apple](app-store-connection-configuration#step-5-enter-app-store-shared-secret). Este método está actualmente obsoleto según Apple. ::: ## Cambiar suscripción al realizar una compra \{#change-subscription-when-making-a-purchase\} Cuando un usuario elige una nueva suscripción en lugar de renovar la actual, el comportamiento depende del store. En Google Play, la suscripción no se actualiza automáticamente. Tendrás que gestionar el cambio en el código de tu app como se describe a continuación. Para reemplazar la suscripción por otra en Android, llama al método `.makePurchase()` con el parámetro adicional: ```kotlin showLineNumbers val subscriptionUpdateParams = AdaptyAndroidSubscriptionUpdateParameters( oldSubVendorProductId = "old_subscription_product_id", replacementMode = AdaptyAndroidSubscriptionUpdateReplacementMode.CHARGE_FULL_PRICE ) val purchaseParams = AdaptyPurchaseParameters.Builder() .setSubscriptionUpdateParams(subscriptionUpdateParams) .build() Adapty.makePurchase( product = product, parameters = purchaseParams ).onSuccess { purchaseResult -> when (purchaseResult) { is AdaptyPurchaseResult.Success -> { val profile = purchaseResult.profile // successful cross-grade } is AdaptyPurchaseResult.UserCanceled -> { // user canceled the purchase flow } is AdaptyPurchaseResult.Pending -> { // the purchase has not been finished yet, e.g. user will pay offline by cash } } }.onError { error -> // Handle the error } ``` Parámetro adicional de la solicitud: | Parámetro | Presencia | Descripción | |:---------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **parameters** | opcional | Un objeto [`AdaptyAndroidSubscriptionUpdateParameters`](https://kmp.adapty.io/////adapty/com.adapty.kmp.models/-adapty-android-subscription-update-parameters/) pasado a través de [`AdaptyPurchaseParameters`](https://kmp.adapty.io/adapty/com.adapty.kmp.models/-adapty-purchase-parameters/). | Puedes leer más sobre las suscripciones y los modos de reemplazo en la documentación de Google para desarrolladores: - [Acerca de los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-modes) - [Recomendaciones de Google para los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-recommendations) - Modo de reemplazo [`CHARGE_PRORATED_PRICE`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#CHARGE_PRORATED_PRICE()). Nota: este método solo está disponible para actualizaciones de suscripción. No se admiten cambios a un nivel inferior. - Modo de reemplazo [`DEFERRED`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#DEFERRED()). Nota: el cambio de suscripción real solo ocurrirá cuando finalice el período de facturación de la suscripción actual. ## Canjear códigos de oferta en iOS \{#redeem-offer-codes-in-ios\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Details> <summary>Sobre los códigos de oferta</summary> Los códigos de oferta te permiten dar descuentos o períodos de prueba gratuitos a usuarios concretos. A diferencia de las ofertas habituales, que se aplican de forma automática, los códigos de oferta se distribuyen fuera de la app — por email, redes sociales o materiales impresos. Los usuarios los canjean introduciendo el código en el App Store, accediendo a una URL de canje o a través de un diálogo dentro de la app. Para configurar códigos de oferta, abre una suscripción en App Store Connect y ve a su sección **Offer Codes**. Puedes crear [tres tipos](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes) de códigos de oferta: - **Free** — la suscripción es gratuita durante un período determinado y la siguiente renovación se cobra al precio completo. - **Pay as you go** — el usuario paga un precio reducido en cada ciclo de facturación durante un período determinado y, después, la suscripción se renueva al precio completo. - **Pay up front** — el usuario paga un precio único reducido por toda la duración de la oferta y, después, la suscripción se renueva al precio completo. No es necesario añadir los códigos de oferta a Adapty. Apple etiqueta cada transacción durante el período de la oferta con la categoría del código de oferta. Esto incluye el canje inicial y todas las renovaciones con descuento posteriores. Adapty detecta la etiqueta y registra cada transacción con la categoría de oferta `offer_code`. Cuando termina el período de oferta y la suscripción se renueva al precio completo, la etiqueta deja de estar presente. Puedes filtrar los análisis por el tipo de oferta **Offer Code** en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds). #### Resolución de discrepancias en los ingresos \{#revenue-discrepancy-troubleshooting\} Si observas que una transacción con código de oferta aparece en Adapty al precio completo del producto en lugar del precio reducido, verifica lo siguiente en App Store Connect: - El código de oferta tiene los precios correctos configurados para todas las regiones donde los usuarios pueden canjearlo. - El precio de la oferta está configurado para el país o región específica del usuario. Apple envía el precio regional en la transacción. Si no hay ningún precio regional configurado para la oferta, Apple puede enviar el precio completo del producto. Puedes filtrar y verificar las transacciones con código de oferta en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds) mediante los filtros de tipo de oferta **Offer Code** y **Offer Discount Type**. #### Códigos promocionales heredados (obsoletos) \{#legacy-promo-codes-deprecated\} <Callout type="warning"> Apple dejó obsoletos los códigos promocionales para compras in-app en marzo de 2026. Los códigos de oferta los sustituyen con más funcionalidades: elegibilidad configurable, fechas de expiración y hasta 1 millón de códigos por trimestre. Si antes usabas códigos promocionales para compras in-app, migra a los códigos de oferta en App Store Connect. </Callout> Los códigos promocionales heredados (limitados a 100 por app y versión) daban acceso gratuito a una suscripción. A diferencia de los códigos de oferta, Apple no incluía información de descuento en las transacciones con código promocional — enviaba el precio completo del producto en el recibo. Por ello, Adapty registraba estas transacciones al precio completo, lo que generaba discrepancias entre los análisis de Adapty y App Store Connect. Si ves transacciones históricas al precio completo que deberían haber sido gratuitas, es probable que provengan de códigos promocionales heredados. Como estos códigos ya están obsoletos, migra a los códigos de oferta para un seguimiento preciso de los ingresos. </Details> Para mostrar la pantalla de canje de códigos en tu app: ```kotlin showLineNumbers Adapty.presentCodeRedemptionSheet() .onSuccess { // code redemption sheet presented successfully } .onError { error -> // handle the error } ``` :::danger Según nuestras observaciones, la pantalla de canje de códigos de oferta en algunas apps puede no funcionar de forma fiable. Recomendamos redirigir al usuario directamente a la App Store. Para hacerlo, debes abrir la URL con el siguiente formato: `https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}` ::: ## Gestionar planes de prepago (Android) \{#manage-prepaid-plans-android\} Si los usuarios de tu app pueden comprar [planes de prepago](https://developer.android.com/google/play/billing/subscriptions#prepaid-plans) (por ejemplo, comprar una suscripción no renovable por varios meses), puedes habilitar las [transacciones pendientes](https://developer.android.com/google/play/billing/subscriptions#pending) para planes de prepago. ```kotlin showLineNumbers Adapty.activate( AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withGoogleEnablePendingPrepaidPlans(true) .build() ).onSuccess { // successful activation }.onError { error -> // handle the error } ``` --- # File: kmp-restore-purchase --- --- title: "Restaurar compras en la app móvil con el SDK de Kotlin Multiplatform" description: "Aprende cómo restaurar compras en Adapty para garantizar una experiencia de usuario fluida." --- Restaurar compras es una función que permite a los usuarios recuperar el acceso a contenido comprado anteriormente, como suscripciones o compras in-app, sin que se les vuelva a cobrar. Esta función es especialmente útil para usuarios que pueden haber desinstalado y reinstalado la app, o que han cambiado a un nuevo dispositivo y quieren acceder a su contenido comprado previamente sin pagar de nuevo. :::note En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin necesidad de código adicional. Si ese es tu caso, puedes saltarte este paso. ::: Para restaurar una compra si no usas el [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`: ```kotlin showLineNumbers Adapty.restorePurchases().onSuccess { profile -> if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) { // successful access restore } }.onError { error -> // handle the error } ``` Parámetros de respuesta: | Parámetro | Descripción | |---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** | <p>Un objeto [`AdaptyProfile`](https://kmp.adapty.io//////adapty/com.adapty.kmp.models/-adapty-profile/). Este modelo contiene información sobre los niveles de acceso, suscripciones y compras no relacionadas con suscripciones.</p><p>Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app.</p> | :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: implement-observer-mode-kmp --- --- title: "Implementar el modo Observer en el SDK de Kotlin Multiplatform" description: "Implementa el modo Observer en Adapty para rastrear eventos de suscripción de usuarios en el SDK de Kotlin Multiplatform." --- Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analítica. Si esto cubre tus necesidades, solo tienes que: 1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. Sigue las instrucciones de configuración para [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform). 2. [Reportar transacciones](report-transactions-observer-mode-kmp) desde tu infraestructura de compras existente a Adapty. ## Configuración del modo Observer \{#observer-mode-setup\} Activa el modo Observer si gestionas las compras y el estado de las suscripciones por tu cuenta y usas Adapty para enviar eventos de suscripción y analíticas. :::important Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlo tú mismo. ::: ```kotlin showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withObserverMode(true) // default false .build() Adapty.activate(configuration = config) .onSuccess { Log.d("Adapty", "SDK initialised in observer mode") } .onError { error -> Log.e("Adapty", "Adapty init error: ${error.message}") } ``` Parámetros: | Parámetro | Descripción | | --------------------------- | ------------------------------------------------------------ | | observerMode | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor predeterminado es `false`. | ## Usar paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\} Si también quieres usar las paywalls y las funciones de pruebas A/B de Adapty, puedes hacerlo, pero requiere una configuración adicional en el modo Observer. Esto es lo que necesitas hacer además de los pasos anteriores: 1. Muestra los paywalls de la forma habitual para [paywalls con Remote Config](present-remote-config-paywalls-kmp). 3. [Asocia los paywalls](report-transactions-observer-mode-kmp) con las transacciones de compra. --- # File: report-transactions-observer-mode-kmp --- --- title: "Reportar transacciones en el modo Observer en el SDK de Kotlin Multiplatform" description: "Reporta transacciones de compra en el modo Observer de Adapty para obtener información sobre usuarios y seguimiento de ingresos en el SDK de Kotlin Multiplatform." --- En el modo Observer, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas reportar las transacciones desde tu app store. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` para reportar de forma explícita cada transacción y que Adapty pueda reconocerla. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra con el paywall que la originó, garantizando un análisis preciso del paywall. ```kotlin showLineNumbers Adapty.reportTransaction( transactionId = "your_transaction_id", variationId = paywall.variationId ).onSuccess { profile -> // Transaction reported successfully // profile contains updated user data }.onError { error -> // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | | --------------- | ---------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | transactionId | obligatorio | El ID de transacción de tu compra en el app store. Normalmente es el token de compra o el identificador de transacción devuelto por el store. | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://kmp.adapty.io//////adapty/com.adapty.kmp.models/-adapty-paywall/). | --- # File: kmp-troubleshoot-purchases --- --- title: "Solucionar problemas de compras en el SDK de Kotlin Multiplatform" description: "Solucionar problemas de compras en el SDK de Kotlin Multiplatform" --- Esta guía te ayuda a resolver problemas comunes al implementar compras manualmente en el SDK de Kotlin Multiplatform. ## makePurchase se llama correctamente, pero el perfil no se actualiza \{#makepurchase-is-called-successfully-but-the-profile-is-not-being-updated\} **Problema**: El método `makePurchase` se completa correctamente, pero el perfil del usuario y el estado de la suscripción no se actualizan en Adapty. **Causa**: Esto normalmente indica una configuración incompleta de Google Play Store. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## makePurchase se invoca dos veces \{#makepurchase-is-invoked-twice\} **Problema**: El método `makePurchase` se está llamando varias veces para la misma compra. **Causa**: Esto suele ocurrir cuando el flujo de compra se activa varias veces debido a problemas de gestión del estado de la interfaz o por interacciones rápidas del usuario. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## AdaptyError.cantMakePayments en el modo observador \{#adaptye-rror-cantmakepayments-in-observer-mode\} **Problema**: Estás recibiendo `AdaptyError.cantMakePayments` al usar `makePurchase` en el modo observador. **Causa**: En el modo observador, debes gestionar las compras por tu cuenta, no usar el método `makePurchase` de Adapty. **Solución**: Si usas `makePurchase` para las compras, desactiva el modo observador. Tienes que elegir entre usar `makePurchase` o gestionar las compras por tu cuenta en el modo observador. Consulta [Implementar el modo observador](implement-observer-mode-kmp) para más detalles. ## Error de Adapty: (code: 103, message: Play Market request failed on purchases updated: responseCode=3, debugMessage=Billing Unavailable, detail: null) \{#adapty-error-code-103-message-play-market-request-failed-on-purchases-updated-responsecode3-debugmessagebilling-unavailable-detail-null\} **Problema**: Estás recibiendo un error de facturación no disponible de Google Play Store. **Causa**: Este error no está relacionado con Adapty. Es un error de la biblioteca Google Play Billing que indica que la facturación no está disponible en el dispositivo. **Solución**: Este error no está relacionado con Adapty. Puedes consultarlo en la documentación de Play Store: [Handle BillingResult response codes](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) | Play Billing | Android Developers. ## No se encuentran makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\} **Problema**: Estás teniendo problemas porque no se encuentran los `makePurchasesCompletionHandlers`. **Causa**: Esto suele estar relacionado con problemas en las pruebas en sandbox. **Solución**: Crea un nuevo usuario de sandbox e inténtalo de nuevo. Esto suele resolver los problemas con los manejadores de finalización de compras en sandbox. --- # File: kmp-user --- --- title: "Users & access in Kotlin Multiplatform SDK" description: "Learn how to work with users and access levels in your Kotlin Multiplatform app with Adapty SDK." --- This page contains all guides for working with users and access levels in your Kotlin Multiplatform app. Choose the topic you need: - **[Identify users](kmp-identifying-users)** - Learn how to identify users in your app - **[Update user data](kmp-setting-user-attributes)** - Set user attributes and profile data - **[Listen for subscription status changes](kmp-listen-subscription-changes)** - Monitor subscription changes in real-time - **[Kids Mode](kids-mode-kmp)** - Implement Kids Mode for your app --- # File: kmp-identifying-users --- --- title: "Identificar usuarios en Kotlin Multiplatform SDK" description: "Identifica usuarios en Adapty para mejorar las experiencias de suscripción personalizadas." --- Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, deberías establecer tu propio Customer User ID. Puedes encontrar usuarios por su Customer User ID en la sección [Perfiles](profiles-crm) y usarlo en la [API del lado del servidor](getting-started-with-server-side-api), que se enviará a todas las integraciones. ### Configurar el ID de usuario del cliente durante la configuración \{#setting-customer-user-id-on-configuration\} Si tienes un ID de usuario durante la configuración, pásalo como parámetro `customerUserId` al método `.activate()`: ```kotlin showLineNumbers Adapty.activate( AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId("YOUR_USER_ID") .build() ).onSuccess { // successful activation }.onError { error -> // handle the error } } ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Configuración del ID de usuario tras la inicialización \{#setting-customer-user-id-after-configuration\} Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo más adelante en cualquier momento con el método `.identify()`. Los casos más habituales para usar este método son tras el registro o la autenticación, cuando el usuario pasa de ser anónimo a estar autenticado. ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID").onSuccess { // successful identify }.onError { error -> // handle the error } ``` Parámetros de la solicitud: - **Customer User ID** (obligatorio): un identificador de usuario de tipo string. :::warning Reenvío de datos significativos del usuario En algunos casos, como cuando un usuario vuelve a iniciar sesión en su cuenta, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si enviaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, deberás reenviar esos datos para el usuario identificado. También es importante tener en cuenta que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes. ::: ### Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\} Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`: ```kotlin showLineNumbers Adapty.logout().onSuccess { // successful logout }.onError { error -> // handle the error } ``` Después puedes iniciar sesión con el método `.identify()`. ## Asignar `appAccountToken` (iOS) \{#assign-appaccounttoken-ios\} [`iosAppAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un **UUID** que te permite vincular las transacciones del App Store con la identidad interna de tus usuarios. StoreKit asocia este token con cada transacción, de modo que tu backend puede relacionar los datos del App Store con tus usuarios. Usa un UUID estable generado por usuario y reutilízalo para la misma cuenta en todos los dispositivos. Esto garantiza que las compras y las notificaciones del App Store queden correctamente vinculadas. Puedes establecer el token de dos formas: durante la activación del SDK o al identificar al usuario. :::important Siempre debes pasar `iosAppAccountToken` junto con `customerUserId`. Si solo pasas el token, no se incluirá en la transacción. ::: ```kotlin showLineNumbers // Durante la configuración: Adapty.activate( AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId( id = "YOUR_USER_ID", iosAppAccountToken = "YOUR_IOS_APP_ACCOUNT_TOKEN" ) .build() ).onSuccess { // activación exitosa }.onError { error -> // manejar el error } // O al identificar usuarios Adapty.identify( customerUserId = "YOUR_USER_ID", iosAppAccountToken = "YOUR_IOS_APP_ACCOUNT_TOKEN" ).onSuccess { // identificación exitosa }.onError { error -> // manejar el error } ``` ## Establecer IDs de cuenta ofuscados (Android) \{#set-obfuscated-account-ids-android\} Google Play requiere IDs de cuenta ofuscados en ciertos casos de uso para mejorar la privacidad y seguridad del usuario. Estos IDs ayudan a Google Play a identificar las compras manteniendo el anonimato de la información del usuario, algo especialmente importante para la prevención de fraudes y los análisis. Es posible que necesites configurar estos IDs si tu aplicación maneja datos sensibles de usuarios o si debes cumplir con regulaciones de privacidad específicas. Los IDs ofuscados permiten a Google Play rastrear las compras sin exponer los identificadores reales de los usuarios. :::important Siempre debes pasar `androidObfuscatedAccountId` junto con `customerUserId`. Si solo pasas el ID de cuenta ofuscado, no se incluirá en la transacción. ::: ```kotlin showLineNumbers // Durante la configuración: Adapty.activate( AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId( id = "YOUR_USER_ID", androidObfuscatedAccountId = "YOUR_OBFUSCATED_ACCOUNT_ID" ) .build() ).onSuccess { // activación correcta }.onError { error -> // gestionar el error } // O al identificar usuarios Adapty.identify( customerUserId = "YOUR_USER_ID", androidObfuscatedAccountId = "YOUR_OBFUSCATED_ACCOUNT_ID" ).onSuccess { // identificación correcta }.onError { error -> // gestionar el error } ``` ## Detectar usuarios en varios dispositivos \{#detect-users-across-devices\} --- no_index: true --- Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`. Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID. **Lo que Adapty puede detectar entre dispositivos** | Tu configuración | Lo que Adapty detecta | Lo que debes hacer | | --- | --- | --- | | Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. | | Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. | | Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. | | Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. | **Restaurar compras en un dispositivo nuevo** Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK. No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado. --- # File: kmp-setting-user-attributes --- --- title: "Establecer atributos de usuario en el SDK de Kotlin Multiplatform" description: "Aprende cómo establecer atributos de usuario en Adapty para mejorar la segmentación de audiencias." --- Puedes establecer atributos opcionales como el correo electrónico, el número de teléfono, etc., en el usuario de tu aplicación. Luego puedes usar estos atributos para crear [segmentos](segments) de usuarios o simplemente verlos en el CRM. ### Establecer atributos de usuario \{#setting-user-attributes\} Para establecer atributos de usuario, llama al método `.updateProfile()`: ```kotlin showLineNumbers val builder = AdaptyProfileParameters.Builder() .withEmail("email@email.com") .withPhoneNumber("+18888888888") .withFirstName("John") .withLastName("Appleseed") .withGender(AdaptyProfile.Gender.FEMALE) .withBirthday(AdaptyProfile.Date(1970, 1, 3)) Adapty.updateProfile(builder.build()) .onSuccess { // profile updated successfully } .onError { error -> // handle the error } ``` Ten en cuenta que los atributos que hayas establecido previamente con el método `updateProfile` no se restablecerán. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Lista de claves permitidas \{#the-allowed-keys-list\} Las claves permitidas `<Key>` de `AdaptyProfileParameters.Builder` y sus valores `<Value>` se muestran a continuación: | Clave | Valor | |---|-----| | <p>email</p><p>phoneNumber</p><p>firstName</p><p>lastName</p> | String | | gender | Enum, los valores permitidos son: `AdaptyProfile.Gender.FEMALE`, `AdaptyProfile.Gender.MALE`, `AdaptyProfile.Gender.OTHER` | | birthday | Date | ### Atributos personalizados de usuario \{#custom-user-attributes\} Puedes definir tus propios atributos personalizados. Estos suelen estar relacionados con el uso de tu aplicación. Por ejemplo, en aplicaciones de fitness podrían ser el número de ejercicios por semana; en aplicaciones de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes usarlos en segmentos para crear paywalls y ofertas dirigidas, y también en análisis para determinar qué métricas de producto influyen más en los ingresos. ```kotlin showLineNumbers val builder = AdaptyProfileParameters.Builder() builder.withCustomAttribute("key1", "value1") ``` Para eliminar una clave existente, usa el método `.withRemovedCustomAttribute()`: ```kotlin showLineNumbers val builder = AdaptyProfileParameters.Builder() builder.withRemovedCustomAttribute("key2") ``` En ocasiones necesitas saber qué atributos personalizados ya se han establecido anteriormente. Para ello, utiliza el campo `customAttributes` del objeto `AdaptyProfile`. :::warning Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor pueden haber cambiado desde la última sincronización. ::: ### Límites \{#limits\} - Hasta 30 atributos personalizados por usuario - Los nombres de clave tienen un máximo de 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.` - El valor puede ser una cadena de texto o un número flotante con un máximo de 50 caracteres. --- # File: kmp-listen-subscription-changes --- --- title: "Verificar el estado de la suscripción en el SDK de Kotlin Multiplatform" description: "Rastrea y gestiona el estado de la suscripción de usuarios en Adapty para mejorar la retención de clientes en tu app de Kotlin Multiplatform." --- Con Adapty, hacer seguimiento del estado de la suscripción es muy sencillo. No tienes que insertar manualmente IDs de productos en tu código. En su lugar, puedes confirmar fácilmente el estado de la suscripción de un usuario comprobando si tiene un [nivel de acceso](access-level) activo. Antes de empezar a verificar el estado de la suscripción, configura las [notificaciones de desarrollador en tiempo real (RTDN)](enable-real-time-developer-notifications-rtdn). ## Nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptyprofile-object\} Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-profile/). Te recomendamos obtener el perfil cuando tu app se inicie, por ejemplo al [identificar un usuario](android-identifying-users#setting-customer-user-id-on-configuration), y actualizarlo cada vez que se produzcan cambios. Así podrás usar el objeto de perfil sin tener que solicitarlo repetidamente. Para recibir notificaciones sobre actualizaciones del perfil, escucha los cambios tal como se describe en la sección [Escuchar actualizaciones del perfil, incluidos los niveles de acceso](android-listen-subscription-changes) a continuación. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\} Para obtener el nivel de acceso desde el servidor, usa el método `.getProfile()`: ```kotlin showLineNumbers Adapty.getProfile().onSuccess { profile -> // check the access }.onError { error -> // handle the error } ``` Parámetros de respuesta: | Parámetro | Descripción | | --------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Profile | <p>Un objeto [AdaptyProfile](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-profile/). En general, solo tienes que comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app.</p><p></p><p>El método `.getProfile` proporciona el resultado más actualizado, ya que siempre intenta consultar la API. Si por alguna razón (por ejemplo, sin conexión a internet), el SDK de Adapty no puede obtener información del servidor, se devolverán los datos de la caché. También es importante tener en cuenta que el SDK de Adapty actualiza la caché de `AdaptyProfile` periódicamente para mantener esta información lo más actualizada posible.</p> | El método `.getProfile()` te proporciona el perfil de usuario desde el que puedes obtener el estado del nivel de acceso. Puedes tener varios niveles de acceso por app. Por ejemplo, si tienes una app de noticias y vendes suscripciones a distintos temas de forma independiente, puedes crear niveles de acceso "sports" y "science". Pero la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes usar simplemente el nivel de acceso predeterminado "premium". A continuación se muestra un ejemplo para comprobar el nivel de acceso predeterminado "premium": ```kotlin showLineNumbers Adapty.getProfile().onSuccess { profile -> if (profile.accessLevels["premium"]?.isActive == true) { // grant access to premium features } }.onError { error -> // handle the error } ``` ### Escuchar actualizaciones del estado de la suscripción \{#listening-for-subscription-status-updates\} Cada vez que cambia la suscripción de un usuario, Adapty lanza un evento. Para recibir mensajes de Adapty, necesitas realizar una configuración adicional: ```kotlin showLineNumbers Adapty.setOnProfileUpdatedListener { profile -> // handle any changes to subscription state } ``` Adapty también lanza un evento al inicio de la aplicación. En ese caso, se pasará el estado de la suscripción almacenado en caché. ### Caché del estado de la suscripción \{#subscription-status-cache\} La caché implementada en el SDK de Adapty almacena el estado de la suscripción del perfil. Esto significa que, aunque el servidor no esté disponible, se puede acceder a los datos en caché para obtener información sobre el estado de la suscripción del perfil. Sin embargo, es importante tener en cuenta que no es posible solicitar datos directamente desde la caché. El SDK consulta periódicamente el servidor cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si hay alguna modificación, como nuevas transacciones u otras actualizaciones, se enviarán a los datos en caché para mantenerlos sincronizados con el servidor. --- # File: kmp-deal-with-att --- --- title: "Gestionar ATT en el SDK de Kotlin Multiplatform" description: "Comienza con Adapty en Kotlin Multiplatform para simplificar la configuración y gestión de suscripciones." --- Si tu aplicación usa el framework AppTrackingTransparency y muestra al usuario una solicitud de autorización de seguimiento, debes enviar el [estado de autorización](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/) a Adapty. ```kotlin showLineNumbers val profileParameters = AdaptyProfileParameters.Builder() .withAttStatus(3) // 3 = ATTrackingManagerAuthorizationStatusAuthorized .build() Adapty.updateProfile(profileParameters) .onSuccess { // ATT status updated successfully } .onError { error -> // handle AdaptyError } ``` :::warning Te recomendamos encarecidamente que envíes este valor lo antes posible cuando cambie; solo así los datos se transmitirán a tiempo a las integraciones que hayas configurado. ::: --- # File: kids-mode-kmp --- --- title: "Modo para Niños en el SDK de Kotlin Multiplatform" description: "Activa fácilmente el Modo para Niños para cumplir con las políticas de Google. Sin GAID ni datos publicitarios recopilados en el SDK de Kotlin Multiplatform." --- Si tu aplicación de Kotlin Multiplatform está destinada a niños, debes seguir las políticas de [Google](https://support.google.com/googleplay/android-developer/answer/9893335). Si usas el SDK de Adapty, unos pocos pasos sencillos te ayudarán a configurarlo para cumplir con estas políticas y superar las revisiones de la app store. ## ¿Qué se requiere? \{#whats-required\} Necesitas configurar el SDK de Adapty para deshabilitar la recopilación de: - [IDFA (Identifier for Advertisers)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers) (iOS) - [Android Advertising ID (AAID/GAID)](https://support.google.com/googleplay/android-developer/answer/6048248) (Android) - [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) Además, te recomendamos usar el ID de usuario del cliente con cuidado. Un ID de usuario con el formato `<Nombre.Apellido>` se considerará definitivamente como recopilación de datos personales, al igual que el uso del correo electrónico. Para el Modo para Niños, la mejor práctica es usar identificadores aleatorios o anonimizados (por ejemplo, IDs hasheados o UUIDs generados por el dispositivo) para garantizar el cumplimiento. ## Habilitar el Modo para Niños \{#enabling-kids-mode\} ### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\} En el Adapty Dashboard, debes deshabilitar la recopilación de direcciones IP. Para ello, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** dentro de **Collect users' IP address**. ### Cambios en el código de tu aplicación móvil \{#updates-in-your-mobile-app-code\} Para cumplir con las políticas, debes deshabilitar la recopilación del Android Advertising ID (AAID/GAID) y la dirección IP al inicializar el SDK de Adapty: ```kotlin showLineNumbers override fun onCreate() { super.onCreate() val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") // highlight-start .withGoogleAdvertisingIdCollectionDisabled(true) // set to `true` .withIpAddressCollectionDisabled(true) // set to `true` // highlight-end .build() Adapty.activate(configuration = config) .onSuccess { Log.d("Adapty", "SDK initialised with privacy settings") } .onError { error -> Log.e("Adapty", "Adapty init error: ${error.message}") } } ``` --- # File: kmp-onboardings --- --- title: "Onboardings in Kotlin Multiplatform SDK" description: "Learn how to work with onboardings in your Kotlin Multiplatform app with Adapty SDK." --- <CustomDocCardList /> --- # File: kmp-get-onboardings --- --- title: "Obtener onboardings en el SDK de Kotlin Multiplatform" description: "Aprende cómo recuperar onboardings en Adapty para Kotlin Multiplatform." --- Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el editor en el Adapty Dashboard, puedes mostrarlo en tu app de Kotlin Multiplatform. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, como se describe a continuación. Antes de comenzar, asegúrate de que: 1. Has instalado el [SDK de Adapty para Kotlin Multiplatform](sdk-installation-kotlin-multiplatform) versión 3.15.0 o superior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). ## Obtener el onboarding \{#fetch-onboarding\} Cuando creas un [onboarding](onboardings) con nuestro editor sin código, se almacena como un contenedor con configuración que tu app necesita obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a cuestionarios o entradas de formularios). El contenedor también registra automáticamente los eventos de analíticas, por lo que no necesitas implementar un seguimiento de vistas por separado. Para obtener el mejor rendimiento, solicita la configuración del onboarding con antelación para que las imágenes tengan tiempo suficiente de descargarse antes de mostrárselas a los usuarios. Para obtener un onboarding, usa el método `getOnboarding`: ```kotlin showLineNumbers Adapty.getOnboarding( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default, loadTimeout = 5.seconds ).onSuccess { paywall -> // the requested paywall }.onError { error -> // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.<p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En ese caso, puede que los usuarios no reciban los datos más recientes, pero tendrán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza con regularidad, por lo que es seguro usarla durante la sesión para evitar peticiones de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización periódica descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings y asegurar la fiabilidad incluso cuando la conexión a internet es limitada.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local.</p><p>Ten en cuenta que en casos excepcionales este método puede agotar el tiempo de espera un poco después de lo especificado en `loadTimeout`, ya que la operación puede estar compuesta de distintas solicitudes internamente.</p> | Parámetros de respuesta: | Parámetro | Descripción | |:----------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | Un objeto [`AdaptyOnboarding`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-onboarding/) con: el identificador y la configuración del onboarding, el Remote Config y otras propiedades. | ## Acelerar la obtención del onboarding con el onboarding de audiencia predeterminada \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} Normalmente, los onboardings se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, si tienes muchas audiencias y onboardings y tus usuarios tienen una conexión a internet débil, la obtención de un onboarding puede tardar más de lo deseado. En esas situaciones, puede que quieras mostrar un onboarding predeterminado para garantizar una experiencia fluida en lugar de no mostrar ninguno. Para esto, puedes usar el método `getOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, tal como se detalla en la sección [Obtener el onboarding](#fetch-onboarding) anterior. :::warning Considera usar `getOnboarding` en lugar de `getOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: Puede generar problemas al mantener varias versiones de la app, lo que exige diseños retrocompatibles o asumir que versiones más antiguas podrían mostrarse incorrectamente. - **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación por país, atribución o atributos personalizados. Si la mayor velocidad de obtención compensa estos inconvenientes en tu caso de uso, utiliza `getOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getOnboarding` tal como se describe [arriba](#fetch-onboarding). ::: ```kotlin showLineNumbers Adapty.getOnboardingForDefaultAudience( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default, ).onSuccess { paywall -> // the requested paywall }.onError { error -> // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.<br/>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil. | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En ese caso, puede que los usuarios no reciban los datos más recientes, pero tendrán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza con regularidad, por lo que es seguro usarla durante la sesión para evitar peticiones de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización periódica descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings y asegurar la fiabilidad incluso cuando la conexión a internet es limitada.</p> | --- # File: kmp-present-onboardings --- --- title: "Presentar onboardings en Kotlin Multiplatform SDK" description: "Aprende a presentar onboardings de forma efectiva para impulsar más conversiones." --- Si has personalizado un onboarding con el builder, no necesitas preocuparte por renderizarlo en el código de tu app Kotlin Multiplatform para mostrárselo al usuario. Ese onboarding incluye tanto lo que debe mostrarse como la forma en que debe hacerlo. Antes de empezar, asegúrate de que: 1. Tienes instalado [Adapty Kotlin Multiplatform SDK](sdk-installation-kotlin-multiplatform) 3.16.1 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). El SDK de Adapty para Kotlin Multiplatform ofrece dos formas de presentar onboardings: - **Con Compose Multiplatform** - **Sin Compose Multiplatform** ## Con Compose Multiplatform \{#with-compose-multiplatform\} Para mostrar un onboarding, usa el método `view.present()` sobre el `view` creado por el método `createOnboardingView`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el onboarding de nuevo, llama a `createOnboardingView` otra vez para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo puede producir un error. ::: ```kotlin showLineNumbers title="Kotlin Multiplatform" viewModelScope.launch { AdaptyUI.createOnboardingView(onboarding = onboarding).onSuccess { view -> view.present() }.onError { error -> // handle the error } } ``` ### Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el onboarding en iOS pasando el parámetro `iosPresentationStyle` al método `present()`. El parámetro acepta los valores `AdaptyUIIOSPresentationStyle.FULLSCREEN` (predeterminado) o `AdaptyUIIOSPresentationStyle.PAGESHEET`. ```kotlin showLineNumbers viewModelScope.launch { val view = AdaptyUI.createOnboardingView(onboarding = onboarding).getOrNull() view?.present(iosPresentationStyle = AdaptyUIIOSPresentationStyle.PAGESHEET) } ``` ### Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\} Por defecto, los enlaces de los onboardings se abren en un navegador in-app. Esto ofrece una experiencia fluida al mostrar las páginas web dentro de tu aplicación, sin que el usuario tenga que cambiar de app. Si prefieres que los enlaces se abran en un navegador externo, puedes personalizar este comportamiento estableciendo el parámetro `externalUrlsPresentation` en `AdaptyWebPresentation.EXTERNAL_BROWSER`: ```kotlin showLineNumbers viewModelScope.launch { AdaptyUI.createOnboardingView( onboarding = onboarding, externalUrlsPresentation = AdaptyWebPresentation.EXTERNAL_BROWSER // default – IN_APP_BROWSER ).onSuccess { view -> view.present() }.onError { error -> // handle the error } } ``` ## Sin Compose Multiplatform \{#without-compose-multiplatform\} :::note `createNativeOnboardingView` forma parte del módulo principal `io.adapty:adapty-kmp`. Si tu proyecto no usa Compose Multiplatform, no necesitas la dependencia `io.adapty:adapty-kmp-ui`. ::: Para integrar un onboarding sin Compose Multiplatform, llama a `createNativeOnboardingView`. Devuelve un `AdaptyNativeOnboardingView` que añades a tu layout: <Tabs> <TabItem value="android" label="Android"> ```kotlin showLineNumbers title="Kotlin Multiplatform (Android)" val nativeView = AdaptyUI.createNativeOnboardingView( context = context, viewModelStoreOwner = activity, onboarding = onboarding, observer = myOnboardingObserver, ) // Embed in your Compose layout: AndroidView( factory = { nativeView.view }, modifier = Modifier.fillMaxSize() ) ``` </TabItem> <TabItem value="ios" label="iOS"> Dado que los métodos predeterminados de la interfaz KMP se convierten en `@required` en Swift, no puedes implementar `AdaptyUIOnboardingsEventsObserver` directamente desde Swift. Primero declara una clase base abierta en `iosMain`: ```kotlin showLineNumbers title="iosMain (Kotlin)" open class BaseOnboardingObserver : AdaptyUIOnboardingsEventsObserver ``` Luego crea una subclase en Swift, sobreescribiendo solo lo que necesites: ```swift showLineNumbers title="Swift" class MyOnboardingObserver: BaseOnboardingObserver { override func onboardingViewOnCloseAction( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta, actionId: String ) { // remove nativeView from your view hierarchy } } let nativeView = AdaptyUI.shared.createNativeOnboardingView( onboarding: onboarding, observer: MyOnboardingObserver() ) // nativeView.viewController is a UIViewController. // Add it to your SwiftUI view or UIKit hierarchy. ``` </TabItem> </Tabs> ### Liberar la vista \{#dispose-the-view\} Llama a `dispose()` cuando elimines la vista de tu layout. Esto cancela el registro del listener de eventos y libera los recursos internos. ```kotlin showLineNumbers title="Kotlin Multiplatform" nativeView.dispose() ``` --- # File: kmp-handling-onboarding-events --- --- title: "Gestionar eventos del onboarding en el SDK de Kotlin Multiplatform" description: "Gestiona eventos relacionados con el onboarding en Kotlin Multiplatform usando Adapty." --- Antes de empezar, asegúrate de que: 1. Has instalado el [SDK de Adapty para Kotlin Multiplatform](sdk-installation-kotlin-multiplatform) 3.15.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). Los onboardings configurados con el builder generan eventos a los que tu app puede responder. A continuación aprenderás cómo hacerlo. ## Configurar el observador de eventos del onboarding \{#set-up-the-onboarding-event-observer\} Para gestionar los eventos del onboarding, necesitas implementar la interfaz `AdaptyUIOnboardingsEventsObserver` y configurarla con `AdaptyUI.setOnboardingsEventsObserver()`. Esto debe hacerse en una etapa temprana del ciclo de vida de tu app, normalmente en tu actividad principal o en la inicialización de la app. ```kotlin // In your app initialization AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` ## Acciones personalizadas \{#custom-actions\} En el builder, puedes añadir una acción **custom** a un botón y asignarle un ID. Luego puedes usar ese ID en tu código y gestionarlo como una acción personalizada. <img src={require('./img/ios-events-1.webp').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Por ejemplo, si un usuario pulsa un botón personalizado como **Login** o **Allow notifications**, se activará el método delegado `onCustomAction` con el ID de acción definido en el builder. Puedes crear tus propios IDs, como "allowNotifications". ```kotlin class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver { override fun onboardingViewOnCustomAction( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta, actionId: String ) { when (actionId) { "openPaywall" -> { // Display paywall from onboarding // You would typically fetch and present a new paywall here mainUiScope.launch { // Example: Get paywall by placement ID // val paywallResult = Adapty.getPaywall("your_placement_id") // paywallResult.onSuccess { paywall -> // val paywallViewResult = AdaptyUI.createPaywallView(paywall) // paywallViewResult.onSuccess { paywallView -> // paywallView.present() // } // } } } "allowNotifications" -> { // Handle notification permissions } else -> { // Handle other custom actions } } } } // Set up the observer AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` <Details> <summary>Ejemplo de evento (clic para expandir)</summary> ```json { "actionId": "allowNotifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ``` </Details> ## Cerrar el onboarding \{#closing-onboarding\} El onboarding se considera cerrado cuando un usuario pulsa un botón con la acción **Close** asignada. Debes gestionar qué ocurre cuando el usuario cierra el onboarding. Por ejemplo: :::important Necesitas gestionar qué ocurre cuando un usuario cierra el onboarding. Por ejemplo, debes dejar de mostrar el propio onboarding. ::: Si estás usando [`createNativeOnboardingView`](kmp-present-onboardings#without-compose-multiplatform), `view.isStandaloneView` es `false` — la implementación por defecto no llama a `view.dismiss()`. En su lugar, elimina la vista de tu layout y llama a `dispose()` en este callback. ```kotlin class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver { override fun onboardingViewOnCloseAction( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta, actionId: String ) { // Dismiss the onboarding screen mainUiScope.launch { view.dismiss() } // Additional cleanup or navigation logic can be added here // For example, navigate back or show main app content } } // Set up the observer AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` <Details> <summary>Ejemplo de evento (clic para expandir)</summary> ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> ## Abrir un paywall \{#opening-a-paywall\} :::tip Gestiona este evento para abrir un paywall si quieres mostrarlo dentro del onboarding. Si prefieres abrirlo una vez cerrado el onboarding, hay una forma más directa: gestiona [`onboardingViewOnCloseAction`](#closing-onboarding) y abre el paywall sin depender de los datos del evento. ::: Si un usuario hace clic en un botón que abre un paywall, recibirás el ID de acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más fluida de trabajar con paywalls dentro de onboardings es hacer que el ID de acción coincida con el ID de placement del paywall. Así puedes usar el ID del placement para obtener y abrir el paywall directamente: ```kotlin class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver { override fun onboardingViewOnPaywallAction( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta, actionId: String ) { // Get the paywall using the placement ID from the action mainUiScope.launch { val paywallResult = Adapty.getPaywall(placementId = actionId) paywallResult.onSuccess { paywall -> val paywallViewResult = AdaptyUI.createPaywallView(paywall) paywallViewResult.onSuccess { paywallView -> paywallView.present() }.onError { error -> // handle the error } }.onError { error -> // handle the error } } } } // Set up the observer AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` <Details> <summary>Ejemplo de evento (clic para expandir)</summary> ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ``` </Details> ## Finalizar la carga del onboarding \{#finishing-loading-onboarding\} Cuando el onboarding termina de cargarse, se invocará este método: ```kotlin class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver { override fun onboardingViewDidFinishLoading( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta ) { // Handle loading completion // You can add any initialization logic here } } // Set up the observer AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` <Details> <summary>Ejemplo de evento (clic para expandir)</summary> ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ``` </Details> ## Eventos de navegación \{#navigation-events\} El método `onboardingViewOnAnalyticsEvent` se llama cuando ocurren distintos eventos de análisis durante el flujo del onboarding. El objeto `event` puede ser uno de los siguientes tipos: |Tipo | Descripción | |------------|-------------| | `AdaptyOnboardingsAnalyticsEventOnboardingStarted` | Cuando el onboarding ha sido cargado | | `AdaptyOnboardingsAnalyticsEventScreenPresented` | Cuando se muestra cualquier pantalla | | `AdaptyOnboardingsAnalyticsEventScreenCompleted` | Cuando se completa una pantalla. Incluye `elementId` opcional (identificador del elemento completado) y `reply` opcional (respuesta del usuario). Se activa cuando los usuarios realizan cualquier acción para salir de la pantalla. | | `AdaptyOnboardingsAnalyticsEventSecondScreenPresented` | Cuando se muestra la segunda pantalla | | `AdaptyOnboardingsAnalyticsEventUserEmailCollected` | Se activa cuando se recoge el correo electrónico del usuario mediante el campo de entrada | | `AdaptyOnboardingsAnalyticsEventOnboardingCompleted` | Se activa cuando un usuario llega a una pantalla con el ID `final`. Si necesitas este evento, asigna el ID `final` a la última pantalla. | | `AdaptyOnboardingsAnalyticsEventUnknown` | Para cualquier tipo de evento no reconocido. Incluye `name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) | Cada evento incluye información `meta` con los siguientes campos: | Campo | Descripción | |------------|-------------| | `onboardingId` | Identificador único del flujo de onboarding | | `screenClientId` | Identificador de la pantalla actual | | `screenIndex` | Posición de la pantalla actual en el flujo | | `screensTotal` | Número total de pantallas en el flujo | Aquí tienes un ejemplo de cómo usar los eventos de análisis para el seguimiento: ```kotlin class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver { override fun onboardingViewOnAnalyticsEvent( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta, event: AdaptyOnboardingsAnalyticsEvent ) { when (event) { is AdaptyOnboardingsAnalyticsEventOnboardingStarted -> { // Track onboarding start trackEvent("onboarding_started", event.meta) } is AdaptyOnboardingsAnalyticsEventScreenPresented -> { // Track screen presentation trackEvent("screen_presented", event.meta) } is AdaptyOnboardingsAnalyticsEventScreenCompleted -> { // Track screen completion with user response trackEvent("screen_completed", event.meta, event.elementId, event.reply) } is AdaptyOnboardingsAnalyticsEventOnboardingCompleted -> { // Track successful onboarding completion trackEvent("onboarding_completed", event.meta) } is AdaptyOnboardingsAnalyticsEventUnknown -> { // Handle unknown events trackEvent(event.name, event.meta) } // Handle other cases as needed } } private fun trackEvent(eventName: String, meta: AdaptyUIOnboardingMeta, elementId: String? = null, reply: String? = null) { // Implement your analytics tracking here // For example, send to your analytics service } } // Set up the observer AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` <Details> <summary>Ejemplos de eventos (clic para expandir)</summary> ```javascript // OnboardingStarted { "meta": { "onboardingId": "onboarding_123", "screenClientId": "welcome_screen", "screenIndex": 0, "screensTotal": 4 } } // ScreenPresented { "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 4 } } // ScreenCompleted { "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 1, "screensTotal": 4 }, "elementId": "profile_form", "reply": "success" } // SecondScreenPresented { "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 1, "screensTotal": 4 } } // UserEmailCollected { "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 1, "screensTotal": 4 } } // OnboardingCompleted { "meta": { "onboardingId": "onboarding_123", "screenClientId": "final_screen", "screenIndex": 3, "screensTotal": 4 } } ``` </Details> --- # File: kmp-onboarding-input --- --- title: "Procesar datos de onboardings en el SDK de Kotlin Multiplatform" description: "Guarda y usa datos de onboardings en tu app de Kotlin Multiplatform con el SDK de Adapty." --- Cuando tus usuarios responden a una pregunta de un cuestionario o introducen datos en un campo de texto, se invocará el método `onboardingViewOnStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código. Por ejemplo: ```kotlin class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver { override fun onboardingViewOnStateUpdatedAction( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta, elementId: String, params: AdaptyOnboardingsStateUpdatedParams ) { // Store user preferences or responses when (params) { is AdaptyOnboardingsSelectParams -> { // Handle single selection val id = params.id val value = params.value val label = params.label AppLogger.d("Selected option: $label (id: $id, value: $value)") } is AdaptyOnboardingsMultiSelectParams -> { // Handle multiple selections } is AdaptyOnboardingsInputParams -> { // Handle text input } is AdaptyOnboardingsDatePickerParams -> { // Handle date selection } } } } // Set up the observer AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` <Details> <summary>Ejemplos de datos guardados (el formato puede variar según tu implementación)</summary> ```javascript // Example of a saved select action { "id": "onboarding_on_state_updated_action", "view": { /* AdaptyUI.OnboardingView object */ }, "meta": { "onboarding_id": "onboarding_123", "screen_cid": "preferences_screen", "screen_index": 1, "total_screens": 3 }, "action": { "element_id": "preference_selector", "element_type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Example of a saved multi-select action { "id": "onboarding_on_state_updated_action", "view": { /* AdaptyUI.OnboardingView object */ }, "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 3 }, "action": { "element_id": "interests_selector", "element_type": "multi_select", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Example of a saved input action { "id": "onboarding_on_state_updated_action", "view": { /* AdaptyUI.OnboardingView object */ }, "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 0, "total_screens": 3 }, "action": { "element_id": "name_input", "element_type": "input", "value": { "type": "text", "value": "John Doe" } } } // Example of a saved date picker action { "id": "onboarding_on_state_updated_action", "view": { /* AdaptyUI.OnboardingView object */ }, "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 0, "total_screens": 3 }, "action": { "element_id": "birthday_picker", "element_type": "date_picker", "value": { "day": 15, "month": 6, "year": 1990 } } } ``` </Details> ## Casos de uso \{#use-cases\} ### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\} Si quieres vincular de inmediato los datos introducidos con el perfil del usuario y evitar pedirle la misma información dos veces, debes [actualizar el perfil del usuario](kmp-setting-user-attributes) con los datos del campo al gestionar la acción. Por ejemplo, si pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name` y quieres establecer el valor de ese campo como el nombre de pila del usuario, y además les pides que introduzcan su correo electrónico en el campo `email`, el código de tu app podría verse así: ```kotlin class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver { override fun onboardingViewOnStateUpdatedAction( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta, elementId: String, params: AdaptyOnboardingsStateUpdatedParams ) { // Store user preferences or responses when (params) { is AdaptyOnboardingsInputParams -> { // Handle text input val builder = AdaptyProfileParameters.Builder() // Map elementId to appropriate profile field when (elementId) { "name" -> { when (val input = params.input) { is AdaptyOnboardingsTextInput -> { builder.withFirstName(input.value) } } } "email" -> { when (val input = params.input) { is AdaptyOnboardingsEmailInput -> { builder.withEmail(input.value) } } } } // Update profile asynchronously mainUiScope.launch { val profileParams = builder.build() val result = Adapty.updateProfile(profileParams) result.onSuccess { profile -> // Profile updated successfully AppLogger.d("Profile updated: ${profile.email}") }.onError { error -> // Handle the error AppLogger.e("Failed to update profile: ${error.message}") } } } } } } // Set up the observer AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` ### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\} Usando cuestionarios en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios una vez que completan el onboarding. Por ejemplo, puedes preguntar a los usuarios sobre su experiencia deportiva y mostrar distintos CTAs y productos a diferentes grupos de usuarios. 1. [Añade un cuestionario](onboarding-quizzes) en el constructor de onboarding y asigna IDs significativos a sus opciones. 2. Gestiona las respuestas del cuestionario según sus IDs y [establece atributos personalizados](kmp-setting-user-attributes) para los usuarios. ```kotlin class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver { override fun onboardingViewOnStateUpdatedAction( view: AdaptyUIOnboardingView, meta: AdaptyUIOnboardingMeta, elementId: String, params: AdaptyOnboardingsStateUpdatedParams ) { // Handle quiz responses and set custom attributes when (params) { is AdaptyOnboardingsSelectParams -> { // Handle quiz selection val builder = AdaptyProfileParameters.Builder() // Map quiz responses to custom attributes when (elementId) { "experience" -> { // Set custom attribute 'experience' with the selected value (beginner, amateur, pro) builder.withCustomAttribute("experience", params.value) } } // Update profile asynchronously mainUiScope.launch { val profileParams = builder.build() val result = Adapty.updateProfile(profileParams) result.onSuccess { profile -> // Profile updated successfully AppLogger.d("Custom attribute 'experience' set to: ${params.value}") }.onError { error -> // Handle the error AppLogger.e("Failed to update profile: ${error.message}") } } } } } } // Set up the observer AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver()) ``` 3. [Crea segmentos](segments) para cada valor de atributo personalizado. 4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado. 5. [Muestra un paywall](kmp-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](kmp-handling-onboarding-events#opening-a-paywall). --- # File: kmp-best-practices --- --- title: "Best practices in Kotlin Multiplatform SDK" description: "Reference patterns for integrating Adapty SDK on Kotlin Multiplatform — call order, error handling, and other production-readiness rules." --- <CustomDocCardList /> --- # File: kmp-sdk-call-order --- --- title: "Orden de llamadas en el SDK de Kotlin Multiplatform" description: "Evita perder el acceso premium, datos de atribución incompletos y errores de activación intermitentes llamando a los métodos del SDK de Adapty en el orden correcto." --- `Adapty.activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que finalice, el SDK no tiene estado. Cualquier llamada realizada antes o en paralelo con `activate()` fallará con un error de activación. Consulta [Gestionar errores en el SDK de Kotlin Multiplatform](kmp-handle-errors). Si tu app autentica usuarios y obtienes un customer user ID después del lanzamiento, llama a `Adapty.identify()` en ese momento. No llames a métodos de acción del usuario hasta que `identify` se complete. Las llamadas que compiten con él pueden devolver un error o asociarse al perfil anónimo creado en la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la propiedad de la instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `identify` y trabaja con el perfil anónimo. Los SDKs de MMP y analíticas (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera sus callbacks de UID antes de llamar a `Adapty.activate`. De lo contrario, el ID de MMP se asociará a un perfil anónimo temporal y no siempre se transfiere al identificado. Para más detalles sobre AppsFlyer, consulta [AppsFlyer](appsflyer). ## El orden correcto \{#the-correct-order\} Tu flujo depende de dos factores: cuándo conoces el customer user ID y si usas un SDK de MMP o analíticas. - **Pasos 2 y 5**: Obligatorios para toda app. Activa el SDK y luego llama a los métodos del SDK. - **Pasos 1 y 3**: Solo necesarios si integras un SDK de MMP o analíticas (AppsFlyer, Adjust, Branch, PostHog). - **Paso 4**: Solo necesario si tu app autentica usuarios y obtiene el customer user ID después del lanzamiento. Si tienes el customer user ID en el lanzamiento de la app, pásalo al `AdaptyConfig.Builder` antes de llamar a `activate()` (paso 2a). Con este flujo nunca se crea un perfil anónimo, por lo que el paso 4 no es necesario. | Paso | Llamada | Cuándo | Notas | |------|---------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | Inicializa tu SDK de MMP o analíticas (AppsFlyer, Adjust, PostHog, Branch) | Lanzamiento de la app, primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerUID`. | | 2a | `Adapty.activate(configuration = AdaptyConfig.Builder("KEY").withCustomerUserId(...).build())` | Lanzamiento de la app, después del paso 1, si tienes el customer user ID | Recomendado. Nunca se crea un perfil anónimo. | | 2b | `Adapty.activate(configuration = AdaptyConfig.Builder("KEY").build())` sin `withCustomerUserId` | Lanzamiento de la app, después del paso 1, si no tienes el customer user ID (o nunca lo obtienes) | Adapty crea un perfil anónimo. | | 3 | `Adapty.setIntegrationIdentifier("appsflyer_id", uid)` para cada MMP | Después del paso 2, antes de cualquier llamada de acción del usuario | Necesario para que los IDs de MMP queden asociados al perfil correcto. | | 4 | `Adapty.identify("YOUR_USER_ID").onSuccess { ... }.onError { ... }` | Después del paso 3 (o del paso 2 si no hay MMP), antes del paso 5 — solo en el flujo 2b con autenticación | Espera `onSuccess` antes de cualquier llamada de acción del usuario. Las llamadas concurrentes durante `identify` pueden asociarse al perfil anónimo. | | 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Después del paso 4 si llamas a `identify`; en caso contrario, después del paso 3 (o del paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. | :::important Saltarse estos pasos provoca pérdida de acceso premium para usuarios recurrentes, `appsflyer_id` ausente en los perfiles y paywalls mostrados a la audiencia incorrecta. ::: ## Instalaciones web2app y de embudo web \{#web2app-and-web-funnel-installs\} Si los usuarios compran en un checkout web (Stripe, Paddle) e instalan la app nativa después, el primer `activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el customer user ID antes del lanzamiento de la app (desde tu flujo de autenticación o el referrer de instalación), pásalo directamente al `AdaptyConfig.Builder`. De lo contrario, la compra web será invisible en el dispositivo hasta que llames a `identify("YOUR_USER_ID")` y luego a `restorePurchases`. Para los metadatos que debes enviar con cada checkout web, consulta: - [Stripe](stripe) - [Paddle](paddle) --- # File: kmp-optimize-paywall-fetching --- --- title: "Optimizar la obtención de paywalls en el SDK de Kotlin Multiplatform" description: "Obtén paywalls de Adapty de forma fiable: tiempos, caché y patrones de respaldo para Kotlin Multiplatform." --- Una obtención fiable de paywalls en Kotlin Multiplatform hace tres cosas: renderiza rápido, devuelve el paywall dirigido a la audiencia correcta y recurre al respaldo de forma elegante cuando la red es lenta. Las reglas que se presentan a continuación cubren los patrones de tiempos, caché y respaldo para lograrlo. :::tip Las reglas asumen que `Adapty.activate()` y `Adapty.identify()` ya se han resuelto. Consulta [Orden de llamadas en el SDK de Kotlin Multiplatform](kmp-sdk-call-order). ::: ## Reglas y errores comunes \{#rules-and-pitfalls\} | Haz esto | No hagas esto | Por qué | |---|---|---| | Obtén el placement que estás a punto de mostrar. | No precargues todos los placements de forma simultánea al arrancar. | La precarga masiva bloquea el hilo principal y produce una pantalla en negro durante el pico de carga. | | Llama a `getPaywall` después de que la atribución haya tenido oportunidad de resolverse — por ejemplo, 1–2 segundos después de `activate` o cuando se dispare `setOnProfileUpdatedListener`. | No llames a `getPaywall` al arrancar la app. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia predeterminada y omite silenciosamente los segmentos y la personalización de ASA. | | Establece un `loadTimeout` y configura un [paywall de respaldo](fallback-paywalls) para cada placement. | No esperes `getPaywall` indefinidamente. | Sin timeout, los usuarios con mala conectividad ven una pantalla en blanco hasta que la red se resuelva, o cierran la app. | Consulta [Obtener paywalls y productos](fetch-paywalls-and-products-kmp) para la referencia de los parámetros `fetchPolicy` y `loadTimeout`, y [Placements](placements) para elegir el placement adecuado. ## Ajustar para conectividad deficiente \{#tune-for-poor-connectivity\} Para mercados con conectividad consistentemente deficiente (zonas rurales, transporte público, regiones afectadas por enrutamiento): - Establece `fetchPolicy = AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad` en cada obtención excepto la primera. - Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard. - Establece `loadTimeout` entre 3 y 5 segundos y acepta el respaldo cuando se agote el tiempo. - No condicionales la visualización del paywall a `Adapty.getProfile()`. Llama a `getPaywall` de forma independiente para que un perfil lento no bloquee la interfaz. --- # File: kmp-test --- --- title: "Prueba y lanzamiento en Kotlin Multiplatform SDK" description: "Aprende a comprobar el estado de las suscripciones en tu app de Kotlin Multiplatform con Adapty." --- Si ya has implementado el SDK de Adapty en tu app de Kotlin Multiplatform, querrás comprobar que todo está configurado correctamente y que las compras funcionan como se espera. Esto implica probar tanto la integración del SDK como el flujo de compra real con el entorno sandbox. ## Prueba tu app \{#test-your-app\} Para realizar pruebas exhaustivas de tus compras in-app, consulta nuestras guías de pruebas específicas por plataforma: [guía de pruebas de iOS](test-purchases-in-sandbox) y [guía de pruebas de Android](testing-on-android). ## Prepárate para el lanzamiento \{#prepare-for-release\} Antes de enviar tu app al store, sigue el [checklist de lanzamiento](release-checklist) para confirmar que: - La conexión con el store y las notificaciones del servidor están configuradas - Las compras se completan y se notifican a Adapty - El acceso se desbloquea y se restaura correctamente - Se cumplen los requisitos de privacidad y revisión --- # File: kmp-reference --- --- title: "Reference for Kotlin Multiplatform SDK" description: "Reference documentation for Adapty Kotlin Multiplatform SDK." --- This page contains reference documentation for Adapty Kotlin Multiplatform SDK. Choose the topic you need: - **[SDK models](https://kmp.adapty.io/adapty/)** - Data models and structures used by the SDK - **[Handle errors](kmp-handle-errors)** - Error handling and troubleshooting --- # File: kmp-handle-errors --- --- title: "Handle errors in Kotlin Multiplatform SDK" description: "Learn how to handle errors in your Kotlin Multiplatform app with Adapty." --- This page covers error handling in the Adapty Kotlin Multiplatform SDK. ## Error handling basics All Adapty SDK methods return results that can be either success or error. Always handle both cases: <Tabs groupId="current-os" queryString> <TabItem value="kotlin" label="Kotlin" default> ```kotlin showLineNumbers Adapty.getProfile { result -> when (result) { is AdaptyResult.Success -> { val profile = result.value // Handle success } is AdaptyResult.Error -> { val error = result.error // Handle error Log.e("Adapty", "Error: ${error.message}") } } } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers Adapty.getProfile(result -> { if (result instanceof AdaptyResult.Success) { AdaptyProfile profile = ((AdaptyResult.Success<AdaptyProfile>) result).getValue(); // Handle success } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); // Handle error Log.e("Adapty", "Error: " + error.getMessage()); } }); ``` </TabItem> </Tabs> ## Common error codes | Error Code | Description | Solution | |------------|-------------|----------| | 1000 | No product IDs found | Check product configuration in dashboard | | 1001 | Network error | Check internet connection | | 1002 | Invalid SDK key | Verify your SDK key | | 1003 | Can't make payments | Device doesn't support payments | | 1004 | Product not available | Product not configured in store | ## Handle specific errors ### Network errors <Tabs groupId="current-os" queryString> <TabItem value="kotlin" label="Kotlin" default> ```kotlin showLineNumbers Adapty.getPaywall("main") { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value // Use paywall } is AdaptyResult.Error -> { val error = result.error when (error.code) { 1001 -> { // Network error - show offline message showOfflineMessage() } else -> { // Other errors showErrorMessage(error.message) } } } } } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers Adapty.getPaywall("main", result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success<AdaptyPaywall>) result).getValue(); // Use paywall } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); switch (error.getCode()) { case 1001: // Network error - show offline message showOfflineMessage(); break; default: // Other errors showErrorMessage(error.getMessage()); break; } } }); ``` </TabItem> </Tabs> ### Purchase errors <Tabs groupId="current-os" queryString> <TabItem value="kotlin" label="Kotlin" default> ```kotlin showLineNumbers product.makePurchase { result -> when (result) { is AdaptyResult.Success -> { val purchase = result.value // Purchase successful showSuccessMessage() } is AdaptyResult.Error -> { val error = result.error when (error.code) { 1003 -> { // Can't make payments showPaymentNotAvailableMessage() } 1004 -> { // Product not available showProductNotAvailableMessage() } else -> { // Other purchase errors showPurchaseErrorMessage(error.message) } } } } } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers product.makePurchase(result -> { if (result instanceof AdaptyResult.Success) { AdaptyPurchase purchase = ((AdaptyResult.Success<AdaptyPurchase>) result).getValue(); // Purchase successful showSuccessMessage(); } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); switch (error.getCode()) { case 1003: // Can't make payments showPaymentNotAvailableMessage(); break; case 1004: // Product not available showProductNotAvailableMessage(); break; default: // Other purchase errors showPurchaseErrorMessage(error.getMessage()); break; } } }); ``` </TabItem> </Tabs> ## Error recovery strategies ### Retry on network errors <Tabs groupId="current-os" queryString> <TabItem value="kotlin" label="Kotlin" default> ```kotlin showLineNumbers fun getPaywallWithRetry(placementId: String, maxRetries: Int = 3) { var retryCount = 0 fun attemptGetPaywall() { Adapty.getPaywall(placementId) { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value // Use paywall } is AdaptyResult.Error -> { val error = result.error if (error.code == 1001 && retryCount < maxRetries) { // Network error - retry retryCount++ Handler(Looper.getMainLooper()).postDelayed({ attemptGetPaywall() }, 1000 * retryCount) // Exponential backoff } else { // Max retries reached or other error showErrorMessage(error.message) } } } } } attemptGetPaywall() } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers public void getPaywallWithRetry(String placementId, int maxRetries) { AtomicInteger retryCount = new AtomicInteger(0); Runnable attemptGetPaywall = new Runnable() { @Override public void run() { Adapty.getPaywall(placementId, result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success<AdaptyPaywall>) result).getValue(); // Use paywall } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); if (error.getCode() == 1001 && retryCount.get() < maxRetries) { // Network error - retry retryCount.incrementAndGet(); new Handler(Looper.getMainLooper()).postDelayed(this, 1000 * retryCount.get()); } else { // Max retries reached or other error showErrorMessage(error.getMessage()); } } }); } }; attemptGetPaywall.run(); } ``` </TabItem> </Tabs> ### Fallback to cached data <Tabs groupId="current-os" queryString> <TabItem value="kotlin" label="Kotlin" default> ```kotlin showLineNumbers class PaywallManager { private var cachedPaywall: AdaptyPaywall? = null fun getPaywall(placementId: String) { Adapty.getPaywall(placementId) { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value cachedPaywall = paywall showPaywall(paywall) } is AdaptyResult.Error -> { val error = result.error if (error.code == 1001 && cachedPaywall != null) { // Network error - use cached paywall showPaywall(cachedPaywall!!) showOfflineIndicator() } else { // No cache available or other error showErrorMessage(error.message) } } } } } } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers public class PaywallManager { private AdaptyPaywall cachedPaywall; public void getPaywall(String placementId) { Adapty.getPaywall(placementId, result -> { if (result instanceof AdaptyResult.Success) { AdaptyPaywall paywall = ((AdaptyResult.Success<AdaptyPaywall>) result).getValue(); cachedPaywall = paywall; showPaywall(paywall); } else if (result instanceof AdaptyResult.Error) { AdaptyError error = ((AdaptyResult.Error) result).getError(); if (error.getCode() == 1001 && cachedPaywall != null) { // Network error - use cached paywall showPaywall(cachedPaywall); showOfflineIndicator(); } else { // No cache available or other error showErrorMessage(error.getMessage()); } } }); } } ``` </TabItem> </Tabs> ## Next steps - [Fix for Code-1000 noProductIDsFound error](InvalidProductIdentifiers-kmp) - [Fix for Code-1003 cantMakePayments error](cantMakePayments-kmp) - [Complete API reference](https://android.adapty.io) - Full SDK documentation --- # File: InvalidProductIdentifiers-kmp --- --- title: "Solución al error Code-1000 noProductIDsFound en el SDK de Kotlin Multiplatform" description: "Resuelve errores de identificador de producto no válido al gestionar suscripciones en Adapty." --- El error con código 1000, `noProductIDsFound`, indica que ninguno de los productos que solicitaste en el paywall está disponible para comprar en el App Store, aunque estén listados allí. A veces este error viene acompañado de una advertencia `InvalidProductIdentifiers`. Si la advertencia aparece sin el error, puedes ignorarla sin problema. Si te encuentras con el error `noProductIDsFound`, sigue estos pasos para resolverlo: ## Paso 1. Comprueba el bundle ID \{#step-2-check-bundle-id\} --- no_index: true --- 1. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu app y ve a la sección **General** → **App Information**. 2. Copia el **Bundle ID** en la subsección **General Information**. <Zoom> <img src="/docs/img/afd5012-bundle_id_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 3. Abre la pestaña [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) desde el menú superior de Adapty y pega el valor copiado en el campo **Bundle ID**. <Zoom> <img src="/docs/img/2d64163-bundle_id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 4. Vuelve a la página **App information** en App Store Connect y copia el **Apple ID** desde allí. 5. En la página [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) del Adapty Dashboard, pega el ID en el campo **Apple app ID**. ## Paso 2. Comprueba los productos \{#step-3-check-products\} 1. Ve a **App Store Connect** y navega a [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) en el menú de la izquierda. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción. Verás tus productos listados en la sección **Subscriptions**. 3. Asegúrate de que el producto que estás probando esté marcado como **Ready to Submit**. Si no lo está, sigue las instrucciones de la página [Producto en App Store](app-store-products). <img src="/assets/shared/img/ready-to-submit.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Compara el ID del producto en la tabla con el que aparece en la pestaña [**Products**](https://app.adapty.io/products) del Adapty Dashboard. Si los IDs no coinciden, copia el ID del producto de la tabla y [crea un producto](create-product) con ese ID en el Adapty Dashboard. <img src="/assets/shared/img/product-id-copy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 3. Comprueba la disponibilidad del producto \{#step-4-check-product-availability\} 1. Vuelve a **App Store Connect** y abre la misma sección **Subscriptions**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción para ver tus productos. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hasta la sección **Availability** y comprueba que todos los países y regiones requeridos estén listados. <img src="/assets/shared/img/product-availability.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 4. Comprueba los precios del producto \{#step-5-check-product-prices\} 1. De nuevo, ve a la sección **Monetization** → **Subscriptions** en **App Store Connect**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hacia abajo hasta **Subscription Pricing** y despliega la sección **Current Pricing for New Subscribers**. <img src="/assets/shared/img/check-prices.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Asegúrate de que todos los precios requeridos estén listados. <img src="/assets/shared/img/product-pricing.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 5. Comprueba que el estado de pago de la app, la cuenta bancaria y los formularios fiscales estén activos \{#step-5-check-app-paid-status-bank-account-and-tax-forms-are-active\} 1. En la página de inicio de [**App Store Connect**](https://appstoreconnect.apple.com/), haz clic en **Business**. <img src="/assets/shared/img/business.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Selecciona el nombre de tu empresa. <img src="/assets/shared/img/business-name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Desplázate hacia abajo y comprueba que tu **Paid Apps Agreement**, **Bank Account** y **Tax forms** aparezcan como **Active**. <img src="/assets/shared/img/appstore-connect-status.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Siguiendo estos pasos deberías poder resolver la advertencia `InvalidProductIdentifiers` y hacer que tus productos estén disponibles en el store. ## Paso 6. Vuelve a crear el producto si está bloqueado \{#step-6-recreate-the-product-if-its-stuck\} Es posible que los pasos 1–5 pasen correctamente —estado `Approved`, Bundle ID coincidente, API key válida— y el SDK siga devolviendo `1000 noProductIDsFound`. En ese caso, puede que el producto esté bloqueado en el registro de Apple. El registro de productos de Apple puede entrar en un estado en el que el producto existe en la interfaz de App Store Connect pero no está expuesto a la ruta de búsqueda de StoreKit. Elimina el producto en App Store Connect y vuelve a crearlo con el mismo ID de producto. Espera hasta 24 horas tras la recreación para que los cambios se propaguen. --- # File: cantMakePayments-kmp --- --- title: "Solución para el error Code-1003 cantMakePayment en el SDK de Kotlin Multiplatform" description: "Resuelve el error al realizar pagos cuando gestionas suscripciones en Adapty." --- El error 1003, `cantMakePayments`, indica que no es posible realizar compras in-app en este dispositivo. Si encuentras el error `cantMakePayments`, normalmente se debe a una de estas razones: - Restricciones del dispositivo: El error no está relacionado con Adapty. Consulta las soluciones más abajo. - Configuración del modo Observer: El método `makePurchase` y el modo Observer no pueden usarse al mismo tiempo. Consulta la sección más abajo. ## Problema: Restricciones del dispositivo \{#issue-device-restrictions\} | Problema | Solución | |-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| | Restricciones de Screen Time | Desactiva las restricciones de compras in-app en [Screen Time](https://support.apple.com/en-us/102470) | | Cuenta suspendida | Contacta con el soporte de Apple para resolver problemas con la cuenta | | Restricciones regionales | Usa una cuenta de App Store de una región compatible | ## Problema: Usar el modo Observer y makePurchase a la vez \{#issue-using-both-observer-mode-and-makepurchase\} Si usas `makePurchases` para gestionar las compras, no necesitas el modo Observer. El [modo Observer](observer-vs-full-mode) solo es necesario si implementas la lógica de compra tú mismo. Por lo tanto, si usas `makePurchase`, puedes eliminar sin problema la activación del modo Observer del código de inicialización del SDK. --- # File: kmp-sdk-migration-guides --- --- title: "Kotlin Multiplatform SDK Migration Guides" description: "Migration guides for Adapty Kotlin Multiplatform SDK versions." --- This page contains all migration guides for Adapty Kotlin Multiplatform SDK. Choose the version you want to migrate to for detailed instructions: - **[Migrate to v. 3.15](migration-to-kmp-315)** --- # File: migration-to-kmp-315 --- --- title: "Guía de migración al SDK de Adapty Kotlin Multiplatform 3.15.0" description: "Pasos de migración para el SDK de Adapty Kotlin Multiplatform 3.15.0" --- El SDK de Adapty Kotlin Multiplatform 3.15.0 es una versión mayor que trae nuevas funcionalidades y mejoras que, sin embargo, pueden requerir algunos pasos de migración por tu parte. 1. Actualiza los nombres de la clase observer y sus métodos. 2. Actualiza el nombre del método para los paywalls de respaldo. 3. Actualiza el nombre de la clase de vista en los métodos de gestión de eventos. ## Actualiza los nombres de la clase observer y sus métodos \{#update-observer-class-and-method-names\} La clase observer y su método de registro han sido renombrados: ```diff - import com.adapty.kmp.AdaptyUIObserver + import com.adapty.kmp.AdaptyUIPaywallsEventsObserver - import com.adapty.kmp.models.AdaptyUIView + import com.adapty.kmp.models.AdaptyUIPaywallView - class MyAdaptyUIObserver : AdaptyUIObserver { - override fun paywallViewDidPerformAction(view: AdaptyUIView, action: AdaptyUIAction) { + class MyAdaptyUIPaywallsEventsObserver : AdaptyUIPaywallsEventsObserver { + override fun paywallViewDidPerformAction(view: AdaptyUIPaywallView, action: AdaptyUIAction) { // handle actions } } // Set up the observer - AdaptyUI.setObserver(MyAdaptyUIObserver()) + AdaptyUI.setPaywallsEventsObserver(MyAdaptyUIPaywallsEventsObserver()) ``` ## Actualiza el nombre del método para los paywalls de respaldo \{#update-fallback-paywalls-method-name\} El nombre del método para configurar los paywalls de respaldo ha cambiado: ```diff showLineNumbers - Adapty.setFallbackPaywalls(assetId = "fallback.json") + Adapty.setFallback(assetId = "fallback.json") .onSuccess { // Fallback paywalls loaded successfully } .onError { error -> // Handle the error } ``` ## Actualiza el nombre de la clase de vista en los métodos de gestión de eventos \{#update-view-class-name-in-event-handling-methods\} Todos los métodos de gestión de eventos usan ahora la nueva clase `AdaptyUIPaywallView` en lugar de `AdaptyUIView`: ```diff - override fun paywallViewDidAppear(view: AdaptyUIView) { + override fun paywallViewDidAppear(view: AdaptyUIPaywallView) { // Handle paywall appearance } - override fun paywallViewDidDisappear(view: AdaptyUIView) { + override fun paywallViewDidDisappear(view: AdaptyUIPaywallView) { // Handle paywall disappearance } - override fun paywallViewDidSelectProduct(view: AdaptyUIPaywallView, productId: String) { + override fun paywallViewDidSelectProduct(view: AdaptyUIView, productId: String) { // Handle product selection } - override fun paywallViewDidStartPurchase(view: AdaptyUIView, product: AdaptyPaywallProduct) { + override fun paywallViewDidStartPurchase(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct) { // Handle purchase start } - override fun paywallViewDidFinishPurchase(view: AdaptyUIView, product: AdaptyPaywallProduct, purchaseResult: AdaptyPurchaseResult) { + override fun paywallViewDidFinishPurchase(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct, purchaseResult: AdaptyPurchaseResult) { // Handle purchase result } - override fun paywallViewDidFailPurchase(view: AdaptyUIView, product: AdaptyPaywallProduct, error: AdaptyError) { + override fun paywallViewDidFailPurchase(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct, error: AdaptyError) { // Add your purchase failure handling logic here } - override fun paywallViewDidFinishRestore(view: AdaptyUIView, profile: AdaptyProfile) { + override fun paywallViewDidFinishRestore(view: AdaptyUIPaywallView, profile: AdaptyProfile) { // Add your successful restore handling logic here } - override fun paywallViewDidFailRestore(view: AdaptyUIView, error: AdaptyError) { + override fun paywallViewDidFailRestore(view: AdaptyUIPaywallView, error: AdaptyError) { // Add your restore failure handling logic here } - override fun paywallViewDidFinishWebPaymentNavigation(view: AdaptyUIView, product: AdaptyPaywallProduct?, error: AdaptyError?) { + override fun paywallViewDidFinishWebPaymentNavigation(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct?, error: AdaptyError?) { // Handle web payment navigation result } - override fun paywallViewDidFailLoadingProducts(view: AdaptyUIView, error: AdaptyError) { + override fun paywallViewDidFailLoadingProducts(view: AdaptyUIPaywallView, error: AdaptyError) { // Add your product loading failure handling logic here } - override fun paywallViewDidFailRendering(view: AdaptyUIView, error: AdaptyError) { + override fun paywallViewDidFailRendering(view: AdaptyUIPaywallView, error: AdaptyError) { // Handle rendering error } ``` --- # End of Documentation _Generated on: 2026-05-15T20:13:57.595Z_ _Successfully processed: 44/44 files_ # REACT-NATIVE - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.596Z Total files: 40 --- # File: sdk-installation-react-native-expo --- --- title: "Instalar y configurar el SDK de Adapty para React Native en un proyecto Expo" description: "Guía paso a paso para instalar el SDK de Adapty para React Native en un proyecto Expo para apps con suscripciones." --- :::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 para React Native](sdk-installation-react-native-pure). ::: 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 visual sin código para crear paywalls multiplataforma fácilmente. AdaptyUI se activa automáticamente junto con el módulo principal. Si quieres 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 Expo? Echa un vistazo a nuestras apps de ejemplo: - [Ejemplo con Expo dev build](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/FocusJournalExpo) para funcionalidad completa con compras reales y Paywall Builder - [Ejemplo con Expo Go y Web](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/ExpoGoWebMock) para pruebas con modo mock ::: Para un recorrido completo de la implementación, también puedes ver el vídeo: <div style={{ textAlign: 'center' }}> <iframe width="560" height="315" src="https://www.youtube.com/embed/TtCJswpt2ms?si=FlFJGvpj-U33yoNK" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> </div> ## Requisitos \{#requirements\} El SDK de Adapty para React Native es compatible con iOS 13.0+, pero el uso de paywalls creados en el [Adapty Paywall Builder](adapty-paywall-builder) requiere iOS 15.0+. :::info Adapty es compatible con Google Play Billing Library hasta la versión 8.x. Por defecto, Adapty trabaja con Google Play Billing Library v.7.0.0, pero si quieres forzar una versión posterior, puedes [añadir la dependencia manualmente](https://developer.android.com/google/play/billing/integrate#dependency). En Expo, esto se puede hacer durante el prebuild o mediante un config plugin. ::: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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. </Callout> ## Instalar el SDK de Adapty \{#install-adapty-sdk\} [![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 Se requiere [Expo Dev Client](https://docs.expo.dev/versions/latest/sdk/dev-client/) (una build de desarrollo personalizada) 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 build local: <Tabs> <TabItem value="eas" label="EAS build" default> ```sh # Para iOS eas build --profile development --platform ios # Para Android eas build --profile development --platform android ``` </TabItem> <TabItem value="local" label="Local build"> ```sh # Para iOS npx expo run:ios # Para Android npx expo run:android ``` </TabItem> </Tabs> 3. Inicia el servidor de desarrollo: ```sh npx expo start --dev-client ``` ## Activar el módulo Adapty del SDK de Adapty \{#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. :::important - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API 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 de 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, usa 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 cuando activas el módulo principal; no necesitas hacer nada más. ## Configuración opcional \{#optional-setup\} ### Registro de actividad \{#logging\} #### Configurar el sistema de registro \{#set-up-the-logging-system\} Adapty registra errores e información importante para ayudarte a entender qué está ocurriendo. Los siguientes niveles están disponibles: | Nivel | Descripción | | ---------- | ------------------------------------------------------------ | | `error` | Solo se registran los errores | | `warn` | Se registran errores y mensajes del SDK que no causan errores críticos, pero que conviene atender | | `info` | Se registran errores, advertencias y varios mensajes informativos | | `verbose` | Se registra 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" // Establece el nivel de registro antes de la activación // Se recomienda 'verbose' para el desarrollo y la primera versión en producción adapty.setLogLevel('verbose'); // O establécelo durante la configuración 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 adicionales de seguridad de datos para cumplir con las directrices de la store o del país. #### Deshabilitar la recopilación y compartición de la dirección IP \{#disable-ip-address-collection-and-sharing\} Al activar el módulo Adapty, establece `ipAddressCollectionDisabled` en `true` para deshabilitar la recopilación y compartición de la dirección IP del usuario. El valor predeterminado es `false`. Usa este parámetro para mejorar la privacidad del usuario, cumplir con las normativas regionales de protección de datos (como el RGPD o la 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, }); ``` #### Deshabilitar la recopilación y compartición del ID de publicidad \{#disable-advertising-id-collection-and-sharing\} Al activar el módulo 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 se active el aviso de App Tracking Transparency, o si tu app no requiere atribución publicitaria ni analíticas basadas 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\} De forma predeterminada, AdaptyUI almacena en caché los medios (como imágenes y vídeos) para mejorar el rendimiento y reducir el uso de la red. Puedes personalizar la configuración de la caché proporcionando una configuración personalizada. Usa `mediaCache` para sobreescribir la configuración predeterminada de la caché: ```typescript adapty.activate('YOUR_PUBLIC_SDK_KEY', { mediaCache: { memoryStorageTotalCostLimit: 200 * 1024 * 1024, // Opcional: tamaño de la caché en memoria en bytes memoryStorageCountLimit: 2147483647, // Opcional: número máximo de elementos en memoria diskStorageSizeLimit: 200 * 1024 * 1024, // Opcional: tamaño de la caché en disco en bytes }, }); ``` Parámetros: | Parámetro | Requerido | Descripción | |-----------|----------|-------------| | memoryStorageTotalCostLimit | opcional | Tamaño total de la caché en memoria en bytes. El valor predeterminado es específico de la plataforma. | | memoryStorageCountLimit | opcional | Límite de elementos en la memoria. El valor predeterminado es específico de la plataforma. | | diskStorageSizeLimit | opcional | Límite de tamaño de archivo en disco en bytes. El valor predeterminado es específico de la plataforma. | ### Habilitar niveles de acceso locales (Android) \{#enable-local-access-levels-android\} De forma predeterminada, 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, }, }); ``` ### Limpiar 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 del perfil en caché, los detalles de los 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 del 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\} #### Configurar el modo mock para Expo Go / Expo Web \{#set-up-mock-mode-for-expo-go--expo-web\} Los entornos Expo Go y Expo Web no tienen acceso a los módulos nativos de Adapty. Para evitar errores en tiempo de ejecución mientras puedes seguir compilando y probando la UI y la lógica de paywalls 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 el **Adapty Paywall Builder (AdaptyUI)**. - Los módulos nativos de Adapty son **completamente ignorados**: ni siquiera los archivos del SDK nativo que falten en la build de Xcode/Android o una clave de API no válida generará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 deshabilita automáticamente. :::: **Por defecto**, el SDK detecta automáticamente los entornos Expo Go y web, y habilita el modo mock. No necesitas configurar nada a menos que quieras personalizar los datos mock. Cuando el modo mock está activo: - Todos los métodos de Adapty devuelven datos mock sin hacer 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 admitidos [aquí](https://react-native.adapty.io/interfaces/adaptymockconfig). ```typescript showLineNumbers title="App.tsx" try { await adapty.activate('YOUR_PUBLIC_SDK_KEY', { mockConfig: { // Personaliza el perfil mock inicial (opcional) }, }); } 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 bridge ya está inicializado, este método no hace nada. ```typescript showLineNumbers title="App.tsx" adapty.enableMock(); // Opcional: pasa mockConfig para personalizar los datos mock // Ahora puedes llamar a métodos antes de la activación await adapty.activate('YOUR_PUBLIC_SDK_KEY'); ``` #### Retrasar la activación del SDK para fines de desarrollo \{#delay-sdk-activation-for-development-purposes\} Adapty obtiene de forma anticipada 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 realizadas por el SDK para obtener datos de usuario actualizados. Al habilitar la propiedad `__debugDeferActivation`, la llamada a activate se retiene hasta que se realiza la siguiente llamada al SDK de Adapty. Esto evita solicitudes innecesarias de datos de autenticación si no son necesarias. Es importante tener en cuenta que **esta funcionalidad está pensada solo para uso en desarrollo**, ya que no cubre todos los escenarios posibles del usuario. En producción, la activación no debe retrasarse, ya que los dispositivos reales suelen recordar los datos de autenticación y no solicitan credenciales repetidamente. Este es el enfoque recomendado para usarlo: ```typescript showLineNumbers title="Typescript" try { adapty.activate('PUBLIC_SDK_KEY', { __debugDeferActivation: isSimulator(), // 'isSimulator' de cualquier librería de terceros }); } catch (error) { console.error('Failed to activate Adapty SDK:', error); // Maneja el error de forma adecuada para tu app } ``` #### Solucionar errores de activación del SDK con el Fast Refresh de React Native \{#troubleshoot-sdk-activation-errors-on-react-natives-fast-refresh\} Al desarrollar con el SDK de Adapty en React Native, puede que te encuentres con 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 recarga rápida de React Native provoca múltiples llamadas de activación durante el desarrollo. Para evitarlo, usa la opción `__ignoreActivationOnFastRefresh` establecida en `__DEV__` (la bandera 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); // Maneja el error de forma adecuada para tu app } ``` ## Solución de problemas \{#troubleshooting\} #### Error de versión mínima de iOS \{#minimum-ios-version-error\} Al compilar para iOS, puede que veas un error sobre la **versión mínima de iOS** o el deployment target, especialmente si usas paywalls creados en el [Adapty Paywall Builder](adapty-paywall-builder), que requieren **iOS 15.0+**. Como Expo genera el proyecto iOS (incluido el `Podfile`) durante `expo prebuild`, **no debes editar el `Podfile` directamente**. En su lugar, configura el deployment target mediante el config plugin `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": { // ...otras configuraciones de Expo... "plugins": [ [ "expo-build-properties", { "ios": { // Usa "13.0" solo para funciones principales de Adapty, // o "15.0" si usas paywalls creados en el paywall builder. "deploymentTarget": "15.0" } } ], ] } } ``` 3. Regenera el proyecto nativo de iOS y vuelve a compilar: ``` npx expo prebuild --clean npx expo run:ios # o `eas build -p ios` en tu CI ``` #### Conflicto de manifiesto con Android Auto Backup \{#android-auto-backup-manifest-conflict\} Al usar Expo con varios SDKs que configuran Android Auto Backup (como Adapty, AppsFlyer o expo-secure-store), puede que te encuentres con un conflicto de fusión 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, necesitas que el plugin de Adapty gestione la configuración de Android backup. Si tu proyecto también usa `expo-secure-store`, deshabilita su propia configuración de backup para evitar solapamientos. Así es como se configura en 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 habilita, permite que el plugin de Adapty controle las reglas de backup de Android. Incluye `"configureAndroidBackup": false` si usas `expo-secure-store` para evitar advertencias, ya que la configuración de backup de SecureStore será gestionada por Adapty. :::important Esta configuración solo respeta los requisitos de backup para Adapty, AppsFlyer y expo-secure-store. Si otras librerías de tu proyecto definen reglas de backup personalizadas, tendrás que configurarlas manualmente. ::: --- # File: sdk-installation-react-native-pure --- --- title: "Instalar y configurar Adapty SDK en un proyecto React Native puro" description: "Guía paso a paso para instalar Adapty SDK en React Native para apps basadas en suscripciones." --- :::important Esta guía aplica únicamente a **proyectos React Native puros (sin Expo)**. Si usas **Expo**, sigue la [guía de instalación para Expo](sdk-installation-react-native-expo). ::: El SDK de Adapty incluye dos módulos clave para integrarse sin problemas en tu app de React Native: - **Core Adapty**: Este módulo es obligatorio 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 visual sin código para crear paywalls multiplataforma fácilmente. 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 funcionalidades básicas. ::: ## Requisitos \{#requirements\} El SDK de Adapty para React Native es compatible con iOS 13.0+, pero para usar paywalls creados en el [Adapty Paywall Builder](adapty-paywall-builder) se requiere iOS 15.0+. :::info Adapty es compatible con Google Play Billing Library hasta la versión 8.x. Por defecto, Adapty trabaja con Google Play Billing Library v.7.0.0, pero si quieres forzar una versión posterior, puedes [añadir la dependencia](https://developer.android.com/google/play/billing/integrate#dependency) manualmente. ::: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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. </Callout> ## Instalar Adapty SDK \{#install-adapty-sdk\} [![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 ``` <details> <summary>Para Android, si tu versión de React Native es anterior a 0.73.0 (haz clic para expandir)</summary> Actualiza el archivo `/android/build.gradle`. Asegúrate de que exista 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" } } ... ``` </details> ## Activar el módulo Core de Adapty 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. :::important - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API 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 el [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 el [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, usa los [consejos](#development-environment-tips). ::: ## Activar el módulo AdaptyUI de Adapty SDK \{#activate-adaptyui-module-of-adapty-sdk\} Si planeas usar el [Paywall Builder](adapty-paywall-builder), necesitas el módulo AdaptyUI. Se activa automáticamente cuando activas el módulo principal; no tienes que hacer nada más. ## Configuración opcional \{#optional-setup\} ### Logging \{#logging\} #### Configurar el sistema de logging \{#set-up-the-logging-system\} Adapty registra errores e información importante para ayudarte a entender qué está ocurriendo. Los niveles disponibles son los siguientes: | Nivel | Descripción | | ---------- | ------------------------------------------------------------ | | `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 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 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 adicionales de seguridad de datos para cumplir con las directrices de la store o del país. #### Deshabilitar la recopilación y el uso compartido de la dirección 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 uso compartido de la dirección IP del usuario. El valor predeterminado es `false`. Usa este parámetro para reforzar la privacidad del usuario, cumplir con las normativas regionales de protección de datos (como RGPD 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, }); ``` #### Deshabilitar la recopilación y el uso compartido del ID de publicidad \{#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 se active el aviso de App Tracking Transparency, o si tu app no requiere 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 la red. Puedes personalizar la configuración de la caché proporcionando una configuración personalizada. Usa `mediaCache` para sobreescribir 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ámetros: | 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 | El límite de elementos en el almacenamiento en memoria. El valor predeterminado depende de la plataforma. | | diskStorageSizeLimit | opcional | El límite de tamaño de 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` está establecido en `true`, el SDK detecta cuando la app se restaura desde una copia de seguridad de iCloud y elimina todos los datos del SDK almacenados localmente, incluida la información del perfil en caché, los detalles de los 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 del 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 para desarrollo \{#delay-sdk-activation-for-development-purposes\} Adapty realiza una precarga de todos los datos necesarios del usuario al activarse 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 frecuentemente solicita autenticación durante el desarrollo. Aunque Adapty no puede controlar el flujo de autenticación de StoreKit, sí puede aplazar las solicitudes que hace el SDK para obtener datos actualizados del usuario. Al habilitar la propiedad `__debugDeferActivation`, la llamada de activación queda en espera hasta que realizas la siguiente llamada al SDK de Adapty. Esto evita avisos de autenticación innecesarios si no son necesarios. Es importante tener en cuenta que **esta funcionalidad está pensada únicamente para el desarrollo**, ya que no cubre todos los escenarios de usuario posibles. En producción, la activación no debería retrasarse, ya que los dispositivos reales normalmente recuerdan los datos de autenticación y no solicitan credenciales repetidamente. Este es el enfoque recomendado: ```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 recarga rápida de React Native activa múltiples llamadas de activación durante el desarrollo. Para evitarlo, usa la opción `__ignoreActivationOnFastRefresh` establecida en `__DEV__` (el indicador del modo 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 } ``` #### Configurar el modo mock para pruebas locales \{#set-up-mock-mode-for-local-testing\} Para el desarrollo y las pruebas locales, puedes habilitar el modo mock para evitar necesitar cuentas sandbox de App Store/Google Play y agilizar las iteraciones. El modo mock omite por completo 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 ni **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 nativos del SDK en la compilación de Xcode/Android o una clave de API 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, deshabilita el modo mock y usa cuentas sandbox. ::: Para habilitar 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 mock 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 otorga acceso premium. Puedes personalizar los datos mock usando `mockConfig` durante la activación. Consulta el formato de configuración y los parámetros soportados [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 bridge 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, '13.0' # For core features only # OR +platform :ios, '15.0' # If using paywalls created in the paywall builder ``` #### Conflicto de 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 `<manifest>` incluya tools: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.app"> ... </manifest> ``` #### 2. Sobreescribe los atributos de copia de seguridad en `<application>` \{#2-override-backup-attributes-in-application\} En el mismo archivo `AndroidManifest.xml`, actualiza la etiqueta `<application>` para que tu app proporcione los valores definitivos e indique al fusionador de manifiestos que reemplace los valores de las librerías: ```xml <application android:name=".App" android:allowBackup="true" android:fullBackupContent="@xml/sample_backup_rules" android:dataExtractionRules="@xml/sample_data_extraction_rules" tools:replace="android:fullBackupContent,android:dataExtractionRules"> ... </application> ``` 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" <?xml version="1.0" encoding="utf-8"?> <data-extraction-rules> <cloud-backup> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </cloud-backup> <device-transfer> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </device-transfer> </data-extraction-rules> ``` **Para Android 11 e inferior** (usa el formato legado de contenido de copia de seguridad completa): ```xml title="sample_backup_rules.xml" <?xml version="1.0" encoding="utf-8"?> <full-backup-content> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.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 flujo de compra usa un `launchMode` no predeterminado, Android puede recrearla o reutilizarla incorrectamente 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 asegurarte de que las compras funcionen correctamente, usa únicamente los modos de lanzamiento `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 <activity android:name=".MainActivity" android:launchMode="standard" /> ``` #### Errores de compilación de Swift 6 causados por la sobreescritura 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, es posible que veas errores de compilación de Swift 6 en los targets de pods de Adapty. Los síntomas típicos incluyen incompatibilidades de `@Sendable` en `AdaptyUIBuilderLogic`, conformidad `Sendable` faltante en los tipos de Adapty, o errores de aislamiento de actores. Los pods de Adapty declaran `s.swift_version = '6.0'` y requieren Swift 6 para compilarse. Tu propio código de app puede seguir 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 de pods: ```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 de los pods de Adapty de la sobreescritura: ```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 indicar **Swift 6**. --- # File: react-native-quickstart-paywalls --- --- title: "Habilitar compras usando paywalls en React Native SDK" description: "Aprende a mostrar paywalls en tu app de React Native con el SDK de Adapty." --- 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) - [**Paywalls**](paywalls): configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar ofertas, precios y combinaciones de productos sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 diferentes paywalls a distintos usuarios. Adapty te ofrece tres formas de habilitar compras en tu app. Elige una según los requisitos de tu app: | Implementación | Complejidad | Cuándo usarla | |---------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Paywall Builder | ✅ Fácil | [Creas un paywall completo y listo para compras en el constructor sin código](quickstart-paywalls). Adapty lo renderiza automáticamente y gestiona todo el flujo de compra, validación de recibos y administración de suscripciones en segundo plano. | | Paywalls creados manualmente | 🟡 Medio | Implementas la UI del paywall en el código de tu app, pero aún obtienes el objeto paywall de Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](react-native-quickstart-manual). | | Modo observer | 🔴 Difícil | Ya tienes tu propia infraestructura de gestión de compras y quieres seguir usándola. Ten en cuenta que el modo observer tiene sus limitaciones en Adapty. Consulta el [artículo](observer-vs-full-mode). | :::important **Los pasos a continuación muestran cómo implementar un paywall creado en el Adapty Paywall Builder.** Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](react-native-making-purchases). ::: Para mostrar un paywall creado en el Adapty Paywall Builder, en el código de tu app solo necesitas: 1. **Obtener el paywall**: Obtén el paywall desde Adapty. 2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app. 3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con el paywall a las respuestas de tu app. Por ejemplo, abrir enlaces o cerrar el paywall cuando los usuarios pulsan 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. Obtener el paywall \{#1-get-the-paywall\} Tus paywalls están asociados a placements configurados en el dashboard. Los placements te permiten ejecutar diferentes paywalls para distintas audiencias o ejecutar [pruebas A/B](ab-tests). Para obtener un paywall creado en el Adapty Paywall Builder, necesitas: 1. Obtener el objeto `paywall` por el ID del [placement](placements) usando el método `getPaywall` y comprobar si es un paywall creado en el builder mediante la propiedad `hasViewConfiguration`. 2. Crear la vista del paywall usando el método `createPaywallView`. La vista contiene los elementos de UI y el estilo necesario para mostrar el paywall. :::important Para obtener la configuración de la vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará. ::: ```typescript showLineNumbers title="React Native" try { const placementId = 'YOUR_PLACEMENT_ID'; const paywall = await adapty.getPaywall(placementId); // the requested paywall } catch (error) { // handle the error } if (paywall.hasViewConfiguration) { try { const view = await createPaywallView(paywall); } catch (error) { // handle the error } } else { //use your custom logic } ``` ## 2. Mostrar el paywall \{#2-display-the-paywall\} Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo. <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="Componente React" default> Para integrar un paywall dentro de tu árbol de componentes existente, usa el componente `AdaptyPaywallView` directamente en la jerarquía de componentes de React Native: ```typescript showLineNumbers title="React Native (TSX)" function MyPaywall({ paywall }) { const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => {}, []); const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => { Linking.openURL(url); }, []); return ( <AdaptyPaywallView paywall={paywall} style={styles.container} onCloseButtonPress={onCloseButtonPress} onUrlPress={onUrlPress} /> ); } ``` </TabItem> <TabItem value="standalone" label="Presentación modal"> Para mostrar el paywall como una pantalla independiente, usa el método `view.present()` en el `view` creado por el método `createPaywallView`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `createPaywallView` una vez más para crear una nueva instancia de `view`. ```typescript showLineNumbers title="React Native" try { await view.present(); } catch (error) { // handle the error } ``` </TabItem> </Tabs> :::tip Para más detalles sobre cómo mostrar un paywall, 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 paywall, el SDK de React Native gestiona automáticamente las compras, la restauración, el cierre del paywall y la apertura de URLs. Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren gestionar las acciones en tu código. También puedes querer sobreescribir su comportamiento predeterminado. Por ejemplo, aquí está el comportamiento predeterminado para el botón de cierre. No necesitas añadirlo al código, pero aquí puedes ver cómo se hace si fuera necesario. <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="Componente React" default> Para el componente React, gestiona las acciones directamente en el componente `AdaptyPaywallView`: ```typescript showLineNumbers title="React Native (TSX)" function MyPaywall({ paywall }) { const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => { Linking.openURL(url); }, []); const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => {}, []); const onCustomAction = useCallback<EventHandlers['onCustomAction']>((actionId) => {}, []); return ( <AdaptyPaywallView paywall={paywall} style={styles.container} onUrlPress={onUrlPress} onCloseButtonPress={onCloseButtonPress} onCustomAction={onCustomAction} /> ); } ``` </TabItem> <TabItem value="standalone" label="Presentación modal"> 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 paywall closing } }); ``` </TabItem> </Tabs> :::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\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> 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 esos pasos juntos en tu app. <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="Componente React" default> ```javascript showLineNumbers title="React Native (TSX)" export default function PaywallScreen() { const [paywall, setPaywall] = useState(null); const loadPaywall = async () => { try { const paywallData = await adapty.getPaywall('YOUR_PLACEMENT_ID'); if (paywallData.hasViewConfiguration) { setPaywall(paywallData); } } catch (error) { console.warn('Error loading paywall:', error); } }; const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => { Linking.openURL(url); }, []); const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => { // Handle close button press }, []); useEffect(() => { loadPaywall(); }, []); return ( <View style={{ flex: 1 }}> {paywall ? ( <AdaptyPaywallView paywall={paywall} style={{ flex: 1 }} onUrlPress={onUrlPress} onCloseButtonPress={onCloseButtonPress} /> ) : ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Button title="Load Paywall" onPress={loadPaywall} /> </View> )} </View> ); } ``` </TabItem> <TabItem value="standalone" label="Presentación modal"> ```javascript showLineNumbers title="React Native" export default function PaywallScreen() { const showPaywall = async () => { try { const paywall = await adapty.getPaywall('YOUR_PLACEMENT_ID'); if (!paywall.hasViewConfiguration) { // use your custom logic return; } const view = await createPaywallView(paywall); view.setEventHandlers({ onCloseButtonPress() { return true; }, }); await view.present(); } catch (error) { // handle any error that may occur during the process console.warn('Error showing paywall:', error); } }; // you can add a button to manually trigger the paywall for testing purposes return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Button title="Show Paywall" onPress={showPaywall} /> </View> ); } ``` </TabItem> </Tabs> --- # File: react-native-check-subscription-status --- --- title: "Comprobar el estado de la suscripción en el SDK de React Native" description: "Aprende a comprobar el estado de la suscripción en tu app de React Native con Adapty." --- Para decidir si los usuarios pueden acceder a contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil. Este artículo te muestra cómo acceder al estado del perfil para decidir qué deben ver los usuarios: si mostrarles un paywall o concederles acceso a las funciones de pago. ## Obtener el estado de la suscripción \{#get-subscription-status\} Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones: - Llama a `getProfile` si necesitas los datos más recientes del perfil de inmediato (por ejemplo, al arrancar la app) o quieres forzar una actualización. - Configura **actualizaciones automáticas del perfil** para mantener una copia local que se refresca automáticamente cada vez que cambia el estado de la suscripción. ### Obtener el perfil \{#get-profile\} La forma más sencilla de obtener el estado de la suscripción es usar el método `getProfile` para acceder al perfil: ```typescript showLineNumbers try { const profile = await adapty.getProfile(); } catch (error) { // handle the error } ``` ### Escuchar actualizaciones de la suscripción \{#listen-to-subscription-updates\} Para recibir actualizaciones del perfil automáticamente en tu app: 1. Usa `adapty.addEventListener('onLatestProfileLoad')` para escuchar cambios en el perfil: Adapty llamará a este método automáticamente cada vez que cambie el estado de la suscripción del usuario. 2. Guarda los datos del perfil actualizado cuando se llame a este método, para poder usarlos en toda tu app sin hacer peticiones de red adicionales. ```javascript class SubscriptionManager { private currentProfile: any = null; constructor() { // Listen for profile updates adapty.addEventListener('onLatestProfileLoad', (profile) => { this.currentProfile = profile; // Update UI, unlock content, etc. }); } // Use stored profile instead of calling getProfile() hasAccess(): boolean { return this.currentProfile?.accessLevels?.['premium']?.isActive ?? false; } } ``` :::note Adapty llama automáticamente al listener del evento `onLatestProfileLoad` cuando arranca tu app, proporcionando datos de suscripción en caché incluso si el dispositivo está sin conexión. ::: ## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\} Cuando necesitas tomar decisiones inmediatas sobre mostrar paywalls o conceder acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en situaciones como el arranque de la app, al entrar en secciones premium o antes de mostrar contenido específico. ```javascript const checkAccessLevel = async () => { try { const profile = await adapty.getProfile(); return profile.accessLevels['YOUR_ACCESS_LEVEL']?.isActive === true; } catch (error) { console.warn('Error checking access level:', error); return false; // Show paywall if access check fails } }; const initializePaywall = async () => { try { await loadPaywall(); const hasAccess = await checkAccessLevel(); if (!hasAccess) { // Show paywall if no access } } catch (error) { console.warn('Error initializing paywall:', error); } }; ``` ## Próximos pasos \{#next-steps\} Ahora que sabes cómo hacer seguimiento del estado de la suscripción, aprende a [trabajar con perfiles de usuario](react-native-quickstart-identify) para asegurarte de que pueden acceder a lo que han pagado. --- # File: react-native-quickstart-identify --- --- title: "Identificar usuarios en el SDK de React Native" description: "Guía de inicio rápido para configurar Adapty para la gestión de suscripciones in-app en React Native." --- :::important Esta guía es para ti si tienes tu propio sistema de autenticación. Aquí aprenderás a trabajar con perfiles de usuario en Adapty para que se alinee con tu sistema de autenticación existente. ::: La forma en que gestionas las compras de los usuarios depende del modelo de autenticación de tu app: - Si tu app no usa autenticación de backend ni almacena datos de usuario, consulta la [sección sobre usuarios anónimos](#anonymous-users). - Si tu app tiene (o tendrá) autenticación de backend, consulta la [sección sobre usuarios identificados](#identified-users). **Conceptos clave**: - Los **perfiles** son las entidades necesarias para que el SDK funcione. Adapty los crea automáticamente. - Pueden ser anónimos **(sin customer user ID)** o identificados **(con customer user ID)**. - Proporcionas el **customer user ID** para cruzar los perfiles de Adapty con tu sistema de autenticación interno. Esto es lo que diferencia a los usuarios anónimos de los identificados: | | Usuarios anónimos | Usuarios identificados | |------------------------------|----------------------------------------------------------|------------------------------------------------------------------------------------| | **Gestión de compras** | Restauración de compras a nivel de store | Mantienen el historial de compras en todos los dispositivos mediante su customer user ID | | **Gestión de perfiles** | Nuevos perfiles en cada reinstalación | El mismo perfil en todas las sesiones y dispositivos | | **Persistencia de datos** | Los datos de usuarios anónimos están ligados a la instalación de la app | Los datos de usuarios identificados persisten entre instalaciones de la app | ## Usuarios anónimos \{#anonymous-users\} Si no tienes autenticación de backend, **no necesitas gestionar la autenticación en el código de la app**: 1. Cuando el SDK se activa en el primer inicio de la app, Adapty **crea un nuevo perfil para el usuario**. 2. Cuando el usuario realiza una compra en la app, esta compra queda **asociada a su perfil de Adapty y a su cuenta en el store**. 3. Cuando el usuario **reinstala** la app o la instala en un **nuevo dispositivo**, Adapty **crea un nuevo perfil anónimo al activarse**. 4. Si el usuario ya había realizado compras en tu app, por defecto, sus compras se sincronizan automáticamente desde el App Store al activarse el SDK. Así, con usuarios anónimos se crearán nuevos perfiles en cada instalación, pero no es un problema porque, en los análisis de Adapty, puedes [configurar qué se considerará una nueva instalación](general#4-installs-definition-for-analytics). :::note Las restauraciones desde copia de seguridad se comportan de forma diferente a las reinstalaciones. Por defecto, cuando un usuario restaura desde una copia de seguridad, el SDK conserva los datos en caché y no crea un nuevo perfil. Puedes configurar este comportamiento con el ajuste `clearDataOnBackup`. [Más información](sdk-installation-react-native-pure#clear-data-on-backup-restore). ::: Para los usuarios anónimos, debes contar las instalaciones por **IDs de dispositivo**. En este caso, cada instalación de la app en un dispositivo se cuenta como una instalación, incluidas las reinstalaciones. ## Usuarios identificados \{#identified-users\} Tienes dos opciones para identificar usuarios en la app: - [**Durante el inicio de sesión/registro:**](#during-loginsignup) Si los usuarios inician sesión después de que tu app arranque, llama a `identify()` con un customer user ID cuando se autentiquen. - [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando se inicia la app, envíalo al llamar a `activate()`. :::important Por defecto, cuando Adapty recibe una compra de un Customer User ID que está actualmente asociado a otro Customer User ID, el nivel de acceso se comparte, de modo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o desactivar el uso compartido por completo. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles. ::: <img src="/assets/shared/img/identify-diagram.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Durante el inicio de sesión/registro \{#during-loginsignup\} Si identificas a los usuarios después del inicio de la app (por ejemplo, cuando inician sesión o se registran), usa el método `identify` para establecer su customer user ID. - Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual. - Si **ya has usado este customer user ID para identificar al usuario antes**, Adapty pasará a trabajar con el perfil asociado a ese customer user ID. :::important Los customer user IDs deben ser únicos para cada usuario. Si hardcodeas el valor del parámetro, todos los usuarios se considerarán como uno solo. ::: Siempre usa `await` con `identify` antes de llamar a otros métodos del SDK. Las llamadas concurrentes generan `#3006 profileWasChanged` o aterrizan en el perfil anónimo. Consulta [Orden de llamadas en el SDK de React Native](react-native-sdk-call-order). ```typescript showLineNumbers try { await adapty.identify("YOUR_USER_ID"); // Unique for each user // successfully identified } catch (error) { // handle the error } ``` ### Durante la activación del SDK \{#during-the-sdk-activation\} Si ya conoces el customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado. Si conoces un customer user ID pero solo lo configuras después de la activación, eso significa que, al activarse, Adapty creará un nuevo perfil anónimo y solo pasará al existente cuando llames a `identify`. Puedes pasar un customer user ID existente (uno que ya hayas usado antes) o uno nuevo. Si pasas uno nuevo, el perfil creado al activarse se vinculará automáticamente a ese customer user ID. :::note Por defecto, la creación de perfiles anónimos no afecta a los dashboards de análisis, porque las instalaciones se cuentan por IDs de dispositivo. Un ID de dispositivo representa una única instalación de la app desde el store en un dispositivo y solo se regenera cuando la app se reinstala. No depende de si es la primera instalación o una repetida, ni de si se usa un customer user ID existente. Crear un perfil (al activar el SDK o al cerrar sesión), iniciar sesión o actualizar la app sin reinstalarla no genera eventos de instalación adicionales. Si quieres contar las instalaciones por usuarios únicos en lugar de por dispositivos, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics). ::: ```typescript showLineNumbers adapty.activate("PUBLIC_SDK_KEY", { customerUserId: "YOUR_USER_ID" // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one. }); ``` ### Cerrar sesión de usuarios \{#log-users-out\} Si tienes un botón para cerrar la sesión de los usuarios, usa el método `logout`. :::important Cerrar la sesión crea un nuevo perfil anónimo para el usuario. ::: ```typescript showLineNumbers try { await adapty.logout(); // successful logout } catch (error) { // handle the error } ``` :::info Para volver a iniciar sesión en la app, usa el método `identify`. ::: ### Permitir compras sin inicio de sesión \{#allow-purchases-without-login\} Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, debes asegurarte de que mantengan el acceso después de iniciar sesión: 1. Cuando un usuario sin sesión iniciada realiza una compra, Adapty la vincula a su ID de perfil anónimo. 2. Cuando el usuario inicia sesión en su cuenta, Adapty pasa a trabajar con su perfil identificado. - Si es un customer user ID nuevo (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que se mantiene todo el historial de compras. - Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), necesitas obtener el nivel de acceso actual después del cambio de perfil. Puedes llamar a [`getProfile`](react-native-check-subscription-status) justo después de la identificación, o [escuchar las actualizaciones del perfil](react-native-check-subscription-status) para que los datos se sincronicen automáticamente. ## Próximos pasos \{#next-steps\} ¡Enhorabuena! Has implementado la lógica de pago in-app en tu app. ¡Te deseamos todo lo mejor con la monetización de tu app! Para sacar aún más partido a Adapty, puedes explorar estos temas: - [**Pruebas**](troubleshooting-test-purchases): Asegúrate de que todo funciona como se espera - [**Onboardings**](react-native-onboardings): Engancha a los usuarios con onboardings y aumenta la retención - [**Integraciones**](configuration): Integra con servicios de atribución de marketing y análisis con una sola línea de código - [**Establecer atributos de perfil personalizados**](react-native-setting-user-attributes): Añade atributos personalizados a los perfiles de usuario y crea segmentos para lanzar pruebas A/B o mostrar diferentes paywalls a distintos usuarios --- # File: adapty-cursor-react-native --- --- title: "Integra Adapty en tu app de React Native con ayuda de IA" description: "Una guía paso a paso para integrar Adapty en tu app de React Native usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA." --- Esta página explica dos formas de integrar Adapty en tu app de React Native. Usa la skill de integración del SDK que encontrarás a continuación para un flujo automatizado de extremo a extremo, o sigue el tutorial manual más abajo. ## Usa la skill de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\} La [skill adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza la integración de principio a fin: configuración del dashboard, instalación del SDK, paywall y verificación por etapas. El tutorial manual que aparece más abajo es la alternativa si tu herramienta no es compatible con el formato Claude Skills. **Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI. ### Instalación \{#install\} Elige el formato según tu herramienta. La lista completa está en el [README de la skill](https://github.com/adaptyteam/adapty-sdk-integration-skill). - **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y luego `claude plugin install adapty-sdk-integration@adapty` desde tu terminal. - **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`. - **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`. - **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de skills de tu herramienta. ### Ejecución \{#run\} En tu proyecto, ejecuta `/adapty-sdk-integration`. La skill detecta tu plataforma y hace algunas preguntas de configuración. Luego guía paso a paso por la configuración del dashboard, la instalación del SDK, el paywall y la verificación — obteniendo la documentación de Adapty relevante en cada etapa. :::note La skill está en beta. Si se bloquea o se comporta de forma inesperada, el tutorial manual que aparece a continuación cubre cada etapa paso a paso. ::: ## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\} Adapty requiere cierta configuración en el dashboard antes de escribir código con el SDK. Puedes hacerlo con una skill interactiva de LLM o manualmente a través del Dashboard. ### Enfoque con skill (recomendado) \{#skill-approach-recommended\} La skill Adapty CLI permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin necesidad de abrir el Dashboard en cada paso. Solo tienes que [conectar tus stores](integrate-payments) en el Dashboard. ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` Una vez añadida la skill, ejecuta `/adapty-cli` en tu agente. Te guiará por cada paso, incluyendo cuándo abrir el Dashboard para conectar tus stores. ### Enfoque manual por el Dashboard \{#dashboard-approach\} Si prefieres configurarlo todo manualmente, esto es lo que necesitas antes de escribir código. Tu LLM no puede consultar los valores del dashboard por ti — tendrás que proporcionarlos tú. 1. **Conecta tus stores**: En el Adapty Dashboard, ve a **App settings → General**. Conecta tanto App Store como Google Play si tu app está disponible en ambas plataformas. Esto es necesario para que las compras funcionen. [Conectar stores](integrate-payments) 2. **Copia tu clave SDK pública**: En el Adapty Dashboard, ve a **App settings → General** y busca la sección **API keys**. En el código, esta es la cadena que pasas a `adapty.activate("YOUR_PUBLIC_SDK_KEY")`. 3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No haces referencia a los productos directamente en el código — Adapty los entrega a través de paywalls. [Añadir productos](quickstart-products) 4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `adapty.getPaywall("YOUR_PLACEMENT_ID")`. [Crear paywall](quickstart-paywalls) 5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena que se comprueba en `profile.accessLevels['premium']?.isActive`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago tienen acceso a funciones distintas según el producto (por ejemplo, un plan `basic` frente a un plan `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar. :::tip Una vez que tengas los cinco, estás listo para escribir código. Dile a tu LLM: "Mi clave SDK pública es X, mi ID de placement es Y" para que pueda generar el código de inicialización y de obtención de paywalls correcto. ::: ### Configurar cuando estés listo \{#set-up-when-ready\} Esto no es necesario para empezar a programar, pero lo querrás a medida que tu integración madure: - **Pruebas A/B**: Configúralas en la página **Placements**. No se necesita ningún cambio de código. [Pruebas A/B](ab-tests) - **Paywalls y placements adicionales**: Añade más llamadas a `getPaywall` con distintos IDs de placement. - **Integraciones de analítica**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta [integraciones de analítica](analytics-integration) e [integraciones de atribución](attribution-integration). ## Dale documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\} ### Usa Context7 (recomendado) \{#use-context7-recommended\} [Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. Tu LLM obtiene la documentación correcta automáticamente según lo que preguntes — sin necesidad de pegar URLs manualmente. Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta: ``` npx ctx7 setup ``` Esto detecta tu editor y configura el servidor Context7. Para la configuración manual, consulta el [repositorio de Context7 en GitHub](https://github.com/upstash/context7). Una vez configurado, haz referencia a la biblioteca de Adapty en tus prompts: ``` Use the adaptyteam/adapty-docs library to look up how to install the React Native SDK ``` :::warning Aunque Context7 elimina la necesidad de pegar enlaces a la documentación manualmente, el orden de implementación es importante. Sigue el [tutorial de implementación](#implementation-walkthrough) que aparece a continuación paso a paso para asegurarte de que todo funciona. ::: ### Usa documentación en texto plano \{#use-plain-text-docs\} Puedes acceder a cualquier documento de Adapty como Markdown en texto plano. Añade `.md` al final de su URL o haz clic en **Copy for LLM** debajo del título del artículo. Por ejemplo: [adapty-cursor-react-native.md](https://adapty.io/docs/es/adapty-cursor-react-native.md). Cada etapa del [tutorial de implementación](#implementation-walkthrough) que aparece a continuación incluye un bloque "Envía esto a tu LLM" con enlaces `.md` para pegar. Para obtener más documentación a la vez, consulta los [archivos de índice y subconjuntos específicos por plataforma](#plain-text-doc-index-files) que aparecen más abajo. ## Tutorial de implementación \{#implementation-walkthrough\} El resto de esta guía recorre la integración de Adapty en el orden de implementación. Cada etapa incluye la documentación que debes enviar a tu LLM, lo que deberías ver al terminar y los problemas más frecuentes. ### Planifica tu integración \{#plan-your-integration\} Antes de escribir código, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA dispone de un modo de planificación (como el de Cursor o Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir código. Dile a tu LLM qué enfoque usas para las compras — esto afecta a las guías que debe seguir: - [**Adapty Paywall Builder**](adapty-paywall-builder): Creas los paywalls en el editor no-code de Adapty y el SDK los renderiza automáticamente. - [**Paywalls creados manualmente**](react-native-making-purchases): Construyes tu propia interfaz de paywall en código, pero sigues usando Adapty para obtener productos y gestionar compras. - [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analítica e integraciones. ¿No sabes cuál elegir? Lee la [tabla de comparación en la guía de inicio rápido](react-native-quickstart-paywalls). ### Instala y configura el SDK \{#install-and-configure-the-sdk\} Añade la dependencia del SDK de Adapty usando npm (o yarn) y actívalo con tu clave SDK pública. Esta es la base — nada más funciona sin esto. Tenemos guías de instalación separadas para proyectos Expo y React Native puro — elige la que corresponda a tu configuración. **Guías:** - [Instalar con Expo](sdk-installation-react-native-expo) - [Instalar con React Native puro](sdk-installation-react-native-pure) Envía esto a tu LLM (elige la que corresponda a tu configuración, o envía ambas): ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/sdk-installation-react-native-expo.md - https://adapty.io/docs/es/sdk-installation-react-native-pure.md ``` :::tip[Punto de verificación] - **Resultado esperado:** La app compila y se ejecuta en iOS y Android. Los logs del bundler Metro muestran el log de activación de Adapty. - **Problema frecuente:** "Public API key is missing" → comprueba que reemplazaste el marcador de posición con tu clave real de App settings. ::: ### Muestra paywalls y gestiona compras \{#show-paywalls-and-handle-purchases\} Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras. Prueba cada compra en el sandbox a medida que avanzas — no esperes hasta el final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración. <Tabs groupId="paywall-approach"> <TabItem value="builder" label="Paywall Builder" default> **Guías:** - [Habilitar compras usando paywalls (inicio rápido)](react-native-quickstart-paywalls) - [Obtener paywalls del Paywall Builder y su configuración](react-native-get-pb-paywalls) - [Mostrar paywalls](react-native-present-paywalls) - [Gestionar eventos de paywall](react-native-handling-events-1) - [Responder a las acciones de los botones](react-native-handle-paywall-actions) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/react-native-quickstart-paywalls.md - https://adapty.io/docs/es/react-native-get-pb-paywalls.md - https://adapty.io/docs/es/react-native-present-paywalls.md - https://adapty.io/docs/es/react-native-handling-events-1.md - https://adapty.io/docs/es/react-native-handle-paywall-actions.md ``` :::tip[Punto de verificación] - **Resultado esperado:** El paywall aparece con los productos configurados. Al pulsar un producto se abre el diálogo de compra en sandbox. - **Problema frecuente:** Paywall vacío o error en `getPaywall` → verifica que el ID de placement coincide exactamente con el del dashboard y que el placement tiene una audiencia asignada. ::: </TabItem> <TabItem value="manual" label="Paywalls manuales"> **Guías:** - [Habilitar compras en tu paywall personalizado (inicio rápido)](react-native-quickstart-manual) - [Obtener paywalls y productos](fetch-paywalls-and-products-react-native) - [Renderizar un paywall diseñado con Remote Config](present-remote-config-paywalls-react-native) - [Realizar compras](react-native-making-purchases) - [Restaurar compras](react-native-restore-purchase) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/react-native-quickstart-manual.md - https://adapty.io/docs/es/fetch-paywalls-and-products-react-native.md - https://adapty.io/docs/es/present-remote-config-paywalls-react-native.md - https://adapty.io/docs/es/react-native-making-purchases.md - https://adapty.io/docs/es/react-native-restore-purchase.md ``` :::tip[Punto de verificación] - **Resultado esperado:** Tu paywall personalizado muestra los productos obtenidos de Adapty. Al pulsar un producto se abre el diálogo de compra en sandbox. - **Problema frecuente:** Array de productos vacío → verifica que el paywall tiene productos asignados en el dashboard y que el placement tiene una audiencia. ::: </TabItem> <TabItem value="observer" label="Modo Observer"> **Guías:** - [Descripción general del modo Observer](observer-vs-full-mode) - [Implementar el modo Observer](implement-observer-mode-react-native) - [Reportar transacciones en modo Observer](report-transactions-observer-mode-react-native) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/observer-vs-full-mode.md - https://adapty.io/docs/es/implement-observer-mode-react-native.md - https://adapty.io/docs/es/report-transactions-observer-mode-react-native.md ``` :::tip[Punto de verificación] - **Resultado esperado:** Tras una compra en sandbox con tu flujo de compra existente, la transacción aparece en el **Event Feed** del dashboard de Adapty. - **Problema frecuente:** Sin eventos → verifica que estás reportando transacciones a Adapty y que las notificaciones del servidor están configuradas para ambos stores. ::: </TabItem> </Tabs> ### Comprueba el estado de la suscripción \{#check-subscription-status\} Tras una compra, comprueba el perfil de usuario para ver si hay un nivel de acceso activo que dé acceso al contenido premium. **Guía:** [Comprobar el estado de la suscripción](react-native-check-subscription-status) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/react-native-check-subscription-status.md ``` :::tip[Punto de verificación] - **Resultado esperado:** Tras una compra en sandbox, `profile.accessLevels['premium']?.isActive` devuelve `true`. - **Problema frecuente:** `accessLevels` vacío tras la compra → comprueba que el producto tiene un nivel de acceso asignado en el dashboard. ::: ### Identifica a los usuarios \{#identify-users\} Vincula las cuentas de usuario de tu app con los perfiles de Adapty para que las compras persistan entre dispositivos. :::important Omite este paso si tu app no tiene autenticación. ::: **Guía:** [Identificar usuarios](react-native-quickstart-identify) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/react-native-quickstart-identify.md ``` :::tip[Punto de verificación] - **Resultado esperado:** Tras llamar a `adapty.identify("your-user-id")`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado. - **Problema frecuente:** Llama a `identify` después de la activación pero antes de obtener paywalls para evitar la atribución a un perfil anónimo. ::: ### Prepárate para el lanzamiento \{#prepare-for-release\} Una vez que tu integración funcione en el sandbox, repasa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción. **Guía:** [Lista de verificación de lanzamiento](release-checklist) Envía esto a tu LLM: ``` Read these Adapty docs before releasing: - https://adapty.io/docs/es/release-checklist.md ``` :::tip[Punto de verificación] - **Resultado esperado:** Todos los elementos de la lista confirmados: conexiones de stores, notificaciones del servidor, flujo de compra, comprobaciones de nivel de acceso y requisitos de privacidad. - **Problema frecuente:** Faltan notificaciones del servidor → configura las App Store Server Notifications en **App settings → iOS SDK** y las Google Play Real-Time Developer Notifications en **App settings → Android SDK**. ::: ## Archivos de índice de documentación en texto plano \{#plain-text-doc-index-files\} Si necesitas dar a tu LLM un contexto más amplio más allá de páginas individuales, alojamos archivos de índice que listan o combinan toda la documentación de Adapty: - [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con enlaces `.md`. Es un [estándar emergente](https://llmstxt.org/) para hacer que los sitios web sean accesibles para los LLMs. Ten en cuenta que para algunos agentes de IA (por ejemplo, ChatGPT) tendrás que descargar `llms.txt` y subirlo al chat como archivo. - [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación del sitio de Adapty combinada en un único archivo. Es muy grande — úsalo solo cuando necesites el panorama completo. - Subconjuntos específicos de React Native: [`react-native-llms.txt`](https://adapty.io/docs/es/react-native-llms.txt) y [`react-native-llms-full.txt`](https://adapty.io/docs/es/react-native-llms-full.txt): Subconjuntos específicos de la plataforma que ahorran tokens en comparación con el sitio completo. --- # File: react-native-get-pb-paywalls --- --- title: "Obtener paywalls de Paywall Builder y su configuración en el SDK de React Native" description: "Aprende a recuperar paywalls de PB en Adapty para un mejor control de suscripciones en tu app de React Native." --- Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso en este proceso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación. :::warning El nuevo Paywall Builder funciona con la versión 3.0 o superior del SDK de React Native. ::: Ten en cuenta que este tema hace referencia a paywalls personalizados con Paywall Builder. Si estás implementando tus paywalls manualmente, consulta el tema [Obtener paywalls y productos para paywalls de Remote Config en tu app móvil](fetch-paywalls-and-products-react-native). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a mostrar paywalls en tu app móvil (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall](create-placement) en el Adapty Dashboard. 4. Instala el [SDK de Adapty](sdk-installation-reactnative) en tu app móvil. </details> ## Obtener el paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\} Si has [diseñado un paywall con Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app móvil para mostrárselo al usuario. Ese tipo de paywall contiene tanto lo que debe mostrarse como la forma en que debe hacerse. Aun así, debes obtener su ID a través del placement, su configuración de vista y luego presentarlo en tu app móvil. Para garantizar un rendimiento óptimo, es fundamental recuperar el paywall y su [configuración de vista](react-native-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dejando tiempo suficiente para que las imágenes se descarguen antes de presentarlas al usuario. Para obtener un paywall, usa el método `getPaywall`: ```typescript showLineNumbers try { const placementId = 'YOUR_PLACEMENT_ID'; const locale = 'en'; const paywall = await adapty.getPaywall(placementId, locale); // the requested paywall } catch (error) { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | |-------------------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En ese caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra al desinstalarla o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls localmente en dos capas: la caché de actualización regular descrita anteriormente y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que la CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeoutMs** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera de este método. Si se alcanza el límite, se devolverán los datos en caché o el fallback local.</p><p>Ten en cuenta que en casos excepcionales este método puede agotar el tiempo de espera un poco después de lo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.</p><p>Para Android: puedes crear `TimeInterval` con funciones de extensión (como `5.seconds`, donde `.seconds` es de `import com.adapty.utils.seconds`), o `TimeInterval.seconds(5)`. Para no establecer ningún límite, usa `TimeInterval.INFINITE`.</p> | Parámetros de respuesta: | Parámetro | Descripción | | :-------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Paywall | Un objeto [`AdaptyPaywall`](https://react-native.adapty.io/interfaces/adaptypaywall) con una lista de IDs de productos, el identificador del paywall, Remote Config y varias otras propiedades. | ## Obtener la configuración de vista del paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important Asegúrate de activar el toggle **Show on device** en el Paywall Builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperar. ::: Después de obtener el paywall, comprueba si incluye una `ViewConfiguration`, lo que indica que fue creado con Paywall Builder. Esto te orientará sobre cómo mostrar el paywall. Si la `ViewConfiguration` está presente, trátalo como un paywall de Paywall Builder; si no, [manéjalo como un paywall de Remote Config](present-remote-config-paywalls-react-native). En el SDK de React Native, llama directamente al método `createPaywallView` sin necesidad de obtener la configuración de vista manualmente primero. :::warning El resultado del método `createPaywallView` solo puede usarse una vez. Si necesitas usarlo de nuevo, llama al método `createPaywallView` nuevamente. Llamarlo dos veces sin recrearlo puede provocar el error `AdaptyUIError.viewAlreadyPresented`. ::: ```typescript showLineNumbers // for the Adapty SDK < 3.14 – import {createPaywallView} from 'react-native-adapty/dist/ui'; if (paywall.hasViewConfiguration) { try { const view = await createPaywallView(paywall); } catch (error) { // handle the error } } else { //use your custom logic } ``` Parámetros: | Parámetro | Presencia | Descripción | | :------------------- | :------- | :----------------------------------------------------------- | | **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. | | **customTags** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas funcionan como marcadores de posición en el contenido del paywall, reemplazados dinámicamente con cadenas específicas para contenido personalizado. Consulta el tema [Etiquetas personalizadas en el Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | | **prefetchProducts** | opcional | Actívalo para optimizar el tiempo de visualización de los productos en pantalla. Cuando es `true`, AdaptyUI obtendrá automáticamente los productos necesarios. Por defecto: `false`. | :::note Si usas varios idiomas, aprende cómo añadir una [localización de Paywall Builder](add-paywall-locale-in-adapty-paywall-builder) y cómo usar los códigos de idioma correctamente [aquí](react-native-localizations-and-locale-codes). ::: Una vez que tengas la vista, [presenta el paywall](react-native-present-paywalls). ## Obtener un paywall para la audiencia predeterminada para cargarlo más rápido \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} Por lo general, los paywalls se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos en los que tienes numerosas audiencias y paywalls y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseable. En esas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ninguno. Para abordar esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener el paywall](#fetch-paywall-designed-with-paywall-builder) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: si necesitas mostrar diferentes paywalls para distintas versiones de la app (actual y futuras), puedes encontrarte con dificultades. Tendrás que diseñar paywalls compatibles con la versión actual (legacy) o aceptar que los usuarios con esa versión puedan encontrar problemas con paywalls que no se renderizan. - **Pérdida de segmentación**: todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluida la basada en países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una carga más rápida de paywalls, usa el método `getPaywallForDefaultAudience` de la siguiente manera. De lo contrario, usa `getPaywall` descrito [arriba](#fetch-paywall-designed-with-paywall-builder). ::: ```typescript showLineNumbers try { const id = 'YOUR_PLACEMENT_ID'; const locale = 'en'; const paywall = await adapty.getPaywallForDefaultAudience(id, locale); // the requested paywall } catch (error) { // handle the error } ``` :::note El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.2 del SDK de React Native. ::: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](react-native-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En ese caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra al desinstalarla o mediante una limpieza manual.</p> | ## Personalizar recursos \{#customize-assets\} Para personalizar imágenes y vídeos en tu paywall, implementa los recursos personalizados. Las imágenes y vídeos destacados tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de recursos personalizados, apuntas a estos elementos por sus IDs y personalizas su comportamiento. Para otras imágenes y vídeos, debes [establecer un ID personalizado](custom-media) en el dashboard de Adapty. Por ejemplo, puedes: - Mostrar una imagen o vídeo diferente a algunos usuarios. - Mostrar una imagen de vista previa local mientras se carga la imagen principal remota. - Mostrar una imagen de vista previa antes de reproducir un vídeo. :::important Para usar esta función, actualiza el SDK de React Native de Adapty a la versión 3.8.0 o superior. ::: Aquí tienes un ejemplo de cómo puedes proporcionar recursos personalizados mediante un diccionario sencillo: ```javascript const customAssets: Record<string, AdaptyCustomAsset> = { 'custom_image': { type: 'image', relativeAssetPath: 'custom_image.png' }, 'hero_video': { type: 'video', fileLocation: { ios: { fileName: 'custom_video.mp4' }, android: { relativeAssetPath: 'videos/custom_video.mp4' } } } }; view = await createPaywallView(paywall, { customAssets }) ``` :::note Si no se encuentra un recurso, el paywall volverá a su apariencia predeterminada. ::: --- # File: react-native-present-paywalls --- --- title: "React Native - Presentar paywalls nuevos creados con Paywall Builder" description: "Presenta paywalls en aplicaciones React Native usando Adapty." --- Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese paywall incluye tanto lo que debe mostrarse como la forma en que debe mostrarse. Antes de empezar, asegúrate de que: 1. Has [creado un paywall](create-paywall). 2. Has añadido el paywall a un [placement](placements). 3. Has [obtenido el paywall y preparado la vista](react-native-get-pb-paywalls). :::warning Esta guía es solo para **paywalls nuevos creados con Paywall Builder**, que requieren SDK v3.0 o posterior. El proceso para presentar paywalls varía según la versión del Paywall Builder utilizada y si son paywalls con Remote Config. - Para presentar **paywalls con Remote Config**, consulta [Renderizar un paywall diseñado con Remote Config](present-remote-config-paywalls). ::: El SDK de Adapty para React Native ofrece dos formas de presentar paywalls: - **Componente de React**: Un componente embebido que puedes integrar en la arquitectura y el sistema de navegación de tu app. - **Presentación modal** ## Componente de React \{#react-component\} :::note El enfoque de **componente de React** requiere SDK 3.14.0 o posterior. ::: Para incrustar un paywall dentro de tu árbol de componentes existente, utiliza el componente `AdaptyPaywallView` directamente en la jerarquía de componentes de React Native. El componente embebido te permite integrarlo en la arquitectura y el sistema de navegación de tu app. :::note En Android, si el paywall no se extiende detrás de la barra de estado, puede aparecer una superposición visual en su parte superior. Te recomendamos desactivarla para tus paywalls. Consulta [Superposición visual en la parte superior del paywall (Android)](#visual-overlay-at-the-top-of-the-paywall-android). ::: ```typescript showLineNumbers title="React Native (TSX)" function MyPaywall({ paywall }) { const paywallParams = useMemo(() => ({ loadTimeoutMs: 3000, }), []); const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => {}, []); const onProductSelected = useCallback<EventHandlers['onProductSelected']>((productId) => {}, []); const onPurchaseStarted = useCallback<EventHandlers['onPurchaseStarted']>((product) => {}, []); const onPurchaseCompleted = useCallback<EventHandlers['onPurchaseCompleted']>((purchaseResult, product) => {}, []); const onPurchaseFailed = useCallback<EventHandlers['onPurchaseFailed']>((error, product) => {}, []); const onRestoreStarted = useCallback<EventHandlers['onRestoreStarted']>(() => {}, []); const onRestoreCompleted = useCallback<EventHandlers['onRestoreCompleted']>((profile) => {}, []); const onRestoreFailed = useCallback<EventHandlers['onRestoreFailed']>((error) => {}, []); const onPaywallShown = useCallback<EventHandlers['onPaywallShown']>(() => {}, []); const onRenderingFailed = useCallback<EventHandlers['onRenderingFailed']>((error) => {}, []); const onLoadingProductsFailed = useCallback<EventHandlers['onLoadingProductsFailed']>((error) => {}, []); const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => {}, []); const onCustomAction = useCallback<EventHandlers['onCustomAction']>((actionId) => {}, []); const onWebPaymentNavigationFinished = useCallback<EventHandlers['onWebPaymentNavigationFinished']>(() => {}, []); return ( <AdaptyPaywallView paywall={paywall} params={paywallParams} style={styles.paywall} onCloseButtonPress={onCloseButtonPress} onProductSelected={onProductSelected} onPurchaseStarted={onPurchaseStarted} onPurchaseCompleted={onPurchaseCompleted} onPurchaseFailed={onPurchaseFailed} onRestoreStarted={onRestoreStarted} onRestoreCompleted={onRestoreCompleted} onRestoreFailed={onRestoreFailed} onPaywallShown={onPaywallShown} onRenderingFailed={onRenderingFailed} onLoadingProductsFailed={onLoadingProductsFailed} onCustomAction={onCustomAction} onUrlPress={onUrlPress} onWebPaymentNavigationFinished={onWebPaymentNavigationFinished} /> ); } ``` ## Presentación modal \{#modal-presentation\} Para mostrar un paywall como pantalla independiente, utiliza el método `view.present()` sobre el `view` creado por el método [`createPaywallView`](react-native-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `createPaywallView` una vez más para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo no está permitido. Dará lugar a un error `AdaptyUIError.viewAlreadyPresented`. ::: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> ```typescript showLineNumbers title="React Native (TSX)" const view = await createPaywallView(paywall); // Opcional: manejar eventos del paywall (cerrar, comprar, restaurar, etc.) // view.setEventHandlers({ ... }); try { await view.present(); } catch (error) { // manejar el error } ``` :::important Llamar a `setEventHandlers` varias veces sobrescribirá los handlers que hayas proporcionado, reemplazando tanto los predeterminados como los configurados anteriormente para esos eventos específicos. ::: </TabItem> <TabItem value="old" label="SDK version < 3.14" default> ```typescript showLineNumbers title="React Native (TSX)" const view = await createPaywallView(paywall); view.registerEventHandlers(); // manejar el cierre, etc. try { await view.present(); } catch (error) { // manejar el error } ``` </TabItem> </Tabs> ### Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el paywall en iOS pasando el parámetro `iosPresentationStyle` al método `present()`. El parámetro acepta los valores `'full_screen'` (predeterminado) o `'page_sheet'`. ```typescript showLineNumbers try { await view.present(iosPresentationStyle: 'page_sheet'); } catch (error) { // handle the error } ``` ## Usar un temporizador definido por el desarrollador \{#use-developer-defined-timer\} Para usar temporizadores definidos por el desarrollador en tu app, utiliza el `timerId`; en este ejemplo, `CUSTOM_TIMER_NY`, el **Timer ID** del temporizador definido por el desarrollador que configuraste en el Adapty dashboard. Esto garantiza que tu app actualice dinámicamente el temporizador con el valor correcto, como `13d 09h 03m 34s` (calculado como la hora de finalización del temporizador, por ejemplo Año Nuevo, menos la hora actual). <Tabs> <TabItem value="component" label="React component"> ```typescript showLineNumbers title="React Native (TSX)" const paywallParams = { customTimers: { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) } }; <AdaptyPaywallView paywall={paywall} params={paywallParams} // ... tus event handlers /> ``` </TabItem> <TabItem value="modal" label="Modal presentation"> ```typescript showLineNumbers title="React Native (TSX)" const customTimers = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) }; const view = await createPaywallView(paywall, { customTimers }); ``` </TabItem> </Tabs> En este ejemplo, `CUSTOM_TIMER_NY` es el **Timer ID** del temporizador definido por el desarrollador que configuraste en el Adapty dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente el temporizador con el valor correcto, como `13d 09h 03m 34s` (calculado como la hora de finalización del temporizador, por ejemplo Año Nuevo, menos la hora actual). ## Mostrar un diálogo \{#show-dialog\} Usa este método en lugar de los diálogos de alerta nativos cuando se muestra una vista de paywall en Android. En Android, las alertas normales de RN aparecen detrás de la vista del paywall, lo que las hace invisibles para los usuarios. Este método garantiza que el diálogo se muestre correctamente por encima del paywall en todas las plataformas. ```typescript showLineNumbers title="React Native (TSX)" try { const action = await view.showDialog({ title: 'Close paywall?', content: 'You will lose access to exclusive offers.', primaryActionTitle: 'Stay', secondaryActionTitle: 'Close', }); if (action === 'secondary') { // El usuario confirmó - cerrar el paywall await view.dismiss(); } // Si es primary - no hacer nada, el usuario se queda } catch (error) { // manejar el error } ``` ## Reemplazar una suscripción por otra \{#replace-one-subscription-with-another\} Cuando un usuario intenta comprar una nueva suscripción mientras tiene otra activa en Android, puedes controlar cómo debe gestionarse la nueva compra pasando parámetros de actualización de suscripción al crear la vista del paywall. Para reemplazar la suscripción actual por la nueva, usa `productPurchaseParams` en `createPaywallView` con los parámetros `oldSubVendorProductId` y `prorationMode`. ```typescript showLineNumbers title="React Native (TSX)" const productPurchaseParams = paywall.productIdentifiers.map((productId) => { let params = {}; if (Platform.OS === 'android') { params.android = { subscriptionUpdateParams: { oldSubVendorProductId: 'PRODUCT_ID_OF_THE_CURRENT_ACTIVE_SUBSCRIPTION', prorationMode: 'with_time_proration', }, }; } return { productId, params }; }); const view = await createPaywallView(paywall, { productPurchaseParams }); ``` ## Solución de problemas \{#troubleshooting\} ### Superposición visual en la parte superior del paywall (Android) \{#visual-overlay-at-the-top-of-the-paywall-android\} :::note Esta configuración es compatible a partir del SDK de React Native 3.15.5 y solo está disponible en proyectos bare de React Native. Si usas un flujo gestionado de Expo, no puedes añadir este recurso de Android directamente. Para aplicar esta configuración, debes crear un plugin de configuración personalizado de Expo que añada el recurso de Android correspondiente y registrarlo en app.config.js. Esto es necesario porque Expo gestiona el proyecto nativo de Android por ti. ::: Si `AdaptyPaywallView` no se extiende detrás de la barra de estado, puede aparecer una superposición visual en su parte superior. Para eliminarla, añade el siguiente recurso booleano a tu app: 1. Ve a `android/app/src/main/res/values`. Si no existe el archivo `bools.xml`, créalo. 2. Añade el siguiente recurso: ```xml <resources> <bool name="adapty_paywall_enable_safe_area_paddings">false</bool> </resources> ``` Ten en cuenta que los cambios se aplican de forma global a todos los paywalls de tu app. --- # File: react-native-handle-paywall-actions --- --- title: "Responder a acciones de botones en React Native SDK" description: "Gestiona las acciones de botones del paywall en React Native usando Adapty para mejorar la monetización de tu app." --- Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente: 1. Añade un [botón en el Paywall Builder](paywall-buttons) y asígnale una acción predefinida o crea un ID de acción personalizado. 2. Escribe código en tu app para gestionar cada acción que hayas asignado. Esta guía muestra cómo gestionar acciones personalizadas y predefinidas en tu código. :::warning **Solo las compras, restauraciones, cierres del paywall y apertura de URLs se gestionan automáticamente.** El resto de acciones de botones requieren una implementación adecuada en el código de la app. ::: ## Cerrar paywalls \{#close-paywalls\} Para añadir un botón que cierre tu paywall: 1. En el Paywall Builder, añade un botón y asígnale la acción **Close**. 2. En el código de tu app, implementa un handler para la acción `close` que descarte el paywall. :::info En el SDK de React Native, la acción `close` desencadena el cierre del paywall por defecto. Sin embargo, puedes cambiar este comportamiento en tu código si lo necesitas. Por ejemplo, cerrar un paywall puede desencadenar la apertura de otro. ::: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> Para el componente React, gestiona la acción de cierre mediante las props de handler de eventos individuales: ```javascript function MyPaywall({ paywall }) { const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => { // Handle close button press - navigate away or hide component navigation.goBack(); }, [navigation]); return ( <AdaptyPaywallView paywall={paywall} style={styles.container} onCloseButtonPress={onCloseButtonPress} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> Para la presentación modal, implementa el handler de cierre: ```javascript const view = await createPaywallView(paywall); const unsubscribe = view.setEventHandlers({ onCloseButtonPress() { return true; // allow paywall closing } }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14" default> Para la versión del SDK < 3.14, solo se admite la presentación modal: ```javascript const view = await createPaywallView(paywall); const unsubscribe = view.registerEventHandlers({ onCloseButtonPress() { return true; // allow paywall closing } }); ``` </TabItem> </Tabs> ## Abrir URLs desde paywalls \{#open-urls-from-paywalls\} :::tip Si quieres añadir un grupo de enlaces (por ejemplo, términos de uso y restauración de compras), añade un elemento **Link** en el Paywall Builder y gestíonalo igual que los botones con la acción **Open URL**. ::: Para añadir un botón que abra un enlace desde tu paywall (p. ej., **Terms of use** o **Privacy policy**): 1. En el Paywall Builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir. 2. En el código de tu app, implementa un handler para la acción `openUrl` que abra la URL recibida en un navegador. :::info En el SDK de React Native, la acción `openUrl` desencadena la apertura de la URL por defecto. Sin embargo, puedes cambiar este comportamiento en tu código si lo necesitas. ::: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> Para el componente React, gestiona la apertura de URLs mediante la prop de handler de eventos: ```javascript function MyPaywall({ paywall }) { const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => { Linking.openURL(url); }, []); return ( <AdaptyPaywallView paywall={paywall} style={styles.container} onUrlPress={onUrlPress} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> Para la presentación modal, implementa el handler de URL: ```javascript const view = await createPaywallView(paywall); const unsubscribe = view.setEventHandlers({ onUrlPress(url) { Linking.openURL(url); return false; // Keep paywall open }, }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14" default> Para la versión del SDK < 3.14, solo se admite la presentación modal: ```javascript const view = await createPaywallView(paywall); const unsubscribe = view.registerEventHandlers({ onUrlPress(url) { Linking.openURL(url); return false; // Keep paywall open }, }); ``` </TabItem> </Tabs> ## Iniciar sesión en la app \{#log-into-the-app\} Para añadir un botón que permita a los usuarios iniciar sesión en tu app: 1. En el Paywall Builder, añade un botón y asígnale la acción **Login**. 2. En el código de tu app, implementa un handler para la acción `login` que identifique a tu usuario. <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> Para el componente React, gestiona el inicio de sesión mediante la prop de handler de eventos: ```javascript function MyPaywall({ paywall }) { const onCustomAction = useCallback<EventHandlers['onCustomAction']>((actionId) => { if (actionId === 'login') { navigation.navigate('Login'); } }, [navigation]); return ( <AdaptyPaywallView paywall={paywall} style={styles.container} onCustomAction={onCustomAction} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> Para la presentación modal, implementa el handler de inicio de sesión: ```javascript const view = await createPaywallView(paywall); const unsubscribe = view.setEventHandlers({ onCustomAction(actionId) { if (actionId === 'login') { navigation.navigate('Login'); } } }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14" default> Para la versión del SDK < 3.14, solo se admite la presentación modal: ```javascript const view = await createPaywallView(paywall); const unsubscribe = view.registerEventHandlers({ onCustomAction(actionId) { if (actionId === 'login') { navigation.navigate('Login'); } } }); ``` </TabItem> </Tabs> ## Gestionar acciones personalizadas \{#handle-custom-actions\} Para añadir un botón que gestione cualquier otra acción: 1. En el Paywall Builder, añade un botón, asígnale la acción **Custom** y asígnale un ID. 2. En el código de tu app, implementa un handler para el ID de acción que hayas creado. Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> Para el componente React, gestiona las acciones personalizadas mediante la prop de handler de eventos: ```javascript function MyPaywall({ paywall }) { const onCustomAction = useCallback<EventHandlers['onCustomAction']>((actionId) => { if (actionId === 'openNewPaywall') { // Display another paywall } }, []); return ( <AdaptyPaywallView paywall={paywall} style={styles.container} onCustomAction={onCustomAction} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> Para la presentación modal, implementa los handlers de acciones personalizadas: ```javascript const unsubscribe = view.setEventHandlers({ onCustomAction(actionId) { if (actionId === 'openNewPaywall') { // Display another paywall } }, }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14" default> Para la versión del SDK < 3.14, solo se admite la presentación modal: ```javascript const unsubscribe = view.registerEventHandlers({ onCustomAction(actionId) { if (actionId === 'openNewPaywall') { // Display another paywall } }, }); ``` </TabItem> </Tabs> --- # File: react-native-handling-events-1 --- --- title: "React Native - Gestionar eventos del paywall" description: "Gestiona eventos de suscripción en React Native con el SDK de Adapty." --- :::important Esta guía cubre la gestión de eventos para compras, restauraciones, selección de productos y renderizado de paywalls. También debes implementar la gestión de botones (cerrar paywall, abrir enlaces, etc.). Consulta nuestra [guía sobre cómo gestionar acciones de botones](react-native-handle-paywall-actions) para más detalles. ::: Los paywalls configurados con el [Paywall Builder](adapty-paywall-builder) no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan algunos eventos a los que tu app puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.) así como notificaciones sobre acciones relacionadas con compras realizadas en el paywall. A continuación encontrarás cómo responder a estos eventos. :::warning Esta guía es solo para **paywalls del nuevo Paywall Builder**, que requieren el SDK de Adapty v3.0 o posterior. ::: Para controlar o monitorizar los procesos que ocurren en la pantalla del paywall dentro de tu app móvil, implementa los manejadores de eventos: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> Para el componente React, gestionas los eventos mediante props individuales de manejadores de eventos en el componente `AdaptyPaywallView`: ```typescript showLineNumbers title="React Native (TSX)" function MyPaywall({ paywall }) { const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => {}, []); const onProductSelected = useCallback<EventHandlers['onProductSelected']>((productId) => {}, []); const onPurchaseStarted = useCallback<EventHandlers['onPurchaseStarted']>((product) => {}, []); const onPurchaseCompleted = useCallback<EventHandlers['onPurchaseCompleted']>((purchaseResult, product) => {}, []); const onPurchaseFailed = useCallback<EventHandlers['onPurchaseFailed']>((error, product) => {}, []); const onRestoreStarted = useCallback<EventHandlers['onRestoreStarted']>(() => {}, []); const onRestoreCompleted = useCallback<EventHandlers['onRestoreCompleted']>((profile) => {}, []); const onRestoreFailed = useCallback<EventHandlers['onRestoreFailed']>((error) => {}, []); const onPaywallShown = useCallback<EventHandlers['onPaywallShown']>(() => {}, []); const onRenderingFailed = useCallback<EventHandlers['onRenderingFailed']>((error) => {}, []); const onLoadingProductsFailed = useCallback<EventHandlers['onLoadingProductsFailed']>((error) => {}, []); const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => { Linking.openURL(url); }, []); const onCustomAction = useCallback<EventHandlers['onCustomAction']>((actionId) => {}, []); const onWebPaymentNavigationFinished = useCallback<EventHandlers['onWebPaymentNavigationFinished']>(() => {}, []); return ( <AdaptyPaywallView paywall={paywall} style={styles.container} onCloseButtonPress={onCloseButtonPress} onProductSelected={onProductSelected} onPurchaseStarted={onPurchaseStarted} onPurchaseCompleted={onPurchaseCompleted} onPurchaseFailed={onPurchaseFailed} onRestoreStarted={onRestoreStarted} onRestoreCompleted={onRestoreCompleted} onRestoreFailed={onRestoreFailed} onPaywallShown={onPaywallShown} onRenderingFailed={onRenderingFailed} onLoadingProductsFailed={onLoadingProductsFailed} onUrlPress={onUrlPress} onCustomAction={onCustomAction} onWebPaymentNavigationFinished={onWebPaymentNavigationFinished} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> Para la presentación modal, implementa el método de manejadores de eventos. :::important Llamar a `setEventHandlers` varias veces sobreescribirá los manejadores que proporciones, reemplazando tanto los predeterminados como los configurados anteriormente para esos eventos específicos. ::: ```javascript showLineNumbers title="React Native (TSX)" const view = await createPaywallView(paywall); const unsubscribe = view.setEventHandlers({ onCloseButtonPress() { return true; }, onAndroidSystemBack() { return true; }, onPurchaseCompleted(purchaseResult, product) { return purchaseResult.type !== 'user_cancelled'; }, onPurchaseStarted(product) { /***/}, onPurchaseFailed(error) { /***/ }, onRestoreCompleted(profile) { /***/ }, onRestoreFailed(error, product) { /***/ }, onProductSelected(productId) { /***/}, onRenderingFailed(error) { /***/ }, onLoadingProductsFailed(error) { /***/ }, onUrlPress(url) { Linking.openURL(url); return false; // Keep paywall open }, onPaywallShown() { /***/ }, onPaywallClosed() { /***/ }, onWebPaymentNavigationFinished() { /***/ }, }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14" default> Para versiones del SDK anteriores a la 3.14, solo se admite la presentación modal: ```javascript showLineNumbers title="React Native (TSX)" const view = await createPaywallView(paywall); const unsubscribe = view.registerEventHandlers({ onCloseButtonPress() { return true; }, onAndroidSystemBack() { return true; }, onPurchaseCompleted(purchaseResult, product) { return purchaseResult.type !== 'user_cancelled'; }, onPurchaseStarted(product) { /***/}, onPurchaseFailed(error, product) { /***/ }, onRestoreCompleted(profile) { /***/ }, onRestoreFailed(error) { /***/ }, onProductSelected(productId) { /***/}, onRenderingFailed(error) { /***/ }, onLoadingProductsFailed(error) { /***/ }, onUrlPress(url) { Linking.openURL(url); return false; // Keep paywall open }, onPaywallShown() { /***/ }, onPaywallClosed() { /***/ }, onWebPaymentNavigationFinished() { /***/ }, }); ``` </TabItem> </Tabs> <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // onCloseButtonPress { "event": "close_button_press" } // onAndroidSystemBack { "event": "android_system_back" } // onUrlPress { "event": "url_press", "url": "https://example.com/terms" } // onCustomAction { "event": "custom_action", "actionId": "login" } // onProductSelected { "event": "product_selected", "productId": "premium_monthly" } // onPurchaseStarted { "event": "purchase_started", "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } // onPurchaseCompleted - Success { "event": "purchase_completed", "purchaseResult": { "type": "success", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } // onPurchaseCompleted - Cancelled { "event": "purchase_completed", "purchaseResult": { "type": "user_cancelled" }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } // onPurchaseFailed { "event": "purchase_failed", "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" }, "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } } // onRestoreCompleted { "event": "restore_completed", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } // onRestoreFailed { "event": "restore_failed", "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } // onRenderingFailed { "event": "rendering_failed", "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } // onLoadingProductsFailed { "event": "loading_products_failed", "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } // onPaywallShown { "event": "paywall_shown" } // onPaywallClosed { "event": "paywall_closed" } // onWebPaymentNavigationFinished { "event": "web_payment_navigation_finished" } ``` </Details> Puedes registrar los manejadores de eventos que necesites y omitir los que no uses. En ese caso, no se crearán los listeners para los eventos no utilizados. No hay manejadores de eventos obligatorios. Los manejadores de eventos devuelven un booleano. Si se devuelve `true`, el proceso de visualización se considera completado, por lo que la pantalla del paywall se cierra y los listeners de eventos de esa vista se eliminan. Algunos manejadores de eventos tienen un comportamiento predeterminado que puedes sobrescribir si lo necesitas: - `onCloseButtonPress`: cierra el paywall cuando se pulsa el botón de cierre. - `onUrlPress`: abre la URL pulsada y mantiene el paywall abierto. - `onAndroidSystemBack` (solo para presentación modal): cierra el paywall cuando se pulsa el botón **Back**. - `onRestoreCompleted`: cierra el paywall tras una restauración exitosa. - `onPurchaseCompleted`: cierra el paywall a menos que el usuario haya cancelado. - `onRenderingFailed`: cierra el paywall si falla su renderizado. ### Manejadores de eventos \{#event-handlers\} | Manejador de evento | Descripción | |:-----------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **onCustomAction** | Se invoca cuando el usuario realiza una acción personalizada, por ejemplo, pulsa un [botón personalizado](paywall-buttons). | | **onUrlPress** | Se invoca cuando el usuario pulsa una URL en tu paywall. | | **onAndroidSystemBack** | Solo presentación modal: se invoca cuando el usuario pulsa el botón **Back** del sistema Android. | | **onCloseButtonPress** | Se invoca cuando el botón de cierre es visible y el usuario lo pulsa. Se recomienda cerrar la pantalla del paywall en este manejador. | | **onPurchaseCompleted** | Se invoca cuando la compra finaliza, ya sea con éxito, cancelada por el usuario o pendiente de aprobación. En caso de compra exitosa, proporciona un `AdaptyProfile` actualizado. Las cancelaciones del usuario y los pagos pendientes (por ejemplo, que requieren aprobación parental) disparan este evento, no `onPurchaseFailed`. | | **onPurchaseStarted** | Se invoca cuando el usuario pulsa el botón de acción "Comprar" para iniciar el proceso de compra. | | **onPurchaseFailed** | Se invoca cuando una compra falla debido a errores (por ejemplo, restricciones de pago, productos no válidos, fallos de red, errores de verificación de transacciones). No se invoca para cancelaciones del usuario ni pagos pendientes, que disparan `onPurchaseCompleted` en su lugar. | | **onRestoreStarted** | Se invoca cuando el usuario inicia un proceso de restauración de compras. | | **onRestoreCompleted** | Se invoca cuando la restauración de compras se realiza correctamente y proporciona un `AdaptyProfile` actualizado. Se recomienda cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de la suscripción](react-native-listen-subscription-changes) para saber cómo comprobarlo. | | **onRestoreFailed** | Se invoca cuando el proceso de restauración falla y proporciona un `AdaptyError`. | | **onProductSelected** | Se invoca cuando se selecciona cualquier producto en la vista del paywall, lo que te permite monitorizar lo que el usuario elige antes de la compra. | | **onRenderingFailed** | Se invoca cuando ocurre un error durante el renderizado de la vista y proporciona un `AdaptyError`. Estos errores no deberían producirse; si te encuentras con uno, comunícanoslo. | | **onLoadingProductsFailed** | Se invoca cuando falla la carga de productos y proporciona un `AdaptyError`. Si no has configurado `prefetchProducts: true` al crear la vista, AdaptyUI recuperará los objetos necesarios del servidor por sí mismo. | | **onPaywallShown** | Se invoca cuando el paywall se muestra al usuario. En iOS, también se invoca cuando el usuario pulsa el [botón de web paywall](web-paywall#step-2a-add-a-web-purchase-button) dentro de un paywall y se abre un web paywall en un navegador in-app. | | **onPaywallClosed** | Solo presentación modal: se invoca cuando el usuario cierra el paywall. En iOS, también se invoca cuando un [web paywall](web-paywall#step-2a-add-a-web-purchase-button) abierto desde un paywall en un navegador in-app desaparece de la pantalla. | | **onWebPaymentNavigationFinished** | Se invoca tras intentar abrir un [web paywall](web-paywall) para realizar una compra, tanto si tuvo éxito como si no. | --- # File: react-native-use-fallback-paywalls --- --- title: "React Native - Usar paywalls de respaldo" description: "Gestiona los casos en que los usuarios están sin conexión o los servidores de Adapty no están disponibles." --- :::warning Los paywalls de respaldo son compatibles con el SDK de React Native v2.11 y versiones posteriores. ::: Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet. * **Si la aplicación no puede acceder a los servidores de Adapty:** Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding. * **Si la aplicación no puede acceder a internet:** Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar. :::important Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty. ::: ## Configuración \{#configuration\} ### Android \{#android\} 1. Añade el archivo de configuración de respaldo a tu aplicación. Elige uno de los siguientes directorios: * **android/app/src/main/assets/** * **android/app/src/main/res/raw/** Nota: La carpeta `res/raw` tiene una convención especial de nomenclatura de archivos (deben comenzar con una letra, sin mayúsculas, sin caracteres especiales excepto el guion bajo y sin espacios en los nombres). 2. Actualiza la propiedad `android` de la constante `FileLocation`: * Si el archivo está en el directorio `assets`, pasa la ruta del archivo relativa a ese directorio. * Si el archivo está en el directorio `res/raw`, pasa el nombre del archivo sin la extensión. ### iOS \{#ios\} 1. Añade el archivo JSON de respaldo al bundle de tu proyecto: abre el menú **File** en XCode y selecciona la opción **Add Files to "YourProjectName"**. 2. Pasa el nombre de tu archivo de configuración a la propiedad `ios` de la constante `FileLocation`. ## Ejemplo \{#example\} <Tabs groupId="current-os" queryString> <TabItem value="current" label="Current (v3.8+)" default> ```typescript showLineNumbers //after v3.8 const fileLocation = { ios: { fileName: 'ios_fallback.json' }, android: { //if the file is located in 'android/app/src/main/assets/' relativeAssetPath: 'android_fallback.json' } } await adapty.setFallback(fileLocation); ``` </TabItem> <TabItem value="old" label="Legacy (before v3.8)" default> ```typescript showLineNumbers //Legacy (before v3.8) const paywallsLocation = { ios: { fileName: 'ios_fallback.json' }, android: { //if the file is located in 'android/app/src/main/assets/' relativeAssetPath: 'android_fallback.json' } } await adapty.setFallbackPaywalls(paywallsLocation); ``` </TabItem> </Tabs> Parámetros: | Parámetro | Descripción | | :------------------- | :------------------------------------------------------- | | **fileLocation** | Objeto que representa la ubicación del archivo de configuración de respaldo. | --- # File: react-native-web-paywall --- --- title: "Implementar paywalls web" description: "Aprende cómo implementar paywalls web en tu app de React Native con el SDK de Adapty." --- :::important Antes de empezar, asegúrate de haber [configurado tu paywall web en el dashboard](web-paywall) e instalado la versión 3.6.1 o posterior del SDK de Adapty. ::: ## Abrir paywalls web \{#open-web-paywalls\} Si trabajas con un paywall que desarrollaste tú mismo, necesitas gestionar los paywalls web mediante el método del SDK. El método `.openWebPaywall`: 1. Genera una URL única que permite a Adapty vincular un paywall específico mostrado a un usuario concreto con la página web a la que se le redirige. 2. Detecta cuando tus usuarios vuelven a la app y luego llama a `.getProfile` a intervalos cortos para determinar si los derechos de acceso del perfil se han actualizado. De esta forma, si el pago se ha realizado con éxito y los derechos de acceso se han actualizado, la suscripción se activa en la app casi de inmediato. ```typescript showLineNumbers title="React Native (TSX)" try { await adapty.openWebPaywall(product); } catch (error) { console.warn('Failed to open web paywall:', error); } ``` :::note Existen dos versiones del método `openWebPaywall`: 1. `openWebPaywall(product)` que genera URLs por paywall y también añade los datos del producto a las URLs. 2. `openWebPaywall(paywall)` que genera URLs por paywall sin añadir los datos del producto a las URLs. Úsalo cuando tus productos en el paywall de Adapty sean distintos a los del paywall web. ::: #### Gestionar errores \{#handle-errors\} | Error | Descripción | Acción recomendada | |-----------------------------------------|--------------------------------------------------------------|--------------------------------------------------------------------------------------| | AdaptyError.paywallWithoutPurchaseUrl | El paywall no tiene configurada una URL de compra web | Comprueba si el paywall está correctamente configurado en el Adapty Dashboard | | AdaptyError.productWithoutPurchaseUrl | El producto no tiene una URL de compra web | Verifica la configuración del producto en el Adapty Dashboard | | AdaptyError.failedOpeningWebPaywallUrl | No se pudo abrir la URL en el navegador | Comprueba la configuración del dispositivo o proporciona un método de pago alternativo | | AdaptyError.failedDecodingWebPaywallUrl | No se pudieron codificar correctamente los parámetros en la URL | Verifica que los parámetros de la URL sean válidos y estén correctamente formateados | ## Abrir paywalls web en un navegador integrado \{#open-web-paywalls-in-an-in-app-browser\} :::important La apertura de paywalls web en un navegador integrado está disponible a partir del SDK de Adapty v. 3.15. ::: Por defecto, los paywalls web se abren en el navegador externo. Para ofrecer una experiencia de usuario fluida, puedes abrir los paywalls web en un navegador integrado. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin cambiar de app. Para activarlo, pasa `WebPresentation.BrowserInApp` como segundo argumento a `openWebPaywall`: ```typescript showLineNumbers title="React Native (TSX)" try { await adapty.openWebPaywall( product, WebPresentation.BrowserInApp, // default – WebPresentation.BrowserOutApp ); } catch (error) { console.warn('Failed to open web paywall:', error); } ``` --- # File: react-native-troubleshoot-paywall-builder --- --- title: "Solucionar problemas del Paywall Builder en el SDK de React Native" description: "Solucionar problemas del Paywall Builder en el SDK de React Native" --- Esta guía te ayuda a resolver los problemas más comunes al usar paywalls diseñados con el Adapty Paywall Builder en el SDK de React Native. ## Falla al obtener la configuración del paywall \{#getting-a-paywall-configuration-fails\} **Problema**: El método `getPaywallConfiguration` no consigue recuperar la configuración del paywall. **Causa**: El paywall no está habilitado para mostrarse en el dispositivo en el Paywall Builder. **Solución**: Activa el interruptor **Show on device** en el Paywall Builder. <img src="/assets/shared/img/show-on-device.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## El número de vistas del paywall es demasiado alto \{#the-paywall-view-number-is-too-big\} **Problema**: El recuento de vistas del paywall muestra el doble del número esperado. **Causa**: Es posible que estés llamando a `logShowPaywall` en tu código, lo que duplica el recuento de vistas si usas el Paywall Builder. Para los paywalls diseñados con el Paywall Builder, las analíticas se registran automáticamente, por lo que no es necesario usar este método. **Solución**: Asegúrate de no llamar a `logShowPaywall` en tu código si estás usando el Paywall Builder. ## Otros problemas \{#other-issues\} **Problema**: Estás experimentando otros problemas relacionados con el Paywall Builder que no se tratan arriba. **Solución**: Migra el SDK a la última versión siguiendo las [guías de migración](react-native-sdk-migration-guides) si es necesario. Muchos problemas se resuelven en versiones más recientes del SDK. --- # File: react-native-quickstart-manual --- --- title: "Habilitar compras en tu paywall personalizado en el SDK de React Native" description: "Integra el SDK de Adapty en tus paywalls personalizados de React Native para habilitar compras in-app." --- Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall, mientras el SDK de Adapty obtiene los productos, gestiona las nuevas compras y restaura las anteriores. :::important **Esta guía es para desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Paywall Builder de Adapty](react-native-quickstart-paywalls). Con el Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compras automáticamente y puedes probar distintos diseños sin volver a publicar tu app. ::: ## Antes de empezar \{#before-you-start\} ### Configura los productos \{#set-up-products\} Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 diferentes paywalls a distintos usuarios. Asegúrate de entender estos conceptos aunque trabajes con tu paywall personalizado. Básicamente, son tu manera de gestionar los productos que vendes en tu app. Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite recuperar tus productos. Para entender qué debes hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart). ### Gestión de usuarios \{#manage-users\} Puedes trabajar con o sin autenticación de backend de tu parte. Sin embargo, el SDK de Adapty gestiona los usuarios anónimos e identificados de forma diferente. Lee la [guía de inicio rápido de identificación](react-native-quickstart-identify) para entender los detalles y asegurarte de que estás trabajando con los usuarios correctamente. ## Paso 1. Obtén los productos \{#step-1-get-products\} Para recuperar los productos de tu paywall personalizado, necesitas: 1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`. 2. Obtener el array de productos para ese paywall usando el método `getPaywallProducts`. ```typescript showLineNumbers async function loadPaywall() { try { const paywall: AdaptyPaywall = await adapty.getPaywall('YOUR_PLACEMENT_ID'); const products: AdaptyPaywallProduct[] = await adapty.getPaywallProducts(paywall); // Use products to build your custom paywall UI } catch (error) { // Handle the error } } ``` ## Paso 2. Acepta las compras \{#step-2-accept-purchases\} Cuando un usuario pulsa en un producto de tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado. ```typescript showLineNumbers async function purchaseProduct(product: AdaptyPaywallProduct) { try { const purchaseResult: AdaptyPurchaseResult = await adapty.makePurchase(product); switch (purchaseResult.type) { case 'success': // Purchase successful, profile updated break; case 'user_cancelled': // User canceled the purchase break; case 'pending': // Purchase is pending (e.g., user will pay offline with cash) break; } } catch (error) { // Handle the error } } ``` ## Paso 3. Restaura las compras \{#step-3-restore-purchases\} Los stores de aplicaciones exigen que todas las apps con suscripciones ofrezcan una forma de que los usuarios puedan restaurar sus compras. Llama al método `restorePurchases` cuando el usuario pulse el botón de restaurar. Esto sincronizará su historial de compras con Adapty y devolverá el perfil actualizado. ```typescript showLineNumbers async function restorePurchases() { try { const profile: AdaptyProfile = await adapty.restorePurchases(); // Restore successful, profile updated } catch (error) { // Handle the error } } ``` ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> 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. Para ver cómo funciona esto en una implementación lista para producción, consulta el archivo [CustomPurchaseScreen.tsx](https://github.com/adaptyteam/AdaptySDK-React-Native/blob/master/examples/ExpoGoWebMock/src/CustomPurchaseScreen.tsx) en nuestra app de ejemplo, que muestra el manejo de compras con gestión de errores, estados de carga y gestión del estado de la UI. A continuación, [comprueba si los usuarios han completado su compra](react-native-check-subscription-status) para determinar si mostrar el paywall o conceder acceso a las funciones de pago. --- # File: fetch-paywalls-and-products-react-native --- --- title: "Obtener paywalls y productos para paywalls con Remote Config en React Native SDK" description: "Obtén paywalls y productos en el SDK de React Native de Adapty para mejorar la monetización de usuarios." --- Antes de mostrar paywalls con Remote Config y paywalls personalizados, necesitas obtener la información sobre ellos. Ten en cuenta que este tema se refiere a paywalls con Remote Config y paywalls personalizados. Para obtener orientación sobre cómo obtener paywalls creados con Paywall Builder, consulta [Obtener paywalls de Paywall Builder y su configuración](react-native-get-pb-paywalls). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a obtener paywalls y productos en tu app (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en el placement](create-placement) en el Adapty Dashboard. 4. [Instala el SDK de Adapty](sdk-installation-reactnative) en tu app. </details> ## Obtener información del paywall \{#fetch-paywall-information\} En Adapty, un [producto](product) es una combinación de productos tanto de App Store como de Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos en placements específicos de tu app. Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`. :::important **No pongas los IDs de productos en el código.** El único ID que debes incluir en el código es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe gestionar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana tres, muéstralos todos sin necesidad de cambios en el código. ::: ```typescript showLineNumbers try { const id = 'YOUR_PLACEMENT_ID'; const locale = 'en'; const paywall = await adapty.getPaywall(id, locale); // el paywall solicitado } catch (error) { // gestionar el error } ``` | Parámetro | Presencia | Descripción | |-------------------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](react-native-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de error. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, puede que los usuarios no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos, sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra al desinstalarla o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](react-native-use-fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN sea inaccesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeoutMs** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el respaldo local.</p><p></p><p>Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.</p> | ¡No pongas los IDs de productos en el código! Dado que los paywalls se configuran de forma remota, los productos disponibles, el número de productos y las ofertas especiales (como períodos de prueba gratuitos) pueden cambiar con el tiempo. Asegúrate de que tu código gestione estos escenarios. Por ejemplo, si inicialmente obtienes 2 productos, tu app debería mostrar esos 2 productos. Sin embargo, si más adelante obtienes 3 productos, tu app debería mostrar los 3 sin necesidad de cambios en el código. Lo único que debes incluir en el código es el ID del placement. Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://react-native.adapty.io/interfaces/adaptypaywall) con: una lista de IDs de productos, el identificador del paywall, Remote Config y varias otras propiedades. | ## Obtener productos \{#fetch-products\} Una vez que tienes el paywall, puedes consultar el array de productos que le corresponde: ```typescript showLineNumbers try { // ...paywall const products = await adapty.getPaywallProducts(paywall); // la lista de productos solicitada } catch (error) { // gestionar el error } ``` Parámetros de respuesta: | Parámetro | Descripción | | :-------- |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | Lista de objetos [`AdaptyPaywallProduct`](https://react-native.adapty.io/interfaces/adaptypaywallproduct) con: identificador del producto, nombre del producto, precio, moneda, duración de la suscripción y varias otras propiedades. | Al implementar tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://react-native.adapty.io/interfaces/adaptypaywallproduct). A continuación se muestran las propiedades más utilizadas, pero consulta el documento enlazado para obtener detalles completos sobre todas las propiedades disponibles. | Propiedad | Descripción | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Título** | Para mostrar el título del producto, usa `product.localizedTitle`. Ten en cuenta que la localización se basa en el país de la store seleccionado por el usuario, no en el idioma del dispositivo. | | **Precio** | Para mostrar una versión localizada del precio, usa `product.price?.localizedString`. Esta localización se basa en la configuración regional del dispositivo. También puedes acceder al precio como número usando `product.price?.amount`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda asociado, usa `product.price?.currencySymbol`. | | **Período de suscripción** | Para mostrar el período (p. ej. semana, mes, año, etc.), usa `product.subscription?.localizedSubscriptionPeriod`. Esta localización se basa en la configuración regional del dispositivo. Para obtener el período de suscripción de forma programática, usa `product.subscription?.subscriptionPeriod`. Desde ahí puedes acceder a la propiedad `unit` para obtener la duración (es decir, 'day', 'week', 'month', 'year' o 'unknown'). El valor `numberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral, verías `'month'` en la propiedad unit y `3` en la propiedad numberOfUnits. | | **Oferta introductoria** | Para mostrar una insignia u otro indicador de que una suscripción contiene una oferta introductoria, consulta la propiedad `product.subscription?.offer?.phases`. Esta es una lista que puede contener hasta dos fases de descuento: la fase de prueba gratuita y la fase de precio introductorio. Dentro de cada objeto de fase se encuentran las siguientes propiedades útiles:<br/>• `paymentMode`: una cadena con los valores `'free_trial'`, `'pay_as_you_go'`, `'pay_up_front'` y `'unknown'`. Los períodos de prueba gratuita serán del tipo `'free_trial'`.<br/>• `price`: el precio con descuento como número. Para períodos de prueba gratuita, busca `0` aquí.<br/>• `localizedNumberOfPeriods`: una cadena localizada según la configuración regional del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `'3 days'` en este campo.<br/>• `subscriptionPeriod`: alternativamente, puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que la sección anterior.<br/>• `localizedSubscriptionPeriod`: un período de suscripción formateado del descuento para la configuración regional del usuario. | ## Acelerar la obtención del paywall con el paywall de audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\} Normalmente, los paywalls se obtienen casi al instante, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos donde tienes numerosas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseable. En estas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún paywall. Para solucionar esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener información del paywall](fetch-paywalls-and-products-react-native#fetch-paywall-information) anterior. :::warning Por qué recomendamos usar `getPaywall` El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes: - **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar diferentes paywalls para distintas versiones de la app (la actual y las futuras), puedes encontrar dificultades. Tendrás que diseñar paywalls que sean compatibles con la versión actual (heredada) o aceptar que los usuarios con la versión actual (heredada) puedan tener problemas con paywalls que no se renderizan. - **Pérdida de segmentación**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluyendo por países, atribución de marketing o tus propios atributos personalizados). Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención más rápida de los paywalls, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, sigue usando `getPaywall` tal como se describe [arriba](fetch-paywalls-and-products-react-native#fetch-paywall-information). ::: ```typescript showLineNumbers try { const id = 'YOUR_PLACEMENT_ID'; const locale = 'en'; const paywall = await adapty.getPaywallForDefaultAudience(id, locale); // el paywall solicitado } catch (error) { // gestionar el error } ``` :::note El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.2 del SDK de React Native. ::: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](react-native-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de error. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, puede que los usuarios no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos, sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra al desinstalarla o mediante una limpieza manual.</p> | --- # File: present-remote-config-paywalls-react-native --- --- title: "Renderizar paywall con Remote Config en React Native SDK" description: "Descubre cómo presentar paywalls con Remote Config en Adapty React Native SDK para personalizar la experiencia del usuario." --- Si has personalizado un paywall usando Remote Config, tendrás que implementar el renderizado en el código de tu app para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú decides qué incluir y cómo se ve tu paywall. Proporcionamos un método para obtener la configuración remota, dándote autonomía para mostrar tu paywall personalizado configurado mediante Remote Config. ## Obtener el Remote Config del paywall y mostrarlo \{#get-paywall-remote-config-and-present-it\} Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores que necesites. ```typescript showLineNumbers try { const paywall = await adapty.getPaywall({ placementId: "YOUR_PLACEMENT_ID" }); const headerText = paywall.remoteConfig?.data?.["header_text"]; } catch (error) { // handle the error } ``` En este punto, una vez que hayas recibido todos los valores necesarios, es momento de renderizarlos y ensamblarlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a distintas pantallas y orientaciones de móvil, ofreciendo una experiencia fluida y cómoda en diferentes dispositivos. :::warning Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls-react-native#track-paywall-view-events) como se describe a continuación, para que Adapty Analytics pueda recopilar información para los embudos y las pruebas A/B. ::: Una vez que hayas terminado de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.makePurchase()` con el producto de tu paywall. Para más detalles sobre el método `.makePurchase()`, consulta [Realizar compras](react-native-making-purchases). Recomendamos [crear un paywall de respaldo llamado fallback paywall](react-native-use-fallback-paywalls). Este respaldo se mostrará al usuario cuando no haya conexión a internet ni caché disponible, garantizando una experiencia fluida incluso en esas situaciones. ## Registrar eventos de visualización del paywall \{#track-paywall-view-events\} Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque los datos de compras se recopilan automáticamente, registrar las visualizaciones del paywall requiere tu intervención, porque solo tú sabes cuándo un usuario ve un paywall. Para registrar un evento de visualización del paywall, simplemente llama a `.logShowPaywall(paywall)`, y quedará reflejado en las métricas de tu paywall en embudos y pruebas A/B. :::important No es necesario llamar a `.logShowPaywall(paywall)` si estás mostrando paywalls creados con el [Paywall Builder](adapty-paywall-builder). ::: ```typescript showLineNumbers await adapty.logShowPaywall(paywall); ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :------- |:--------------------------------------------------------------------------------------------| | **paywall** | obligatorio | Un objeto [`AdaptyPaywall`](https://react-native.adapty.io/interfaces/adaptypaywall). | --- # File: react-native-making-purchases --- --- title: "Realizar compras in-app en React Native SDK" description: "Guía sobre cómo gestionar compras in-app y suscripciones usando Adapty." --- Mostrar paywalls en tu app móvil es un paso esencial para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, con solo mostrar esos paywalls es suficiente para gestionar las compras únicamente si usas el [Paywall Builder](adapty-paywall-builder) para personalizar tus paywalls. Si no usas el Paywall Builder, debes utilizar un método independiente llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método es la puerta de entrada para que los usuarios interactúen con los paywalls y realicen sus transacciones. Si tu paywall tiene una oferta promocional activa para el producto que un usuario intenta comprar, Adapty la aplicará automáticamente en el momento de la compra. :::warning Ten en cuenta que la oferta introductoria solo se aplicará automáticamente si usas los paywalls configurados con el Paywall Builder. En otros casos, deberás [verificar la elegibilidad del usuario para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Saltarte este paso puede hacer que tu app sea rechazada durante la revisión. Además, podría suponer cobrar el precio completo a usuarios que tienen derecho a una oferta introductoria. ::: Asegúrate de haber [realizado la configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras. ## Realizar una compra \{#make-purchase\} :::note **¿Usas el [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente: puedes saltarte este paso. **¿Buscas una guía paso a paso?** Consulta la [guía de inicio rápido](react-native-implement-paywalls-manually) para instrucciones de implementación completas con todo el contexto. ::: ```typescript showLineNumbers 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 } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :-------- |:-------------------------------------------------------------------------------------------------------------------------------| | **Product** | requerido | Un objeto [`AdaptyPaywallProduct`](https://react-native.adapty.io/interfaces/adaptypaywallproduct) obtenido del paywall. | Parámetros de la respuesta: | Parámetro | Descripción | |---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** | <p>Si la solicitud fue exitosa, la respuesta contiene este objeto. Un objeto [AdaptyProfile](https://react-native.adapty.io/interfaces/adaptyprofile) proporciona información completa sobre los niveles de acceso, suscripciones y compras únicas de un usuario dentro de la app.</p><p>Comprueba el estado del nivel de acceso para verificar si el usuario tiene el acceso necesario a la app.</p> | :::warning **Nota:** si todavía usas la versión de StoreKit de Apple inferior a v2.0 y la versión del SDK de Adapty inferior a v.2.9.0, debes proporcionar el [secreto compartido de App Store de Apple](app-store-connection-configuration#step-5-enter-app-store-shared-secret) en su lugar. Apple ha declarado este método como obsoleto. ::: ## Cambiar la suscripción al realizar una compra \{#change-subscription-when-making-a-purchase\} Cuando un usuario elige una nueva suscripción en lugar de renovar la actual, el funcionamiento depende del store: - En App Store, la suscripción se actualiza automáticamente dentro del grupo de suscripciones. Si un usuario compra una suscripción de un grupo mientras ya tiene una de otro, ambas suscripciones estarán activas al mismo tiempo. - En Google Play, la suscripción no se actualiza automáticamente. Tendrás que gestionar el cambio en el código de tu app móvil como se describe a continuación. Para reemplazar la suscripción por otra en Android, llama al método `.makePurchase()` con el parámetro adicional: ```typescript showLineNumbers try { const purchaseResult = await adapty.makePurchase(product, params); 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 } ``` Parámetro adicional de la solicitud: | Parámetro | Presencia | Descripción | | :--------- | :-------- | :----------------------------------------------------------- | | **params** | requerido | un objeto de tipo [`MakePurchaseParamsInput`](https://react-native.adapty.io/types/makepurchaseparamsinput). | :::info **Versión 3.8.2+**: La estructura `MakePurchaseParamsInput` ha sido actualizada. `oldSubVendorProductId` y `prorationMode` ahora están anidados bajo `subscriptionUpdateParams`, e `isOfferPersonalized` se ha movido al nivel superior. Ejemplo: ```javascript makePurchase(product, { android: { subscriptionUpdateParams: { oldSubVendorProductId: 'old_product_id', prorationMode: 'charge_prorated_price' }, isOfferPersonalized: true } }); ``` ::: Puedes leer más sobre suscripciones y modos de reemplazo en la documentación de Google para desarrolladores: - [Acerca de los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-modes) - [Recomendaciones de Google para los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-recommendations) - Modo de reemplazo [`CHARGE_PRORATED_PRICE`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#CHARGE_PRORATED_PRICE()). Nota: este método solo está disponible para actualizaciones de suscripción. No se admiten degradaciones. - Modo de reemplazo [`DEFERRED`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#DEFERRED()). Nota: el cambio real de suscripción solo ocurrirá cuando finalice el período de facturación de la suscripción actual. ## Canjear códigos de oferta en iOS \{#redeem-offer-codes-in-ios\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Details> <summary>Sobre los códigos de oferta</summary> Los códigos de oferta te permiten dar descuentos o períodos de prueba gratuitos a usuarios concretos. A diferencia de las ofertas habituales, que se aplican de forma automática, los códigos de oferta se distribuyen fuera de la app — por email, redes sociales o materiales impresos. Los usuarios los canjean introduciendo el código en el App Store, accediendo a una URL de canje o a través de un diálogo dentro de la app. Para configurar códigos de oferta, abre una suscripción en App Store Connect y ve a su sección **Offer Codes**. Puedes crear [tres tipos](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes) de códigos de oferta: - **Free** — la suscripción es gratuita durante un período determinado y la siguiente renovación se cobra al precio completo. - **Pay as you go** — el usuario paga un precio reducido en cada ciclo de facturación durante un período determinado y, después, la suscripción se renueva al precio completo. - **Pay up front** — el usuario paga un precio único reducido por toda la duración de la oferta y, después, la suscripción se renueva al precio completo. No es necesario añadir los códigos de oferta a Adapty. Apple etiqueta cada transacción durante el período de la oferta con la categoría del código de oferta. Esto incluye el canje inicial y todas las renovaciones con descuento posteriores. Adapty detecta la etiqueta y registra cada transacción con la categoría de oferta `offer_code`. Cuando termina el período de oferta y la suscripción se renueva al precio completo, la etiqueta deja de estar presente. Puedes filtrar los análisis por el tipo de oferta **Offer Code** en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds). #### Resolución de discrepancias en los ingresos \{#revenue-discrepancy-troubleshooting\} Si observas que una transacción con código de oferta aparece en Adapty al precio completo del producto en lugar del precio reducido, verifica lo siguiente en App Store Connect: - El código de oferta tiene los precios correctos configurados para todas las regiones donde los usuarios pueden canjearlo. - El precio de la oferta está configurado para el país o región específica del usuario. Apple envía el precio regional en la transacción. Si no hay ningún precio regional configurado para la oferta, Apple puede enviar el precio completo del producto. Puedes filtrar y verificar las transacciones con código de oferta en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds) mediante los filtros de tipo de oferta **Offer Code** y **Offer Discount Type**. #### Códigos promocionales heredados (obsoletos) \{#legacy-promo-codes-deprecated\} <Callout type="warning"> Apple dejó obsoletos los códigos promocionales para compras in-app en marzo de 2026. Los códigos de oferta los sustituyen con más funcionalidades: elegibilidad configurable, fechas de expiración y hasta 1 millón de códigos por trimestre. Si antes usabas códigos promocionales para compras in-app, migra a los códigos de oferta en App Store Connect. </Callout> Los códigos promocionales heredados (limitados a 100 por app y versión) daban acceso gratuito a una suscripción. A diferencia de los códigos de oferta, Apple no incluía información de descuento en las transacciones con código promocional — enviaba el precio completo del producto en el recibo. Por ello, Adapty registraba estas transacciones al precio completo, lo que generaba discrepancias entre los análisis de Adapty y App Store Connect. Si ves transacciones históricas al precio completo que deberían haber sido gratuitas, es probable que provengan de códigos promocionales heredados. Como estos códigos ya están obsoletos, migra a los códigos de oferta para un seguimiento preciso de los ingresos. </Details> Para mostrar la hoja de canje de códigos en tu app: ```typescript showLineNumbers adapty.presentCodeRedemptionSheet(); ``` :::danger Según nuestras observaciones, la hoja de canje de códigos de oferta en algunas apps puede no funcionar de manera fiable. Recomendamos redirigir al usuario directamente a la App Store. Para hacerlo, debes abrir la URL con el siguiente formato: `https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}` ::: ## Gestionar planes prepagos (Android) \{#manage-prepaid-plans-android\} Si los usuarios de tu app pueden comprar [planes prepagos](https://developer.android.com/google/play/billing/subscriptions#prepaid-plans) (por ejemplo, adquirir una suscripción no renovable por varios meses), puedes habilitar las [transacciones pendientes](https://developer.android.com/google/play/billing/subscriptions#pending) para planes prepagos. ```typescript showLineNumbers adapty.activate("PUBLIC_SDK_KEY", { android: { pendingPrepaidPlansEnabled: true } }); ``` --- # File: react-native-restore-purchase --- --- title: "Restaurar compras en una app móvil con React Native SDK" description: "Aprende a restaurar compras en Adapty para garantizar una experiencia de usuario fluida." --- La restauración de compras tanto en iOS como en Android es una funcionalidad que permite a los usuarios recuperar el acceso a contenido adquirido anteriormente, como suscripciones o compras in-app, sin que se les vuelva a cobrar. Esta funcionalidad es especialmente útil para usuarios que hayan desinstalado y reinstalado la app, o que hayan cambiado a un nuevo dispositivo y quieran acceder a su contenido previamente comprado sin pagar de nuevo. :::note En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin que tengas que añadir código adicional. Si es tu caso, puedes saltarte este paso. ::: Para restaurar una compra cuando no utilizas el [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`: ```typescript showLineNumbers try { const profile = await adapty.restorePurchases(); const isSubscribed = profile.accessLevels['YOUR_ACCESS_LEVEL']?.isActive; if (isSubscribed) { // restore access } } catch (error) { // handle the error } ``` Parámetros de respuesta: | Parámetro | Descripción | |---------|-----------| | **Profile** | <p>Un objeto [`AdaptyProfile`](https://react-native.adapty.io/interfaces/adaptyprofile). Este modelo contiene información sobre niveles de acceso, suscripciones y compras únicas.</p><p>Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app.</p> | :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: implement-observer-mode-react-native --- --- title: "Implementar el modo Observer en React Native SDK" description: "Implementa el modo Observer en Adapty para rastrear eventos de suscripción de usuarios en React Native SDK." --- Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analíticas. Si esto cubre tus necesidades, solo tienes que: 1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. Sigue las instrucciones de configuración para [React Native](sdk-installation-reactnative). 2. [Reportar transacciones](report-transactions-observer-mode-react-native) desde tu infraestructura de compras existente a Adapty. ### Configuración del modo Observer \{#observer-mode-setup\} Activa el modo Observer si gestionas las compras y el estado de la suscripción tú mismo y usas Adapty solo para enviar eventos de suscripción y analíticas. :::important Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlas tú mismo. ::: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { observerMode: true, // Enable observer mode }); ``` Parámetros: | Parámetro | Descripción | | --------------------------- | ------------------------------------------------------------ | | observerMode | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor por defecto es `false`. | ## Usar los paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\} Si también quieres usar los paywalls y las funciones de pruebas A/B de Adapty, puedes hacerlo, pero requiere una configuración adicional en el modo Observer. Esto es lo que necesitas hacer además de los pasos anteriores: 1. Muestra los paywalls de la forma habitual para los [paywalls de Remote Config](present-remote-config-paywalls-react-native). 3. [Asocia los paywalls](report-transactions-observer-mode-react-native) con las transacciones de compra. --- # File: report-transactions-observer-mode-react-native --- --- title: "Reportar transacciones en Observer Mode en el SDK de React Native" description: "Reporta transacciones de compra en el Observer Mode de Adapty para obtener información sobre usuarios y seguimiento de ingresos en el SDK de React Native." --- <Tabs groupId="sdk-version" queryString> <TabItem value="current" label="Adapty SDK v3.4+ (current)" default> En Observer Mode, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Debes reportar las transacciones desde tu app store. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` para reportar explícitamente cada transacción y que Adapty pueda reconocerla. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra al paywall que la originó, garantizando análisis precisos del paywall. ```typescript showLineNumbers const variationId = paywall.variationId; try { await adapty.reportTransaction(transactionId, variationId); } catch (error) { // handle the `AdaptyError` } ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | transactionId | obligatorio | <ul><li> Para iOS: identificador de la transacción.</li><li> Para Android: identificador de cadena (`purchase.getOrderId`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.</li></ul> | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://react-native.adapty.io/interfaces/adaptypaywall). | </TabItem> <TabItem value="old" label="Adapty SDK 3.3.x (legacy)" default> En Observer Mode, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Debes reportar las transacciones desde tu app store o restaurarlas. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` en ambas plataformas para reportar explícitamente cada transacción, y usa `restorePurchases` en Android como paso adicional para asegurarte de que Adapty la reconozca. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a estos métodos, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra al paywall que la originó, garantizando análisis precisos del paywall. ```typescript showLineNumbers if (Platform.OS === 'android') { try { await adapty.restorePurchases(); } catch (error) { // handle the error } } ... const variationId = paywall.variationId; try { await adapty.reportTransaction(transactionId, variationId); } catch (error) { // handle the `AdaptyError` } ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | transactionId | obligatorio | <ul><li> Para iOS, StoreKit 1: un objeto [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction).</li><li> Para iOS, StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).</li><li> Para Android: identificador de cadena (`purchase.getOrderId`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.</li></ul> | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://react-native.adapty.io/interfaces/adaptypaywall). | </TabItem> <TabItem value="old2" label="Adapty SDK up to 3.2.x (legacy)" default> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> **Reportar transacciones** - Las versiones hasta la 3.1.x escuchan automáticamente las transacciones en el App Store, por lo que no se requiere reporte manual. - La versión 3.2 no admite Observer Mode. </TabItem> <TabItem value="kotlin" label="Android and Android-based cross-platforms" default> **Reportar transacciones** Usa `restorePurchases` para reportar una transacción a Adapty en Observer Mode, tal como se explica en la página [Restaurar compras en el código móvil](react-native-restore-purchase). :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `restorePurchases`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: </TabItem> </Tabs> **Asociar paywalls a transacciones** El SDK de Adapty no puede determinar el origen de las compras, ya que eres tú quien las procesa. Por lo tanto, si planeas usar paywalls y/o pruebas A/B en Observer Mode, debes asociar la transacción proveniente de tu app store con el paywall correspondiente en el código de tu app móvil. Es importante hacerlo correctamente antes de publicar tu app; de lo contrario, generará errores en los análisis. ```typescript const variationId = paywall.variationId; try { await adapty.setVariationId('transactionId', variationId); } catch (error) { // handle the `AdaptyError` } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | ------------- | --------- | ------------------------------------------------------------ | | transactionId | obligatorio | <p>Para iOS, StoreKit 1: un objeto [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction).</p><p>Para iOS, StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).</p><p>Para Android: identificador de cadena (purchase.getOrderId de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.</p> | | variationId | obligatorio | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://react-native.adapty.io/interfaces/adaptypaywall). | </TabItem> </Tabs> --- # File: react-native-troubleshoot-purchases --- --- title: "Solucionar problemas de compras en React Native SDK" description: "Solucionar problemas de compras en React Native SDK" --- Esta guía te ayuda a resolver los problemas más comunes al implementar compras manualmente en el SDK de React Native. ## makePurchase se ejecuta correctamente, pero el perfil no se actualiza \{#makepurchase-is-called-successfully-but-the-profile-is-not-being-updated\} **Problema**: El método `makePurchase` se completa correctamente, pero el perfil del usuario y el estado de la suscripción no se actualizan en Adapty. **Causa**: Normalmente indica que la configuración de Google Play Store está incompleta o tiene errores. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## makePurchase se invoca dos veces \{#makepurchase-is-invoked-twice\} **Problema**: El método `makePurchase` se está llamando varias veces para la misma compra. **Causa**: Esto suele ocurrir cuando el flujo de compra se activa varias veces por problemas de gestión del estado de la UI o interacciones rápidas del usuario. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## AdaptyError.cantMakePayments en modo observer \{#adaptyelrorcantmakepayments-in-observer-mode\} **Problema**: Recibes `AdaptyError.cantMakePayments` al usar `makePurchase` en modo observer. **Causa**: En el modo observer, debes gestionar las compras por tu cuenta, no usar el método `makePurchase` de Adapty. **Solución**: Si usas `makePurchase` para las compras, desactiva el modo observer. Tienes que elegir entre usar `makePurchase` o gestionar las compras por tu cuenta en el modo observer. Consulta [Implementar el modo Observer](implement-observer-mode-react-native) para más detalles. ## Error de Adapty: (code: 103, message: Play Market request failed on purchases updated: responseCode=3, debugMessage=Billing Unavailable, detail: null) \{#adapty-error-code-103-message-play-market-request-failed-on-purchases-updated-responsecode3-debugmessagebilling-unavailable-detail-null\} **Problema**: Estás recibiendo un error de facturación no disponible de Google Play Store. **Causa**: Este error no está relacionado con Adapty. Es un error de la Biblioteca de Facturación de Google Play que indica que la facturación no está disponible en el dispositivo. **Solución**: Este error no está relacionado con Adapty. Puedes consultar más información en la documentación de Play Store: [Handle BillingResult response codes](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) | Play Billing | Android Developers. ## No se encuentran makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\} **Problema**: Estás encontrando problemas porque no se encuentran los `makePurchasesCompletionHandlers`. **Causa**: Esto suele estar relacionado con problemas en las pruebas en sandbox. **Solución**: Crea un nuevo usuario sandbox e inténtalo de nuevo. Esto suele resolver los problemas del manejador de finalización de compras relacionados con el sandbox. ## Otros problemas \{#other-issues\} **Problema**: Experimentas otros problemas relacionados con las compras que no se tratan arriba. **Solución**: Migra el SDK a la última versión siguiendo las [guías de migración](react-native-sdk-migration-guides) si es necesario. Muchos problemas se resuelven en versiones más recientes del SDK. --- # File: react-native-identifying-users --- --- title: "Identificar usuarios en el SDK de React Native" description: "Aprende cómo identificar usuarios en tu app de React Native con el SDK de Adapty." --- Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, debes establecer tu propio Customer User ID. Puedes encontrar usuarios por su Customer User ID en la sección [Perfiles](profiles-crm) y usarlo en la [API del lado del servidor](getting-started-with-server-side-api), que se enviará a todas las integraciones. ### Establecer el ID de usuario en la configuración \{#setting-customer-user-id-on-configuration\} Si tienes un ID de usuario durante la configuración, pásalo como parámetro `customerUserId` al método `.activate()`: ```typescript showLineNumbers adapty.activate("PUBLIC_SDK_KEY", { customerUserId: "YOUR_USER_ID" }); ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Configurar el ID de usuario del cliente tras la configuración \{#setting-customer-user-id-after-configuration\} Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo más adelante en cualquier momento con el método `.identify()`. Los casos más habituales para usar este método son después del registro o la autenticación, cuando el usuario pasa de ser anónimo a estar autenticado. ```typescript showLineNumbers try { await adapty.identify("YOUR_USER_ID"); // successfully identified } catch (error) { // handle the error } ``` Parámetros de la solicitud: - **Customer User ID** (obligatorio): un identificador de usuario de tipo string. :::warning Reenvío de datos significativos del usuario En algunos casos, como cuando un usuario vuelve a iniciar sesión en su cuenta, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si pasaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, deberás reenviar esos datos para el usuario identificado. También es importante tener en cuenta que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes. ::: ### Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\} Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`: ```typescript showLineNumbers try { await adapty.logout(); // successful logout } catch (error) { // handle the error } ``` Luego puedes iniciar sesión del usuario usando el método `.identify()`. ## Asignar `appAccountToken` (iOS) \{#assign-appaccounttoken-ios\} [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un **UUID** que te permite vincular las transacciones del App Store con la identidad interna de tus usuarios. StoreKit asocia este token con cada transacción, de modo que tu backend puede relacionar los datos del App Store con tus usuarios. Usa un UUID estable generado por usuario y reutilízalo para la misma cuenta en todos los dispositivos. Así te aseguras de que las compras y las notificaciones del App Store queden correctamente vinculadas. Puedes establecer el token de dos maneras: durante la activación del SDK o al identificar al usuario. :::important Siempre debes pasar `appAccountToken` junto con `customerUserId`. Si solo pasas el token, no se incluirá en la transacción. ::: ```typescript showLineNumbers // Durante la configuración: adapty.activate("PUBLIC_SDK_KEY", { customerUserId: "YOUR_USER_ID", ios: { appAccountToken: "YOUR_APP_ACCOUNT_TOKEN" }, }); // O al identificar usuarios try { await adapty.identify("YOUR_USER_ID", { ios: {appAccountToken: 'YOUR_APP_ACCOUNT_TOKEN'} }); // identificado correctamente } catch (error) { // gestiona el error } ``` ### Establecer IDs de cuenta ofuscados (Android) \{#set-obfuscated-account-ids-android\} Google Play requiere IDs de cuenta ofuscados en ciertos casos de uso para mejorar la privacidad y seguridad del usuario. Estos IDs ayudan a Google Play a identificar las compras manteniendo el anonimato de la información del usuario, lo que resulta especialmente importante para la prevención del fraude y el análisis de datos. Es posible que necesites establecer estos IDs si tu aplicación maneja datos de usuario sensibles o si debes cumplir con normativas de privacidad específicas. Los IDs ofuscados permiten a Google Play hacer seguimiento de las compras sin exponer los identificadores reales de los usuarios. ```typescript showLineNumbers // Durante la configuración: adapty.activate("PUBLIC_SDK_KEY", { customerUserId: "YOUR_USER_ID", android: { obfuscatedAccountId: 'YOUR_OBFUSCATED_ACCOUNT_ID' } }); // O al identificar usuarios try { await adapty.identify("YOUR_USER_ID", { android: { obfuscatedAccountId: 'YOUR_OBFUSCATED_ACCOUNT_ID' } }); // identificado correctamente } catch (error) { // gestionar el error } ``` ## Detectar usuarios en varios dispositivos \{#detect-users-across-devices\} --- no_index: true --- Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`. Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID. **Lo que Adapty puede detectar entre dispositivos** | Tu configuración | Lo que Adapty detecta | Lo que debes hacer | | --- | --- | --- | | Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. | | Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. | | Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. | | Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. | **Restaurar compras en un dispositivo nuevo** Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK. No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado. --- # File: react-native-setting-user-attributes --- --- title: "Establecer atributos de usuario en el SDK de React Native" description: "Aprende cómo actualizar atributos de usuario y datos de perfil en tu app de React Native con el SDK de Adapty." --- Puedes establecer atributos opcionales como correo electrónico, número de teléfono, etc., para los usuarios de tu app. Luego puedes usar esos atributos para crear [segmentos](segments) de usuarios o simplemente verlos en el CRM. ### Establecer atributos de usuario \{#setting-user-attributes\} Para establecer atributos de usuario, llama al método `.updateProfile()`: ```typescript showLineNumbers // Only for TypeScript validation const params: AdaptyProfileParameters = { email: 'email@email.com', phoneNumber: '+18888888888', firstName: 'John', lastName: 'Appleseed', gender: 'other', birthday: new Date().toISOString(), }; try { await adapty.updateProfile(params); } catch (error) { // handle `AdaptyError` } ``` Ten en cuenta que los atributos que hayas establecido previamente con el método `updateProfile` no se restablecerán. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Lista de claves permitidas \{#the-allowed-keys-list\} Las claves permitidas `<Key>` de `AdaptyProfileParameters.Builder` y los valores `<Value>` correspondientes se indican a continuación: | Clave | Valor | |---|-----| | <p>email</p><p>phoneNumber</p><p>firstName</p><p>lastName</p> | String | | gender | Enum, los valores permitidos son: `female`, `male`, `other` | | birthday | Date | ### Atributos de usuario personalizados \{#custom-user-attributes\} Puedes definir tus propios atributos personalizados. Estos suelen estar relacionados con el uso de tu app. Por ejemplo, en aplicaciones de fitness pueden ser el número de ejercicios por semana; en apps de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes usarlos en segmentos para crear paywalls y ofertas dirigidas, y también en analíticas para determinar qué métricas de producto influyen más en los ingresos. ```typescript showLineNumbers try { await adapty.updateProfile({ codableCustomAttributes: { key_1: 'value_1', key_2: 2, }, }); } catch (error) { // handle `AdaptyError` } ``` Para eliminar una clave existente, usa el método `.withRemoved(customAttributeForKey:)`: ```typescript showLineNumbers try { // to remove a key, pass null as its value await adapty.updateProfile({ codableCustomAttributes: { key_1: null, key_2: null, }, }); } catch (error) { // handle `AdaptyError` } ``` A veces necesitas saber qué atributos personalizados ya están configurados. Para ello, usa el campo `customAttributes` del objeto `AdaptyProfile`. :::warning Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor podrían haber cambiado después de la última sincronización. ::: ### Límites \{#limits\} - Hasta 30 atributos personalizados por usuario - Los nombres de clave pueden tener hasta 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.` - El valor puede ser una cadena de texto o un número flotante con un máximo de 50 caracteres. --- # File: react-native-listen-subscription-changes --- --- title: "Comprobar el estado de suscripción en el SDK de React Native" description: "Haz seguimiento y gestión del estado de suscripción del usuario en Adapty para mejorar la retención de clientes en tu app de React Native." --- Con Adapty, hacer seguimiento del estado de suscripción es muy sencillo. No tienes que insertar manualmente los IDs de producto en tu código. En su lugar, puedes comprobar fácilmente el estado de suscripción de un usuario verificando si tiene un [nivel de acceso](access-level) activo. <details> <summary>Antes de empezar a comprobar el estado de suscripción (haz clic para ampliar)</summary> - Para iOS, configura las [notificaciones del servidor de App Store](enable-app-store-server-notifications) - Para Android, configura las [notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn) </details> ## Nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptyprofile-object\} Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://react-native.adapty.io/interfaces/adaptyprofile). Te recomendamos recuperar el perfil cuando arranque tu app, por ejemplo cuando [identificas a un usuario](react-native-identifying-users#setting-customer-user-id-on-configuration), y actualizarlo cada vez que se produzcan cambios. Así podrás usar el objeto de perfil sin tener que solicitarlo repetidamente. Para recibir notificaciones de las actualizaciones del perfil, escucha los cambios de perfil tal como se describe en la sección [Escuchar actualizaciones del perfil, incluidos los niveles de acceso](react-native-listen-subscription-changes) que encontrarás más abajo. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\} Para obtener el nivel de acceso desde el servidor, usa el método `.getProfile()`: ```typescript showLineNumbers try { const profile = await adapty.getProfile(); } catch (error) { // handle the error } ``` Parámetros de respuesta: | Parámetro | Descripción | | --------- | ------------------------------------------------------------ | | Profile | <p>Un objeto [AdaptyProfile](https://react-native.adapty.io/interfaces/adaptyprofile). Por lo general, solo necesitas comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app.</p><p></p><p>El método `.getProfile` devuelve el resultado más actualizado, ya que siempre intenta consultar la API. Si por algún motivo (por ejemplo, sin conexión a internet) el SDK de Adapty no puede recuperar información del servidor, se devolverán los datos de la caché. También es importante tener en cuenta que el SDK de Adapty actualiza la caché de `AdaptyProfile` de forma periódica para mantener esta información lo más actualizada posible.</p> | El método `.getProfile()` te proporciona el perfil de usuario desde el que puedes obtener el estado del nivel de acceso. Puedes tener múltiples niveles de acceso por app. Por ejemplo, si tienes una app de noticias y vendes suscripciones a diferentes temáticas de forma independiente, puedes crear niveles de acceso "sports" y "science". Pero la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes usar simplemente el nivel de acceso predeterminado "premium". Aquí tienes un ejemplo para comprobar el nivel de acceso "premium" predeterminado: ```typescript showLineNumbers try { const profile = await adapty.getProfile(); const isActive = profile.accessLevels?.["premium"]?.isActive; if (isActive) { // grant access to premium features } } catch (error) { // handle the error } ``` ### Escuchar actualizaciones del estado de suscripción \{#listening-for-subscription-status-updates\} Cada vez que cambia la suscripción del usuario, Adapty lanza un evento. Para recibir mensajes de Adapty, necesitas realizar algunas configuraciones adicionales: ```typescript showLineNumbers // Create an "onLatestProfileLoad" event listener adapty.addEventListener('onLatestProfileLoad', profile => { // handle any changes to subscription state }); ``` Adapty también lanza un evento al iniciar la aplicación. En ese caso, se pasará el estado de suscripción almacenado en caché. ### Caché del estado de suscripción \{#subscription-status-cache\} La caché implementada en el SDK de Adapty almacena el estado de suscripción del perfil. Esto significa que, aunque el servidor no esté disponible, se puede acceder a los datos en caché para obtener información sobre el estado de suscripción del perfil. No obstante, hay que tener en cuenta que no es posible solicitar datos directamente desde la caché. El SDK consulta el servidor periódicamente cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si hay alguna modificación, como nuevas transacciones u otras actualizaciones, se enviarán a los datos en caché para mantenerlos sincronizados con el servidor. --- # File: react-native-deal-with-att --- --- title: "Gestionar ATT en el SDK de React Native" description: "Empieza con Adapty en React Native para simplificar la configuración y gestión de suscripciones." --- Si tu aplicación utiliza el framework AppTrackingTransparency y muestra al usuario una solicitud de autorización de seguimiento de la app, debes enviar el [estado de autorización](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/) a Adapty. ```typescript showLineNumbers try { await adapty.updateProfile({ // you can also pass a string value (validated via tsc) if you prefer appTrackingTransparencyStatus: AppTrackingTransparencyStatus.Authorized, }); } catch (error) { // handle `AdaptyError` } ``` :::warning Te recomendamos encarecidamente que envíes este valor lo antes posible cuando cambie; solo así los datos se enviarán a tiempo a las integraciones que hayas configurado. ::: --- # File: kids-mode-react-native --- --- title: "Modo Niños en React Native SDK" description: "Activa fácilmente el Modo Niños para cumplir con las políticas de Apple y Google. Sin recopilación de IDFA, GAID ni datos publicitarios en el SDK de React Native." --- Si tu aplicación React Native está dirigida a niños, debes cumplir las políticas de [Apple](https://developer.apple.com/kids/) y [Google](https://support.google.com/googleplay/android-developer/answer/9893335). Si usas el SDK de Adapty, unos pocos pasos sencillos te permitirán configurarlo para cumplir con estas políticas y superar las revisiones de la tienda. ## ¿Qué se requiere? \{#whats-required\} Necesitas configurar el SDK de Adapty para deshabilitar la recopilación de: - [IDFA (Identifier for Advertisers)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers) (iOS) - [Android Advertising ID (AAID/GAID)](https://support.google.com/googleplay/android-developer/answer/6048248) (Android) - [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) Además, te recomendamos utilizar el ID de usuario del cliente con cuidado. Un ID de usuario con el formato `<NombreApellido>` se considerará definitivamente como recopilación de datos personales, al igual que el uso del correo electrónico. Para el Modo Niños, la mejor práctica es utilizar identificadores aleatorios o anonimizados (por ejemplo, IDs hasheados o UUIDs generados por el dispositivo) para garantizar el cumplimiento. ## Activar el Modo Niños \{#enabling-kids-mode\} ### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\} En el Adapty Dashboard, debes deshabilitar la recopilación de direcciones IP. Para ello, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** en la sección **Collect users' IP address**. ### Cambios en el código de tu aplicación móvil \{#updates-in-your-mobile-app-code\} ¡La compatibilidad con el Modo Niños en React Native estará disponible próximamente! Por ahora, puedes consultar las guías nativas de cada plataforma: - [Modo Niños en iOS SDK](kids-mode) para la configuración en iOS - [Modo Niños en Android SDK](kids-mode-android) para la configuración en Android --- # File: react-native-get-onboardings --- --- title: "Obtener onboardings en React Native SDK" description: "Aprende cómo recuperar onboardings en Adapty para React Native." --- Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el builder en el Adapty Dashboard, puedes mostrarlo en tu app de React Native. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, como se describe a continuación. Antes de comenzar, asegúrate de que: 1. Tienes instalado el [SDK de Adapty para React Native](sdk-installation-reactnative) en la versión 3.8.0 o superior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). ## Obtener el onboarding \{#fetch-onboarding\} Cuando creas un [onboarding](onboardings) con nuestro builder sin código, se almacena como un contenedor con configuración que tu app debe obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a preguntas o entradas de formularios). El contenedor también registra automáticamente los eventos de analíticas, por lo que no necesitas implementar un seguimiento de vistas por separado. Para un rendimiento óptimo, obtén la configuración del onboarding con antelación para que las imágenes tengan tiempo suficiente de descargarse antes de mostrárselas a los usuarios. Para obtener un onboarding, usa el método `getOnboarding`: ```typescript showLineNumbers try { const placementId = 'YOUR_PLACEMENT_ID'; const locale = 'en'; const onboarding = await adapty.getOnboarding(placementId, locale); // the requested onboarding } catch (error) { // handle the error } ``` Luego, llama al método `createOnboardingView` para crear una instancia de vista. :::warning El resultado del método `createOnboardingView` solo puede usarse una vez. Si necesitas volver a usarlo, llama de nuevo al método `createOnboardingView`. Llamarlo dos veces sin recrearlo puede provocar el error `AdaptyUIError.viewAlreadyPresented`. ::: ```typescript showLineNumbers // for the Adapty SDK < 3.14 – import {createOnboardingView} from 'react-native-adapty/dist/ui'; if (onboarding.hasViewConfiguration) { try { const view = await createOnboardingView(onboarding); } catch (error) { // handle the error } } else { //use your custom logic } ``` Parámetros: | Parámetro | Presencia | Descripción | |-------------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | requerido | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizations and locale codes](localizations-and-locale-codes) para más información sobre los códigos de localización y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen conexión inestable a internet, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo inestable que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN sea inaccesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeoutMs** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el fallback local.</p><p>Ten en cuenta que, en casos excepcionales, este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.</p> | Parámetros de respuesta: | Parámetro | Descripción | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | Un objeto [`AdaptyOnboarding`](https://react-native.adapty.io/interfaces/adaptyonboarding) con: el identificador y la configuración del onboarding, Remote Config y otras propiedades. | ## Acelera la obtención del onboarding con el onboarding de audiencia predeterminada \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} Normalmente, los onboardings se obtienen casi al instante, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos donde tienes numerosas audiencias y onboardings, y tus usuarios tienen una conexión a internet débil, obtener un onboarding puede tardar más de lo deseado. En esas situaciones, puede que quieras mostrar un onboarding predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún onboarding. Para abordar esto, puedes usar el método `getOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, como se detalla en la sección [Obtener el onboarding](#fetch-onboarding) anterior. :::warning Considera usar `getOnboarding` en lugar de `getOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: Puede generar problemas al dar soporte a varias versiones de la app, lo que requiere diseños retrocompatibles o aceptar que las versiones más antiguas podrían mostrarse incorrectamente. - **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación basada en país, atribución o atributos personalizados. Si una obtención más rápida compensa estos inconvenientes para tu caso de uso, utiliza `getOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getOnboarding` como se describe [arriba](#fetch-onboarding). ::: ```typescript showLineNumbers try { const placementId = 'YOUR_PLACEMENT_ID'; const locale = 'en'; const onboarding = await adapty.getOnboardingForDefaultAudience(placementId, locale); // the requested onboarding } catch (error) { // handle the error } ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | requerido | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizations and locale codes](localizations-and-locale-codes) para más información sobre los códigos de localización y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen conexión inestable a internet, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo inestable que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN sea inaccesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | --- # File: react-native-present-onboardings --- --- title: "Presentar onboardings en React Native SDK" description: "Descubre cómo presentar onboardings en React Native para aumentar las conversiones y los ingresos." --- Si has personalizado un onboarding con el builder, no tienes que preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese onboarding ya contiene tanto lo que debe mostrarse como la forma en que debe hacerlo. Antes de empezar, asegúrate de que: 1. Tienes instalado [Adapty React Native SDK](sdk-installation-reactnative) 3.8.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). El SDK de Adapty para React Native ofrece dos formas de presentar onboardings: - **Componente React**: Un componente embebido que te permite integrarlo en la arquitectura y el sistema de navegación de tu app. - **Presentación modal** ## Componente React \{#react-component\} Para insertar un onboarding dentro de tu árbol de componentes existente, usa el componente `AdaptyOnboardingView` directamente en la jerarquía de componentes de React Native. El componente embebido te permite integrarlo en la arquitectura y el sistema de navegación de tu app. :::note En Android, recomendamos una configuración adicional para `AdaptyOnboardingView` con el fin de evitar un artefacto visual en el renderizado. Consulta [La UI del sistema se superpone al contenido del onboarding en Android](#system-ui-overlaps-onboarding-content-on-android). ::: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const onAnalytics = useCallback<OnboardingEventHandlers['onAnalytics']>((event, meta) => {}, []); const onClose = useCallback<OnboardingEventHandlers['onClose']>((actionId, meta) => {}, []); const onCustom = useCallback<OnboardingEventHandlers['onCustom']>((actionId, meta) => {}, []); const onPaywall = useCallback<OnboardingEventHandlers['onPaywall']>((actionId, meta) => {}, []); const onStateUpdated = useCallback<OnboardingEventHandlers['onStateUpdated']>((action, meta) => {}, []); const onFinishedLoading = useCallback<OnboardingEventHandlers['onFinishedLoading']>((meta) => {}, []); const onError = useCallback<OnboardingEventHandlers['onError']>((error) => {}, []); return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} onAnalytics={onAnalytics} onClose={onClose} onCustom={onCustom} onPaywall={onPaywall} onStateUpdated={onStateUpdated} onFinishedLoading={onFinishedLoading} onError={onError} /> ); } ``` </TabItem> <TabItem value="old" label="SDK version < 3.14" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { return ( <AdaptyOnboardingView onboarding={onboarding} style={{ flex: 1 }} eventHandlers={{ onAnalytics(event, meta) { // Handle analytics events }, onClose(actionId, meta) { // Handle close actions }, onCustom(actionId, meta) { // Handle custom actions }, onPaywall(actionId, meta) { // Handle paywall actions }, onStateUpdated(action, meta) { // Handle state updates }, onFinishedLoading(meta) { // Handle when onboarding finishes loading }, onError(error) { // Handle errors }, }} /> ); } ``` </TabItem> </Tabs> ## Presentación modal \{#modal-presentation\} Para mostrar un onboarding como pantalla independiente que el usuario pueda cerrar, usa el método `view.present()` sobre el `view` creado por el método `createOnboardingView`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el onboarding de nuevo, llama a `createOnboardingView` otra vez para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo no está permitido. Producirá un error `AdaptyUIError.viewAlreadyPresented`. ::: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> ```typescript showLineNumbers title="React Native (TSX)" const view = await createOnboardingView(onboarding); // Optional: handle onboarding events (close, custom actions, etc) // view.setEventHandlers({ ... }); try { await view.present(); } catch (error) { // handle the error } ``` </TabItem> <TabItem value="old" label="SDK version < 3.14" default> ```typescript showLineNumbers title="React Native (TSX)" const view = await createOnboardingView(onboarding); view.registerEventHandlers(); // handle close press, etc try { await view.present(); } catch (error) { // handle the error } ``` </TabItem> </Tabs> ### Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el paywall en iOS pasando el parámetro `iosPresentationStyle` al método `present()`. El parámetro acepta los valores `'full_screen'` (por defecto) o `'page_sheet'`. ```typescript showLineNumbers try { await view.present(iosPresentationStyle: 'page_sheet'); } catch (error) { // handle the error } ``` ## Loader durante el onboarding \{#loader-during-onboarding\} Al presentar un onboarding en React Native, puede que notes un breve destello blanco o una pantalla de carga antes de que aparezca el onboarding. Esto ocurre mientras se inicializa la vista nativa subyacente. Puedes gestionarlo de distintas formas según tus necesidades y tu flujo de trabajo. #### Controlar la splash screen con onFinishedLoading \{#control-splash-screen-using-onfinishedloading\} :::note Este enfoque solo está disponible al usar el componente React. No está disponible para la presentación modal. ::: El enfoque recomendado para React Native es mantener visible tu splash screen o una capa personalizada hasta que el onboarding haya cargado por completo y ocultarla manualmente. Al usar el componente React (`AdaptyOnboardingView`), espera al evento `onFinishedLoading` antes de ocultar tu splash screen o capa de superposición: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const [isLoading, setIsLoading] = useState(true); const onFinishedLoading = useCallback<OnboardingEventHandlers['onFinishedLoading']>((meta) => { // Hide your splash screen or custom overlay here setIsLoading(false); }, []); return ( <> <AdaptyOnboardingView onboarding={onboarding} onFinishedLoading={onFinishedLoading} // ... other callbacks /> {isLoading && <YourCustomLoadingOverlay />} </> ); } ``` </TabItem> <TabItem value="old" label="SDK version < 3.14"> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const [isLoading, setIsLoading] = useState(true); return ( <> <AdaptyOnboardingView onboarding={onboarding} eventHandlers={{ onFinishedLoading(meta) { // Hide your splash screen or custom overlay here setIsLoading(false); }, // ... other handlers }} /> {isLoading && <YourCustomLoadingOverlay />} </> ); } ``` </TabItem> </Tabs> #### Personalizar el loader nativo \{#customize-native-loader\} :::important El flujo gestionado de Expo no permite colocar layouts nativos personalizados (por ejemplo, `res/layout` en Android). Para apps con Expo, controlar la splash screen o usar una capa de React Native es la única solución viable. ::: Puedes reemplazar el loader nativo usando layouts específicos de cada plataforma en Android e iOS. Si usas la presentación modal, esta es tu única opción. Sin embargo, este enfoque suele ser menos cómodo para apps React Native: - Requiere implementaciones separadas para Android e iOS - No es compatible con el flujo gestionado de Expo Define un placeholder para cada plataforma: - **iOS**: Añade `AdaptyOnboardingPlaceholderView.xib` a tu proyecto de Xcode. [Más información](ios-present-onboardings#add-smooth-transitions-between-the-splash-screen-and-onboarding). - **Android**: Crea `adapty_onboarding_placeholder_view.xml` en `res/layout` y define allí un placeholder. [Más información](android-present-onboardings#add-smooth-transitions-between-the-splash-screen-and-onboarding). ## Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\} :::important La personalización de cómo se abren los enlaces en los onboardings está disponible a partir de la versión 3.15.1 del SDK de Adapty. ::: Por defecto, los enlaces de los onboardings se abren en un navegador integrado en la app. Esto ofrece una experiencia fluida al mostrar las páginas web dentro de tu aplicación, sin que el usuario tenga que cambiar de app. Si prefieres abrir los enlaces en un navegador externo, puedes personalizar este comportamiento estableciendo el parámetro `externalUrlsPresentation` en `WebPresentation.BrowserOutApp`: <Tabs groupId="rn-onboarding-views" queryString> <TabItem value="component" label="React component" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const onAnalytics = useCallback<OnboardingEventHandlers['onAnalytics']>((event, meta) => {}, []); const onClose = useCallback<OnboardingEventHandlers['onClose']>((actionId, meta) => {}, []); const onCustom = useCallback<OnboardingEventHandlers['onCustom']>((actionId, meta) => {}, []); const onPaywall = useCallback<OnboardingEventHandlers['onPaywall']>((actionId, meta) => {}, []); const onStateUpdated = useCallback<OnboardingEventHandlers['onStateUpdated']>((action, meta) => {}, []); const onFinishedLoading = useCallback<OnboardingEventHandlers['onFinishedLoading']>((meta) => {}, []); const onError = useCallback<OnboardingEventHandlers['onError']>((error) => {}, []); return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} externalUrlsPresentation={WebPresentation.BrowserOutApp} // default – BrowserInApp onAnalytics={onAnalytics} onClose={onClose} onCustom={onCustom} onPaywall={onPaywall} onStateUpdated={onStateUpdated} onFinishedLoading={onFinishedLoading} onError={onError} /> ); } ``` </TabItem> <TabItem value="modal" label="Modal presentation"> ```typescript showLineNumbers title="React Native (TSX)" const view = await createOnboardingView( onboarding, { externalUrlsPresentation: WebPresentation.BrowserOutApp } // default – BrowserInApp ); try { await view.present(); } catch (error) { // handle the error } ``` </TabItem> </Tabs> ## Solución de problemas \{#troubleshooting\} ### La UI del sistema se superpone al contenido del onboarding en Android \{#system-ui-overlaps-onboarding-content-on-android\} :::note Esta configuración solo está disponible en proyectos de React Native sin gestión de Expo. Si usas el flujo gestionado de Expo, no puedes añadir este recurso de Android directamente. Para aplicar esta configuración, debes crear un plugin de configuración de Expo personalizado que añada el recurso de Android correspondiente y registrarlo en `app.config.js`. Esto es necesario porque Expo gestiona el proyecto nativo de Android por ti. ::: Al usar `AdaptyOnboardingView` en Android, los elementos de la UI del sistema, como la barra de estado y la barra de navegación, pueden aparecer encima del contenido del paywall. Para evitarlo, añade el siguiente recurso booleano a tu app: 1. Ve a `android/app/src/main/res/values`. Si no existe el archivo `bools.xml`, créalo. 2. Añade el siguiente recurso: ```xml <resources> <bool name="adapty_onboarding_enable_safe_area_paddings">false</bool> </resources> ``` Ten en cuenta que los cambios se aplican de forma global a todos los onboardings de tu app. ## Siguientes pasos \{#next-steps\} Una vez que hayas presentado tu onboarding, querrás [gestionar las interacciones y eventos del usuario](react-native-handling-onboarding-events). Aprende a manejar los eventos del onboarding para responder a las acciones del usuario y realizar un seguimiento de la analítica. --- # File: react-native-handling-onboarding-events --- --- title: "Manejar eventos de onboarding en React Native SDK" description: "Maneja eventos relacionados con el onboarding en React Native usando Adapty." --- Los onboardings configurados con el builder generan eventos a los que tu app puede responder. La forma de manejarlos depende del enfoque de presentación que estés usando: - **Presentación modal**: Requiere configurar manejadores de eventos que gestionen los eventos de todas las vistas de onboarding - **Componente React**: Maneja los eventos mediante parámetros de callback en línea directamente en el widget Antes de empezar, asegúrate de que: 1. Tienes instalado el [SDK de React Native de Adapty](sdk-installation-reactnative) versión 3.8.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). Para controlar o monitorizar los procesos que ocurren en la pantalla de onboarding dentro de tu app, implementa manejadores de eventos: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> Para el componente React, los eventos se manejan mediante props individuales de manejadores de eventos en el componente `AdaptyOnboardingView`: ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const onAnalytics = useCallback<OnboardingEventHandlers['onAnalytics']>((event, meta) => {}, []); const onClose = useCallback<OnboardingEventHandlers['onClose']>((actionId, meta) => {}, []); const onCustom = useCallback<OnboardingEventHandlers['onCustom']>((actionId, meta) => {}, []); const onPaywall = useCallback<OnboardingEventHandlers['onPaywall']>((actionId, meta) => {}, []); const onStateUpdated = useCallback<OnboardingEventHandlers['onStateUpdated']>((action, meta) => {}, []); const onFinishedLoading = useCallback<OnboardingEventHandlers['onFinishedLoading']>((meta) => {}, []); const onError = useCallback<OnboardingEventHandlers['onError']>((error) => {}, []); return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} onAnalytics={onAnalytics} onClose={onClose} onCustom={onCustom} onPaywall={onPaywall} onStateUpdated={onStateUpdated} onFinishedLoading={onFinishedLoading} onError={onError} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> Para la presentación modal, implementa el método de manejadores de eventos. :::important Llamar a `setEventHandlers` varias veces sobrescribirá los manejadores que proporciones, reemplazando tanto los predeterminados como los configurados anteriormente para esos eventos específicos. ::: ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.setEventHandlers({ onAnalytics(event, meta) { // Track analytics events }, onClose(actionId, meta) { // Handle close action view.dismiss(); return true; }, onCustom(actionId, meta) { // Handle custom actions }, onPaywall(actionId, meta) { // Handle paywall actions }, onStateUpdated(action, meta) { // Handle user input updates }, onFinishedLoading(meta) { // Onboarding finished loading }, onError(error) { // Handle loading errors }, }); try { await view.present(); } catch (error) { // handle the error } ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14"> Para versiones del SDK anteriores a 3.14, solo se admite la presentación modal: ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.registerEventHandlers({ onAnalytics(event, meta) { // Track analytics events }, onClose(actionId, meta) { // Handle close action view.dismiss(); return true; }, onCustom(actionId, meta) { // Handle custom actions }, onPaywall(actionId, meta) { // Handle paywall actions }, onStateUpdated(action, meta) { // Handle user input updates }, onFinishedLoading(meta) { // Onboarding finished loading }, onError(error) { // Handle loading errors }, }); try { await view.present(); } catch (error) { // handle the error } ``` </TabItem> </Tabs> ## Tipos de eventos \{#event-types\} Las siguientes secciones describen los distintos tipos de eventos que puedes manejar, independientemente del enfoque de presentación que estés usando. ### Manejar acciones personalizadas \{#handle-custom-actions\} En el builder, puedes añadir una acción **custom** a un botón y asignarle un ID. <img src="/assets/shared/img/ios-events-1.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Después, puedes usar ese ID en tu código y manejarlo como una acción personalizada. Por ejemplo, si un usuario pulsa un botón personalizado como **Login** o **Allow notifications**, el manejador de eventos se disparará con el parámetro `actionId` que coincide con el **Action ID** del builder. Puedes crear tus propios IDs, como "allowNotifications". <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const onCustom = useCallback<OnboardingEventHandlers['onCustom']>((actionId, meta) => { switch (actionId) { case 'login': login(); break; case 'allow_notifications': allowNotifications(); break; } }, []); return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} onCustom={onCustom} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.setEventHandlers({ onCustom(actionId, meta) { switch (actionId) { case 'login': login(); break; case 'allow_notifications': allowNotifications(); break; } }, }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14"> ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.registerEventHandlers({ onCustom(actionId, meta) { switch (actionId) { case 'login': login(); break; case 'allow_notifications': allowNotifications(); break; } }, }); ``` </TabItem> </Tabs> <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```json { "actionId": "allow_notifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ``` </Details> ### Finalización de la carga del onboarding \{#finishing-loading-onboarding\} Cuando un onboarding termina de cargarse, se dispara este evento: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const onFinishedLoading = useCallback<OnboardingEventHandlers['onFinishedLoading']>((meta) => { console.log('Onboarding loaded:', meta.onboardingId); }, []); return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} onFinishedLoading={onFinishedLoading} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.setEventHandlers({ onFinishedLoading(meta) { console.log('Onboarding loaded:', meta.onboardingId); }, }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14"> ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.registerEventHandlers({ onFinishedLoading(meta) { console.log('Onboarding loaded:', meta.onboardingId); }, }); ``` </TabItem> </Tabs> <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ``` </Details> ### Cerrar el onboarding \{#closing-onboarding\} El onboarding se considera cerrado cuando el usuario pulsa un botón con la acción **Close** asignada. <img src="/assets/shared/img/ios-events-2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::important Ten en cuenta que debes gestionar qué ocurre cuando el usuario cierra el onboarding. Por ejemplo, necesitas dejar de mostrar el propio onboarding. ::: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding, navigation }) { const onClose = useCallback<OnboardingEventHandlers['onClose']>((actionId, meta) => { navigation.goBack(); }, [navigation]); return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} onClose={onClose} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.setEventHandlers({ onClose(actionId, meta) { await view.dismiss(); return true; }, }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14"> ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.registerEventHandlers({ onClose(actionId, meta) { await view.dismiss(); return true; }, }); ``` </TabItem> </Tabs> <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> ### Abrir un paywall \{#opening-a-paywall\} :::tip Maneja este evento para abrir un paywall si quieres abrirlo dentro del onboarding. Si prefieres abrirlo después de que se cierre, hay una forma más directa: maneja la acción de cierre y abre el paywall sin depender de los datos del evento. ::: Si el usuario pulsa un botón que abre un paywall, recibirás el ID de acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más sencilla de trabajar con paywalls en onboardings es hacer que el ID de acción coincida con el ID del placement del paywall. <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const onPaywall = useCallback<OnboardingEventHandlers['onPaywall']>((actionId, meta) => { openPaywall(actionId); }, []); return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} onPaywall={onPaywall} /> ); } const openPaywall = async (placementId) => { // Implement your paywall opening logic here }; ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> Ten en cuenta que, en iOS, solo se puede mostrar una vista (paywall u onboarding) en pantalla al mismo tiempo. Si presentas un paywall encima de un onboarding, no podrás controlar el onboarding en segundo plano de forma programática. Si intentas cerrar el onboarding, se cerrará el paywall en su lugar, dejando el onboarding visible. Para evitarlo, cierra siempre la vista del onboarding antes de presentar el paywall. ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.setEventHandlers({ onPaywall(actionId, meta) { view.dismiss().then(() => { openPaywall(actionId); }); }, }); const openPaywall = async (placementId) => { // Implement your paywall opening logic here }; ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14"> Ten en cuenta que, en iOS, solo se puede mostrar una vista (paywall u onboarding) en pantalla al mismo tiempo. Si presentas un paywall encima de un onboarding, no podrás controlar el onboarding en segundo plano de forma programática. Si intentas cerrar el onboarding, se cerrará el paywall en su lugar, dejando el onboarding visible. Para evitarlo, cierra siempre la vista del onboarding antes de presentar el paywall. ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.registerEventHandlers({ onPaywall(actionId, meta) { view.dismiss().then(() => { openPaywall(actionId); }); }, }); const openPaywall = async (placementId) => { // Implement your paywall opening logic here }; ``` </TabItem> </Tabs> <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ``` </Details> ### Seguimiento de la navegación \{#tracking-navigation\} Recibes un evento de análisis cuando se producen distintos eventos relacionados con la navegación durante el flujo del onboarding: <Tabs groupId="version" queryString> <TabItem value="new" label="SDK version 3.14 or later" default> <Tabs groupId="presentation-method" queryString> <TabItem value="platform" label="React component" default> ```typescript showLineNumbers title="React Native (TSX)" function MyOnboarding({ onboarding }) { const onAnalytics = useCallback<OnboardingEventHandlers['onAnalytics']>((event, meta) => { trackEvent(event.name, meta.onboardingId); }, []); return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} onAnalytics={onAnalytics} /> ); } ``` </TabItem> <TabItem value="standalone" label="Modal presentation"> ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.setEventHandlers({ onAnalytics(event, meta) { trackEvent(event.name, meta.onboardingId); }, }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="old" label="SDK version < 3.14"> ```javascript showLineNumbers title="React Native" const view = await createOnboardingView(onboarding); const unsubscribe = view.registerEventHandlers({ onAnalytics(event, meta) { trackEvent(event.name, meta.onboardingId); }, }); ``` </TabItem> </Tabs> El objeto `event` puede ser uno de los siguientes tipos: | Tipo | Descripción | |------------|-------------| | `onboardingStarted` | Cuando el onboarding ha terminado de cargarse | | `screenPresented` | Cuando se muestra cualquier pantalla | | `screenCompleted` | Cuando se completa una pantalla. Incluye `elementId` opcional (identificador del elemento completado) y `reply` opcional (respuesta del usuario). Se dispara cuando el usuario realiza cualquier acción para salir de la pantalla. | | `secondScreenPresented` | Cuando se muestra la segunda pantalla | | `userEmailCollected` | Se dispara cuando se recoge el correo electrónico del usuario mediante el campo de entrada | | `onboardingCompleted` | Se dispara cuando el usuario llega a una pantalla con el ID `final`. Si necesitas este evento, [asigna el ID `final` a la última pantalla](design-onboarding). | | `unknown` | Para cualquier tipo de evento no reconocido. Incluye `name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) | Cada evento incluye información `meta` con los siguientes campos: | Campo | Descripción | |------------|-------------| | `onboardingId` | Identificador único del flujo de onboarding | | `screenClientId` | Identificador de la pantalla actual | | `screenIndex` | Posición de la pantalla actual en el flujo | | `screensTotal` | Número total de pantallas en el flujo | <Details> <summary>Ejemplos de eventos (Haz clic para expandir)</summary> ```javascript // onboardingStarted { "name": "onboarding_started", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } // screenPresented { "name": "screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 4 } } // screenCompleted { "name": "screen_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 }, "params": { "element_id": "profile_form", "reply": "success" } } // secondScreenPresented { "name": "second_screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // userEmailCollected { "name": "user_email_collected", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // onboardingCompleted { "name": "onboarding_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> --- # File: react-native-onboarding-input --- --- title: "Procesar datos de onboardings en el SDK de React Native" description: "Guarda y usa datos de onboardings en tu app de React Native con el SDK de Adapty." --- Cuando tus usuarios responden a una pregunta de un quiz o introducen datos en un campo de texto, se invocará el método `onStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código. Por ejemplo: ```javascript // Presentación a pantalla completa const unsubscribe = view.registerEventHandlers({ onStateUpdated(action, meta) { // Procesar datos }, }); // Widget embebido <AdaptyOnboardingView onboarding={onboarding} eventHandlers={{ onStateUpdated(action, meta) { // Procesar datos }, }} /> ``` Consulta el formato de la acción [aquí](https://react-native.adapty.io/types/onboardingstateupdatedaction). <Details> <summary>Ejemplos de datos guardados (el formato puede variar según tu implementación)</summary> ```javascript // Ejemplo de una acción de selección guardada { "elementId": "preference_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "preferences_screen", "screenIndex": 1, "screensTotal": 3 }, "params": { "type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Ejemplo de una acción de selección múltiple guardada { "elementId": "interests_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 3 }, "params": { "type": "multiSelect", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Ejemplo de una acción de entrada guardada { "elementId": "name_input", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "input", "value": { "type": "text", "value": "John Doe" } } } // Ejemplo de una acción de selector de fecha guardada { "elementId": "birthday_picker", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "datePicker", "value": { "day": 15, "month": 6, "year": 1990 } } } ``` </Details> ## Casos de uso \{#use-cases\} ### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\} Si quieres vincular inmediatamente los datos introducidos con el perfil del usuario y evitar pedirle la misma información dos veces, debes [actualizar el perfil de usuario](react-native-setting-user-attributes) con esos datos al gestionar la acción. Por ejemplo, si pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name` y quieres usar ese valor como su nombre de pila, y también les pides su correo electrónico en el campo `email`, en el código de tu app quedaría así: ```javascript showLineNumbers // Presentación a pantalla completa const unsubscribe = view.registerEventHandlers({ onStateUpdated(action, meta) { // Guardar preferencias o respuestas del usuario if (action.elementType === 'input') { const profileParams = {}; // Mapear elementId al campo de perfil correspondiente switch (action.elementId) { case 'name': if (action.value.type === 'text') { profileParams.firstName = action.value.value; } break; case 'email': if (action.value.type === 'email') { profileParams.email = action.value.value; } break; } // Actualizar el perfil si hay datos que actualizar if (Object.keys(profileParams).length > 0) { adapty.updateProfile(profileParams).catch(error => { // gestionar el error }); } } }, }); // Widget embebido <AdaptyOnboardingView onboarding={onboarding} eventHandlers={{ onStateUpdated(action, meta) { // Guardar preferencias o respuestas del usuario if (action.elementType === 'input') { const profileParams = {}; // Mapear elementId al campo de perfil correspondiente switch (action.elementId) { case 'name': if (action.value.type === 'text') { profileParams.firstName = action.value.value; } break; case 'email': if (action.value.type === 'email') { profileParams.email = action.value.value; } break; } // Actualizar el perfil si hay datos que actualizar if (Object.keys(profileParams).length > 0) { adapty.updateProfile(profileParams).catch(error => { // gestionar el error }); } } }, }} /> ``` ### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\} Usando quizzes en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios cuando completan el onboarding. Por ejemplo, puedes preguntarles sobre su experiencia con el deporte y mostrar distintos CTAs y productos a diferentes grupos de usuarios. 1. [Añade un quiz](onboarding-quizzes) en el constructor de onboarding y asigna IDs con significado a sus opciones. 2. Gestiona las respuestas del quiz según sus IDs y [establece atributos personalizados](react-native-setting-user-attributes) para los usuarios. ```javascript showLineNumbers // Presentación a pantalla completa const unsubscribe = view.registerEventHandlers({ onStateUpdated(action, meta) { // Gestionar respuestas del quiz y establecer atributos personalizados if (action.elementType === 'select') { const profileParams = {}; // Mapear respuestas del quiz a atributos personalizados switch (action.elementId) { case 'experience': // Establecer el atributo personalizado 'experience' con el valor seleccionado (beginner, amateur, pro) profileParams.codableCustomAttributes = { experience: action.value.value }; break; } // Actualizar el perfil si hay datos que actualizar if (Object.keys(profileParams).length > 0) { adapty.updateProfile(profileParams).catch(error => { // gestionar el error }); } } }, }); // Widget embebido <AdaptyOnboardingView onboarding={onboarding} eventHandlers={{ onStateUpdated(action, meta) { // Gestionar respuestas del quiz y establecer atributos personalizados if (action.elementType === 'select') { const profileParams = {}; // Mapear respuestas del quiz a atributos personalizados switch (action.elementId) { case 'experience': // Establecer el atributo personalizado 'experience' con el valor seleccionado (beginner, amateur, pro) profileParams.codableCustomAttributes = { experience: action.value.value }; break; } // Actualizar el perfil si hay datos que actualizar if (Object.keys(profileParams).length > 0) { adapty.updateProfile(profileParams).catch(error => { // gestionar el error }); } } }, }} /> ``` 3. [Crea segmentos](segments) para cada valor de atributo personalizado. 4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado. 5. [Muestra un paywall](react-native-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](react-native-handling-onboarding-events#opening-a-paywall). --- # File: react-native-sdk-call-order --- --- title: "Orden de llamadas en el SDK de React Native" description: "Evita la pérdida de acceso premium, la atribución incompleta y los errores intermitentes #2002 llamando a los métodos del SDK de Adapty en el orden correcto." --- `adapty.activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que se resuelva, el SDK no tiene estado. Cualquier llamada realizada antes o en paralelo con `activate()` fallará con [`#2002 notActivated`](react-native-handle-errors#custom-network-codes). Si tu app autentica usuarios y obtienes un customer user ID después del lanzamiento, llama a `adapty.identify()` en ese momento. No llames a métodos de acción del usuario hasta que `identify` se resuelva. Las llamadas que compiten con él fallan con [`#3006 profileWasChanged`](react-native-handle-errors#custom-network-codes) o aterrizan en el perfil anónimo creado durante la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la propiedad de la instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `identify` y sigue trabajando con el perfil anónimo. Los SDK de MMP y análisis (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera sus callbacks de UID antes de llamar a `adapty.activate`. De lo contrario, el ID del MMP aterriza en un perfil anónimo temporal y no siempre se transfiere al identificado. Para más detalles sobre AppsFlyer, consulta [AppsFlyer](appsflyer). ## El orden correcto \{#the-correct-order\} Tu ruta depende de dos cosas: cuándo conoces el customer user ID y si usas un SDK de MMP o análisis. - **Pasos 2 y 5**: Obligatorios para todas las apps. Activa el SDK y luego llama a los métodos del SDK. - **Pasos 1 y 3**: Requeridos solo si integras un SDK de MMP o análisis (AppsFlyer, Adjust, Branch, PostHog). - **Paso 4**: Requerido solo si tu app autentica usuarios y recopila el customer user ID después del lanzamiento. Si tienes el customer user ID al lanzar la app, pásalo directamente a `activate()` (paso 2a). Esta ruta nunca crea un perfil anónimo, por lo que el paso 4 no es necesario. | Paso | Llamada | Cuándo | Notas | |------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | Inicializa tu SDK de MMP o análisis (AppsFlyer, Adjust, PostHog, Branch) | Al lanzar la app, primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerUID`. | | 2a | `adapty.activate('YOUR_PUBLIC_SDK_KEY', { customerUserId: 'YOUR_USER_ID' })` | Al lanzar la app, después del paso 1, si tienes el customer user ID | Recomendado. Nunca se crea un perfil anónimo. | | 2b | `adapty.activate('YOUR_PUBLIC_SDK_KEY')` sin `customerUserId` | Al lanzar la app, después del paso 1, si no tienes el customer user ID (o nunca lo recopilas) | Adapty crea un perfil anónimo. | | 3 | `adapty.updateAttribution(data, source, networkUserId)` para cada MMP | Después del paso 2, antes de cualquier llamada de acción del usuario | Necesario para que los IDs de MMP lleguen al perfil correcto. | | 4 | `await adapty.identify('YOUR_USER_ID')` | Después del paso 3 (o del paso 2 si no hay MMP), antes del paso 5 — solo en la ruta 2b con autenticación | Siempre usa `await`. Las llamadas concurrentes durante `identify` producen `#3006 profileWasChanged`. | | 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Después del paso 4 si llamas a `identify`; de lo contrario, después del paso 3 (o del paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. | :::important Saltarse estos pasos provoca pérdida de acceso premium para usuarios que regresan, `appsflyer_id` ausente en los perfiles y paywalls devueltos para la audiencia incorrecta. ::: ## Instalaciones de web2app y web funnel \{#web2app-and-web-funnel-installs\} Si los usuarios compran en un checkout web (Stripe, Paddle) y luego instalan la app nativa, el primer `activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el customer user ID antes del lanzamiento de la app (desde tu flujo de autenticación o el referrer de instalación), pásalo directamente a `activate()`. De lo contrario, la compra web será invisible en el dispositivo hasta que llames a `identify('YOUR_USER_ID')` y luego a `restorePurchases`. Para los metadatos que debes enviar con cada checkout web, consulta: - [Stripe](stripe) - [Paddle](paddle) --- # File: react-native-optimize-paywall-fetching --- --- title: "Optimizar la carga de paywalls en React Native SDK" description: "Carga paywalls de Adapty de forma fiable: temporización, caché y patrones de respaldo para React Native." --- Una carga de paywall fiable en React Native hace tres cosas: renderiza rápido, devuelve el paywall dirigido a la audiencia correcta y activa el respaldo de forma elegante cuando la red va lenta. Las reglas a continuación cubren los patrones de temporización, caché y respaldo para lograrlo. :::tip Las reglas asumen que `adapty.activate()` y `adapty.identify()` ya se han resuelto. Consulta [Orden de llamadas en React Native SDK](react-native-sdk-call-order). ::: ## Reglas y errores comunes \{#rules-and-pitfalls\} | Haz esto | No hagas esto | Por qué | |---|---|---| | Carga el placement que estás a punto de mostrar. | Precargues todos los placements de forma concurrente al inicio. | La precarga masiva bloquea el hilo JS y produce una pantalla en negro durante la ráfaga. | | Llama a `getPaywall` después de que la atribución haya tenido oportunidad de resolverse — por ejemplo, 1–2 segundos después de `activate` o cuando se dispare `onProfileUpdate`. | Llames a `getPaywall` en el montaje del componente raíz. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia por defecto y omite silenciosamente los segmentos y la personalización de ASA. | | Configura un `loadTimeoutMs` y un [paywall de respaldo](fallback-paywalls) para cada placement. | Esperes indefinidamente a `getPaywall`. | Sin tiempo de espera, los usuarios con mala conectividad ven una pantalla en blanco hasta que la red responde — o cierran la app. | Consulta [Obtener paywalls y productos](fetch-paywalls-and-products-react-native) para la referencia de los parámetros `fetchPolicy` y `loadTimeoutMs`, y [Placements](placements) para elegir el placement adecuado. ## Ajustar para mala conectividad \{#tune-for-poor-connectivity\} Para mercados con conectividad constantemente deficiente (zonas rurales, transporte, regiones afectadas por enrutamiento): - Establece `fetchPolicy: .returnCacheDataElseLoad` en cada carga excepto la primera. - Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard. - Establece `loadTimeoutMs` entre 3 y 5 segundos y acepta el respaldo cuando se agote el tiempo. - No condicionar la visualización del paywall a `getProfile()`. Llama a `getPaywall` de forma independiente para que un perfil lento no bloquee la interfaz. --- # File: react-native-test --- --- title: "Prueba y lanzamiento en React Native SDK" description: "Aprende cómo probar y lanzar tu aplicación React Native con el SDK de Adapty." --- Si ya has implementado el SDK de Adapty en tu aplicación React Native, querrás comprobar que todo está configurado correctamente y que las compras funcionan según lo esperado tanto en iOS como en Android. Esto implica probar tanto la integración del SDK como el flujo de compra real con el entorno sandbox de Apple y el entorno de pruebas de Google Play. ## Prueba tu aplicación \{#test-your-app\} Para hacer pruebas exhaustivas de tus compras in-app, consulta nuestras guías de pruebas por plataforma: [guía de pruebas para iOS](test-purchases-in-sandbox) y [guía de pruebas para Android](testing-on-android). ## Prepárate para el lanzamiento \{#prepare-for-release\} Antes de enviar tu aplicación a la store, sigue la [lista de verificación para el lanzamiento](release-checklist) para confirmar que: - La conexión con la store y las notificaciones del servidor están configuradas - Las compras se completan y se registran en Adapty - El acceso se desbloquea y se restaura correctamente - Se cumplen los requisitos de privacidad y revisión --- # File: InvalidProductIdentifiers-react-native --- --- title: "Solución para el error Code-1000 noProductIDsFound en el SDK de React Native" description: "Resuelve errores de identificador de producto no válido al gestionar suscripciones en Adapty." --- El error con código 1000, `noProductIDsFound`, indica que ninguno de los productos que solicitaste en el paywall está disponible para su compra en el App Store, aunque aparezcan listados en él. Este error a veces viene acompañado de una advertencia `InvalidProductIdentifiers`. Si la advertencia aparece sin error, puedes ignorarla con tranquilidad. Si estás encontrando el error `noProductIDsFound`, sigue estos pasos para resolverlo: ## Paso 1. Verifica el bundle ID \{#step-2-check-bundle-id\} --- no_index: true --- 1. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu app y ve a la sección **General** → **App Information**. 2. Copia el **Bundle ID** en la subsección **General Information**. <Zoom> <img src="/docs/img/afd5012-bundle_id_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 3. Abre la pestaña [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) desde el menú superior de Adapty y pega el valor copiado en el campo **Bundle ID**. <Zoom> <img src="/docs/img/2d64163-bundle_id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 4. Vuelve a la página **App information** en App Store Connect y copia el **Apple ID** desde allí. 5. En la página [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) del Adapty Dashboard, pega el ID en el campo **Apple app ID**. ## Paso 2. Verifica los productos \{#step-3-check-products\} 1. Ve a **App Store Connect** y navega a [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) en el menú de la izquierda. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción. Verás tus productos listados en la sección **Subscriptions**. 3. Asegúrate de que el producto que estás probando esté marcado como **Ready to Submit**. <img src="/assets/shared/img/ready-to-submit.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Compara el ID de producto de la tabla con el que aparece en la pestaña [**Products**](https://app.adapty.io/products) del Adapty Dashboard. Si los IDs no coinciden, copia el ID del producto de la tabla y [crea un producto](create-product) con ese ID en el Adapty Dashboard. <img src="/assets/shared/img/product-id-copy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 3. Verifica la disponibilidad del producto \{#step-4-check-product-availability\} 1. Vuelve a **App Store Connect** y abre la misma sección **Subscriptions**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción para ver tus productos. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hasta la sección **Availability** y comprueba que todos los países y regiones necesarios aparecen en la lista. <img src="/assets/shared/img/product-availability.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 4. Verifica los precios del producto \{#step-5-check-product-prices\} 1. De nuevo, ve a la sección **Monetization** → **Subscriptions** en **App Store Connect**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hacia abajo hasta **Subscription Pricing** y despliega la sección **Current Pricing for New Subscribers**. <img src="/assets/shared/img/check-prices.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Asegúrate de que todos los precios necesarios aparecen en la lista. <img src="/assets/shared/img/product-pricing.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 5. Verifica que el estado de pago de la app, la cuenta bancaria y los formularios fiscales estén activos \{#step-5-check-app-paid-status-bank-account-and-tax-forms-are-active\} 1. En la página de inicio de [**App Store Connect**](https://appstoreconnect.apple.com/), haz clic en **Business**. <img src="/assets/shared/img/business.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Selecciona el nombre de tu empresa. <img src="/assets/shared/img/business-name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Desplázate hacia abajo y comprueba que tu **Paid Apps Agreement**, **Bank Account** y **Tax forms** muestran el estado **Active**. <img src="/assets/shared/img/appstore-connect-status.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Siguiendo estos pasos deberías poder resolver la advertencia `InvalidProductIdentifiers` y publicar tus productos en el store. ## Paso 6. Recrea el producto si está bloqueado \{#step-6-recreate-the-product-if-its-stuck\} Los pasos 1 a 5 pueden superarse correctamente —estado `Approved`, Bundle ID coincidente, API key válida— y aun así el SDK devuelve `1000 noProductIDsFound`. En ese caso, es posible que el producto esté bloqueado en el registro de Apple. El registro de productos de Apple puede entrar ocasionalmente en un estado en el que un producto existe en la interfaz de App Store Connect pero no está expuesto en la ruta de búsqueda de StoreKit. Elimina el producto en App Store Connect y vuelve a crearlo con el mismo ID de producto. Espera hasta 24 horas tras la recreación para que los cambios se propaguen. --- # File: cantMakePayments-react-native --- --- title: "Solución para el error Code-1003 cantMakePayment en el SDK de React Native" description: "Resuelve el error de pago al gestionar suscripciones en Adapty." --- El error 1003, `cantMakePayments`, indica que no es posible realizar compras in-app en este dispositivo. Si encuentras el error `cantMakePayments`, normalmente se debe a una de estas razones: - Restricciones del dispositivo: El error no está relacionado con Adapty. Consulta las soluciones más abajo. - Configuración del modo Observer: El método `makePurchase` y el modo Observer no pueden usarse al mismo tiempo. Consulta la sección más abajo. ## Problema: Restricciones del dispositivo \{#issue-device-restrictions\} | Problema | Solución | |-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| | Restricciones de Screen Time | Desactiva las restricciones de compras in-app en [Screen Time](https://support.apple.com/en-us/102470) | | Cuenta suspendida | Contacta con el soporte de Apple para resolver problemas con la cuenta | | Restricciones regionales | Usa una cuenta de App Store de una región compatible | ## Problema: Usar el modo Observer y makePurchase a la vez \{#issue-using-both-observer-mode-and-makepurchase\} Si usas `makePurchases` para gestionar las compras, no necesitas el modo Observer. El [modo Observer](observer-vs-full-mode) solo es necesario si implementas la lógica de compra tú mismo. Por lo tanto, si usas `makePurchase`, puedes eliminar sin problema la activación del modo Observer del código de inicialización del SDK. --- # File: migration-react-native-314 --- --- title: "Migrar el SDK de Adapty React Native a la versión 3.14" description: "Migra al SDK de Adapty React Native v3.14 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK de Adapty React Native 3.14.0 es una versión mayor que introduce mejoras que requieren pasos de migración de tu parte: - El método `registerEventHandlers` ha sido reemplazado por el método `setEventHandlers`. - En `AdaptyOnboardingView`, los manejadores de eventos ahora se pasan como props individuales en lugar de un objeto `eventHandlers` - Se ha introducido un nuevo estilo de importación simplificado para los componentes de UI - El método `logShowOnboarding` ha sido eliminado - La versión mínima de React Native se ha actualizado a 0.73.0 - El estilo de presentación predeterminado de iOS para paywalls y onboardings ha cambiado de hoja de página a pantalla completa ## Reemplaza `registerEventHandlers` por `setEventHandlers` \{#replace-registereventhandlers-with-seteventhandlers\} El método `registerEventHandlers` utilizado para trabajar con el Paywall Builder y el Onboarding Builder de Adapty ha sido reemplazado por el método `setEventHandlers`. Si usas el Paywall Builder y/o el Onboarding Builder de Adapty, busca `registerEventHandlers` en el código de tu app y reemplázalo por `setEventHandlers`. Este cambio se ha introducido para que el comportamiento del método sea más claro: los manejadores ahora funcionan de uno en uno porque cada uno devuelve `true`/`false`, y tener varios manejadores para un mismo evento hacía que el comportamiento resultante fuera confuso. Ten en cuenta que al usar componentes de React como `AdaptyOnboardingView` o `AdaptyPaywallView`, no es necesario devolver `true`/`false` desde los manejadores de eventos, ya que controlas la visibilidad del componente mediante tu propia gestión de estado. Los valores de retorno solo son necesarios para la presentación de pantallas modales donde el SDK gestiona el ciclo de vida de la vista. :::important Llamar a `setEventHandlers` varias veces sobreescribirá los manejadores que proporciones, reemplazando tanto los predeterminados como los establecidos previamente para esos eventos específicos. ::: ```diff showLineNumbers - const unsubscribe = view.registerEventHandlers({ - // your event handlers - }) const unsubscribe = view.setEventHandlers({ // your event handlers }) ``` ## Actualiza las rutas de importación de los componentes de UI \{#update-import-paths-for-ui-components\} El SDK de Adapty 3.14.0 introduce un estilo de importación simplificado para los componentes de UI. En lugar de importar desde `react-native-adapty/dist/ui`, ahora puedes importar directamente desde `react-native-adapty`. El nuevo estilo de importación es más coherente con las prácticas estándar de React Native y hace que las declaraciones de importación sean más limpias. Si usas componentes de UI como `AdaptyPaywallView` o `AdaptyOnboardingView`, actualiza tus importaciones como se muestra a continuación: ```diff showLineNumbers - import { AdaptyPaywallView } from 'react-native-adapty/dist/ui'; + import { AdaptyPaywallView } from 'react-native-adapty'; - import { AdaptyOnboardingView } from 'react-native-adapty/dist/ui'; + import { AdaptyOnboardingView } from 'react-native-adapty'; - import { createPaywallView } from 'react-native-adapty/dist/ui'; + import { createPaywallView } from 'react-native-adapty'; - import { createOnboardingView } from 'react-native-adapty/dist/ui'; + import { createOnboardingView } from 'react-native-adapty'; ``` :::note Por compatibilidad con versiones anteriores, el estilo de importación antiguo (`react-native-adapty/dist/ui`) sigue siendo compatible. Sin embargo, recomendamos usar el nuevo estilo de importación para mayor consistencia y claridad. ::: ## Actualiza los manejadores de eventos del onboarding en el componente de React \{#update-onboarding-event-handlers-in-the-react-component\} Los manejadores de eventos para los onboardings se han movido fuera del objeto `eventHandlers` en `AdaptyOnboardingView`. Si estás mostrando onboardings con `AdaptyOnboardingView`, actualiza la estructura de manejo de eventos. :::important Ten en cuenta la forma en que recomendamos implementar los manejadores de eventos. Para evitar recrear objetos en cada renderizado, usa `useCallback` para las funciones que manejan eventos. ::: ```diff showLineNumbers import React, { useCallback } from 'react'; - import { AdaptyOnboardingView } from 'react-native-adapty/dist/ui'; + import { AdaptyOnboardingView } from 'react-native-adapty'; + import type { OnboardingEventHandlers } from 'react-native-adapty'; + + function MyOnboarding({ onboarding }) { + const onAnalytics = useCallback<OnboardingEventHandlers['onAnalytics']>((event, meta) => {}, []); + const onClose = useCallback<OnboardingEventHandlers['onClose']>((actionId, meta) => {}, []); + const onCustom = useCallback<OnboardingEventHandlers['onCustom']>((actionId, meta) => {}, []); + const onPaywall = useCallback<OnboardingEventHandlers['onPaywall']>((actionId, meta) => {}, []); + const onStateUpdated = useCallback<OnboardingEventHandlers['onStateUpdated']>((action, meta) => {}, []); + const onFinishedLoading = useCallback<OnboardingEventHandlers['onFinishedLoading']>((meta) => {}, []); + const onError = useCallback<OnboardingEventHandlers['onError']>((error) => {}, []); + return ( <AdaptyOnboardingView onboarding={onboarding} style={styles.container} - eventHandlers={{ - onAnalytics(event, meta) { /* ... */ }, - onClose(actionId, meta) { /* ... */ }, - onCustom(actionId, meta) { /* ... */ }, - onPaywall(actionId, meta) { /* ... */ }, - onStateUpdated(action, meta) { /* ... */ }, - onFinishedLoading(meta) { /* ... */ }, - onError(error) { /* ... */ }, - }} + onAnalytics={onAnalytics} + onClose={onClose} + onCustom={onCustom} + onPaywall={onPaywall} + onStateUpdated={onStateUpdated} + onFinishedLoading={onFinishedLoading} + onError={onError} /> ); + } ``` :::note Por compatibilidad con versiones anteriores, el prop `eventHandlers` sigue siendo compatible, pero está obsoleto. Recomendamos migrar a props individuales de manejadores de eventos como se muestra arriba. ::: ## Elimina `logShowOnboarding` \{#delete-logshowonboarding\} En el SDK de Adapty 3.14.0, hemos eliminado el método `logShowOnboarding` del SDK. Si has estado usando este método, no estará disponible cuando actualices el SDK a la versión 3.14 o posterior. En su lugar, puedes [crear onboardings en el constructor de onboardings sin código de Adapty](onboardings). Las analíticas de estos onboardings se registran automáticamente y tienes muchas opciones de personalización. ## Actualiza React Native \{#update-react-native\} A partir del SDK de Adapty 3.14.0, la versión mínima compatible de React Native es 0.73.0. Si estás usando una versión anterior, actualiza React Native a la versión 0.73.0 o posterior para que tu experiencia con el SDK de Adapty sea consistente y fiable. ## Actualiza el estilo de presentación de iOS para paywalls y onboardings modales \{#update-ios-presentation-style-for-modal-paywalls-and-onboardings\} En el SDK de Adapty 3.14.0, el estilo de presentación predeterminado de iOS para paywalls y onboardings mostrados con el método `view.present()` ha cambiado de hoja de página a pantalla completa. Si quieres mantener el estilo de presentación de hoja de página anterior, pasa el parámetro `iosPresentationStyle` al método `present()`: ```typescript showLineNumbers title="React Native (TSX)" try { await view.present({ iosPresentationStyle: 'page_sheet' }); } catch (error) { // handle the error } ``` --- # File: react-native-migration-guide-380 --- --- title: "Migrar el SDK de Adapty React Native a v. 3.8" description: "Migra al SDK de Adapty React Native v3.8 para obtener mejor rendimiento y nuevas funciones de monetización." --- Adapty SDK 3.8.0 es una versión mayor que incorpora mejoras que, sin embargo, pueden requerir algunos pasos de migración por tu parte. ## Actualizar el tipo de entrada para obtener los parámetros del placement \{#update-input-type-for-getting-placement-params\} `GetPaywallParamsInput` ha sido renombrado a `GetPlacementParamsInput`: ```diff showLineNumbers - type GetPaywallParamsInput = { + type GetPlacementParamsInput = { placementId: string; locale?: string; fetchPolicy?: AdaptyPlacementFetchPolicy; loadTimeoutMs?: number; } ``` ## Actualizar el método de respaldo \{#update-fallback-method\} El método para configurar los respaldos ha sido actualizado, y el tipo para especificar las ubicaciones de respaldo ha sido renombrado: ```diff showLineNumbers - adapty.setFallbackPaywalls(paywallsLocation: Input.FallbackPaywallsLocation); + adapty.setFallback(fileLocation: Input.FileLocation); ``` ## Actualizar el acceso a las propiedades del paywall \{#update-paywall-property-access\} Las siguientes propiedades han sido movidas de `AdaptyPaywall` a `AdaptyPlacement`: ```diff showLineNumbers - paywall.abTestName - paywall.audienceName - paywall.revision - paywall.placementId + paywall.placement.abTestName + paywall.placement.audienceName + paywall.placement.revision + paywall.placement.id ``` --- # File: migration-to-react-native-sdk-34 --- --- title: "Migrar el SDK de Adapty para React Native a v. 3.4" description: "Migra al SDK de Adapty para React Native v3.4 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK de Adapty 3.4.0 es una versión mayor que introduce mejoras que requieren pasos de migración por tu parte. ## Actualizar los archivos del paywall de respaldo \{#update-fallback-paywall-files\} Actualiza los archivos del paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK: 1. [Descarga los archivos actualizados del paywall de respaldo](fallback-paywalls) desde el Adapty Dashboard. 2. [Reemplaza los paywalls de respaldo existentes en tu aplicación móvil](react-native-use-fallback-paywalls) con los nuevos archivos. ## Actualizar la implementación del Modo Observador \{#update-implementation-of-observer-mode\} Si usas el Modo Observador, asegúrate de actualizar su implementación. Anteriormente, se utilizaban diferentes métodos para reportar transacciones a Adapty. En la nueva versión, el método `reportTransaction` debe utilizarse de forma consistente tanto en Android como en iOS. Este método reporta explícitamente cada transacción a Adapty, asegurando que sea reconocida. Si se utilizó un paywall, pasa el ID de variación para vincular la transacción a él. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: ```diff showLineNumbers - if (Platform.OS === 'android') { - try { - await adapty.restorePurchases(); - } catch (error) { - // handle the error - } - } const variationId = paywall.variationId; try { await adapty.reportTransaction(transactionId, variationId); } catch (error) { // handle the `AdaptyError` } ``` --- # File: migration-to-react-native330 --- --- title: "Migrar el SDK de Adapty React Native a la v. 3.3" description: "Migra al SDK de Adapty React Native v3.3 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- Adapty SDK 3.3.1 es una versión mayor que incluye mejoras que pueden requerir algunos pasos de migración de tu parte. 1. Actualiza al SDK de Adapty v3.3.x. 2. Actualiza los modelos. 3. Elimina el método `getProductsIntroductoryOfferEligibility`. 4. Actualiza el proceso de compra. 5. Actualiza la presentación de paywalls del Paywall Builder. 6. Revisa la implementación del temporizador definido por el desarrollador. 7. Actualiza el manejo de eventos de compra del Paywall Builder. 8. Actualiza el manejo de eventos de acción personalizada del Paywall Builder. 9. Modifica el callback `onProductSelected`. 10. Elimina los parámetros de integración de terceros del método `updateProfile`. 11. Actualiza las configuraciones de integración para Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase y Google Analytics, Mixpanel, OneSignal y Pushwoosh. 12. Actualiza la implementación del modo Observer. ## Actualiza el SDK de Adapty React Native a 3.3.x \{#upgrade-adapty-react-native-sdk-to-33x\} Antes de la versión 3.3.1, el SDK `react-native-adapty` era el SDK principal y obligatorio para que Adapty funcionara correctamente en tu app. El SDK `@adapty/react-native-ui` era opcional y solo era necesario si usabas el Paywall Builder de Adapty. A partir de la versión 3.3.1, el SDK `@adapty/react-native-ui` está obsoleto y su funcionalidad se ha integrado en el SDK `react-native-adapty`. Para actualizar a la versión 3.3.1, sigue estos pasos: 1. Actualiza el paquete `react-native-adapty` a la versión 3.3.1. 2. Elimina el paquete `@adapty/react-native-ui` de las dependencias de tu proyecto. 3. Sincroniza las dependencias de tu proyecto para aplicar los cambios. ## Cambios en los modelos \{#changes-in-models\} ### Nuevos modelos \{#new-models\} 1. [AdaptySubscriptionOffer](https://react-native.adapty.io/interfaces/adaptysubscriptionoffer): ```typescript showLineNumbers export interface AdaptySubscriptionOffer { readonly identifier: AdaptySubscriptionOfferId; phases: AdaptyDiscountPhase[]; android?: { offerTags?: string[]; }; } ``` 2. [AdaptySubscriptionOfferId](https://react-native.adapty.io/types/adaptysubscriptionofferid): ```typescript showLineNumbers export type AdaptySubscriptionOfferId = | { id?: string; type: 'introductory'; } | { id: string; type: 'promotional' | 'win_back'; }; ``` ### Modelos modificados \{#changed-models\} 1. [AdaptyPaywallProduct](https://react-native.adapty.io/interfaces/adaptypaywallproduct): - Se renombró la propiedad `subscriptionDetails` a `subscription`. <p> </p> ```diff showLineNumbers - subscriptionDetails?: AdaptySubscriptionDetails; + subscription?: AdaptySubscriptionDetails; ``` 2. [AdaptySubscriptionDetails](https://react-native.adapty.io/interfaces/adaptysubscriptiondetails): - Se eliminó `promotionalOffer`. Ahora la oferta promocional se entrega dentro de la propiedad `offer` solo si está disponible. En ese caso, `offer?.identifier?.type` será `'promotional'`. - Se eliminó `introductoryOfferEligibility` (las ofertas solo se devuelven si el usuario es elegible). - Se eliminó `offerId`. El ID de la oferta ahora se almacena en `AdaptySubscriptionOffer.identifier`. - `offerTags` se movió a `AdaptySubscriptionOffer.android`. <p> </p> ```diff showLineNumbers - introductoryOffers?: AdaptyDiscountPhase[]; + offer?: AdaptySubscriptionOffer; ios?: { - promotionalOffer?: AdaptyDiscountPhase; subscriptionGroupIdentifier?: string; }; android?: { - offerId?: string; basePlanId: string; - introductoryOfferEligibility: OfferEligibility; - offerTags?: string[]; renewalType?: 'prepaid' | 'autorenewable'; }; } ``` 3. [AdaptyDiscountPhase](https://react-native.adapty.io/interfaces/adaptydiscountphase): - El campo `identifier` se eliminó del modelo `AdaptyDiscountPhase`. El identificador de la oferta ahora se almacena en `AdaptySubscriptionOffer.identifier`. <p> </p> ```diff showLineNumbers - ios?: { - readonly identifier?: string; - }; ``` ### Modelos eliminados \{#remove-models\} 1. `AttributionSource`: - Ahora se usa un string en los lugares donde antes se usaba `AttributionSource`. 2. `OfferEligibility`: - Este modelo se ha eliminado porque ya no es necesario. Ahora, una oferta solo se devuelve si el usuario es elegible. ## Elimina el método `getProductsIntroductoryOfferEligibility` \{#remove-getproductsintroductoryoffereligibility-method\} Antes del SDK de Adapty 3.3.1, los objetos de producto siempre incluían las ofertas, incluso si el usuario no era elegible. Esto requería verificar la elegibilidad manualmente antes de usar la oferta. A partir de la versión 3.3.1, el objeto de producto incluye ofertas solo si el usuario es elegible. Esto simplifica el proceso, ya que puedes asumir que el usuario es elegible si hay una oferta presente. ## Actualiza el proceso de compra \{#update-making-purchase\} En versiones anteriores, las compras canceladas y pendientes se trataban como errores y devolvían los códigos `2: 'paymentCancelled'` y `25: 'pendingPurchase'`, respectivamente. A partir de la versión 3.3.1, las compras canceladas y pendientes se consideran resultados exitosos y deben manejarse en consecuencia: ```typescript showLineNumbers 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 } ``` ## Actualiza la presentación de paywalls del Paywall Builder \{#update-paywall-builder-paywall-presentation\} Para ver ejemplos actualizados, consulta la documentación [Presentar paywalls nuevos del Paywall Builder en React Native](react-native-present-paywalls). ```diff showLineNumbers - 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 } ``` ## Actualiza la implementación del temporizador definido por el desarrollador \{#update-developer-defined-timer-implementation\} Renombra el parámetro `timerInfo` a `customTimers`: ```diff showLineNumbers - 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 }) ``` ## Modifica los eventos de compra del Paywall Builder \{#modify-paywall-builder-purchase-events\} Antes: - Las compras canceladas activaban el callback `onPurchaseCancelled`. - Las compras pendientes devolvían el código de error `25: 'pendingPurchase'`. Ahora: - Ambas se manejan mediante el callback `onPurchaseCompleted`. #### Pasos para migrar: \{#steps-to-migrate\} 1. Elimina el callback `onPurchaseCancelled`. 2. Elimina el manejo del código de error `25: 'pendingPurchase'`. 3. Actualiza el callback `onPurchaseCompleted`: ```typescript showLineNumbers 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; // highlight-start 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; // highlight-end } // highlight-start return purchaseResult.type !== 'user_cancelled'; // highlight-end }, }); ``` ## Modifica los eventos de acción personalizada del Paywall Builder \{#modify-paywall-builder-custom-action-events\} Callbacks eliminados: - `onAction` - `onCustomEvent` Callback añadido: - Nuevo callback `onCustomAction(actionId)`. Úsalo para acciones personalizadas. ## Modifica el callback `onProductSelected` \{#modify-onproductselected-callback\} Antes, `onProductSelected` requería el objeto `product`. Ahora requiere `productId` como string. ## Elimina los parámetros de integración de terceros del método `updateProfile` \{#remove-third-party-integration-parameters-from-updateprofile-method\} Los identificadores de integración de terceros ahora se configuran con el método `setIntegrationIdentifier`. El método `updateProfile` ya no los acepta. ## Actualiza la configuración del SDK de integraciones de terceros \{#update-third-party-integration-sdk-configuration\} Para garantizar que las integraciones funcionen correctamente con el SDK de Adapty React Native 3.3.1 y versiones posteriores, actualiza las configuraciones de tu SDK para las siguientes integraciones según se describe en las secciones a continuación. Además, si usabas `AttributionSource` para obtener el identificador de atribución, cambia tu código para proporcionar el identificador requerido como string. ### Adjust \{#adjust\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Adjust](adjust#connect-your-app-to-adjust). ```diff showLineNumbers 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 \{#airbridge\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AirBridge](airbridge#connect-your-app-to-airbridge). ```diff showLineNumbers 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 \{#amplitude\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Amplitude](amplitude#sdk-configuration). ```diff showLineNumbers 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 \{#appmetrica\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AppMetrica](appmetrica#sdk-configuration). ```diff showLineNumbers 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 \{#appsflyer\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AppsFlyer](appsflyer#connect-your-app-to-appsflyer). ```diff showLineNumbers 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 \{#branch\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Branch](branch#connect-your-app-to-branch). ```diff showLineNumbers 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 \{#facebook-ads\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Facebook Ads](facebook-ads#connect-your-app-to-facebook-ads). ```diff showLineNumbers 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 y Google Analytics \{#firebase-and-google-analytics\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Firebase y Google Analytics](firebase-and-google-analytics). ```diff showLineNumbers 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 \{#mixpanel\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Mixpanel](mixpanel#sdk-configuration). ```diff showLineNumbers 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 \{#onesignal\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con OneSignal](onesignal#sdk-configuration). <Tabs groupId="current-os" queryString> <TabItem value="v5+" label="OneSignal SDK v5+ (current)" default> ```diff showLineNumbers import { adapty } from 'react-native-adapty'; import OneSignal from 'react-native-onesignal'; OneSignal.User.pushSubscription.addEventListener('change', (subscription) => { const subscriptionId = subscription.current.id; if (subscriptionId) { - adapty.updateProfile({ - oneSignalSubscriptionId: subscriptionId, - }); + adapty.setIntegrationIdentifier("one_signal_subscription_id", subscriptionId); } }); ``` </TabItem> <TabItem value="pre-v5" label="OneSignal SDK v. up to 4.x (legacy)" default> ```diff showLineNumbers import { adapty } from 'react-native-adapty'; import OneSignal from 'react-native-onesignal'; OneSignal.addSubscriptionObserver(event => { const playerId = event.to.userId; - adapty.updateProfile({ - oneSignalPlayerId: playerId, - }); + adapty.setIntegrationIdentifier("one_signal_player_id", playerId); }); ``` </TabItem> </Tabs> ### Pushwoosh \{#pushwoosh\} Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Pushwoosh](pushwoosh#sdk-configuration). ```diff showLineNumbers 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` } ``` ## Actualiza la implementación del modo Observer \{#update-observer-mode-implementation\} Actualiza la forma en que vinculas los paywalls a las transacciones. Antes, usabas el método `setVariationId` para asignar el `variationId`. Ahora puedes incluir el `variationId` directamente al registrar la transacción con el nuevo método `reportTransaction`. Consulta el ejemplo de código final en [Asociar paywalls con transacciones de compra en el modo Observer](report-transactions-observer-mode-react-native). :::warning No olvides registrar la transacción con el método `reportTransaction`. Si omites este paso, Adapty no reconocerá la transacción, no otorgará niveles de acceso, no la incluirá en los análisis ni la enviará a las integraciones. ¡Este paso es imprescindible! ::: :::note Ten en cuenta que el orden de los parámetros del método `reportTransaction` es diferente al del método `setVariationId`. ::: ```diff showLineNumbers const variationId = paywall.variationId; try { - await adapty.setVariationId(variationId, transactionId); + await adapty.reportTransaction(transactionId, variationId); } catch (error) { // handle the `AdaptyError` } ``` --- # File: migration-to-react-native-sdk-v3 --- --- title: "Migrar el SDK de Adapty para React Native a la versión 3.0" description: "Migra al SDK de Adapty para React Native v3.0 para un mejor rendimiento y nuevas funciones de monetización." --- El SDK de Adapty v.3.0 incluye compatibilidad con el nuevo y emocionante [Adapty Paywall Builder](adapty-paywall-builder), la nueva versión de la herramienta no-code y fácil de usar para crear paywalls. Con su máxima flexibilidad y ricas capacidades de diseño, tus paywalls serán más efectivos y rentables. ## Actualizar a la versión 3.0.1 \{#upgrade-to-version-301\} 1. Actualiza a la versión 3.0.1 como de costumbre. 2. Reemplaza los archivos de paywall de respaldo: 1. [Descarga la versión más reciente](fallback-paywalls) desde el Adapty Dashboard. 2. Guárdalos en el dispositivo del usuario y pásalos al método `.setFallbackPaywalls` tal como se describe [aquí](react-native-use-fallback-paywalls). --- # End of Documentation _Generated on: 2026-05-15T20:13:57.622Z_ _Successfully processed: 40/40 files_ # TUTORIAL - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.624Z Total files: 252 --- # File: is-adapty-right-for-me --- --- title: "¿Es Adapty lo que necesito?" description: "Descubre cómo Adapty se adapta a tu caso de uso. Tanto si estás lanzando una nueva app, optimizando ingresos o migrando desde otra herramienta — aquí es donde empezar." --- Adapty es una plataforma de compras in-app para apps móviles. Gestiona suscripciones, compras únicas y consumibles — desde el procesamiento de compras y la validación de recibos hasta analíticas, pruebas A/B e integraciones. Así es como Adapty funciona en distintos escenarios. ## Estoy lanzando una nueva app con compras in-app \{#im-launching-a-new-app-with-in-app-purchases\} Tanto si vas a vender suscripciones, compras únicas o consumibles, Adapty cubre toda la infraestructura: - **SDKs para 7 plataformas**: iOS, Android, React Native, Flutter, Unity, Kotlin Multiplatform y Capacitor. - **Gestión de compras**: Suscripciones con renovaciones y lógica de reintentos, compras únicas, consumibles y validación de recibos — todo gestionado por ti. - **Constructor de paywalls sin código**: Diseña y publica paywalls sin escribir código de UI. - **Analíticas desde el primer día**: Haz seguimiento de ingresos, trials, conversiones y más en cuanto lleguen tus primeros usuarios. ¿Listo para empezar? Sigue la [guía de inicio rápido](quickstart). ## Quiero pruebas A/B, analíticas e integraciones \{#i-want-ab-tests-analytics-and-integrations\} Adapty te ayuda a optimizar lo que ya funciona: - **Pruebas A/B**: Prueba distintos precios, diseños de paywalls, duraciones de trial y ofertas promocionales para encontrar lo que mejor convierte. Usa [Growth Autopilot](autopilot) para obtener un plan de pruebas basado en datos a partir del análisis de competidores y del mercado. - **Gráficos de analíticas**: Haz seguimiento de MRR, LTV, churn, retención y docenas de otras métricas. - **Segmentación de audiencia**: Dirige paywalls y ofertas personalizadas a grupos de usuarios específicos. - **Configuración remota de paywalls**: Itera sobre tus paywalls sin publicar una nueva versión de la app. - **Integraciones con terceros**: Envía eventos de compra a Amplitude, AppsFlyer, Adjust, Mixpanel y otras herramientas que ya usa tu equipo. Explora [Pruebas A/B](ab-tests), [Analíticas](analytics), [Integraciones de servicios de analíticas](analytics-integration) o [Integraciones de servicios de atribución](attribution-integration). ## Quiero implementar compras in-app con un LLM \{#i-want-to-implement-in-app-purchases-with-an-llm\} La documentación de Adapty está optimizada para usarse con asistentes de programación con IA como Cursor, Claude, ChatGPT y otros. Todas las páginas están disponibles en Markdown plano, y ofrecemos guías de implementación paso a paso asistidas por LLM para cada plataforma: - **Guías listas para copiar y pegar**: Envía la guía a tu LLM y deja que te guíe en cada etapa de la implementación. - **Acceso en Markdown**: Añade `.md` a cualquier URL de la documentación o haz clic en **Copy for LLM** para obtener una versión en texto limpio. - **Soporte de Context7 MCP**: Conecta la documentación de Adapty directamente a tu IDE con IA. Elige tu plataforma y empieza: [Integra Adapty con asistencia de IA](adapty-cursor). ## Quiero gestionar y optimizar campañas de Apple Ads \{#i-want-to-run-and-optimize-apple-ads-campaigns\} Si usas Apple Search Ads, el Apple Ads Manager de Adapty conecta el rendimiento de tus campañas directamente con las métricas de ingresos — sin necesidad de un MMP: - **Datos de rendimiento en tiempo real**: Haz seguimiento de campañas, grupos de anuncios y palabras clave. - **Seguimiento de ingresos de extremo a extremo**: Sigue la cadena desde la búsqueda hasta la instalación, el trial, la suscripción y el LTV. - **Predicciones y recomendaciones con IA**: Prevé retornos y obtén sugerencias de escalado. - **Automatizaciones basadas en reglas**: Mantén estables tus objetivos de CPA y ROAS. Empieza con [Apple Ads Manager](adapty-ads-manager). ## Quiero saber de dónde vienen mis usuarios \{#i-want-to-track-where-my-users-come-from\} Adapty User Acquisition es una solución de atribución integrada que conecta el gasto en publicidad con las instalaciones de la app y los ingresos por suscripciones: - **Dashboard de marketing unificado**: Visualiza ROAS, instalaciones e ingresos de todos tus canales en un solo lugar. - **Atribución integrada**: Conecta campañas publicitarias con instalaciones e ingresos sin depender de MMPs externos. - **Enlaces de seguimiento**: Genera enlaces en Adapty y añádelos a tus campañas para una atribución precisa. - **Deeplinks diferidos**: Dirige a los usuarios al contenido correcto tras la instalación, aunque no tuvieran la app cuando hicieron clic. - **Análisis de cohortes**: Analiza el rendimiento de adquisición y el comportamiento de los usuarios a lo largo del tiempo. Más información sobre [Adapty User Acquisition](adapty-user-acquisition). ## Quiero iterar rápido sin publicar nuevas versiones \{#i-want-to-iterate-fast-without-app-releases\} Una vez integrado Adapty, la mayor parte del trabajo diario ocurre en el dashboard — sin necesidad de nuevas versiones de la app: - **Constructor de paywalls sin código**: Diseña y actualiza paywalls en un editor visual y publica los cambios al instante. - **Pruebas A/B desde el dashboard**: Lanza experimentos, ajusta precios e intercambia ofertas sin tocar el código. - **Analíticas en el dashboard**: Monitoriza ingresos, churn, trials y conversiones en tiempo real. - **Informes por Slack y correo electrónico**: Recibe actualizaciones automáticas sobre las métricas que importan a tu equipo. Explora el [Paywall Builder](adapty-paywall-builder) o consulta [Analíticas](charts). ## Vendo en la web y necesito una app móvil \{#i-sell-on-the-web-and-need-a-mobile-app\} Si tus usuarios ya pagan a través de una web y estás añadiendo una app móvil, Adapty sincroniza las compras entre plataformas: - **Integración con Stripe y Paddle**: Sincroniza automáticamente las compras web en Adapty. - **Sincronización web a móvil**: Los usuarios que pagaron en la web obtienen acceso en tu app, y viceversa. - **Analíticas multiplataforma unificadas**: Visualiza los ingresos web y móvil en un solo dashboard. Configura la [integración con Stripe](stripe), la [integración con Paddle](paddle), o aprende a [sincronizar suscriptores web y móvil](sync-subscribers-from-web). ## Estoy migrando desde otra herramienta \{#im-migrating-from-another-tool\} Adapty facilita la migración desde otras plataformas de suscripciones: - **Guías de migración**: Instrucciones paso a paso para trasladarte desde otras plataformas de suscripciones. - **Observer Mode**: Mantén tu código de facturación actual y adopta Adapty de forma incremental con el [modo Observer](observer-vs-full-mode) — empieza con analíticas y pruebas A/B, y amplía cuando estés listo. - **Importación de datos históricos**: Lleva tu historial de transacciones existente a Adapty para que tus analíticas sigan siendo completas. Más información sobre [cómo migrar a Adapty](migrate-to-adapty-from-another-solutions) e [importar datos históricos](importing-historical-data-to-adapty). --- ¿Todavía explorando? La [guía de inicio rápido](quickstart) siempre es un buen punto de partida. --- # File: integrate-payments --- --- title: "Integrar con stores o plataformas de pago" description: "Integra Adapty con App Store, Google Play, stores personalizados, Stripe y Paddle." --- Para empezar con Adapty, primero integra con los stores donde tus usuarios compran productos. Adapty se conecta a varios app stores y proveedores de pago web, centralizando todas tus compras in-app y análisis en un solo lugar. ## Integrar con stores y pagos web \{#integrate-with-stores-and-web-payments\} Elige tu store a continuación para ver los pasos de integración detallados: - [App Store](initial_ios) - [Google Play](initial-android) - Pagos web: - [Stripe](stripe) - [Paddle](paddle) - [Otros stores](custom-store) ## Próximos pasos \{#next-steps\} Una vez que hayas conectado tu store o plataforma de pago, puedes continuar con [agregar productos](quickstart-products). --- # File: quickstart-products --- --- title: "Añadir productos" description: "Añade productos in-app o suscripciones a Adapty y vincúlalos con tus listados de App Store, Google Play, Stripe, Paddle o una tienda personalizada." --- :::tip ¿Estás configurando Adapty de forma programática? Puedes completar este paso con la [CLI para desarrolladores](developer-cli-quickstart). ::: Antes de usar las funciones principales de Adapty, necesitas añadir cada producto que vendes y vincularlo a cada store o plataforma de pago que utilices. Esta configuración te permite mostrar productos en los dispositivos de los usuarios y hacer seguimiento en analíticas más adelante. En Adapty, cualquier cosa que venda tu app es un **producto**. Si el mismo artículo existe en el App Store, Google Play o Stripe, puedes agruparlos en un único producto en Adapty. Configúralo una vez y gestiona todo desde un solo lugar. Vamos a añadir tu primer producto. <Tabs groupId="products" queryString> <TabItem value="no-products" label="No products in stores yet" default> <div style={{ maxWidth: '560px', margin: '0 auto 2rem', position: 'relative', aspectRatio: '16/9', width: '100%' }}> <iframe style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} src="https://www.youtube.com/embed/qUpC2XG-r5E?si=7Komyv4_PUQ4FaEH" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen /> </div> </TabItem> <TabItem value="products-in-stores" label="Products in stores already"> <div style={{ maxWidth: '560px', margin: '0 auto 2rem', position: 'relative', aspectRatio: '16/9', width: '100%' }}> <iframe style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} src="https://www.youtube.com/embed/nlkdKCF0SwY?si=VVigzHcpv3waKJmI" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen /> </div> </TabItem> </Tabs> ## Añade tu primer producto \{#add-your-first-product\} :::tip Esta guía de inicio rápido cubre lo básico para crear un producto. Para más detalles, consulta la guía sobre [cómo crear productos](create-product). ::: Supongamos que quieres añadir una suscripción mensual como producto. 1. Ve a [Products](https://app.adapty.io/products) desde el menú principal de Adapty. 2. Haz clic en **Create product** en la parte superior derecha. <img src={require('./img/products-tab.webp').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::important **Los siguientes pasos dependen de si ya tienes productos en el App Store y/o Google Play:** ::: <Tabs groupId="products" queryString> <TabItem value="no-products" label="No products in stores yet" default> :::important Antes de empezar, asegúrate de haber configurado la integración con [App Store](initial_ios) y/o [Google Play](initial-android). Para el App Store, asegúrate de haber [añadido la clave de la API de App Store Connect](app-store-connection-configuration#step-6-add-app-store-connect-api-key) para que Adapty pueda enviar los productos. ::: 3. Selecciona **Create a new product and push to stores**. 4. Añade los detalles del producto: - **Product name**: El nombre visible solo para ti en el Adapty Dashboard. - **Access Level**: El identificador único que determina qué funciones se desbloquean tras la compra. Si todos los usuarios de pago de tu app acceden a las mismas funciones, puedes usar el nivel de acceso predeterminado: `premium`. Para configuraciones más complejas, crea [niveles de acceso](access-level) adicionales. - **Subscription duration**: Selecciona la duración de la suscripción en la lista. - **Weekly/Monthly/2 Months/3 Months/6 Months/Annual**: La duración de la suscripción. - **Lifetime**: Usa el período de por vida para los productos que desbloquean las funciones premium de la app de forma permanente. - **Non-Subscriptions**: Para los productos que no son suscripciones y, por tanto, no tienen duración, usa non-subscriptions. Pueden usarse para desbloquear funciones adicionales, productos consumibles, etc. - **Consumables**: Los artículos consumibles se pueden comprar varias veces y se agotan durante el uso de la aplicación. Algunos ejemplos son la moneda del juego o extras. Ten en cuenta que los productos consumibles no afectan a los niveles de acceso. - **Price (USD)**: El precio del producto en USD. Este precio se usará como base para calcular y establecer automáticamente los precios en todos los países. Más adelante podrás [personalizar el precio para distintos países y regiones](edit-product#set-country-specific-prices). <img src={require('./img/create-product-push.webp').default} style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **Save & Continue** y cambia a la pestaña **App Store** o **Google Play** para rellenar los detalles del producto en la store. <Tabs> <TabItem value="App Store" label="App Store" default> - **Product ID**: Crea un ID único y permanente para el producto. - **Product group**: Selecciona un grupo de productos existente que hayas creado en App Store Connect, o haz clic en **Create new Product Group** y define su nombre e ID. Una vez que Adapty lo cree, podrás seleccionarlo en el desplegable. - **Screenshot**: Sube una captura de pantalla de la compra in-app que muestre claramente el artículo o servicio ofrecido. Esta captura se usa únicamente para la revisión del App Store y no se muestra en la tienda. Consulta los requisitos de tamaño y formato [aquí](https://developer.apple.com/help/app-store-connect/reference/app-information/screenshot-specifications/). :::warning Si es tu primer producto para esta app, debes enviarlo manualmente para revisión en App Store Connect. Esto no será necesario más adelante. Una vez finalizada la revisión, el estado del producto en Adapty se actualizará automáticamente. ::: </TabItem> <TabItem value="Google Play" label="Google Play" default> - **Base Product ID**: Crea un ID único y permanente para el producto. - **Subscription**: Selecciona un grupo de suscripciones existente que hayas creado en la Google Play Console, o haz clic en **Create new Product Group** y define su nombre e ID. Una vez que Adapty lo cree, podrás seleccionarlo en el desplegable. </TabItem> </Tabs> 6. Configura la oferta introductoria (prueba gratuita) seleccionando su **Free duration** en el desplegable. En esta configuración inicial puedes añadir una prueba gratuita introductoria. Una vez que el producto principal sea aprobado por las stores, podrás [añadir más ofertas](offers) (por ejemplo, promocionales o de recuperación) vinculando sus IDs existentes desde la consola de tu store. </TabItem> <TabItem value="products-in-stores" label="Products in stores already"> 3. Selecciona **Connect an existing store product**. 4. Añade los detalles del producto: - **Product name**: El nombre visible solo para ti en el Adapty Dashboard. - **Access level ID**: El identificador único que determina qué funciones se desbloquean tras la compra. Si todos los usuarios de pago de tu app acceden a las mismas funciones, puedes usar el nivel de acceso predeterminado: `premium`. Para configuraciones más complejas, crea [niveles de acceso](access-level) adicionales. - **Subscription duration**: Selecciona la duración de la suscripción en la lista. - **Weekly/Monthly/2 Months/3 Months/6 Months/Annual**: La duración de la suscripción. - **Lifetime**: Usa el período de por vida para los productos que desbloquean las funciones premium de la app de forma permanente. - **Non-Subscriptions**: Para los productos que no son suscripciones y, por tanto, no tienen duración, usa non-subscriptions. Pueden usarse para desbloquear funciones adicionales, productos consumibles, etc. - **Consumables**: Los artículos consumibles se pueden comprar varias veces y se agotan durante el uso de la aplicación. Algunos ejemplos son la moneda del juego o extras. Ten en cuenta que los productos consumibles no afectan a los niveles de acceso. - **Price (USD)**: El precio del producto en USD. Si tu producto ya está en la store, este valor no afectará a su precio real; puedes seleccionar cualquier valor de la lista. Más adelante podrás [personalizar los precios para distintas regiones](edit-product#set-country-specific-prices) directamente en el Adapty Dashboard. <img src={require('./img/product-info.webp').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <br /> 5. Añade los detalles de la store. Elige tu store: <Tabs> <TabItem value="App Store" label="App Store" default> - **App Store Product ID**: El identificador único que se usa para acceder a tu producto en los dispositivos. Si no lo encuentras, comprueba que el ID sea correcto y que pertenezca a la app correcta. </TabItem> <TabItem value="Google Play" label="Google Play" default> - **Google Play Product ID**: El identificador del producto en la Play Store. Selecciónalo en la lista de IDs de productos existentes. Si no lo encuentras, comprueba que el ID sea correcto y que pertenezca a la app correcta. - **Base plan ID**: El ID que define el plan base del producto en la Play Store. - **Legacy fallback product**: Un producto de respaldo que se usa exclusivamente para apps con versiones antiguas del SDK de Adapty (versión 2.5 e inferiores). Especifica el valor con el siguiente formato: `<subscription_id>:<base_plan_id>`. <details> <summary>Haz clic aquí para saber dónde encontrar el Product ID y el Base plan ID de Google Play.</summary> 1. Ve a **Monetize with Play > Products > Subscriptions** en tu cuenta de [Google Play Console](https://play.google.com/console/developers/android/app). 2. Abre la **Subscription** correspondiente a la compra. 3. Verás el Product ID en la sección **Subscription details** y el Base plan ID en la columna **ID and duration** de la sección **Base plans and offers**. <img src={require('./img/play-store-id.png').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </details> </TabItem> <TabItem value="Stripe" label="Stripe" default> - **Stripe Product ID**: El identificador único del producto en Stripe. - **Stripe Price ID**: El identificador único en Stripe para el precio asociado al producto. <details> <summary>Haz clic aquí para saber dónde encontrar el Product ID y el Price ID de Stripe.</summary> 1. Ve a tu [Catálogo de productos](https://dashboard.stripe.com/products?active=true) en Stripe. 2. Abre el producto que necesites. 3. Verás: - El Stripe Product ID (con formato `prod_...`) en la esquina superior derecha. - El Stripe Price ID (con formato `price_...`) en la columna **API ID** de la sección **Pricing**. <img src={require('./img/product-stripe.png').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </details> </TabItem> <TabItem value="Paddle" label="Paddle" default> - **Paddle Product ID**: El identificador único del producto en Paddle. - **Paddle Price ID**: El identificador único en Paddle para el precio asociado al producto. <details> <summary>Haz clic aquí para saber dónde encontrar el Product ID y el Price ID de Paddle.</summary> 1. Ve a tu [Catálogo de productos](https://vendors.paddle.com/products-v2) en Paddle. 2. Abre el producto que necesites. 3. Verás: - El Paddle Product ID (con formato `pro_...`) en la sección **Additional details**. - El Paddle Price ID (con formato `pri_...`) en la columna **ID** de la sección **Prices**. <img src={require('./img/paddle-product-price.webp').default} style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </details> </TabItem> <TabItem value="Custom" label="Custom store" default> Puedes seleccionar una store personalizada existente o añadir una nueva y asociarle un producto. Ten en cuenta que Adapty solo hace seguimiento de las transacciones del App Store, Google Play y Stripe. Para stores personalizadas, deberás enviar las transacciones usando el método [Set transaction](api-adapty/operations/setTransaction) de la API de servidor de Adapty. </TabItem> </Tabs> 6. Si lo necesitas, puedes [crear ofertas](create-offer) para el producto. Para añadir ofertas, haz clic en **Yes, add offers**. Si no, haz clic en **No, thanks**. Tu producto aparecerá en la lista de productos. <img src={require('./img/created-product.png').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </TabItem> </Tabs> ## Próximos pasos \{#next-steps\} Una vez que hayas añadido tus productos a Adapty, puedes pasar a [configurar paywalls](quickstart-paywalls), ya que es la única manera de empezar a venderlos. --- # File: quickstart-paywalls --- --- title: "Añadir un paywall para vender productos" description: "Crea y diseña paywalls en Adapty, luego añádelos a los placements para mostrar storefronts específicos a audiencias concretas." --- :::info Para continuar con esta guía, asegúrate de haber completado la [integración con el store](integrate-payments) y de haber creado al menos un producto tal como se describe en la [guía anterior sobre cómo añadir productos](quickstart-products). ::: :::tip ¿Configurando Adapty de forma programática? Puedes completar este paso usando el [Developer CLI](developer-cli-quickstart). ::: En Adapty, **los paywalls son la única forma de entregar productos a través de tu app**. Así puedes hacer un seguimiento fácil del rendimiento de diferentes conjuntos de productos entre grupos de usuarios y gestionar cómo se presentan visualmente. Para sacarle el máximo partido a Adapty, en esta guía crearemos un paywall para vender el producto que añadiste en el paso anterior. Cómo funciona: - **Paywall**: Un paywall es un contenedor para uno o más productos. Puede incluir un paywall visual creado en el paywall builder, información del producto o una configuración JSON para usar en tu código. Obtén más información sobre los [paywalls](paywalls). - **Placement**: Un placement es un punto concreto de tu app donde muestras un paywall, un onboarding o una prueba A/B. Los placements te permiten dirigirte a [audiencias](audience) específicas con tu paywall. Obtén más información sobre los [placements](placements). :::note Aunque no diseñes un paywall con Adapty, igualmente debes seguir esta guía y crear uno. Así podrás incluir tus productos en él y hacer seguimiento de las métricas de monetización. ::: <div style={{ maxWidth: '560px', margin: '0 auto 2rem', position: 'relative', aspectRatio: '16/9', width: '100%' }}> <iframe style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} src="https://www.youtube.com/embed/e4o7Z2tUGL8?si=ipwbW3VVN0fIg0R0" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen /> </div> ## 1. Crear el paywall \{#1-build-paywall\} Crear un paywall es cuestión de unos pocos clics: 1. Ve a [**Paywalls**](https://app.adapty.io/paywalls) en el menú principal de Adapty. 2. Haz clic en **Create paywall**. 3. Introduce un **Paywall name**. Es un identificador interno en el Adapty Dashboard. 4. Haz clic en **Add product** y elige los productos que quieres mostrar en el paywall. 5. Haz clic en **Create as a draft**. <img src="/assets/shared/img/quickstart-paywall.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Diseñar el paywall \{#design-paywall\} La forma más sencilla de diseñar un paywall es crearlo con el editor visual sin código de Adapty, que no requiere conocimientos de diseño ni de programación. Puedes elegir entre una amplia selección de plantillas diseñadas por profesionales o construir un paywall completamente personalizado para tu app. :::note Si no quieres usar el paywall builder, puedes implementar los paywalls manualmente usando [Remote Config](customize-paywall-with-remote-config) con payloads JSON personalizados. Más información sobre <InlineTooltip tooltip="implementar paywalls manualmente">Sigue la guía para tu plataforma: [iOS](ios-implement-paywalls-manually), [Android](android-implement-paywalls-manually), [React Native](react-native-implement-paywalls-manually), [Flutter](flutter-implement-paywalls-manually), [Unity](unity-implement-paywalls-manually).</InlineTooltip>. ::: :::tip Si tu app está publicada en el App Store, puedes crear un paywall único y de alta conversión adaptado a tu app en cuestión de segundos. Usa el generador de IA en la pestaña **Builder & Generator**. ::: <img src="/assets/shared/img/design-paywall-templates.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Vamos a diseñar tu primer paywall. Puedes crear paywalls atractivos con facilidad: 1. Abre **Builder & Generator** en la página del paywall. 2. Haz clic en **Build no-code paywall**. 3. Elige una plantilla y confirma tu elección. 4. Añade y personaliza los elementos según necesites. 5. Haz clic en **Save**. Para más detalles, consulta el artículo completo sobre el [Paywall builder](adapty-paywall-builder#paywall-elements). ## 2. Añadir el paywall a un placement \{#2-add-paywall-to-placement\} Ahora necesitas crear un <InlineTooltip tooltip="placement">Un placement es un punto concreto de tu app donde muestras un paywall, un onboarding o una prueba A/B. Los placements te permiten dirigirte a [audiencias](audience) específicas con tu paywall. Obtén más información sobre los [placements](placements).</InlineTooltip> con el paywall que acabas de crear. Empecemos con el más esencial: el placement de onboarding. Más adelante podrás añadir más [placements con significado](choose-meaningful-placements) a lo largo del recorrido del usuario. 1. Ve a [**Placements**](https://app.adapty.io/placements/paywalls) en el menú principal de Adapty. 2. Haz clic en **Create placement**. 3. Introduce un **Placement name** (por ejemplo, `main` u `onboarding`). Es un identificador interno en el Adapty Dashboard. 4. Introduce un **Placement ID**. Usarás este ID en el SDK de Adapty para cargar el paywall del placement. 5. Haz clic en **Run Paywall** y elige el paywall que quieres mostrar. 6. Haz clic en **Save & publish**. En el código de tu app solo tienes que incluir directamente los IDs de placement. Todo lo demás se configura en el Adapty Dashboard y puede modificarse en cualquier momento sin necesidad de actualizar la app. :::tip Adapty te da la flexibilidad de mostrar diferentes paywalls a distintos grupos de usuarios y analizar el rendimiento. Más información sobre [audiencias](audience) y [pruebas A/B](ab-tests). ::: <img src="/assets/shared/img/add-placement.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Pasos siguientes \{#next-steps\} Tras vincular tu paywall a un placement en Adapty, el siguiente paso es mostrarlo en un dispositivo. Pasemos a [integrar el SDK de Adapty](quickstart-sdk) en tu app. --- # File: quickstart-sdk --- --- title: "Integra el SDK de Adapty en el código de tu app" description: "Integra Adapty con App Store, Google Play, stores personalizadas, Stripe y Paddle." --- Integra el SDK de Adapty en tu app para: - Gestionar compras, validación de recibos y suscripciones sin configuración adicional - Crear y probar paywalls sin actualizar la app - Obtener análisis detallados de compras sin ninguna configuración: cohortes, LTV, churn y análisis de embudo incluidos - Mantener el estado de la suscripción del usuario siempre actualizado entre sesiones y dispositivos - Integrar tu app con servicios de atribución de marketing y análisis con una sola línea de código ## Cómo funciona \{#how-does-it-work\} Para una implementación básica del SDK de Adapty, solo necesitas ocuparte de tres cosas: 1. Instalar e inicializar el SDK. 2. Delegar el manejo de las compras in-app a Adapty. 3. Monitorear el estado de la suscripción en el perfil. Adapty determina el estado, el tipo y la fecha de vencimiento de la suscripción; el SDK simplemente consume esa información. El orden y los detalles pueden variar de una app a otra, pero básicamente eso es todo. ## Empezar \{#get-started\} Elige tu plataforma y comienza: **iOS** - **[Inicio rápido del SDK](ios-sdk-overview)** - **[Apps de ejemplo](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples)** **Android** - **[Inicio rápido del SDK](android-sdk-overview)** - **[App de ejemplo](https://github.com/adaptyteam/AdaptySDK-Android/tree/master/app)** **React Native** - **[Inicio rápido del SDK](react-native-sdk-overview)** - **[Apps de ejemplo](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/)** **Flutter** - **[Inicio rápido del SDK](flutter-sdk-overview)** - **[App de ejemplo](https://github.com/adaptyteam/AdaptySDK-Flutter/tree/master/example)** **Unity** - **[Inicio rápido del SDK](unity-sdk-overview)** - **[App de ejemplo](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Assets)** **Capacitor** - **[Inicio rápido del SDK](capacitor-sdk-overview)** - **[Apps de ejemplo](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples)** **Kotlin Multiplatform**: - **[Inicio rápido del SDK](kmp-sdk-overview)** - **[App de ejemplo](https://github.com/adaptyteam/AdaptySDK-KMP/tree/main/example)** ## Próximos pasos \{#next-steps\} Una vez que hayas configurado el SDK de Adapty en el código de la app, puedes pasar a [probar la implementación](quickstart-test). --- # File: quickstart-test --- --- title: "Prueba tu integración con Adapty" description: "Verifica rápidamente tu integración con Adapty probando la activación del SDK, la obtención de paywalls y las compras in-app en App Store, Google Play, Stripe y Paddle." --- ¡Todo listo! Ahora asegúrate de que tu integración funciona como se espera y de que puedes ver tus compras en el Adapty Dashboard. Realizar una compra de prueba es la mejor forma de verificar que tu integración funciona de extremo a extremo. Empieza con una compra in-app y luego valida los resultados. ## 1. Prueba las compras in-app \{#1-test-in-app-purchases\} Sigue la guía según tu store o plataforma de pago. ### App Store \{#app-store\} Te recomendamos usar una cuenta de prueba (Sandbox Apple ID) y realizar las pruebas en un dispositivo real. Para conocer todos los pasos de prueba en detalle, consulta el artículo sobre [pruebas en el Sandbox de App Store](test-purchases-in-sandbox). :::warning Realiza las pruebas en un dispositivo real para obtener los resultados más fiables. Opcionalmente puedes usar el simulador, pero no lo recomendamos ya que es menos confiable. ::: ### Google Play Store \{#google-play-store\} Crea un usuario de prueba y prueba tu app en un dispositivo real. Para conocer todos los pasos de prueba en detalle, consulta el artículo sobre [pruebas en Google Play Store](testing-on-android). :::note Google [recomienda](https://support.google.com/googleplay/android-developer/answer/14316361) usar un dispositivo real para las pruebas. Si decides usar un emulador, asegúrate de que tenga Google Play instalado para garantizar que tu app funciona correctamente. ::: ### Stripe \{#stripe\} Para probar compras en Stripe, necesitas conectar Stripe a Adapty usando la clave API del modo de prueba de Stripe. Las transacciones que realices desde el modo de prueba de Stripe se considerarán Sandbox en Adapty. Para conocer todos los pasos de conexión, consulta el [artículo de integración con Stripe](stripe#6-test-your-integration). ### Paddle \{#paddle\} Para probar compras en Paddle, necesitas conectar Paddle a Adapty usando la clave API del entorno de prueba de Paddle. Las transacciones que realices desde el entorno de prueba de Paddle se considerarán Test en Adapty. Para conocer todos los pasos de conexión, consulta el [artículo de integración con Paddle](paddle#4-test-your-integration). ## 2. Valida las compras de prueba \{#2-validate-test-purchases\} Después de realizar una compra de prueba, comprueba si aparece la transacción correspondiente en el [**Event Feed**](https://app.adapty.io/event-feed) del Adapty Dashboard. Si la compra no aparece en el **Event Feed**, significa que Adapty no la está registrando. Más información en la guía detallada sobre [validación de compras de prueba](validate-test-purchases). <img src="/assets/shared/img/test-event-feed.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Próximos pasos \{#next-steps\} ¡Felicidades por completar el onboarding de Adapty! Ya estás listo para hacer crecer tus compras in-app. Prepárate para el lanzamiento en producción: <Button id="release-checklist"> Lista de verificación para el lanzamiento </Button> O puedes continuar con lo siguiente: - **[Pruebas A/B](ab-tests)**: Experimenta con distintos precios, duraciones de suscripción, períodos de prueba y elementos visuales para identificar las combinaciones más efectivas. - **[Analíticas](how-adapty-analytics-works)**: Explora métricas de monetización detalladas para entender el comportamiento de los usuarios y optimizar el rendimiento de los ingresos. - **Integraciones**: Adapty envía [eventos de suscripción](events) a herramientas de analítica y atribución de terceros, como [Amplitude](amplitude), [AppsFlyer](appsflyer), [Adjust](adjust), [Branch](branch), [Mixpanel](mixpanel), [Facebook Ads](facebook-ads), [AppMetrica](appmetrica) y un [Webhook](webhook) personalizado. --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> --- # File: release-checklist --- --- title: "Lista de verificación para el lanzamiento" description: "Sigue la lista de verificación de Adapty para garantizar un proceso de actualización de tu app sin problemas." --- ¡Nos alegra que hayas elegido Adapty! Esperamos que la implementación haya ido bien. Esta guía te llevará paso a paso para asegurarte de que tu app esté lista para publicarse en los stores y de que el flujo de monetización funcione correctamente. ## Elementos esenciales antes del lanzamiento \{#pre-flight-essentials\} Lo que necesitas antes de empezar la validación: - Un dispositivo real con una cuenta sandbox - Acceso al Adapty Dashboard - Acceso a App Store Connect / Google Play Console :::note Aunque las compras sandbox pueden ejecutarse en simuladores, necesitas dispositivos reales para probar todos los flujos, incluidos los diálogos de pago y las solicitudes biométricas. ::: <Button id="test-purchases-in-sandbox"> Guía de pruebas para App Store </Button> <Button id="testing-on-android"> Guía de pruebas para Google Play </Button> ## Validaciones universales \{#universal-validations\} - [ ] **Conexión con el store**: Asegúrate de haber conectado Adapty a App Store y/o Google Play: - [ ] [App Store](initial_ios) - [ ] [Google Play](initial-android) - [ ] **Entrega de eventos de suscripción**: Confirma que las notificaciones del servidor estén configuradas: - [ ] [Notificaciones del servidor de App Store](enable-app-store-server-notifications) - [ ] [Notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn) - [ ] **Identificación de perfiles**: Valida la lógica de identificación de usuarios y asegúrate de que las compras se asignen al perfil correcto: - [ ] [Comprueba que la lógica de identificación en tu código coincide con tu caso de uso](ios-quickstart-identify) - [ ] [Asegúrate de entender la lógica padre/heredero para compartir acceso de pago entre perfiles de usuario](sharing-paid-access-between-user-accounts) - [ ] **Ofertas**: Si tienes ofertas promocionales de App Store en la app, asegúrate de haber [añadido tu clave de compra in-app](app-store-connection-configuration#step-4-for-trials-and-special-offers--set-up-promotional-offers) tanto en el campo principal como en la sección **App Store promotional offers**. - [ ] **Recopilación de datos**: Garantiza el cumplimiento de la privacidad: - [ ] Si necesitas cumplir con normativas de privacidad como el RGPD o la CCPA, o si tu app está destinada a menores, controla si [habilitas la recopilación y el uso compartido de IDFA e IP](sdk-installation-ios#data-policies). - [ ] Si tu app usa AppTrackingTransparency, asegúrate de [enviar el estado de autorización a Adapty](ios-deal-with-att). - [ ] **Etiquetas de privacidad**: [Más información](apple-app-privacy) sobre los datos que recopila Adapty y qué indicadores debes configurar para la revisión. ## Validaciones de compras \{#purchase-validations\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Antes de publicar, asegúrate de que las compras in-app de tu app funcionan correctamente y de que tu paywall está listo para la revisión del store. La forma de validar las compras in-app depende de cómo las hayas implementado: - Muestras un paywall creado en el Adapty Paywall Builder - Has implementado tu propio paywall y usas el método `makePurchase` dentro de él para gestionar las compras - Usas Adapty en modo observador (ya sea con el Adapty Paywall Builder o con tu paywall personalizado) <Tabs groupId="paywall" queryString> <TabItem value="builder" label="Adapty Paywall Builder" default> **Objetivo**: Adapty renderiza el paywall, los usuarios pueden comprar productos, el acceso se desbloquea y el flujo de restauración funciona. - [ ] Tu app [muestra el paywall](ios-present-paywalls) desde el mismo placement que vas a publicar. - [ ] El paywall se muestra en pantalla. Si la carga tarda demasiado (por ejemplo, si tú o tus usuarios tenéis una conexión inestable), considera [ajustar tu política de obtención](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder). - [ ] El paywall coincide con la variante esperada (audiencia/idioma si aplica). Puedes [cambiar la prioridad de la audiencia](change-audience-priority) si es necesario. - [ ] Los productos y precios aparecen en el paywall. Ten en cuenta que la API de Apple puede ocasionalmente mostrar precios incorrectos durante las pruebas (especialmente con configuraciones de región distintas), así que prioriza probar la funcionalidad del flujo de compra sobre la exactitud de los precios, ya que Adapty no afecta a los precios del store. - [ ] La compra sandbox se completa con éxito. Se recibe el callback de compra exitosa. - [ ] El acceso se desbloquea y persiste. Confirma que [el acceso de pago se concede según el perfil de Adapty actual](ios-check-subscription-status#connect-profile-with-paywall-logic). - [ ] Tras la compra, el perfil de Adapty tiene un nivel de acceso activo. - [ ] Las funciones de pago se desbloquean cuando el perfil contiene ese nivel de acceso (no solo en el callback de compra). - [ ] La restauración de compras funciona. Cuando reinstales la app o la instales en un dispositivo nuevo, la restauración automática de compras funciona según la configuración de [Compartir acceso de pago](sharing-paid-access-between-user-accounts). Si no tienes autenticación en el backend, las compras se restauran automáticamente independientemente de la configuración. En otros casos, asegúrate de que los usuarios puedan restaurar sus compras tras reinstalar la app. - [ ] Requisitos para la revisión del store: - [ ] El botón **Restore purchases** está en el paywall. Puedes añadirlo en el paywall builder y procesará las restauraciones de compras automáticamente al pulsarlo. - [ ] Los Términos de uso y la Política de privacidad son accesibles desde la pantalla del paywall, y al hacer clic en estos enlaces se abren en un navegador. </TabItem> <TabItem value="makepurchase" label="Custom paywall (makePurchase)" default> **Objetivo**: Tú renderizas la interfaz; Adapty gestiona las compras, las actualizaciones del perfil y las restauraciones. - [ ] Los IDs de productos no están codificados en el código de tu app. Solo codificas los IDs de [placement](placements). - [ ] Tu app [obtiene los productos](fetch-paywalls-and-products) desde el mismo placement que vas a publicar. - [ ] La lista de productos se carga correctamente. Si la carga tarda demasiado (por ejemplo, si tú o tus usuarios tenéis una conexión inestable), considera [ajustar tu política de obtención](fetch-paywalls-and-products#fetch-paywall-information). - [ ] Los productos obtenidos coinciden con la variante esperada (audiencia/idioma si aplica). Puedes [cambiar la prioridad de la audiencia](change-audience-priority) si es necesario. - [ ] Los productos y precios aparecen en el paywall. Ten en cuenta que la API de Apple puede ocasionalmente mostrar precios incorrectos durante las pruebas (especialmente con configuraciones de región distintas), así que prioriza probar la funcionalidad del flujo de compra sobre la exactitud de los precios, ya que Adapty no afecta a los precios del store. - [ ] La compra sandbox con [makePurchase](making-purchases) se completa con éxito: - [ ] El resultado de compra exitosa se gestiona correctamente. - [ ] Los resultados pendientes/fallidos/cancelados se gestionan de forma adecuada. - [ ] Si [usas un Remote Config](present-remote-config-paywalls), sus valores se obtienen correctamente en tu paywall. - [ ] Cuando se muestra un paywall, se llama al [método `logShowPaywall`](present-remote-config-paywalls#track-paywall-view-events). - [ ] La compra sandbox se completa con éxito. Se recibe el callback de compra exitosa. - [ ] El acceso se desbloquea y persiste. Confirma que [el acceso de pago se concede según el perfil de Adapty actual](ios-check-subscription-status#connect-profile-with-paywall-logic). - [ ] Tras la compra, el perfil de Adapty tiene un nivel de acceso activo. - [ ] Las funciones de pago se desbloquean cuando el perfil contiene ese nivel de acceso (no solo en el callback de compra). - [ ] La restauración de compras funciona. Cuando reinstales la app o la instales en un dispositivo nuevo, la restauración automática de compras funciona según la configuración de [Compartir acceso de pago](sharing-paid-access-between-user-accounts). Si no tienes autenticación en el backend, las compras se restauran automáticamente independientemente de la configuración. En otros casos, asegúrate de que los usuarios puedan restaurar sus compras tras reinstalar la app. - [ ] Requisitos para la revisión del store: - [ ] El botón **Restore purchases** es accesible y [gestiona las restauraciones](restore-purchase). - [ ] Los Términos de uso y la Política de privacidad son accesibles desde la pantalla del paywall, y al hacer clic en estos enlaces se abren en un navegador. </TabItem> <TabItem value="observer" label="Observer mode"> **Objetivo**: Tú gestionas las compras, las actualizaciones del perfil y las restauraciones; Adapty recibe el reporte de transacciones. - [ ] **Tu app completa las compras usando tu propio flujo de compra** (StoreKit / BillingClient / backend): - [ ] La compra sandbox se completa con éxito en la interfaz del store. - [ ] Los resultados pendientes/fallidos/cancelados se gestionan de forma adecuada en tu app. - [ ] **Las transacciones se reportan a Adapty**. - [ ] El modo observador está [habilitado en el código de tu app](implement-observer-mode). - [ ] La compra aparece en el Event Feed de Adapty. - [ ] Las renovaciones, cancelaciones y reembolsos se reflejan con el tiempo (según corresponda). - [ ] **Las vistas del paywall se rastrean**. El [método `logShowPaywall`](present-remote-config-paywalls#track-paywall-view-events) se llama cuando se muestra un paywall. - [ ] **La restauración de compras funciona en tu implementación**. Reinstalar la app o cambiar de dispositivo restaura el acceso correctamente. - [ ] **Requisitos para la revisión del store**: - [ ] La acción **Restore purchases** es accesible y activa tu flujo de restauración. - [ ] Los Términos de uso y la Política de privacidad son accesibles desde el paywall o la pantalla de compra y se abren en un navegador. </TabItem> </Tabs> Si tienes alguna pregunta sobre la integración del SDK de Adapty, usa el chatbot de IA en la esquina inferior derecha o contáctanos en [support@adapty.io](mailto:support@adapty.io). --- # File: observer-vs-full-mode --- --- title: "Modo Observer" description: "Compara el modo Observer y el modo completo en Adapty para suscripciones." --- Adapty es una plataforma de compras in-app potente y flexible diseñada para impulsar tus ingresos y tu base de suscriptores. Con funciones como paywalls personalizables adaptados a segmentos de usuarios específicos, pruebas A/B de precios, duración, períodos de prueba y elementos visuales, además de herramientas analíticas completas para la monetización de apps e integraciones con terceros, Adapty potencia tu estrategia de crecimiento. Sin embargo, si ya tienes tu propia infraestructura de compras y no estás preparado para migrar al sistema de Adapty, puedes explorar el modo Observer de Adapty. Este modo limitado prescinde del uso de paywalls de Adapty, de su segmentación por audiencias de usuarios, de la gestión de suscripciones (incluidas renovaciones y reintentos de cobro), y se centra únicamente en la analítica. A pesar de sus limitaciones, el modo Observer sigue ofreciendo sólidas capacidades analíticas, como integración con sistemas de atribución, analítica avanzada, mensajería y perfiles CRM. Ambos modos tienen el mismo precio y requieren que actualices tu app móvil, por lo que la elección se reduce básicamente a migrar a la infraestructura de Adapty para obtener toda la funcionalidad, o mantener tu infraestructura actual y obtener únicamente integraciones con terceros y capacidades analíticas. | Funcionalidad | Modo Observer | Modo completo | |-------------|-------------|---------| | **Analítica completa** | ✅ | ✅ | | **Integraciones con terceros** | ✅ | ✅ | | **Respuesta a eventos de compra para dar/restringir acceso de pago a tus usuarios** | ❌ | ✅ | | **Responsable del mantenimiento de la infraestructura de compras** | Tú | Adapty | | **Pruebas A/B** | <p>Viable, pero requiere una cantidad significativa de código y configuración adicionales, más que en el modo completo.</p> | ✅ | | **Tiempo de implementación** | <p>Para analítica e integraciones: menos de una hora</p><p>Con pruebas A/B: hasta una semana con pruebas exhaustivas</p> | Varias horas | ## Cómo funciona el modo Observer \{#how-observer-mode-works\} En el modo Observer, tú reportas las nuevas transacciones de Apple/Google al SDK de Adapty, y el SDK las reenvía al backend de Adapty. Eres responsable de gestionar el acceso al contenido de pago en tu app, completar las transacciones, gestionar las renovaciones, resolver los problemas de facturación, etc. ## Cómo configurar el modo Observer \{#how-to-set-up-observer-mode\} 1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios). 2. Actívalo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. Sigue las instrucciones de configuración para [iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk), [Android](sdk-installation-android#activate-adapty-module-of-adapty-sdk), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk), [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform#activate-adapty-sdk) y [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk). 3. [Reporta las transacciones](report-transactions-observer-mode) desde tu infraestructura de compras existente a Adapty para iOS y frameworks multiplataforma basados en iOS. 4. (opcional) Si quieres usar integraciones con terceros, configúralas como se describe en el artículo [Configurar integraciones con terceros](configuration). :::warning Al operar en modo Observer, el SDK de Adapty no finaliza las transacciones, así que asegúrate de gestionar este aspecto tú mismo. ::: ## Cómo usar paywalls y pruebas A/B en el modo Observer \{#how-to-use-paywalls-and-ab-tests-in-observer-mode\} En el modo Observer, el SDK de Adapty no puede determinar el origen de las compras, ya que estas se realizan en tu propia infraestructura. Por lo tanto, si pretendes usar paywalls y/o pruebas A/B en el modo Observer, debes asociar la transacción procedente de tu store con el paywall correspondiente en el código de tu app móvil al reportar una transacción. Además, los paywalls diseñados con el Paywall Builder deben mostrarse de una forma especial cuando se usa el modo Observer: - Muestra paywalls en el modo Observer para [iOS](implement-observer-mode) o [Android](android-present-paywall-builder-paywalls-in-observer-mode). - [Asocia paywalls a transacciones de compra](report-transactions-observer-mode) al reportar transacciones en el modo Observer. --- # File: migration-from-revenuecat --- --- title: "Migración desde RevenueCat" description: "Migra de RevenueCat a Adapty con nuestra guía paso a paso." --- Tu plan de migración tiene 5 pasos lógicos y dura una media de 2 horas. El 90 % de todas las migraciones se completan en menos de un día laborable. 1. Aprende las diferencias clave y crea y prepara una cuenta de Adapty _(5 minutos)_; 2. Instala el SDK de Adapty para tu plataforma ([iOS](sdk-installation-ios), [Android](sdk-installation-android), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter), [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform), [Unity](sdk-installation-unity)) en lugar del SDK de RevenueCat _(1 hora)_; 3. Configura las [notificaciones de servidor de Apple App Store](enable-app-store-server-notifications) para Adapty y (opcionalmente) el [reenvío de eventos sin procesar](enable-app-store-server-notifications#raw-events-forwarding) _(5 minutos)_; 4. Prueba y publica la actualización de tu app _(30 minutos)_; 5. (Opcional) Solicita al soporte de RevenueCat los datos históricos en formato CSV _(5 minutos)_; 6. (Opcional) Importa los datos históricos a través del soporte de Adapty _(30 minutos)_. :::info Tus suscriptores migrarán automáticamente Todos los usuarios que alguna vez hayan activado una suscripción pasarán a Adapty automáticamente en cuanto abran la nueva versión de tu app con el SDK de Adapty. La validación del estado de la suscripción y el acceso premium se restaurarán de forma automática. ::: Antes de publicar una nueva versión de tu app con el SDK de Adapty, asegúrate de revisar nuestra [lista de verificación para el lanzamiento](release-checklist). ## Aprende las diferencias clave y crea y prepara una cuenta de Adapty \{#learn-the-core-differences-create-and-prepare-an-adapty-account\} Los SDKs de Adapty y RevenueCat tienen un diseño similar. La mayor diferencia está en el uso de red y la velocidad: el SDK de Adapty está diseñado para proporcionarte información lo más rápido posible cuando la solicitas. Por ejemplo, al pedir un paywall, primero recibes el [Remote Config](customize-paywall-with-remote-config) para precomponer tu onboarding o paywall, y luego solicitas los productos en una petición separada. Los nombres son ligeramente distintos: | RevenueCat | Adapty | | :---------- | :-------------- | | Package | Product | | Offering | Paywall | | Paywall | Paywall Builder | | Entitlement | Access level | Adapty tiene el concepto de [placement](placements). Es un lugar lógico dentro de tu app donde el usuario puede realizar una compra. En la mayoría de los casos, tendrás uno o dos placements: - Onboarding (ya que el 80 % de todas las compras se realizan ahí); - General (se muestra en los ajustes o dentro de la app después del onboarding). <img src="/assets/shared/img/2406d97-image.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '300px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Instala el SDK de Adapty y reemplaza el SDK de RevenueCat \{#install-adapty-sdk-and-replace-revenuecat-sdk\} Instala el SDK de Adapty para tu plataforma ([iOS](sdk-installation-ios), [Android](sdk-installation-android), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter), [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform), [Unity](sdk-installation-unity)) en tu app. Necesitas reemplazar algunos métodos del SDK en el lado de la app. Veamos las funciones más comunes y cómo sustituirlas por las del SDK de Adapty. ### Activación del SDK \{#sdk-activation\} Reemplaza `Purchases.configure` con `Adapty.activate`. ### Obtener paywalls (offerings) \{#getting-paywalls-offerings\} Reemplaza `Purchases.shared.getOfferings` con [`Adapty.getPaywall`](fetch-paywalls-and-products#fetch-paywall-information). En Adapty, siempre solicitas el paywall mediante el [placement id](placements). En la práctica, solo obtienes 1 o 2 paywalls como máximo, así que lo hemos diseñado así a propósito para acelerar el SDK y reducir el uso de red. ### Obtener un usuario (perfil del cliente) \{#getting-a-user-customer-profile\} Reemplaza `Purchases.shared.getCustomerInfo` con `Adapty.getProfile`. ### Obtener productos \{#getting-products\} En RevenueCat, usas la siguiente estructura: `Purchases.shared.getOfferings` y luego `self.offering?.availablePackages`. En Adapty, primero solicitas un paywall (ver arriba) para acceder de inmediato al [Remote Config](customize-paywall-with-remote-config) de Adapty, y luego obtienes los productos con [`Adapty.getPaywallProducts`](fetch-paywalls-and-products#fetch-products). ### Realizar una compra \{#making-a-purchase\} Reemplaza `Purchases.shared.purchase` con [`Adapty.makePurchase`](making-purchases#make-purchase). ### Verificar el nivel de acceso (entitlement) \{#checking-access-level-entitlement\} Obtén el perfil del cliente (lee el apartado anterior primero) y luego reemplaza `customerInfo?.entitlements["premium"]?.isActive == true` con [`profile.accessLevels["premium"]?.isActive == true`](subscription-status#retrieving-the-access-level-from-the-server). ### Restaurar una compra \{#restore-purchase\} Reemplaza `Purchases.shared.restorePurchases` con [`Adapty.restorePurchases`](restore-purchase). ### Comprobar si el usuario ha iniciado sesión \{#check-if-the-user-is-logged-in\} Reemplaza `Purchases.shared.isAnonymous` con `if profile.customerUserId == nil`. ### Iniciar sesión con un usuario \{#log-in-user\} Reemplaza `Purchases.shared.logIn` con [`Adapty.identify`](identifying-users#set-customer-user-id-after-configuration). ### Cerrar sesión de un usuario \{#log-out-user\} Reemplaza `Purchases.shared.logOut` con [`Adapty.logout`](identifying-users#logging-out-and-logging-in). ## Cambia las notificaciones del servidor de App Store a Adapty \{#switch-app-store-server-side-notifications-to-adapty\} Lee cómo hacerlo [aquí](migrate-to-adapty-from-another-solutions#changing-apple-server-notifications). ## Prueba y publica una nueva versión de tu app \{#test-and-release-a-new-version-of-your-app\} Si estás leyendo esto, ya has: - [x] Configurado el Adapty Dashboard - [x] Instalado el SDK de Adapty - [x] Reemplazado la lógica del SDK con las funciones de Adapty - [x] Cambiado las notificaciones del servidor de App Store a Adapty y, opcionalmente, activado el reenvío de eventos sin procesar a RevenueCat - [ ] Realizado una compra en sandbox - [ ] Publicado una nueva versión de la app Si has marcado los puntos anteriores, haz una compra de prueba en el Sandbox y luego publica la app. :::info Repasa la [lista de verificación para el lanzamiento](release-checklist). Haz la revisión final con nuestra lista para validar la integración existente o añadir funciones adicionales como integraciones de [atribución](attribution-integration) o [análisis](analytics-integration). ::: ## (Opcional) Exporta tus datos históricos de RevenueCat en formato CSV \{#optional-export-your-revenuecat-historical-data-in-csv-format\} :::warning No te precipites con la importación de datos históricos Deberías esperar al menos una semana después de publicar la versión con el SDK antes de importar los datos históricos. Durante ese tiempo recopilaremos toda la información sobre los precios de compra desde el SDK, por lo que los datos que importes serán más relevantes. ::: Exporta tus datos históricos de RevenueCat en formato CSV siguiendo las instrucciones de la [documentación oficial de RevenueCat](https://www.revenuecat.com/docs/integrations/scheduled-data-exports). ## (Opcional) Solicita al soporte de RevenueCat los tokens de compra de Google \{#optional-ask-revenuecat-support-for-google-purchase-tokens\} Si necesitas importar transacciones de Google Play, contacta con el soporte de RevenueCat para obtener un archivo CSV con los Google Purchase Tokens a través de su [página de soporte](https://app.revenuecat.com/settings/support). El Google Purchase Token es un identificador único que proporciona Google Play para cada transacción, imprescindible para rastrear y verificar las compras con precisión en Adapty. Esta información no se incluye en el archivo de exportación estándar. El archivo contiene las siguientes tres columnas: - `user_id` - `google_purchase_token` - `google_product_id` ## Escríbenos para importar tus datos históricos \{#write-us-to-import-your-historical-data\} Contáctanos a través del chat del sitio web o envíanos un correo a [support@adapty.io](mailto:support@adapty.io) con tus archivos CSV. 1. Envía el archivo CSV que exportaste de RevenueCat directamente a nuestro equipo de soporte. 2. Si vas a importar transacciones de Google Play, incluye el archivo CSV con los Google Purchase Tokens que recibiste del soporte de RevenueCat. 3. Indícanos qué ID de usuario debe usarse como Customer User ID (el identificador principal de usuario en Adapty): `rc_original_app_user_id` o `rc_last_seen_app_user_id_alias`. Nuestro equipo de soporte importará tus transacciones a Adapty. Se importarán los siguientes datos para cada transacción: | Parámetro | Descripción | | ----------------------------- | ------------------------------------------------------------ | | user_id | Customer User ID, el identificador principal de tu usuario en Adapty y en tu sistema. | | apple_original_transaction_id | Para cadenas de suscripciones, esta es la fecha de compra de la transacción original, vinculada por `store_original_transaction_id`. | | google_product_id | El ID del producto en Google Play Store. | | google_purchase_token | Un identificador único proporcionado por Google Play para cada transacción, necesario para la validación. | | country | El país del usuario. | | created_at | La fecha y hora de creación del usuario. | | subscription_expiration_date | La fecha y hora en que expira la suscripción. | | email | El correo electrónico del usuario final. | | phone_number | El número de teléfono del usuario final. | | idfa | El Identificador para Anunciantes (IDFA), asignado por Apple al dispositivo de un usuario. | | idfv | El Identificador para Proveedores (IDFV), un código asignado a todas las apps de un mismo desarrollador y compartido entre esas apps en un dispositivo. | | advertising_id | Un identificador único proporcionado por el sistema operativo Android que los anunciantes pueden usar para el seguimiento. | | attribution_channel | El nombre del canal de marketing. | | attribution_campaign | El nombre de la campaña de marketing. | | attribution_ad_group | El grupo de anuncios de atribución. | | attribution_ad_set | El conjunto de anuncios de atribución. | | attribution_creative | La palabra clave creativa de atribución. | Además, se importarán los identificadores de integración para las siguientes integraciones: Amplitude, Mixpanel, AppsFlyer, Adjust y FacebookAds. ## Preguntas frecuentes \{#faq\} ### Instalé el SDK de Adapty correctamente y publiqué una nueva versión de la app. ¿Qué pasará con mis suscriptores existentes que no actualicen a la versión con el SDK de Adapty? \{#i-successfully-installed-adapty-sdk-and-released-a-new-app-version-with-it-what-will-happen-to-my-legacy-subscribers-who-did-not-update-to-a-version-with-adapty-sdk\} La mayoría de los usuarios cargan sus teléfonos por la noche, que es cuando App Store suele actualizar automáticamente todas sus apps, por lo que no debería ser un problema. Puede que quede un pequeño número de suscriptores de pago que no hayan actualizado, pero seguirán teniendo acceso al contenido premium. No tienes que preocuparte por ello ni forzarlos a actualizar. ### ¿Necesito exportar mis datos históricos de RevenueCat lo antes posible o los perderé? \{#do-i-need-to-export-my-historical-data-from-revenuecat-as-quickly-as-possible-or-will-i-lose-it\} No hace falta hacerlo con prisa; primero publica la versión con el SDK de Adapty y luego compártenos tus datos históricos. Restauraremos el historial de pagos de tus usuarios y completaremos los [perfiles](profiles-crm) y los [gráficos](charts). ### Uso MMP (AppsFlyer, Adjust, etc.) y herramientas de análisis (Mixpanel, Amplitude, etc.). ¿Cómo me aseguro de que todo funcionará correctamente? \{#i-use-mmp-appsflyer-adjust-etc-and-analytics-mixpanel-amplitude-etc-how-do-i-make-sure-that-everything-will-work\} Primero tienes que pasarnos los IDs de esos servicios de terceros a través de nuestro SDK para que podamos enviarles datos. Lee la guía de [integración de atribución](attribution-integration) y de [integración de análisis](analytics-integration). Para los datos históricos y los usuarios existentes, **asegúrate de pasarnos esos IDs a partir de los datos que exportaste de RevenueCat.** --- # File: migration-from-superwall --- --- title: "Migración desde Superwall" description: "Migra de Superwall a Adapty con una guía paso a paso que mapea cada llamada al SDK y cada concepto." --- La mayoría de las migraciones desde Superwall a Adapty llevan unas dos horas. Cambias el SDK, apuntas las notificaciones del servidor de la store a Adapty y publicas una nueva versión de la app. Tus suscriptores de pago conservan su acceso — Adapty lo restaura desde los recibos de App Store y Google Play en el primer arranque. :::info Tus suscriptores migrarán automáticamente Todos los usuarios que alguna vez hayan activado una suscripción pasan a Adapty en cuanto abran una nueva versión de tu app con el SDK de Adapty. La validación del estado de la suscripción y el acceso premium se restauran automáticamente. ::: ## Cómo está organizada esta guía \{#how-this-guide-is-organized\} La migración tiene seis pasos: 1. [Mapea los conceptos de Superwall a Adapty](#map-your-superwall-concepts-to-adapty) 2. [Instala el SDK de Adapty](#install-the-adapty-sdk) 3. [Reemplaza las llamadas al SDK](#replace-sdk-calls) 4. [Cambia las notificaciones del servidor de App Store y Google Play](#switch-app-store-and-google-play-server-notifications) 5. [Prueba y publica](#test-and-release) 6. [(Opcional) Importa datos históricos](#optional-import-historical-data) ## Mapea los conceptos de Superwall a Adapty \{#map-your-superwall-concepts-to-adapty\} La mayoría de los conceptos de Superwall tienen un equivalente directo en Adapty: | Superwall | Adapty | Qué cambia | | :------------------- | :------------------------------------------------ | :--------------------------------------------------------------------------- | | Campaign | [Placement](placements) + [Audience](audience) | La lógica de la campaña se divide en un placement (la ubicación) y una audiencia (la regla). | | Placement | [Placement](placements) | Mismo concepto, mismo nombre. | | Audience filter | [Audience](audience) | Los conjuntos de reglas viven dentro de un placement. | | Entitlement | [Nivel de acceso](access-level) | Identificador con nombre (por ejemplo, `premium`). | | WebView paywall | [Paywall de Paywall Builder](adapty-paywall-builder) | Renderizado por el SDK de Adapty de forma nativa en lugar de un `WKWebView`. | | `PurchaseController` | Integrado | No hay protocolo que implementar — Adapty gestiona las compras. | | Feature gating | Comprobación de [nivel de acceso](access-level) | Comprueba `profile.accessLevels["premium"]?.isActive`. | Hay dos cambios conceptuales que vale la pena tener en cuenta antes de tocar el código: - **Obtener y presentar son pasos separados**: El método `register` de Superwall obtiene el paywall, evalúa la campaña y presenta la interfaz en una sola llamada. Adapty divide estos pasos — obtienes el paywall, su configuración de vista y luego lo presentas. Esto añade unas pocas líneas, pero te permite precargar configuraciones, mostrar un estado de carga personalizado o cancelar la presentación según tu propia lógica. - **El estado de la suscripción es por nivel de acceso**: Superwall expone una sola propiedad publicada `subscriptionStatus`. Adapty devuelve un [`AdaptyProfile`](https://swift.adapty.io/documentation/adapty/adaptyprofile) con niveles de acceso con nombre, de modo que un usuario puede tener los niveles de acceso `sports` y `science` de forma independiente. Para lecturas síncronas, guarda en caché el perfil del `AdaptyDelegate` en lugar de llamar a `getProfile()` en cada carga de vista. ## Instala el SDK de Adapty \{#install-the-adapty-sdk\} Instala el SDK de Adapty para tu plataforma — [iOS](sdk-installation-ios), [Android](sdk-installation-android), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter), [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform), [Unity](sdk-installation-unity) o [Capacitor](sdk-installation-capacitor) — y elimina SuperwallKit de tu proyecto al mismo tiempo. ## Reemplaza las llamadas al SDK \{#replace-sdk-calls\} Revisa cada área de tu integración y sustituye la llamada de Superwall por su equivalente en Adapty. Los enlaces al final de cada subsección cubren los siete SDKs de plataforma — sigue el que corresponda a tu app. ### Inicializa el SDK \{#initialize-the-sdk\} Reemplaza `Superwall.configure` con `Adapty.activate`. Consulta la guía de instalación para tu plataforma — [iOS](sdk-installation-ios), [Android](sdk-installation-android), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter), [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform), [Unity](sdk-installation-unity) o [Capacitor](sdk-installation-capacitor). ### Identifica y desconecta usuarios \{#identify-and-log-out-users\} Reemplaza `Superwall.shared.identify` con `Adapty.identify` y `Superwall.shared.reset` con `Adapty.logout`. Ambos SDKs generan un perfil anónimo en el primer arranque, por lo que estas llamadas solo son necesarias cuando un usuario inicia o cierra sesión. Vuelve a obtener los paywalls después de identificar — el nuevo usuario puede resolverse a una audiencia diferente. Consulta la guía de identificación para tu plataforma — [iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), [Kotlin Multiplatform](kmp-identifying-users), [Unity](unity-identifying-users) o [Capacitor](capacitor-identifying-users). ### Obtén y presenta un paywall \{#fetch-and-present-a-paywall\} Reemplaza `Superwall.shared.register` con un flujo de dos pasos: obtén el paywall con `Adapty.getPaywall`, carga su configuración de vista con `AdaptyUI.getPaywallConfiguration` y luego preséntalo. Dos diferencias a destacar: - **El feature gating reemplaza el closure `feature:`**: Después de que se cierre el paywall, comprueba el nivel de acceso activo en el perfil devuelto (o en `Adapty.getProfile`) y ramifica desde ahí. - **Los paywalls son renderizados por el SDK**: Superwall renderiza los paywalls dentro de un `WKWebView`. Adapty renderiza los paywalls del Paywall Builder de forma nativa — las fuentes, la información del producto y los botones los dibuja el SDK. Consulta la guía de inicio rápido de paywalls para tu plataforma — [iOS](ios-quickstart-paywalls), [Android](android-quickstart-paywalls), [React Native](react-native-quickstart-paywalls), [Flutter](flutter-quickstart-paywalls), [Kotlin Multiplatform](kmp-quickstart-paywalls), [Unity](unity-quickstart-paywalls) o [Capacitor](capacitor-quickstart-paywalls). ### Comprueba el estado de la suscripción \{#check-subscription-status\} Reemplaza `Superwall.shared.subscriptionStatus` con una comprobación del nivel de acceso con nombre en el perfil: `profile.accessLevels["premium"]?.isActive`. Observa los cambios mediante `AdaptyDelegate.didLoadLatestProfile(_:)` en lugar del patrón de propiedad `@Published`, y guarda el perfil en caché en tu lado para lecturas síncronas. Consulta la guía de estado de suscripción para tu plataforma — [iOS](ios-check-subscription-status), [Android](android-check-subscription-status), [React Native](react-native-check-subscription-status), [Flutter](flutter-check-subscription-status), [Kotlin Multiplatform](kmp-check-subscription-status), [Unity](unity-check-subscription-status) o [Capacitor](capacitor-check-subscription-status). ### Gestiona compras y restauraciones \{#handle-purchases-and-restores\} Con el Paywall Builder, ambos SDKs procesan las compras automáticamente dentro de la interfaz del paywall — **puedes saltarte este paso**. Para paywalls personalizados, Superwall requiere una implementación de `PurchaseController`. Adapty no: reemplaza `PurchaseController.purchase` con `Adapty.makePurchase` y `PurchaseController.restorePurchases` con `Adapty.restorePurchases`. El SDK gestiona la validación por su cuenta. Consulta la guía de inicio rápido de paywall personalizado para tu plataforma — [iOS](ios-quickstart-manual), [Android](android-quickstart-manual), [React Native](react-native-quickstart-manual), [Flutter](flutter-quickstart-manual), [Kotlin Multiplatform](kmp-quickstart-manual), [Unity](unity-quickstart-manual) o [Capacitor](capacitor-quickstart-manual). ### Configura atributos de usuario \{#set-user-attributes\} Reemplaza `Superwall.shared.setUserAttributes` con `Adapty.updateProfile`. Consulta la guía de atributos de usuario para tu plataforma — [iOS](setting-user-attributes), [Android](android-setting-user-attributes), [React Native](react-native-setting-user-attributes), [Flutter](flutter-setting-user-attributes), [Kotlin Multiplatform](kmp-setting-user-attributes), [Unity](unity-setting-user-attributes) o [Capacitor](capacitor-setting-user-attributes). ## Cambia las notificaciones del servidor de App Store y Google Play \{#switch-app-store-and-google-play-server-notifications\} Apunta las notificaciones del servidor de la store a Adapty. Adapty funciona sin ellas, pero las analíticas, las integraciones de terceros y las métricas de pruebas A/B dependen de ellas: - **App Store**: Sigue [Habilitar notificaciones del servidor de App Store](enable-app-store-server-notifications). - **Google Play**: Sigue [Habilitar notificaciones en tiempo real para desarrolladores](enable-real-time-developer-notifications-rtdn). Si quieres ejecutar Superwall y Adapty en paralelo durante el lanzamiento, usa el [reenvío de eventos sin procesar](enable-app-store-server-notifications#raw-events-forwarding) — Adapty reenvía los eventos de la store a Superwall mientras verificas la nueva integración. ## Prueba y publica \{#test-and-release\} Antes de publicar, comprueba cada elemento: - [x] Configurado el Adapty Dashboard (productos, paywalls, placements, niveles de acceso) - [x] Instalado el SDK de Adapty - [x] Reemplazadas las llamadas al SDK de Superwall por sus equivalentes en Adapty - [x] Apuntadas las notificaciones del servidor de App Store y Google Play a Adapty - [ ] Realizada una compra en sandbox - [ ] Enviada una nueva versión de la app Revisa el [checklist de lanzamiento](release-checklist) para una validación final. ## (Opcional) Importa datos históricos \{#optional-import-historical-data\} Superwall no es dueño de tu estado de suscripción — lo son App Store y Google Play. Adapty valida los recibos en el primer arranque, por lo que los usuarios de pago conservan su acceso sin necesidad de ninguna importación. Si quieres que las transacciones históricas queden registradas en las analíticas de Adapty, sigue [Importar datos históricos a Adapty](importing-historical-data-to-adapty). Espera al menos una semana después del lanzamiento del SDK para que tenga tiempo de recopilar precios de compra actualizados. ## Preguntas frecuentes \{#faq\} ### ¿Qué pasa con los suscriptores que no actualizan la app? \{#what-happens-to-subscribers-who-dont-update-the-app\} La mayoría de los usuarios actualizan sus apps automáticamente durante la noche, por lo que la proporción de usuarios en la versión anterior disminuye rápidamente. Los suscriptores en la versión antigua conservan su acceso directamente a través de App Store o Google Play — no es necesario forzar una actualización. ### ¿Las audiencias de mis campañas de Superwall se migran? \{#do-my-superwall-campaign-audiences-carry-over\} No. Los filtros de audiencia de Superwall y las audiencias de Adapty se configuran en dashboards diferentes y usan identificadores distintos. Recrea tu segmentación como [audiencias](audience) dentro de los [placements](placements) de Adapty. La mayoría de las apps tienen uno o dos placements (onboarding y un trigger general dentro de la app), por lo que la reconstrucción suele ser rápida. ### ¿Tiene Adapty un equivalente a `getPresentationResult`? \{#does-adapty-have-an-equivalent-to-getpresentationresult\} No como una sola llamada. Para comprobar si un placement mostraría un paywall, llama a `Adapty.getPaywall(placementId:)` y ramifica según el resultado. Si la llamada tiene éxito, hay un paywall asignado para la audiencia de ese usuario. Si falla porque no hay ningún paywall configurado, omite la presentación y ejecuta tu lógica de respaldo. --- # File: importing-historical-data-to-adapty --- --- title: "Importar datos históricos en Adapty" description: "Importa datos históricos en Adapty para obtener analíticas detalladas." --- Después de instalar el SDK de Adapty y publicar tu app, puedes acceder a tus usuarios y suscriptores en la sección [Profiles](profiles-crm). Pero ¿qué pasa si tienes una infraestructura legacy y necesitas migrar a Adapty, o simplemente quieres ver tus datos existentes en Adapty? :::note La importación de datos no es obligatoria Adapty otorgará automáticamente niveles de acceso a los usuarios históricos y restaurará sus eventos de compra en cuanto abran la app con el SDK de Adapty integrado. Para este caso de uso, importar datos históricos no es necesario. Sin embargo, importar los datos garantiza unas analíticas precisas si tienes un volumen significativo de transacciones históricas, aunque en general no es un requisito para la migración. ::: Para importar datos en Adapty: 1. Exporta tus transacciones a un archivo CSV (se deben proporcionar archivos separados para iOS, Android y Stripe). Consulta la sección [Formato del archivo de importación](importing-historical-data-to-adapty#import-file-format) más abajo para conocer los requisitos detallados. 2. Si algún archivo supera 1 GB, prepara una muestra de datos con aproximadamente 100 líneas. 3. Sube todos los archivos a Google Drive (puedes comprimirlos, pero mantenlos separados). 4. Para las transacciones de iOS, asegúrate de que la sección **In-app purchase API** en [**App settings**](https://app.adapty.io/settings/ios-sdk) esté completada con el **Issuer ID**, **Key ID** y la **Private key** (archivo .P8), incluso si usas StoreKit 1. Consulta las secciones [Provide Issuer ID and Key ID](app-store-connection-configuration#step-2-provide-issuer-id-and-key-id) y [Upload In-App Purchase Key file](app-store-connection-configuration#step-3-upload-in-app-purchase-key-file) para obtener instrucciones detalladas. 5. Comparte los enlaces con nuestro equipo a través de [correo electrónico](mailto:support@adapty.io) o del chat en línea en el Adapty Dashboard. No te preocupes: importar datos históricos no creará duplicados, aunque esos datos se solapen con entradas ya existentes en Adapty. ## Limitaciones conocidas para Android \{#known-limitations-for-android\} 1. Solo se restaurarán las suscripciones activas; las transacciones expiradas no se restaurarán. 2. Solo se restaurarán las renovaciones más recientes de una suscripción; no se restaurará toda la cadena de compras. 3. Si el precio del producto ha cambiado desde la compra, se utilizará el precio actual, lo que puede dar lugar a precios incorrectos. :::note Si tienes un gran volumen de transacciones de Android, es posible que necesites [solicitar un aumento de cuota de la Google Play Developer API](google-play-quota-increase) antes de comenzar la importación para evitar superar el límite predeterminado de la API. ::: ## Formato del archivo de importación \{#import-file-format\} :::tip Si estás migrando desde RevenueCat, puedes enviar el archivo de exportación de RevenueCat directamente, sin necesidad de convertirlo. Consulta la [documentación de RevenueCat](https://www.revenuecat.com/docs/integrations/scheduled-data-exports) para obtener instrucciones de exportación. ::: Prepara tus datos en uno o varios archivos que cumplan las siguientes reglas: - [ ] El formato del archivo es .CSV. - [ ] Archivos separados para importaciones de Android, iOS y Stripe. - [ ] Cada archivo de importación contiene todas las [columnas requeridas](importing-historical-data-to-adapty#required-fields). - [ ] Las columnas de los archivos de importación tienen encabezados. - [ ] Los encabezados de columna coinciden exactamente con los de la columna **Column name** de la tabla de abajo. Comprueba que no haya errores tipográficos. - [ ] Las columnas que no son obligatorias pueden estar ausentes del archivo. No añadas columnas vacías para datos que no tengas. - [ ] Los archivos de importación no deben tener columnas adicionales que no se mencionen en la tabla. Si las hay, elimínalas. - [ ] Los valores están separados por comas. - [ ] Los valores no están entre comillas. - [ ] Si hay varios **apple_original_transaction_id** para un mismo usuario, añádelos todos como líneas separadas para cada **apple_original_transaction_id**. De lo contrario, es posible que no podamos restaurar las compras consumibles. Usa los siguientes archivos como ejemplos para [iOS](https://raw.githubusercontent.com/adaptyteam/adapty-docs/refs/heads/main/Downloads/adapty_import_ios_sample.csv) y [Android](https://raw.githubusercontent.com/adaptyteam/adapty-docs/refs/heads/main/Downloads/adapty_import_android_sample.csv). ### Columnas disponibles en el archivo de importación \{#available-import-file-columns\} | Nombre de columna | Presencia | Descripción | |-----------|--------|-----------| | **user_id** | obligatorio | ID de tu usuario | | **apple_original_transaction_id** | obligatorio para iOS | <p>El ID de transacción original u OTID ([más información](https://developer.apple.com/documentation/appstoreserverapi/originaltransactionid)), utilizado en el mecanismo de importación de StoreKit 2. Como un usuario puede tener varios OTID, basta con proporcionar al menos uno para una importación exitosa.</p><p></p><p>**Nota:** Para esta importación es necesario que las credenciales de la In-app purchase API estén configuradas en tu Adapty Dashboard. Aprende cómo hacerlo [aquí](app-store-connection-configuration#step-3-upload-in-app-purchase-key-file).</p> | | **google_product_id** | obligatorio para Google | ID del producto en la Google Play Store. | | **google_purchase_token** | obligatorio para Google | Identificador único que representa al usuario y el ID del producto de la compra in-app que realizó | | **google_is_subscription** | obligatorio para Google | Los valores posibles son `1` \| `0` | | **stripe_token** | obligatorio para Stripe | Token de un objeto de Stripe que representa una compra única. Puede ser el token de una Suscripción de Stripe (`sub_...`) o de un Payment Intent (`pi_...`). | | **subscription_expiration_date** | opcional | La fecha de expiración de la suscripción, es decir, la próxima fecha de cobro, con fecha y hora con zona horaria (2020-12-31T23:59:59-06:00) | | **created_at** | opcional | Fecha y hora de creación del perfil (2019-12-31 23:59:59-06:00) | | **birthday** | opcional | La fecha de nacimiento del usuario en formato 2000-12-31 | | **email** | opcional | El correo electrónico de tu usuario | | **gender** | opcional | El género del usuario | | **phone_number** | opcional | El número de teléfono de tu usuario | | **country** | opcional | formato [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) | | **first_name** | opcional | El nombre de tu usuario | | **last_name** | opcional | El apellido de tu usuario | | **last_seen** | opcional | La fecha y hora con zona horaria (2020-12-31T23:59:59-06:00) | | **idfa** | opcional | El identificador para anunciantes (IDFA) es un identificador de dispositivo aleatorio que Apple asigna al dispositivo del usuario. Solo aplicable a apps de iOS | | **idfv** | opcional | El identificador para proveedores (IDFV) es un código único asignado a todas las apps desarrolladas por un mismo desarrollador, en este caso las tuyas. Solo aplicable a apps de iOS | | **advertising_id** | opcional | El Advertising ID es un código único asignado por el sistema operativo Android que los anunciantes pueden usar para identificar de forma única el dispositivo de un usuario | | **amplitude_user_id** | opcional | El ID de usuario de Amplitude | | **amplitude_device_id** | opcional | El ID de dispositivo de Amplitude | | **mixpanel_user_id** | opcional | ID de usuario de Mixpanel | | **appmetrica_profile_id** | opcional | ID de perfil de usuario de AppMetrica | | **appmetrica_device_id** | opcional | El ID de dispositivo de AppMetrica | | **appsflyer_id** | opcional | Identificador único de AppsFlyer | | **adjust_device_id** | opcional | El ID de dispositivo de Adjust | | **facebook_anonymous_id** | opcional | Identificador único generado por Facebook para usuarios que interactúan con tu app o sitio web de forma anónima, es decir, sin haber iniciado sesión en Facebook | | **branch_id** | opcional | Identificador único de Branch | | **attribution_source** | opcional | La integración de origen de la atribución, por ejemplo, appsflyer | | **attribution_status** | opcional | organic | | **attribution_channel** | opcional | El canal de atribución que trajo la transacción | | **attribution_campaign** | opcional | La campaña de atribución que trajo la transacción | | **attribution_ad_group** | opcional | El grupo de anuncios de atribución que trajo la transacción | | **attribution_ad_set** | opcional | El conjunto de anuncios de atribución que trajo la transacción | | **attribution_creative** | opcional | Elementos visuales o textuales específicos utilizados en un anuncio o campaña de marketing que se rastrean para determinar su efectividad a la hora de generar acciones deseadas, como clics, conversiones o instalaciones | | **custom_attributes** | opcional | Define hasta 30 atributos personalizados como un diccionario JSON en formato clave-valor: <ul><li>**key**: (string) El nombre del atributo personalizado</li><li> **value**: (string, entero, float o booleano) El valor del atributo personalizado.</li></ul><p> Formato: `"{'string_value': 'some_value', 'float_value': 123.0, 'int_value': 456}"`.</p><p>Ten en cuenta el uso de comillas dobles y simples en el formato. Los valores booleanos y enteros se convertirán a float.</p> | ### Campos obligatorios \{#required-fields\} Hay 2 grupos de campos obligatorios para cada plataforma: **user_id** y los datos que identifican las compras específicas de la plataforma correspondiente. Consulta la tabla a continuación para conocer los campos obligatorios por plataforma. | Plataforma | Campos obligatorios | |--------|---------------| | iOS | <p>user_id</p><p>apple_original_transaction_id</p> | | Android | <p>user_id</p><p>google_product_id</p><p>google_purchase_token</p><p>google_is_subscription</p> | | Stripe | <p>user_id</p><p>stripe_token</p> | Sin estos campos, Adapty no podrá obtener las transacciones. Para unas analíticas de cohorte precisas, especifica `created_at`. Si no se proporciona, asumiremos que la fecha de instalación coincide con la fecha de la primera compra. ### Importar datos en Adapty \{#import-data-to-adapty\} Ponte en contacto con nosotros y comparte tus archivos de importación a través de [support@adapty.io](mailto:support@adapty.io) o del chat en línea en el [Adapty Dashboard](https://app.adapty.io/overview). --- # File: migrate-integrations-to-adapty --- --- title: "Migrar integraciones a Adapty" description: "Cambia las integraciones de analytics y atribución de una solución legacy a Adapty sin duplicar eventos ni interrumpir campañas." --- Migrar a Adapty requiere algo más que cambiar el SDK. Tus integraciones de analytics y atribución con terceros — herramientas como Amplitude y Adjust — también necesitan una transición coordinada. Si se hace con cuidado, el cambio genera muy pocos eventos duplicados o perdidos y no interrumpe tus campañas. ## Mapea tus eventos \{#map-your-events\} Los nombres de eventos son personalizables en la mayoría de las integraciones de Adapty. Puedes configurarlos para que coincidan con los nombres que ya usas en tus dashboards y campañas. Tanto tus informes de analytics como los de campañas seguirán funcionando con los mismos nombres de eventos tras el cambio. Para ver la lista completa de eventos disponibles en Adapty, consulta [Eventos](events). En el caso de Adjust, la integración utiliza IDs de eventos en lugar de nombres de eventos personalizados. Transfiere tus IDs de eventos existentes desde el dashboard de Adjust a la configuración de la integración de Adapty. Consulta la [guía de integración de Adjust](adjust) para más detalles. ## Cómo crea Adapty los eventos de integración \{#how-adapty-creates-integration-events\} Para enviar un evento a una integración, Adapty necesita tener un perfil de usuario. Un perfil se crea de una de estas dos formas: - **Importación histórica**: el perfil se crea cuando [importas datos históricos de transacciones](importing-historical-data-to-adapty) antes de que el SDK entre en funcionamiento. - **Interacción con el SDK**: el perfil se crea automáticamente cuando el usuario abre la app con el SDK de Adapty por primera vez. Adapty se entera de las compras realizadas en el sistema legacy en tiempo real. Sin embargo, solo puede enviar un evento de integración una vez que el perfil del comprador existe. Ese perfil se crea cuando el usuario abre la app con el SDK de Adapty. Los usuarios que no actualicen a la nueva versión no generarán eventos de integración. ## Prepárate antes del día de la migración \{#prepare-before-migration-day\} ### Excluye los eventos históricos \{#exclude-historical-events\} Activa **Exclude Historical Events** en los [ajustes de tu integración](configuration). Esto impide que los eventos anteriores a la primera sesión del usuario con el SDK de Adapty se envíen a la integración. Esta configuración es especialmente importante durante la [importación histórica](importing-historical-data-to-adapty), cuando Adapty procesa un gran volumen de transacciones pasadas de una vez. Sin ella, esas transacciones generarán un gran volumen de eventos en tu herramienta de analytics. ### Configura la integración con antelación \{#set-up-the-integration-in-advance\} Adapty te permite configurar y probar una integración mientras la mantienes desactivada. Puedes establecer credenciales, mapeo de eventos y filtros sin activar la integración hasta que estés listo. La configuración se guarda cuando la activas, así que no se pierde nada por mantenerla desactivada hasta el día de la migración. Para encontrar tu integración, consulta [Integraciones de atribución](attribution-integration), [Integraciones de analytics](analytics-integration), [Integraciones de servicios de mensajería](messaging) o [Integraciones de Webhook y ETL](webhook-and-etl). ## Realiza el cambio el día de la migración \{#switch-on-migration-day\} Desactiva la integración en tu solución legacy y actívala en Adapty al mismo tiempo. Ejecutar ambas simultáneamente generará eventos duplicados. Pausa las campañas de adquisición grandes el día de la migración. Esto reduce el riesgo de errores en la optimización de campañas causados por eventos en la ventana de solapamiento. ## Qué esperar \{#what-to-expect\} Algunos eventos de integración perdidos o duplicados durante la migración son inevitables. Cuando el cambio se realiza correctamente, el número de eventos afectados es insignificante. La principal fuente de huecos es el momento descrito anteriormente: Adapty solo puede enviar eventos de integración para una compra después de que exista el perfil del usuario. Las compras realizadas en el sistema legacy no generan eventos de integración en Adapty hasta que el comprador abre la app con el SDK de Adapty. ## Integraciones frente a notificaciones server-to-server \{#integrations-vs-server-to-server-notifications\} Adapty recomienda usar integraciones en lugar de reenviar las notificaciones server-to-server sin procesar del store directamente a tus herramientas de analytics o atribución. Con las integraciones: - **Formato unificado**: los eventos de todos los stores — App Store, Google Play, Stripe — utilizan el mismo formato de evento. - **Datos enriquecidos**: los eventos incluyen datos que Adapty recopila, como el estado de la suscripción y los atributos del usuario. Las notificaciones sin procesar no incluyen esto. --- # File: whats-new --- --- title: "Novedades" description: "Mantente al día con las últimas funciones y mejoras de Adapty" --- Descubre las últimas funciones, mejoras, actualizaciones del SDK y mejoras en la documentación que te ayudan a optimizar la estrategia de monetización de tu app. Esta página destaca los lanzamientos más importantes de cada mes. :::note ¿Tienes comentarios sobre las nuevas funciones? ¡Nos encantaría escucharte! Contáctanos a través del [tablón de comentarios sobre el producto](https://adapty.featurebase.app/en?b=69831ba5e82e7a3391632ec2). ::: ## Abril 2026 \{#april-2026\} - **Adapty Mail**: Campañas de email generadas por IA que convierten a usuarios en prueba en suscriptores de pago. Crea, envía y atribuye campañas desde tu proyecto de Adapty sin necesidad de ninguna plataforma de email independiente. [Más información](adapty-mail) - **Diagnóstico de paywall en Autopilot**: Descubre qué mejorar en tu paywall antes de crear una prueba. Sube una captura de pantalla y Autopilot te devuelve recomendaciones basadas en benchmarks de las apps con mejor rendimiento en tu categoría, además de sugerencias de diseño y texto generadas por IA. Las recomendaciones con benchmark se convierten en rondas de prueba A/B en tu plan de crecimiento. [Más información](autopilot-analysis#paywall-analysis) - **Orientación más clara para cada sugerencia de Autopilot**: Cada hipótesis ahora explica por qué es importante (una explicación basada en datos de cómo tu paywall se desvía de los patrones establecidos), qué cambiar y cómo configurar la prueba A/B, y qué métricas observar en la nueva sección "Cómo interpretar tus resultados". [Más información](autopilot-growth-plan#step-1-view-the-hypothesis) - **Mantén actualizado tu plan de crecimiento de Autopilot**: Actualiza el análisis para incorporar los últimos datos de mercado y nuevas sugerencias, y restaura cualquier versión anterior desde el historial de versiones si las nuevas no dan en el clavo. Las hipótesis ahora están agrupadas en pestañas de Precios, Visual y Geoprecios, y se pueden omitir o eliminar rondas individuales. [Más información](autopilot-growth-plan#restore-an-older-version-of-the-growth-plan) - **Distribución de ingresos por duración en Autopilot**: Comprueba si tus ingresos están sobreconcentrados en una duración de suscripción. Un nuevo gráfico de Market Insights muestra la composición de tus ingresos por duración junto con la media del sector para tu categoría y país. [Más información](autopilot-analysis#revenue-distribution-by-duration) - **LTV y predicciones de ingresos actualizadas**: El LTV predicho y los ingresos ahora utilizan los datos de retención de cohortes de tu propia app cuando hay suficiente historial, y promedios entre apps en caso contrario — así incluso las apps más nuevas obtienen predicciones utilizables en análisis y pruebas A/B. [Más información](predicted-ltv-and-revenue) - **Enviar todos los eventos en Adapty UA**: Dale a Meta y TikTok una visión más completa de las conversiones para un modelado de audiencias más preciso. Adapty ahora permite reenviar instalaciones y transacciones de usuarios orgánicos y no atribuidos a tu píxel, no solo de usuarios vinculados a una campaña. [Meta](ua-facebook#send-all-events) | [TikTok](ua-tiktok#send-all-events) - **Documentación en ruso y turco**: La documentación de Adapty ya está disponible en ruso (Русский) y turco (Türkçe). Cambia de idioma usando el selector de idioma en la navegación superior. ## Marzo 2026 \{#march-2026\} - **CLI para desarrolladores**: Gestiona tu cuenta de Adapty desde la terminal sin abrir el Dashboard. El CLI te permite crear apps, definir niveles de acceso, configurar productos, crear paywalls y configurar placements — todo scriptable para entornos automatizados. También hay disponible una [skill de Adapty CLI](https://github.com/adaptyteam/adapty-cli/tree/main/skills/adapty-cli) para ayudar a los asistentes de programación con IA a trabajar con el CLI. [Más información](developer-cli) - **Página general en Apple Ads Manager**: Consulta todas las métricas clave de Apple Ads en un solo lugar, cada una con un gráfico de tendencias. Filtra por app mediante el desplegable del encabezado, personaliza qué métricas se muestran y ajusta el tipo de gráfico y la visualización de ingresos. [Más información](ads-manager-overview) - **Inteligencia de mercado en Apple Ads Manager**: Descubre en qué palabras clave publican anuncios tus competidores en más de 50 países y añade las palabras clave de mejor rendimiento directamente a tus campañas. [Más información](ads-manager-market-intelligence) - **Automatizaciones de palabras clave de ciclo completo en Apple Ads Manager**: Ajusta pujas automáticamente, pausa o activa palabras clave y muévelas entre grupos de anuncios según las reglas de rendimiento que definas. [Más información](ads-manager-automations-keyword-rules) - **Historial de pujas en Apple Ads Manager**: Consulta el registro completo de cambios de la puja CPT de cualquier palabra clave: cuándo ocurrió cada cambio, los valores anterior y nuevo, y qué regla de automatización lo desencadenó. [Más información](ads-manager-manage-keywords#bid-history) - **Rondas visuales en Autopilot**: Las sugerencias de diseño de paywall ahora son rondas de primera clase en tu plan de crecimiento — aparecen en la barra lateral junto a las rondas de monetización. Cada ronda visual incluye un mockup de diseño, una descripción de cuándo funciona mejor ese patrón y las métricas clave que tiene como objetivo. [Más información](autopilot-growth-plan#view-the-growth-plan) - **Añade tu propia hipótesis a Autopilot**: Amplía tu plan de crecimiento con rondas personalizadas. Añade un título, descripción, tipo de ronda (monetización o visual), métricas objetivo y — en el caso de rondas de monetización — los productos involucrados. [Más información](autopilot-growth-plan#add-your-own-hypothesis) - **Reordena las rondas de Autopilot**: Arrastra y reordena las etapas de tu plan de crecimiento para ejecutar los experimentos en el orden que mejor se adapte a tu estrategia. [Más información](autopilot) - **Precios geográficos en Autopilot**: Prueba cambios de precio por país como un nuevo tipo de ronda en tu plan de crecimiento. A partir de los datos de Market Insights, Autopilot recomienda si aumentar, reducir o mantener los precios en cada país. Añade una recomendación como ronda de precios geográficos para ejecutarla como prueba A/B — se pueden ejecutar hasta 5 simultáneamente. [Más información](autopilot-growth-plan#geo-pricing-hypotheses) - **Automatizaciones de términos de búsqueda en Apple Ads Manager**: Promociona automáticamente los términos de búsqueda ganadores a palabras clave de coincidencia exacta y niégalos en el origen, sin necesidad de descargar informes manualmente. Las reglas se pueden crear desde plantillas o construir desde cero con condiciones y programaciones personalizadas. [Más información](ads-manager-automations-search-terms) - **Puja de Maximizar Conversiones en Apple Ads Manager**: Al crear campañas, ahora puedes seleccionar Maximizar Conversiones como estrategia de puja. El algoritmo de Apple maximiza las descargas dentro de tu presupuesto, guiado por un CPA objetivo opcional. [Más información](ads-manager-create-campaign) - **Integración con FunnelFox en Adapty UA**: Ya está disponible la nueva integración con FunnelFox en Adapty UA. [FunnelFox](ua-funnelfox) - **Documentación en chino**: La documentación de Adapty ya está disponible en chino (中文). Cambia de idioma con el selector de idioma en la navegación superior. ## Febrero de 2026 \{#february-2026\} - **Precios de productos por país**: Establece precios distintos por país directamente en el Adapty Dashboard — Adapty sincroniza los cambios con App Store Connect y Google Play de forma automática. Cada actualización de precios queda registrada en el registro de auditoría, sin que ningún cambio pase desapercibido. [Más información](edit-product) - **Precios de la competencia por país en Autopilot**: Compara los precios de tu suscripción con los de la competencia en tus mercados principales. [Más información](autopilot-analysis#market-and-competitor-analysis) - **Control de versiones de onboarding**: Rastrea y gestiona versiones de tus onboardings con un historial completo de versiones. Revisa los cambios y revierte cuando lo necesites. [Más información](onboarding-version-control) - **Gráficos de conversión de paywall en analytics**: Dos nuevos gráficos de conversión — Paywall view → Trial y Paywall view → Paid — muestran cómo tus paywalls convierten visitantes en suscriptores. [Más información](analytics-conversion) - **Segmentos duplicados**: Copia un segmento existente con todos sus filtros en lugar de reconstruir uno similar desde cero. Útil cuando se gestionan varias campañas o pruebas A/B con audiencias superpuestas. [Más información](segments#duplicate-segments) - **Notificaciones push en la app móvil de Adapty**: Configura notificaciones push para 14 tipos de eventos directamente en la app de Adapty para iOS y mantente al tanto de la actividad de suscripciones sin abrir el dashboard. [Más información](push-notifications) - **Kotlin Multiplatform SDK 3.15**: Añade soporte para onboardings, paywalls web y mejoras en la API. [Más información](migration-to-kmp-315) - **Capacitor SDK 3.16**: Añade soporte para Capacitor 8. Los proyectos que usen Capacitor 7 deben quedarse en el SDK v3.15. [Más información](migration-to-capacitor-316) - **Guías de integración del SDK asistidas por LLM**: Guías paso a paso para integrar Adapty con la ayuda de asistentes de programación con IA. Cada guía lleva a tu LLM a través de la implementación completa, desde la configuración en el dashboard hasta las compras. [iOS](adapty-cursor) | [Android](adapty-cursor-android) | [React Native](adapty-cursor-react-native) | [Flutter](adapty-cursor-flutter) | [Unity](adapty-cursor-unity) | [Kotlin Multiplatform](adapty-cursor-kmp) | [Capacitor](adapty-cursor-capacitor) ## Enero de 2026 \{#january-2026\} - **SDK de Capacitor publicado oficialmente**: El SDK de Capacitor ya está listo para producción tras un exhaustivo proceso de pruebas. Crea apps de suscripción para iOS y Android con Capacitor y soporte completo de integración con Adapty. [Más información](capacitor-sdk-overview) - **Autopilot para apps nuevas**: El análisis de Autopilot ya está disponible aunque tu app no tenga un historial extenso de transacciones. Obtén recomendaciones de optimización de precios basadas en datos y crea tu plan de crecimiento desde el primer día. [Más información](autopilot) - **Oportunidades de precios globales en Autopilot**: Identifica el potencial de ingresos en tus mercados más rentables con recomendaciones de precios por país. Autopilot analiza las tasas de conversión y el poder adquisitivo de tus 5 principales países, y te ofrece información basada en datos sobre si conviene subir, bajar o mantener los precios según el Índice de Precios de Adapty. [Más información](autopilot) - **Métricas de conversión de recuperación de facturación**: Nuevos gráficos de análisis rastrean los ingresos recuperados por problemas de facturación y períodos de gracia. Monitoriza "Billing issue converted", "Billing issue converted revenue", "Grace period converted" y "Grace period converted revenue" para medir tus esfuerzos de retención y recuperación. - **Gestión de anuncios directa en Apple Ads Manager**: Crea y gestiona tus campañas de Apple Ads directamente desde Adapty sin cambiar de plataforma. [Más información](ads-manager-manage-ads) - **Análisis de Apple Ads Manager**: Accede a métricas de rendimiento detalladas a nivel de anuncio y datos de atribución dentro de Adapty. Consulta el rendimiento de campañas, análisis de grupos de anuncios e información de atribución en un dashboard unificado. [Más información](adapty-ads-manager-analytics) - **Gráficos de atribución de Apple Ads**: Combina múltiples métricas de atribución en gráficos personalizables para analizar el rendimiento de tus Apple Ads junto con los datos de suscripción. [Más información](adapty-ads-manager-analytics#charts) - **Segmentos de atribución de Apple Ads**: Crea segmentos de usuarios basados en datos de atribución de Apple Ads con un flujo de trabajo simplificado de dos clics. Dirige tus campañas a usuarios por campaña, grupo de anuncios o palabra clave para análisis y experimentos más precisos. [Más información](ads-manager-create-segments) - **Nueva plataforma de documentación**: El sitio de documentación ha migrado a una nueva plataforma, lo que permite actualizaciones de funciones más rápidas y una experiencia de usuario mejorada con búsqueda, navegación y organización de contenido optimizadas. ## Diciembre 2025 \{#december-2025\} - **Documentación de Apple Ads Manager**: Combina los datos de tus campañas de Apple Search Ads con métricas de ingresos en un único dashboard de análisis. La nueva documentación cubre la creación de campañas, la gestión de grupos de anuncios y las formas de hacer seguimiento del ROI de tu inversión publicitaria junto al rendimiento de las suscripciones. [Más información](ads-manager) - **Paywalls web in-app**: Muestra paywalls basados en web dentro de tu app usando un navegador in-app, ofreciendo una experiencia fluida sin redirecciones externas. [iOS](ios-web-paywall#open-web-paywalls-in-an-in-app-browser) | [Android](android-web-paywall#open-web-paywalls-in-an-in-app-browser) | [React Native](react-native-web-paywall#open-web-paywalls-in-an-in-app-browser) | [Flutter](flutter-web-paywall#open-web-paywalls-in-an-in-app-browser) - **Traducción por línea con IA en Paywall Builder**: Selecciona elementos de texto específicos en tu paywall y tradúcelos con ayuda de la IA, lo que hace la localización más precisa y flexible. [Más información](add-paywall-locale-in-adapty-paywall-builder#translating-paywalls-with-ai) - **Segmentos dinámicos**: Crea segmentos de audiencia dinámicos que se actualizan automáticamente en función de ventanas de tiempo móviles. Por ejemplo, crea un segmento para "usuarios que instalaron la app en los últimos 7 días" que se actualice continuamente para mostrar siempre a tus clientes más recientes. [Más información](segments#available-attributes) - **Guías de configuración de campañas en Meta y TikTok**: Documentación paso a paso para crear y hacer seguimiento de campañas en Meta (Facebook e Instagram) y TikTok, con integración de seguimiento de conversiones y analíticas. [Meta](meta-create-campaign) | [TikTok](tiktok-create-campaign) - **Guías de inicio rápido para implementación manual de paywalls**: Implementa compras in-app más rápido con guías paso a paso que muestran cómo integrar el SDK de Adapty en tu UI de paywall personalizada. [iOS](ios-implement-paywalls-manually) | [Android](android-implement-paywalls-manually) | [React Native](react-native-implement-paywalls-manually) | [Flutter](flutter-implement-paywalls-manually) | [Unity](unity-implement-paywalls-manually) | [Kotlin Multiplatform](kmp-quickstart-manual) | [Capacitor](capacitor-quickstart-manual) - **Navegador integrado para los enlaces del onboarding**: Los enlaces externos en los onboardings se abren ahora por defecto en un navegador integrado, manteniendo a los usuarios dentro de la app. Puedes personalizar este comportamiento para usar navegadores externos si lo necesitas. [iOS](ios-present-onboardings#customize-how-links-open-in-onboardings) | [Android](android-present-onboardings#customize-how-links-open-in-onboardings) | [React Native](react-native-present-onboardings#customize-how-links-open-in-onboardings) - **Sugerencias mejoradas de Autopilot**: Autopilot ahora ofrece mejores recomendaciones de optimización de precios basadas en un análisis más detallado de los datos de tu suscripción. [Prueba Autopilot](autopilot) - **Modo oscuro en la documentación**: La documentación ahora es compatible con el modo oscuro, con detección automática de las preferencias del sistema o activación manual desde la esquina superior derecha. --- # File: generate-in-app-purchase-key --- --- title: "Generar la In-App Purchase Key en App Store Connect" description: "Genera una clave de compra in-app para transacciones seguras." --- La **In-App Purchase Key** es una clave de API especializada que se crea en App Store Connect para validar las compras confirmando su autenticidad. :::note Para generar claves de API para la App Store Server API, debes tener el rol de Admin o de Account Holder en App Store Connect. También puedes consultar cómo generar claves de API en la [Documentación para Desarrolladores de Apple](https://developer.apple.com/documentation/appstoreserverapi/creating-api-keys-to-authorize-api-requests). ::: 1. Abre **App Store Connect**. Ve a la sección [**Users and Access** → **Integrations** → **In-App Purchase**](https://appstoreconnect.apple.com/access/integrations/api/subs). 2. Haz clic en el botón de añadir **(+)** junto al título **Active**. <img src="/assets/shared/img/6d737db-generate_in-app_key.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Generate In-App Purchase Key** que se abre, introduce el nombre de la clave para tu referencia futura. No se utilizará en Adapty. 4. Haz clic en el botón **Generate**. Una vez que se cierre la ventana **Generate in-App Purchase Key**, verás la clave creada en la lista **Active**. <img src="/assets/shared/img/fac066b-download_inapp_file.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Una vez generada tu clave de API, haz clic en el botón **Download In-App Purchase Key** para obtener la clave como archivo. <img src="/assets/shared/img/d59faff-download_in-app_purchase_key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. En la ventana **Download in-App Purchase Key**, haz clic en el botón **Download**. El archivo se guardará en tu ordenador. Es fundamental mantener este archivo seguro para subirlo al Adapty Dashboard en el futuro. Ten en cuenta que el archivo generado solo se puede descargar una vez, así que asegúrate de guardarlo en un lugar seguro hasta que lo subas. La clave .p8 generada desde la sección **In-App Purchase** se utilizará al [configurar la integración inicial de Adapty con el App Store](app-store-connection-configuration#step-3-upload-in-app-purchase-key-file). **Próximos pasos:** - [Configurar la integración con App Store](app-store-connection-configuration) --- # File: app-store-connection-configuration --- --- title: "Configurar la integración con App Store" description: "Configura tu conexión con App Store para un seguimiento de suscripciones sin problemas." --- <div style={{ maxWidth: '560px', margin: '0 auto 2rem', position: 'relative', aspectRatio: '16/9', width: '100%' }}> <iframe style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} src="https://www.youtube.com/embed/VJQbzoTCkqs?si=l7BPX9mIu6GVGZ0Z" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen /> </div> Esta sección describe cómo establecer la conexión entre App Store y Adapty para tu app de iOS. Esto es necesario para que podamos mostrar analíticas de suscripciones y validar compras. Puedes completar la integración durante el onboarding inicial o más tarde en **App Settings** dentro del Adapty Dashboard. Aunque puede que hayas configurado inicialmente la integración de tu app y Adapty durante el onboarding, puedes modificar estos ajustes más tarde en **App settings**. :::danger Los cambios de configuración se pueden hacer de forma segura durante la fase sandbox, hasta que tu app móvil esté en producción con el SDK de Adapty instalado. Los cambios después del lanzamiento pueden interrumpir el flujo de compra en tu app. ::: ## Paso 1. Proporciona el Bundle ID y el Apple app ID \{#step-1-provide-bundle-id-and-apple-app-id\} El Bundle ID es el identificador único de tu app en el App Store. Es necesario para el funcionamiento básico de Adapty, como el procesamiento de suscripciones. --- no_index: true --- 1. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu app y ve a la sección **General** → **App Information**. 2. Copia el **Bundle ID** en la subsección **General Information**. <Zoom> <img src="/docs/img/afd5012-bundle_id_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 3. Abre la pestaña [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) desde el menú superior de Adapty y pega el valor copiado en el campo **Bundle ID**. <Zoom> <img src="/docs/img/2d64163-bundle_id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 4. Vuelve a la página **App information** en App Store Connect y copia el **Apple ID** desde allí. 5. En la página [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) del Adapty Dashboard, pega el ID en el campo **Apple app ID**. ## Paso 2. Proporciona el Issuer ID y el Key ID \{#step-2-provide-issuer-id-and-key-id\} El **In-app purchase Issuer ID**, denominado **Issuer ID** en App Store Connect, es un ID especial que identifica al emisor que creó el token de autenticación. El **In-App Purchase Key ID**, denominado **Key ID** en App Store Connect, es un identificador único asociado a una clave criptográfica que has generado en la sección [Generar In-App Purchase Key en App Store Connect](generate-in-app-purchase-key). 1. Abre **App Store Connect**. Ve a la sección [**Users and Access** → **Integrations** → **In-App Purchase**](https://appstoreconnect.apple.com/access/integrations/api/subs). 2. En la lista **Active**, encuentra la clave que creaste en la sección [Generar In-App Purchase Key en App Store Connect](generate-in-app-purchase-key). <img src="/assets/shared/img/19a2868-issuer_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Copia el **Issuer ID** y pégalo en el campo **In-app purchase Issuer ID** del Adapty Dashboard. <img src="/assets/shared/img/c2b42e7-issuer_id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Copia el **Key ID** y pégalo en el campo **In-app purchase Key ID** del Adapty Dashboard. ## Paso 3. Sube el archivo In-App Purchase Key \{#step-3-upload-in-app-purchase-key-file\} Sube el archivo **In-App Purchase Key** que descargaste en la sección [Generar In-App Purchase Key en App Store Connect](generate-in-app-purchase-key) <img src="/assets/shared/img/88cdfff-download_inapp_file.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> en el campo **Private key (.p8 file)** del Adapty Dashboard. <img src="/assets/shared/img/253b840-in-app_file_upload.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 4. Para pruebas y ofertas especiales – configura las ofertas promocionales \{#step-4-for-trials-and-special-offers--set-up-promotional-offers\} :::important Este paso es necesario si tu app tiene [pruebas u otras ofertas promocionales](offers). ::: 1. Copia el mismo Key ID que usaste en el [Paso 2](#step-2-provide-issuer-id-and-key-id) en el campo **Subscription key ID** de la sección **App Store promotional offers**. 2. Sube el mismo archivo **In-App Purchase Key** que usaste en el [Paso 3](#step-3-upload-in-app-purchase-key-file) en el área **Subscription key (.p8 file)** de la sección **App Store promotional offers**. <img src="/assets/shared/img/promo-key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 5. Introduce el App Store shared secret \{#step-5-enter-app-store-shared-secret\} El **App Store shared secret**, también conocido como App Store Connect Shared Secret, es una cadena hexadecimal de 32 caracteres que se usa para las compras in-app y la validación de recibos de suscripciones. 1. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu app y ve a la sección **General** → **App Information**. 2. Desplázate hacia abajo hasta la subsección **App-Specific Shared Secret**. <img src="/assets/shared/img/2bd112a-shared_secret_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::info Si la subsección **App-Specific Shared Secret** no aparece, asegúrate de tener el rol de Account Holder o Admin. Si tienes el rol de Admin y aun así no ves la subsección **App-Specific Shared Secret**, pide al Account Holder de la app (la persona que creó la aplicación en App Store Connect) que genere el App Store shared secret para la app. Después de eso, la subsección también será visible para los Admins. ::: 3. Haz clic en el botón **Manage**. <img src="/assets/shared/img/2d8b4c0-shared_secret_apple_copy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En la ventana **App-Specific Shared Secret** que se abre, copia el **Shared Secret**. Si no aparece ningún shared secret, primero haz clic en el botón **Manage** o **Generate** (el que esté disponible) y luego copia el **Shared Secret**. 5. Pega el **Shared Secret** copiado en el campo **App Store shared secret** del Adapty Dashboard. <img src="/assets/shared/img/4f9624d-shared_secret.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Haz clic en el botón **Save** del Adapty Dashboard para confirmar los cambios. ## Paso 6. Añade la clave API de App Store Connect \{#step-6-add-app-store-connect-api-key\} Genera una clave API de App Store Connect y añádela a Adapty para poder [gestionar tus productos en el App Store desde el Adapty dashboard](create-product#create-product-and-push-to-store): 1. En App Store Connect, ve a [**Users and Access > Integrations > Team keys**](https://appstoreconnect.apple.com/access/integrations/api) y haz clic en **+**. <img src="/assets/shared/img/app-store-connect-api.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la ventana **Generate API key**, introduce un nombre para la clave y concédele acceso **Admin**. <img src="/assets/shared/img/generate-api-key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Download** junto a tu clave. Ten en cuenta que solo puedes descargarla una vez. <img src="/assets/shared/img/download-api-key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En el Adapty dashboard, ve a [**App settings > iOS SDK**](https://app.adapty.io/settings/ios-sdk) y haz clic en **Connect API key**. <img src="/assets/shared/img/connect-api-key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Rellena los campos de la ventana: - **Issuer ID**: Cópialo desde [**Users and Access > Integrations > Team keys**](https://appstoreconnect.apple.com/access/integrations/api). Está encima de la tabla **API keys**. <img src="/assets/shared/img/issuer-id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> - **Key ID**: Cópialo desde [**Users and Access > Integrations > Team keys**](https://appstoreconnect.apple.com/access/integrations/api). Está en la tabla **API keys**, junto a tu clave. <img src="/assets/shared/img/key-id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> - **API key**: Sube el archivo de clave API que descargaste desde App Store Connect. <img src="/assets/shared/img/app-store-connect-key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '500px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Haz clic en **Connect**. **Próximos pasos** - [Habilitar notificaciones del servidor de App Store](enable-app-store-server-notifications) --- # File: enable-app-store-server-notifications --- --- title: "Activar notificaciones de servidor de App Store" description: "Activa las notificaciones de servidor de App Store para rastrear eventos de suscripción en tiempo real." --- Configurar las notificaciones de servidor de App Store es fundamental para garantizar la precisión de los datos, ya que te permite recibir actualizaciones en tiempo real desde App Store, incluyendo información sobre reembolsos y otros eventos. :::important Se requiere Adapty iOS SDK 2.10.0 o posterior para soporte completo de App Store Server Notifications V2. ::: 1. Copia la **URL for App Store server notification** en el Adapty Dashboard. <img src="/assets/shared/img/2901185-app_server_notifications.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu aplicación y ve a la sección **General** → **App Information**, subsección **App Store Server Notifications**. 3. Pega la **URL for App Store server notification** copiada en los campos **Production Server URL** y **Sandbox Server URL**. <img src="/assets/shared/img/86fb3d2-app_server_notifications_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Reenvío de eventos sin procesar \{#raw-events-forwarding\} En algunos casos, puede que quieras seguir recibiendo eventos S2S sin procesar desde Apple. Para continuar recibiéndolos mientras usas Adapty, simplemente añade tu endpoint al campo **URL for forwarding raw Apple events** y te enviaremos los eventos tal como los recibimos de Apple. <img src="/assets/shared/img/e9f4bba-CleanShot_2021-03-16_at_19.30.272x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> **Siguientes pasos** Configura el SDK de Adapty para: - [iOS](sdk-installation-ios) - [React Native](sdk-installation-reactnative) - [Flutter](sdk-installation-flutter) - [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform) - [Unity](sdk-installation-unity) --- # File: troubleshoot-app-store-integration --- --- title: "Solucionar problemas de integración con App Store" description: "Resuelve los problemas más comunes de configuración con la App Store de Apple: acuerdos pendientes, retrasos en notificaciones del servidor y discrepancias de precios." --- Este artículo cubre los problemas más comunes de integración con App Store. Cada sección describe los síntomas, la causa raíz y la solución. ## Los productos no aparecen \{#products-dont-appear\} Dos síntomas distintos apuntan a la misma causa raíz: - La clave API de App Store Connect está configurada correctamente, pero Adapty no puede obtener los productos. - Los productos existen en App Store Connect pero no aparecen en Adapty, o aparecen menos de los esperados. El SDK reporta "Product Id not found" al intentar realizar una compra. La causa más común es que los **acuerdos de Apple estén sin firmar** — el acuerdo de pago, los formularios fiscales o los formularios bancarios en estado pendiente o sin firmar. Cuando los acuerdos están pendientes, la API de App Store Connect devuelve silenciosamente un 403 en los endpoints relacionados con productos. No se muestra ningún error claro en Adapty; los productos simplemente se descartan sin aviso. Ve a **App Store Connect → Agreements, Tax, and Banking** y firma todos los acuerdos pendientes. Luego vuelve a sincronizar en **App settings → iOS SDK** de Adapty. ## Las notificaciones del servidor de App Store muestran "Delayed" \{#app-store-server-notifications-show-delayed\} En App Store Connect, el estado de las App Store Server Notifications puede aparecer como **Delayed**. Esto significa que Apple tiene retraso en el envío de notificaciones de eventos de suscripción: renovaciones, cancelaciones y problemas de facturación se acumulan en cola y llegan tarde. Las estadísticas de instalaciones no se ven afectadas. Adapty contabiliza las instalaciones desde el primer lanzamiento de la app, no a partir de notificaciones del servidor. Si los datos de renovación o cancelación van por detrás, el estado Delayed es la causa más probable. El estado suele desaparecer automáticamente a medida que Apple procesa el backlog. ## Los precios en Adapty no coinciden con App Store \{#prices-in-adapty-dont-match-app-store\} El campo **price** en la página de edición de productos de Adapty se comporta de forma distinta según cómo se haya añadido el producto. Si creas un producto en Adapty y lo publicas en la store desde el dashboard, este precio se utiliza como precio inicial en la store. Si añades un producto que ya existe en la store, este precio es un marcador de posición. Las analíticas, integraciones y SDK de Adapty utilizan los precios reales obtenidos de App Store, independientemente de este valor. Los cambios en los precios de App Store no se sincronizan para actualizar el marcador de posición, y por ahora no es posible editar ese marcador desde el dashboard. ## La exportación de precios en CSV está vacía \{#csv-price-export-is-empty\} Si tu exportación de precios en CSV solo devolvió las cabeceras de columna, significa que la clave API de App Store Connect no está completamente configurada. Consulta [Paso 6 — Añadir clave API de App Store Connect](app-store-connection-configuration#step-6-add-app-store-connect-api-key). ## No se pueden publicar nuevos productos en App Store \{#cant-push-new-products-to-app-store\} Adapty puede publicar nuevos productos en App Store Connect cuando los creas en el dashboard. La opción de publicar queda bloqueada si la integración con App Store no está completamente configurada. Se requieren dos ajustes: - **Apple app ID**: Configúralo en [Paso 1 — Proporcionar Bundle ID y Apple app ID](app-store-connection-configuration#step-1-provide-bundle-id-and-apple-app-id). - **App Store Connect API key**: Configúrala en [Paso 6 — Añadir clave API de App Store Connect](app-store-connection-configuration#step-6-add-app-store-connect-api-key). --- # File: enabling-of-devepoler-api --- --- title: "Habilitar las APIs de desarrollador en Google Play Console" description: "Habilita la API de desarrollador de Adapty para automatizar y simplificar la gestión de suscripciones en tu app." --- <div style={{ maxWidth: '560px', margin: '0 auto 2rem', position: 'relative', aspectRatio: '16/9', width: '100%' }}> <iframe style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} src="https://www.youtube.com/embed/7dN50n5bcLc?si=c2znttIb--4VcrRO" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen /> </div> Si tu app móvil está disponible en Play Store, activar las APIs de desarrollador es fundamental para integrarla con Adapty. Este paso garantiza una comunicación fluida entre tu app y nuestra plataforma, facilitando procesos automatizados y análisis de datos en tiempo real para optimizar tu modelo de suscripciones. Las siguientes APIs deben estar habilitadas: - [Google Play Android Developer API](https://console.cloud.google.com/apis/library/androidpublisher.googleapis.com) - [Google Play Developer Reporting API](https://console.cloud.google.com/apis/library/playdeveloperreporting.googleapis.com) - [Cloud Pub/Sub API](https://console.cloud.google.com/marketplace/product/google/pubsub.googleapis.com) Si tu app no se distribuye a través de Play Store, puedes omitir este paso. Sin embargo, si vendes a través de Play Store, puedes posponerlo por ahora, aunque es esencial para el funcionamiento básico de Adapty. Una vez completado el proceso de onboarding, puedes configurar los ajustes del store en la sección **App settings**. Así se habilitan las APIs de desarrollador en Google Play Console: 1. Abre la [Google Cloud Console](https://console.cloud.google.com/). 2. En la esquina superior izquierda de la ventana de Google Cloud, selecciona el proyecto que deseas usar o crea uno nuevo. Asegúrate de usar el mismo proyecto de Google Cloud hasta que hayas subido el archivo de clave de cuenta de servicio a Adapty. <img src="/assets/shared/img/fd66a11-google_cloud_project.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Abre la página de la [**Google Play Android Developer API**](https://console.cloud.google.com/apis/library/androidpublisher.googleapis.com). <img src="/assets/shared/img/f754f72-google_play_api.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en el botón **Enable** y espera a que aparezca el estado **Enabled**. Esto indica que la Google Android Developer API está habilitada. <img src="/assets/shared/img/d47ed14-google_play_api_create_credentials.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Abre la página de la [**Google Play Developer Reporting API**](https://console.cloud.google.com/apis/library/playdeveloperreporting.googleapis.com). <img src="/assets/shared/img/966cf73-Google_play_developer_reporting_api.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Haz clic en el botón **Enable** y espera a que aparezca el estado **Enabled**. <img src="/assets/shared/img/e776d77-Google_play_developer_reporting_api_enabled.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Abre la página de la [**Cloud Pub/Sub API**](https://console.cloud.google.com/marketplace/product/google/pubsub.googleapis.com). <img src="/assets/shared/img/b13f609-enable_Cloud_Pub_Sub_API.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 8. Haz clic en el botón **Enable** y espera a que aparezca el estado **Enabled**. <img src="/assets/shared/img/3f45602-Cloud_Pub_Sub_API_enabled.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Las APIs de desarrollador están habilitadas. Puedes verificarlo en la página [**APIs & Services**](https://console.cloud.google.com/apis/dashboard) de la Google Cloud Console. Desplázate hacia abajo por la página y comprueba que la tabla al final contiene las 3 APIs: - Google Play Android Developer API - Google Play Developer Reporting API - Cloud Pub/Sub API <img src="/assets/shared/img/b81d174-google_enabled_api.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> **Siguiente paso** - [Crear una cuenta de servicio en la Google Cloud Console](create-service-account) --- # File: create-service-account --- --- title: "Crear una cuenta de servicio en Google Cloud Console" description: "Aprende a crear una cuenta de servicio para el acceso seguro a la API en Adapty." --- Para que Adapty pueda automatizar el acceso a los datos, es necesario crear una cuenta de servicio en Google Play Console. 1. Abre la sección [**IAM & Admin** -> **Service accounts**](https://console.cloud.google.com/iam-admin/serviceaccounts) de Google Cloud Console. Asegúrate de estar usando el proyecto correcto. <img src="/assets/shared/img/17bbf45-google_cloud_create_service_account.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la ventana **Service accounts**, haz clic en el botón **Create service account**. <img src="/assets/shared/img/b93eec1-service_account_details.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la subsección **Service account details** de la ventana **Create service account**, introduce el **Service Account Name** que quieras. Te recomendamos incluir "Adapty" en el nombre para indicar el propósito de esta cuenta. El **Service account ID** se generará automáticamente. 4. Copia la dirección de correo electrónico de la cuenta de servicio y guárdala para usarla más adelante. 5. Haz clic en el botón **Create and continue**. <img src="/assets/shared/img/e69d713-grant_access_to_project.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. En la lista desplegable **Select a role** de la subsección **Grant this service account access to project**, selecciona **Pub/Sub -> Pub/Sub Admin**. Este rol es necesario para habilitar las notificaciones en tiempo real para desarrolladores. <img src="/assets/shared/img/976299c-service_account_role.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Haz clic en el botón **Add another role**. 8. En la nueva lista desplegable **Role**, selecciona **Monitoring -> Monitoring Viewer**. Este rol es necesario para permitir la monitorización de la cola de notificaciones. 9. Haz clic en el botón **Continue**. <img src="/assets/shared/img/ffe8d82-grant_user_access.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 10. Haz clic en el botón **Done** sin realizar ningún cambio. Se abrirá la ventana **Service accounts**. **Siguiente paso** - [Conceder permisos a la cuenta de servicio en Google Play Console](grant-permissions-to-service-account) --- # File: grant-permissions-to-service-account --- --- title: "Conceder permisos a la cuenta de servicio en la Google Play Console" description: "Concede permisos a las cuentas de servicio para un acceso seguro y eficiente a la API." --- Concede los permisos necesarios a la cuenta de servicio que Adapty utilizará para gestionar suscripciones y validar compras. 1. Abre la página [**Users and permissions**](https://play.google.com/console/u/0/developers/8970033217728091060/users-and-permissions) en la Google Play Console y haz clic en el botón **Invite new users**. <img src="/assets/shared/img/7b0e614-users_and_permissions.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la página **Invite user**, introduce el correo electrónico de los usuarios de servicio que has creado. <img src="/assets/shared/img/3afd002-invite_user.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Cambia a la pestaña **Account permissions**. <img src="/assets/shared/img/4e2717b-account_permissions.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Selecciona los siguientes permisos: - View app information and download bulk reports (read-only) - View financial data, orders, and cancellation survey responses - Manage orders and subscriptions - Manage store presence 5. Haz clic en el botón **Invite user**. 6. En la ventana **Send invite?**, haz clic en el botón **Send invite**. La cuenta de servicio aparecerá en la lista de usuarios. **Siguientes pasos** - [Genera el archivo de clave de la cuenta de servicio en la Google Play Console](create-service-account-key-file) --- # File: create-service-account-key-file --- --- title: "Generar el archivo de clave de cuenta de servicio en Google Play Console" description: "Aprende a crear un archivo de clave de cuenta de servicio para una integración fluida con Adapty." --- Para vincular tu app móvil en Play Store con Adapty, necesitarás generar archivos especiales de clave de cuenta de servicio en Google Play Console y subirlos a Adapty. Estos archivos ayudan a proteger tu app y evitan accesos no autorizados. :::warning Por lo general, la nueva cuenta de servicio tarda al menos 24 horas en activarse. Sin embargo, existe un [truco](https://stackoverflow.com/a/60691844). Tras crear la cuenta de servicio en [Google Play Console](https://play.google.com/apps/publish/), abre cualquier aplicación y ve a **Monetize** -> **Products** -> **Subscriptions/In-app products**. Edita la descripción de cualquier producto y guarda los cambios. Esto debería activar la cuenta de servicio de inmediato, y luego puedes revertir los cambios. ::: 1. Abre la sección [**Service accounts**](https://console.cloud.google.com/iam-admin/serviceaccounts) en Google Play Console. Asegúrate de haber seleccionado el proyecto correcto. <img src="/assets/shared/img/c3156cb-action_manage_keys.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la ventana que se abre, haz clic en **Add key** y elige **Create new key** en el menú desplegable. <img src="/assets/shared/img/44b30ee-create_new_key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Create private key for [Your_project_name]**, haz clic en **Create**. Tu clave privada se guardará en tu ordenador como un archivo JSON. Puedes encontrarlo usando el nombre de archivo que aparece en la ventana **Private key saved to your computer**. <img src="/assets/shared/img/e7b8101-cretae_private_key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En la ventana **Create private key for Your_project_name**, haz clic en el botón **Create**. Esta acción guardará tu clave privada en tu ordenador como un archivo JSON. Puedes usar el nombre del archivo que aparece en la ventana **Private key saved to your computer** para localizarlo si lo necesitas. <img src="/assets/shared/img/187ddc6-Private_key_saved.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Necesitarás este archivo cuando [configures la integración con Google Play Store](google-play-store-connection-configuration). :::warning Por lo general, la nueva cuenta de servicio tarda al menos 24 horas en activarse. Sin embargo, existe un [truco](https://stackoverflow.com/a/60691844). Tras crear la cuenta de servicio en [Google Play Console](https://play.google.com/apps/publish/), abre cualquier aplicación y ve a **Monetize** -> **Products** -> **Subscriptions/In-app products**. Edita la descripción de cualquier producto y guarda los cambios. Esto debería activar la cuenta de servicio de inmediato, y luego puedes revertir los cambios. ::: **Siguientes pasos** - [Configurar la integración con Google Play Store](google-play-store-connection-configuration) --- # File: google-play-store-connection-configuration --- --- title: "Configurar la integración con Google Play Store" description: "Configura la conexión con Google Play Store en Adapty para gestionar las compras in-app sin problemas." --- Esta sección describe el proceso de integración de tu aplicación móvil distribuida a través de Google Play con Adapty. Tendrás que introducir los datos de configuración de tu app desde la Play Store en el Adapty Dashboard. Este paso es fundamental para validar las compras y recibir actualizaciones de suscripciones desde la Play Store dentro de Adapty. Puedes completar este proceso durante el onboarding inicial o realizar cambios posteriormente en los **App Settings** del Adapty Dashboard. :::danger Los cambios de configuración solo son válidos antes de publicar tu aplicación móvil con los paywalls de Adapty integrados. Modificar la configuración tras el lanzamiento romperá la integración y los paywalls dejarán de mostrarse en tu aplicación. ::: ## Paso 1. Proporciona el Package name \{#step-1-provide-package-name\} El Package name es el identificador único de tu app en Google Play Store. Es necesario para el funcionamiento básico de Adapty, como el procesamiento de suscripciones. 1. Abre la [Google Play Developer Console](https://play.google.com/console/u/0/developers). 2. Selecciona la app cuyo ID necesitas. Se abrirá la ventana **Dashboard**. <img src="/assets/shared/img/7889edb-package_name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Busca el ID del producto bajo el nombre de la aplicación y cópialo. 4. Abre los [**App settings**](https://app.adapty.io/settings/android-sdk) desde el menú superior de Adapty. <img src="/assets/shared/img/b00066c-package_name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. En la pestaña **Android SDK** de la ventana **App settings**, pega el **Package name** copiado. ## Paso 2. Sube el archivo de clave de cuenta \{#step-2-upload-the-account-key-file\} 1. Sube el archivo de clave privada de cuenta de servicio en formato JSON que creaste en el paso [Crear archivo de clave de cuenta de servicio](create-service-account) en el área **Service account key file**. <img src="/assets/shared/img/20fdba1-service_key_file.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> No olvides hacer clic en el botón **Save** para confirmar los cambios. **Próximos pasos** - [Activar las notificaciones en tiempo real para desarrolladores (RTDN) en la Google Play Console](enable-real-time-developer-notifications-rtdn) --- # File: enable-real-time-developer-notifications-rtdn --- --- title: "Habilitar notificaciones en tiempo real para desarrolladores (RTDN) en Google Play Console" description: "Mantente informado sobre eventos críticos y garantiza la exactitud de los datos habilitando las Notificaciones en Tiempo Real para Desarrolladores (RTDN) en Google Play Console para Adapty. Aprende a configurar RTDN para recibir actualizaciones instantáneas sobre reembolsos y otros eventos importantes de la Play Store" --- Configurar las notificaciones en tiempo real para desarrolladores (RTDN) es fundamental para garantizar la exactitud de los datos, ya que te permite recibir actualizaciones al instante desde la Play Store, incluyendo información sobre reembolsos y otros eventos. ## Habilitar notificaciones \{#enable-notifications\} 1. Asegúrate de tener **Google Cloud Pub/Sub** habilitado. Abre [este enlace](https://console.cloud.google.com/flows/enableapi?apiid=pubsub) y selecciona el proyecto de tu app. Si todavía no has habilitado **Google Cloud Pub/Sub**, debes hacerlo aquí. <img src="/assets/shared/img/pubsub.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Ve a [**App settings > Android SDK**](https://app.adapty.io/settings/android-sdk) desde el menú superior de Adapty y copia el contenido del campo **Enable Pub/Sub API** que aparece junto al título **Google Play RTDN topic name**. <img src="/assets/shared/img/a72ff2d-copy_topic.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <p> </p> :::note Si el contenido del campo **Enable Pub/Sub API** tiene un formato incorrecto (el formato correcto empieza por `projects/...`), consulta la sección [Corregir el formato incorrecto en el campo Enable Pub/Sub API](enable-real-time-developer-notifications-rtdn#fixing-incorrect-format-in-enable-pubsub-api-field) para obtener ayuda. ::: 3. Abre la [Google Play Console](https://play.google.com/console/), elige tu app y ve a **Monetize with Play** -> **Monetization setup**. En la sección **Google Play Billing**, marca la casilla **Enable real-time notifications**. 4. Pega el contenido del campo **Enable Pub/Sub API** que copiaste en los **App Settings** de Adapty en el campo **Topic name**. 5. Haz clic en **Save changes** en la Google Play Console. <img src="/assets/shared/img/e55ba0e-paste_topic_name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Probar las notificaciones \{#test-notifications\} Para comprobar si te has suscrito correctamente a las notificaciones en tiempo real para desarrolladores: 1. Guarda los cambios en la configuración de Google Play Console. 2. Debajo del campo **Topic name** en Google Play Console, haz clic en **Send test notification**. <img src="/assets/shared/img/rtdn-test.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Ve a [**App settings > Android SDK**](https://app.adapty.io/settings/android-sdk) en Adapty. Si se ha enviado una notificación de prueba, verás su estado encima del nombre del topic. <img src="/assets/shared/img/rtdn-adapty-test.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Corregir el formato incorrecto en el campo Enable Pub/Sub API \{#fixing-incorrect-format-in-enable-pubsub-api-field\} Si el contenido del campo **Enable Pub/Sub API** tiene un formato incorrecto (el formato correcto empieza por `projects/...`), sigue estos pasos para solucionar el problema: ### 1. Verificar la habilitación de la API y los permisos \{#1-verify-api-enablement-and-permissions\} Comprueba detenidamente que todas las APIs necesarias estén habilitadas y que los permisos estén correctamente concedidos a la cuenta de servicio. Aunque ya hayas completado estos pasos, es importante revisarlos de nuevo para asegurarte de que no se omitió ninguno. Repite los pasos de las siguientes secciones: 1. [Habilitar las APIs de desarrollador en Google Play Console](enabling-of-devepoler-api) 2. [Crear una cuenta de servicio en Google Cloud Console](create-service-account) 3. [Conceder permisos a la cuenta de servicio en Google Play Console](grant-permissions-to-service-account) 4. [Generar el archivo de clave de la cuenta de servicio en Google Play Console](create-service-account-key-file) 5. [Configurar la integración con Google Play Store](google-play-store-connection-configuration) ### 2. Ajustar las políticas de dominio \{#2-adjust-domain-policies\} Cambia las políticas **Domain restricted contacts** y **Domain restricted sharing**: 1. Abre la [Google Cloud Console](https://console.cloud.google.com/) y selecciona el proyecto donde creaste la cuenta de servicio para gestionar tu app. 2. En la sección **Quick Access**, elige **IAM & Admin**. <img src="/assets/shared/img/google-cloud-IAM-and-Admin.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En el panel izquierdo, elige **Organization Policies**. 4. Busca la política **Domain restricted contacts**. <img src="/assets/shared/img/google-cloud-policy-action.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en el botón de puntos suspensivos en la columna **Actions** y elige **Edit policy**. 6. En la ventana de edición de la política: 1. En **Policy source**, selecciona el botón de opción **Override parent's policy**. 2. En **Policy enforcement**, selecciona el botón de opción **Replace**. 3. En **Rules**, haz clic en el botón **ADD A RULE**. <img src="/assets/shared/img/google-cloud-edit-policy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En **New rule** -> **Policy values**, elige **Allow All**. <img src="/assets/shared/img/google-cloud-allow-all-policy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **SET POLICY**. 7. Repite los pasos 4-6 para la política **Domain restricted sharing**. Por último, vuelve a generar el contenido del campo **Enable Pub/Sub API** situado junto al título **Google Play RTDN topic name**. El campo tendrá ahora el formato correcto. Asegúrate de cambiar **Policy source** de vuelta a **Inherit parent's policy** para las políticas actualizadas una vez que hayas habilitado correctamente las Notificaciones en Tiempo Real para Desarrolladores (RTDN). ## Reenvío de eventos sin procesar \{#raw-events-forwarding\} En algunos casos, puede que quieras seguir recibiendo eventos S2S sin procesar de Google. Para continuar recibiéndolos mientras usas Adapty, simplemente añade tu endpoint en el campo **URL for forwarding raw Google events** y enviaremos los eventos tal cual los recibimos de Google. <img src="/assets/shared/img/e388892-001774-September-22-GhkjOFbT.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- **Próximos pasos** Configura el SDK de Adapty para: - [Android](sdk-installation-android) - [React Native](sdk-installation-reactnative) - [Flutter](sdk-installation-flutter) - [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform) - [Unity](sdk-installation-unity) --- # File: stripe --- --- title: "Integración inicial con Stripe" description: "Integra Stripe con Adapty para gestionar pagos de suscripciones sin problemas." --- Adapty admite flujos de suscripción web2app rastreando pagos y suscripciones web realizados a través de [Stripe](https://stripe.com/). Esta integración cubre compras iniciadas en la web (Stripe Checkout, páginas de pago alojadas o flujos web personalizados) y las sincroniza con el acceso a la app móvil y con los datos de analítica. Es útil en los siguientes escenarios: - Proporcionar automáticamente acceso a funciones de pago a usuarios que compraron en la web y después instalaron la app e iniciaron sesión en su cuenta - Centralizar toda la analítica de suscripciones en un único Adapty Dashboard (incluidas cohortes, predicciones y el resto de las herramientas de analítica) Aunque las compras web son cada vez más populares para las apps, la Apple App Store solo permite un sistema diferente al de las compras in-app para bienes digitales en Estados Unidos. Asegúrate de no promocionar tus suscripciones web dentro de la app en otros países, ya que podrías recibir un rechazo o un baneo. A continuación se describen los pasos para configurar la integración con Stripe. :::important Esta integración se centra en rastrear y sincronizar compras web de Stripe. Si necesitas redirigir a los usuarios desde la app a un checkout web, consulta [Paywalls web](web-paywall). ::: ## 1\. Conectar Stripe a Adapty \{#1-connect-stripe-to-adapty\} Esta integración se basa principalmente en que Adapty obtenga datos de suscripciones de Stripe a través del webhook. Por eso, debes conectar tu cuenta de Adapty a tu cuenta de Stripe proporcionando las API Keys y usando la URL del webhook de Adapty en Stripe. Para automatizar la configuración del webhook, instala la app de Adapty en Stripe: :::note Los pasos a continuación son los mismos para los modos de Producción y Test de Stripe, pero deberás usar diferentes API keys para cada uno. ::: 0. Decide si vas a conectar Stripe en modo test o en modo live. Si lo haces primero en modo test, deberás repetir los pasos a continuación para el modo live. 1. Ve al [Stripe App Marketplace](https://marketplace.stripe.com/apps/adapty) e instala la app de Adapty. Ten en cuenta que el modo sandbox no permite instalar apps; solo puedes hacerlo en modo de producción o test. <img src="/assets/shared/img/stripe1.png"/> 2. Concede a la app los permisos necesarios. Esto permitirá a Adapty acceder a los datos e historial de suscripciones. Luego haz clic en **Continue to app settings** para continuar. En la parte inferior del pop-up de permisos puedes elegir si instalar la app en modo live o test. <img src="/assets/shared/img/stripe2.png"/> 3. En el pop-up, genera una nueva clave restringida. Deberás verificar tu identidad mediante tu correo electrónico, Touch ID o clave de seguridad. Una vez generada la clave, no podrás volver a verla, así que guárdala de forma segura en un gestor de contraseñas o un almacén de secretos. <img src="/assets/shared/img/stripe4.png"/> 4. Copia la clave generada del pop-up y ve a [App Settings → Stripe](https://app.adapty.io/settings/stripe) en Adapty. Pega la clave en la sección **Stripe App Restricted API Key** según tu modo. Ten en cuenta que debes generar claves diferentes para los modos test y live. <img src="/assets/shared/img/Stripe3.png"/> ¡Listo! A continuación, crea tus productos en Stripe y agrégalos a Adapty. <Details> <summary>Flujo de instalación obsoleto</summary> 1. Ve a [Developers → API Keys](https://dashboard.stripe.com/apikeys) en Stripe: <img src="/assets/shared/img/6549602-CleanShot_2023-12-06_at_17.29.122x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el botón **Reveal live (test) key button** junto al título **Secret key**, cópiala y ve a [App Settings → Stripe](https://app.adapty.io/settings/stripe) en Adapty. Pega la clave aquí: <img src="/assets/shared/img/2989508-CleanShot_2023-12-07_at_14.59.122x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. A continuación, copia la Webhook URL de la parte inferior de la misma página en Adapty. Ve a [**Developers** → **Webhooks**](https://dashboard.stripe.com/webhooks) en Stripe y haz clic en el botón **Add endpoint**: <img src="/assets/shared/img/e7149f5-CleanShot_2023-12-07_at_17.31.392x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Pega la URL del webhook de Adapty en el campo **Endpoint URL**. Luego elige **Latest API version** en el campo **Version** del webhook. A continuación, selecciona los siguientes eventos: - charge.refunded - customer.subscription.created - customer.subscription.deleted - customer.subscription.paused - customer.subscription.resumed - customer.subscription.updated - invoice.created - invoice.updated - payment_intent.succeeded <img src="/assets/shared/img/cbc5404-CleanShot_2023-12-07_at_17.36.232x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Pulsa "Add endpoint" y luego "Reveal" bajo "Signing secret". Esta es la clave que se usa para decodificar los datos del webhook en Adapty; cópiala tras revelarla: <img src="/assets/shared/img/0460cbb-CleanShot_2023-12-07_at_17.52.582x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Por último, pega esta clave en App Settings → Stripe de Adapty, bajo "Stripe Webhook Secret": <img src="/assets/shared/img/055db20-CleanShot_2023-12-07_at_14.56.212x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Details> ## 2\. Crear productos en Stripe \{#2-create-products-on-stripe\} :::note Si estás configurando esto en modo test, asegúrate de que Stripe también esté en modo Test antes de continuar con este paso. ::: Ve al [Catálogo de productos](https://dashboard.stripe.com/products?active=true) de Stripe y crea los productos que quieras vender junto con sus planes de precios. Ten en cuenta que Stripe te permite tener varios planes de precios por producto, lo que resulta útil para adaptar tu oferta sin necesidad de crear productos adicionales. <img src="/assets/shared/img/b202e2e-CleanShot_2023-12-06_at_15.06.262x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning Por el momento, Adapty solo admite precios de tipo **Flat rate** ($9,99/mes) o **Package pricing** ($9,99/10 unidades), ya que se comportan de forma similar a los stores de apps. Las opciones **Tiered pricing**, **Usage-based fee** y **Customer chooses price** no están soportadas. ::: ## 3\. Añadir productos de Stripe a Adapty \{#3-add-stripe-products-to-adapty\} :::warning ¡Los productos son obligatorios! Asegúrate de crear tus productos de Stripe en el Adapty Dashboard. Adapty solo rastrea eventos para transacciones vinculadas a estos productos, así que no omitas este paso; de lo contrario, no se crearán eventos de transacción. ::: Tratamos Stripe igual que App Store y Google Play: es simplemente otro store donde vendes tus productos digitales. Por eso se configura de forma similar: añade los productos de Stripe (concretamente su `product_id` y `price_id`) en la sección de Productos de Adapty: <img src="/assets/shared/img/stripe-add-product.webp" style={{ border: 'none', /* border width and color */ width: '500px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Los IDs de producto en Stripe tienen el formato `prod_...` y los IDs de precio `price_...`. Son fáciles de encontrar para cada producto en el [Catálogo de productos](https://dashboard.stripe.com/products?active=true) de Stripe, una vez que abres cualquier producto: <img src="/assets/shared/img/14a72d7-CleanShot_2023-12-06_at_17.32.512x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Una vez añadidos todos los productos necesarios, el siguiente paso es informar a Stripe sobre qué usuario está realizando la compra, para que Adapty pueda identificarlo. ## 4\. Enriquecer las compras web con tu ID de usuario \{#4-enrich-purchases-made-on-the-web-with-your-user-id\} Adapty se basa en los webhooks de Stripe como única fuente de información para otorgar y actualizar los niveles de acceso de los usuarios. Pero debes proporcionar información adicional desde tu lado cuando trabajas con Stripe para que esta integración funcione correctamente. Para que los niveles de acceso sean consistentes entre plataformas (web o móvil), debes asegurarte de que haya un único ID de usuario en el que Adapty pueda basarse al reconocer los webhooks. Puede ser el correo electrónico, el número de teléfono o cualquier otro ID del sistema de autenticación que utilices. Decide qué ID quieres usar para identificar a tus usuarios. Luego, accede a la parte de tu código que inicializa el pago a través de Stripe y añade este ID de usuario al objeto `metadata` de la [Suscripción de Stripe](https://docs.stripe.com/api/subscriptions/object#subscription_object-metadata) (`sub_...`) o del objeto [Checkout Session](https://docs.stripe.com/api/checkout/sessions/create#create_checkout_session-metadata) (`ses_...`) con la clave `customer_user_id`, así: ```json showLineNumbers title="Stripe Metadata contents" {'customer_user_id': "YOUR_USER_ID"} ``` Esta simple adición es lo único que tienes que hacer en tu código. Después de eso, Adapty procesará todos los webhooks que reciba de Stripe, extraerá este `metadata` y asociará correctamente las suscripciones con tus clientes. :::warning El ID de usuario es obligatorio De lo contrario, no tenemos forma de identificar a este usuario y otorgarle el nivel de acceso en el móvil. Si no proporcionas `customer_user_id` en el `metadata`, tendrás la opción de hacer que Adapty busque `customer_user_id` en otros lugares: bien en el campo `email` del objeto Customer de Stripe, o bien en el `client_reference_id` de la Session de Stripe. Más información sobre cómo configurar el comportamiento de creación de perfiles [más abajo](stripe#profile-creation-behavior). ::: :::note El Customer en Stripe también es obligatorio Si usas Checkout Sessions, [asegúrate de que estás creando un Customer de Stripe](https://docs.stripe.com/api/checkout/sessions/create#create_checkout_session-customer_creation) estableciendo `customer_creation` en `always`. ::: ## 5\. Dar acceso a los usuarios en el móvil \{#5-provide-access-to-users-on-the-mobile\} Para asegurarte de que tus usuarios móviles que llegan desde la web puedan acceder a las funciones de pago, simplemente llama a `Adapty.activate()` o `Adapty.identify()` con el mismo `customer_user_id` que proporcionaste en el paso anterior (consulta <InlineTooltip tooltip="Identificar usuarios">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users) y [Unity](unity-identifying-users)</InlineTooltip> para más información). ## 6\. Probar tu integración \{#6-test-your-integration\} Asegúrate de haber completado los pasos anteriores tanto para Sandbox como para Producción. Las transacciones que realices desde el modo Test de Stripe se considerarán Sandbox en Adapty. :::info ¡Eso es todo! Tus usuarios ya pueden completar compras en la web y acceder a las funciones de pago en tu app. Además, puedes ver toda la analítica de suscripciones en un único lugar. ::: ## Comportamiento de creación de perfiles \{#profile-creation-behavior\} Adapty necesita vincular una compra a un [perfil de cliente](profiles-crm) para que esté disponible en el móvil; por eso, de forma predeterminada, crea perfiles al recibir webhooks de Stripe. Puedes elegir qué usar como ID de usuario del cliente en Adapty: 1. **Por defecto y recomendado:** el `customer_user_id` que proporcionaste en el metadata en el [paso 4 anterior](stripe#4-enrich-purchases-made-on-the-web-with-your-user-id) 2. El campo `email` del objeto Customer de Stripe (consulta la [documentación de Stripe](https://docs.stripe.com/api/customers/object#customer_object-email)) 3. El campo `client_reference_id` del objeto Session de Stripe (consulta la [documentación de Stripe](https://docs.stripe.com/api/checkout/sessions/create#create_checkout_session-client_reference_id)) Puedes configurar qué ID quieres usar en [App Settings → Stripe](https://app.adapty.io/settings/stripe). :::warning **Nota:** si una transacción concreta de Stripe no contiene el ID especificado, no se creará ningún perfil. Esa transacción permanecerá anónima hasta que sea recogida por algún perfil (por ejemplo, si usas [validación S2S](api-adapty/operations/validateStripePurchase) más adelante y nos informas manualmente sobre esa transacción). Aparecerá en Analytics, pero no en las secciones que dependen del conteo de perfiles (LTV, Cohortes, Conversiones, etc.) y no podrás verla en el Event feed. ::: También tienes una cuarta opción: no crear perfiles en absoluto, aunque no se recomienda por las limitaciones descritas anteriormente en Analytics. ## Limitaciones actuales \{#current-limitations\} ### Actualizaciones, degradaciones y prorrateo \{#upgrading-downgrading-and-proration\} Los cambios de suscripción como actualizaciones o degradaciones pueden dar lugar a cargos prorrateados. Adapty no tendrá en cuenta estos cargos en los cálculos de ingresos. Lo mejor es deshabilitar estas opciones manualmente desde el dashboard de Stripe. También puedes desactivarlas estableciendo el valor del atributo `proration_behaviour` en `none` a través de la API de Stripe. ### Cancelaciones \{#cancellations\} Stripe tiene dos opciones de cancelación de suscripciones: 1. Cancelación inmediata: la suscripción se cancela de inmediato con o sin opción de prorrateo 2. Cancelación al final del período: la suscripción se cancela al final del período de facturación actual (similar a las suscripciones in-app en los stores de apps). Adapty admite ambas opciones, pero el cálculo de ingresos para la cancelación inmediata no tendrá en cuenta la opción de prorrateo. ### Problemas de facturación y período de gracia \{#billing-issues-and-grace-period\} Cuando un cliente tiene un problema con su pago, Adapty generará un evento de problema de facturación y se revocará el acceso. De momento no admitimos el período de gracia de Stripe; esto formará parte de versiones futuras. ### Reembolsos \{#refunds\} Adapty solo rastrea reembolsos totales. Los reembolsos parciales o por prorrateo no están soportados actualmente. ### Unicidad del ID de transacción \{#transaction-id-uniqueness\} Adapty vincula perfiles y transacciones usando `store_transaction_id` y `store_original_transaction_id`. Estos **deben ser únicos** entre los entornos de Test y Producción. #### Por qué esto importa \{#why-this-matters\} Si el mismo ID de transacción existe en ambos entornos, Adapty los trata como una sola transacción, lo que provoca: - Que las compras de Producción hereden los niveles de acceso y los IDs de producto de Test - IDs de producto y entornos incorrectos en las respuestas de la API - Problemas en la vinculación de perfiles y en los eventos de suscripción #### Cómo garantizar la unicidad \{#how-to-ensure-uniqueness\} Los IDs de factura de Stripe pueden solaparse entre los entornos Test y Live. Para evitar colisiones entre entornos, elige una de estas opciones: #### Opción 1: Numeración a nivel de cuenta con prefijos de entorno \{#option-1-account-level-numbering-with-environment-prefixes\} Configura prefijos por separado para cada entorno: 1. En el Stripe Dashboard, cambia al modo Test. 2. Ve a [Settings → Billing → Invoices](https://dashboard.stripe.com/settings/account/?support_details=true). 3. Establece **Invoice numbering** en **Sequentially across your account**. 4. Establece **Invoice prefix** en TEST- (o cualquier otro prefijo específico del entorno de test). 5. Cambia al modo Live y repite los pasos 2-4, usando LIVE- (o cualquier otro prefijo específico del entorno de producción) como prefijo. #### Opción 2: Numeración a nivel de cliente \{#option-2-customer-level-numbering\} Establece **Invoice numbering** en [**Stripe settings** -> **Billing** -> pestaña **Invoices**](https://dashboard.stripe.com/settings/account/?support_details=true) en **Sequentially for each customer (customer-level)**. Incluso con la configuración anterior, si eliminas una factura, Stripe puede reutilizar ese ID para nuevas facturas del mismo cliente. Lo mejor es evitar eliminar facturas siempre que sea posible. ## Saca más partido a tus datos de Stripe \{#get-more-from-your-stripe-data\} Una vez integrado con Stripe, Adapty está listo para ofrecer información de inmediato. Para aprovechar al máximo tus datos de Stripe, puedes configurar integraciones adicionales de Adapty para reenviar eventos de Stripe y centralizar toda la analítica de suscripciones en un único Adapty Dashboard. :::tip Para mejorar la analítica, puedes incluir un `variation_id` en tus metadatos de Stripe para atribuir compras a instancias específicas de paywall. Esto es especialmente útil cuando implementas paywalls web propios y quieres rastrear qué paywall concreto llevó a la conversión. Ten en cuenta que `variation_id` solo se lee de los metadatos en los objetos Stripe Subscription (`sub_...`) y Checkout Session (`ses_...`): ```json showLineNumbers title="Stripe Metadata with variation_id" { 'customer_user_id': "YOUR_USER_ID", 'variation_id': "YOUR_VARIATION_ID" } ``` ::: Integraciones que puedes usar para reenviar y analizar tus eventos de Stripe: - [Amplitude](amplitude/) - [Webhook](webhook) - [Firebase](firebase-and-google-analytics) - [Mixpanel](mixpanel) - [Posthog](posthog) ### Eventos de Stripe soportados \{#supported-stripe-events\} Adapty admite los siguientes eventos de Stripe: - charge.refunded - customer.subscription.created - customer.subscription.deleted - customer.subscription.paused - customer.subscription.resumed - customer.subscription.updated - invoice.created - invoice.updated - payment_intent.succeeded --- # File: paddle --- --- title: "Integración inicial con Paddle" description: "Integra Paddle con Adapty para gestionar pagos de suscripciones sin complicaciones." --- Adapty admite flujos de suscripción web2app rastreando pagos y suscripciones web realizados a través de [Paddle](https://www.paddle.com/). Esta integración cubre las compras iniciadas desde la web y las sincroniza con el acceso a la app móvil y los análisis, junto con las compras in-app de los stores. Es útil en los siguientes escenarios: - Recopilar datos de suscripción tanto de compras in-app como de compras en el sitio web en un único sistema - Conceder acceso a funciones de pago en tu aplicación móvil a usuarios que compraron en tu sitio web - Ver análisis y datos de suscripción de todos los canales de venta en un solo dashboard :::note Apple ahora permite que las aplicaciones de la App Store de EE. UU. incluyan enlaces a sistemas de pago externos, aunque es posible que las aplicaciones aún necesiten ofrecer compras in-app junto con las opciones externas. Consulta las directrices actuales de la App Store para tu región y categoría de aplicación. ::: :::note Esta integración se centra en el seguimiento y la sincronización de compras web de Paddle. Si necesitas enviar usuarios desde la app a un checkout web, usa los [paywalls web](web-paywall) de Adapty. ::: Para configurar la integración con Paddle, sigue estos pasos: ## 1\. Conectar Paddle con Adapty \{#1-connect-paddle-to-adapty\} La integración utiliza webhooks para enviar datos de suscripción desde Paddle a Adapty. Para conectar tus cuentas de Adapty y Paddle, necesitarás: 1. Proporcionar tus claves API de Paddle. 2. Añadir la URL del webhook de Adapty a Paddle. :::note Los pasos a continuación se aplican tanto a Producción como a Test. Puedes configurar ambos simultáneamente. Los enlaces proporcionados corresponden al entorno de Producción — para obtener los enlaces del entorno de Test, simplemente añade `sandbox-` al principio de cada URL. Por ejemplo, usa `https://sandbox-vendors.paddle.com/authentication-v2` en lugar de `https://vendors.paddle.com/authentication-v2`. ::: ### 1.1. Obtén y añade las claves API de Paddle \{#get-and-add-paddle-api-keys\} 1. En Paddle, ve a [Developer Tools → Authentication](https://vendors.paddle.com/authentication-v2) y haz clic en **New API key**. <img src="/assets/shared/img/paddle-new-key.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Dale un nombre a la clave y establece la fecha de expiración. Para que la clave API funcione con Adapty, necesitas concederle el permiso **Read** para todas las entidades. Haz clic en **Save**. <img src="/assets/shared/img/paddle-key.webp" style={{ border: 'none', /* border width and color */ width: '300px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Copy key**. <img src="/assets/shared/img/copy-paddle-key.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En Adapty, ve a [App Settings → Paddle](https://app.adapty.io/settings/paddle) y pega la clave en la sección **Paddle API key**. :::warning Si estableciste una fecha de vencimiento para tu clave de API de Paddle, debes generar manualmente una nueva clave y actualizarla en Adapty antes de que expire. La integración dejará de funcionar sin previo aviso cuando la clave caduque, y los usuarios no podrán realizar compras. ::: <img src="/assets/shared/img/paddle-api-keys-adapty.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### 1.2. Añadir eventos que se enviarán a Adapty \{#add-events-that-will-be-sent-to-adapty\} 1. Copia la **Webhook URL** de la misma página de **Paddle** en Adapty. 2. En Paddle, ve a [**Developer Tools → Notifications**](https://vendors.paddle.com/notifications-v2) y haz clic en **New destination** para añadir un webhook. <img src="/assets/shared/img/paddle-webhook.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Introduce un nombre descriptivo para el webhook. Te recomendamos incluir "Adapty" en él para que puedas encontrarlo fácilmente cuando lo necesites. 4. Pega la **Webhook URL** de Adapty en el campo **URL**. Asegúrate de usar el webhook para el entorno correcto. 5. Establece **Notification type** en **Webhook**. <img src="/assets/shared/img/paddle-create-webhook.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Selecciona los siguientes eventos: - `subscription.created` - `subscription.updated` - `transaction.created` - `transaction.updated` - `adjustment.created` - `adjustment.updated` <img src="/assets/shared/img/paddle_events.png" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Haz clic en **Save destination** para finalizar la configuración del webhook. ### 1.3. Obtener y añadir la clave secreta del webhook \{#retrieve-and-add-the-webhook-secret-key\} 1. En la ventana **Notifications**, haz clic en los tres puntos junto al webhook que acabas de crear y selecciona **Edit destination**. 2. Aparecerá un nuevo campo llamado **Secret key** en el panel **Edit destination**. Cópialo. <img src="/assets/shared/img/paddle-webhook-secret-key-copy.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En Adapty, ve a [App Settings → Paddle](https://app.adapty.io/settings/paddle) y pega la clave en el campo **Notification secret key**. Esta clave se usa para verificar los datos del webhook en Adapty. <img src="/assets/shared/img/paddle-webhook-secret-key.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### 1.4. Vincula los clientes de Paddle con los perfiles de Adapty \{#14-match-paddle-customers-with-adapty-profiles\} Adapty necesita vincular cada compra a un [perfil de cliente](profiles-crm) para que pueda usarse en tu app. De forma predeterminada, los perfiles se crean automáticamente cuando Adapty recibe webhooks de Paddle. Puedes elegir qué valor usar como `customer_user_id` en Adapty: 1. **Predeterminado y recomendado:** El `customer_user_id` que pasas en el campo `custom_data` (ver [documentación de Paddle](https://developer.paddle.com/build/transactions/custom-data)) 2. El `email` del objeto Paddle Customer (ver [documentación de Paddle](https://developer.paddle.com/paddle-js/methods/paddle-checkout-open/#parameters)) 3. El ID de cliente de Paddle en formato `ctm-...` (ver [documentación de Paddle](https://developer.paddle.com/paddle-js/methods/paddle-checkout-open/#parameters)) 4. No crear perfiles. Elige esta opción si quieres tener mayor control sobre los perfiles de tus clientes y gestionarlos tú mismo. Puedes configurar qué valor usar en el campo **Profile creation behavior** en [App Settings → Paddle](https://app.adapty.io/settings/paddle). <img src="/assets/shared/img/paddle-users.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## 2. Añade productos de Paddle a Adapty \{#2-add-paddle-products-to-adapty\} :::warning Asegúrate de añadir tus productos de Paddle al Adapty Dashboard o de agregar un ID de producto de Paddle a tus productos existentes. Adapty solo registra eventos para transacciones vinculadas a estos productos. Si omites este paso, no se crearán eventos de transacción. ::: Paddle funciona en Adapty igual que App Store y Google Play: es otra plataforma donde vendes productos digitales. Para configurarlo, añade los valores de `product_id` y `price_id` correspondientes de Paddle en la sección [Products](https://app.adapty.io/products) de Adapty. En Paddle, los IDs de producto tienen el formato `pro_...` y los IDs de precio `pri_...`. Los encontrarás en tu [catálogo de productos de Paddle](https://vendors.paddle.com/products-v2) una vez que abras un producto específico: <img src="/assets/shared/img/paddle-product-price.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Una vez añadidos tus productos, el siguiente paso es asegurarte de que Adapty pueda vincular la compra al usuario correcto. ## 3\. Dar acceso a los usuarios en el móvil \{#provide-access-to-users-on-the-mobile\} Para asegurarte de que los usuarios que compran en la web obtengan acceso en el móvil, llama a `Adapty.activate()` o `Adapty.identify()` usando el mismo `customer_user_id` que pasaste al realizar la compra. Consulta [Identificar usuarios](identifying-users) para más detalles. ## 4\. Probar tu integración \{#test-your-integration\} Una vez que todo esté configurado, puedes probar tu integración. Las transacciones realizadas en el entorno de Test de Paddle aparecerán como **Test** en Adapty. Las transacciones del entorno de Producción aparecerán como **Production**. Tu integración ya está completa. Los usuarios pueden comprar suscripciones en tu sitio web y acceder automáticamente a las funciones premium en tu aplicación móvil, mientras tú haces seguimiento de todos los análisis de suscripciones desde tu Adapty Dashboard unificado. ## Consideraciones importantes \{#important-considerations\} - En las analíticas de Adapty, los importes de las transacciones incluyen impuestos y comisiones de Paddle, lo que difiere del dashboard de Paddle, donde los importes se muestran después de impuestos y comisiones. Esto significa que las cifras que ves en Adapty serán más altas que las de tu dashboard de Paddle. - A diferencia de otros stores, los reembolsos en Paddle solo afectan a la transacción específica que se reembolsa y no cancelan automáticamente la suscripción. La suscripción seguirá activa a menos que se cancele de forma explícita. - También puedes incluir `variation_id` en el campo `custom_data` para atribuir compras a instancias de paywall específicas. Adapty procesará estos datos desde los webhooks y los incluirá en las analíticas. ### Períodos de prueba de pago \{#paid-trials\} Al trabajar con períodos de prueba de pago en Paddle, necesitas crear dos productos en Adapty: 1. Crea un producto que no sea suscripción y vincúlalo al precio de Paddle que cobra por el período de prueba. 2. Luego crea un producto de suscripción (mensual, semanal, etc.) y vincúlalo al precio de Paddle que tiene el componente de prueba gratuita. Desde el punto de vista de Paddle, se trata de un único producto con dos precios en una sola transacción: un precio para el cargo del período de prueba (p. ej., $0,99) y otro precio para la prueba gratuita ($0,00). Desde la perspectiva de Adapty, esto genera dos eventos separados: una compra única por el pago de la prueba y un evento de inicio de prueba para el producto de suscripción. Por ejemplo, cuando un usuario inicia una prueba de pago de $0,99 para una suscripción de $9,99/mes, Paddle crea una sola transacción con ambos precios, mientras que Adapty lo procesa como una compra única de $0,99 (pago inmediato) y un evento de inicio de prueba a $0,00 (suscripción futura a $9,99/mes). :::note Cuando los usuarios cancelan una prueba de pago, recibirás los eventos **Trial expired** y **Trial renewal canceled**. ::: ## Saca más partido a tus datos de Paddle \{#get-more-from-your-paddle-data\} :::important Para que tus eventos de Paddle funcionen con las integraciones, tus usuarios deben haber iniciado sesión en la app con su cuenta de App Store/Google Play al menos una vez. ::: Una vez que te integres con Paddle, Adapty está listo para ofrecer información de inmediato. Para aprovechar al máximo tus datos de Paddle, puedes configurar integraciones adicionales de Adapty para reenviar eventos de Paddle, centralizando todos tus análisis de suscripciones en un único Adapty Dashboard. Integraciones que puedes usar para reenviar y analizar tus eventos de Paddle: - [AppsFlyer](appsflyer) - [Webhook](webhook) - [Posthog](posthog) ## Limitaciones actuales \{#current-limitations\} - **Cancelaciones**: Paddle tiene dos opciones de cancelación de suscripción: 1. Cancelación inmediata: La suscripción se cancela de inmediato. 2. Cancelación al final del período: La suscripción se cancela al final del período de facturación actual (similar a las suscripciones in-app en los stores). - **Reembolsos**: Adapty registra los reembolsos totales y parciales. - **Período de gracia**: Por defecto, Paddle aplica un período de gracia fijo de 30 días para problemas de facturación, durante el cual la suscripción permanece activa. Puedes [personalizar la duración del período de gracia y la acción al final del mismo (pausar o cancelar la suscripción)](https://developer.paddle.com/build/retain/configure-payment-recovery-dunning#prerequisites). **Pruebas**: Si el cobro falla al finalizar una prueba, el estado de la suscripción cambia a `past_due`. En producción, Paddle Retain aplica una ventana de gestión de impagos para intentar recuperar el pago antes de cancelar o pausar la suscripción. En sandbox, Retain no está disponible, por lo que no se realizan reintentos de pago y la suscripción permanece en `past_due` indefinidamente. --- **Ver también:** - [Validar una compra en Paddle, obtener un nivel de acceso e importar el historial de transacciones desde Paddle con la API del lado del servidor](api-adapty/operations/validatePaddlePurchase) --- # File: custom-store --- --- title: "Integración inicial con otras stores" description: "Integración inicial de Adapty con App Store: Guía rápida" --- ¡Nos alegra mucho tenerte con nosotros en Adapty! Nuestra prioridad es ayudarte a ponerte en marcha cuanto antes y obtener los mejores resultados posibles para tu app. La integración inicial solo es necesaria para [App Store](initial_ios), [Google Play](initial-android), [Stripe](stripe) y [Paddle](paddle), ya que Adapty verifica tus apps, productos y ofertas con estas stores. Adapty no valida datos con otras app stores ni procesa las compras realizadas a través de ellas. Sin embargo, puedes marcar los productos vendidos en otras stores para que Adapty conceda acceso al contenido de pago tras una compra exitosa, refleje las transacciones en tus analíticas y las comparta mediante integraciones. <img src="/assets/shared/img/Adapty-Communication-Scheme.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <p> </p> :::important Asegúrate de que tu backend procese la compra y envíe la transacción a Adapty mediante la [API server-side de Adapty](getting-started-with-server-side-api). Adapty solo concederá acceso, disparará un evento de transacción, lo enviará a las integraciones y lo reflejará en las analíticas una vez que se reciba la transacción. ::: Para marcar un producto como vendido a través de una app store personalizada, selecciona la app store al crear el producto. Si la store que necesitas no aparece en la lista, así es como puedes crearla: 1. En la página **Products**, abre el producto que quieres vender a través de una app store personalizada. 2. Elige la app store a través de la que quieres vender. Si no aparece en la lista, haz clic en el botón **Create Custom Store**. <img src="/assets/shared/img/create_custom-appstore.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Introduce el **Title** y el **Store ID** de la store. 4. Haz clic en el botón **Create store**. Si tu backend está configurado correctamente, Adapty recibirá las transacciones de productos de esta store personalizada, las reflejará en las analíticas, en el [**Event Feed**](event-feed) y en las [integraciones](https://app.adapty.io/integrations), y concederá acceso según corresponda. ## Saca más partido a los datos de tu store personalizada \{#get-more-from-your-custom-store-data\} :::important Para que los eventos de tu store personalizada funcionen con las integraciones, tus usuarios deben haber iniciado sesión en la app con su cuenta de App Store/Google Play al menos una vez. ::: Una vez que configures la integración con tu store personalizada, Adapty está listo para ofrecerte información de inmediato. Para aprovechar al máximo tus datos, puedes configurar integraciones adicionales de Adapty para reenviar los eventos de la store personalizada y reunir todas tus analíticas de suscripciones en un único Adapty Dashboard. Integraciones que puedes usar para reenviar y analizar los eventos de tu store personalizada: - [AppsFlyer](appsflyer) - [Webhook](webhook) - [Posthog](posthog) --- # File: transfer-apps --- --- title: "Transferir tu app a una cuenta diferente" description: "Cambia el propietario de la app en Adapty" --- Transfiere tu app a un propietario diferente cuando tu empresa sea adquirida, estés vendiendo tu app o reorganizando entidades empresariales. El proceso de transferencia implica coordinar cambios en Adapty, App Store Connect y Google Play Console para garantizar la continuidad del servicio. ## Transferir la propiedad de la app \{#transfer-app-ownership\} Completa primero la transferencia en la store y luego transfiere la app en Adapty. Este orden garantiza que las compras sigan funcionando durante toda la transición. :::note No elimines ni vuelvas a crear productos durante el proceso de transferencia. No cambies los IDs de los productos hasta después de verificar que la transferencia se completó correctamente. ::: ### Transferencia en App Store (iOS) \{#app-store-ios-transfer\} :::important Las claves de la API de App Store Connect (Issuer ID, Key ID, archivo .p8) están vinculadas a la cuenta, no a la app. Tras la transferencia, debes generar nuevas claves de API desde la cuenta del nuevo propietario y actualizarlas en Adapty. ::: 1. **Nuevo propietario:** Crea una cuenta en Adapty en [app.adapty.io](https://app.adapty.io) si aún no tienes una. 2. **Propietario anterior:** Inicia la transferencia de la app en App Store Connect siguiendo la [guía de transferencia](https://developer.apple.com/help/app-store-connect/transfer-an-app/overview-of-app-transfer) de Apple. 3. **Nuevo propietario:** Acepta la transferencia en App Store Connect. 4. **Propietario anterior:** Envía un correo a [support@adapty.io](mailto:support@adapty.io) para transferir la app en Adapty. Indica el nombre de la app y la dirección de correo electrónico del nuevo propietario. 5. **Nuevo propietario:** Tras recibir la app en Adapty, completa la [guía de integración de App Store](initial_ios) para generar y configurar todas las credenciales desde tu cuenta. ### Transferencia en Google Play (Android) \{#google-play-android-transfer\} 1. **Nuevo propietario:** Crea una cuenta en Adapty en [app.adapty.io](https://app.adapty.io) si aún no tienes una. 2. **Ambos propietarios:** Asegúrate de que ambas cuentas de desarrollador de Google Play estén completamente registradas. 3. **Propietario anterior:** Envía una solicitud de transferencia a través de Google Play Console o del soporte para desarrolladores de Google Play. Google puede solicitar documentación adicional como números DUNS, contratos o prueba de venta. 4. **Nuevo propietario:** Revisa y aprueba la solicitud de transferencia. 5. **Google:** El equipo de soporte de Google procesa la transferencia, normalmente en unos pocos días hábiles, aunque puede tardar más según la verificación de la cuenta, la complejidad de las suscripciones y la configuración de pagos. 6. **Propietario anterior:** Una vez que Google complete la transferencia, envía un correo a [support@adapty.io](mailto:support@adapty.io) para transferir la app en Adapty. Indica el nombre de la app y la dirección de correo electrónico del nuevo propietario. 7. **Nuevo propietario:** Tras recibir la app en Adapty, completa la [guía de integración de Google Play](initial-android) para generar y configurar todas las credenciales desde tu cuenta. La transferencia incluye usuarios, suscripciones, estadísticas, valoraciones y la ficha de la store. La continuidad de facturación se mantiene para los suscriptores existentes, pero los pagos pasan a la cuenta de comerciante del nuevo propietario solo después de que se complete la transferencia. Los informes de pagos y los pedidos anteriores a la transferencia permanecen en la cuenta original. Consulta la [guía de transferencia](https://support.google.com/googleplay/android-developer/answer/6230247) de Google para conocer los requisitos detallados. ## Mitigación de riesgos y tiempos \{#risk-mitigation-and-timing\} **Lo que sigue funcionando durante la transferencia:** - Compras (el shared secret de la app valida los recibos) - Renovaciones (el shared secret sigue siendo válido) - Acceso de los suscriptores existentes - El SDK sigue funcionando **Lo que deja de funcionar temporalmente:** - Llamadas a la API de App Store Connect (hasta que se configuren las nuevas claves) - Notificaciones del servidor (hasta que se reconfigure el endpoint) - Los análisis pueden tener huecos durante la transición de credenciales **Momento recomendado:** - Realiza las transferencias en períodos de poco tráfico (entre las 3 a.m. y las 6 a.m. en la zona horaria principal de tus usuarios) - Ten al nuevo propietario listo para configurar las credenciales inmediatamente después de aceptar la transferencia en la store - Reserva entre 15 y 30 minutos entre la aceptación de la transferencia y la finalización de la integración con Adapty **Tras completar la transferencia:** - Verifica la validación de recibos de inmediato - Supervisa las tasas de éxito de la renovación automática durante 48 horas - Comprueba que las notificaciones del servidor están llegando a tus sistemas - Verifica que las nuevas compras se están registrando correctamente ## Verificar que la transferencia se completó correctamente \{#verify-transfer-completed-successfully\} Tras completar tanto la transferencia en Adapty como en la store: 1. **Comprobar el acceso al Dashboard**: El nuevo propietario debe ver la app en su Adapty Dashboard. 2. **Verificar la conexión de la clave de API**: Comprueba que la nueva clave de API de App Store Connect o la cuenta de servicio de Google Play se conecta correctamente en Adapty. 3. **Probar la conexión del SDK**: Ejecuta tu app y verifica que el SDK de Adapty se inicializa sin errores. --- # File: installation-of-adapty-sdks --- --- title: "Instalación del SDK de Adapty" description: "Instala los SDKs de Adapty para iOS, Android y apps multiplataforma." --- Tienes tres formas de empezar según tus preferencias: - **Sigue las guías de inicio rápido por plataforma**: Las guías incluyen fragmentos de código listos para producción, así que la implementación no lleva mucho tiempo. - [iOS](ios-sdk-overview) - [Android](android-sdk-overview) - [React Native](react-native-sdk-overview) - [Flutter](flutter-sdk-overview) - [Unity](unity-sdk-overview) - [Kotlin Multiplatform](kmp-sdk-overview) - [Capacitor](capacitor-sdk-overview) - **Usa LLMs**: Nuestra documentación es compatible con LLMs. Lee nuestra [guía](adapty-cursor) sobre cómo sacar el máximo partido a los LLMs con la documentación de Adapty. - **Explora las apps de ejemplo**: - [iOS (Swift)](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples) - [Android (Kotlin)](https://github.com/adaptyteam/AdaptySDK-Android/tree/master/app) - [React Native (Ejemplo básico en RN puro)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/BasicExample) - [React Native (Ejemplo avanzado – útil para desarrollo, ya que permite trabajar con casos más complejos)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/AdaptyDevtools) - [React Native (Build de desarrollo con Expo)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/FocusJournalExpo) - [React Native (Expo Go y Web)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/ExpoGoWebMock) - [Flutter (Dart)](https://github.com/adaptyteam/AdaptySDK-Flutter/tree/master/example) - [Unity (C#)](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Assets) - [Kotlin Multiplatform](https://github.com/adaptyteam/AdaptySDK-KMP/tree/main/example) - [Capacitor](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples) --- # File: sample-apps --- --- title: "Apps de ejemplo" description: "" --- Para ayudarte a empezar con el SDK de Adapty, hemos preparado apps de ejemplo que muestran cómo integrar y usar sus funciones principales. Estas apps incluyen implementaciones listas para usar de paywalls, compras y seguimiento de analíticas. <img src="/assets/shared/img/adapty-scheme.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## ¿Por qué usar las apps de ejemplo? \{#why-use-sample-apps\} - **Integración rápida:** Descubre cómo funciona el SDK de Adapty en una app real. - **Buenas prácticas:** Sigue los patrones de implementación recomendados. - **Depuración y pruebas:** Usa las apps de ejemplo para resolver problemas y experimentar antes de integrar Adapty en tu propio proyecto. ## Apps de ejemplo disponibles \{#available-sample-apps\} - [iOS (Swift)](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples) - [Android (Kotlin)](https://github.com/adaptyteam/AdaptySDK-Android/tree/master/app) - [React Native (Ejemplo básico en RN puro)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/BasicExample) - [React Native (Ejemplo avanzado — útil para desarrollo, ya que permite trabajar con casos más complejos)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/AdaptyDevtools) - [React Native (Expo dev build)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/FocusJournalExpo) - [React Native (Expo Go & Web)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/ExpoGoWebMock) - [Flutter (Dart)](https://github.com/adaptyteam/AdaptySDK-Flutter/tree/master/example) - [Unity (C#)](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Assets) - [Kotlin Multiplatform](https://github.com/adaptyteam/AdaptySDK-KMP/tree/main/example) - [Capacitor (React)](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-react-example) - [Capacitor (Vue.js)](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-vue-example) - [Capacitor (Angular)](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-angular-example) - [Capacitor (Herramientas de desarrollo avanzadas)](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/adapty-devtools) --- # File: create-product --- --- title: "Crear producto" description: "Guía paso a paso para crear nuevos productos de suscripción en Adapty para una mejor gestión de ingresos." --- La forma de crear productos en Adapty depende de si ya los tienes en los stores: - **[Si los productos aún no existen en App Store y/o Google Play, créalos en Adapty y envíalos directamente a los stores](#create-product-and-push-to-store)**. - **[Si los productos ya existen en App Store y/o Google Play, créalos en Adapty y conecta los productos existentes del store.](#create-product-and-connect-existing-store-products)** :::tip También puedes crear productos de forma programática usando el [Developer CLI](developer-cli-reference#adapty-products-create). ::: ## Crear producto y enviarlo al store \{#create-product-and-push-to-store\} :::warning Antes de empezar, asegúrate de haber configurado la integración con los stores que necesitas: - [App Store](initial_ios) - [Google Play](initial-android) Si configuraste la integración con App Store hace tiempo, asegúrate también de haber [añadido la clave API de App Store Connect](app-store-connection-configuration#step-6-add-app-store-connect-api-key). ::: <div style={{ maxWidth: '560px', margin: '0 auto 2rem', position: 'relative', aspectRatio: '16/9', width: '100%' }}> <iframe style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} src="https://www.youtube.com/embed/qUpC2XG-r5E?si=7Komyv4_PUQ4FaEH" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen /> </div> Para añadir un nuevo producto a tu app: 1. Ve a **[Products](https://app.adapty.io/products)** desde el menú principal de Adapty. <img src="/assets/shared/img/products-tab.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en **Create product** en la esquina superior derecha. Adapty admite todos los tipos de productos: suscripciones, no consumibles \(incluido acceso de por vida\) y consumibles. 3. Selecciona **Create a new product and push to stores**. <img src="/assets/shared/img/push-to-stores.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Introduce los siguientes datos: - **Product name**: escribe el nombre del producto que se usará en el Adapty Dashboard. El nombre es principalmente para tu referencia, así que elige el que te resulte más cómodo en el Adapty Dashboard. - **Access Level**: selecciona el [nivel de acceso](access-level) al que pertenece el producto. El nivel de acceso determina las funciones que se desbloquean tras la compra. Ten en cuenta que esta lista solo contiene los niveles de acceso creados previamente. El nivel de acceso `premium` se crea en Adapty por defecto, pero también puedes [añadir más niveles de acceso](access-level). - **Subscription duration**: selecciona la duración de la suscripción de la lista. - **Weekly/Monthly/2 Months/3 Months/6 Months/Annual**: la duración de la suscripción. - **Lifetime**: usa el período de por vida para los productos que desbloquean las funciones premium de la app para siempre. - **Non-Subscriptions**: para los productos que no son suscripciones y por tanto no tienen duración, usa non-subscriptions. Pueden usarse para desbloquear funciones adicionales, productos consumibles, etc. - **Consumables**: los artículos consumibles se pueden comprar varias veces y se agotan durante la vida útil de la aplicación. Ejemplos son la moneda del juego y los extras. Ten en cuenta que los productos consumibles no afectan a los niveles de acceso. - **Price (USD)**: el precio del producto en USD. Este precio se usará como base para calcular y establecer automáticamente los precios en todos los países. Podrás [personalizar el precio para distintos países y regiones](edit-product#set-country-specific-prices) más adelante. <img src="/assets/shared/img/create-product-push.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **Save & Continue**. 6. Configura la información del producto para App Store si tienes previsto publicar allí: - **Product ID**: crea un ID único y permanente para el producto. - **Product group**: selecciona un grupo de productos existente que hayas creado en App Store Connect o haz clic en **Create new Product Group** e indica su nombre. Cuando Adapty lo cree, podrás seleccionarlo desde el desplegable. - **Screenshot**: sube una captura de pantalla de la compra in-app que muestre claramente el artículo o servicio que se ofrece. Esta captura de pantalla se usa solo para la revisión de App Store y no se muestra en la App Store. Consulta los requisitos de tamaño y formato de la captura de pantalla [aquí](https://developer.apple.com/help/app-store-connect/reference/app-information/screenshot-specifications/). <img src="/assets/shared/img/push-app-store.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Haz clic en **Push data to App Store**. :::warning Si es tu primer producto para esta app, debes enviarlo manualmente para revisión en App Store Connect. Esto no será necesario más adelante. Una vez finalizada la revisión, el estado del producto en Adapty se actualizará automáticamente. ::: 8. Configura la información del producto para Google Play si tienes previsto publicar allí: - **Base Product ID**: crea un ID único y permanente para el producto. - **Subscription**: selecciona un grupo de suscripciones existente que hayas creado en Google Play Console o haz clic en **Create new Product Group** e indica su nombre e ID. Cuando Adapty lo cree, podrás seleccionarlo desde el desplegable. :::note El período de gracia y el período de retención de cuenta se establecerán automáticamente con los valores predeterminados según las reglas de Play Store. Puedes cambiarlos más adelante en Google Play Console. ::: <img src="/assets/shared/img/push-google-play.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 9. Haz clic en **Push data to Play Store**. 10. Para iOS, configura la oferta introductoria —prueba gratuita— seleccionando su **Free duration** desde el desplegable. En esta configuración inicial puedes añadir una prueba gratuita introductoria. Una vez que los stores aprueben el producto principal, podrás [añadir más ofertas](offers) (p. ej., promocionales, de recuperación) vinculando sus ID existentes desde la consola de tu store. <img src="/assets/shared/img/intro.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::important Las ofertas introductorias no se sincronizan automáticamente con Google Play. A diferencia de App Store, Google Play no tiene un tipo de "oferta introductoria" independiente: las pruebas gratuitas y las ofertas con descuento se configuran como **offers** en un plan base. [Crea la oferta en Google Play Console y vincúlala a tu producto de Adapty](google-play-offers). ::: 11. Por último, haz clic en **Save** para confirmar la creación del producto. ## Crear producto y conectar productos existentes del store \{#create-product-and-connect-existing-store-products\} :::warning Antes de empezar, asegúrate de haber: - Configurado la integración con los stores que necesitas: - [App Store](initial_ios) - [Google Play](initial-android) - Creado productos en los stores que necesitas: - [App Store](app-store-products) - [Google Play](android-products) **Si no tienes ningún producto creado**, considera seguir la guía [Enviar a los stores](#create-product-and-push-to-store) para crearlos tanto en Adapty como en los stores al mismo tiempo. ::: <div style={{ maxWidth: '560px', margin: '0 auto 2rem', position: 'relative', aspectRatio: '16/9', width: '100%' }}> <iframe style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} src="https://www.youtube.com/embed/nlkdKCF0SwY?si=VVigzHcpv3waKJmI" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen /> </div> Para añadir un nuevo producto a tu app: 1. Ve a **[Products](https://app.adapty.io/products)** desde el menú principal de Adapty. <img src="/assets/shared/img/products-tab.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en **Create product** en la esquina superior derecha. Adapty admite todos los tipos de productos: suscripciones, no consumibles \(incluido acceso de por vida\) y consumibles. 3. Selecciona **Connect an existing store product**. <img src="/assets/shared/img/existing-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Introduce los siguientes datos: - **Product name**: escribe el nombre del producto que se usará en el Adapty Dashboard. El nombre es principalmente para tu referencia, así que elige el que te resulte más cómodo en el Adapty Dashboard. - **Access Level ID**: selecciona el [nivel de acceso](access-level) al que pertenece el producto. El nivel de acceso determina las funciones que se desbloquean tras la compra. Ten en cuenta que esta lista solo contiene los niveles de acceso creados previamente. El nivel de acceso `premium` se crea en Adapty por defecto, pero también puedes [añadir más niveles de acceso](access-level). - **Subscription duration**: selecciona la duración de la suscripción de la lista. - **Weekly/Monthly/2 Months/3 Months/6 Months/Annual**: la duración de la suscripción. - **Lifetime**: usa el período de por vida para los productos que desbloquean las funciones premium de la app para siempre. - **Non-Subscriptions**: para los productos que no son suscripciones y por tanto no tienen duración, usa non-subscriptions. Pueden usarse para desbloquear funciones adicionales, productos consumibles, etc. - **Consumables**: los artículos consumibles se pueden comprar varias veces y se agotan durante la vida útil de la aplicación. Ejemplos son la moneda del juego y los extras. Ten en cuenta que los productos consumibles no afectan a los niveles de acceso. - **Price (USD)**: el precio del producto en USD. Si tu producto ya está en el store, este valor no afectará a su precio real en el store; puedes seleccionar cualquier valor de la lista. Más adelante, puedes [personalizar los precios para distintas regiones](edit-product#set-country-specific-prices) directamente en el Adapty Dashboard. <img src="/assets/shared/img/product-info.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **Continue**. 6. Configura la información del producto de cada store: - **App Store:** - **App Store Product ID:** este identificador único se usa para acceder a tu producto en los dispositivos. Selecciónalo de la lista. Si no lo ves en la lista, revisa su configuración en App Store Connect y asegúrate de que es correcta y pertenece a esta app. - **Play Store:** - **Google Play Product ID:** este es el identificador del producto en Play Store. Selecciónalo de la lista. Si no lo ves en la lista, revisa su configuración en Google Play Console y asegúrate de que es correcta y pertenece a esta app. - **Base Plan ID:** este ID define el plan base del producto en Play Store. Al añadir el Product ID de una suscripción en Play Store, debes proporcionar un Base Plan ID. Un plan base define los detalles esenciales de una suscripción: el período de facturación, el tipo de renovación (automática o de prepago) y el precio asociado. Ten en cuenta que, en Adapty, cada combinación de la misma suscripción con diferentes planes base se trata como un producto separado. - **Legacy fallback product**: un producto de respaldo se usa exclusivamente para apps que usan versiones antiguas del SDK de Adapty (versiones 2.5 e inferiores). Al marcar un producto como compatible con versiones anteriores en Google Play Console, Adapty puede identificar si puede ser comprado por versiones antiguas del SDK. Para este campo, especifica el valor en el siguiente formato: `<subscription_id>:<base_plan_id>`. - **Stripe**: - **Stripe Product ID**: identificador único de un producto en Stripe. - **Stripe Price ID**: en Stripe, los objetos de precio incluyen más que el importe; también cubren el comportamiento fiscal, los niveles de volumen y los intervalos de suscripción. Como un mismo producto puede tener varios precios, especifica el ID de precio correcto al crear un producto en Adapty. - **Paddle**: - **Paddle Product ID**: identificador único de un producto en Paddle. - **Paddle Price ID**: en Paddle, los objetos de precio incluyen más que el importe; también cubren el comportamiento fiscal, los niveles de volumen y los intervalos de suscripción. Como un mismo producto puede tener varios precios, especifica el ID de precio correcto al crear un producto en Adapty. 7. **Opcional:** puedes añadir productos de cualquier store personalizado haciendo clic en **Add custom store**. En la ventana **Manage custom store info**, puedes seleccionar un store personalizado existente o añadir uno nuevo y asociarle un producto. Ten en cuenta que Adapty solo registra transacciones de App Store, Google Play y Stripe. Para stores personalizados, deberás enviar las transacciones mediante la API server-side de Adapty con el [método Set transaction](ss-set-transaction). 8. Haz clic en **Save product** para finalizar la creación del producto. La sincronización del estado del producto puede tardar hasta cinco minutos, así que espera a que se actualice en la tabla. 9. Puedes [crear ofertas](create-offer) para el producto si lo necesitas. Para añadir ofertas, haz clic en **Yes, add offers**. De lo contrario, haz clic en **No, thanks**. :::note Las ofertas introductorias solo se crean en Adapty cuando se envía un producto al store. Al importar productos o para productos creados previamente, las ofertas introductorias no se sincronizan ni se muestran en Adapty, pero seguirán funcionando correctamente en la app. ::: ## Próximos pasos \{#next-steps\} ¡Enhorabuena! Has añadido tus productos a Adapty. ¿Qué viene ahora? - Si aún no has configurado las ofertas introductorias/promocionales, puedes [hacerlo](offers) ahora. - Si no quieres hacerlo o ya lo has hecho, continúa con la [configuración de los paywalls](quickstart-paywalls) para habilitar las compras in-app. - Si quieres hacer ajustes en los productos del store (p. ej., establecer precios regionales o configurar el período de gracia), hazlo en App Store Connect o Google Play Console. - Lee cómo puedes [editar productos](edit-product) más adelante. --- # File: edit-product --- --- title: "Editar producto" description: "Modifica y gestiona tus productos de suscripción en Adapty para un mejor seguimiento de ingresos." --- En Adapty, puedes editar el nombre del producto, el nivel de acceso, los precios regionales y los IDs de store conectados, además de ver el registro de auditoría para hacer seguimiento de los cambios de precio. La duración de la suscripción no es editable una vez creado el producto, por lo que tendrás que crear uno nuevo si necesitas cambiarla. :::warning Aunque puedes editar cualquier producto, es fundamental asegurarte de que los cambios en productos ya usados en paywalls activos no generen discrepancias en tus analíticas. **No se recomienda editar el nivel de acceso, el ID de producto de App Store ni el ID de producto de Play Store**, ya que puede afectar la claridad de las analíticas. Edítalos únicamente si cometiste un error, como una errata en el ID del producto. Si ya no usas el producto y quieres reemplazarlo por otro, te recomendamos encarecidamente crear un nuevo producto y actualizar los paywalls y las pruebas A/B correspondientes. ::: ## Editar producto \{#edit-product\} Para editar el producto: 1. Ve a **[Products](https://app.adapty.io/products)** desde el menú principal de Adapty. 2. Haz clic en la fila del producto en la tabla, o haz clic en los tres puntos junto al producto y selecciona **Edit**. 3. En la ventana **Edit** que se abre, realiza los cambios necesarios. Para más detalles sobre las opciones disponibles, consulta la sección [Crear producto](create-product). 4. Haz clic en **Save**. :::warning Los cambios que hagas en App Store Connect o Google Play Console no se sincronizan de vuelta con Adapty. El precio que se muestra en Adapty se establece cuando creas el producto y no se actualiza si cambias el precio en la store. Esto no afecta a tus analíticas de ingresos: Adapty obtiene los datos de ingresos directamente de las stores. El campo de precio en el dashboard es solo para tu referencia. ::: :::note Si cambias el nivel de acceso, el cambio se aplica únicamente a las nuevas suscripciones. Para los suscriptores existentes, el nivel de acceso actual permanece sin cambios y se actualiza automáticamente en la próxima renovación de la suscripción. ::: <img src={require('./img/edit-product.png').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Establecer precios por país \{#set-country-specific-prices\} Puedes configurar precios diferentes para distintas regiones directamente en el Adapty Dashboard, y estos precios por país se aplicarán automáticamente a tus productos en App Store Connect y/o Google Play Console. Para establecer precios por país: 1. [Abre el producto para editarlo](#edit-product). 2. Haz clic en **Download** para exportar tus precios actuales de las stores en el formato correcto, o crea un nuevo archivo CSV. <img src={require('./img/download-prices.webp').default} style={{ border: '1px solid #727272', /* border width and color */ width: '500px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Actualiza los precios en el archivo CSV. Respeta el [formato](#csv-file-format). Si dejas el precio de algún país sin cambios o no lo incluyes en el archivo, no ocurrirá nada. Al subir el CSV, Adapty compara los precios y actualiza únicamente los que sean diferentes. 4. En la ventana **Edit**, haz clic en **Upload** y selecciona el archivo CSV. <img src={require('./img/upload-prices.webp').default} style={{ border: '1px solid #727272', /* border width and color */ width: '500px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Si quieres que los cambios también se apliquen a los suscriptores existentes, selecciona **Apply to existing subscribers**. 6. Revisa los cambios que se aplicarán y haz clic en **Save changes**. <img src={require('./img/country-level-price.webp').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Formato del archivo CSV \{#csv-file-format\} :::tip Puedes reutilizar el mismo archivo CSV si tienes productos similares en una misma app o si quieres establecer los mismos precios en diferentes apps. ::: La forma más sencilla de editar precios en CSV es [descargar un archivo con los precios actuales y editarlo directamente](#set-country-specific-prices). Sin embargo, si lo creas tú mismo, el archivo debe contener las siguientes columnas: - `region_name` - `region_code` - `app_store_currency` - `app_store_requested_price` - `play_store_currency` - `play_store_requested_price` Ejemplo: ``` region_name,region_code,app_store_currency,app_store_requested_price,play_store_currency,play_store_requested_price United States,US,,8.99,,8.99 United Arab Emirates,AE,USD,8.99,AED,39.99 Germany,DE,USD,8.99,USD,8.99 ``` ## Ver el registro de auditoría \{#view-audit-log\} Adapty registra todos los cambios de precio de cada producto, para que puedas hacer seguimiento de quién realizó los cambios y cuándo. Para ver el registro de auditoría: 1. Ve a **[Products](https://app.adapty.io/products)** desde el menú principal de Adapty. 2. Haz clic en los tres puntos junto al producto y selecciona **Audit log**. La tabla del registro de auditoría muestra cada cambio de precio con la fecha, el nombre y rol del miembro del equipo, y el número de cambios. Para descargar un desglose CSV detallado de un evento, haz clic en el icono de descarga de esa fila. <img src={require('./img/audit-log.webp').default} style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: delete-product --- --- title: "Eliminar producto" description: "Descubre cómo eliminar un producto de suscripción en Adapty sin interrumpir el flujo de ingresos de tu app." --- Solo puedes eliminar productos que no estén en uso en ningún paywall. Para eliminar el producto: 1. Ve a **[Products](https://app.adapty.io/products)** desde el menú principal de Adapty. 2. Haz clic en el botón de **3 puntos** junto al producto y selecciona **Delete**. <img src="/assets/shared/img/delete-product.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Introduce el nombre del producto que vas a eliminar. <img src="/assets/shared/img/b945add-delete_product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Delete forever**. --- # File: add-product-to-paywall --- --- title: "Añadir producto a un paywall" description: "Aprende a añadir y gestionar productos en paywalls en Adapty." --- Para que un producto sea visible y seleccionable dentro de un [paywall](paywalls) para los usuarios de tu app, sigue estos pasos: 1. Al [configurar un paywall](create-paywall), haz clic en **Add product** bajo el título **Products**. 2. En el menú desplegable que se abre, selecciona los productos que se mostrarán a tus clientes. La lista contiene únicamente los productos creados previamente. El orden de los productos se mantiene en el lado del SDK, por lo que es importante tener en cuenta el orden deseado al configurar el paywall. Además, puedes especificar una oferta para un producto si lo deseas. <img src="/assets/shared/img/0479b51-ad_product_to_paywall.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Create as draft** o **Save and publish** según el estado del paywall. Ten en cuenta que, tras la creación, no se recomienda editar, añadir ni eliminar productos del paywall, ya que esto puede afectar a las métricas del paywall. --- # File: app-store-offers --- --- title: "Ofertas en App Store" description: "Configura y gestiona las ofertas de App Store para aumentar la retención de usuarios." --- :::info Configura tus [productos de la store](quickstart-products) antes de seguir esta guía. ::: Las ofertas en App Store son promociones especiales, pruebas gratuitas o descuentos para suscripciones de renovación automática. Incluyen descuentos y ofertas combinadas que ayudan a atraer nuevos usuarios y aumentar la conversión. Hay cuatro tipos de oferta en App Store, y Adapty los admite todos: - **[Ofertas introductorias](#introductory-offers) para nuevos usuarios**: - Períodos de suscripción gratuitos o con descuento - Solo los nuevos usuarios son elegibles (aquellos que nunca han activado una oferta introductoria ni han tenido una suscripción) - No necesitas vincularlas a productos en Adapty. Adapty aplica las ofertas automáticamente para los usuarios elegibles que compran el producto. - **Ofertas [promocionales](#promotional-offers) y de [recuperación](#win-back-offers)**: - Adapty aplica estas ofertas automáticamente en el momento de la compra, pero primero debes configurarlas en tus productos y paywalls. - Las ofertas promocionales incluyen períodos de suscripción gratuitos, descuentos en porcentaje y descuentos de precio fijo. Cualquier usuario puede ser elegible. - Las ofertas de recuperación incluyen períodos de suscripción gratuitos o descuentos en porcentaje. Solo los usuarios que se han dado de baja son elegibles. - **Códigos de oferta**: Para más información, consulta [Canjear códigos de oferta en iOS](making-purchases#redeem-offer-codes-in-ios). :::important Para usar las ofertas de App Store, sube tu [clave de suscripción](app-store-connection-configuration#step-4-for-trials-and-special-offers--set-up-promotional-offers) al Adapty Dashboard. ::: ## Ofertas introductorias \{#introductory-offers\} Adapty aplica automáticamente las ofertas introductorias en iOS si el usuario es elegible. Para activar las ofertas introductorias en los productos que vendes, solo tienes que crearlas en App Store Connect: 1. Abre tu app en App Store Connect y ve a **Monetization > Subscriptions**. 2. Selecciona un grupo de suscripciones y navega hasta la suscripción que necesitas. La suscripción debe tener una duración configurada. 3. Haz clic en **View all Subscription Pricing** y cambia a la pestaña **Introductory offers**. Haz clic en **Set up introductory offer**. 4. Selecciona los países y regiones donde estará disponible la oferta introductoria. 5. Selecciona las fechas de inicio y fin de la oferta introductoria. Si la oferta introductoria no tiene una fecha de fin específica, selecciona **No end date**. Haz clic en **Next**. 6. Selecciona el tipo de oferta introductoria. Según lo que elijas, también deberás definir la duración y el precio de la oferta. Lee más en la [documentación de Apple](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-introductory-offers-for-auto-renewable-subscriptions). 7. Revisa tu selección y haz clic en **Confirm**. Una vez que termines esta configuración, no necesitas hacer nada más en Adapty. La oferta se activa para los usuarios elegibles que compran el producto. Asegúrate de mostrar un paywall con este producto solo a los usuarios que son elegibles para la oferta. ## Ofertas promocionales \{#promotional-offers\} Adapty aplica automáticamente las ofertas promocionales si los usuarios son elegibles. Configura tus ofertas en App Store Connect primero y luego añádelas a un producto y paywall en Adapty: 1. Abre tu app en App Store Connect y ve a **Monetization > Subscriptions** desde el menú de la izquierda. 2. Selecciona un grupo de suscripciones y navega hasta la suscripción que necesitas. La suscripción debe tener una duración configurada. 3. Haz clic en **View all Subscription Pricing** y cambia a la pestaña **Promotional offers**. Haz clic en **Set up promotional offer**. 4. Introduce los detalles de la oferta promocional. Estos valores no se pueden cambiar después de crearlos y se reutilizarán, así que elige con cuidado. - **Promotional offer reference name**: El nombre de la oferta promocional. No será visible para tus usuarios. - **Promotional offer identifier**: El código de identificación de la oferta promocional. Lo usarás para añadir la oferta a Adapty. 5. Selecciona el tipo de oferta promocional. El tipo determina si los usuarios pagan un precio reducido o reciben un período gratuito. Para un descuento, selecciona **Pay as you go** o **Pay up front**. Para un período de suscripción gratuito, selecciona **Free**. Luego establece la duración y el precio de la oferta. Lee más en la [documentación de Apple](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-promotional-offers-for-auto-renewable-subscriptions). 6. Si es necesario, establece precios diferentes para distintos países y regiones y haz clic en **Next**. 7. Revisa tu elección y haz clic en **Confirm**. 8. [Añade la oferta promocional](create-offer) a Adapty. ## Ofertas de recuperación \{#win-back-offers\} :::important Antes de poder crear una oferta de recuperación, tu suscripción debe estar aprobada por App Review. ::: Adapty aplica automáticamente las ofertas de recuperación si los usuarios son elegibles. Configura tus ofertas en App Store Connect primero y luego añádelas a un producto y paywall en Adapty: 1. Abre tu app en App Store Connect y ve a **Monetization > Subscriptions** desde el menú de la izquierda. 2. Selecciona un grupo de suscripciones y navega hasta la suscripción que necesitas. La suscripción debe tener una duración configurada. 3. Haz clic en **View all Subscription Pricing** y cambia a la pestaña **Win-back offers**. Haz clic en **Create offer**. 4. Introduce los detalles de la oferta de recuperación. Estos valores no se pueden cambiar después de crearlos. - **Reference name**: El nombre de la oferta. No será visible para tus usuarios. - **Offer identifier**: El código de identificación de la oferta. Lo usarás para añadir la oferta a Adapty. 5. Configura el tipo, la duración y el precio de la oferta. Lee más en la [documentación de Apple](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-win-back-offers). 6. Revisa tu elección y haz clic en **Confirm**. 7. [Añade la oferta](create-offer) a Adapty. ## Pasos siguientes \{#next-steps\} Después de añadir las ofertas, continúa con la configuración: - Si también tienes **apps en Google Play**, configura las [ofertas de Google Play](google-play-offers). - Si tienes **ofertas promocionales o de recuperación**, [añádelas a Adapty](create-offer). - Si solo tienes **ofertas introductorias** y no tienes ofertas promocionales ni de recuperación, ya has terminado. Estas secciones pueden seguir siendo útiles: - [Trabajar con ofertas en el Paywall Builder de Adapty](create-offer#paywall-builder) - [Cómo funciona Adapty con las ofertas](create-offer#how-adapty-works-with-offers) --- # File: google-play-offers --- --- title: "Ofertas en Google Play" description: "Configura las ofertas de Google Play para mejorar la monetización y la retención de tu app." --- En Google Play, las ofertas de cualquier tipo (períodos de prueba gratuitos o pagos con descuento) se añaden como **offers**. Para crear una oferta, primero debes crear una suscripción y añadir un plan base de renovación automática. Las ofertas siempre se crean para planes base dentro de suscripciones. En la captura de pantalla a continuación, puedes ver una suscripción `premium_access`(1) con dos planes base: `1-month` (2) y `1-year` (3). <img src="/assets/shared/img/c0b1dfa-001930-November-03-XYnbieeu.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para crear una oferta en Google Play Console: 1. Haz clic en **Add offer** y elige el plan base de la lista. <img src="/assets/shared/img/75a5d69-eb0bc9a-001931-November-03-eQdthUMx.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Introduce el ID de la oferta. Se usará más adelante en los análisis y en el Adapty Dashboard, así que dale un nombre descriptivo. <img src="/assets/shared/img/ff282c2-c0b1dfa-001930-November-03-XYnbieeu.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Elige los criterios de elegibilidad: 1. **New customer acquisition**: la oferta estará disponible solo para nuevos suscriptores que no la hayan utilizado antes. Es la opción más habitual y la que deberías usar por defecto. 2. **Upgrade**: esta oferta estará disponible para los clientes que actualicen desde otra suscripción. Úsala cuando quieras promocionar planes más caros a tus suscriptores actuales; por ejemplo, clientes que pasan del nivel bronce al nivel oro de tu suscripción. 3. **Developer determined**: puedes controlar quién puede usar esta oferta desde el código de la app. Úsala con precaución en producción para evitar posibles fraudes: los clientes podrían activar una suscripción gratuita o con descuento una y otra vez. Un buen caso de uso para este tipo de oferta es recuperar suscriptores que han cancelado. <img src="/assets/shared/img/ee302dc-a506e5a-001934-November-03-TVBLOz2L.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Añade hasta dos fases de precio a tu oferta. Hay tres tipos de fase disponibles: 1. **Free trial**: la suscripción se puede usar gratis durante un período de tiempo configurado (mínimo 3 días). Es la oferta más habitual. 2. **Single payment**: la suscripción es más barata si los clientes pagan por adelantado. Por ejemplo, normalmente un plan mensual cuesta 9,99 $, pero con este tipo de oferta, los primeros tres meses cuestan 19,99 $, un descuento del 30%. 3. **Discounted recurring payment**: la suscripción es más barata durante los primeros `n` períodos. Por ejemplo, normalmente un plan mensual cuesta 9,99 $, pero con este tipo de oferta, cada uno de los primeros tres meses cuesta 4,99 $, un descuento del 50%. Una oferta puede tener dos fases. En ese caso, la primera fase debe ser un Free trial y la segunda puede ser un Single payment o un Discounted recurring payment. Se aplicarán en ese orden. <img src="/assets/shared/img/d6267f3-a48f79e-001936-November-03-A13wutRh.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::important Ten en cuenta que los paywalls creados con el Adapty Paywall Builder mostrarán únicamente la primera fase de una oferta de suscripción de Google con varias fases. No obstante, cuando un usuario compre el producto, todas las fases de la oferta se aplicarán tal como están configuradas en Google Play. ::: 5. Activa la oferta para usarla en la app. <img src="/assets/shared/img/d3fc09b-f149ba6-001937-November-03-MO9Gz3ap.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Continúa con [cómo añadir la oferta a Adapty](create-offer). :::note Los IDs de oferta pueden ser iguales para distintos planes base. ::: ## Pasos siguientes \{#next-steps\} Una vez añadidas las ofertas, continúa con la configuración: - Si también tienes **apps en App Store**, consulta la [guía de App Store](app-store-offers). - Si tienes **apps solo en Google Play**, sigue [esta guía](create-offer) para añadir ofertas a Adapty. --- # File: create-offer --- --- title: "Añadir ofertas a Adapty" description: "Crea y gestiona ofertas especiales de suscripción con las herramientas de Adapty." --- Adapty te permite ofrecer pruebas o descuentos a suscriptores nuevos, actuales o que han cancelado. Una vez que los hayas configurado en App Store Connect o Google Play Console, debes añadirlos a Adapty en dos pasos: 1. [Añadir ofertas a los productos en Adapty usando los IDs de oferta de las stores.](#1-create-offer) 2. [Añadir ofertas a los paywalls para que puedan aplicarse.](#2-add-offer-to-paywall) :::warning Las ofertas introductorias (App Store) se aplican automáticamente si el usuario es elegible. No las añadas a los productos en Adapty. Esta guía explica cómo configurar ofertas promocionales (App Store), ofertas de recuperación (App Store) y todas las ofertas de Google Play. ::: ## 0. Antes de empezar \{#0-before-you-start\} Antes de configurar ofertas en Adapty, asegúrate de lo siguiente: 1. Has creado todas las ofertas que necesitas en la store: - [App Store](app-store-offers) - [Google Play](google-play-offers) 2. Has creado los [productos](create-product) en Adapty y añadido sus IDs. 3. Para App Store: Has subido [la clave de compra in-app para ofertas promocionales](app-store-connection-configuration#step-4-for-trials-and-special-offers--set-up-promotional-offers). ## 1. Añadir oferta a un producto en Adapty \{#1-add-offer-to-product-in-adapty\} Una vez que tu oferta promocional (tanto para Play Store como para App Store) o tu oferta de recuperación (para App Store) esté configurada en las stores, añadirla a Adapty es sencillo: 1. Abre [**Products**](https://app.adapty.io/products) desde el menú principal de Adapty. Busca el producto al que quieres añadir una oferta. 2. Encuentra el producto al que quieres añadir una oferta. En la columna **Actions**, haz clic en el botón de **3 puntos** junto al producto y selecciona **Edit**. 3. En la ventana **Edit product**, haz clic en **+** y selecciona **Add offers**. 4. Haz clic en **Add offer**. 5. Introduce los detalles de la oferta para el producto. Estos son los campos disponibles: - **Offer name**: Asigna un nombre a la oferta para identificarla fácilmente en Adapty. Usa el nombre que más te convenga. - **App Store Offer type**: Selecciona el tipo de oferta de App Store que estás añadiendo: Promotional o Win-back. (Las ofertas introductorias no hace falta añadirlas: se aplican automáticamente si están disponibles.) - **App Store Offer ID**: Es el ID único de la oferta [que configuraste en App Store](app-store-products). - **Play Store Offer ID**: De igual modo, es el ID único de la oferta [que configuraste en Play Store](android-products). :::tip Si el campo **App Store Offer ID** o **Play Store Offer ID** no está activo, cambia a la pestaña **Products** y selecciona un ID de producto. ::: 6. (opcional) Añade más ofertas si lo necesitas haciendo clic en **Add offer**. 7. Haz clic en **Save** para añadir las ofertas al producto. ## 2. Añadir oferta a un paywall \{#2-add-offer-to-paywall\} :::info No puedes añadir ofertas a paywalls en estado **live**. Si quieres añadir una oferta a un paywall existente, [duplícalo](duplicate-paywalls) y configura los productos en el nuevo paywall. ::: Para que una oferta sea visible y seleccionable dentro de un [paywall](paywalls) para los usuarios de tu app, sigue estos pasos: 1. Al crear o editar un paywall, en la pestaña **General**, añade el producto al que acabas de añadir la oferta. 2. Elige la oferta que [creaste antes](create-offer) para este producto en la lista **Offer**. La lista solo está disponible para los productos que tienen ofertas. 3. Si lo necesitas, añade más productos y ofertas, pero solo puedes añadir una oferta por cada producto. ### Paywall Builder \{#paywall-builder\} :::info Los paywalls creados con el Paywall Builder de Adapty mostrarán únicamente la primera fase de una [oferta de suscripción multifase de Google](https://support.google.com/googleplay/android-developer/answer/12154973). Sin embargo, ten en cuenta que cuando un usuario compra el producto, se aplicarán todas las fases de la oferta tal como se configuraron en Google Play. ::: Cuando creas un paywall en el Paywall Builder de Adapty, tienes más opciones de personalización para los trials: - **Toggle**: En **Products**, establece **Product grouping** en **Toggle** y añade combinaciones de producto-oferta para cada estado del toggle. El caso de uso más habitual es incluir un producto sin oferta (p. ej., compra directa sin prueba gratuita) y otro producto diferente con una oferta asociada. - **Texto dinámico**: Puedes hacer que el texto del botón de compra cambie según la oferta disponible para el usuario que ve el paywall. Puedes configurar textos distintos para **Default**, **Free trial**, **Pay as you go** y **Pay up front**. ## Cómo funciona Adapty con las ofertas \{#how-adapty-works-with-offers\} Ten en cuenta lo siguiente sobre el funcionamiento de las ofertas en Adapty: - Cuando un usuario es elegible para una oferta, Adapty aplica automáticamente la oferta que hayas configurado en el momento en que el usuario realiza una compra. - Si un producto tiene tanto una oferta introductoria como ofertas promocionales configuradas en App Store, los usuarios elegibles recibirán primero la oferta introductoria. Una vez finalizado su período, si el usuario sigue siendo elegible para la oferta promocional y la has configurado en Adapty, se aplicará cuando intente comprar el producto de nuevo. - Si quieres tener más control sobre cómo se aplican las ofertas o necesitas vender tu producto sin ofertas en determinados casos, tienes varias opciones: - Configura los criterios de elegibilidad en App Store o Google Play Console - Crea un producto separado sin ofertas en App Store o Google Play Console - Crea un producto separado sin ofertas en Adapty, añade paywalls con ambas variantes del producto a un [placement](placements) y utiliza [segmentos](segments) de audiencia para controlar qué paywall se muestra a cada usuario. Por ejemplo, puedes crear segmentos basados en **Subscription product** o **Paid access level**, o usar [atributos personalizados](profiles-crm) para implementar tu propia lógica. --- # File: create-paywall --- --- title: "Crear paywall" description: "Aprende a crear paywalls de alta conversión usando el Paywall Builder de Adapty." --- Un [paywall](paywalls) es una configuración de Adapty que define qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos en tu app. Necesitas un paywall independientemente de cómo lo muestres: - [**Paywall Builder**](adapty-paywall-builder): Diseña una pantalla en el editor sin código. Adapty la renderiza y gestiona las compras. - **Paywall personalizado**: Implementa tu propia interfaz y usa la configuración del paywall para recuperar los productos. Una vez creado, asigna el paywall a un [placement](placements) — los placements controlan qué paywall ven los usuarios. Los productos de un paywall en producción son fijos, por lo que sus métricas siempre reflejan la misma combinación, lo que te permite comparar el rendimiento entre distintos conjuntos de productos y precios. :::tip También puedes crear paywalls mediante programación usando la [CLI para desarrolladores](developer-cli-reference#adapty-paywalls-create). ::: <details> <summary>Antes de empezar a crear paywalls (haz clic para expandir)</summary> 1. [Crea al menos un producto](create-product). 2. (opcional) [Crea una oferta](create-offer). </details> ## Crear paywall \{#create-paywall\} Para crear un nuevo paywall en el Adapty Dashboard: 1. Ve a [**Paywalls**](https://app.adapty.io/paywalls) en el menú principal de Adapty. Esta página muestra un resumen de todos tus paywalls y sus métricas. 2. Haz clic en **Create paywall**. 3. En la página **Paywalls / New paywall**, introduce un **Paywall name** para identificar este paywall en todo el Adapty Dashboard. 4. Haz clic en **Add product**. 5. Selecciona los productos que se mostrarán a tus clientes. :::note - El orden de los productos en esta lista se mantendrá en el SDK, así que ordénalos según tus preferencias. - Una vez que un paywall se muestre en producción, no podrás cambiar sus productos, ya que esto podría afectar a las métricas del paywall. ::: 6. Si ofreces pruebas gratuitas u otras ofertas para tus productos, agrégalas aquí, o no estarán disponibles. Elige una oferta que hayas [creado anteriormente](create-offer) para ese producto desde la lista **Offer**. La lista solo está disponible para los productos que tienen ofertas. 7. Haz clic en **Create as a draft** para confirmar la creación del paywall. ¡Tu paywall ya está creado! <img src="/assets/shared/img/create-paywall.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '900px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Próximos pasos \{#next-steps\} Después de crear tu primer paywall: 1. Agrégalo a un [placement](placements). Los IDs de placement serán las únicas entidades hardcodeadas. Los usarás para obtener los productos que quieres vender. 2. El modo de trabajo con el paywall a partir de aquí depende de tu implementación: - Si quieres usar el [Adapty Paywall Builder](adapty-paywall-builder), diseña el paywall en el editor sin código. Adapty renderizará el paywall y gestionará la lógica de compra, mientras que tú solo necesitarás mostrar el paywall en el código de la app. - Si tienes un paywall personalizado que quieres usar, consulta nuestras guías para implementar compras in-app con Adapty en tu plataforma: - [iOS](ios-implement-paywalls-manually) - [Android](android-implement-paywalls-manually) - [React Native](react-native-implement-paywalls-manually) - [Flutter](flutter-implement-paywalls-manually) - [Unity](unity-implement-paywalls-manually) - [Kotlin Multiplatform](kmp-implement-paywalls-manually) --- # File: paywall-builder-templates --- --- title: "Plantilla de paywall" description: "Usa las plantillas del Paywall Builder de Adapty para crear paywalls de alta conversión." --- Las plantillas de paywall prediseñadas tienen un diseño profesional y están pensadas para simplificar el proceso de creación de tu paywall. Las crean diseñadores expertos para que puedas presentar tus productos de forma atractiva con el mínimo esfuerzo. ¡Solo añade tu logo, dale el toque de tu marca y listo para cautivar a tu audiencia y disparar las ventas! <img src="/assets/shared/img/501b43c-PB-templates_listing.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning Esta sección describe el nuevo Paywall Builder, compatible con los SDK de iOS, Android y React Native versión 3.0 o superior, y con los SDK de Flutter y Unity versión 3.3.0 o superior. Para información sobre el Paywall Builder antiguo, compatible con el SDK de Adapty v2.x o anterior, consulta [Plantillas del Paywall Builder antiguo](paywall-builder-templates-legacy). ::: ## Por qué usar plantillas de paywall \{#why-use-paywall-templates\} Las plantillas son un punto de partida excelente: ofrecen diseños bien estructurados y con gran atractivo visual. Puedes usarlas tal cual o hacer pequeños ajustes para adaptarlas a la estética de tu marca. Estas son las razones por las que las plantillas prediseñadas son una opción inteligente: - **Ahorro de tiempo**: monta un paywall de aspecto profesional rápidamente sin necesidad de un trabajo de diseño exhaustivo. - **Coherencia**: garantiza una apariencia uniforme alineada con estándares de diseño probados. - **Personalización**: adapta cada plantilla con los elementos de tu marca para hacerla única. Para quienes prefieren un enfoque más libre, las plantillas de diseño minimalista ofrecen un lienzo en blanco. Estas plantillas incluyen posiciones básicas, lo que facilita dar rienda suelta a tu creatividad y construir un paywall desde cero con el versátil, completo y fácil de usar Paywall Builder de Adapty. <img src="/assets/shared/img/eba907e-PB_templates_minimal_design.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Elegir plantilla de paywall \{#choose-paywall-template\} Al crear un nuevo paywall, Adapty ofrece una selección de plantillas. Puedes cambiar de plantilla en cualquier momento después de eso. Sin embargo, es importante tener en cuenta que reemplazar una plantilla descartará cualquier cambio realizado en el diseño de tu paywall. Para no perder tu trabajo, te recomendamos duplicar el paywall antes de cambiar de plantilla, así podrás volver al paywall guardado si lo necesitas. 1. Ve a los **Layout settings** del paywall. 2. Haz clic en **Change template**. <img src="/assets/shared/img/change-template.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Choose template** que se abre, explora y selecciona una nueva plantilla. <img src="/assets/shared/img/984a1e9-PB_select_template.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en **Choose** para confirmar el cambio de plantilla. Ten en cuenta que reemplazar una plantilla descartará cualquier cambio realizado en el diseño de tu paywall. --- # File: paywall-generator --- --- title: "Generador de paywalls" description: "Genera paywalls con IA y lánzalos rápidamente." --- :::info El generador de paywalls con IA solo está disponible para apps publicadas en la Apple App Store. ::: Puedes crear un paywall único y con alta tasa de conversión adaptado a tu app en cuestión de segundos con nuestro generador de IA integrado. Así podrás lanzar tus paywalls más rápido y dejar de perder tiempo pensando por dónde empezar. :::note No puedes indicar qué elementos aparecerán dentro del paywall. El generador de paywalls está pensado principalmente para conseguir unos visuales perfectos generando textos e imágenes. ::: ## Generar paywalls \{#generate-paywalls\} Para generar un paywall: 1. En los **Layout settings** del paywall, haz clic en **Change template > Generate template**. O, para un paywall nuevo, haz clic en **Generate paywall** desde la pestaña **Builder & Generator tab**. 2. Escribe tu prompt en el campo de texto. O selecciona uno de los prompts predefinidos para probar qué puede crear el generador de paywalls para tu app. Consulta [los consejos a continuación](#how-to-write-prompts-for-paywall-generator) para aprender a escribir el prompt más efectivo y obtener un paywall con un aspecto cuidado y listo para producción. 3. El generador de paywalls obtendrá información sobre tu app desde la App Store y generará un paywall relevante con los productos que hayas añadido. Si encuentras algún problema, asegúrate de tener el [**Apple App ID** en los **App settings**](app-store-connection-configuration#step-1-provide-bundle-id-and-apple-app-id). 4. Elige una de las cinco plantillas generadas y haz clic en **Pick & Open in Builder**, o chatea con el generador como lo harías con tus agentes de IA favoritos para mejorar el resultado de la generación. :::important Puedes generar hasta 10 conjuntos de plantillas al día para una app. Cada chat puede contener hasta 10 generaciones. Si alcanzas este límite, crea un nuevo chat y usa el contexto del chat anterior para escribir un prompt detallado. ::: 5. Los chats se organizan como hilos y se guardan en la cuenta de tu app. Puedes volver a los chats en cualquier momento y probar otras plantillas de los hilos. Siempre puedes comprobar qué prompts funcionaron mejor o ajustar algunas plantillas que hayas usado anteriormente. ## Cómo escribir prompts para el generador de paywalls \{#how-to-write-prompts-for-paywall-generator\} Estos consejos te ayudarán a escribir prompts para generar un paywall listo para producción: - Cuantos más detalles especifiques en el prompt, mejor. - **Mal**: Haz que mi paywall tenga un aspecto moderno - **Bien**: Crea un paywall moderno y minimalista con fondo claro, botones redondeados y degradados sutiles. - No describas tu app en detalle; Adapty la obtiene automáticamente de la App Store: - **Mal**: Crea un paywall navideño para mi app que enseña a la gente a dibujar. - **Bien**: Crea un paywall festivo con temática navideña, colores cálidos, copos de nieve dibujados a mano y un titular alegre como "Desbloquea todas las lecciones". - Describe los visuales y el texto, no el diseño o la estructura: - **Mal**: Añade reseñas de usuarios al carrusel. - **Bien**: Incluye una cita breve de un usuario satisfecho, como "¡Esta app mejoró completamente mis habilidades de dibujo!", cerca de la parte inferior de la pantalla. - Especifica el estilo de la imagen o ilustración con claridad. - **Mal**: Añade una imagen de personas haciendo ejercicio. - **Bien**: Añade una ilustración de estilo plano de dos personas haciendo yoga sobre esterillas en un estudio luminoso y minimalista. Usa colores pastel suaves para que encaje con una temática de bienestar. --- # File: manage-paywall-ui-elements --- --- title: "Gestionar elementos de la UI del paywall" description: "Personaliza y gestiona los elementos de la UI del paywall para mejorar la experiencia del usuario." --- Tras elegir una plantilla, sus elementos se mostrarán en el panel izquierdo. Usa este panel para organizar los elementos de tu paywall. Los elementos aparecerán en el paywall en el mismo orden en que figuran en el panel izquierdo. :::warning Esta sección describe el nuevo Paywall Builder, que funciona con los SDKs de iOS, Android y React Native en la versión 3.0 o superior, y con los SDKs de Flutter y Unity en la versión 3.3.0 o superior. Para información sobre el Paywall Builder legacy compatible con Adapty SDK v2.x o anterior, consulta [Legacy Paywall Builder](adapty-paywall-builder-legacy). ::: ## Añadir elemento \{#add-element\} Para añadir un elemento a tu paywall por encima del layout: 1. Haz clic en el botón **Add Element** del panel izquierdo. 2. Elige el elemento que quieres añadir. El nuevo elemento aparecerá en la lista encima del **Footer**. <img src="/assets/shared/img/ee68985-PB_add_new_element.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para añadir un elemento a un elemento compuesto: 1. Haz clic en el botón **Plus** junto al elemento compuesto. 2. Selecciona el elemento que quieres añadir. <img src="/assets/shared/img/abbf9ef-PB_add_element.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Renombrar elemento del paywall \{#rename-paywall-element\} Para renombrar un elemento: 1. Haz clic en el elemento en el panel izquierdo para abrir sus detalles. <img src="/assets/shared/img/a5b26ba-edit_element.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el botón de puntos suspensivos en el panel derecho y elige la opción **Edit**. 3. Escribe el nuevo nombre del elemento y pulsa **Enter**. ## Duplicar elemento \{#duplicate-element\} Para duplicar un elemento: 1. Haz clic en el elemento en el panel izquierdo para abrir sus detalles. <img src="/assets/shared/img/07e81d6-duplicate_element.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el botón de puntos suspensivos en el panel derecho y elige la opción **Duplicate**. El elemento duplicado, con "Copy" añadido a su nombre, aparecerá en el panel izquierdo como una copia completa del original. ## Mover elemento \{#move-element\} Para mover un elemento: arrastra y suelta el elemento a su nueva posición en el layout o dentro de un elemento compuesto. Una línea morada indica una posición disponible para el elemento, mientras que una línea roja muestra una posición no accesible. <img src="/assets/shared/img/3c9252a-PB_move_between_layers.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Ocultar / mostrar elemento \{#hide--show-element\} Aunque ya hayas creado y configurado un elemento, puedes ocultarlo temporalmente del paywall. Esto resulta útil si planeas añadirlo más adelante sin perder toda la configuración que hiciste. Al ocultar un elemento, el paywall se muestra como si el elemento nunca hubiera sido añadido; todos los alineamientos y espacios se recalculan y redesplazan. Para ocultar un elemento: 1. Haz clic en el elemento en el panel izquierdo para abrir sus detalles. 2. Haz clic en el botón de puntos suspensivos en el panel derecho y elige la opción **Hide**. <img src="/assets/shared/img/pb-hide-element.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El elemento oculto queda marcado tanto en el panel principal —como una nota— como en el panel izquierdo cuando lo seleccionas. <img src="/assets/shared/img/pb-hidden-element.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para volver a mostrar el elemento, haz clic en el botón de puntos suspensivos en el panel derecho y elige la opción **Show**. ## Eliminar elemento \{#delete-element\} Para eliminar un elemento del paywall: 1. Haz clic en el elemento en el panel izquierdo para abrir sus detalles. <img src="/assets/shared/img/d6763f3-delete_element.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el botón de puntos suspensivos en el panel derecho y elige la opción **Delete**. --- # File: paywall-device-compatibility-preview --- --- title: "Vista previa de paywalls" description: "Previsualiza la compatibilidad de los paywalls en distintos dispositivos para una experiencia óptima." --- :::important Esta sección describe el nuevo Paywall Builder, que funciona con iOS, Android y los SDK de React Native versión 3.0 o superior, y los SDK de Flutter y Unity versión 3.3.0 o superior. Para información sobre el Paywall Builder heredado compatible con Adapty SDK v2.x o anterior, consulta [Vista previa de compatibilidad de dispositivos del Paywall Builder heredado](paywall-layout-and-products-legacy#device-compatibility-preview). ::: Tienes dos formas de previsualizar tu paywall en diferentes tipos de pantalla: - **Vista previa en dispositivos**: Comprueba que todo se ve como esperas en dispositivos reales en cualquier etapa del desarrollo. - **Vista previa en el Adapty Dashboard**: Previsualiza tu paywall mientras lo diseñas. ## Vista previa en dispositivos \{#preview-on-devices\} Para previsualizar tu paywall en un dispositivo real: 1. [Descarga la app de Adapty desde la App Store](https://apps.apple.com/us/app/adapty/id6739359219). 2. En el paywall builder, haz clic en **Test on device**. <img src="/assets/shared/img/test-on-device.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <br/> 3. Selecciona el idioma del paywall. 4. Escanea el código QR con la cámara del dispositivo o abre el enlace. Esto abrirá tu paywall en la app móvil de Adapty. :::note En modo de prueba, Adapty no puede acceder a tus productos en los stores, por lo que los precios mostrados en el paywall no son reales. ::: <img src="/assets/shared/img/qr-code.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Vista previa en el Adapty Dashboard \{#preview-in-the-adapty-dashboard\} Puedes previsualizar tu paywall en diferentes tipos de pantalla usando el panel del lado derecho del paywall builder. Esto te ayuda a asegurarte de que tu paywall se vea bien en distintos dispositivos y tamaños de pantalla. Desde aquí puedes: - Seleccionar el dispositivo en el que previsualizar tu paywall. - Cambiar entre los modos de vista previa horizontal y vertical — especialmente útil para paywalls diseñados para iPad. - Acercar o alejar la vista previa. - Previsualizar [variables de etiquetas para información de productos](paywall-builder-tag-variables#how-to-use-tag-variables). :::tip Configura el [ancho máximo](paywall-layout-and-products#content-layout) de los elementos para optimizar el diseño en iPads. ::: <img src="/assets/shared/img/paywall-preview.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: paywall-layout-and-products --- --- title: "Diseño del paywall" description: "Diseña los layouts de tus paywalls y gestiona productos en Adapty para mejorar la conversión." --- Tras seleccionar una plantilla para tu paywall en el Paywall Builder de Adapty, puedes personalizar su apariencia visual para que se ajuste al estilo de tu marca. Los ajustes de Layout ofrecen una gran variedad de controles para modificar el layout, el fondo y el aspecto general del paywall. Exploremos estos ajustes: los ajustes de layout controlan los aspectos básicos del paywall, incluyendo la plantilla, el color de fondo, las fuentes predeterminadas, el flujo de compra, el layout del contenido y los botones superiores. :::warning Esta sección describe el nuevo Paywall Builder, que funciona con los SDKs de iOS, Android y React Native versión 3.0 o superior, y los SDKs de Flutter y Unity versión 3.3.0 o superior. Para información sobre el Paywall Builder heredado compatible con Adapty SDK v2.x o anterior, consulta [Diseño del paywall en el Legacy Paywall Builder](paywall-layout-and-products-legacy#layout). ::: ## Flujo de compra \{#purchase-flow\} Decide cómo completarán las compras tus usuarios. Hay dos opciones: - **Products as list + purchase button**: Los usuarios seleccionan los productos primero y luego pulsan el botón de compra para iniciar la transacción. <img src="/assets/shared/img/a816501-PB_products_list.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> - **Products as purchase buttons**: Cada producto es un botón, y la compra comienza cuando el usuario pulsa el botón de ese producto. <img src="/assets/shared/img/4114858-PB_products_buttons.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Color de fondo \{#background-color\} Mantén la coherencia visual estableciendo un fondo predeterminado para tu paywall. Usa el campo **Background color** en los **Layout settings**. Haz clic en el cuadrado de color para abrir la ventana de configuración, donde puedes elegir un color sólido o un degradado en pestañas separadas. <img src="/assets/shared/img/8a095a9-PB_background_color.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Ajustes de fuente de tu paywall \{#font-settings-of-your-paywall\} Es importante que el paywall sea visualmente coherente con el resto de tu app, y uno de los factores visuales más importantes es la fuente que usas. Puedes elegir simplemente usar la fuente del sistema (SF Pro para iOS, Roboto para Android), usar una de las fuentes comunes disponibles o subir tu propia fuente personalizada: <img src="/assets/shared/img/a306fd5-PB_default_font.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Los ajustes de fuente en los **Layout settings** se aplican a todos los componentes del paywall de forma predeterminada. Puedes sobrescribir estos ajustes para elementos específicos, como cuadros de texto o listas, al editarlos individualmente. Si cambias la fuente predeterminada en los **Layout settings**, no afectará a los elementos que ya tengan fuentes individuales. Aprende cómo subir una fuente personalizada [aquí](using-custom-fonts-in-paywall-builder). ## Layout del contenido \{#content-layout\} No tienes que ajustar manualmente los márgenes y el ancho de cada elemento de contenido del paywall. Ve a **Content layout** para modificar todos los ajustes siguientes para todos los elementos de contenido a la vez: - **Default child margin**: Define el espacio alrededor de cada elemento hijo. - **Spacing**: Define el espacio entre elementos dentro de un layout. - **Max width**: Establece el ancho máximo de los elementos para optimizar el layout en iPads. Recomendamos 600pt para un diseño limpio y equilibrado. :::warning El parámetro Max width solo está disponible a partir de Adapty SDK v3.7.0 o superior. ::: <img src="/assets/shared/img/content-layout.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para ajustar el layout de un elemento específico —como establecer el ancho máximo del footer— ve a la sección **Layout** en **App Icon, Header, Feature List, Products** o **Footer**. ## Botones superiores \{#top-buttons\} Añade hasta 2 botones superiores a tu paywall para ofrecer a los usuarios opciones como cerrar el paywall. Personaliza su apariencia y comportamiento de la siguiente manera: 1. Activa el toggle **Top Button** o **Top Button 2**. 2. Elige el aspecto y la posición del botón. La vista previa se actualizará al instante. <img src="/assets/shared/img/960ea1d-PB_Layout_button.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> | Ajuste del botón | Descripción | |--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Action | <p>Elige la acción que el paywall debe ejecutar cuando un usuario pulse este botón. Si eliges acciones estándar, el paywall generará un evento estándar que podrás gestionar de forma estándar en el código de tu app.</p><p>Si eliges una acción personalizada, deberás procesar la acción por su `CustomActionID` en el código de tu app.</p> | | Style | Elige si quieres que el botón tenga aspecto de icono o de texto. Si eliges un icono, selecciona el tipo de icono en el campo correspondiente. | 3. Para retrasar la aparición del botón, ajusta el control deslizante **Show after delay**. <img src="/assets/shared/img/9f9a159-PB_delay_slider.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: paywall-head-picture --- --- title: "Imagen principal del paywall" description: "Personaliza tu paywall con una imagen principal para mejorar las tasas de conversión en Adapty." --- La imagen principal es el elemento estrella de tu paywall: define el tono, establece el tema y capta la atención del usuario desde el primer momento. Esta imagen juega un papel fundamental en el aspecto y la sensación de tu paywall tanto en iOS como en Android. :::warning Esta sección describe el nuevo Paywall Builder, que funciona con los SDKs de iOS, Android y React Native en la versión 3.0 o superior, y con los SDKs de Flutter y Unity en la versión 3.3.0 o superior. Para obtener información sobre el Paywall Builder anterior, compatible con Adapty SDK v2.x o versiones anteriores, consulta [Imagen principal del Paywall Builder heredado](paywall-layout-and-products-legacy#main-image-and-sizing). ::: ## Formato y tamaño de la imagen principal \{#hero-image-format-and-size\} La imagen principal es el elemento central del diseño de tu paywall, esencial para captar la atención de los usuarios y animarles a actuar. Sigue estas pautas para asegurarte de que tu imagen sea efectiva y visualmente atractiva: - **Formatos**: JPG y PNG. - **Tamaño recomendado**: Archivos de hasta 2 MB para una carga más rápida. - **Composición de la imagen**: Las fotos con el objeto principal centrado y rodeado de espacio suficiente suelen transmitir el mensaje de forma efectiva. - **Imágenes de impacto**: Las fotos emotivas o con colores vibrantes funcionan muy bien. - **Uso de gráficos**: Ideal para añadir atractivo visual, con espacios separados reservados para el texto. <img src="/assets/shared/img/70000ff-PB_hero_image.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Tienes control total sobre el tamaño de la imagen principal: ajusta sus proporciones para conseguir el equilibrio visual que desees en tu paywall. Especifica el tamaño de la imagen como un porcentaje del área total de la pantalla para una alineación perfecta. ## Opciones de diseño de la imagen principal \{#hero-picture-layout-options\} La **imagen principal superpuesta** añade profundidad y dinamismo a tu paywall. Al estar posicionada como fondo fijo en la parte inferior, crea un efecto impresionante mientras el resto de los elementos se desplazan sobre ella. Esto hace que la imagen principal parezca estática, ofreciendo una experiencia visualmente atractiva mientras los usuarios hacen scroll por el contenido. <img src="/assets/shared/img/01c702a-overlay_head_picture.gif" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El **diseño transparente** ofrece una imagen principal a pantalla completa y muy llamativa que capta la atención de inmediato. Este diseño es perfecto para mostrar una selección limitada de productos o contenido, ocupando toda la pantalla y generando un impacto poderoso y directo sin necesidad de hacer scroll. <img src="/assets/shared/img/0401532-transparent_hero_picture.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::note Usa el diseño transparente para mostrar contenido mínimo, ya que no implica scroll, lo que hace que tu mensaje sea claro e impactante. ::: El **diseño plano** imita una landing page fluida, presentando todos los elementos en una capa continua y desplazable. Los usuarios disfrutan de una narrativa suave y cohesionada mientras hacen scroll por el contenido, perfecta para integrar tus productos o historias de forma efectiva en un flujo unificado. <img src="/assets/shared/img/7c13a16-flat_hero_picture.gif" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::note Ideal para contar historias o presentar una serie de ofertas, el diseño plano te permite crear una secuencia atractiva que cautiva a los usuarios. ::: ## Máscara de la imagen principal \{#hero-image-mask\} El **tipo de máscara** define la forma de la imagen principal, permitiéndote aplicar efectos creativos que mejoran la presentación visual. Para los diseños de imagen plana o superpuesta, elige entre distintos tipos de máscara para adaptar el diseño a tus necesidades. <img src="/assets/shared/img/b484e36-Image_mask.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Ajusta el redondeo de la máscara de la imagen usando valores numéricos para conseguir el aspecto perfecto para tu imagen principal. ## Cómo eliminar una imagen principal \{#how-to-remove-a-hero-image\} Para eliminar una imagen principal de un paywall: 1. Abre el elemento **Hero image**. <img src="/assets/shared/img/67f57af-PB_remove_hero_image.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Cambia su altura a cero. --- # File: paywall-video --- --- title: "Vídeo hero del paywall" description: "Mejora los paywalls con contenido de vídeo para aumentar el engagement en Adapty." --- Añadir un clip de vídeo a tu paywall es una forma estupenda de captar la atención de los usuarios y aumentar las conversiones. Los vídeos explican tus ofertas con mayor claridad, destacan las funciones clave y crean una experiencia más dinámica y visualmente atractiva. <img src="/assets/shared/img/paywall-video-hands.gif" style={{ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning El vídeo hero es compatible con el SDK de Adapty en las siguientes versiones: - iOS: desde v3.1.0 - Android: desde v3.1.1 - React Native: desde v3.1.0 - Flutter: desde v3.2.0 - Unity: desde v3.3.0 Si el vídeo no es compatible o en casos de respaldo, se mostrará el primer fotograma del vídeo en su lugar. ::: Añade el **Hero video** en lugar del elemento **Hero image**: 1. Primero, activa el modo de vídeo. <img src="/assets/shared/img/add-paywall-video.webp" style={{ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Luego, arrastra y suelta tu archivo de vídeo en el área **Video file**. <img src="/assets/shared/img/drag-and-drop-video.webp" style={{ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Formatos compatibles \{#supported-formats\} | Especificación | Detalles | |--------------------|----------------| | Extensiones | MP4 y WEBM | | Tamaño mínimo | 640×480 | | Duración máxima | 30 seg | | Audio | No compatible | --- # File: paywall-product-block --- --- title: "Lista de productos del paywall" description: "Descubre cómo configurar los bloques de productos del paywall en Adapty para optimizar las compras in-app." --- La lista de productos es un elemento clave de tu paywall que muestra tus ofertas de forma organizada y atractiva. Esta lista es fundamental para guiar a los usuarios hacia la compra. La pestaña **Content** contiene los productos que se mostrarán en el paywall. Son los mismos productos que añadiste al paywall cuando lo creaste. Puedes ajustar la lista de productos. Esto afectará a la lista de productos en la pestaña **General** del paywall. Después de revisar la lista de productos: 1. Elige qué producto debe estar preseleccionado por defecto en el campo **Selected product**. 2. Define cómo debe verse un producto cuando está seleccionado o no en la pestaña **Style** de la sección **Products**. 3. Configura la vista general del bloque en la pestaña **Layout** o [añade grupos de productos](#add-products-group) para combinar diseños. :::warning Esta sección describe el nuevo Paywall Builder, que funciona con los SDK de iOS, Android y React Native versión 3.0 o superior, y los SDK de Flutter y Unity versión 3.3.0 o superior. Para más información sobre el Paywall Builder antiguo compatible con el SDK de Adapty v2.x o anterior, consulta [Productos del paywall en el Paywall Builder antiguo](paywall-layout-and-products-legacy#products). ::: ## Personalización de la vista del producto \{#product-view-customisation\} Mejorar el atractivo visual de determinados productos puede reorientar significativamente la atención del usuario. Destacar un producto o una oferta especial puede animar a los usuarios a centrarse en él. Veamos algunas opciones de personalización muy útiles. ### Insignia del producto \{#product-badge\} Una insignia de producto es una pequeña etiqueta que se puede añadir a un producto. Esta insignia puede aportar información promocional adicional y orientar la elección del usuario. El tamaño de la insignia se ajusta automáticamente al texto, y su posición se optimiza según el diseño de tu paywall. <img src="/assets/shared/img/eedb135-PB_product_badge.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para añadir una insignia de producto: 1. Activa el interruptor **Product badge** en la configuración de un producto específico. <img src="/assets/shared/img/2a5779d-PB_product_badge_config.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Personaliza la vista y el texto de la insignia según tus necesidades de diseño. ### Producto preseleccionado \{#selected-product\} Para el flujo de compra **Products as list + purchase button**, puedes **preseleccionar** un producto para orientar sutilmente a los usuarios hacia él. Esto puede ser especialmente eficaz para guiar la elección del usuario. Si añades varios [grupos de productos](#add-products-group), la misma opción **Selected product** se aplicará a todos los grupos. <img src="/assets/shared/img/3f37969-PB_preselected_product.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para preseleccionar un producto: 1. Abre el elemento **Products**. <img src="/assets/shared/img/da4e4c4-PB_preselect_product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la pestaña **Content**, elige el producto que quieres preseleccionar en la lista desplegable **Selected product**. 3. Ajusta la vista del producto seleccionado y la vista predeterminada de los demás productos en la pestaña **Style** si es necesario. <img src="/assets/shared/img/ac7411e-PB_setup_selected_default_button.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Producto destacado \{#highlighted-product\} Para el flujo de compra **Products as purchase buttons**, puedes **destacar** un producto preferido para convertirlo en la opción principal y captar de inmediato la atención del usuario. <img src="/assets/shared/img/afc2882-PB_highlighted_product.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para destacar un producto: 1. En el panel izquierdo, elige el producto que quieres destacar. <img src="/assets/shared/img/a630507-PB_highlight_product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la subsección **Style**, ajusta el diseño para que el producto destaque más. ### Ofertas del producto \{#product-offers\} Cada producto puede mostrar texto diferente para las ofertas en la subsección **Text**. La pestaña **Default** contiene el texto que se muestra sin oferta. Es un buen lugar para usar: - [variables de etiqueta](paywall-builder-tag-variables) para contenido dinámico y localizado - [etiquetas personalizadas](custom-tags-in-paywall-builder) para tu propio contenido dinámico Empieza a escribir con un corchete angular y Adapty sugerirá las variables de etiqueta disponibles para insertar datos localizados de los stores. <img src="/assets/shared/img/0de6871-PB_text.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Cambiar entre 2 conjuntos de productos con un interruptor de prueba \{#switch-between-2-product-sets-by-trial-toggle\} Para crear una experiencia de usuario más versátil, puedes permitir a los usuarios cambiar entre dos conjuntos de productos usando un interruptor. Esto es especialmente útil para diferenciar entre productos estándar y pruebas. Para añadir un interruptor: 1. En el elemento **Products**, cambia la opción **Products grouping** a **Toggle (for free trials and other offers)**. Esto añadirá dos subsecciones: **Toggle on** y **Toggle off**. <img src="/assets/shared/img/d859a58-PB_turn_on_toggle.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Añade productos a ambas subsecciones para crear las vistas para cuando el interruptor esté activado o desactivado. 3. En el elemento **Toggle**, establece el **Default state** para elegir si el interruptor debe comenzar activado o desactivado en el paywall. <img src="/assets/shared/img/4127a67-PB_toggle_default_state.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Cambiar entre conjuntos de productos por pestañas \{#switch-between-product-sets-by-tab\} Las pestañas del paywall te permiten agrupar tus productos por categorías, mostrando todas las opciones posibles a tus usuarios. Son especialmente útiles si: - Tu app ofrece varios planes semanales, mensuales o anuales - Tienes diferentes niveles como Plus, Gold o Premium También puedes añadir elementos como listas de características en las pestañas para ayudar a los usuarios a ver las diferencias entre niveles. Para añadir pestañas: 1. En el elemento **Products**, establece **Products grouping** en **Tabs (for comparing plan groups)**. Esto dividirá tus productos en dos grupos de pestañas iniciales. <img src="/assets/shared/img/paywall-tabs.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Si necesitas más pestañas, haz clic en **Add tab group**. <img src="/assets/shared/img/add-tab-group.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Organiza tus productos dentro de estas pestañas. <img src="/assets/shared/img/tabs-divided.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Abre el primer grupo de pestañas y en **Tab title** introduce el nombre que aparecerá en el paywall. <img src="/assets/shared/img/tab-rename.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Asigna un nombre interno a la pestaña en un campo separado para facilitar su identificación. Este nombre no será visible en el paywall, pero puede ayudarte a identificar la pestaña en las listas. <img src="/assets/shared/img/tab-internal-rename.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Repite los pasos 4-5 para cada pestaña. 7. Elige qué pestaña estará activa cuando el usuario abra el paywall. Ve a **Tab control** y selecciona la pestaña predeterminada en la lista **Selected tab**. <img src="/assets/shared/img/tab-selected.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Mostrar productos adicionales bajo un botón \{#show-extra-products-under-a-button\} Para mantener tu paywall simple, puedes ocultar algunos productos o grupos de productos bajo un botón (como "Ver más planes" o la etiqueta que prefieras). Esto ayuda a los usuarios a centrarse primero en tus principales opciones y, al mismo tiempo, les da la posibilidad de explorar otros planes si lo desean. Es una forma estupenda de hacer el paywall más limpio y mejorar las conversiones. Así se hace: 1. En el elemento **Products**, establece la opción **Products grouping** en **Button (for more alternative plans)**. Esto dividirá tus productos en dos grupos: **Shown** y **More plans**. <img src="/assets/shared/img/view-more-plans.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Distribuye tus productos entre estos grupos. **Shown** es para los productos que quieres mostrar de inmediato. **More plans** es para los productos ocultos tras el botón, que solo se muestran cuando el usuario hace clic en él. <img src="/assets/shared/img/divided-plans.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Personaliza el texto y el diseño del botón en el elemento **View more plans** según tus necesidades. <img src="/assets/shared/img/view-more-plans-button.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Estas opciones te ayudan a crear una lista de productos clara y visualmente atractiva que guía a los usuarios hacia la compra. ## Mostrar planes adicionales en una hoja inferior \{#show-extra-plans-in-a-bottom-sheet\} Para simplificar tu paywall, puedes ocultar algunos productos y mostrarlos solo cuando el usuario haga clic en un botón (como "Ver más planes" o la etiqueta que elijas). Esta acción abre una hoja deslizante desde abajo con los productos ocultos. Esta configuración ayuda a los usuarios a centrarse primero en tus opciones principales y, al mismo tiempo, les da flexibilidad para explorar planes adicionales si les interesa. Es una forma eficaz de simplificar el paywall y potencialmente aumentar las conversiones. Así se hace: 1. En el elemento **Products**, establece la opción **Products grouping** en **Bottom Sheet (for more alternative plans)**. Esto dividirá tus productos en dos grupos: **Shown** y **More plans**. <img src="/assets/shared/img/bottom-sheet.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Distribuye tus productos entre estos grupos. **Shown** es para los productos que quieres mostrar de inmediato. **More plans** es para los productos que están ocultos inicialmente y solo se muestran cuando el usuario hace clic en el botón. <img src="/assets/shared/img/bottom-sheet-divided-plans.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Personaliza el texto y el diseño del botón en el elemento **View More Plans** para adaptarlo a tu diseño y mensaje. <img src="/assets/shared/img/bottom-sheet-button.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. La hoja inferior usará automáticamente el mismo formato de visualización de la lista de productos que tu paywall principal, ya sean botones de compra independientes o cada producto actuando como un botón. Puedes personalizar el diseño, el texto, el estilo y la selección de producto predeterminada de la hoja inferior. <img src="/assets/shared/img/bottom-sheet-layout.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Estas opciones te ayudan a crear una lista de productos sencilla y fácil de usar. ## Añadir un grupo de productos \{#add-products-group\} Si quieres aplicar diseños tanto vertical como horizontal a diferentes productos o añadir texto entre productos, puedes añadir otro grupo de productos. :::note Añadir un grupo de productos desactiva la opción [Agrupación de productos](#switch-between-2-product-sets-by-trial-toggle). Elige entre añadir otro grupo de productos o agrupar los productos dentro del mismo bloque. ::: Para añadir un grupo de productos: 1. Haz clic en **Add element** o en **+** en el **Footer**. 2. Selecciona **Products**. 3. Añade productos. Como no puedes tener el mismo producto en grupos diferentes, primero debes eliminarlo del otro grupo. <img src="/assets/shared/img/product-group.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: paywall-buttons --- --- title: "Botón de paywall" description: "Personaliza los botones de paywall en Adapty para mejorar las interacciones con los usuarios y aumentar las conversiones." --- :::warning **Solo las compras y restauraciones se gestionan automáticamente.** Todas las demás acciones de los botones, como cerrar paywalls o abrir enlaces, requieren implementar las respuestas adecuadas en el código de la app: - [iOS](handle-paywall-actions) - [Android](android-handle-paywall-actions) - [React Native](react-native-handle-paywall-actions) - [Flutter](flutter-handle-paywall-actions) - [Unity](unity-handle-paywall-actions) ::: Un botón de paywall es un elemento de interfaz que permite a los usuarios: - Comprar productos - Iniciar sesión - Restaurar compras - Cerrar el paywall - Activar acciones personalizadas (por ejemplo, abrir otro paywall) :::info Esta sección describe el nuevo Paywall Builder, que funciona con: - SDKs de iOS, Android y React Native versión 3.0 o superior - SDKs de Flutter y Unity versión 3.3.0 o superior ::: ### Botones de compra \{#purchase-buttons\} Los botones de compra: - Se conectan a los productos seleccionados en tu paywall - Inician la compra al pulsarlos Cuando añades un botón de compra a tu paywall, este procesa automáticamente las compras que realizan tus usuarios. Por tanto, no necesitas gestionar las compras en el código de la app. :::note Puedes llamar más la atención sobre los botones de compra animándolos. El Paywall Builder admite actualmente los tipos de animación **Arrow** y **Pulse**. Ten en cuenta que, para añadir la animación **Arrow**, primero debes configurar el **Arrow icon** en la sección **Content**. Cada animación te permite elegir una opción de aceleración (Linear, Ease In, Ease Out, Ease In Out) para controlar cómo se acelera o desacelera. Las animaciones están disponibles en los SDKs de Adapty para iOS, Android, React Native y Flutter a partir de la versión 3.10.0. Consulta la [guía de migración](migration-to-android-310) para Android. ::: <img src="/assets/shared/img/purchase-button.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Enlaces \{#links\} Para cumplir con algunos requisitos de la store, puedes añadir enlaces a: - Términos de servicio - Política de privacidad - Restauración de compras Para añadir enlaces: 1. Añade un elemento **Link** en el Paywall Builder. 2. Añade el manejador `openUrl` a tu código: - [iOS](handle-paywall-actions) - [Android](android-handle-paywall-actions) - [React Native](react-native-handle-paywall-actions) - [Flutter](flutter-handle-paywall-actions) - [Unity](unity-handle-paywall-actions) <img src="/assets/shared/img/pb-links.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Botones personalizados \{#custom-buttons\} Los botones personalizados son necesarios para: - Cerrar el paywall (`close`) - Abrir una URL (`openUrl`) - Restaurar compras (`restore`) - Iniciar sesión (`login`) - Activar acciones personalizadas (por ejemplo, abrir otro paywall) Para que la mayoría de los botones funcionen, debes **gestionar sus IDs de acción en tu código**: - [iOS](handle-paywall-actions) - [Android](android-handle-paywall-actions) - [React Native](react-native-handle-paywall-actions) - [Flutter](flutter-handle-paywall-actions) - [Unity](unity-handle-paywall-actions) Por ejemplo, un botón de cerrar necesita el manejador de la acción `close`. :::important `close` se gestiona automáticamente en los SDKs de iOS, Android y React Native. `openUrl` se gestiona automáticamente en los SDKs de iOS y Android. Sin embargo, si es necesario, puedes sobreescribir el comportamiento predeterminado. `restore` siempre se gestiona automáticamente. ::: Al gestionar acciones personalizadas en tu código, puedes implementar escenarios como: - Abrir otro paywall - Ejecutar varias acciones en secuencia (como cerrar y abrir) Ten en cuenta que necesitarás construir estos escenarios usando el sistema de gestión de acciones; no son funciones integradas. <img src="/assets/shared/img/pb-custom-button.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: paywall-carousel --- --- title: "Carrusel de paywall" description: "Configura carruseles de paywall en Adapty para aumentar el engagement y las suscripciones." --- Un carrusel es un conjunto dinámico de tarjetas deslizables que se pueden mover hacia la izquierda o la derecha, creando una experiencia visual atractiva. Es una herramienta fantástica para diseñar paywalls que no solo llamen la atención, sino que también enganchen a los usuarios con contenido interactivo. <img src="/assets/shared/img/81eaa26-PB_carousel.gif" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning Los carruseles solo están disponibles en el [nuevo Paywall Builder](adapty-paywall-builder), que funciona con los SDKs de iOS, Android y React Native versión 3.0 o superior, y los SDKs de Flutter y Unity versión 3.3.0 o superior. El Paywall Builder heredado con el SDK de Adapty v2.x o anterior no admite la funcionalidad de carrusel de paywall. ::: Aprovecha el poder de los carruseles para elevar el atractivo de tu paywall: 1. **Añade tarjetas**: Empieza añadiendo tarjetas a tu carrusel; cada una es un lienzo para tu toque creativo. 2. **Personaliza las tarjetas**: Rellena tus tarjetas con distintos elementos: imágenes, texto, botones y más. El ajuste de altura del carrusel garantiza que todas las tarjetas tengan el mismo tamaño, dando un aspecto elegante y uniforme. Saca partido de la flexibilidad de los carruseles para mostrar productos destacados, resaltar ofertas exclusivas o contar historias convincentes. Con este elemento tan dinámico, tus paywalls no solo destacarán, sino que también ofrecerán una experiencia de usuario fluida e inmersiva. --- # File: paywall-card --- --- title: "Tarjeta de paywall" description: "Diseña e implementa tarjetas de paywall en Adapty para mejorar la participación." --- Una tarjeta es un elemento de paywall que combina varios otros elementos en un único bloque. La tarjeta en sí puede ser visible o no, según lo que necesites. Para hacerla visible, añádele un fondo o imagen de fondo, marco, etc. <img src="/assets/shared/img/16fd800-PB_card_example.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning Las tarjetas de paywall solo están disponibles en el [nuevo Paywall Builder](adapty-paywall-builder), que funciona con los SDKs de iOS, Android y React Native versión 3.0 o superior, y los SDKs de Flutter y Unity versión 3.3.0 o superior. El Paywall Builder heredado con el SDK de Adapty v2.x o anterior no admite la funcionalidad de tarjetas de paywall. ::: 1. Añade una tarjeta como elemento independiente a un paywall o a otro elemento de paywall, por ejemplo, a un carrusel. 2. Añade los elementos que necesites dentro de la tarjeta. 3. Configura la apariencia de la tarjeta: fondo, forma, marco, etc. <img src="/assets/shared/img/66d9877-PB_card.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: paywall-timer --- --- title: "Temporizador de paywall" description: "Usa la función de temporizador de paywall de Adapty para aumentar las conversiones y crear urgencia." --- El temporizador del paywall es una herramienta ideal para promocionar ofertas especiales y de temporada con límite de tiempo. Sin embargo, es importante tener en cuenta que este temporizador no está vinculado a la validez de la oferta ni a la duración de la campaña. Es simplemente una cuenta regresiva independiente que parte del valor que tú configures y disminuye hasta cero. Cuando el temporizador llega a cero, no ocurre nada: simplemente se queda en cero. <img src="/assets/shared/img/87de83a-Timer_withou_text.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning Los temporizadores de paywall solo están disponibles en el [nuevo Paywall Builder](adapty-paywall-builder), que funciona con los SDKs de iOS, Android y React Native versión 3.0 o superior, y los SDKs de Flutter y Unity versión 3.3.0 o superior. El Paywall Builder heredado con el SDK de Adapty v2.x o anterior no es compatible con la funcionalidad de temporizadores de paywall. ::: Puedes personalizar el texto antes y después del temporizador para crear el mensaje que quieras, por ejemplo: "La oferta termina en: 10:00 seg." <img src="/assets/shared/img/f1be626-timer_example.webp" style={{ border: 'none', /* border width and color */ width: '200px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 1. Añade un temporizador como elemento independiente en un paywall o dentro de otro elemento del paywall, como una tarjeta. 2. Configura los ajustes del temporizador: formato y separador, valor inicial, texto antes y después (si lo necesitas), color, fuente, espaciado, etc. <img src="/assets/shared/img/e83e891-timer.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Modo del temporizador \{#timer-mode\} Puedes controlar el comportamiento del temporizador cuando los usuarios lo ven mediante el parámetro **Timer mode**. Hay 3 modos estándar que funcionan sin configuración adicional: simplemente selecciona la opción que necesites en la lista desplegable: | Modo | Descripción | | ------------------------------------- | ------------------------------------------------------------ | | **Reset timer on every paywall view** | El temporizador se reinicia cada vez que el usuario ve el paywall, comenzando desde el valor inicial cada vez. | | **Reset timer on every app launch** | El temporizador comienza la primera vez que el usuario ve el paywall y sigue contando en primer plano o en segundo plano hasta que se reinicia la aplicación. Si el usuario ve el paywall varias veces en la misma sesión, verá el mismo temporizador en cuenta regresiva. Una vez que se cierra la aplicación, el temporizador se reinicia, y la próxima vez que se abra la aplicación, el temporizador vuelve a empezar desde el principio. | | **Keep timer across app launches** | El temporizador comienza la primera vez que el usuario ve el paywall y sigue contando en primer plano o en segundo plano, incluso si se cierra la aplicación. El usuario verá el mismo temporizador cada vez que vuelva al paywall, independientemente de los reinicios de la aplicación o del paywall. | | **Developer defined** | Puedes configurar cualquier temporizador que necesites directamente en el código de tu aplicación. Empieza introduciendo un **Timer ID** y luego úsalo en tu código tal como se explica en la sección [Cómo configurar temporizadores definidos por el desarrollador en tu aplicación](paywall-timer#how-to-set-up-developer-defined-timers-in-your-mobile-app) para configurar el temporizador como prefieras. | ## ¿Qué ocurre cuando termina el temporizador? \{#what-happens-when-the-timer-ends\} Puedes personalizar qué sucede cuando se agota el temporizador. ¿Mostrar otra pantalla con una nueva oportunidad? ¿O quizás un paywall diferente? Requiere algo de código, pero con nuestra documentación lo resolverás sin problema. 1. Activa el toggle **Trigger custom action when the timer runs out**. <img src="/assets/shared/img/timer-action-on.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Introduce el ID de la acción que quieres activar en el campo **Timer action ID**. <img src="/assets/shared/img/timer-action-id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Usa este ID de acción en tu app para definir qué debe ocurrir cuando el temporizador termine. Trátalo como cualquier otra acción personalizada, tal como se explica en nuestra guía **Manejo de Eventos: Acciones** para [iOS](ios-handling-events#user-generated-events) y [Android](android-handling-events#user-generated-events). ## ¿Cómo configurar temporizadores definidos por el desarrollador en tu app? \{#how-to-set-up-developer-defined-timers-in-your-mobile-app\} Para usar temporizadores personalizados en tu app, crea un objeto que siga el protocolo `AdaptyTimerResolver`. Este objeto define cómo debe renderizarse cada temporizador personalizado. Si lo prefieres, puedes usar directamente un diccionario `[String: Date]`, ya que ya cumple con este protocolo. Aquí tienes un ejemplo: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers @MainActor struct AdaptyTimerResolverImpl: AdaptyTimerResolver { func timerEndAtDate(for timerId: String) -> Date { switch timerId { case "CUSTOM_TIMER_6H": Date(timeIntervalSinceNow: 3600.0 * 6.0) // 6 hours case "CUSTOM_TIMER_NY": Calendar.current.date(from: DateComponents(year: 2025, month: 1, day: 1)) ?? Date(timeIntervalSinceNow: 3600.0) default: Date(timeIntervalSinceNow: 3600.0) // 1 hour } } } ``` En este ejemplo, `CUSTOM_TIMER_NY` y `CUSTOM_TIMER_6H` son los **Timer ID**s de los temporizadores definidos por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente cada temporizador con el valor correcto. Por ejemplo: - `CUSTOM_TIMER_NY`: El tiempo restante hasta el final del temporizador, como el día de Año Nuevo. - `CUSTOM_TIMER_6H`: El tiempo restante en un período de 6 horas que comenzó cuando el usuario abrió el paywall. </TabItem> <TabItem value="kotlin" label="Kotlin" default> ```kotlin showLineNumbers ... val customTimers = mapOf( "CUSTOM_TIMER_NY" to Calendar.getInstance(TimeZone.getDefault()).apply { set(2025, 0, 1) }.time, // Año Nuevo 2025 ) val timerResolver = AdaptyUiTimerResolver { timerId -> customTimers.getOrElse(timerId, { Date(System.currentTimeMillis() + 3600 * 1000L) /* en 1 hora */ } ) } ``` En este ejemplo, `CUSTOM_TIMER_NY` es el **Timer ID** del temporizador definido por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu aplicación actualice dinámicamente el temporizador con el valor correcto, como `13d 09h 03m 34s` (calculado como la hora de finalización del temporizador, por ejemplo, el día de Año Nuevo, menos la hora actual). </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers ... Map<String, Date> customTimers = new HashMap<>(); customTimers.put( "CUSTOM_TIMER_NY", new Calendar.Builder().setTimeZone(TimeZone.getDefault()).setDate(2025, 0, 1).build().getTime() ); AdaptyUiTimerResolver timerResolver = new AdaptyUiTimerResolver() { @NonNull @Override public Date timerEndAtDate(@NonNull String timerId) { Date date = customTimers.get(timerId); return date != null ? date : new Date(System.currentTimeMillis() + 3600 * 1000L); /* en 1 hora */ } }; ``` En este ejemplo, `CUSTOM_TIMER_NY` es el **Timer ID** del temporizador definido por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente el temporizador con el valor correcto, como `13d 09h 03m 34s` (calculado como la hora de fin del temporizador, por ejemplo el Año Nuevo, menos la hora actual). </TabItem> <TabItem value="flutter" label="Flutter" default> ```dart showLineNumbers try { final view = await AdaptyUI().createPaywallView( paywall: paywall, customTags: ..., customTimers: { 'CUSTOM_TIMER_6H': DateTime.now().add(const Duration(seconds: 3600 * 6)), 'CUSTOM_TIMER_NY': DateTime(2025, 1, 1), // New Year 2025 }, preloadProducts: ..., ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` En este ejemplo, `CUSTOM_TIMER_NY` y `CUSTOM_TIMER_6H` son los **Timer ID**s de los temporizadores definidos por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu aplicación actualice dinámicamente cada temporizador con el valor correcto. Por ejemplo: - `CUSTOM_TIMER_NY`: El tiempo restante hasta el final del temporizador, como el Año Nuevo. - `CUSTOM_TIMER_6H`: El tiempo restante en un período de 6 horas que comenzó cuando el usuario abrió el paywall. </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers var parameters = new AdaptyUICreateViewParameters() .SetCustomTimers( new Dictionary<string, DateTime> { { "CUSTOM_TIMER_6H", DateTime.Now.AddHours(6) }, // 6 horas { "CUSTOM_TIMER_NY", new DateTime(2025, 1, 1) } // Año Nuevo 2025 } ) AdaptyUI.CreateView(paywall, parameters, (view, error) => { // manejar el resultado }); ``` - En este ejemplo, `CUSTOM_TIMER_NY` y `CUSTOM_TIMER_6H` son los **Timer ID**s de los temporizadores definidos por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente cada temporizador con el valor correcto. Por ejemplo: - `CUSTOM_TIMER_NY`: El tiempo restante hasta el fin del temporizador, como el día de Año Nuevo. - `CUSTOM_TIMER_6H`: El tiempo restante en un período de 6 horas que comenzó cuando el usuario abrió el paywall. </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers let customTimers = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) } //and then you can pass it to createPaywallView as follows: view = await createPaywallView(paywall, { customTimers }) ``` En este ejemplo, `CUSTOM_TIMER_NY` es el **Timer ID** del temporizador definido por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente el temporizador con el valor correcto, como `13d 09h 03m 34s` (calculado como el tiempo de finalización del temporizador, por ejemplo el Año Nuevo, menos el tiempo actual). </TabItem> </Tabs> --- # File: paywall-dark-mode --- --- title: "Modo oscuro en paywalls" description: "Activa el modo oscuro en paywalls de Adapty para mejorar la experiencia del usuario." --- El modo oscuro se ha convertido en una función esencial para mejorar la experiencia del usuario en aplicaciones móviles, especialmente para quienes pasan mucho tiempo interactuando con el contenido. La mayoría de las apps que admiten el modo oscuro también optan por tener dos versiones del paywall (clara y oscura) para mantener la coherencia, y Adapty permite hacer exactamente eso. Cuando el modo oscuro está activado, el paywall se adapta automáticamente al modo actual del dispositivo: muestra la versión clara si el dispositivo está en modo claro, y la versión oscura si está en modo oscuro. <img src="/assets/shared/img/darkmode.webp" style={{ border: 'none', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning El modo oscuro es compatible con: - iOS: a partir de la v3.1.0 - Android: a partir de la v3.1.1 - React Native: a partir de la v3.1.0 - Flutter: a partir de la v3.2.0 - Unity: a partir de la v3.3.0 También está disponible en paywalls de respaldo. ::: Para configurar el modo oscuro en tu paywall: 1. Primero, activa el modo oscuro en **Layout settings** del paywall: <img src="/assets/shared/img/dark-mode.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Ahora puedes configurar los modos claro y oscuro por separado. Para cambiar al modo oscuro, activa el interruptor **Dark Mode** en el menú izquierdo del paywall: <img src="/assets/shared/img/dark-mode-switch.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Una vez que hayas cambiado al modo oscuro, puedes ajustar los elementos según sea necesario. El modo oscuro te permite usar una imagen o vídeo diferente, así como opciones de color y fondo independientes. <img src="/assets/shared/img/dark-mode-done.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: paywall-builder-tag-variables --- --- title: "Variables de etiqueta para información de productos en Paywall Builder" description: "Usa variables de etiqueta en el Paywall Builder de Adapty para personalizar la experiencia del usuario y aumentar las ventas." --- El Paywall Builder de Adapty te permite personalizar todo el texto de tus productos y sus ofertas. Si trabajas con varios idiomas, te recomendamos encarecidamente usar variables. ### Cómo funciona \{#how-it-works\} Cuando añades variables de etiqueta de nuestra lista a los textos de tus productos, el SDK obtiene los datos localizados ya precargados de los stores para reemplazar las etiquetas. Esto garantiza que el texto de tu paywall siempre esté perfectamente adaptado al idioma correcto. **Ejemplo**: Supongamos que tienes una "Suscripción Premium" disponible tanto en EE. UU. como en España. En EE. UU. podría mostrarse como "Premium Subscription for $4.99/month", mientras que en España aparecería como "Suscripción Premium por €4,99/mes". Las variables de etiqueta te permiten localizar automáticamente estas cadenas de texto a partir de los datos del store, garantizando que los títulos y precios sean siempre correctos. :::warning Esta sección describe el nuevo Paywall Builder, que funciona con los SDKs de iOS, Android y React Native en la versión 3.0 o superior, y con los SDKs de Flutter y Unity en la versión 3.3.0 o superior. Para información sobre el Paywall Builder legacy compatible con el SDK de Adapty v2.x o anterior, consulta [Variables de etiqueta en el Paywall Builder legacy](paywall-builder-tag-variables-legacy). ::: ### Cómo usar las variables de etiqueta \{#how-to-use-tag-variables\} :::note Las variables de etiqueta solo se pueden usar al describir productos y ofertas en el componente Product del Paywall Builder. ::: 1. En el panel izquierdo del Paywall Builder, selecciona el producto que quieres personalizar. 2. Usa las variables de la [tabla a continuación](paywall-builder-tag-variables#full-list-of-variables) en cualquier campo de texto para describir el producto y sus ofertas. <img src="/assets/shared/img/0351661-tag_variables.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Comprueba la vista previa en el lado derecho de la pantalla para asegurarte de que todo se muestra correctamente. :::note La vista previa no usa valores reales para reemplazar las variables; estos solo los recupera el SDK en el dispositivo. Sin embargo, sí muestra datos de plantilla en el mismo formato que el resultado real. Puedes desactivar este comportamiento haciendo clic en el icono del ojo en la esquina inferior derecha de la vista previa y desactivando el toggle **Tags preview values**. La vista previa mostrará entonces los valores reales de las variables: <img src="/assets/shared/img/da92d39-tags_preview_values.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ::: ### Lista completa de variables \{#full-list-of-variables\} | Variable de etiqueta | Descripción | Ejemplo | | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | | `<PROD_TITLE/>` | Título localizado del producto | Premium Subscription | | `<PROD_PRICE/>` | Precio localizado del producto. En suscripciones, este es el precio por un período de facturación. | $9.99 | | `<PROD_PRICE_PER_DAY/>` | Precio de la suscripción dividido entre el número de días del período de facturación. **Devuelve una cadena vacía para los productos que no son suscripciones.** | $0.33 | | `<PROD_PRICE_PER_WEEK/>` | Precio de la suscripción dividido entre el número de semanas del período de facturación. **Devuelve una cadena vacía para los productos que no son suscripciones.** | $2.33 | | `<PROD_PRICE_PER_MONTH/>` | Precio de la suscripción dividido entre el número de meses del período de facturación. Si el período de facturación es inferior a un mes, se ajusta para representar un mes completo. **Devuelve una cadena vacía para los productos que no son suscripciones.** | $9.99 | | `<PROD_PRICE_PER_YEAR/>` | Precio de la suscripción ajustado para representar un año completo de uso. Por ejemplo, el precio de una suscripción mensual se multiplicaría por 12, mientras que el precio anual permanece igual. **Devuelve una cadena vacía para los productos que no son suscripciones.** | $119.88 | | `<OFFER_PRICE/>` | Precio localizado de una oferta (introductoria o promocional). **Aplicable solo a suscripciones de renovación automática; devuelve una cadena vacía si el usuario no es elegible para ninguna oferta.** | $0.99 | | `<OFFER_BILLING_PERIOD/>` | Período de facturación localizado de una oferta (introductoria o promocional). Igual que `<OFFER_FULL_DURATION/>` para ofertas de prueba gratuita y pago anticipado. **Aplicable solo a suscripciones de renovación automática; devuelve una cadena vacía si el usuario no es elegible para ninguna oferta.** | 1 week | | `<OFFER_FULL_DURATION/>` | Duración total localizada de una oferta (introductoria o promocional). **Aplicable solo a suscripciones de renovación automática; devuelve una cadena vacía si el usuario no es elegible para ninguna oferta.** | 1 month | ### Etiquetas de oferta para distintos tipos de oferta \{#offer-tags-for-different-offer-types\} :::note Puedes aprender más sobre las ofertas y cómo configurarlas en Adapty [aquí](offers) ::: Entender las etiquetas de oferta puede resultar algo confuso, así que veámoslo con un ejemplo. Supongamos que tienes una suscripción semanal llamada "Premium Subscription" por $5, con tres posibles ofertas: - **Pay As You Go**. Las primeras 3 semanas por $3 (facturadas semanalmente), y luego $5/semana. - **Pay Up Front**. Las primeras 3 semanas por $8 (facturadas inmediatamente), y luego $5/semana. - **Free Trial**. La primera semana gratis, y luego $5/semana. Para este producto, `<PROD_TITLE/>` sería "Premium Subscription" y `<PROD_PRICE/>` sería $5. Sin embargo, los valores de las etiquetas de oferta —dependiendo de cuál sea elegible para el usuario— serían: | Variable de etiqueta | Pay As You Go | Pay Upfront | Free Trial | | :------------------------ | :------------ | :---------- | :--------- | | `<OFFER_PRICE/>` | $3 | $8 | $0 | | `<OFFER_BILLING_PERIOD/>` | 1 week | 3 weeks | 1 week | | `<OFFER_FULL_DURATION/>` | 3 weeks | 3 weeks | 1 week | Así que para los tipos de oferta distintos de "Pay As You Go", `<OFFER_BILLING_PERIOD/>` y `<OFFER_FULL_DURATION/>` serán iguales. --- # File: custom-tags-in-paywall-builder --- --- title: "Etiquetas personalizadas en Paywall Builder" description: "Aprende a usar etiquetas personalizadas en el Paywall Builder de Adapty para personalizar la experiencia del usuario y optimizar las conversiones." --- Las etiquetas personalizadas te permiten evitar crear paywalls separados para distintos escenarios. Imagina un único paywall que se adapta dinámicamente según los datos del usuario. Por ejemplo, en lugar de un genérico "¡Hola!", podrías saludar a los usuarios de forma personal con "¡Hola, Juan!" o "¡Hola, Ana!" :::warning Esta sección describe el nuevo Paywall Builder, compatible con los SDKs de iOS, Android y React Native versión 3.0 o superior, y con los SDKs de Flutter y Unity versión 3.3.0 o superior. Para información sobre el Paywall Builder heredado compatible con el SDK de Adapty v2.x o anterior, consulta [Etiquetas personalizadas en el Paywall Builder heredado](custom-tags-in-legacy-paywall-builder). ::: Aquí tienes algunas formas de usar las etiquetas personalizadas: - Mostrar el nombre o el correo electrónico del usuario en el paywall. - Mostrar el día de la semana actual para impulsar las ventas (p. ej., "Feliz jueves"). - Añadir detalles personalizados sobre los productos que vendes (como el nombre de un programa de fitness o un número de teléfono en una app VoIP). Las etiquetas personalizadas te ayudan a crear un paywall flexible que se adapta a distintas situaciones, haciendo que la interfaz de tu app sea más personalizada y atractiva. :::warning Asegúrate de añadir textos de respaldo para cada línea con etiquetas personalizadas. Recuerda incluir textos de respaldo para cada línea que contenga etiquetas personalizadas. En algunos casos, tu app puede no saber con qué reemplazar una etiqueta personalizada, especialmente si los usuarios tienen una versión antigua del SDK de AdaptyUI. Para evitarlo, añade siempre un texto de respaldo que sustituya las líneas que contengan etiquetas desconocidas. Sin esto, los usuarios podrían ver las etiquetas mostradas como código (`<USERNAME/>`). ::: ## Cómo añadir una etiqueta personalizada a un paywall \{#how-to-add-a-custom-tag-to-a-paywall\} Puedes añadir una o más etiquetas personalizadas a cualquier línea de texto en el Paywall Builder. Para añadir una etiqueta personalizada: 1. Introduce la etiqueta personalizada con el formato `<CUSTOM_TAG/>` o escribe un corchete angular de apertura (\<) en la línea de texto. El sistema sugerirá la etiqueta en el formato correcto. Algunos aspectos a tener en cuenta: - En el Paywall Builder de Adapty, las etiquetas personalizadas van entre corchetes angulares (`<CUSTOM_TAG/>`), pero en el código de tu app deben referenciarse directamente (CUSTOM_TAG). - Las etiquetas personalizadas distinguen entre mayúsculas y minúsculas. - Las etiquetas personalizadas no pueden coincidir con ninguna de las [variables de etiqueta](paywall-builder-tag-variables) reservadas para información de productos en Adapty. <img src="/assets/shared/img/1ea0b95-adding_custom_tag.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Después de añadir la etiqueta personalizada, asegúrate de introducir un texto de respaldo. Este texto aparecerá en tu app si no reconoce una etiqueta personalizada concreta, evitando que los usuarios vean la etiqueta mostrada como código. El texto de respaldo reemplaza toda la línea que contiene la etiqueta personalizada. <img src="/assets/shared/img/4d43874-custom_tag_fallback.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Cómo usar etiquetas personalizadas en tu app \{#how-to-use-custom-tags-in-your-mobile-app\} Para usar etiquetas personalizadas en tu app, crea un objeto `tagResolver` —un diccionario o mapa que asocia etiquetas personalizadas con los valores de cadena que las sustituirán cuando se renderice el paywall. Aquí tienes un ejemplo: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers let tagResolver = [ "USERNAME": "John", ] let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall, tagResolver: tagResolver // or any other AdaptyTagResolver protocol implementation ) ``` </TabItem> <TabItem value="kotlin" label="Kotlin" default> ```kotlin showLineNumbers val customTags = mapOf("USERNAME" to "John") val tagResolver = AdaptyUiTagResolver { tag -> customTags[tag] } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers Map<String, String> customTags = new HashMap<>(); customTags.put("USERNAME", "John"); AdaptyUiTagResolver tagResolver = customTags::get; ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform" default> ```kotlin showLineNumbers val customTags = mapOf( "USERNAME" to "John", "CUSTOM_TAG_NAME" to "Walter White", "CUSTOM_TAG_PHONE" to "+1 234 567890" ) val view = AdaptyUI.createPaywallView( paywall = paywall, customTags = customTags ) ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```dart showLineNumbers final customTags = { 'USERNAME': 'John', }; try { final view = await AdaptyUI().createPaywallView( paywall: paywall, customTags: customTags, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers var parameters = new AdaptyUICreateViewParameters() .SetCustomTags( new Dictionary<string, string> { { "CUSTOM_TAG_NAME", "John Appleseed" } } ) AdaptyUI.CreateView(paywall, parameters, (view, error) => { // handle the result }); ``` </TabItem> <TabItem value="rn" label="React Native" default> ```typescript showLineNumbers let customTags: Record<string, string> = { "USERNAME": "John" } //and then you can pass it to createPaywallView as follows: view = await createPaywallView(paywall, { customTags }) ``` </TabItem> </Tabs> En este ejemplo, `USERNAME` es una etiqueta personalizada que introduciste en el dashboard de Adapty como `<USERNAME/>`. El `tagResolver` garantiza que tu app reemplace dinámicamente esta etiqueta personalizada por el valor especificado, como `John`. Te recomendamos crear y rellenar el `tagResolver` justo antes de presentar tu paywall. Una vez listo, pásalo al método de AdaptyUI que uses para mostrar el paywall. Puedes leer más sobre cómo presentar paywalls en [iOS](ios-present-paywalls), [Android](android-present-paywalls), [React Native](react-native-present-paywalls), [Flutter](flutter-present-paywalls) o [Unity](unity-present-paywalls). --- # File: custom-media --- --- title: "Imágenes y vídeos personalizados en el nuevo Paywall Builder" description: "" --- Puedes añadir cualquier imagen o vídeo a un paywall, pero a veces querrás mostrar contenido multimedia personalizado para el usuario — por ejemplo, un vídeo con su avatar elegido. Para eso está el contenido multimedia personalizado. El contenido multimedia personalizado es una imagen o vídeo que tu app llama por ID desde tu código. Reemplaza un archivo multimedia estándar que hayas añadido al paywall en el Paywall Builder. :::info Para usar esta función, actualiza el SDK de Adapty a la versión 3.7.0 o posterior. ::: ## ¿Dónde puedo usar contenido multimedia personalizado? \{#where-can-i-use-custom-media\} En cualquier lugar donde normalmente usarías una imagen o vídeo normal: - Como imagen principal - Como vídeo principal - Como icono normal - En el fondo de una tarjeta - En el fondo de un carrusel ## ¿Cómo se usa el contenido multimedia personalizado? \{#how-to-use-custom-media\} Para configurar contenido multimedia personalizado: 1. Marca la casilla **Use custom media ID** bajo el área de carga. 2. Introduce el ID del medio. Para imágenes principales y vídeos principales, los IDs están predefinidos. 3. Sube una imagen o vídeo de respaldo en la sección de carga de archivos. 4. Para más información, consulta <InlineTooltip tooltip="las guías sobre cómo obtener paywalls del Paywall Builder en tu app">[iOS](get-pb-paywalls), [Android](android-get-pb-paywalls), [React Native](react-native-get-pb-paywalls), [Flutter](flutter-get-pb-paywalls) y [Unity](unity-get-pb-paywalls)</InlineTooltip> y llama al medio por su ID en tu código. Si la versión del SDK de Adapty es inferior a 3.7.0, o si el ID de medio personalizado no está definido en tu código, se mostrará en su lugar la imagen o vídeo de respaldo. <img src="/assets/shared/img/custom-media.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: using-custom-fonts-in-paywall-builder --- --- title: "Fuentes personalizadas en Paywall Builder" description: "Mejora el Paywall Builder de Adapty con fuentes personalizadas para optimizar el diseño." --- La coherencia visual es clave para un buen diseño. Al crear paywalls sin código, puede que quieras usar una fuente personalizada que encaje con el resto de tu app. Aquí explicamos cómo personalizar fuentes y cómo puedes usarlas. :::warning Esta sección describe el nuevo Paywall Builder, que funciona con los SDKs de iOS, Android y React Native versión 3.0 o superior, y los SDKs de Flutter y Unity versión 3.3.0 o superior. Para información sobre el Paywall Builder heredado compatible con el SDK de Adapty v2.x o anterior, consulta [Fuentes personalizadas en el Paywall Builder heredado](using-custom-fonts-in-legacy-paywall-builder). ::: ## Qué se puede personalizar \{#what-can-be-customized\} Cada elemento de texto en el Paywall Builder puede tener su propia fuente y estilo. Puedes ajustarlo en los controles de fuente de cada elemento de texto: <img src="/assets/shared/img/56a8845-choose_font.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En algunos casos, es más práctico cambiar la fuente de todo el paywall. Puedes hacerlo en la sección Layout del Paywall Builder [ajustando la Fuente del Paywall](paywall-layout-and-products#font-settings-of-your-paywall). ## Fuentes disponibles por defecto \{#fonts-available-by-default\} Cuando creas un paywall en el Builder, Adapty usa una fuente del sistema por defecto. Esto normalmente significa SF Pro en iOS y Roboto en Android, aunque puede variar según el dispositivo. También puedes elegir entre fuentes de uso común como Arial, Times New Roman, Courier New, Georgia, Palatino y Verdana. Cada una de estas fuentes incluye varias opciones de estilo: <img src="/assets/shared/img/f9d87dc-default_fonts.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::note Estas fuentes no se incluyen como parte del SDK de Adapty y solo se usan con fines de previsualización. No podemos garantizar que funcionen perfectamente en todos los dispositivos. Sin embargo, en nuestras pruebas, la mayoría de los dispositivos las reconocen sin ningún esfuerzo adicional por tu parte. También puedes [consultar qué fuentes están disponibles por defecto en iOS](https://developer.apple.com/fonts/system-fonts/). ::: ## Cómo añadir una fuente personalizada al Adapty Dashboard \{#how-to-add-a-custom-font-to-the-adapty-dashboard\} Si necesitas más opciones de las que ofrece la configuración por defecto, puedes añadir una fuente personalizada. Una vez añadida, estará disponible en toda la app y podrás usarla en cualquier línea de texto de cualquier paywall. 1. Elige **Add custom font** en cualquiera de los desplegables de fuente: <img src="/assets/shared/img/7498a5a-add_custom_font.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la ventana **Add Custom Font**: 1. Sube el archivo de fuente (tamaño máximo: 10 MB). 2. Introduce un nombre para referenciarlo en el Paywall Builder. 3. Especifica los nombres de fuente correctos para ambas plataformas. 4. Asegúrate de que el archivo de fuente esté incluido en el bundle de tu app si aún no lo has añadido. <img src="/assets/shared/img/89fb748-CleanShot_2024-02-07_at_13.21.552x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning El archivo de fuente que subes no se envía al dispositivo; solo se usa con fines de previsualización. Nuestro SDK recibe únicamente las cadenas de texto que referencian la fuente a usar al renderizar el paywall. Por eso, debes incluir el mismo archivo de fuente en el bundle de la app y proporcionar los nombres de fuente correctos para cada plataforma para que todo funcione sin problemas. No te preocupes, no llevará mucho tiempo. ::: :::note Al subir el archivo de fuente a Adapty, confirmas que tienes derecho a usarla en tu app. ::: ## Cómo obtener el nombre de fuente correcto en iOS \{#getting-the-correct-font-name-on-ios\} Hay dos formas de obtener el ID correcto de una fuente: la primera requiere algo de código, y la segunda consiste en usar una app llamada "Font Book", disponible en macOS. Si ya has añadido una fuente personalizada al bundle de tu app, probablemente ya la estás referenciando por su nombre de fuente. Para confirmarlo, llama a `UIFont.familyNames()` para obtener el nombre de familia de la fuente y luego úsalo en `UIFont.fontNames(forFamilyName: familyName)`. Puedes hacerlo en `viewDidLoad` y después eliminar el fragmento de código: ```swift showLineNumbers title="Swift" override func viewDidLoad() { super.viewDidLoad() ... for family in UIFont.familyNames.sorted() { print("Family: \(family)") let names = UIFont.fontNames(forFamilyName: family) for fontName in names { print("- \(fontName)") } } } ``` El `fontName` del fragmento anterior es lo que buscas. Podría tener un aspecto similar a "MyFont-Regular". El segundo método es más sencillo: instala la fuente en tu Mac, abre la app **Font Book**, encuentra la fuente y usa su `PostScript name`: <img src="/assets/shared/img/bb8a902-CleanShot_2024-01-12_at_20.32.222x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Cómo obtener el nombre de fuente correcto en Android \{#getting-the-correct-font-name-on-android\} Si has añadido correctamente el archivo de fuente a la carpeta de recursos, simplemente proporciona el nombre del archivo. Asegúrate de que esté en minúsculas y solo contenga letras, números y guiones bajos; de lo contrario, puede que no funcione. Puedes confirmar que el nombre del archivo es correcto llamando a `ResourcesCompat.getFont(context, R.font.my_font)`, donde `my_font` es el nombre de archivo que estás usando. En este caso, `my_font` es exactamente lo que debes introducir al crear una fuente personalizada en Adapty. ## Cómo añadir los archivos de fuente al bundle de tu app \{#adding-the-font-files-to-your-apps-bundle\} Si ya estás usando una fuente personalizada en otra parte de tu app, solo tienes que añadir las fuentes del paywall de la misma manera. Si no es así, asegúrate de incluir el archivo de fuente en el proyecto y bundle de tu app. Aquí puedes leer cómo hacerlo: - En iOS: [En la documentación oficial de Apple](https://developer.apple.com/documentation/uikit/adding-a-custom-font-to-your-app) - En Android: [En la documentación oficial de Android](https://developer.android.com/develop/ui/views/text-and-emoji/fonts-in-xml) :::important Al descargar paquetes de fuentes desde Adapty, recibirás todas las variaciones de fuente en un archivo comprimido. Añade al bundle de tu app solo los archivos de fuente específicos que usa tu paywall para minimizar el tamaño de la app. Por ejemplo, si en tu paywall solo usas `OpenSans-Regular.ttf`, no incluyas `OpenSans-Bold.ttf` en el bundle de tu app. ::: --- # File: migration-to-new-paywall-builder --- --- title: "Migración al nuevo Paywall Builder" description: "Migra al nuevo Paywall Builder de Adapty para mejorar el flujo de suscripción." --- Estamos encantados de presentar el [**nuevo Paywall Builder**](adapty-paywall-builder). Esta avanzada herramienta sin código está diseñada para hacer que la creación de paywalls personalizados sea más intuitiva y potente que nunca, permitiéndote diseñar paywalls atractivos y visualmente cuidados con total facilidad. ¡No necesitas conocimientos técnicos ni de diseño! ## Características clave del nuevo Paywall Builder \{#key-features-of-the-new-paywall-builder\} - **Amplia selección de plantillas**: Elige entre una gran variedad de plantillas diseñadas profesionalmente para comenzar a crear tu paywall. Estas plantillas ofrecen distintos estilos y disposiciones para adaptarse a diferentes necesidades y preferencias. - **Mayor flexibilidad**: Disfruta de más libertad creativa gracias al uso de capas de diseño y nuevos elementos como carruseles, tarjetas, listas de productos y pies de página. Estas mejoras te dan la libertad creativa para construir cualquier tipo de paywall que tengas en mente. - **Elementos existentes mejorados**: Los elementos existentes han sido significativamente mejorados, ofreciendo más opciones y capacidades para dar vida a tus ideas de paywall. ## Versiones paralelas del Paywall Builder \{#parallel-paywall-builder-versions\} Adapty ofrece dos versiones del Paywall Builder de forma simultánea: - **Nuevo Paywall Builder**: Ubicado en la pestaña **Builder & Generator** del paywall en el Adapty Dashboard, esta versión es la más reciente y versátil. Los paywalls creados aquí requieren los SDK de iOS, Android y React Native en la versión 3.0 o superior, y los SDK de Flutter y Unity en la versión 3.3.0 o superior. - **Legacy Paywall Builder**: Disponible en la pestaña **Legacy Builder**, esta versión obsoleta solo debe usarse para dar soporte a versiones antiguas de la app con SDK inferiores a v3.x.x. Recomendamos no usarla para nuevos paywalls, ya que pronto quedará en desuso. ## Migrar paywalls al nuevo builder \{#migrating-paywalls-to-the-new-builder\} Migrar un paywall del builder antiguo al nuevo creará una versión nueva de tu paywall en la pestaña **Builder & Generator**. Esta versión se puede editar con el nuevo Paywall Builder y se mostrará en apps con el SDK de Adapty v3.0 o posterior. La versión antigua permanece en la pestaña **Legacy Builder** y es compatible con apps con SDK 2.x o anterior. Mantendrás los paywalls en ambos formatos por separado, y los cambios en un formato no afectarán al otro. ## Pasos para migrar un paywall \{#steps-to-migrate-a-paywall\} Para migrar un paywall al nuevo Paywall Builder: 1. Abre el paywall que quieres migrar. 2. Ve a la pestaña **Builder & Generator**. 3. Haz clic en **Migrate paywall**. <img src="/assets/shared/img/migrate-paywall.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Una vez completada la migración, revisa el resultado y asegúrate de que el paywall tiene el aspecto esperado. Si no es así, corrígelo. 5. Haz clic en **Save**. 6. Si hay algún problema, aparecerá resaltado en rojo para que lo veas de inmediato. Corrígelo y guarda el paywall de nuevo. <img src="/assets/shared/img/migration-issues.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Puedes migrar tus paywalls de uno en uno para revisarlos y corregirlos según sea necesario. :::info Ten en cuenta que los paywalls creados en el nuevo Paywall Builder solo aparecerán en versiones de la app con el SDK de Adapty v3.0 o posterior. ::: ## Valoramos tu opinión \{#we-value-your-feedback\} Tu opinión es muy importante para nosotros. Si encuentras algún problema o tienes sugerencias de mejora, no dudes en contactarnos. Estamos aquí para ayudarte y mejorar tu experiencia con el nuevo Paywall Builder. 📧 **Contáctanos**: [Soporte de Adapty](mailto:support@adapty.io) ¡Disfruta creando con el nuevo Paywall Builder y lleva tu estrategia de monetización al siguiente nivel con nuestras herramientas y funciones mejoradas! --- # File: customize-paywall-with-remote-config --- --- title: "Diseña tu paywall con Remote Config" description: "Personaliza tu paywall con Remote Config en Adapty para una mejor segmentación." --- El Remote Config de un paywall es una herramienta muy potente que ofrece opciones de configuración flexibles. Permite usar payloads JSON personalizados para adaptar tus paywalls con precisión. Con él puedes definir parámetros como títulos, imágenes, fuentes, colores y mucho más. <details> <summary>Antes de empezar a personalizar un paywall (haz clic para expandir)</summary> 1. [Crea un producto](create-product). 2. [Crea un paywall y añade el producto](create-paywall). </details> Para empezar a personalizar un paywall con Remote Config: 1. Abre la sección [**Paywalls**](https://app.adapty.io/paywalls) en el menú principal de Adapty. 2. Haz clic en el paywall para abrirlo. <img src="/assets/shared/img/remote-config.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Cambia a la pestaña **Remote config**. <img src="/assets/shared/img/remote-config-3.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El Remote Config tiene 2 vistas: - [Tabla](customize-paywall-with-remote-config#table-view-of-the-remote-config) - [JSON](customize-paywall-with-remote-config#json-view-of-the-remote-config) Tanto la vista **Table** como la vista **JSON** incluyen los mismos elementos de configuración. La única diferencia es una cuestión de preferencia: la vista de tabla ofrece un menú contextual que puede ser útil para corregir errores de localización. Puedes alternar entre vistas haciendo clic en la pestaña **Table** o **JSON** cuando lo necesites. Sea cual sea la vista que elijas para personalizar tu paywall, podrás acceder a estos datos desde el SDK usando las propiedades `remoteConfig` o `remoteConfigString` de `AdaptyPaywall` y hacer ajustes en tu paywall. También puedes actualizar los valores del Remote Config de forma programática mediante la [API del servidor](api-adapty/operations/updatePaywall) para modificar dinámicamente las configuraciones del paywall sin tener que actualizar el dashboard manualmente. Aquí tienes algunos ejemplos de cómo puedes usar un Remote Config. <Tabs groupId="current-os" queryString> <TabItem value="Titles" label="Títulos" default> ```json showLineNumbers { "screen_title": "Today only: Subscribe, and get 7 days for free!" } # Test titles or others texts ``` </TabItem> <TabItem value="Images" label="Imágenes" default> ```json showLineNumbers { "background_image": "https://adapty.io/media/paywalls/bg1.webp" } # Test images on your paywall ``` </TabItem> <TabItem value="Fonts" label="Fuentes" default> ```json showLineNumbers { "font_family": "San Francisco", "font_size": 16 } # Test fonts ``` </TabItem> <TabItem value="Color" label="Color" default> ```json showLineNumbers { "subscribe_button_color": "purple" } # Test colors of buttons, texts etc. ``` </TabItem> <TabItem value="HTML" label="HTML" default> ```json showLineNumbers { "photo_gallery": "https://adapty.io/media/paywalls/link-to-html-snippet.html" } # Any HTML code that can be displayed on the paywall ``` </TabItem> <TabItem value="Soft/Hard Paywall" label="Paywall Soft/Hard" default> ```json showLineNumbers { "hard_paywall": true } # By setting it to true, you disalow skipping paywall without subscribing # You have to handle this logic in your app ``` </TabItem> <TabItem value="Translations" label="Traducciones" default> ```json showLineNumbers { "title": { "en": "Try for free!", "es": "¡Prueba gratis!", "ru": "Попробуй бесплатно!" } } ``` </TabItem> </Tabs> Puedes combinar distintas opciones y crear las tuyas propias. Así podrás probar diferentes títulos, textos, imágenes, fuentes, colores, etc. ### Vista JSON del Remote Config \{#json-view-of-the-remote-config\} En la vista **JSON** del Remote Config puedes introducir cualquier dato con formato JSON: <img src="/assets/shared/img/3356ff5-remote_config_JSON.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Vista de tabla del Remote Config \{#table-view-of-the-remote-config\} Si no estás acostumbrado a trabajar con código y necesitas corregir algunos valores del JSON, Adapty tiene la vista **Table** para ti. <img src="/assets/shared/img/4c27b2f-remote_config_table.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Es una copia de tu JSON en formato de tabla, fácil de leer y entender. El código de colores ayuda a identificar los distintos tipos de datos. Para añadir una clave, haz clic en el botón **Add row**. Verificamos automáticamente la correspondencia entre valores y tipos, y mostramos una alerta si tus cambios pueden generar un JSON inválido. <img src="/assets/shared/img/ef682d8-add_raw.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Las opciones adicionales de fila son especialmente útiles para las [localizaciones del paywall](add-remote-config-locale): <img src="/assets/shared/img/17bcf80-remote_config_table_options.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Ahora es el momento de [crear un placement](create-placement) y añadir el paywall. Después podrás <InlineTooltip tooltip="mostrar tus paywalls de Remote Config">[iOS](present-remote-config-paywalls), [Android](present-remote-config-paywalls-android), [React Native](present-remote-config-paywalls-react-native), [Flutter](present-remote-config-paywalls-flutter), y [Unity](present-remote-config-paywalls-unity)</InlineTooltip> en tu app. --- # File: add-paywall-locale-in-adapty-paywall-builder --- --- title: "Añadir idioma al paywall en Adapty Paywall Builder" description: "Añade paywalls localizados en el Paywall Builder de Adapty para mejorar la experiencia de usuario en todo el mundo." --- Localizar es un proceso tedioso que requiere tiempo y precisión. Con el Paywall Builder, Adapty hace casi todo el trabajo por ti, ya que la mayoría de las cosas funcionan de forma inmediata. Esta página explica cómo funciona. Supongamos que ya terminaste de configurar tu paywall en la localización predeterminada `en` y estás satisfecho con el resultado. Ahora es el momento de añadir otro idioma. ## Añadir y configurar la localización \{#add-and-set-up-localization\} 1. Haz clic en **Add locale** y selecciona todos los idiomas que quieras incluir en tu app. <img src="/assets/shared/img/add-PB-locale.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Abre el menú **Localization** para ver todos los idiomas añadidos. Los nuevos idiomas se rellenarán automáticamente con los valores del idioma predeterminado. <img src="/assets/shared/img/localization.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Ahora puedes traducir el contenido manualmente, usar IA o exportar el archivo de localización para traductores externos. ## Traducir paywalls con IA \{#translating-paywalls-with-ai\} La traducción con IA es una forma rápida y eficiente de localizar tu paywall. Detectamos automáticamente qué líneas nunca se han traducido o han cambiado en inglés desde su última traducción, y las marcamos como pendientes de actualización. Las líneas que ya estaban traducidas y no han cambiado mantendrán su traducción original y no se volverán a traducir. El formato de texto enriquecido (negrita, cursiva, texto de color, etc.) no se conservará en la versión traducida. Ajusta manualmente el texto traducido según sea necesario. 1. Haz clic en el icono de localización para seleccionar los idiomas a traducir: - **En el encabezado de la columna del idioma**: Traduce todas las líneas a la vez; ideal para la traducción inicial o cuando actualices el idioma completo. - **En líneas individuales**: Traduce líneas concretas de forma independiente; útil para hacer cambios puntuales sin afectar al resto de traducciones. <img src="/assets/shared/img/localization-table-language-PB.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en **AI Translate** para aplicar las traducciones. Las líneas del paywall se traducirán y se añadirán a la tabla. ## Exportar archivos de localización para traducción externa \{#exporting-localization-files-for-external-translation\} Puedes exportar archivos de localización para compartirlos con tus traductores y luego importar los resultados traducidos de vuelta a Adapty. Al exportar con el botón **Export** se crean archivos `.csv` individuales para cada idioma, agrupados en un único archivo comprimido. Si solo necesitas un archivo, puedes exportarlo directamente desde el menú específico del idioma. <img src="/assets/shared/img/localization-single-export-pb.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Una vez que recibas los archivos traducidos, usa el botón **Import** para subirlos todos a la vez o de forma individual. Adapty validará automáticamente los archivos para asegurarse de que coinciden con el formato correcto y la estructura de configuración del paywall. ### Formato del archivo de importación \{#import-file-format\} Para que la importación sea correcta, el archivo debe cumplir los siguientes requisitos: - **Nombre y extensión del archivo:** El nombre del archivo debe coincidir con el idioma que representa y tener extensión `.csv`. Puedes verificar y copiar el nombre del idioma en el Adapty Dashboard. Si el nombre no se reconoce, la importación fallará. <img src="/assets/shared/img/copy_locale.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> - **CSV válido:** El archivo debe tener un formato CSV válido. Los archivos inválidos no se podrán importar. - **Solo comas como separadores**: Usa comas como separadores. Otros separadores producirán errores. - **Línea de encabezado**: El archivo debe incluir una línea de encabezado. - **Nombres de columna correctos:** Los nombres de las columnas deben ser **id** y **value**. - **Sin entidades adicionales:** Asegúrate de que el archivo no incluye entidades que no estén presentes en la configuración actual del paywall. Las entidades adicionales producirán errores. - **Importación parcial:** El archivo puede incluir todas o solo algunas entidades de la configuración actual del paywall. | **Problema** | **Solución** | | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Los archivos .csv importados no son válidos** | Valida el archivo para asegurarte de que cumple los estándares CSV. Comprueba si faltan comas o hay comas de más, separadores incorrectos, líneas de encabezado ausentes, y verifica que los nombres de columna sean **id** y **value**. | | **Algunos idiomas no aparecen en la tabla** | Asegúrate de que los nombres de los archivos coincidan exactamente con los nombres de idioma que aparecen en la tabla de localización. Si no coinciden, renómbralos en consecuencia. Verifica también el contenido del archivo para asegurarte de que corresponde a la configuración del paywall. | ## Localización manual \{#manual-localization\} A veces puede que quieras ajustar traducciones, añadir imágenes distintas para idiomas concretos, o incluso modificar directamente las configuraciones remotas. 1. Elige el elemento que quieras traducir e introduce el nuevo valor. Puedes actualizar tanto valores de tipo **String** como de tipo **List**, o reemplazar imágenes por otras más adecuadas para el idioma. <img src="/assets/shared/img/pb_localization.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Aprovecha el menú contextual del idioma inglés para resolver problemas de localización de forma eficiente: - **Copy this value to all locales**: Sobrescribe cualquier cambio realizado en los idiomas no ingleses de la fila seleccionada, reemplazándolos con el valor del idioma inglés. - **Revert all row changes to original values**: Descarta los cambios realizados durante la sesión actual y restaura los valores a su último estado guardado. <img src="/assets/shared/img/locale_options.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Después de añadir idiomas a un paywall, asegúrate de implementar correctamente los códigos de idioma en el código de tu app. Consulta <InlineTooltip tooltip="cómo usar las localizaciones y los códigos de idioma en tu app">[iOS](localizations-and-locale-codes), [Android](android-localizations-and-locale-codes), [React Native](react-native-localizations-and-locale-codes), [Flutter](flutter-localizations-and-locale-codes) y [Unity](unity-localizations-and-locale-codes)</InlineTooltip>. ### Previsualizar el resultado de la localización \{#preview-the-localization-result\} Puedes revisar tus textos mientras editas simplemente volviendo a la pestaña **Builder & Generator** y seleccionando otro idioma: <img src="/assets/shared/img/locales.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::note Presta atención al código de idioma (`en`, `fr` e `it`). Deberás pasarlo al método `getViewConfiguration` de nuestro SDK para obtener la localización correcta. <img src="/assets/shared/img/copy_locale.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Puedes obtener más información en <InlineTooltip tooltip="las guías sobre cómo obtener paywalls del Paywall Builder en tu app">[iOS](get-pb-paywalls), [Android](android-get-pb-paywalls), [React Native](react-native-get-pb-paywalls), [Flutter](flutter-get-pb-paywalls) y [Unity](unity-get-pb-paywalls)</InlineTooltip>. ::: Una vez que añadas idiomas a un paywall, consulta <InlineTooltip tooltip="las guías sobre cómo usar las localizaciones y los códigos de idioma en tu app">[iOS](localizations-and-locale-codes), [Android](android-localizations-and-locale-codes)</InlineTooltip> para obtener más información. --- # File: add-remote-config-locale --- --- title: "Localizar paywalls con Remote Config" description: "Añade localizaciones de Remote Config para personalizar los paywalls de Adapty." --- Adaptar los paywalls a diferentes idiomas es fundamental en un mundo con culturas diversas. La localización te permite crear experiencias personalizadas para usuarios de regiones específicas. Para cada paywall puedes añadir versiones en distintos idiomas, asegurando que tu producto conecte con las audiencias locales. Si no usas el Paywall Builder de Adapty para diseñar paywalls, aún puedes localizar tus paywalls personalizados y gestionar las localizaciones sin volver a publicar la app: 1. Creas un Remote Config con variables en el Adapty Dashboard. Las variables pueden representar texto, contenido multimedia u otros tipos de contenido. 2. Defines los valores de cada variable para cada localización. 3. Gestionas las variables en el código de la app. 4. Cuando obtienes un paywall con productos y envías una localización, recibes los valores de las variables correspondientes. De esta forma, las localizaciones no están codificadas en el código de la app y puedes ajustarlas en cualquier momento. Tanto en la vista de tabla como en formato JSON, puedes ajustar fácilmente los ajustes de cada idioma. Por ejemplo, traducir claves de texto, cambiar valores booleanos (p. ej., `TRUE` para inglés, `FALSE` para italiano), o incluso intercambiar imágenes de fondo. ## Configurar la localización para paywalls con Remote Config \{#set-up-localization-for-remote-configured-paywalls\} 1. Ve a la sección [**Paywalls**](https://app.adapty.io/paywalls) en Adapty. 2. Haz clic en el paywall para abrirlo. 3. Ve a la pestaña **Remote config**. <img src="/assets/shared/img/switch_to_remote_config.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en **Locales** y selecciona los idiomas que quieres admitir. Guarda los cambios para añadir estas localizaciones al paywall. <img src="/assets/shared/img/add_locale.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Ahora puedes traducir el contenido manualmente, usar IA o exportar el archivo de localización para traductores externos. ## Traducir paywalls con IA \{#translate-paywalls-with-ai\} La traducción con IA es una forma rápida y eficaz de localizar tu paywall. Puedes traducir tanto valores de tipo **String** como **List**. Por defecto, todas las líneas están seleccionadas (resaltadas en violeta). Las líneas que ya han sido traducidas aparecen en verde y no se incluirán en la nueva traducción por defecto. Las líneas que no están seleccionadas ni traducidas aparecen en gris. <img src="/assets/shared/img/localization-table.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <img src="/assets/shared/img/localization-json.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 1. Selecciona las líneas que quieres traducir. Es recomendable desmarcar las líneas con IDs, URLs y variables para que la IA no las traduzca. 2. Selecciona los idiomas para la traducción. <img src="/assets/shared/img/localization-table-language.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **AI Translate** para aplicar las traducciones. Las líneas seleccionadas se traducirán y se añadirán al paywall, quedando marcadas en verde. ## Exportar archivos de localización para traductores externos \{#exporting-localization-files-for-external-translation\} Aunque la localización con IA es cada vez más popular, puede que prefieras un método más fiable, como usar traductores profesionales o una agencia de traducción con experiencia contrastada. En ese caso, puedes exportar los archivos de localización para compartirlos con tus traductores e importar los resultados traducidos de vuelta a Adapty. Al exportar con el botón **Export** se crean archivos `.json` individuales para cada idioma, agrupados en un único archivo comprimido. Si solo necesitas un archivo, puedes exportarlo directamente desde el menú específico del idioma. <img src="/assets/shared/img/localization-single-export.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Una vez que hayas recibido los archivos traducidos, usa el botón **Import** para subirlos todos a la vez o de forma individual. Adapty validará automáticamente los archivos para asegurarse de que tienen el formato correcto. ### Formato del archivo de importación \{#import-file-format\} Para que la importación se realice correctamente, el archivo debe cumplir los siguientes requisitos: - **Nombre y extensión del archivo:** El nombre del archivo debe coincidir con la localización que representa y tener extensión `.json`. Puedes verificar y copiar el nombre de la localización en el Adapty Dashboard. Si el nombre no se reconoce, la importación fallará. <img src="/assets/shared/img/locale-name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> - **JSON válido:** El archivo debe ser un JSON válido. Si no lo es, la importación fallará. ## Localización manual \{#manual-localization\} A veces puede que quieras ajustar traducciones, añadir imágenes diferentes para localizaciones específicas o incluso modificar las configuraciones remotas directamente. 1. Elige el elemento que quieres traducir e introduce un nuevo valor. Puedes actualizar tanto valores de tipo **String** como **List**, o reemplazar imágenes por otras más adecuadas para la localización. <img src="/assets/shared/img/032b429-remote_config_localization.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Aprovecha el menú contextual de la localización en inglés para resolver problemas de localización de forma eficiente: - **Copy this value to all locales**: Sobreescribe los cambios realizados en localizaciones que no sean la inglesa para la fila seleccionada, reemplazándolos con el valor de la localización en inglés. - **Revert all row changes to original values**: Descarta los cambios realizados durante la sesión actual y restaura los valores al último estado guardado. <img src="/assets/shared/img/d7e70f1-remote_confi_loc_table_options.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Después de añadir localizaciones a un paywall, asegúrate de implementar correctamente los códigos de localización en el código de tu app. Consulta <InlineTooltip tooltip="las guías sobre cómo usar localizaciones y códigos de localización en tu app">[iOS](localizations-and-locale-codes), [Android](android-localizations-and-locale-codes)</InlineTooltip> --- # File: web-paywall-configuration --- --- title: "Configuración del web paywall" --- Una vez que hagas clic en **Create web paywall** en la página **Web paywall**, se te redirigirá a una página independiente para configurar el diseño del web paywall y el método de pago. ## Configura un método de pago \{#set-up-a-payment-method\} Primero, necesitas conectar un proveedor de pagos que gestione las compras. Las opciones disponibles son: - Stripe - Paddle - Paypal - Solidgate :::important Para garantizar un seguimiento preciso de los análisis del web paywall en Adapty, necesitas [añadir tus productos](product) junto con los IDs de producto correspondientes de Stripe/Paddle/otro proveedor de pagos en Adapty. ::: Para configurar un proveedor de pagos: 1. En la página de lista de web paywalls, haz clic en **Settings** y cambia a la pestaña **Integrations**. 2. Selecciona un proveedor de pagos y sigue las instrucciones de integración que aparecen en pantalla. <img src="/assets/shared/img/web-paywall-configuration-1.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. ⚠️ Si eliges Stripe, asegúrate de usar las claves del entorno **Test Mode** aunque la interfaz diga **Sandbox**. De lo contrario, tu web paywall no funcionará. Los **Sandboxes** de Stripe aún no son compatibles. <img src="/assets/shared/img/web-paywall-configuration-stripe.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Configura la verificación de dominio de Apple Pay \{#set-up-apple-pay-domain-verification\} En **Settings > Domains**, selecciona tu proveedor de pagos principal para usarlo en la verificación de dominio. Luego, verifica los dominios de tu paywall con el proveedor correspondiente: **Stripe**: 1. Ve a [Payment method domain settings](https://dashboard.stripe.com/settings/payment_method_domains) y haz clic en **Add a new domain**. 2. Añade `app.funnelfox.com` y tu subdominio personal del paywall (tendrá un aspecto similar a `paywalls-....fnlfx.com`). Para encontrar tu subdominio, ve a **Settings > Domains** y copia el valor de **Hosted subdomain**. **Paddle**: 1. En la consola de Paddle, ve a **Checkout > Website approval** y haz clic en **Add a new domain**. 2. Añade `app.funnelfox.com` y tu subdominio personal del paywall (tendrá un aspecto similar a `paywalls-....fnlfx.com`). Para encontrar tu subdominio, ve a **Settings > Domains** y copia el valor de **Hosted subdomain**. El proceso de aprobación en Paddle es manual, por lo que tendrás que esperar hasta que los dominios pasen de `Pending` a `Approved`. **FunnelFox Billing**: Sigue las [instrucciones de integración de FunnelFox Billing](https://funnelfox.com/docs/billing/integration-billing-funnelfox). **SolidGate**: 1. En tu Solidgate Dashboard, ve a **Developers > Apple Pay Domains**. 2. Haz clic en **+ Add new domain** y pega el dominio de tu proyecto (desde **Settings > Domains** en FunnelFox). Añade también tu dominio personalizado, si corresponde. 3. Para usar Apple Pay en modo de vista previa, añade también `http://app.funnelfox.com/`. ## Crea y configura un web paywall \{#create-and-configure-a-web-paywall\} 1. En la página de lista de web paywalls, haz clic en **Create a paywall**. 2. Introduce un nombre para el paywall y haz clic en **Create**. <img src="/assets/shared/img/web-paywall-configuration-2.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Se te redirigirá a una plantilla básica con dos opciones de suscripción y el botón de compra de Apple Pay. La primera pantalla muestra los planes de suscripción. La segunda y la tercera son pantallas de pago. Cada pantalla corresponde a un plan que ofreces. Si solo tienes un plan, elimina la pantalla extra. Si tienes más, debes duplicar las pantallas de pago. La última pantalla que ven los usuarios tras una compra exitosa es donde debes indicar claramente que pueden volver a tu app. <img src="/assets/shared/img/web-paywall-configuration-10.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Configura la lista de planes: añade o elimina planes y precios. Todos los precios y planes que ves en pantalla no se añaden de forma dinámica, por lo que debes configurarlos manualmente. <img src="/assets/shared/img/web-paywall-configuration-8.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Añade o configura una pantalla de pago para cada plan que tengas. Te recomendamos añadir el importe total en cada pantalla de pago para que los usuarios sepan cuánto van a pagar antes de hacer clic en el botón de compra. 6. En las pantallas de pago, ya tienes el botón de Apple Pay. Para que funcione, configura en cada pantalla: 1. **Product type**: Selecciona si quieres añadir un período de prueba o un descuento. 2. **Trial period**: Introduce la duración del período de prueba. 3. **Product**: Selecciona tu producto de tu proveedor de pagos. :::important Asegúrate de que el producto esté añadido en Adapty. De lo contrario, el resultado de la compra se establecerá por defecto. ::: 4. **Subscription discount**: Opcionalmente, selecciona un cupón de tu proveedor de pagos. <img src="/assets/shared/img/web-paywall-configuration-6.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Ahora necesitas asociar los planes con las pantallas de pago. En la pantalla de selección de plan, haz clic en el botón **Continue** y selecciona una pantalla de destino para cada plan. <img src="/assets/shared/img/web-paywall-configuration-9.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Cuando tengas el paywall listo, necesitas obtener su enlace para activarlo en Adapty. La forma de obtenerlo depende de si lo estás probando o lanzando en el entorno de producción: 1. **Para pruebas en sandbox**: Haz clic en **Preview** en la parte superior derecha y copia el enlace. 2. **Para producción**: Haz clic en **Publish** en la parte superior derecha. Haz clic en **Home** y copia el enlace de la columna **URL**. <img src="/assets/shared/img/web-paywall-configuration-11.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ¡Listo! Usa este enlace para [continuar con la configuración](web-paywall#step-2-trigger-the-paywall). --- # File: fallback-paywalls --- --- title: "Fallback paywalls" description: "Use fallback paywalls to ensure seamless user experience in Adapty." --- To maintain a fluid user experience, it is important that you set up **fallback versions** for your [paywalls](paywalls) and [onboardings](onboardings). When your application loads a paywall, the Adapty SDK requests paywall configuration data from our servers. But what if the device cannot connect to Adapty due to network issues or server outages? * If the user accessed the paywall before, and the device cached its data, the application loads paywall data **from cache**. * If the device did not cache the paywall, the application looks for a locally stored configuration file. It allows the application to display the paywall without an error. Adapty automatically generates fallback configuration files for you to download and use. Each file contains platform-specific configurations for *all* your placements. ## Get started 1. [Download the fallback configuration file](/local-fallback-paywalls) from Adapty. 2. Use the Adapty SDK to configure your fallback paywalls: * [iOS](ios-use-fallback-paywalls) * [Android](android-use-fallback-paywalls) * [React Native](react-native-use-fallback-paywalls) * [Flutter](flutter-use-fallback-paywalls) * [Unity](unity-use-fallback-paywalls) * [Kotlin Multiplatform](kmp-use-fallback-paywalls) * [Capacitor](capacitor-use-fallback-paywalls) ## Limitations Fallback paywalls are hard-coded and locally stored, so they lack the dynamic capabilities of regular Adapty paywalls. * Fallback paywalls don't support [internationalization](paywall-localization). When Adapty generates the configuration file, it uses the default `en` locale. * Each placement can only have one fallback paywall. If your setup inlcudes different paywall configurations for different [audiences](audience), Adapty uses the configuration intended for "All users". * Fallback paywalls don't support [A/B testing](ab-tests). If a paywall participates in an A/B test, its fallback configuration file will include the variation with the highest weight. * Fallback paywalls cannot be [managed remotely](customize-paywall-with-remote-config). If you want to update the configuration file, you need to release a new version of the app on App Store / Google Play. --- # File: local-fallback-paywalls --- --- title: "Descargar paywalls de respaldo" description: "Usa paywalls de respaldo locales en Adapty para garantizar flujos de suscripción sin interrupciones." --- Adapty genera automáticamente archivos de configuración JSON para tus [paywalls de respaldo](/fallback-paywalls), uno por plataforma. Estos archivos también contienen los datos de respaldo para tus [onboardings](local-fallback-onboarding). Si un placement tiene más de un paywall u onboarding, la versión de respaldo incluirá la variación con el mayor peso o la audiencia más amplia. Adapty actualiza estos archivos cada vez que modificas tus paywalls u onboardings. Sigue estos pasos para descargar tus configuraciones de respaldo: 1. Abre la página **[Placements](https://app.adapty.io/placements)**. 2. Haz clic en el botón **Fallbacks**. 3. Selecciona tu plataforma de destino (*iOS* o *Android*) en el menú desplegable. 4. Selecciona tu versión del SDK para iniciar la descarga. <img src="/assets/shared/img/9c63367-placements.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Tras la descarga \{#after-the-download\} Sigue la guía de configuración para tu plataforma: * [iOS](ios-use-fallback-paywalls) * [Android](android-use-fallback-paywalls) * [React Native](react-native-use-fallback-paywalls) * [Flutter](flutter-use-fallback-paywalls) * [Unity](unity-use-fallback-paywalls) * [Kotlin Multiplatform](kmp-use-fallback-paywalls) * [Capacitor](capacitor-use-fallback-paywalls) --- # File: paywall-metrics --- --- title: "Métricas de paywall" description: "Rastrea y analiza las métricas de rendimiento de tu paywall para mejorar los ingresos por suscripción." --- Adapty recopila una serie de métricas para ayudarte a medir mejor el rendimiento de tus paywalls. Todas las métricas se actualizan en tiempo real, excepto las vistas, que se actualizan cada varios minutos. Todas las métricas, excepto las vistas, se atribuyen al producto dentro del paywall. Este documento describe las métricas disponibles, sus definiciones y cómo se calculan. Las métricas de paywall están disponibles en la lista de paywalls, lo que te ofrece una visión general del rendimiento de todos tus paywalls. Esta vista consolidada presenta métricas agregadas para cada paywall, lo que te permite evaluar su efectividad e identificar áreas de mejora. Para un análisis más detallado de cada paywall, puedes navegar a las métricas de detalle del paywall. En esta sección encontrarás métricas completas específicas del paywall seleccionado, con información más profunda sobre su rendimiento. <img src="/assets/shared/img/d73bd6c-CleanShot_2023-07-19_at_16.05.412x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Controles de métricas \{#metrics-controls\} El sistema muestra las métricas según el período de tiempo seleccionado y las organiza de acuerdo con el parámetro de la columna izquierda con tres niveles de sangría. Para un paywall activo (Live), las métricas cubren el período desde la fecha de inicio del paywall hasta la fecha actual. Para los paywalls inactivos, las métricas abarcan todo el período desde la fecha de inicio hasta el final del período de tiempo seleccionado. Los paywalls en borrador y archivados se incluyen en la tabla de métricas, pero si no hay datos disponibles para esos paywalls, aparecerán listados sin ninguna métrica. #### Opciones de vista para los datos de métricas \{#view-options-for-metrics-data\} <img src="/assets/shared/img/15df73d-Area.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> La página de paywall ofrece dos opciones de vista para los datos de métricas: basada en placement y basada en audiencia. En la vista basada en placement, las métricas se agrupan por los placements asociados al paywall. Esto permite analizar las métricas según los distintos placements. En la vista basada en audiencia, las métricas se agrupan por la audiencia objetivo del paywall. Puedes evaluar las métricas específicas de los distintos segmentos de audiencia. Puedes seleccionar la vista preferida mediante el menú desplegable en la parte superior de la página de detalle del paywall. #### Filtrado por fecha de instalación del perfil \{#profile-install-date-filtration\} <img src="/assets/shared/img/6c9639d-Area.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> La casilla **Filter metrics by install date** permite filtrar las métricas según la fecha de instalación del perfil, en lugar de los filtros predeterminados que usan la fecha de prueba/compra para las transacciones o la fecha de vista para las vistas de paywall. Al seleccionar esta casilla, puedes centrarte en medir el rendimiento de la adquisición de usuarios para un período específico alineando las métricas con la fecha de instalación del perfil. Esta opción es útil para personalizar el análisis de métricas según tus necesidades concretas. #### Rangos de tiempo \{#time-ranges\} <img src="/assets/shared/img/e8ace98-CleanShot_2023-07-19_at_16.12.442x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Puedes elegir entre varios períodos de tiempo para analizar los datos de métricas, lo que te permite centrarte en duraciones específicas como días, semanas, meses o rangos de fechas personalizados. #### Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analytics](controls-filters-grouping-compare-proceeds) ::: Adapty ofrece herramientas potentes para filtrar y personalizar el análisis de métricas según tus necesidades. En la página de métricas de Adapty tienes acceso a varios rangos de tiempo, opciones de agrupación y posibilidades de filtrado. - Filtrar por: Audiencia, país, paywall, estado del paywall, grupo de paywall, placement, país, store, producto y store del producto. - Agrupar por: Producto y store. #### Gráfico de métrica individual \{#single-metrics-chart\} Uno de los componentes principales de la página de métricas de paywall es la sección del gráfico, que representa visualmente las métricas seleccionadas y facilita el análisis. La sección del gráfico en la página de métricas de paywall incluye un gráfico de barras horizontales que representa visualmente los valores de la métrica elegida. Cada barra del gráfico corresponde a un valor de métrica y es proporcional en tamaño, lo que facilita comprender los datos de un vistazo. La línea horizontal indica el período de tiempo analizado, y la columna vertical muestra los valores numéricos de las métricas. El valor total de todos los valores de métrica se muestra junto al gráfico. <img src="/assets/shared/img/10a9b9c-Area.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Además, al hacer clic en el icono de flecha en la esquina superior derecha de la sección del gráfico, la vista se amplía y muestra las métricas seleccionadas en toda la línea del gráfico. #### Resumen total de métricas \{#total-metrics-summary\} Junto al gráfico de métrica individual se muestra la sección de resumen total de métricas, que presenta los valores acumulados para las métricas seleccionadas en un momento específico, con la posibilidad de cambiar la métrica mostrada mediante un menú desplegable. <img src="/assets/shared/img/b7ff0c8-CleanShot_2023-07-19_at_16.19.332x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Definiciones de métricas \{#metrics-definitions\} <img src="/assets/shared/img/1b07fd8-Area.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### Revenue \{#revenue\} Esta métrica representa el importe total de dinero generado en USD por compras y renovaciones. Ten en cuenta que el cálculo de los ingresos no incluye la comisión de App Store / Play Store y se calcula antes de deducir ninguna tarifa. #### Proceeds \{#proceeds\} Esta métrica representa el importe real de dinero recibido por el propietario de la app en USD por compras y renovaciones, después de deducir la comisión aplicable de App Store / Play Store. :::important Notifica a Adapty si tu app está inscrita en un programa de comisión reducida. Para garantizar cálculos correctos, especifica tu estado en el [programa Small Business](app-store-small-business-program) y el [programa de tarifa de servicio reducida](google-reduced-service-fee) en la [configuración de tu app](general). ::: Refleja los ingresos netos que contribuyen directamente a las ganancias de la app. Para más información sobre cómo se calculan los proceeds, puedes consultar la [documentación](analytics-cohorts#revenue-vs-proceeds) de Adapty. #### ARPPU \{#arppu\} ARPPU es el ingreso promedio por usuario de pago. Se calcula dividiendo los ingresos totales entre el número de usuarios de pago únicos. $15.000 de ingresos / 1.000 usuarios de pago = $15 de ARPPU. #### ARPAS \{#arpas\} El ingreso promedio por suscriptor activo te permite medir los ingresos promedio generados por cada suscriptor activo. Se calcula dividiendo los ingresos totales entre el número de suscriptores que han activado una prueba o suscripción. Por ejemplo, si los ingresos totales son $5.000 y hay 1.000 suscriptores, el ARPAS sería $5. Esta métrica ayuda a evaluar el potencial de monetización promedio por suscriptor. #### Tasa de conversión única (CR) a compras \{#unique-conversion-rate-cr-to-purchases\} La tasa de conversión única a compras se calcula dividiendo el número de compras entre el número de vistas únicas. Por ejemplo, si hay 10 compras y 100 vistas únicas, la tasa de conversión única a compras sería del 10%. Esta métrica se centra en la proporción de compras respecto al número único de vistas, lo que aporta información sobre la efectividad de convertir visitantes únicos en clientes de pago. #### CR a compras \{#cr-to-purchases\} La tasa de conversión a compras se calcula dividiendo el número de compras entre el número total de vistas. Por ejemplo, si hay 10 compras y 100 vistas, la tasa de conversión a compras sería del 10%. Esta métrica indica el porcentaje de vistas que resultan en compras, lo que aporta información sobre la efectividad de tu paywall para convertir usuarios en clientes de pago. #### CR única a pruebas \{#unique-cr-to-trials\} La tasa de conversión única a pruebas se calcula dividiendo el número de pruebas iniciadas entre el número de vistas únicas. Por ejemplo, si hay 30 pruebas iniciadas y 100 vistas únicas, la tasa de conversión única a pruebas sería del 30%. Esta métrica mide el porcentaje de vistas únicas que resultan en activaciones de prueba, lo que aporta información sobre la efectividad de tu paywall para convertir visitantes únicos en usuarios de prueba. #### Compras \{#purchases\} Las compras representan el total acumulado de las distintas transacciones realizadas en el paywall. Las siguientes transacciones se incluyen en esta métrica (las renovaciones no se incluyen): - Compras nuevas realizadas directamente en el paywall. - Conversiones de pruebas que se activaron inicialmente en el paywall. - Downgrades, upgrades y cross-grades de suscripciones realizados en el paywall. - Restauraciones de suscripciones en el paywall, como cuando una suscripción se reactiva tras su vencimiento sin renovación automática. Al considerar estos diferentes tipos de transacciones, la métrica de compras ofrece una visión completa de la actividad global de adquisición y monetización en tu paywall. #### Pruebas \{#trials\} La métrica de pruebas representa el número total de pruebas que se han activado. Refleja el número de usuarios que han iniciado períodos de prueba a través de tu paywall. Esta métrica ayuda a rastrear la efectividad de tu oferta de prueba y puede aportar información sobre el engagement de los usuarios y la conversión de pruebas a suscripciones de pago. #### Pruebas canceladas \{#trials-canceled\} La métrica de pruebas canceladas representa el número de pruebas en las que se ha desactivado la renovación automática. Esto ocurre cuando los usuarios se dan de baja manualmente de la prueba, lo que indica su decisión de no continuar con la suscripción tras el período de prueba. El seguimiento de las pruebas canceladas proporciona información valiosa sobre el comportamiento de los usuarios y permite entender la tasa a la que estos abandonan la prueba. #### Reembolsos \{#refunds\} La métrica de reembolsos representa el número de compras y suscripciones reembolsadas. Esto incluye transacciones que han sido revertidas o reembolsadas por diversas razones, como solicitudes del cliente, problemas de pago u otras políticas de reembolso aplicables. #### Tasa de reembolso \{#refund-rate\} La tasa de reembolso se calcula dividiendo el número de reembolsos entre el número de primeras compras (las renovaciones no se incluyen). Por ejemplo, si hay 5 reembolsos y 1.000 primeras compras, la tasa de reembolso sería del 0,5%. #### Vistas \{#views\} La métrica de vistas representa el número total de veces que los usuarios han visto el paywall. Cada vez que un usuario visita el paywall, se cuenta como una vista independiente. Por ejemplo, si un usuario visita el paywall dos veces, se registrarán dos vistas. El seguimiento de las vistas te ayuda a entender el nivel de engagement e interacción de los usuarios con tu paywall, lo que aporta información sobre el comportamiento de los usuarios y la efectividad de la ubicación y el diseño de tu paywall. #### Vistas únicas \{#unique-views\} La métrica de vistas únicas representa el número de instancias únicas en las que los usuarios han visto el paywall. A diferencia de las vistas totales, que cuentan cada visita como una vista independiente, las vistas únicas cuentan la visita de cada usuario al paywall una sola vez, independientemente de cuántas veces acceda a él. Por ejemplo, si un usuario visita el paywall dos veces, se registrará como una vista única. El seguimiento de las vistas únicas proporciona una medida más precisa del engagement de los usuarios y el alcance de tu paywall, ya que se centra en los usuarios individuales en lugar del número total de visitas. :::warning Asegúrate de enviar las vistas del paywall a Adapty mediante el método `.logShowPaywall()`. De lo contrario, las vistas del paywall no se contabilizarán en las métricas y las conversiones no serán relevantes. ::: --- # File: migrate-paywalls --- --- title: "Migrar paywalls entre apps" description: "Aprende cómo migrar paywalls de otras apps en Adapty." --- Con Adapty no necesitas crear un paywall desde cero para cada app. Si gestionas varias apps, puedes migrar la configuración del Paywall Builder de cualquier paywall creado con el builder de una app a otra. La migración copia toda la configuración visual: - Ajustes de diseño del paywall y de todos sus elementos - Contenido multimedia - Localización La migración solo aplica a la configuración del builder y no copia los productos ni el Remote Config. :::note Si migras una configuración del Paywall Builder que usa fuentes personalizadas, pruébalas en un dispositivo, ya que pueden mostrarse incorrectamente. ::: ## Migrar un paywall \{#migrate-paywall\} :::important Solo puedes migrar paywalls creados en el **nuevo** Paywall Builder de Adapty. Para migrar paywalls del builder **legacy**, primero debes migrarlos al nuevo Paywall Builder. ::: Para migrar una configuración del Paywall Builder: 1. **Para un paywall nuevo**: Comienza la [creación del paywall](create-paywall) y añade productos. Luego haz clic en **Build no-code paywall** para abrir la biblioteca de plantillas. **Para un paywall existente**: Ve a la sección **Layout settings** de la pestaña **Builder & Generator** y haz clic en **Change template**. 2. Haz clic en **Choose paywall** dentro del recuadro **Copy a design from your apps** al editar la plantilla del paywall. <img src="/assets/shared/img/migrate-paywall-builder.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Selecciona la app y el paywall del que quieres copiar la configuración. <img src="/assets/shared/img/migrate-app.png" style={{ border: '1px solid #727272', /* border width and color */ width: '500px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en **Copy Selected Paywall**. Tras la migración, puedes hacer los cambios que necesites sin que afecten al paywall original. --- # File: duplicate-paywalls --- --- title: "Duplicar paywall" description: "Aprende a gestionar paywalls duplicados y optimizar el rendimiento de los paywalls en Adapty." --- Si necesitas hacer pequeños cambios en un paywall existente de Adapty, especialmente cuando ya se está usando en tu app y no quieres estropear tus analíticas, puedes simplemente duplicarlo. Luego puedes usar esos duplicados para reemplazar los paywalls originales en algunos o todos los placements según necesites. Esto crea una copia del paywall con todos sus detalles, como su nombre, productos y cualquier promoción. Al nombre del nuevo paywall se le añadirá "Copy" para que puedas distinguirlo fácilmente del original. Para duplicar un paywall en el Adapty Dashboard: 1. Abre la sección [**Paywalls**](https://app.adapty.io/paywalls) en el menú principal de Adapty. La página de lista de paywalls del Adapty Dashboard ofrece una vista general de todos los paywalls presentes en tu cuenta. 2. Haz clic en el botón de **3 puntos** junto al paywall y selecciona la opción **Duplicate**. <img src="/assets/shared/img/duplicate.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Ajusta el nuevo paywall y haz clic en el botón **Save**. 4. Adapty te pedirá que reemplaces los paywalls originales por sus duplicados en los placements si el paywall original se está usando actualmente en algún placement. Si eliges **Create and replace original**, los nuevos paywalls pasarán a estar **Live** inmediatamente. También puedes crearlos como nuevos paywalls en estado **Draft** y añadirlos a los placements más adelante. --- # File: archive-paywalls --- --- title: "Archivar paywall" description: "Aprende cómo archivar paywalls obsoletos en Adapty sin perder datos." --- A medida que trabajas con Adapty y ajustas la configuración de tus paywalls, puede que acumules paywalls que ya no encajan con tu estrategia o campañas actuales. Estos paywalls sin uso, dejados como `Inactive`, pueden desordenar tu espacio de trabajo y dificultar encontrar los que realmente importan. Para resolver esto, Adapty ofrece la opción de archivar estos paywalls innecesarios. Archivarlos garantiza que se guarden de forma segura sin eliminarlos permanentemente, listos para consultarse si es necesario en el futuro. Además, los paywalls archivados se pueden filtrar de la vista predeterminada, despejando tu espacio de trabajo y simplificando la interfaz. En esta guía, te explicamos cómo archivar paywalls en Adapty de forma eficiente para que tengas mayor control sobre la gestión de tus paywalls. Un recordatorio importante: los paywalls activos que estén en uso en al menos un placement no se pueden archivar. Si quieres archivar uno de esos paywalls, primero elimínalo de todos los placements. :::note No puedes archivar un paywall si está siendo utilizado en una prueba A/B no archivada. De este modo, el usuario puede ver las métricas detalladas de una prueba A/B completada, y el paywall vinculado forma parte de esos datos. ::: **Para archivar un paywall:** 1. Abre la sección [**Paywalls**](https://app.adapty.io/paywalls) en el menú principal de Adapty. 2. Haz clic en el botón **3-dot** junto al paywall y selecciona la opción **Archive**. <img src="/assets/shared/img/archive-paywall.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Cuando estés en la ventana **Archive paywall**, escribe el nombre del paywall que deseas archivar y haz clic en el botón **Archive**. --- # File: restore-paywall --- --- title: "Restaurar paywall desde el archivo" description: "Restaura paywalls en Adapty para garantizar servicios de suscripción ininterrumpidos para los usuarios." --- La posibilidad de archivar paywalls es una funcionalidad muy útil para simplificar la gestión de tus paywalls. Te permite ocultar los paywalls que ya no necesitas, reduciendo el desorden en tu espacio de trabajo. Además, la opción de restaurar paywalls archivados te da flexibilidad para reincorporarlos a tu estrategia si vuelven a ser útiles. Los paywalls archivados pueden estar excluidos de la vista predeterminada. Para verlos, selecciona **Archived** en el filtro **State**. **Para devolver un paywall desde el archivo** 1. Abre la sección [**Paywalls**](https://app.adapty.io/paywalls) en el menú principal de Adapty. 2. Asegúrate de que los paywalls archivados se muestran en la lista. Si no es así, actualiza el filtro de la derecha. <img src="/assets/shared/img/paywall-filter.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en el botón de **3 puntos** junto al paywall archivado y selecciona **Back to active**. <img src="/assets/shared/img/restore-paywall.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: choose-meaningful-placements --- --- title: "Elige placements con sentido" description: "Optimiza los placements de paywalls con Adapty para aumentar la participación de los usuarios y los ingresos." --- Al [crear placements](create-placement), es fundamental tener en cuenta el flujo lógico de tu app y la experiencia de usuario que quieres generar. La mayoría de las apps no deberían tener más de 5 [placements](placements) sin sacrificar la capacidad de ejecutar experimentos. Aquí tienes un ejemplo de cómo puedes estructurar tus placements: <img src="/assets/shared/img/placement-flows.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 1. **Flujo de onboarding:** Esta etapa representa la primera interacción de tus usuarios con la app. Es una oportunidad excelente para presentarles la propuesta de valor de tu app mediante placements tanto de onboarding como de paywall. Más del 80 % de las suscripciones se activan durante el flujo de onboarding, por lo que conviene enfocarse en vender las suscripciones más rentables en este punto. Con Adapty, puedes tener fácilmente diferentes [onboardings](onboardings) y [paywalls](paywalls) para distintas audiencias, y ejecutar pruebas A/B para encontrar la mejor opción para tu app. Por ejemplo, puedes ejecutar una prueba A/B para usuarios de EE. UU., mostrando suscripciones más caras el 50 % del tiempo. 2. **Ajustes de la app:** Si el usuario no se ha suscrito durante el flujo de onboarding, puedes crear un placement de paywall dentro de la app. Puede estar en los ajustes de la app o tras haber completado una acción objetivo específica. Como los usuarios dentro de la app tienden a reflexionar más antes de suscribirse, los productos de este paywall podrían ser algo menos costosos que los de la etapa de onboarding. 3. **Promo:** Si el usuario no se ha suscrito tras ver el paywall varias veces, puede ser señal de que los precios son demasiado altos para él o de que duda sobre las suscripciones. En ese caso, puedes mostrarle una oferta especial con la suscripción más asequible o incluso un producto de acceso de por vida. Esto puede ayudar a convencer a los usuarios sensibles al precio o escépticos ante las suscripciones para que realicen una compra. La mayoría de las apps tendrán una lógica y unos placements similares, siguiendo el recorrido del usuario y los momentos clave donde se pueden mostrar paywalls, onboardings o pruebas A/B para impulsar las conversiones y los ingresos. Puedes configurarlos en cada placement para experimentar y optimizar tus estrategias de monetización. --- # File: create-placement --- --- title: "Crear un placement" description: "Crea y gestiona placements en Adapty para mejorar el rendimiento de los paywalls." --- Un [placement](placements) es una ubicación concreta dentro de tu app móvil donde puedes mostrar un paywall, un onboarding o una prueba A/B. Por ejemplo, la selección de suscripción puede aparecer en el flujo de inicio, mientras que un producto consumible (como monedas de oro) podría mostrarse cuando al usuario se le acaban las monedas en un juego. Puedes mostrar los mismos o diferentes paywalls, onboardings o pruebas A/B en distintos placements o a diferentes segmentos de usuarios — denominados "audiencias" en Adapty. Consulta la sección [Elige placements con sentido](choose-meaningful-placements) para obtener consejos sobre cómo elegir el placement adecuado. :::tip También puedes crear placements de forma programática usando el [Developer CLI](developer-cli-reference#adapty-placements-create). ::: :::info Aunque el proceso de creación de placements es similar para paywalls y onboardings, no puedes crear un mismo placement para ambos, ya que procesan métricas distintas. ::: ## Crear y configurar un placement \{#create-and-configure-a-placement\} 1. Ve a **[Placements](https://app.adapty.io/placements)** desde el menú principal de Adapty. 2. Haz clic en **Create placement**. <img src="/assets/shared/img/create-placement-2.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Introduce un **Placement name**. Es un identificador interno en el Adapty Dashboard. Puedes editarlo más adelante si lo necesitas. 4. Introduce un **Placement ID**. Usarás este ID en el SDK de Adapty para llamar a los [paywalls](paywalls), [onboardings](onboardings) y [pruebas A/B](ab-tests) del placement. No podrás editarlo después, ya que es único para cada placement. A continuación, asigna un paywall, onboarding o prueba A/B al placement. Adapty es compatible con [audiencias](audience) — segmentos de usuarios basados en [segmentos](segments) — para que puedas mostrar contenido diferente a distintos grupos de usuarios. Si no necesitas segmentación, la audiencia predeterminada *All users* cubre a todos los usuarios. :::note Para continuar, asegúrate de haber creado el paywall, onboarding o prueba A/B que quieres lanzar, así como la audiencia que deseas especificar. ::: 1. En la ventana **Placements/ Your placement**, añade un paywall, onboarding o prueba A/B para mostrar a la audiencia predeterminada *All users*. Para ello, haz clic en el botón **Run paywall** o **Run A/B test** y selecciona el paywall, onboarding o prueba A/B deseado en la lista desplegable. 2. Si quieres usar más de una audiencia en el placement para crear paywalls personalizados adaptados a diferentes grupos de usuarios, haz clic en el botón **Add audience** y elige el segmento de usuarios que desees de la lista. <Zoom> <img src="/docs/img/placement-add-audience.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 3. Ahora añade el paywall, onboarding o prueba A/B que se mostrará a esta audiencia. 4. Añade tantas audiencias como necesites. 5. Si tienes más de una audiencia, comprueba que tienen las prioridades correctas. 6. Haz clic en el botón **Save and publish**. Una vez guardado y publicado tu placement, tienes todo lo necesario: usa el **Placement ID** en el código de tu app para obtenerlo y mostrarlo. ## Siguientes pasos \{#next-steps\} Mostrar paywalls en tu app: [iOS](ios-present-paywalls) | [Android](android-present-paywalls) | [React Native](react-native-present-paywalls) | [Flutter](flutter-present-paywalls) | [Unity](unity-present-paywalls) | [Kotlin Multiplatform](kmp-present-paywalls) | [Capacitor](capacitor-present-paywalls) Mostrar onboardings en tu app: [iOS](ios-present-onboardings) | [Android](android-present-onboardings) | [React Native](react-native-present-onboardings) | [Flutter](flutter-present-onboardings) | [Unity](unity-present-onboardings) | [Kotlin Multiplatform](kmp-present-onboardings) | [Capacitor](capacitor-present-onboardings) --- # File: edit-placement --- --- title: "Editar placement" description: "Aprende cómo editar placements en Adapty para optimizar la visibilidad de paywalls y la participación de los usuarios." --- Un [placement](placements) designa una ubicación específica dentro de tu app móvil donde se puede mostrar un paywall, un onboarding o una prueba A/B. Por ejemplo, la selección de suscripción puede aparecer en el flujo de inicio, mientras que un producto consumible (como monedas de oro) podría mostrarse cuando a un usuario se le agotan las monedas en un juego. Tienes la flexibilidad de mostrar los mismos o diferentes paywalls, onboardings o pruebas A/B en múltiples placements o segmentos de usuarios, denominados audiencias en Adapty. Para editar un placement existente: 1. Ve a **[Placements](https://app.adapty.io/placements)** desde el menú principal de Adapty. Si quieres editar un placement para onboarding, cambia a la pestaña **Onboardings**. 2. Haz clic en el placement que deseas editar. 3. Haz clic en **Edit placement** en la parte superior derecha. 4. Realiza los cambios que necesites. Para más detalles sobre las opciones de esta ventana, consulta la sección [Crear placement](create-placement). 5. Haz clic en el botón **Save and publish** para confirmar los cambios. --- # File: export-placements --- --- title: "Exportar placement" description: "Aprende cómo exportar placements en Adapty para optimizar la visibilidad de los paywalls y la interacción con los usuarios." --- Cuando trabajas con varios paywalls y onboardings, es importante saber cuáles se muestran a cada usuario. Puedes exportar todos los ajustes de tus [placement](placements) a un archivo CSV para ver qué paywall u onboarding aparece para cada audiencia y revisar tu configuración tras realizar cambios o ejecutar experimentos. :::tip Si te resulta más cómodo, puedes [exportar placements usando la API server-side](api-export-analytics/operations/retrievePlacementInfo). ::: Para exportar los placements de paywalls u onboardings: 1. Ve a **[Placements](https://app.adapty.io/placements)** en el menú principal. Cambia a la pestaña **Paywalls** u **Onboardings**, ya que los placements de cada uno se exportan por separado. 2. Haz clic en **Export to CSV**. <img src="/assets/shared/img/export-placement.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El archivo CSV exportado contiene la siguiente información sobre tus placements: - ID del placement - Nombre del placement - Nombre de la audiencia - Nombre del segmento - Nombre de la prueba A/B entre placements - Nombre de la prueba A/B - Nombre del paywall --- # File: delete-placement --- --- title: "Eliminar placement" description: "Descubre cómo eliminar un placement en Adapty sin afectar el rendimiento de tu paywall." --- Un [placement](placements) designa una ubicación específica dentro de tu app móvil donde se puede mostrar un paywall, onboarding o una prueba A/B. :::danger Aunque tienes la opción de eliminar cualquier placement, es fundamental asegurarte de no eliminar un placement que esté en uso activo en tu app móvil. Eliminar un placement de paywall activo hará que se muestre de forma permanente el paywall de respaldo local si lo has [configurado](fallback-paywalls), y no podrás reemplazarlo nunca con un paywall dinámico en las versiones de la app ya publicadas. ::: Para eliminar un placement existente: 1. Ve a **[Placements](https://app.adapty.io/placements)** desde el menú principal de Adapty. Si quieres eliminar un placement de onboarding, cambia a la pestaña **Onboardings**. 2. Haz clic en el botón de **3 puntos** junto al placement y selecciona la opción **Delete**. <img src="/assets/shared/img/delete-placement.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Delete placement** que se abre, escribe el nombre del producto que vas a eliminar. <img src="/assets/shared/img/8177c51-delete_placement.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en el botón **Delete forever** para confirmar la eliminación. --- # File: add-audience-paywall-ab-test --- --- title: "Añadir audiencia y paywall o prueba A/B a un placement" description: "Ejecuta pruebas A/B en paywalls para distintos segmentos de audiencia en Adapty." --- Las **audiencias** en Adapty son grupos de usuarios definidos por [segmentos](segments). Te permiten mostrar paywalls, onboardings y pruebas A/B a los usuarios que deben verlos. Construye segmentos con filtros para asegurarte de que cada grupo recibe el contenido adecuado. Cuando añades una audiencia a un [placement](placements), diriges paywalls, onboardings o pruebas A/B a un grupo de usuarios específico. Vincular una audiencia a un placement garantiza que los usuarios correctos vean el contenido correcto en el momento adecuado de su recorrido por la app. Abre el placement donde quieres añadir un paywall, un onboarding o una prueba A/B, o crea uno nuevo en el menú [Placements](https://app.adapty.io/placements). :::note Para continuar, asegúrate de haber creado el paywall, onboarding o prueba A/B que quieres lanzar, así como la audiencia que deseas especificar. ::: 1. En la ventana **Placements/ Your placement**, añade un paywall, onboarding o prueba A/B para mostrar a la audiencia predeterminada *All users*. Para ello, haz clic en el botón **Run paywall** o **Run A/B test** y selecciona el paywall, onboarding o prueba A/B deseado en la lista desplegable. 2. Si quieres usar más de una audiencia en el placement para crear paywalls personalizados adaptados a diferentes grupos de usuarios, haz clic en el botón **Add audience** y elige el segmento de usuarios que desees de la lista. <Zoom> <img src="/docs/img/placement-add-audience.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 3. Ahora añade el paywall, onboarding o prueba A/B que se mostrará a esta audiencia. 4. Añade tantas audiencias como necesites. 5. Si tienes más de una audiencia, comprueba que tienen las prioridades correctas. 6. Haz clic en el botón **Save and publish**. --- # File: change-audience-priority --- --- title: "Cambiar la prioridad de audiencia en un placement" description: "Ajusta las prioridades de audiencia en Adapty para dirigirte a los usuarios con ofertas personalizadas." --- Cuando tienes diferentes audiencias de usuarios en un mismo [placement](placements), un usuario puede pertenecer a más de una audiencia. Por ejemplo, si has definido audiencias como "Principiantes", "Corredores" y una audiencia general como "Todos los usuarios", es fundamental determinar qué audiencia concreta considerar primero cuando un usuario cae en varias categorías. <img src="/assets/shared/img/afee54f-2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En este caso, nos basamos en la prioridad de audiencia. La prioridad de audiencia es un orden numérico donde el n.º 1 es la más alta. Determina la secuencia en la que se comprueban las audiencias. En términos más sencillos, la prioridad de audiencia ayuda a Adapty a decidir qué audiencia aplicar primero al seleccionar el paywall, onboarding o prueba A/B que se mostrará. Si la prioridad de una audiencia es baja, los usuarios que potencialmente encajan pueden quedar excluidos y ser derivados a otra audiencia con mayor prioridad. Las audiencias multiplacement, es decir, las creadas para [pruebas A/B multiplacement](ab-tests#ab-test-types), siempre tienen prioridad sobre las audiencias normales. La audiencia "Todos los usuarios" siempre tiene la prioridad más baja, ya que es un fallback e incluye a todos los que no coinciden con ninguna otra audiencia. Para ajustar las prioridades de audiencia en un placement: 1. Al crear un nuevo placement o editar uno existente, haz clic en **Edit priority**. El botón solo es visible si se han añadido al menos tres audiencias al placement ("Todos los usuarios" y otras dos). Si hay menos, el orden es obvio: la audiencia "Todos los usuarios" va siempre al final. <img src="/assets/shared/img/edit-priority.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la ventana **Edit audience priorities** que se abre, arrastra y suelta las audiencias para reordenarlas correctamente. <img src="/assets/shared/img/reorder_audiences.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en el botón **Save**. --- # File: placement-metrics --- --- title: "Métricas de placement" description: "Analiza las métricas de placement en Adapty para mejorar el rendimiento de tus paywalls." --- Con Adapty tienes la flexibilidad de crear y gestionar múltiples placements en tu app, cada uno asociado a paywalls o pruebas A/B distintas. Esta versatilidad te permite dirigirte a segmentos de usuarios específicos, experimentar con distintas ofertas o modelos de precios, y optimizar la estrategia de monetización de tu app. Para obtener información valiosa sobre el rendimiento de tus placements y la interacción de los usuarios con tus ofertas, Adapty registra diversas interacciones y transacciones relacionadas con los paywalls mostrados. El sólido sistema de analíticas captura métricas como vistas, vistas únicas, compras, trials, reembolsos, tasas de conversión e ingresos. Las métricas recopiladas se actualizan continuamente en tiempo real y puedes consultarlas y analizarlas cómodamente desde el dashboard de Adapty. Tienes libertad para personalizar el rango de tiempo para el análisis, aplicar filtros según distintos parámetros y comparar métricas entre varios placements, segmentos de usuarios o productos. Las métricas de placement están disponibles en la lista de placements, donde puedes obtener una visión general del rendimiento de todos tus placements. Esta vista de alto nivel muestra métricas agregadas para cada placement, lo que te permite comparar su rendimiento e identificar tendencias. Para un análisis más detallado de cada placement, puedes navegar a las métricas de detalle del placement. En esta página encontrarás métricas exhaustivas específicas del placement seleccionado. Estas métricas ofrecen información más profunda sobre cómo está funcionando un placement concreto, lo que te permite evaluar su efectividad y tomar decisiones basadas en datos. <img src="/assets/shared/img/3e711fc-CleanShot_2023-07-26_at_14.55.042x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Controles de métricas \{#metrics-controls\} El sistema muestra las métricas según el período de tiempo seleccionado y las organiza de acuerdo con el parámetro de la columna izquierda con cuatro niveles de sangría. #### Opciones de vista para los datos de métricas \{#view-options-for-metrics-data\} La página de métricas de placement ofrece dos opciones de vista: basada en paywall y basada en audiencia. <img src="/assets/shared/img/9d26b32-Export-1690376094858.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En la vista basada en paywall, las métricas se agrupan por los placements asociados al paywall. Esto permite analizar las métricas según los distintos placements. En la vista basada en audiencia, las métricas se agrupan por la audiencia objetivo del paywall. Los usuarios pueden evaluar métricas específicas de los distintos segmentos de audiencia. #### Filtrado por fecha de instalación del perfil \{#profile-install-date-filtration\} <img src="/assets/shared/img/b1e4155-Export-1690375904086.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### Rangos de tiempo \{#time-ranges\} Puedes elegir entre varios períodos de tiempo para analizar los datos de métricas, lo que te permite centrarte en duraciones específicas como días, semanas, meses o rangos de fechas personalizados. <img src="/assets/shared/img/15d2c3e-CleanShot_2023-07-26_at_16.49.272x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: Adapty ofrece potentes herramientas para filtrar y personalizar el análisis de métricas según tus necesidades. Desde la página de métricas de Adapty tienes acceso a distintos rangos de tiempo, opciones de agrupación y posibilidades de filtrado. - ✅ Filtrar por: Audiencia, paywall, grupo de paywall, placement, país, store. - ✅ Agrupar por: Segmento, store y producto #### Gráfico de métrica única \{#single-metrics-chart\} Uno de los componentes clave de la página de métricas de placement es la sección de gráficos, que representa visualmente las métricas seleccionadas y facilita el análisis. La sección de gráficos de la página de métricas de placements incluye un gráfico de barras horizontales que representa visualmente los valores de la métrica elegida. Cada barra del gráfico corresponde a un valor de métrica y es proporcional en tamaño, lo que facilita entender los datos de un vistazo. La línea horizontal indica el período analizado y la columna vertical muestra los valores numéricos de las métricas. El valor total de todos los valores de métricas se muestra junto al gráfico. <img src="/assets/shared/img/4623c5b-Export-1690375597411.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Además, al hacer clic en el icono de flecha en la esquina superior derecha de la sección del gráfico, la vista se amplía y muestra las métricas seleccionadas en toda la línea del gráfico. #### Resumen total de métricas \{#total-metrics-summary\} Junto al gráfico de métrica única se muestra la sección de resumen total de métricas, que presenta los valores acumulados para las métricas seleccionadas en un momento concreto, con la posibilidad de cambiar la métrica mostrada mediante un menú desplegable. <img src="/assets/shared/img/0f647cf-CleanShot_2023-07-26_at_14.55.492x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Definiciones de métricas \{#metrics-definitions\} Descubre el potencial de las métricas de placement con nuestras definiciones completas. Desde los ingresos hasta las tasas de conversión, obtén información valiosa que impulsará tus estrategias de monetización y el éxito de tu app. <img src="/assets/shared/img/771a0f0-Export-1690375049771.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### Ingresos \{#revenue\} Esta métrica representa el importe total en USD generado por compras y renovaciones dentro de placements específicos. Ten en cuenta que el cálculo de ingresos no incluye la comisión de Apple App Store ni de Google Play Store y se calcula antes de deducir cualquier tarifa. #### Proceeds \{#proceeds\} Esta métrica representa el importe real en USD recibido por el propietario de la app procedente de compras y renovaciones dentro de placements específicos, tras deducir la comisión aplicable de Apple App Store o Google Play Store. Refleja los ingresos netos que contribuyen directamente a las ganancias de la app. Para más información sobre cómo se calculan los proceeds, puedes consultar la [documentación](analytics-cohorts#revenue-vs-proceeds) de Adapty. #### ARPPU \{#arppu\} ARPPU son las siglas de Average Revenue Per Paying User (ingreso medio por usuario de pago) y mide el ingreso promedio generado por cada usuario de pago dentro de placements específicos. Se calcula dividiendo los ingresos totales entre el número de usuarios de pago únicos. Por ejemplo, si los ingresos totales son 15.000 $ y hay 1.000 usuarios de pago, el ARPPU sería de 15 $. #### ARPAS \{#arpas\} El ARPAS, o Average Revenue Per Active Subscriber (ingreso medio por suscriptor activo), permite medir el ingreso promedio generado por cada suscriptor activo dentro de placements específicos. Se calcula dividiendo los ingresos totales entre el número de suscriptores que han activado un trial o una suscripción. Por ejemplo, si los ingresos totales son 5.000 $ y hay 1.000 suscriptores, el ARPAS sería de 5 $. Esta métrica ayuda a evaluar el potencial de monetización promedio por suscriptor. #### ARPU \{#arpu\} Solo para placements de onboarding. El ARPU es el ingreso medio por usuario que visualizó el onboarding. Se calcula dividiendo los ingresos totales entre el número de espectadores únicos. #### CR único a compras \{#unique-cr-to-purchases\} La tasa de conversión única a compras se calcula dividiendo el número de compras dentro de placements específicos entre el número de vistas únicas. Se centra en la proporción de compras respecto al número único de vistas, ofreciendo información sobre la efectividad de convertir visitantes únicos dentro de placements específicos en clientes de pago. #### CR a compras \{#cr-to-purchases\} La tasa de conversión a compras se calcula dividiendo el número de compras dentro de placements específicos entre el número total de vistas de paywalls. Indica el porcentaje de vistas dentro de placements específicos que resultan en compras, ofreciendo información sobre la efectividad de tu paywall para convertir usuarios en clientes de pago. #### CR único a trials \{#unique-cr-to-trials\} La tasa de conversión única a trials se calcula dividiendo el número de trials iniciados dentro de placements específicos entre el número de vistas únicas. Mide el porcentaje de vistas únicas dentro de placements específicos que resultan en activaciones de trial, ofreciendo información sobre la efectividad de tu paywall para convertir visitantes únicos en usuarios de trial. #### Compras \{#purchases\} Las compras representan el total acumulado de diversas transacciones realizadas en el paywall dentro de placements específicos. Las siguientes transacciones están incluidas en esta métrica (las renovaciones no están incluidas): - Nuevas compras realizadas directamente dentro de placements específicos. - Conversiones de trials que se activaron inicialmente dentro de placements específicos. - Rebajas, mejoras y cambios de tipo de suscripciones realizados dentro de placements específicos. - Restauraciones de suscripciones dentro de placements específicos, como cuando una suscripción se reactiva tras su vencimiento sin renovación automática. Al considerar estos diferentes tipos de transacciones, la métrica de compras ofrece una visión completa de la actividad de adquisición y monetización dentro de placements específicos. #### Trials \{#trials\} La métrica de trials representa el número total de trials que se han activado dentro de placements específicos. Refleja el número de usuarios que han iniciado períodos de trial a través de tu paywall dentro de esos placements. Esta métrica ayuda a hacer un seguimiento de la efectividad de tu oferta de trial y puede ofrecer información sobre la participación de los usuarios y la conversión de trials a suscripciones de pago. #### Trials cancelados \{#trials-canceled\} La métrica de trials cancelados representa el número de trials dentro de placements específicos en los que se ha desactivado la función de renovación automática. Esto ocurre cuando los usuarios cancelan manualmente el trial, lo que indica su decisión de no continuar con la suscripción una vez finalizado el período de trial. Hacer un seguimiento de los trials cancelados proporciona información valiosa sobre el comportamiento de los usuarios y te permite entender la tasa a la que los usuarios optan por abandonar el trial dentro de placements específicos. #### Reembolsos \{#refunds\} La métrica de reembolsos representa el número de compras y suscripciones reembolsadas dentro de placements específicos. Esto incluye transacciones que han sido revertidas o reembolsadas por diversos motivos, como solicitudes de clientes, problemas de pago u otras políticas de reembolso aplicables. #### Tasa de reembolso \{#refund-rate\} La tasa de reembolso se calcula dividiendo el número de reembolsos dentro de placements específicos entre el número de primeras compras (las renovaciones no están incluidas). Por ejemplo, si hay 5 reembolsos y 1.000 primeras compras, la tasa de reembolso sería del 0,5 %. #### Vistas \{#views\} La métrica de vistas representa el número total de veces que el paywall dentro de placements específicos ha sido visualizado por los usuarios. Cada vez que un usuario visita el paywall dentro de esos placements, se cuenta como una vista independiente. Hacer un seguimiento de las vistas te ayuda a entender el nivel de participación e interacción de los usuarios con tu paywall, ofreciendo información sobre el comportamiento de los usuarios y la efectividad de la ubicación y el diseño de tu paywall dentro de áreas específicas de tu app. #### Vistas únicas \{#unique-views\} La métrica de vistas únicas representa el número de instancias únicas en las que el paywall dentro de placements específicos ha sido visualizado por los usuarios. A diferencia de las vistas totales, que cuentan cada visita como una vista independiente, las vistas únicas cuentan la visita de cada usuario al paywall dentro de esos placements solo una vez, independientemente de cuántas veces acceda a él. Hacer un seguimiento de las vistas únicas proporciona una medida más precisa de la participación de los usuarios y el alcance de tu paywall dentro de placements específicos, ya que se centra en usuarios individuales en lugar del número total de visitas. #### Completaciones y completaciones únicas \{#completions--unique-completions\} Solo para placements de onboarding. Las completaciones cuentan el número de veces que los usuarios completan tu placement de onboarding, es decir, que recorren desde la primera hasta la última pantalla. Si alguien lo completa dos veces, eso son dos **completaciones** pero una **completación única**. #### Tasa de completaciones únicas \{#unique-completions-rate\} Solo para placements de onboarding. El número de completaciones únicas dividido entre el número de vistas únicas. Esta métrica te ayuda a entender cómo interactúan los usuarios con el placement de onboarding y a realizar cambios si observas que los usuarios lo ignoran. --- # File: create-access-level --- --- title: "Crear nivel de acceso" description: "Crea y asigna niveles de acceso en Adapty para una mejor segmentación de usuarios." --- Los niveles de acceso te permiten controlar lo que los usuarios de tu app pueden hacer sin necesidad de codificar IDs de productos específicos. Cada producto define cuánto tiempo obtiene el usuario un determinado nivel de acceso. Así, cuando un usuario realiza una compra, Adapty concede acceso a la app durante un período específico (para suscripciones) o de forma permanente (para compras de por vida). Cuando creas una app en el Adapty Dashboard, el nivel de acceso `premium` se genera automáticamente. Este es el nivel de acceso predeterminado y no se puede eliminar. :::tip También puedes crear niveles de acceso de forma programática usando el [Developer CLI](developer-cli-reference#adapty-access-levels-create). ::: Para crear un nuevo nivel de acceso: 1. Ve a **[Products](https://app.adapty.io/access-levels)** desde el menú principal de Adapty y selecciona la pestaña **Access levels**. <img src="/assets/shared/img/access-level-list.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en **Create access level**. <img src="/assets/shared/img/b8646ca-image.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Create access level**, asígnale un ID. Este ID servirá como identificador dentro de tu app, permitiendo el acceso a funciones adicionales cuando el usuario realice una compra. Además, este identificador ayuda a distinguir un nivel de acceso de otros dentro de la app. Asegúrate de que sea claro y fácil de entender para tu comodidad. 4. Haz clic en **Create access level** para confirmar la creación del nivel de acceso. --- # File: assigning-access-level-to-a-product --- --- title: "Asignar nivel de acceso a un producto" description: "Asigna niveles de acceso a los productos para optimizar la gestión de suscripciones." --- Cada [producto](product) necesita un nivel de acceso asociado para garantizar que los usuarios reciban el contenido restringido correspondiente tras la compra. Adapty determina automáticamente la duración de la suscripción, que sirve como fecha de expiración del nivel de acceso. En el caso de un producto de acceso de por vida, si un usuario lo compra, el nivel de acceso permanece activo indefinidamente sin fecha de expiración. Para vincular un nivel de acceso a un producto: 1. Al [configurar un producto](create-product), selecciona el nivel de acceso en la lista **Access Level ID**. <img src="/assets/shared/img/access-level-product.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en **Save**. --- # File: give-access-level-to-specific-customer --- --- title: "Asignar nivel de acceso a un cliente específico" description: "Asigna niveles de acceso específicos a clientes usando las herramientas avanzadas de Adapty." --- Puedes ajustar manualmente el nivel de acceso de un cliente concreto directamente desde el Adapty Dashboard. Esto resulta muy útil en escenarios de soporte. Por ejemplo, si quieres ampliar el uso premium de un usuario una semana extra como agradecimiento por haber dejado una reseña estupenda. ## Asignar nivel de acceso a un cliente específico en el Adapty Dashboard \{#give-access-level-to-a-specific-customer-in-the-adapty-dashboard\} 1. Ve a **[Profiles and Segments](https://app.adapty.io/placements)** desde el menú principal de Adapty. <img src="/assets/shared/img/profiles-list.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el cliente al que quieres conceder acceso. 3. Haz clic en **Add access level**. <img src="/assets/shared/img/add-access-level.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Selecciona el nivel de acceso que quieres conceder y cuándo debe expirar para ese cliente. 5. Haz clic en **Apply**. ## Asignar nivel de acceso a un cliente específico mediante la API \{#give-access-level-to-a-specific-customer-via-api\} También puedes conceder un nivel de acceso a un cliente desde tu servidor usando la API de Adapty. Esto es muy útil si tienes bonificaciones por referidos u otros eventos relacionados con tus productos. Consulta más detalles en la página [Conceder nivel de acceso con la API del servidor](api-adapty/operations/grantAccessLevel). --- # File: local-access-levels --- --- title: "Niveles de acceso locales" description: "Gestiona los niveles de acceso en caso de interrupciones temporales." --- :::important Ten en cuenta lo siguiente: - Los niveles de acceso locales son compatibles con el SDK de Adapty a partir de la versión 3.12. - Por defecto, los niveles de acceso locales están desactivados en Android por seguridad adicional. Si los necesitas, actívalos durante la inicialización del SDK: [Android](sdk-installation-android#enable-local-access-levels), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter#enable-local-access-levels-android). ::: Cada producto que configuras tiene un [**nivel de acceso**](access-level) asociado. Cuando tus usuarios realizan una compra, el SDK de Adapty asigna el nivel de acceso al [perfil](profiles-crm) del usuario, por lo que debes usar este nivel de acceso para determinar si los usuarios pueden acceder al contenido de pago en la app. El SDK de Adapty es muy fiable y es muy raro que sus servidores no estén disponibles. Sin embargo, incluso en ese caso excepcional, tus usuarios no lo notarán. Si un usuario realiza una compra pero Adapty no puede recibir respuesta, el SDK pasa a verificar las compras directamente en el store. Por tanto, el nivel de acceso se concede de forma local en la app y no se necesita ninguna configuración adicional para activarlo. El SDK lo gestiona automáticamente en segundo plano, y los usuarios accederán a lo que han pagado con total normalidad. Ten en cuenta lo siguiente sobre cómo funcionan los niveles de acceso locales: - Cuando los usuarios vuelven a estar en línea, la información de las transacciones se envía automáticamente a los servidores de Adapty, que aplican las transacciones al perfil del usuario y devuelven el perfil actualizado al SDK. - Los datos actualizados no aparecerán en los análisis de Adapty hasta que se envíen los datos. - Los niveles de acceso locales solo funcionan cuando los servidores de Adapty están caídos. En caso contrario, el SDK utilizará los datos en caché. - Los niveles de acceso locales no funcionan con productos consumibles, excepto cuando un producto consumible tiene asignado un tipo de suscripción (mensual, anual, semanal, etc.) en el dashboard de Adapty. --- # File: create-onboarding --- --- title: "Crear onboarding" --- Los [onboardings](onboardings) presentan a los nuevos usuarios el valor, las funciones y los consejos de uso de tu aplicación móvil. ## Paso 1. Crear un onboarding \{#step-1-create-an-onboarding\} Para crear un nuevo onboarding en el Adapty Dashboard: 1. Ve a **Onboardings** desde el menú principal de Adapty. Esta página muestra un resumen de todos los onboardings que has configurado, junto con sus métricas. Haz clic en **Create onboarding**. <img src="/assets/shared/img/create-onboarding1.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Escribe un nombre descriptivo para tu onboarding y haz clic en **Proceed to build onboarding**. <img src="/assets/shared/img/create-onboarding2.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Se te redirigirá al constructor de onboardings. Contiene una plantilla de demo por defecto que puedes explorar para entender cómo los onboardings recopilan datos y cómo puedes [personalizarlos mediante variables y cuestionarios](onboarding-user-engagement). Siéntete libre de eliminar las pantallas que no necesites y [diseñar tu propia experiencia de onboarding](design-onboarding) allí. <img src="/assets/shared/img/create-onboarding3.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Cuando esté listo, haz clic en el botón **Preview** en la parte superior derecha. Completa el flujo de onboarding tú mismo para asegurarte de que todo funciona como se espera. <img src="/assets/shared/img/create-onboarding4.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Si todo funciona correctamente, haz clic en **Publish** en la parte superior derecha. Espera a que se publique antes de volver a Adapty. De lo contrario, perderás tu progreso. :::danger Si no haces clic en **Publish**, el SDK no podrá obtener el onboarding que has creado. ::: <img src="/assets/shared/img/create-onboarding5.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Una vez publicado tu onboarding, haz clic en **Back to Adapty**. Tu onboarding está creado y puedes añadirlo a un placement para empezar a usarlo. ## Paso 2. Crear un placement para tu onboarding \{#step-2-create-a-placement-for-your-onboarding\} 1. Ve a **Placements** desde el menú principal y cambia a la pestaña **Onboardings**. Haz clic en **Create placement**. <img src="/assets/shared/img/create-onboarding6.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Introduce el nombre y el ID del placement. Luego, haz clic en **Run onboarding** y selecciona el onboarding que se mostrará a todos los usuarios. 3. Si tienes un onboarding diferente preparado para un grupo de usuarios específico, [añade más audiencias](audience) y selecciona un onboarding distinto para ellas. ## Paso 3. Integrar el onboarding en tu app \{#step-3-integrate-the-onboarding-into-your-app\} :::important Los onboardings están disponibles para apps que usen el SDK de Adapty v3.8.0+ (iOS, Android, React Native, Flutter), v3.14.0+ (Unity) o v3.15.0+ (Kotlin Multiplatform, Capacitor). ::: Para empezar a mostrar onboardings en tu app, intégralos con el SDK de Adapty: - [iOS](ios-onboardings) - [Android](android-onboardings) - [React Native](react-native-onboardings) - [Flutter](flutter-onboardings) - [Unity](unity-onboardings) - [Kotlin Multiplatform](kmp-onboardings) - [Capacitor](capacitor-onboardings) Para entender qué onboarding funciona mejor, también puedes ejecutar [pruebas A/B](ab-tests). --- # File: onboarding-layout --- --- title: "Diseño de onboarding" description: "Constructor de onboarding de Adapty: contenedores para el diseño, ajuste del espaciado y estilo de los elementos." --- El constructor de onboarding sin código para aplicaciones móviles ofrece dos capas de diseño: - Diseño de pantalla: padding global y cuadrícula mediante contenedores. - Diseño de elementos: espaciado, posición, bordes y sombras por elemento. :::tip Para reordenar pantallas o elementos, simplemente arrástralos y suéltalos en el panel izquierdo. ::: ## Diseño de pantalla \{#screen-layout\} Puedes ajustar una pantalla de dos formas: - [Usando los ajustes de estilo de pantalla](#screen-style-settings) - [Usando contenedores](#containers) ### Ajustes de estilo de pantalla \{#screen-style-settings\} Para reducir o aumentar la distancia entre los elementos y el borde de la pantalla: 1. Selecciona la pantalla en el panel izquierdo. 2. Ve a la pestaña **Styles** en la parte derecha. 3. Establece el padding superior, inferior y horizontal en la sección **Padding**. <img src="/assets/shared/img/screen-layout.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Contenedores \{#containers\} Es posible que quieras añadir texto e imágenes en paralelo, galerías deslizables o ventanas emergentes modales. Los contenedores lo facilitan, ya que te permiten crear columnas, filas, carruseles y superposiciones centradas. Para añadir un contenedor: 1. Haz clic en **Add** en la parte superior izquierda. 2. Ve a **Containers** y elige uno: - **Columns**: Divide la pantalla en secciones verticales para contenido en paralelo (p. ej., diseños de dos columnas con texto o imagen más texto). - **Rows**: Alinea los elementos en una banda horizontal con espaciado uniforme. - **Carousel**: Permite a los usuarios deslizarse por una serie de tarjetas. - **Popup**: Muestra contenido en una superposición centrada sobre la página. 3. Crea los elementos que quieras añadir y arrástralos al contenedor desde el menú izquierdo. <img src="/assets/shared/img/containers.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Diseño de elementos \{#element-layout\} Para ajustar cada elemento de forma individual: 1. Selecciona el elemento en el panel izquierdo. 2. Ve a **Styles** en el menú derecho. 3. En la sección **Container**, establece: - **Offset**: Desplaza el elemento horizontalmente o verticalmente. - **Position**: Define el punto de anclaje del elemento: - **In content**: Flujo normal del documento. - **Attached**: Posición fija: permanece visible en el viewport (p. ej., botón fijo en la parte inferior). - **Attached on scroll**: Se fija después de desplazarse hasta él (comportamiento sticky). - **Padding**: Define el espacio interior entre el contenido del elemento y su borde. - **Background**: Aplica un color sólido detrás del elemento. Asegúrate de que el fondo del elemento coincida con el [fondo de la pantalla](#screen-background-customization) (p. ej., usa gris o negro para onboardings con pantallas mayoritariamente oscuras). - **Roundness**: Determina el radio de las esquinas del elemento. - **Border**: Añade un contorno alrededor del elemento y especifica su grosor. - **Border Color**: Especifica el color del borde del elemento. - **Add shadows**: Añade una sombra con offset, desenfoque/expansión y color configurables. :::note Además de estos ajustes básicos de diseño de elementos, puedes personalizar aún más la apariencia de elementos específicos como [multimedia](onboarding-media#media-customization), [texto](onboarding-text#text--list-customization), [botones](onboarding-buttons#button-customization), [cuestionarios](onboarding-quizzes#quiz-customization) y otros usando la pestaña **Styles** del elemento. ::: <img src="/assets/shared/img/element-layout.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Personalización del fondo de pantalla \{#screen-background-customization\} El fondo no solo afecta al diseño de tu onboarding, sino también a la pantalla de carga hasta que el onboarding se cargue por completo. Puedes rellenar el fondo de tu onboarding con un color o subir una imagen o vídeo: 1. Selecciona la pantalla en el panel izquierdo. 2. Ve a la pestaña **Styles** en la parte derecha. 3. En la sección **Background**, selecciona un color de fondo o haz clic en el área de carga para subir una imagen o vídeo. Para los archivos multimedia, sigue los requisitos de [formatos y tamaño compatibles](onboarding-media#supported-formats-and-size). :::tip Para que las transiciones entre pantallas sean suaves, elige un color de fondo que coincida con el diseño general de tu onboarding (p. ej., usa gris o negro para onboardings con pantallas mayoritariamente oscuras) o personaliza la [pantalla de inicio](ios-present-onboardings#add-smooth-transitions-between-the-splash-screen-and-onboarding). ::: --- # File: onboarding-media --- --- title: "Contenido multimedia del onboarding" description: "Crea onboardings atractivos en Adapty con imágenes, vídeos, gráficos animados y fondos personalizados." --- Los elementos multimedia te ayudan a crear onboardings atractivos que demuestran el valor de tu app y guían a los usuarios hacia la conversión. Usa imágenes y vídeos para mostrar funciones, gráficos animados para visualizar beneficios y fondos estratégicos para reforzar tu marca. ## Imágenes y vídeos \{#images-and-videos\} Las imágenes y los vídeos son perfectos para previsualizar funciones y hacer recorridos por la app. Mostrar a los usuarios lo que van a desbloquear es más efectivo que describirlo. Para subir contenido multimedia: 1. Haz clic en **Add** arriba a la izquierda. 2. Ve a **Media & Display** y elige **Image/Video**. 3. Haz clic en el área de carga de la derecha y selecciona la imagen o el vídeo que quieras subir. <img src="/assets/shared/img/onboarding-media.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Formatos y tamaño admitidos \{#supported-formats-and-size\} | Especificación | Detalles | |-----------------------|---------------------------------| | Extensiones | PNG, JPG, JPEG, WEBP, MP4, WEBM | | Tamaño máximo | 15 MB | | Resolución máxima | 1920x1920 | Si quieres añadir un elemento animado no compatible (como Lottie), puedes convertirlo a vídeo (por ejemplo, con [esta herramienta](https://www.lottielab.com/lottie/lottie-to-video)) e incrustarlo como vídeo. ## Gráficos animados \{#animated-charts\} Los gráficos son animaciones que visualizan resultados y personalizan la experiencia del usuario en tus onboardings. Para añadir un gráfico: 1. Haz clic en **Add** arriba a la izquierda. 2. Ve a **Media & Display** y elige **Chart**. 3. Personaliza tu gráfico en la parte derecha: - **Type**: Elige un tipo de curva. Ten en cuenta que el tipo de curva no está directamente relacionado con los valores. - Insignias **Left** y **Right**: Nombra los puntos inicial y final del gráfico. - **X Labels** y **Date Range**: Por defecto, el eje X muestra fechas. Puedes personalizar el rango de fechas o especificar valores personalizados. - **Animation Duration**: Establece la duración de la animación para que se adapte a tu diseño. :::tip Usa [variables](onboarding-variables) para la visualización dinámica de datos en los gráficos. ::: <img src="/assets/shared/img/chart-onboarding.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Personalización de multimedia \{#media-customization\} Además del [diseño de elementos](onboarding-layout#element-layout) básico, puedes personalizar aún más la apariencia de imágenes, vídeos y gráficos: 1. Selecciona el elemento en la parte izquierda. 2. Ve a **Styles** en el menú de la derecha. 3. Según el tipo de elemento, puedes ajustar las siguientes opciones: - **Image/video**: Anchura, altura, redondez, opacidad, alineación. - **Chart**: Color y grosor de línea, relleno de insignia, redondez, fuente y color, fuente y color del eje X. ## Eliminar multimedia \{#delete-media\} Puedes eliminar todo el elemento multimedia o solo el archivo para subir uno nuevo: - **Eliminar el elemento multimedia**: Haz clic derecho sobre el elemento multimedia en la parte izquierda y selecciona **Delete**. - **Eliminar el archivo multimedia**: Haz clic en la vista previa del multimedia en la derecha. Aparecerá el área de carga para el nuevo archivo. <img src="/assets/shared/img/onboarding-delete.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: onboarding-text --- --- title: "Texto en onboarding" description: "Añade y estiliza títulos, subtítulos, párrafos y listas en el constructor de onboarding de Adapty, y personaliza el texto para experiencias de usuario acordes a tu marca." --- Los elementos de texto te ayudan a crear conversaciones claras y personalizadas con tus usuarios. Añade títulos, párrafos o listas con un solo clic, dales estilo para que se adapten a tu marca y usa [variables dinámicas](onboarding-variables) para personalizar el contenido para cada usuario. ## Añadir texto \{#add-text\} Puedes añadir varios elementos de texto a las pantallas de tu onboarding. Para añadir elementos de texto: 1. Haz clic en **Add** en la parte superior izquierda. 2. Ve a **Typography** y elige uno: - **Title**: titulares principales o títulos de pantalla que captan la atención al instante. - **Subtitle**: una línea de apoyo breve que amplía el título. - **Text**: texto de cuerpo para descripciones de funciones, avisos legales o frases inspiradoras. - **Rich text**: formato mixto para preguntas frecuentes, términos de servicio o cualquier texto que necesite enlaces y énfasis. 3. Haz clic en el nuevo elemento para editar su contenido. 4. (Opcional) Selecciona cualquier parte del texto para abrir un tooltip de personalización rápida: negrita, cursiva, enlaces, color de texto o restablecer estilos. Para editar un elemento de texto existente, simplemente haz clic en él y realiza los cambios en modo WYSIWYG. :::tip Si necesitas usar el mismo elemento de texto en varias pantallas, puedes copiarlo y pegarlo: selecciona el elemento y pulsa Ctrl+C (o ⌘+C en Mac), navega a otra pantalla, selecciona el elemento después del cual quieres pegar y pulsa Ctrl+V (o ⌘+V en Mac). ::: <img src="/assets/shared/img/onboarding-text.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Añadir listas \{#add-lists\} Puedes añadir listas numeradas y con viñetas: 1. Haz clic en **Add** en la parte superior izquierda. 2. Ve a **Typography** y elige uno: - **Numbered list**: perfecta para guías paso a paso. - **Bullet list**: destaca beneficios o características clave sin implicar un orden. 3. Ve a la pestaña **Element** de la derecha para editar los elementos de la lista o subir una imagen como marcador de elemento. Para editar un elemento de lista existente, haz clic en él y realiza los cambios en la pestaña **Element**. <img src="/assets/shared/img/onboarding-list.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Añadir enlaces externos \{#add-external-links\} Para añadir un enlace externo: 1. Haz clic en **Add** en la parte superior izquierda. 2. En la sección **Typography**, selecciona **Title**, **Subtitle**, **Text** o **Rich text**. 3. Introduce tu texto. 4. Selecciona el texto que quieres convertir en enlace. 5. Haz clic en el icono **Link** en el menú de personalización rápida que aparece sobre el texto. 6. Pega la URL externa. 7. Haz clic en **✓** para aplicar el enlace. :::info En versiones del SDK de Adapty anteriores a la 3.15.1, los enlaces externos en los onboardings se abren en el navegador predeterminado del dispositivo. A partir del SDK de Adapty v3.15.1, los enlaces externos se abren de forma predeterminada en un navegador dentro de la app, lo que permite a los usuarios permanecer en tu app sin cambiar a otra aplicación. Si lo necesitas, puedes [personalizar este comportamiento](ios-present-onboardings#customize-how-links-open-in-onboardings). ::: <img src="/assets/shared/img/onboarding-url.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Personalización de texto y listas \{#text--list-customization\} Además del [diseño de elemento](onboarding-layout#element-layout) básico, puedes personalizar la apariencia del texto y las listas: 1. Selecciona el elemento en la parte izquierda. 2. Ve a **Styles** en el menú de la derecha. 3. Según el tipo de elemento, puedes ajustar las siguientes opciones: - **Text**: color del párrafo, fuente, alineación y altura de línea, color de los enlaces, fuente y decoración. - **List**: color del texto y del marcador de texto, fuente, ancho, alto y redondez de la imagen del marcador. :::tip Para ir más rápido: - Después de personalizar un elemento de texto, puedes hacer clic en **Apply styles to all paragraphs** para aplicar los mismos estilos en todas las pantallas del onboarding de una sola vez. - Para cambiar la fuente de todos los elementos de texto de una pantalla concreta, selecciona la pantalla y ve a **Styles > Text** en el menú de la derecha. ::: <img src="/assets/shared/img/onboarding-customization.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Fuentes \{#fonts\} En el constructor de onboarding puedes elegir entre una gran variedad de fuentes. :::info La carga de fuentes personalizadas aún no está disponible. ::: Puedes configurar las fuentes de forma global para todo el onboarding o de forma individual para cada elemento: - Para establecer la fuente principal que se usará en el onboarding: 1. Selecciona cualquier pantalla en la parte izquierda. 2. Cambia a la pestaña **Styles** y selecciona una **Font**. 3. Todos los elementos de todas las pantallas heredarán la fuente que hayas seleccionado. - Para establecer una fuente para un único elemento: 1. Selecciona el elemento. 2. Cambia a la pestaña **Styles** y selecciona una **Font**. 3. La fuente seleccionada se usará para ese elemento aunque cambies la fuente principal. :::note No puedes usar SF Pro porque no es adecuada para aplicaciones multiplataforma, pero te recomendamos usar Inter en su lugar, ya que son bastante similares. ::: --- # File: onboarding-buttons --- --- title: "Botones de onboarding" description: "Añade botones para navegar entre pantallas, cerrar el onboarding o ir al paywall." --- Aprende a añadir y configurar botones estándar, animados, glossy y de cuenta atrás en el editor de onboarding sin código de Adapty. Guía a los usuarios, aumenta las conversiones y cierra tu flujo, todo sin escribir una sola línea de código. ## Añadir botones \{#add-buttons\} Usa un Pulse Button para captar la atención y aumentar el porcentaje de clics. O añade un Countdown Button en las pantallas de expiración de prueba para generar urgencia e impulsar las actualizaciones. Para añadir un botón: 1. Haz clic en **Add** en la parte superior izquierda. 2. Selecciona **Buttons** y elige uno: - **Button** - **Pulse Button** - **Glossy Button** - **Pulse Glossy Button** - **Countdown Button** 3. Elige la [acción del botón](onboarding-actions) en el desplegable **On Press** de la derecha: - **Navigate**: Lleva al usuario a una pantalla de onboarding concreta. - **Show/Hide element**: Muestra u oculta un elemento de destino. - **Open paywall**: Abre la pantalla del paywall para realizar compras. Aprende a gestionar la apertura del paywall en [iOS](ios-handling-onboarding-events#opening-a-paywall), [Android](android-handle-onboarding-events#opening-a-paywall), [React Native](react-native-handling-onboarding-events#opening-a-paywall) y [Flutter](flutter-handling-onboarding-events#opening-a-paywall). - **Scroll to**: Desplaza la página hasta un elemento específico. - **Custom**: Ejecuta la lógica de tu evento personalizado. Por ejemplo, puede usarse para abrir una ventana de inicio de sesión o solicitar permisos de la app. Aprende a gestionar acciones personalizadas en [iOS](ios-handling-onboarding-events#custom-actions), [Android](android-handle-onboarding-events#custom-actions), [React Native](react-native-handling-onboarding-events#handle-custom-actions) y [Flutter](flutter-handling-onboarding-events#handle-custom-actions). - **Close onboarding**: Cierra el flujo de onboarding. Aprende a gestionar el cierre del onboarding en [iOS](ios-handling-onboarding-events#closing-onboarding), [Android](android-handle-onboarding-events#closing-onboarding), [React Native](react-native-handling-onboarding-events#closing-onboarding) y [Flutter](flutter-handling-onboarding-events#closing-onboarding). Para editar el texto de un botón, haz clic en la vista previa del botón y realiza los cambios en modo WYSIWYG. :::tip [Anida un popup](onboarding-layout#containers) con un Pulse Glossy Button para hacer upsell de funciones premium a mitad del flujo. <img src="/assets/shared/img/popup.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '300px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ::: <img src="/assets/shared/img/add-button.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Personalización de botones \{#button-customization\} Además del [diseño de elementos](onboarding-layout#element-layout) básico, puedes personalizar el aspecto de los botones: 1. Selecciona el elemento de botón en la parte izquierda. 2. Ve a **Styles** en el menú de la derecha. 3. Según el tipo de botón, puedes ajustar estas opciones: - **Todos los botones**: Anchura, relleno, fondo, redondez, borde, color de borde, sombras, flecha siguiente y tamaño de flecha, desplazamiento derecho, color del texto o de la cuenta atrás, fuente y altura de línea. - **Pulse Button**: Duración de la animación y easing, color y tamaño de la sombra, crecimiento del botón. - **Glossy Button**: Color, anchura, ángulo y duración de la animación de la línea glossy. - **Pulse Glossy Button**: Duración de la animación y easing, color y tamaño de la sombra, crecimiento del botón, color, anchura, ángulo y duración de la animación de la línea glossy. <img src="/assets/shared/img/button-customize.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: onboarding-html --- --- title: "HTML personalizado" description: "Inserta fragmentos de HTML pequeños y ligeros en el editor de onboarding sin código de Adapty para crear widgets interactivos e integraciones de terceros." --- El HTML personalizado te permite crear interacciones únicas, incrustar widgets de terceros o probar elementos experimentales rápidamente sin actualizar la app. :::note Los elementos de HTML personalizado no se precargan ni se almacenan en caché, por lo que recomendamos usar HTML sin procesar solo para elementos pequeños y ligeros. ::: Para insertar código HTML personalizado: 1. Haz clic en **Add** en la parte superior izquierda. 2. Ve a **Media & Display** y elige **Raw HTML**. 3. Inserta o edita tu código HTML en el panel derecho. <img src="/assets/shared/img/onboarding-html.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: onboarding-navigation-branching --- --- title: "Navegación en el onboarding" description: "Configura la navegación estática y dinámica en el constructor de onboarding sin código de Adapty para guiar a los usuarios a través de los flujos." --- La navegación y ramificación te permite guiar a los usuarios por cada paso del onboarding: usa rutas estáticas para llevar a todos a las pantallas principales, y navegación dinámica para adaptar el flujo según las elecciones de cada usuario. Todo sin escribir una sola línea de código. ## Configurar la navegación \{#set-up-navigation\} Puedes configurar la navegación estática y dinámica, así como el cierre del onboarding, usando [botones](onboarding-buttons) y [cuestionarios](onboarding-quizzes). :::info En los cuestionarios, solo los de respuesta única son compatibles con la navegación. Los cuestionarios de respuesta múltiple pueden usarse para configurar la [visibilidad condicional de elementos](onboarding-element-visibility). ::: ### Navegación estática \{#static-navigation\} La navegación estática dirige a todos los usuarios a la misma pantalla de destino. Para configurarla: 1. Añade un botón o un cuestionario de respuesta única. 2. Selecciona el botón o el cuestionario y ve a la pestaña **Element** de la derecha. 3. Configura la sección **On Press** del botón o el apartado **Behaviour** del cuestionario: - **Action on** (solo para cuestionarios): Selecciona **Option** para desbloquear los ajustes de navegación del cuestionario. - **Action**: Selecciona **Navigate**. - **Data**: Selecciona **Static** para dirigir a todos los usuarios a la misma pantalla de destino. - **Destination**: Elige la pantalla de destino. :::note Con la navegación estática, el cuestionario lleva a los usuarios a la misma pantalla independientemente de la respuesta que seleccionen. ::: <img src="/assets/shared/img/static-navigation.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Navegación dinámica \{#dynamic-navigation\} La navegación dinámica dirige a los usuarios según sus respuestas en los cuestionarios: - **Respuestas a cuestionarios en pantallas anteriores**: tanto los botones como los cuestionarios de respuesta única pueden activar la navegación. - **Respuestas a cuestionarios en la pantalla actual**: solo los cuestionarios de respuesta única pueden activar la navegación. Para configurarla: 1. Añade un botón o un cuestionario de respuesta única que navegará a los usuarios. 2. Selecciona el botón o el cuestionario y ve a la pestaña **Element** de la derecha. 3. Configura la sección **On Press** del botón o el apartado **Behaviour** del cuestionario: - **Action on** (solo para cuestionarios): Selecciona **Option** para desbloquear los ajustes de navegación del cuestionario. - **Action**: Selecciona **Navigate**. - **Data**: Selecciona **Dynamic** para dirigir a los usuarios según sus respuestas anteriores en los cuestionarios. - **State**: Elige el cuestionario cuyas respuestas determinan el destino del usuario. 4. Selecciona la pantalla de destino para cada opción del cuestionario. El botón o cuestionario dirigirá dinámicamente a los usuarios a los destinos que hayas configurado. <img src="/assets/shared/img/dynamic-navigation.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Cierre del onboarding \{#onboarding-closure\} Si el recorrido del usuario requiere cerrar el flujo de onboarding, puedes configurarlo con botones o cuestionarios de respuesta única: 1. Añade un botón o un cuestionario de respuesta única. 2. Selecciona el botón o el cuestionario y ve a la pestaña **Element** de la derecha. 3. Configura la sección **On Press** del botón o el apartado **Behaviour** del cuestionario: - **Action on** (solo para cuestionarios): Selecciona **Option** para desbloquear los ajustes de navegación del cuestionario. - **Action**: Selecciona **Close onboarding**. Aprende a gestionar el cierre del onboarding en [iOS](ios-handling-onboarding-events#closing-onboarding), [Android](android-handle-onboarding-events#closing-onboarding), [React Native](react-native-handling-onboarding-events#closing-onboarding) y [Flutter](flutter-handling-onboarding-events#closing-onboarding). <img src="/assets/shared/img/close-onboarding.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: onboarding-quizzes --- --- title: "Cuestionarios de onboarding" description: "Añade cuestionarios interactivos a tus onboardings de Adapty para recopilar preferencias de usuario y crear flujos personalizados, sin necesidad de código." --- Convierte tu onboarding en una conversación bidireccional: añade cuestionarios en el editor sin código de Adapty para recopilar preferencias, segmentar usuarios y [ramificar flujos](onboarding-user-engagement#onboarding-flow-branching) según las respuestas en tiempo real. Empezarás a obtener información en minutos, sin escribir una sola línea de código. ## Añadir cuestionarios \{#add-quizzes\} Puedes añadir distintos tipos de cuestionario —texto, emojis u opciones con imagen— para recopilar información del usuario: 1. Haz clic en **Add** en la parte superior izquierda. 2. Selecciona **Quiz** y elige uno: - **Simple**: Lista de selección única con opciones de texto. Úsala para segmentar usuarios por un atributo principal (p. ej., "¿Cuál es tu rol?"). - **Multiple choice**: Permite seleccionar más de una opción de texto. Ideal para conocer todos los intereses del usuario (p. ej., funciones favoritas). - **Emoji**: Opciones representadas con emojis para respuestas rápidas. Perfecto para verificaciones de opinión rápidas (p. ej., "¿Cuánto te entusiasma esto?"). - **Media picker**: Sube imágenes o vídeos como opciones seleccionables. Ideal para elecciones que dependen de lo visual (p. ej., elige tu tema favorito). - **Rating**: Los usuarios valoran en una escala numérica o de estrellas. Úsalo para medir satisfacción o confianza (p. ej., valora esta función del 1 al 5). - **Popup question**: Muestra una pregunta en forma de modal. Excelente para preguntas que requieren respuesta inmediata. 3. Configura el cuestionario en el panel derecho: - **Required**: Hace que responder sea obligatorio antes de continuar. - **Layout**: Elige entre lista o cuadrícula de imágenes. - **Multiple answers**: Permite selección múltiple (desactiva las opciones de navegación del cuestionario). - **Show checkboxes**: Muestra casillas de verificación cuando la selección múltiple está activada. 4. Configura las opciones del cuestionario en el panel derecho: - **Label**: Texto que se muestra para cada opción. - **Value**: El valor que se envía a los análisis y a los payloads de webhooks. - **Image type**: Sube contenido multimedia o usa emojis. 5. Configura las [acciones](onboarding-actions) que se dispararán cuando el usuario seleccione una opción. Consulta más información en la [guía para diseñar cuestionarios](#how-to-design-quizzes) a continuación. <img src="/assets/shared/img/add-quiz.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Cómo diseñar cuestionarios \{#how-to-design-quizzes\} A continuación se muestra un ejemplo sencillo de configuración de un cuestionario. Supongamos que tienes una app de recetas y quieres saber si tus usuarios son veganos o vegetarianos, y luego conocer más sobre sus preferencias según su respuesta. #### Paso 1. Añadir pantallas \{#step-1-add-screens\} 1. Añade una nueva pantalla e inserta un elemento **Quiz** en ella. <img src="/assets/shared/img/onboarding-user-engagement1.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Añade pantallas para los distintos grupos de usuarios. En nuestro ejemplo, estas pantallas recopilarán información adicional, por lo que también contendrán cuestionarios. <img src="/assets/shared/img/onboarding-user-engagement2.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Añade una pantalla final para indicar que el onboarding ha terminado y que los usuarios pueden ir directamente a la app. <img src="/assets/shared/img/onboarding-user-engagement3.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### Paso 2. Configurar la navegación \{#step-2-configure-navigation\} 1. Para configurar la navegación dinámica, selecciona el elemento **Options** en la primera pantalla del cuestionario. En la sección **Behavior**, añade **Action on Option**. Como queremos redirigir a los usuarios a distintas pantallas según sus respuestas, selecciona **Navigate** como acción, elige **Dynamic** en **Data** y selecciona tu elemento **Options** en **State**. Luego asocia cada opción con una pantalla. <img src="/assets/shared/img/onboarding-user-engagement4.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En ambas pantallas condicionales, configura el botón de navegación. Como necesitamos omitir la segunda pantalla condicional, apunta el botón de navegación directamente a la pantalla que quieres mostrar a continuación. :::tip Si quieres personalizar el propio onboarding según las respuestas del cuestionario, consulta la guía de [navegación](onboarding-navigation-branching#dynamic-navigation) o la de [uso de variables](onboarding-variables). ::: <img src="/assets/shared/img/onboarding-user-engagement5.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Personalización del cuestionario \{#quiz-customization\} Más allá del [diseño básico de elementos](onboarding-layout#element-layout), puedes personalizar la apariencia del cuestionario: 1. Selecciona el elemento del cuestionario en el panel izquierdo. 2. Ve a **Styles** en el menú derecho. 3. Ajusta estos parámetros: - **Options**: Altura, relleno, fondo, redondez, borde y color del borde. - **Text**: Color, fuente y alineación. - **Pressed State**: Fondo, color del texto y color del borde. :::tip Tras personalizar un elemento del cuestionario, puedes hacer clic en **Apply styles to all options** en la parte inferior para aplicar los mismos estilos en todas las pantallas del onboarding de una sola vez. ::: <img src="/assets/shared/img/quiz-customization.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Guardar respuestas del cuestionario \{#save-quiz-answers\} También puedes procesar las respuestas del cuestionario en el código de tu app para almacenarlas o utilizarlas en tu aplicación. Para ello, debes gestionar el evento de respuesta del cuestionario en el código de la app. Consulta la guía de tu plataforma: - [iOS](ios-handling-onboarding-events) - [Android](android-handle-onboarding-events) - [React Native](react-native-handling-onboarding-events) - [Flutter](flutter-handling-onboarding-events) --- # File: onboarding-actions --- --- title: "Acciones de onboarding" description: "Configura acciones—navegar, abrir paywalls, disparar eventos y cerrar flujos—en el editor de onboarding sin código de Adapty." --- Las acciones son los comportamientos interactivos que asignas a los elementos del onboarding, permitiéndoles responder a la interacción del usuario o gestionar eventos. Al definir un disparador (como pulsar un botón o completar un loader) y seleccionar un tipo de acción, controlas cómo los usuarios se mueven e interactúan con tu flujo de onboarding. :::tip Aprende más sobre los flujos de onboarding con ramificaciones en el artículo detallado. ::: ## Añadir acciones \{#add-actions\} El proceso de configuración depende del elemento al que adjuntes la acción. Puedes añadir acciones a los siguientes elementos: - **Botones**: Configura las acciones en el [desplegable **On Press** de la pestaña **Element**](onboarding-buttons#add-buttons). - **Quizzes**: Configura las acciones en la [sección **Behaviour** de la pestaña **Element**](onboarding-quizzes#step-2-configure-navigation). - **Loaders**: Configura las acciones en la sección **Complete action** de la pestaña **Element**. Por ejemplo, aquí puedes ver dónde encontrarlo para los quizzes: <img src="/assets/shared/img/onboarding-user-engagement4.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Tipos de acción \{#action-types\} Al configurar acciones, elige uno de los siguientes tipos: #### Navegar \{#navigate\} Lleva al usuario a otra pantalla del onboarding, permitiéndote controlar el flujo según las acciones o selecciones del usuario. Ideal para encadenar múltiples acciones con lógica de varios pasos usando quizzes. #### Mostrar/Ocultar elemento \{#showhide-element\} Activa o desactiva la visibilidad de un elemento determinado para mostrar contenido condicional dentro de una pantalla. Úsalo para mostrar contenido adicional solo cuando el usuario lo necesite. #### Abrir paywall \{#open-paywall\} Lanza el paywall de tu app para mostrar compras o suscripciones. Aprende cómo gestionar la apertura del paywall en [iOS](ios-handling-onboarding-events#opening-a-paywall), [Android](android-handle-onboarding-events#opening-a-paywall), [React Native](react-native-handling-onboarding-events#opening-a-paywall) y [Flutter](flutter-handling-onboarding-events#opening-a-paywall). #### Desplazar a \{#scroll-to\} Desplaza la vista de forma programática hasta un elemento destino en la pantalla actual. Útil en pantallas largas cuando se pulsa un botón "Ver detalles". #### Personalizada \{#custom\} Te permite definir y ejecutar tu propia lógica basada en el [ID de acción](#action-id). Usa esta acción para disparar comportamientos que no estén cubiertos por los tipos de acción estándar. Aprende cómo gestionar acciones personalizadas en [iOS](ios-handling-onboarding-events#custom-actions), [Android](android-handle-onboarding-events#custom-actions), [React Native](react-native-handling-onboarding-events#handle-custom-actions) y [Flutter](flutter-handling-onboarding-events#handle-custom-actions). #### Cerrar onboarding \{#close-onboarding\} Finaliza el flujo de onboarding y cierra la interfaz. Úsalo cuando los usuarios terminen la configuración para volver directamente a la app principal. Aprende cómo gestionar el cierre del onboarding en [iOS](ios-handling-onboarding-events#closing-onboarding), [Android](android-handle-onboarding-events#closing-onboarding), [React Native](react-native-handling-onboarding-events#closing-onboarding) y [Flutter](flutter-handling-onboarding-events#closing-onboarding). ## Disparadores de acción \{#action-triggers\} Las acciones se ejecutan según el elemento al que están asociadas: - **Botón**: Se ejecuta cuando el usuario pulsa un botón o cuando un temporizador finaliza. - **Quiz**: Se ejecuta cuando se selecciona una opción. - **Loader**: Se activa cuando un Loader o un Processing termina. ## ID de acción \{#action-id\} :::important El ID de acción no es lo mismo que el [ID de elemento](onboarding-variables) usado para insertar datos dinámicos con variables. Asegúrate de no confundirlos. ::: Al configurar acciones personalizadas para botones, puede que quieras gestionar distintos botones de la misma manera usando IDs de acción: 1. Al [añadir un botón](onboarding-buttons#add-buttons), asígnale un ID en la sección **On Press** de la pestaña **Element**. 2. [Usa el ID de acción asignado en tu código fuente](ios-handling-onboarding-events#custom-actions). ::::note En dispositivos iOS, los onboardings solo admiten acciones en la sección **On Press**. La sección **On Press Extra** no funcionará porque solo se puede mostrar una vista a la vez: si una acción abre una vista (como un paywall), la otra acción no puede ejecutarse simultáneamente. ::: <img src="/assets/shared/img/ios-events-1.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: onboarding-variables --- --- title: "Variables de onboarding" description: "Usa variables dinámicas en el editor de onboarding sin código de Adapty para personalizar contenido, capturar datos de usuario y crear flujos adaptados." --- Las variables son valores que se establecen a partir de la entrada del usuario o datos del entorno. Son esenciales para crear experiencias de onboarding personalizadas y atractivas. ## Para qué sirven las variables \{#what-variables-are-for\} Las variables te permiten insertar datos dinámicos —como respuestas de cuestionarios o texto introducido por el usuario— directamente en las pantallas de tu onboarding. Cada usuario ve contenido personalizado sin necesidad de programar. Por ejemplo, puedes saludar a los usuarios por su nombre usando campos de texto, o redirigir a quienes respondan un cuestionario a pantallas de seguimiento personalizadas. Para usar variables, coloca el ID del elemento de la fuente de datos entre dobles llaves, así: `{{element_id}}`. Como variables puedes utilizar los datos recopilados en pantallas anteriores: - **Inputs**: La variable contiene los datos introducidos por el usuario. - [**Quizzes**](onboarding-quizzes): La variable contiene el texto de las opciones seleccionadas. Si se permiten varias respuestas, la variable incluirá todas las opciones seleccionadas separadas por una coma y un espacio. :::note El ID del elemento no es lo mismo que el [ID de acción](onboarding-actions#action-id) usado para la lógica de acciones personalizadas. Asegúrate de no confundirlos. ::: ## Usar variables \{#use-variables\} Así se usan las variables: 1. Crea un elemento Input o una opción de quiz y asígnale un ID. <img src="/assets/shared/img/onboarding-user-engagement6.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Usa el ID del elemento en los textos del onboarding con el formato `{{element-id}}`. Por ejemplo, puedes personalizar el texto con el nombre del usuario. <img src="/assets/shared/img/onboarding-user-engagement7.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Cuando los usuarios introduzcan sus datos durante el onboarding, aparecerán de forma dinámica en todos los lugares donde hayas colocado variables. <img src="/assets/shared/img/onboarding-user-engagement8.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: onboarding-element-visibility --- --- title: "Visibilidad de elementos del onboarding" description: "Configura la navegación estática y dinámica en el editor de onboarding sin código de Adapty para guiar a los usuarios a través de los flujos." --- También puedes añadir visibilidad condicional a elementos específicos. Los elementos condicionales solo son visibles para los usuarios que han dado respuestas concretas en el cuestionario. Por ejemplo, los usuarios que respondieron "Principiante" a la pregunta "¿Cuál es tu nivel de experiencia?" verán información adicional en el siguiente paso. Para hacer que un elemento sea condicional: 1. Ve a la pestaña **Element** en la parte derecha. 2. Selecciona **Conditional** en la sección **Visible**. 3. Configura la condición eligiendo: - ID del cuestionario - operador - respuesta del cuestionario 4. (Opcional) Haz clic en **Advanced condition** para añadir varias condiciones. Por ejemplo, si seleccionaste 'goal' como ID del cuestionario, 'Has' como operador y 'Education' como respuesta del cuestionario, el elemento solo será visible para los usuarios cuyas respuestas a ese cuestionario incluyan la opción 'Education'. <img src="/assets/shared/img/element-condition.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: onboarding-metrics --- --- title: "Métricas de onboarding" description: "Sigue y analiza las métricas de rendimiento del onboarding para mejorar los ingresos por suscripción." --- Adapty recopila una serie de métricas para ayudarte a medir mejor el rendimiento de los onboardings. Todas las métricas se actualizan en tiempo real, excepto las vistas, que se actualizan cada varios minutos. Este documento describe las métricas disponibles, sus definiciones y cómo se calculan. :::important Los ingresos del onboarding se calculan a partir de todas las transacciones que ocurrieron después de que el onboarding fue mostrado. ::: Las métricas de onboarding están disponibles en la lista de onboardings, ofreciéndote una visión general del rendimiento de todos tus onboardings. Esta vista consolidada presenta métricas agregadas para cada onboarding, lo que te permite evaluar su efectividad e identificar áreas de mejora. Para un análisis más detallado de cada onboarding, puedes navegar a las métricas de detalle del onboarding. En esta sección encontrarás métricas exhaustivas específicas del onboarding seleccionado, con información más profunda sobre su rendimiento. <img src="/assets/shared/img/onboarding-metrics1.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Controles de métricas \{#metrics-controls\} El sistema muestra las métricas según el período de tiempo seleccionado y las organiza conforme al parámetro de la columna izquierda con tres niveles de sangría. Para los onboardings activos, las métricas cubren el período desde la fecha de inicio del onboarding hasta la fecha actual. Para los onboardings inactivos, las métricas abarcan todo el período desde la fecha de inicio hasta el final del período de tiempo seleccionado. Los onboardings en borrador y archivados se incluyen en la tabla de métricas, pero si no hay datos disponibles, se mostrarán sin métricas. ### Opciones de visualización para los datos de métricas \{#view-options-for-metrics-data\} La página de onboarding ofrece dos opciones de visualización para los datos de métricas: - Vista por placement: Las métricas se agrupan por los placements asociados al onboarding. Esto permite analizar las métricas según los distintos placements. - Vista por audiencia: Las métricas se agrupan por la audiencia objetivo del onboarding. Puedes evaluar las métricas específicas de los distintos segmentos de audiencia. El menú desplegable en la parte superior de la página de onboarding te permite seleccionar la vista preferida. <img src="/assets/shared/img/onboarding-metrics2.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Filtrar métricas por fecha de instalación \{#filter-metrics-by-install-date\} La casilla **Filter metrics by install date** te permite analizar los datos según cuándo los usuarios instalaron tu app. Esto te ayuda a medir con qué eficacia estás captando nuevos usuarios durante períodos de tiempo concretos. Es una opción práctica cuando quieres personalizar tu análisis. <img src="/assets/shared/img/onboarding-metrics3.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Rangos de tiempo \{#time-ranges\} Puedes analizar los datos de métricas usando un rango de tiempo, lo que te permite centrarte en duraciones específicas como días, semanas, meses o rangos de fechas personalizados. <img src="/assets/shared/img/onboarding-metrics4.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Filtros y grupos \{#filters-and-groups\} Adapty ofrece herramientas potentes para filtrar y personalizar el análisis de métricas según tus necesidades. La página de métricas de Adapty te da acceso a distintos rangos de tiempo, opciones de agrupación y posibilidades de filtrado. - Filtrar por: Attribution, Country, Onboarding audience, Onboarding A/B tests, Onboarding placement, Paywall, Store, State. - Agrupar por: Product o Store. <img src="/assets/shared/img/onboarding-metrics5.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Gráfico de métrica individual \{#single-metric-chart\} La sección del gráfico muestra tus datos en un sencillo gráfico de barras. El gráfico te ayuda a ver rápidamente: - Los valores exactos de cada métrica. - Datos por período. Aparece una suma total junto al gráfico, dándote el panorama completo de un vistazo. Haz clic en el icono de flecha para expandir el gráfico. <img src="/assets/shared/img/onboarding-metrics6.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Resumen de métricas totales \{#total-metrics-summary\} Junto al gráfico de métrica individual hay una sección de resumen de métricas totales. Esta sección muestra los valores acumulados de las métricas seleccionadas en un momento concreto. Puedes cambiar la métrica mostrada usando un menú desplegable. <img src="/assets/shared/img/onboarding-metrics7.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Definiciones de métricas \{#metrics-definitions\} ### Vistas y vistas únicas \{#views--unique-views\} Las vistas cuentan el número de veces que los usuarios ven tu onboarding. Si alguien lo visita dos veces, eso son dos **vistas** pero una sola **vista única**. Esta métrica te ayuda a entender con qué frecuencia se ha mostrado tu onboarding. ### Completaciones y completaciones únicas \{#completions--unique-completions\} Las completaciones cuentan el número de veces que los usuarios completan tu onboarding, es decir, que pasan de la primera a la última pantalla. Si alguien lo completa dos veces, eso son dos **completaciones** pero una sola **completación única**. ### Tasa de completaciones únicas \{#unique-completions-rate\} El número de completaciones únicas dividido entre el número de vistas únicas. Esta métrica te ayuda a entender cómo interactúa la gente con el onboarding y a hacer cambios si notas que lo están ignorando. ### Ingresos \{#revenue\} **Revenue** muestra tus ganancias totales en USD por compras y renovaciones. Es el importe antes de cualquier deducción. ### Ganancias netas de la tienda \{#proceeds\} [**Proceeds**](analytics-cohorts#revenue-vs-proceeds) muestra lo que recibes después de que App Store/Play Store aplica su comisión, pero antes de impuestos. :::important Notifica a Adapty si tu app está inscrita en un programa de comisión reducida. Para garantizar cálculos correctos, especifica tu estado en el [Small Business Program](app-store-small-business-program) y el [Reduced Service Fee program](google-reduced-service-fee) en los [ajustes de tu app](general). ::: ### Ganancias netas finales \{#net-proceeds\} Tus ganancias finales después de deducir tanto las comisiones de la tienda como los impuestos. ### ARPPU \{#arppu\} El ARPPU es el ingreso medio por usuario de pago. Se calcula dividiendo los ingresos totales entre el número de usuarios de pago únicos. 15 000 $ de ingresos / 1000 usuarios de pago = 15 $ de ARPPU. ### ARPU \{#arpu\} El ARPU es el ingreso medio por usuario que ha visto el onboarding. Se calcula dividiendo los ingresos totales entre el número de espectadores únicos. ### ARPAS \{#arpas\} El ARPAS muestra cuánto genera de media cada suscriptor activo. Simplemente divide tus ingresos totales entre el número de suscriptores activos. Por ejemplo: 5000 $ de ingresos ÷ 1000 suscriptores = 5 $ de ARPAS. ### CR de compras y CR único de compras \{#cr-purchases--unique-cr-purchases\} La **tasa de conversión a compras** muestra qué porcentaje de las vistas del onboarding llevan a una compra. Por ejemplo, 10 compras de 100 vistas es una tasa de conversión del 10%. El **CR único de compras** mide qué porcentaje de usuarios únicos que ven tu onboarding acaban realizando una compra, contando a cada usuario solo una vez, independientemente de cuántas veces lo vean. ### CR de trials y CR único de trials \{#cr-trials--unique-cr-trials\} La **tasa de conversión a trials** muestra qué porcentaje de las vistas del onboarding llevan a iniciar un trial. Por ejemplo, 10 trials de 100 vistas es una tasa de conversión del 10%. El **CR único de trials** mide qué porcentaje de usuarios únicos que ven tu onboarding inician un trial, contando a cada usuario solo una vez, independientemente de cuántas veces lo vean. ### Compras \{#purchases\} **Purchases** cuenta todas las transacciones de tu onboarding, excepto las renovaciones. Esto incluye: - Compras directas nuevas - Conversiones de trial - Cambios de plan (actualizaciones, degradaciones, cambios de categoría) - Restauraciones de suscripción Esta métrica te da una imagen completa de la nueva actividad relacionada con transacciones de tu onboarding. ### Trials \{#trials\} **Trials** cuenta el número de usuarios que iniciaron períodos de prueba gratuitos a través de tu onboarding. Esto te ayuda a seguir con qué eficacia tus ofertas de trial atraen a usuarios antes de que decidan pagar. ### Trials cancelados \{#trials-cancelled\} **Trials cancelled** muestra cuántos usuarios desactivaron la renovación automática durante su período de trial. Esto te indica cuántas personas decidieron no continuar con una suscripción de pago después de probar tu servicio. ### Reembolsos \{#refunds\} **Refunds** cuenta cuántas compras y suscripciones fueron devueltas para un reembolso, independientemente del motivo. ### Tasa de reembolso \{#refund-rate\} La **tasa de reembolso** muestra el porcentaje de primeras compras reembolsadas. Ejemplo: 5 reembolsos de 1000 compras = 0,5 % de tasa de reembolso. Las renovaciones no se contabilizan en este cálculo. --- # File: get-paid-in-onboardings --- --- title: "Conectar paywalls con onboardings" --- Puedes conectar un paywall a un onboarding para que los usuarios vean una oferta de suscripción durante el flujo de onboarding o justo al terminarlo. Hay dos formas de hacerlo: - **Mostrar un paywall después del onboarding**: El paywall se abre automáticamente cuando el usuario cierra el onboarding. Elige esta opción si quieres que todos los usuarios vean el paywall al final del flujo, sin que tengan que hacer nada. - **Mostrar un paywall dentro del onboarding**: Un botón dentro del onboarding activa el paywall. Elige esta opción si quieres que los usuarios puedan suscribirse en un momento concreto del flujo — por ejemplo, después de ver la propuesta de valor en una pantalla específica. Antes de empezar, crea un [paywall](paywalls) y un [onboarding](onboardings) y añádelos a placements. :::important Necesitas dos placements distintos: uno para el paywall y otro para el onboarding. Asegúrate de usar los IDs de placement correctos cuando obtengas el onboarding y el paywall en tu código. ::: ## Mostrar el paywall después del onboarding \{#show-paywall-after-onboarding\} Para mostrar un paywall después del onboarding, solo tienes que gestionar el evento que se genera cada vez que los usuarios cierran el onboarding. En cuanto los usuarios cierran el onboarding, se dispara el [evento](ios-handling-onboarding-events#closing-onboarding). Por tanto, si quieres mostrar un paywall inmediatamente después del onboarding, puedes implementar <InlineTooltip tooltip="obtener y mostrar un paywall">[iOS](ios-present-paywalls), [Android](android-present-paywalls), [React Native](react-native-present-paywalls), [Flutter](flutter-present-paywalls) y [Unity](unity-present-paywalls)</InlineTooltip> como reacción a ese evento. ## Mostrar el paywall dentro del onboarding \{#show-paywall-inside-onboarding\} 1. En el editor de onboardings, crea un botón que redirija a los usuarios al paywall. Selecciona **Open paywall** como su acción. 2. Puedes asignar cualquier ID de acción al botón y usarlo para identificar el paywall que necesitas abrir. Sin embargo, la forma más sencilla de abrir paywalls desde onboardings es que el ID de acción sea igual al ID del placement. Así puedes obtener y mostrar los paywalls directamente sin tener que codificar los IDs de placement en el código. Para ello, ve al Adapty Dashboard y busca el ID del placement del paywall. En el editor, pega el ID del placement del paywall en el campo ID. <img src="/assets/shared/img/get-paid-in-onboardings2.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Ahora, con este botón, cada vez que tus usuarios lo pulsen se generará una acción con el ID de acción correspondiente. Para gestionar esta acción en el código de tu app, tendrás que [obtener el paywall](fetch-paywalls-and-products) y luego [mostrarlo](ios-quickstart-paywalls). --- # File: target-onboardings-to-different-user-groups --- --- title: "Segmentar onboardings a distintos grupos de usuarios" description: "Muestra diferentes onboardings según la fuente de adquisición u otros atributos del usuario." --- Puedes mostrar distintos flujos de onboarding según los atributos del usuario: fuente de adquisición, campaña, geografía, tipo de dispositivo, estado en el ciclo de vida o intención de uso. Adaptar el contenido del onboarding a grupos específicos de usuarios mejora las tasas de activación y el engagement inicial. La configuración requiere trabajo tanto del equipo de desarrollo como en el dashboard. ## Antes de empezar \{#before-you-start\} - **Versión del SDK**: Tu app debe usar Adapty SDK v3.8.0+ (iOS, Android, React Native, Flutter), v3.14.0+ (Unity) o v3.15.0+ (Kotlin Multiplatform, Capacitor). Consulta [Onboardings](onboardings) para más detalles. - **Onboardings**: Crea al menos dos onboardings en Adapty antes de empezar — uno por defecto y uno por segmento. Consulta [Crear onboarding](create-onboarding). - **Herramientas de atribución**: Si tu app usa AppsFlyer, Adjust, Branch u otra [integración de atribución](attribution-integration), es posible que los datos de campaña y fuente ya estén disponibles como atributos de usuario en Adapty. Puedes usar estos datos directamente en los segmentos — salta al Paso 2. ## Paso 1. Asignar atributos personalizados a los usuarios *(Desarrollador)* \{#step-1-assign-custom-attributes-to-users-developer\} Llama a `updateProfile` al inicio de la sesión del usuario, antes de que se muestre el onboarding. El atributo debe estar disponible cuando Adapty evalúe qué onboarding mostrar. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS"> ```swift do { var builder = AdaptyProfileParameters.Builder() builder = try builder.with(customAttribute: "yoga", forKey: "campaign") try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Android"> ```kotlin val builder = AdaptyProfileParameters.Builder() builder.withCustomAttribute("campaign", "yoga") Adapty.updateProfile(builder.build()) { error -> if (error != null) { // handle the error } } ``` </TabItem> <TabItem value="rn" label="React Native"> ```typescript try { await adapty.updateProfile({ codableCustomAttributes: { campaign: 'yoga', }, }); } catch (error) { // handle `AdaptyError` } ``` </TabItem> <TabItem value="flutter" label="Flutter"> ```dart try { final builder = AdaptyProfileParametersBuilder() ..setCustomStringAttribute('yoga', 'campaign'); await Adapty().updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity"> ```csharp try { var builder = new Adapty.ProfileParameters.Builder() .SetCustomStringAttribute("campaign", "yoga"); Adapty.UpdateProfile(builder.Build(), (error) => { if (error != null) { // handle the error } }); } catch (Exception e) { // handle the exception } ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform"> ```kotlin val builder = AdaptyProfileParameters.Builder() builder.withCustomAttribute("campaign", "yoga") Adapty.updateProfile(builder.build()) .onSuccess { // profile updated successfully } .onError { error -> // handle the error } ``` </TabItem> <TabItem value="capacitor" label="Capacitor"> ```typescript try { await adapty.updateProfile({ codableCustomAttributes: { campaign: 'yoga', }, }); } catch (error) { // handle the error } ``` </TabItem> </Tabs> Para conocer los tipos de atributos disponibles y los límites de claves y valores, consulta la guía de atributos de usuario para tu plataforma: [iOS](setting-user-attributes) | [Android](android-setting-user-attributes) | [React Native](react-native-setting-user-attributes) | [Flutter](flutter-setting-user-attributes) | [Unity](unity-setting-user-attributes) | [Kotlin Multiplatform](kmp-setting-user-attributes) | [Capacitor](capacitor-setting-user-attributes) ## Paso 2. Añadir usuarios a segmentos *(Dashboard)* \{#step-2-add-users-to-segments-dashboard\} Agrupa los usuarios por los atributos asignados en [segmentos](segments): 1. En el dashboard, ve a **Profiles & Segments** y cambia a la pestaña **Segments**. Ahí, haz clic en **Create segment**. 2. Rellena el nombre y la descripción del segmento. 3. Haz clic en **Add users attributes** y selecciona un atributo de la lista. 4. Crea un segmento por cada atributo que quieras usar. :::note Para que un atributo personalizado aparezca en la lista, debe estar asignado al menos a un perfil. Añade un atributo personalizado a cualquier perfil desde el Adapty Dashboard para que esté disponible en la lista. ::: ## Paso 3. Configurar el placement *(Dashboard)* \{#step-3-set-up-placement-dashboard\} Para que Adapty decida qué onboarding mostrar, crea un placement y añade todos los segmentos como audiencias: 1. En el dashboard, ve a **Placements** y cambia a la pestaña **Onboardings**. Ahí, haz clic en **Create placement**. 2. Rellena el nombre y el ID del placement. 3. Haz clic en **Run onboarding**. En el diálogo, selecciona el onboarding que se mostrará por defecto a los usuarios que no pertenezcan a ningún segmento. Haz clic en **Submit**. 4. En la parte superior derecha, haz clic en **Add audience**. Selecciona un segmento y el onboarding que se mostrará a los usuarios de ese segmento. Haz clic en **Submit**. 5. Añade audiencias para todos los segmentos y onboardings que necesites. 6. Haz clic en **Save & Publish** para que este placement esté disponible para el SDK de Adapty. ## Paso 4. Mostrar onboardings *(Desarrollador)* \{#step-4-display-onboardings-developer\} Usa el ID del placement para obtener y mostrar onboardings en tu app. Sigue la guía específica de tu plataforma: [iOS](ios-present-onboardings) | [Android](android-present-onboardings) | [React Native](react-native-present-onboardings) | [Flutter](flutter-present-onboardings) | [Unity](unity-present-onboardings) | [Kotlin Multiplatform](kmp-present-onboardings) | [Capacitor](capacitor-present-onboardings) :::tip Para verificar que el targeting funciona: usa un perfil de usuario de prueba con un atributo personalizado asignado y confirma que se muestra el onboarding correcto. En el Adapty Dashboard, comprueba **Profiles & Segments** para confirmar que el usuario aparece en el segmento esperado. ::: --- # File: localize-onboardings --- --- title: "Localizar onboardings" description: "Gestiona las localizaciones de onboardings para llegar a una audiencia global." --- :::important Las localizaciones en onboardings están disponibles en los SDKs de Adapty a partir de la versión 3.11. Si tu versión del SDK es inferior a 3.11, se usará el idioma predeterminado. ::: La localización puede ser un proceso laborioso que requiere atención al detalle. Cuando usas el Onboarding Builder, Adapty se encarga de la mayor parte del trabajo pesado. Esta página explica cómo funciona el proceso de localización. Una vez que hayas terminado de configurar tu onboarding en el idioma predeterminado y estés satisfecho con el resultado, ya puedes añadir soporte para otros idiomas. ## Añadir y configurar una localización \{#add-and-set-up-localization\} 1. En el Onboarding Builder, haz clic en el icono de globo en la esquina superior derecha y selecciona **Add locale**. <img src="/assets/shared/img/add-locale.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Elige el **Language**. 3. **Opcional**: Activa **Translate with AI** para traducir automáticamente todo el contenido del onboarding original al idioma seleccionado. 4. Haz clic en **Create**. Ahora puedes traducir el contenido manualmente, usar IA o exportar el archivo de localización para traductores externos. Adapty creará una copia de tu onboarding donde podrás modificar tanto el contenido de texto como los elementos visuales para adaptarlos a tu audiencia objetivo. :::tip Puedes personalizar el diseño de cada idioma (imágenes, colores, diseños) para dirigirte mejor a distintas audiencias. Sin embargo, los cambios estructurales (añadir o eliminar pantallas y elementos) solo se pueden realizar en el idioma predeterminado. ::: <img src="/assets/shared/img/new-locale.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Traducir onboardings con IA \{#translate-onboardings-using-ai\} La traducción con IA ofrece una forma rápida y eficiente de localizar tu onboarding. La traducción suele tardar entre 1 y 2 minutos según el tamaño del onboarding. Puedes usar la traducción con IA en dos momentos distintos: - **Al añadir un nuevo idioma:** Marca **Translate with AI** durante la creación del idioma para obtener una versión pretraducida. - **Después de crear un idioma:** Ve al icono de globo → **Manage locales** y haz clic en **Translate with AI** junto al idioma que desees. :::important Los cambios manuales que hayas hecho en la versión localizada se sobreescribirán. Además, los cambios realizados en el idioma predeterminado después de la traducción con IA no se reflejarán automáticamente en las versiones traducidas; tendrás que volver a ejecutar la traducción. ::: ## Exportar archivos de localización para traductores externos \{#export-localization-files-for-external-translation\} Puedes exportar archivos de localización para compartirlos con tus traductores e importar después los resultados traducidos de vuelta en Adapty. Al hacer clic en el botón **Export** se crea un único archivo `.tsv` con todos los idiomas. ## Importar archivos \{#import-files\} Una vez recibido el archivo traducido, usa el botón **Import** para subirlo. Adapty validará automáticamente los archivos para asegurarse de que coinciden con el formato correcto y la estructura de configuración del paywall. :::tip Si envías este archivo a varios traductores al mismo tiempo, recuerda eliminar las columnas extra del archivo al importarlo. De lo contrario, algunas traducciones serán sobreescritas por columnas sin cambios. ::: ### Formato del archivo de importación \{#import-file-format\} Para que la importación sea correcta, el archivo debe cumplir los siguientes requisitos: - **Extensión del archivo:** El archivo debe tener extensión `.tsv`. - **Solo tabulaciones como separadores**: Usa tabulaciones como separadores. Otros separadores causarán errores. - **Línea de encabezado**: El archivo debe incluir una línea de encabezado. - **Nombres de columnas correctos:** Los nombres de columnas deben ser **Key** y los nombres completos de las localizaciones. - **Nombres de clave correctos**: Los valores de la columna **Key** deben permanecer sin cambios, ya que corresponden a identificadores de pantallas y elementos. - **Sin entidades adicionales:** Asegúrate de que el archivo no incluya entidades que no estén presentes en la configuración actual del onboarding. Las entidades extra causarán errores. - **Importación parcial:** El archivo puede incluir todas o solo algunas entidades de la configuración actual del paywall. ## Limitaciones existentes \{#existing-limitations\} Una vez que un usuario abre un onboarding, el idioma mostrado queda bloqueado durante aproximadamente 24 horas. Si el usuario cambia el idioma del dispositivo o de la app durante ese tiempo, el onboarding seguirá mostrándose en el idioma original. Transcurridas las 24 horas, se aplicará el nuevo idioma. Esto afecta a dos situaciones: - El usuario abre el onboarding en el idioma A, cierra la app, cambia el idioma del dispositivo al idioma B y vuelve a abrir la app → el onboarding sigue mostrándose en el idioma A - El usuario abre el onboarding en el idioma A, lo cierra, cambia el idioma de la app al idioma B y vuelve a abrir el onboarding → el onboarding sigue mostrándose en el idioma A --- # File: onboarding-version-control --- --- title: "Control de versiones de onboarding" description: "Restaura versiones anteriores de tus onboardings." --- Cada vez que haces **Save**, **Preview** o **Publish** en tu onboarding, Adapty añade la versión actual a tu **historial de versiones**. Puedes consultar el historial y cambiar a cualquiera de las versiones guardadas. :::warning Adapty no guarda automáticamente tus onboardings durante el [proceso de edición](design-onboarding). Haz **Publish** en tu onboarding si quieres añadirlo al historial de versiones. ::: Abre el menú "versions" en la esquina superior derecha de la página para ver tu historial de versiones. Cada versión incluye una marca de tiempo para que sepas cuándo se guardó. Haz clic en el botón de flechas de una de las entradas para revertir a ese estado. :::important Recuerda hacer **Publish** con los cambios después de revertir a una versión anterior. ::: --- # File: onboarding-offline --- --- title: "Modo sin conexión" description: "Gestiona la apertura de un onboarding sin conexión a internet." --- Se necesita conexión a internet para obtener el flujo de onboarding desde el servidor. Esto es fundamental para recuperar los datos del onboarding y mostrar la secuencia de onboarding junto con el paywall con los productos que siguen. Tanto el contenido del onboarding como el del paywall se cargan dinámicamente desde el servidor, lo que garantiza que siempre estén actualizados. ## Modo sin conexión \{#offline-mode\} Para optimizar la experiencia del usuario, la secuencia de onboarding se carga de la siguiente manera: - **Carga de la primera pantalla**: Solo se requiere cargar la primera pantalla del flujo de onboarding al inicio. Esto permite minimizar los tiempos de carga, incluso en conexiones móviles lentas como 3G o 4G. - **Precarga**: Una vez que la primera pantalla está cargada y visible, se comienza a precargar en segundo plano las pantallas siguientes (incluidos fuentes, vídeos e imágenes). Si un usuario pierde la conexión a internet durante el proceso de onboarding, verá una pantalla de error con dos opciones: - **Intentar de nuevo**: Al pulsar **Try again**, el sistema intentará cargar de nuevo el flujo de onboarding. Si la conexión se restablece y el contenido se carga correctamente, el onboarding se reanudará desde donde el usuario lo dejó, conservando todo el progreso. - **Cerrar**: Si el usuario decide cerrar el onboarding, se activará el evento [close](ios-handling-onboarding-events#closing-onboarding) con `"action_id": "error"`. <img src="/assets/shared/img/onboarding_offline.png" style={{ border: '1px solid #727272', /* border width and color */ width: '300px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: local-fallback-onboarding --- --- title: "Descargar onboardings de respaldo" description: "Usa onboardings de respaldo locales en Adapty para garantizar flujos de suscripción sin interrupciones." --- Para cargar un [onboarding](onboardings), tu aplicación solicita los datos de configuración a Adapty. Las configuraciones de onboarding almacenan las URLs de tus flujos de onboarding. Si un problema de red interrumpe la conexión entre tu aplicación y los servidores de Adapty, no podrás configurar ni mostrar correctamente tus onboardings. Para acceder a los datos de configuración de onboarding sin conexión, guarda un archivo de configuración de respaldo dentro del código de tu app. Puedes descargar archivos de respaldo ya preparados con los datos de configuración de tus onboardings y [paywalls](fallback-paywalls) directamente desde Adapty. Sigue las instrucciones a continuación para descargar el archivo y añadirlo al código de tu aplicación. :::important Los onboardings de respaldo **requieren conexión a internet**, ya que el contenido del onboarding siempre se almacena en línea. El archivo de respaldo solo guarda la configuración de los onboardings. Lee el artículo sobre el [modo sin conexión de onboarding](onboarding-offline) para entender qué ocurre cuando la aplicación no puede cargar el onboarding. ::: <details> <summary>Antes de configurar los onboardings de respaldo (Haz clic para expandir)</summary> 1. Crea un [onboarding](onboardings). 2. [Crea un placement](create-placement) para el onboarding con el fin de determinar dónde aparece en la app. </details> Adapty genera automáticamente archivos de configuración JSON para tus onboardings de respaldo, uno por plataforma. Estos archivos también contienen datos de respaldo para tus [paywalls](local-fallback-paywalls). Si un único placement tiene más de un onboarding o paywall, la versión de respaldo incluirá la variación con mayor peso o la audiencia más amplia. Adapty actualiza estos archivos cada vez que modificas tus paywalls u onboardings. Sigue estos pasos para descargar tus configuraciones de respaldo: 1. Abre la página **[Placements](https://app.adapty.io/placements)**. 2. Haz clic en el botón **Fallbacks**. 3. Selecciona tu plataforma de destino (*iOS* o *Android*) en el menú desplegable. 4. Selecciona tu versión del SDK para iniciar la descarga. <img src="/assets/shared/img/9c63367-placements.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <details> <summary>Ejemplo de onboarding de respaldo (Haz clic para expandir)</summary> ``` "PLACEMENT_ID": {"data": [{"variation_id":"cb1c0ef8-aecd-4a53-a6f3-b98266e66884", "onboarding_id":"daf25858-3fa2-4981-8500-9c8a30e5b7e6", "onboarding_name":"ONBOARDING_NAME", "onboarding_builder": {"config_url":"CONFIG_URL", "lang":"en"}, "remote_config":null, "cross_placement_info":null, "weight":100}], "meta": {"placement": {"developer_id":"DEVELOPER_ID", "is_tracking_purchases":true, "audience_name":"All Users", "placement_audience_version_id":"a9eb3ab8-3178-477d-84d4-ef9d3978e48b", "revision":0, "ab_test_name":"A/B_TEST_NAME" } } }, ``` </details> ## Después de la descarga \{#after-the-download\} Sigue la guía de configuración para tu plataforma: * [iOS](ios-use-fallback-paywalls) * [Android](android-use-fallback-paywalls) * [React Native](react-native-use-fallback-paywalls) * [Flutter](flutter-use-fallback-paywalls) * [Unity](unity-use-fallback-paywalls) * [Kotlin Multiplatform](kmp-use-fallback-paywalls) * [Capacitor](capacitor-use-fallback-paywalls) --- # File: customize-onboardings-with-remote-config --- --- title: "Personalizar el onboarding con Remote Config" description: "Personaliza tu onboarding con Remote Config en Adapty para una segmentación más precisa." --- El Remote Config del onboarding es una herramienta que ofrece opciones de configuración flexibles. Permite usar payloads JSON personalizados para ajustar tus onboardings con precisión. Con él puedes definir distintos parámetros como títulos, imágenes, fuentes, colores y mucho más. Por ejemplo, puedes usar Remote Config para enviar metadatos adicionales. Antes de empezar a personalizar un onboarding, [crea un onboarding](create-onboarding). Para empezar a personalizar un onboarding con Remote Config: 1. Abre el onboarding desde **Onboardings** y haz clic en **Edit onboarding**. <img src="/assets/shared/img/customize-onboarding-remote-config1.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Cambia a la pestaña **Remote config**. El Remote Config tiene dos vistas: - [Tabla](customize-paywall-with-remote-config#table-view-of-the-remote-config) - [JSON](customize-paywall-with-remote-config#json-view-of-the-remote-config) Tanto la vista **Table** como la vista **JSON** incluyen los mismos elementos de configuración. La única diferencia es una cuestión de preferencia: la vista de tabla ofrece un menú contextual que puede resultar útil para corregir errores de localización. Puedes cambiar entre vistas haciendo clic en la pestaña **Table** o **JSON** cuando lo necesites. <img src="/assets/shared/img/customize-onboarding-remote-config2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Independientemente de la vista que hayas elegido para personalizar tu onboarding, podrás acceder a estos datos desde el SDK usando la propiedad `remoteConfig` de `AdaptyOnboarding` y hacer los ajustes necesarios en tu onboarding. Puedes combinar distintas opciones y crear las tuyas propias. ## Vista JSON del Remote Config \{#json-view-of-the-remote-config\} En la vista **JSON** del Remote Config puedes introducir cualquier dato con formato JSON: <img src="/assets/shared/img/customize-onboarding-remote-config3.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Vista de tabla del Remote Config \{#table-view-of-the-remote-config\} Si no trabajas habitualmente con código y necesitas corregir algunos valores JSON, Adapty tiene la vista **Table** para ti. <img src="/assets/shared/img/customize-onboarding-remote-config4.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Es una copia de tu JSON en formato de tabla, fácil de leer y entender. El código de colores ayuda a identificar los distintos tipos de datos. Para añadir una clave, haz clic en **Add row**. Verificamos automáticamente la correspondencia entre valores y tipos, y mostramos una alerta si tus correcciones pueden generar un JSON no válido. <img src="/assets/shared/img/customize-onboarding-remote-config5.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: profiles-crm --- --- title: "Profiles/CRM" description: "Manage user profiles and CRM data in Adapty to enhance audience segmentation." --- Profiles is a CRM for your users. With Profiles, you can: 1. Find specific users by profile ID, customer user ID, email, or transaction ID. 2. View the user's event timeline, including billing issues, grace periods, and other [events](events). 3. Analyze user's properties such as subscription state, total revenue/proceeds, and more. 4. Grant the user a subscription. <img src="/assets/shared/img/profiles.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::link To understand how Adapty creates and links user profiles, see [How profiles work](how-profiles-work). ::: ## Finding users In the Profiles list, you can search for a specific user by: - **Profile ID**: Adapty's internal identifier for the user (also called Adapty ID). - **Customer user ID**: Your app's identifier for the user, if you've set one. - **Email**: The user's email, if sent as a custom attribute. - **Transaction ID**: The store transaction ID from a purchase. Click any row to open the user's full profile. ## Subscription state In the Profiles list, you can filter and sort users by subscription state. The state values are: | **Estado** del usuario | Descripción | | :--------------------- | :----------------------------------------------------------- | | Subscribed | El usuario tiene una suscripción activa con la renovación automática habilitada. | | Auto-renew off | El usuario desactivó la renovación automática, pero sigue teniendo acceso a las funciones premium hasta que finalice el período de suscripción. | | Subscription cancelled | El usuario canceló su suscripción y esta ha finalizado por completo. | | Billing issue | No se pudo cobrar al usuario debido a un problema de facturación, ya sea tras el vencimiento de su suscripción o del período de prueba. | | Grace period | El usuario se encuentra actualmente en un período de gracia debido a un problema de facturación que ocurrió al intentar cobrarle tras el vencimiento de su suscripción o del período de prueba. | | Active trial | El usuario tiene una suscripción activa que se encuentra actualmente en su período de prueba. | | Trial cancelled | El usuario canceló el período de prueba y no tiene una suscripción activa. | | Never subscribed | El usuario nunca se ha suscrito ni ha iniciado un período de prueba, y sigue siendo un usuario freemium. | ## User attributes <img src="/assets/shared/img/ce8df4d-CleanShot_2023-06-26_at_20.32.232x.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> You can send additional user properties to Adapty using the SDK. By default, Adapty sets: | Property | Description | | ---------------- | ------------------------------------------------------------ | | Customer user ID | An identifier of your end user in your system. | | Adapty ID | Internal Adapty identifier of your end user, called Profile ID. | | IDFA | The Identifier for Advertisers, assigned by Apple to a user's device. Requires App Tracking Transparency (ATT) permission on iOS 14+. Not available on Android. | | Country | Country of your end user. | | OS | The operating system used by the end user. | | Device | The end-user-visible device model name. | | Install date | The date when the user was first recorded in Adapty: <ul><li>The date the user was created. </li><li>If the user installed your app before you integrated Adapty, the install date reflects the date of their first transaction.</li><li>If applicable, the date provided during a historical data import.</li></ul> | | Created at | The date the user was created. | Send at least your internal user ID or user email. This lets you find users by these identifiers in the Profiles list. After you install the SDK, Adapty automatically collects user events from the payment queue and displays them in the user profile. The attributes in the table above are collected automatically — you do not need to send them. ### Custom attributes In the **Attributes** section of a profile, you can see custom attributes set via the SDK or API. You can also assign attributes manually using the **Add attribute** button. <img src="/assets/shared/img/378c1fb-add_attribute.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> ## Granting a subscription In a profile, you can extend an active subscription or grant a user lifetime access to an access level — without requiring them to make a purchase. <img src="/assets/shared/img/b1d74fd-edit_paid_access_level.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> This is most useful for: - Compensating a user after a billing or support issue. - Running manual promotions or beta programs. - Testing subscription flows without a real purchase. To grant access, open the user's profile, go to the **Access levels** section, and click **Edit**. Set the expiration date and save. The expiration date must be in the future and cannot be decreased once set. Adjusting it for active subscriptions does not affect ongoing payments. :::note Granting access does not create App Store or Google Play purchase events. The user's event feed and analytics will differ from a real purchase flow. ::: You can also grant access programmatically using the [Grant access level](api-adapty/operations/grantAccessLevel) API method. ## Sharing paid access between user accounts :::link Main article: [Sharing paid access between user accounts](sharing-paid-access-between-user-accounts) ::: ### Access sharing history When access levels are shared or transferred, the user’s profile shows a link to the connected profile — the profile that shared access, or the profile that received it. To view the connected profile, in the user’s **Profile**, click the link next to the access level. <img src="/assets/shared/img/profile-access-level-origin.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Next steps - To understand how Adapty creates and links profiles, see [How profiles work](how-profiles-work). - To configure the access sharing policy, see [Sharing paid access between user accounts](sharing-paid-access-between-user-accounts). - To grant access programmatically, see the [Grant access level](api-adapty/operations/grantAccessLevel) API method. --- # File: how-profiles-work --- --- title: "Cómo funcionan los perfiles" description: "Entiende cómo Adapty crea, rastrea y vincula perfiles de usuario, incluyendo perfiles anónimos, usuarios identificados y relaciones padre/heredero." --- Cada usuario de tu app tiene un perfil de Adapty que registra sus compras, eventos y estado de suscripción. Entender cómo se crean y vinculan los perfiles te ayuda a prevenir errores de integración, evitar la fragmentación de datos e interpretar correctamente la información en la sección [Perfiles](profiles-crm). ## Creación de perfil \{#profile-creation\} Adapty crea automáticamente un perfil la primera vez que un usuario abre tu app. **Sin un Customer User ID**, el perfil es anónimo. Se crea un nuevo perfil anónimo cada vez que: - Un usuario reinstala la app - Un usuario cierra sesión en tu app (cuando tu app llama a `Adapty.logout()`) Las compras están vinculadas a la instalación de la app, no a una identidad de usuario persistente. **Con un Customer User ID**, el perfil persiste entre reinstalaciones y dispositivos. Usar un Customer User ID te permite: 1. Rastrea a un usuario en reinstalaciones y múltiples dispositivos. 2. Busca usuarios por su customer user ID en la sección [**Profiles**](profiles-crm). 3. Usa el customer user ID en la [API server-side](getting-started-with-server-side-api). 4. Adapty envía el customer user ID a todas las integraciones. El comportamiento del perfil con un customer user ID depende de cuándo lo configures: - **Al activar el SDK**: Adapty usa el perfil existente con ese customer user ID (para usuarios que regresan) o crea un nuevo perfil (para usuarios nuevos). - **Después de activar el SDK**: Adapty crea un perfil anónimo al activarse. Cuando identificas al usuario más adelante, Adapty vincula el customer user ID al perfil anónimo (para usuarios nuevos) o cambia al perfil existente con ese ID (para usuarios que regresan). **Qué enfoque usar:** - **ID de usuario disponible al iniciar la app** (por ejemplo, guardado de una sesión anterior): pásalo a `activate()` al inicializar el SDK. - **Los usuarios inician sesión después de abrir la app**: llama a `identify()` tras la autenticación. Adapty vincula el ID al perfil actual (si el ID es nuevo) o cambia al perfil existente (si el ID ya existe). - **Los usuarios pueden comprar antes de iniciar sesión**: llama a `identify()` después del login. Si el customer user ID ya existe en Adapty, recupera el perfil a continuación para sincronizar el nivel de acceso actual. Para más detalles de implementación, consulta la guía del SDK sobre [identificación de usuarios](identifying-users). :::note Si un usuario que regresa usaba tu app anteriormente sin un customer user ID, esos perfiles anónimos no se fusionan automáticamente cuando empiezas a identificar en la activación del SDK. Para mantener el historial completo de esos usuarios, usa `identify()` después del inicio de sesión. ::: ## Perfiles padre e hijo \{#parent-and-inheritor-profiles\} Cuando la misma suscripción de la store está asociada a más de un perfil de Adapty, Adapty trata esos perfiles como una cadena: un perfil **padre** y uno o más perfiles **herederos** que comparten el acceso de la misma compra. Esto ocurre cuando: - [El acceso de pago compartido entre cuentas de usuario](sharing-paid-access-between-user-accounts) está habilitado y un usuario inicia sesión en un dispositivo donde un perfil diferente realizó previamente la compra. - Un usuario reinstala la app sin `customer_user_id`, y el nuevo perfil recoge la compra de la instalación anterior. - Diferentes usuarios identificados restauran compras en el mismo dispositivo. - Una app se transfiere entre Team IDs de Apple y la nueva app recoge las compras realizadas con el antiguo Team ID. **Cómo se selecciona el perfil principal.** El perfil padre es el **primer perfil en registrar la compra** — determinado por el orden de los recibos de compra en Adapty, no por el orden de creación del perfil. Por ejemplo: instalas la app y no realizas ninguna compra, luego la reinstales y compras una suscripción. El segundo perfil se convierte en el padre porque realizó la compra. El primer perfil se convierte en el heredero y obtiene acceso a través del uso compartido. **Cómo se distribuyen los eventos:** - **Eventos transaccionales** (compras, renovaciones, cancelaciones, problemas de facturación, períodos de gracia, reembolsos): Aparecen únicamente en el **perfil principal** que realizó la compra. Todas las renovaciones y actualizaciones de suscripción siguen apareciendo en ese perfil. - **Eventos `access_level_updated`**: Aparecen en **el perfil principal y en los perfiles herederos** cada vez que cambia el estado del nivel de acceso. Esto mantiene todos los perfiles vinculados al día sobre su estado de acceso actual. El perfil padre muestra el historial completo de transacciones. Los perfiles herederos solo muestran sus actualizaciones de nivel de acceso y un enlace al perfil padre en la sección **Access level**. <img src="/assets/shared/img/98d0dad-non-original_profile.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> **Seguimiento de la misma suscripción en varios perfiles.** Cada perfil heredero tiene su propio `profile_id`, por lo que este no es estable a lo largo de una cadena. Para identificar la misma suscripción en varios perfiles —por ejemplo, al reconciliar eventos de webhook o relacionar perfiles del dashboard con un mismo usuario subyacente— utiliza el identificador del lado del store. | Campo | Usar para | | --- | --- | | `store_original_transaction_id` | Identificar una cadena de suscripciones entre perfiles. Único por suscripción de Apple. | | `profiles_sharing_access_level` (campo de webhook) | Todos los perfiles con nivel de acceso activo gracias a la suscripción, cuando el uso compartido está habilitado. | | `profile_id` | **No** es adecuado para el seguimiento entre perfiles: cada heredero tiene el suyo propio. | ## Compartir acceso de pago entre cuentas de usuario \{#sharing-paid-access-between-user-accounts\} :::link Artículo principal: [Compartir acceso de pago entre cuentas de usuario](sharing-paid-access-between-user-accounts) ::: Para configurar tu política de compartición de nivel de acceso, en la página de configuración [**General**](general), selecciona una opción de sharing. Puedes establecer una política separada para el [entorno sandbox](test-purchases-in-sandbox). --- no_index: true --- **Habilitado (predeterminado)** Los usuarios identificados (aquellos con un [Customer User ID](identifying-users#set-customer-user-id-on-configuration)) pueden compartir el mismo [nivel de acceso](access-level) proporcionado por Adapty si su dispositivo está vinculado al mismo Apple/Google ID. Esto es útil cuando un usuario reinstala la app e inicia sesión con un correo diferente: seguirá teniendo acceso a su compra anterior. Con esta opción, varios usuarios identificados pueden compartir el mismo nivel de acceso. Aunque el nivel de acceso se comparte, todas las transacciones pasadas y futuras se registran como eventos en el Customer User ID original para mantener análisis coherentes y conservar un historial completo de transacciones, incluyendo períodos de prueba, compras de suscripción, renovaciones y más, vinculados al mismo perfil. **Transferir acceso al nuevo usuario** Los usuarios identificados pueden seguir accediendo al [nivel de acceso](access-level) proporcionado por Adapty, incluso si inician sesión con un [Customer User ID](identifying-users#set-customer-user-id-on-configuration) diferente o reinstalan la app, siempre que el dispositivo esté vinculado al mismo Apple/Google ID. A diferencia de la opción anterior, Adapty transfiere la compra entre usuarios identificados. Esto garantiza que el contenido adquirido esté disponible, pero solo un usuario puede tener acceso a la vez. Por ejemplo, si UserA compra una suscripción y UserB inicia sesión en el mismo dispositivo y restaura las transacciones, UserB obtendrá acceso a la suscripción y se le revocará a UserA. Si uno de los usuarios (ya sea el nuevo o el anterior) no está identificado, el nivel de acceso seguirá compartiéndose entre esos perfiles en Adapty. Aunque el nivel de acceso se transfiere, todas las transacciones pasadas y futuras se registran como eventos en el Customer User ID original para mantener análisis coherentes y conservar un historial completo de transacciones, incluyendo períodos de prueba, compras de suscripción, renovaciones y más, vinculados al mismo perfil. Después de cambiar a **Transferir acceso al nuevo usuario**, los niveles de acceso no se transferirán entre perfiles de inmediato. El proceso de transferencia para cada nivel de acceso específico se activa solo cuando Adapty recibe un evento del store, como la renovación de una suscripción, una restauración o al validar una transacción. **Deshabilitado** El primer perfil de usuario identificado que obtenga un nivel de acceso lo conservará para siempre. Esta es la mejor opción si la lógica de negocio de tu app requiere que las compras estén vinculadas a un único Customer User ID. Ten en cuenta que los niveles de acceso siguen compartiéndose entre usuarios anónimos. Puedes "desvincular" una compra [eliminando el perfil del usuario propietario](ss-delete-profile). Tras la eliminación, el nivel de acceso queda disponible para el primer perfil de usuario que lo reclame, ya sea anónimo o identificado. Deshabilitar el uso compartido solo afecta a los nuevos usuarios. Las suscripciones que ya se comparten entre usuarios seguirán compartiéndose incluso después de deshabilitar esta opción. :::warning Apple y Google exigen que las compras in-app se compartan o transfieran entre usuarios porque se basan en el Apple/Google ID para asociar la compra. Sin el uso compartido, restaurar compras podría no funcionar tras reinstalaciones posteriores. Deshabilitar el uso compartido puede impedir que los usuarios recuperen el acceso al iniciar sesión. Recomendamos deshabilitar el uso compartido solo si tus usuarios **deben iniciar sesión** antes de realizar una compra. De lo contrario, un usuario identificado podría comprar una suscripción, iniciar sesión en otra cuenta y perder el acceso de forma permanente. ::: ### ¿Qué opción debo elegir? \{#which-setting-should-i-choose\} | Mi app... | Opción a elegir | | ------------------------------------------------------------ | ------------------------------------------------------------ | | No tiene sistema de inicio de sesión y solo usa los IDs de perfil anónimos de Adapty. | Usa la opción predeterminada, ya que los niveles de acceso siempre se comparten entre IDs de perfil anónimos en las tres opciones. | | Tiene un sistema de inicio de sesión opcional y permite a los clientes realizar compras antes de crear una cuenta. | Elige **Transferir acceso al nuevo usuario** para garantizar que los clientes que compren sin una cuenta puedan restaurar sus transacciones más adelante. | | Requiere que los clientes creen una cuenta antes de comprar, pero permite que las compras estén vinculadas a varios Customer User IDs. | Elige **Transferir acceso al nuevo usuario** para garantizar que solo un Customer User ID tenga acceso a la vez, permitiendo a los usuarios iniciar sesión con un Customer User ID diferente sin perder su acceso de pago. | | Requiere que los clientes creen una cuenta antes de comprar, con reglas estrictas que vinculan las compras a un único Customer User ID. | Elige **Deshabilitado** para garantizar que las transacciones nunca se transfieran entre cuentas. | ## Marcas de tiempo de eventos con fechas futuras (Apple/iOS) \{#event-timestamps-with-future-dates-appleios\} Este comportamiento es exclusivo de la App Store de Apple. El sistema de notificaciones de Google Play no envía eventos con antelación. Las marcas de tiempo de eventos en los perfiles e integraciones pueden mostrar fechas futuras porque Apple envía los eventos de renovación por adelantado. - **Por qué ocurre**: Apple hace esto para garantizar que las suscripciones se renueven automáticamente antes de que expiren, evitando interrupciones en el servicio del usuario. Para más detalles, consulta el Apple Developer Forum: [Server Notifications for Subscriptions](https://developer.apple.com/forums/tags/app-store-server-notifications). - **Tipos de eventos afectados**: Por lo general, esto aplica a las renovaciones de suscripción y las conversiones de prueba a pago. Estos eventos pueden tener marcas de tiempo futuras porque Apple notifica a los sistemas con antelación. - **Otros tipos de eventos**: Las compras in-app adicionales y los cambios de plan de suscripción se registran con sus marcas de tiempo reales, ya que estos eventos no se pueden predecir con antelación. - **Impacto en Analytics y el Event Feed**: Estos eventos solo aparecerán en **Analytics** y el **Event Feed** una vez que sus marcas de tiempo hayan pasado. Los eventos con marcas de tiempo futuras no se muestran en ninguna de las dos secciones. - **Impacto en las integraciones**: Adapty envía los eventos a las integraciones en cuanto los recibe. Si un evento tiene una marca de tiempo futura, Adapty lo envía a tu integración con esa marca de tiempo futura sin modificar. ## Pasos siguientes \{#next-steps\} - Para usar el dashboard de Profiles para encontrar y gestionar usuarios, consulta [Profiles](profiles-crm). - Para configurar la identificación de usuarios en tu app, consulta la guía de SDK para [identificar usuarios](identifying-users). - Para configurar la política de compartición de acceso, consulta [Compartir acceso de pago entre cuentas de usuario](sharing-paid-access-between-user-accounts). --- # File: sharing-paid-access-between-user-accounts --- --- title: "Compartir acceso de pago entre cuentas de usuario" description: "Cómo compartir el acceso de pago entre diferentes cuentas de usuario para usuarios con varios dispositivos o múltiples perfiles en la app" --- Cuando un usuario realiza una compra, Adapty asigna un nuevo [nivel de acceso](access-level) a su [perfil](identifying-users) activo. Este nivel de acceso autoriza al comprador a acceder al contenido de pago. El perfil del comprador puede cambiar involuntariamente si reinstala la app o inicia sesión en una nueva cuenta dentro de la app. Para garantizar un acceso ininterrumpido, Adapty comparte automáticamente el nivel de acceso del usuario entre el perfil original y los que le siguen. Este enfoque funciona bien para la mayoría de las aplicaciones. Sin embargo, si tu lógica de negocio lo requiere, puedes seleccionar una política de compartición de acceso de pago más restrictiva. Abre la página de [General Settings](https://app.adapty.io/settings/general) para configurar una política de compartir niveles de acceso. Para facilitar las pruebas, puedes cambiar esta configuración solo para el [entorno sandbox](#sharing-paid-access-on-sandbox). <Details> :::important Si tu aplicación no autentica usuarios, puedes ignorar esta configuración. Los perfiles anónimos asociados a la misma cuenta de la store *siempre* comparten su nivel de acceso. ::: <summary>¿Qué política de compartir acceso debo elegir? (Haz clic para expandir)</summary> | Mi aplicación... | Mejor opción | | ------------------------------------------------------------ | ------------------------------------------------------------ | | No tiene capacidades de autenticación y solo usa los IDs de perfil anónimos de Adapty. | Usa la opción **Enabled (default)**. | | Permite autenticar usuarios, pero les permite hacer compras sin una cuenta. | Activa la opción **Transfer access to new user**. Los usuarios podrán registrarse y reclamar las compras anónimas. | | Requiere que los clientes creen una cuenta antes de comprar, pero puede vincular un único producto a varios Customer User IDs. | Activa la opción **Transfer access to new user**. Varias cuentas podrán acceder al producto, pero solo de forma secuencial. | | Requiere que los clientes creen una cuenta antes de comprar, con reglas estrictas que vinculan las compras a un único Customer User ID. | **Desactiva** el uso compartido del nivel de acceso. | </Details> <img src="/assets/shared/img/sharing-paid-access.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Habilitado (por defecto) \{#enabled-default\} Esta configuración funciona mejor para aplicaciones **sin autenticación integrada**. Tras la compra, todos los perfiles asociados a la misma cuenta del store heredan automáticamente el nivel de acceso. * Si un usuario inicia sesión en tu app con un nuevo conjunto de credenciales, conserva el acceso al contenido de pago. * Si un usuario reinstala tu aplicación tras un restablecimiento de fábrica, conserva el acceso al contenido de pago. * Si un usuario instala la aplicación en otros dispositivos con la misma cuenta de la store, la compra estará disponible en todos los dispositivos, incluso si cada instancia de la aplicación tiene su propio perfil de cliente. ## Transferir el acceso a un nuevo usuario \{#transfer-access-to-new-user\} Esta configuración es ideal para aplicaciones que permiten compras **con o sin autenticación**, o que quieren aplicar una política de **un dispositivo por usuario**. Adapty limita el acceso a las compras a 1 customer ID a la vez. El propietario del dispositivo puede reinstalar la app, iniciar y cerrar sesión, pero no puede acceder al mismo producto desde más de un customer ID al mismo tiempo. Con esta opción activada, los perfiles anónimos (por ejemplo, un perfil que se activa cuando el usuario cierra sesión) siempre heredan el nivel de acceso del último ID de cliente activo. Esto es necesario para evitar que se pierda el acceso más adelante. :::warning Cuando desactivas la configuración predeterminada y activas **Transfer access to new user**, Adapty no actualiza inmediatamente los niveles de acceso de los perfiles de cliente existentes. El cambio se produce cuando el usuario genera un nuevo evento en el store: por ejemplo, renueva la suscripción o restaura sus compras. ::: ## Desactivar el acceso compartido de pago \{#disable-paid-access-sharing\} Esta configuración **solo es adecuada** para aplicaciones con **autenticación obligatoria** o una implementación propia de gestión de accesos. En cualquier otro caso, los usuarios podrían no poder acceder a sus compras, y tu aplicación corre el riesgo de **no superar la revisión obligatoria del store**. Si deshabilitas el acceso compartido de pago, Adapty vincula el producto al [ID de cliente](identifying-users#set-customer-user-id-on-configuration) activo en el momento de la compra y no comparte el nivel de acceso con ningún otro perfil. Esta política permite una distribución estricta de productos en proporción 1 a 1. :::warning Al deshabilitar el acceso compartido de pago, impides que los IDs de cliente hereden el acceso de pago. Si un ID de cliente ya heredó acceso de pago en el pasado, ese acceso no se puede revocar automáticamente. ::: :::important En situaciones de emergencia, puede que necesites [eliminar un perfil de usuario](api-adapty/operations/deleteProfile) para que el siguiente perfil disponible (ya sea identificado o anónimo) pueda reclamar su nivel de acceso. ::: ## Compartir acceso de pago en sandbox \{#sharing-paid-access-on-sandbox\} Puedes establecer una política de compartición de acceso de pago específicamente para el entorno sandbox. Al probar compras en sandbox, ten en cuenta el siguiente comportamiento: * Apple almacena información sobre tus compras anteriores en el historial de compras de la cuenta. El SDK de Adapty también puede acceder a él. * Si reinstalaas la aplicación y Adapty detecta que el producto ya fue comprado, el perfil activo heredará el nivel de acceso. * Si Apple detecta una compra existente para el producto, no permitirá realizar la misma compra dos veces, aunque el perfil activo no tenga el nivel de acceso necesario. Este comportamiento se produce **independientemente de la configuración de acceso de pago compartido**. Si tu app no muestra el paywall, no puedes comprar el producto. La única solución es **borrar el historial de compras de tu cuenta**. Consulta la [guía de pruebas en sandbox](test-purchases-in-sandbox) para obtener instrucciones detalladas. ## Compartición de acceso de pago en los análisis \{#paid-access-sharing-in-analytics\} * Adapty registra las transacciones a medida que se producen. Una misma transacción puede estar asociada a más de un perfil, pero no se contabiliza más de una vez. * Si dos o más perfiles comparten el mismo nivel de acceso, la compra se atribuye al [perfil principal](how-profiles-work#parent-and-inheritor-profiles). * La herencia del nivel de acceso no afecta a las estadísticas de instalación. Para saber cómo Adapty contabiliza las instalaciones, puedes seleccionar una de las dos [definiciones de instalación](installs#calculation) disponibles en la página de configuración. --- # File: segments --- --- title: "Segmentos" description: "Crea y gestiona segmentos de usuarios para una mejor segmentación en Adapty." --- Un **segmento** es un conjunto de filtros que agrupa usuarios con características comunes. Usa segmentos para dirigir paywalls y pruebas A/B de forma más precisa. Una vez creado un segmento, puedes [usarlo como **audiencia** en Placements y pruebas A/B](audience) para controlar qué paywall ve cada usuario (uno o varios). Algunos ejemplos: - Muestra un paywall estándar a los no suscriptores y ofrece un descuento a los usuarios que hayan cancelado previamente una suscripción o prueba. - Muestra paywalls diferentes a usuarios de distintos países. - Segmenta usuarios basándote en datos de atribución de Apple Search Ads. - Asegúrate de que los usuarios en versiones antiguas de la app sigan viendo el paywall actual, mientras que las versiones más recientes reciban el actualizado. - [En Analytics](controls-filters-grouping-compare-proceeds#filter-and-group-data), filtra por segmentos para ver el rendimiento de grupos de usuarios específicos. Agrupa por segmento para comparar el rendimiento o la contribución dentro de **All users**. <img src="/assets/shared/img/3244407-Segments.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Creación \{#creation\} Para crear un segmento, introduce un nombre y selecciona los atributos que definen sus filtros. Si seleccionas varios atributos, los usuarios deben cumplir todas las condiciones. Adapty aplica lógica AND entre atributos. <img src="/assets/shared/img/1af9744-new_cohort.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Atributos disponibles \{#available-attributes\} :::note Aunque muchos atributos de usuario se establecen automáticamente (como **Country** o **Calculated total revenue USD**), los atributos **Age**, **App user ID**, **Attribution**, **Gender** y **Custom attributes** no se definen automáticamente. Debes [establecer los atributos de usuario](setting-user-attributes) o [pasar los datos de atribución](attribution-integration) si quieres usarlos para segmentación. ::: :::tip Para los atributos basados en fechas, puedes filtrar usando: - **Fecha fija**: Selecciona fechas concretas en un calendario (por ejemplo, mostrar una oferta especial a usuarios que instalaron la app entre el Black Friday y el Cyber Monday) - **Rango relativo**: Define ventanas de tiempo dinámicas como "Últimos 7 días" o "Últimos 3 meses" (por ejemplo, volver a captar usuarios que no se han visto en más de 30 días, o apuntar a instalaciones recientes) Los rangos relativos se actualizan automáticamente, lo que los hace ideales para campañas continuas. Las fechas fijas funcionan mejor para promociones acotadas en el tiempo. ::: | Atributo | Filtrar por | |---------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Age** | La edad del usuario. Ten en cuenta que la edad se calcula cuando Adapty la recibe por primera vez y no se actualiza después. | | **App User ID** | El identificador del usuario en tu app ([customer_user_id](profiles-crm#user-attributes)). Puedes filtrar por su presencia o ausencia; por ejemplo, para mostrar un paywall solo a usuarios que no han iniciado sesión. | | **App version (current)** | La versión actual de la app instalada en el dispositivo del usuario donde Adapty recibió datos de eventos por última vez — **se actualiza a medida que el usuario actualiza la app**, por lo que siempre refleja la versión que están ejecutando en ese momento. Úsala para dirigirte a todos los usuarios que ejecutan una versión específica, incluidos quienes actualizaron a ella desde una versión anterior. Al crear un segmento, selecciona el icono de lápiz junto a **App version** y añade una nueva versión para poder usarla de inmediato.<br/> La condición **version > X.X** te permite medir el impacto en la conversión de todas las versiones de la app anteriores o posteriores a una específica sin listar cada versión individualmente.<br/><br/> **Formato:** Las cadenas de versión deben seguir el formato [SemVer](https://semver.org/). Los ceros iniciales en cualquier parte no son válidos — `26.03.4` no coincidirá, mientras que `26.3.4` sí. Las versiones no válidas se excluyen del segmento de forma silenciosa. | | **App version (on install)** | La versión de la app instalada en el dispositivo del usuario cuando Adapty recibió datos de eventos por primera vez — **fijada en el momento de esa instalación y nunca actualizada**, incluso después de que el usuario actualice la app. Úsala para dirigirte a usuarios según la versión que instalaron originalmente, no su versión actual. `App version (on install) = 1.5.7` solo coincide con usuarios cuya primera instalación fue 1.5.7 y excluye de forma silenciosa a quienes actualizaron a 1.5.7 desde una versión anterior — para capturar también a quienes actualizaron, usa **App version (current)** en su lugar.<br/><br/> **Formato:** Las cadenas de versión deben seguir el formato [SemVer](https://semver.org/). Los ceros iniciales en cualquier parte no son válidos — `26.3.04` no coincidirá, mientras que `26.3.4` sí. Las versiones no válidas se excluyen del segmento de forma silenciosa. | | **Attribution: Ad Group** | El grupo de anuncios de la atribución. | | **Attribution: Ad Set** | El conjunto de anuncios de la atribución. | | **Attribution: Campaign** | El nombre de la campaña de marketing. | | **Attribution: Creative** | La palabra clave del creativo de atribución. | | **Attribution: Channel** | El nombre del canal de marketing. | | **Attribution: Source** | El origen de la atribución. | | **Attribution: Status** | El estado de la atribución. Valores posibles: <ul><li> **Organic** – El usuario instaló la app sin ninguna influencia de marketing de pago (p. ej., búsqueda directa en App Store/Google Play, boca a boca o alcance orgánico en redes sociales).</li><li> **Non-organic** – El usuario fue captado a través de un canal de marketing de pago (p. ej., anuncios, campañas de influencers, programas de referidos).</li><li> **Unknown** – No hay datos de atribución disponibles para este usuario.</li></ul> | | **Calculated subscription state** | El [estado de suscripción actual](profiles-crm#subscription-state) del usuario, que indica si la suscripción está activa, cancelada o si hubo un problema de facturación sin resolver. | | **Calculated total revenue USD** | Los ingresos totales generados por este usuario. | | **Country** | El país del cliente, determinado por su dirección IP más reciente. Adapty actualiza la señal de IP como máximo una vez por semana, por lo que puede desfasarse si el usuario cambia de ubicación o usa una VPN. Para dirigirte al país de la cuenta de App Store / Play Store del usuario, usa **Country from store account**. | | **Country from store account** | El país asociado a la cuenta de iOS o Android del usuario en el store. Ten en cuenta que Adapty solo recopila el país del store para dispositivos iOS con la versión 13 o posterior. | | **Creation date** | La fecha en que se creó el perfil (cuando la app se instaló por primera vez en el dispositivo del usuario). | | **Device** | Tipo de dispositivo basado en metadatos. Por ejemplo, 'Samsung Galaxy' o 'iPhone 13'. | | **Gender** | El género del usuario. Ten en cuenta que el valor lo estableces tú mismo. | | **Installation date** | La fecha en que el usuario instaló la app. | | **Language** | El idioma del dispositivo del usuario. | | **Last seen** | La última fecha en que el usuario abrió la app. | | **OS** | La versión del sistema operativo del dispositivo del usuario. | | **Paid access level** | El nivel de acceso asignado al usuario. | | **Platform** | La plataforma del dispositivo del usuario. Valores posibles: `iOS`, `macOS`, `iPadOS`, `visionOS`, `Android`. <br/> Si los usuarios acceden a tu app desde varias plataformas (p. ej., iOS y Android), la pertenencia al segmento se evalúa de forma independiente para cada plataforma usando los datos más recientes de ese dispositivo específico. Esto permite una segmentación por plataforma incluso para el mismo perfil de usuario. | | **Subscription expiration date** | La fecha de vencimiento de la suscripción o su presencia/ausencia. Muestra `none` para compras de acceso de por vida y permanece vacío si el usuario tiene un perfil pero nunca ha tenido una prueba, suscripción o compra de acceso de por vida. | | **Subscription product** | El ID del producto más reciente de la suscripción activa del cliente. | | **[Custom attributes](profiles-crm#custom-attributes)** | Define tus propios atributos para crear segmentos muy precisos basados en propiedades exclusivas de tu app o negocio. | ## Atributos personalizados \{#custom-attributes\} Define atributos personalizados para crear segmentos más precisos basados en propiedades únicas de tu app o negocio. :::note - Puedes configurar atributos personalizados en el SDK o en el Adapty Dashboard. Para la configuración mediante SDK, sigue las instrucciones [aquí](setting-user-attributes#custom-user-attributes). - Cambiar un atributo personalizado después de usarlo en un segmento puede desincronizar al usuario de ese segmento en [analytics](controls-filters-grouping-compare-proceeds#filter-and-group-data). Los datos reflejarán el valor anterior. ::: ### Cómo configurar un atributo personalizado \{#how-to-configure-a-custom-attribute\} En el Adapty Dashboard, selecciona **Create custom attributes** en el menú desplegable de atributos. <img src="/assets/shared/img/883d3b2-CleanShot_2023-03-16_at_17.20.452x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> | Campo | Descripción | | ------ |--------------------------------------------------------------------------------------------------------------------------------------| | **Name** | Una etiqueta para el atributo personalizado, usada solo en el Adapty Dashboard. | | **Key** | Un identificador único para el atributo. Debe coincidir con la clave utilizada en el SDK. | | **Type** | Elige entre:<ul><li>String: Requiere una lista predefinida de valores posibles.</li><li>Number: Solo acepta valores numéricos.</li></ul> | | **Values** | Si seleccionas `String`, introduce la lista de valores posibles. Si eliges `Number`, el atributo solo aceptará valores numéricos. Los atributos numéricos admiten decimales y pueden usarse con operadores de comparación. | Después de rellenar los campos obligatorios, puedes usar los atributos personalizados en tus segmentos, [pruebas A/B](ab-tests) y mucho más. Cada perfil puede tener hasta 30 atributos personalizados. ## Total de usuarios y muestra aleatoria \{#total-number-and-random-sample\} Una vez creado un segmento, Adapty muestra el número total de usuarios que cumplen los criterios del segmento. Adapty también muestra una muestra aleatoria de 40 usuarios que se ajustan a los criterios. Úsala para comprobar tu segmento y asegurarte de que está configurado correctamente. <img src="/assets/shared/img/segment-random-set.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Duplicar segmentos \{#duplicate-segments\} Si necesitas un segmento similar a uno existente, duplícalo en lugar de crearlo desde cero. Esto ahorra tiempo a los equipos que gestionan varias campañas o pruebas A/B con grupos de usuarios que se solapan. Duplicar un segmento crea una copia con todos sus filtros y descripción. Al nombre del nuevo segmento se le añade "(copy)" para que puedas distinguirlo del original. El nuevo segmento es independiente del original: los cambios en uno no afectan al otro. Para duplicar un segmento en el Adapty Dashboard: 1. Abre la sección **Profiles & Segments** en el menú principal de Adapty y cambia a la pestaña [**Segments**](https://app.adapty.io/segments). 2. Haz clic en el botón de **3 puntos** junto al segmento y selecciona **Duplicate**. <img src="/assets/shared/img/duplicate-segment.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Abre el nuevo segmento y ajusta sus filtros según sea necesario. ## Eliminar segmentos \{#delete-segments\} Cuando ya no necesites un segmento, puedes eliminarlo de forma permanente. Adapty bloquea la eliminación si el segmento se está usando como audiencia en alguno de los siguientes casos: - **Un placement**: Al menos un placement no eliminado usa el segmento como audiencia. - **Una prueba A/B (activa o completada)**: Al menos una prueba A/B no eliminada usa el segmento como audiencia. Para eliminar segmentos, Adapty considera como activas tanto las pruebas A/B **Live** como las **Completed**. Una prueba completada sigue utilizando la audiencia para mostrar el paywall o onboarding posterior a la prueba a los usuarios que coincidan, y las métricas históricas de la prueba están vinculadas a ese segmento. El segmento solo se libera cuando se elimina la propia prueba A/B. :::warning La eliminación de un segmento es permanente. El segmento no se puede restaurar. ::: Para eliminar un segmento en el Adapty Dashboard: 1. Ve a **Profiles & Segments** en el menú principal de Adapty y cambia a la pestaña [**Segments**](https://app.adapty.io/segments). 2. Haz clic en el botón de **3 puntos** junto al segmento y selecciona **Delete**. 3. Escribe el nombre del segmento en el campo de confirmación y haz clic en **Delete forever**. :::info Si el segmento está en uso, el cuadro de diálogo muestra los placements y las pruebas A/B que lo referencian. Para desbloquear la eliminación, abre cada placement o prueba A/B de la lista y elimina el segmento de su audiencia, o elimina el placement o la prueba A/B por completo. Una vez que nada haga referencia al segmento, podrás eliminarlo. ::: --- # File: event-feed --- --- title: "Feed de eventos" description: "Supervisa y analiza la actividad de los usuarios con el feed de eventos de Adapty." --- El feed de eventos te permite rastrear visualmente los [Eventos](events) generados por Adapty y comprobar el estado de su exportación a integraciones de terceros, incluido el webhook. :::warning Ten en cuenta que las transacciones creadas mediante la [API del servidor (versión 1)](server-side-api-specs-legacy#requests) no aparecen en el **Event Feed**. Para asegurarte de que se incluyan, usa la [API del servidor (versión 2)](api-adapty/operations/setTransaction). ::: <img src="/assets/shared/img/event-status.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <p> </p> :::note El estado de envío de AppsFlyer, Facebook Ads y Branch puede ser inexacto, ya que no siempre devuelven errores cuando se producen. ::: Para ver el perfil del usuario que ha iniciado la transacción, haz clic en el botón **View Profile** en los detalles del evento. --- # File: ab-tests --- --- title: "Prueba A/B" description: "Optimiza los precios de suscripción con pruebas A/B en Adapty para mejorar las tasas de conversión." --- :::tip Puedes obtener un plan de prueba A/B accionable sin hacer la investigación tú mismo. [Growth Autopilot](autopilot) audita tu paywall, compara a tus competidores y genera sugerencias a partir de datos anonimizados de más de 20.000 aplicaciones de suscripción rastreadas por Adapty. ::: Aumenta los ingresos de tu app ejecutando pruebas A/B en Adapty. Compara diferentes paywalls y onboardings para encontrar lo que mejor convierte, sin necesidad de cambios en el código. Por ejemplo, puedes probar: - Precios de suscripción - Diseño, textos y estructura del paywall - Períodos de prueba y duraciones de suscripción - Diseños de onboarding ## Requisitos previos \{#prerequisites\} Antes de configurar una prueba A/B, necesitas tener: - **Paywalls**: Un [paywall](paywalls) es una pantalla que se muestra a los usuarios para presentar tus ofertas de suscripción. Debes crear al menos dos paywalls antes de configurar una prueba A/B. - **Placements**: Un [placement](placements) es un espacio con nombre en tu app donde se muestra un paywall u onboarding. Tu prueba A/B se ejecuta dentro de uno o más placements. - **Onboardings**: Para las pruebas A/B de onboarding, también debes crear al menos dos [onboardings](onboardings). :::warning Si no usas el [Adapty Paywall Builder](adapty-paywall-builder), [envía las vistas del paywall a Adapty](present-remote-config-paywalls#track-paywall-view-events) con `.logShowPaywall()`. Sin este método, Adapty no puede calcular las vistas del paywall en la prueba y las estadísticas de conversión serán inexactas. ::: ## Tipos de prueba A/B \{#ab-test-types\} Adapty admite tres tipos de prueba A/B: - **Regular**: Se ejecuta en un único placement de paywall. - **Onboarding**: Se ejecuta en un único placement de onboarding. - **Crossplacement**: Se ejecuta en múltiples placements de paywall, mostrando la misma variante al usuario en todos ellos. Para una comparación completa de tipos, casos de uso y reglas de prioridad, consulta [Tipos de prueba A/B](ab-test-types). ## Próximos pasos \{#next-steps\} - [Growth Autopilot](autopilot) — Analiza tu paywall, obtén información de mercado y genera un plan de prueba A/B - [Tipos de prueba A/B](ab-test-types) — Aprende sobre los tipos de prueba y cuándo usar cada uno - [Crear, ejecutar y detener una prueba A/B](run_stop_ab_tests) — Configura y ejecuta tu primera prueba - [Resultados y métricas de la prueba A/B](results-and-metrics) — Entiende los datos de tu prueba A/B y elige un ganador --- # File: ab-test-types --- --- title: "Tipos de pruebas A/B" description: "Aprende sobre los tres tipos de pruebas A/B en Adapty: regular, onboarding y crossplacement." --- Adapty ofrece tres tipos de pruebas A/B, cada uno adecuado para diferentes escenarios de prueba: - **Prueba A/B regular:** Una prueba A/B creada para un único placement de [paywall](paywalls). - **Prueba A/B de onboarding:** Una prueba A/B creada para un único placement de [onboarding](onboardings). - **Prueba A/B crossplacement:** Una prueba A/B creada para múltiples placements de paywall en tu app. Una vez que la prueba A/B asigna una <InlineTooltip tooltip="variante">Las variantes de una prueba A/B son versiones alternativas del paywall u onboarding que se van a probar.</InlineTooltip>, muestra esa variante de forma consistente en todas las secciones seleccionadas de tu app. :::warning Las pruebas A/B crossplacement solo están disponibles para Adapty SDK a partir de la versión v3.5.0. Las pruebas A/B de onboarding requieren Adapty SDK v3.8.0+ (iOS, Android, React Native, Flutter), v3.14.0+ (Unity) o v3.15.0+ (Kotlin Multiplatform, Capacitor). Los usuarios con versiones anteriores las omiten. ::: Cada paywall/onboarding recibe un peso que distribuye el tráfico durante la prueba. Por ejemplo, con pesos del 70 % y el 30 %, el primer paywall se muestra a aproximadamente 700 de cada 1.000 usuarios y el segundo a unos 300. En las pruebas crossplacement, los pesos se establecen por variante, no por paywall. Esta configuración te permite comparar distintos paywalls y tomar decisiones basadas en datos para la estrategia de monetización de tu app. ## Cuándo usar cada tipo \{#when-to-use-each-type\} Cada tipo de prueba A/B es útil si: - **Pruebas A/B regulares** y **de onboarding**: - Tu app solo tiene un placement. - Quieres ejecutar la prueba A/B en un único placement y hacer seguimiento de los cambios económicos solo para ese placement, aunque tu app tenga varios. - Quieres ejecutar una prueba A/B con usuarios antiguos (aquellos que ya han visto al menos un paywall de Adapty). - **Prueba A/B crossplacement**: - Quieres sincronizar variantes en múltiples placements. Por ejemplo, puedes cambiar los precios en el flujo de onboarding y en los ajustes de la app al mismo tiempo. - Quieres evaluar la economía global de tu app. Ejecutar la prueba en todos los placements hace que las estadísticas sean más fáciles de analizar que probar placements de forma aislada. - Quieres ejecutar una prueba A/B solo con usuarios nuevos, es decir, usuarios que nunca han visto ningún paywall de Adapty. - Quieres usar múltiples paywalls dentro de una única variante: <img src="/assets/shared/img/ab-test-variants.png" alt="Ejemplo de múltiples paywalls dentro de una única variante de prueba A/B crossplacement" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Diferencias clave \{#key-differences\} | Característica | Prueba A/B regular | Prueba A/B crossplacement | | ------------------------------------ |---------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------| | **Qué se prueba** | Un paywall/onboarding | Conjunto de paywalls pertenecientes a una variante | | **Consistencia de la variante** | La variante se determina de forma independiente para cada placement | La misma variante se usa en todos los placements de paywall | | **Segmentación de audiencia** | Definida por placement de paywall/onboarding | Compartida en todos los placements de paywall | | **Analítica** | Analizas un placement de paywall/onboarding | Analizas toda la app en los placements que forman parte de la prueba | | **Distribución del peso de variante**| Por paywall/onboarding | Por conjunto de paywalls | | **Usuarios** | Para todos los usuarios | Solo usuarios nuevos (los que no han visto ningún paywall de Adapty) | | **Versión de Adapty SDK** | Cualquiera para paywalls. Para onboardings: v3.8.0+ (iOS, Android, React Native, Flutter), v3.14.0+ (Unity), v3.15.0+ (KMP, Capacitor) | 3.5.0+ | | **Ideal para** | Probar cambios independientes en un único placement de paywall/onboarding sin considerar la economía global de la app | Evaluar estrategias de monetización globales en toda la app | ## Lógica de selección de pruebas A/B \{#ab-test-selection-logic\} **Las pruebas A/B crossplacement tienen prioridad sobre las pruebas A/B regulares.** Sin embargo, las pruebas crossplacement solo se muestran a **usuarios nuevos**: aquellos que nunca han visto ningún paywall de Adapty (el método `getPaywall` del SDK nunca ha sido llamado para ellos). Esto garantiza la consistencia de los resultados entre placements. El siguiente diagrama muestra la lógica que usa Adapty para seleccionar una prueba A/B para un placement: <img src="/assets/shared/img/ab-tests-scheme.webp" alt="Diagrama que muestra la lógica de selección de pruebas A/B para un placement de paywall" style={{ border: '1px solid #727272', /* border width and color */ width: '350px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En la página **A/B Tests**, las pruebas regulares, de onboarding y crossplacement aparecen en pestañas separadas. <img src="/assets/shared/img/ab-tests-tabs.png" alt="Página de lista de pruebas A/B con pestañas para los tipos Regular, Onboarding y Crossplacement" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Limitaciones de las pruebas A/B crossplacement \{#crossplacement-ab-test-limitations\} Las pruebas A/B crossplacement no pueden incluir placements de onboarding. Las pruebas A/B crossplacement garantizan que cada usuario vea la misma variante en todos los placements de la prueba. Esto implica las siguientes limitaciones: * Solo pueden participar usuarios nuevos. Un usuario nuevo es aquel que no ha visto ningún paywall de Adapty y cuya app nunca ha llamado a `getPaywall`. Adapty no puede garantizar una cadena de paywalls consistente para otros usuarios. * El primer placement que encuentra el usuario determina qué paywall muestra Adapty. No puedes cambiar la asignación de un usuario ni inscribir al mismo usuario en más de una prueba A/B crossplacement. :::warning Una vez que un usuario recibe un paywall crossplacement, lo verá durante 90 días, incluso después de detener la prueba. Para cambiar esta duración, en los ajustes **General**, modifica **[Cross-placement variation stickiness](general#9-cross-placement-variation-stickiness)**. ::: ## Prioridad de las pruebas A/B crossplacement \{#crossplacement-ab-test-priority\} * Las pruebas A/B crossplacement siempre tienen prioridad sobre las pruebas A/B regulares y de onboarding. Si un usuario nuevo cumple los requisitos tanto para una prueba crossplacement como para una prueba regular en el mismo placement, se muestra la prueba crossplacement. * Cuando varias pruebas A/B crossplacement con la misma audiencia comparten el mismo placement, Adapty asigna automáticamente la prioridad de las pruebas según el orden en que se añadieron. La primera prueba tiene la prioridad más alta. No puedes cambiarla manualmente. * Las pruebas que se dirigen a segmentos más pequeños de tu audiencia tienen automáticamente prioridad sobre las que se dirigen al segmento de todos los usuarios. :::note En Analytics, una prueba A/B crossplacement aparece como varias pruebas secundarias, una por placement. Las pruebas secundarias siguen el patrón de nomenclatura `<nombre-de-prueba> child-0`, `<nombre-de-prueba> child-1`, etc. La numeración coincide con el orden de los placements en la página de detalles de la prueba A/B. Para ver los resultados de un placement específico, filtra por **Placement**. ::: ## Próximos pasos \{#next-steps\} - [Crear, ejecutar y detener una prueba A/B](run_stop_ab_tests) — Configura y lanza tu primera prueba - [Resultados y métricas de pruebas A/B](results-and-metrics) — Analiza el rendimiento y elige un ganador --- # File: run_stop_ab_tests --- --- title: "Crear, ejecutar y detener una prueba A/B" description: "Guía paso a paso para crear, ejecutar y detener pruebas A/B en Adapty." --- Este artículo cubre el ciclo de vida completo de una prueba A/B en Adapty: crear la prueba, ejecutarla y detenerla cuando estés listo para revisar los resultados. ## Requisitos previos \{#prerequisites\} Antes de configurar una prueba A/B, debes tener: - Al menos dos [paywalls](paywalls) creados (u [onboardings](onboardings) para una prueba A/B de onboarding) - Un [placement](placements) configurado en tu app - Una decisión sobre qué tipo de prueba A/B usar — consulta [Tipos de prueba A/B](ab-test-types) :::warning Si no estás usando el [Adapty Paywall Builder](adapty-paywall-builder), [envía las vistas de paywall a Adapty](present-remote-config-paywalls#track-paywall-view-events) con `.logShowPaywall()`. Sin este método, Adapty no puede calcular las vistas del paywall en la prueba y las estadísticas de conversión serán inexactas. ::: :::info Las pruebas A/B en Adapty siguen un flujo en dos pasos. Primero creas la prueba y la guardas como borrador — no se activa de inmediato. Cuando estés listo, la ejecutas por separado. Esto te permite revisar la configuración antes de que los usuarios la vean. ::: ## Crear una prueba A/B \{#create-an-ab-test\} Al crear una nueva prueba A/B, debes incluir al menos dos [paywalls](paywalls) u [onboardings](onboardings), según el tipo de prueba. Para crear una nueva prueba A/B: 1. Ve a [A/B tests](ab-tests) desde el menú principal de Adapty. <img src="/assets/shared/img/go-to-abtests.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la parte superior derecha, haz clic en **Create A/B test**. <img src="/assets/shared/img/create-abtest.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Create the A/B test**, escribe un **Test name**. Este campo es obligatorio. Elige un nombre que describa claramente de qué trata la prueba para poder identificarla al revisar los resultados. 4. Rellena el **Test goal** para describir lo que quieres lograr (por ejemplo, aumentar las suscripciones o reducir la tasa de abandono). 5. Haz clic en **Select placement** y elige un placement de paywall para una prueba A/B normal o un placement de onboarding para una prueba A/B de onboarding. 6. Configura el contenido de la prueba en la tabla **Variants**. Cada fila es una variante y cada columna es un placement. Añade un paywall en cada intersección. Por defecto, la tabla tiene 2 variantes y 1 placement. Puedes añadir hasta 20 variantes y varios placements. En cuanto añadas un segundo placement, la prueba se convierte en una prueba A/B Crossplacement. <img src="/assets/shared/img/abtest-variants.png" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> Referencia: <div style={{ marginLeft: '2em' }}> <table> <tr> <td>1</td> <td>Renombra la variante para hacerla más descriptiva.</td> </tr> <tr> <td>2</td> <td>Cambia el peso de la variante. La suma de todas las variantes debe ser igual al 100%.</td> </tr> <tr> <td>3</td> <td>Añade más variantes si es necesario.</td> </tr> <tr> <td>4</td> <td>Añade más placements si es necesario.</td> </tr> <tr> <td>5</td> <td>Añade paywalls u onboardings para mostrar en los placements de cada variante.</td> </tr> </table> </div> 7. Guarda la prueba. Tienes dos opciones: 1. **Save as draft**: La prueba no se activará de inmediato. Puedes lanzarla más tarde desde el placement o desde la lista de pruebas A/B. Úsala para revisar la configuración antes del lanzamiento. 2. **Run A/B test**: Lanza la prueba de inmediato. La prueba se activa en cuanto haces clic en este botón. Una vez guardada como borrador, continúa en [Ejecutar una prueba A/B](#run-an-ab-test). ## Editar una prueba A/B \{#edit-an-ab-test\} Solo puedes editar las pruebas A/B que están guardadas como borradores. Una vez que la prueba está activa, no se puede modificar. Para actualizar una prueba en curso, usa la opción **Modify** — esto crea un duplicado con el mismo nombre donde puedes hacer cambios. Adapty detiene la prueba original, y tanto la versión original como la modificada aparecen por separado en tus analíticas. ## Ejecutar una prueba A/B \{#run-an-ab-test\} Ejecutar una prueba A/B en Adapty significa asignarla a un placement para que pueda empezar a mostrar paywalls y onboardings a los usuarios. 1. Ve a la sección [A/B tests](ab-tests) desde el menú principal de Adapty. 2. Asegúrate de estar viendo la lista correcta — las pruebas A/B **Regular**, **Onboardings** y **Crossplacement** se muestran en pestañas separadas entre las que puedes cambiar. <img src="/assets/shared/img/run-ab-test.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Cambia a la pestaña **Drafts**. Solo se pueden iniciar las pruebas en borrador. 4. Junto a la prueba que quieres lanzar, haz clic en **Run A/B test**. <img src="/assets/shared/img/run-ab-test-2.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Se abre la ventana **Edit A/B test**. Revisa la configuración y haz los cambios finales que necesites. Si falta el placement o la audiencia, agrégalos ahora. 6. Después de revisar la configuración, haz clic en **Run A/B test** para iniciarla. Tras lanzar la prueba, puedes hacer seguimiento de su progreso y ver los datos de rendimiento en la página [Resultados y métricas de la prueba A/B](results-and-metrics). ## Detener una prueba A/B \{#stop-an-ab-test\} Cuando detienes una prueba A/B, esta finaliza y puedes revisar los resultados. También decides qué mostrar a los usuarios en los placements afectados una vez que la prueba concluya. <img src="/assets/shared/img/stop-ab-test.png" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 1. Abre la sección [A/B tests](https://app.adapty.io/ab-tests) y ve a la pestaña **Live**. 2. Junto a la prueba que quieres detener, haz clic en el menú de tres puntos y elige **Stop A/B test**. 3. En la ventana **Stop the A/B test**, decide qué debe ocurrir una vez que finalice la prueba. Tienes tres opciones: | Opción | Descripción | |--------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Mostrar uno de los paywalls/onboardings probados | Elige el paywall o onboarding ganador según los resultados de la prueba, como ingresos, probabilidad de ser el mejor (**P2BB**) e ingresos por cada 1.000 usuarios. Este paywall u onboarding se mostrará para el placement y la audiencia seleccionados. | | Seleccionar paywalls/onboardings que no participen en la prueba A/B | Elige cualquier paywall u onboarding que no forme parte de la prueba A/B actual. Úsalo cuando ninguna de las variantes probadas haya cumplido tus objetivos. | | No mostrar ningún paywall/onboarding específico | Para el placement y la audiencia seleccionados, no se seleccionará ningún paywall u onboarding específico al finalizar la prueba A/B. En cambio, se mostrará el siguiente paywall u onboarding disponible según la prioridad de audiencia. Es una buena opción si prefieres que tu configuración actual decida qué paywall u onboarding mostrar, sin seleccionar uno manualmente. | :::note Detener una prueba A/B es irreversible — la prueba no puede reiniciarse. Asegúrate de haber recopilado suficientes datos antes de decidir detenerla. ::: 4. Haz clic en el botón **Stop and complete this A/B test**. Una vez finalizada la prueba A/B, dejará de estar activa y los paywalls u onboardings de ella ya no se mostrarán a los nuevos usuarios. Puedes seguir accediendo a los resultados y métricas de la prueba A/B en la [página de métricas de prueba A/B](results-and-metrics#metrics-controls) para revisar el rendimiento de los usuarios que participaron mientras la prueba estaba activa. Las métricas pueden seguir actualizándose a medida que se atribuyen nuevos eventos de compra o ingresos a esos usuarios. --- # File: ab-test-no-paywall-variants --- --- title: "Añadir variantes de prueba A/B sin paywalls" description: "Ejecuta una prueba A/B donde una variante omite el paywall, usando una bandera de Remote Config para controlar si el paywall se muestra." --- Puedes medir el impacto de tu paywall ejecutando una prueba A/B contra una variante vacía. Una variante muestra tu paywall; la otra no muestra nada. Tu app lee una bandera del Remote Config del paywall para decidir si renderizarlo. ## Cómo funciona \{#how-it-works\} La configuración usa dos paywalls en el mismo placement: - **Paywall A**: El paywall que quieres probar, con `show_paywall` establecido en `true` en su Remote Config. - **Paywall B**: Un paywall vacío con `show_paywall` establecido en `false` en su Remote Config. Cuando `getPaywall` devuelve un paywall, tu app lee la bandera `show_paywall`. Si la bandera es `true`, la app renderiza el paywall. Si la bandera es `false`, la app omite el renderizado y el usuario continúa sin ver un paywall. ## 1. Añade la bandera show_paywall en el Remote Config \{#1-add-the-show_paywall-flag-in-remote-config\} Necesitas dos paywalls en el mismo placement: el Paywall A (el que quieres probar) y el Paywall B (un paywall vacío). Añade un campo `show_paywall` a cada uno para que tu app pueda ramificarse usando la misma clave en ambas variantes. Para añadir la bandera al Paywall A: 1. Abre la sección [**Paywalls**](https://app.adapty.io/paywalls) en el menú principal de Adapty y selecciona el Paywall A. 2. Cambia a la pestaña **Remote config**. 3. En la vista **Table**, haz clic en **Add row** y crea un campo con el nombre `show_paywall` y el valor `true`. En la vista **JSON**, la entrada tiene este aspecto: ```json showLineNumbers { "show_paywall": true } ``` 4. Guarda los cambios. Repite los mismos pasos para el Paywall B, pero establece `show_paywall` en `false`. Para más detalles sobre el Remote Config, consulta [Diseña un paywall con Remote Config](customize-paywall-with-remote-config). :::tip Establecer `show_paywall` en ambas variantes hace que la ruta de código sea idéntica para los dos grupos y facilita ampliar el test con más variantes más adelante. ::: ## 2. Configura la prueba A/B \{#2-set-up-the-ab-test\} 1. [Crea una prueba A/B](run_stop_ab_tests) en el placement y añade ambos paywalls como variantes. 2. Establece los pesos de las variantes para distribuir el tráfico entre los usuarios que ven el paywall y los que no. ## 3. Comprueba la bandera en tu app \{#3-check-the-flag-in-your-app\} Lee `show_paywall` del Remote Config del paywall devuelto por `getPaywall`. Si la bandera es `false`, omite el renderizado y deja que el usuario continúe. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> ```swift showLineNumbers do { let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID") let showPaywall = paywall.remoteConfig?.dictionary?["show_paywall"] as? Bool ?? true if showPaywall { // Render the paywall } } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Android"> ```kotlin showLineNumbers Adapty.getPaywall("YOUR_PLACEMENT_ID") { result -> when (result) { is AdaptyResult.Success -> { val paywall = result.value val showPaywall = paywall.remoteConfig?.dataMap?.get("show_paywall") as? Boolean ?: true if (showPaywall) { // Render the paywall } } is AdaptyResult.Error -> { // handle the error } } } ``` </TabItem> <TabItem value="react-native" label="React Native"> ```typescript showLineNumbers try { const paywall = await adapty.getPaywall({ placementId: "YOUR_PLACEMENT_ID" }); const showPaywall = paywall.remoteConfig?.data?.["show_paywall"] ?? true; if (showPaywall) { // Render the paywall } } catch (error) { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter"> ```dart showLineNumbers try { final paywall = await Adapty().getPaywall(id: "YOUR_PLACEMENT_ID"); final bool showPaywall = paywall.remoteConfig?.dictionary?['show_paywall'] as bool? ?? true; if (showPaywall) { // Render the paywall } } on AdaptyError catch (adaptyError) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity"> ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", (paywall, error) => { if (error != null) { // handle the error return; } var showPaywall = paywall.RemoteConfig?.Dictionary?["show_paywall"] as bool? ?? true; if (showPaywall) { // Render the paywall } }); ``` </TabItem> <TabItem value="kmp" label="Kotlin Multiplatform"> ```kotlin showLineNumbers Adapty.getPaywall( placementId = "YOUR_PLACEMENT_ID" ).onSuccess { paywall -> val showPaywall = paywall.remoteConfig?.dataMap?.get("show_paywall") as? Boolean ?: true if (showPaywall) { // Render the paywall } }.onError { error -> // handle the error } ``` </TabItem> <TabItem value="capacitor" label="Capacitor"> ```typescript showLineNumbers try { const paywall = await adapty.getPaywall({ placementId: 'YOUR_PLACEMENT_ID' }); const showPaywall = paywall.remoteConfig?.data?.['show_paywall'] ?? true; if (showPaywall) { // Render the paywall } } catch (error) { // handle the error } ``` </TabItem> </Tabs> El valor de respaldo `true` mantiene el paywall visible cuando falta la bandera, por lo que los paywalls existentes que no tienen el campo no se ven afectados. :::important Si renderizas el paywall tú mismo (sin el [Paywall Builder](adapty-paywall-builder)), llama a [`logShowPaywall`](present-remote-config-paywalls#track-paywall-view-events) cuando muestres el Paywall A. Sin esto, Adapty no puede contabilizar las visualizaciones del paywall en el test. No registres una visualización para el Paywall B, ya que nunca se muestra. ::: ## Próximos pasos \{#next-steps\} - [Crear, ejecutar y detener una prueba A/B](run_stop_ab_tests) — Configura el test que incluye ambas variantes - [Resultados y métricas de la prueba A/B](results-and-metrics) — Compara la variante sin paywall con tu paywall --- # File: results-and-metrics --- --- title: "Resultados y métricas de las pruebas A/B" description: "Analiza los resultados y las métricas clave en Adapty para mejorar el rendimiento de las suscripciones y la interacción de los usuarios con tu app." --- Descubre datos e insights valiosos de nuestras [pruebas A/B](ab-tests), donde comparamos distintos paywalls y onboardings para ver cómo afectan al comportamiento de los usuarios, la interacción y las tasas de conversión. Analizando las métricas y los resultados de aquí, podrás tomar decisiones inteligentes y mejorar el rendimiento de tu app. Sumérgete en los datos para encontrar insights accionables y potenciar el éxito de tu app. ## Resultados de la prueba A/B \{#ab-test-results\} Estas son las tres métricas que Adapty ofrece para los resultados de las pruebas A/B: <img src="/assets/shared/img/ab-test-results.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> **Revenue**: Esta métrica muestra el importe total en USD generado por compras y renovaciones, menos los reembolsos realizados a los usuarios. Incluye tanto la compra inicial como las renovaciones posteriores de la suscripción. Revenue te ayuda a entender el rendimiento financiero de cada variante de la prueba A/B e identificar cuál genera más dinero. Consulta más información sobre las métricas de [paywall](paywall-metrics) y [onboarding](onboarding-metrics). **Probability to be best**: Adapty utiliza un sólido marco de análisis matemático para analizar los resultados de las pruebas A/B y ofrece una métrica denominada Probability to be best. Esta métrica evalúa la probabilidad de que una variante concreta sea la opción de mejor rendimiento (en términos de ingresos a largo plazo) entre todas las variantes probadas. Se expresa como un porcentaje que va del 1% al 100%. Para más información sobre cómo Adapty calcula esta métrica, consulta la [documentación.](maths-behind-it) La opción de mejor rendimiento, determinada por Revenue per 1K user, se resalta en verde y se selecciona automáticamente como opción predeterminada. **Revenue per 1K users**: La métrica de ingresos por cada 1.000 usuarios calcula el promedio de ingresos generados por cada 1.000 usuarios para cada variante de la prueba A/B. Esta métrica te ayuda a entender la eficiencia de ingresos de tus variantes, independientemente del número total de usuarios. Permite comparar el rendimiento de las distintas variantes en una escala estandarizada y tomar decisiones informadas basadas en la eficiencia de generación de ingresos. **Prediction intervals for revenue 1K users**: La métrica de ingresos por cada 1.000 usuarios también incluye intervalos de predicción. Estos intervalos representan el rango dentro del cual se predice que caerán los ingresos reales por cada 1.000 usuarios de una variante determinada, basándose en los datos disponibles y el análisis estadístico. En el contexto de las pruebas A/B, al analizar los ingresos generados por diferentes variantes, calculamos el promedio de ingresos por cada 1.000 usuarios para cada variante. Dado que los ingresos pueden variar entre usuarios, los intervalos de predicción ofrecen una indicación clara de los valores plausibles para los ingresos por cada 1.000 usuarios, teniendo en cuenta la variabilidad e incertidumbre asociadas al proceso de predicción. Al incorporar los intervalos de predicción a la métrica de ingresos por cada 1.000 usuarios, Adapty te permite evaluar la eficiencia de ingresos de las variantes de tu prueba A/B considerando el rango de posibles resultados. Esta información te ayuda a tomar decisiones basadas en datos y a optimizar tu estrategia de suscripción de forma eficaz, teniendo en cuenta la incertidumbre del proceso de predicción y los valores plausibles para los ingresos por cada 1.000 usuarios. Al analizar estas métricas de Adapty, puedes obtener información sobre el rendimiento financiero, la significancia estadística y la eficiencia de ingresos de las variantes de tus pruebas A/B, lo que te permite tomar decisiones basadas en datos y optimizar tu estrategia de suscripción de manera efectiva. ## Métricas de las pruebas A/B \{#ab-test-metrics\} Adapty proporciona un conjunto completo de métricas para ayudarte a medir de forma efectiva el rendimiento de tus pruebas A/B realizadas en las variaciones de tu paywall u onboarding. Estas métricas se actualizan continuamente en tiempo real, excepto las vistas, que se actualizan de forma periódica. Entender estas métricas te ayudará a evaluar la efectividad de las distintas variaciones y a tomar decisiones basadas en datos para optimizar tu estrategia de paywall u onboarding. Las métricas de las pruebas A/B están disponibles en la lista de pruebas A/B, donde puedes obtener una visión general del rendimiento de todas tus pruebas A/B. Esta vista completa ofrece métricas agregadas para cada variación de la prueba, lo que te permite comparar su rendimiento e identificar diferencias significativas. Para un análisis más detallado de cada prueba A/B, puedes acceder a las métricas de detalle de la prueba A/B. Esta sección proporciona métricas detalladas específicas para la prueba A/B seleccionada, lo que te permite profundizar en el rendimiento de las variaciones individuales. Todas las métricas, excepto las vistas, se atribuyen al producto dentro del paywall o del onboarding. ## Controles de métricas \{#metrics-controls\} El sistema muestra las métricas según el período de tiempo seleccionado y las organiza de acuerdo con el parámetro de la columna izquierda con tres niveles de sangría. ### Filtrado por fecha de instalación del perfil \{#profile-install-date-filtration\} <img src="/assets/shared/img/2bf4d9f-Area.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> La casilla **Filter metrics by install date** permite filtrar las métricas según la fecha de instalación del perfil, en lugar de los filtros predeterminados que usan la fecha de prueba/compra para las transacciones o la fecha de visualización para las vistas de paywall u onboarding. Al marcar esta casilla, puedes centrarte en medir el rendimiento de adquisición de usuarios para un período específico, alineando las métricas con la fecha de instalación del perfil. Esta opción es útil para personalizar el análisis de métricas según tus necesidades concretas. ### Rangos de tiempo \{#time-ranges\} Puedes elegir entre un rango de períodos de tiempo para analizar los datos de métricas, lo que te permite centrarte en duraciones específicas como días, semanas, meses o rangos de fechas personalizados. <img src="/assets/shared/img/ab-test-time-ranges.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de Analytics](controls-filters-grouping-compare-proceeds) ::: Adapty ofrece herramientas potentes para filtrar y personalizar el análisis de métricas según tus necesidades. Con la página de métricas de Adapty, tienes acceso a distintos rangos de tiempo, opciones de agrupación y posibilidades de filtrado. - ✅ Filtrar por: Audiencia, atribución, país, paywall, estado del paywall, grupo de paywalls, onboarding, placement, país, store, producto y store del producto. - ✅ Agrupar por: Producto y store. :::note Cuando filtras por prueba A/B, las pruebas A/B entre placements aparecen como pruebas secundarias independientes (por ejemplo, `My test child-0`, `My test child-1`), una por placement. Consulta [Limitaciones de las pruebas A/B entre placements](ab-test-types#crossplacement-ab-test-limitations) para más detalles. ::: ## Gráfico de métrica individual \{#single-metrics-chart\} Uno de los componentes clave de la página de métricas de paywall u onboarding es la sección de gráficos, que representa visualmente las métricas seleccionadas y facilita su análisis. <img src="/assets/shared/img/e6b0674-Area.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> La sección de gráficos de la página de métricas de la prueba A/B incluye un gráfico de barras horizontales que representa visualmente los valores de la métrica seleccionada. Cada barra del gráfico corresponde a un valor de métrica y su tamaño es proporcional, lo que facilita entender los datos de un vistazo. La línea horizontal indica el período de tiempo analizado, y la columna vertical muestra los valores numéricos de las métricas. El valor total de todos los valores de las métricas se muestra junto al gráfico. Además, al hacer clic en el icono de flecha en la esquina superior derecha de la sección del gráfico, se expande la vista y se muestran las métricas seleccionadas en la línea completa del gráfico. ## Resumen de la prueba A/B \{#ab-test-summary\} Junto al gráfico de métrica individual, se muestra la sección de resumen de detalles de la prueba A/B, que incluye información sobre el estado, la duración, los placements y otros detalles relacionados con la prueba A/B. <img src="/assets/shared/img/90fa3f5-Area.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Definiciones de métricas \{#metrics-definitions\} Estas son las métricas clave disponibles para las pruebas A/B: <img src="/assets/shared/img/30c7b68-Area.gif" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Revenue \{#revenue\} Revenue representa el importe total en USD generado por compras y renovaciones resultantes de la prueba A/B. Incluye la compra inicial y las renovaciones de suscripción posteriores. La métrica de ingresos se calcula antes de deducir la comisión de App Store o Play Store. Consulta más información sobre las métricas de ingresos de [paywall](paywall-metrics#revenue) y [onboarding](onboarding-metrics#revenue). ### CR to purchases \{#cr-to-purchases\} La tasa de conversión a compras mide la efectividad de tu prueba A/B para convertir vistas en compras reales. Se calcula dividiendo el número de compras por el número de vistas. Por ejemplo, si tuviste 10 compras y 100 vistas, la tasa de conversión a compras sería del 10%. ### CR trials \{#cr-trials\} La tasa de conversión (CR) a trials es el número de trials iniciados desde la prueba A/B dividido por el número de vistas. La tasa de conversión a trials mide la efectividad de tu prueba A/B para convertir vistas en activaciones de trial. Se calcula dividiendo el número de trials iniciados por el número de vistas. ### Purchases \{#purchases\} La métrica de compras representa el número total de transacciones realizadas dentro del paywall o el onboarding como resultado de la prueba A/B. Incluye los siguientes tipos de compras: - Nuevas compras realizadas. - Conversiones de trials activados. - Cambios de nivel inferior, superior y cruzados de suscripciones. - Restauraciones de suscripciones (por ejemplo, cuando una suscripción expira sin renovación automática y se restaura posteriormente). Ten en cuenta que las renovaciones no están incluidas en la métrica de compras. ### Trials \{#trials\} La métrica de trials indica el número total de trials activados como resultado de la prueba A/B. ### Trials cancelled \{#trials-cancelled\} La métrica de trials cancelados representa el número de trials en los que se ha desactivado la renovación automática. Esto ocurre cuando los usuarios cancelan manualmente su suscripción al trial. ### Refunds \{#refunds\} Los reembolsos de la prueba A/B representan el número de compras y suscripciones reembolsadas específicamente relacionadas con las variaciones probadas. ### Views \{#views\} Las vistas son el número de visualizaciones de los paywalls o los onboardings que componen la prueba A/B. Si el usuario visita dos veces, se contarán como dos visitas. ### Unique views \{#unique-views\} Las vistas únicas son el número de visualizaciones únicas del paywall o del onboarding. Si el usuario lo visita dos veces, se contará como una única vista. ### Probability to be the best \{#probability-to-be-the-best\} La métrica Probability to be the best cuantifica la probabilidad de que una variante específica dentro de una prueba A/B sea la opción de mejor rendimiento entre todos los paywalls u onboardings probados. Proporciona una probabilidad numérica que indica el rendimiento relativo de cada paywall u onboarding. Se expresa como un porcentaje que va del 1% al 100%. ### ARPU (Average revenue per user) \{#arpu-average-revenue-per-user\} Solo para pruebas A/B de onboarding. Mide el promedio de ingresos generados por cada usuario en un período específico. Se calcula dividiendo los ingresos totales por el número de usuarios únicos. ### ARPPU (Average revenue per paying user) \{#arppu-average-revenue-per-paying-user\} ARPPU son las siglas de Average Revenue Per Paying User (ingresos medios por usuario de pago) resultantes de la prueba A/B. Se calcula como los ingresos totales divididos por el número de usuarios de pago únicos. Por ejemplo, si has generado 15.000 $ en ingresos de 1.000 usuarios de pago, el ARPPU sería de 15 $. ### ARPAS (Average revenue per active subscriber) \{#arpas-average-revenue-per-active-subscriber\} ARPAS es una métrica que permite medir el promedio de ingresos generados por suscriptor activo en la ejecución de la prueba A/B. Se calcula dividiendo los ingresos totales por el número de suscriptores que han activado un trial o una suscripción. Por ejemplo, si los ingresos totales son 5.000 $ y tienes 1.000 suscriptores, el ARPAS sería de 5 $. Esta métrica ayuda a evaluar el potencial de monetización promedio por suscriptor. ### Proceeds \{#proceeds\} La métrica de proceeds para la prueba A/B representa el importe real en USD recibido por el propietario de la app por compras y renovaciones, tras deducir la comisión aplicable de App Store o Play Store. Refleja los ingresos netos específicamente asociados a las variaciones probadas en la prueba A/B, contribuyendo directamente a las ganancias de la app. Para más información sobre cómo se calculan los proceeds, puedes consultar la [documentación](analytics-cohorts#revenue-vs-proceeds) de Adapty. ### Unique subscribers \{#unique-subscribers\} La métrica de suscriptores únicos representa el recuento de personas distintas que se han suscrito o activado un trial a través de las variaciones de la prueba A/B. Cada suscriptor se contabiliza una sola vez, independientemente del número de suscripciones o trials que inicie. ### Unique paid subscribers \{#unique-paid-subscribers\} La métrica de suscriptores de pago únicos representa el número de personas únicas que han completado con éxito una compra y se han convertido en suscriptores de pago a través de las variaciones de la prueba A/B. ### Refund rate \{#refund-rate\} La tasa de reembolso para la prueba A/B se calcula dividiendo el número de reembolsos específicamente asociados a las variaciones de la prueba por el número de primeras compras (las renovaciones quedan excluidas). Por ejemplo, si hay 5 reembolsos y 1.000 primeras compras, la tasa de reembolso sería del 0,5%. ### Unique CR purchases \{#unique-cr-purchases\} La tasa de conversión única a compras para la prueba A/B se calcula dividiendo el número de compras específicamente asociadas a las variaciones de la prueba por el número de vistas únicas. Por ejemplo, si hay 10 compras y 100 vistas únicas, la tasa de conversión única a compras sería del 10%. ### Unique CR trials \{#unique-cr-trials\} La tasa de conversión única a trials para la prueba A/B se calcula dividiendo el número de trials iniciados específicamente asociados a las variaciones de la prueba por el número de vistas únicas. Por ejemplo, si hay 30 trials iniciados y 100 vistas únicas, la tasa de conversión única a trials sería del 30%. ### Completions y completions únicos \{#completions--unique-completions\} Solo para pruebas A/B de onboarding. Los completions cuentan el número de veces que los usuarios completan tu onboarding a través de las variaciones de la prueba A/B, es decir, que pasan de la primera a la última pantalla. Si alguien lo completa dos veces, eso cuenta como dos **completions** pero un único **unique completion**. ### Unique completions rate \{#unique-completions-rate\} Solo para pruebas A/B de onboarding. El número de completions únicos dividido por el número de vistas únicas. Esta métrica te ayuda a entender cómo interactúan los usuarios con el onboarding a través de las variaciones de la prueba A/B y a realizar cambios si detectas que los usuarios lo ignoran. --- # File: maths-behind-it --- --- title: "Matemáticas detrás de las pruebas A/B" description: "Comprende las matemáticas detrás del análisis de suscripciones para obtener mejores insights sobre ingresos." --- Las pruebas A/B son una técnica muy eficaz para comparar el rendimiento de dos versiones diferentes de un paywall u onboarding. El objetivo final es determinar qué versión resulta más efectiva en función de los ingresos medios por usuario a lo largo de 12 meses. Sin embargo, esperar un año completo para recopilar datos y tomar decisiones no es viable. Por eso se utiliza el ingreso por usuario en 2 semanas como métrica proxy, elegida a partir del análisis de datos históricos para aproximarse a la métrica objetivo. Para obtener resultados precisos y fiables, es fundamental emplear un método estadístico robusto capaz de manejar distintos tipos de datos. La estadística bayesiana, un enfoque muy extendido en el análisis de datos moderno, ofrece un marco flexible e intuitivo para las pruebas A/B. Al incorporar conocimiento previo y actualizarlo con nuevos datos, los métodos bayesianos permiten tomar mejores decisiones en condiciones de incertidumbre. Este documento es una guía completa del análisis matemático que utiliza Adapty para evaluar los resultados de las pruebas A/B y aportar insights valiosos para la toma de decisiones basada en datos. ## Enfoque de Adapty para el análisis estadístico \{#adaptys-approach-to-statistical-analysis\} Adapty aplica un enfoque exhaustivo de análisis estadístico para evaluar el rendimiento de las pruebas A/B y ofrecer resultados precisos y fiables. Nuestra metodología consta de los siguientes pasos clave: 1. **Definición de la métrica:** Para llevar a cabo una prueba A/B con éxito, hay que identificar y definir la métrica clave que se alinee con los objetivos específicos del análisis. Adapty aprovechó una gran cantidad de datos históricos de aplicaciones de suscripción para determinar cuál encaja mejor como métrica proxy para el objetivo a largo plazo de ingresos medios al cabo de 1 año, y esa métrica es el ARPU a los 14 días. 2. **Formulación de hipótesis:** Creamos dos hipótesis para la prueba A/B. La hipótesis nula (H0) asume que no existe una diferencia significativa entre el grupo de control (A) y el grupo de prueba (B). La hipótesis alternativa (H1) sugiere que sí existe una diferencia significativa entre los dos o más grupos. 3. **Selección de distribución:** Elegimos la familia de distribución más adecuada en función de las características de los datos y la métrica observada. La opción más habitual es la distribución log-normal (teniendo en cuenta los valores cero). 4. **Cálculo de la probabilidad de ser el mejor:** Utilizando el enfoque bayesiano para las pruebas A/B, calculamos la probabilidad de que cada variante de paywall u onboarding que participa en el test sea la mejor opción. Este valor está relacionado con los p-valores que usábamos antes, pero es esencialmente un enfoque diferente, más robusto y más fácil de interpretar. 5. **Interpretación de resultados:** La probabilidad de ser el mejor es exactamente lo que sugiere su nombre. Cuanto mayor sea la probabilidad, mayor es la verosimilitud de que una opción concreta sea la mejor para el objetivo planteado. Tú mismo debes establecer el umbral para la toma de decisiones; dependerá de muchos otros factores propios de tu situación, pero un valor de probabilidad habitual es el 95%. 6. **Intervalos de predicción:** Adapty calcula intervalos de predicción para las métricas de rendimiento de cada grupo, ofreciendo un rango de valores dentro del cual es probable que se encuentre el parámetro real de la población. Esto ayuda a cuantificar la incertidumbre asociada a las métricas de rendimiento estimadas. ## Determinación del tamaño de la muestra \{#sample-size-determination\} Determinar un tamaño de muestra adecuado es fundamental para obtener resultados de pruebas A/B fiables y concluyentes. Adapty tiene en cuenta factores como la potencia estadística y el tamaño del efecto esperado, que siguen siendo importantes incluso bajo el enfoque bayesiano, para garantizar un tamaño de muestra suficiente. Los métodos para estimar el tamaño de muestra requerido, específicos del enfoque bayesiano que empleamos actualmente, garantizan la fiabilidad del análisis. Para saber más sobre el funcionamiento de las pruebas A/B, te recomendamos consultar nuestra documentación sobre [cómo crear](ab-tests) y [ejecutar pruebas A/B](run_stop_ab_tests), así como comprender las distintas [métricas y resultados de las pruebas A/B](results-and-metrics). El marco analítico de Adapty para las pruebas A/B emplea ahora un enfoque bayesiano, aunque el foco sigue estando en la definición de métricas, la formulación de hipótesis y la selección de distribuciones. Sin embargo, en lugar de calcular p-valores, ahora computamos las distribuciones posteriores y calculamos la probabilidad de que cada variante sea la mejor. También determinamos los intervalos de predicción. Este enfoque revisado, que sigue siendo exhaustivo e incluso más robusto, está diseñado para ofrecer insights más intuitivos y fáciles de interpretar. El objetivo sigue siendo capacitar a las empresas para que optimicen sus estrategias, mejoren su rendimiento e impulsen el crecimiento a partir de un análisis estadístico sólido de sus pruebas A/B. --- # File: autopilot-how-it-works --- --- title: "Growth Autopilot: Cómo funciona" description: "Entiende la lógica detrás del Growth Autopilot y confía en nosotros para hacer crecer tus ingresos." --- [Growth Autopilot](autopilot) te ayuda a descubrir qué experimentos ejecutar basándose en tus datos de rendimiento reales y en cómo están funcionando apps similares en tu mercado. En lugar de adivinar qué podría funcionar, recibes recomendaciones concretas para pruebas con mayor probabilidad de mejorar tus resultados. Este artículo ofrece una visión transparente de cómo funciona Autopilot: qué datos utiliza, cómo evalúa las oportunidades y por qué aparecen ciertas recomendaciones. El objetivo es que puedas usarlo con confianza como parte de tu flujo de trabajo de crecimiento. ## Qué hace realmente Autopilot \{#what-autopilot-actually-does\} Autopilot analiza las métricas de tu app y tus paywalls para identificar los experimentos con mayor probabilidad de aumentar tus ingresos. Para ello, tiene en cuenta: - **Tu configuración actual**: precios, pruebas, productos y cómo convierten - **Patrones del mercado**: cómo estructuran sus ofertas apps similares y qué cobran - **Tu historial de pruebas**: qué experimentos ya has realizado y qué revelaron - **Potencial de crecimiento**: qué cambios tienen más probabilidades de marcar la diferencia Autopilot utiliza IA para evaluar todos estos factores conjuntamente y convertirlos en pruebas A/B que puedes lanzar de inmediato. Obtienes un plan listo para usar sin necesidad de investigar a la competencia ni adivinar qué probar a continuación. ## Los datos detrás de Autopilot \{#the-data-behind-autopilot\} Cada recomendación se construye a partir de tres fuentes de datos principales que trabajan juntas. #### Los datos propios de tu app \{#your-apps-own-data\} Autopilot analiza el rendimiento actual de tu app: - Métricas de conversión en tus paywalls - Estructura de precios y productos Esto le da a Autopilot una base de partida antes de sugerir cualquier cambio. :::note No usamos los datos de rendimiento de tu app para entrenar recomendaciones de otras apps. Tus datos son privados. ::: #### Análisis de paywall \{#paywall-analysis\} Autopilot analiza la captura de pantalla de tu paywall y compara su diseño con los patrones establecidos por las apps de mayor rendimiento en tu categoría. Evalúa las decisiones de maquetación, los textos, el desglose de suscripciones y los elementos orientados a la conversión, como las insignias de ahorro o las secciones de reseñas. Este análisis genera dos tipos de recomendaciones: - **Recomendaciones basadas en benchmarks** fundamentadas en lo que hacen de manera diferente las apps con mejor rendimiento — cada una respaldada por una estadística concreta (por ejemplo, "Utilizada por el 72% de las apps de Educación con mejor rendimiento"). - **Recomendaciones de análisis visual** generadas por IA a partir de tu captura de pantalla, que incluyen mejoras de copy, cambios de diseño y otros ajustes visuales. Estas recomendaciones se integran directamente en tu [plan de crecimiento](autopilot-growth-plan#view-the-growth-plan) como rondas de pruebas A/B. #### Datos de la competencia \{#competitor-data\} Autopilot compara tu configuración con aplicaciones similares en tu mercado usando información pública como precios, estructuras de suscripción y patrones comunes en tu categoría. Estas comparaciones son específicas por país, ya que los precios y estructuras de la competencia varían según el mercado. Los precios de la competencia proceden de fuentes externas y públicas como el App Store, lo que los diferencia de los datos anonimizados de la red de Adapty que utiliza el análisis de métricas. De este modo, estás probando estrategias que ya funcionan en apps similares a la tuya, no simples ideas al azar. Al ver el análisis, puedes comparar tus benchmarks y los precios de la competencia en paralelo. Si apps similares obtienen mejores resultados con una estructura o un precio diferente, es una buena señal de que ese mismo enfoque podría funcionarte a ti también. :::tip Autopilot selecciona competidores relevantes automáticamente en función de con quién puedes competir de forma realista. En general, te recomendamos mantener esas sugerencias en lugar de añadir apps que estén muy por delante o muy por detrás. Si tu app pertenece a varias categorías, puede que quieras ajustar la lista para centrarte en el segmento de mercado más relevante. ::: #### Puntos de referencia del sector \{#industry-benchmarks\} Autopilot utiliza datos anonimizados de 20 000 apps de suscripción rastreadas por Adapty para mostrarte cómo te comparas con la media de tu categoría en un país concreto. Los datos se agregan a nivel de red y nunca se vinculan a una app específica. Por ejemplo, tu embudo de conversión puede compararse con la media de apps de tu categoría y país. Así puedes ver si estás por debajo de la media, en la media o ya por delante. #### Datos de mercado geográfico \{#geographic-market-data\} Autopilot analiza mercados geográficos individuales —basándose en patrones de la red de 20.000 aplicaciones de Adapty— para identificar dónde los ajustes de precios regionales podrían generar más ingresos. Para cada país, evalúa: - **Tasa de conversión**: cómo se compara la tasa de instalación a pago con la media global. Una tasa más alta puede indicar margen para subir precios; una más baja puede señalar sensibilidad al precio. - **Índice de precios**: la posición del país en el [Índice de Precios de Adapty](https://uploads.adapty.io/adapty_pricing_index.pdf), que refleja el poder adquisitivo de sus habitantes. Puedes actuar sobre estas recomendaciones creando pruebas A/B a partir de las [sugerencias de precios por región](autopilot-growth-plan#geo-pricing-hypotheses) de tu plan de crecimiento. ## Cómo decide Autopilot qué recomendar \{#how-autopilot-decides-what-to-recommend\} Autopilot genera un conjunto de sugerencias para mejorar la conversión de tu paywall. Estas sugerencias están diseñadas para probarse una a una y así medir de forma fiable el impacto de cada cambio. Así es como Autopilot elabora sus sugerencias: 1. **Encontrar las mayores oportunidades** Autopilot revisa tus precios, productos y el rendimiento del embudo de conversión, y los compara con patrones del sector y aplicaciones similares. El análisis se ejecuta en la moneda de tu mercado principal —no solo en USD—, por lo que las recomendaciones de precio se ajustan a lo que realmente pagan tus suscriptores. Busca dónde tienes más margen de mejora, ya sea ajustando el precio, añadiendo un período de prueba o cambiando la estructura de tu oferta. 2. **Selecciona el siguiente experimento** Cada hipótesis se genera a partir del historial de pruebas existente. Autopilot sabe qué experimentos ya has ejecutado, cuáles ganaron y qué direcciones aún vale la pena explorar. La siguiente sugerencia se basa en lo que reveló la anterior, en lugar de seguir una secuencia fija. 3. **Rondas de ganador contra retador** Tras cada experimento, el ganador se convierte en tu nueva línea base. Ese resultado da forma a la siguiente recomendación en tu plan de crecimiento: Autopilot conserva lo que funcionó, descarta lo que no, y elige el siguiente test a partir de ahí. 4. **Mantenlo práctico** Autopilot solo sugiere pruebas que puedes lanzar con tus productos y configuración actuales, o con pequeños cambios como crear un nuevo producto o ajustar un precio. El objetivo es que las pruebas sean rápidas y manejables. 5. **Mostrarte el razonamiento** Para cada recomendación, Autopilot proporciona una hipótesis clara que explica exactamente por qué vale la pena ejecutar esta prueba. Verás cómo se comparan tus métricas actuales con las de la competencia y los promedios del sector, cuál es la oportunidad y qué métricas clave esperamos mejorar. Esto convierte la experimentación en un proceso repetible donde cada prueba te enseña algo y te acerca a un paywall más efectivo. ## Qué ocurre después de cada experimento \{#what-happens-after-each-experiment\} Las recomendaciones no se agotan. Cada prueba completada se convierte en la base para nuevos experimentos. Mientras sigas probando, Autopilot seguirá sugiriendo qué probar a continuación. Para actualizar los datos de mercado subyacentes, también puedes volver a ejecutar el análisis completo. Esto incorpora los últimos datos de precios de competidores, referencias de conversión y tendencias de categoría. Una vez que hayas optimizado tu línea base, puede que también quieras competir con rivales más avanzados. Este enfoque iterativo te ayuda a seguir maximizando tus ingresos a medida que tu app crece y el mercado evoluciona. :::tip ¿Listo para probarlo? Accede a [Growth Autopilot](autopilot-analysis) para analizar tus paywalls y generar un plan de crecimiento con pruebas A/B. Usa el asistente integrado para lanzar pruebas complejas sin complicaciones: te guiará paso a paso en la creación de productos, la duplicación de paywalls y la configuración de segmentos. ::: --- # File: autopilot-analysis --- --- title: "Análisis de Paywall y Mercado" description: "Genera un plan de crecimiento basado en datos adaptado a tu app." --- Sigue los pasos de este artículo para ejecutar el análisis de Growth Autopilot y generar un plan de crecimiento. Si ya generaste un plan de crecimiento, haz clic en la pestaña [Resultados del análisis](#next-steps) para ver los informes. ## Análisis del paywall \{#paywall-analysis\} ### Selecciona un paywall para análisis \{#select-a-paywall-for-analysis\} 1. Abre la página **Growth Autopilot** y haz clic en el botón [Get Growth plan](https://app.adapty.io/ab-tests/analysis/start). 2. En la página **Paywall Diagnostic**, selecciona un **Placement** y un **Paywall** en los desplegables. Adapty preselecciona el placement con más ingresos y su paywall principal. Para analizar otro paywall, cambia primero el placement. 3. Sube una captura de pantalla. Growth Autopilot necesita una captura de pantalla para analizar el diseño y el contenido de tu paywall. 4. Revisa los productos activos del paywall. Las tarjetas de producto a la derecha muestran la duración de la suscripción, el precio y el período de prueba de cada producto. 5. Haz clic en **Confirm & Analyze** para continuar. Adapty analiza tu paywall y muestra el informe de diagnóstico. ### Informe de análisis de paywall \{#paywall-analysis-report\} Cuando seleccionas un paywall y subes una captura de pantalla, Adapty analiza tu paywall en busca de patrones de diseño consolidados, destacando tanto los aciertos como las oportunidades de mejora. #### Qué está funcionando bien \{#whats-working-well\} Esta sección destaca el uso de patrones establecidos que maximizan la conversión. Por ejemplo: un badge de ahorro visible, una sección de reseñas de usuarios destacada o desgloses de suscripción claros. #### Qué corregir en tu paywall \{#what-to-fix-on-your-paywall\} Adapty agrupa sus recomendaciones en dos categorías: - **Recomendaciones basadas en benchmarks**: Sugerencias respaldadas por datos de las apps con mejor rendimiento en tu categoría. Cada recomendación incluye una estadística de referencia (por ejemplo, "Usada por el 72% de las apps de Educación con mejor rendimiento") y una descripción del cambio propuesto. - **Recomendaciones de análisis visual**: Sugerencias generadas por IA a partir de la captura de pantalla de tu paywall. Incluyen: mejoras en el copy, cambios de diseño y más. :::tip Tu [plan de crecimiento](autopilot-growth-plan#view-the-growth-plan) incluirá rondas de pruebas A/B basadas en las recomendaciones de referencia. Puedes añadir sugerencias de análisis visual al plan manualmente. ::: Haz clic en **Get Market Insights** para continuar. ## Análisis de mercado y competencia \{#market-and-competitor-analysis\} :::note El análisis de mercado y competencia requiere completar primero el [análisis de paywall](#paywall-analysis). ::: El análisis de Market Insights compara los precios y métricas de conversión de tu app con los de sus competidores y la media del sector. Las comparaciones son específicas por país. Para ofrecer un punto de referencia, Adapty agrega y analiza datos de apps de la App Store en tu subcategoría y país; información que no está disponible públicamente en ningún otro lugar. ### Seleccionar competidores \{#select-competitors\} Selecciona hasta 5 competidores para la comparación. Adapty elegirá 5 automáticamente y sugerirá 5 más. También puedes añadir apps manualmente con un enlace de App Store. Para obtener mejores resultados, selecciona apps con un MRR mayor que el tuyo. Haz clic en **Generate report** para confirmar la lista y espera a que finalice el análisis. ### Seleccionar un país \{#select-a-country\} Usa el desplegable **Country** para seleccionar uno de tus principales países y obtener un análisis detallado. ### Distribución de ingresos \{#revenue-distribution\} Este gráfico de distribución de ingresos muestra de qué países provienen tus ingresos, con desglose por porcentajes. Destaca tus 5 países principales, que son el foco del resto del análisis. ### Precios de la competencia \{#competitor-pricing\} La tabla de precios de la competencia compara los precios de suscripción de tu paywall con los de tus competidores en el [país seleccionado](#select-a-country). Incluye columnas separadas para cada duración de suscripción. ### Embudo de conversión \{#conversion-funnel\} El gráfico muestra tus tasas de conversión — Vistas a prueba, Prueba a pago y Vistas a pago — junto a los promedios de aplicaciones similares. ### Distribución de ingresos por duración \{#revenue-distribution-by-duration\} Este gráfico muestra qué duraciones de suscripción contribuyen más a tus ingresos, en comparación con la media del sector. Si tus ingresos están muy concentrados en una sola duración, puede ser una señal de oportunidad para optimizar tu estrategia de precios. ### ARPU de Activación El gráfico **Activation ARPU: your app vs. category** compara el ingreso promedio por instalación nueva de tu app con el promedio de la categoría. Úsalo junto con el [embudo de conversión](#conversion-funnel): - La conversión muestra cuántos usuarios pagan. - El ARPU de Activación muestra el ingreso promedio por usuario. Una tasa de conversión alta con un ARPU de Activación bajo puede indicar que las ofertas tienen un precio demasiado bajo. La métrica es **basada en cohortes**. Adapty toma los usuarios que instalaron la app en los últimos 90 días y divide los ingresos que generaron entre su número. #### Comparación con otras métricas \{#comparison-to-other-metrics\} El ARPU de activación no coincidirá con los valores de ARPU que ves en otras partes del dashboard — cada métrica mide algo diferente. - **[El gráfico de análisis de ARPU](arpu)**: incluye renovaciones de cohortes anteriores, por lo que el valor es varias veces mayor que el ARPU de activación. - **[Gráfico de ingresos](revenue), filtro de período establecido en "Activation"**: solo cuenta el primer pago de cada usuario. No cuenta las renovaciones realizadas por la cohorte en la ventana de 90 días. - **[Ingresos por cohorte](analytics-cohorts) (90 días)**: el equivalente más cercano — usa esta métrica como referencia. ## Próximos pasos \{#next-steps\} Lee el artículo [Gestionar y ejecutar tu plan de crecimiento](autopilot-growth-plan) para aprender a ejecutar pruebas A/B basadas en los resultados del análisis. Siempre puedes ver los resultados de tu análisis desde la página del Plan de crecimiento. Solo tienes que hacer clic en la pestaña Analysis Results. --- # File: autopilot-growth-plan --- --- title: "Gestionar y ejecutar tu plan de crecimiento" description: "Ejecuta pruebas A/B, archiva hipótesis y amplía el plan de crecimiento." --- Tras completar [el análisis](autopilot-analysis), Adapty te presenta tu plan de crecimiento: una lista de hipótesis de mejora accionables. Cada elemento del plan de crecimiento sugiere un nuevo precio, una mejora de diseño o un ajuste de precio por país. Abre una hipótesis para probarlo con una prueba A/B. ## Ver el plan de crecimiento \{#view-the-growth-plan\} Cambia entre las distintas pestañas de la pantalla para filtrar las sugerencias por tipo: - Las **hipótesis de precios** exploran nuevos puntos de precio o configuraciones de prueba. Cada ronda se basa en una recomendación concreta del diagnóstico del paywall o del informe de insights de mercado. - Las **hipótesis visuales** son sugerencias de mejora de diseño. Pueden implicar cambios en el texto, el layout u otros elementos visuales. - Las [hipótesis de geo-precios](#geo-pricing-hypotheses) prueban ajustes de precio específicos por país. Puedes [añadir tus propias hipótesis](#add-your-own-hypothesis) de precios y visuales. Las pruebas basadas en estas sugerencias pueden ejecutarse en cualquier orden, pero solo una a la vez. Como las audiencias de las rondas de precios geográficos no se solapan, estas pruebas pueden ejecutarse en paralelo entre sí. ### Hipótesis de precios geográficos \{#geo-pricing-hypotheses\} :::important Las compras únicas no son elegibles para la optimización de precios regionales. ::: Abre la pestaña **Geo-pricing** para ver la lista de recomendaciones de rondas de precios geográficos. Cada recomendación se dirige a un país con un único cambio de precio y se ejecuta como una prueba A/B independiente. Adapty detecta los países que necesitan ajustes de precios y proporciona recomendaciones basadas en datos validadas por el [Adapty Pricing Index](https://uploads.adapty.io/adapty_pricing_index.pdf). #### Cómo genera Adapty las sugerencias de precios geográficos \{#how-adapty-makes-geo-pricing-suggestions\} - Las recomendaciones de precios se basan en datos de App Store. La prueba A/B resultante puede ejecutarse tanto en App Store como en Google Play. - El porcentaje de cambio de precio es el mismo para todas las duraciones de suscripción. - Todos los precios se redondean al nivel de precio de App Store más cercano. - Los precios se muestran en moneda local (por ejemplo, EUR o GBP) si Adapty dispone de datos de transacciones para ese país. Cuando no hay datos locales disponibles, los precios se muestran en USD. ## Modificar el plan \{#modify-the-plan\} ### Obtener nuevas recomendaciones \{#get-new-recommendations\} Puedes actualizar el plan de crecimiento para obtener nuevas recomendaciones automáticas basadas en los datos de mercado más recientes. Haz clic en **Update** y vuelve a iniciar el análisis. Una vez finalizado, Adapty actualizará las recomendaciones del plan de crecimiento. Si las nuevas sugerencias no te convencen, siempre puedes [restaurar](#restore-an-older-version-of-the-growth-plan) una versión anterior. ### Añadir tu propia hipótesis \{#add-your-own-hypothesis\} Autopilot te permite ampliar tu plan de crecimiento con nuevas rondas. Haz clic en el botón "Add Hypothesis" para añadir una nueva hipótesis. Rellena el formulario: añade un título, una descripción y selecciona el tipo de ronda (**Monetization** o **Visual**). - Selecciona las métricas que esperas mejorar en el menú desplegable. - Al añadir una ronda de monetización, debes especificar qué productos involucra la prueba. ## Ejecuta las pruebas \{#run-the-tests\} Puedes ejecutar las pruebas en cualquier orden, pero debes ejecutarlas **de una en una**. Una vez que finalice una prueba, lleva la estrategia ganadora a la siguiente ronda. Cada ronda te acerca a una configuración más eficiente para tu aplicación. Según nuestras estimaciones, ejecutar un conjunto completo de pruebas de Autopilot podría aumentar tus ingresos **hasta un 80%**. :::important Cada sugerencia incluye la duración mínima para la prueba A/B. Sigue estas recomendaciones para obtener los datos más precisos antes de pasar a la siguiente etapa. Tendrás que detener la prueba A/B manualmente. ::: Abre una hipótesis y haz clic en **Set Up & Run Test** para iniciar el asistente de creación de pruebas A/B. ### Paso 1: Ver la hipótesis \{#step-1-view-the-hypothesis\} El primer paso presenta un resumen de la hipótesis. Describe los cambios sugeridos y explica el razonamiento detrás de ellos. Haz clic en "Set up & Run Test" para pasar al siguiente paso. ### Paso 2: Crear nuevos productos \{#step-2-create-new-products\} Si la prueba implica un cambio de precio, el segundo paso te ayuda a crear nuevos productos para la variante de prueba. Las hipótesis visuales omiten este paso. * Haz clic en **Create a new product and push to stores** para crear un nuevo producto desde cero. * Haz clic en **Connect an existing product** si el producto necesario ya existe en la configuración de tu store. ### Paso 3: Configurar el segmento y el paywall \{#step-3-set-up-segment-and-paywall\} El tercer paso te permite configurar la variante de prueba del paywall. Adapty te sugiere duplicar el paywall y aplicar los cambios recomendados. Para las **hipótesis de precios geográficos**, el asistente te pide que selecciones un segmento geolocalizado existente o que crees uno nuevo. Haz clic en **Next** cuando el nuevo paywall esté listo y el segmento configurado. ### Paso 4: Revisar y lanzar \{#step-4-review--launch\} El último paso es un resumen de la prueba que está a punto de ejecutarse. Incluye: - Las métricas clave para **Variant A vs Variant B**: nombre del paywall, selección de productos, duración de la prueba y precio. - La **Duration** (duración), el **Traffic** (distribución) y los **Subscribers** (tamaño mínimo de muestra) de la prueba. - Una sección **How to interpret results** que describe qué señales indican el éxito de la prueba. Revisa la configuración y haz clic en **Launch Test** para iniciar la prueba A/B. ## Restaurar una versión anterior del plan de crecimiento \{#restore-an-older-version-of-the-growth-plan\} Haz clic en el botón **Clock** para ver el historial de versiones y volver a una versión anterior del análisis. ## Eliminar tu plan de crecimiento \{#delete-your-growth-plan\} Para eliminar tu plan de crecimiento, abre el resumen del plan y haz clic en el botón **Trash**. Esta acción es irreversible. :::warning - Las pruebas A/B que ya hayas creado no se eliminarán. - Esta acción elimina todas las versiones del plan de crecimiento. No podrás restaurar una versión anterior del análisis después de esto. ::: Para crear un nuevo análisis, [empieza de nuevo](autopilot-analysis#select-a-paywall-for-analysis). --- # File: how-adapty-analytics-works --- --- title: "Cómo funciona Adapty Analytics" description: "Aprende cómo funciona Adapty Analytics para rastrear el rendimiento de suscripciones de forma eficiente." --- Este artículo describe cómo funciona Adapty Analytics: qué datos muestra, de dónde provienen y cómo se procesan. También explica las decisiones de diseño que hacen que Adapty Analytics sea diferente y cómo estas decisiones te benefician. ## Adapty Analytics vs. analíticas del store \{#adapty-analytics-vs-store-analytics\} - **Variedad de datos**: Los stores solo pueden mostrar sus propios datos y no pueden acceder al comportamiento del usuario dentro de la app. Adapty puede combinar datos de múltiples stores, así como fuentes adicionales: plataformas de marketing y redes publicitarias. El SDK de Adapty registra las interacciones de los usuarios con los paywalls y onboardings. - **Frecuencia de actualización**: Los stores de apps suelen actualizar sus datos una vez al día, lo que puede limitar tu capacidad de tomar decisiones en tiempo real. Adapty ofrece analíticas [casi en tiempo real](#data-processing). - **Métricas avanzadas**: Los stores de apps muestran métricas básicas como descargas, ingresos y tasas de retención. Adapty también calcula métricas avanzadas, como los ingresos recurrentes o el ingreso medio por usuario. Secciones dedicadas analizan problemas de suscripciones: abandono de usuarios, fallos de facturación, etc. Consulta el artículo [Tabla de comparación de métricas](metric-comparison-table) para ver la lista completa. - **Predicciones**: Adapty usa algoritmos avanzados de machine learning para [predecir el LTV y los ingresos futuros](predicted-ltv-and-revenue). ## Datos y sus fuentes \{#data-and-its-sources\} Adapty Analytics procesa los siguientes datos en [gráficos y tablas](analytics): - [Eventos de suscripción](events) generados a lo largo del ciclo de vida del usuario: inicios de prueba, compras, renovaciones, cancelaciones, fallos de facturación y reembolsos. Adapty los agrega en los [gráficos de analíticas](analytics) y los reenvía en tiempo real a [webhooks](webhook), el [feed de eventos](event-feed) e [integraciones basadas en eventos](analytics-integration). - [Datos de transacciones](revenue): ingresos, reembolsos, país del comprador, etc. - **Datos de la aplicación**, como el número de instalaciones o las [interacciones con paywalls](paywalls). - [Datos de atribución para transacciones](attribution-integration): fuentes de tráfico y campañas publicitarias. Estos datos provienen de las siguientes fuentes: - El <InlineTooltip tooltip="SDK de Adapty">[iOS](ios-sdk-overview), [Android](android-sdk-overview), [React Native](react-native-sdk-overview), [Flutter](flutter-sdk-overview), [Unity](unity-sdk-overview), [Kotlin Multiplatform](kmp-sdk-overview), [Capacitor](capacitor-sdk-overview) </InlineTooltip> reporta datos de comportamiento del usuario desde dentro de la app. Si Adapty gestiona tu flujo de compras, el SDK comparte información de primera mano sobre los eventos de compra. Si usas el [modo observador](observer-vs-full-mode), el SDK recibe [informes de eventos](report-transactions-observer-mode) que configuras manualmente. - Los stores utilizan comunicación servidor a servidor para notificar a Adapty sobre las transacciones (pruebas, renovaciones de suscripciones, cancelaciones, etc.). - Los [servicios de atribución](attribution-integration) de terceros (Appsflyer, Adjust, Branch, etc.) comparten datos sobre fuentes de tráfico y campañas publicitarias. Si configuras [Adapty User Acquisition](adapty-user-acquisition), Adapty puede gestionar los datos de tus campañas publicitarias por su cuenta, omitiendo este paso. - Los usuarios pueden [importar manualmente datos históricos de transacciones](importing-historical-data-to-adapty) para que Adapty los analice y muestre. Un problema en alguna de las fuentes puede afectar la calidad general de tus datos de analíticas. Consulta la sección [Solución de problemas](#troubleshooting) para más información. ## Integraciones de terceros \{#third-party-integrations\} Puedes habilitar [Adapty User Acquisition](adapty-user-acquisition) para ampliar las capacidades analíticas de Adapty con datos de campañas publicitarias. Esto te ayudará a descubrir correlaciones entre el gasto en campañas publicitarias y el comportamiento de los usuarios. Del mismo modo, puedes [exportar](analytics-integration) datos de analíticas a plataformas de terceros o a un [servidor privado](webhook), y analizar los datos de Adapty en otra plataforma. ## Procesamiento de datos \{#data-processing\} Adapty ofrece analíticas casi en tiempo real, lo que permite a los usuarios reaccionar rápidamente ante cambios en las métricas clave. - **Gráficos de analíticas**: los datos aparecen con un **retraso de 15 a 30 minutos** después de que ocurre una transacción. Adapty necesita ese tiempo para validar la transacción, aplicar comisiones e impuestos, y agregar los datos. - **[Feed de eventos](event-feed)**: se actualiza en tiempo real, en cuanto los stores entregan un evento. - **[Webhooks](webhook) e integraciones basadas en eventos** (AppsFlyer, Branch, etc.): Adapty reenvía los eventos en cuanto ocurren; el retraso de 15 a 30 minutos de los gráficos de analíticas no aplica aquí. El servicio receptor puede introducir su propio tiempo de procesamiento. Cada superficie tiene su propio ritmo. El mismo evento puede aparecer en momentos ligeramente distintos en los gráficos, el feed de eventos y tus integraciones. Es normal que haya pequeñas diferencias entre ellos. ## Comisiones e impuestos \{#commissions-and-taxes\} Al consultar gráficos relacionados con ingresos, puedes elegir entre **Ingresos brutos**, **Ingresos tras comisiones** e **Ingresos tras comisiones e impuestos**. ### Comisiones \{#commissions\} Los stores deducen una comisión de cada transacción. Si tu organización está inscrita en un programa de comisión reducida, cambia la configuración de Adapty para modificar los cálculos de la tasa de comisión: * [Programa para pequeñas empresas de App Store](app-store-small-business-program) * [Programa de tarifa de servicio reducida](google-reduced-service-fee) de Google Los stores informan automáticamente si otros factores reducen la comisión de tu transacción: * [Renovaciones para suscripciones de App Store de más de 1 año](https://developer.apple.com/app-store/subscriptions/) — comisión del 15% * Tarifas específicas por país (por ejemplo, [21% para apps de App Store distribuidas en Japón](https://developer.apple.com/support/app-distribution-in-japan/#business-terms)) ### Impuestos \{#taxes\} **Adapty no calcula impuestos.** Apple y Google determinan la tasa impositiva aplicable a cada transacción y se la comunican a Adapty, que muestra el valor tal como lo recibe. La tasa impositiva que aparece para una transacción depende de: - El **país de facturación del comprador** y la tasa impositiva local vigente en ese país. - Las **reglas de gestión de impuestos del store**. En algunas jurisdicciones, el store recauda y remite los impuestos en nombre del desarrollador; en otras, el desarrollador es el responsable. - Para las transacciones de App Store, la **categoría fiscal** asignada a la app o a la compra in-app (libros, noticias, vídeos, etc.): las categorías pueden tributar a distintas tasas según la normativa local. Las tasas impositivas pueden variar significativamente entre apps, e incluso entre transacciones dentro de la misma app, debido a la combinación de países de los compradores, las reglas de gestión de los stores y (en el caso de App Store) la categoría fiscal asignada. Para las reglas oficiales, consulta la documentación oficial de los stores: - [App Store: Información sobre impuestos](https://developer.apple.com/help/app-store-connect/making-payments-to-apple/understanding-taxes/) - [Google Play: Tasas impositivas e IVA](https://support.google.com/googleplay/android-developer/answer/138000) ## Solución de problemas \{#troubleshooting\} :::link Artículo principal: [Discrepancias y solución de problemas](discrepancies-and-troubleshooting) ::: * Una fuente de datos mal configurada o ausente puede afectar negativamente a todo el sistema de analíticas. Si encuentras problemas con los datos, asegúrate de que tus integraciones con los stores y plataformas de terceros estén configuradas y activas. * Si comparas los gráficos de Adapty con otras plataformas de analíticas, puede que notes discrepancias. Esto es un comportamiento esperado que puede deberse a diferencias en el procesamiento de datos. Lee el artículo de la [guía sobre discrepancias](discrepancies-and-troubleshooting) para conocer las causas más habituales. --- # File: metric-comparison-table --- --- title: "Comparar diferentes métricas" description: "Tablas de referencia para las métricas de análisis de Adapty, organizadas por categoría." --- Este es un resumen de las métricas disponibles en Adapty Analytics. Úsalo para entender qué mide cada métrica y en qué se diferencia de las relacionadas. Para una explicación más detallada de cómo Adapty procesa los datos analíticos, consulta [Cómo funciona el análisis de Adapty](how-adapty-analytics-works). :::note Este artículo no cubre las métricas de [Adapty User Acquisition](adapty-user-acquisition). Lee [UA analytics](ua-analytics) para saber más sobre las métricas de campañas publicitarias (Gasto, CPI, ROAS, CTR, entre otras). ::: ## Métricas globales \{#global-metrics\} Las métricas globales hacen seguimiento del rendimiento de toda la app, en todos los placements y paywalls. ### Ingresos \{#revenue\} Estas métricas miden cuánto dinero genera la app y de qué fuentes. | Métrica | Descripción | Diferencia clave | |--------|-------------|----------------| | [Ingresos](revenue) | Ingresos totales de suscripciones y compras únicas, menos reembolsos | Ingresos reales generados. Puede mostrar ingresos brutos, ingresos después de comisión, o ingresos después de impuestos y comisión según los [controles del gráfico](controls-filters-grouping-compare-proceeds) | | [MRR](mrr) | Ingresos recurrentes mensuales de suscripciones activas | Ingresos mensuales predecibles de tu app. Excluye compras únicas y suscripciones no recurrentes. | | [ARR](arr) | Ingresos recurrentes anuales de suscripciones activas | Se calcula como el MRR pero a escala anual. Útil para proyectar ingresos anuales | | [ARPU](arpu) | Ingresos medios por usuario | Divide los ingresos entre el número total de usuarios, tanto de pago como gratuitos. Muestra cuántos ingresos genera cada usuario de media | | [ARPPU](arppu) | Ingresos medios por usuario de pago | Solo cuenta los usuarios que realizaron una compra durante el período seleccionado, incluidas las transacciones reembolsadas. Siempre es mayor que el ARPU | | [LTV (valor del ciclo de vida)](ltv) | Ingresos de clientes de pago divididos entre el número de clientes de pago en una cohorte | Valor realizado por cliente de pago a lo largo del tiempo. A diferencia del ARPPU (un solo período), el LTV muestra los ingresos totales durante toda la relación con el cliente. Se puede ver por renovaciones o por días de calendario | | [LTV predicho](predicted-ltv-and-revenue) | Valor estimado del ciclo de vida por usuario en una cohorte | Estimación prospectiva. A diferencia del LTV realizado, proyecta el valor futuro a partir de los patrones de retención históricos de la cohorte. Disponible para 3, 6, 9, 12, 18 y 24 meses | | [Ingresos predichos](predicted-ltv-and-revenue) | Ingresos totales estimados que generará una cohorte | Estimación prospectiva. A diferencia de los Ingresos realizados, predice el total que generará una cohorte en el período seleccionado. Se actualiza diariamente | | [No suscripciones](non-subscriptions) | Número de compras in-app: consumibles, no consumibles y suscripciones no renovables | Excluye las suscripciones de renovación automática. | | [Eventos de reembolso](refund-events) | Número de compras o suscripciones reembolsadas | Atribuidos a la fecha del reembolso, no a la fecha de compra original. | | [Dinero reembolsado](refund-money) | Importe total reembolsado durante el período seleccionado | Impacto financiero de los reembolsos. Se calcula antes de las comisiones del store. A diferencia de los Eventos de reembolso (un recuento), muestra el importe monetario | ### Suscriptores y conversión \{#subscribers-and-conversion\} Estas métricas hacen seguimiento de cómo los usuarios entran en la app y avanzan por el embudo. | Métrica | Descripción | Diferencia clave | |--------|-------------|----------------| | [Instalaciones](installs) | Número de instalaciones de la app durante el período | Cuenta una de las siguientes opciones según [la definición de instalación](general#4-installs-definition-for-analytics): <br /> • Instalaciones de dispositivo (un usuario que reinstala la app se cuenta de nuevo) <br /> • Usuarios únicos (solo cuenta usuarios que hayan establecido `customer_user_id`. Los usuarios anónimos se excluyen completamente — si no hay usuarios identificados, el recuento es 0) | | [Nuevas pruebas](new-trials) | Pruebas activadas durante el período | Cuenta cada inicio de prueba, incluso si la prueba ya ha expirado o se ha convertido a pago en el momento de ver el gráfico | | [Pruebas activas](active-trials) | Número de pruebas que aún no han expirado | Solo cuenta las pruebas activas al final del período | | [Nuevas suscripciones](reactivated-subscriptions) | Suscripciones activadas por primera vez durante el período | Excluye renovaciones y reactivaciones | | [Suscripciones activas](active-subscriptions) | Número de suscripciones de pago que aún no han expirado | Excluye pruebas y suscripciones con renovación cancelada | | [Instalación a prueba](analytics-conversion#install---trial) | Porcentaje de instaladores que iniciaron una prueba | El denominador incluye todos los instaladores, no solo los visitantes del paywall, por lo que la tasa puede ser menor que la de Vista de paywall a prueba. Ambas métricas también pueden divergir si la app no registra vistas de paywall. Esto puede ocurrir con un paywall personalizado que no llama a `logShowPaywall`, o cuando un usuario inicia su prueba desde una [compra in-app promocionada](https://developer.apple.com/documentation/storekit/supporting-promoted-in-app-purchases-in-your-app). | | [Vista de paywall a prueba](analytics-conversion#paywall-view---trial) | Porcentaje de visitantes del paywall que iniciaron una prueba | Solo cuenta usuarios que vieron un paywall, por lo que la tasa puede ser mayor que Instalación a prueba | | [Prueba a pago](analytics-conversion#trial---paid) | Porcentaje de usuarios en prueba que compraron una suscripción | Mide la calidad de la prueba y la eficiencia de conversión. A diferencia de Instalación a pago, se centra solo en usuarios que completaron una prueba | | [Instalación a pago](analytics-conversion#install---paid) | Porcentaje de instaladores que compraron una primera suscripción | Cuenta todos los instaladores, no solo los visitantes del paywall. La tasa puede ser menor que Vista de paywall a pago. Incluye tanto compras directas como conversiones de prueba a pago | | [Vista de paywall a pago](analytics-conversion#paywall-view---paid) | Porcentaje de visitantes del paywall que finalmente compraron una suscripción | Solo cuenta usuarios que vieron un paywall, por lo que la tasa puede ser mayor que Instalación a pago. Incluye usuarios que completaron primero una prueba | ### Retención y renovación de suscripciones \{#retention-and-subscription-renewal\} Estas métricas hacen seguimiento de qué tan bien la app retiene a los suscriptores de pago a lo largo del tiempo. | Métrica | Descripción | Diferencia clave | |--------|-------------|----------------| | [Retención](analytics-retention) | Porcentaje de suscriptores originales que permanecen tras cada período de facturación: 1.ª renovación, 2.ª renovación, etc. | Hace seguimiento de los suscriptores desde el primer pago. A diferencia de las métricas período a período que aparecen a continuación, siempre compara con el grupo original, lo que permite ver el panorama completo de un vistazo | | [Pago a 2.º período](analytics-conversion#paid---2nd-period) | Porcentaje de suscriptores por primera vez que renovaron para el segundo período | Mide la transición entre dos períodos adyacentes específicos. A diferencia de la Retención, se centra en la renovación más crítica: la primera | | [2.º a 3.er período](analytics-conversion#2nd-period---3rd-period) | Porcentaje de renovación del 2.º al 3.er período | Indica la estabilidad de retención temprana tras la renovación inicial | | [3.er a 4.º período](analytics-conversion#3rd-period---4th-period) | Porcentaje de renovación del 3.er al 4.º período | Indicador de retención a medio plazo | | [4.º a 5.º período](analytics-conversion#4th-period---5th-period) | Porcentaje de renovación del 4.º al 5.º período | Indicador de fidelización a largo plazo | | [6 meses+](analytics-conversion#6-months-) | Porcentaje de suscriptores por primera vez que permanecen suscritos durante más de 6 meses | Mide tiempo de calendario, no número de renovaciones. Un suscriptor anual se considera retenido a los 6 meses aunque no haya renovado | | [1 año+](analytics-conversion#1-year-) | Porcentaje de suscriptores por primera vez que permanecen suscritos durante más de 12 meses | Hito de retención anual | | [2 años+](analytics-conversion#2-years-) | Porcentaje de suscriptores por primera vez que permanecen suscritos durante más de 24 meses | Hito de retención a largo plazo | ### Cancelaciones \{#churn\} Estas métricas miden cuántos suscriptores y usuarios en prueba pierde la app. | Métrica | Descripción | Diferencia clave | |--------|-------------|----------------| | [Renovación de pruebas cancelada](trials-renewal-cancelled) | Pruebas en las que el usuario desactivó la renovación automática | El usuario mantiene el acceso a la prueba hasta que finaliza, pero no se convertirá a pago automáticamente. A diferencia de Renovación de suscripciones cancelada, se aplica a usuarios en prueba que aún no han pagado | | [Pruebas expiradas (canceladas)](expired-churned-trials) | Pruebas que expiraron: el usuario perdió el acceso a las funciones premium | El usuario ya ha perdido el acceso. Se atribuye a la fecha de expiración, aunque el usuario haya cancelado la renovación en un período anterior. Se puede agrupar por motivo (voluntario o por facturación) | | [Renovación de suscripciones cancelada](cancelled-subscriptions) | Suscripciones en las que el usuario desactivó la renovación automática | El usuario sigue teniendo acceso hasta que finalice el período. Indica riesgo de cancelación, no cancelación real: el usuario puede volver a activar la renovación automática antes de que expire el período | | [Suscripciones canceladas (expiradas)](churned-expired-subscriptions) | Suscripciones que expiraron: el usuario perdió el acceso a las funciones premium | Cancelación real. El usuario ya ha perdido el acceso. Se atribuye a la fecha de expiración, aunque el usuario haya cancelado la renovación en un período anterior. Se puede agrupar por motivo (voluntario o por facturación) | ### Problemas de facturación y recuperación de ingresos \{#billing-issues-and-revenue-recovery\} Estas métricas hacen seguimiento de la eficacia con la que la app recupera los ingresos perdidos por problemas de facturación. | Métrica | Descripción | Diferencia clave | |--------|-------------|----------------| | [Período de gracia](grace-period) | Suscripciones que entraron en período de gracia debido a un fallo de facturación | Incluye usuarios que superaron el período de gracia y perdieron el acceso | | [Período de gracia a pago](analytics-conversion#grace-period---paid) | Porcentaje de usuarios en período de gracia que renovaron antes de que este terminara | Una tasa (%). Responde a «¿qué proporción de usuarios en período de gracia se recuperó?» | | [Período de gracia convertido](grace-period-converted) | Número absoluto de suscripciones en período de gracia que se renovaron con éxito | Los mismos eventos que Período de gracia a pago, pero expresados como recuento en lugar de porcentaje | | [Ingresos convertidos del período de gracia](grace-period-converted-revenue) | Ingresos procedentes de recuperaciones durante el período de gracia | Impacto financiero de la función de período de gracia | | [Problema de facturación](billing-issue) | Suscripciones que entraron en estado de problema de facturación | Comienza tras la expiración del período de gracia. A diferencia del Período de gracia, solo cuenta usuarios que ya han perdido el acceso premium | | [Problema de facturación a pago](analytics-conversion#billing-issue---paid) | Porcentaje de usuarios con problema de facturación que renovaron antes de que finalizara el ciclo de facturación | Una tasa (%). Responde a «¿qué proporción de usuarios con problema de facturación se recuperó?» | | [Problema de facturación convertido](billing-issue-converted) | Número absoluto de suscripciones con problema de facturación que se renovaron con éxito | Un recuento de suscripciones con problema de facturación que se renovaron con éxito. Los mismos eventos que Problema de facturación a pago, pero expresados como recuento en lugar de porcentaje | | [Ingresos convertidos de problema de facturación](billing-issue-converted-revenue) | Ingresos procedentes de recuperaciones de problemas de facturación | Impacto financiero de la recuperación por problemas de facturación | ## Métricas de paywall, placement y onboarding \{#paywall-placement-and-onboarding-metrics\} Estas métricas se calculan para [paywalls](paywall-metrics), [placements](placement-metrics) y [onboardings](onboarding-metrics) individuales. Miden el rendimiento de un paywall o placement específico, no de la app en su conjunto. La columna **Métrica global asociada** muestra la métrica correspondiente de la sección de análisis global. | Métrica | Descripción | Diferencia clave | Métrica global asociada | |--------|-------------|----------------|---------------| | [Ingresos netos](paywall-metrics#proceeds) | Ingresos después de impuestos y comisión para un placement individual | Equivalente a [Ingresos](revenue) después de impuestos y comisión | [Ingresos](revenue) | | [ARPPU](paywall-metrics#arppu) | Ingresos medios por usuario de pago para este paywall o placement | El mismo cálculo que el ARPPU global, pero limitado a un paywall o placement concreto | [ARPPU](arppu) | | [ARPAS](paywall-metrics#arpas) | Ingresos divididos entre el número de suscriptores activos (en prueba y de pago) | Cuenta usuarios en prueba. A diferencia del ARPPU, refleja el potencial de ingresos de toda la base de suscriptores | — | | [Vistas](paywall-metrics#views) | Número total de veces que se mostró un paywall o placement | Cuenta cada visualización. Un usuario que ve el mismo paywall dos veces cuenta como 2 vistas | — | | [Vistas únicas](paywall-metrics#unique-views) | Número de usuarios únicos que vieron un paywall o placement | Cada usuario se cuenta una sola vez, independientemente de cuántas veces lo haya visto. A diferencia de las Vistas, mide el alcance en lugar de la frecuencia de interacción | — | | [CR a compras](paywall-metrics#cr-to-purchases) | Compras divididas entre el total de vistas | Usa el total de vistas (incluidas las repetidas del mismo usuario) como denominador | [Vista de paywall a pago](analytics-conversion#paywall-view---paid) | | [CR único a compras](paywall-metrics#unique-conversion-rate-cr-to-purchases) | Compras divididas entre vistas únicas | Usa las vistas únicas como denominador. Tasa mayor que el CR no único porque los visitantes recurrentes se cuentan una sola vez | [Vista de paywall a pago](analytics-conversion#paywall-view---paid) | | [CR a pruebas](paywall-metrics#unique-cr-to-trials) | Pruebas iniciadas divididas entre el total de vistas | Mide con qué eficacia un paywall convierte vistas en pruebas | [Vista de paywall a prueba](analytics-conversion#paywall-view---trial) | | [CR único a pruebas](paywall-metrics#unique-cr-to-trials) | Pruebas iniciadas divididas entre vistas únicas | Se calcula como el CR a pruebas, pero con los visitantes únicos como denominador | [Vista de paywall a prueba](analytics-conversion#paywall-view---trial) | | [Compras](paywall-metrics#purchases) | Número total de transacciones para este paywall: nuevas compras, conversiones de prueba, actualizaciones, degradaciones y suscripciones que regresan | Excluye renovaciones. | [Ingresos](revenue) | | [Pruebas](paywall-metrics#trials) | Total de pruebas activadas a través de este paywall | Limitado a este paywall únicamente | [Nuevas pruebas](new-trials) | | [Pruebas canceladas](paywall-metrics#trials-canceled) | Número de pruebas en las que el usuario desactivó la renovación automática | Limitado solo a las pruebas de este paywall | [Renovación de pruebas cancelada](trials-renewal-cancelled) | | [Tasa de reembolso](paywall-metrics#refund-rate) | Reembolsos divididos entre compras por primera vez (renovaciones excluidas) | Una tasa (%), no un recuento. Normaliza los reembolsos respecto al número de compras | [Eventos de reembolso](refund-events) (recuento, no tasa) | | [Finalizaciones](onboarding-metrics#completions--unique-completions) | Número de veces que los usuarios completaron un flujo de onboarding de principio a fin | Solo para placements y onboardings. Cuenta cada finalización, incluidas las repetidas del mismo usuario | — | | [Finalizaciones únicas](onboarding-metrics#completions--unique-completions) | Número de usuarios únicos que completaron un flujo de onboarding | Solo para placements y onboardings. Cada usuario se cuenta una sola vez. A diferencia de las Finalizaciones, mide cuántas personas individuales terminaron el flujo | — | | [Tasa de finalizaciones únicas](onboarding-metrics#unique-completions-rate) | Finalizaciones únicas divididas entre vistas únicas | Solo para placements y onboardings. Mide la eficacia del onboarding: qué proporción de usuarios que lo iniciaron llegaron hasta el final | — | --- # File: overview --- --- title: "Página de resumen de analíticas" description: "Consulta varios gráficos de analíticas de Adapty en una misma página para tener una visión general del rendimiento de tu app" --- La [página de resumen](https://app.adapty.io/overview) muestra las métricas combinadas de todas tus apps en un solo lugar. Es la página de inicio del dashboard y también está disponible desde el menú lateral izquierdo. Para ver los datos de una sola app, abre un [gráfico](charts) individual. ## Gráficos \{#charts\} El resumen muestra un subconjunto personalizable de los [gráficos de analíticas](charts) de Adapty. Para ver descripciones y una comparación detallada de todos los gráficos disponibles, consulta la [tabla de comparación de métricas](metric-comparison-table). Para personalizar qué gráficos aparecen y en qué orden, haz clic en **Edit** en la esquina superior derecha. Desde ahí puedes eliminar, añadir o reordenar los gráficos: Los siguientes gráficos están disponibles: - [Revenue](revenue) - [MRR](mrr) - [ARR](arr) - [ARPU](arpu) - [ARPPU](arppu) - [ARPAS](placement-metrics#arpas) - [Installs](installs) - [New trials](new-trials) - [New subscriptions](reactivated-subscriptions) - [Active trials](active-trials) - [Active subscriptions](active-subscriptions) - [New non-subscriptions](non-subscriptions) - [Refund events](refund-events) - [Refund money](refund-money) - [Subscriptions renewal canceled](cancelled-subscriptions) - [Conversion rate from Install to Trial, Install to Paid, and Trial to Paid](analytics-conversion) ## Controles \{#controls\} La página de resumen es compatible con la mayoría de los [controles de analíticas](controls-filters-grouping-compare-proceeds), incluidos el filtrado, la agrupación y la comparación de periodos de tiempo. La única función exclusiva del resumen es la agrupación y el filtrado por app. Como la página combina datos de todas tus apps, la vista por app muestra cómo contribuye cada una a tus métricas de negocio: ## Conteo de instalaciones y zona horaria \{#install-count-and-timezone\} El resumen combina datos de todas tus apps con **su propia zona horaria y configuración de conteo de instalaciones** — los valores por app no se aplican aquí. - **Installs**: elige cómo contabilizar las instalaciones. **By device installations** trata cada instalación en un dispositivo —incluidas las reinstalaciones— como independiente. **By unique users** solo cuenta la primera instalación por usuario identificado. Para cambiar la configuración, haz clic en **Edit Metrics** y selecciona una [opción diferente](general#4-installs-definition-for-analytics) en el desplegable. - **Timezone**: para cambiar la zona horaria del resumen, haz clic en **Edit Metrics** y selecciona una zona horaria en el desplegable. Esto es especialmente útil si las distintas apps de tu cuenta usan zonas horarias diferentes. --- # File: controls-filters-grouping-compare-proceeds --- --- title: "Controles de análisis" description: "Filtra, agrupa y compara los datos de análisis de Adapty." --- Adapty ofrece controles para refinar los datos en cada pestaña de análisis: rango de tiempo, comparación de períodos, filtrado, agrupación y visualización de gráficos. La disponibilidad varía según la pestaña. **Controles disponibles por pestaña de análisis:** | Control | Gráficos | Cohortes | Embudos | Retención | Conversión | LTV | | :--- | :---: | :---: | :---: | :---: | :---: | :---: | | Rango de fechas | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Comparación de períodos | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | Filtro | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Grupo | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | | Visualización de gráfico | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | Vista de tabla | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Exportación CSV | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Comisiones e impuestos | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ### Establece el rango de fechas \{#set-the-date-range\} Usa el calendario **Date range** que aparece sobre cada gráfico para elegir el período de tiempo. Los análisis de Adapty utilizan la **zona horaria UTC**; la [página de Overview](overview) tiene su propia zona horaria configurable. #### Rangos predefinidos \{#preset-ranges\} Usa la opción **Custom** para especificar fechas de inicio y fin arbitrarias. Los rangos predefinidos son: | Preset | Starts | Ends | | --- | --- | --- | | Last 7 days | Hace 6 días | Hoy | | Last 28 days | Hace 27 días | Hoy | | Last month | La misma fecha del mes anterior | Hoy | | Last 3 months | Hace 3 meses | Hoy | | Last 6 months | Hace 6 meses | Hoy | | Last year | Hace 1 año | Hoy | | Previous month | Primer día del mes anterior | Último día del mes anterior | | This month | Día 1 del mes actual | Hoy | | This quarter | Día 1 del trimestre actual | Hoy | | This year | 1 de enero del año actual | Hoy | :::tip Usa **Últimos 28 días** para hacer seguimiento de productos con suscripción semanal: el rango cubre cuatro ciclos semanales completos, así que ninguna semana parcial distorsiona la comparación. ::: #### Escala de tiempo \{#time-scale\} Cada punto de datos del gráfico representa un bloque de tiempo: elige entre día, semana, mes, trimestre y año en el menú desplegable. Día y semana muestran subidas y bajadas a corto plazo; mes, trimestre y año reflejan tendencias a más largo plazo. En los análisis de [cohortes](analytics-cohorts) y [LTV](ltv), esta misma configuración se denomina **cohort length** — consulta esos artículos para más detalles. ### Comparar dos períodos de tiempo \{#compare-two-time-periods\} Haz clic en la opción de comparación junto al calendario para superponer el período actual con uno anterior. La primera comparación que ofrece Adapty es el período inmediatamente anterior, con la misma duración. Para cambiar el rango de comparación, vuelve a hacer clic en la opción y elige un rango personalizado. La comparación aparece: - **En el gráfico** — líneas, áreas o columnas superpuestas, con cero o una agrupación seleccionada. - **Como valor numérico** — la diferencia entre los dos períodos, mostrada en verde (mayor) o rojo (menor). - **En un tooltip** — pasa el cursor sobre cualquier punto de datos para ver la diferencia numérica de ese punto. ### Filtrar y agrupar datos \{#filter-and-group-data\} **Filtra** para restringir el gráfico a los datos que coincidan con uno o más atributos (por ejemplo, un único país o producto). **Agrupa** para dividir el total del gráfico en series separadas: una por cada valor de atributo. Por ejemplo, agrupa los ingresos por país para obtener una línea de ingresos independiente para cada país en lugar de un total combinado. **Atributos disponibles para filtrar y agrupar:** | Atributo | Filtro | Grupo | Descripción | | --- | :---: | :---: | --- | | Atribución | ✅ | ✅ | Reportado por, Estado, Canal, Campaña, Grupo de anuncios, Conjunto de anuncios y Creatividad (Keyword). Requiere [integración de atribución](attribution-integration). | | Audiencia | ✅ | ✅ | La [audiencia](audience) a la que pertenece el usuario. | | Estado de renovación | ❌ | ✅ | Si la suscripción se renovará en el próximo período. | | Período | ✅ | ✅ | Etapa del ciclo de vida de la suscripción: **Trial**, **Activation** (primer pago) o **Renewal 1**–**Renewal 5**, **Renewals 6+** (renovaciones posteriores). | | País | ✅ | ✅ | El país de la store del usuario. Si no está disponible, Adapty lo infiere a partir del código de moneda o la IP del dispositivo. | | Tipo de oferta | ✅ | ✅ | La oferta aplicada a la transacción: <ul><li>**Introductory** — una oferta introductoria de pago en el período inicial de la suscripción (Ofertas Introductorias de App Store y ofertas de pago de Play Store; **no** incluye pruebas gratuitas).</li><li>**Promotional** — Ofertas Promocionales de App Store y equivalentes.</li><li>**Offer Code** — códigos promocionales que el cliente introduce en la store.</li><li>**No offer** — sin oferta aplicada.</li></ul> | | ID de oferta | ✅ | ✅ | Un ID de oferta específico. | | Tipo de descuento de oferta | ✅ | ✅ | Free Trial, Pay As You Go o Pay Up Front. | | Paywall | ✅ | ✅ | El [paywall](paywalls) utilizado para la compra. | | Pruebas A/B | ✅ | ❌ | La [prueba A/B](ab-tests) activa durante la compra. | | Placement | ✅ | ✅ | El [placement](placements) donde se realizó la compra. | | Store | ✅ | ✅ | La store que procesó la transacción: App Store, Google Play, Stripe, etc. | | Producto | ✅ | ✅ | El [producto](product) — suscripciones y compras únicas. | | Duración | ✅ | ✅ | La duración del producto. | | Segmento | ✅ | ✅ | Un [segmento](segments) de usuarios. Agrupa por segmento para comparar el rendimiento del segmento con **All users**. <ul><li>Los embudos no admiten agrupación por segmento.</li><li>Si cambias un atributo personalizado después de que un segmento lo use, Adapty puede excluir al usuario del segmento en los análisis. Los datos siguen mostrando el valor anterior.</li></ul> | | Motivo de reembolso | ✅ | ✅ | El motivo por el que se reembolsó una transacción (por ejemplo, **Refund** o **Upgraded**). Disponible en los gráficos de reembolsos y resolución de problemas de facturación. | | Motivo de expiración | ❌ | ✅ | El motivo por el que expiró una suscripción o prueba: **Cancelled by customer**, **Billing issue**, **Customer hasn't agreed to price increase**, **Unknown** o **Refund**. Disponible en suscripciones Expired (Churned) y trials Expired (Churned). | | Cohorte (solo LTV) | ❌ | ✅ | En el gráfico de LTV, agrupa por duración de cohorte: **Day**, **Week**, **Month** o **Year**. Reemplaza Agrupar por atribución en este gráfico. | No todas las vistas de análisis admiten todos los filtros o atributos de agrupación mencionados. ARPU e Installs en la pestaña Charts están limitados a Attribution, Country, Segment, Store y (solo como filtro) pruebas A/B. Las pestañas LTV, Cohorts, Funnels, Retention y Conversion admiten cada una un subconjunto diferente. Para ver la compatibilidad exacta, consulta el artículo correspondiente a ese gráfico o pestaña. ### Cambiar la visualización del gráfico \{#change-the-chart-visualization\} Elige cómo mostrar el gráfico desde el menú desplegable de visualización: - **Columna apilada** — cada columna muestra el total, dividido en segmentos de color por grupo. - **Área apilada** — igual que la columna apilada, pero con áreas rellenas que conectan los puntos de datos. - **Línea** — una línea por grupo, sin relleno. - **Columna apilada al 100%** — cada columna alcanza la altura total del gráfico; los segmentos muestran la proporción relativa (porcentaje) de cada grupo en lugar de los valores reales. Útil para visualizar proporciones a lo largo del tiempo. - **Área apilada al 100%** — igual que la columna apilada al 100%, pero con áreas rellenas en lugar de columnas. ### Ver los datos como tabla \{#view-data-as-a-table\} Debajo de cada gráfico hay una tabla con los mismos datos, con fechas como columnas. La fila y la columna Total muestran agregados que no son visibles en el propio gráfico. ### Exportar datos a CSV \{#export-data-to-csv\} Haz clic en el botón **Export** para descargar los datos del gráfico como archivo CSV. :::tip Para acceso programático o programado, usa la [API de exportación](export-analytics-api) — devuelve los mismos datos que la descarga CSV. ::: ### Mostrar ingresos brutos o netos \{#display-gross-or-net-revenue\} Para los gráficos relacionados con ingresos ([Revenue](revenue), [MRR](mrr), [ARR](arr), [ARPU](arpu), [ARPPU](arppu)), Adapty ofrece un desplegable con tres modos de visualización: - **Gross revenue** — ingresos totales antes de cualquier deducción. - **Proceeds after store commission** — ingresos menos la comisión del store, con impuestos incluidos. - **Proceeds after store commission and taxes** — ingresos menos comisión e impuestos. Para más información sobre el cálculo de comisiones e impuestos, consulta [Comisiones e impuestos](how-adapty-analytics-works#commissions-and-taxes) en *Cómo funciona Adapty Analytics*. --- # File: revenue --- --- title: "Ingresos" description: "Realiza un seguimiento y analiza los ingresos de tu app con los datos de suscripción de Adapty." --- El gráfico de Ingresos muestra el total de ingresos obtenidos tanto de suscripciones como de compras únicas, menos los ingresos que se reembolsaron posteriormente. Es la métrica principal para monitorear el rendimiento financiero de tu app. Cambia a una resolución mensual para evaluar las tendencias generales de los últimos 12 meses. Agrupa el gráfico por producto, segmento de usuarios o fuente de atribución para ver de dónde provienen los ingresos, y presta atención a la proporción entre nuevos y renovaciones para entender qué parte del negocio impulsa el crecimiento. ## Cálculo \{#calculation\} :::warning La calculadora a continuación **no tiene en cuenta** la [comisión del store e impuestos](how-adapty-analytics-works#commissions-and-taxes). Compara el resultado con tus cálculos de **ingresos brutos**. ::: Los ingresos son el total de todas las transacciones pagadas en el período (nuevas suscripciones, renovaciones, conversiones de prueba, compras únicas) menos los reembolsos procesados en el período: **Ingresos = transacciones totales − reembolsos**. - **Fecha de transacción**: el importe completo de cada transacción se registra el día de la compra, no se distribuye a lo largo de la duración de la suscripción. - **Fecha de reembolso**: los reembolsos reducen los ingresos el día en que se efectúa el reembolso, no el día de la compra original. El gráfico muestra los ingresos brutos por defecto. Usa los [controles del gráfico](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue) para cambiar entre la vista bruta, post-comisión o post-comisión-e-impuestos. <CompoundCalculator client:load heading="Ingresos" formuLatex="\sum P_i \times Q_i - D" variables={[ { nameInTheFormula: "P", variableName: "price", variableDescription: "Precio por unidad", variableValue: 10 }, { nameInTheFormula: "Q", variableName: "qty", variableDescription: "Cantidad", variableValue: 1, isInteger: true }, { nameInTheFormula: "D", variableName: "refunds", variableDescription: "Importe reembolsado", variableValue: 35, global: true } ]} rowFormula="price * qty" resultFormula="_sum - refunds" defaultRows={[ { price: 10, qty: 5 }, { price: 50, qty: 10 }, { price: 100, qty: 1 } ]} /> ## Divisa \{#currency\} --- no_index: true --- Adapty muestra todos los gráficos monetarios en **dólares estadounidenses**, independientemente de la moneda original de la transacción. Esto incluye Revenue, MRR, ARR, ARPU, ARPPU, LTV, ingresos previstos, reembolsos y las cifras de ingresos dentro de los informes de cohortes y pruebas A/B. No hay ninguna opción para mostrarlos en otra moneda. Adapty convierte cada transacción a USD usando un tipo de cambio de [currencylayer.com](https://currencylayer.com/) que se actualiza cada 8 horas, **fijado en el momento de la transacción**. Los valores históricos en USD no se recalculan cuando varía el tipo de cambio. Los valores en moneda local están disponibles por transacción en: - Los campos `price` y `currency_code` en los webhooks - Las columnas `price` y `currency_code` en las exportaciones de S3, GCS y BigQuery - La página de perfil (vista por transacción) Para los informes financieros en moneda local, extrae los valores en moneda local por transacción desde una exportación y agrégatelos tú mismo. ## Precios de renovación \{#renewal-pricing\} --- no_index: true --- Adapty calcula los ingresos por renovación al precio actual del producto, incluso para los usuarios que tenían un precio anterior cuando se suscribieron por primera vez. Después de cambiar un precio en App Store Connect o Google Play, las cifras de Revenue, MRR y ARR del dashboard para los suscriptores existentes pueden diferir de los ingresos reales recaudados: Adapty aplica el nuevo precio, aunque el store haya mantenido a esos usuarios en el precio anterior. Para verificarlo, compara el campo `price` por transacción en la exportación de S3, GCS o BigQuery con el dashboard para las mismas transacciones. El campo de exportación refleja lo que el store reportó (el precio que el cliente pagó realmente); el dashboard refleja el precio actual del producto. ## Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Estado de renovación, período, producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación detallada de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#revenue). - [MRR](mrr) - [ARR](arr) - [ARPU](arpu) - [ARPPU](arppu) --- # File: mrr --- --- title: "MRR" description: "Entiende y optimiza el Monthly Recurring Revenue (MRR) en Adapty." --- El gráfico de Monthly Recurring Revenue (MRR) muestra los ingresos de tus suscripciones de pago activas, normalizados a una cifra mensual. Refleja los ingresos estables que genera tu negocio de suscripciones, independientemente de la duración de cada suscripción. Para ver cómo contribuye cada cohorte de suscriptores a los ingresos recurrentes a lo largo del tiempo, agrupa el gráfico por mes de primera compra y cambia a una resolución mensual. La vista de área apilada muestra la aportación de cada cohorte mes a mes. ## Cálculo \{#calculation\} :::warning La calculadora de abajo **no tiene en cuenta** la [comisión del store e impuestos](how-adapty-analytics-works#commissions-and-taxes). Compara el resultado con tus cálculos de **ingresos brutos**. ::: El MRR normaliza los ingresos de cada suscripción a su equivalente mensual: una suscripción anual a 240 $ aporta 20 $ al mes, no 240 $ de golpe. Esto mantiene el MRR estable independientemente de cómo se distribuyan los períodos de facturación de las suscripciones. El MRR es la suma de (precio × suscriptores activos ÷ período de facturación en meses) para todos tus tipos de suscripción. Las suscripciones semanales usan un período de facturación de ≈0,23 meses. <SimpleCalculator client:load heading="MRR" formuLatex="\sum_{subscriptions}^{}\frac{P_s\times N_s}{D_m}" variables={[ { nameInTheFormula: "P_s", variableName: "subscriptionPrice", variableDescription: "Price", variableValue: 10 }, { nameInTheFormula: "N_s", variableName: "activeSubs", variableDescription: "Subscribers", variableValue: 1, isInteger: true }, { nameInTheFormula: "D_m", variableName: "duration", variableDescription: "Subscription period", variableValue: 1, options: [ { label: "Weekly", value: 0.23 }, { label: "Monthly", value: 1 }, { label: "2 months", value: 2 }, { label: "3 months", value: 3 }, { label: "6 months", value: 6 }, { label: "Annual", value: 12 } ] } ]} formulaCalculation="(subscriptionPrice * activeSubs) / duration" isSum={true} defaultRows={[ { subscriptionPrice: 240, activeSubs: 2, duration: 12}, { subscriptionPrice: 30, activeSubs: 10, duration: 1}, { subscriptionPrice: 10, activeSubs: 20, duration: 0.23}, ]} /> El MRR excluye: - Compras únicas, consumibles y suscripciones no renovables (estos no generan ingresos recurrentes predecibles). - Suscripciones reembolsadas: se eliminan de todos los períodos en los que estuvieron activas. ## Divisa \{#currency\} --- no_index: true --- Adapty muestra todos los gráficos monetarios en **dólares estadounidenses**, independientemente de la moneda original de la transacción. Esto incluye Revenue, MRR, ARR, ARPU, ARPPU, LTV, ingresos previstos, reembolsos y las cifras de ingresos dentro de los informes de cohortes y pruebas A/B. No hay ninguna opción para mostrarlos en otra moneda. Adapty convierte cada transacción a USD usando un tipo de cambio de [currencylayer.com](https://currencylayer.com/) que se actualiza cada 8 horas, **fijado en el momento de la transacción**. Los valores históricos en USD no se recalculan cuando varía el tipo de cambio. Los valores en moneda local están disponibles por transacción en: - Los campos `price` y `currency_code` en los webhooks - Las columnas `price` y `currency_code` en las exportaciones de S3, GCS y BigQuery - La página de perfil (vista por transacción) Para los informes financieros en moneda local, extrae los valores en moneda local por transacción desde una exportación y agrégatelos tú mismo. ## Precios de renovación \{#renewal-pricing\} --- no_index: true --- Adapty calcula los ingresos por renovación al precio actual del producto, incluso para los usuarios que tenían un precio anterior cuando se suscribieron por primera vez. Después de cambiar un precio en App Store Connect o Google Play, las cifras de Revenue, MRR y ARR del dashboard para los suscriptores existentes pueden diferir de los ingresos reales recaudados: Adapty aplica el nuevo precio, aunque el store haya mantenido a esos usuarios en el precio anterior. Para verificarlo, compara el campo `price` por transacción en la exportación de S3, GCS o BigQuery con el dashboard para las mismas transacciones. El campo de exportación refleja lo que el store reportó (el precio que el cliente pagó realmente); el dashboard refleja el precio actual del producto. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Estado de renovación, período, producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creativo de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación en paralelo de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#revenue). - [Revenue](revenue) - [ARR](arr) - [ARPU](arpu) - [ARPPU](arppu) --- # File: arr --- --- title: "ARR" description: "Haz un seguimiento del Revenue Anual Recurrente (ARR) y optimiza tu estrategia de suscripciones." --- El gráfico de Revenue Anual Recurrente muestra los ingresos de todas las suscripciones de renovación automática activas normalizados a un año. El gráfico considera activa cualquier suscripción de pago no vencida. El ARR es una métrica fundamental para rastrear el crecimiento de tu negocio de suscripciones y predecir los ingresos futuros. ## Cálculo \{#calculation\} :::warning La calculadora de abajo **no tiene en cuenta** la [comisión del store y los impuestos](how-adapty-analytics-works#commissions-and-taxes). Compara el resultado con tus cálculos de **ingresos brutos**. ::: El ARR es la versión anualizada de tus ingresos recurrentes por suscripción. Es más útil cuando las suscripciones anuales son tu producto principal; para negocios con suscripciones principalmente mensuales o semanales, el [MRR](mrr) es más informativo. El ARR es la suma de (precio × suscriptores activos ÷ período de facturación en años) para todos tus tipos de suscripción. Usa 1/12 para mensual y 1/52 para semanal. <SimpleCalculator client:load heading="ARR" formuLatex="\sum \frac{P_s \times U_s}{D_y}" variables={[ { nameInTheFormula: "P_s", variableName: "price", variableDescription: "Subscription price", variableValue: 240 }, { nameInTheFormula: "U_s", variableName: "subs", variableDescription: "Active paid subs", variableValue: 2, isInteger: true }, { nameInTheFormula: "D_y", variableName: "periods", variableDescription: "Subscription period", variableValue: 1, options: [ { label: "Weekly", value: "1/52" }, { label: "Monthly", value: "1/12" }, { label: "2 months", value: "2/12" }, { label: "3 months", value: "3/12" }, { label: "6 months", value: "6/12" }, { label: "Annual", value: 1 } ] } ]} formulaCalculation="(price * subs ) / periods" isSum={true} defaultRows={[ { price: 240, subs: 2, periods: "1" }, { price: 30, subs: 10, periods: "1/12" }, { price: 10, subs: 20, periods: "1/52" } ]} /> El ARR excluye las suscripciones reembolsadas: cuando se reembolsa una suscripción, Adapty la elimina del cálculo durante todos los períodos en que estuvo activa. ## Divisa \{#currency\} --- no_index: true --- Adapty muestra todos los gráficos monetarios en **dólares estadounidenses**, independientemente de la moneda original de la transacción. Esto incluye Revenue, MRR, ARR, ARPU, ARPPU, LTV, ingresos previstos, reembolsos y las cifras de ingresos dentro de los informes de cohortes y pruebas A/B. No hay ninguna opción para mostrarlos en otra moneda. Adapty convierte cada transacción a USD usando un tipo de cambio de [currencylayer.com](https://currencylayer.com/) que se actualiza cada 8 horas, **fijado en el momento de la transacción**. Los valores históricos en USD no se recalculan cuando varía el tipo de cambio. Los valores en moneda local están disponibles por transacción en: - Los campos `price` y `currency_code` en los webhooks - Las columnas `price` y `currency_code` en las exportaciones de S3, GCS y BigQuery - La página de perfil (vista por transacción) Para los informes financieros en moneda local, extrae los valores en moneda local por transacción desde una exportación y agrégatelos tú mismo. ## Precios de renovación \{#renewal-pricing\} --- no_index: true --- Adapty calcula los ingresos por renovación al precio actual del producto, incluso para los usuarios que tenían un precio anterior cuando se suscribieron por primera vez. Después de cambiar un precio en App Store Connect o Google Play, las cifras de Revenue, MRR y ARR del dashboard para los suscriptores existentes pueden diferir de los ingresos reales recaudados: Adapty aplica el nuevo precio, aunque el store haya mantenido a esos usuarios en el precio anterior. Para verificarlo, compara el campo `price` por transacción en la exportación de S3, GCS o BigQuery con el dashboard para las mismas transacciones. El campo de exportación refleja lo que el store reportó (el precio que el cliente pagó realmente); el dashboard refleja el precio actual del producto. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Estado de renovación, período, producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creativo de atribución. ## Métricas relacionadas \{#similar-metrics\} Para una comparación lado a lado de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#revenue). - [Revenue](revenue) - [MRR](mrr) - [ARPU](arpu) - [ARPPU](arppu) --- # File: arpu --- --- title: "ARPU" description: "Analiza el ingreso promedio por usuario (ARPU) para optimizar la generación de ingresos." --- El gráfico ARPU (ingreso promedio por usuario) muestra el ingreso promedio generado por usuario durante un período determinado. Esta métrica se calcula dividiendo el ingreso total generado por una cohorte de clientes entre el número de usuarios de esa cohorte. Usa el ARPU para comparar el rendimiento de ingresos entre segmentos de usuarios — por fuente de atribución, país o producto. ## Cálculo \{#calculation\} :::warning La calculadora de abajo **no tiene en cuenta** la [comisión del store e impuestos](how-adapty-analytics-works#commissions-and-taxes). Compara el resultado con tus cálculos de **ingresos brutos**. ::: El ARPU muestra el ingreso promedio que genera tu app por usuario — un indicador habitual de eficiencia en la monetización. El ARPU es el ingreso del período (menos los reembolsos) dividido entre el número total de usuarios de la app en ese período. <CompoundCalculator client:load heading="ARPU" formuLatex="\frac{\sum P_i \times Q_i - D}{U_p}" variables={[ { nameInTheFormula: "P", variableName: "price", variableDescription: "Precio del producto", variableValue: 10 }, { nameInTheFormula: "Q", variableName: "qty", variableDescription: "Productos comprados", variableValue: 1, isInteger: true }, { nameInTheFormula: "D", variableName: "refunds", variableDescription: "Importe reembolsado", variableValue: 35, global: true }, { nameInTheFormula: "Up", variableName: "users", variableDescription: "Total de usuarios", variableValue: 160, global: true, isInteger: true } ]} rowFormula="price * qty" resultFormula="(_sum - refunds) / users" defaultRows={[ { price: 10, qty: 5 }, { price: 50, qty: 10 }, { price: 100, qty: 1 } ]} /> ## Moneda \{#currency\} --- no_index: true --- Adapty muestra todos los gráficos monetarios en **dólares estadounidenses**, independientemente de la moneda original de la transacción. Esto incluye Revenue, MRR, ARR, ARPU, ARPPU, LTV, ingresos previstos, reembolsos y las cifras de ingresos dentro de los informes de cohortes y pruebas A/B. No hay ninguna opción para mostrarlos en otra moneda. Adapty convierte cada transacción a USD usando un tipo de cambio de [currencylayer.com](https://currencylayer.com/) que se actualiza cada 8 horas, **fijado en el momento de la transacción**. Los valores históricos en USD no se recalculan cuando varía el tipo de cambio. Los valores en moneda local están disponibles por transacción en: - Los campos `price` y `currency_code` en los webhooks - Las columnas `price` y `currency_code` en las exportaciones de S3, GCS y BigQuery - La página de perfil (vista por transacción) Para los informes financieros en moneda local, extrae los valores en moneda local por transacción desde una exportación y agrégatelos tú mismo. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: atribución, país y store. - ✅ Agrupar por: país, store, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación detallada de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#revenue). - [Ingresos](revenue) - [MRR](mrr) - [ARPPU](arppu) - [ARR](arr) --- # File: arppu --- --- title: "ARPPU" description: "Entiende el ARPPU (Ingreso Promedio por Usuario de Pago) y cómo afecta la monetización de tu app." --- El gráfico de ingreso promedio por usuario de pago (ARPPU) muestra el ingreso promedio por usuario que ha realizado algún pago. Refleja los ingresos reales generados por los clientes de pago, divididos entre el número de clientes, menos los reembolsos. Agrupa el ARPPU por atribución para ver qué canales de adquisición traen usuarios de pago con mayor valor. ## Cálculo \{#calculation\} :::warning La calculadora de abajo **no tiene en cuenta** la [comisión del store e impuestos](how-adapty-analytics-works#commissions-and-taxes). Compara el resultado con tus cálculos de **ingresos brutos**. ::: El ARPPU muestra el ingreso promedio por usuario de pago — normalmente mucho más alto que el [ARPU](arpu), porque los usuarios que no pagan quedan excluidos del denominador. El ARPPU es el ingreso del período (menos reembolsos) dividido entre el número de usuarios de pago en ese período. <CompoundCalculator client:load heading="ARPPU" formuLatex="\frac{\sum P_i \times Q_i - D}{U_p}" variables={[ { nameInTheFormula: "P", variableName: "price", variableDescription: "Precio del producto", variableValue: 10 }, { nameInTheFormula: "Q", variableName: "qty", variableDescription: "Productos comprados", variableValue: 1, isInteger: true }, { nameInTheFormula: "D", variableName: "refunds", variableDescription: "Importe reembolsado", variableValue: 35, global: true }, { nameInTheFormula: "Up", variableName: "users", variableDescription: "Usuarios de pago", variableValue: 16, global: true, isInteger: true } ]} rowFormula="price * qty" resultFormula="(_sum - refunds) / users" defaultRows={[ { price: 10, qty: 5 }, { price: 50, qty: 10 }, { price: 100, qty: 1 } ]} /> Un usuario cuya compra fue posteriormente reembolsada sigue contando en el denominador de usuarios de pago. Los reembolsos elevados pueden reducir el ARPPU más rápido de lo esperado. ## Moneda \{#currency\} --- no_index: true --- Adapty muestra todos los gráficos monetarios en **dólares estadounidenses**, independientemente de la moneda original de la transacción. Esto incluye Revenue, MRR, ARR, ARPU, ARPPU, LTV, ingresos previstos, reembolsos y las cifras de ingresos dentro de los informes de cohortes y pruebas A/B. No hay ninguna opción para mostrarlos en otra moneda. Adapty convierte cada transacción a USD usando un tipo de cambio de [currencylayer.com](https://currencylayer.com/) que se actualiza cada 8 horas, **fijado en el momento de la transacción**. Los valores históricos en USD no se recalculan cuando varía el tipo de cambio. Los valores en moneda local están disponibles por transacción en: - Los campos `price` y `currency_code` en los webhooks - Las columnas `price` y `currency_code` en las exportaciones de S3, GCS y BigQuery - La página de perfil (vista por transacción) Para los informes financieros en moneda local, extrae los valores en moneda local por transacción desde una exportación y agrégatelos tú mismo. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Estado de renovación, período, producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creativo de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación de estas métricas en paralelo, consulta la [tabla de comparación de métricas](metric-comparison-table#revenue). - [Ingresos](revenue) - [MRR](mrr) - [ARPU](arpu) - [ARR](arr) --- # File: installs --- --- title: "Instalaciones" description: "Rastrea las instalaciones de la aplicación y comprende su impacto en las suscripciones con Adapty." --- El gráfico de Instalaciones muestra cuántos usuarios instalaron tu aplicación durante el período seleccionado. Lo que cuenta como instalación —y cómo se agrupa cada una— depende de la configuración de conteo de instalaciones. Este artículo explica cómo seleccionar el modo de conteo adecuado y cómo [conciliar las posibles discrepancias](#troubleshooting) entre las diferentes fuentes de análisis. ## Qué cuenta como instalación \{#what-counts-as-an-install\} El SDK de Adapty registra una "instalación" y la envía a Adapty cuando el usuario lanza tu aplicación por primera vez. Esto tiene dos consecuencias: - Una instalación aparece en Adapty cuando el usuario abre la app por primera vez, lo que puede ocurrir horas o días después de descargarla. - Si un usuario descarga la app pero nunca la abre, Adapty no lo contabiliza. Tu **zona horaria de informes** en App Settings determina en qué día se registra cada instalación. Una instalación a las 23:30 UTC del 1 de junio se registra el 2 de junio si tu zona horaria es +02:00, mientras que App Store Connect o Google Play pueden mostrarla el 1 de junio. ### Modos de conteo \{#counting-modes\} La configuración **Installs definition for analytics** determina qué cuenta como una nueva instalación. Para cambiarla, abre [App Settings → General → Installs definition for analytics](general#4-installs-definition-for-analytics). | Modo | Qué cuenta | Ejemplo | Métrica de terceros | Posibles discrepancias | | --- | --- | --- | --- | --- | | **New device_ids** (recomendado) | **Cada instalación de la app** — incluidas las reinstalaciones. La autenticación, la creación de perfiles y las actualizaciones de versión no suman al conteo. | Un usuario en 5 dispositivos = 5 instalaciones. <br /> <br /> Reinstalar en el mismo dispositivo = 2 instalaciones. | App Store: <br /> **Total Active Devices** <br /> <br /> Google Play: **Devices** | **Mayor que el número de descargas** si las reinstalaciones son frecuentes. <br /> <br /> **Menor que el número de descargas** si muchos usuarios descargan la app sin abrirla. | | **New customer_user_ids** | Solo **la primera instalación** por [usuario identificado](identifying-users). Los dispositivos adicionales y los usuarios anónimos no se cuentan. | Un usuario en 5 dispositivos = 1 instalación. <br /> <br /> Reinstalar e iniciar sesión de nuevo = no hay nueva instalación. <br /> <br /> Usar la app sin cuenta = no hay nueva instalación. | Estadísticas de registro de tu sistema de autenticación | **Se queda vacío** si no identificas usuarios en absoluto. | | **New profiles in Adapty** (heredado) | Cuenta cada instalación y reinstalación, **así como los perfiles anónimos creados al cerrar sesión**. | Un usuario, un dispositivo, 3 cierres de sesión = 4 instalaciones. | Ninguna | **Mayor que todas las métricas externas**. Cuenta cada perfil anónimo creado al cerrar sesión como una instalación. | Usa **New device_ids** salvo que tengas una razón específica para cambiarlo. ## Solución de problemas \{#troubleshooting\} ### El conteo de Adapty es mayor que el de App Store Connect o Google Play \{#adaptys-count-is-higher-than-app-store-connect-or-google-play\} Dos causas probables: - **Reinstalaciones.** Si tu [modo de conteo](#counting-modes) está configurado en **New device_ids**, Adapty cuenta tanto los primeros lanzamientos como las reinstalaciones posteriores. "Total Downloads" de App Store Connect solo cuenta la descarga inicial. - **Fecha del primer lanzamiento ≠ fecha de descarga.** Los stores atribuyen por fecha de descarga. Los usuarios que abren la app tarde aparecen en días diferentes. Para hacer una comparación más limpia, abre **App Store Connect → Total Active Devices** o **Google Play → Devices**. Esas métricas son a nivel de dispositivo y se acercan más al modo **New device_ids** de Adapty. ### El conteo de Adapty es cero \{#adaptys-count-is-zero\} Si tu modo de conteo es **New customer_user_ids**, pero no <InlineTooltip tooltip="autenticas usuarios">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), [Unity](unity-identifying-users), [Kotlin Multiplatform](kmp-quickstart-identify), [Capacitor](capacitor-quickstart-identify)</InlineTooltip>, Adapty no registrará ninguna instalación. En ese modo, las instalaciones anónimas se excluyen. Cambia a **New device_ids** o implementa la identificación de usuarios. ### El conteo de Adapty difiere de AppsFlyer o Adjust \{#adaptys-count-differs-from-appsflyer-or-adjust\} Los MMP atribuyen instalaciones por su propio evento de inicio del SDK o de primer contacto. Estos se activan en un momento diferente al del primer lanzamiento del SDK de Adapty — cierta discrepancia es normal. ## Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país y store. - ✅ Agrupar por: País, store, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación lado a lado de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#subscribers-and-conversion). - [Nuevas suscripciones](reactivated-subscriptions) - [Suscripciones activas](active-subscriptions) - [Nuevas pruebas](new-trials) --- # File: active-subscriptions --- --- title: "Suscripciones activas" description: "Monitoriza y gestiona las suscripciones activas con los análisis avanzados de Adapty." --- El gráfico de suscripciones activas muestra el número de suscripciones de pago únicas que aún no han expirado al final de cada período seleccionado. Incluye las suscripciones in-app regulares (no expiradas) que han comenzado y están activas en este momento, y excluye tanto las pruebas gratuitas como las suscripciones con renovaciones canceladas. Es un indicador del tamaño y crecimiento de tu base de suscriptores. ## Cálculo \{#calculation\} La métrica de suscripciones activas cuenta las suscripciones de pago no expiradas al final de cada período. Para las suscripciones sin período de gracia, la expiración ocurre cuando pasa la siguiente fecha de renovación sin que esta se complete con éxito. Por ejemplo: 500 suscripciones activas al final del mes pasado, más 50 nuevas este mes, menos 25 que expiraron este mes = 525 suscripciones activas al final de este mes. :::note Cuando se reembolsa una suscripción, se resta del recuento de suscripciones activas. Esto garantiza que la métrica refleje con precisión las suscripciones que están generando ingresos para tu app en este momento. ::: ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación lado a lado de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#subscribers-and-conversion). - [Suscripciones canceladas (expiradas)](churned-expired-subscriptions) - [Suscripciones canceladas](cancelled-subscriptions) - [Compras únicas](non-subscriptions) --- # File: reactivated-subscriptions --- --- title: "Nuevas suscripciones" description: "Rastrea las nuevas suscripciones en Adapty para monitorear las primeras conversiones y las conversiones de pruebas gratuitas a suscripciones de pago." --- El gráfico de Nuevas suscripciones muestra el número de suscripciones nuevas (activadas por primera vez) en tu app. Esta métrica indica la cantidad de nuevas suscripciones que se inician en un período de tiempo concreto, incluyendo tanto las suscripciones que empiezan desde cero como las pruebas gratuitas que se convierten en suscripciones de pago. No incluye renovaciones de suscripciones ni suscripciones que se hayan reactivado. ## Cálculo \{#calculation\} La métrica de nuevas suscripciones cuenta las activaciones de suscripciones por primera vez durante el período, tanto las que empiezan desde cero como las pruebas gratuitas que se convierten en suscripciones de pago. :::note El recuento de Nuevas suscripciones incluye todas las suscripciones que se activaron por primera vez durante el período, aunque posteriormente se reembolsaran. Esto significa que la cifra mostrada puede ser mayor que el número de suscripciones que finalmente generaron ingresos. Para entender el impacto neto, compara esta métrica con el gráfico de Eventos de reembolso. ::: ## Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Producto, país, store, paywall, duración, estado de renovación, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creativo de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación directa de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#subscribers-and-conversion). - [Suscripciones activas](active-subscriptions) - [Suscripciones canceladas (expiradas)](churned-expired-subscriptions) - [Suscripciones canceladas](cancelled-subscriptions) - [Compras únicas](non-subscriptions) --- # File: non-subscriptions --- --- title: "Compras no recurrentes" description: "Aprende a gestionar productos sin suscripción en Adapty y a hacer un seguimiento eficiente de las compras de los usuarios." --- El gráfico de compras no recurrentes contiene las compras in-app que no son suscripciones de renovación automática: consumibles, no consumibles y suscripciones sin renovación. Las renovaciones quedan excluidas. :::note "Compras no recurrentes" es un concepto más amplio que "compras únicas": los consumibles y las suscripciones sin renovación pueden comprarse más de una vez. ::: ## Cálculo \{#calculation\} Cada compra in-app no recurrente pertenece a uno de estos tres tipos: - **Consumibles**: artículos que los usuarios pueden comprar varias veces, como comida para peces en una app de pesca o moneda adicional del juego. - **No consumibles**: artículos que los usuarios compran una sola vez y usan de forma permanente, como un circuito en un juego de carreras o una versión sin anuncios. - **Suscripciones sin renovación**: suscripciones que caducan tras un período determinado y no se renuevan automáticamente, como un acceso anual a un catálogo de contenidos. El contenido puede ser estático, pero la suscripción no se renueva al expirar. :::note Este gráfico solo contabiliza los eventos de compra y no resta las compras reembolsadas. Si tienes productos no recurrentes que se reembolsan con frecuencia, la cifra mostrada será superior al número real de compras que generaron ingresos. ::: ## Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall y store. - ✅ Agrupar por: Producto, país, store, paywall, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para comparar estas métricas en paralelo, consulta la [tabla comparativa de métricas](metric-comparison-table#revenue). - [Suscripciones activas](active-subscriptions) - [Nuevas suscripciones](reactivated-subscriptions) - [Suscripciones canceladas (expiradas)](churned-expired-subscriptions) - [Suscripciones canceladas](cancelled-subscriptions) --- # File: cancelled-subscriptions --- --- title: "Cancelación de renovación de suscripciones" description: "Gestiona eficientemente las suscripciones canceladas con las herramientas de gestión de Adapty." --- El gráfico de cancelación de renovación de suscripciones muestra el número de suscripciones cuya renovación automática ha sido desactivada (cancelada por el usuario). Cuando se desactiva la renovación automática de una suscripción, significa que no se renovará automáticamente para el siguiente período. Sin embargo, el usuario sigue teniendo acceso a las funciones premium de la app hasta el final del período actual. ## Cálculo \{#calculation\} La métrica de cancelación de renovación de suscripciones cuenta las suscripciones cuya renovación automática fue desactivada durante el período. El usuario conserva el acceso premium hasta que finalice el período de facturación actual, pero la suscripción no se renovará automáticamente después. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas relacionadas \{#similar-metrics\} Para una comparación lado a lado de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#churn). - [Suscripciones activas](active-subscriptions) - [Suscripciones canceladas (expiradas)](churned-expired-subscriptions) - [Nuevas suscripciones](reactivated-subscriptions) - [No suscripciones](non-subscriptions) --- # File: churned-expired-subscriptions --- --- title: "Suscripciones canceladas (expiradas)" description: "Gestiona las suscripciones canceladas y expiradas para mejorar la retención de usuarios." --- El gráfico de suscripciones canceladas (expiradas) muestra el número de suscripciones que han expirado, lo que significa que el usuario ya no tiene acceso a las funciones premium de la app. Normalmente, esto ocurre cuando el usuario decide dejar de pagar al final del período de suscripción o cuando se produce un problema de facturación. Agrupa por motivo de expiración para separar la cancelación voluntaria de la originada por problemas de pago. ## Cálculo \{#calculation\} La métrica de suscripciones canceladas (expiradas) cuenta las suscripciones que expiraron durante el período, es decir, aquellas en las que el usuario perdió el acceso a las funciones premium. Esto incluye tanto a los usuarios que decidieron no renovar como a los que perdieron la suscripción por un problema de facturación. ## Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Motivo de expiración, producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#churn). - [Suscripciones activas](active-subscriptions) - [Suscripciones nuevas](reactivated-subscriptions) - [Suscripciones canceladas](cancelled-subscriptions) - [Compras no relacionadas con suscripciones](non-subscriptions) --- # File: active-trials --- --- title: "Pruebas activas" description: "Rastrea y gestiona las pruebas de suscripción activas con Adapty analytics." --- El gráfico de pruebas activas en Adapty muestra el número de pruebas gratuitas no expiradas que están actualmente activas al final de un período determinado. Activas significa suscripciones que aún no han expirado; por lo tanto, los usuarios todavía tienen acceso a las funciones de pago de la aplicación. ## Cálculo \{#calculation\} La métrica de pruebas activas cuenta las pruebas gratuitas no expiradas al final de cada período. Cancelar la renovación automática no elimina una prueba del recuento — solo lo hace la expiración. Por ejemplo: 100 pruebas activas ayer, más 10 nuevas hoy, menos 5 que expiraron hoy = 105 pruebas activas hoy. ## Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analytics](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Estado de renovación, período, producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación lado a lado de estas métricas, consulta la [Tabla de comparación de métricas](metric-comparison-table#subscribers-and-conversion). - [Nuevas pruebas](new-trials) - [Renovación de prueba cancelada](trials-renewal-cancelled) - [Pruebas expiradas](expired-churned-trials) --- # File: new-trials --- --- title: "Nuevas pruebas" description: "Gestiona las nuevas pruebas de suscripción y optimiza las tasas de conversión de prueba a pago." --- El gráfico de nuevas pruebas muestra el número de pruebas activadas durante el período de tiempo seleccionado. Úsalo para hacer seguimiento del volumen de pruebas procedentes de campañas publicitarias y otros esfuerzos de captación. ## Cálculo \{#calculation\} La métrica de nuevas pruebas cuenta las pruebas iniciadas durante el período, independientemente de si siguen activas al final del mismo. Por ejemplo, si 50 usuarios inician una prueba en mayo, el dato de mayo muestra 50, aunque algunas ya hayan expirado o se hayan convertido a pago cuando consultes el gráfico. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación en paralelo de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#subscribers-and-conversion). - [Pruebas activas](active-trials) - [Renovación de prueba cancelada](trials-renewal-cancelled) - [Pruebas expiradas](expired-churned-trials) --- # File: trials-renewal-cancelled --- --- title: "Renovaciones de prueba canceladas" description: "Entiende las renovaciones de prueba, cancelaciones y flujos de suscripción con los datos de Adapty." --- El gráfico Trials renewal cancelled muestra el número de pruebas con renovación cancelada (cancelada por el usuario). Cuando se desactiva la renovación de una prueba, significa que esa prueba no se convertirá automáticamente en una suscripción de pago, aunque el usuario seguirá teniendo acceso a las funciones premium de la app hasta el final del período actual. ## Cálculo \{#calculation\} La métrica de renovaciones de prueba canceladas cuenta las pruebas cuya renovación automática fue desactivada por el usuario durante el período. El usuario mantiene el acceso de prueba hasta que esta finaliza, pero la prueba no se convertirá automáticamente en una suscripción de pago. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analytics](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: estado de renovación, período, producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación lado a lado de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#churn). - [Nuevas pruebas](new-trials) - [Pruebas activas](active-trials) - [Pruebas expiradas](expired-churned-trials) --- # File: expired-churned-trials --- --- title: "Pruebas expiradas (churned)" description: "Gestiona las pruebas expiradas y canceladas de forma efectiva con los análisis de Adapty." --- El gráfico de pruebas expiradas (churned) muestra el número de pruebas que han expirado, dejando a los usuarios sin acceso a las funciones premium de la app. En la mayoría de los casos, esto ocurre cuando los usuarios deciden no pagar por la app o tienen problemas de facturación. ## Cálculo \{#calculation\} La métrica de pruebas expiradas cuenta las pruebas que finalizaron durante el período, es decir, el usuario perdió acceso a las funciones premium. Esto incluye tanto a los usuarios que decidieron no convertirse como a aquellos cuya conversión falló por un problema de facturación. Agrupa por **Expiration reason** para separar el churn voluntario del causado por problemas de facturación. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: motivo de expiración, producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creativo de atribución. ## Métricas similares \{#similar-metrics\} Para comparar estas métricas en paralelo, consulta la [tabla de comparación de métricas](metric-comparison-table#churn). - [Nuevas pruebas](new-trials) - [Pruebas activas](active-trials) - [Cancelación de renovación de pruebas](trials-renewal-cancelled) --- # File: refund-events --- --- title: "Eventos de reembolso" description: "Gestiona los eventos de reembolso en Adapty para reducir la pérdida de usuarios y optimizar los ingresos." --- El gráfico de eventos de reembolso muestra cuántas compras y suscripciones fueron reembolsadas. Adapty asocia cada evento de reembolso a la fecha en que se emitió el reembolso, no a la fecha de inicio de la suscripción. ## Cálculo \{#calculation\} Adapty cuenta cada compra o suscripción reembolsada dentro del período seleccionado. Cada reembolso se atribuye a la fecha en que ocurrió, no a cuando comenzó la suscripción. Los reembolsos de pruebas gratuitas se excluyen porque no generan ingresos. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto, duración y motivo de reembolso. - ✅ Agrupar por: Producto, país, store, paywall, duración, motivo de reembolso, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación detallada de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#revenue). - [Dinero reembolsado](refund-money) - [Problema de facturación](billing-issue) - [Período de gracia](grace-period) --- # File: refund-money --- --- title: "Reembolsar dinero" description: "Aprende a procesar reembolsos de suscripciones en Adapty sin perder ingresos." --- El gráfico Refund money muestra el importe reembolsado durante el período seleccionado. Adapty vincula cada evento de reembolso a la fecha en que se emitió, por lo que los ingresos disminuyen en ese mismo período. ## Cálculo \{#calculation\} Adapty solo contabiliza las transacciones que generan ingresos: nuevas suscripciones de pago, renovaciones y compras únicas. Las pruebas gratuitas, que no generan ingresos y no pueden reembolsarse, quedan excluidas. Cada importe reembolsado se vincula a la fecha en que se procesó, por lo que la disminución de ingresos aparece en ese mismo período. :::info El importe del reembolso se calcula antes de deducir la comisión del store. ::: ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto, duración y motivo del reembolso. - ✅ Agrupar por: Producto, país, store, paywall, duración, motivo del reembolso, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Gestión de solicitudes de reembolso \{#refund-request-management\} El Refund saver ayuda a los usuarios de Adapty a gestionar las solicitudes de reembolso del App Store de Apple de forma más eficiente mediante la automatización. Ahorra tiempo y reduce la pérdida de ingresos al simplificar el proceso. Con notificaciones en tiempo real e información práctica, esta herramienta facilita la gestión de las solicitudes de reembolso cumpliendo con las directrices de Apple. Más información sobre el [Refund saver](refund-saver). ## Métricas similares \{#similar-metrics\} Para una comparación lado a lado de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#revenue). - [Refund events](refund-events) - [Billing issue](billing-issue) - [Período de gracia](grace-period) --- # File: grace-period --- --- title: "Período de gracia" description: "Comprende cómo funcionan los períodos de gracia en suscripciones y mejora la retención de usuarios." --- El gráfico de período de gracia muestra el número de suscripciones que han entrado en estado de período de gracia debido a un [problema de facturación](billing-issue). Durante este período, la suscripción permanece activa mientras el store intenta cobrar al suscriptor. Si el pago no se recibe correctamente antes de que termine el período de gracia, la suscripción entra en estado de problema de facturación. ## Cálculo \{#calculation\} La métrica de período de gracia cuenta las suscripciones que entraron en período de gracia durante el intervalo de tiempo seleccionado. El período de gracia comienza cuando falla el pago de renovación de una suscripción y dura hasta 6 días para suscripciones semanales o 16 días para otros períodos de facturación. Si el pago se procesa durante esta ventana, la suscripción continúa con normalidad; si no, la suscripción entra en estado de [problema de facturación](billing-issue). ## Filtros y agrupación disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas similares \{#similar-metrics\} Para una comparación en paralelo de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#billing-issues-and-revenue-recovery). - [Dinero reembolsado](refund-money) - [Eventos de reembolso](refund-events) - [Problema de facturación](billing-issue) --- # File: grace-period-converted --- --- title: "Período de gracia convertido" description: "Realiza el seguimiento del número de suscripciones que entraron en el período de gracia y se renovaron antes de que finalizara." --- El gráfico **Grace period converted** muestra el número de suscripciones que entraron en el estado de [período de gracia](grace-period) y se renovaron con éxito antes de que el período finalizara. ### Cálculo \{#calculation\} El gráfico Grace period converted muestra el número diario de renovaciones de suscripciones para usuarios que se encuentran en un período de gracia. El período de gracia comienza cuando la suscripción entra en el estado de problema de facturación debido a un fallo en el pago y finaliza tras un tiempo determinado (6 días para las suscripciones semanales, 16 días para el resto de suscripciones) o cuando el pago se recibe correctamente. El gráfico ofrece información sobre la efectividad del período de gracia y puede ayudar a identificar posibles problemas en el procesamiento de pagos o en la gestión de suscripciones. ### Filtros disponibles \{#available-filters\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, audiencia, motivo de reembolso, país, tipo de oferta, ID de oferta, tipo de descuento de oferta, prueba A/B, paywall, store, placement, período, segmento, producto y duración. ### Uso del gráfico Grace period converted \{#grace-period-converted-chart-usage\} Usa este gráfico para hacer un seguimiento de la efectividad del período de gracia a la hora de recuperar suscripciones con problemas de pago. Al monitorizar las tendencias de conversión a lo largo del tiempo, puedes identificar patrones en la resolución de pagos y evaluar el impacto de los cambios realizados en los flujos de actualización de pago o en las estrategias de comunicación durante los períodos de gracia. ### Métricas similares \{#similar-metrics\} - [Problema de facturación](billing-issue) - [Problema de facturación convertido](billing-issue-converted) - [Ingresos por problema de facturación convertido](billing-issue-converted-revenue) - [Período de gracia](grace-period) - [Ingresos por período de gracia convertido](grace-period-converted-revenue) - [Dinero reembolsado](refund-money) - [Eventos de reembolso](refund-events) --- # File: grace-period-converted-revenue --- --- title: "Ingresos por conversión en período de gracia" description: "Realiza un seguimiento de los ingresos totales por conversiones en período de gracia." --- El gráfico de **Grace period converted revenue** muestra los ingresos generados por las [conversiones en período de gracia](grace-period-converted): suscripciones que entraron en estado de [período de gracia](grace-period) y se renovaron con éxito antes de que ese período finalizara. ### Cálculo \{#calculation\} El gráfico de Grace period converted revenue muestra los ingresos diarios generados por las renovaciones de suscripciones de usuarios en período de gracia. El período de gracia comienza cuando la suscripción entra en estado de problema de facturación debido a un fallo de pago y finaliza tras un tiempo determinado (6 días para suscripciones semanales, 16 días para el resto) o cuando el pago se recibe correctamente. El gráfico ofrece información sobre la efectividad del período de gracia y puede ayudar a identificar posibles problemas con el procesamiento de pagos o la gestión de suscripciones. ### Filtros disponibles \{#available-filters\} :::link Artículo principal: [Controles de analítica](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: atribución, audiencia, motivo de reembolso, país, tipo de oferta, ID de oferta, tipo de descuento de oferta, prueba A/B, paywall, store, placement, período, segmento, producto y duración. ### Uso del gráfico de ingresos por conversión en período de gracia \{#grace-period-converted-revenue-chart-usage\} Usa este gráfico para medir el impacto financiero del período de gracia mediante el seguimiento de los ingresos recuperados de suscripciones con problemas de pago. Esto te permite cuantificar la efectividad de tu estrategia de período de gracia y evaluar el retorno de inversión de implementar funcionalidades o comunicaciones relacionadas con el período de gracia. ### Métricas similares \{#similar-metrics\} - [Problema de facturación](billing-issue) - [Problema de facturación convertido](billing-issue-converted) - [Ingresos por problema de facturación convertido](billing-issue-converted-revenue) - [Período de gracia](grace-period) - [Período de gracia convertido](grace-period-converted) - [Dinero reembolsado](refund-money) - [Eventos de reembolso](refund-events) --- # File: billing-issue --- --- title: "Problema de facturación" description: "Resuelve problemas de facturación de suscripciones con las herramientas de soporte de Adapty." --- El gráfico de problema de facturación muestra el número de suscripciones que han entrado en el estado de Problema de facturación. Este estado se activa normalmente cuando el store, como Apple o Google, no puede recibir el pago del suscriptor por algún motivo. Esto puede ocurrir por razones como una tarjeta de crédito vencida o fondos insuficientes. ## Cálculo \{#calculation\} La métrica de problema de facturación cuenta las suscripciones que entraron en el estado de problema de facturación durante el período. Una suscripción entra en este estado cuando el store (Apple o Google) no puede procesar el pago de renovación, normalmente por una tarjeta de crédito vencida o fondos insuficientes. Durante el estado de problema de facturación, la suscripción no está activa. Si la función de [período de gracia](grace-period) está habilitada, la suscripción entra en el estado de problema de facturación solo después de que el período de gracia expire sin que se haya realizado el pago. ## Filtros y agrupaciones disponibles \{#available-filters-and-grouping\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Producto, país, store, paywall, duración, estado de atribución, canal de atribución, campaña de atribución, grupo de anuncios de atribución, conjunto de anuncios de atribución y creatividad de atribución. ## Métricas relacionadas \{#similar-metrics\} Para una comparación lado a lado de estas métricas, consulta la [tabla de comparación de métricas](metric-comparison-table#billing-issues-and-revenue-recovery). - [Problema de facturación convertido](billing-issue-converted) - [Ingresos por problema de facturación convertido](billing-issue-converted-revenue) - [Dinero reembolsado](refund-money) - [Eventos de reembolso](refund-events) - [Período de gracia](grace-period) - [Período de gracia convertido](grace-period-converted) - [Ingresos por período de gracia convertido](grace-period-converted-revenue) --- # File: billing-issue-converted --- --- title: "Problema de facturación convertido" description: "Realiza un seguimiento del número de problemas de facturación que se resuelven antes del final del ciclo de facturación." --- El gráfico Billing issue converted muestra el número diario de suscripciones que entraron en el estado [Billing Issue](billing-issue) y se renovaron antes del final del ciclo de facturación. ### Cálculo \{#calculation\} El gráfico Billing issue converted muestra el número de suscripciones que entraron en el estado [Billing Issue](billing-issue) en el ciclo de facturación actual y se renovaron en ese día. Una suscripción entra en el estado Billing Issue cuando el store (por ejemplo, Apple o Google) no puede procesar el pago del suscriptor por algún motivo, como una tarjeta de crédito caducada o fondos insuficientes. Durante el estado Billing Issue, la suscripción no se considera activa; si la función de período de gracia está habilitada en la configuración del store, la suscripción solo pasará al estado Billing Issue una vez que haya expirado el período de gracia. ### Filtros disponibles \{#available-filters\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: atribución, audiencia, motivo de reembolso, país, tipo de oferta, ID de oferta, tipo de descuento de oferta, prueba A/B, paywall, store, placement, período, segmento, producto y duración. ### Uso del gráfico Billing issue converted \{#billing-issue-converted-chart-usage\} Usa este gráfico para evaluar con qué eficacia se resuelven los problemas de facturación dentro del ciclo de facturación tras expirar el período de gracia. Al monitorear las tendencias de resolución a lo largo del tiempo, puedes identificar patrones en la recuperación de pagos y valorar el impacto de los cambios en la lógica de reintento de pagos o en las estrategias de comunicación durante los problemas de facturación. ### Métricas similares \{#similar-metrics\} - [Billing issue](billing-issue) - [Billing issue converted revenue](billing-issue-converted-revenue) - [Refund money](refund-money) - [Refund events](refund-events) - [Grace period](grace-period) - [Grace period converted](grace-period-converted) - [Grace period converted revenue](grace-period-converted-revenue) --- # File: billing-issue-converted-revenue --- --- title: "Ingresos convertidos por problemas de facturación" description: "Resuelve problemas de facturación en suscripciones con las herramientas de soporte de Adapty." --- El gráfico **Billing issue converted revenue** muestra los ingresos procedentes de las [conversiones de problemas de facturación](billing-issue-converted): suscripciones que entraron en el estado [Billing Issue](billing-issue) y se renovaron antes del final del ciclo de facturación. ### Cálculo \{#calculation\} El gráfico **Billing issue converted revenue** muestra los ingresos diarios de las suscripciones que entraron en el estado [Billing Issue](billing-issue) durante el ciclo de facturación actual y se renovaron en ese día. Una suscripción entra en el estado Billing Issue cuando el store (por ejemplo, Apple o Google) no puede procesar el pago del suscriptor por algún motivo, como una tarjeta de crédito caducada o fondos insuficientes. Durante el estado Billing Issue, la suscripción no se considera activa; si la función de período de gracia está habilitada en la configuración del store, la suscripción solo pasará al estado Billing Issue después de que haya expirado dicho período de gracia. ### Filtros disponibles \{#available-filters\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: - ✅ Filtrar por: atribución, audiencia, motivo de reembolso, país, tipo de oferta, ID de oferta, tipo de descuento de oferta, prueba A/B, paywall, store, placement, período, segmento, producto y duración. ### Uso del gráfico Billing issue converted revenue \{#billing-issue-converted-revenue-chart-usage\} Usa este gráfico para medir el impacto financiero de los problemas de facturación resueltos, haciendo seguimiento de los ingresos recuperados de suscripciones tras la expiración del período de gracia. Esto te permite cuantificar la efectividad de tu estrategia de recuperación ante problemas de facturación y evaluar el retorno de la inversión al implementar mecanismos de reintento de cobro o comunicaciones dirigidas a usuarios. ### Métricas similares \{#similar-metrics\} - [Billing issue](billing-issue) - [Billing issue converted](billing-issue-converted) - [Refund money](refund-money) - [Refund events](refund-events) - [Grace period](grace-period) - [Grace period converted](grace-period-converted) - [Grace period converted revenue](grace-period-converted-revenue) --- # File: ltv --- --- title: "Lifetime Value (LTV)" description: "Aprende a calcular y optimizar el Lifetime Value (LTV) en Adapty." --- El LTV realizado (Lifetime Value) por cliente de pago muestra los ingresos que una cohorte de clientes de pago generó realmente después de descontar los reembolsos, divididos por el número de clientes de pago en esa cohorte. Este gráfico te indica cuántos ingresos generas de media por cada cliente de pago. Adapty diseña el gráfico de LTV para responder varias preguntas importantes sobre los ingresos y el comportamiento de los clientes de tu app: 1. ¿Cuánto dinero genera cada cohorte a lo largo de su ciclo de vida con tu app? 2. ¿En qué momento se amortiza una cohorte? 3. ¿Cómo puedes optimizar el gasto en marketing y adquisición para atraer clientes valiosos con un LTV alto? 4. ¿Cuánto tiempo se tarda en recuperar la inversión en adquisición de nuevos clientes? El gráfico de LTV trabaja con los datos de la app que recopilamos a través del SDK y los eventos in-app. Con esta información, podrás obtener una visión clara del rendimiento de tus suscripciones y de cuántos ingresos generan tus suscriptores durante un período de tiempo determinado. Puedes usar estos datos para tomar decisiones fundamentadas sobre tu oferta de suscripciones, el gasto publicitario y las estrategias de adquisición de clientes. Además, los filtros te permiten segmentar los datos por país, atribución y otras variables, lo que te da una comprensión más detallada de tu base de clientes. ### LTV por renovaciones \{#ltv-by-renewals\} La vista **LTV by renewals** presenta datos relativos al período de suscripción (P), capturando específicamente la primera vez que un cliente realiza un pago. En el caso de una suscripción semanal, esto corresponde al siguiente período de suscripción semanal. ### LTV por días \{#ltv-by-days\} La vista **LTV by days** organiza y filtra los datos por intervalos diarios, semanales o mensuales. Ofrece información sobre los ingresos totales generados por todos los usuarios que instalaron la app en un día, semana o mes concreto, divididos por el número de usuarios de pago durante ese mismo período. Esta vista proporciona información valiosa sobre el seguimiento de ingresos y permite comprender de forma exhaustiva el comportamiento de los usuarios a lo largo del tiempo. ### Duración de la cohorte y marco temporal \{#cohort-length-and-time-frame\} --- no_index: true --- Dos ajustes de tiempo controlan lo que muestra la tabla: - **Time frame** — el rango de fechas. Se configura en el calendario que hay encima de la tabla. - **Cohort length** — el tamaño de cada fila: día, semana, mes, trimestre o año. Con una longitud mensual, cada fila cubre un mes de instalaciones. Los dos funcionan de forma independiente. Por ejemplo: un time frame de 6 meses más una cohort length mensual te da una tabla con 6 filas. Un time frame de 1 año más una cohort length semanal te da 52 filas. ### Cálculo \{#calculation\} El LTV realizado se calcula usando los ingresos totales generados por cada cohorte de clientes, menos los reembolsos. _LTV del día/semana/mes = Ingresos obtenidos de todos los usuarios de pago que instalaron la app ese día/semana/mes / el número de usuarios de pago que instalaron la app ese día/semana/mes_ El cálculo del LTV incluye actualizaciones, rebajas y reactivaciones, como cuando un usuario cambia de plan de suscripción o de precio. Tiene en cuenta los ingresos generados por la suscripción inicial y las renovaciones posteriores basadas en el plan actualizado. ### Agrupación y filtrado disponibles \{#available-grouping-and-filtering\} :::link Artículo principal: [Controles de analíticas](controls-filters-grouping-compare-proceeds) ::: Tanto los filtros como las agrupaciones se pueden aplicar a las vistas de renovaciones y días del gráfico de LTV, lo que te permite profundizar en cohortes específicas y entender su comportamiento a lo largo del tiempo. - ✅ Filtrar por: Atribución, país, paywall, store, producto y duración. - ✅ Agrupar por: Producto, país, store, duración y por día, semana, mes y año de la cohorte. El gráfico de LTV realizado en Adapty ayuda a obtener información valiosa sobre el comportamiento de los clientes, optimizar las estrategias de marketing, hacer seguimiento del rendimiento de los ingresos y tomar decisiones basadas en datos para maximizar el valor a largo plazo de los clientes. --- # File: analytics-cohorts --- --- title: "Análisis de cohortes" description: "Usa cohortes de análisis en Adapty para hacer seguimiento del engagement de usuarios y tendencias de suscripciones." --- Las cohortes de Adapty están diseñadas para responder a varias preguntas importantes: 1. ¿En qué día empieza a ser rentable una cohorte? 2. ¿Cuánto dinero genera la app para una cohorte concreta? 3. ¿Cuánto puedo gastar para atraer a un cliente de pago? 4. ¿Cuánto tiempo se tarda en recuperar la inversión publicitaria? Las cohortes trabajan con los datos de la app que recopilamos a través del SDK y las notificaciones de la store, y no requieren ninguna configuración adicional por tu parte. ## Cohortes por renovaciones o por días \{#cohorts-by-renewals-or-by-days\} Puedes analizar las cohortes por renovaciones o por días. El control cambia los encabezados de las columnas y, en consecuencia, también cambia el enfoque del análisis. El seguimiento **por días** ofrece información valiosa para la elaboración de presupuestos y la comprensión de los plazos de pago. Es especialmente útil para hacer seguimiento de productos que no son suscripciones, como consumibles o compras únicas. En este modo, el color azul en las celdas de la tabla tiende a concentrarse en el centro de las filas debido a dos factores clave. En primer lugar, ver las cohortes por días permite visualizar de forma temprana los pagos asociados a productos de corta duración que, en la vista por renovaciones, se agrupan con las renovaciones mensuales y anuales. En segundo lugar, los pagos diferidos contribuyen al patrón de distribución, ya que algunos usuarios pagan más tarde de lo esperado. En cambio, el seguimiento **por renovaciones** muestra la retención y el churn de las cohortes de un pago a otro, sin tener en cuenta la fecha. Así, los usuarios tardíos que pagaron con cualquier retraso (puede ser de meses) se suman al número de su período de suscripción. Este enfoque no refleja la situación de los ingresos en el calendario, pero es definitivamente más cómodo para analizar la retención y el churn de las cohortes y obtener conclusiones sobre su comportamiento. Elige el modo que prefieras o usa ambos para obtener más conclusiones e ideas. ## Cómo construye Adapty las cohortes \{#how-adapty-builds-cohorts\} Veamos con el ejemplo de cohortes por renovaciones cómo se forma la tabla. Para construir cohortes, usamos dos medidas: instalaciones de la app y transacciones (compras). Cada fila de una cohorte representa un intervalo de tiempo concreto: desde un día hasta un año. Cada fila comienza con el número de usuarios que instalaron la app durante ese intervalo y activaron una suscripción o realizaron una compra de producto de por vida o sin suscripción. Cada columna siguiente en la fila muestra el número de usuarios que renovaron una suscripción hasta ese período. M3 significa mes 3 e indica que los suscriptores tuvieron 3 renovaciones consecutivas hasta ese punto; W7 significa semana 7, e Y2 significa año 2. A veces puedes ver P2 en las cohortes. P significa período de suscripción. Adapty lo muestra en lugar de W/M/Y cuando hay varios productos con diferentes períodos de renovación presentes en la misma cohorte. Usamos colores con gradiente para resaltar las diferencias en los valores de las cohortes. Los números más grandes tienen colores más saturados. En la imagen a continuación puedes ver una cohorte típica. 1. Esta cohorte muestra los datos solo para productos semanales (marca #1). 2. No excluye los ingresos y muestra el revenue como valores absolutos (marca #2). 3. El período de tiempo con el que trabajamos son los últimos 6 meses y la longitud de la cohorte es de 1 mes (marca #3). 4. La fila **Total** (marca #4) muestra el valor acumulado para cada período. $442K en la primera celda de la fila **Total** acumula el revenue del primer período (activación de suscripción) de todos los meses (nov., dic. y así sucesivamente) hasta el final del período. La celda Total muestra el número de clientes que instalaron la app durante todo el período. 5. La primera columna de la fila Nov 2023 (marca #5) muestra el revenue del primer período (activación de suscripción) de $37,7K de los clientes que instalaron la app en nov. 2023. El número de clientes que instalaron la app en nov. 2023, que es 95.129, se muestra en la columna de encabezado. La segunda columna de la fila Nov 2023 muestra el revenue de la semana 2 (suscripciones renovadas a la 2.ª semana) de $8,77K de los que instalaron la app en nov. 2023. 6. En la tabla puedes ver el Revenue Total, ARPU, ARPPU y ARPAS (marca #6). Puedes leer más sobre ellos un poco más adelante en este artículo. 7. Puedes configurar las columnas en la parte derecha de la tabla usando el campo desplegable **Columns** (marca #7). 8. Encima de la tabla a la derecha (marca #8), también hay un campo desplegable para calcular las comisiones de las stores y los cálculos de impuestos para los análisis de cohortes específicos. Puedes aprender cómo Adapty calcula las comisiones de las stores e impuestos en [este artículo](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue). Tras elegir la opción correspondiente en el desplegable, los datos de revenue se recalcularán en función de ella. 9. En el lado derecho de la tabla, puedes ver el revenue predicho (Predicted Revenue) y el valor de vida predicho (Predicted LTV) (marca #9). El campo **Predicted Revenue** estima el revenue total generado por una cohorte de suscriptores dentro de un período de tiempo específico, mientras que el campo **Predicted LTV** representa el valor anticipado de cada usuario en la cohorte. Puedes pasar el cursor por cualquier celda de la cohorte para ver las métricas detalladas de ese período. Las celdas con líneas oblicuas en el fondo son períodos que aún no han terminado, por lo que los valores en ellas pueden incrementarse. ## Duración de la cohorte y período de tiempo \{#cohort-length-and-time-frame\} --- no_index: true --- Dos ajustes de tiempo controlan lo que muestra la tabla: - **Time frame** — el rango de fechas. Se configura en el calendario que hay encima de la tabla. - **Cohort length** — el tamaño de cada fila: día, semana, mes, trimestre o año. Con una longitud mensual, cada fila cubre un mes de instalaciones. Los dos funcionan de forma independiente. Por ejemplo: un time frame de 6 meses más una cohort length mensual te da una tabla con 6 filas. Un time frame de 1 año más una cohort length semanal te da 52 filas. ## Filtros, métricas, segmentos de cohorte y exportación en CSV \{#filters-metrics-cohort-segments-and-export-in-csv\} :::link Artículo principal: [Controles de análisis](controls-filters-grouping-compare-proceeds) ::: Por defecto, Adapty construye cohortes usando datos de todas las compras. Puedes filtrar por duración del producto, productos específicos, país, store, paywall, segmento y datos de atribución. A la derecha del panel de control, hay un botón para exportar los datos de la cohorte a CSV. Luego puedes abrirlo en Excel, Google Sheets o importarlo a tu propio sistema de análisis. Hay 6 métricas que se pueden mostrar en las cohortes: Subscriptions, Payers, Revenue, ARPU, ARPPU y ARPAS. Puedes mostrarlas como valores absolutos o como cambio relativo desde el inicio de la cohorte. ## Subscriptions, payers, revenue total, ARPU, ARPPU y ARPAS \{#subscriptions-payers-total-revenue-arpu-arppu-and-arpas\} **Subscriptions** es el recuento total de suscripciones activas, compras de por vida y compras sin suscripción realizadas por una cohorte dentro de un período de tiempo seleccionado. Monitorizar esta métrica te ayuda a entender el comportamiento de los clientes y la efectividad de tus ofertas. Esta información te permite refinar tu estrategia de producto, adaptar los esfuerzos de marketing y optimizar los flujos de ingresos. **Payers** es el número total de usuarios que realizaron una compra dentro de una cohorte. Te ayuda a entender cuántos usuarios únicos contribuyen a tu revenue. Para apps con una cantidad significativa de compras que no son suscripciones, esta métrica puede destacar el alcance real de tus ofertas de productos, mostrando si una amplia base de usuarios está realizando compras o si el revenue lo impulsa un grupo más pequeño de compradores recurrentes. Entender el número de payers ayuda a evaluar el engagement de los clientes, planificar marketing dirigido y optimizar las estrategias de revenue. **Total revenue** se acumula para una cohorte dentro de un período de tiempo seleccionado (25 nov. 2022 — 24 may. 2023). Te ayuda a entender cuánto dinero recopilaste de los usuarios de una cohorte específica y a calcular el ROAS. Por ejemplo, si el gasto publicitario de septiembre de 2022 fue de $10.000 y los ingresos totales de la cohorte de septiembre de 2022 son de $30.000, ROAS=3:1. **ARPU** es el revenue promedio por usuario. Se calcula como revenue total / número de usuarios únicos. $60.000 de revenue / 5.000 usuarios = $12 de ARPU. Es útil comparar este valor con el coste por instalación (CPI) para entender la efectividad de tus campañas de marketing. **ARPPU** es el revenue promedio por usuario de pago. Se calcula como revenue total / número de usuarios únicos de pago. $60.000 de revenue / 1.000 usuarios de pago = $60 de ARPPU. Te ayuda a entender cuánto dinero te aporta de media un cliente de pago. **ARPAS** es el revenue promedio por suscriptor activo. Se calcula como revenue total / número de suscriptores activos. Por suscriptores entendemos a quienes activaron un período de prueba o una suscripción. $60.000 de revenue / 1.500 suscriptores = $40 de ARPAS. ## Comisiones e impuestos \{#commission-fees-and-taxes\} Un aspecto importante del cálculo del revenue en cohortes es la inclusión de las comisiones de las stores y los impuestos (que pueden variar según el país de la cuenta de la store del usuario). Adapty actualmente admite el cálculo de comisiones e impuestos tanto para App Store como para Play Store en el análisis de cohortes. Para más detalles sobre cómo Adapty calcula los impuestos y comisiones en sus análisis, consulta nuestra [documentación](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue). ## Revenue vs Proceeds \{#revenue-vs-proceeds\} Tanto Revenue como Proceeds son métricas monetarias. Puedes pensar en Revenue como ingresos brutos y en Proceeds como ingresos netos. Revenue no tiene en cuenta las comisiones de App Store / Play Store, mientras que Proceeds sí. Por eso, Proceeds siempre es menor que Revenue. La comisión real deducida varía en función de múltiples factores, como la elegibilidad para programas como el [Small Business Program](app-store-small-business-program) (15%), tarifas reducidas para suscripciones a largo plazo (15% tras un año de renovación), tarifas específicas por país y tarifas estándar (hasta el 30%). Adapty determina automáticamente la tasa de comisión aplicable para cada transacción que realizan tus clientes y calcula Proceeds en función de ella. Para más información sobre cómo se determinan las tasas de comisión, consulta la documentación sobre [comisiones de la store e impuestos](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue). ## Predicción: Revenue y LTV \{#prediction-revenue-and-ltv\} **El revenue predicho** es el revenue total estimado que se espera que genere una cohorte de suscriptores de pago dentro del período seleccionado tras la creación de la cohorte. Se calcula multiplicando el LTV predicho de la cohorte por el número predicho de usuarios de pago dentro de la cohorte. Por ejemplo, si el LTV predicho es de $50 y hay 100 usuarios de pago en una cohorte, el Predicted Revenue sería de $5.000. **El LTV predicho** es el valor de vida estimado por suscriptor de pago, que representa el revenue promedio que se espera que genere cada suscriptor de pago dentro del período seleccionado tras la creación de la cohorte. Estas predicciones se basan en patrones históricos de retención de cohortes, usando los propios datos de la app cuando hay suficiente historial disponible y promedios entre apps en caso contrario. Para documentación detallada sobre el modelo de predicción de Adapty, consulta nuestra [documentación de Predicción](predicted-ltv-and-revenue). Las cohortes de Adapty proporcionan información detallada sobre el comportamiento de los usuarios y el rendimiento financiero dentro de tu app. Al analizar las cohortes por renovaciones o por días, puedes determinar cuándo se vuelven rentables, hacer seguimiento del revenue, calcular el revenue promedio por usuario y entender el tiempo que se tarda en recuperar el gasto publicitario. Con filtros, métricas y opciones de exportación personalizables, Adapty te permite tomar decisiones basadas en datos y optimizar las estrategias de captación de usuarios y monetización para lograr el máximo éxito de tu app. --- # File: analytics-funnels --- --- title: "Análisis de embudos" description: "Comprende los embudos de análisis en Adapty para monitorear el comportamiento de los usuarios y mejorar las conversiones." --- Los embudos de Adapty están diseñados para ayudarte con este tipo de preguntas: 1. ¿Qué porcentaje de las instalaciones se convierte en clientes de pago? 2. ¿Qué parte de quienes probaron el producto se convirtió en usuarios fieles? 3. ¿En qué pasos hay mayor abandono y hay que prestar más atención? 4. ¿Por qué los clientes dejan de pagar? Con un gráfico de embudo, también puedes encontrar más información sobre el comportamiento de los usuarios configurando filtros y grupos. Los embudos trabajan con los datos que recopilamos a través del SDK y las notificaciones de la store, y no requieren ninguna configuración adicional por tu parte. :::note Los embudos reflejan los datos de instalación según tu definición de instalación en [App Settings](general#4-installs-definition-for-analytics). ::: ## El gráfico de embudo paso a paso \{#funnel-chart-step-by-step\} Repasemos los elementos de un embudo para entender cómo leer el recorrido del usuario en el gráfico. ### Instalaciones \{#installs\} La 1.ª columna (1) es el número de instalaciones. Se muestra como valor absoluto (2) del total de instalaciones (no usuarios únicos) y también como 100%, el número de entrada más alto para el cálculo relativo de las conversiones posteriores. Si un usuario elimina la app y la vuelve a instalar, se contarán dos instalaciones por separado. El área gris adyacente muestra los parámetros de transición entre pasos. El porcentaje de conversión al siguiente paso (Paywall mostrado) aparece en una etiqueta (3). El porcentaje de abandono y el valor absoluto de la pérdida se muestran debajo (4). ### Paywall mostrado \{#paywall-displayed\} La 2.ª columna (5) muestra el número de usuarios de la app que vieron un paywall al menos una vez (6). Solo se tienen en cuenta los usuarios cuya instalación ocurrió en el período seleccionado. Si un usuario ve un paywall en el período seleccionado pero la fecha de instalación está fuera del rango, esa vista no se contabiliza. También aparece el porcentaje de dichas vistas respecto al 1.er paso (7). Puedes observar que este porcentaje es igual al de la etiqueta gris (3) del 1.er paso. Esta igualdad solo se da en estos primeros pasos. Recopilamos los datos de este paso desde todos tus paywalls que usan el método `logShowPaywall()`. Asegúrate de enviar cada vista de paywall a Adapty con este método tal como se describe en la [documentación](present-remote-config-paywalls#track-paywall-view-events). El área gris junto a la 2.ª columna indica la transición. El porcentaje de conversión al siguiente paso (Período de prueba) aparece en una etiqueta (8). El porcentaje de abandono y el valor absoluto de los clientes perdidos tras el paywall se muestran debajo (9). ### Períodos de prueba \{#trials\} La 3.ª columna (10) muestra el número de períodos de prueba activados en los paywalls por los clientes que instalaron la app dentro del período seleccionado (11). Si el filtro está configurado para productos sin período de prueba, este valor es cero y la columna aparece vacía. También aparece el porcentaje de períodos de prueba respecto al 1.er paso, que muestra la conversión de instalaciones a períodos de prueba (12). Puedes notar que este porcentaje no coincide ahora con la etiqueta gris (8) de la conversión del paso anterior. Esto se debe a que comparamos el valor actual con el 1.er paso en la parte superior del gráfico, y con el paso anterior en las etiquetas grises. Por lo tanto, el área gris junto a la 3.ª columna muestra el porcentaje de conversión al siguiente paso (Pago), que aparece en una etiqueta (13). El porcentaje de abandono y el valor absoluto de los clientes perdidos durante el período de prueba se muestran debajo (14). ### Suscripciones y renovaciones \{#subscriptions-and-renewals\} La 4.ª columna muestra el número de suscripciones activadas (15). Para productos sin período de prueba, este número incluye las suscripciones directas desde un paywall. Para productos con período de prueba, contiene el número de períodos de prueba convertidos en suscripciones de pago. Si tienes ambos tipos de productos, con y sin período de prueba, se mostrará la suma de ambos. El porcentaje en la parte superior muestra la conversión desde las instalaciones (16). El porcentaje en la etiqueta gris muestra la conversión al siguiente paso (renovación al 2.º período) (17). El porcentaje y el valor absoluto del abandono antes de la renovación al 2.º período se muestran bajo la conversión (18). Este paso inicia una secuencia de pasos con una estructura similar. Tras la 2.ª renovación viene la 3.ª, luego la 4.ª, etc. Si hay suficientes datos en el historial de tu app, puedes ver decenas de períodos usando el desplazamiento horizontal. La lógica para estos pasos es la misma: - porcentaje desde las instalaciones en la parte superior, - porcentaje desde el paso anterior en la parte inferior, - el número absoluto de renovaciones en la parte superior, - el número absoluto de abandonos en la parte inferior, - un popup con los motivos de abandono al pasar el cursor. ### Motivos de abandono \{#churn-reasons\} Adapty detalla las estadísticas de *abandono* desde la etapa de período de prueba en adelante. Cada usuario que entró en una etapa pero no pasó a la siguiente cuenta como un caso de abandono. * Si un evento específico (por ejemplo, la expiración de un período de prueba o un problema de facturación) causó la falta de conversión, Adapty muestra el motivo. * El estado **unknown** es un estado temporal. Indica que el usuario aún no ha encontrado el evento que le permite pasar a la siguiente etapa. En la etapa de período de prueba, esto suele significar que el período de prueba aún no ha finalizado. Esto ocurre con frecuencia al ver los embudos para rangos de fechas cortos o días individuales, ya que los períodos de prueba necesitan tiempo para resolverse. Adapty actualizará la información una vez que el usuario convierta o cancele el período de prueba. ### Vista de tabla, filtros y exportación CSV \{#table-view-filters-and-csv-export\} El gráfico de embudo se complementa con datos en una tabla para facilitar el trabajo con los números. Esta tabla sigue el enfoque del embudo con algunas diferencias. Hay columnas que muestran datos de todos los pasos, excepto el del 1.er pago. En su lugar, aparecen dos columnas separadas: Instalación → Pago y Período de prueba → Pago. Muestran el momento clave de conversión en el que un usuario gratuito se convierte en de pago. Puede parecer que hay una división por tipo de producto: la columna Instalación → Pago muestra solo productos sin período de prueba, mientras que la columna Período de prueba → Pago contiene solo productos con período de prueba. Pero no funciona exactamente así, ya que también consideramos a aquellos usuarios cuyo período de prueba ha expirado y compran un producto con período de prueba como si no lo tuviera. Al profundizar en los números, encontrarás potentes herramientas de filtrado para nuevas hipótesis. Establece condiciones en distintas dimensiones y recoge información real basada en datos. Varía según: 1. Tipo de producto: precio, duración, etc. 2. Rango de tiempo. 3. Segmentación por país. 4. Atribución de tráfico. 5. Store. Selecciona Número absoluto (#), Porcentaje relativo (%) o ambos para ver solo los datos que necesitas. Por último, a la derecha del panel de control hay un botón para exportar los datos del embudo a CSV. Después puedes abrirlo en Excel o en Google Sheets, o importarlo en tu propio sistema de análisis. :::important Notifica a Adapty si tu app está inscrita en un programa de comisión reducida. Para garantizar los cálculos correctos, especifica el estado de tu [Small Business Program](app-store-small-business-program) y tu [Reduced Service Fee program](google-reduced-service-fee) en la [configuración de tu app](general). ::: --- # File: analytics-retention --- --- title: "Análisis de retención" description: "Comprende las analíticas de retención de usuarios y optimiza tu estrategia de suscripciones." --- Los gráficos de retención pueden ayudarte a responder las siguientes preguntas: 1. ¿Cómo retiene tu app a los clientes de un período a otro? 2. ¿Qué productos son más atractivos y generan mayor fidelización? 3. ¿Qué grupos de usuarios son más leales? 4. ¿Qué nivel de retención puede usarse como referencia para el crecimiento? 5. Y, por supuesto, cómo ahorrar dinero invirtiendo en la audiencia ya captada en lugar de captar nuevos usuarios. Configura filtros y grupos para obtener información valiosa sobre el comportamiento de los usuarios. La retención se calcula con los datos que recopilamos a través del SDK y las notificaciones de la store, sin necesidad de ninguna configuración adicional por tu parte. ### ¿Cómo calculamos la retención? \{#how-do-we-calculate-retention\} En el gráfico de retención puedes ver cómo varía el número de usuarios según el paso en el que se encuentran: prueba (si la casilla "show trials" está marcada), el 1.er pago, el 2.º pago, etc. Veamos qué usuarios se contabilizan al seleccionar un rango de fechas para el gráfico de retención. Por ejemplo, si seleccionas los últimos 3 meses en el calendario y la casilla "show trials" no está marcada, solo se cuentan los usuarios que han realizado su 1.ª suscripción durante ese período. Si la casilla "show trials" está marcada y se seleccionan los últimos 3 meses, se cuentan todos los usuarios que han iniciado una prueba en ese período. Para estos suscriptores, mostramos la retención absoluta en el paso N como el número de usuarios que han realizado el pago N, y calculamos la retención relativa en ese paso como la proporción entre la cantidad absoluta del pago N y el total de suscripciones (o pruebas) del rango de tiempo seleccionado. :::info La retención cambia de forma retroactiva Independientemente de cuándo consultes el gráfico, el número base (100%) se mantiene igual para el período seleccionado. Sin embargo, la retención hacia el siguiente período puede crecer con el tiempo. Por ejemplo, para una suscripción mensual, si se realizan 20 primeras compras entre el 1 y el 31 de diciembre, se espera que la retención al segundo período crezca a lo largo de enero (e incluso después) a medida que los usuarios vayan entrando al siguiente período de suscripción, ya sea a tiempo o con algo de retraso por distintos motivos (por ejemplo, el período de gracia). ::: ### Oportunidades de retención \{#retention-opportunities\} Veamos cómo sacar más partido a la función de retención de Adapty. Más allá de la pasión por los números, lo que realmente importa es el valor de negocio que se obtiene al aplicar los resultados analíticos, así que conviene tener claros los objetivos desde el principio. Explorando las funciones del gráfico en profundidad, resulta útil entender el impacto que pueden tener estos datos. Así que veamos juntos el POR QUÉ y el CÓMO. 1 - trabajar con la audiencia. En primer lugar, la retención tiene que ver con el público objetivo, sus preferencias y si tu producto cumple o no sus expectativas a lo largo del ciclo de consumo. Si alguna vez te has preguntado cómo medir la relación clave de tu negocio que genera ingresos, la retención es la respuesta. Esta métrica es valiosa porque venderle a un cliente existente suele ser más barato que hacerlo a un desconocido, por dos motivos: requiere menos esfuerzo y el ticket promedio es más alto. Por eso, cuando la retención baja, invertir en la fidelización de tus suscriptores puede ser una buena decisión. 2 - trabajar con el producto. El segundo motivo del POR QUÉ es que los gráficos de retención muestran el ciclo de vida real de consumo de tu producto y permiten hacer previsiones a largo plazo. Si quieres mejorar, ajusta lo que determina la entrega del producto para modificar su ciclo de vida y vuelve a hacer la previsión para acercarte más a tus objetivos de negocio. Estos ajustes pueden formar parte de una visión estratégica que funcione de la mano con la rutina de previsión. Y sí, este proceso nunca termina, porque todos corremos rápido para mantenernos en el mismo lugar en un entorno que cambia constantemente. 3 - trabajar con el mercado. Ir más rápido que los principales competidores está bien, pero a veces salirse de la carrera habitual puede aportar mayores beneficios. Al analizar el comportamiento de los usuarios en distintos países y stores, las particularidades locales pueden revelar insights sorprendentes y nuevas oportunidades para el negocio. El contexto cultural y de mercado puede analizarse desde la perspectiva de la retención para utilizarlo después en la segmentación y el desarrollo futuro. Por ejemplo, es posible descubrir mercados sin explotar en ciertas regiones y crecer allí más rápido. El uso de los datos de retención no se limita, por supuesto, a esta interpretación básica, pero puede ser un buen punto de partida si quieres obtener valor real de forma rápida. ### Curvas, vista de tabla, filtros y exportación a CSV \{#curves-table-view-filters-and-csv-export\} Ahora que estamos alineados en cuanto a los objetivos de la retención y las formas básicas de interpretación, repasemos las herramientas que lo hacen todo más sencillo. El núcleo de la función de retención en Adapty es el gráfico. Muestra cómo varía el nivel de retención según los pasos del ciclo de vida del cliente. Los pasos se muestran en el eje horizontal: Trial, Paid (1.ª suscripción), P2 (2.ª suscripción), P3, P4, etc. Ten en cuenta que el eje comienza con el paso Trial solo cuando la casilla "Show trials" está seleccionada. Para el cálculo de datos, esta casilla funciona de la siguiente manera. Cuando "Show trials" está seleccionada y el eje comienza con el paso Trial, solo se muestran los escenarios que incluyen pruebas; no se muestran transacciones directas desde instalaciones, y el paso Paid solo incluye transacciones provenientes de pruebas. Cuando "Show trials" no está seleccionada y el eje comienza con el paso Paid, ese primer paso incluye todas las primeras transacciones, tanto las provenientes de pruebas como las directas desde instalaciones. Al pasar el cursor sobre el gráfico, se muestra un popover con un resumen de los datos. Y si pasas el cursor sobre una columna de la tabla inferior, también verás un popover con los datos relevantes en el gráfico. La tabla contiene los mismos grupos y filtros seleccionados para el gráfico. Combina filtros y agrupaciones libremente para análisis avanzados. Obtén insights reales basados en datos. Varía: 1. Tipo de producto. 2. Duración. 3. Rango de tiempo. 4. País. 5. Atribución de tráfico. 6. Store. Usa los controles #Absolute y %Relative para ver los datos que necesitas. Por último, a la derecha del panel de control hay un botón para exportar los datos del embudo a CSV. Luego puedes abrirlo en Excel, Google Sheets o importarlo en tu propio sistema analítico para continuar el análisis y las previsiones en el entorno que prefieras. :::warning Asegúrate de indicar que tu app está incluida en el Small Business Program en [Adapty General Settings](https://app.adapty.io/settings/general). ::: --- # File: analytics-conversion --- --- title: "Análisis de conversión" description: "Mide las tasas de conversión de suscripciones con las herramientas de análisis de Adapty." --- Mientras que los embudos ofrecen una visión general de alto nivel y la retención se centra en la fidelización, el análisis de conversión está diseñado para ayudarte a evaluar la efectividad en cada paso clave del recorrido del usuario, a lo largo del tiempo. Las conversiones responden a las siguientes preguntas: 1. ¿Cómo cambian las conversiones de la app con el tiempo? ¿Existen tendencias estacionales? 2. ¿Cómo se ven afectadas las conversiones en el momento de actividades de marketing u otras circunstancias nuevas? 3. ¿Cómo responden los usuarios de diferentes regiones a las actualizaciones de tu app? 4. ¿Qué tipos de producto convierten mejor a lo largo del tiempo? La conversión se calcula con los datos que recopilamos a través del SDK de Adapty y las notificaciones del store, y no requiere ninguna configuración adicional de tu parte. ## Controles principales y gráficos \{#main-controls-and-charts\} Aunque los ingresos suelen ser la métrica de referencia para medir el éxito, son solo una parte del panorama general. Entender cómo evoluciona tu negocio a lo largo del tiempo, considerando diferentes comportamientos de usuario y etapas del ciclo de vida, es igual de importante. Ahí es donde entra el análisis de conversiones. Puedes encontrar información más valiosa sobre el comportamiento de los usuarios configurando filtros y grupos. Para identificar y analizar tendencias, controla cómo evolucionan tus conversiones de forma diaria, mensual o anual. En el lado izquierdo del gráfico encontrarás el control de pasos de conversión. Esto te permite elegir qué conversiones específicas rastrear, como Instalación → Prueba, Prueba → Pago, o Pago → Renovación. Cada métrica de conversión sigue esta lógica: - Sea **X** el número de usuarios que entraron al estado inicial en una fecha seleccionada (p. ej., instalaciones). - Sea **Y** el número de esos usuarios que finalmente alcanzaron el estado objetivo (p. ej., inicio de prueba). - La tasa de conversión se calcula como: **Conversión = (Y / X) × 100%** :::note La fecha que aparece en el gráfico corresponde a cuándo los usuarios entraron al estado inicial (X), es decir, el momento en que se volvieron elegibles para convertir. ::: A continuación encontrarás la explicación de cada conversión, con un ejemplo de referencia. ### Instalación -> Pago \{#install---paid\} Esta métrica muestra qué porcentaje de usuarios que instalaron la app en una fecha determinada terminaron comprando su primera suscripción. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de instalaciones en una fecha seleccionada (igual para todos los productos, ya que no se elige ningún producto en el momento de la instalación). - **Y** = número de esos usuarios que finalmente compraron su primera suscripción (con o sin prueba). **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 instalaciones. - Para el 8 de enero, 20 de esos usuarios se habían suscrito. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más del grupo del 1 de enero habían comprado una suscripción. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto significa que el 50% de los usuarios que instalaron la app el 1 de enero acabaron convirtiéndose a una suscripción de pago, hasta el momento actual. </details> ### Instalación -> Prueba \{#install---trial\} Esta métrica muestra el porcentaje de usuarios que instalaron la app en una fecha determinada y finalmente iniciaron una prueba. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de instalaciones en una fecha seleccionada (igual para todos los productos, ya que no se elige ningún producto en el momento de la instalación). - **Y** = número de esos usuarios que finalmente activaron una prueba, en cualquier momento. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 instalaciones. - Para el 8 de enero, 20 de esos usuarios habían iniciado una prueba. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más del grupo del 1 de enero habían iniciado una prueba. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto significa que el 50% de los usuarios que instalaron la app el 1 de enero finalmente iniciaron una prueba, hasta el momento actual. </details> ### Vista de paywall -> Prueba \{#paywall-view---trial\} Esta métrica rastrea cuántos usuarios iniciaron la prueba después de ver un paywall. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de usuarios que vieron un paywall en una fecha seleccionada. - **Y** = número de usuarios que iniciaron la prueba en cualquier momento posterior. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 vistas del paywall. - Para el 8 de enero, 20 de esos usuarios habían iniciado la prueba. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más habían iniciado la prueba. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto muestra que el 50% de los usuarios que vieron un paywall el 1 de enero iniciaron la prueba, hasta el momento actual. </details> ### Vista de paywall -> Pago \{#paywall-view---paid\} Esta métrica rastrea cuántos usuarios realizaron una compra después de ver un paywall. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de usuarios que vieron un paywall en una fecha seleccionada. - **Y** = número de usuarios que realizaron una compra en cualquier momento posterior. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 vistas del paywall. - Para el 8 de enero, 20 de esos usuarios habían realizado una compra. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más habían realizado una compra. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto muestra que el 50% de los usuarios que vieron un paywall el 1 de enero realizaron una compra, hasta el momento actual. </details> ### Prueba -> Pago \{#trial---paid\} Esta métrica muestra el porcentaje de usuarios que iniciaron una prueba en una fecha determinada y más tarde compraron su primera suscripción. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de pruebas iniciadas en una fecha seleccionada. - **Y** = número de esos usuarios que finalmente compraron una suscripción tras su prueba. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero se iniciaron 100 pruebas. - Para el 8 de enero, 20 de esos usuarios se habían suscrito. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más del grupo del 1 de enero se habían suscrito. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto significa que el 50% de los usuarios que iniciaron una prueba el 1 de enero acabaron convirtiéndose a una suscripción de pago, hasta el momento actual. </details> ### Pago -> 2.º Período \{#paid---2nd-period\} Esta métrica muestra el porcentaje de usuarios que renovaron su suscripción tras el primer pago. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de primeras suscripciones en una fecha seleccionada. - **Y** = número de usuarios que renovaron para un segundo período, en cualquier momento posterior (normalmente tras un ciclo de suscripción; incluye renovaciones durante el período de gracia). - **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 primeras suscripciones. - Para el 8 de enero, 20 de ellas habían renovado. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más de ese grupo habían renovado. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto muestra que el 50% de los usuarios que realizaron su primer pago de suscripción el 1 de enero renovaron para un segundo período, hasta el momento actual. </details> ### 2.º Período -> 3.er Período \{#2nd-period---3rd-period\} Esta métrica rastrea cuántos usuarios renovaron de nuevo tras su segundo período de suscripción. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de suscripciones en segundo período en una fecha seleccionada. - **Y** = número de usuarios que renovaron para un tercer período, en cualquier momento posterior (normalmente tras otro ciclo de facturación; incluye renovaciones durante el período de gracia). **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 suscripciones en segundo período. - Para el 8 de enero, 20 de esos usuarios habían renovado. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más habían renovado. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto muestra que el 50% de los usuarios que entraron en su segundo período de suscripción el 1 de enero renovaron para un tercero, hasta el momento actual. </details> ### 3.er Período -> 4.º Período \{#3rd-period---4th-period\} Esta métrica muestra el porcentaje de usuarios que renovaron tras su tercer período de suscripción. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de suscripciones en tercer período en una fecha seleccionada. - **Y** = número de usuarios que renovaron para un cuarto período en cualquier momento posterior (normalmente tras un ciclo de facturación; incluye renovaciones durante el período de gracia). **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 suscripciones en tercer período. - Para el 8 de enero, 20 usuarios habían renovado. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más renovaron. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto significa que el 50% de los usuarios que entraron en su tercer período de suscripción el 1 de enero renovaron para un cuarto, hasta el momento actual. </details> ### 4.º Período -> 5.º Período \{#4th-period---5th-period\} Esta métrica muestra el porcentaje de usuarios que renovaron tras su cuarto período de suscripción. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de suscripciones en cuarto período en una fecha seleccionada. - **Y** = número de usuarios que renovaron para un quinto período en cualquier momento posterior (normalmente tras un ciclo de facturación; incluye renovaciones durante el período de gracia). **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 suscripciones en cuarto período. - Para el 8 de enero, 20 usuarios habían renovado. - El 8 de enero, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de febrero, 30 usuarios más renovaron. - El 1 de febrero, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto significa que el 50% de los usuarios que entraron en su cuarto período de suscripción el 1 de enero renovaron para un quinto, hasta el momento actual. </details> ### 6 Meses + \{#6-months-\} Esta métrica muestra el porcentaje de usuarios que permanecieron suscritos durante más de 6 meses desde su primera suscripción. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de primeras suscripciones en una fecha seleccionada. - **Y** = número de esos usuarios que renovaron al menos una vez después de 6 meses desde la fecha de suscripción original. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero hubo 100 primeras suscripciones. - Para la primera semana de julio, 20 de ellas habían renovado (p. ej., en su 25.ª suscripción semanal). - El 8 de julio, la conversión del 1 de enero = (20 / 100) × 100% = 20% - Para el 1 de agosto, 30 más habían renovado después de 6 meses. - El 1 de agosto, la conversión del 1 de enero = ((20 + 30) / 100) × 100% = 50% Esto significa que el 50% de los usuarios que se suscribieron el 1 de enero permanecieron suscritos más de 6 meses a fecha del 1 de agosto. </details> ### 1 Año + \{#1-year-\} Esta métrica muestra el porcentaje de usuarios que permanecieron suscritos durante más de 12 meses desde su primera suscripción. <details> <summary>Cómo funciona</summary> **Sea**: - **X** = número de primeras suscripciones en una fecha seleccionada. - **Y** = número de esos usuarios que renovaron al menos una vez después de 12 meses desde la fecha de suscripción original. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero de 2021 hubo 100 primeras suscripciones. - Para la primera semana de enero de 2022, 20 habían renovado. - El 8 de enero de 2022, la conversión = (20 / 100) × 100% = 20% - Para el 1 de febrero de 2022, 30 más habían renovado después de 12 meses. - El 1 de febrero de 2022, la conversión = ((20 + 30) / 100) × 100% = 50% Esto significa que el 50% de los usuarios que se suscribieron el 1 de enero de 2021 permanecieron activos durante más de un año. </details> ### 2 Años + \{#2-years-\} Esta métrica muestra el porcentaje de usuarios que permanecieron suscritos durante más de 24 meses desde su primera fecha de pago. <details> <summary>Cómo funciona</summary> **Sea**: - X = número de primeras suscripciones en una fecha seleccionada. - Y = número de esos usuarios que renovaron al menos una vez después de 24 meses desde la fecha de suscripción original. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero de 2020 hubo 100 primeras suscripciones. - Para la primera semana de enero de 2022, 20 de ellas habían renovado. - El 8 de enero de 2022, la conversión = (20 / 100) × 100% = 20% - Para el 1 de febrero de 2022, 30 más habían renovado después de 2 años. - El 1 de febrero de 2022, la conversión = ((20 + 30) / 100) × 100% = 50% Esto significa que el 50% de los usuarios que se suscribieron el 1 de enero de 2020 seguían activos después de 2 años, a fecha del 1 de febrero de 2022. </details> ### Período de gracia -> Pago \{#grace-period---paid\} Esta métrica muestra el porcentaje de usuarios que entraron en un [período de gracia de suscripción](grace-period) y resolvieron el problema *antes* de que finalizara dicho período. <details> <summary>Cómo funciona</summary> **Sea**: - X = número de suscriptores que entraron en el período de gracia. - Y = número de esos usuarios que renovaron la suscripción antes de que expirase el período de gracia. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero de 2025, la suscripción de 100 personas no pudo renovarse automáticamente. Entraron en un período de gracia de 16 días, con vencimiento el 17 de enero. - 50 personas actualizaron su información de pago entre el 1 y el 17 de enero, y su suscripción se renovó con éxito. - El 17 de enero de 2025, la conversión = (50 / 100) × 100% = 50% </details> ### Problema de facturación -> Pago \{#billing-issue---paid\} Esta métrica muestra el porcentaje de usuarios que tuvieron un [problema de facturación](/billing-issue) y reanudaron el pago antes de que finalizara el ciclo de facturación. <details> <summary>Cómo funciona</summary> **Sea**: - X = número de suscriptores que tuvieron un problema de facturación. - Y = número de esos usuarios que renovaron su suscripción en el tiempo transcurrido entre el problema de facturación y el final del ciclo de facturación. **Fórmula**: Conversión = (Y / X) × 100% **Ejemplo**: - El 1 de enero, 100 suscriptores tuvieron un problema de facturación cuando su suscripción no pudo renovarse automáticamente. - Nota: si hay un período de gracia habilitado, el estado de problema de facturación comienza solo tras el vencimiento del período de gracia. En este ejemplo, se asume que el período de gracia finalizó el 1 de enero. - Para el 8 de enero, 10 de esos usuarios habían resuelto el problema de pago y renovado. - El 8 de enero, la conversión del 1 de enero = (10 / 100) × 100% = 10% - Para el 31 de enero (fin del ciclo de facturación), 10 usuarios más habían renovado. - El 31 de enero, la conversión del 1 de enero = ((10 + 10) / 100) × 100% = 20% Esto muestra que el 20% de los usuarios que entraron en estado de problema de facturación el 1 de enero resolvieron el problema y renovaron antes del final de su ciclo de facturación. </details> ## Agrupación y rangos de fechas \{#grouping-and-time-ranges\} El objeto de análisis cuando se selecciona la conversión es el gráfico. Muestra cómo cambia el porcentaje de conversión a lo largo del tiempo. Usa el selector de fechas para elegir opciones rápidas del período de tiempo. El gráfico suele contener varias curvas. Hasta cinco de ellas se seleccionan por defecto en la lista de agrupación, y puedes cambiar la selección marcando las casillas en el área a la derecha del gráfico. Cuando abres la página por primera vez, la duración del producto se selecciona como agrupación predeterminada. Luego, tu configuración se guarda en caché y la próxima vez verás el grupo que seleccionaste recientemente. Las siguientes agrupaciones están disponibles: - Producto - País - Store - Paywall - Duración - Atribución de marketing Si el rango de fechas elegido no es suficiente para mostrar resultados, puede aparecer una notificación que sugiere una fecha relevante y la opción de ajustar el rango automáticamente para hacerlo con un solo clic. ## Vista de tabla, filtros y exportación CSV \{#table-view-filters-and-csv-export\} La comparación de las curvas ofrece una imagen clara, y para sacar más partido usa la vista de tabla debajo del gráfico. La tabla está sincronizada con el gráfico, de modo que al pasar el cursor sobre una columna verás el pop-up asociado sobre las curvas. La agrupación mencionada anteriormente afecta tanto a los gráficos como a la tabla. Establece un filtro rápido por producto o usa otros más avanzados, como Producto, País, Store, Duración y Atribución. Sabemos que es importante poder trabajar con los números como prefieras. Por eso, a la derecha del panel de control hay un botón para exportar los datos del embudo a CSV. Puedes abrirlo en Excel o Google Sheets, o importarlo en tu propio sistema analítico para continuar el análisis y las previsiones en tu entorno preferido. :::important Notifica a Adapty si tu app está inscrita en un programa de comisión reducida. Para garantizar cálculos correctos, especifica el estado de tu [Small Business Program](app-store-small-business-program) y tu [programa de tarifa de servicio reducida](google-reduced-service-fee) en la [configuración de tu app](general). ::: --- # File: reports --- --- title: "Informes" description: "Genera informes detallados de suscripción en Adapty para analizar los ingresos de la app y el comportamiento de los usuarios." --- Recibe información puntual y relevante directamente en tu bandeja de entrada: ingresos, tasa de cancelación, suscriptores activos, pruebas activas y más, las mismas métricas disponibles en [Charts](charts). Estos informes pueden llegar de forma diaria, semanal o mensual y muestran la evolución comparando el período más reciente con el anterior. Los datos que enviamos en los informes se basan en lo que hayas configurado en tu página [**Overview**](https://app.adapty.io/overview): métricas, su orden, zona horaria de los informes y tipo de ingresos. Tienes la flexibilidad de elegir el nivel de detalle que prefieras para tus informes: resumen o por app. Un informe de resumen es un único correo con información agregada de todas tus apps (o del subconjunto que hayas seleccionado). Un informe por app, en cambio, solo contendrá los datos de una app específica. Te recomendamos activar los informes de resumen para todas las apps y los informes por app para las que hayas lanzado recientemente, las de alta prioridad o aquellas de las que seas directamente responsable. Independientemente del nivel de detalle elegido, los informes por correo se entregan a las 9 AM en tu zona horaria local: los diarios llegan cada día, los semanales los lunes y los mensuales el primer día del mes. Cada informe incluye los datos actuales junto con comparaciones respecto al período anterior (por ejemplo, el informe diario de hoy compara los datos de ayer con los del día anterior; el informe semanal de hoy compara los de la semana pasada con los de la anterior, etc.). Sea cual sea el informe que selecciones, recibirás la información más actualizada y precisa directamente en tu bandeja de entrada. ## Activar informes \{#enable-reports\} 1. Abre la sección [**Account**](https://app.adapty.io/account) en el menú superior de Adapty. 2. En la sección **Email reports**, elige los tipos de informes que deseas recibir: diarios, semanales y/o mensuales. 2. Personaliza cada tipo de informe seleccionando las apps correspondientes. Para ello, haz clic en el botón **Edit**. 3. En la ventana del informe, elige las apps que quieres incluir. 4. Por último, haz clic en el botón **Save changes** para aplicar tu selección. ## Configurar tu zona horaria \{#set-your-time-zone\} 1. Abre la sección [**Overview**](https://app.adapty.io/overview) en el menú principal de Adapty. 2. Haz clic en el botón **Edit** y elige tu zona horaria. 3. Haz clic en el botón **Done** para guardar. --- # File: discrepancies-and-troubleshooting --- --- title: "Solucionar discrepancias en los datos" description: "Encuentra la causa de las divergencias en los datos" --- Los usuarios de Adapty pueden encontrar **discrepancias** al comparar conjuntos de datos similares de diferentes fuentes. Esto puede ocurrir, en particular, al comparar: * Gráficos de Adapty con informes de la store * Gráficos de Adapty con gráficos de terceros * Distintos gráficos dentro de Adapty ## Algoritmo de solución de problemas \{#troubleshooting-algorithm\} La mayoría de las discrepancias entre Adapty y otras plataformas son esperadas y normales. Se producen porque **las distintas fuentes procesan los mismos datos de forma diferente**. En otros casos, indican un **problema con tu configuración de Adapty**. Si sospechas que tus datos varían de una plataforma a otra, lo mejor es [exportar los datos sin procesar](export-analytics-api-requests) y **comparar los archivos**. * Incluso las stores pueden tener problemas relacionados con el procesamiento y la presentación de datos. Accede a los **datos de transacciones sin procesar** de las stores para una comparación más precisa. * Al comparar Adapty con otra plataforma de analytics, usa los informes de transacciones de la store como fuente de verdad y punto de comparación. * Es más fácil identificar inconsistencias con un conjunto de datos limitado. Compara volúmenes pequeños de datos: céntrate en un producto específico y en un solo día. * Identifica si tu discrepancia proviene de una diferencia en los **precios** o en el **número de eventos**. Los problemas de precios se pueden corregir con una [actualización del producto](#product-pricing). Los problemas de eventos pueden indicar [problemas en el servidor](#issues-with-server-notifications-and-rtdn). * Consulta el [feed de eventos](event-feed) para monitorizar los eventos entrantes; puede que notes comportamientos inesperados. Una vez que identifiques dónde divergen los datos, puedes investigar las siguientes causas comunes: ## Problemas con las notificaciones del servidor y RTDN \{#issues-with-server-notifications-and-rtdn\} Adapty no recibe los datos de eventos necesarios si no configuraste correctamente las conexiones con la store. Esto afecta especialmente a los eventos que ocurren sin la participación directa del usuario: renovaciones de suscripciones, problemas de facturación, etc. Completa la configuración server-to-server lo antes posible ([App Store](enable-app-store-server-notifications) | [Play Store](enable-real-time-developer-notifications-rtdn)) y [espera](#data-delays) a que las stores establezcan la conexión. Puedes [subir manualmente](importing-historical-data-to-adapty) los datos faltantes de App Store Connect a Adapty. ## Datos faltantes \{#missing-data\} ### Usuarios con versiones desactualizadas de la app \{#users-with-out-of-date-app-versions\} Si algunos de tus usuarios ejecutan una versión más antigua de tu app sin el SDK de Adapty, Adapty no recibe sus datos. Por esta razón, las cifras de Adapty y de otras fuentes divergirán. ### Problemas de integración \{#integration-issues\} Algunas integraciones de Adapty (por ejemplo, Adjust o AppsFlyer) requieren código adicional en la aplicación para funcionar. Si configuras el dashboard de Adapty pero no actualizas tu aplicación, los datos necesarios no aparecerán en Adapty. ### Datos históricos faltantes \{#missing-historical-data\} Adapty no tiene acceso a los datos históricos de tu aplicación, a menos que los [importes manualmente](importing-historical-data-to-adapty). Si el [rango de fechas](controls-filters-grouping-compare-proceeds#set-the-date-range) de un gráfico comienza antes de que integraras Adapty y no importaste los datos históricos, sus valores diferirán de otras fuentes. ## Retrasos en los datos \{#data-delays\} Adapty busca proporcionar un análisis casi en tiempo real de la economía de tu aplicación. Se aplican las siguientes limitaciones y excepciones: * Cuando integras Adapty por primera vez, es posible que los datos no aparezcan de inmediato. * Cuando activas una integración con una plataforma de terceros, puede haber un retraso antes de que los datos se sincronicen completamente. * Una vez que Adapty recibe los datos de la store, tarda otros **15-30 minutos** en procesarlos y mostrarlos en la página de Analytics. * El intercambio de datos entre Adapty y terceros **no siempre es instantáneo** debido a la cantidad de variables implicadas. * Los cálculos de algunas métricas avanzadas (como las [predicciones de cohorte](predicted-ltv-and-revenue)) requieren una cantidad determinada de datos. Adapty solo realizará estos cálculos cuando haya recopilado suficientes datos. ## Hora y calendario \{#time-and-calendar\} #### Fechas y zonas horarias \{#dates-and-timezones\} Una de las razones más comunes de las discrepancias percibidas en los datos es una diferencia en la configuración de zona horaria. Adapty cuenta los días según la zona horaria `UTC`. Si otra plataforma usa una zona horaria diferente, los cálculos serán distintos. La diferencia disminuirá a medida que aumentes la escala. Puedes [cambiar la configuración de zona horaria](general#3-reporting-timezone) para cada aplicación. #### El calendario fiscal de Apple \{#the-apple-fiscal-calendar\} Apple utiliza su propio [calendario contable](https://adapty.io/apple-fiscal-calendar/) para determinar los períodos de ventas y las fechas de pago. Cada "mes" en el calendario consta de **4 o 5 semanas** y **puede incluir días de los meses del calendario adyacentes**. Los pagos suelen realizarse entre 30 y 45 días después de que finalice el período de ventas. Por ejemplo, el período de ventas de "enero de 2026" comienza el 28 de diciembre de 2025, es decir, 4 días antes del inicio del mes del calendario. La fecha de pago estimada para este período es el 5 de marzo. No compares los datos de los informes de pagos de Apple con los meses del calendario. En su lugar, selecciona un [rango de fechas personalizado](controls-filters-grouping-compare-proceeds#set-the-date-range) que corresponda al período de ventas necesario. #### Fechas de transacción \{#transaction-dates\} Algunos servicios (por ejemplo, AppsFlyer) pueden aplicar reglas de [cohorte](analytics-cohorts) al mostrar transacciones y atribuirlas a la fecha de instalación de la aplicación, en lugar de la fecha en que ocurrió la propia transacción. ## Cálculo de ingresos \{#revenue-calculation\} ### Comisiones e impuestos \{#fees-and-taxes\} Dependiendo de la [configuración](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue), los gráficos de Adapty pueden mostrar tus **ingresos brutos**, los **ingresos tras la comisión de la store** o los **ingresos tras la comisión de la store e impuestos**. Algunas stores y plataformas de terceros pueden carecer de la capacidad de mostrar los ingresos brutos o deducir los impuestos automáticamente. Si ves una discrepancia entre dos gráficos de ingresos diferentes, asegúrate de que la comparación sea válida. ### Cancelaciones y reembolsos \{#cancellations-and-refunds\} Las distintas plataformas muestran los datos de reembolsos de forma diferente. Adapty trata los reembolsos como ingresos negativos. Si un usuario se suscribe y solicita un reembolso al día siguiente, ambos eventos se reflejarán en los gráficos de Adapty, cada uno en su propio día. Otras plataformas pueden restar el valor del reembolso de la transacción original. ## Compras en sandbox \{#sandbox-purchases\} El [feed de eventos](event-feed) muestra las compras realizadas por cuentas sandbox. Los gráficos de analytics no lo hacen. Sin embargo, si tus datos de importación histórica contienen compras sandbox, Adapty no podrá distinguirlas y sus gráficos reflejarán las compras sandbox históricas. ## Instalaciones y descargas \{#installs-and-downloads\} Las stores (en particular Apple App Store) pueden rastrear las descargas de usuarios directamente. Sus estadísticas pueden incluir casos en los que la aplicación se instaló pero nunca se lanzó. Adapty solo puede registrar una instalación cuando un usuario lanza la aplicación, independientemente de tu [definición de instalación](general#4-installs-definition-for-analytics). ## País y store \{#country-and-store\} Para garantizar informes precisos, Adapty [puede inferir](controls-filters-grouping-compare-proceeds#filter-and-group-data) el país del usuario a partir de su IP. Las stores siempre atribuyen las descargas y compras a una app store específica. Si necesitas distinguir claramente entre ambos, puedes [crear un nuevo segmento de usuarios](segments) con el atributo `Country by store account` y [filtrar el analytics por segmento](controls-filters-grouping-compare-proceeds#filter-and-group-data). ## Precios de productos \{#product-pricing\} Si un precio incorrecto del producto causa una discrepancia en los ingresos, cambiar el precio no lo corrige retroactivamente. Para cambiar los precios de las transacciones existentes, necesitas forzar su sustitución importando los datos correctos. Cuando un usuario restaura una compra antigua después de un cambio de precio, Apple puede informar incorrectamente del valor de la compra. Debes importar los datos históricos para que Adapty refleje el valor correcto. ## Conflictos de atribución \{#attribution-conflicts\} Adapty solo puede usar [una única fuente de atribución](attribution-integration#prevent-data-issues) para cada transacción. No puedes reemplazar estos datos posteriormente. Si tu configuración incluye varios proveedores de atribución que no coinciden entre sí, la misma transacción en dos plataformas diferentes puede aparecer con dos fuentes de tráfico distintas. ## Diferencias en la terminología \{#differences-in-terminology\} Las distintas plataformas pueden tener nombres diferentes para el mismo concepto. Las métricas relacionadas con los [ingresos](#fees-and-taxes) varían en nombre de una plataforma a otra: | Adapty | App Store Connect | Google Play Console | |--------|-------------------|----------------------| | **Ingresos brutos** | Sales | Gross Revenue | | **Ingresos tras la comisión de la store** | N/A | N/A | | **Ingresos tras la comisión de la store e impuestos** | Proceeds | Earnings | | **ARPPU** | Proceeds per paying user | ARPPU | Otras métricas también pueden diferir en su definición: - **Suscripciones**: - Adapty no cuenta los nuevos trials como suscripciones. Una [nueva suscripción](reactivated-subscriptions) siempre comienza con una transacción económica. - Otras plataformas, como Google Play Console, pueden contar **cada trial como una nueva suscripción**, incluso antes de que se realice el primer pago. - **Retención**: - Adapty mide la retención basándose en el número de renovaciones de suscripciones. - App Store Connect considera que un usuario es retenido si abre la aplicación en el día especificado. Un usuario sin suscripción contará, pero el usuario suscrito que no abrió la app ese día no contará. - La métrica "Retained Installers" de Google Play Console mide la retención según el número de días que la aplicación permanece instalada en el dispositivo del usuario. Los usuarios que no abren la aplicación contribuyen a esta métrica. --- # File: predicted-ltv-and-revenue --- --- title: "Predicciones en cohortes" description: "Usa la analítica predictiva de Adapty para proyectar el LTV y los ingresos." --- Las predicciones de Adapty están diseñadas para ayudarte a responder las siguientes preguntas: 1. ¿Cuál es el valor de vida (LTV) proyectado de tus cohortes de usuarios? 2. ¿Qué cohortes tienen más posibilidades de generar mayores ingresos en el futuro? 3. ¿Cuánto puedes invertir teniendo en cuenta el retorno proyectado? Con las predicciones de Adapty puedes tomar decisiones basadas en datos sobre ingresos y crecimiento. El modelo de predicción de Adapty estima el potencial de ingresos a largo plazo de las cohortes de usuarios de tu app. Para cada cohorte, proyecta cómo evolucionarán los ingresos, el número de suscriptores de pago y el LTV medio a lo largo del tiempo. Esto te ayuda a tomar decisiones informadas sobre adquisición de usuarios, estrategias de marketing y desarrollo de producto. Adapty ofrece LTV proyectado y ingresos proyectados para cohortes de suscriptores de pago. Las predicciones se muestran en la página de análisis de cohortes para 3, 6, 9, 12, 18 y 24 meses desde la creación de la cohorte. En apps con un historial muy limitado, el modelo recurre a promedios entre apps, por lo que las predicciones para apps más recientes puede que no reflejen del todo el comportamiento específico de sus usuarios. ## Cómo funciona el modelo \{#how-the-model-works\} El modelo de predicción de Adapty utiliza patrones de retención de datos históricos de cohortes para proyectar ingresos futuros y LTV. Para cada combinación de app y tipo de suscripción, el modelo mide cómo cambian los suscriptores de pago y los ingresos totales de un período de renovación al siguiente. Calcula dos tasas de retención —una para suscriptores y otra para ingresos— basándose en las cohortes históricas de la app. Estas tasas se aplican luego a nuevas cohortes para proyectar su evolución a 3, 6, 9, 12, 18 y 24 meses desde la creación de la cohorte. Los datos utilizados están completamente anonimizados. El modelo genera dos valores para cada cohorte: - **Ingresos proyectados**: los ingresos totales que se espera que genere una cohorte dentro del horizonte seleccionado. - **LTV proyectado**: los ingresos proyectados divididos entre el número proyectado de suscriptores de pago en la cohorte. ### Pesos específicos de la app y entre apps \{#app-specific-and-cross-app-weights\} Por defecto, las predicciones para una cohorte utilizan los pesos de retención aprendidos a partir de las cohortes históricas de esa misma app, reflejando el comportamiento específico de sus usuarios. Cuando una app no tiene suficiente historial para un horizonte de predicción determinado, Adapty recurre a pesos de retención promediados entre todas las apps del mismo tipo de suscripción. Por ejemplo, una proyección a 12 meses para una app que solo tiene seis meses de antigüedad utiliza el fallback entre apps. Este fallback se aplica de forma independiente por horizonte, por lo que la misma cohorte puede usar los pesos propios de la app para la predicción a 3 meses y los pesos entre apps para la predicción a 12 meses. ### Disponibilidad y actualizaciones \{#availability-and-updates\} Las predicciones están disponibles después de que una cohorte completa su primer período de renovación —normalmente una semana después de la creación para suscripciones semanales y aproximadamente cuatro semanas para suscripciones mensuales. A partir de entonces, las predicciones se actualizan diariamente con los datos transaccionales más recientes, de modo que reflejan el comportamiento actual de la cohorte. ### Limitaciones \{#limitations\} - **Calidad de los datos**: El comportamiento inusual de una cohorte o las cohortes con muy pocos suscriptores de pago reducen la precisión. Las cohortes con menos de 100 suscriptores de pago quedan excluidas de los datos de entrenamiento del modelo. - **Apps nuevas**: Las apps sin suficiente historial utilizan pesos de fallback entre apps, que puede que no reflejen el comportamiento específico de la app. - **Antigüedad de la cohorte**: Las predicciones para un horizonte determinado se ocultan cuando la cohorte supera ese horizonte. Por ejemplo, las predicciones a 3 meses dejan de mostrarse después de tres meses, y no se muestran predicciones para cohortes con más de 24 meses de antigüedad. ## En el Dashboard \{#in-the-dashboard\} Para ver las predicciones, navega a la página de análisis de cohortes en tu Adapty Dashboard. Para más información sobre cohortes, consulta [Análisis de cohortes](analytics-cohorts). <img src="/assets/shared/img/4d808b4-Export-1691486610612.gif" alt="Página de análisis de cohortes mostrando las columnas de Ingresos Proyectados y LTV Proyectado" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> La columna **Predicted revenue** muestra los ingresos totales estimados que se espera que genere una cohorte de suscriptores durante el período de tiempo seleccionado desde la creación de la cohorte. Este valor se calcula usando el modelo de predicción de Adapty, basado en los patrones históricos de retención de cohortes de la app. La columna **Predicted LTV** muestra el valor de vida estimado de cada usuario en la cohorte seleccionada. Este valor se calcula dividiendo los ingresos proyectados entre el número proyectado de usuarios de pago en la cohorte. ### Seleccionar el horizonte \{#select-the-horizon\} Para cambiar el horizonte de predicción, selecciona un valor en el desplegable **Predictions**. Las opciones disponibles son 3, 6, 9, 12, 18 y 24 meses desde la creación de la cohorte. ### Filtrar por producto \{#filter-by-product\} Puedes filtrar los ingresos proyectados y el LTV por producto. Por defecto, las predicciones se construyen a partir de todos los datos de compra; filtrar por producto muestra la contribución de cada producto. <img src="/assets/shared/img/66a9c61-Export-1691486288948.gif" alt="Análisis de cohortes filtrado por producto" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Cuándo las predicciones no están disponibles \{#when-predictions-are-unavailable\} Cuando no se puede generar una predicción para una cohorte, las columnas de Ingresos Proyectados y LTV Proyectado muestran guiones largos (—) en lugar de valores. Esto puede ocurrir por varios motivos: - **Tiempo insuficiente desde la creación de la cohorte**: Las predicciones solo están disponibles después de que la cohorte completa su primer período de renovación —aproximadamente una semana para suscripciones semanales y unas cuatro semanas para suscripciones mensuales. - **Tamaño de la cohorte reducido**: Muy pocos suscriptores de pago para generar una proyección fiable. - **Comportamiento inusual de la cohorte**: La cohorte se desvía significativamente de los patrones que espera el modelo. Esperar algunas semanas puede resolverlo a medida que se acumulan más datos. - **Horizonte superado**: La cohorte es más antigua que el horizonte de predicción seleccionado. Por ejemplo, la predicción a 3 meses se oculta después de tres meses, la predicción a 12 meses después de doce meses, y no se muestran predicciones para cohortes con más de 24 meses de antigüedad. :::warning Al activar las predicciones, ten en cuenta que puede haber un retraso máximo de 24 horas antes de que los datos de predicción de Ingresos y LTV estén disponibles en tu Adapty Dashboard. ::: --- # File: predictions-in-ab-tests --- --- title: "Predicciones en pruebas A/B" description: "Aprende cómo las predicciones en las pruebas A/B ayudan a perfeccionar las estrategias de precios de suscripción." --- Bienvenido a la documentación de Análisis Predictivo de Adapty para nuestra funcionalidad de pruebas A/B. Esta herramienta te proporcionará información sobre los resultados futuros de tus pruebas A/B en curso y te ayudará a tomar decisiones basadas en datos más rápidamente 🚀 con las predicciones potenciadas por ML de Adapty. ### ¿Qué son las predicciones en pruebas A/B? \{#what-are-ab-test-predictions\} Las predicciones de pruebas A/B de Adapty utilizan técnicas avanzadas de aprendizaje automático (concretamente modelos de gradient boosting) para pronosticar el potencial de ingresos a largo plazo de los paywalls que se comparan en una prueba A/B. Este modelo predictivo te permite seleccionar el paywall más efectivo basándote en los ingresos proyectados al cabo de un año, en lugar de basarte únicamente en las métricas que observas mientras la prueba está en curso. Esto te permite decidir el ganador de forma más fiable y rápida, sin tener que esperar semanas a que se acumulen los datos. ### ¿Cómo funciona el modelo? \{#how-does-the-model-work\} El modelo se entrena con un amplio historial de datos de pruebas A/B procedentes de una gran variedad de apps en distintas categorías. Incorpora un amplio conjunto de características para predecir los ingresos que es probable que genere un paywall en el año siguiente al inicio del experimento. Estas características incluyen: - Transacciones de usuarios y tasas de conversión en diferentes períodos - Distribución geográfica de los usuarios - Plataforma de uso (iOS o Android) - Tasas de cancelación y reembolso - Productos de suscripción y sus duraciones (diaria, mensual, anual, etc.) - Otros datos relacionados con transacciones El modelo también tiene en cuenta los períodos de prueba en los paywalls, utilizando tasas de conversión históricas para predecir los ingresos como si los usuarios ya hubieran convertido. Esto garantiza una comparación justa entre paywalls con y sin ofertas de prueba, ya que también se tienen en cuenta las pruebas activas que potencialmente podrían generar ingresos en el futuro. ### ¿En qué se diferencia el P2BB Predicho del P2BB normal? \{#how-is-predicted-p2bb-different-from-just-the-p2bb\} Nuestras pruebas A/B utilizan el enfoque bayesiano: básicamente modelamos la distribución de los ingresos por usuario (o "Ingresos por cada 1.000 usuarios", para ser más precisos) y luego calculamos la probabilidad de que una distribución sea "realmente" mejor que la otra y no por pura casualidad — a esto lo llamamos Probabilidad de ser el mejor o P2BB (puedes obtener más información sobre nuestro enfoque [aquí](maths-behind-it)). Es importante tener en cuenta que al hacer esto, nos basamos únicamente en los ingresos que se han acumulado durante el tiempo que lleva ejecutándose la prueba. Por tanto, si quisieras realizar una prueba comparando una suscripción anual con una semanal, tendrías que esperar mucho tiempo para entender realmente cuál rinde mejor. Algo similar ocurre cuando comparas suscripciones con período de prueba frente a suscripciones sin período de prueba en una prueba A/B — ya que las pruebas activas que podrían potencialmente cambiar la dinámica del ganador nunca se tienen en cuenta en los ingresos. Aquí es donde entra en juego nuestro modelo predictivo. Con la distribución de ingresos actual de una prueba A/B y entrenado sobre un amplio conjunto de datos, es capaz de predecir la versión futura de la distribución de ingresos (concretamente tras 1 año). Y tras hacerlo, produce un P2BB predicho — el que obtendrías si ejecutaras la prueba durante todo el año. Ten en cuenta que a veces el P2BB predicho puede contradecir el P2BB actual. Cuando esto ocurre, resaltamos las filas de variación en amarillo, así: <img src="/assets/shared/img/74577c6-CleanShot_2024-02-15_at_13.08.452x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Consideramos que esto es una señal de que deberías acumular más datos para confirmar el ganador o profundizar en la prueba A/B para averiguar la causa. En general, recomendamos confiar en el P2BB predicho sobre el P2BB actual porque simplemente tiene en cuenta más datos, aunque la decisión final es, por supuesto, tuya. ### Precisión y certeza del modelo \{#model-accuracy-and-certainty\} El modelo alcanza un alto nivel de precisión, con un Error Porcentual Absoluto Medio (MAPE) ligeramente inferior al 10%. Este nivel de precisión permite a las empresas confiar en las predicciones del modelo al tomar decisiones basadas en datos. Para garantizar aún más la estabilidad, el modelo emplea un criterio de "certeza" basado en tres factores: - Un intervalo de predicción estrecho: el modelo tiene confianza en su resultado - Una cantidad suficiente de suscripciones e ingresos en la prueba - Han transcurrido al menos 2 semanas desde el inicio de la prueba Una predicción se considera fiable cuando se cumplen al menos dos de estos tres criterios. Cuando comienza una nueva prueba A/B, el modelo proporciona una predicción de ingresos por 1.000 usuarios a un año vista (nuestra métrica principal en pruebas A/B) para cada paywall. Las predicciones solo se muestran cuando cumplen los criterios de certeza. Si los datos son insuficientes, el modelo indicará "datos insuficientes para la predicción". ### Limitaciones y consideraciones \{#limitations-and-considerations\} Aunque nuestro modelo predictivo es una herramienta potente, es importante tener en cuenta sus limitaciones. El rendimiento del modelo depende de la calidad y representatividad de los datos disponibles. El comportamiento inusual de una cohorte o las apps nuevas que no están incluidas en el conjunto de entrenamiento pueden afectar a la precisión de las predicciones. No obstante, las predicciones se actualizan diariamente para reflejar los datos y comportamientos de usuario más recientes. Esto garantiza que la información que recibes siempre se basa en los datos más actuales. 🚧 Nota: Esta herramienta es un complemento, no un sustituto, de tu criterio experto y tu comprensión de la dinámica particular de tu app. Utiliza estas predicciones como guía junto con otras métricas y el conocimiento del mercado para tomar decisiones fundamentadas. --- # File: adapty-ads-manager --- --- title: "Apple Ads Manager" description: "Get realtime analytics from Apple Ads and manage and optimize your campaigns" --- **Apple Ads Manager** is an all-in-one platform designed to help you manage, optimize, and scale your Apple Ads campaigns more efficiently. It connects your Apple Search Ads performance with key revenue metrics such as installs, trials, subscriptions, and lifetime value without requiring an MMP. With real-time analytics, AI-driven forecasts, and smart automation, Apple Ads Manager eliminates tedious manual bid changes, spreadsheets, and guesswork, and replaces them with clear insights and tools that help you take action faster. With Apple Ads Manager, you get: - **[Overview](ads-manager-overview)**: All key metrics at a glance — spend, revenue, ROAS, CPA, and more — each with a daily trend chart - **Real-time performance data**: Across campaigns, ad groups, and keywords - **End-to-end revenue tracking**: From search → install → trial → subscription → LTV - **AI predictions & recommendations**: For profitable scaling - **Bulk management**: Of bids, budgets, statuses, and structures - **Rule-based automations**: Manage the full keyword lifecycle - **[Market Intelligence](ads-manager-market-intelligence)**: Competitor keyword strategies across 50+ countries <CustomDocCardList ids={['adapty-ads-manager-get-started', 'ads-manager-overview', 'adapty-ads-manager-analytics', 'ads-manager-create-campaign', 'ads-manager-create-ad-group', 'ads-manager-manage-keywords', 'ads-manager-automations', 'ads-manager-market-intelligence']} /> ## Why Apple Ads Manager? Because we give you the **most accurate data in the market.** Unlike the native Apple Ads console or MMPs, our data is **real-time, lossless, and fully connected** to trials, subscriptions, and LTV — without delays or attribution gaps. With its easy implementation and smooth user experience, you can manage everything in one place without having to switch between multiple tools. ## Get started To get started with Apple Ads Manager, follow the [guide](adapty-ads-manager-get-started), and you're all set to explore --- # File: adapty-ads-manager-get-started --- --- title: "Empieza con Apple Ads Manager" description: "Importa tus datos históricos de Apple Ads y recibe actualizaciones en tiempo real en el dashboard" --- [Apple Ads Manager](adapty-ads-manager) es tu plataforma de optimización y analítica para Apple Ads. En esta guía aprenderás a empezar a trabajar con Apple Ads Manager en dos pasos: 1. Instala el SDK de Adapty y deja que rastree tus datos de compras. 2. Conecta Apple Ads Manager a tu cuenta de Apple Ads para importar tus datos históricos y empezar a recibir actualizaciones en tiempo real. :::note Apple Ads Manager no usa la [integración de Apple Ads](apple-search-ads) de **App settings**. Para usar Apple Ads Manager, solo tienes que completar la configuración descrita en esta guía. ::: ## 1. Instala el SDK de Adapty \{#1-install-the-adapty-sdk\} :::important Adapty Ads Manager es un **producto independiente**. Puedes usarlo aunque tus paywalls, suscripciones o analítica no estén gestionados por Adapty; no es necesario migrar toda tu infraestructura a Adapty. Para obtener datos de ingresos precisos, la configuración mínima consiste en instalar el SDK de Adapty en modo observador y habilitar las notificaciones del servidor de App Store en Adapty. ::: Para conectar tus datos de ingresos con el rendimiento de las campañas, deja que Adapty registre tus compras: 1. El primer paso depende de si ya tienes compras in-app implementadas: - Si **ya tienes compras in-app implementadas con Adapty**, no necesitas hacer nada más en este momento. - Si **ya tienes compras in-app implementadas sin Adapty** y no planeas migrar a Adapty, instala el SDK de Adapty para tu plataforma en modo observador. En este paso solo tienes que añadir el SDK a tu proyecto, activarlo con el modo observador habilitado e informar las transacciones: - [iOS](implement-observer-mode) - [Android](implement-observer-mode-android) - [React Native](implement-observer-mode-react-native) - [Flutter](implement-observer-mode-flutter) - [Unity](implement-observer-mode-unity) - [Kotlin Multiplatform](implement-observer-mode-kmp) - [Capacitor](implement-observer-mode-capacitor) - Si **aún no tienes compras in-app implementadas y quieres usar Adapty**, completa los pasos de la [guía de inicio rápido](quickstart) para delegar la gestión de compras a Adapty. 2. Para recibir actualizaciones relacionadas con los ingresos directamente desde App Store, [habilita las notificaciones del servidor de App Store en Adapty](enable-app-store-server-notifications). ## 2. Conecta Apple Ads \{#2-connect-apple-ads\} :::important Necesitas tener el rol **Account Admin** en Apple Ads para conectar Apple Ads a Adapty. ::: Ahora tienes que conectar tu cuenta de Apple Ads Manager con tu cuenta de Apple Ads: 1. En el Adapty Dashboard, cambia a Apple Ads Manager en la parte superior izquierda. 2. Haz clic en **Continue with Apple**. 3. Inicia sesión en tu cuenta de Apple. 4. Selecciona qué acceso quieres conceder a Apple Ads Manager: - **Read and Write**: proporciona acceso a todos los grupos de campañas. - **Limited access**: elige grupos de campañas específicos y asigna el rol **Read & Write** para conceder acceso solo a esos grupos. 5. Haz clic en **Grant access**. Tras esto, Adapty comenzará a sincronizar tus datos históricos de Apple Ads. Ya puedes empezar a explorar Adapty Ads Manager, aunque tardará un tiempo hasta que se importen todos los datos históricos. ## Próximos pasos \{#whats-next\} Una vez que hayas sincronizado correctamente tus datos de transacciones, continúa aprendiendo cómo: - [Gestionar tus campañas, grupos de anuncios y palabras clave](ads-manager) - [Configurar reglas de automatización para ajustar pujas según el rendimiento de las campañas](ads-manager-automations) --- # File: ads-manager-overview --- --- title: "Resumen en Apple Ads Manager" description: "Ve todas tus métricas clave de Apple Ads en un solo lugar, cada una con un gráfico de tendencia." --- La página **Overview** muestra todas las métricas clave de Apple Ads en un solo lugar, cada una con un gráfico de tendencia. Por defecto, muestra datos de todas las aplicaciones conectadas. Para ver una sola aplicación, selecciónala en el menú desplegable de aplicaciones del encabezado. Para abrirla, ve a **Overview** en la barra lateral izquierda de Apple Ads Manager. ## Métricas \{#metrics\} Cada métrica aparece como una tarjeta con un gráfico de tendencia para el rango de fechas seleccionado. Para ver las definiciones y fórmulas de las métricas, consulta [Métricas en Apple Ads Manager](adapty-ads-manager-metrics). ## Configurar las métricas mostradas \{#configure-displayed-metrics\} Para cambiar qué métricas aparecen en la página **Overview**, haz clic en **Edit metrics**. Desde ahí puedes: - **Añadir una métrica**: Haz clic en **Add metric** y marca las casillas de las métricas que quieras. - **Eliminar una métrica**: Desmarca su casilla en el panel **Add metric**, o haz clic en **×** junto a ella. ## Controles \{#controls\} Usa los controles de la parte superior para ajustar lo que muestra la página **Overview**: - **Date range**: Elige un período predefinido (**Last 7 days**, **Last 30 days**, **Last 90 days**) o introduce un rango personalizado. Todos los gráficos y valores de resumen se actualizan para reflejar el período seleccionado. - **Chart type**: Cambia entre vistas de columna apilada, línea y gráfico circular. - **Revenue display**: Elige cómo se calculan las métricas de ingresos: - **Gross revenue**: Ingresos totales antes de cualquier deducción. - **Proceeds after store commission**: Ingresos después de deducir la comisión de Apple. - **Proceeds after store commissions and taxes**: Ingresos netos después de deducir tanto la comisión de Apple como los impuestos aplicables. --- # File: adapty-ads-manager-metrics --- --- title: "Métricas en Apple Ads Manager" description: "Consulta el análisis de la app en Apple Ads Manager." --- Apple Ads Manager ofrece métricas completas para medir el rendimiento de las campañas y el comportamiento de los usuarios. ## Rendimiento \{#performance\} | Métrica | Descripción | |--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Spend | La suma del coste de cada toque de un cliente en tu anuncio. | | Impressions | El número de veces que tu anuncio patrocinado apareció en los resultados de búsqueda del App Store durante el período de informe. | | CPM | La cantidad media que pagas por cada mil impresiones de anuncios. CPM promedio = Gasto / (Impresiones / 1000) Nota: para campañas de resultados de búsqueda en App Store con modelo de precios CPT, muestra el CPM efectivo. | | Taps | El número de veces que los usuarios tocaron el anuncio durante el período de informe. | | CPT | La cantidad media que pagas por cada toque en tu anuncio. CPT promedio = Gasto / Toques | | TTR | El número de veces que los clientes tocaron tu anuncio dividido por el total de impresiones que recibió. TTR = Toques / Impresiones × 100% | | Downloads (Total) | El número total de nuevas descargas y redescargas por toque y por visualización de un anuncio durante el período de informe. | | Downloads (View-Through) | El número de descargas y redescargas de usuarios que vieron tu anuncio en una ventana de 24 horas pero no lo tocaron. | | Downloads (Tap-Through) | El número total de nuevas descargas y redescargas de usuarios que tocaron tu anuncio en una ventana de 30 días. | | Avg CPA (Total) | El coste medio por adquisición (CPA) total es el gasto total de la campaña dividido por el total de descargas resultantes de una visualización o un toque en tu anuncio durante el período de informe. | | Avg CPA (Tap-Through) | El coste medio por adquisición (CPA) por toque es el gasto total de la campaña dividido por el número de descargas por toque durante el período de informe. | | Download Rate (Total) | El total de descargas resultantes de una visualización o un toque en tu anuncio dividido por el número total de toques durante el período de informe. Fórmula: (Descargas totales / Toques) × 100% si Toques > 0, de lo contrario 0% | | Download Rate (Tap-Through) | El total de descargas resultantes de toques en tu anuncio dividido por el número total de toques durante el período de informe. Fórmula: (Descargas por toque / Toques) × 100% si Toques > 0, de lo contrario 0% | | DPM (Total) | Descargas por millar (DPM) es el número de descargas por cada mil impresiones. Fórmula: (Descargas totales / Impresiones) × 1000 si Impresiones > 0, de lo contrario 0 | ## Conversiones \{#conversions\} :::note Los ingresos, ARPU, ARPPU, ARPAS, ROAS y ROI también están disponibles como métricas de cohorte para el análisis temporal de grupos de usuarios. ::: | Métrica | Descripción | |--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Conversions | Conversions es el número total de eventos de conversión durante el período de informe. Fórmula: Trials iniciados + Suscripciones iniciadas + No suscripciones | | Conversion CR | Conversion CR (Tasa de conversión) es el porcentaje del total de descargas que resultaron en una conversión. Fórmula: (Conversiones / Descargas totales) × 100% si Descargas totales > 0, de lo contrario 0% | | Cost per Conversion | Cost per Conversion es el gasto total dividido por el número de conversiones. Fórmula: Gasto / Conversiones si Conversiones > 0, de lo contrario 0 | | Revenue | Revenue es el importe total generado por compras, renovaciones u otras conversiones monetizadas en tu app dentro del período seleccionado (antes de la comisión del store). | | ROAS | ROAS (Retorno sobre el gasto publicitario) es los ingresos de tus anuncios divididos por el gasto publicitario, expresado como porcentaje. Fórmula: (Ingresos / Gasto) × 100% si Gasto > 0, de lo contrario 0% | | ROI | ROI (Retorno sobre la inversión) mide el beneficio neto en relación con el gasto. Fórmula: ((Ingresos − Gasto) / Gasto) × 100% si Gasto > 0, de lo contrario 0% | | ARPU | ARPU (Ingresos medios por usuario) es el ingreso medio por usuario. Se calcula como los ingresos totales divididos por el número de usuarios únicos. 60.000 $ de ingresos / 5.000 usuarios = 12 $ de ARPU. Es útil comparar este valor con el coste por instalación (CPI) para entender la eficacia de tus campañas de marketing. | | ARPPU | ARPPU (Ingresos medios por usuario de pago) es el ingreso medio por usuario de pago. Se calcula como los ingresos totales divididos por el número de usuarios de pago únicos. 60.000 $ de ingresos / 1.000 usuarios de pago = 60 $ de ARPPU. Ayuda a entender cuánto genera de media un cliente de pago. | | ARPAS | ARPAS es el ingreso medio por suscriptor activo. Se calcula como los ingresos totales / número de suscriptores activos. Por suscriptores entendemos aquellos que han activado un período de prueba o una suscripción. 60.000 $ de ingresos / 1.500 suscriptores = 40 $ de ARPAS. | | Installs | Installs es el número total de usuarios que han instalado la app por primera vez, así como cualquier reinstalación por parte de usuarios existentes. Esto incluye múltiples instalaciones del mismo usuario en distintos dispositivos. Ten en cuenta que las descargas incompletas o las instalaciones canceladas antes de completarse no se contabilizan. | | Installs CR | Installs CR (Tasa de conversión) es el porcentaje de usuarios del total de descargas que instalaron la app. Fórmula: (Instalaciones / Descargas totales) × 100% si Descargas totales > 0, de lo contrario 0% | | CPI | CPI (Coste por instalación) es el coste por instalación registrado por Adapty. Fórmula: Gasto / Instalaciones si Instalaciones > 0, de lo contrario 0 | | Trials | Trials es el número de nuevas suscripciones de prueba iniciadas durante el período de informe. | | Trial CR | Trial CR (Tasa de conversión) es el porcentaje del total de descargas que iniciaron una prueba. Fórmula: (Trials / Descargas totales) × 100% si Descargas totales > 0, de lo contrario 0% | | Cost per Trial | Cost per Trial es el gasto total dividido por el número de nuevos inicios de prueba. Fórmula: Gasto / Trials si Trials > 0, de lo contrario 0 | | Trials converted | Trials converted es el número de suscripciones de prueba que se convirtieron con éxito en suscripciones de pago durante el período de informe. | | Trial converted CR | Trial converted CR (Tasa de conversión) es el porcentaje de suscripciones de prueba que se convirtieron en suscripciones de pago. Fórmula: (Trials convertidos / Trials) × 100% si Trials > 0, de lo contrario 0% | | Cost per Trial converted | Cost per Trial converted es el gasto total dividido por el número de trials convertidos en el mismo período. Fórmula: Gasto / Trials convertidos si Trials convertidos > 0, de lo contrario 0 | | Subscriptions | Subscriptions es el número total de nuevos registros de suscripción (sin prueba) durante el período de informe. | | Subscription CR | Subscription CR (Tasa de conversión) es el porcentaje del total de descargas que iniciaron una suscripción de pago (sin prueba gratuita). Fórmula: (Suscripciones / Descargas totales) × 100% si Descargas totales > 0, de lo contrario 0% | | Cost per Subscription | Cost per Subscription es el gasto total dividido por el número de nuevas suscripciones iniciadas. Fórmula: Gasto / Suscripciones si Suscripciones > 0, de lo contrario 0 | | Non-subscriptions started | Non-subscriptions started es el número total de compras in-app únicas que no son suscripciones. | | Non-subscription CR | Non-subscription CR (Tasa de conversión) es el porcentaje del total de descargas que realizaron una compra sin suscripción en tu app. Fórmula: (No suscripciones iniciadas / Descargas totales) × 100% si Descargas totales > 0, de lo contrario 0% | | Cost per Non-subscription | Cost per Non-subscription es el gasto total dividido por el número de compras sin suscripción. Fórmula: Gasto / No suscripciones iniciadas si No suscripciones iniciadas > 0, de lo contrario 0 | ## Descargas avanzadas \{#advanced-downloads\} | Métrica | Descripción | |--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | New Downloads (Total) | El número total de nuevas descargas por toque y por visualización durante el período de informe. | | New Downloads (View-Through) | Nuevas descargas de usuarios que vieron tu anuncio pero no lo tocaron y no habían descargado tu app anteriormente, contabilizadas en una ventana de 24 horas. | | New Downloads (Tap-Through) | Nuevas descargas de usuarios que tocaron tu anuncio y no habían descargado tu app anteriormente. Se contabilizan en una ventana de atribución de 30 días. | | New Download Share (Tap-Through) | Muestra qué porcentaje del total de descargas por toque son nuevas descargas (de usuarios que tocaron tu anuncio en una ventana de atribución de 30 días). | | Redownloads (Total) | El número total de redescargas por toque y por visualización durante el período de informe. | | Redownloads (View-Through) | Redescargas de usuarios que vieron tu anuncio pero no lo tocaron en una ventana de 24 horas. Se contabilizan cuando un usuario descarga tu app, la elimina y la vuelve a descargar en el mismo dispositivo u otro diferente tras ver un anuncio. | | Redownloads (Tap-Through) | Redescargas de usuarios que tocaron tu anuncio en una ventana de atribución de 30 días. Se contabilizan cuando un usuario descarga tu app, la elimina y la vuelve a descargar en el mismo dispositivo u otro diferente tras tocar un anuncio. | | Redownloads Share (Tap-Through) | Muestra el porcentaje del total de descargas por toque que son redescargas. | ## Información \{#insights\} | Métrica | Descripción | |--------|------------------------------------------------------------------------------------------------------------------------------------------| | Impression Share | Impression Share es el porcentaje de impresiones que recibió tu anuncio en comparación con el total de impresiones para el mismo término de búsqueda. | | Rank | Rank (actual) es la posición actual de tu app en términos de cuota de impresiones para el término de búsqueda seleccionado en un país o región específicos. | | Search Popularity | Search Popularity (actual) es la popularidad de los términos de búsqueda según el país o la región. La clasificación va de 1 a 5, siendo 5 el mayor volumen de búsqueda. | --- # File: ads-manager-create-campaign --- --- title: "Gestionar campañas en Apple Ads Manager" description: "Crea y edita campañas de Apple Ads en Apple Ads Manager." --- Apple Ads Manager tiene una integración bidireccional con Apple Ads: obtienes datos de rendimiento casi en tiempo real y puedes crear y editar campañas directamente desde el Adapty Dashboard de forma mucho más cómoda que en la interfaz nativa. Si creas una campaña en el dashboard nativo de Apple Ads, aparecerá automáticamente en Apple Ads Manager en un plazo de 24 horas. Además de [explorar métricas completas de campaña](adapty-ads-manager-analytics), puedes gestionar todos los ajustes de campaña: - Crear campañas - Editar campañas existentes - Lanzar y pausar campañas ## Qué es una campaña \{#what-is-campaign\} Una campaña se centra en una sola app y muestra anuncios en un placement del App Store. Cada campaña incluye un presupuesto diario y [grupos de anuncios](ads-manager-create-ad-group) orientados a una estrategia específica para promocionar tu app. La campaña seguirá gastando según la configuración de su presupuesto. :::important Ten en cuenta que una campaña no puede funcionar por sí sola; los grupos de anuncios son el nivel donde se configuran la puja predeterminada, la audiencia y las palabras clave. Sin un grupo de anuncios, la campaña no tiene segmentación ni pujas y no mostrará anuncios. Crea la campaña y luego [añade al menos un grupo de anuncios](ads-manager-create-ad-group) para activarla. ::: ## Crear campañas \{#create-campaigns\} Para crear una nueva campaña de Apple Ads: 1. Ve a **Ads Manager** desde el menú lateral. En cualquier pestaña, haz clic en **+** encima de la tabla y selecciona **Create campaign**. 2. Selecciona la app para la que quieres lanzar la campaña. 3. Selecciona un grupo de campaña para la campaña. 4. En la sección **Campaign Settings**, configura: - **Campaign name**: La etiqueta que asignas para identificar y buscar tu campaña en el dashboard. - **Ad placement**: La superficie específica del App Store donde aparecerán tus anuncios: - **Search tab**: Promociona tu app en la parte superior de la lista de apps sugeridas cuando los usuarios visitan la pestaña de búsqueda en el App Store. - **Search results**: Promociona tu app en la parte superior de los resultados cuando los usuarios buscan apps relevantes en el App Store. - **Product pages**: Muestra tu anuncio en la parte superior de la lista **You might also like** para usuarios que han bajado en páginas de productos relevantes. - **Countries or regions**: Las ubicaciones geográficas donde tu campaña puede mostrarse y gastar presupuesto. - **Ad scheduling**: La fecha y hora de inicio del anuncio. Opcionalmente, también puedes establecer la fecha y hora de fin. 5. En la sección **Bid Strategy**, selecciona cómo se gestionan las pujas: - **Manage Bids**: Tú configuras y ajustas las pujas manualmente a nivel de grupo de anuncios y palabra clave. Introduce un **Daily budget**. - **Maximize Conversions**: Las pujas automatizadas de Apple maximizan las descargas dentro de tu presupuesto. Introduce un **Daily budget** y un **Target CPA** para orientar el algoritmo hacia un coste por descarga específico. [Más información](https://ads.apple.com/app-store/best-practices/maximize-conversions) 6. Haz clic en **Create**. 7. Continúa [creando un grupo de anuncios](ads-manager-create-ad-group) para activar la campaña. ## Editar campañas \{#edit-campaigns\} Para editar cualquier campaña creada: 1. Abre los ajustes de la campaña usando cualquiera de estos métodos: - Haz clic en el nombre de la campaña en **Ads Manager > Campaigns**. Luego, haz clic en **Edit campaign** en la parte superior derecha. - O selecciona la casilla junto al nombre de la campaña y haz clic en **Actions > Edit campaign settings**. 2. Ajusta los ajustes de la campaña. Ten en cuenta que solo puedes editar el nombre, los países y el presupuesto diario. Si quieres cambiar el placement del anuncio o la programación, crea una nueva campaña. 3. Haz clic en **Save changes**. :::note Los cambios realizados en una campaña directamente en Apple Ads se sincronizan automáticamente con Apple Ads Manager, aunque pueden tardar algo de tiempo en aparecer allí. ::: ## Exportar campañas \{#export-campaigns\} Para exportar la tabla de campañas como CSV, haz clic en el icono de descarga que hay encima de la tabla y selecciona **Export current page** o **Export all pages**. **Export all pages** descarga todas las campañas de todas las páginas en un único archivo. Un modal de progreso muestra el avance de la descarga; puedes cancelarla en cualquier momento. Hay dos filtros opcionales disponibles: - **Enabled only**: Incluye solo las campañas activas. - **With spend ≥**: Incluye solo las campañas con un gasto superior a un umbral especificado. - **Group by country**: Desglosa cada fila de campaña por país. La tabla se exporta tal como aparece en tu dashboard, con las columnas que hayas seleccionado para mostrar. ## Lanzar y pausar campañas \{#launch--pause-campaigns\} Para lanzar o pausar cualquier campaña desde Apple Ads Manager: 1. Ve a **Ads Manager > Campaigns**. 2. Activa o desactiva el interruptor junto al nombre de la campaña en la columna **Status**. --- # File: ads-manager-create-ad-group --- --- title: "Gestionar grupos de anuncios en Apple Ads Manager" description: "Crea y edita grupos de anuncios de Apple Ads en Apple Ads Manager." --- Apple Ads Manager tiene una integración bidireccional con Apple Ads: recibes datos de rendimiento casi en tiempo real y puedes crear y editar campañas directamente desde el Adapty Dashboard de una forma mucho más cómoda que en la interfaz nativa. Si creas un grupo de anuncios en el dashboard nativo de Apple Ads, aparecerá automáticamente en Apple Ads Manager en un plazo de 24 horas. Además de [explorar métricas completas de campaña](adapty-ads-manager-analytics), puedes gestionar todos los ajustes de los grupos de anuncios: - Crear grupos de anuncios - Editar grupos de anuncios existentes - Activar y pausar grupos de anuncios ## Qué es un grupo de anuncios \{#what-is-ad-group\} Un grupo de anuncios pertenece a una [campaña](ads-manager-create-campaign) y es donde configuras la segmentación y la estrategia de puja para tus anuncios. Cada grupo de anuncios incluye ajustes de puja, segmentación de audiencia y [palabras clave](ads-manager-manage-keywords) que determinan cuándo y a quién se muestran tus anuncios. Los grupos de anuncios te permiten organizar tu estrategia publicitaria dentro de una campaña y probar diferentes enfoques de segmentación. :::important Ten en cuenta que una campaña no puede ejecutarse sin grupos de anuncios. Los grupos de anuncios son el nivel donde se configura la puja predeterminada, la audiencia y las palabras clave, por lo que sin al menos un grupo de anuncios, la campaña no tiene segmentación ni puja y no se publicará. Crea primero la campaña y luego [añade al menos un grupo de anuncios](ads-manager-create-ad-group) para activarla. ::: ## Crear grupos de anuncios \{#create-ad-groups\} Para crear un nuevo grupo de anuncios de Apple Ads: 1. Ve a **Ads Manager** desde el menú lateral. En cualquier pestaña, haz clic en **+** encima de la tabla y selecciona **Create ad group**. 2. Selecciona la app a la que quieres añadir el grupo de anuncios. 3. Selecciona la campaña a la que quieres añadir el grupo de anuncios. 4. Configura los ajustes del grupo de anuncios: - **Ad group name**: La etiqueta que asignas para identificar y buscar tu grupo de anuncios en el dashboard. - **Default max CPT bid**: La cantidad máxima que estás dispuesto a pagar por un toque en tu anuncio. Esta puja se aplica a todas las palabras clave del grupo de anuncios, a menos que establezcas pujas individuales para cada una. - **CPA cap (limits impressions)** (Opcional): Este ajuste especifica la cantidad máxima que estás dispuesto a gastar por conversión de toque (por ejemplo, una descarga u otra acción objetivo). Establece un límite de puja para todas las palabras clave del grupo de anuncios. El límite de puja se calcula multiplicando el CPA cap que indiques por tu tasa de conversión de toque: `CPA Cap × CR (Tap-Through)`. Si la puja máxima CPT de la palabra clave es inferior a este valor, se aplicará la puja máxima CPT más baja. Por ejemplo, si tu CPA cap es de 5 $ y tu tasa de conversión de toque es del 65 %, la puja máxima aplicada a todas las palabras clave del grupo de anuncios sería de 3,25 $. Si el CPT máximo está fijado en 4 $, la puja máxima aplicada seguiría siendo de 3,25 $. - **Search Match**: Actívalo para hacer coincidir automáticamente tu anuncio con búsquedas relevantes sin necesidad de especificar palabras clave. Cuando está activado, Apple Ads puede mostrar tu anuncio en búsquedas relacionadas con los metadatos y la categoría de tu app. - **Audience**: Los criterios de segmentación que determinan qué usuarios ven tus anuncios. - **All eligible users**: Muestra tus anuncios a todos los usuarios elegibles para tu campaña. - **Specific audiences**: Segmenta grupos de usuarios específicos configurando: - **Devices**: Segmenta iPad, iPhone o ambos. - **Customer type**: Segmenta todos los usuarios, nuevos usuarios o usuarios que regresan. - **Gender**: Segmenta por género o todos los usuarios. - **Age range**: Segmenta rangos de edad específicos o todos los usuarios. - **Ad scheduling** (Opcional): Define cuándo empiezan a publicarse tus anuncios: - **Start date and time**: Cuándo debe comenzar a servir anuncios el grupo de anuncios. - **End date** (Opcional): Cuándo debe dejar de servir anuncios el grupo de anuncios. 5. Haz clic en **Create**. 6. Si el tipo de placement de tu campaña es **Search results**, ahora puedes [añadir palabras clave](ads-manager-manage-keywords) para comenzar a publicar anuncios. Para otros tipos de placement, ya está todo listo. ## Editar grupos de anuncios \{#edit-ad-groups\} Para editar cualquier grupo de anuncios creado: 1. Abre la configuración de la campaña mediante cualquiera de estos métodos: - Haz clic en el nombre de la campaña en **Ads Manager > Ad groups**. Luego, haz clic en **Edit ad group** en la parte superior derecha. - O selecciona la casilla junto al nombre del grupo de anuncios y haz clic en **Actions > Edit ad group settings**. 2. Ajusta la configuración del grupo de anuncios. Puedes editar todos los ajustes excepto la app y la campaña. 3. Haz clic en **Save changes**. :::note Los cambios realizados en un grupo de anuncios directamente en Apple Ads se sincronizan automáticamente con Apple Ads Manager, aunque pueden tardar un tiempo en reflejarse en Apple Ads Manager. ::: ## Exportar grupos de anuncios \{#export-ad-groups\} Para exportar la tabla de grupos de anuncios como CSV, haz clic en el icono de descarga encima de la tabla y selecciona **Export current page** o **Export all pages**. **Export all pages** descarga todos los grupos de anuncios de todas las páginas en un único archivo. Un modal de progreso hace seguimiento de la descarga; puedes cancelarla en cualquier momento. Hay dos filtros opcionales disponibles: - **Enabled only**: Incluye solo los grupos de anuncios activos. - **With spend ≥**: Incluye solo los grupos de anuncios con gasto por encima de un umbral especificado. - **Group by country**: Desglosa cada fila de grupo de anuncios por país. La tabla se exporta tal como aparece en tu dashboard, con las columnas que hayas seleccionado mostrar. ## Activar y pausar grupos de anuncios \{#launch--pause-ad-groups\} Para activar o pausar cualquier grupo de anuncios desde Apple Ads Manager: 1. Ve a **Ads Manager > Ad groups**, o navega a la página de una campaña para ver sus grupos de anuncios. 2. Activa o desactiva el interruptor junto al nombre del grupo de anuncios en la columna **Status**. --- # File: ads-manager-manage-keywords --- --- title: "Gestionar palabras clave en Apple Ads Manager" description: "Añade y gestiona palabras clave, palabras clave negativas y palabras clave SKAG en Apple Ads Manager." --- Apple Ads Manager tiene una integración bidireccional con Apple Ads: obtienes datos de rendimiento casi en tiempo real y puedes crear y editar palabras clave directamente desde el Adapty Dashboard de forma mucho más cómoda que en la interfaz nativa. Si creas una palabra clave en el dashboard nativo de Apple Ads, aparecerá automáticamente en Apple Ads Manager en un plazo de 24 horas. Además de [explorar análisis completos](adapty-ads-manager-analytics), puedes gestionar todos los ajustes de tus palabras clave: - Añadir palabras clave a grupos de anuncios - Añadir palabras clave negativas - Añadir palabras clave como SKAG (Single Keyword Ad Group) - Editar palabras clave directamente en la tabla - Realizar acciones en bloque sobre varias palabras clave - Activar y pausar palabras clave ## Qué son las palabras clave \{#what-are-keywords\} Las palabras clave son los términos de búsqueda que hacen que tus anuncios aparezcan en los resultados de búsqueda de la App Store. Se organizan dentro de [grupos de anuncios](ads-manager-create-ad-group), que pertenecen a [campañas](ads-manager-create-campaign). Esta estructura jerárquica te permite organizar y gestionar tu estrategia publicitaria de forma eficaz. :::important Las palabras clave solo se aplican a campañas con el tipo de placement **Search results**. Para campañas con otros tipos de placement (Search tab o Product pages), no se usan palabras clave. ::: ### Palabras clave estándar \{#standard-keywords\} Las palabras clave estándar son los términos principales sobre los que pujas para activar tus anuncios. Cuando los usuarios buscan estos términos en la App Store, tu anuncio puede aparecer en los resultados de búsqueda. ### Palabras clave negativas \{#negative-keywords\} Las palabras clave negativas evitan que tu anuncio aparezca en búsquedas que no son relevantes para tu app. Al añadirlas, puedes reducir el gasto en búsquedas irrelevantes. Las palabras clave negativas se pueden añadir a nivel de grupo de anuncios o como palabras clave negativas entre grupos, que se aplican a varias campañas a la vez. ### Palabras clave como SKAG (Single Keyword Ad Group) \{#keywords-as-skag-single-keyword-ad-group\} SKAG (Single Keyword Ad Group) es una estrategia en la que creas grupos de anuncios individuales, cada uno con una sola palabra clave. Este enfoque te permite: - Tener un control preciso sobre las pujas para palabras clave de alto valor - Analizar mejor el rendimiento a nivel de palabra clave SKAG es especialmente útil para identificar las palabras clave con mejor rendimiento y maximizar su potencial mediante grupos de anuncios dedicados. ## Añadir palabras clave \{#add-keywords\} Para añadir palabras clave a un grupo de anuncios: 1. Ve a **Ads Manager** desde el menú lateral. En cualquier pestaña, haz clic en **+** encima de la tabla y selecciona **Add keywords** en el desplegable. 2. En el modal, selecciona las campañas y grupos de anuncios a los que quieres añadir palabras clave. Tras seleccionar grupos de anuncios en una campaña, puedes seleccionar otra campaña y añadir más grupos a la lista. 3. Haz clic en **Select** para continuar. 4. En el diálogo **Add keywords**, escribe las palabras clave en el campo **Keywords list**. Si tienes un archivo delimitado por comas con palabras clave, puedes pegar su contenido para que Apple Ads Manager las suba todas en bloque. 5. Para cada palabra clave en la tabla, configura: - **Match type**: Selecciona el tipo de concordancia **Exact** o **Broad** - **CPT bid**: Establece la puja máxima de coste por tap para esta palabra clave, o déjala vacía para usar la puja máxima CPT predeterminada del grupo de anuncios 6. Revisa tus palabras clave y haz clic en **Add X keywords** (donde X es el número de palabras clave que estás añadiendo). :::important Una vez guardada una palabra clave, no se puede cambiar su tipo de concordancia. Si necesitas cambiarlo, elimina la palabra clave y añádela de nuevo con el tipo de concordancia deseado. ::: ## Añadir palabras clave negativas \{#add-negative-keywords\} Para añadir palabras clave negativas: 1. Ve a **Ads Manager** desde el menú lateral. En cualquier pestaña, haz clic en **+** encima de la tabla y selecciona **Add negative keywords** en el desplegable. 2. En el modal **Add negative keywords to**, selecciona en qué nivel quieres añadir las palabras clave negativas: - **Selected campaigns**: Añade palabras clave negativas a nivel de campaña. - **Selected ad groups**: Añade palabras clave negativas a nivel de grupo de anuncios. - **All ad groups in selected campaigns**: Añade palabras clave negativas a nivel de grupo de anuncios en todos los grupos de anuncios de las campañas seleccionadas. :::note Ten en cuenta lo siguiente: - Las palabras clave negativas a nivel de grupo de anuncios tienen mayor prioridad que las de nivel de campaña. - Si añades palabras clave negativas a todos los grupos de anuncios de las campañas seleccionadas, tendrás que añadirlas manualmente si decides agregar nuevos grupos de anuncios a esas campañas más adelante. ::: 3. Escribe las palabras clave negativas en el campo **Keywords list**. Si tienes un archivo delimitado por comas con palabras clave, puedes pegar su contenido para que Apple Ads Manager las suba todas en bloque. 4. Para cada palabra clave en la tabla, selecciona el **Match type**: - **Exact**: Excluye únicamente la palabra clave exacta o variaciones muy cercanas. - **Broad**: Excluye la palabra clave y términos de búsqueda relacionados. O selecciona las casillas junto a ellas y cambia el tipo de concordancia en bloque. 5. Revisa tus palabras clave negativas y haz clic en **Add X keywords** (donde X es el número de palabras clave que estás añadiendo). :::note Las palabras clave negativas entre grupos son especialmente útiles cuando quieres excluir ciertos términos de búsqueda en varias campañas a la vez, lo que ahorra tiempo y garantiza la coherencia en tu estrategia publicitaria. ::: ## Añadir palabras clave como SKAG \{#add-keywords-as-skag\} Para añadir palabras clave como SKAG (Single Keyword Ad Group): 1. Ve a **Ads Manager** desde el menú lateral. En cualquier pestaña, haz clic en **+** encima de la tabla y selecciona **Add keywords as SKAG** en el desplegable. 2. Selecciona las campañas donde quieres crear los grupos de anuncios SKAG. Puedes seleccionar varias campañas. 3. Por defecto, se crearán nuevos grupos de anuncios con la configuración predeterminada dirigida a todos los usuarios. Si quieres cambiarlo, selecciona **Copy settings from ad group** y elige un grupo de anuncios existente del que copiar la configuración. 4. Configura los ajustes para los nuevos grupos de anuncios: - **Ad group name prefix**: Prefijo opcional para añadir al nombre de cada grupo de anuncios (p. ej., "SKAG_" creará "SKAG_keyword1", "SKAG_keyword2", etc.). Puedes hacer clic en **Tag** para añadir dinámicamente la palabra clave, el nombre de la campaña y el país a los nombres de los grupos. - **CPT bid** y **CPA cap**: Establece la puja para todas las palabras clave a la vez o selecciona **Set CPT bid and CPA cap for each word manually** para configurarlas individualmente para cada palabra clave. 5. Escribe las palabras clave en el campo **Keywords list**. Si tienes un archivo delimitado por comas con palabras clave, puedes pegar su contenido para que Apple Ads Manager las suba todas en bloque. 6. Para cada palabra clave en la tabla, selecciona el **Match type**: - **Exact**: Coincide solo con la palabra clave exacta o variaciones muy cercanas - **Broad**: Coincide con la palabra clave y términos de búsqueda relacionados O selecciona las casillas junto a ellas y cambia el tipo de concordancia en bloque. 7. Selecciona **Check for duplicates in target campaign** para asegurarte de que no haya palabras clave idénticas en las campañas de destino. 8. Haz clic en **Create** para crear los grupos de anuncios SKAG. Cada palabra clave se colocará en su propio grupo de anuncios dentro de cada campaña seleccionada, lo que te permitirá gestionarlas y optimizarlas de forma independiente. ## Editar palabras clave \{#edit-keywords\} Para editar palabras clave existentes: 1. Ve a **Ads Manager > Keywords** o **Ads Manager > Negative keywords** y busca la palabra clave que quieres editar en la tabla, o navega a la página de una campaña, luego a la de un grupo de anuncios, y busca la palabra clave. 2. Edita los valores directamente en la tabla: - **CPT bid**: Haz clic en el valor de la puja e introduce una nueva puja máxima de coste por tap - **Status**: Usa el interruptor para pausar o activar la palabra clave :::note Las ediciones de palabras clave realizadas directamente en Apple Ads se sincronizan automáticamente con Apple Ads Manager, pero pueden tardar un tiempo en mostrarse en Apple Ads Manager. ::: ## Acciones en bloque \{#bulk-actions\} Puedes realizar acciones en bloque sobre varias palabras clave para ahorrar tiempo y gestionarlas de forma más eficiente. Para realizar acciones en bloque: 1. Ve a la pestaña **Ads Manager > Keywords** o **Ads Manager > Negative keywords**. 2. Selecciona varias palabras clave marcando las casillas junto a las que quieres gestionar. 3. Haz clic en el desplegable **Actions** y selecciona una de las siguientes opciones: - **Add as keywords**: Añade las palabras clave seleccionadas como palabras clave estándar - **Add as negative keywords**: Añade las palabras clave seleccionadas como palabras clave negativas - **Add as SKAG**: Crea Single Keyword Ad Groups para las palabras clave seleccionadas - **Activate**: Activa las palabras clave seleccionadas - **Pause**: Pausa las palabras clave seleccionadas - **Edit CPT bids**: Edita las pujas CPT de las palabras clave seleccionadas. Puedes editarlas de distintas formas: - **Set to**: Establece varias pujas a una cantidad específica. - **Increase by/decrease by**: Aumenta o reduce las pujas en una cantidad específica en USD o en un porcentaje de la puja. Opcionalmente, establece el límite superior de puja para evitar gastos excesivos accidentales. - **Set to average CPT**: Usa la métrica CPT (coste por tap) para alinear la puja con ella. Establece un coeficiente multiplicador. Por ejemplo, usa 0,9 cuando el rendimiento esté por debajo de las expectativas, o 1,1 cuando las supere. - **Set to average CPA**: Usa la métrica CPA (coste por adquisición) para alinear la puja con ella. Establece un coeficiente multiplicador. :::tip Las acciones en bloque son especialmente útiles para: - Convertir palabras clave entre distintos tipos (estándar, negativas, SKAG) - Añadir rápidamente palabras clave con otros tipos de concordancia para varias palabras clave a la vez - Filtrar las palabras clave con mejor rendimiento y ajustar sus pujas - Identificar palabras clave con bajo rendimiento y pausarlas ::: ## Exportar palabras clave \{#export-keywords\} Para exportar la tabla de palabras clave como CSV, haz clic en el icono de descarga encima de la tabla y selecciona **Export current page** o **Export all pages**. **Export all pages** descarga todas las palabras clave de todas las páginas en un único archivo. Un modal de progreso muestra el avance de la descarga; puedes cancelarla en cualquier momento. Hay dos filtros opcionales disponibles: - **Enabled only**: Incluye solo las palabras clave activas. - **With spend ≥**: Incluye solo las palabras clave con un gasto superior al umbral especificado. - **Group by country**: Desglosa cada fila de palabra clave por país. La tabla se exporta tal como aparece en tu dashboard, con las columnas que hayas seleccionado para mostrar. ## Explorar gráficos a nivel de palabra clave \{#explore-keyword-level-charts\} Puedes abrir un gráfico para cualquier palabra clave directamente desde la tabla **Ads Manager > Keywords**. Esto permite un análisis de rendimiento preciso, día a día, para cada palabra clave individualmente. Para mostrar un gráfico, haz clic en el icono de gráfico junto a la palabra clave en la tabla. Por defecto, el gráfico mostrará la métrica **Spend** para la palabra clave seleccionada. Puedes mostrar varias métricas a la vez para detectar correlaciones y cambios a lo largo del tiempo. Haz clic en **+** para añadir una nueva métrica. Haz clic en **Reset** para empezar de nuevo o simplemente desmarca las casillas de las métricas para ocultarlas. ## Historial de pujas \{#bid-history\} Para ver el historial de pujas de una palabra clave, haz clic en el botón **Bid History** junto a ella en la tabla. Se abre un panel con dos pestañas. - La pestaña **Metrics** muestra un gráfico con métricas a lo largo del tiempo. Puedes añadir y eliminar métricas de la misma forma que en los gráficos a nivel de palabra clave: haz clic en **+** para añadir, o desmarca las casillas para ocultarlas. Aparece un marcador en cada punto donde se cambió la puja; coloca el cursor sobre un marcador para ver el importe exacto de la puja en esa fecha. Úsalo para correlacionar los cambios de puja con variaciones en el rendimiento: si una métrica bajó o subió tras un cambio, el marcador señala cuándo ocurrió. - La pestaña **Bid History** muestra una tabla con todos los cambios de puja en orden cronológico inverso, con los valores de puja anterior y nuevo, la fecha de cada cambio y qué lo desencadenó. --- # File: ads-manager-manage-ads --- --- title: "Gestionar anuncios en Apple Ads Manager" description: "Crea y edita anuncios de Apple Ads en Apple Ads Manager." --- Apple Ads Manager tiene una integración bidireccional con Apple Ads: obtienes datos de rendimiento casi en tiempo real y puedes crear y editar anuncios directamente desde el Adapty Dashboard de una forma mucho más cómoda que en la interfaz nativa. Si creas un anuncio en el dashboard nativo de Apple Ads, aparecerá automáticamente en Apple Ads Manager en un plazo de 24 horas. ## Qué son los anuncios \{#what-are-ads\} Un anuncio es un creativo asignado a un [grupo de anuncios](ads-manager-create-ad-group) dentro de una [campaña](ads-manager-create-campaign). Puedes asignar un anuncio activo por grupo de anuncios. ## Crear anuncios \{#create-ads\} Antes de empezar, asegúrate de haber creado: - **Grupo de anuncios**. Puedes [crearlo directamente en el dashboard de Apple Ads Manager](ads-manager-create-ad-group). - **Página de producto personalizada**. Debes [configurarla directamente en Apple Ads](https://developer.apple.com/help/app-store-connect/create-custom-product-pages/configure-multiple-product-page-versions/). Debe estar aprobada por la App Store antes de poder usarla en tu anuncio. :::note Si ya tienes un anuncio activo en el grupo de anuncios seleccionado, se pausará para ejecutar el nuevo anuncio. ::: Para crear un nuevo anuncio de Apple Ads: 1. Ve a **Ads Manager** desde el menú lateral. En cualquier pestaña, haz clic en **+** sobre la tabla y selecciona **Create ad**. 2. Selecciona la app para la que quieres lanzar el anuncio. 3. Selecciona un grupo de anuncios para el anuncio. 4. Introduce el nombre del anuncio. 5. Selecciona el estado del anuncio. Desmarca la casilla **Status** si quieres activarlo más adelante. 6. Haz clic en **Select CPP**. Verás todas las páginas de producto personalizadas de tu app aprobadas por la App Store. Solo puedes seleccionar una página de producto personalizada. 7. Haz clic en **Create ad**. ## Editar anuncios \{#edit-ads\} :::note Una vez creado un anuncio, solo puedes editar su nombre. No puedes cambiar su CPP ni moverlo a un grupo de anuncios diferente. ::: Para editar el nombre de un anuncio, usa cualquiera de las siguientes opciones: - Haz clic en el nombre del anuncio en **Ads Manager > Ads**. Edita el nombre y haz clic en la marca de verificación que aparece junto a él. - Marca la casilla junto al nombre del anuncio y haz clic en **Actions > Edit ad**. Cambia el nombre del anuncio y haz clic en **Save changes**. :::note Los cambios realizados en un anuncio directamente en Apple Ads se sincronizan automáticamente con Apple Ads Manager, aunque pueden tardar un tiempo en reflejarse. ::: ## Exportar anuncios \{#export-ads\} Para exportar la tabla de anuncios como CSV, haz clic en el icono de descarga sobre la tabla y selecciona **Export current page** o **Export all pages**. **Export all pages** descarga todos los anuncios de todas las páginas en un único archivo. Un modal de progreso hace el seguimiento de la descarga; puedes cancelarla en cualquier momento. Hay dos filtros opcionales disponibles: - **Enabled only**: incluye solo los anuncios activos. - **With spend ≥**: incluye solo los anuncios con gasto superior al umbral especificado. - **Group by country**: desglosa cada fila de anuncio por país. La tabla se exporta tal como aparece en tu dashboard, con las columnas que hayas seleccionado para mostrar. ## Lanzar y pausar anuncios \{#launch--pause-ads\} Para lanzar o pausar cualquier anuncio desde Apple Ads Manager, usa cualquiera de las siguientes opciones: - Marca o desmarca la casilla en la columna **Status** en **Ads Manager > Ads**. - Marca la casilla junto al nombre del anuncio y haz clic en **Actions > Edit ad**. Cambia el nombre del anuncio y haz clic en **Save changes**. --- # File: ads-manager-create-segments --- --- title: "Crear segmentos basados en la atribución de Apple Ads en Apple Ads Manager" description: "Crea segmentos a partir de campañas, grupos de anuncios y palabras clave en dos clics desde Apple Ads Manager." --- Puedes crear [segmentos](segments) de usuarios directamente desde [Apple Ads Manager](adapty-ads-manager) seleccionando campañas, grupos de anuncios o palabras clave y convirtiéndolos en segmentos en unos pocos clics. Esto facilita la personalización de paywalls y ofertas según la fuente de adquisición, sin necesidad de configurar las condiciones del segmento manualmente. Después de crear un segmento, puedes usarlo para asignar distintos productos y precios, ejecutar pruebas A/B y personalizar el aspecto del paywall. ## Casos de uso \{#use-cases\} Estos son algunos ejemplos de cómo se pueden usar en la práctica los segmentos creados a partir de datos de Apple Ads: - **Paywalls basados en palabras clave**. Muestra un paywall centrado en funcionalidades a los usuarios provenientes de palabras clave de alta intención, y un paywall general a los usuarios de palabras clave de descubrimiento más amplio. - **Ofertas a nivel de campaña**. Ofrece períodos de prueba más largos o precios especiales a los usuarios de campañas de Apple Ads seleccionadas, manteniendo una oferta estándar para el resto. - **Coherencia entre anuncio y paywall**. Dirige a los usuarios de grupos de anuncios que promocionan funcionalidades específicas hacia paywalls que destacan esas funcionalidades en primer lugar. - **Optimización de campañas con mayor ROI**. Muestra un paywall premium a precio completo a los usuarios de campañas que generan de forma consistente un mayor valor de vida. ## Crear segmentos \{#create-segments\} Para crear un segmento desde Apple Ads Manager: 1. Ve a **Ads Manager** y cambia a la pestaña **Campaigns**, **Ad groups** o **Keywords**. Marca las casillas junto a las entidades que quieras usar. Ten en cuenta que, si seleccionas varias entidades, se usarán para crear un único segmento para todas ellas y no un segmento por cada entidad. 2. Haz clic en **Actions > Create segment from campaigns/ad groups/keywords**. 3. Si es necesario, actualiza los detalles del segmento en la ventana **Create segment**: - **Adapty project**: La app en Adapty en la que quieres crear este segmento. - **Build segment from campaign/ad group**: Al crear un segmento a partir de campañas o grupos de anuncios, puedes ajustar las campañas o grupos de anuncios seleccionados en este paso. - **Segment name** - **Segment description** 4. Haz clic en **Create**. 5. Una vez creado el segmento, puedes prepararte para usarlo: - Añádelo a un [placement](placements) para usarlo con un paywall u onboarding existente - Diseña un nuevo [paywall](adapty-paywall-builder) u [onboarding](onboardings) que se mostrará a los usuarios del segmento - Ejecuta una [prueba A/B](ab-tests) --- # File: ads-manager-automations-keyword-rules --- --- title: "Reglas de palabras clave en Apple Ads Manager" description: "Gestiona automáticamente el ciclo de vida de las palabras clave — ajusta pujas, activa o pausa palabras clave y muévelas entre grupos de anuncios — en función del rendimiento de la campaña." --- Las reglas de palabras clave actúan sobre tus palabras clave de forma automática en función del rendimiento en todo el embudo — desde instalaciones hasta trials, suscripciones e ingresos. Define condiciones usando métricas como gasto, CPA, ROAS y datos de cohortes, y elige qué hace la regla cuando se cumplen esas condiciones. Las reglas se ejecutan según una programación que tú estableces. Responden a los cambios de rendimiento sin intervención manual. ## Acciones disponibles \{#available-actions\} Cada regla de palabras clave realiza una acción cuando se cumplen sus condiciones: | Acción | Qué hace | |--------|-------------| | **Change bid** | Aumenta, reduce o establece la puja CPT | | **Enable keyword** | Reactiva una palabra clave en pausa | | **Pause keyword** | Pausa una palabra clave activa | | **Add as keyword to…** | Copia la palabra clave a otro grupo de anuncios con una puja y tipo de concordancia especificados | | **Add as negative keyword to…** | Añade la palabra clave como negativa en los grupos de anuncios o campañas indicados | ## Crear una regla de palabras clave \{#create-a-keyword-rule\} Puedes crear reglas de palabras clave desde plantillas o manualmente desde cero. ### Desde una plantilla \{#from-a-template\} Adapty ofrece plantillas listas para usar para los escenarios de optimización más habituales. Algunas plantillas frecuentes son: - **Cut waste on non-converting keywords**: Reduce las pujas cuando Gasto > X e Instalaciones o Trials = 0. - **Scale winning keywords**: Aumenta las pujas cuando ROAS > objetivo o CPA < objetivo. Para crear una regla desde una plantilla: 1. En la barra lateral izquierda, ve a **Automations** y haz clic en **Templates**. 2. Elige una plantilla y haz clic en **Next**. 3. Revisa y ajusta la configuración precargada: - **Rule name**: Se establece automáticamente con el nombre de la plantilla y la fecha actual (por ejemplo, "Scale Winning Keywords - [2025-11-12]"). - **Apply to**: Selecciona los grupos de campañas, aplicaciones, campañas o grupos de anuncios donde debe aplicarse la regla. - **Conditions**: Modifica las condiciones preconfiguradas si es necesario. - **Action**: Modifica la acción preestablecida si es necesario. - **Schedule**: Define con qué frecuencia debe ejecutarse la regla. 4. Haz clic en **Save** para activar la regla. ### Manualmente \{#manually\} Para crear una regla de palabras clave personalizada desde cero: 1. En la barra lateral izquierda, ve a **Automations**, haz clic en **Create rule** y selecciona **Keywords** como tipo de regla. 2. Escribe un **Rule name** descriptivo. 3. En la sección **Apply to**, selecciona los grupos de campañas, aplicaciones, campañas o grupos de anuncios donde debe aplicarse la regla. 4. Haz clic en **Add condition** y selecciona una [métrica](adapty-ads-manager-metrics) de la lista. Las métricas se calculan para el rango de tiempo seleccionado en la divisa de tu cuenta. Los datos se actualizan casi en tiempo real, por lo que las reglas siempre usan datos de rendimiento recientes. 5. Establece el período de tiempo (por ejemplo, Últimos 3 días o Últimos 7 días), elige el operador de comparación e introduce el valor umbral. 6. Para añadir más condiciones, haz clic en **Add condition** y selecciona un operador **And** u **Or** a la izquierda. 7. En la sección **Action**, selecciona qué ocurre cuando se cumplen las condiciones: **Change bid** - **Action type**: Selecciona **Increase by**, **Decrease by** o **Set to**. - **Value type**: Cambia entre **$** (absoluto) y **%** (relativo a la puja actual en el momento en que se ejecuta la regla). - **Upper bid limit** (opcional): Puja máxima para evitar pujas desorbitadas si la regla se activa varias veces ante señales muy fuertes. **Enable keyword** - Sin configuración adicional. La regla reactiva las palabras clave en pausa que cumplen las condiciones. **Pause keyword** - Sin configuración adicional. La regla pausa las palabras clave activas que cumplen las condiciones. **Add as keyword to…** - **Target ad groups**: Selecciona los grupos de anuncios que recibirán las palabras clave copiadas. - **CPT bid**: Establece la puja inicial para las palabras clave copiadas. - **Match type**: Selecciona **Exact** o **Broad**. - **Skip if keyword already exists**: Cuando está activado, omite los términos que ya existen en el grupo de anuncios de destino. **Add as negative keyword to…** - **Scope**: Selecciona los grupos de anuncios o campañas donde se añadirá la palabra clave negativa. - **Match type**: Selecciona **Exact** o **Broad**. 8. En la sección **Schedule**: - Elige la frecuencia: **Every day**, **Every 2 days**, **Every week**, etc. - Selecciona la hora de ejecución (todas las horas están en UTC). Las reglas se ejecutan a la hora programada en UTC. La ejecución suele terminar en pocos minutos, tras lo cual puedes ver los cambios en Logs y en el dashboard principal. 9. Haz clic en **Save** para crear la regla. ## Buenas prácticas \{#best-practices\} - **Empieza con un alcance reducido**: Aplica las reglas nuevas a pocas campañas o grupos de anuncios primero para validar su comportamiento antes de ampliarlas. - **Usa ventanas de retrospectiva cortas para campañas activas**: En campañas de ritmo rápido, los últimos 3–7 días suelen funcionar mejor que 30 días. - **Combina gasto y conversiones**: Evita las reglas de una sola métrica. Usa Gasto junto con Instalaciones, Trials o ROAS para obtener señales más fiables. - **Establece límites de puja en las reglas de Change bid**: El límite superior de puja evita pujas desbocadas cuando una señal fuerte activa la regla varias veces. - **Usa Enable keyword con datos de cohortes**: Una palabra clave pausada por un CPA inicial pobre puede mostrar un ROAS D31 o D61 sólido una vez que los datos de cohorte maduran. Establece una condición sobre el ROAS de cohorte y reactívala automáticamente cuando supere tu objetivo. - **Usa Add as keyword to… para pipelines de prueba a escalado**: Cuando una palabra clave en una campaña de prueba alcanza tu objetivo de CPA, cópiala automáticamente a una campaña de escala. - **Usa Add as negative keyword to… para mantener limpias las campañas de Discovery**: Cuando una palabra clave se confirma como keyword de concordancia exacta, exclúyela en tus campañas de Discovery o Search Match para evitar competir por la misma consulta. - **Espera antes de que las reglas de puja actúen sobre palabras clave recién promovidas**: Si usas [automatizaciones de términos de búsqueda](ads-manager-automations-search-terms) para promover términos a campañas de palabras clave, deja que esas palabras clave acumulen datos durante uno o dos días antes. --- # File: ads-manager-automations-search-terms --- --- title: "Automatizaciones de términos de búsqueda en Apple Ads Manager" description: "Promociona automáticamente los términos de búsqueda ganadores como palabras clave y niégalos en origen para escalar el tráfico de descubrimiento sin trabajo manual" --- Las campañas de Discovery y Search Match generan datos de términos de búsqueda. Convertir esos datos en una lista estructurada de palabras clave requiere descargar informes, filtrar términos y añadirlos manualmente a los grupos de anuncios. Las automatizaciones de términos de búsqueda hacen esto de forma automática: cuando un término cumple tus condiciones, la regla actúa sobre él según la acción que hayas configurado. Existen dos tipos de acción para las reglas de términos de búsqueda: - **Add as keyword**: Promociona el término como palabra clave de coincidencia exacta en un grupo de anuncios de destino y, opcionalmente, lo niega en la campaña de origen para evitar gasto duplicado. - **Add as negative keyword**: Niega el término directamente, sin promocionarlo. Úsalo para filtrar términos de búsqueda irrelevantes o que desperdician presupuesto en campañas de Discovery y Search Match. El caso de uso habitual de **Add as keyword** es: dejar que las campañas de Discovery o Search Match recopilen consultas reales de usuarios, usar una regla para detectar los términos que superan un umbral de rendimiento y llevarlos a una campaña de Probing como palabras clave de coincidencia exacta, negándolos al mismo tiempo en el origen. Una campaña de Probing es una campaña de Apple Search Ads dedicada a probar palabras clave promocionadas con pujas controladas. El caso de uso habitual de **Add as negative keyword** es: si un término aparece con frecuencia pero nunca convierte (por ejemplo, muchas impresiones y cero taps), niégalo automáticamente para dejar de malgastar presupuesto en él. ## Crear una regla de automatización de términos de búsqueda \{#create-a-search-term-automation-rule\} Puedes crear reglas de automatización de términos de búsqueda a partir de plantillas o manualmente desde cero. :::note Antes de crear una regla, asegúrate de tener campañas de Discovery o Search Match activas recopilando datos de términos de búsqueda. Las campañas de solo coincidencia exacta no generan informes de términos de búsqueda, por lo que la regla no tendrá nada sobre lo que actuar. ::: ### A partir de una plantilla \{#from-a-template\} Para crear una regla a partir de una plantilla: 1. En la barra lateral izquierda, ve a **Automations** y haz clic en **Templates**. 2. Elige una plantilla y haz clic en **Next**. 3. Revisa y ajusta la configuración predefinida: - **Rule name**: Se establece automáticamente con el nombre de la plantilla y la fecha actual. - **Apply to**: Selecciona los grupos de campañas, apps, campañas o grupos de anuncios donde la regla debe buscar términos de búsqueda. - **Conditions**: Modifica las condiciones preconfiguradas si es necesario. - **Actions**: Ajusta los grupos de anuncios de destino, la puja CPT y el alcance de las palabras clave negativas si es necesario. - **Schedule**: Establece con qué frecuencia debe ejecutarse la regla. 4. Haz clic en **Save** para activar la regla. ### Manualmente \{#manually\} Para crear una regla de automatización de términos de búsqueda personalizada desde cero: 1. En la barra lateral izquierda, ve a **Automations**, haz clic en **Create rule** y selecciona **Search terms** como tipo de regla. 2. Introduce un **Rule name** descriptivo que identifique el propósito de la regla. 3. En la sección **Apply to**, selecciona los grupos de campañas, apps, campañas o grupos de anuncios donde la regla debe buscar términos de búsqueda. 4. Haz clic en **Add condition** y selecciona una [métrica](adapty-ads-manager-metrics) de la lista. Las métricas se calculan para el rango de tiempo seleccionado en la divisa de tu cuenta. Los datos se actualizan casi en tiempo real, por lo que las reglas siempre usan datos de rendimiento actualizados. 5. Establece el período de tiempo (por ejemplo, los últimos 3 días o los últimos 7 días), elige el operador de comparación e introduce el valor umbral. 6. Para añadir más condiciones, haz clic en **Add condition** y selecciona un operador **And** u **Or** a la izquierda. 7. En la sección **Action**, selecciona qué ocurre cuando un término de búsqueda cumple las condiciones: **Add as keyword** Promociona los términos de búsqueda que coincidan como palabras clave de coincidencia exacta en un grupo de anuncios de destino. - **Target ad groups**: Selecciona los grupos de anuncios que recibirán las palabras clave promocionadas. Para construir un pipeline de discovery, selecciona grupos de anuncios en una campaña de Probing u otra campaña estructurada. - **CPT bid**: Establece la puja inicial de coste por tap para cada palabra clave promocionada. Opciones: puja predeterminada del grupo de anuncios, CPT actual del término de búsqueda o un valor específico. - **Skip if keyword already exists**: Cuando está activado, omite los términos que ya existen en el grupo de anuncios de destino. - **Add as negative**: Añade los mismos términos como palabras clave negativas para evitar pagar dos veces por el mismo tráfico. - **Scope**: Selecciona los grupos de anuncios donde se añaden las palabras clave negativas. :::tip Activa **Add as negative** en la misma regla: promociona el término a una campaña estructurada y niégalo en el origen en un solo paso. Así mantienes limpias tus campañas de Discovery y construyes tu embudo de palabras clave automáticamente. ::: **Add as negative keyword** Niega el término de búsqueda que coincide sin promocionarlo. - **Scope**: Selecciona los grupos de anuncios o campañas donde se añade la palabra clave negativa. - **Match type**: Selecciona **Exact** o **Broad**. Usa esta acción para negar términos de búsqueda irrelevantes o de baja calidad en campañas de Discovery y Max Conversion. Por ejemplo: si un término tiene más de 50 impresiones y 0 taps, niégalo automáticamente. 8. En la sección **Schedule**: - Elige la frecuencia: **Every day**, **Every 2 days**, **Every week**, etc. - Selecciona la hora de ejecución (todas las horas están en UTC). Las reglas se ejecutan a la hora programada en UTC. La ejecución suele terminar en pocos minutos, tras lo cual puedes ver los cambios en Logs y en el dashboard principal. 9. Haz clic en **Save** para crear la regla. Tras ejecutarse la regla, ve a **Automations** → **Logs** y abre la entrada correspondiente. Una ejecución correcta muestra cada término de búsqueda evaluado con su campaña de origen, grupo de anuncios de destino, resultado de la acción y resultado de la negación. Si no aparece ningún término, las condiciones no se cumplieron: revisa el umbral o amplía la ventana de lookback. ## Buenas prácticas \{#best-practices\} - **Usa campañas de Discovery o Search Match como origen**: Estos tipos de campaña recopilan consultas reales de usuarios, lo que da a tus reglas un gran conjunto de términos de búsqueda que evaluar. - **Ajusta el umbral a tu ventana de lookback**: Dos o más descargas en 7 días es un buen punto de partida. Una ventana más larga (14–30 días) baja el listón efectivo: los términos pueden superarlo con conversiones poco frecuentes. Para apps de alto volumen, acorta la ventana y sube el umbral. - **Niega siempre en el origen al promocionar**: Si añades un término como palabra clave en una campaña de Probing pero no lo niegas en Discovery, ambas campañas compiten por la misma consulta. Activa **Add as negative** en la misma regla. - **Elige los grupos de anuncios de destino con criterio**: Dirige los términos promocionados a un grupo de anuncios de Probing específico en lugar de a una campaña amplia. Esto mantiene limpia tu estructura de palabras clave y facilita el análisis del rendimiento. - **Revisa los logs después de cada ejecución**: Consulta la pestaña Logs para confirmar qué términos se promocionaron y adónde fueron. Al principio, ejecuta la regla manualmente tras configurarla para validar que funciona como esperas. Consulta [Automations](ads-manager-automations#explore-logs) para saber cómo leer los logs. - **Da tiempo a las palabras clave promocionadas antes de que actúen las reglas de palabras clave**: Si usas reglas de palabras clave en las mismas campañas, excluye las palabras clave recién promocionadas o espera uno o dos días antes de que las reglas actúen sobre ellas. Una regla de palabras clave puede dispararse sobre un término reciente sin historial de rendimiento y reducir su puja antes de que convierta. - **Usa Add as negative keyword para términos con muchas impresiones y cero taps**: Las campañas de Discovery y Max Conversion suelen mostrar términos de búsqueda irrelevantes. Una regla con "Impressions > 50 AND Taps = 0" los detecta automáticamente y los niega antes de que acumulen más impresiones desperdiciadas. ## Exportar términos de búsqueda \{#export-search-terms\} Para exportar la tabla de términos de búsqueda como CSV, haz clic en el icono de descarga sobre la tabla y selecciona **Export current page** o **Export all pages**. **Export all pages** descarga todos los términos de búsqueda de todas las páginas en un único archivo. Un modal de progreso rastrea la descarga; puedes cancelarla en cualquier momento. La tabla se exporta tal como aparece en tu dashboard, con las columnas que hayas seleccionado para mostrar. --- # File: ads-manager-market-intelligence --- --- title: "Market Intelligence en Apple Ads Manager" description: "Descubre qué palabras clave usan tus competidores en Apple Ads en más de 50 países y añádelas directamente a tus campañas." --- Market Intelligence muestra las palabras clave en las que pujan tus competidores en Apple Ads, en más de 50 países. Los datos se agregan de los últimos 30 días y se actualizan diariamente. Úsalo para: - **Saltarte la campaña Discovery**: Descubre en qué palabras clave ya pujan tus competidores en lugar de gastar presupuesto para encontrar lo que funciona. Empieza desde el primer día con una lista de palabras clave probada. - **Encontrar palabras clave de baja competencia**: Identifica términos de cola larga donde tus competidores tienen poca Share of Voice, menos competencia, menor coste por tap y mejor CPA. - **Proteger tu marca**: Comprueba si tus competidores están pujando por el nombre de tu app y en qué países, y recupera ese tráfico. - **Entrar en nuevos mercados con datos**: Consulta qué palabras clave usan tus competidores en un país antes de invertir un solo euro allí. - **Descubrir competidores que se te habían pasado**: Busca por palabra clave para ver quién aparece de forma orgánica en términos de tu categoría y añádelos a tu análisis. ## Ejecutar un análisis de Market Intelligence \{#run-a-market-intelligence-analysis\} ### 1. Selecciona tu app \{#1-select-your-app\} En la barra lateral izquierda, ve a **Market Intelligence**. Selecciona la app que quieres analizar en el desplegable y haz clic en **Continue**. ### 2. Selecciona competidores \{#2-select-competitors\} Añade los competidores que quieres analizar: - **Suggested competitors**: Adapty detecta posibles competidores en función de la categoría de tu app. Revisa la lista y selecciona los que quieras incluir. - **Search by keyword or app name**: Escribe una palabra clave (por ejemplo, «budget tracker») o el nombre de una app en el campo de búsqueda. Cambia el país si es necesario y selecciona apps de los resultados. Repite la búsqueda con distintas palabras clave para descubrir competidores con diferentes intenciones de búsqueda. - **Saved list**: Haz clic en **Create list** para guardar hasta 20 competidores y reutilizarlos en análisis futuros. También puedes cargar una lista que hayas creado anteriormente. Cuando hayas seleccionado tus competidores, haz clic en **Run analysis**. ### 3. Explora los resultados \{#3-explore-results\} Los resultados se organizan en cuatro pestañas: **Overview**, **Most Contested**, **By App** y **By Country**. #### Overview \{#overview\} La pestaña predeterminada muestra un resumen del análisis: - **Stats bar**: Total de competidores analizados, países con actividad en Apple Ads, palabras clave únicas encontradas en todos los mercados y la palabra clave más disputada. - **Keywords found by country**: Un gráfico de barras que muestra el volumen de palabras clave por país en los 25 principales mercados. - **Top 10 competitors by keyword coverage**: Una tabla clasificada con el recuento total de palabras clave de cada competidor, la Share of Voice media (Avg SOV) y los países en los que están activos. #### Most Contested \{#most-contested\} Muestra las palabras clave en las que el mayor número de competidores está activo al mismo tiempo. Usa esta pestaña para encontrar términos de alta demanda en tu categoría y ver dónde se concentra más la competencia. Usa el campo de búsqueda para filtrar la lista por palabra clave. #### By App \{#by-app\} Muestra los datos de palabras clave de cada competidor individualmente. Usa esta pestaña para profundizar en la estrategia de palabras clave de una app concreta por países. Haz clic en **Add filter** para filtrar por app, país o palabra clave. Para exportar los datos como CSV, haz clic en el icono de descarga. #### By Country \{#by-country\} Muestra los datos de palabras clave agrupados por mercado. Usa esta pestaña cuando quieras centrarte en el panorama competitivo de un país concreto antes de entrar o expandirte allí. Haz clic en **Add filter** para filtrar por app, país o palabra clave. Para exportar los datos como CSV, haz clic en el icono de descarga. ### 4. Añade palabras clave a las campañas \{#4-add-keywords-to-campaigns\} Una vez identificadas las palabras clave que merece la pena probar, añádelas a tus campañas sin salir de la herramienta: 1. En la tabla de palabras clave, marca las casillas junto a las palabras clave que quieras usar. Para seleccionar todas las palabras clave visibles, usa la casilla del encabezado de la tabla. 2. Haz clic en **Add to campaign**. 3. Selecciona si quieres añadirlas como palabras clave, palabras clave negativas o SKAG. Luego, selecciona la campaña y el grupo de anuncios de destino, establece el tipo de concordancia y la puja CPT, y confirma. ## Qué buscar en los resultados \{#what-to-look-for\} Estos patrones merecen atención en los resultados: - **Términos de cola larga con baja Share of Voice**: Las palabras clave donde los competidores tienen poca cuota de impresiones son menos disputadas. Suelen tener un coste por tap más bajo y mejores tasas de conversión porque la intención del usuario es más específica. - **Palabras clave que aún no tienes en tus campañas**: Busca términos que usen tus competidores y que tú no hayas probado todavía. Son términos que ya han demostrado generar tráfico en Apple Ads dentro de tu categoría. - **Cobertura de palabras clave de marca**: Busca el nombre de tu propia app. Si aparecen competidores, están pujando por tu marca. Añade esas palabras clave a tus campañas con pujas agresivas para proteger tu tráfico. - **Vacíos por país**: Comprueba en qué países están activos tus competidores. Los mercados con poca o ninguna actividad de competidores son más fáciles de entrar y requieren menos presupuesto para ganar tracción. --- # File: ads-manager-settings --- --- title: "Configuración en Apple Ads Manager" description: "Configura los ajustes en Apple Ads Manager." --- Ve a **Settings** en la esquina inferior izquierda del dashboard de Apple Ads Manager para configurar los ajustes de tu cuenta. ## Grupos de campañas \{#campaign-groups\} En la pestaña **Campaign groups** puedes ver todas las cuentas de Apple Ads conectadas a Apple Ads Manager y añadir nuevas. Si conectas varias cuentas de Apple Ads, todas sus analíticas se agregarán en un único dashboard de Apple Ads Manager. Para añadir una nueva cuenta de Apple Ads, haz clic en **Connect Apple Ads account** y sigue la [guía](adapty-ads-manager-get-started). ## Gestionar suscripción \{#manage-subscription\} En la pestaña **Manage subscription** puedes ver tu plan de suscripción actual y actualizar tu método de pago. ## Configuración de usuario \{#user-settings\} En la pestaña **User settings** puedes activar el interruptor **Hide Paused by Default**. Cuando está habilitado, las campañas, grupos de anuncios y palabras clave pausados se ocultarán para que puedas centrarte en los datos de rendimiento activos. Evita activar esta opción si experimentas frecuentemente lanzando y pausando distintas campañas, grupos de anuncios y palabras clave, ya que puede que necesites acceder a los elementos pausados en cualquier momento. --- # File: adapty-user-acquisition --- --- title: "User Acquisition (Adapty UA)" description: "Elimina la necesidad de MMPs y calcula toda la economía de tu app en un solo lugar." --- <CustomDocCardList ids={['user-acquisition', 'ua-analytics', 'ua-integrations', 'ua-tracking-links', 'ua-deferred-data']} /> Adapty User Acquisition es una solución de atribución que conecta las campañas publicitarias con las instalaciones de la app y los ingresos por suscripción, combinando datos de plataformas de anuncios, enlaces de seguimiento y tu app. Ofrece un dashboard de marketing analytics unificado que consolida todos tus datos de adquisición en un solo lugar. - Calcula el ROAS (retorno sobre el gasto publicitario) en todos tus canales - Visualiza toda la economía de tu app en un solo lugar - Obtén datos de atribución precisos para tomar mejores decisiones - Analiza el rendimiento de cohortes y el comportamiento de los usuarios a lo largo del tiempo :::tip ¿Quieres saber más sobre cómo Adapty User Acquisition puede ayudarte? [Reserva una llamada](https://calendly.com/tnurutdinov-adapty/30min) con nosotros. ::: ## ¿Por qué Adapty UA? \{#why-adapty-ua\} Medir el rendimiento de la adquisición de usuarios es todo un reto. Los datos suelen estar dispersos entre plataformas, la atribución se complica por los cambios en privacidad y desarrollar soluciones propias requiere mucho tiempo. Adapty UA ofrece atribución integrada y analytics unificados en un único dashboard de marketing. Todas tus métricas de adquisición —desde el gasto en anuncios hasta las instalaciones y los ingresos por suscripción— se consolidan automáticamente y se actualizan en tiempo real. Sin más reconciliación de datos entre hojas de cálculo ni saltos entre distintas herramientas. Así puedes centrarte en hacer crecer tu app en lugar de gestionar sistemas de datos. ## Cómo funciona \{#how-it-works\} Adapty User Acquisition atribuye las instalaciones de la app y los ingresos por suscripción a las campañas publicitarias combinando datos de plataformas de anuncios, enlaces de seguimiento y tu app. En términos generales: - Las plataformas de anuncios aportan la estructura de las campañas y el gasto publicitario - Los enlaces de seguimiento generados en Adapty UA trasladan el contexto de la campaña desde la web hasta la instalación de la app - El SDK de Adapty envía eventos de instalación e ingresos desde tu app El flujo de atribución funciona así: 1. **Se genera un enlace de seguimiento en Adapty UA y se añade a una campaña publicitaria.** El enlace contiene los parámetros de la campaña, como la plataforma, la campaña, el conjunto de anuncios y el creativo. 2. **Un usuario hace clic en el anuncio e instala la app desde el store.** El usuario es redirigido a través del enlace de seguimiento e instala la app desde el App Store o Google Play. 3. **La app envía un evento de instalación a Adapty.** En el primer lanzamiento, el SDK de Adapty envía un evento de instalación. Adapty extrae los parámetros de campaña asociados a esa instalación. 4. **La instalación se atribuye a una campaña.** Usando los parámetros de campaña del enlace de seguimiento, Adapty asocia la instalación con la campaña que la generó. 5. **El gasto publicitario y los ingresos se conectan.** Adapty obtiene los datos de gasto publicitario de las plataformas compatibles (actualmente Meta Ads y TikTok for Business) y vincula los eventos de suscripción y compra con las instalaciones atribuidas. Como resultado, Adapty proporciona métricas a nivel de campaña —instalaciones, ingresos, LTV y ROAS— en un dashboard de analytics unificado. Puedes analizar cohortes, hacer seguimiento del rendimiento a lo largo del tiempo y tomar decisiones de optimización basadas en datos, todo sin tener que reconciliar manualmente información de distintas fuentes. :::tip Los enlaces de seguimiento también pueden incluir parámetros personalizados, lo que permite que tu app gestione [deep links diferidos](ua-deferred-data) y reaccione a los datos de la campaña al procesar el evento de instalación. ::: --- # File: user-acquisition --- --- title: "Comenzar con Adapty User Acquisition" description: "Conéctate con Adapty User Acquisition para combinar el gasto en publicidad y los ingresos por suscripción y ver toda la economía de tu app en un solo lugar." --- Adapty User Acquisition te ayuda a conectar el gasto en publicidad con los ingresos por suscripción en campañas web-to-app, ofreciéndote una visión completa de la economía de tu app en un solo lugar. Para ver tus datos de ingresos en Adapty User Acquisition, primero debes activar la integración en el Adapty Dashboard. No necesitas proporcionar claves API, tokens ni identificadores. Solo actualiza y configura el SDK de Adapty. :::important User Acquisition está disponible con: - SDK de iOS, Android y Flutter versión 3.9.1 o superior. - SDK de React Native y Capacitor versión 3.10.0 o superior. - SDK de Unity versión 3.12.0 o superior. - SDK de Kotlin Multiplatform versión 3.15.0 o superior. ::: ## Antes de empezar \{#before-you-start\} Para conectar tus datos de ingresos con el rendimiento de las campañas, permite que Adapty realice el seguimiento de tus compras: - Si **ya tienes compras in-app implementadas con Adapty**, no necesitas hacer nada más en esta etapa. - Si **aún no tienes compras in-app implementadas y quieres usar Adapty**, completa los pasos de la [guía de inicio rápido](quickstart) para delegar el manejo de compras a Adapty. - Si **ya tienes compras in-app implementadas sin Adapty** y no planeas migrar a Adapty, [instala el SDK de Adapty para tu plataforma en modo observador](implement-observer-mode). En esta etapa solo necesitas añadir el SDK a tu proyecto, activarlo con el modo observador habilitado e informar las transacciones. Esta configuración habilita la atribución web-to-app: - Cuando los usuarios instalan tu app, el SDK de Adapty obtiene los detalles de la instalación a partir de los parámetros del enlace, para que Adapty UA pueda obtener los datos de la campaña. - El SDK de Adapty conoce todos los eventos relacionados con ingresos dentro de la app y puede atribuirlos a campañas web. ## Paso 1. Activa la integración de User Acquisition \{#step-1-enable-the-user-acquisition-integration\} Para empezar a enviar eventos de ingresos a Adapty UA: 1. Ve a [Integrations > Adapty](https://app.adapty.io/integrations/user-acquisition) en el Adapty Dashboard. 2. Activa el interruptor. Una vez que tus eventos empiecen a dispararse, verás los siguientes detalles de cada evento: - Nombre del evento - Estado - Entorno - Fecha y hora <img src="/assets/shared/img/toggle-ua.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Eventos compatibles \{#supported-events\} Por defecto, Adapty envía tres grupos de eventos a User Acquisition: - Trials - Suscripciones - Problemas Puedes consultar la lista completa de eventos compatibles [aquí](events). <img src="/assets/shared/img/events-ua.png" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 2. Conecta tu plataforma de publicidad y añade enlaces de seguimiento \{#step-2-connect-your-ad-platform-and-add-tracking-links\} Adapty utiliza enlaces de seguimiento para conectar las instalaciones de la app con los datos de la campaña. Debes usar un enlace de seguimiento como URL de destino en cada campaña publicitaria que quieras medir en Adapty UA. Si publicas anuncios en varias plataformas, configura los enlaces de seguimiento para cada plataforma por separado. Adapty trabaja con plataformas de publicidad de dos maneras: - **Integraciones nativas (Meta Ads, TikTok Ads).** Adapty se conecta directamente a la plataforma publicitaria. Los enlaces de seguimiento se generan automáticamente y los parámetros de la campaña se rellenan de forma dinámica según dónde se use el enlace. Puedes usar el mismo enlace en diferentes campañas, conjuntos de anuncios o creatividades, y Adapty recibirá automáticamente los datos correctos de la campaña y el gasto en publicidad. - **Solo enlaces de seguimiento (todas las demás plataformas publicitarias).** Adapty no se conecta a la plataforma publicitaria. Los enlaces de seguimiento se crean manualmente y todos los parámetros de la campaña deben definirse explícitamente al crear el enlace. Los datos de gasto en publicidad no están disponibles para estas plataformas. <Tabs> <TabItem value="meta" label="Meta Ads" default> Para crear un enlace de seguimiento para Meta Ads: 1. Ve a [Integrations > Meta](https://app.adapty.io/ua/integrations/facebook/accounts) en el Adapty UA Dashboard y haz clic en **Continue with Facebook**. 2. Inicia sesión con tu cuenta de Facebook y haz clic en **Continue**. 3. Revisa los permisos solicitados y haz clic en **Save**. 4. Cambia a la pestaña **Web campaigns** y haz clic en **Create campaign**. Selecciona la app y haz clic en **Save**. 5. En la pestaña **General**, expande la sección **iOS** y/o **Android** y pega las URLs de la aplicación en el App Store y/o Google Play. Luego haz clic en **Save**. 6. Copia el valor del campo **Click link** para **un solo enlace** o para un enlace específico de plataforma. Luego, en Meta Ads Manager, abre tu anuncio y pega este enlace como URL de destino. :::important En el campo **Website URL**, pega `https://api-ua.adapty.io/api/v1/attribution/click`. Pega el resto del enlace en el campo **URL parameters** de la sección **Tracking**. Esto ayudará a que tu anuncio de Meta sea aprobado. Consulta más [recomendaciones para configurar tus anuncios en Meta Ads Manager](meta-create-campaign). ::: 7. Ahora, cuando lances tu anuncio en Meta Ads, sus datos estarán disponibles para su análisis en el dashboard de Adapty UA. </TabItem> <TabItem value="tiktok" label="TikTok for Business"> Para crear un enlace de seguimiento para TikTok for Business: 1. Ve a [Integrations > TikTok Ads](https://app.adapty.io/ua/integrations/tiktok/accounts) en el Adapty UA Dashboard y haz clic en **Continue with TikTok**. 2. Inicia sesión con tu cuenta de TikTok y haz clic en **Continue**. 3. Revisa los permisos solicitados y haz clic en **Save**. 4. Cambia a la pestaña **Web campaigns** y haz clic en **Create campaign**. Selecciona la app y haz clic en **Save**. 5. En la pestaña **General**, expande la sección **iOS** y/o **Android** y pega las URLs de la aplicación en el App Store y/o Google Play. Luego haz clic en **Save**. 6. Copia el valor del campo **Click link** para **un solo enlace** o para un enlace específico de plataforma. Luego, en TikTok Ads Manager, al crear tu anuncio, pega este valor en el campo **Tracking URL** de la sección **Advanced Settings**. Esto permitirá que Adapty conecte las instalaciones y las compras con los anuncios en TikTok. Consulta la [guía para configurar tu campaña en TikTok Ads](tiktok-create-campaign). 7. Ahora, cuando lances tu anuncio en TikTok for Business, sus datos estarán disponibles para su análisis en el dashboard de Adapty UA. </TabItem> <TabItem value="others" label="Other ad platforms"> Para crear un enlace de seguimiento para otras plataformas publicitarias: 1. En el dashboard de Adapty UA, ve a **Tracking links** desde el menú lateral. Allí, haz clic en **Create link**. 2. Selecciona tu app de la lista y haz clic en **Next**. 3. Rellena los parámetros del enlace para asociarlo con la campaña y el anuncio que quieres rastrear. 4. Por defecto, estás creando un One Link. Detecta automáticamente la plataforma del usuario y lo redirige al App Store o Google Play tras registrar el clic. Si prefieres usar URLs de redirección separadas para cada plataforma, desmarca la casilla **One Link** y proporciona los enlaces de la store específicos para cada plataforma manualmente. 5. Haz clic en **Create**. 6. Abre la página de tu enlace de seguimiento y copia el **Click link** de una de las secciones: - **One link** – usa este enlace para rastrear clics y redirigir automáticamente a los usuarios a la store correcta. - **iOS link** o **Android link** — versiones opcionales específicas de plataforma si quieres enlaces separados para cada store. 7. Ve a tu plataforma publicitaria y pega el enlace en tu anuncio como URL de destino. </TabItem> </Tabs> ## Paso 3. Lanza tu campaña web-to-app y visualiza los resultados \{#step-3-launch-your-web-to-app-campaign-and-view-results\} Una vez que tu campaña esté activa y los usuarios comiencen a instalar tu app, Adapty empieza a atribuir las instalaciones y los ingresos a tus campañas. En el [dashboard de analíticas de Adapty UA](ua-analytics), verás métricas a nivel de campaña como: - Instalaciones y conversiones - Ingresos por suscripción y compras - Desglose del rendimiento por plataforma publicitaria, campaña, conjunto de anuncios y creatividad Las métricas aparecen en cuanto se reciben los eventos de instalación e ingresos desde tu app. Los datos de gasto en publicidad están disponibles para las plataformas con integraciones nativas. ## Más información \{#learn-more\} Continúa con la documentación detallada sobre las analíticas de Adapty User Acquisition y las guías prácticas para lanzar campañas en las principales plataformas publicitarias: - [**Analíticas en Adapty UA**](ua-analytics): Descubre cómo usar el dashboard de analíticas de forma eficaz. - [**Métricas en Adapty UA**](ua-metrics): Explora las métricas disponibles para el análisis de adquisición de usuarios. - [**Integraciones**](ua-integrations): Revisa las plataformas publicitarias e integraciones compatibles con Adapty UA. - [**Lanzar anuncios en Meta Ads Manager**](meta-create-campaign): Aprende a configurar y lanzar campañas en Meta Ads Manager. - [**Lanzar anuncios en TikTok for Business**](tiktok-create-campaign): Aprende a configurar y lanzar campañas en TikTok for Business. --- # File: ua-metrics --- --- title: "Métricas en Adapty UA" description: "Aprende sobre las métricas disponibles en Adapty UA." --- Adapty User Acquisition ofrece **métricas** completas para medir el rendimiento de las campañas y el comportamiento de los usuarios. Estas métricas están disponibles como valores estándar, y algunas también se ofrecen como **métricas de cohorte** para el análisis temporal de grupos de usuarios. ## Métricas estándar \{#standard-metrics\} | **Métrica** | Descripción | Cohorte | |------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| | **Spend** | La suma del coste de cada toque de un cliente en tu anuncio. | No | | **Impressions** | El número de veces que se mostró tu anuncio durante el período seleccionado. | No | | **Clicks** | El número de veces que los usuarios hicieron clic en tu anuncio durante el período del informe. | No | | **CPI** | **CPI (Coste por instalación)** es el importe que pagas por cada instalación. <br/>**Fórmula**: `Spend / Installs` | No | | **CPC** | **CPC (Coste por clic)** es el importe que pagas por cada clic en tu anuncio. <br/>**Fórmula**: `Spend / Clicks` | No | | **CPM** | **CPM (Coste por mil)** es el importe que pagas por cada mil impresiones de tu anuncio. <br/>**Fórmula**: `Spend / (Impressions / 1000)` | No | | **ICR** | **ICR (Tasa de conversión de instalaciones)** es el porcentaje de clics en el anuncio que resultaron en instalaciones. <br/>**Fórmula**: `(Installs / Clicks) × 100%` | No | | **IPM** | **IPM (Instalaciones por mil)** representa el número de instalaciones por cada mil impresiones del anuncio. <br/>**Fórmula**: `(Installs / Impressions) × 1000` | No | | **CTR** | **CTR (Tasa de clics)** es el porcentaje de impresiones que resultaron en clics. <br/>**Fórmula**: `(Clicks / Impressions) × 100%` | No | | **Inline link clicks** | El número de veces que los usuarios hicieron clic en enlaces en línea dentro de los creativos de tu anuncio o en la página de la app. | No | | **Cost per inline link click** | El importe medio que pagas por cada clic en un enlace en línea. <br/>**Fórmula**: `Spend / Inline Link Clicks` | No | | **Inline link click CTR** | El porcentaje de impresiones que resultaron en clics en enlaces en línea. <br/>**Fórmula**: `(Inline Link Clicks / Impressions) × 100%` | No | | **Installs** | El número total de usuarios que instalaron tu app (incluidas las reinstalaciones) durante el período del informe. | No | | **Revenue** | El importe total generado por las compras vinculadas a esta campaña (antes de la comisión del store) durante el período seleccionado. | Sí | | **ROAS** | **ROAS (Retorno sobre el gasto publicitario)** es el ingreso de tus anuncios dividido entre el gasto publicitario, expresado como porcentaje. <br/> **Fórmula**: `(Revenue / Spend) × 100% si Spend > 0, de lo contrario 0%` | Sí | | **ARPU** | **ARPU (Ingreso medio por usuario)** es el ingreso promedio por usuario en la cohorte. <br/>**Fórmula**: `Revenue / Users` | Sí | | **LTV** | **LTV (Valor de vida del cliente)** es el ingreso promedio atribuido a un usuario a lo largo de su ciclo de vida. <br/>**Fórmula**: `Revenue / Installs` | No | | **Cost per trial** | El importe medio que pagas por cada prueba iniciada. <br/>**Fórmula**: `Spend / Count trial started` | No | | **Cost per subscription** | El importe medio que pagas por cada producto de suscripción comprado. <br/>**Fórmula**: `Spend / Count subscription started` | No | | **Count subscription events** | El grupo de métricas para contar los eventos relacionados con suscripciones durante el período del informe. Las métricas son: <br/>- Count subscription started<br/>- Count subscription renewed<br/>- Count subscription renewal cancelled<br/>- Count subscription renewal reactivated<br/>- Count subscription expired<br/>- Count [subscription deferred](https://adapty.io/glossary/subscription-purchase-deferral/)<br/>- Count subscription refunded | Sí | | **Count trial events** | El grupo de métricas para contar los eventos relacionados con períodos de prueba durante el período del informe. Las métricas son: <br/>- Count trial started<br/>- Count trial converted<br/>- Count trial expired<br/>- Count trial renewal reactivated | Sí | | **Count billing issue detected** | El número de problemas de facturación detectados durante el período del informe. | Sí | | **Count entered grace period** | El número de suscripciones que entraron en un período de gracia debido a problemas de facturación. | Sí | | **Count non-subscription events** | El grupo de métricas para contar los eventos no relacionados con suscripciones durante el período del informe. Las métricas son: <br/>- Count non-subscription purchased<br/>- Count non-subscription refunded | Sí | | **Subscription events rate** | Métricas que muestran la tasa de eventos relacionados con suscripciones respecto a las instalaciones de la app durante el período del informe. Las métricas son: <br/>- Rate subscription started<br/>- Rate subscription renewed<br/>- Rate subscription renewal cancelled<br/>- Rate subscription renewal reactivated<br/>- Rate subscription expired<br/>- Rate [subscription deferred](https://adapty.io/glossary/subscription-purchase-deferral/)<br/>- Rate subscription refunded | Sí | | **Trial events rate** | Métricas que muestran la tasa de eventos relacionados con períodos de prueba respecto a las instalaciones de la app durante el período del informe. Las métricas son: <br/>- Rate trial started<br/>- Rate trial converted<br/>- Rate trial expired<br/>- Rate trial renewal reactivated | Sí | | **Rate billing issue detected** | La tasa de problemas de facturación respecto a las instalaciones de la app durante el período del informe. | Sí | | **Rate entered grace period** | La tasa de suscripciones que entraron en un período de gracia respecto a las instalaciones de la app durante el período del informe. | Sí | | **Non-subscription events rate** | Métricas que muestran la tasa de eventos no relacionados con suscripciones respecto a las instalaciones de la app durante el período del informe. Las métricas son: <br/>- Rate non-subscription purchased<br/>- Rate non-subscription refunded | Sí | ## Métricas de predicción \{#predicted-metrics\} Las métricas de predicción proyectan el rendimiento futuro de una cohorte a partir de los datos históricos de la propia app. Están disponibles en múltiples períodos de cohorte, como D30, D60, D90, D180 y D360, además de períodos personalizados que puedes añadir en días. Para saber cómo se calculan estos valores, consulta [Métricas de predicción en Adapty UA](ua-predicted-metrics). | **Métrica** | Descripción | Cohorte | |---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| | **pRevenue** | Ingreso total previsto que se espera que genere una cohorte hasta el horizonte objetivo. Se modela a partir de la retención histórica de cohortes de la app. | Sí | | **pROAS** | Retorno sobre el gasto publicitario previsto para el mismo horizonte. **Fórmula**: `(pRevenue / Spend) × 100%` | Sí | | **pAdProfit** | Ingreso previsto neto del gasto publicitario durante el horizonte. **Fórmula**: `pRevenue − Spend` | Sí | | **pARPU** | Ingreso promedio previsto por instalación durante el horizonte (LTV predicho). **Fórmula**: `pRevenue / Installs` | Sí | | **pARPPU** | Ingreso promedio previsto por usuario de pago durante el horizonte. **Fórmula**: `pRevenue / paying users at d{N}`, donde `d{N}` coincide con el horizonte seleccionado. | Sí | --- # File: ua-predicted-metrics --- --- title: "Métricas predichas en Adapty UA" description: "Pronostica ingresos, ROAS, beneficio publicitario y LTV para cohortes en Adapty UA." --- :::important Este artículo cubre las predicciones en Adapty User Acquisition únicamente. Para el LTV predicho y los ingresos en la página de análisis de cohortes, consulta [Predicciones en cohortes](predicted-ltv-and-revenue). ::: Adapty UA proyecta los ingresos futuros y la economía unitaria de cada cohorte, de modo que puedas comparar campañas antes de que hayan tenido tiempo de madurar. Las predicciones se generan a partir de los datos históricos de cohortes de la propia app y se actualizan a diario. Son especialmente útiles para evaluar cohortes recientes que aún no han completado ciclos de suscripción largos. ## Métricas predichas \{#predicted-metrics\} | **Métrica** | Descripción | |---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **pRevenue** | Ingresos totales predichos que se espera que genere una cohorte hasta el horizonte objetivo. Se modela a partir de la retención histórica de cohortes de la app. | | **pROAS** | Retorno sobre el gasto publicitario predicho durante el mismo horizonte. **Fórmula**: `(pRevenue / Spend) × 100%` | | **pAdProfit** | Ingresos predichos netos de gasto publicitario durante el horizonte. **Fórmula**: `pRevenue − Spend` | | **pARPU** | Ingreso medio predicho por instalación durante el horizonte (LTV predicho). **Fórmula**: `pRevenue / Installs` | | **pARPPU** | Ingreso medio predicho por usuario de pago durante el horizonte. **Fórmula**: `pRevenue / paying users at d{N}`, donde `d{N}` coincide con el horizonte seleccionado. | `pRevenue` es el valor base. Las otras cuatro métricas se derivan de él usando datos observados de la cohorte — gasto, instalaciones y usuarios de pago — no ejecutando el modelo por separado. Cada métrica predicha está disponible en múltiples períodos de cohorte: D0, D3, D7, D30, D60, D90, D180 y D360. También puedes añadir un período personalizado en días. El período define hasta qué punto en el futuro se proyecta el valor desde la fecha de instalación de la cohorte. ## Cómo se calculan las predicciones \{#how-predictions-are-calculated\} Las predicciones se construyen a partir de las cohortes históricas de cada app. El modelo mide cómo crecieron los ingresos de cohortes pasadas tras su día de referencia y luego proyecta la cohorte actual hacia adelante usando la misma trayectoria. ### Día de referencia \{#baseline-day\} Las predicciones solo están disponibles una vez que la cohorte alcanza su día de referencia. El día de referencia es el primer día en que típicamente se ha recibido el 90% de los ingresos iniciales de una cohorte. Los ingresos iniciales cuentan los inicios de suscripción, las conversiones de prueba y las compras únicas; las renovaciones no se contabilizan para este umbral. El día de referencia depende de la duración del período de prueba y la combinación de productos de la app: - **Apps sin períodos de prueba**: El día de referencia suele situarse en los primeros días tras la instalación. - **Apps con períodos de prueba cortos**: El día de referencia típicamente cae poco después de que el período de prueba convierte. - **Apps con períodos de prueba más largos**: El día de referencia puede estar una semana o más después de la instalación, ya que la mayoría de los ingresos iniciales solo se materializan una vez que el período de prueba finaliza. ### Proyección por tipo de suscripción \{#projection-by-subscription-type\} En el día de referencia, los ingresos iniciales de la cohorte se dividen en cinco categorías: suscripciones mensuales, anuales, semanales y trimestrales, más compras únicas. Cada categoría se proyecta hacia adelante de forma independiente usando una trayectoria medida a partir de las cohortes pasadas de la app. El modelo da más peso a las cohortes recientes y a las cohortes con una economía comparable, como un ingreso por transacción similar y una combinación de productos parecida. Por tanto, una predicción refleja cómo han rendido realmente las cohortes más recientes y más similares de la app. ## Cuándo están disponibles las predicciones \{#when-predictions-are-available\} Una predicción solo se muestra si la cohorte tiene suficientes datos para sustentarla. Cuando no se puede producir un valor, la columna muestra una raya larga (`—`) en su lugar. Razones habituales por las que una predicción no está disponible: - **La cohorte no ha alcanzado su día de referencia**: El modelo necesita que los ingresos iniciales de la cohorte se estabilicen antes de poder proyectar hacia adelante. - **Datos históricos insuficientes para la app**: Si la app no tiene suficientes cohortes pasadas del tipo de suscripción relevante, el modelo no puede ajustar tasas de retención fiables. Las predicciones se recalculan a diario con los datos de transacciones más recientes, por lo que los valores de la misma cohorte pueden cambiar a medida que se observan más ingresos. --- # File: ua-tracking-links --- --- title: "Links de seguimiento en Adapty User Acquisition" description: "Rastrea tus campañas y mide su éxito desde cualquier lugar." --- Los links de seguimiento te permiten medir de dónde vienen tus usuarios y conectar las instalaciones con las campañas publicitarias. Cuando alguien hace clic en tu anuncio, Adapty registra el clic y posteriormente lo relaciona con el evento de instalación enviado por el SDK. Así puedes ver qué canales, campañas, conjuntos de anuncios y anuncios generan más ingresos en tu [página de Analytics](ua-analytics). Puedes crear dos tipos de links de seguimiento: - **One link** — un enlace universal que detecta automáticamente la plataforma del usuario, registra el clic y lo redirige al App Store o Google Play. - **Links específicos por store** — enlaces dirigidos a cada plataforma que registran el clic y redirigen automáticamente a los usuarios al App Store o Google Play. También puedes añadirles parámetros de deep link diferido. ## Crear links de seguimiento \{#create-tracking-links\} Para crear un link de seguimiento: 1. En el dashboard de Adapty UA, ve a **Tracking links** desde el menú lateral. Luego haz clic en **Create link**. 2. Selecciona tu app de la lista y haz clic en **Next**. 3. Completa los parámetros del enlace para asociarlo con la campaña y el anuncio que quieres rastrear. | Parámetro | Descripción | |-------------------|----------------------------------------------------------------------------------------------------| | **Name** | El nombre interno del link de seguimiento. | | **Channel** | La fuente de tráfico, como Meta, Reddit o TikTok. Se usa para agrupar campañas en analytics. | | **Campaign ID** | El identificador único de la campaña en tu plataforma publicitaria. | | **Campaign name** | El nombre legible de la campaña. | | **Ad set ID** | El identificador único del conjunto de anuncios (ad group) en tu plataforma publicitaria. | | **Ad set name** | El nombre del conjunto de anuncios. | | **Ad ID** | El identificador único del anuncio individual. | | **Ad name** | El nombre del anuncio o variación creativa. | 4. Por defecto, estás creando un One Link. Detecta automáticamente la plataforma del usuario y lo redirige al App Store o Google Play después de registrar el clic. Si prefieres usar URLs de redirección distintas para cada plataforma, desmarca la casilla **One Link** e introduce manualmente los enlaces específicos de cada store. 5. Haz clic en **Create**. 6. Abre la página de tu link de seguimiento y copia el **Click link** de una de las secciones: - **One link** — usa este enlace para rastrear clics y redirigir automáticamente a los usuarios al store correspondiente. - **iOS link** o **Android link** — versiones opcionales específicas por plataforma si quieres enlaces separados para cada store. :::tip También puedes configurar parámetros adicionales del enlace para [trabajar con datos diferidos](ua-deferred-data). Por ejemplo, puedes implementar deep linking diferido. ::: 7. Ve a tu plataforma publicitaria y pega el enlace en tu anuncio como URL de destino. A partir de ahora, las instalaciones de la app se asociarán con los anuncios y campañas de los que provienen, para que puedas medir la efectividad de tus campañas en la página **Analytics**. --- # File: ua-deferred-data --- --- title: "Deeplinks diferidos en Adapty User Acquisition" description: "Configura deeplinks en Adapty User Acquisition" --- Los deeplinks diferidos te permiten pasar datos personalizados a tu app cuando los usuarios la instalan tras hacer clic en tus anuncios. Por ejemplo, puedes dirigirlos a una sección concreta de la app justo después de instalarla y abrirla por primera vez. Así es como funciona: 1. Cuando un usuario hace clic en tu anuncio, Adapty guarda los datos del clic. 2. Cuando Adapty registra el evento de instalación, obtiene los datos diferidos del clic. 3. Después de que el usuario instale la app y la abra por primera vez, Adapty recupera los datos almacenados y tu app recibe los parámetros personalizados, lo que te permite reaccionar a distintos valores en el código de la app. Adapty admite los siguientes parámetros de datos diferidos: - `ios_deferred_data` - `android_deferred_data` - `deferred_data_sub[1-10]` Para añadir parámetros de datos diferidos, agrégalos a tu enlace en la configuración de tu campaña: 1. Abre tu campaña desde la página **Integrations -> Meta/TikTok Ads**. O bien, abre tu enlace de seguimiento desde la página **Tracking links**. Copia el enlace de clic que usarás en tu campaña. 2. En tu plataforma de anuncios (Meta, TikTok, Google Ads, etc.), pega el enlace en el campo de URL de destino del anuncio y luego añade los parámetros de datos diferidos como parámetros de consulta adicionales, cada uno precedido por `&`. Por ejemplo, para llevar a los usuarios de iOS a una pantalla de bienvenida tras la instalación, añade `&ios_deferred_data=welcome`. La URL de destino final tendrá este aspecto: ``` https://api-ua.adapty.io/api/v1/attribution/click?adpt_cid=__ADAPTY__ID__&ios_deferred_data=welcome&campaign_id=__CAMPAIGN_ID__&adset_id=__AID__&ad_id=__CID__&campaign_name=__CAMPAIGN_NAME__&adset_name=__AID_NAME__&ad_name=__CID_NAME__&redirect_url=__APP_LINK__ ``` 3. Responde a los parámetros en el código de tu app. Ten en cuenta que los parámetros de datos diferidos están dentro del parámetro `payload`, y que `payload` es un JSON escapado, por lo que necesitas parsearlo en el código de tu app. Por ejemplo, así puedes gestionar instalaciones donde `ios_deferred_data` es `welcome`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers Adapty.delegate = self nonisolated func onInstallationDetailsSuccess(_ details: AdaptyInstallationDetails) { guard let payloadStr = details.payload, let data = payloadStr.data(using: .utf8), let payload = try? JSONSerialization.jsonObject(with: data) as? [String: Any], let deeplink = payload["ios_deferred_data"] as? String, deeplink == "welcome" else { return } DispatchQueue.main.async { print("Navigate to welcome screen") // navigate to your screen here } } ``` </TabItem> <TabItem value="android" label="Kotlin"> ```kotlin showLineNumbers Adapty.setOnInstallationDetailsListener(object : OnInstallationDetailsListener { override fun onInstallationDetailsSuccess(details: AdaptyInstallationDetails) { details.payload?.let { runCatching { val json = JSONObject(it) if (json.optString("android_deferred_data") == "welcome") { println("Navigate to welcome screen") // navigate here } }.onFailure(Throwable::printStackTrace) } } }) ``` </TabItem> <TabItem value="rn" label="React Native" default> ```typescript showLineNumbers adapty.addEventListener('onInstallationDetailsSuccess', details => { // Parse the payload JSON and navigate to welcome screen if needed try { if (details.payload) { const payload = JSON.parse(details.payload); if (payload.ios_deferred_data === 'welcome') { // Navigate to welcome screen // Replace with your app's navigation logic // For example, using React Navigation: // navigation.navigate('Welcome'); console.log('Navigate to welcome screen'); } } } catch (error) { console.error('Error parsing installation details payload:', error); } }); ``` </TabItem> <TabItem value="flutter" label="Flutter"> ```dart showLineNumbers Adapty().onUpdateInstallationDetailsSuccessStream.listen((details) { final payloadStr = details.payload; if (payloadStr == null) return; final payload = json.decode(payloadStr) as Map<String, dynamic>; if (payload['ios_deferred_data'] == 'welcome') { print('Navigate to welcome screen'); } }); ``` </TabItem> </Tabs> --- # File: ua-attribution-data --- --- title: "Recibir datos de atribución en tu app" description: "Accede a los datos de atribución de campaña en tu app después de que Adapty asocie una instalación a una campaña." --- Cuando Adapty asocia una instalación a una campaña, devuelve los datos de atribución a tu app a través del callback `onInstallationDetailsSuccess`. Usa estos datos para personalizar la experiencia del usuario según el canal o la campaña que generó la instalación. Los datos de atribución se devuelven como un objeto `attribution` anidado dentro del campo `payload`. Contiene los siguientes campos: | Campo | Descripción | |---|---| | `channel` | Canal de adquisición (p. ej. `facebook`, `tiktok`, `google`, `organic`) | | `campaign_id` | Identificador de la campaña | | `campaign_name` | Nombre de la campaña | | `adset_id` | Identificador del conjunto de anuncios / grupo de anuncios | | `adset_name` | Nombre del conjunto de anuncios / grupo de anuncios | | `ad_id` | Identificador del anuncio / creatividad | | `ad_name` | Nombre del anuncio / creatividad | Todos los campos son opcionales. Para instalaciones orgánicas o cuando no se pudo determinar la atribución, el campo `payload` no incluye el objeto `attribution`. Para leer los datos de atribución en tu app: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers Adapty.delegate = self nonisolated func onInstallationDetailsSuccess(_ details: AdaptyInstallationDetails) { guard let payloadDict = details.payload?.dictionary, let attribution = payloadDict["attribution"] as? [String: Any] else { return } let channel = attribution["channel"] as? String let campaignName = attribution["campaign_name"] as? String let adName = attribution["ad_name"] as? String print("Channel: \(channel ?? "organic")") } ``` </TabItem> <TabItem value="android" label="Kotlin"> ```kotlin showLineNumbers Adapty.setOnInstallationDetailsListener(object : OnInstallationDetailsListener { override fun onInstallationDetailsSuccess(details: AdaptyInstallationDetails) { val payloadStr = details.payload ?: return runCatching { val payload = JSONObject(payloadStr) val attribution = payload.optJSONObject("attribution") ?: return val channel = attribution.optString("channel") val campaignName = attribution.optString("campaign_name") val adName = attribution.optString("ad_name") println("Channel: $channel") }.onFailure(Throwable::printStackTrace) } }) ``` </TabItem> <TabItem value="rn" label="React Native"> ```typescript showLineNumbers adapty.addEventListener('onInstallationDetailsSuccess', details => { try { if (!details.payload) return; const payload = JSON.parse(details.payload); const attribution = payload.attribution; if (!attribution) return; const channel = attribution.channel; const campaignName = attribution.campaign_name; const adName = attribution.ad_name; console.log('Channel:', channel ?? 'organic'); } catch (error) { console.error('Error parsing payload:', error); } }); ``` </TabItem> <TabItem value="flutter" label="Flutter"> ```dart showLineNumbers Adapty().onUpdateInstallationDetailsSuccessStream.listen((details) { final payloadStr = details.payload; if (payloadStr == null) return; final payload = json.decode(payloadStr) as Map<String, dynamic>; final attribution = payload['attribution'] as Map<String, dynamic>?; if (attribution == null) return; final channel = attribution['channel'] as String?; final campaignName = attribution['campaign_name'] as String?; final adName = attribution['ad_name'] as String?; print('Channel: ${channel ?? 'organic'}'); }); ``` </TabItem> </Tabs> --- # File: ua-facebook --- --- title: "Integrar Meta Ads con Adapty UA" description: "Conecta Meta Ads con Adapty UA para rastrear y optimizar el rendimiento de las campañas en Facebook, Instagram, Messenger y Audience Network." --- La integración de Meta en Adapty UA te permite rastrear y optimizar el rendimiento de las campañas en Facebook, Instagram, Messenger y Audience Network. :::tip Consulta nuestra [guía para configurar anuncios en Meta Ads Manager](meta-create-campaign). ::: ## Paso 1. Conecta tu cuenta de Facebook \{#step-1-connect-your-facebook-account\} Para conectar Meta Ads con Adapty UA, ve a **Integrations > Meta** en el menú lateral izquierdo. Tienes dos opciones: - **Continue with Facebook**: Se conecta mediante OAuth. Úsala si accedes a Meta Ads Manager con tu cuenta personal o empresarial de Facebook. - **Add system token**: Se conecta usando un token de usuario del sistema permanente. Úsala si tu organización gestiona cuentas publicitarias a través de un usuario del sistema de Meta Business. <Tabs> <TabItem value="oauth" label="Continue with Facebook"> :::important Asegúrate de que tu cuenta de Facebook tenga acceso a las campañas y píxeles que necesitas. ::: 1. Haz clic en **Continue with Facebook**. 2. Inicia sesión con tu cuenta de Facebook y haz clic en **Continue**. 3. Revisa los permisos solicitados y haz clic en **Save**. </TabItem> <TabItem value="system" label="Add system token"> 1. [Genera un token de acceso de usuario del sistema](https://developers.facebook.com/documentation/ads-commerce/marketing-api/collaborative-ads/managed-partner-ads/api-guide/prerequisites/generate-access-token-system-user) en Meta Business Manager. 2. Haz clic en **Add system token**. 3. Pega el token y haz clic en **Connect**. </TabItem> </Tabs> Tras esto, todas tus cuentas publicitarias se añadirán a Adapty UA. Ya puedes continuar añadiendo campañas. ## Paso 2. Añade campañas \{#step-2-add-campaigns\} Para añadir una campaña de Meta a Adapty User Acquisition y rastrear cómo funcionan tus anuncios de Meta en Adapty: 1. Cambia a la pestaña **Web campaigns** y haz clic en **Create campaign**. Selecciona la app y haz clic en **Save**. 2. En la pestaña **General**, despliega la sección **iOS** y/o **Android** y pega las URLs de la aplicación en App Store y/o Google Play. 3. Copia el valor del campo **Click link**. Luego, en Meta Ads Manager, abre tu anuncio y pega este enlace. Esto permitirá que Adapty vincule las instalaciones y compras con los anuncios en Meta. 4. Para enviar los eventos de conversión de vuelta a Meta, también puedes asociar tus píxeles de Meta con campañas en Adapty UA. Para ello, selecciona uno de tus píxeles existentes en el desplegable **Pixel**. ## Paso 3. Mapea los eventos \{#step-3-map-events\} Para enviar eventos de conversión a Meta y optimizar las campañas, necesitas configurar el mapeo de eventos en la sección **Events names**. Esto permite que Adapty envíe automáticamente eventos de suscripción a tu píxel de Meta cuando los usuarios realizan acciones en tu app. En la sección **Events names**, activa los eventos que quieras rastrear en Meta Ads Manager. Para cada evento habilitado, selecciona el evento de Meta correspondiente en el desplegable o define uno personalizado. Por defecto, Adapty mapea los eventos de Adapty a los eventos estándar de Meta. Haz clic en **Save** para aplicar la configuración del mapeo de eventos. ## Configuración adicional \{#additional-configuration\} ### Parámetros adicionales \{#additional-parameters\} El campo **Additional parameter** te permite añadir datos personalizados para su análisis fuera de Adapty. Es útil cuando necesitas pasar datos específicos de campaña o de usuario a herramientas externas de análisis o partners de atribución. En el campo **Additional parameter**, introduce los datos personalizados que quieras incluir con el rastreo de atribución. El parámetro adicional se incluirá en todos los datos de atribución enviados a Meta y puede usarse para análisis y optimización avanzada de campañas. Por ejemplo, si estás ejecutando varias variaciones de la misma campaña, podrías añadir `variant=A` o `variant=B` para distinguir entre diferentes enfoques creativos. :::important Los parámetros adicionales cambian el **Click link** que pegas en Meta Ads Manager. Si ya copiaste ese enlace y añadiste un parámetro personalizado después, asegúrate de copiar y pegar el enlace actualizado que contiene el parámetro personalizado. ::: <br/> ### Configuración \{#settings\} La pestaña **Settings** controla cómo Adapty vincula las acciones de los usuarios con tus campañas de Meta Ads. Estos ajustes determinan las ventanas de tiempo para la atribución determinista y probabilística. Para configurarlos, ve a la pestaña **Settings** en la configuración de tu campaña. Aquí encontrarás dos ajustes principales: - **Deterministic matching window**: Utiliza identificadores exactos del dispositivo (como IDFA en iOS o Advertising ID en Android) para vincular usuarios con campañas con alta precisión. Establécelo en 168 horas (7 días) para obtener la máxima precisión de atribución — este es el valor predeterminado y recomendado. Cuando un usuario hace clic en tu anuncio de Meta e instala la app dentro de esta ventana, Adapty puede atribuir definitivamente la instalación a ese clic específico usando identificadores del dispositivo. - **Probabilistic matching window**: Utiliza modelos estadísticos y fingerprinting del dispositivo para vincular usuarios cuando la atribución determinista no es posible. Establécelo en 6 horas para la mayoría de las campañas — este es el valor predeterminado y funciona bien en la mayoría de los casos. Para campañas con alto volumen de clics, puedes reducirlo a 1-2 horas. Para los usuarios que no pueden vincularse de forma determinista (por ajustes de privacidad u otros factores), Adapty usa la atribución probabilística dentro de esta ventana más corta. Haz clic en **Save** para aplicar la configuración. ### Anulación de ingresos \{#revenue-override\} Si rastreas eventos de prueba y quieres que Meta les atribuya ingresos, usa la sección **Revenue override**. Aparece cuando el evento **Trial started** está habilitado. Para cada objetivo de evento de prueba, establece el porcentaje del precio de la suscripción que se reportará como ingresos. Por ejemplo, con el 30%, Adapty enviará el 30% del precio de la suscripción como valor de conversión para los eventos de prueba. Para añadir una anulación: 1. Habilita **Trial started** en la sección **Events names**. 2. En **Revenue override**, haz clic en **Add override**. 3. Selecciona el evento objetivo e introduce un porcentaje de ingresos (0–100). 4. Haz clic en **Save**. ### Enviar todos los eventos \{#send-all-events\} Por defecto, Adapty envía eventos a tu píxel solo para los usuarios atribuidos a una campaña de Meta. Activa **Send all events** para también reenviar eventos de usuarios orgánicos y no atribuidos al píxel. Cuando está habilitado, cada evento de instalación y transacción se envía al píxel, independientemente de la atribución de la campaña. Úsalo para proporcionar a Meta datos de conversión más amplios para el modelado de audiencias y la optimización de campañas. Para habilitar esta opción, en la configuración de la campaña selecciona **Send all events (forward organic/non-attributed events to this pixel)** y haz clic en **Save**. --- # File: ua-tiktok --- --- title: "Integrar TikTok for Business con Adapty UA" description: "Conecta TikTok for Business a Adapty UA para hacer seguimiento y optimizar el rendimiento de campañas en TikTok Ads Manager." --- La integración de TikTok for Business en Adapty UA te permite hacer seguimiento y optimizar el rendimiento de tus campañas en TikTok. :::tip Consulta nuestra [guía para configurar anuncios en TikTok for Business](tiktok-create-campaign). ::: ## Paso 1. Conecta tu cuenta de TikTok \{#step-1-connect-your-tiktok-account\} 1. Ve a **Integrations > TikTok Ads** en la barra lateral izquierda y haz clic en **Continue with TikTok**. 2. Inicia sesión con tu cuenta de TikTok y haz clic en **Continue**. 3. Revisa los permisos solicitados y haz clic en **Save**. Tras esto, todas tus cuentas publicitarias se añadirán a Adapty UA. Ya puedes continuar añadiendo campañas. ## Paso 2. Añade campañas \{#step-2-add-campaigns\} Para añadir una campaña de TikTok for Business a Adapty User Acquisition y hacer seguimiento de cómo funcionan tus anuncios de TikTok en Adapty: 1. Cambia a la pestaña **Web campaigns** y haz clic en **Create campaign**. Selecciona la app y haz clic en **Save**. 2. En la pestaña **General**, despliega la sección **iOS** y/o **Android** y pega las URLs de la aplicación en App Store y/o Google Play. 3. Copia el valor del campo **Click link**. Luego, en TikTok Ads Manager, al crear tu anuncio, pega ese valor en el campo **Tracking URL** dentro de la sección **Advanced Settings**. Esto permitirá a Adapty vincular las instalaciones y compras con los anuncios en TikTok. 4. (Opcional) Para reenviar los eventos de conversión a TikTok, también puedes asociar tus píxeles de TikTok con campañas en Adapty UA. Para ello, selecciona uno de tus píxeles existentes en el desplegable **Pixel**. ## Paso 3. Mapea los eventos \{#step-3-map-events\} Para enviar eventos de conversión a TikTok y optimizar campañas, debes configurar el mapeo de eventos en la sección **Events names**. Esto permite a Adapty enviar automáticamente eventos de suscripción a tu píxel de TikTok cuando los usuarios realizan acciones en tu app. En la sección **Events names**, activa los eventos que quieras rastrear en TikTok Ads Manager. Para cada evento habilitado, selecciona el evento de TikTok correspondiente en el desplegable o define uno personalizado. Por defecto, Adapty mapea los eventos de Adapty a los eventos estándar de TikTok. Haz clic en **Save** para aplicar la configuración de mapeo de eventos. ## Configuración adicional \{#additional-configuration\} ### Parámetros adicionales \{#additional-parameters\} El campo **Additional parameter** te permite añadir puntos de datos personalizados para análisis fuera de Adapty. Es útil cuando necesitas pasar datos específicos de campaña o de usuario a herramientas de análisis externas o socios de atribución. En el campo **Additional parameter**, introduce los datos personalizados que quieras incluir con tu seguimiento de atribución. El parámetro adicional se incluirá en todos los datos de atribución enviados a TikTok y puede usarse para análisis y optimización avanzados de campañas. Por ejemplo, si estás ejecutando varias variaciones de la misma campaña, podrías añadir `variant=A` o `variant=B` para distinguir entre diferentes enfoques creativos. :::important Los parámetros adicionales modifican el **Click link** que pegas en TikTok Ads Manager. Si ya has copiado este enlace allí y añades un parámetro personalizado posteriormente, asegúrate de copiar y pegar el enlace de clic actualizado que contiene el parámetro personalizado. ::: <br/> ### Configuración \{#settings\} La pestaña **Settings** controla cómo Adapty relaciona las acciones de los usuarios con tus campañas de TikTok Ads. Estos ajustes determinan las ventanas de tiempo para la atribución tanto determinista como probabilística. Para configurarlos, ve a la pestaña **Settings** en la configuración de tu campaña. Aquí encontrarás dos ajustes principales: - **Ventana de atribución determinista**: Utiliza identificadores de dispositivo exactos (como IDFA en iOS o Advertising ID en Android) para relacionar usuarios con campañas con alta precisión. Configúrala en 168 horas (7 días) para obtener la máxima precisión de atribución; este es el valor predeterminado y recomendado. Cuando un usuario hace clic en tu anuncio de TikTok e instala tu app dentro de esta ventana, Adapty puede atribuir la instalación de forma definitiva a ese clic concreto usando identificadores de dispositivo. - **Ventana de atribución probabilística**: Utiliza modelos estadísticos y huellas digitales del dispositivo para relacionar usuarios cuando la atribución determinista no es posible. Configúrala en 6 horas para la mayoría de las campañas; este es el valor predeterminado y funciona bien en la mayoría de los casos. Para campañas con un alto volumen de clics, puedes reducirla a 1-2 horas. Para los usuarios que no pueden ser relacionados de forma determinista (por ajustes de privacidad u otros factores), Adapty usa la atribución probabilística dentro de esta ventana más corta. Haz clic en **Save** para aplicar la configuración. ### Anulación de ingresos \{#revenue-override\} Si haces seguimiento de eventos de prueba y quieres que TikTok atribuya ingresos a ellos, usa la sección **Revenue override**. Aparece cuando el evento **Trial started** está habilitado. Para cada objetivo de evento de prueba, establece el porcentaje del precio de suscripción que se reportará como ingreso. Por ejemplo, al 30%, Adapty envía el 30% del precio de suscripción como valor de conversión para los eventos de prueba. Para añadir una anulación: 1. Habilita **Trial started** en la sección **Events names**. 2. En **Revenue override**, haz clic en **Add override**. 3. Selecciona el evento objetivo e introduce un porcentaje de ingresos (0–100). 4. Haz clic en **Save**. ### Enviar todos los eventos \{#send-all-events\} Por defecto, Adapty envía eventos a tu píxel solo para los usuarios atribuidos a una campaña de TikTok. Activa **Send all events** para reenviar también los eventos de usuarios orgánicos y no atribuidos al píxel. Cuando está habilitado, cada evento de instalación y transacción se envía al píxel, independientemente de la atribución de campaña. Úsalo para proporcionar a TikTok datos de conversión más amplios para el modelado de audiencias y la optimización de campañas. Para habilitar esta opción, en la configuración de la campaña, selecciona **Send all events (forward organic/non-attributed events to this pixel)** y haz clic en **Save**. --- # File: ua-funnelfox --- --- title: "Integrar FunnelFox con Adapty UA" description: "Conecta los embudos web2app de FunnelFox con Adapty UA para rastrear el camino de adquisición completo desde el punto de contacto web hasta el suscriptor de pago." --- [FunnelFox](https://funnelfox.com) es una plataforma para crear embudos web2app que te permite captar y cobrar a usuarios fuera del App Store, evitando su comisión y otras restricciones. Una vez conectado, FunnelFox envía eventos de transacción a Adapty UA, lo que te proporciona un camino de atribución completo desde el punto de contacto web hasta el suscriptor de pago. Para configurar la integración, vincula uno o más proyectos de FunnelFox a tu app de Adapty mediante un **Project ID**. ## Cómo funciona \{#how-it-works\} Cuando un usuario completa una compra en tu embudo de FunnelFox, este envía el evento de transacción a Adapty UA. Adapty utiliza el **Project ID** para identificar a qué app pertenece la transacción. El evento se almacena y aparece en tus análisis de UA. Cada transacción incluye: - **Evento del ciclo de vida de la suscripción**: iniciada, renovada, cancelada, trial convertido, reembolsada y más - **Datos de atribución**: identificadores de campaña, conjunto de anuncios y anuncio; parámetros UTM; click IDs de plataforma (fbclid, ttclid, gclid) - **Datos de embudo y experimento**: nombre del embudo y nombre del experimento de FunnelFox, para que puedas comparar variantes de pruebas A/B Adapty determina automáticamente el **canal** (Facebook, TikTok, Google u orgánico) a partir del click ID de la transacción. No es necesario configurarlo manualmente. :::note Las transacciones de FunnelFox utilizan la **primera fecha de pago** como fecha de cohorte en lugar de la fecha de instalación, ya que las compras web no tienen evento de instalación de app. ::: ## Configurar la integración \{#configure-integration\} ### Paso 1. Obtén tu Project ID en FunnelFox \{#step-1-get-your-project-id-in-funnelfox\} 1. En tu dashboard de FunnelFox, haz clic en **Settings** en la barra lateral izquierda. 2. En la sección **Project info**, copia el valor de **ID**. ### Paso 2. Añade el proyecto en Adapty UA \{#step-2-add-the-project-in-adapty-ua\} 1. En Adapty UA, ve a [**Integrations > FunnelFox**](https://app.adapty.io/ua/integrations/funnelfox). 2. Pega el Project ID que copiaste de FunnelFox. 3. Haz clic en **Save**. Para conectar proyectos adicionales de FunnelFox, haz clic en **Add project** y repite ambos pasos para cada proyecto adicional. --- # File: ua-custom-s3 --- --- title: "Custom S3" description: "Exporta datos de adquisición de usuarios a tu almacenamiento compatible con S3 personalizado para análisis e informes avanzados." --- La integración de Adapty UA con almacenamiento compatible con S3 personalizado te permite guardar de forma segura los datos de tus campañas de adquisición de usuarios en tu propia solución de almacenamiento compatible con S3. Podrás guardar el rendimiento de tus campañas, los datos de atribución y los eventos de adquisición de usuarios en tu bucket S3 personalizado como archivos .csv. Para configurar esta integración, solo tienes que seguir unos pocos pasos en la consola de tu almacenamiento compatible con S3 y en el dashboard de Adapty UA. :::note Adapty UA envía tus datos cada **24h** a las 4:00 UTC. Cada archivo contendrá los datos de los eventos creados durante todo el día natural anterior en UTC. Por ejemplo, los datos exportados automáticamente a las 4:00 UTC del 8 de marzo contendrán todos los eventos creados el 7 de marzo desde las 00:00:00 hasta las 23:59:59 en UTC. ::: ## Configurar la integración con S3 personalizado \{#set-up-custom-s3-integration\} Para empezar a recibir datos, configura la integración en Adapty UA: 1. Ve a [**Integrations** -> **Custom S3**](https://app.adapty.io/ua/integrations/custom-s3) 2. Activa el interruptor **Export install events to custom S3**. 3. Rellena los campos obligatorios para establecer la conexión entre tu almacenamiento S3 personalizado y los perfiles de Adapty UA. | Campo | Descripción | |:----------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Access Key ID** | Identificador único que se usa para autenticar el acceso de un usuario o aplicación a tu servicio de almacenamiento compatible con S3. Encuéntralo en la consola de tu proveedor de almacenamiento. | | **Secret Access Key** | Clave privada que se usa junto con el Access Key ID para autenticar el acceso de un usuario o aplicación a tu servicio de almacenamiento compatible con S3. Encuéntrala en la consola de tu proveedor de almacenamiento. | | **S3 Bucket Name** | Nombre único a nivel global que identifica un bucket S3 específico dentro de tu entorno de almacenamiento. Los buckets S3 son un servicio de almacenamiento sencillo que permite guardar y recuperar objetos de datos, como archivos e imágenes, en la nube. | | **Region** (Opcional) | Obtén tu región desde la consola de administración. | | **Folder Inside the Bucket** (Opcional) | Nombre de la carpeta que quieres crear dentro del bucket S3 seleccionado. Ten en cuenta que S3 simula carpetas mediante prefijos de clave de objeto, que son esencialmente nombres de carpeta. | | **Custom Endpoint URL** | URL del endpoint de tu servicio de almacenamiento compatible con S3. Tu proveedor de almacenamiento debe proporcionarla (p. ej., MinIO, DigitalOcean Spaces, Wasabi, etc.). | :::note También puedes especificar directorios anidados en el campo del nombre del bucket S3, p. ej. `adapty-ua-events/com.sample-app` ::: ## Exportación manual de datos \{#manual-data-export\} Además de la exportación automática de datos de eventos a tu almacenamiento S3 personalizado, Adapty UA también ofrece una función de exportación manual de archivos. Con esta función puedes seleccionar una fecha para los datos de adquisición de usuarios y exportarlos manualmente a tu bucket S3. Esto te da mayor control sobre qué datos exportas y cuándo lo haces. ## Estructura de la tabla \{#table-structure\} En la integración con S3 personalizado, Adapty UA proporciona una tabla para almacenar el historial de eventos de instalación. La tabla contiene información sobre el perfil de usuario, los ingresos y las ganancias, y el store de origen, entre otros datos. :::warning Ten en cuenta que esta estructura puede crecer con el tiempo, con nuevos datos introducidos por nosotros o por terceros con los que trabajamos. Asegúrate de que el código que la procesa sea suficientemente robusto y se base en campos específicos, no en la estructura como un todo. ::: Esta es la estructura de la tabla para los eventos: | Columna | Descripción | |--------------------------|-----------------------------------------------------| | `adapty_profile_id` | Identificador único del perfil de Adapty | | `install_id` | Identificador único de instalación | | `created_at` | Marca de tiempo de creación del registro (ISO 8601) | | `installed_at` | Marca de tiempo de instalación de la app (ISO 8601) | | `store` | App store (`ios`, `android`) | | `country` | Código de país del usuario (ISO 3166-1 alpha-2) | | `ip_address` | Dirección IP del cliente | | `idfa` | iOS Identifier for Advertisers | | `idfv` | iOS Identifier for Vendors | | `gaid` | Google Advertising ID (Android) | | `android_id` | ID de dispositivo Android | | `app_set_id` | Android App Set ID | | `channel` | Canal de atribución | | `campaign_id` | Identificador de campaña | | `campaign_name` | Nombre de la campaña | | `adset_id` | Identificador del conjunto de anuncios | | `adset_name` | Nombre del conjunto de anuncios | | `ad_id` | Identificador del anuncio | | `ad_name` | Nombre del anuncio | | `keyword_id` | Identificador de palabra clave | | `keyword_name` | Nombre de la palabra clave | | `asa_org_id` | ID de organización de Apple Search Ads | | `asa_keyword_match_type` | Tipo de concordancia de palabra clave ASA (`Exact`, `Broad`) | | `asa_attribution` | Datos de atribución de ASA (cadena JSON) | | `asa_conversion_type` | Tipo de conversión de ASA | | `asa_country_or_region` | País o región de ASA | | `asa_creative_set_name` | Nombre del conjunto creativo de ASA | | `fbclid` | Facebook Click ID | | `ttclid` | TikTok Click ID | | `utm_source` | Parámetro UTM source | | `utm_medium` | Parámetro UTM medium | | `utm_campaign` | Parámetro UTM campaign | | `utm_term` | Parámetro UTM term | | `utm_content` | Parámetro UTM content | --- # File: ua-amazon-s3 --- --- title: "Amazon S3" description: "Exporta datos de adquisición de usuarios a S3 para análisis e informes avanzados." --- La integración de Adapty UA con Amazon S3 te permite almacenar de forma segura los datos de tus campañas de adquisición de usuarios en un único lugar centralizado. Podrás guardar datos de rendimiento de campañas, datos de atribución y eventos de adquisición de usuarios en tu bucket de Amazon S3 como archivos .csv. Para configurar esta integración, deberás seguir unos sencillos pasos en la AWS Console y en el dashboard de Adapty UA. :::note Adapty UA envía tus datos cada **24h** a las 4:00 UTC. Cada archivo contendrá datos de los eventos creados durante el día natural anterior completo en UTC. Por ejemplo, los datos exportados automáticamente a las 4:00 UTC del 8 de marzo contendrán todos los eventos creados el 7 de marzo desde las 00:00:00 hasta las 23:59:59 UTC. ::: ## Cómo configurar la integración con Amazon S3 \{#how-to-set-up-amazon-s3-integration\} Para empezar a recibir datos, necesitarás las siguientes credenciales: 1. Access key ID 2. Secret access key 3. Nombre del bucket de S3 4. Nombre de la carpeta dentro del bucket de S3 :::note Directorios anidados Puedes especificar directorios anidados en el campo del nombre del bucket de Amazon S3, p. ej. adapty-ua-events/com.sample-app ::: ### Paso 1. Crear las credenciales de Amazon S3 \{#step-1-create-amazon-s3-credentials\} Esta guía te ayudará a crear las credenciales necesarias en tu AWS Console. #### 1.1. Crear una política de acceso \{#11-create-access-policy\} 1. Ve al [IAM Policy Dashboard](https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/policies) en tu AWS Console 2. Selecciona la opción **Create Policy** <img src="/assets/shared/img/7af075c-CleanShot_2023-03-21_at_10.52.002x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En el editor de políticas, pega el siguiente JSON y cambia `adapty-s3-integration-test` por el nombre de tu bucket: ```json showLineNumbers title="Json" { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowListObjectsInBucket", "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::adapty-s3-integration-test" }, { "Sid": "AllowAllObjectActions", "Effect": "Allow", "Action": "s3:*Object", "Resource": [ "arn:aws:s3:::adapty-s3-integration-test/*", "arn:aws:s3:::adapty-s3-integration-test" ] }, { "Sid": "AllowBucketLocation", "Effect": "Allow", "Action": "s3:GetBucketLocation", "Resource": "arn:aws:s3:::adapty-s3-integration-test" } ] } ``` <img src="/assets/shared/img/d4e474a-CleanShot_2023-03-21_at_10.56.212x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Tras completar la configuración de la política, puedes añadir etiquetas (opcional) y luego hacer clic en **Next** para pasar al paso final 5. En este paso, asigna un nombre a tu política y haz clic en el botón **Create policy** para finalizar el proceso de creación <img src="/assets/shared/img/7dcb02f-CleanShot_2023-03-21_at_11.03.372x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### 1.2. Crear un usuario IAM \{#12-create-iam-user\} Para que Adapty UA pueda subir informes de datos sin procesar a tu bucket, necesitarás proporcionarle el Access Key ID y el Secret Access Key de un usuario con acceso de escritura al bucket correspondiente. 1. Ve a la IAM Console y selecciona la [sección Users](https://console.aws.amazon.com/iamv2/home#/users) 2. Haz clic en el botón **Add users** <img src="/assets/shared/img/bb612c8-CleanShot_2023-03-21_at_11.12.392x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Asigna un nombre al usuario, elige **Access key – Programmatic access** y continúa con los permisos <img src="/assets/shared/img/467ee4d-j6aoX.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En el siguiente paso, selecciona la opción **Add user to group** y luego haz clic en el botón **Create group** <img src="/assets/shared/img/bfd0e80-CleanShot_2023-03-21_at_11.24.592x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. A continuación, asigna un nombre a tu User Group y selecciona la política que creaste anteriormente 6. Una vez seleccionada la política, haz clic en el botón **Create group** para completar el proceso <img src="/assets/shared/img/df29c12-CleanShot_2023-03-21_at_11.28.052x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Después de crear el grupo correctamente, **selecciónalo** y continúa al siguiente paso <img src="/assets/shared/img/1f3722e-CleanShot_2023-03-21_at_11.36.192x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 8. Como este es el último paso de esta sección, puedes continuar simplemente haciendo clic en el botón **Create User** <img src="/assets/shared/img/ea43722-CleanShot_2023-03-21_at_11.40.462x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 9. Por último, puedes **descargar las credenciales en formato .csv** o bien copiarlas y pegarlas directamente desde el dashboard <img src="/assets/shared/img/bcf35e1-S3created.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Paso 2. Configurar la integración en Adapty UA \{#step-2-configure-integration-in-adapty-ua\} 1. Ve a [**Integrations** -> **Amazon S3**](https://app.adapty.io/ua/integrations/s3) 2. Activa el toggle **Export install events to Amazon S3**. 3. Rellena los siguientes campos para establecer la conexión entre Amazon S3 y los perfiles de Adapty UA: | Campo | Descripción | |:-----------------------------| :----------------------------------------------------------- | | **Access Key ID** | Identificador único que se usa para autenticar el acceso de un usuario o aplicación a un servicio de AWS. Encuéntralo en el [archivo csv](ua-amazon-s3#step-1-create-amazon-s3-credentials) descargado. | | **Secret Access Key** | Clave privada que se usa junto con el Access Key ID para autenticar el acceso de un usuario o aplicación a un servicio de AWS. Encuéntrala en el [archivo csv](ua-amazon-s3#step-1-create-amazon-s3-credentials) descargado. | | **S3 Bucket Name** | Nombre único global que identifica un bucket de S3 específico dentro de la nube de AWS. Los buckets de S3 son un servicio de almacenamiento simple que permite guardar y recuperar objetos de datos, como archivos e imágenes, en la nube. | | **Folder Inside the Bucker** | El nombre de la carpeta que quieres tener dentro del bucket de S3 seleccionado. Ten en cuenta que S3 simula carpetas usando prefijos de clave de objeto, que son esencialmente nombres de carpeta. | | **Region** (Opcional) | Obtén tu región en la AWS Management Console bajo tu cuenta de usuario IAM. | ## Exportación manual de datos \{#manual-data-export\} Además de la exportación automática de datos de eventos a Amazon S3, Adapty UA también ofrece la funcionalidad de exportación manual de archivos. Con esta función, puedes seleccionar una fecha concreta para los datos de adquisición de usuarios y exportarlos manualmente a tu bucket de S3. Esto te da mayor control sobre qué datos exportas y cuándo lo haces. ## Estructura de la tabla \{#table-structure\} En la integración con AWS S3, Adapty UA proporciona una tabla para almacenar el historial de datos de eventos de instalación. La tabla contiene información sobre el perfil del usuario, los ingresos y beneficios netos, y la store de origen, entre otros datos. :::warning Ten en cuenta que esta estructura puede crecer con el tiempo, con nuevos datos introducidos por nosotros o por terceros con los que trabajamos. Asegúrate de que el código que la procesa sea lo suficientemente robusto y se base en campos concretos, no en la estructura en su conjunto. ::: Esta es la estructura de la tabla para los eventos: | Columna | Descripción | |--------------------------|-------------------------------------------| | `adapty_profile_id` | Identificador único del perfil de Adapty | | `install_id` | Identificador único de instalación | | `created_at` | Marca de tiempo de creación del registro (ISO 8601) | | `installed_at` | Marca de tiempo de instalación de la app (ISO 8601) | | `store` | App store (`ios`, `android`) | | `country` | Código de país del usuario (ISO 3166-1 alpha-2) | | `ip_address` | Dirección IP del cliente | | `idfa` | iOS Identifier for Advertisers | | `idfv` | iOS Identifier for Vendors | | `gaid` | Google Advertising ID (Android) | | `android_id` | ID de dispositivo Android | | `app_set_id` | Android App Set ID | | `channel` | Canal de atribución | | `campaign_id` | Identificador de campaña | | `campaign_name` | Nombre de la campaña | | `adset_id` | Identificador del conjunto de anuncios | | `adset_name` | Nombre del conjunto de anuncios | | `ad_id` | Identificador del anuncio | | `ad_name` | Nombre del anuncio | | `keyword_id` | Identificador de palabra clave | | `keyword_name` | Nombre de la palabra clave | | `asa_org_id` | ID de organización de Apple Search Ads | | `asa_keyword_match_type` | Tipo de coincidencia de palabra clave de ASA (`Exact`, `Broad`) | | `asa_attribution` | Datos de atribución de ASA (cadena JSON) | | `asa_conversion_type` | Tipo de conversión de ASA | | `asa_country_or_region` | País o región de ASA | | `asa_creative_set_name` | Nombre del conjunto creativo de ASA | | `fbclid` | Facebook Click ID | | `ttclid` | TikTok Click ID | | `utm_source` | Parámetro de fuente UTM | | `utm_medium` | Parámetro de medio UTM | | `utm_campaign` | Parámetro de campaña UTM | | `utm_term` | Parámetro de término UTM | | `utm_content` | Parámetro de contenido UTM | --- # File: ua-google-cloud-storage --- --- title: "Google Cloud Storage" description: "Integra Google Cloud Storage con Adapty UA para almacenar de forma segura los datos de adquisición de usuarios." --- La integración de Adapty UA con Google Cloud Storage te permite almacenar los datos de tus campañas de adquisición de usuarios de forma segura en un único lugar centralizado. Podrás guardar los datos de rendimiento de tus campañas, los datos de atribución y los eventos de adquisición de usuarios en tu bucket de Google Cloud Storage como archivos .csv. Para configurar esta integración, deberás seguir unos sencillos pasos en la consola de Google Cloud y en el dashboard de Adapty UA. :::note Programación Adapty UA envía tus datos a Google Cloud Storage cada 24h a las 4:00 UTC. Cada archivo contendrá los datos de los eventos creados durante el día natural anterior completo en UTC. Por ejemplo, los datos exportados automáticamente a las 4:00 UTC del 8 de marzo incluirán todos los eventos creados el 7 de marzo desde las 00:00:00 hasta las 23:59:59 en UTC. ::: ## Cómo configurar la integración con Google Cloud Storage \{#how-to-set-up-google-cloud-storage-integration\} ### Paso 1. Crear las credenciales de Google Cloud Storage \{#step-1-create-google-cloud-storage-credentials\} Esta guía te ayudará a crear las credenciales necesarias en la consola de Google Cloud Platform. Para que Adapty UA pueda subir informes de datos sin procesar a tu bucket designado, se necesita la clave de la cuenta de servicio, así como acceso de escritura al bucket correspondiente. Al proporcionar la clave de la cuenta de servicio y conceder acceso de escritura al bucket, permites que Adapty UA transfiera de forma segura y eficiente los informes de datos sin procesar desde su plataforma a tu entorno de almacenamiento. :::warning Ten en cuenta que solo admitimos la autorización mediante clave HMAC de cuenta de servicio, por lo que es imprescindible que tu clave HMAC de cuenta de servicio tenga asignados los roles "Storage Object Viewer", "Storage Legacy Bucket Writer" y "Storage Object Creator" para habilitar el acceso correcto a Google Cloud Storage. ::: #### 2.1. Crear una cuenta de servicio \{#21-create-service-account\} 1. Ve a la sección [IAM](https://console.cloud.google.com/projectselector2/iam-admin/serviceaccounts) de tu cuenta de Google Cloud y selecciona el proyecto correspondiente o crea uno nuevo <img src="/assets/shared/img/30a81ef-CleanShot_2023-03-17_at_15.22.142x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. A continuación, crea una nueva cuenta de servicio para Adapty UA haciendo clic en el botón "+ CREATE SERVICE ACCOUNT" <img src="/assets/shared/img/98f8ebf-CleanShot_2023-03-17_at_15.40.062x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Rellena los campos del primer paso, ya que el acceso se concederá en una etapa posterior. Para obtener más información sobre esta página, consulta la documentación [aquí](https://docs.cloud.google.com/iam/docs/service-accounts-create) <img src="/assets/shared/img/2190c50-CleanShot_2023-03-17_at_15.48.552x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Para crear y descargar una [clave JSON privada](https://docs.cloud.google.com/iam/docs/keys-create-delete), ve a la sección KEYS y haz clic en el botón "ADD KEY" <img src="/assets/shared/img/8a45468-CleanShot_2023-03-17_at_15.58.092x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. En la sección DETAILS, localiza el valor de Email vinculado a la cuenta de servicio recién creada y cópialo. Esta información será necesaria en los próximos pasos para autorizar la cuenta y permitirle escribir en el bucket <img src="/assets/shared/img/6ccd0f0-CleanShot_2023-03-17_at_16.03.162x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### 2.2. Configurar los permisos del bucket \{#22-configure-bucket-permissions\} 6. Ve a la página de [Buckets](https://console.cloud.google.com/storage/browser) de Google Cloud Storage y selecciona un bucket existente o crea uno nuevo para almacenar los informes de datos de adquisición de usuarios de Adapty UA 7. Ve a la sección PERMISSIONS y selecciona la opción [GRANT ACCESS](https://docs.cloud.google.com/identity/docs/how-to?hl=en) <img src="/assets/shared/img/3cdd937-CleanShot_2023-03-17_at_16.14.232x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 8. En la sección PERMISSIONS, introduce el Email de la cuenta de servicio obtenido en el quinto paso anterior y selecciona el rol Storage Object Creator 9. Por último, haz clic en SAVE para aplicar los cambios <img src="/assets/shared/img/62801f4-CleanShot_2023-03-17_at_16.17.312x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 10. Recuerda guardar el nombre del bucket para futuras referencias 11. Tras completar estos pasos, habrás finalizado la configuración necesaria en la consola de Google Cloud. El último paso consiste en introducir el nombre del bucket y descargar el archivo JSON para usarlo en Adapty UA <img src="/assets/shared/img/c967e16-CleanShot_2023-03-17_at_16.23.332x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Paso 2. Configurar la integración en Adapty UA \{#step-2-configure-integration-in-adapty-ua\} 1. Ve a [**Integrations** -> **Google Cloud Storage**](https://app.adapty.io/ua/integrations/google-cloud-storage) 2. Activa el toggle **Export install events to Google Cloud Storage** 3. Rellena los campos requeridos para establecer la conexión entre Google Cloud Storage y Adapty UA: | Campo | Descripción | |:------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Google Cloud service account key file** | El [archivo de clave JSON](ua-google-cloud-storage#step-1-create-google-cloud-storage-credentials) privado descargado. | | **Google Cloud bucket name** | El nombre del bucket en Google Cloud Storage donde deseas almacenar tus datos. Debe ser único dentro del entorno de Google Cloud Storage y no puede contener espacios. | | **Folder inside the bucket** | El nombre de la carpeta dentro del bucket donde deseas almacenar tus datos. Debe ser único dentro del bucket y puede usarse para organizar los datos. Este campo es opcional. | ## Exportación manual de datos \{#manual-data-export\} Además de la exportación automática de datos de eventos a Google Cloud Storage, Adapty UA también ofrece una funcionalidad de exportación manual de archivos. Con esta función, puedes seleccionar una fecha concreta para los datos de adquisición de usuarios y exportarlos manualmente a tu bucket de GCS. Esto te da mayor control sobre los datos que exportas y cuándo lo haces. ## Estructura de la tabla \{#table-structure\} En la integración con Google Cloud Storage, Adapty UA proporciona una tabla para almacenar el historial de datos de eventos de instalación. La tabla contiene información sobre el perfil del usuario, los ingresos y beneficios, y el store de origen, entre otros datos. :::warning Ten en cuenta que esta estructura puede crecer con el tiempo, con nuevos datos introducidos por nosotros o por terceros con los que trabajamos. Asegúrate de que el código que procesa estos datos sea lo suficientemente robusto y se base en campos específicos, no en la estructura en su conjunto. ::: A continuación se muestra la estructura de la tabla para los eventos: | Columna | Descripción | |--------------------------|-----------------------------------------------------| | `adapty_profile_id` | Identificador único de perfil de Adapty | | `install_id` | Identificador único de instalación | | `created_at` | Marca de tiempo de creación del registro (ISO 8601) | | `installed_at` | Marca de tiempo de instalación de la app (ISO 8601) | | `store` | Store de la app (`ios`, `android`) | | `country` | Código de país del usuario (ISO 3166-1 alpha-2) | | `ip_address` | Dirección IP del cliente | | `idfa` | Identificador de iOS para anunciantes | | `idfv` | Identificador de iOS para proveedores | | `gaid` | ID de publicidad de Google (Android) | | `android_id` | ID de dispositivo Android | | `app_set_id` | ID de conjunto de apps de Android | | `channel` | Canal de atribución | | `campaign_id` | Identificador de campaña | | `campaign_name` | Nombre de campaña | | `adset_id` | Identificador de conjunto de anuncios | | `adset_name` | Nombre del conjunto de anuncios | | `ad_id` | Identificador de anuncio | | `ad_name` | Nombre del anuncio | | `keyword_id` | Identificador de palabra clave | | `keyword_name` | Nombre de la palabra clave | | `asa_org_id` | ID de organización de Apple Search Ads | | `asa_keyword_match_type` | Tipo de concordancia de palabra clave de ASA (`Exact`, `Broad`) | | `asa_attribution` | Datos de atribución de ASA (cadena JSON) | | `asa_conversion_type` | Tipo de conversión de ASA | | `asa_country_or_region` | País o región de ASA | | `asa_creative_set_name` | Nombre del conjunto creativo de ASA | | `fbclid` | ID de clic de Facebook | | `ttclid` | ID de clic de TikTok | | `utm_source` | Parámetro de fuente UTM | | `utm_medium` | Parámetro de medio UTM | | `utm_campaign` | Parámetro de campaña UTM | | `utm_term` | Parámetro de término UTM | | `utm_content` | Parámetro de contenido UTM | --- # File: adapty-mail --- --- title: "Adapty Mail" description: "AI-generated email campaigns that turn trial users into paid subscribers." --- <CustomDocCardList ids={['mail-get-started', 'mail-collect-emails', 'mail-sending-domain', 'mail-create-campaign', 'mail-analytics']} /> Adapty Mail turns your Adapty user data into AI-generated email sequences that convert trial users into paid subscribers. It uses the profile data already in your Adapty project to build, send, and attribute campaigns — no separate email platform required. ## Why Adapty Mail? Sending targeted email campaigns requires copy, design, sending infrastructure, and revenue attribution. Each of these is a separate problem to solve. Adapty Mail handles all of it. Paste your App Store link and get a complete email sequence in under 2 minutes, sent from your own domain with personalized checkout links and purchase attribution. ## How it works 1. **Collect emails**: Your app passes user emails and `customer_user_id` values to Adapty via SDK. Adapty Mail uses this data to identify recipients and attribute revenue to the specific email that drove each purchase. 2. **Build a web paywall**: The checkout page every email links to. 3. **Generate a sequence**: AI analyzes your App Store listing and produces 3–15 emails: copy, design, hero images, and personalized checkout links, all matched to your app's category and brand. 4. **Launch a flow**: Pick a trigger (for example, subscription expired, subscription refunded, or never purchased) and a segment, then attach your campaign. Emails start sending automatically, and revenue from email-driven purchases is attributed back to the specific email that drove the conversion. ## Requirements To use Adapty Mail, you need: - An Adapty account - Email collection set up in your app — see [Collect user emails](mail-collect-emails) - `customer_user_id` configured in your Adapty SDK - A domain you control with access to its DNS settings - A web payment provider (Stripe, Paddle, or PayPal) ## Get started Follow the [Get started with Adapty Mail](mail-get-started) guide to complete setup and launch your first campaign. --- # File: mail-get-started --- --- title: "Primeros pasos con Adapty Mail" description: "Configura Adapty Mail y lanza tu primer flujo de emails." --- En esta guía, configurarás Adapty Mail y lanzarás tu primer flujo de emails. La configuración tiene seis partes: 1. [Configura tu SDK de Adapty](#1-configure-your-adapty-sdk) 2. [Conecta Adapty Mail con los datos de tu app](#2-connect-adapty-mail-to-your-app-data) 3. [Configura tu dominio de envío](#3-set-up-your-sending-domain) 4. [Crea un web paywall](#4-create-a-web-paywall) 5. [Genera una campaña con IA](#5-generate-a-campaign-with-ai) 6. [Lanza un flujo](#6-launch-a-flow) ## Antes de empezar \{#before-you-start\} Asegúrate de tener esto listo antes de comenzar: - **Acceso a DNS**: Puedes añadir registros a tu dominio raíz. - **Proveedor de pagos web**: Tienes una cuenta de Stripe, Paddle o PayPal con tus productos de suscripción configurados. ## 1. Configura tu SDK de Adapty \{#1-configure-your-adapty-sdk\} :::important Adapty Mail es un **producto independiente**. Puedes usarlo aunque tus paywalls, suscripciones o analíticas no estén gestionados por Adapty — no es necesario migrar todo tu stack. Para obtener datos de ingresos precisos, la configuración mínima es instalar el SDK de Adapty en modo observador y activar las notificaciones del servidor de App Store. ::: Adapty Mail necesita tres cosas de tu app: datos de compras (para atribuir los ingresos al email que generó cada conversión), un identificador de usuario estable y los emails de los usuarios. 1. **Permite que Adapty registre tus ingresos.** El primer paso depende de si ya tienes compras in-app implementadas: - Si **ya tienes compras in-app implementadas con Adapty**, no necesitas hacer nada más en este punto. - Si **ya tienes compras in-app implementadas sin Adapty** y no planeas migrar a Adapty, instala el SDK de Adapty para tu plataforma en modo observador. En este punto solo necesitas añadir el SDK a tu proyecto, activarlo con el modo observador habilitado y reportar las transacciones. Guías por plataforma: [iOS](implement-observer-mode), [Android](implement-observer-mode-android), [React Native](implement-observer-mode-react-native), [Flutter](implement-observer-mode-flutter), [Unity](implement-observer-mode-unity), [Kotlin Multiplatform](implement-observer-mode-kmp), [Capacitor](implement-observer-mode-capacitor). - Si **todavía no tienes compras in-app implementadas y quieres usar Adapty**, completa los pasos de la [guía de inicio rápido](quickstart) para delegar la gestión de compras a Adapty. Luego [activa las notificaciones del servidor de App Store en Adapty](enable-app-store-server-notifications) para recibir actualizaciones relacionadas con ingresos directamente desde App Store. 2. **Configura la identificación de usuarios.** Pasa un ID estable — el ID de usuario de tu backend, Firebase UID u otro similar — ya sea llamando a `Adapty.identify()` o pasando `customerUserId` a `.activate()` al arrancar el SDK. El `customer_user_id` es la forma en que Adapty Mail relaciona campañas, clics y compras con el perfil correcto. Guías por plataforma: [iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), [Unity](unity-identifying-users), [Kotlin Multiplatform](kmp-identifying-users), [Capacitor](capacitor-identifying-users). 3. **Recoge los emails de los usuarios.** En cuanto un usuario proporcione su email en tu app (por ejemplo, al registrarse o en el checkout), pásaselo a Adapty llamando a `updateProfile` con el atributo de email. Cada destinatario de una campaña necesita este valor. Guías por plataforma: [iOS](setting-user-attributes), [Android](android-setting-user-attributes), [React Native](react-native-setting-user-attributes), [Flutter](flutter-setting-user-attributes), [Unity](unity-setting-user-attributes), [Kotlin Multiplatform](kmp-setting-user-attributes), [Capacitor](capacitor-setting-user-attributes). Si tu app todavía no recoge emails, consulta [Estrategias para recopilar emails](mail-collect-emails#email-collection-strategies). ## 2. Conecta Adapty Mail con los datos de tu app \{#2-connect-adapty-mail-to-your-app-data\} Con el SDK integrado, dos ajustes del dashboard conectan tu app con Adapty Mail para que el producto pueda usar lo que el SDK envía. 1. **Activa la integración con Adapty.** En Adapty Mail, ve a **Settings → Integrations** y activa la integración de **Adapty**. Al habilitarla, Adapty envía información sobre todos los eventos generados por tus clientes — nuevas suscripciones, renovaciones, pruebas, conversiones, reembolsos e incidencias de facturación. Estos eventos determinan directamente en qué segmento cae un cliente, a qué campaña se le asigna y si el envío de emails se detiene en función de su estado de compra. 2. **Añade la URL de App Store o Google Play.** En Adapty Mail, ve a **Settings → App metadata** y pega una URL directa de la store — `https://apps.apple.com/…id{numbers}` para iOS o `https://play.google.com/store/apps/details?id={package}` para Android. ## 3. Configura tu dominio de envío \{#3-set-up-your-sending-domain\} Adapty Mail envía desde tu propio dominio. Añades los registros DNS una sola vez — todas las campañas usan el mismo dominio verificado. 1. En Adapty Mail, ve a **Settings → Email Domains**. 2. Introduce tu dominio raíz (por ejemplo, `yourapp.com`) y haz clic en **Preview**. Solo se aceptan dominios apex — los subdominios como `app.yourapp.com` se rechazan al introducirlos. 3. Adapty genera dos subdominios de envío (`mail.yourapp.com` y `email.yourapp.com`). Haz clic en **Confirm** para ver los registros DNS necesarios. 4. En tu registrador de dominio, añade los 10 registros DNS que se muestran (5 por subdominio): - 3 registros CNAME (DKIM) por subdominio - 1 registro MX (Mail-From) por subdominio - 1 registro TXT (SPF, `v=spf1 include:amazonses.com ~all`) por subdominio 5. Opcionalmente, añade un registro DMARC TXT en tu dominio raíz (recomendado). 6. Vuelve a **Settings → Email Domains** y haz clic en **Check Verification**. Tiempos de verificación de un vistazo: - **Comprobación automática**: La primera verificación se ejecuta unos 5 minutos después de que envíes los registros. Los intervalos aumentan hasta una vez por hora hasta que se encuentran los registros. - **Comprobación manual**: Haz clic en **Check Verification** en cualquier momento para lanzar una verificación inmediata. - **Propagación DNS**: Normalmente en minutos, hasta 48 horas en casos excepcionales. - **Ventana de verificación**: 7 días. Si caduca, tus registros DNS se mantienen — vuelve a introducir tu dominio en **Settings → Email Domains** para abrir una nueva ventana. Para más detalles sobre cada tipo de registro y el calentamiento del dominio, consulta [Configura tu dominio de envío](mail-sending-domain). ## 4. Crea un web paywall \{#4-create-a-web-paywall\} Cada email enlaza a un web paywall — la página de checkout a la que llegan los usuarios al hacer clic en un CTA. Tienes dos opciones: - **Generar con IA**: Deja que el editor de web paywalls integrado cree uno para tu app. - **Usar tu propio paywall alojado**: Conecta un paywall que ya tienes publicado. Para empezar, en Adapty Mail ve a **Web Paywalls → Create**. ### Opción A: Generar con IA \{#option-a-generate-with-ai\} 1. Selecciona **Generate with AI**. 2. Haz clic en **Log in to the paywall builder**. El editor de web paywalls se abre en una nueva pestaña. Si todavía no has iniciado sesión, hazlo con tus credenciales de Adapty. 3. En el editor, activa la integración con tu proveedor de pagos (Stripe, Paddle o PayPal). Consulta [Configuración del web paywall](web-paywall-configuration) para más detalles. 4. Vuelve a Adapty Mail y haz clic en **Proceed to generation**. 5. Revisa el paywall generado, luego guárdalo y publícalo. ### Opción B: Usar tu propio paywall alojado \{#option-b-use-your-own-hosted-paywall\} 1. Selecciona **Enter URL manually**. 2. Pega la URL de tu paywall alojado. La URL debe incluir los marcadores `{email}` y `{external_profile_id}` como parámetros de consulta — Adapty Mail los rellena por destinatario para que la página de checkout sepa quién es el visitante. Ejemplo: ``` https://example.com/paywall?email={email}&profile={external_profile_id} ``` 3. Guarda y publica. :::important El paywall debe estar publicado antes de poder gestionar el tráfico de checkout. Los paywalls sin publicar devuelven un error cuando los usuarios hacen clic en los enlaces de checkout del email. ::: Para entender la anatomía del embudo de checkout y cómo funciona la personalización, consulta [Configura el checkout](mail-checkout). ## 5. Genera una campaña con IA \{#5-generate-a-campaign-with-ai\} La IA crea la secuencia completa de emails por ti — textos, diseño, imágenes de cabecera y enlaces de checkout personalizados, todo adaptado a tu app. 1. En Adapty Mail, ve a **Campaigns** y haz clic en **Create**. 2. Establece el nombre de la campaña. 3. En el desplegable **Web paywall**, selecciona el web paywall que añadiste en el paso anterior. 4. Haz clic en **Generate emails**. 5. En el diálogo de generación, elige un tono y un idioma. Por defecto, la IA determina el número de emails a generar según las mejores prácticas y el contexto de la app. Si quieres fijar el número tú mismo, haz clic en **Set number manually** e indícalo (**1–15**, por defecto 4). 6. Haz clic en **Generate**. La generación suele tardar unos minutos. El sistema agota el tiempo de espera a los 5 minutos si no puede completarla — vuelve a intentarlo si ocurre. 7. Previsualiza cada email. Puedes regenerar emails individuales, editar el texto o abrir el editor HTML para un control más preciso. 8. Haz clic en **Create** para guardar la campaña. La campaña se guarda como **borrador** y todavía no está enviando — las campañas se activan solo cuando se vinculan a un flujo (siguiente paso). No hay ninguna acción de "publicar" separada en el editor de campañas. ## 6. Lanza un flujo \{#6-launch-a-flow\} Un flujo combina un **disparador** (un evento como la expiración de una suscripción) con un **segmento**, y envía a ese segmento la **campaña** que elijas. Adapty Mail viene con tres disparadores fijos, cada uno con su propia vista de flujo. 1. En Adapty Mail, ve a **Flows** y abre el disparador que quieras configurar: - **Never purchased** — usuarios que se registraron pero todavía no han comprado. - **Expired** — suscripciones que han terminado. - **Refunded** — compras que han sido reembolsadas. 2. Haz clic en **Create** para abrir el diálogo. 3. En el diálogo: - Elige un **Segment** (por ejemplo, **All Users** para dirigirte a todos los que alcancen este disparador, o crea un nuevo segmento basado en los atributos del perfil). - Deja el tipo de contenido en **Campaign** (la opción de prueba A/B se explica en [Pruebas A/B](mail-ab-testing)). - Selecciona la **Campaign** que guardaste en el paso 5. 4. Haz clic en **Save**. El flujo se activa de inmediato — no hay un paso de lanzamiento separado. A partir de este momento, los usuarios que coincidan con el segmento empezarán a recibir la campaña en cuanto se produzca el evento disparador. :::note Puedes añadir más de una fila segmento → campaña al mismo disparador; se ejecutan por orden de prioridad. La fila **All Users**, si se usa, debe ser la última (de menor prioridad) para capturar a todos los que no coincidan con un segmento más específico. ::: ## Solución de problemas \{#troubleshooting\} | Problema | Solución | | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | Verificación DNS atascada | Comprueba que los registros coinciden exactamente — sin puntos finales, con los destinos CNAME correctos. Espera 5–10 minutos y haz clic en **Check Verification** de nuevo | | Ventana de verificación caducada | Tus registros siguen en su lugar. Vuelve a introducir tu dominio en **Settings → Email Domains** para abrir una nueva ventana | | Generación fallida o tiempo de espera agotado | Comprueba tu conexión a internet e inténtalo de nuevo. Si el problema persiste, contacta con el soporte de Adapty | ## Más información \{#learn-more\} - **[Recopilar emails de usuarios](mail-collect-emails)**: Estrategias para conseguir cobertura de emails si tu app todavía no los recoge. - **[Configura tu dominio de envío](mail-sending-domain)**: Detalles de los registros DNS, niveles de calentamiento y solución de problemas. - **[Configura el checkout](mail-checkout)**: Anatomía del embudo de checkout y personalización. - **[Analíticas de campañas](mail-analytics)**: Monitoriza la entrega, el engagement y los ingresos. - **[Pruebas A/B](mail-ab-testing)**: Prueba múltiples versiones de secuencias. --- # File: mail-collect-emails --- --- title: "Recopilar emails de usuarios para Adapty Mail" description: "Pasa emails de usuarios e identificadores estables a Adapty para que las campañas lleguen a tus usuarios." --- Adapty Mail necesita un `customer_user_id` estable y un email por cada usuario al que envía mensajes. Configura ambos en el código de tu app antes de lanzar una campaña. ## Recopilar emails de usuarios \{#collect-user-emails\} Deben llegar a Adapty dos valores por usuario: un `customer_user_id` estable que identifique al usuario y el email en sí. La identificación tiene que ir primero — sin ella, Adapty no tiene ningún perfil al que asociar el email. 1. **Identifica al usuario.** Pasa un ID estable — el ID de usuario de tu backend, el UID de Firebase o similar — ya sea pasándolo como `customerUserId` a `.activate()` al iniciar el SDK, o llamando a `Adapty.identify()` más tarde (por ejemplo, al iniciar sesión). En cualquier caso, el ID debe establecerse antes de mostrar cualquier paywall. Guías por plataforma: [iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), [Unity](unity-identifying-users), [Kotlin Multiplatform](kmp-identifying-users), [Capacitor](capacitor-identifying-users). 2. **Pasa el email.** En cuanto el usuario proporcione su email, envíalo a Adapty mediante `updateProfile` usando el parámetro `email`. Guías por plataforma: [iOS](setting-user-attributes), [Android](android-setting-user-attributes), [React Native](react-native-setting-user-attributes), [Flutter](flutter-setting-user-attributes), [Unity](unity-setting-user-attributes), [Kotlin Multiplatform](kmp-setting-user-attributes), [Capacitor](capacitor-setting-user-attributes). :::important - Pasa siempre un `customer_user_id` **estable**, nunca un identificador anónimo. Si un usuario desinstala y vuelve a instalar tu app, Adapty usa este ID para vincular la reinstalación al perfil existente y atribuir las compras al usuario correcto. - Obtén el consentimiento explícito del usuario antes de recopilar y enviar emails a Adapty. Eres responsable del cumplimiento del RGPD, CAN-SPAM y normativas similares en tus mercados objetivo. ::: <Details> <summary>Verifica la cobertura de tu email</summary> Tras implementar la recopilación, comprueba la cobertura en Adapty: 1. Ve a **Customers → Profiles**. 2. Filtra los perfiles que tengan un email configurado. Apunta a al menos un 30–50 % de cobertura de email entre tus usuarios activos antes de lanzar tu primera campaña. No necesitas esperar al 100 % — lanza en cuanto alcances el 30 %. Los usuarios que proporcionen su email más adelante se inscriben automáticamente en las campañas activas cuando cumplen los requisitos. </Details> ## Estrategias de recopilación de emails \{#email-collection-strategies\} La mayoría de las apps no recopilan emails por defecto. Elige el enfoque que mejor se adapte al estado actual de tu app. | Estrategia | Ideal para | Cómo funciona | | ------------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Autenticación existente** | Apps con cualquier tipo de inicio de sesión | Ya tienes el email — pásalo a Adapty después de que el usuario se autentique. Consulta la referencia de métodos de autenticación más abajo para saber dónde leerlo. | | **Puerta de email antes del paywall** | Apps sin autenticación — salud, bienestar, astrología, editores de fotos | Añade una pantalla de entrada de email entre el onboarding y el paywall. La conversión suele ser del 70–90 % porque los usuarios ya han invertido tiempo. | | **Checkout del web paywall builder** | Mínimo trabajo de SDK; email capturado en web | La primera pantalla del web paywall builder recopila el email y lo pasa a Adapty — útil para usuarios que hacen clic en una campaña antes de que haya una puerta in-app activa. | | **Paso de onboarding** | Onboarding basado en cuestionarios (fitness, nutrición, educación) | Coloca un campo de entrada de email 2–3 pasos dentro del onboarding. Preséntalo como entrega de valor ("Te enviaremos tu plan personalizado por email") y evita que el paso sea omitible. | | **Importación mediante API del servidor** | Apps con una lista de emails existente en otra plataforma | Usa el endpoint [Update profile](api-adapty/operations/updateProfile) con `email` y `external_profile_id` para hacer coincidir los registros con los perfiles existentes. | ## Limitaciones \{#limitations\} - **Usuarios anónimos**: Los usuarios sin un `customer_user_id` estable no pueden recibir campañas. Identifícalos cuando creen una cuenta o inicien sesión — a partir de ese momento, cualquier email que proporcionen se asocia a su perfil de Adapty. - **Usuarios sin email**: Los perfiles sin email quedan excluidos de la entrega de campañas y no aparecen en los análisis de campañas. En cuanto proporcionen un email, pasan a ser elegibles para futuras campañas. --- # File: mail-sending-domain --- --- title: "Configura tu dominio de envío para Adapty Mail" description: "Añade registros DNS, verifica tu dominio y entiende el proceso de warm-up para que Adapty Mail pueda enviar en tu nombre." --- Adapty Mail envía campañas desde tu propio dominio, no desde una dirección compartida, para que la reputación del remitente quede bajo tu control. Esta configuración se realiza una sola vez y todas las campañas usarán el mismo dominio verificado. Para los pasos mínimos, consulta la sección de dominio en [Primeros pasos con Adapty Mail](mail-get-started#3-set-up-your-sending-domain). Este artículo cubre la configuración completa, el funcionamiento de la verificación y el comportamiento automático del warm-up. ## Requisitos \{#requirements\} - **Dominio raíz (apex)**: introduce tu dominio raíz (por ejemplo, `yourapp.com`), no un subdominio. Las entradas como `app.yourapp.com` se rechazan en la validación. - **Registros NS activos**: el dominio debe resolver. Adapty Mail realiza una consulta DNS durante la configuración y rechaza dominios sin registros NS válidos. - **Un dominio por proyecto de Adapty**: un dominio no puede compartirse entre proyectos. Si el dominio ya está registrado en cualquier proyecto —el tuyo o el de otra persona—, la configuración falla. ## Configura tu dominio de envío \{#set-up-your-sending-domain\} El asistente de configuración tiene tres pantallas: introducir el dominio, confirmar los subdominios generados y añadir los registros DNS. Las tres se encuentran en **Settings → Email Domains**. 1. **Introduce tu dominio.** Escribe tu dominio raíz en el campo **Domain** y haz clic en **Preview**. Adapty Mail valida el formato (ASCII, dos etiquetas, sin guiones al inicio o al final, TLD de 2 o más caracteres) y comprueba que el DNS resuelve. 2. **Confirma los subdominios.** Adapty Mail genera dos subdominios de envío con prefijos fijos — `mail.yourapp.com` y `email.yourapp.com` — cada uno con su propia identidad SES. También crea un subdominio Mail-From bajo cada uno (`hello.mail.yourapp.com` y `hello.email.yourapp.com`). Revísalos y haz clic en **Confirm**. 3. **Añade los registros DNS.** La pantalla final lista todos los registros que debes añadir — 10 en total, 5 por subdominio de envío, más un registro DMARC opcional en el dominio raíz. Usa **Download CSV** para exportar la lista completa, o copia los registros uno a uno en tu registrador. Haz clic en **Done** cuando los registros estén en su lugar. <Details> <summary>Referencia de registros DNS</summary> Para cada subdominio de envío (`mail.yourapp.com` y `email.yourapp.com`), añade: **DKIM — 3 registros CNAME.** Firmas criptográficas que demuestran que el correo no fue alterado en tránsito. | Campo | Formato | | ----- | ----------------------------------- | | Type | CNAME | | Name | `{token}._domainkey.{subdomain}` | | Value | `{token}.dkim.amazonses.com` | **Mail-From — 1 registro MX.** Gestiona los rebotes. | Campo | Formato | | -------- | ---------------------------------------------------------------- | | Type | MX | | Name | `hello.{subdomain}` (por ejemplo, `hello.mail.yourapp.com`) | | Priority | `10` | | Value | `feedback-smtp.{region}.amazonses.com` | **SPF — 1 registro TXT.** Autoriza a Adapty a enviar en tu nombre. | Campo | Formato | | ----- | -------------------------------------- | | Type | TXT | | Name | `hello.{subdomain}` | | Value | `"v=spf1 include:amazonses.com ~all"` | En tu dominio raíz, añade el registro DMARC opcional: | Campo | Formato | | ----- | --------------------- | | Type | TXT | | Name | `_dmarc.{domain}` | | Value | `v=DMARC1; p=reject` | Los tokens, la región y cualquier otro valor provienen de AWS SES en el momento de la configuración. Cópialos siempre desde la pantalla de registros DNS en Adapty Mail, no desde esta referencia. </Details> ## Cómo funciona la verificación \{#how-verification-works\} Una vez añadidos los registros DNS, Adapty Mail consulta el DNS automáticamente, aunque también puedes activar comprobaciones de forma manual. - **Sondeo automático**: el sondeo comienza 5 minutos después de que envíes la solicitud y luego se duplica en cada ronda — 10 min, 20 min, 40 min — hasta alcanzar un máximo de 60 min. Continúa hasta que se encuentren los registros o se cierre la ventana de 7 días. - **Comprobación manual**: haz clic en **Check Verification** para forzar una comprobación inmediata. Hay un período de enfriamiento de 60 segundos entre comprobaciones manuales — si las realizas demasiado rápido, recibirás el mensaje *"Verification check is on cooldown."* - **Estados**: el DKIM y el Mail-From de cada subdominio se rastrean de forma independiente como **Pending**, **Success** o **Failed**. Un dominio se considera totalmente verificado solo cuando los cuatro estados muestran **Success**. - **Plazo de 7 días**: si la verificación no se completa en 7 días, la identidad se marca como **Failed**. Tus registros DNS permanecen en tu registrador — vuelve a introducir el dominio en **Settings → Email Domains** para iniciar una nueva ventana. - **Tras la verificación**: si eliminas o cambias los registros DNS más adelante, AWS SES degradará eventualmente la identidad. Mantén los registros en su lugar mientras planees seguir enviando. - **Propagación DNS**: generalmente tarda minutos; en casos excepcionales puede tardar hasta 48 horas. ## Warm-up del dominio \{#domain-warm-up\} Los dominios nuevos no tienen reputación ante proveedores de correo como Gmail o Yahoo, por lo que envíos de gran volumen desde un dominio recién creado corren el riesgo de acabar en spam. Adapty Mail gestiona el warm-up automáticamente aumentando tu límite de envíos diarios a lo largo de 14 niveles. No requiere ninguna configuración. ### Cómo funcionan los niveles \{#how-tiers-work\} Tu dominio comienza en el **Nivel 1** (200 envíos/día) y avanza automáticamente cuando las métricas de entregabilidad se mantienen saludables. Si las tasas de rebote suben o las tasas de quejas aumentan, el avance se pausa y puede revertirse hasta que la reputación se recupere. | Nivel | Límite diario | | ----- | ------------- | | 1 | 200 | | 2 | 400 | | 3 | 800 | | 4 | 1.500 | | 5 | 2.500 | | 6 | 4.000 | | 7 | 6.000 | | 8 | 8.000 | | 9 | 10.000 | | 10 | 13.000 | | 11 | 16.000 | | 12 | 20.000 | | 13 | 25.000 | | 14 | 30.000 | Tu nivel actual y tu límite diario se muestran en **Settings → Email Domains**. ### Impacto en el lanzamiento según el tamaño de la audiencia \{#impact-on-launch-by-audience-size\} | Tamaño de la audiencia | Efecto al lanzar | | ---------------------- | --------------------------------------------- | | Menos de 200 usuarios | Se alcanza a toda la audiencia el primer día | | 200–2.000 usuarios | La entrega se distribuye en varios días | | Más de 2.000 usuarios | La entrega se distribuye en 1 o 2 semanas | :::tip Lanza tu primera campaña en cuanto se complete la verificación DNS. Cuanto antes empieces a enviar, antes avanzará tu dominio por los niveles y alcanzará la capacidad diaria máxima. ::: ## Limitaciones \{#limitations\} - **Un dominio por proyecto**: solo puedes tener un dominio de envío por proyecto de Adapty. Para cambiar a un dominio diferente, contacta con soporte — el dashboard no tiene una acción de "cambiar dominio". - **Unicidad entre proyectos**: un dominio ya registrado en otro proyecto no puede reutilizarse. Si ves el mensaje *"Domain is already registered to another project"*, elige un dominio diferente o contacta con soporte. - **Los dominios verificados no pueden eliminarse**: una vez que cualquier subdominio alcanza el estado **Success**, el dashboard bloquea la eliminación. Los dominios pendientes sí pueden eliminarse, pero igualmente tendrás que eliminar manualmente los registros DNS de tu registrador. - **Prefijos de subdominio fijos**: los prefijos `mail.`, `email.` y el prefijo Mail-From `hello.` están definidos de forma fija — no se pueden personalizar. Si esos subdominios ya están en uso en tu DNS, la configuración generará conflictos. - **Solo dominios raíz (apex)**: las entradas de subdominios, los puntos finales y los nombres de host de una sola etiqueta se rechazan. - **Sin dominios internacionalizados**: no se admiten Punycode ni IDN. El dominio debe ser solo ASCII. ## Solución de problemas \{#troubleshooting\} | Problema | Solución | | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | "Enter a valid domain (e.g. example.com)" | Revisa la entrada: solo dominio raíz, solo ASCII, TLD de 2 o más caracteres, sin guiones al inicio ni al final. | | "Domain does not have valid DNS records" | El propio dominio raíz debe resolver. Confirma que tus registros NS están activos antes de volver a intentarlo. | | "Domain is already registered to another project" | Elige un dominio diferente o contacta con soporte si crees que el registro es un error. | | "Verification check is on cooldown" | Espera 60 segundos entre comprobaciones manuales. El sondeo automático continúa en segundo plano. | | Verificación atascada en Pending | Comprueba que los registros DNS coinciden exactamente — sin puntos finales, con los destinos CNAME correctos. El DNS puede tardar hasta 48 horas en propagarse. | | "Cannot delete domain: one or more identities have been successfully verified" | Un dominio verificado no puede eliminarse desde el dashboard. Contacta con soporte para obtener ayuda. | | Los correos van a spam | Confirma que tu registro DMARC está publicado. Los dominios nuevos necesitan tiempo de warm-up — consulta [Warm-up del dominio](#domain-warm-up). | | Tasa de rebote alta | Verifica que tu lista de audiencia contiene direcciones válidas con consentimiento de suscripción. Los rebotes ralentizan o pausan el avance de nivel. | --- # File: mail-checkout --- --- title: "Configurar el checkout para Adapty Mail" description: "Crea un paywall web y conecta un proveedor de pago para ofrecer un checkout web personalizado a tus campañas de correo electrónico." --- Cada correo que envía Adapty Mail contiene un enlace de checkout único para ese destinatario. Al hacer clic, el usuario llega a un embudo de checkout web que lo identifica por perfil, le presenta tu oferta y procesa el pago. Los embudos de checkout se gestionan en **Web Paywalls** dentro de Adapty Mail y se editan en el **web paywall builder** integrado. ## Requisitos \{#requirements\} - Una cuenta en un proveedor de pagos web (Stripe, Paddle o PayPal) con tus productos de suscripción configurados — necesitarás conectarla dentro del web paywall builder No necesitas una cuenta separada para el web paywall builder. Está integrado con Adapty Mail: un espacio de trabajo se aprovisiona automáticamente la primera vez que inicias sesión, y accedes al editor con tus credenciales de Adapty. Esto es independiente de cualquier paywall web que hayas configurado en la página de paywalls del dashboard principal de Adapty — los paywalls web de Adapty Mail son entidades separadas, gestionadas íntegramente desde Adapty Mail. ## Configurar tu embudo de checkout \{#set-up-your-checkout-funnel\} 1. En Adapty Mail, ve a **Web Paywalls → Create**. 2. Elige **Generate with AI** para construir un embudo, o pega la URL de un paywall web existente. 3. Si lo generas con IA: haz clic en **Log in to the paywall builder**. El web paywall builder se abre en una nueva pestaña. Si aún no has iniciado sesión, hazlo con tus credenciales de Adapty. 4. En el builder, conecta tu proveedor de pagos (Stripe, Paddle o PayPal) en los ajustes de pago. Consulta [Configuración del paywall web](web-paywall-configuration) para más detalles. 5. Revisa el paywall generado y haz clic en **Publish** dentro del builder. :::important El paywall debe estar publicado antes de poder gestionar tráfico de checkout. Los paywalls no publicados devuelven un error cuando los usuarios hacen clic en los enlaces de checkout del correo. ::: :::note mail-checkout-setup.webp ::: ## Cómo es el checkout \{#what-the-checkout-looks-like\} Cuando un usuario hace clic en un enlace de checkout, pasa por un embudo de cuatro pantallas: **1. Confirmación de correo electrónico** Saluda al usuario por nombre o correo y confirma su identidad antes de continuar. **2. Página principal de conversión** Una presentación de ventas a pantalla completa. La IA genera el texto e imágenes de cada sección: | Sección | Qué genera la IA | |---|---| | Titular | Titular llamativo centrado en beneficios | | Subtítulo | Propuesta de valor complementaria | | Insignia de oferta | Insignia de urgencia (sin precios inventados — usa lenguaje promocional vago) | | Botón de CTA | Texto orientado a la acción, 2–5 palabras | | Beneficios | 3–6 tarjetas de beneficios con emoji y texto | | Características | 3–8 descripciones de características con título y subtítulo | | Planes | Título de selección de plan y texto del temporizador de oferta | | Prueba social | Texto de prueba comunitaria y 3–5 reseñas realistas de usuarios | | FAQ | 3–6 preguntas frecuentes con respuestas | | Garantía | Texto de garantía de devolución del dinero o satisfacción | **3. Pago exitoso** Un mensaje de celebración con los próximos pasos y una imagen generada por IA. **4. Pago fallido** Un mensaje amigable que invita al usuario a intentarlo de nuevo. El estado del checkout se conserva. :::note mail-checkout-screens.webp ::: ## Cómo funciona la personalización \{#how-personalization-works\} Cada correo contiene una URL de checkout única con el `customer_user_id` y la dirección de correo del destinatario como parámetros: ``` https://your-funnel.com/?cid={{customer_user_id}}&email={{email}} ``` Adapty genera estas URLs automáticamente al enviar cada correo — no es necesaria ninguna configuración en el web paywall builder. Cuando el usuario hace clic, el builder lee los parámetros para identificarlo. Cuando se completa una compra, Adapty asocia el ingreso al correo específico que generó la conversión. Estos datos aparecen en [Analíticas de campaña](mail-analytics). ## Solución de problemas \{#troubleshooting\} | Problema | Solución | |---|---| | El enlace de checkout no abre | Verifica que el paywall esté publicado en el web paywall builder | | El usuario no se identifica en el checkout | Confirma que se llamó a `Adapty.identify()` con el ID de usuario correcto antes de enviar el correo | | La compra no se atribuye al correo | Comprueba que el parámetro `cid` esté presente en la URL de checkout — contacta con soporte si faltan parámetros | --- # File: mail-email-campaigns --- --- title: "Email campaigns in Adapty Mail" description: "Design multi-email sequences, pick the right tone, and target the right users." --- A campaign in Adapty Mail is a complete multi-email sequence — copy, design, hero images, and delays — generated for your app in one pass. A campaign on its own doesn't send: it saves as a **draft** and starts delivering only once you attach it to a [flow](mail-create-flow), which binds it to a trigger and audience. Use the guides below to create campaigns, pick the right tone, and target the right users. <CustomDocCardList ids={['mail-create-campaign', 'mail-suppression']} /> --- # File: mail-create-campaign --- --- title: "Crear una campaña en Adapty Mail" description: "Genera una secuencia completa de correos a partir de los metadatos de tu app en el store y refínala antes de adjuntarla a un flow." --- Adapty Mail genera una secuencia completa de correos electrónicos —textos, diseño, imágenes hero, líneas de asunto y delays— a partir de los metadatos de tu app en el store. Sin necesidad de redactar ni diseñar nada. La campaña se guarda como borrador y solo empieza a enviarse cuando la adjuntas a un [flow](mail-create-flow). ## Antes de empezar \{#before-you-start\} - **Paywall web guardado**: cada campaña debe estar vinculada a un paywall web. El backend rechaza las campañas que no tienen uno. Consulta [Crear un paywall web](mail-get-started#4-create-a-web-paywall) si aún no tienes uno. - **URL de App Store o Google Play en Settings**: la IA lee los metadatos de la app (nombre, categoría, descripción, capturas de pantalla) desde esta URL para personalizar la secuencia. Añádela en **Settings → App metadata** si aún no está configurada. ## 1. Genera la secuencia \{#1-generate-the-sequence\} 1. En Adapty Mail, ve a **Campaigns** y haz clic en **Create**. 2. Asigna un nombre a la campaña. 3. En el desplegable **Web paywall**, selecciona el paywall web al que quieres que enlacen los correos. 4. Haz clic en **Generate emails**. 5. En el diálogo de generación, elige un tono y un idioma. Por defecto, la IA determina el número de correos según las mejores prácticas y el contexto de tu app. Si quieres definirlo tú mismo, haz clic en **Set number manually** y elige un valor (**1–15**, por defecto **4**). La lista de tonos se genera específicamente para la categoría de tu app; una app diferente verá opciones distintas. Tu elección influye en las líneas de asunto, los titulares, el texto del cuerpo y el CTA de todos los correos, pero no afecta al diseño ni a las imágenes hero. Una vez que haces clic en **Generate**, el tono queda vinculado a esa campaña. Para probar otro tono, crea una nueva campaña, ya que cada generación puede producir una combinación diferente de opciones. 6. Haz clic en **Generate**. La generación suele tardar unos minutos. El backend expira tras 5 minutos si no puede completarla; vuelve a intentarlo si ocurre esto. ## 2. Revisa y refina \{#2-review-and-refine\} Tras la generación, se muestra una vista previa de la secuencia completa. Para cada correo puedes ver: - **Variantes de línea de asunto**: tres opciones de asunto por correo. Adapty Mail las prueba durante el envío y continúa con la que mejor funciona. Consulta [Pruebas A/B](mail-ab-testing). - **Titular, texto del cuerpo y CTA**: el bloque de contenido principal. - **Imagen hero**: una imagen generada que encaja con el contenido del correo y tu marca. - **Diseño y delay**: cómo está organizado el correo y cuánto tiempo después del correo anterior se envía. Puedes: - **Regenerar correos individuales**: la IA reescribe el texto y genera una nueva imagen hero para un solo correo. La posición, el timing y el sistema de diseño general (colores, tipografía, modo oscuro) no cambian; solo se modifica el correo en cuestión. - **Editar el HTML directamente**: se abre un editor de código HTML para tener control total sobre cualquier cosa que la IA no haya resuelto correctamente. :::note Los correos se adaptan automáticamente. Los diseños de varias columnas colapsan a una sola columna en pantallas de menos de 620 px, y cada diseño se prueba en Gmail (web y móvil), Apple Mail (macOS e iOS), Outlook de escritorio, Yahoo Mail y Samsung Mail, tanto en modo claro como oscuro. ::: ## 3. Guarda como borrador \{#3-save-as-a-draft\} Haz clic en **Create** para guardar la campaña como borrador. Todavía no se envía ningún correo; el editor de campañas no tiene una acción separada de "publicar" o "lanzar". El estado de una campaña refleja si está adjunta a un flow activo: - **draft**: no adjunta a ningún flow. - **live**: adjunta a un flow y enrutando usuarios en este momento. - **inactive**: estaba adjunta, pero el wrapper de prueba A/B del flow ha finalizado. - **archived**: eliminada del dashboard. :::important Un borrador de campaña nunca se envía por sí solo. Para empezar a entregar correos, necesitas: - Adjuntar la campaña directamente a un [flow](mail-create-flow), o - Incluirla en una [prueba A/B](mail-ab-testing) y adjuntar la prueba A/B a un flow. Hasta que ocurra alguna de estas dos cosas, la campaña permanece en `draft` y no llega a ningún destinatario. ::: --- # File: mail-suppression --- --- title: "Cancelación de suscripción y supresión en Adapty Mail" description: "Cómo Adapty Mail deja de enviar correos a los usuarios — mediante cancelación de suscripción, rebotes de SES, quejas y el mecanismo de condición de parada." --- Adapty Mail deja de enviar correos a un usuario en dos situaciones distintas: - **Supresión**: El usuario queda excluido de todos los envíos futuros en este proyecto (canceló la suscripción, rebotó, se quejó, fue rechazado o limitado). - **Condición de parada**: La secuencia actual del usuario se cancela porque convirtió. No está suprimido y sigue siendo elegible para otras campañas. Ambos mecanismos son por proyecto. La supresión en un proyecto de Adapty no afecta a otro. ## Cancelación de suscripción \{#unsubscribe\} Todos los correos que envía Adapty Mail incluyen un enlace para cancelar la suscripción en el pie de página. 1. El usuario hace clic en el enlace. Adapty Mail abre una página de confirmación. 2. El usuario confirma. El backend marca el perfil con `suppression_reason = 'unsubscribe'`, cancela la secuencia restante y excluye el perfil de los envíos futuros en el proyecto. El token de la URL de cancelación codifica el `profile_id` y el `scheduled_email_id`, por lo que no se requiere inicio de sesión. :::note Adapty Mail también envía la cabecera `List-Unsubscribe: <URL>, <mailto:>` junto con `List-Unsubscribe-Post: List-Unsubscribe=One-Click`. Gmail y Yahoo lo exigen a los remitentes masivos (RFC 8058). Los clientes que admiten esta cabecera ofrecen un botón de cancelación con un solo clic directamente en la bandeja de entrada, sin necesidad de página de confirmación. ::: ## Supresión automática \{#automatic-suppression\} Adapty Mail escucha los eventos de entrega de AWS SES a través de SNS y suprime al usuario de inmediato en cualquiera de los siguientes casos: | Evento | Código de motivo | Qué significa | | --------- | ---------------- | ---------------------------------------------------------------------------------------- | | Bounce | `bounce` | La dirección de correo no es válida, el buzón está lleno o el dominio no existe. | | Complaint | `complaint` | El usuario marcó el correo como spam. | | Reject | `reject` | SES rechazó el mensaje antes de enviarlo. | | Throttle | `throttle` | La tasa de envío superó los límites de seguridad del dominio. | En todos los casos el resultado es el mismo: el usuario se añade a la lista de supresión, se cancela la secuencia restante y queda excluido de los envíos futuros en el proyecto. :::important Adapty Mail **no** distingue entre rebotes permanentes y temporales. Cualquier rebote — incluidas condiciones temporales como un buzón lleno — suprime al usuario de inmediato. No hay ventana de reintento. ::: ## Condición de parada \{#stop-condition\} Cuando un usuario convierte en mitad de una secuencia, Adapty Mail cancela sus correos pendientes con el motivo `stop_condition`. La conversión ocurre cuando el estado de su suscripción llega a **Subscribed**, o el estado de su compra única llega a **Purchased**. La condición de parada es diferente a la supresión: - **Supresión**: Excluye al usuario de todos los envíos futuros en el proyecto. - **Condición de parada**: Cancela únicamente la secuencia actual. El usuario sigue siendo elegible para otras campañas — por ejemplo, un flujo de renovación o de recuperación dirigido a suscriptores activos. Las cancelaciones por condición de parada aparecen junto a las supresiones en las analíticas de campaña. ## Gestión de la supresión \{#managing-suppression\} Adapty Mail no tiene interfaz en el dashboard para ver o eliminar usuarios suprimidos. Para quitar la supresión de un perfil — por ejemplo, alguien que marcó accidentalmente un correo de prueba como spam — contacta con el soporte de Adapty. ## Qué gestiona Adapty Mail para el cumplimiento normativo \{#what-adapty-mail-handles-for-compliance\} Adapty Mail incluye: - **Enlace de cancelación de suscripción**: Incluido en el pie de página de todos los correos, se procesa de inmediato al confirmarlo el usuario. - **Cabeceras List-Unsubscribe**: Enviadas con cada correo para la cancelación con un solo clic desde la bandeja de entrada (RFC 8058). - **Supresión automática**: Se activa con los eventos de rebote, queja, rechazo y limitación de SES. Aspectos de los que eres responsable: - **Dirección postal física**: CAN-SPAM exige que figure en el pie de página. Adapty Mail no la inyecta automáticamente — añádela en el diseño de tu campaña. - **Consentimiento explícito de opt-in**: Recógelo antes de pasar el correo de un usuario a Adapty. Consulta [Recopilar correos de usuarios](mail-collect-emails). - **Solicitudes de eliminación del RGPD**: Adapty Mail no expone un endpoint de "eliminar mis datos". Coordínate con el soporte de Adapty si un usuario ejerce su derecho al borrado. --- # File: mail-flows --- --- title: "Flows in Adapty Mail" description: "How flows route campaigns to the right users at the right moment — triggers, segments, and priority rules." --- <CustomDocCardList ids={['mail-create-flow']} /> A **flow** turns a saved campaign into scheduled deliveries. It binds a trigger event (a user's subscription state) to a segment (which users qualify) and the campaign they receive. Adapty Mail evaluates every flow whenever a matching event fires — no polling, no cron, no manual launch. ## Triggers Adapty Mail ships with three fixed triggers, each with its own flow view under **Flows**: - **Never purchased**: Users who signed up but haven't purchased yet. - **Expired**: Subscriptions that have ended. - **Refunded**: Purchases that got refunded. Triggers are not configurable — you can't create custom triggers or extend the list. ## The All Users segment Adapty Mail ships with a built-in **All Users** segment that has no filters — every user in the project qualifies. It's most useful in flows as a catch-all row, serving anyone not matched by a more specific segment above it. All Users can't be edited or deleted. See [Segments](mail-segments) for the full rundown. ## Priority Each trigger view holds a list of **segment → campaign** (or segment → A/B test) rows, ordered by priority. When a user hits the trigger, Adapty Mail: 1. Walks the rows top to bottom. 2. Sends the campaign in the first row whose segment matches. 3. Stops. Later rows are not evaluated for that user. Order matters. A broad segment placed above a narrower one swallows every user who would otherwise match the narrower row. To reorder, drag the handle on the left of any row — the backend reassigns priority numbers 1, 2, 3… based on the saved order. :::important The **All Users** row, if present, must be last (lowest priority). The backend rejects saves where All Users isn't in the final slot — it would otherwise swallow every user before more specific segments get a chance to match. ::: ## Content types A row can deliver either a single campaign or an A/B test: - **Campaign**: Sends one campaign to everyone who matches the segment. - **A/B Test**: Wraps two or more campaigns with configurable weights, routes incoming users across them randomly, and tracks per-variant metrics. See [A/B testing](mail-ab-testing). ## Lifecycle Flow rows have no draft state. A row is live the moment you save it — from that point on, users who hit the trigger and match the segment are routed to its campaign. - **Create a row**: Starts delivering immediately on save. - **Edit a row**: The change applies to users who hit the trigger from then on. Users already mid-sequence continue with the previous configuration. - **Delete a row**: New users stop entering the sequence. Users already mid-sequence may continue receiving their scheduled emails — there's no automatic cancellation. A/B test rows follow their own lifecycle (**draft → live → finished**) controlled separately from the row itself. See [A/B testing](mail-ab-testing). --- # File: mail-create-flow --- --- title: "Crear y gestionar filas de flujo en Adapty Mail" description: "Añade, reordena, edita y elimina filas en un flujo para enrutar campañas a tus usuarios." --- Cada [flujo](mail-flows) es una lista priorizada de filas **segmento → campaña** dentro de una vista de disparo fija. Esta guía explica cómo añadir, reordenar, editar y eliminar esas filas. Para entender los conceptos de disparadores, prioridades y tipos de contenido, consulta [Flujos](mail-flows). ## Añadir una fila \{#add-a-row\} 1. En Adapty Mail, ve a **Flows** y abre el disparador que quieras configurar. 2. Haz clic en **Create** para abrir el diálogo. 3. En el diálogo: - **Segment**: elige un segmento o **All Users** como opción general. - **Content type**: **Campaign** para una campaña individual, o **A/B Test** para comparar varias — consulta [Pruebas A/B](mail-ab-testing). - **Campaign**: selecciona la campaña a enviar. 4. Haz clic en **Save**. La fila queda activa de inmediato. Los usuarios que activen el disparador y coincidan con el segmento empezarán a recibir la campaña desde ese momento. ## Reordenar filas \{#reorder-rows\} Arrastra el controlador a la izquierda de una fila para cambiar su prioridad. Adapty Mail asigna automáticamente `priority: 1, 2, 3…` según el orden guardado. Una fila de **All Users** debe permanecer en la última posición — arrastrarla por encima de otra fila queda bloqueado al guardar. ## Editar una fila \{#edit-a-row\} Haz clic en **Change content** en una fila para volver a abrir el diálogo con los valores actuales ya rellenos. Puedes cambiar el segmento, el tipo de contenido y la campaña, y luego hacer clic en **Save** para aplicar los cambios. Una fila que use una prueba A/B solo puede editarse mientras la prueba esté en estado **draft**. Una vez lanzada la prueba, su contenido queda bloqueado hasta que la finalices. ## Eliminar una fila \{#delete-a-row\} Abre el menú de acciones de la fila y haz clic en **Delete**. No hay diálogo de confirmación — la fila se elimina de inmediato. - **Filas de campaña**: pueden eliminarse en cualquier momento. - **Filas con una prueba A/B activa**: no pueden eliminarse. Finaliza primero la prueba con **Finish A/B test** y luego elimina la fila. :::note Eliminar una fila impide que nuevos usuarios entren en la secuencia. Los usuarios que ya estén en medio de la secuencia pueden seguir recibiendo sus correos programados — no hay cancelación automática. ::: --- # File: mail-segments --- --- title: "Segmentos en Adapty Mail" description: "Crea segmentos de audiencia reutilizables basados en datos de perfil y compras para dirigir flujos y pruebas A/B." --- Un **segmento** es un segmento de audiencia reutilizable. Lo defines una vez — en **Segments** — y lo referencias desde flujos y pruebas A/B. Los segmentos son definiciones de filtros, no instantáneas: se evalúan bajo demanda cuando se activa un disparador de flujo, por lo que la membresía siempre refleja los datos de perfil más recientes. ## Crear un segmento \{#create-a-segment\} 1. En Adapty Mail, ve a **Segments** y haz clic en **+ Create**. Se abre la página de creación con el título **New Segment**. 2. Dale al segmento un **Name** (obligatorio) y una **Description** opcional. 3. En **Filters**, haz clic en **Add filter** por cada [regla](#available-filter-fields) que quieras añadir. Cada filtro se convierte en una tarjeta desplegable con la etiqueta **Filter 1**, **Filter 2**, etc. 4. Para cada filtro, elige un campo, un operador y el valor de comparación. 5. Guarda el segmento. :::important Los filtros se combinan con **AND** — el usuario debe cumplir todos los filtros para pertenecer al segmento. La lógica OR y los grupos anidados no están disponibles. Cada campo solo puede aparecer una vez por segmento; para comparar el mismo campo con varios valores, divide la lógica en segmentos separados. ::: ## Campos de filtro disponibles \{#available-filter-fields\} | Grupo | Campo | Tipo | | -------------- | ------------------------- | ------- | | Profile | Email | String | | Profile | Age | Integer | | Profile | Country | String | | Profile | External profile ID | String | | Profile | Created at | Date | | Purchase state | Total revenue (USD) | Decimal | | Purchase state | Subscription state | Enum | | Purchase state | Subscription purchased at | Date | | Purchase state | Subscription expires at | Date | | Purchase state | One-time purchase state | Enum | | Purchase state | One-time purchased at | Date | **Valores de Subscription state**: Never purchased, Subscribed, Auto-renew off, Billing issue, Grace period, Expired, Refunded. **Valores de One-time purchase state**: Never purchased, Purchased, Refunded. Operadores disponibles por tipo de campo: - **String**: equals, not equals, is set, is not set. - **Number**: equals, not equals, less than, greater than, less than or equal, greater than or equal, between, is set, is not set. - **Date**: equals, not equals, before, after, on or before, on or after, between, is set, is not set. ## El segmento de sistema All Users \{#the-all-users-system-segment\} Adapty Mail incluye un segmento integrado llamado **All Users** que no tiene filtros — cualquier usuario del proyecto cumple los criterios. No se puede editar ni eliminar. Cuando se usa en un flujo, actúa como la fila comodín de última prioridad (consulta [Flows](mail-flows) para la regla de prioridad). ## Ciclo de vida \{#lifecycle\} El estado de un segmento se calcula según cómo se esté usando: - **Draft**: Creado, no vinculado a ningún flujo ni prueba A/B. - **Live**: Vinculado a un flujo activo o a una prueba A/B. - **Inactive**: Estaba vinculado, pero la prueba A/B finalizó o se eliminó la fila del flujo. - **Archived**: Eliminado de forma temporal y oculto de la lista principal. La página Segments tiene un filtro de estado en la barra de herramientas para que puedas filtrar la lista por cualquiera de estos estados. ## Editar y eliminar un segmento \{#edit-and-delete-a-segment\} - **Name y description**: Siempre editables. - **Filtros en un segmento Draft**: Totalmente editables. - **Filtros en un segmento Live**: Bloqueados. Una vez que un segmento es referenciado por una fila de flujo activa o una prueba A/B, los filtros pasan a ser de solo lectura. Solo puedes cambiar el nombre o actualizar la descripción. Para modificar el targeting, crea un nuevo segmento y reemplázalo en la fila del flujo. - **Eliminar**: Elimina el segmento de forma temporal. Los segmentos Live no se pueden eliminar — primero quítalos del flujo (o finaliza la prueba A/B). ## Limitaciones \{#limitations\} - **Sin lógica OR ni anidamiento**: Los filtros solo se combinan con AND. - **Un campo por segmento**: Un segmento no puede tener dos filtros sobre el mismo campo (por ejemplo, dos comprobaciones de país). - **Sin vista previa del tamaño**: El editor no muestra cuántos usuarios cumplen actualmente los filtros. - **Filtros bloqueados una vez activos**: Los segmentos activos son de solo lectura, excepto el nombre y la descripción. --- # File: mail-ab-testing --- --- title: "Pruebas A/B en Adapty Mail" description: "Compara campañas de email completas entre sí adjuntando una prueba A/B a un flujo." --- Una prueba A/B en Adapty Mail compara dos o más campañas de email completas entre sí. Cada variante es una campaña independiente y completa. Cuando un usuario coincide con el segmento de la prueba en un [flujo](mail-flows), Adapty Mail lo dirige a una de las variantes según los pesos configurados y registra la entrega, el engagement y los ingresos por variante. ## Qué es una variante \{#what-a-variation-is\} Cada variante es una campaña completa. Las variantes pueden diferir en cualquier aspecto que pueda diferir una campaña: el copy, las imágenes principales, el tono, la longitud de la secuencia o los tiempos de espera entre emails. La propia prueba A/B no expone esos elementos como controles; tú creas las campañas por separado y las añades como variantes. ## Crear una prueba A/B \{#create-an-ab-test\} 1. Crea primero las campañas en **Campaigns**. Cada variante necesita su propia campaña. 2. En Adapty Mail, ve a **A/B Tests** y haz clic en **Create**. 3. Añade cada campaña como variante y establece su peso. Los pesos deben sumar **100%**. 4. Asigna un segmento para controlar a qué usuarios se aplica la prueba. 5. Guarda. La prueba se guarda como **borrador** y aún no envía nada. Para activarla, debe adjuntarse a un flujo. ## Lanzar desde un flujo \{#launch-from-a-flow\} Las pruebas A/B no se pueden lanzar desde la página A/B Tests — tanto el lanzamiento como la finalización se realizan dentro de una fila de flujo. 1. En Adapty Mail, ve a **Flows** y abre el trigger donde quieres ejecutar la prueba. 2. Haz clic en **Create** en una nueva fila. En el diálogo, establece **Content type** como **A/B Test**, selecciona la prueba que guardaste y haz clic en **Save**. 3. En la fila, haz clic en **Launch A/B test**. El estado de la prueba pasa de **borrador** a **activo** y los usuarios entrantes que coincidan con el segmento empezarán a ser dirigidos a las variantes. Consulta [Crear un flujo](mail-create-flow) para más información sobre las filas de flujo. ## Cómo funciona el enrutamiento \{#how-routing-works\} Cuando un usuario activa el trigger del flujo y coincide con el segmento de la prueba A/B, Adapty Mail elige una variante mediante selección **aleatoria ponderada**: el peso de cada variante determina su proporción del sorteo. El enrutamiento no es determinista por usuario. ## Ver los resultados \{#read-results\} En la página A/B Tests, cada variante muestra sus contadores brutos y las tasas derivadas: - **Delivery**: Envíos, Entregas, Rebotes. - **Engagement**: Aperturas, Clics, Bajas. - **Revenue**: Compras, Ingresos. Consulta [Analíticas de campañas](mail-analytics) para saber qué cuenta cada métrica y cómo se atribuyen los ingresos. ## Finalizar la prueba \{#finish-the-test\} Al igual que el lanzamiento, la finalización se realiza desde la fila del flujo, no desde la página A/B Tests. 1. Abre la fila del flujo donde se está ejecutando la prueba. 2. Haz clic en **Finish A/B test**. 3. En el diálogo **Finish A/B test**, selecciona la campaña ganadora en el desplegable **Replace with campaign** — o déjalo vacío para eliminar el segmento del flujo por completo. 4. Confirma. :::note Los usuarios que ya están a mitad de secuencia en cualquier variante — ganadora o perdedora — seguirán recibiendo sus emails programados. No se les cambia a la ganadora. ::: ## Ciclo de vida \{#lifecycle\} Una prueba A/B pasa por cuatro estados: - **Draft**: Creada, aún no adjuntada a una fila de flujo activa. - **Live**: Adjuntada y lanzada; enrutando usuarios entrantes. - **Finished**: Detenida mediante **Finish A/B test**. - **Archived**: Eliminada de forma temporal de la lista. --- # File: mail-analytics --- --- title: "Analíticas de campaña en Adapty Mail" description: "Métricas de entrega, engagement e ingresos por variación, mostradas en el editor de Flows y en la página de A/B Tests." --- Adapty Mail registra cada evento de entrega de SES y cada evento de ingresos atribuido a un clic en un email. Las métricas se muestran **por fila de variación** en dos lugares: - **Página de Flows**: Integrada en cada fila de segmento en una vista de trigger, independientemente de si el contenido de la fila es una campaña o una prueba A/B. - **Página de A/B Tests**: Solo para pruebas A/B — aquí es donde se comparan las variantes una al lado de la otra. El conjunto de métricas es idéntico en ambas vistas. No existe un dashboard de Analytics independiente; las analíticas viven junto a las filas que se están midiendo. ## Qué se registra por variación \{#whats-tracked-per-variation\} Cada fila de variación expone los siguientes recuentos brutos: - **Sends**: Emails enviados a SES. - **Deliveries**: Entregas en la bandeja de entrada confirmadas por SES. - **Bounces**: Rebotes reportados por SES. Los rebotes duros y blandos no se distinguen — ambos cuentan como un **Bounce**. - **Opens**: Cargas del píxel. Apple Mail Privacy Protection precarga imágenes en iOS 15+ e infla este recuento — usa los clics y los ingresos como señales más fiables. - **Clicks**: Clics en enlaces del cuerpo del email. - **Unsubs**: Cancelaciones de suscripción desde el enlace del pie de página o la cabecera `List-Unsubscribe`. - **Purchases**: Eventos de compra atribuidos, contados de forma única por email programado. Un destinatario que realiza varias compras desde el mismo email programado cuenta una sola vez. - **Revenue**: Suma de ingresos atribuidos (USD) de inicios de suscripción, renovaciones y compras únicas. ## Tasas derivadas \{#derived-rates\} Cada tasa se calcula a partir de los recuentos brutos. Los denominadores varían — elige el que corresponda al paso del embudo que estás midiendo. | Tasa | Fórmula | | ------------- | ---------------------- | | Del Rate | Deliveries / Sends | | Bounce Rate | Bounces / Sends | | Open/Send | Opens / Sends | | Open/Del | Opens / Deliveries | | Click/Send | Clicks / Sends | | Click/Del | Clicks / Deliveries | | Click/Open | Clicks / Opens | | Purch/Send | Purchases / Sends | | Purch/Del | Purchases / Deliveries | | Purch/Open | Purchases / Opens | | Purch/Click | Purchases / Clicks | | Unsub/Send | Unsubs / Sends | | Unsub/Del | Unsubs / Deliveries | | Unsub/Open | Unsubs / Opens | | Rev/Send | Revenue / Sends | ## Atribución de ingresos \{#revenue-attribution\} Los ingresos se atribuyen mediante **último clic** en un enlace rastreado: 1. Cuando un destinatario hace clic en cualquier enlace de un email, Adapty Mail almacena el `scheduled_email_id` en ese perfil en un almacén temporal. 2. Si llega un evento de compra sin atribución existente, Adapty Mail rellena el `scheduled_email_id` almacenado en la transacción, siempre que la marca de tiempo de la compra sea posterior al clic. 3. Las compras sin un clic rastreado previo quedan sin atribuir. El parámetro rastreado es `scheduled_email_id`. La URL de pago también incluye la identidad del destinatario mediante los marcadores `{email}` y `{external_profile_id}` para que el paywall web pueda personalizar el flujo — ese es un mecanismo independiente de la atribución. Consulta [Configurar el pago](mail-checkout). ## Limitaciones \{#limitations\} - **Sin dashboard consolidado**: Las métricas se muestran de forma integrada en el editor de Flows y en la página de A/B Tests — no hay una vista de resumen entre proyectos o campañas. - **La página de A/B Tests oculta las filas de campaña**: Las filas de flujo de campaña única se almacenan como envoltorios internos de prueba A/B, pero la página de A/B Tests las filtra. Para ver sus métricas, abre el flujo al que están vinculadas. - **Sin filtro de fechas**: Los recuentos son acumulativos desde el primer evento de SES en la variación — no es posible acotar a "los últimos 7 días" en la interfaz. - **Sin detalle por destinatario**: La vista de analíticas no muestra el historial de emails de usuarios individuales. - **Sin distinción entre rebote blando y duro**: Todos los rebotes, temporales o permanentes, se agrupan en un único recuento de Bounce. - **Las compras cuentan emails programados únicos, no destinatarios**: Un destinatario que coincide con varios emails de la secuencia puede contribuir más de una vez en el agregado total de la variación. - **Consistencia eventual, no en tiempo real**: Los datos se agregan desde las tablas de eventos de ClickHouse. Los eventos recientes suelen aparecer en minutos, pero no hay garantía de streaming. --- # File: configuration --- --- title: "Configurar integración de terceros" description: "Aprende cómo configurar los ajustes de Adapty para optimizar la gestión de suscripciones." --- Con las integraciones de Adapty, puedes transmitir eventos de suscripción y datos de compras a tu plataforma o flujo de trabajo preferido de forma sencilla. Tanto si buscas información sobre el comportamiento de los usuarios, estrategias de captación de clientes o análisis de producto avanzados para tu equipo de marketing, Adapty puede reenviar sin esfuerzo los eventos de compras in-app a la integración que elijas. Adapty registra automáticamente las compras in-app y los eventos de suscripción, como trials, conversiones, renovaciones y cancelaciones. Estos [eventos](events) se comunican de forma automática a las integraciones que hayas configurado. Esto te permite interactuar con los clientes según la fase en la que se encuentren y analizar las actividades relacionadas con los ingresos dentro de tu app. ## Ajustes de integración \{#integration-settings\} <img src="/assets/shared/img/20bf659-CleanShot_2023-08-22_at_13.26.562x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Las integraciones ofrecen las siguientes opciones de configuración que afectan a todos los eventos enviados a través de ella: | Ajuste | Descripción | |:--------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Reporting Proceeds** | Selecciona cómo se presentan los valores de ingresos: netos de las comisiones de App Store y Play Store, o brutos (antes de deducciones). Activa la casilla "Send sales as proceeds" para mostrar las ventas como ingresos netos tras descontar las comisiones de App Store / Play Store. | | **Send Trial Price** | Si está marcado, Adapty transmitirá el precio de la suscripción para el evento Trial Started. | | **Exclude Historical Events** | Elige excluir los eventos que ocurrieron antes de que el usuario instalara la app con el SDK de Adapty. Esto evita la duplicación de eventos y garantiza informes precisos. Por ejemplo, si un usuario activó una suscripción mensual el 10 de enero y actualizó la app con el SDK de Adapty el 6 de marzo, Adapty omitirá los eventos anteriores al 6 de marzo y conservará los posteriores. | | **Report User's Currency** | Elige si las ventas se reportan en la divisa del usuario o en USD. | | **Send User Attributes** | Si deseas enviar atributos específicos del usuario, como preferencias de idioma, y tu plan de OneSignal admite más de 10 etiquetas, selecciona esta opción. Al activarla, se permite incluir información adicional más allá de las 10 etiquetas predeterminadas. Ten en cuenta que superar los límites de etiquetas puede generar errores. | | **Send Attributions** | Activa esta opción para transmitir información de atribución (por ejemplo, atribución de AppsFlyer) y recibir los detalles correspondientes. | | **Send Play Store purchase token** | Activa esta opción para recibir el token de Play Store necesario para revalidar la compra si es preciso. Añadirá el parámetro `play_store_purchase_token` al evento. | | **Delay events with future datetime** | **Solo para AppsFlyer y webhooks personalizados**: Cuando está activado, los eventos de renovación y conversión de trial se envían en la fecha en que realmente ocurren. Cuando está desactivado (por defecto), estos eventos se envían en cuanto se detectan, aunque la fecha sea futura. | | **Data residency** | **Solo para Mixpanel y Amplitude**: Selecciona la residencia de datos para determinar dónde se procesan y almacenan tus eventos. | ## Configurar los eventos \{#configure-the-events\} Debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar a la plataforma de integración seleccionada desde Adapty. Debes activar los que necesites. Es importante tener en cuenta que la personalización de nombres de eventos está disponible en ciertas integraciones, mientras que en otras los nombres de eventos están fijos y no se pueden modificar. Además, con determinadas integraciones como [Airbridge](airbridge#configure-events-and-tags), por ejemplo, tienes la flexibilidad de asociar varios nombres de eventos a un único evento de Adapty. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). <img src="/assets/shared/img/c79f5cd-screencapture-app-adapty-io-integrations-pushwoosh-2023-08-22-13_31_07.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Aunque recomendamos utilizar los nombres de eventos predeterminados de Adapty, tienes la libertad de adaptarlos según tus necesidades específicas. --- # File: events --- --- title: "Eventos para enviar a integraciones de terceros" description: "Realiza el seguimiento de los eventos clave de suscripción con las herramientas de análisis de Adapty." --- Apple y Google envían los eventos de suscripción directamente a los servidores mediante las [Notificaciones del servidor de App Store](enable-app-store-server-notifications) y las [Notificaciones de desarrollador en tiempo real (RTDN)](enable-real-time-developer-notifications-rtdn). Como resultado, las apps móviles no pueden enviar eventos a los sistemas de análisis en tiempo real de forma fiable. Por ejemplo, si un usuario se suscribe pero nunca vuelve a abrir la app, el desarrollador no recibirá ninguna actualización del estado de la suscripción sin un servidor. Adapty cubre esta brecha recopilando datos de suscripción y convirtiéndolos en eventos legibles. Estos eventos de integración se envían en formato JSON. Aunque todos los eventos comparten la misma estructura, sus campos varían según el tipo de evento, el store y la configuración específica. Puedes consultar los campos exactos incluidos en cada evento en las páginas de integración correspondientes. Para entender cómo determinar si un evento se procesó correctamente o si algo salió mal, consulta la página de [estados de eventos](event-statuses). ## Tipos de eventos \{#event-types\} La mayoría de los eventos se crean y envían a todas las integraciones configuradas si están habilitadas. Sin embargo, el evento **Access level updated** solo se activa si la [integración de webhook](webhook) está configurada y este evento está habilitado. Este evento aparecerá en el [Event Feed](https://app.adapty.io/event-feed) y también se enviará al webhook, pero no se compartirá con otras integraciones. Si no hay ninguna integración de webhook configurada o este tipo de evento no está habilitado, el evento **Access level updated** no se creará y no aparecerá en el [Event Feed](https://app.adapty.io/event-feed). | Nombre del evento | Descripción | |:-----------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | subscription_started | Se activa cuando un usuario activa una suscripción de pago sin período de prueba, es decir, se le cobra de inmediato. | | subscription_renewed | Ocurre cuando se renueva una suscripción y se cobra al usuario. Este evento comienza a partir de la segunda facturación, tanto en suscripciones con prueba como sin ella. | | subscription_renewal_cancelled | El usuario ha desactivado la renovación automática de la suscripción. El usuario conserva el acceso a las funciones premium hasta el final del período de suscripción pagado. | | subscription_renewal_reactivated | Se activa cuando un usuario reactiva la renovación automática de la suscripción. | | subscription_expired | Se activa cuando una suscripción finaliza por completo tras ser cancelada. Por ejemplo, si un usuario cancela una suscripción el 12 de diciembre pero esta permanece activa hasta el 31 de diciembre, el evento se registra el 31 de diciembre cuando la suscripción expira. | | subscription_paused | Ocurre cuando un usuario activa la [pausa de suscripción](https://developer.android.com/google/play/billing/lifecycle/subscriptions#pause) (solo Android). | | subscription_deferred | Se activa cuando una compra de suscripción se [aplaza](https://adapty.io/glossary/subscription-purchase-deferral/), lo que permite a los usuarios retrasar el pago manteniendo el acceso a las funciones premium. Esta función está disponible a través de la Google Play Developer API y puede usarse para pruebas gratuitas o para ayudar a usuarios con dificultades económicas. | | non_subscription_purchase | Cualquier compra que no sea una suscripción, como el acceso de por vida o productos consumibles como monedas del juego. | | trial_started | Se activa cuando un usuario activa una suscripción de prueba. | | trial_converted | Ocurre cuando finaliza una prueba y se cobra al usuario (primera compra). Por ejemplo, si un usuario tiene una prueba hasta el 14 de enero pero se le cobra el 7 de enero, este evento se registra el 7 de enero. | | trial_renewal_cancelled | El usuario desactivó la renovación automática de la suscripción durante el período de prueba. El usuario conserva el acceso a las funciones premium hasta que finalice la prueba, pero no se le cobrará ni comenzará una suscripción. | | trial_renewal_reactivated | Ocurre cuando un usuario reactiva la renovación automática de la suscripción durante el período de prueba. | | trial_expired | Se activa cuando finaliza una prueba sin convertirse en suscripción. | | entered_grace_period | Ocurre cuando falla un intento de pago y el usuario entra en un período de gracia (si está habilitado). El usuario conserva el acceso premium durante este tiempo. | | billing_issue_detected | Se activa cuando ocurre un problema de facturación durante un intento de cobro (p. ej., saldo insuficiente en la tarjeta). | | subscription_refunded | Se activa cuando se reembolsa una suscripción (p. ej., por parte del soporte de Apple). | | non_subscription_purchase_refunded | Se activa cuando se reembolsa una compra que no es una suscripción. | | access_level_updated | Ocurre cuando se actualiza el nivel de acceso de un usuario. | Los eventos anteriores cubren completamente el estado de los usuarios en cuanto a compras. Veamos algunos ejemplos. ### Ejemplo 1 \{#example-1\} _El usuario activó una suscripción mensual el 1 de abril con un periodo de prueba de 7 días. El día 4, se dio de baja._ En ese caso, se enviarán los siguientes eventos: 1. `trial_started` el 1 de abril 2. `trial_renewal_cancelled` el 4 de abril 3. `trial_expired` el 7 de abril ### Ejemplo 2 \{#example-2\} _El usuario activó una suscripción mensual el 1 de abril con un periodo de prueba de 7 días. El día 10, se dio de baja._ En ese caso, se enviarán los siguientes eventos: 1. `trial_started` el 1 de abril 2. `trial_converted` el 7 de abril 3. `subscription_renewal_cancelled` el 10 de abril 4. `subscription_expired` el 1 de mayo Para un desglose detallado de qué eventos se activan en cada escenario, consulta los [flujos de eventos](event-flows). --- # File: event-flows --- --- title: "Flujos de eventos" description: "Descubre esquemas detallados de flujos de eventos de suscripción en Adapty. Aprende cómo se generan y envían los eventos de suscripción a las integraciones, lo que te ayuda a rastrear los momentos clave en el recorrido de tus clientes." --- En Adapty recibirás distintos eventos de suscripción a lo largo del recorrido del usuario en tu app. Estos flujos de suscripción describen los escenarios más habituales para ayudarte a entender los eventos que Adapty genera cuando los usuarios se suscriben, cancelan o reactivan suscripciones. Ten en cuenta que Apple procesa los pagos de suscripción varias horas antes del inicio o renovación real. En los flujos siguientes mostramos el inicio/renovación de la suscripción y el cargo ocurriendo al mismo tiempo para mantener los diagramas claros. Además, los eventos relacionados con la misma acción ocurren de forma simultánea y pueden aparecer en tu **Event Feed** en cualquier orden, que podría diferir de la secuencia mostrada en nuestros diagramas. ## Ciclo de vida de la suscripción \{#subscription-lifecycle\} ### Flujo de compra inicial \{#initial-purchase-flow\} Este flujo ocurre cuando un cliente compra una suscripción por primera vez sin período de prueba. En esta situación, se crean los siguientes eventos: - **Subscription started** - **Access level updated** para conceder acceso al usuario Cuando llega la fecha de renovación de la suscripción, esta se renueva. En este caso, se crean los siguientes eventos: - **Subscription renewal** para iniciar un nuevo período de la suscripción - **Access level updated** para actualizar la fecha de vencimiento de la suscripción, extendiendo el acceso por otro período Las situaciones en las que el pago no se realiza correctamente o el usuario cancela la renovación se describen en [Flujo de resultado de problema de facturación](event-flows#billing-issue-outcome-flow) y [Flujo de cancelación de suscripción](event-flows#subscription-cancellation-flow), respectivamente. <img src="/assets/shared/img_webhook_flows/Initial_Purchase_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Flujo de cancelación de suscripción \{#subscription-cancellation-flow\} Cuando un usuario cancela su suscripción, se generan los siguientes eventos: - **Subscription renewal canceled** para indicar que la suscripción sigue activa hasta el final del período actual, tras el cual el usuario perderá el acceso - El evento **Access level updated** se crea para deshabilitar la renovación automática del nivel de acceso Una vez que la suscripción finaliza, se activa el evento **Subscription expired (churned)** para marcar el fin de la suscripción. <img src="/assets/shared/img_webhook_flows/Subscription_Cancellation_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Si se aprueba un reembolso, el siguiente evento reemplaza a **Subscription expired (churned)**: - **Subscription refunded** para finalizar la suscripción y proporcionar los detalles del reembolso <img src="/assets/shared/img_webhook_flows/Subscription_Cancellation_Flow_with_a_Refund.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En Stripe, una suscripción puede cancelarse de forma inmediata, omitiendo el período restante. En este caso, todos los eventos se crean simultáneamente: - **Subscription renewal cancelled** - **Subscription expired (churned)** - **Access Level updated** para eliminar el acceso del usuario Si se aprueba un reembolso, también se activa el evento **Subscription refunded** en el momento de la aprobación. <img src="/assets/shared/img_webhook_flows/Subscription_Immediate_Cancellation_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Flujo de reactivación de suscripción \{#subscription-reactivation-flow\} Si un usuario cancela una suscripción, esta expira y luego vuelve a comprarla, se creará un evento **Subscription renewed**. Aunque haya un período sin acceso, Adapty lo trata como una única cadena de transacciones vinculada por el `vendor_original_transaction_id`. Por tanto, la nueva compra se considera una renovación. Los eventos **Access level updated** se crearán dos veces: - al finalizar la suscripción, para revocar el acceso del usuario - al recomprar la suscripción, para conceder el acceso <img src="/assets/shared/img_webhook_flows/Subscription_Rejoin_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Flujo de pausa de suscripción (solo Android) \{#subscription-pause-flow-android-only\} Este flujo se aplica cuando un usuario pausa y posteriormente reanuda una suscripción en Android. Pausar una suscripción tiene efectos diferidos. Si el usuario pausa una suscripción antes de que se renueve, la suscripción sigue activa y el usuario conserva el acceso de pago durante el resto del período de facturación. 1. Cuando el usuario pausa una suscripción, se activa el evento **Subscription paused (Android only)**. 2. Al finalizar el período de suscripción, Adapty activa el evento **Access level updated** para revocar el acceso del usuario. 3. Cuando el usuario reanuda la suscripción, se activan los siguientes eventos: - **Subscription renewed** - **Access level updated** para restaurar el acceso del usuario Estas suscripciones pertenecerán a la misma cadena de transacciones, vinculadas por el mismo **vendor_original_transaction_id**. <img src="/assets/shared/img_webhook_flows/Subscription_Paused_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Flujos de prueba \{#trial-flows\} Si usas períodos de prueba en tu app, recibirás eventos adicionales relacionados con las pruebas. ### Flujo de prueba con conversión exitosa \{#trial-with-successful-conversion-flow\} El flujo más habitual ocurre cuando un usuario inicia una prueba, proporciona una tarjeta de crédito y se convierte exitosamente en una suscripción estándar al final del período de prueba. En este caso, los siguientes eventos se crean en el momento del inicio de la prueba: - **Trial started** para marcar el inicio de la prueba - **Access level updated** para conceder acceso El evento **Trial converted** se crea cuando comienza la suscripción estándar. <img src="/assets/shared/img_webhook_flows/Trial_Flow_with_Successful_Conversion.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Flujo de prueba gratuita sin conversión exitosa \{#trial-without-successful-conversion-flow\} Si un usuario cancela la prueba gratuita antes de que se convierta en una suscripción, en el momento de la cancelación se crean los siguientes eventos: - **Trial renewal cancelled** para deshabilitar la conversión automática de la prueba gratuita en una suscripción - **Access level updated** para deshabilitar la renovación del acceso El usuario tendrá acceso hasta el final del período de prueba, momento en el que se crea el evento **Trial expired** para marcar su fin. <img src="/assets/shared/img_webhook_flows/Trial_Flow_without_Successful_Conversion.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Reactivación de suscripción tras un período de prueba expirado \{#subscription-reactivation-after-expired-trial-flow\} Si un período de prueba expira (por un problema de pago o cancelación) y el usuario luego compra una suscripción, se crean los siguientes eventos: - **Access level updated** para conceder acceso al usuario - **Trial converted** Aunque haya un intervalo entre el período de prueba y la suscripción, Adapty vincula ambos mediante `vendor_original_transaction_id`. Esta conversión se trata como parte de una cadena de transacciones continua que comienza con un período de prueba de precio cero. Por eso se crea el evento **Trial converted** en lugar de **Subscription started**. <img src="/assets/shared/img_webhook_flows/Subscription_Reactivation_Flow_after_Expired_Trial.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Cambios de producto \{#product-changes\} Esta sección cubre los ajustes realizados en suscripciones activas, como actualizaciones, degradaciones o compras de un producto de otro grupo. ### Flujo de cambio de producto inmediato \{#immediate-product-change-flow\} Después de que un usuario cambia un producto, el cambio puede aplicarse en el sistema de forma inmediata antes de que finalice la suscripción (principalmente en casos de actualización o sustitución de un producto). En este caso, en el momento del cambio de producto: - El nivel de acceso cambia y se crean dos eventos **Access level updated**: 1. para eliminar el acceso al primer producto. 2. para conceder acceso al segundo producto. - La suscripción antigua finaliza y se realiza un reembolso (se crea el evento **Subscription refunded** con `cancellation_reason` = `upgraded`). Ten en cuenta que no se crea ningún evento **Subscription expired (churned)**; el evento **Subscription refunded** lo reemplaza. - La nueva suscripción comienza (se crea el evento **Subscription started** para el nuevo producto). <img src="/assets/shared/img_webhook_flows/Immediate_Product_Change_Flow_Upgrade.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Si un usuario realiza un downgrade de su suscripción, la primera suscripción se mantendrá activa hasta el final del período pagado y, cuando finalice, será reemplazada por la nueva suscripción de nivel inferior. En este caso, solo se creará de inmediato el evento **Access level updated** para deshabilitar la renovación automática del acceso. El resto de los eventos se crearán en el momento en que se produzca el reemplazo efectivo de la suscripción: - Se crea otro evento **Access level updated** para conceder acceso al segundo producto. - Se crea el evento **Subscription expired (churned)** para finalizar la suscripción del primer producto. - Se crea el evento **Subscription started** para iniciar una nueva suscripción con el nuevo producto. <img src="/assets/shared/img_webhook_flows/Delayed_Product_Change_Downgrade.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Flujo de cambio de producto diferido \{#delayed-product-change-flow\} También existe una variante en la que el usuario cambia el producto en el momento de la renovación de la suscripción. Esta variante es muy similar a la anterior: se creará un evento **Access level updated** para deshabilitar la autorenovación del acceso al producto anterior. El resto de eventos se crearán en el momento en que el usuario cambie la suscripción y el cambio quede registrado en el sistema: - Se crea otro evento **Access level updated** para conceder acceso al segundo producto. - Se crea el evento **Subscription expired (churned)** para finalizar la suscripción del primer producto. - Se crea el evento **Subscription started** para iniciar una nueva suscripción con el nuevo producto. <img src="/assets/shared/img_webhook_flows/Product_Change_on_Renewal_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Flujo de resultados ante un problema de facturación \{#billing-issue-outcome-flow\} Si los intentos de convertir una prueba o renovar una suscripción fallan por un problema de facturación, lo que ocurre a continuación depende de si hay un período de gracia habilitado. Con un período de gracia, si el pago se realiza correctamente, la prueba se convierte o la suscripción se renueva. Si falla, el store seguirá intentando cobrar al usuario por la suscripción y, si continúa fallando, el store pondrá fin a la prueba o suscripción por sí mismo. Por lo tanto, en el momento del problema de facturación, se crean los siguientes eventos en Adapty: - **Billing issue detected** - **Entered grace period** (si el período de gracia está activado) - **Access level updated** para mantener el acceso hasta el final del período de gracia Si el pago se completa más adelante, Adapty registra un evento **Trial converted** o **Subscription renewed**, y el usuario no pierde el acceso. Si el pago finalmente falla y el store cancela la suscripción, Adapty genera estos eventos: - **Trial expired** o **Subscription expired (churned)** con `cancellation_reason: billing_error` - **Access level updated** para revocar el acceso del usuario <img src="/assets/shared/img_webhook_flows/Billing_Issue_Outcome_Flow_with_Grace_Period.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Sin período de gracia, el Período de Reintento de Cobro (el período durante el cual el store sigue intentando cobrar al usuario) comienza de inmediato. Si el pago nunca tiene éxito antes de que finalice el período de gracia, el flujo es el mismo: se crean los mismos eventos cuando el store cancela la suscripción automáticamente: - Evento **Trial expired** o **Subscription expired (churned)** con un `cancellation_reason` de `billing_error` - **Access level updated** para revocar el acceso del usuario <img src="/assets/shared/img_webhook_flows/Billing_Issue_Outcome_Flow_without_Grace_Period.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Flujos para compartir compras entre cuentas de usuario \{#sharing-purchases-across-user-accounts-flows\} Cuando un <InlineTooltip tooltip="Customer User ID">[iOS](identifying-users#set-customer-user-id-on-configuration), [Android](android-identifying-users#setting-customer-user-id-on-configuration), [React Native](react-native-identifying-users#setting-customer-user-id-on-configuration), [Flutter](flutter-identifying-users#setting-customer-user-id-on-configuration), y [Unity](unity-identifying-users#setting-customer-user-id-on-configuration)</InlineTooltip> intenta restaurar o extender una suscripción ya vinculada a un <InlineTooltip tooltip="Customer User ID">[iOS](identifying-users#set-customer-user-id-on-configuration), [Android](android-identifying-users#setting-customer-user-id-on-configuration), [React Native](react-native-identifying-users#setting-customer-user-id-on-configuration), [Flutter](flutter-identifying-users#setting-customer-user-id-on-configuration), y [Unity](unity-identifying-users#setting-customer-user-id-on-configuration)</InlineTooltip> diferente, la configuración **Sharing paid access between user accounts** de Adapty controla cómo se gestiona el acceso. El flujo variará según la opción seleccionada. :::note Para las transacciones de Apple Family Sharing (`in_app_ownership_type=FAMILY_SHARED`), solo se activa el evento **Access level updated** — los eventos de suscripción por producto que se describen a continuación no se generan. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz completa de eventos. ::: ### Transferir el nivel de acceso al nuevo usuario \{#transfer-access-to-new-user-flow\} La opción recomendada es transferir el nivel de acceso al nuevo usuario. Esto preserva el historial de transacciones del usuario original para mantener la coherencia en los análisis. Solo se crearán 2 eventos **Access level updated**: 1. para retirar el acceso al primer usuario 2. para conceder el acceso al segundo usuario <img src="/assets/shared/img_webhook_flows/Transfer_Access_to_New_User_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> A continuación se describen los campos relacionados con la asignación y transferencia del nivel de acceso en los eventos generados en este escenario: - **Usuario A: Nivel de acceso actualizado (se envía cuando el Usuario A realiza una compra de suscripción en la app)** ```json showLineNumbers { "profile_id": "00000000-0000-0000-0000-000000000000", "customer_user_id": UserA, "event_properties": { "profile_has_access_level": true, }, "profiles_sharing_access_level": null } ``` - **Usuario A: Nivel de acceso actualizado (se envía cuando se reinstala la app y el Usuario B inicia sesión, revocando el acceso del Usuario A)** ```json showLineNumbers { "profile_id": "00000000-0000-0000-0000-000000000000", "customer_user_id": UserA, "event_properties": { "profile_has_access_level": false, }, "profiles_sharing_access_level": null } ``` - **Usuario B: Nivel de acceso actualizado (enviado cuando el Usuario B inicia sesión y se concede el acceso)** ```json showLineNumbers { "profile_id": "00000000-0000-0000-0000-000000000001", "customer_user_id": UserB, "event_properties": { "profile_has_access_level": true, }, "profiles_sharing_access_level": null } ``` ### Flujo de acceso compartido entre usuarios \{#shared-access-between-users-flow\} Esta opción permite que varios usuarios compartan el mismo nivel de acceso si su dispositivo está conectado con el mismo Apple/Google ID. Resulta útil cuando un usuario reinstala la app e inicia sesión con un correo diferente: seguirá teniendo acceso a su compra anterior. Con esta opción, varios usuarios identificados pueden compartir el mismo nivel de acceso. Mientras el nivel de acceso se comparte, todas las transacciones se registran bajo el <InlineTooltip tooltip="Customer User ID">original [iOS](identifying-users#set-customer-user-id-on-configuration), [Android](android-identifying-users#setting-customer-user-id-on-configuration), [React Native](react-native-identifying-users#setting-customer-user-id-on-configuration), [Flutter](flutter-identifying-users#setting-customer-user-id-on-configuration) y [Unity](unity-identifying-users#setting-customer-user-id-on-configuration)</InlineTooltip> para mantener el historial completo de transacciones y los análisis. Por lo tanto, solo se creará 1 evento: **Access level updated** para conceder acceso al segundo usuario. <img src="/assets/shared/img_webhook_flows/Share_Access_Between_Users_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> A continuación se describen los campos relacionados con la asignación y el intercambio de niveles de acceso en los eventos generados en este escenario: **Usuario B: Access level updated (enviado cuando el Usuario B inicia sesión y se concede el acceso)** ```json showLineNumbers { "profile_id": "00000000-0000-0000-0000-000000000000", "customer_user_id": UserA, "event_properties": { "profile_has_access_level": true, }, "profiles_sharing_access_level": [ { "profile_id": "00000000-0000-0000-0000-000000000001, "customer_user_id": UserB } ] } ``` ### Flujo de acceso no compartido entre usuarios \{#access-not-shared-between-users-flow\} Con esta opción, solo el primer perfil de usuario que reciba el nivel de acceso lo conserva de forma permanente. Es ideal cuando las compras deben estar vinculadas a un único <InlineTooltip tooltip="Customer User ID">[iOS](identifying-users#set-customer-user-id-on-configuration), [Android](android-identifying-users#setting-customer-user-id-on-configuration), [React Native](react-native-identifying-users#setting-customer-user-id-on-configuration), [Flutter](flutter-identifying-users#setting-customer-user-id-on-configuration), y [Unity](unity-identifying-users#setting-customer-user-id-on-configuration)</InlineTooltip>. <img src="/assets/shared/img_webhook_flows/Share_Access_Between_Users_Disabled_Flow.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: event-statuses --- --- title: "Estados de los eventos de integración" description: "" --- Adapty determina la entregabilidad en función del código de estado HTTP, considerando cualquier respuesta fuera del rango `200-399` como un error. Puedes hacer seguimiento del estado de los eventos de integración en la **Event List** dentro del Adapty Dashboard. El sistema muestra los estados de todas las integraciones habilitadas, independientemente de si un tipo de evento específico está activado para una integración concreta. - Negro: El evento se envió correctamente. - <span style={{ color: 'grey' }}>Gris:</span> El tipo de evento está deshabilitado para esta integración. - <span style={{ color: 'red' }}>Rojo:</span> Hay un problema con la integración que requiere atención. Para más detalles sobre los eventos fallidos, pasa el cursor sobre el nombre de la integración para ver un tooltip con información específica del error. <img src="/assets/shared/img/event-status.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El **Event Feed** muestra datos de las últimas dos semanas para optimizar el rendimiento. Esta limitación mejora la velocidad de carga de la página, lo que facilita a los usuarios navegar y analizar los eventos de forma eficiente. --- # File: adjust --- --- title: "Adjust" description: "Conecta Adjust con Adapty para un mejor seguimiento de suscripciones y análisis." --- [Adjust](https://www.adjust.com/) es una de las principales plataformas MMP (Mobile Measurement Partner) que recopila y presenta datos de campañas de marketing para ayudar a las empresas a medir el rendimiento de sus campañas. Adapty proporciona un conjunto completo de datos que te permite rastrear [eventos de suscripción](events) de los stores en un solo lugar. Con Adapty, puedes ver fácilmente el comportamiento de tus suscriptores, conocer sus preferencias y usar esa información para comunicarte con ellos de forma dirigida y efectiva. Esta integración te permite rastrear eventos de suscripción en Adjust y analizar con precisión cuántos ingresos generan tus campañas. La integración entre Adapty y Adjust funciona de dos maneras principales. 1. **Adapty recibe datos de atribución de Adjust** Una vez que hayas configurado la integración con Adjust, Adapty comenzará a recibir datos de atribución de Adjust. Puedes acceder a estos datos fácilmente en la página del perfil del usuario. <img src="/assets/shared/img/98769d9-CleanShot_2023-08-11_at_14.39.182x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. **Adapty envía eventos de suscripción a Adjust** Adapty puede enviar todos los eventos de suscripción configurados en tu integración a Adjust. Como resultado, podrás rastrear estos eventos dentro del dashboard de Adjust. Esta integración es muy útil para evaluar la efectividad de tus campañas publicitarias. ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty a Adjust \{#connect-adapty-to-adjust\} 1. Abre el Adapty Dashboard y ve a [Integrations > Adjust](https://app.adapty.io/integrations/adjust). 2. Activa el interruptor en la parte superior de la página. 3. Rellena los campos e introduce tus credenciales de acceso. <img src="/assets/shared/img/5064125-CleanShot_2023-08-11_at_14.43.382x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Si habilitaste la autorización OAuth en la plataforma de Adjust, es obligatorio proporcionar un **OAuth Token** durante el proceso de integración para tus apps de iOS y Android. 4. A continuación, proporciona los **app tokens** para tus apps de iOS y Android. Abre tu dashboard de Adjust y verás tus apps. <img src="/assets/shared/img/adjust-apps.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::note Puedes tener aplicaciones de Adjust distintas para iOS y Android, por eso en Adapty hay dos secciones independientes para ello. Si solo tienes una app de Adjust, simplemente introduce la misma información en ambas. ::: 5. Selecciona tu app de la lista y copia el **App Token**. Pega el token en el campo correspondiente del dashboard de Adapty. <img src="/assets/shared/img/adjust-token.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Configurar eventos y etiquetas \{#configure-events-and-tags\} Adjust funciona de manera algo diferente al resto de plataformas. Debes crear los eventos manualmente en el dashboard de Adjust, obtener los tokens de evento y copiarlos en los eventos correspondientes en Adapty. El primer paso es encontrar los tokens de evento para todos los eventos que quieres que Adapty envíe. Para eso: 1. En el dashboard de Adjust, abre tu app y cambia a la pestaña **Events**. <img src="/assets/shared/img/adjust-events.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 1. Copia el token del evento y pégalo en Adapty. Debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar desde Adapty a Adjust. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). <img src="/assets/shared/img/adjust-event-token.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Adapty enviará eventos de suscripción a Adjust mediante una integración servidor a servidor, lo que te permitirá ver todos los eventos de suscripción en tu dashboard de Adjust y vincularlos a tus campañas de adquisición. :::important Ten en cuenta lo siguiente: - Adjust no admite eventos con más de 58 días de antigüedad. Si tienes un evento anterior a ese plazo, Adapty lo enviará a Adjust, pero la fecha y hora del evento se reemplazará por la marca de tiempo actual. - Adjust no es compatible con IPv6. Si desactivas la recopilación de IP en el SDK desde **App settings** o al activar el SDK, puede que solo se envíe una IPv6 del backend y el seguimiento falle — mantén la recopilación de IP del SDK activada para asegurarte de que se use IPv4. ::: ### Conectar tu app a Adjust \{#connect-your-app-to-adjust\} Tras completar los pasos anteriores, añade los dos métodos siguientes a tu app. Establecerán la comunicación entre tu app y Adjust: 1. **Para enviar datos de suscripción a Adjust**: pasa el ID de dispositivo de Adjust al método `setIntegrationIdentifier()` del SDK. 2. **Para recibir datos de atribución de Adjust**: actualiza los datos de atribución con el método `updateAttribution()` del SDK. Para Adjust versión 5.0 o posterior, usa el siguiente ejemplo: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers class AdjustModuleImplementation { func updateAdjustAdid() { Adjust.adid { adid in guard let adid else { return } Adapty.setIntegrationIdentifier(key: "adjust_device_id", value: adid) } } func updateAdjustAttribution() { Adjust.attribution { attribution in guard let attribution = attribution?.dictionary() else { return } Adapty.updateAttribution(attribution, source: "adjust") } } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adjust.getAdid { adid -> if (adid == null) return@getAdid Adapty.setIntegrationIdentifier("adjust_device_id", adid) { error -> if (error != null) { // handle the error } } } Adjust.getAttribution { attribution -> if (attribution == null) return@getAttribution Adapty.updateAttribution(attribution, "adjust") { error -> // handle the error } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers Adjust.getAdid(adid -> { if (adid == null) return; Adapty.setIntegrationIdentifier("adjust_device_id", adid, error -> { if (error != null) { // handle the error } }); }); Adjust.getAttribution(attribution -> { if (attribution == null) return; Adapty.updateAttribution(attribution, "adjust", error -> { // handle the error }); }); ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers 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); }); ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```javascript showLineNumbers try { final adid = await Adjust.getAdid(); if (adid == null) { // handle the error } await Adapty().setIntegrationIdentifier( key: "adjust_device_id", value: adid, ); final attributionData = await Adjust.getAttribution(); var attribution = Map<String, String>(); if (attributionData.trackerToken != null) attribution['trackerToken'] = attributionData.trackerToken!; if (attributionData.trackerName != null) attribution['trackerName'] = attributionData.trackerName!; if (attributionData.network != null) attribution['network'] = attributionData.network!; if (attributionData.adgroup != null) attribution['adgroup'] = attributionData.adgroup!; if (attributionData.creative != null) attribution['creative'] = attributionData.creative!; if (attributionData.clickLabel != null) attribution['clickLabel'] = attributionData.clickLabel!; if (attributionData.costType != null) attribution['costType'] = attributionData.costType!; if (attributionData.costAmount != null) attribution['costAmount'] = attributionData.costAmount!.toString(); if (attributionData.costCurrency != null) attribution['costCurrency'] = attributionData.costCurrency!; if (attributionData.fbInstallReferrer != null) attribution['fbInstallReferrer'] = attributionData.fbInstallReferrer!; await Adapty().updateAttribution(attribution, source: "adjust"); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity" default> ```csharp showLineNumbers // 1. To update ADID Adjust.GetAdid((adid) => { if (adid == null) { // handle the error return; } Adapty.SetIntegrationIdentifier("adjust_device_id", adid, (error) => { if (error != null) { // handle the error return; } }); }); // 2. To update Attribution // in your adjust configuration scope: adjustConfig.AttributionChangedDelegate = AttributionChangedCallback; public void AttributionChangedCallback(AdjustAttribution attributionData) { var attribution = new Dictionary<string, string>(); if (attributionData.TrackerToken != null) attribution["trackerToken"] = attributionData.TrackerToken; if (attributionData.TrackerName != null) attribution["trackerName"] = attributionData.TrackerName; if (attributionData.Network != null) attribution["network"] = attributionData.Network; if (attributionData.Adgroup != null) attribution["adgroup"] = attributionData.Adgroup; if (attributionData.Creative != null) attribution["creative"] = attributionData.Creative; if (attributionData.ClickLabel != null) attribution["clickLabel"] = attributionData.ClickLabel; if (attributionData.CostType != null) attribution["costType"] = attributionData.CostType; if (attributionData.CostAmount != null) attribution["costAmount"] = attributionData.CostAmount.ToString(); if (attributionData.CostCurrency != null) attribution["costCurrency"] = attributionData.CostCurrency; if (attributionData.FbInstallReferrer != null) attribution["fbInstallReferrer"] = attributionData.FbInstallReferrer; // you will probably need to install Newtonsoft.Json package, if not yet var attributionJsonString = Newtonsoft.Json.JsonConvert.SerializeObject(attribution); Adapty.UpdateAttribution(attributionJsonString, "adjust", (error) => { if (error != null) { // handle the error } }); } ``` </TabItem> </Tabs> ## Estructura del evento \{#event-structure\} Adapty envía los eventos seleccionados a Adjust según la configuración de la sección **Events names** en la [**página de integración de Adjust**](https://app.adapty.io/integrations/adjust). Cada evento tiene la siguiente estructura: ```json { "event_token": "EVENT_TOKEN_FROM_CONFIG", "app_token": "APP_TOKEN_FROM_CONFIG", "s2s": 1, "environment": "production", "created_at_unix": 1709294400, "currency": "USD", "revenue": 9.99, "customer_user_id": "user_12345", "external_device_id": "user_12345", "ip_address": "192.168.100.1", "user_agent": "Mozilla/5.0 (Linux; Android 14; SM-S901B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", "android_id": "875646c2-4a56-4211-8931-168532479006", "gps_adid": "875646c2-4a56-4211-8931-168532479006", "callback_params": "{\"integration_event_id\":\"550e8400-e29b-41d4-a716-446655440000\",\"customer_user_id\":\"user_12345\",\"vendor_product_id\":\"com.example.app.yearly.premium\",\"transaction_id\":\"GPA.3312-4512-1100-55923\",\"original_transaction_id\":\"GPA.3312-4512-1100-55923\",\"store\":\"play_store\",\"store_country\":\"US\",\"price_usd\":9.99,\"proceeds_usd\":8.49,\"price_local\":9.99,\"proceeds_local\":8.49,\"net_revenue_usd\":8.49,\"net_revenue_local\":8.49,\"tax_amount_usd\":0.0,\"tax_amount_local\":0.0,\"consecutive_payments\":3,\"rate_after_first_year\":false}", "partner_params": "{\"integration_event_id\":\"550e8400-e29b-41d4-a716-446655440000\",\"customer_user_id\":\"user_12345\",\"vendor_product_id\":\"com.example.app.yearly.premium\",\"transaction_id\":\"GPA.3312-4512-1100-55923\",\"original_transaction_id\":\"GPA.3312-4512-1100-55923\",\"store\":\"play_store\",\"store_country\":\"US\",\"price_usd\":9.99,\"proceeds_usd\":8.49,\"price_local\":9.99,\"proceeds_local\":8.49,\"net_revenue_usd\":8.49,\"net_revenue_local\":8.49,\"tax_amount_usd\":0.0,\"tax_amount_local\":0.0,\"consecutive_payments\":3,\"rate_after_first_year\":false}" } ``` Donde: | Parámetro | Tipo | Descripción | |:---------------------|:--------|:---------------------------------------------------------------------------------------------------------------------------------------------| | `app_token` | String | El App Token de Adjust de tu configuración de integración. | | `event_token` | String | El Event Token de Adjust asociado al evento específico de Adapty. | | `s2s` | Integer | Indicador de evento servidor a servidor. | | `environment` | String | `sandbox` o `production`. | | `created_at_unix` | Integer | Marca de tiempo del evento en segundos. | | `currency` | String | Código de moneda (p. ej., "USD") de la transacción. Se incluye solo cuando los ingresos superan 0,001, ya que Adjust requiere que tanto los ingresos como la moneda se envíen juntos. | | `revenue` | Float | Importe de ingresos de la transacción. Solo se incluye cuando el valor supera 0,001. Ten en cuenta que los eventos de reembolso se envían sin propiedades de ingresos, ya que Adjust no admite valores de ingresos negativos. | | `customer_user_id` | String | El Customer User ID del usuario. | | `external_device_id` | String | Igual que `customer_user_id`. | | `ip_address` | String | Dirección IP del usuario (solo IPv4). | | `user_agent` | String | Cadena User Agent del dispositivo. | | `adid` | String | ID de dispositivo de Adjust (si se conoce). | | `android_id` | String | **Solo Android**. Google Advertising ID. | | `gps_adid` | String | **Solo Android**. Google Advertising ID. | | `idfa` | String | **Solo iOS**. ID para anunciantes. | | `idfv` | String | **Solo iOS**. ID para proveedores. | | `callback_params` | String | Cadena JSON con todos los [campos del evento](webhook-event-types-and-fields#for-most-event-types) disponibles. Solo se incluyen los campos con valor no nulo. | | `partner_params` | String | Igual que `callback_params`. | ## Resolución de problemas \{#troubleshooting\} ### Discrepancia en los ingresos \{#revenue-discrepancy\} Si hay una discrepancia en los ingresos entre Adapty y Adjust, es posible que no todos tus usuarios estén usando la versión de la app que incluye el SDK de Adapty. Para garantizar la consistencia de los datos, puedes obligar a tus usuarios a actualizar a una versión de la app que incluya el SDK de Adapty. --- # File: airbridge --- --- title: "Airbridge" description: "Conecta Adapty con Airbridge para rastrear información de marketing y atribución." --- [Airbridge](https://www.airbridge.io/) ofrece un análisis integrado del rendimiento de marketing para sitios web y aplicaciones móviles, consolidando datos recopilados de múltiples dispositivos, plataformas y canales. Con el motor de resolución de identidad de Airbridge, puedes combinar datos de identidad dispersos de clientes procedentes de interacciones web y de la app en una identidad unificada basada en personas, lo que se traduce en una atribución más precisa. Adapty proporciona un conjunto completo de datos que te permite rastrear [eventos de suscripción](events) desde los stores en un solo lugar. Con Adapty, puedes ver fácilmente cómo se comportan tus suscriptores, descubrir qué les gusta y usar esa información para comunicarte con ellos de forma dirigida y efectiva. La integración entre Adapty y Airbridge funciona de dos maneras principales. 1. **Recibir datos de atribución desde Airbridge** Una vez configurada la integración con Airbridge, Adapty comenzará a recibir datos de atribución de Airbridge. Puedes acceder y consultar estos datos fácilmente en la página del usuario. 2. **Enviar eventos de suscripción a Airbridge** Adapty puede enviar todos los eventos de suscripción configurados en tu integración a Airbridge. Como resultado, podrás rastrear estos eventos dentro del dashboard de Airbridge. Esta integración es útil para evaluar la efectividad de tus campañas publicitarias. ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty con Airbridge \{#connect-adapty-to-airbridge\} Para integrar Airbridge, ve a [Integrations > Airbridge](https://app.adapty.io/integrations/airbridge), activa el interruptor y rellena los campos. Primero, introduce las credenciales para establecer la conexión entre tus perfiles de Airbridge y Adapty. Se requieren el nombre de la app de Airbridge y el token de API de Airbridge. <img src="/assets/shared/img/2b31d90-Untitled-1_1.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Ambos se encuentran en tu dashboard de Airbridge, en la sección [Third-party Integrations > Adapty](https://app.airbridge.io/app/testad/integrations/third-party/adapty). <img src="/assets/shared/img/5a2f627-Screenshot_2023-02-21_at_11.19.29_AM.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El campo del token de API de Adapty se genera previamente en el backend de Adapty. Debes copiar el valor del token de API de Adapty y pegarlo en el dashboard de Airbridge en el campo Adapty Authorization Token. <img src="/assets/shared/img/ff422d1-CleanShot_2023-03-01_at_17.11.412x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Configurar eventos y etiquetas \{#configure-events-and-tags\} Debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar a Airbridge desde Adapty. <img src="/assets/shared/img/eb4e3a9-CleanShot_2023-08-22_at_13.58.472x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Simplemente activa los que necesites. ### Conectar tu app con Airbridge \{#connect-your-app-to-airbridge\} Para la integración, debes pasar `airbridge_device_id` al perfil y llamar a `setIntegrationIdentifier` tal como se muestra en el siguiente ejemplo: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers do { try await Adapty.setIntegrationIdentifier( key: "airbridge_device_id", value: AirBridge.deviceUUID() ) } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers Airbridge.getDeviceInfo().getUUID(object: AirbridgeCallback.SimpleCallback<String>() { override fun onSuccess(result: String) { Adapty.setIntegrationIdentifier("airbridge_device_id", result) { error -> if (error != null) { // handle the error } } } override fun onFailure(throwable: Throwable) { } }) ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```javascript showLineNumbers final deviceUUID = await Airbridge.state.deviceUUID; try { await Adapty().setIntegrationIdentifier( key: "airbridge_device_id", value: deviceUUID, ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers try { const deviceId = await Airbridge.state.deviceUUID(); await adapty.setIntegrationIdentifier("airbridge_device_id", deviceId); } catch (error) { // handle `AdaptyError` } ``` </TabItem> </Tabs> Lee más sobre airbridgeDeviceId en la [documentación de Airbridge.](https://help.airbridge.io/en/developers/airbridge-device-id-faq) Adapty puede tardar hasta 24 horas en recibir los datos de atribución de Airbridge tras un evento de suscripción. Adapty los mostrará en el dashboard de inmediato. ## Estructura del evento \{#event-structure\} Adapty envía los eventos seleccionados a Airbridge según lo configurado en la sección **Events names** de la [**página de integración de Airbridge**](https://app.adapty.io/integrations/airbridge). Cada evento tiene la siguiente estructura: ```json { "user": { "externalUserID": "user_12345", "externalUserEmail": "user@example.com", "attributes": { "is_premium": true } }, "device": { "deviceUUID": "550e8400-e29b-41d4-a716-446655440000", "deviceModel": "iPhone 14 Pro", "osName": "iOS", "osVersion": "17.0.1", "locale": "en-US", "timezone": "America/New_York", "ifa": "00000000-0000-0000-0000-000000000000", "ifv": "00000000-0000-0000-0000-000000000000" }, "app": { "packageName": "com.example.app", "version": "1.2.3" }, "eventUUID": "d4f6f1f4-96fb-4a31-bafd-599fef77be90", "eventTimestamp": 1709294400000, "eventData": { "goal": { "category": "airbridge.subscribe", "customAttributes": { "isTrialConverted": true }, "semanticAttributes": { "transactionID": "GPA.3383-4699-1373-07113", "totalValue": 9.99, "currency": "USD", "period": "P1M", "isRenewal": true, "renewalCount": 2, "products": [ { "productID": "yearly.premium.6999", "name": "yearly.premium.6999", "position": 1 } ] } } } } ``` Donde: | Parámetro | Tipo | Descripción | |:---------------------------------------------|:--------|:-----------------------------------------------------------------------------------| | `user` | Object | Información del usuario. | | `user.externalUserID` | String | El Customer User ID del usuario. | | `user.externalUserEmail` | String | La dirección de correo electrónico del usuario (si está disponible). | | `user.attributes` | Object | Atributos personalizados del usuario. | | `device` | Object | Información del dispositivo. | | `device.deviceUUID` | String | El UUID del dispositivo en Airbridge. | | `device.deviceModel` | String | Modelo del dispositivo (p. ej., "iPhone 14 Pro"). | | `device.osName` | String | Nombre del sistema operativo (p. ej., "iOS", "Android"). | | `device.osVersion` | String | Versión del sistema operativo. | | `device.ifa` | String | **Solo iOS**. ID para anunciantes. | | `device.ifv` | String | **Solo iOS**. ID para proveedores. | | `device.gaid` | String | **Solo Android**. Google Advertising ID. | | `app` | Object | Información de la app. | | `app.packageName` | String | El nombre del paquete / bundle ID de la aplicación. | | `app.version` | String | La versión de la aplicación. | | `eventUUID` | String | ID único del evento en Adapty. | | `eventTimestamp` | Long | Marca de tiempo del evento en milisegundos. | | `eventData` | Object | Detalles del evento. | | `eventData.goal.category` | String | La categoría del evento en Airbridge (mapeada desde el evento de Adapty). | | `eventData.goal.semanticAttributes` | Object | Atributos estándar del evento. | | `...semanticAttributes.transactionID` | String | ID de transacción del store. | | `...semanticAttributes.totalValue` | Float | Importe de los ingresos. | | `...semanticAttributes.currency` | String | Código de divisa (p. ej., "USD"). | | `...semanticAttributes.period` | String | Período de suscripción en formato de duración ISO 8601 (p. ej., "P1M"). | | `...semanticAttributes.isRenewal` | Boolean | `true` si se trata de una transacción de renovación. | | `...semanticAttributes.renewalCount` | Integer | Número de renovaciones exitosas. | | `...semanticAttributes.products` | Array | Lista de productos involucrados en el evento. | | `...semanticAttributes.products[].productID` | String | El ID del producto en el store (p. ej., "yearly.premium.6999"). | | `...semanticAttributes.products[].name` | String | Igual que `productID`. | | `...semanticAttributes.products[].position` | Integer | La posición del producto en la lista (siempre 1). | --- # File: apple-search-ads --- --- title: "Apple Ads" description: "Integra Apple Ads con Adapty para optimizar las conversiones de suscripción." --- :::important La integración de Apple Ads en **App settings** se usa únicamente para análisis básicos y para las integraciones de SplitMetrics Acquire y Asapty. [Apple Ads Manager](adapty-ads-manager) utiliza una conexión independiente. Conecta tu cuenta de Apple Ads en los [ajustes de Apple Ads Manager](adapty-ads-manager-get-started). ::: Adapty puede ayudarte a obtener datos de atribución de Apple Ads y analizar tus métricas con segmentación por campaña y palabra clave. Adapty recopila automáticamente los datos de atribución de Apple Ads a través de su SDK y el framework AdServices. Una vez que hayas configurado la integración con Apple Ads, Adapty comenzará a recibir datos de atribución de Apple Ads. Puedes acceder a estos datos fácilmente y consultarlos en la página de perfiles. <img src="/assets/shared/img/ba4a3e9-CleanShot_2023-08-21_at_15.14.592x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty al framework AdServices \{#connect-adapty-to-the-adservices-framework\} Apple Ads a través de [AdServices](https://developer.apple.com/documentation/adservices) requiere cierta configuración en el Adapty Dashboard, y también deberás habilitarlo en la parte de la app. Para configurar Apple Ads usando el framework AdServices a través de Adapty, sigue estos pasos: #### Paso 1: Configurar Info.plist \{#step-1-configure-infoplist\} Añade `AdaptyAppleSearchAdsAttributionCollectionEnabled` al archivo `Info.plist` de la app y establece su valor en `YES` (booleano). #### Paso 2: Obtener la clave pública \{#step-2-obtain-public-key\} En el Adapty Dashboard, ve a [Settings -> Apple Ads.](https://app.adapty.io/settings/apple-search-ads) Localiza la clave pública pregenerada (Adapty te proporciona un par de claves) y cópiala. <img src="/assets/shared/img/baa5998-CleanShot_2023-08-21_at_14.55.542x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::note Si usas un servicio alternativo o tu propia solución para la atribución de Apple Ads, puedes subir tu propia clave privada. ::: #### Paso 3: Configurar la gestión de usuarios en Apple Ads \{#step-3-configure-user-management-on-apple-ads\} En tu [cuenta de Apple Ads](https://ads.apple.com/app-store), ve a la página **Settings > User Management**. Para que Adapty pueda obtener los datos de atribución, debes invitar a otra cuenta de Apple ID y concederle acceso como API Account Manager. Puedes usar cualquier cuenta a la que tengas acceso o crear una nueva específicamente para este fin. Lo importante es que debes poder iniciar sesión en Apple Ads con ese Apple ID. <img src="/assets/shared/img/ec183b2-kdjsfldsfjkdsfdfd.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### Paso 4: Generar las credenciales de API \{#step-4-generate-api-credentials\} A continuación, inicia sesión en Apple Ads con la cuenta recién añadida. Ve a Settings -> API en la interfaz de Apple Ads. Pega la clave pública que copiaste anteriormente en el campo correspondiente. Genera nuevas credenciales de API. #### Paso 5: Configurar Adapty con las credenciales de Apple Ads \{#step-5-configure-adapty-with-apple-ads-credentials\} Copia los campos Client ID, Team ID y Key ID de los ajustes de Apple Ads. En el Adapty Dashboard, pega estas credenciales en los campos correspondientes. <img src="/assets/shared/img/7356113-CleanShot_2023-08-21_at_15.08.512x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Conectar tu app a la red AdServices \{#connect-your-app-to-the-adservices-network\} Una vez que completes [la configuración del framework AdServices](#connect-the-adservices-framework), Adapty empieza a recopilar automáticamente los datos de atribución de Apple Search Ads. No necesitas añadir ningún código al SDK. En aplicaciones iOS, estos datos de atribución **siempre** tendrán prioridad sobre los datos de otras fuentes. Si este comportamiento no es el deseado, *desactiva* la atribución de ASA siguiendo las instrucciones a continuación. ## Desactivar la integración \{#disable-integration\} Para desactivar la atribución de Apple Search Ads, abre la pestaña [**App Settings** -> **Apple Search Ads**](https://app.adapty.io/settings/apple-search-ads) y desactiva el interruptor **Receive Apple Search Ads attribution**. <img src="/assets/shared/img/asa-disable.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning Ten en cuenta que desactivar esto detendrá completamente la recepción de análisis de ASA. Como resultado, ASA dejará de usarse en los análisis y no se enviará a las integraciones. Además, SplitMetrics Acquire y Asapty dejarán de funcionar, ya que dependen de la atribución de ASA para operar correctamente. La atribución recibida antes de este cambio no se verá afectada. ::: ## Subir tus propias claves \{#uploading-your-own-keys\} :::note Opcional Estos pasos no son necesarios para la atribución de Apple Ads, solo para trabajar con otros servicios como Asapty o tu propia solución. ::: Puedes usar tu propio par de claves pública-privada si estás utilizando otros servicios o una solución propia para la atribución de ASA. ### Paso 1 \{#step-1\} Genera la clave privada en el Terminal ```text showLineNumbers title="Text" openssl ecparam -genkey -name prime256v1 -noout -out private-key.pem ``` Súbela en Adapty Settings -> Apple Ads (botón Upload private key) ### Paso 2 \{#step-2\} Genera la clave pública en el Terminal ```text showLineNumbers title="Text" openssl ec -in private-key.pem -pubout -out public-key.pem ``` Puedes usar esta clave pública en los ajustes de Apple Ads de la cuenta con el rol API Account Manager. Así podrás usar los valores generados de Client ID, Team ID y Key ID tanto en Adapty como en otros servicios. --- # File: switch-from-appsflyer-s2s-api-2-to-3 --- --- title: "Cambiar de AppsFlyer S2S API 2 a 3" description: "Actualiza de AppsFlyer S2S API 2 a 3 en Adapty." --- Según las [novedades oficiales de AppsFlyer](https://support.appsflyer.com/hc/en-us/articles/20509378973457-Bulletin-Upgrading-the-AppsFlyer-S2S-API), para ofrecer un uso más seguro de la API y reducir el fraude, AppsFlyer ha actualizado su API servidor a servidor (S2S) para eventos in-app. El endpoint actual quedará obsoleto en el futuro, por lo que recomendamos empezar a planificar la migración. Adapty es compatible con AppsFlyer S2S API 3 y te permite realizar el cambio desde API 2 sin complicaciones. Ten en cuenta que este cambio es unidireccional, por lo que no podrás volver a API 2 una vez realizado. Para cambiar de AppsFlyer S2S API 2 a 3: 1. Abre el [sitio de AppsFlyer](https://www.appsflyer.com/home) e inicia sesión. 2. Haz clic en **Tu nombre de cuenta** -> **Security Center** en la esquina superior izquierda del dashboard. <img src="/assets/shared/img/be299ea-appsflyer_security_center.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Manage your account security**, haz clic en el botón **Manage your AppsFlyer API and S2S tokens**. 4. Si no tienes un token S2S, haz clic en el botón **New token**. Si ya lo tienes, continúa con el paso 8. <img src="/assets/shared/img/7934920-appsflyer_new_token.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. En la ventana **New token**, introduce el nombre del token. Este nombre es solo para tu referencia. 6. Selecciona **S2S** en la lista **Choose type**. 7. No olvides hacer clic en el botón **Create new token** para guardar el nuevo token. 8. En la ventana **Tokens**, copia el token S2S. <img src="/assets/shared/img/d014c25-appsflyer_tokens.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 9. Abre [**Integrations** -> **AppsFlyer**](https://app.adapty.io/integrations/appsflyer) en el Adapty Dashboard. 10. En el campo **AppsFlyer S2S API**, selecciona **API 3**. <img src="/assets/shared/img/c0b3e72-appsflyer_switch_API.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 11. Pega la clave S2S copiada en los campos **Dev key for iOS** y **Dev key for Android**. 12. Haz clic en el botón **Save** para confirmar el cambio. En ese momento, tu integración cambia instantáneamente a AppsFlyer S2S API 3 y los nuevos eventos se enviarán a la nueva URL: `https://api3.appsflyer.com/inappevent`. --- # File: asapty --- --- title: "Asapty" description: "Descubre Asapty y su papel en el ecosistema de suscripciones de Adapty." --- Con la integración de [Asapty](https://asapty.com/) puedes optimizar tus campañas de Search Ads. Adapty envía eventos de suscripción a Asapty para que puedas crear dashboards personalizados basados en la atribución de Apple Search Ads. Esta integración en concreto no añade ningún dato de atribución a Adapty, ya que obtenemos todo lo necesario directamente desde [ASA](apple-search-ads). ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty con Asapty \{#connect-adapty-to-asapty\} Para integrar Asapty, ve a [Integrations > Asapty](https://app.adapty.io/integrations/asapty) en el Adapty Dashboard y rellena el campo con tu Asapty ID. <img src="/assets/shared/img/895de2b-CleanShot_2023-08-14_at_18.57.462x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El Asapty ID se encuentra en la sección Settings > General de tu cuenta de Asapty. ### Configurar eventos y etiquetas \{#configure-events-and-tags\} Justo debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar a Asapty desde Adapty. Activa únicamente los que necesites. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). <img src="/assets/shared/img/58ddf41-CleanShot_2023-08-15_at_15.11.072x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Recomendamos usar los nombres de evento predeterminados que proporciona Asapty, aunque puedes cambiarlos según tus necesidades. ### Conectar tu app con Asapty \{#connect-your-app-to-asapty\} Una vez completados los pasos anteriores, Adapty recibe automáticamente los datos de atribución de Asapty. No es necesario solicitar explícitamente esos datos en el código de tu aplicación. Para mejorar la precisión de la atribución, configura Asapty para que incluya el `customerUserId` en los datos de cada evento. ## Estructura de eventos de Asapty \{#asapty-event-structure\} Adapty envía eventos a Asapty mediante una petición GET con parámetros de consulta. Cada URL de evento tiene este aspecto: ``` https://asapty.com/_api/mmpEvents/?source=adapty&asaptyid=a1b2c3d4&keywordid=12345&adgroupid=67890&campaignid=11223&conversiondate=1709294400000&event_name=subscription_renewed&install_time=1709100000&app_name=MyApp&json=%7B%22af_revenue%22%3A%229.99%22%2C%22af_currency%22%3A%22USD%22...%7D ``` Parámetros de consulta: | Parámetro | Tipo | Descripción | |:-----------------|:-------|:------------------------------------------------------------------| | `source` | String | Siempre "adapty". | | `asaptyid` | String | El Asapty ID de tus credenciales. | | `keywordid` | String | ID de palabra clave de Apple Search Ads (si está disponible). | | `adgroupid` | String | ID del grupo de anuncios de Apple Search Ads (si está disponible).| | `campaignid` | String | ID de campaña de Apple Search Ads (si está disponible). | | `conversiondate` | Long | Marca de tiempo del evento en **milisegundos**. | | `event_name` | String | Nombre del evento (mapeado desde el evento de Adapty). | | `install_time` | Long | Marca de tiempo de la instalación en segundos. | | `app_name` | String | Título de la app en Adapty (si está disponible). | | `json` | String | Cadena JSON codificada en URL con los detalles del evento (ver más abajo). | El parámetro `json` es una cadena JSON codificada en URL que contiene los siguientes campos: | Parámetro | Tipo | Descripción | |:--------------------------|:-------|:---------------------------------------------------| | `af_revenue` | String | Importe de ingresos como cadena de texto. | | `af_currency` | String | Código de moneda (p. ej., "USD"). | | `transaction_id` | String | ID de transacción del store. | | `original_transaction_id` | String | ID de transacción original del store. | | `purchase_date` | Long | Marca de tiempo de la compra en milisegundos. | | `original_purchase_date` | Long | Marca de tiempo de la compra original en milisegundos. | | `environment` | String | `Production` o `Sandbox`. | | `vendor_product_id` | String | ID del producto en el store. | | `profile_country` | String | Código de país basado en la IP del usuario. | | `store_country` | String | Código de país del store del usuario. | ## Solución de problemas \{#troubleshooting\} - Asegúrate de haber configurado [Apple Search Ads](apple-search-ads) en Adapty y de haber [subido las credenciales](https://app.adapty.io/settings/apple-search-ads); sin ellas, Asapty no funcionará. - Solo los perfiles con atribución de ASA detallada y no orgánica enviarán sus eventos a Asapty. Verás el mensaje "The user profile is missing the required integration data." si la atribución no es suficiente. - Los perfiles creados antes de configurar las integraciones no podrán enviar sus eventos a Asapty. - Si la integración con Adapty no funciona a pesar de estar correctamente configurada, comprueba que el toggle **Receive Apple Search Ads attribution in Adapty** esté activado en la pestaña [**App Settings** -> **Apple Search Ads**](https://app.adapty.io/settings/apple-search-ads). --- # File: branch --- --- title: "Branch" description: "Integra Branch con Adapty para rastrear deep links y conversiones de la app." --- [Branch](https://www.branch.io/) permite a los clientes llegar, interactuar y evaluar resultados en distintos dispositivos, canales y plataformas. Es una plataforma fácil de usar diseñada para aumentar los ingresos móviles mediante enlaces especializados que funcionan perfectamente en todos los dispositivos, canales y plataformas. Adapty ofrece un conjunto completo de datos que te permite rastrear los [eventos de suscripción](events) de los stores en un solo lugar. Con Adapty, puedes ver fácilmente el comportamiento de tus suscriptores, conocer sus preferencias y usar esa información para comunicarte con ellos de forma dirigida y efectiva. La integración entre Adapty y Branch funciona de dos maneras principales. 1. **Recibir datos de atribución de Branch** Una vez que hayas configurado la integración con Branch, Adapty comenzará a recibir datos de atribución de Branch. Puedes acceder y ver estos datos fácilmente en la página de perfil del usuario. <img src="/assets/shared/img/49f4aa7-CleanShot_2023-08-11_at_17.36.072x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. **Enviar eventos de suscripción a Branch** Adapty puede enviar todos los eventos de suscripción configurados en tu integración a Branch. Como resultado, podrás rastrear esos eventos dentro del dashboard de Branch. ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty con Branch \{#connect-adapty-to-branch\} Para integrar Branch, ve a [Integrations > Branch](https://app.adapty.io/integrations/branch) en el Adapty Dashboard, activa el interruptor y rellena los campos. <img src="/assets/shared/img/817a051-CleanShot_2023-08-11_at_15.54.372x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para obtener el valor del campo **Branch Key**, abre tu [Account Settings](https://dashboard.branch.io/account-settings/profile) de Branch y localiza el campo **Branch Key**. Úsalo para el campo **Key test** (para Sandbox) o **Key live** (para Producción) en el Adapty Dashboard. En Branch, cambia entre los entornos Live y Tests para obtener la clave correspondiente. <img src="/assets/shared/img/130e58b-CleanShot_2023-08-11_at_15.24.162x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Configurar eventos y etiquetas \{#configure-events-and-tags\} Debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar a Branch desde Adapty. Activa únicamente los que necesites. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). Puedes enviar un evento con los ingresos netos \(después del recorte de Apple/Google\) o solo los ingresos brutos. También puedes marcar la casilla para reportar en la moneda del usuario. <img src="/assets/shared/img/a645cf8-CleanShot_2023-08-11_at_15.18.282x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Te recomendamos usar los nombres de evento predeterminados que proporciona Adapty. Sin embargo, puedes cambiarlos según tus necesidades. Adapty enviará los eventos de suscripción a Branch mediante una integración servidor a servidor, lo que te permitirá ver todos los eventos de suscripción en tu dashboard de Branch y vincularlos a tus campañas de adquisición. ### Conectar tu app con Branch \{#connect-your-app-to-branch\} 1. Llama al método `.setIntegrationIdentifier()` del SDK para inicializar la conexión. Puedes pasar tu Branch Identity ID al parámetro `customerUserId`. --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="note"> Si usas un ID de usuario de terceros como Customer User ID, no lo pases durante `activate()` — es posible que el SDK de terceros aún no lo haya generado. En su lugar, llama primero a `activate()` sin CUID, luego a `setIntegrationIdentifier()`, y después a `identify()` con el CUID. </Callout> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers do { try await Adapty.setIntegrationIdentifier( key: "branch_id", value: <BRANCH_IDENTITY_ID> ) } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers // login and update attribution and identifier Branch.getAutoInstance(this) .setIdentity("YOUR_USER_ID") { referringParams, error -> referringParams?.let { data -> Adapty.updateAttribution(data, "branch") { error -> if (error != null) { //handle the error } } } } // logout Branch.getAutoInstance(context).logout() ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```javascript showLineNumbers import 'package:flutter_branch_sdk/flutter_branch_sdk.dart'; FlutterBranchSdk.setIdentity('YOUR_USER_ID'); ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers Branch.setIdentity("your user id"); ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers import branch from 'react-native-branch'; branch.setIdentity('YOUR_USER_ID'); ``` </TabItem> </Tabs> 2. Usa el método `.updateAttribution()` para guardar los datos de atribución. Si no especificaste el Branch user ID en el paso anterior, pásalo al parámetro `networkUserId` aquí. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers class YourBranchImplementation { func initializeBranch() { // Pass the attribution you receive from the initializing method of Branch iOS SDK to Adapty. Branch.getInstance().initSession(launchOptions: launchOptions) { (data, error) in if let data { Adapty.updateAttribution(data, source: .branch) } } } } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers //everything is in the above snippet for Android ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```javascript showLineNumbers try { await Adapty().setIntegrationIdentifier( key: "branch_id", value: <BRANCH_IDENTITY_ID>, ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers using AdaptySDK; Branch.initSession(delegate(Dictionary<string, object> parameters, string error) { string attributionString = JsonUtility.ToJson(parameters); Adapty.UpdateAttribution( attributionString, "branch", (error) => { // handle the error }); }); ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers import { adapty, AttributionSource } from 'react-native-adapty'; import branch from 'react-native-branch'; branch.subscribe({ enComplete: ({ params, }) => { adapty.updateAttribution(params, "branch"); }, }); ``` </TabItem> </Tabs> ## Estructura del evento \{#event-structure\} Adapty envía los eventos seleccionados a Branch tal como se configuran en la sección **Events names** de la [**página de integración de Branch**](https://app.adapty.io/integrations/branch). Cada evento tiene la siguiente estructura: ```json { "branch_key": "key_live_kaFuWw8WvY7n1ss7...", "name": "PURCHASE", "user_data": { "os": "iOS", "developer_identity": "user_12345", "country": "US", "ip": "192.168.100.1", "idfa": "00000000-0000-0000-0000-000000000000", "idfv": "00000000-0000-0000-0000-000000000000", "aaid": "00000000-0000-0000-0000-000000000000" }, "event_data": { "transaction_id": "GPA.3383-4699-1373-07113", "revenue": 9.99, "currency": "USD" }, "custom_data": { "vendor_product_id": "yearly.premium.6999", "original_transaction_id": "GPA.3383-4699-1373-07113", "store": "play_store", "environment": "production" } } ``` Donde: | Parámetro | Tipo | Descripción | |:-------------------------------|:-------|:-------------------------------------------------------------------------------------------------------------------------------------| | `branch_key` | String | Tu Branch Key. | | `name` | String | El nombre del evento de Branch (mapeado desde el evento de Adapty, p. ej., "PURCHASE"). | | `user_data` | Object | Información del usuario. | | `user_data.os` | String | "Android" o "iOS". | | `user_data.developer_identity` | String | El Customer User ID del usuario. | | `user_data.country` | String | Código de país basado en la IP del usuario. | | `user_data.ip` | String | Dirección IP del usuario. | | `user_data.idfa` | String | **Solo iOS**. ID para anunciantes. | | `user_data.idfv` | String | **Solo iOS**. ID para vendedores. | | `user_data.aaid` | String | **Solo Android**. Google Advertising ID. | | `event_data` | Object | Métricas estándar del evento (solo presente para PURCHASE y eventos similares). | | `event_data.transaction_id` | String | ID de transacción del store. | | `event_data.revenue` | Float | Importe de los ingresos. | | `event_data.currency` | String | Código de moneda (p. ej., "USD"). | | `custom_data` | Object | Atributos detallados del evento (contiene todos los [campos de evento](webhook-event-types-and-fields#for-most-event-types) disponibles). | --- # File: facebook-ads --- --- title: "Facebook Ads" description: "Integra Facebook Ads con Adapty para un marketing de suscripciones efectivo." --- Con la integración de Facebook Ads, puedes consultar fácilmente las estadísticas de tu app en Meta Analytics. Adapty envía eventos al Meta Ads Manager, lo que te ayuda a crear audiencias similares basadas en suscripciones para obtener mejores resultados. Así puedes ver con precisión cuánto dinero generan tus anuncios gracias a las suscripciones. La integración entre Adapty y Facebook Ads funciona de la siguiente manera: Adapty envía todos los eventos de suscripción configurados en tu integración a Facebook Ads. Esta integración es muy útil para evaluar la efectividad de tus campañas publicitarias. ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty con Facebook Ads \{#connect-adapty-to-facebook-ads\} Para integrar Facebook Ads y analizar las métricas de tu app, puedes configurar la integración con Meta Analytics. Al enviar eventos al Meta Ads Manager, puedes crear audiencias similares basadas en eventos de suscripción como las renovaciones. Para configurar esta integración, ve a [Integrations > Facebook Ads](https://app.adapty.io/integrations/facebookanalytics) en el Adapty Dashboard y proporciona las credenciales requeridas. :::note Ten en cuenta que la integración de Facebook Ads solo funciona en iOS 14.5+ para usuarios que hayan dado su consentimiento ATT. ::: <img src="/assets/shared/img/fd84ddf-CleanShot_2023-08-15_at_15.45.442x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 1. Para encontrar el App ID, abre la página de tu app en [App Store Connect](https://appstoreconnect.apple.com/), ve a la página **App Information** en la sección **General** y busca el **Apple ID** en la parte inferior izquierda de la pantalla. 2. Necesitas una aplicación en la plataforma [Meta for Developers](https://developers.facebook.com/). Inicia sesión en tu app y accede a la configuración avanzada. Encontrarás el **App ID** en la cabecera. <img src="/assets/shared/img/4b326c4-001563-August-23-4tO3JVso.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Desactiva el seguimiento del lado del cliente en la configuración de tu Meta SDK para evitar el doble conteo de ingresos en Meta Ads Manager. Puedes encontrar este ajuste en tu Meta Developer Console en **App Settings > Advanced Settings**. Establece **Log in-app events automatically** en "No". Esto garantizará que los eventos de ingresos solo se registren a través de la integración de Adapty. Para rastrear eventos de instalación y uso, deberás activar el Meta SDK en tu código. Puedes encontrar los detalles de implementación en la documentación del Meta SDK para tu plataforma: - [iOS SDK](https://developers.facebook.com/docs/ios/getting-started) - [Android SDK](https://developers.facebook.com/docs/android/getting-started) - [Unity SDK](https://developers.facebook.com/docs/unity/getting-started/canvas) <img src="/assets/shared/img/c4eb8eb-001565-August-23-483KKBbC.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> También puedes usar esta integración con apps Android. Si configuras la configuración del Android SDK en **App Settings**, con introducir el **Facebook App ID** es suficiente. ### Configurar eventos y etiquetas \{#configure-events-and-tags\} Ten en cuenta que la integración de Facebook Ads está orientada específicamente a empresas que utilizan Meta para sus campañas publicitarias y las optimizan en función del comportamiento de los clientes. Es compatible con los eventos estándar de Meta para fines de optimización. Por ello, no es posible modificar el nombre del evento en la integración de Meta Ads. Adapty mapea automáticamente los eventos de tus clientes a sus correspondientes eventos de Meta para un análisis preciso. | Evento de Adapty | Evento de Meta Ads | | :---------------------------- | :-------------------------- | | Subscription initial purchase | Subscribe | | Subscription renewed | Subscribe | | Subscription cancelled | CancelSubscription | | Trial started | StartTrial | | Trial converted | Subscribe | | Trial cancelled | CancelTrial | | Non subscription purchase | fb_mobile_purchase | | Billing issue detected | billing_issue_detected | | Entered grace period | entered_grace_period | | Auto renew off | auto_renew_off | | Auto renew on | auto_renew_on | | Auto renew off subscription | auto_renew_off_subscription | | Auto renew on subscription | auto_renew_on_subscription | StartTrial, Subscribe y CancelSubscription son eventos estándar. <img src="/assets/shared/img/8a5df9d-CleanShot_2023-07-04_at_12.47.312x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para activar eventos específicos, simplemente activa los que necesites. Si se seleccionan varios nombres de eventos, Adapty consolidará los datos de todos los eventos seleccionados en un único nombre de evento de Adapty. ### Conectar tu app con Facebook Ads \{#connect-your-app-to-facebook-ads\} Si sigues los pasos anteriores, Facebook recibirá automáticamente los datos de suscripción desde Adapty. Tras los cambios en el IDFA en iOS 14.5, recomendamos que solicites el `facebookAnonymousId` del usuario a Facebook. De este modo, si el IDFA del usuario no está disponible, la integración seguirá funcionando. Sigue la <InlineTooltip tooltip="guía para establecer atributos de usuario">[iOS](setting-user-attributes), [Android](android-setting-user-attributes), [React Native](react-native-setting-user-attributes), [Flutter](flutter-setting-user-attributes) y [Unity](unity-setting-user-attributes)</InlineTooltip> para configurar este parámetro. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers do { try await Adapty.setIntegrationIdentifier( key: "facebook_anonymous_id", value: AppEvents.shared.anonymousID ) } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.setIntegrationIdentifier( "facebook_anonymous_id", AppEventsLogger.getAnonymousAppDeviceGUID(context) ) { error -> if (error != null) { // handle the error } } ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers try { const anonymousId = await AppEventsLogger.getAnonymousID(); await adapty.setIntegrationIdentifier("facebook_anonymous_id", anonymousId); } catch (error) { // handle `AdaptyError` } ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```text There is no official SDK for Flutter ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp anonymousID is not available in the official SDK https://github.com/facebook/facebook-sdk-for-unity/issues/676 ``` </TabItem> </Tabs> ## Estructura del evento \{#event-structure\} Adapty envía eventos a Facebook Ads (Meta) a través de la Graph API. Cada evento tiene la siguiente estructura: ```json { "event": "CUSTOM_APP_EVENTS", "app_user_id": "user_12345", "advertiser_id": "00000000-0000-0000-0000-000000000000", "advertiser_tracking_enabled": 1, "application_tracking_enabled": 1, "custom_events": "[{\"_eventName\":\"Subscribe\",\"_logTime\":1709294400,\"fb_num_items\":1,\"fb_content_type\":\"in_app\",\"fb_content_id\":\"yearly.premium.6999\",\"fb_currency\":\"USD\",\"fb_order_id\":\"GPA.3383...\",\"fb_transaction_id\":\"GPA.3383...\",\"_valueToSum\":9.99}]", "extinfo": "[\"i2\",\"com.example.app\",\"1.0.0\",\"100\",\"17.0.1\",\"iPhone14,3\",\"en_US\",\"GMT+3\",\"\",0,0,0,0,0,0,\"GMT+3\"]", "anon_id": "facebook_anon_id_123" } ``` Donde: | Parámetro | Tipo | Descripción | |:---|:---|:---| | `event` | String | Siempre "CUSTOM_APP_EVENTS". | | `app_user_id` | String | El Customer User ID del usuario. | | `advertiser_id` | String | IDFA (iOS) o Advertising ID (Android). | | `advertiser_tracking_enabled` | Integer | `1` si el seguimiento está habilitado (ATT autorizado), `0` en caso contrario. | | `application_tracking_enabled` | Integer | Siempre `1`. | | `custom_events` | String | Cadena codificada en JSON con los objetos de evento (ver más abajo). | | `extinfo` | String | Cadena codificada en JSON con información de la app y el dispositivo (p. ej., versión, SO, idioma). | | `anon_id` | String | Facebook Anonymous ID (si está disponible). | El parámetro `custom_events` es un array codificado en JSON de objetos que contiene: | Parámetro | Tipo | Descripción | |:---|:---|:---| | `_eventName` | String | El nombre del evento de Meta Ads (p. ej., "Subscribe"). | | `_logTime` | Long | Marca de tiempo del evento en segundos. | | `_valueToSum` | Float | Importe de los ingresos. | | `fb_content_id` | String | El ID del producto en el store. | | `fb_currency` | String | Código de moneda (p. ej., "USD"). | | `fb_order_id` | String | ID de la transacción original. | | `fb_transaction_id` | String | ID de la transacción original. | | `fb_content_type` | String | Siempre "in_app". | | `fb_num_items` | Integer | Siempre 1 para eventos de compra. | --- # File: singular --- --- title: "Singular" description: "Integra Singular con Adapty para analizar datos de marketing y suscripciones." --- [Singular](https://www.singular.net/) es una de las principales plataformas MMP (Mobile Measurement Partner) que recopila y presenta datos de campañas de marketing, lo que ayuda a las empresas a hacer seguimiento del rendimiento de sus campañas. Adapty proporciona un conjunto completo de datos que te permite rastrear [eventos de suscripción](events) desde los stores en un solo lugar. Con Adapty, puedes ver fácilmente el comportamiento de tus suscriptores, conocer sus preferencias y usar esa información para comunicarte con ellos de forma dirigida y efectiva. Esta integración te permite, por tanto, rastrear eventos de suscripción en Singular y analizar con precisión cuántos ingresos generan tus campañas. Adapty puede enviar a Singular todos los eventos de suscripción configurados en tu integración. Como resultado, podrás hacer seguimiento de esos eventos desde el dashboard de Singular. Esta integración es especialmente útil para evaluar la efectividad de tus campañas publicitarias. ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty con Singular \{#connect-adapty-to-singular\} Para configurar la integración con Singular, ve a [Integrations > Singular](https://app.adapty.io/integrations/singular) en tu Adapty Dashboard, activa el interruptor y rellena los campos. Las siguientes credenciales están disponibles: - **Singular SDK Key**: Obligatoria. La clave SDK de producción de tu app en Singular. - **Singular SDK Key (Sandbox)**: Opcional. La clave SDK para tu app de Singular en sandbox. Si no se configura, los eventos de sandbox no se enviarán a Singular. Ambas claves se encuentran en el dashboard de Singular en **Developer tools -> SDK Keys -> SDK Key (**no** SDK Secret)**: <img src="/assets/shared/img/4bc50d1-singular_sdk_key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar a Singular desde Adapty. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). <img src="/assets/shared/img/e67de0c-singular_events.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Te recomendamos usar los nombres de evento predeterminados que proporciona Adapty, aunque puedes modificarlos según tus necesidades. Adapty enviará los eventos de suscripción a Singular mediante una integración server-to-server, lo que te permitirá ver todos los eventos de suscripción en tu dashboard de Singular y vincularlos con tus campañas de adquisición. :::warning Los perfiles creados antes de configurar las integraciones no podrán enviar sus eventos a Singular. ::: ### Conectar tu app con Singular \{#connect-your-app-to-singular\} La integración entre Adapty y Singular es server-to-server, por lo que no necesitas añadir ningún código adicional en tu aplicación. ## Estructura del evento \{#event-structure\} Adapty envía eventos a Singular mediante una solicitud GET con parámetros de consulta. Cada evento tiene esta estructura: ```json { "n": "subscription_renewed", "a": "singular_sdk_key_123", "p": "iOS", "i": "com.example.app", "ip": "192.168.100.1", "idfa": "00000000-0000-0000-0000-000000000000", "idfv": "00000000-0000-0000-0000-000000000000", "ve": "17.0.1", "att_authorization_status": 3, "custom_user_id": "user_12345", "utime": 1709294400, "amt": 9.99, "cur": "USD", "purchase_product_id": "yearly.premium.6999", "purchase_transaction_id": "GPA.3383...", "e": "{\"is_revenue_event\":true,\"amt\":9.99,\"cur\":\"USD\",\"purchase_product_id\":\"yearly.premium.6999\",\"purchase_transaction_id\":\"GPA.3383...\"}" } ``` Donde: | Parámetro | Tipo | Descripción | |:---------------------------|:--------|:---------------------------------------------------------------| | `n` | String | El nombre del evento (mapeado desde el evento de Adapty). | | `a` | String | Tu Singular SDK Key. | | `p` | String | Plataforma ("iOS" o "Android"). | | `i` | String | ID de la app en el store (Bundle ID). | | `ip` | String | Dirección IP del usuario. | | `idfa` | String | **Solo iOS**. ID for Advertisers (en mayúsculas). | | `idfv` | String | **Solo iOS**. ID for Vendors (en mayúsculas). | | `aifa` | String | **Solo Android**. Google Advertising ID (en minúsculas). | | `andi` | String | **Solo Android**. Android ID (en minúsculas). | | `asid` | String | **Solo Android**. App Set ID (en minúsculas). | | `ve` | String | Versión del sistema operativo. | | `att_authorization_status` | Integer | **Solo iOS**. Estado ATT (p. ej., `3` para autorizado). | | `custom_user_id` | String | El Customer User ID del usuario. | | `utime` | Long | Marca de tiempo UNIX del evento en segundos. | | `amt` | Float | Importe de los ingresos. | | `cur` | String | Código de moneda (p. ej., "USD"). | | `purchase_product_id` | String | El ID del producto en el store. | | `purchase_transaction_id` | String | ID de transacción original. | | `e` | String | Cadena JSON con los detalles del evento (ver más abajo). | El parámetro `e` (datos de evento personalizados) es una cadena codificada en JSON que contiene: | Parámetro | Tipo | Descripción | |:--------------------------|:--------|:-----------------------------------------------| | `is_revenue_event` | Boolean | `true` si el evento incluye ingresos. | | `amt` | Float | Importe de los ingresos. | | `cur` | String | Código de moneda. | | `purchase_product_id` | String | El ID del producto en el store. | | `purchase_transaction_id` | String | ID de transacción original. | --- # File: tenjin --- --- title: "Integración con Tenjin" description: "" --- Tenjin es una plataforma de atribución y analítica móvil para desarrolladores de apps y profesionales del marketing. Proporciona herramientas para medir y optimizar campañas de adquisición de usuarios, ofreciendo información detallada sobre el rendimiento de la app y el comportamiento de los usuarios. Con su enfoque transparente y flexible, Tenjin agrega datos de redes publicitarias y stores de apps, lo que permite a los equipos analizar el ROI, rastrear conversiones y monitorear métricas clave de rendimiento. Al reenviar [eventos de suscripción](events) a Tenjin, puedes ver exactamente de dónde provienen las conversiones y qué campañas generan más valor en todos los canales, plataformas y dispositivos. En esencia, los dashboards de Tenjin ofrecen analítica avanzada para campañas de marketing. Al reenviar la atribución de Tenjin a Adapty, enriqueces la analítica de Adapty con criterios de filtrado adicionales que puedes usar en el análisis de cohortes y conversiones. Esta integración funciona de dos maneras principales: 1. **Recibir datos de atribución de Tenjin** Una vez integrado, Adapty recopila datos de atribución de Tenjin. Puedes acceder a esta información en la página del perfil del usuario en el Adapty Dashboard. 2. **Enviar eventos de suscripción a Tenjin** Adapty envía eventos de compra a Tenjin en tiempo real. Estos eventos ayudan a evaluar la efectividad de tus campañas publicitarias directamente en el dashboard de Tenjin. | Característica de integración | Descripción | | ----------------------------- | ------------------------------------------------------------ | | Frecuencia | Tiempo real | | Dirección de datos | <p>Transmisión bidireccional:</p><ul><li> **Eventos de Adapty**: Del servidor de Adapty al servidor de Tenjin</li><li> **Atribución de Tenjin**: Del SDK de Tenjin al servidor de Adapty</li></ul> | | Punto de integración de Adapty | <ul><li> SDKs de Tenjin y Adapty en el código de la app móvil</li><li> Servidor de Adapty</li></ul> | ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty con Tenjin \{#connect-adapty-to-tenjin\} 1. Abre la página [**Integrations** -> **Tenjin**](https://app.adapty.io/integrations/tenjin) en el Adapty Dashboard. 2. Activa el interruptor para habilitar la integración. <img src="/assets/shared/img/tenjin-toggle.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Inicia sesión en el [Tenjin Dashboard](https://tenjin.com/). 4. Ve a **Configuration** -> **Apps** en el menú de navegación. <img src="/assets/shared/img/tenjin-apps.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Selecciona la app para tu plataforma (iOS o Android) y navega a la pestaña **App and SDK**. 6. En la pestaña **App and SDK**, haz clic en **Copy** en la columna **SDK Key**. Si aún no tienes una SDK Key, haz clic en el botón **Generate SDK Key** para crear una. <img src="/assets/shared/img/tenjin-copy-sdk-key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Vuelve al Adapty Dashboard y pega la SDK Key copiada en el campo de la plataforma correspondiente: - Para apps iOS: pégala en el campo **iOS SDK Key** o **iOS Sandbox SDK Key** - Para apps Android: pégala en el campo **Android SDK Key** o **Android Sandbox SDK Key** :::info Tenjin no tiene un modo Sandbox específico para la integración server-to-server. Usa una app de Tenjin separada o la misma clave tanto para eventos de producción como de sandbox. ::: <img src="/assets/shared/img/tenjin-keys.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 8. Si tienes apps en ambas plataformas, repite los pasos 5-7 para la otra plataforma. 9. (opcional) Ajusta la sección **How the revenue data should be sent** si es necesario. Para una explicación detallada de sus configuraciones, consulta la sección [Integration settings](configuration#integration-settings). 10. Haz clic en **Save** para finalizar la configuración. Adapty comenzará a enviar eventos de compra a Tenjin y a recibir datos de atribución. Puedes ajustar el intercambio de eventos en la sección **Events names**. ### Configurar eventos y etiquetas \{#configure-events-and-tags\} Tenjin solo acepta eventos de compra y **Trial started**. En la sección **Events names**, selecciona qué eventos compartir con Tenjin según tus objetivos de seguimiento. <img src="/assets/shared/img/tenjin-events.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Conectar tu app con Tenjin \{#connect-your-app-to-tenjin\} Usa el método del SDK `Adapty.updateAttribution()` para obtener los datos de atribución de Tenjin y enviarlos a Adapty. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers func updateTenjinId() { guard let tenjinId = TenjinSDK.getAnalyticsInstallationId() else { return } do { try await Adapty.setIntegrationIdentifier( key: "tenjin_analytics_installation_id", value: tenjinId ) } catch { // handle the error } } func updateTenjinAttribution() { let instance = TenjinSDK.getInstance("<YOUR_TENJIN_API_TOKEN>") instance?.getAttributionInfo { info, _ in guard let info else { return } Task { do { try await Adapty.updateAttribution(info, source: "tenjin") } catch { // handle the error } } } } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.setIntegrationIdentifier("tenjin_analytics_installation_id", tenjinSdk.analyticsInstallationId) { error -> if (error != null) { // handle the error } } tenjinSdk.getAttributionInfo { attribution -> if (attribution == null) return@getAttributionInfo Adapty.updateAttribution(attribution, "tenjin") { error -> if (error != null) { // handle the error } } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers Adapty.setIntegrationIdentifier("tenjin_analytics_installation_id", tenjinSdk.getAnalyticsInstallationId(), error -> { if (error != null) { // handle the error } }); tenjinSdk.getAttributionInfo(attribution -> { if (attribution == null) return; Adapty.updateAttribution(attribution, "tenjin", error -> { // handle the error }); }); ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```javascript showLineNumbers try { final tenjinId = await TenjinSDK.instance.getAnalyticsInstallationId(); if (tenjinId != null) { await Adapty().setIntegrationIdentifier( key: 'tenjin_analytics_installation_id', value: tenjinId, ); } final attribution = await TenjinSDK.instance.getAttributionInfo(); if (attribution != null) { await Adapty().updateAttribution(attribution, source: 'tenjin'); } } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers using AdaptySDK; using System.Linq; BaseTenjin instance = Tenjin.getInstance("<SDK_KEY>"); var tenjinId = instance.GetAnalyticsInstallationId(); Adapty.SetIntegrationIdentifier( "tenjin_analytics_installation_id", tenjinId, (error) => { // handle the error }); instance.GetAttributionInfo((attribution) => { var dynamicAttribution = attribution.ToDictionary( kvp => kvp.Key, kvp => (dynamic)kvp.Value ); Adapty.UpdateAttribution( dynamicAttribution, "tenjin", (error) => { // handle the error }); }); ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers // ... const posthog = usePostHog() // ... try { await adapty.setIntegrationIdentifier("tenjin_analytics_installation_id", await Tenjin.getAnalyticsInstallationId()); } catch (error) { // handle `AdaptyError` } ``` </TabItem> </Tabs> ## Estructura de los eventos \{#event-structure\} Adapty envía los eventos seleccionados a Tenjin tal como se configuraron en la sección **Events names** de la [**página de integración con Tenjin**](https://app.adapty.io/integrations/tenjin). Cada evento tiene la siguiente estructura: ```json showLineNumbers title="Json" { "price": 99.0, "locale": "en-US", "country": "ME", "postcut": "false", "currency": "USD", "platform": "ios", "quantity": 1, "bundle_id": "com.adapty.adaptydemoapp", "ip_address": "127.0.0.1", "os_version": "18.1.1", "product_id": "month.premium.99", "app_version": "3.2.0", "sdk_version": "server", "device_model": "iPhone 13 Mini", "advertising_id": "00000000-0000-0000-0000-000000000000", "os_version_release": "18.1.1", "developer_device_id": "00000000-0000-0000-0000-000000000000", "analytics_installation_id": "00000000-0000-0000-0000-000000000000" } ``` Donde: | **Parámetro** | **Tipo** | **Descripción** | | ----------------------------- | ---------------- | ------------------------------------------------------------ | | **price** | Float | El precio unitario del artículo comprado en la unidad estándar de la moneda (por ejemplo, USD se reporta en dólares). | | **locale** | String | El locale del dispositivo. En Android: `Locale.getDefault().toString()`. En iOS: `[[NSLocale currentLocale] localeIdentifier]`. | | **country** | String | El código de país ISO del locale (por ejemplo, US para Estados Unidos). | | **postcut** | String (Boolean) | Indica si la compra se envió después del recorte de la plataforma. 1 para verdadero, 0 para falso. | | **currency** | String | El código de moneda ISO (por ejemplo, USD para dólares estadounidenses). | | **platform** | String | La plataforma del dispositivo (por ejemplo, ios, android, windows, amazon). | | **quantity** | Integer | El número de unidades compradas. | | **bundle_id** | String | El identificador de bundle de la app (por ejemplo, `com.example.app`). | | **ip_address** | String (IPv4) | La dirección IP del usuario. Se usa para determinar el país. | | **os_version** | String | La versión del sistema operativo del dispositivo. En Android: `String.valueOf(Build.VERSION.SDK_INT)`. En iOS: `[[UIDevice currentDevice] systemVersion]`. | | **product_id** | String | Identificador único del producto comprado. | | **app_version** | Float, Decimal | La versión de la app. En Android: `context.getPackageManager().getPackageInfo()`. En iOS: `[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]`. | | **sdk_version** | String | La versión del SDK en uso, siempre establecida en `server`. | | **device_model** | String | El modelo del dispositivo. En Android: `Build.MODEL`. En iOS: `sysctl("hw.machine")`. | | **advertising_id** | UUID | El ID publicitario del dispositivo. Obligatorio en Android. En iOS puede estar vacío o ser todo ceros. | | **os_version_release** | String | La versión de lanzamiento del sistema operativo. En Android: `String.valueOf(Build.VERSION.RELEASE)`. En iOS: `[[UIDevice currentDevice] systemVersion]`. | | **developer_device_id** | UUID | El identificador del proveedor (solo iOS). | | **analytics_installation_id** | UUID | ID de instalación de analítica. Para más detalles, consulta la documentación en `https://docs.tenjin.com`. | --- # File: amplitude --- --- title: "Amplitude" description: "Integra Amplitude con Adapty para obtener mejores insights sobre el comportamiento de los usuarios." --- [Amplitude](https://amplitude.com/) es un potente servicio de analítica para móviles. Con Adapty, puedes enviar eventos a Amplitude fácilmente, ver cómo se comportan los usuarios y tomar decisiones inteligentes. Adapty ofrece un conjunto completo de datos que te permite rastrear [eventos de suscripción](events) desde los stores en un solo lugar y enviarlos a tu cuenta de Amplitude. Esto te permite correlacionar el comportamiento de tus usuarios con su historial de pagos en Amplitude, y basar tus decisiones de producto en datos reales. ### Cómo configurar la integración con Amplitude \{#how-to-set-up-amplitude-integration\} En Adapty puedes configurar flujos separados para **eventos de producción** y **de prueba** provenientes del entorno sandbox de Apple o Stripe, o de una cuenta de prueba de Google. - Para eventos de producción, introduce las claves API de **Production** desde el dashboard de Amplitude, con una clave API única para cada plataforma: iOS, Android y Stripe. - Para eventos de prueba, usa los campos de **Sandbox** según sea necesario. Para configurar la integración con Amplitude: 1. Abre [**Integrations** -> **Amplitude**](https://app.adapty.io/integrations/amplitude) en tu Adapty Dashboard. <img src="/assets/shared/img/3b50552-CleanShot_2023-08-15_at_16.47.102x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Activa **Amplitude integration** para habilitarla. 3. Rellena los campos de la integración: | Campo | Descripción | | ------------------------------------------ | ------------------------------------------------------------ | | **Amplitude iOS/ Android/ Stripe API key** | Introduce la **API Key** de Amplitude para iOS/ Android/ Stripe en Adapty. Encuéntrala en **Project settings** dentro de Amplitude. Para más ayuda, consulta la [documentación de Amplitude](https://amplitude.com/docs/apis/authentication). Comienza con las claves de **Sandbox** para pruebas y luego cambia a las claves de **Production** tras pruebas exitosas. | <img src="/assets/shared/img/2297782-CleanShot_2023-08-15_at_16.53.512x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Ajustes opcionales para mayor personalización: | Parámetro | Descripción | | --------------------------------------- | ------------------------------------------------------------ | | **How the revenue data should be sent** | Elige si enviar los ingresos brutos o los ingresos después de impuestos y comisiones. Consulta [Comisión del store e impuestos](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue) para más detalles. | | **Exclude historical events** | Elige excluir los eventos anteriores a la instalación del SDK de Adapty para evitar datos duplicados. Por ejemplo, si un usuario se suscribió el 10 de enero pero instaló el SDK de Adapty el 6 de marzo, Adapty solo enviará eventos a partir del 6 de marzo. | | **Send User Attributes** | Selecciona esta opción para enviar atributos específicos del usuario, como preferencias de idioma. | | **Always populate user_id** | Adapty envía automáticamente `device_id` como `amplitudeDeviceId`. Para `user_id`, esta configuración define el comportamiento: <ul><li>**ON**: Envía el `profile_id` de Adapty si `amplitudeUserId` o `customer_user_id` no están disponibles.</li><li>**OFF**: Deja `user_id` vacío si ninguno de los IDs está disponible.</li></ul> | 5. Elige los eventos que deseas recibir y [asigna sus nombres](amplitude#events-and-tags). 6. Haz clic en **Save** para confirmar los cambios. Una vez que hagas clic en **Save**, Adapty comenzará a enviar eventos a Amplitude. Además de los eventos, Adapty envía el [estado de la suscripción](subscription-status) y el ID del producto de suscripción a las [propiedades de usuario de Amplitude](https://amplitude.com/docs/data/user-properties-and-events). ### Eventos y etiquetas \{#events-and-tags\} Debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar a Amplitude desde Adapty. Simplemente activa los que necesites. Consulta la lista completa de eventos disponibles en Adapty [aquí](events). <img src="/assets/shared/img/da67694-CleanShot_2023-08-15_at_16.52.352x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Recomendamos usar los nombres de eventos predeterminados que proporciona Adapty. No obstante, puedes cambiarlos según tus necesidades. Adapty enviará los eventos de suscripción a Amplitude mediante una integración servidor a servidor, lo que te permitirá ver todos los eventos de suscripción en tu dashboard de Amplitude. ### Configuración del SDK \{#sdk-configuration\} Usa el método `setIntegrationIdentifier()` para establecer el parámetro `amplitude_device_id`. Es imprescindible configurarlo para que la integración funcione. Si tienes registro de usuarios, también puedes pasar `amplitude_user_id`. --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="note"> Si usas un ID de usuario de terceros como Customer User ID, no lo pases durante `activate()` — es posible que el SDK de terceros aún no lo haya generado. En su lugar, llama primero a `activate()` sin CUID, luego a `setIntegrationIdentifier()`, y después a `identify()` con el CUID. </Callout> <Tabs groupId="current-os" queryString> <TabItem value="Swift" label="iOS (Swift)" default> **Configurar amplitudeDeviceId** ```swift showLineNumbers do { try await Adapty.setIntegrationIdentifier( key: "amplitude_device_id", value: Amplitude.instance().deviceId ) } catch { // handle the error } ``` **Configurar amplitudeUserId** ```swift showLineNumbers do { try await Adapty.setIntegrationIdentifier( key: "amplitude_user_id", value: "YOUR_AMPLITUDE_USER_ID" ) } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> **Configurar amplitudeDeviceId** ```kotlin showLineNumbers //for Amplitude maintenance SDK (obsolete) val amplitude = Amplitude.getInstance() val amplitudeDeviceId = amplitude.getDeviceId() val amplitudeUserId = amplitude.getUserId() //for actual Amplitude Kotlin SDK val amplitude = Amplitude( Configuration( apiKey = AMPLITUDE_API_KEY, context = applicationContext ) ) val amplitudeDeviceId = amplitude.store.deviceId // Adapty.setIntegrationIdentifier("amplitude_device_id", amplitudeDeviceId) { error -> if (error != null) { // handle the error } } ``` **Configurar amplitudeUserId** ```kotlin showLineNumbers //for Amplitude maintenance SDK (obsolete) val amplitude = Amplitude.getInstance() val amplitudeDeviceId = amplitude.getDeviceId() val amplitudeUserId = amplitude.getUserId() //for actual Amplitude Kotlin SDK val amplitude = Amplitude( Configuration( apiKey = AMPLITUDE_API_KEY, context = applicationContext ) ) val amplitudeUserId = amplitude.store.userId // Adapty.setIntegrationIdentifier("amplitude_user_id", amplitudeUserId) { error -> if (error != null) { // handle the error } } ``` </TabItem> <TabItem value="Flutter" label="Flutter (Dart)" default> **Configurar amplitudeDeviceId** ```javascript showLineNumbers final Amplitude amplitude = Amplitude.getInstance(instanceName: "YOUR_INSTANCE_NAME"); try { await Adapty().setIntegrationIdentifier( key: "amplitude_device_id", value: amplitude.getDeviceId(), ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` **Configurar amplitudeUserId** ```javascript showLineNumbers final Amplitude amplitude = Amplitude.getInstance(instanceName: "YOUR_INSTANCE_NAME"); try { await Adapty().setIntegrationIdentifier( key: "amplitude_user_id", value: "YOUR_AMPLITUDE_USER_ID", ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="Unity" label="Unity (C#)" default> **Configurar amplitudeDeviceId** ```csharp showLineNumbers using AdaptySDK; Adapty.SetIntegrationIdentifier( "amplitude_device_id", amplitude.getDeviceId(), (error) => { // handle the error }); ``` **Configurar amplitudeUserId** ```csharp showLineNumbers using AdaptySDK; Adapty.SetIntegrationIdentifier( "amplitude_user_id", "YOUR_AMPLITUDE_USER_ID", (error) => { // handle the error }); ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> **Configurar amplitudeDeviceId** ```typescript showLineNumbers try { await adapty.setIntegrationIdentifier("amplitude_device_id", deviceId); } catch (error) { // handle `AdaptyError` } ``` **Configurar amplitudeUserId** ```typescript showLineNumbers try { await adapty.setIntegrationIdentifier("amplitude_user_id", userId); } catch (error) { // handle `AdaptyError` } ``` </TabItem> </Tabs> ## Estructura del evento de Amplitude \{#amplitude-event-structure\} Adapty envía eventos a Amplitude a través de la HTTP API v2. Cada evento tiene la siguiente estructura: ```json { "api_key": "your_amplitude_api_key", "events": [ { "partner_id": "adapty", "event_type": "subscription_renewed", "time": 1709294400000, "insert_id": "123e4567-e89b-12d3-a456-426614174000", "user_id": "user_12345", "device_id": "device_12345", "platform": "iOS", "os_name": "iOS", "productId": "yearly.premium.6999", "revenue": 9.99, "event_properties": { "vendor_product_id": "yearly.premium.6999", "original_transaction_id": "GPA.3383...", "currency": "USD", "environment": "Production", "store": "app_store" }, "user_properties": { "subscription_state": "subscribed", "subscription_product": "yearly.premium.6999" } } ] } ``` Donde: | Parámetro | Tipo | Descripción | |:----------------------------|:-------|:---------------------------------------------------------------------| | `api_key` | String | Tu clave API de Amplitude. | | `events` | Array | Lista de objetos de evento (Adapty envía uno a la vez). | | `events[].partner_id` | String | Siempre "adapty". | | `events[].event_type` | String | El nombre del evento (mapeado desde el evento de Adapty). | | `events[].time` | Long | Marca de tiempo del evento en milisegundos. | | `events[].insert_id` | String | ID único del evento (UUID). | | `events[].user_id` | String | ID de usuario de Amplitude o ID de usuario del cliente. | | `events[].device_id` | String | ID del dispositivo en Amplitude. | | `events[].platform` | String | Plataforma (p. ej., "iOS", "Android"). | | `events[].os_name` | String | Nombre del sistema operativo. | | `events[].productId` | String | El ID del producto en el store. | | `events[].revenue` | Float | Importe de los ingresos. | | `events[].event_properties` | Object | Atributos detallados del evento (contiene todos los [campos de evento](webhook-event-types-and-fields#for-most-event-types) disponibles). | | `events[].user_properties` | Object | Atributos del usuario, como el estado de la suscripción. | --- # File: appmetrica --- --- title: "AppMetrica" description: "Integra AppMetrica con Adapty para un análisis detallado de suscripciones." --- [AppMetrica](https://appmetrica.yandex.com/about) es una herramienta de análisis gratuita que te ayuda a rastrear el comportamiento de los usuarios y analizar el rendimiento de tu app móvil en tiempo real. Al integrar AppMetrica con Adapty, puedes obtener información más detallada sobre tus métricas de suscripción y la interacción de los usuarios. ## Cómo configurar la integración con AppMetrica \{#how-to-set-up-appmetrica-integration\} La configuración de la integración con AppMetrica consta de dos pasos principales: 1. Configurar la integración en el Adapty Dashboard 2. Configurar la integración en el código de tu app ### Configuración en el dashboard \{#dashboard-configuration\} Para configurar la integración con AppMetrica: 1. Abre la [lista de apps de AppMetrica](https://appmetrica.yandex.ru/application/list) 2. Selecciona la app que quieres rastrear 3. Ve a **Settings > Main** y copia el **Application ID** y la **Post API key** <img src="/assets/shared/img/appmetrica.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Ve a [Integrations > AppMetrica](https://app.adapty.io/integrations/appmetrica) en el Adapty Dashboard 5. Pega tus credenciales de AppMetrica. <img src="/assets/shared/img/appmetrica_creds.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Eventos y etiquetas \{#events-and-tags\} Adapty te permite enviar tres grupos de eventos a AppMetrica. Puedes habilitar los eventos que necesitas para rastrear el rendimiento de tu app. Para ver la lista completa de eventos disponibles, consulta nuestra [documentación de eventos](events). :::note AppMetrica sincroniza los eventos cada 4 horas, por lo que puede haber un retraso antes de que los eventos aparezcan en tu dashboard. ::: <img src="/assets/shared/img/6ed2d88-CleanShot_2023-08-18_at_14.59.042x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::tip Recomendamos usar los nombres de eventos predeterminados de Adapty para mantener la coherencia, aunque puedes personalizarlos para que coincidan con tu configuración de análisis existente. ::: ### Configuración de ingresos \{#revenue-settings\} De forma predeterminada, Adapty envía los datos de ingresos como propiedades en los eventos, que aparecen en el informe de Events de AppMetrica. Puedes configurar cómo se calculan y muestran estos datos: - **Revenue calculation**: Elige cómo se calculan los valores de ingresos para que coincidan con tus necesidades de informes financieros: - **Gross revenue**: Muestra los ingresos totales antes de cualquier deducción, útil para rastrear el importe completo que pagan los clientes - **Proceeds after store commission**: Muestra los ingresos después de deducir las comisiones de App Store/Play Store, lo que te ayuda a rastrear los ingresos reales - **Proceeds after store commission and taxes**: Muestra los ingresos netos después de las comisiones del store y los impuestos aplicables, lo que ofrece la imagen más precisa de tus ganancias - **Report user's currency**: Cuando está habilitado, las ventas se informan en la moneda local del usuario, lo que facilita el análisis de ingresos por región. Cuando está deshabilitado, todas las ventas se convierten a USD para mantener informes coherentes en diferentes mercados. - **Send revenue events**: Habilita esta opción para que los datos de ingresos aparezcan no solo en el informe de Events, sino también en el informe [In-app and ad revenue](https://appmetrica.yandex.com/docs/en/mobile-reports/revenue-report) de AppMetrica. Asegúrate de no enviar ingresos desde ningún otro lugar, ya que esto podría generar duplicados. - **Exclude historical events**: Cuando está habilitado, Adapty no enviará eventos que ocurrieron antes de que el usuario instalara la app con el SDK de Adapty. Esto ayuda a evitar la duplicación de datos si ya estabas enviando eventos a analytics antes de integrar Adapty. <img src="/assets/shared/img/appmetrica_revenue.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Configuración del SDK \{#sdk-configuration\} Para habilitar la integración con AppMetrica en tu app, necesitas configurar dos identificadores: 1. `appmetrica_device_id`: Necesario para la integración básica 2. `appmetrica_profile_id`: Opcional, pero recomendado si tu app tiene registro de usuarios Usa el método `setIntegrationIdentifier()` para establecer estos valores. A continuación se muestra cómo implementarlo en cada plataforma: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="note"> Si usas un ID de usuario de terceros como Customer User ID, no lo pases durante `activate()` — es posible que el SDK de terceros aún no lo haya generado. En su lugar, llama primero a `activate()` sin CUID, luego a `setIntegrationIdentifier()`, y después a `identify()` con el CUID. </Callout> <Tabs groupId="current-os" queryString> <TabItem value="Swift" label="iOS (Swift)" default> **Setting appmetrica_device_id** ```swift showLineNumbers AppMetrica.requestStartupIdentifiers(on: nil) { ids, error in if let error { // handle AppMetrica error return } guard let deviceIDHash = ids?[.deviceIDHashKey] as? String else { // handle AppMetrica error return } Task { do { try await Adapty.setIntegrationIdentifier( key: "appmetrica_device_id", value: deviceIDHash ) } catch { // handle the error } } } ``` **Setting appmetrica_profile_id** ```swift showLineNumbers do { try await Adapty.setIntegrationIdentifier( key: "appmetrica_profile_id", value: "YOUR_APPMETRICA_PROFILE_ID" ) } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> **Setting appmetrica_device_id** ```kotlin showLineNumbers val startupParamsCallback = object: StartupParamsCallback { override fun onReceive(result: StartupParamsCallback.Result?) { val deviceIdHash = result?.deviceIdHash ?: return Adapty.setIntegrationIdentifier("appmetrica_device_id", deviceIdHash) { error -> if (error != null) { // handle the error } } } override fun onRequestError( reason: StartupParamsCallback.Reason, result: StartupParamsCallback.Result? ) { //handle the error } } AppMetrica.requestStartupParams(context, startupParamsCallback, listOf(StartupParamsCallback.APPMETRICA_DEVICE_ID_HASH)) ``` **Setting appmetrica_profile_id** ```kotlin showLineNumbers val startupParamsCallback = object: StartupParamsCallback { override fun onReceive(result: StartupParamsCallback.Result?) { val deviceIdHash = result?.deviceIdHash ?: return Adapty.setIntegrationIdentifier("appmetrica_device_id", deviceIdHash) { error -> if (error != null) { // handle the error } } Adapty.setIntegrationIdentifier("appmetrica_profile_id", "YOUR_ADAPTY_CUSTOMER_USER_ID") { error -> if (error != null) { // handle the error } } } override fun onRequestError( reason: StartupParamsCallback.Reason, result: StartupParamsCallback.Result? ) { //handle the error } } AppMetrica.requestStartupParams(context, startupParamsCallback, listOf(StartupParamsCallback.APPMETRICA_DEVICE_ID_HASH)) ``` </TabItem> <TabItem value="Flutter" label="Flutter (Dart)" default> **Setting appmetrica_device_id** ```javascript showLineNumbers final startupParams = await AppMetrica.requestStartupParams([AppMetricaStartupParams.deviceIdHashKey]); final deviceIdHash = startupParams.result?.deviceIdHash; if (deviceIdHash != null) { try { await Adapty().setIntegrationIdentifier( key: "appmetrica_device_id", value: deviceIdHash, ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } } ``` **Setting appmetrica_profile_id** ```javascript showLineNumbers try { await Adapty().setIntegrationIdentifier( key: "appmetrica_profile_id", value: "YOUR_APPMETRICA_PROFILE_ID", ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="Unity" label="Unity (C#)" default> **Setting appmetrica_device_id** ```csharp showLineNumbers using AdaptySDK; using Io.AppMetrica; AppMetrica.RequestStartupParams( (result, errorReason) => { string deviceIdHash = result.DeviceIdHash; if (deviceIdHash != null) { Adapty.SetIntegrationIdentifier( "appmetrica_device_id", deviceIdHash, (error) => { // handle the error }); } }, new List<string>() { StartupParamsKey.AppMetricaDeviceIDHash } ); ``` **Setting appmetrica_profile_id** ```csharp showLineNumbers Adapty.SetIntegrationIdentifier( "appmetrica_profile_id", "YOUR_APPMETRICA_PROFILE_ID", (error) => { // handle the error }); ``` </TabItem> <TabItem value="RN" label="React Native (TS)" default> **Setting appmetrica_device_id** ```typescript showLineNumbers // ... const startupParamsCallback = async ( params?: StartupParams, reason?: StartupParamsReason ) => { const deviceIdHash = params?.deviceIdHash if (deviceIdHash) { try { await adapty.setIntegrationIdentifier("appmetrica_device_id", deviceIdHash); } catch (error) { // handle `AdaptyError` } } } AppMetrica.requestStartupParams(startupParamsCallback, [DEVICE_ID_HASH_KEY]) ``` **Setting appmetrica_profile_id** ```typescript showLineNumbers try { await adapty.setIntegrationIdentifier("appmetrica_profile_id", 'YOUR_ADAPTY_CUSTOMER_USER_ID'); } catch (error) { // handle `AdaptyError` } ``` </TabItem> </Tabs> ## Estructura de eventos de AppMetrica \{#appmetrica-event-structure\} Adapty envía eventos a AppMetrica mediante solicitudes POST con parámetros pasados como parámetros de consulta. Para cada evento de Adapty, AppMetrica recibe hasta **dos solicitudes separadas**: 1. **Evento de perfil** (siempre enviado): Contiene metadatos del evento 2. **Evento de ingresos** (opcional): Contiene datos de ingresos si la opción "Send revenue events" está habilitada en el Adapty Dashboard ### Solicitud de evento de perfil \{#profile-event-request\} Se envía a: `https://api.appmetrica.yandex.ru/logs/v1/import/events` Ejemplo de URL con parámetros de consulta: ``` POST https://api.appmetrica.yandex.ru/logs/v1/import/events?post_api_key=your_key&application_id=your_app_id&event_name=subscription_renewed&event_timestamp=1709294400&event_json=%7B%22vendor_product_id%22%3A%22yearly.premium%22...%7D&os_name=ios&ios_ifa=00000000-0000-0000-0000-000000000000&ios_ifv=12345678-1234-1234-1234-123456789012&profile_id=user_12345&session_type=foreground ``` Parámetros de consulta: | Parámetro | Tipo | Descripción | |:-----------------------|:-------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `post_api_key` | String | Tu Post API Key de AppMetrica. | | `application_id` | String | Tu Application ID de AppMetrica. | | `event_name` | String | El nombre del evento (mapeado desde el evento de Adapty). | | `event_timestamp` | Long | Marca de tiempo UNIX del evento en segundos. Se limita a los últimos 7 días si es más antiguo. | | `event_json` | String | Cadena JSON codificada en URL que contiene todos los [campos del evento](webhook-event-types-and-fields#for-most-event-types) disponibles. Solo se incluyen los campos no nulos. | | `os_name` | String | "ios" o "android". | | `profile_id` | String | AppMetrica Profile ID (si está configurado), en caso contrario el Customer User ID (si está disponible). | | `appmetrica_device_id` | String | AppMetrica Device ID Hash. Solo se envía si `profile_id` no está disponible. | | `session_type` | String | Siempre "foreground". | | `ios_ifa` | String | **Solo iOS**. ID for Advertisers. | | `ios_ifv` | String | **Solo iOS**. ID for Vendors. | | `google_aid` | String | **Solo Android**. Google Advertising ID. | ### Solicitud de evento de ingresos (opcional) \{#revenue-event-request-optional\} Se envía a: `https://api.appmetrica.yandex.ru/logs/v1/import/revenue` Esta solicitud solo se envía cuando la opción "Send revenue events" está habilitada en la configuración de la integración en tu Adapty Dashboard. Ejemplo de URL con parámetros de consulta: ``` POST https://api.appmetrica.yandex.ru/logs/v1/import/revenue?post_api_key=your_key&application_id=your_app_id&revenue_event_type=subscription_renewed&price=9.99¤cy=USD&product_id=yearly.premium&quantity=1&transaction_id=GPA.3383...&payload=%7B%22vendor_product_id%22%3A%22yearly.premium%22...%7D&os_name=ios&ios_ifa=00000000-0000-0000-0000-000000000000&profile_id=user_12345&session_type=foreground ``` Parámetros de consulta: | Parámetro | Tipo | Descripción | |:-----------------------|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `post_api_key` | String | Tu Post API Key de AppMetrica. | | `application_id` | String | Tu Application ID de AppMetrica. | | `revenue_event_type` | String | El tipo de evento de ingresos (por ejemplo, "subscription_renewed", "refund", "intro_started"). Consulta el [mapeo de eventos de AppMetrica](#revenue-event-type-mapping). | | `price` | Float | Importe de ingresos (según tu configuración de cálculo de ingresos). | | `currency` | String | Código de moneda (por ejemplo, "USD"). | | `product_id` | String | El ID del producto en el store. | | `quantity` | Integer | Siempre 1. | | `transaction_id` | String | ID de transacción del store. | | `payload` | String | Cadena JSON codificada en URL que contiene los detalles del evento. Se recorta automáticamente si supera los 30 KB eliminando campos opcionales por orden de importancia para preservar los datos más críticos. | | `os_name` | String | "ios" o "android". | | `profile_id` | String | AppMetrica Profile ID (si está configurado), en caso contrario el Customer User ID (si está disponible). | | `appmetrica_device_id` | String | AppMetrica Device ID Hash. Solo se envía si `profile_id` no está disponible. | | `session_type` | String | Siempre "foreground". | | `ios_ifa` | String | **Solo iOS**. ID for Advertisers. | | `ios_ifv` | String | **Solo iOS**. ID for Vendors. | | `google_aid` | String | **Solo Android**. Google Advertising ID. | --- # File: firebase-and-google-analytics --- --- title: "Firebase y Google Analytics" description: "Integra Firebase y Google Analytics con Adapty para obtener mejores insights." --- Si utilizas productos de Google como Google Analytics y Firebase, puedes enriquecer tus datos analíticos con eventos de Adapty mediante la integración descrita en este artículo. Los eventos se envían a través de Google Analytics a Firebase y pueden usarse en cualquiera de estos servicios. Esta función te permite relacionar el comportamiento de tus usuarios con su historial de pagos en Firebase, lo que te facilita tomar decisiones de producto bien fundamentadas. ## Cómo configurar la integración con Firebase \{#how-to-set-up-firebase-integration\} ### 1\. Configura Firebase \{#1-set-up-firebase\} Primero, debes habilitar la integración entre Firebase y Google Analytics. Puedes hacerlo en tu Firebase Console, en la pestaña **Integrations**. <img src="/assets/shared/img/14b6d84-CleanShot_2023-08-18_at_20.37.462x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### 2\. Integra con Adapty \{#2-integrate-with-adapty\} A continuación, Adapty necesita tu Firebase App ID y el API Secret de Google Analytics para enviar eventos y propiedades de usuario. Puedes encontrar estos parámetros en la Firebase Console y en la pestaña Data Streams de Google Analytics, respectivamente. <img src="/assets/shared/img/14d8224-CleanShot_2023-08-21_at_12.14.182x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> A continuación, accede a la página de detalles del Stream de la app dentro de la sección Data Streams de la configuración de administrador en [Google Analytics](https://analytics.google.com/analytics/web/#/). <img src="/assets/shared/img/b26ae6a-CleanShot_2023-08-21_at_12.28.482x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En **Additional settings**, ve a la página **Measurement Protocol API secrets** y crea un nuevo **API Secret** si no existe. Copia el valor. <img src="/assets/shared/img/7404bde-CleanShot_2023-08-21_at_12.33.242x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <img src="/assets/shared/img/0266112-CleanShot_2023-08-21_at_12.34.442x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El siguiente paso será ajustar la integración en el Adapty Dashboard. Deberás proporcionarnos el Firebase App ID y el API Secret de Google Analytics para tus plataformas iOS, Android y/o Stripe. :::note Si usas la integración con Stripe, ten en cuenta sus limitaciones en la [guía](stripe#current-limitations) correspondiente. Estas limitaciones también se aplicarán a la integración con Firebase. ::: <img src="/assets/shared/img/4eaae3f-CleanShot_2023-08-21_at_12.35.312x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Configuración del SDK \{#sdk-configuration\} :::important Para que la integración funcione, asegúrate de añadir Firebase a tu app primero: - [iOS](https://firebase.google.com/docs/ios/setup) - [Android](https://firebase.google.com/docs/android/setup) - [React Native](https://firebase.google.com/docs/web/setup) - [Flutter](https://firebase.google.com/docs/flutter/setup) - [Unity](https://firebase.google.com/docs/unity/setup) ::: A continuación, debes configurar el SDK de Adapty para asociar a tus usuarios con Firebase. Para cada usuario, debes enviar el `firebase_app_instance_id` a Adapty. A continuación puedes ver un ejemplo del código que se puede usar para integrar el SDK de Firebase y el SDK de Adapty. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers FirebaseApp.configure() if let appInstanceId = Analytics.appInstanceID() { do { try await Adapty.setIntegrationIdentifier( key: "firebase_app_instance_id", value: appInstanceId ) } catch { // handle the error } } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers //after Adapty.activate() FirebaseAnalytics.getInstance(context).appInstanceId.addOnSuccessListener { appInstanceId -> Adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId) { error -> if (error != null) { // handle the error } } } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers //after Adapty.activate() FirebaseAnalytics.getInstance(context).getAppInstanceId().addOnSuccessListener(appInstanceId -> { Adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId, error -> { if (error != null) { // handle the error } }); }); ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```javascript showLineNumbers final appInstanceId = await FirebaseAnalytics.instance.appInstanceId; try { await Adapty().setIntegrationIdentifier( key: "firebase_app_instance_id", value: appInstanceId, ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers using AdaptySDK; // We suppose FirebaseAnalytics Unity Plugin is already installed Firebase.Analytics .FirebaseAnalytics .GetAnalyticsInstanceIdAsync() .ContinueWithOnMainThread((task) => { if (!task.IsCompletedSuccessfully) { // handle error return; } var firebaseId = task.Result var builder = new Adapty.ProfileParameters.Builder(); Adapty.SetIntegrationIdentifier( "firebase_app_instance_id", firebaseId, (error) => { // handle the error }); }); ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers try { const appInstanceId = await analytics().getAppInstanceId(); await adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId); } catch (error) { // handle `AdaptyError` } ``` </TabItem> </Tabs> ## Envío de eventos y propiedades de usuario \{#sending-events-and-user-properties\} Ahora es el momento de decidir qué eventos recibirás en Firebase y Google Analytics. <img src="/assets/shared/img/7923397-set_up_events_names.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Verás que algunos eventos tienen nombres específicos, como "Purchase", mientras que otros son eventos habituales de Adapty. Esta diferencia se debe a los [tipos de eventos de Google Analytics](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events). Actualmente, los eventos compatibles son [Refund](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#refund) y [Purchase](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#purchase). El resto son eventos personalizados. Por lo tanto, asegúrate de que los nombres de tus eventos sean [compatibles](https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=firebase#limitations) con Google Analytics. También puedes configurar el envío de propiedades de usuario desde el Adapty Dashboard. <img src="/assets/shared/img/e053006-CleanShot_2023-08-21_at_12.50.162x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Esto significa que Adapty enriquecerá tus eventos con `subscription_state` y `subscription_product_id`. Sin embargo, también debes [habilitar](https://support.google.com/analytics/answer/14240153?hl=en) esta función en Google Analytics. Para usar **User properties** en tus análisis, empieza por asignarlas a una dimensión personalizada en la Firebase Console, a través de **Custom Definitions**, seleccionando el ámbito **User**, y asignándoles un nombre y una descripción. <img src="/assets/shared/img/1962ef1-CleanShot_2023-08-21_at_12.48.222x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <img src="/assets/shared/img/2425cc0-CleanShot_2023-08-21_at_12.52.532x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Asegúrate de que los nombres de tus propiedades de usuario sean `subscription_state` y `subscription_product_id`. De lo contrario, no podremos enviarte los datos del estado de la suscripción. ¡Y eso es todo! Espera a recibir nuevos insights de Google. ## Solución de problemas \{#troubleshooting\} ### Discrepancia de datos \{#data-discrepancy\} Si existe una discrepancia de datos entre Adapty y Firebase, puede deberse a que no todos tus usuarios utilizan la versión de la app que incluye el SDK de Adapty. Para garantizar la consistencia de los datos, puedes obligar a tus usuarios a actualizar la app a una versión que incluya el SDK de Adapty. Además, los eventos de sandbox se envían a Firebase por defecto y esto no se puede deshabilitar. Por eso, en situaciones en las que una app tiene pocos eventos de producción y muchos de sandbox, puede haber una discrepancia notable entre los datos de Analytics de Adapty y los de Firebase. ### Los eventos aparecen como entregados en Adapty pero no están disponibles en Firebase \{#events-are-shown-as-delivered-in-adapty-but-not-available-in-firebase\} Existe un retraso entre el momento en que Adapty envía los eventos y el momento en que aparecen en el dashboard de Google Analytics. Se recomienda consultar el Realtime Dashboard de tu cuenta de Google Analytics para ver los eventos más recientes en tiempo real. --- # File: mixpanel --- --- title: "Mixpanel" description: "Conecta Mixpanel con Adapty para potentes analíticas de suscripciones." --- [Mixpanel](https://mixpanel.com/home/) es un potente servicio de analítica de producto. Su solución de seguimiento basada en eventos permite a los equipos de producto obtener información valiosa sobre las mejores estrategias de adquisición, conversión y retención de usuarios en distintas plataformas. Esta integración te permite llevar todos los eventos de Adapty a Mixpanel. Como resultado, obtendrás una visión más completa de tu negocio de suscripciones y las acciones de tus clientes. Adapty proporciona un conjunto completo de datos que te permite hacer seguimiento de los [eventos de suscripción](events) desde los stores en un solo lugar. Con Adapty, puedes ver fácilmente cómo se comportan tus suscriptores, entender qué les gusta y usar esa información para comunicarte con ellos de forma dirigida y eficaz. ## Cómo configurar la integración con Mixpanel \{#how-to-set-up-mixpanel-integration\} 1. Abre la página [Integrations -> Mixpanel](https://app.adapty.io/integrations/mixpanel) en el Adapty Dashboard. 2. Activa el interruptor e introduce tu **Mixpanel Token**. Puedes especificar un token para todas las plataformas o limitarlo a plataformas concretas si solo quieres recibir datos de algunas de ellas. <img src="/assets/shared/img/mixpanel.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Cómo encontrar tu Mixpanel Token \{#finding-your-mixpanel-token\} Para obtener tu **Mixpanel Token**: 1. Inicia sesión en tu [Mixpanel Dashboard](https://mixpanel.com/settings/project/). 2. Abre **Settings** y selecciona **Organization Settings**. <img src="/assets/shared/img/mixpanel-settings.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la barra lateral izquierda, ve a **Projects** y selecciona tu proyecto. <img src="/assets/shared/img/mixpanel-project-id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Cómo funciona la integración \{#how-the-integration-works\} Adapty mapea automáticamente las propiedades de evento relevantes —como el ID de usuario y los ingresos— a las [propiedades nativas de Mixpanel](https://docs.mixpanel.com/docs/data-structure/user-profiles). Esto garantiza un seguimiento e informes precisos de los eventos relacionados con suscripciones. Además, Adapty acumula datos de ingresos por usuario y actualiza sus [Propiedades de Perfil de Usuario](https://docs.mixpanel.com/docs/data-structure/user-profiles), incluidas `subscription state` y `subscription product ID`. Una vez recibido un evento, Mixpanel actualiza los campos correspondientes en tiempo real. ## Eventos y etiquetas \{#events-and-tags\} Debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar a Mixpanel desde Adapty. Activa simplemente los que necesites. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). <img src="/assets/shared/img/mixpanel-events.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Recomendamos usar los nombres de evento predeterminados que proporciona Adapty. Aun así, puedes cambiarlos según tus necesidades. ## Configuración del SDK \{#sdk-configuration\} Usa el método `.setIntegrationIdentifier()` para establecer `mixpanelUserId`. Si no se establece, Adapty usa tu ID de usuario (`customerUserId`) o, si es nulo, el ID de Adapty. Asegúrate de que el ID de usuario que utilizas para enviar datos a Mixpanel desde tu app sea el mismo que envías a Adapty. --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="note"> Si usas un ID de usuario de terceros como Customer User ID, no lo pases durante `activate()` — es posible que el SDK de terceros aún no lo haya generado. En su lugar, llama primero a `activate()` sin CUID, luego a `setIntegrationIdentifier()`, y después a `identify()` con el CUID. </Callout> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers do { try await Adapty.setIntegrationIdentifier( key: "mixpanel_user_id", value: Mixpanel.mainInstance().distinctId ) } catch { // handle the error } ``` </TabItem> <TabItem value="swift-callback" label="iOS (Swift-Callback)" default> ```swift showLineNumbers let builder = AdaptyProfileParameters.Builder() .with(mixpanelUserId: Mixpanel.mainInstance().distinctId) Adapty.updateProfile(params: builder.build()) ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.setIntegrationIdentifier("mixpanel_user_id", mixpanelAPI.distinctId) { error -> if (error != null) { // handle the error } } ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```javascript showLineNumbers final mixpanel = await Mixpanel.init("Your Token", trackAutomaticEvents: true); final distinctId = await mixpanel.getDistinctId(); try { await Adapty().setIntegrationIdentifier( key: "mixpanel_user_id", value: distinctId, ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers using AdaptySDK; var distinctId = Mixpanel.DistinctId; if (distinctId != null) { Adapty.SetIntegrationIdentifier( "mixpanel_user_id", distinctId, (error) => { // handle the error }); } ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers // If you already have a shared Mixpanel instance in your app, use that instance instead. const trackAutomaticEvents = true; const mixpanel = new Mixpanel('YOUR_PROJECT_TOKEN', trackAutomaticEvents); await mixpanel.init(); // This is Mixpanel's current distinct_id (auto-generated, or set via mixpanel.identify(...)) const mixpanelUserId = await mixpanel.getDistinctId(); try { await adapty.setIntegrationIdentifier("mixpanel_user_id", mixpanelUserId); } catch (error) { // handle `AdaptyError` } ``` </TabItem> </Tabs> ## Estructura de eventos de Mixpanel \{#mixpanel-event-structure\} Adapty envía eventos a Mixpanel usando el método `track`. Las propiedades del evento tienen esta estructura: ```json { "event": "subscription_renewed", "properties": { "ip": 0, "time": 1709294400, "$insert_id": "123e4567-e89b-12d3-a456-426614174000", "vendor_product_id": "yearly.premium.6999", "original_transaction_id": "GPA.3383...", "currency": "USD", "environment": "Production", "store": "app_store", "purchase_date": "2024-03-01T12:00:00.000000+0000" } } ``` Donde: | Parámetro | Tipo | Descripción | |:-------------------------------------|:--------|:---------------------------------------------------------------| | `event` | String | El nombre del evento (mapeado desde el evento de Adapty). | | `properties` | Object | Propiedades del evento. | | `properties.ip` | Integer | Dirección IP (enviada como 0 en comunicaciones servidor a servidor). | | `properties.time` | Long | Marca de tiempo UNIX del evento en segundos. | | `properties.$insert_id` | String | ID único del evento (UUID) para deduplicación. | | `properties.vendor_product_id` | String | El ID de producto del store. | | `properties.original_transaction_id` | String | ID de la transacción original. | | `properties.currency` | String | Código de moneda. | | `properties.store` | String | Nombre del store (p. ej., "app_store"). | | `properties.environment` | String | Entorno ("Sandbox" o "Production"). | ### Actualizaciones del perfil de usuario \{#user-profile-updates\} Adapty también actualiza el Perfil de Usuario de Mixpanel usando `people_set` con las siguientes propiedades: | Parámetro | Tipo | Descripción | |:--------------------------|:-------|:--------------------------------------------------------------------| | `subscription_state` | String | Estado actual de la suscripción (p. ej., "subscribed"). | | `subscription_product_id` | String | ID del producto de suscripción activo. | --- # File: posthog --- --- title: "PostHog" description: "" --- PostHog es una plataforma de analítica que ofrece herramientas para rastrear el comportamiento de los usuarios, visualizar el uso del producto y analizar la retención. Con funcionalidades como el seguimiento de eventos, los flujos de usuario y los feature flags, está diseñada para ayudarte a entender mejor tu producto y mejorarlo. Integrar PostHog con Adapty permite hacer un seguimiento fluido de los eventos relacionados con suscripciones, como inicios de prueba, renovaciones y cancelaciones. Al enviar estos eventos a PostHog, puedes analizar cómo los cambios en las suscripciones afectan al comportamiento de los usuarios, evaluar el rendimiento de los paywalls y obtener información más detallada sobre tus estrategias de monetización, todo dentro de tu flujo de trabajo de analítica habitual. ## Características de la integración \{#integration-characteristics\} | Característica de la integración | Descripción | | -------------------------------- | ------------------------------------------------------------ | | Frecuencia | Tiempo real; es posible que los eventos no aparezcan de inmediato en el dashboard de PostHog. | | Dirección de los datos | Los eventos de Adapty se envían desde el servidor de Adapty al servidor de PostHog. | | Punto de integración de Adapty | <ul><li> Los SDK de PostHog y Adapty en el código de la aplicación móvil</li><li> El servidor de Adapty</li></ul> | ## Estructura de eventos de PostHog \{#posthog-event-structure\} Adapty envía los eventos seleccionados a PostHog según lo configurado en la sección **Events names** de la [página de integración de PostHog](https://app.adapty.io/integrations/posthog). Cada evento tiene esta estructura: ```json showLineNumbers { "distinct_id": "john.doe@example.com", "timestamp": "2025-01-08T11:06:12+00:00", "event": "subscription_started", "properties": { "$set": { "email": "user@example.com", "first_name": "John", "last_name": "Doe", "birthday": "1990-01-01", "gender": "male", "os": "iOS" }, "timezone": "America/New_York", "ip_address": "10.168.1.1", "*": "{{other_event_properties}}" } } ``` Donde | **Parámetro** | **Tipo** | **Descripción** | | --------------- | -------------------- | ------------------------------------------------------------ | | **distinct_id** | String | Identificador único del usuario (p. ej., `profile.posthog_distinct_user_id`, `customer_user_id` o `profile_id`). | | **timestamp** | ISO 8601 fecha y hora | La fecha y hora del evento. | | **event** | String | El nombre del evento tal como lo definiste en la sección Events names de la [configuración de PostHog](https://app.adapty.io/integrations/posthog). | | **properties** | Object | Contiene [properties.$set](posthog#propertiesset-parameters) y todas las [propiedades específicas del evento](messaging#event-properties). Cada propiedad es opcional y no se enviará a PostHog si no está presente. | ### Parámetros de properties.$set Cada parámetro del objeto `properties.$set` es opcional y no se enviará a PostHog si no está presente. | **Parámetro** | **Tipo** | **Descripción** | | --------------- | -------------------- | ------------------------------------------------------------ | | **email** | String | Dirección de correo electrónico del usuario. | | **first_name** | String | Nombre del usuario. | | **last_name** | String | Apellido del usuario. | | **birthday** | String (Date) | Fecha de nacimiento del usuario. | | **gender** | String | Género del usuario. | | **os** | String | Sistema operativo del dispositivo del usuario. | ## Configuración de la integración con PostHog \{#setting-up-posthog-integration\} 1. Abre la página [Integrations -> PostHog](https://app.adapty.io/integrations/posthog) en el Adapty Dashboard y activa el botón. <img src="/assets/shared/img/posthog-on.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Inicia sesión en el [PostHog Dashboard](https://posthog.com/). 3. Ve a **Settings -> Project**. 4. En la ventana **Project**, desplázate hacia abajo hasta la sección **Project ID** y copia la **Project API key**. 5. Pega la API key en el campo **Project API key** del Adapty Dashboard. PostHog no tiene un modo Sandbox específico para la integración servidor a servidor. 6. Elige tu **PostHog Deployment**: | Opción | Descripción | | ------ | ----------- | | us/eu | Despliegues de PostHog alojados por defecto. | | Custom | Para instancias autoalojadas. Introduce la URL de tu instancia en el campo **PostHog Instance URL**. | 7. (Opcional) Si usas un despliegue de PostHog autoalojado, introduce la dirección de tu despliegue en el campo **PostHog Instance URL**. 8. (opcional) Ajusta opciones como **Reporting Proceeds**, **Exclude Historical Events**, **Report User's Currency** y **Send Trial Price**. Consulta [Configuración de la integración](configuration#integration-settings) para más detalles sobre estas opciones. 9. (opcional) También puedes personalizar qué eventos se envían a PostHog en la sección **Events names**. Desactiva los eventos que no necesites o cámbiales el nombre según convenga. 10. Haz clic en **Save** para finalizar la configuración. ## Configuración del SDK \{#sdk-configuration\} Para habilitar la recepción de datos de atribución desde PostHog, pasa el valor `distinctId` a Adapty tal como se muestra a continuación: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="note"> Si usas un ID de usuario de terceros como Customer User ID, no lo pases durante `activate()` — es posible que el SDK de terceros aún no lo haya generado. En su lugar, llama primero a `activate()` sin CUID, luego a `setIntegrationIdentifier()`, y después a `identify()` con el CUID. </Callout> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="Swift" default> ```swift showLineNumbers do { let distinctId = PostHogSDK.shared.getDistinctId() try await Adapty.setIntegrationIdentifier( key: "posthog_distinct_user_id", value: distinctId ) } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Kotlin" default> ```kotlin showLineNumbers Adapty.setIntegrationIdentifier("posthog_distinct_user_id", PostHog.distinctId()) { error -> if (error != null) { // handle the error } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers Adapty.setIntegrationIdentifier("posthog_distinct_user_id", PostHog.distinctId(), error -> { if (error != null) { // handle the error } }); ``` </TabItem> <TabItem value="flutter" label="Flutter" default> ```javascript showLineNumbers try { final distinctId = await Posthog().getDistinctId(); await Adapty().setIntegrationIdentifier( key: "posthog_distinct_user_id", value: distinctId, ); } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity" default> No hay un SDK oficial de PostHog para Unity. </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers // ... const posthog = usePostHog(); // ... try { await adapty.setIntegrationIdentifier("posthog_distinct_user_id", posthog.getDistinctId()); } catch (error) { // handle `AdaptyError` } ``` </TabItem> </Tabs> Adapty enviará ahora eventos a PostHog y recibirá atribución de él. --- # File: splitmetrics --- --- title: "SplitMetrics Acquire" description: "Usa SplitMetrics con Adapty para pruebas A/B de suscripciones y optimización." --- Con la integración de [SplitMetrics Acquire](https://splitmetrics.com/acquire/), puedes ver exactamente cuánto dinero generan tus Apple Search Ads en suscripciones. Además, puedes hacer seguimiento de tus usuarios durante meses para saber cuánto rinden tus anuncios a lo largo del tiempo. Además, Adapty envía [eventos de suscripción](events) a SplitMetrics Acquire para que puedas crear dashboards personalizados y automatizaciones allí, basándote en la atribución de Apple Search Ads. No añade ningún dato de atribución a Adapty, ya que obtenemos directamente de ASA todo lo que necesitamos. ## Cómo configurar la integración con SplitMetrics Acquire \{#how-to-set-up-splitmetrics-acquire-integration\} Para integrar SplitMetrics Acquire, ve a [Integrations > SplitMetrics Acquire](https://app.adapty.io/integrations/splitmetrics) e introduce las credenciales. <img src="/assets/shared/img/8255349-CleanShot_2023-08-14_at_17.39.422x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Abre tu cuenta de SplitMetrics Acquire, pasa el cursor sobre uno de los logotipos de MMP y haz clic en el botón **Settings**. Busca tu Client ID en el cuadro de diálogo bajo el elemento **5**, cópialo y pégalo en Adapty como **Client ID**. <img src="/assets/shared/img/4d0b2b6-Adapty.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <img src="/assets/shared/img/4f8d0b8-AdaptyGuide.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> También deberás configurar el Apple App ID para usar la integración. Para encontrar tu App ID, abre la página de tu app en App Store Connect, ve a la página **App Information** en la sección **General** y busca el **Apple ID** en la parte inferior izquierda de la pantalla. <img src="/assets/shared/img/61578ee-CleanShot_2022-04-20_at_17.55.03.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Eventos y etiquetas \{#events-and-tags\} Debajo de las credenciales, hay tres grupos de eventos que puedes enviar a SplitMetrics Acquire desde Adapty. Simplemente activa los que necesites. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). <img src="/assets/shared/img/1b0c777-CleanShot_2023-08-11_at_14.56.362x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Recomendamos usar los nombres de evento predeterminados que ofrece Adapty. Sin embargo, puedes cambiarlos según tus necesidades. Adapty enviará los eventos de suscripción a SplitMetrics Acquire mediante una integración server-to-server, lo que te permitirá ver todos los eventos de suscripción en tu dashboard de SplitMetrics. ## Configuración del SDK \{#sdk-configuration\} No necesitas configurar nada en el lado del SDK, pero recomendamos enviar el `customerUserId` a Adapty para mayor precisión. :::warning Asegúrate de haber configurado [Apple Search Ads](apple-search-ads) en Adapty y de haber [subido las credenciales](https://app.adapty.io/settings/apple-search-ads); sin ellas, SplitMetrics Acquire no funcionará. ::: ## Solución de problemas \{#troubleshooting\} Si la integración con SplitMetrics Acquire no funciona a pesar de haberla configurado correctamente: - Asegúrate de que el toggle **Receive Apple Search Ads attribution in Adapty** esté activado en [App Settings -> Apple Search Ads tab](https://app.adapty.io/settings/apple-search-ads), de haber configurado [Apple Search Ads](apple-search-ads) en Adapty y de haber [subido las credenciales](https://app.adapty.io/settings/apple-search-ads); sin ellas, SplitMetrics no funcionará. - Comprueba que los perfiles tengan atribución ASA no orgánica. Solo los perfiles con atribución ASA detallada y no orgánica enviarán sus eventos a Adapty. ## Estructura de eventos de SplitMetrics Acquire \{#splitmetrics-acquire-event-structure\} Adapty envía eventos a SplitMetrics Acquire mediante una solicitud GET utilizando parámetros de consulta. Cada evento tiene esta estructura: ```json { "source": "Apple Search Ads", "app_id": "123456789", "name": "subscription_renewed", "type": "subscription_renewed", "revenue": 9.99, "currency": "USD", "tap_time": "2024-03-01 12:00:00", "open_time": "2024-03-01 12:05:00", "event_time": "2024-03-02 12:00:00", "adaccount_id": "123456", "campaign_id": "123456789", "adgroup_id": "123456789", "keyword_id": "123456789", "creative_set_id": "123456789", "Ad_id": "123456789", "country_or_region": "US", "conversion_type": "Download", "user_id": "user_12345", "att_status": "3", "device_type": "iphone", "app_version": "1.2.3", "sdk_version": "2.10.0", "ios_version": "17.2", "event_value": "{\"vendor_product_id\":\"yearly.premium.6999\",\"original_transaction_id\":\"GPA.3383...\"}", "event_id": "123e4567-e89b-12d3-a456-426614174000" } ``` It looks like your message got cut off — there's no MDX documentation included to translate. Could you paste the MDX content you'd like translated from English to Spanish (es-ES)? Once you share it, I'll get started right away. | Parámetro | Tipo | Descripción | |:--------------------|:-------|:---------------------------------------------------------------------------------------------------------------------| | `source` | String | Siempre "Apple Search Ads". | | `app_id` | String | ID de la app en Apple. | | `name` | String | Nombre del evento (mapeado desde el evento de Adapty). | | `type` | String | Tipo de evento (igual que `name`). | | `revenue` | Float | Importe de los ingresos. | | `currency` | String | Código de moneda. | | `tap_time` | String | Fecha y hora del toque en el anuncio. | | `open_time` | String | Fecha y hora de la apertura de la app (instalación). | | `event_time` | String | Fecha y hora del evento. | | `adaccount_id` | String | ID de organización de ASA. | | `campaign_id` | String | ID de campaña de ASA. | | `adgroup_id` | String | ID de grupo de anuncios de ASA. | | `keyword_id` | String | ID de palabra clave de ASA. | | `creative_set_id` | String | ID del conjunto creativo de ASA. | | `Ad_id` | String | ID de anuncio de ASA. | | `country_or_region` | String | País o región del store. | | `conversion_type` | String | Tipo de conversión (p. ej., "Download"). | | `user_id` | String | Customer User ID o ID de perfil de Adapty. | | `att_status` | String | Estado de uso del seguimiento (0-3). | | `device_type` | String | Tipo de dispositivo (p. ej., "iphone", "ipad"). | | `app_version` | String | Versión de la aplicación. | | `sdk_version` | String | Versión del SDK de Adapty. | | `ios_version` | String | Versión de iOS. | | `event_value` | String | Cadena JSON con todos los [detalles del evento](webhook-event-types-and-fields#for-most-event-types) disponibles. | | `event_id` | String | ID único del evento (UUID). | --- # File: braze --- --- title: "Braze" description: "Integra Braze con Adapty para una mejor interacción con clientes y notificaciones push." --- Como una de las principales soluciones de captación de clientes, [Braze](https://www.braze.com/) ofrece una amplia gama de herramientas para notificaciones push, email, SMS y mensajería in-app. Al integrar Adapty con Braze, puedes acceder fácilmente a todos tus eventos de suscripción en un solo lugar, lo que te permite activar comunicaciones automatizadas basadas en esos eventos. Adapty proporciona un conjunto completo de datos que te permite rastrear [eventos de suscripción](events) de todas las stores en un solo lugar y puede usarse para actualizar los perfiles de tus usuarios en Braze. Con Adapty, puedes ver fácilmente el comportamiento de tus suscriptores, conocer sus preferencias y utilizar esa información para comunicarte con ellos de forma dirigida y efectiva. Por tanto, esta integración te permite rastrear eventos de suscripción en tu dashboard de Braze y relacionarlos con tus [campañas de adquisición.](https://www.braze.com/product/journey-orchestration) Adapty envía eventos de suscripción, propiedades de usuario y compras a Braze, para que puedas crear comunicaciones dirigidas a clientes mediante notificaciones push de Braze tras una integración sencilla y rápida, tal como se describe a continuación. ## Cómo configurar la integración con Braze \{#how-to-set-up-braze-integration\} Para integrar Braze, ve a [Integrations -> Braze](https://app.adapty.io/integrations/braze), activa el interruptor y rellena los campos. El primer paso del proceso de integración es proporcionar las credenciales necesarias para establecer una conexión entre tus perfiles de Braze y Adapty. Necesitarás la **REST API Key**, tu **Braze Instance ID** y los **App IDs** para iOS y Android para que la integración funcione correctamente: <img src="/assets/shared/img/5f1e62c-adapty_braze.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 1. La **REST API Key** se puede crear en **Braze Dashboard** → **Settings** → **API Keys**. Asegúrate de que tu clave tenga el permiso `users.track` al crearla: <img src="/assets/shared/img/b5fdf16-adapty_braze_create_api_key.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <img src="/assets/shared/img/1e5b4b8-adapty_braze_api_key_users_track.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Para obtener el **Braze Instance ID**, fíjate en la URL de tu Braze Dashboard y accede a la sección de [Braze Docs](https://www.braze.com/docs/api/basics/#endpoints) donde se especifica el ID de instancia. Tendrá un formato regional como US-03, EU-01, etc. 3. Los App IDs de iOS y Android también se encuentran en Braze Dashboard → Settings → API Keys. Cópialos desde aquí: <img src="/assets/shared/img/1e6d21b-adapty_braze_app_ids.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Eventos, atributos de usuario y compras \{#events-user-attributes-and-purchases\} Justo debajo de las credenciales hay tres grupos de eventos que puedes enviar a Braze desde Adapty. Activa simplemente los que necesites. También puedes cambiar los nombres de los eventos según lo que necesites enviar a Braze. Consulta la lista completa de eventos que ofrece Adapty [aquí](events): <img src="/assets/shared/img/702e628-adapty_braze_events_names.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Adapty enviará eventos de suscripción y atributos de usuario a Braze mediante una integración servidor a servidor, lo que te permitirá verlos en tu Braze Dashboard y configurar campañas basándote en ellos. Para los eventos que generan ingresos, como las conversiones de prueba y las renovaciones, Adapty enviará esta información a Braze como compras. [Aquí](messaging#event-properties) encontrarás las especificaciones completas de las propiedades de los eventos que se envían a Braze. :::note Atributos de usuario útiles Adapty envía algunos atributos de usuario para la integración con Braze de forma predeterminada. Puedes consultar la lista que se muestra a continuación para determinar cuáles se adaptan mejor a tus necesidades. ::: | Atributo de usuario | Tipo | Valor | |--------------|----|-----| | `adapty_customer_user_id` | String | Contiene el valor del identificador único del usuario definido por el cliente. Se puede encontrar tanto en el [Dashboard](profiles-crm) de Adapty como en Braze. | | `adapty_profile_id` | String | Contiene el valor del identificador único del Perfil de Usuario de Adapty, que se puede encontrar en el [Dashboard](profiles-crm) de Adapty. | | `environment` | String | <p>Indica si el usuario opera en un entorno sandbox o de producción.</p><p></p><p>Los valores son `Sandbox` o `Production`</p> | | `store` | String | <p>Contiene el nombre de la Store utilizada para realizar la compra.</p><p></p><p>Valores posibles:</p><p>`app_store` o `play_store`.</p> | | `vendor_product_id` | String | <p>Contiene el valor del ID de producto en la store de Apple/Google.</p><p></p><p>p. ej., org.locals.12345</p> | | `subscription_expires_at` | String | <p>Contiene la fecha de vencimiento de la suscripción más reciente.</p><p></p><p>El formato del valor es:</p><p>YYYY-MM-DDTHH:mm:ss.SSS+TZ</p><p>p. ej., 2023-02-15T17:22:03.000+0000</p> | | `active_subscription` | String | El valor se establecerá en `true` en cualquier evento de compra o renovación, o en `false` si la suscripción ha expirado. | | `period_type` | String | <p>Indica el tipo de período más reciente para la compra o renovación.</p><p></p><p>Los valores posibles son</p><p>`trial` para un período de prueba o `normal` para el resto.</p> | Todos los valores float se redondearán a int. Los strings permanecen igual. Además de la lista predefinida de etiquetas disponibles, es posible enviar [atributos personalizados](segments#custom-attributes) mediante etiquetas. Esto permite mayor flexibilidad en el tipo de datos que se pueden incluir con la etiqueta y puede resultar útil para rastrear información específica relacionada con un producto o servicio. Todos los atributos de usuario personalizados se envían automáticamente a Braze si el usuario marca la casilla **Send user attributes** en [la página de integración](https://app.adapty.io/integrations/braze). ## Configuración del SDK \{#sdk-configuration\} Para vincular perfiles de usuario en Adapty y Braze, debes configurar el SDK de Braze con el mismo ID de usuario que Adapty o usar su método `.changeUser()`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers let braze = Braze(configuration: configuration) braze.changeUser(userId: "adapty_customer_user_id") ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers Braze.getInstance(context).changeUser("adapty_customer_user_id") ``` </TabItem> </Tabs> --- # File: onesignal --- --- title: "OneSignal" description: "Integra OneSignal con Adapty para mejorar el engagement basado en notificaciones push." --- [OneSignal](https://onesignal.com/) es una plataforma líder de engagement con clientes que ofrece notificaciones push, correo electrónico, SMS y mensajería in-app. Integrar Adapty con OneSignal te permite acceder a todos tus eventos de suscripción en un solo lugar, lo que te permite activar comunicaciones automatizadas basadas en esos eventos. Con Adapty puedes hacer seguimiento de [eventos de suscripción](events) en múltiples stores, analizar el comportamiento de los usuarios y usar esos datos para una comunicación más segmentada. Esta integración te ayuda a monitorear eventos de suscripción dentro de tu dashboard de OneSignal y asociarlos con tus [campañas de adquisición](https://documentation.onesignal.com/docs/en/automated-messages). Adapty actualiza las etiquetas de OneSignal en función de los eventos de suscripción, lo que te permite enviar notificaciones push personalizadas con una configuración mínima. **Características de la integración** | Característica de la integración | Descripción | | :-------------------------------- | :----------------------------------------------------------- | | Frecuencia | Actualizaciones en tiempo real | | Dirección de los datos | Unidireccional: de Adapty al servidor de OneSignal | | Punto de integración de Adapty | <ul><li>SDK de OneSignal y de Adapty en el código de la app móvil</li><li>Servidor de Adapty</li></ul>| ## Configurar la integración con OneSignal \{#setting-up-one-signal-integration\} Para configurar la integración: 1. Abre [Integrations → OneSignal](https://app.adapty.io/integrations/onesignal) en tu Adapty Dashboard. <img src="/assets/shared/img/onesignal-on.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Activa el interruptor de la integración. 3. Introduce tu **OneSignal App ID**. Para configurar la integración con OneSignal, ve a [Integrations -> OneSignal](https://app.adapty.io/integrations/onesignal) en tu Adapty dashboard, activa el interruptor y configura las credenciales de la integración. ## Obtener tu OneSignal App ID \{#retrieving-your-onesignal-app-id\} Encuentra tu **OneSignal App ID** en tu [OneSignal Dashboard](https://dashboard.onesignal.com/login): 1. Ve a **Settings** → **Keys & IDs**. <img src="/assets/shared/img/onesignal-dashboard.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Copia tu **OneSignal App ID** y pégalo en el campo **App ID** del Adapty Dashboard. <img src="/assets/shared/img/onesignal-id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Puedes encontrar más información sobre el ID de OneSignal en la [siguiente documentación.](https://documentation.onesignal.com/docs/en/keys-and-ids) ### Configurar eventos \{#configuring-events\} Adapty te permite enviar tres grupos de eventos a OneSignal. Activa los que necesites en el Adapty Dashboard. Puedes ver la lista completa de eventos disponibles con una descripción detallada [aquí](events). <img src="/assets/shared/img/onesignal.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Adapty envía eventos de suscripción a OneSignal mediante una integración servidor a servidor, lo que te permite hacer seguimiento de toda la actividad relacionada con suscripciones en OneSignal. :::warning A partir del 17 de abril de 2023, el Plan Gratuito de OneSignal ya no incluye esta integración. Solo está disponible en los planes **Growth**, **Professional** y superiores. Para más información, consulta [OneSignal Pricing](https://onesignal.com/pricing). ::: ## Etiquetas personalizadas \{#custom-tags\} Esta integración actualiza y asigna diversas propiedades a tus usuarios de Adapty como etiquetas, que luego se envían a OneSignal. Consulta la lista de etiquetas a continuación para encontrar las que mejor se adapten a tus necesidades. :::warning OneSignal tiene un límite de etiquetas. Esto incluye tanto las etiquetas generadas por Adapty como las ya existentes en OneSignal. Superar el límite puede provocar errores al enviar eventos. ::: | Etiqueta | Tipo | Descripción | |---|----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `adapty_customer_user_id` | String | El identificador único del usuario en tu app. Debe ser coherente en tu sistema, Adapty y OneSignal. | | `adapty_profile_id` | String | El ID de perfil del usuario en Adapty, disponible en tu [Adapty Dashboard](profiles-crm). | | `environment` | String | `Sandbox` o `Production`, que indica el entorno actual del usuario. | | `store` | String | Store donde se compró el producto. Opciones: **app_store**, **play_store**, **stripe** o el nombre de tu [store personalizado](custom-store). | | `vendor_product_id` | String | El ID del producto en el store (p. ej., `org.locals.12345`). | | `subscription_expires_at` | String | Fecha de expiración de la suscripción más reciente (`YYYY-MM-DDTHH:MM:SS+0000`, p. ej., `2023-02-10T17:22:03.000000+0000`). | | `last_event_type` | String | El tipo de evento más reciente de la [lista de eventos de Adapty](events).<br/> Ten en cuenta lo siguiente:<br/>- Para el evento **Subscription expired**, Adapty envía la propiedad `last_event_type` como `subscription_cancelled`.<br/>- Para **Trial renew canceled** — como `auto_renew_off`<br/>- Para **Subscription renew canceled** — como `auto_renew_off_subscription` | | `purchase_date` | String | Fecha de la última transacción (`YYYY-MM-DDTHH:MM:SS+0000`, p. ej., `2023-02-10T17:22:03.000000+0000`). | | `active_subscription` | String | `true` si el usuario tiene una suscripción activa y `false` si la suscripción ha expirado. | | `period_type` | String | Indica el tipo de período más reciente para la compra o renovación. Valores posibles: `trial` para un período de prueba o `normal` para todos los demás casos. | Todos los valores de tipo float se redondean a enteros. Las cadenas de texto se mantienen sin cambios. Además de las etiquetas predefinidas, puedes enviar [atributos personalizados](segments#custom-attributes) como etiquetas, lo que proporciona mayor flexibilidad en los datos que incluyes. Esto es útil para rastrear detalles específicos relacionados con tu producto o servicio. Los atributos personalizados de usuario se envían automáticamente a OneSignal si la casilla **Send user attributes** está marcada en la [página de integración](https://app.adapty.io/integrations/onesignal). Si no está marcada, Adapty envía exactamente 10 etiquetas. Si está marcada, se pueden enviar más de 10 etiquetas, lo que permite capturar más datos. ## Configuración del SDK \{#sdk-configuration\} Hay dos formas de integrar OneSignal con Adapty: 1. **Legacy (anterior a v5):** Usa `playerId` (obsoleto en el [OneSignal SDK v5](https://github.com/OneSignal/OneSignal-iOS-SDK/releases/tag/5.0.0)). 2. **Actual (v5+):** Usa `subscriptionId`. :::warning Asegúrate de enviar `playerId` (para el OneSignal SDK anterior a v5) o `subscriptionId` (para el OneSignal SDK v5+) a Adapty. Sin esto, las etiquetas de OneSignal no se actualizarán y la integración no funcionará correctamente. ::: <Tabs groupId="current-version" queryString> <TabItem value="v5+" label="OneSignal SDK v5+ (current)" default> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers // SubscriptionID OneSignal.Notifications.requestPermission({ accepted in Task { try await Adapty.setIntegrationIdentifier( key: "one_signal_subscription_id", value: OneSignal.User.pushSubscription.id ) } }, fallbackToSettings: true) ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers // SubscriptionID val oneSignalSubscriptionObserver = object: IPushSubscriptionObserver { override fun onPushSubscriptionChange(state: PushSubscriptionChangedState) { Adapty.setIntegrationIdentifier("one_signal_subscription_id", state.current.id) { error -> if (error != null) { // handle the error } } } } ``` </TabItem> <TabItem value="java" label="(Android) Java" default> ```java showLineNumbers // SubscriptionID IPushSubscriptionObserver oneSignalSubscriptionObserver = state -> { Adapty.setIntegrationIdentifier("one_signal_subscription_id", state.getCurrent().getId(), error -> { if (error != null) { // handle the error } }); }; ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```javascript showLineNumbers // 1. Since OneSignal.User.pushSubscription.id may return null if called too early, // OneSignal suggests to listen for the updates: OneSignal.User.pushSubscription.addObserver((state) { if (state.current.optedIn) { // now you can try to retrieve subscriptionId } }); // 2. Then you can push subscriptionId to Adapty: final subscriptionId = OneSignal.User.pushSubscription.id; if (subscriptionId != null) { await Adapty().setIntegrationIdentifier(key: "one_signal_subscription_id", value: subscriptionId); } ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers using AdaptySDK; using OneSignalSDK; var pushUserId = OneSignal.Default.PushSubscriptionState.userId; Adapty.SetIntegrationIdentifier( "one_signal_player_id", pushUserId, (error) => { // handle the error }); ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers OneSignal.User.pushSubscription.addEventListener('change', (subscription) => { const subscriptionId = subscription.current.id; if (subscriptionId) { adapty.setIntegrationIdentifier("one_signal_subscription_id", subscriptionId); } }); ``` </TabItem> </Tabs> </TabItem> <TabItem value="pre-v5" label="OneSignal SDK v. up to 4.x (legacy)" default> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers // PlayerID // in your OSSubscriptionObserver implementation func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges) { if let playerId = stateChanges.to.userId { Task { try await Adapty.setIntegrationIdentifier( key: "one_signal_player_id", value: playerId ) } } } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers // PlayerID val osSubscriptionObserver = OSSubscriptionObserver { stateChanges -> stateChanges?.to?.userId?.let { playerId -> Adapty.setIntegrationIdentifier("one_signal_player_id", playerId) { error -> if (error != null) { // handle the error } } } } ``` </TabItem> <TabItem value="java" label="Java" default> ```java showLineNumbers // PlayerID OSSubscriptionObserver osSubscriptionObserver = stateChanges -> { OSSubscriptionState to = stateChanges != null ? stateChanges.getTo() : null; String playerId = to != null ? to.getUserId() : null; if (playerId != null) { Adapty.setIntegrationIdentifier("one_signal_player_id", playerId, error -> { if (error != null) { // handle the error } }); } }; ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```javascript showLineNumbers // PlayerID (pre-v5 OneSignal SDK) // in your OSSubscriptionObserver implementation func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges) { if let playerId = stateChanges.to.userId { Task { try await Adapty.setIntegrationIdentifier( key: "one_signal_player_id", value: playerId ) } } } ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers OneSignal.addSubscriptionObserver(event => { const playerId = event.to.userId; adapty.setIntegrationIdentifier("one_signal_player_id", playerId); }); ``` </TabItem> </Tabs> </TabItem> </Tabs> Más información en la documentación de OneSignal: - [ID de suscripción push](https://documentation.onesignal.com/docs/en/mobile-sdk-reference#user-pushsubscription-id) - [Cambios en la suscripción push](https://documentation.onesignal.com/docs/en/mobile-sdk-reference#addobserver-push-subscription-changes) ## Gestionar múltiples dispositivos \{#dealing-with-multiple-devices\} Si un usuario tiene varios dispositivos, hacer seguimiento de los eventos de compra y las suscripciones puede ser complicado. OneSignal ofrece una forma de gestionar esto mediante [IDs de usuario externos](https://documentation.onesignal.com/docs/en/users). Para mantener los datos del usuario coherentes entre dispositivos: 1. Relaciona los distintos dispositivos en tu **servidor** y envía esos datos a OneSignal. 2. Usa el [customer_user_id](identifying-users) de Adapty como [externalUserId](https://documentation.onesignal.com/docs/en/users#external-id) en OneSignal. Si tu app no tiene un sistema de registro, considera usar otro identificador único que sea constante en todos los dispositivos del usuario. Es importante mantener la coherencia del identificador de usuario en todos los dispositivos y actualizar OneSignal cada vez que cambie el ID de un usuario. Esto simplifica el seguimiento de la actividad y las suscripciones, garantiza una mensajería coherente y permite obtener análisis más precisos y una mejor experiencia de usuario. Para más detalles, consulta la [documentación de ID de usuario externo de OneSignal](https://documentation.onesignal.com/docs/en/users). --- # File: pushwoosh --- --- title: "Pushwoosh" description: "Integra Pushwoosh con Adapty para un seguimiento de notificaciones push sin fricciones." --- Adapty utiliza eventos de suscripción para actualizar las etiquetas de perfil de [Pushwoosh](https://www.pushwoosh.com/), de modo que puedas crear comunicaciones dirigidas con tus clientes mediante notificaciones push tras una configuración de integración rápida y sencilla como la que se describe a continuación. ## Cómo configurar la integración con Pushwoosh \{#how-to-set-up-pushwoosh-integration\} Para integrar Pushwoosh, ve a [**Integrations** -> **Pushwoosh**](https://app.adapty.io/integrations/pushwoosh), activa el interruptor y rellena los campos. En primer lugar, introduce las credenciales para establecer la conexión entre tus perfiles de Pushwoosh y Adapty. Se necesitan el App ID y el auth token de Pushwoosh. <img src="/assets/shared/img/64e48a1-CleanShot_2023-08-18_at_11.13.212x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 1. El **App ID** se encuentra en tu dashboard de Pushwoosh. <img src="/assets/shared/img/ee27687-CleanShot_2023-08-18_at_14.37.442x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. El **Auth token** se encuentra en la sección API Access dentro de la configuración de Pushwoosh. <img src="/assets/shared/img/50e634b-CleanShot_2023-08-18_at_14.35.022x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Eventos y etiquetas \{#events-and-tags\} Debajo de las credenciales encontrarás tres grupos de eventos que puedes enviar a Pushwoosh desde Adapty. Activa simplemente los que necesites. También puedes cambiar los nombres de los eventos antes de enviarlos a Pushwoosh. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). <img src="/assets/shared/img/392dc31-screencapture-app-adapty-io-integrations-pushwoosh-2023-08-22-13_31_07.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Adapty enviará los eventos de suscripción a Pushwoosh mediante una integración server-to-server, lo que te permitirá ver todos los eventos de suscripción en tu Pushwoosh Dashboard. :::note Etiquetas personalizadas Con Adapty también puedes usar tus propias etiquetas personalizadas para la integración con Pushwoosh. Consulta la lista de etiquetas que se muestra a continuación para determinar cuál se adapta mejor a tus necesidades. ::: | Etiqueta | Tipo | Valor | |---|----|-----| | `adapty_customer_user_id` | String | Contiene el valor del identificador único del usuario, que puede encontrarse en el lado de Pushwoosh. | | `adapty_profile_id` | String | Contiene el valor del identificador único del perfil de usuario de Adapty, que puede encontrarse en tu [dashboard](profiles-crm) de Adapty. | | `environment` | String | <p>Indica si el usuario opera en un entorno sandbox o de producción.</p><p></p><p>Los valores son `Sandbox` o `Production`.</p> | | `store` | String | <p>Contiene el nombre del store utilizado para realizar la compra.</p><p></p><p>Valores posibles:</p><p>`app_store` o `play_store`.</p> | | `vendor_product_id` | String | <p>Contiene el valor del Product ID en la store de Apple o Google.</p><p></p><p>p. ej., org.locals.12345</p> | | `subscription_expires_at` | String | <p>Contiene la fecha de expiración de la última suscripción.</p><p></p><p>El formato del valor es:</p><p>año-mes díaTHhora:minuto:segundo</p><p>p. ej., 2023-02-10T17:22:03.000000+0000</p> | | `last_event_type` | String | Indica el tipo del último evento recibido de la lista de [eventos estándar de Adapty](events) que has habilitado para la integración. | | `purchase_date` | String | <p>Contiene la fecha de la última transacción (compra original o renovación).</p><p></p><p>El formato del valor es:</p><p>año-mes díaTHhora:minuto:segundo</p><p>p. ej., 2023-02-10T17:22:03.000000+0000</p> | | `original_purchase_date` | String | <p>Contiene la fecha de la primera compra según la transacción.</p><p></p><p>El formato del valor es:</p><p>año-mes díaTHhora:minuto:segundo</p><p>p. ej., 2023-02-10T17:22:03.000000+0000</p> | | `active_subscription` | String | El valor se establecerá en `true` ante cualquier evento de compra o renovación, o en `false` si la suscripción ha expirado. | | `period_type` | String | <p>Indica el tipo de período más reciente para la compra o renovación.</p><p></p><p>Los valores posibles son</p><p>`trial` para un período de prueba o `normal` para el resto.</p> | Todos los valores float se redondearán a int. Las cadenas de texto permanecen igual. Además de la lista predefinida de etiquetas disponibles, es posible enviar [atributos personalizados](segments#custom-attributes) mediante etiquetas. Esto ofrece mayor flexibilidad en el tipo de datos que se pueden incluir y resulta útil para rastrear información específica relacionada con un producto o servicio. Todos los atributos personalizados de usuario se envían automáticamente a Pushwoosh si el usuario marca la casilla **Send user custom attributes** en [la página de integración](https://app.adapty.io/integrations/pushwoosh). ## Configuración del SDK \{#sdk-configuration\} Para vincular Adapty con Pushwoosh, necesitas enviarnos el valor `HWID`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (Swift)" default> ```swift showLineNumbers do { try await Adapty.setIntegrationIdentifier( key: "pushwoosh_hwid", value: Pushwoosh.sharedInstance().getHWID() ) } catch { // handle the error } ``` </TabItem> <TabItem value="kotlin" label="Android (Kotlin)" default> ```kotlin showLineNumbers Adapty.setIntegrationIdentifier("pushwoosh_hwid", Pushwoosh.getInstance().hwid) { error -> if (error != null) { // handle the error } } ``` </TabItem> <TabItem value="java" label="Android (Java)" default> ```java showLineNumbers Adapty.setIntegrationIdentifier("pushwoosh_hwid", Pushwoosh.getInstance().getHwid(), error -> { if (error != null) { // handle the error } }); ``` </TabItem> <TabItem value="flutter" label="Flutter (Dart)" default> ```javascript showLineNumbers final hwid = await Pushwoosh.getInstance.getHWID; try { await Adapty().setIntegrationIdentifier( key: "pushwoosh_hwid", value: hwid, ); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="unity" label="Unity (C#)" default> ```csharp showLineNumbers using AdaptySDK; Adapty.SetIntegrationIdentifier( "pushwoosh_hwid", Pushwoosh.Instance.HWID, (error) => { // handle the error }); ``` </TabItem> <TabItem value="rn" label="React Native (TS)" default> ```typescript showLineNumbers // ... try { await adapty.setIntegrationIdentifier("pushwoosh_hwid", hwid); } catch (error) { // handle `AdaptyError` } ``` </TabItem> </Tabs> --- # File: slack --- --- title: "Slack" description: "Integra Slack con Adapty para recibir notificaciones en tiempo real sobre eventos de suscripción." --- [Slack](https://slack.com/) es una plataforma de mensajería y productividad para equipos que seguramente no necesita presentación. Con esta integración, recibirás notificaciones en Slack cada vez que Adapty registre un evento de ingresos. Esto es muy útil si quieres celebrar cada subida de tu MRR o si quieres estar al tanto de cancelaciones de pruebas, problemas de facturación, reembolsos y más. ## Cómo configurar la integración con Slack \{#how-to-set-up-slack-integration\} Necesitarás: - crear una app en tu workspace de Slack - darle permiso para publicar mensajes - y luego proporcionar la información necesaria a Adapty en [Integrations → Slack](https://app.adapty.io/integrations/slack). ### 1\. Crear una app en Slack \{#1-create-an-app-in-slack\} 1. Ve al [panel de Slack API](https://api.slack.com/apps) y crea una app así: <img src="/assets/shared/img/f43aedc-CleanShot_2024-01-04_at_18.27.412x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <img src="/assets/shared/img/08fa9e6-CleanShot_2024-01-04_at_18.28.142x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Dale cualquier nombre (`Adapty`, por ejemplo) y agrégala a tu workspace: <img src="/assets/shared/img/5002bb1-CleanShot_2024-01-04_at_18.29.132x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### 2\. Dar permiso para publicar y obtener un token para tu app \{#2-give-permission-to-post-and-get-a-token-for-your-app\} Serás redirigido a la página de tu app en Slack. 1. Desplázate hacia abajo y haz clic en **Permissions**: <img src="/assets/shared/img/9750451-CleanShot_2024-01-04_at_18.48.072x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Tras la redirección, desplázate hacia abajo hasta **Scopes** y haz clic en **Add an OAuth Scope**: <img src="/assets/shared/img/db5b5f4-CleanShot_2024-01-04_at_18.50.262x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Otorga los permisos `chat:write`, `chat:write.public` y `chat:write.customize`. Estos son necesarios para publicar en tus canales y personalizar los mensajes: <img src="/assets/shared/img/d97ccb9-CleanShot_2024-01-04_at_18.51.572x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate de nuevo hasta la parte superior de la página y haz clic en **Install to Workspace**: <img src="/assets/shared/img/14608e3-CleanShot_2024-01-04_at_19.17.58.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **Allow**: <img src="/assets/shared/img/143967e-CleanShot_2024-01-04_at_18.53.292x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Después de esto, serás redirigido a la misma página, pero ahora tendrás disponible un OAuth Token (`xoxb-...`). Esto es exactamente lo que necesitas para completar la configuración: <img src="/assets/shared/img/59b33ee-CleanShot_2024-01-04_at_18.55.222x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### 3\. Configurar la integración en Adapty \{#3-configure-the-integration-in-adapty\} 1. Ve a [**Integrations** → **Slack**](https://app.adapty.io/integrations/slack): <img src="/assets/shared/img/b4ffd71-CleanShot_2024-01-04_at_19.05.222x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Pega el token `xoxb-...` del paso anterior y elige en qué canales publicará la app. Puedes configurar la integración para recibir eventos solo en producción, en sandbox o en ambos. También puedes elegir en qué moneda se mostrarán los mensajes (la original o convertida a USD). :::note Ten en cuenta que si quieres publicar mensajes de Adapty en un canal privado, deberás añadir manualmente la app `Adapty` que creaste en Slack a ese canal. De lo contrario, no funcionará. ::: 3. Por último, puedes elegir qué eventos quieres recibir en **Events**: <img src="/assets/shared/img/970a7bb-CleanShot_2024-01-04_at_19.09.472x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ¡Listo! Los eventos se enviarán a los canales que hayas especificado. Podrás ver los ingresos cuando corresponda y consultar el perfil del cliente en Adapty: <img src="/assets/shared/img/852b8c8-CleanShot_2024-01-04_at_19.11.332x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: s3-exports --- --- title: "Amazon S3" description: "Exporta datos de suscripción a S3 para análisis e informes avanzados." --- La integración de Adapty con Amazon S3 te permite almacenar de forma segura los datos de eventos y visitas a paywalls en un único lugar centralizado. Podrás guardar tus [eventos de suscripción](events) en tu bucket de Amazon S3 como archivos .csv. Para configurar esta integración, solo tienes que seguir unos sencillos pasos en la consola de AWS y en el Adapty Dashboard. :::note Programación Adapty envía tus datos cada **24h** a las 4:00 UTC. Cada archivo contendrá datos de los eventos creados durante el día natural anterior completo en UTC. Por ejemplo, los datos exportados automáticamente a las 4:00 UTC del 8 de marzo contendrán todos los eventos creados el 7 de marzo entre las 00:00:00 y las 23:59:59 UTC. ::: ## Cómo configurar la integración con Amazon S3 \{#how-to-set-up-amazon-s3-integration\} Para empezar a recibir datos, necesitarás las siguientes credenciales: 1. Access key ID 2. Secret access key 3. S3 bucket name 4. Folder name inside the S3 bucket :::note Directorios anidados Puedes especificar directorios anidados en el campo S3 bucket name, por ejemplo: adapty-events/com.sample-app ::: Para integrar Amazon S3, ve a [**Integrations** -> **Amazon S3**](https://app.adapty.io/integrations/s3), activa el interruptor y rellena los campos. En primer lugar, introduce las credenciales para establecer la conexión entre Amazon S3 y los perfiles de Adapty. <img src="/assets/shared/img/2b1a6e3-CleanShot_2023-03-24_at_14.51.272x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En el Adapty Dashboard, los siguientes campos son necesarios para configurar la conexión: | Campo | Descripción | | :--- | :--- | | **Access Key ID** | Identificador único que se usa para autenticar el acceso de un usuario o aplicación a un servicio de AWS. Encuéntralo en el [archivo csv](s3-exports#how-to-create-amazon-s3-credentials) descargado. | | **Secret Access Key** | Clave privada que se usa junto con el Access Key ID para autenticar el acceso de un usuario o aplicación a un servicio de AWS. Encuéntrala en el [archivo csv](s3-exports#how-to-create-amazon-s3-credentials) descargado. | | **S3 Bucket Name** | Nombre único a nivel global que identifica un bucket de S3 específico dentro de la nube de AWS. Los buckets de S3 son un servicio de almacenamiento simple que permite a los usuarios guardar y recuperar objetos de datos, como archivos e imágenes, en la nube. | | **Folder Inside the Bucker** | El nombre de la carpeta que quieres crear dentro del bucket de S3 seleccionado. Ten en cuenta que S3 simula carpetas mediante prefijos de clave de objeto, que son esencialmente nombres de carpetas. | ## Cómo crear credenciales de Amazon S3 \{#how-to-create-amazon-s3-credentials\} Esta guía te ayudará a crear las credenciales necesarias en tu consola de AWS. ### 1\. Crear política de acceso \{#create-access-policy\} Primero, ve al [Panel de políticas de IAM](https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/policies) en tu consola de AWS y selecciona la opción **Create Policy**. <img src="/assets/shared/img/7af075c-CleanShot_2023-03-21_at_10.52.002x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En el editor de políticas, pega el siguiente JSON y cambia `adapty-s3-integration-test` por el nombre de tu bucket: ```json showLineNumbers title="Json" { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowListObjectsInBucket", "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::adapty-s3-integration-test" }, { "Sid": "AllowAllObjectActions", "Effect": "Allow", "Action": "s3:*Object", "Resource": [ "arn:aws:s3:::adapty-s3-integration-test/*", "arn:aws:s3:::adapty-s3-integration-test" ] }, { "Sid": "AllowBucketLocation", "Effect": "Allow", "Action": "s3:GetBucketLocation", "Resource": "arn:aws:s3:::adapty-s3-integration-test" } ] } ``` <img src="/assets/shared/img/d4e474a-CleanShot_2023-03-21_at_10.56.212x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Una vez completada la configuración de la política, puedes añadir etiquetas (opcional) y hacer clic en **Next** para continuar con el paso final. En este paso, deberás asignar un nombre a tu política y simplemente hacer clic en el botón **Create policy** para finalizar el proceso de creación. <img src="/assets/shared/img/7dcb02f-CleanShot_2023-03-21_at_11.03.372x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### 2\. Crear un usuario IAM \{#2-create-iam-user\} Para que Adapty pueda subir informes de datos brutos a tu bucket, deberás proporcionarles el Access Key ID y el Secret Access Key de un usuario con acceso de escritura al bucket específico. Para ello, ve a la consola de IAM y selecciona la [sección Users](https://console.aws.amazon.com/iamv2/home#/users). Desde allí, haz clic en el botón **Add users**. <img src="/assets/shared/img/bb612c8-CleanShot_2023-03-21_at_11.12.392x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Dale un nombre al usuario, elige **Access key – Programmatic access** y continúa con los permisos. <img src="/assets/shared/img/467ee4d-j6aoX.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para el siguiente paso, selecciona la opción **Add user to group** y luego haz clic en el botón **Create group**. <img src="/assets/shared/img/bfd0e80-CleanShot_2023-03-21_at_11.24.592x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> A continuación, asigna un nombre a tu Grupo de Usuarios y selecciona la política que creaste anteriormente. Una vez seleccionada, haz clic en el botón **Create group** para completar el proceso. <img src="/assets/shared/img/df29c12-CleanShot_2023-03-21_at_11.28.052x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Una vez creado el grupo con éxito, **selecciónalo** y continúa con el siguiente paso. <img src="/assets/shared/img/1f3722e-CleanShot_2023-03-21_at_11.36.192x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Como este es el último paso de esta sección, puedes continuar haciendo clic en el botón **Create User**. <img src="/assets/shared/img/ea43722-CleanShot_2023-03-21_at_11.40.462x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Por último, puedes **descargar las credenciales en formato .csv** o copiarlas y pegarlas directamente desde el dashboard. <img src="/assets/shared/img/bcf35e1-S3created.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Exportación manual de datos \{#manual-data-export\} Además de la exportación automática de datos de eventos a Amazon S3, Adapty también ofrece una funcionalidad de exportación manual de archivos. Con esta función, puedes seleccionar un intervalo de tiempo específico para los datos de eventos y exportarlos a tu bucket de S3 de forma manual. Esto te da mayor control sobre los datos que exportas y cuándo los exportas. El rango de fechas especificado se usará para exportar los eventos creados desde la Fecha A 00:00:00 UTC hasta la Fecha B 23:59:59 UTC. <img src="/assets/shared/img/466bd29-CleanShot_2023-03-21_at_12.35.252x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Estructura de la tabla \{#table-structure\} En la integración con AWS S3, Adapty proporciona una tabla para almacenar datos históricos de eventos de transacciones y visitas a paywalls. La tabla contiene información sobre el perfil del usuario, los ingresos y beneficios, y el store de origen, entre otros datos. En esencia, estas tablas registran todas las transacciones generadas por una app durante un período de tiempo determinado. :::warning Ten en cuenta que esta estructura puede crecer con el tiempo, ya que nosotros o los terceros con los que trabajamos podemos añadir nuevos datos. Asegúrate de que el código que la procesa sea lo suficientemente robusto y se base en campos concretos, no en la estructura en su conjunto. ::: Aquí está la estructura de la tabla para los eventos: | Columna | Descripción | |---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **profile_id** | ID de usuario de Adapty. | | **event_type** | Nombre del evento en minúsculas. Consulta la sección [Eventos](events) para conocer los tipos de eventos. | | **event_datetime** | Fecha en formato ISO 8601. | | **transaction_id** | Identificador único de una transacción, como una compra o renovación. | | **original_transaction_id** | Identificador de la transacción de compra original. | | **subscription_expires_at** | Fecha de vencimiento de la suscripción. Normalmente en el futuro. | | **environment** | Puede ser Sandbox o Production. | | **revenue_usd** | Ingresos en USD. Puede estar vacío. | | **proceeds_usd** | Ingresos netos en USD. Puede estar vacío. | | **net_revenue_usd** | Ingresos netos (tras impuestos) en USD. Puede estar vacío. | | **tax_amount_usd** | Importe deducido en concepto de impuestos en USD. Puede estar vacío. | | **revenue_local** | Ingresos en moneda local. Puede estar vacío. | | **proceeds_local** | Ingresos netos en moneda local. Puede estar vacío. | | **net_revenue_local** | Ingresos netos (tras impuestos) en moneda local. Puede estar vacío. | | **tax_amount_local** | Importe deducido en concepto de impuestos en moneda local. Puede estar vacío. | | **customer_user_id** | ID de usuario del desarrollador. Por ejemplo, puede ser tu UUID de usuario, email u otro identificador. Null si no lo has configurado. | | **store** | Puede ser _app_store_ o _play_store_. | | **product_id** | ID del producto en Apple App Store, Google Play Store o Stripe. | | **base_plan_id** | [ID del plan base](https://support.google.com/googleplay/android-developer/answer/12154973) en Google Play Store o [ID de precio](https://docs.stripe.com/products-prices/how-products-and-prices-work#use-products-and-prices) en Stripe. | | **developer_id** | ID del desarrollador (SDK) del paywall donde se originó la transacción. | | **ab_test_name** | Nombre de la prueba A/B donde se originó la transacción. | | **ab_test_revision** | Revisión de la prueba A/B donde se originó la transacción. | | **paywall_name** | Nombre del paywall donde se originó la transacción. | | **paywall_revision** | Revisión del paywall donde se originó la transacción. | | **profile_county** | País del perfil determinado por Adapty a partir de la IP. | | **install_date** | Fecha de instalación en formato ISO 8601. | | **idfv** | [identifierForVendor](https://developer.apple.com/documentation/uikit/uidevice/identifierforvendor) en dispositivos iOS | | **idfa** | [advertisingIdentifier](https://developer.apple.com/documentation/adsupport/asidentifiermanager/advertisingidentifier) en dispositivos iOS | | **advertising_id** | El Advertising ID es un código único asignado por el sistema operativo Android que los anunciantes pueden usar para identificar de forma única el dispositivo de un usuario. | | **ip_address** | IP del dispositivo (puede ser IPv4 o IPv6, con preferencia por IPv4 cuando esté disponible). Se actualiza cada vez que cambia la IP del dispositivo. | | **cancellation_reason** | <p>Motivo por el que el usuario canceló una suscripción.</p><p></p><p>Puede ser:</p><p>**iOS & Android** _voluntarily_cancelled_, _billing_error_, _refund_</p><p>**iOS** _price_increase_, _product_was_not_available_, _unknown_, _upgraded_</p><p>**Android** _new_subscription_replace_, _cancelled_by_developer_</p> | | **android_app_set_id** | Un [AppSetId](https://developer.android.com/design-for-safety/privacy-sandbox/reference/adservices/appsetid/AppSetId): ID único por dispositivo y por cuenta de desarrollador, restablecible por el usuario, para casos de uso publicitario sin monetización. | | **android_id** | En Android 8.0 (nivel de API 26) y versiones superiores, un número de 64 bits (expresado como cadena hexadecimal), único para cada combinación de clave de firma de la app, usuario y dispositivo. Para más detalles, consulta la [documentación para desarrolladores de Android](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID). | | **device** | Nombre del modelo de dispositivo visible para el usuario final. | | **currency** | Código de moneda de 3 letras (ISO-4217) de la transacción. | | **store_country** | País del perfil determinado por la store de Apple/Google. | | **attribution_source** | Fuente de atribución. | | **attribution_network_user_id** | ID asignado al usuario por la fuente de atribución. | | **attribution_status** | Puede ser organic, non_organic o unknown. | | **attribution_channel** | Nombre del canal de marketing. | | **attribution_campaign** | Nombre de la campaña de marketing. | | **attribution_ad_group** | Grupo de anuncios de atribución. | | **attribution_ad_set** | Conjunto de anuncios de atribución. | | **attribution_creative** | Palabra clave creativa de atribución. | | **attributes** | JSON de [atributos de usuario personalizados](setting-user-attributes#custom-user-attributes). Incluye los atributos personalizados que hayas configurado para enviar desde tu app móvil. Para enviarlo, activa la opción **Send User Attributes** en la página [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook). | | **integration_ids** | Todos los IDs de integración asociados a un perfil. Diccionario. Ejemplo: {'mixpanel_user_id': 'mixpanelUserId-test', 'facebook_anonymous_id': 'facebookAnonymousId-test'} | Here is the table structure for the paywall visits: | Columna | Descripción | | :-------------------- | :------------------------------------------------------------------------------------------------------------------ | | **profile_id** | ID de usuario de Adapty. | | **customer_user_id** | ID de usuario del desarrollador. Por ejemplo, puede ser tu UUID de usuario, email o cualquier otro ID. Null si no lo configuraste. | | **profile_country** | País del perfil determinado por la store de Apple/Google. | | **install_date** | Fecha ISO 8601 en que se produjo la instalación. | | **store** | Puede ser _app_store_ o _play_store_. | | **paywall_showed_at** | La fecha en que el paywall se mostró al cliente. | | **developer_id** | ID de desarrollador (SDK) del paywall donde se originó la transacción. | | **ab_test_name** | Nombre de la prueba A/B donde se originó la transacción. | | **ab_test_revision** | Revisión de la prueba A/B donde se originó la transacción. | | **paywall_name** | Nombre del paywall donde se originó la transacción. | | **paywall_revision** | Revisión del paywall donde se originó la transacción. | ## Eventos y etiquetas \{#events-and-tags\} Puedes gestionar qué datos comunica la integración. La integración ofrece las siguientes opciones de configuración: | Parámetro | Descripción | | :--------------------------------- | :----------------------------------------------------------- | | **Exclude Historical Events** | Elige excluir los eventos que ocurrieron antes de que el usuario instalara la app con el SDK de Adapty. Esto evita la duplicación de eventos y garantiza informes precisos. Por ejemplo, si un usuario activó una suscripción mensual el 10 de enero y actualizó la app con el SDK de Adapty el 6 de marzo, Adapty omitirá los eventos anteriores al 6 de marzo y conservará los posteriores. | | **Include events without profile** | Elige incluir las transacciones que no están vinculadas a un perfil de usuario en Adapty. Esto puede incluir compras realizadas antes de instalar el SDK de Adapty o transacciones recibidas desde las notificaciones del servidor del store que no pueden asociarse inmediatamente a un usuario concreto. | | **Send User Attributes** | Si deseas enviar atributos específicos del usuario, como preferencias de idioma, y tu plan de OneSignal admite más de 10 etiquetas, selecciona esta opción. Al activarla, se permite incluir información adicional más allá de las 10 etiquetas predeterminadas. Ten en cuenta que superar los límites de etiquetas puede provocar errores. | <img src="/assets/shared/img/s3-settings.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Debajo de los ajustes de integración, hay tres grupos de eventos que puedes exportar, enviar y almacenar en Amazon S3 desde Adapty. Activa los que necesites. Consulta la lista completa de eventos que ofrece Adapty [aquí](events). <img src="/assets/shared/img/fd5ccb9-CleanShot_2023-08-17_at_14.49.282x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: google-cloud-storage --- --- title: "Google Cloud Storage" description: "Integra Google Cloud Storage con Adapty para almacenar datos de forma segura." --- Activa la integración con Google Cloud Storage para almacenar de forma segura los [eventos de suscripción](events) y los [datos de visitas a paywalls](paywall-metrics) en un único lugar: tu bucket de Google Cloud Storage. Cada día a las 4 AM UTC, Adapty subirá archivos .csv con los datos del día anterior a tus buckets. Puedes elegir si quieres recibir datos de **eventos**, datos de **visitas a paywalls**, o **ambos**. También puedes exportar estos datos [manualmente](#manual-data-export) en cualquier momento y para cualquier período de tiempo. Para configurar la integración, [genera una clave de acceso al bucket](#create-google-cloud-storage-credentials) en tu consola de Google Cloud y [añádela en los ajustes de Adapty](#set-up-google-cloud-storage-integration). ## Programación y duración de las subidas \{#upload-schedule-and-duration\} Adapty sube datos a Google Cloud Storage cada 24 horas, a las 04:00 UTC. Los archivos contienen datos de los eventos creados durante el día natural anterior (UTC). El archivo subido el 8 de marzo incluirá todos los eventos creados el 7 de marzo, de 00:00:00 a 23:59:59 UTC. El proceso puede tardar varias horas, dependiendo del número total de archivos en cola y del volumen de datos que hayas solicitado. Si Adapty incluye datos históricos en tu primera subida, tardará más que las subidas diarias posteriores. ## Configurar la integración con Google Cloud Storage \{#set-up-google-cloud-storage-integration\} Necesitas una clave de cuenta de servicio de Google Cloud válida con **acceso de escritura**. Para generarla, sigue los pasos de la sección [crear credenciales](#create-google-cloud-storage-credentials). :::warning Puedes usar distintos buckets con diferentes credenciales para eventos y visitas a paywalls. Sin embargo, si **cualquiera** de las credenciales es inválida, [**ambas subidas fallarán**](#troubleshooting). ::: Ve a [**Integrations** -> **Google Cloud Storage**](https://app.adapty.io/integrations/google-cloud-storage) y abre la pestaña correspondiente (**Events** o **Paywall visits**). Activa la integración. Sube el archivo con tu **clave de cuenta de servicio de Google Cloud**. Especifica el **bucket** y la **carpeta** de destino. Guarda los cambios. ### Configuración opcional para datos de eventos \{#optional-settings-for-event-data\} Puedes especificar qué eventos incluir en el informe y definir nombres personalizados para ellos. Consulta el artículo de [eventos](events) para ver la lista completa de eventos disponibles. | Nombre | Valor predeterminado | Descripción | | ------------------------------ | ----------------- | ----------- | | Exclude historical events | true | Excluye información sobre eventos ocurridos antes de integrar el SDK de Adapty en tu app. <br /> <br />Si tu plataforma de analítica recibió eventos de suscripción **antes** de que empezaras a usar Adapty, esta opción evita que reciba eventos duplicados. <Details summary="Ejemplo práctico"><p>Un usuario compró una suscripción mensual el 10 de enero. La actualización del 1 de marzo de tu aplicación fue la primera en incluir el SDK de Adapty. <br /> <br /> Si este ajuste está **activado**, el informe no incluirá el evento "subscription started" de enero ni el evento "subscription renewed" de febrero. **Sí** incluirá el evento "subscription renewed" del 10 de marzo.</p> </Details> | | Include events without profile | false | Incluye transacciones que no están vinculadas a un perfil de usuario o que no pueden asociarse de inmediato a un usuario concreto. Pueden ser compras realizadas antes de instalar el SDK de Adapty, o transacciones recibidas mediante notificaciones del servidor. | | Send user attributes | false | Incluye [atributos personalizados del usuario](setting-user-attributes), como datos del usuario y de uso de la app. Selecciona esta opción si tu plan de OneSignal admite más de 10 etiquetas. Ten en cuenta que superar los límites de etiquetas puede generar errores. | ## Crear credenciales de Google Cloud Storage \{#create-google-cloud-storage-credentials\} Esta guía te ayudará a crear las credenciales necesarias en Google Cloud Platform Console. Para que Adapty pueda subir informes de datos sin procesar a tu bucket, se necesita la clave de cuenta de servicio y acceso de escritura al bucket correspondiente. Al proporcionar la clave de cuenta de servicio y conceder acceso de escritura al bucket, permites que Adapty transfiera de forma segura y eficiente los informes de datos desde su plataforma a tu entorno de almacenamiento. :::warning Ten en cuenta que solo admitimos la autorización mediante clave HMAC de cuenta de servicio, por lo que es fundamental asegurarse de que tu clave HMAC de cuenta de servicio tenga los roles "Storage Object Viewer", "Storage Legacy Bucket Writer" y "Storage Object Creator" asignados para habilitar el acceso correcto a Google Cloud Storage. ::: 1. En el primer paso, ve a la sección [IAM](https://console.cloud.google.com/projectselector2/iam-admin/serviceaccounts) de tu cuenta de Google Cloud y elige el proyecto correspondiente o crea uno nuevo. 1. A continuación, crea una nueva cuenta de servicio para Adapty haciendo clic en el botón "+ CREATE SERVICE ACCOUNT". 2. Rellena los campos del primer paso, ya que el acceso se concederá en una etapa posterior. Para obtener más detalles sobre esta página, consulta la documentación [aquí](https://docs.cloud.google.com/iam/docs/service-accounts-create). 3. Para crear y descargar una [clave JSON privada](https://docs.cloud.google.com/iam/docs/keys-create-delete), ve a la sección KEYS y haz clic en el botón "ADD KEY". 4. En la sección DETAILS, localiza el valor Email vinculado a la cuenta de servicio recién creada y cópialo. Esta información será necesaria en los pasos siguientes para autorizar la cuenta y permitirle escribir en el bucket. 5. A continuación, ve a la página de [Buckets](https://console.cloud.google.com/storage/browser) de Google Cloud Storage y selecciona un bucket existente o crea uno nuevo para almacenar los informes de datos de eventos o visitas de Adapty. Luego ve a la sección PERMISSIONS y selecciona la opción [GRANT ACCESS](https://docs.cloud.google.com/identity/docs/how-to?hl=en). 6. En la sección PERMISSIONS, introduce el Email de la cuenta de servicio obtenido en el quinto paso, selecciona el rol Storage Object Creator y haz clic en SAVE para aplicar los cambios. Recuerda guardar el nombre del bucket para consultarlo más adelante. ## Exportación manual de datos \{#manual-data-export\} Además de la exportación automática de datos de eventos a Google Cloud Storage, Adapty también ofrece una función de exportación manual de archivos. Con esta función, puedes seleccionar un intervalo de tiempo específico para los datos de eventos y exportarlos manualmente a tu bucket de GCS. Esto te da un mayor control sobre los datos que exportas y cuándo los exportas. El rango de fechas especificado se usará para exportar los eventos creados desde la Fecha A 00:00:00 UTC hasta la Fecha B 23:59:59 UTC. ## Estructura de datos \{#data-structure\} Adapty usa archivos `.csv` para exportar datos en formato tabular. :::warning El contenido de los eventos puede aumentar con el tiempo, con nuevos datos introducidos por nosotros o por terceros con los que trabajamos. Asegúrate de que el código que los procesa sea lo suficientemente robusto y se base en campos concretos, no en la estructura como un todo. ::: ### Eventos \{#events\} Puedes [modificar](#optional-settings-for-event-data) la lista de eventos que se incluyen en tus informes. | Columna | Descripción | |------|-----------| | **profile_id** | ID de usuario de Adapty. | | **event_type** | Nombre del evento en minúsculas. Consulta la sección [Eventos](events) para conocer los tipos de eventos. | | **event_datetime** | Fecha en formato ISO 8601. | | **transaction_id** | Identificador único de una transacción, como una compra o renovación. | | **original_transaction_id** | Identificador de la transacción de compra original. | | **subscription_expires_at** | Fecha de expiración de la suscripción. Generalmente en el futuro. | | **environment** | Puede ser Sandbox o Production. | | **revenue_usd** | Ingresos en USD. Puede estar vacío. | | **proceeds_usd** | Ganancias en USD. Puede estar vacío. | | **net_revenue_usd** | Ingresos netos (después de impuestos) en USD. Puede estar vacío. | | **tax_amount_usd** | Importe descontado por impuestos en USD. Puede estar vacío. | | **revenue_local** | Ingresos en moneda local. Puede estar vacío. | | **proceeds_local** | Ganancias en moneda local. Puede estar vacío. | | **net_revenue_local** | Ingresos netos (después de impuestos) en moneda local. Puede estar vacío. | | **tax_amount_local** | Importe descontado por impuestos en moneda local. Puede estar vacío. | | **customer_user_id** | ID de usuario del desarrollador. Por ejemplo, puede ser tu UUID de usuario, email u otro identificador. Nulo si no lo has definido. | | **store** | Puede ser *app_store* o *play_store*. | | **product_id** | ID del producto en Apple App Store, Google Play Store o Stripe. | | **base_plan_id** | [ID del plan base](https://support.google.com/googleplay/android-developer/answer/12154973) en Google Play Store o [ID de precio](https://docs.stripe.com/products-prices/how-products-and-prices-work#use-products-and-prices) en Stripe. | | **developer_id** | ID de desarrollador (SDK) del paywall donde se originó la transacción. | | **ab_test_name** | Nombre de la prueba A/B donde se originó la transacción. | | **ab_test_revision** | Revisión de la prueba A/B donde se originó la transacción. | | **paywall_name** | Nombre del paywall donde se originó la transacción. | | **paywall_revision** | Revisión del paywall donde se originó la transacción. | | **profile_country** | País del perfil determinado por Adapty, basado en la IP. | | **install_date** | Fecha en formato ISO 8601 de cuando ocurrió la instalación. | | **idfv** | [identifierForVendor](https://developer.apple.com/documentation/uikit/uidevice/identifierforvendor) en dispositivos iOS. | | **idfa** | [advertisingIdentifier](https://developer.apple.com/documentation/adsupport/asidentifiermanager/advertisingidentifier) en dispositivos iOS. | | **advertising_id** | El Advertising ID es un código único asignado por el sistema operativo Android que los anunciantes pueden usar para identificar de forma única el dispositivo de un usuario. | | **ip_address** | IP del dispositivo (puede ser IPv4 o IPv6, con preferencia por IPv4 cuando esté disponible). Se actualiza cada vez que cambia la IP del dispositivo. | | **cancellation_reason** | <p>El motivo por el que el usuario canceló una suscripción.</p><p></p><p>Valores posibles:</p><p>**iOS y Android** — *voluntarily_cancelled*, *billing_error*, *refund*</p><p>**Solo iOS** — *price_increase*, *product_was_not_available*, *unknown*, *upgraded*</p><p>**Solo Android** — *new_subscription_replace*, *cancelled_by_developer*</p> | | **android_app_set_id** | Un [AppSetId](https://developer.android.com/design-for-safety/privacy-sandbox/reference/adservices/appsetid/AppSetId): ID único por dispositivo y cuenta de desarrollador, restablecible por el usuario, para casos de uso publicitario sin monetización. | | **android_id** | En Android 8.0 (nivel de API 26) y versiones superiores, un número de 64 bits (expresado como cadena hexadecimal), único para cada combinación de clave de firma de app, usuario y dispositivo. Para más detalles, consulta la [documentación para desarrolladores de Android](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID). | | **device** | Nombre del modelo de dispositivo visible para el usuario final. | | **currency** | Código de divisa de 3 letras (ISO-4217) de la transacción. | | **store_country** | País del perfil determinado por Apple/Google store. | | **attribution_source** | Fuente de atribución. | | **attribution_network_user_id** | ID asignado al usuario por la fuente de atribución. | | **attribution_status** | Puede ser organic, non_organic o unknown. | | **attribution_channel** | Nombre del canal de marketing. | | **attribution_campaign** | Nombre de la campaña de marketing. | | **attribution_ad_group** | Grupo de anuncios de atribución. | | **attribution_ad_set** | Conjunto de anuncios de atribución. | | **attribution_creative** | Palabra clave creativa de atribución. | | **attributes** | JSON con los [atributos personalizados del usuario](setting-user-attributes#custom-user-attributes). Incluirá todos los atributos personalizados que hayas configurado para enviar desde tu app móvil. Para enviarlo, activa la opción **Send User Attributes** en la página [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook). | | **integration_ids** | Todos los IDs de integración asociados a un perfil. Diccionario. Ejemplo: {'mixpanel_user_id': 'mixpanelUserId-test', 'facebook_anonymous_id': 'facebookAnonymousId-test'} | ### Visitas a paywalls \{#paywall-visits\} | Columna | Descripción | | :-------------------- | :----------------------------------------------------------------------------------------------------------- | | **profile_id** | ID de usuario de Adapty. | | **customer_user_id** | ID de usuario del desarrollador. Por ejemplo, puede ser tu UUID de usuario, email u otro identificador. Nulo si no lo has definido. | | **profile_country** | País del perfil determinado por Apple/Google store. | | **install_date** | Fecha en formato ISO 8601 de cuando ocurrió la instalación. | | **store** | Puede ser *app_store* o *play_store*. | | **paywall_showed_at** | La fecha en que se mostró el paywall al cliente. | | **developer_id** | ID de desarrollador (SDK) del paywall donde se originó la transacción. | | **ab_test_name** | Nombre de la prueba A/B donde se originó la transacción. | | **ab_test_revision** | Revisión de la prueba A/B donde se originó la transacción. | | **paywall_name** | Nombre del paywall donde se originó la transacción. | | **paywall_revision** | Revisión del paywall donde se originó la transacción. | ## Solución de problemas \{#troubleshooting\} Adapty comprueba la validez de tus claves de acceso **antes** de comenzar la subida. Aunque solo una de tus claves de Google Cloud Storage sea inválida, Adapty **cancela la subida** y genera un error. Para garantizar subidas ininterrumpidas, reemplaza tus claves antes de que expiren. Si actualizas la clave de **eventos**, no olvides actualizar también la clave de **visitas a paywalls**, y viceversa. --- # File: webhook-event-types-and-fields --- --- title: "Tipos de eventos y campos de webhook" description: "" --- Adapty envía webhooks en respuesta a eventos de suscripción. Esta sección define los tipos de eventos y los datos que contiene cada webhook. ## Tipos de eventos de webhook \{#webhook-event-types\} Puedes enviar todos los tipos de eventos a tu webhook o elegir solo algunos. Consulta nuestros [Flujos de eventos](event-flows) para saber qué tipo de datos entrantes puedes esperar y cómo estructurar tu lógica de negocio en torno a ellos. Puedes desactivar los tipos de eventos que no necesites al [configurar tu integración con Webhook](set-up-webhook-integration#configure-webhook-integration-in-the-adapty-dashboard). También puedes sustituir los IDs de eventos predeterminados de Adapty por los tuyos propios si es necesario. | Nombre del evento | Descripción | |:-----------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | subscription_started | Se activa cuando un usuario activa una suscripción de pago sin período de prueba, es decir, se le cobra de inmediato. | | subscription_renewed | Ocurre cuando se renueva una suscripción y se cobra al usuario. Este evento comienza a partir de la segunda facturación, tanto en suscripciones con prueba como sin ella. | | subscription_renewal_cancelled | El usuario ha desactivado la renovación automática de la suscripción. El usuario conserva el acceso a las funciones premium hasta el final del período de suscripción pagado. | | subscription_renewal_reactivated | Se activa cuando un usuario reactiva la renovación automática de la suscripción. | | subscription_expired | Se activa cuando una suscripción finaliza por completo tras ser cancelada. Por ejemplo, si un usuario cancela una suscripción el 12 de diciembre pero esta permanece activa hasta el 31 de diciembre, el evento se registra el 31 de diciembre cuando la suscripción expira. | | subscription_paused | Ocurre cuando un usuario activa la [pausa de suscripción](https://developer.android.com/google/play/billing/lifecycle/subscriptions#pause) (solo Android). | | subscription_deferred | Se activa cuando una compra de suscripción se [aplaza](https://adapty.io/glossary/subscription-purchase-deferral/), lo que permite a los usuarios retrasar el pago manteniendo el acceso a las funciones premium. Esta función está disponible a través de la Google Play Developer API y puede usarse para pruebas gratuitas o para ayudar a usuarios con dificultades económicas. | | non_subscription_purchase | Cualquier compra que no sea una suscripción, como el acceso de por vida o productos consumibles como monedas del juego. | | trial_started | Se activa cuando un usuario activa una suscripción de prueba. | | trial_converted | Ocurre cuando finaliza una prueba y se cobra al usuario (primera compra). Por ejemplo, si un usuario tiene una prueba hasta el 14 de enero pero se le cobra el 7 de enero, este evento se registra el 7 de enero. | | trial_renewal_cancelled | El usuario desactivó la renovación automática de la suscripción durante el período de prueba. El usuario conserva el acceso a las funciones premium hasta que finalice la prueba, pero no se le cobrará ni comenzará una suscripción. | | trial_renewal_reactivated | Ocurre cuando un usuario reactiva la renovación automática de la suscripción durante el período de prueba. | | trial_expired | Se activa cuando finaliza una prueba sin convertirse en suscripción. | | entered_grace_period | Ocurre cuando falla un intento de pago y el usuario entra en un período de gracia (si está habilitado). El usuario conserva el acceso premium durante este tiempo. | | billing_issue_detected | Se activa cuando ocurre un problema de facturación durante un intento de cobro (p. ej., saldo insuficiente en la tarjeta). | | subscription_refunded | Se activa cuando se reembolsa una suscripción (p. ej., por parte del soporte de Apple). | | non_subscription_purchase_refunded | Se activa cuando se reembolsa una compra que no es una suscripción. | | access_level_updated | Ocurre cuando se actualiza el nivel de acceso de un usuario. | ## Estructura de eventos del webhook \{#webhook-event-structure\} Adapty te enviará únicamente los eventos que hayas seleccionado en la sección **Events names** de la página [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook). Los eventos de webhook se serializan en JSON. El cuerpo de la solicitud `POST` a tu servidor contendrá el evento serializado con la siguiente estructura. Todos los eventos siguen la misma estructura, pero sus campos varían según el tipo de evento, el store y tu configuración específica. Los atributos de usuario son los [atributos de usuario personalizados](setting-user-attributes#custom-user-attributes) que hayas configurado, por lo que contienen lo que hayas definido. Los campos de datos de atribución son iguales para todos los tipos de eventos; sin embargo, la lista de atribuciones dependerá de las fuentes de atribución que uses en tu app móvil. A continuación encontrarás un ejemplo de evento: ```json title="Json" showLineNumbers { "profile_id": "00000000-0000-0000-0000-000000000000", "customer_user_id": "UserIdInYourSystem", "idfv": "00000000-0000-0000-0000-000000000000", "idfa": "00000000-0000-0000-0000-000000000000", "advertising_id": "00000000-0000-0000-0000-000000000000", "profile_install_datetime": "2000-01-31T00:00:00.000000+0000", "user_agent": "ExampleUserAgent/1.0 (Device; OS Version) Browser/Engine", "email": "john.doe@company.com", "event_type": "subscription_started", "event_datetime": "2000-01-31T00:00:00.000000+0000", "event_properties": { "store": "play_store", "currency": "USD", "price_usd": 4.99, "profile_id": "00000000-0000-0000-0000-000000000000", "cohort_name": "All Users", "environment": "Production", "price_local": 4.99, "base_plan_id": "b1", "developer_id": "onboarding_placement", "ab_test_name": "onboarding_ab_test", "ab_test_revision": 1, "paywall_name": "UsedPaywall", "proceeds_usd": 4.2315, "variation_id": "00000000-0000-0000-0000-000000000000", "purchase_date": "2024-11-15T10:45:36.181000+0000", "store_country": "AR", "event_datetime": "2000-01-31T00:00:00.000000+0000", "proceeds_local": 4.2415, "tax_amount_usd": 0, "transaction_id": "0000000000000000", "net_revenue_usd": 4.2415, "profile_country": "AR", "paywall_revision": "1", "profile_event_id": "00000000-0000-0000-0000-000000000000", "tax_amount_local": 0, "net_revenue_local": 4.2415, "vendor_product_id": "onemonth_no_trial", "profile_ip_address": "10.10.1.1", "consecutive_payments": 1, "rate_after_first_year": false, "original_purchase_date": "2000-01-31T00:00:00.000000+0000", "original_transaction_id": "0000000000000000", "subscription_expires_at": "2000-01-31T00:00:00.000000+0000", "profile_has_access_level": true, "profile_total_revenue_usd": 4.99, "promotional_offer_id": null, "store_offer_category": null, "store_offer_discount_type": null }, "event_api_version": 1, "profiles_sharing_access_level": [{"profile_id": "00000000-0000-0000-0000-000000000000", "customer_user_id": "UserIdInYourSystem"}], "attributions": { "appsflyer": { "ad_set": "Keywords 1.12", "status": "non_organic", "channel": "Google Ads", "ad_group": null, "campaign": "Social media influencers - Rest of the world", "creative": null, "created_at": "2000-01-31T00:00:00.000000+0000" } }, "user_attributes": {"Favourite_color": "Violet", "Pet_name": "Fluffy"}, "integration_ids": {"firebase_app_instance_id": "val1", "branch_id": "val2", "one_signal_player_id": "val3"}, "play_store_purchase_token": { "product_id": "product_123", "purchase_token": "token_abc_123", "is_subscription": true } } ``` ### Campos del evento \{#event-fields\} Los parámetros del evento son los mismos para todos los tipos de evento. | **Campo** | **Tipo** | **Descripción** | |---|---|---| | **advertising_id** | UUID | ID de publicidad (solo Android). | | **attributions** | JSON | [Datos de atribución](webhook-event-types-and-fields#attributions). Se incluye si **Send Attribution** está habilitado en los [ajustes del Webhook](https://app.adapty.io/integrations/customwebhook). | | **customer_user_id** | String | ID de usuario de tu app (UUID, email u otro ID) si lo configuras en el código de tu app al [identificar usuarios](ios-quickstart-identify). Si no identificas usuarios en el código de la app o este usuario concreto es anónimo (no ha iniciado sesión), este campo es `null`. | | **email** | String | Email del usuario si lo configuras mediante el método [`updateProfile`](setting-user-attributes) en el SDK de Adapty o al crear/actualizar perfiles a través de la API del servidor. Si no pasas el valor `email` al SDK o al método de la API, este campo es `null`. | | **event_api_version** | Integer | Versión de la API de Adapty (actual: `1`). | | **event_datetime** | ISO 8601 | Marca de tiempo del evento en formato [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) (p. ej., `2020-07-10T15:00:00.000000+0000`). | | **event_properties** | JSON | [Propiedades del evento](webhook-event-types-and-fields#event-properties). | | **event_type** | String | Nombre del evento en formato Adapty. Consulta los [tipos de eventos del Webhook](webhook-event-types-and-fields#webhook-event-types) para ver la lista completa. | | **idfa** | UUID | ID de publicidad (solo Apple). **IDFA** en el perfil en el [Adapty Dashboard](https://app.adapty.io/profiles/users). Puede ser `null` si no está disponible por restricciones de seguimiento, modo para niños o ajustes de privacidad. | | **idfv** | UUID | Identificador para proveedores (IDFV), único por desarrollador. **IDFV** en el perfil en el [Adapty Dashboard](https://app.adapty.io/profiles/users). | | **integration_ids** | JSON | IDs de integración del usuario si los configuras mediante el método `setIntegrationIdentifier` en el SDK de Adapty o al crear/actualizar perfiles a través de la API del servidor. `null` si no están disponibles o las integraciones están deshabilitadas. | | **play_store_purchase_token** | JSON | [Token de compra de Play Store](webhook-event-types-and-fields#play-store-purchase-token), incluido si **Send Play Store purchase token** está habilitado en los [ajustes del Webhook](https://app.adapty.io/integrations/customwebhook). | | **profile_id** | UUID | ID de perfil generado automáticamente por Adapty para cada perfil. Un mismo ID de Apple/Google puede asociarse a diferentes IDs de perfil si no identificas usuarios o permites compras antes del inicio de sesión. Más información sobre [cómo funciona Adapty con perfiles padre/heredero](how-profiles-work#parent-and-inheritor-profiles). | | **profile_install_datetime** | ISO 8601 | Marca de tiempo de instalación en formato [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) (p. ej., `2020-07-10T15:00:00.000000+0000`). | | **profiles_sharing_access_level** | JSON | Lista de usuarios que [comparten el nivel de acceso](general#6-sharing-paid-access-between-user-accounts) excluyendo el perfil del usuario actual. Si el uso compartido de niveles de acceso está habilitado para tu app, esta lista incluye otros perfiles que se han usado con el mismo ID de Apple/Google.<br/>Formato: <ul><li>**profile_id**: (UUID) ID de Adapty</li><li>**customer_user_id**: (String) Customer User ID si se ha proporcionado</li></ul> | | **user_agent** | String | User-agent del navegador del dispositivo. | | **user_attributes** | JSON | Datos personalizados que puedes configurar para enriquecer los perfiles de usuario con información específica de la app. Se usan habitualmente para registrar preferencias del usuario (p. ej., tema, idioma) o indicadores de comportamiento (onboarding completado, uso de funciones). <br/>Con formato de pares clave-valor donde las claves son cadenas y los valores pueden ser cadenas o números (p. ej., `{"Favourite_color": "Violet", "Pet_name": "Fluffy"}`). <br/>Puedes configurar atributos personalizados manualmente en el Adapty Dashboard para perfiles individuales, de forma programática mediante el método `updateProfile` en el SDK de Adapty, o a través de la API del servidor al crear/actualizar perfiles. <br/>Se incluye si **Send User Attributes** está habilitado en los [ajustes del Webhook](https://app.adapty.io/integrations/customwebhook). <p>Aunque los valores de los atributos personalizados en el código de la app móvil pueden definirse como floats o strings, los atributos recibidos a través de la API del servidor o una importación histórica pueden llegar en distintos formatos. En ese caso, los valores booleanos y enteros se convertirán a float.</p> | ### Atribuciones \{#attributions\} Para enviar los datos de atribución, activa la opción **Send Attribution** en la página [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook). Si has activado el envío de datos de atribución y has configurado las [integraciones de atribución](attribution-integration), los datos que se muestran a continuación se enviarán con el evento para cada fuente. Los mismos datos de atribución se envían a todos los tipos de eventos. ```json title="Json" showLineNumbers { "attributions": { "appsflyer": { "ad_set": "sample_ad_set_123", "status": "non_organic", "channel": "sample_channel", "ad_group": "sample_ad_group_456", "campaign": "sample_ios_campaign", "creative": "sample_creative_789", "created_at": "2000-01-31T00:00:00.000000+0000", "network_user_id": "0000000000000-0000000" } } } ``` | Nombre del campo | Tipo de campo | Descripción | | :------------------ | :------------ | :------------------------------------------------- | | **ad_set** | String | Conjunto de anuncios de atribución. | | **status** | String | Puede ser `organic`, `non_organic,` o `unknown`. | | **channel** | String | Nombre del canal de marketing. | | **ad_group** | String | Grupo de anuncios de atribución. | | **campaign** | String | Nombre de la campaña de marketing. | | **creative** | String | Palabra clave creativa de atribución. | | **created_at** | ISO 8601 date | Fecha y hora de creación del registro de atribución. | | **network_user_id** | String | ID asignado al usuario por la fuente de atribución. | ### IDs de integración \{#integration-ids\} Los siguientes IDs de integración se utilizan actualmente en los eventos: - `adjust_device_id` - `airbridge_device_id` - `amplitude_device_id` - `amplitude_user_id` - `appmetrica_device_id` - `appmetrica_profile_id` - `appsflyer_id` - `branch_id` - `facebook_anonymous_id` - `firebase_app_instance_id` - `mixpanel_user_id` - `pushwoosh_hwid` - `one_signal_player_id` - `one_signal_subscription_id` - `tenjin_analytics_installation_id` - `posthog_distinct_user_id` ### Token de compra de Play Store \{#play-store-purchase-token\} Este campo incluye todos los datos necesarios para revalidar una compra, si fuera necesario. Solo se envía si la opción **Send Play Store purchase token** está habilitada en la [configuración de la integración de Webhook](https://app.adapty.io/integrations/customwebhook). | Campo | Tipo | Descripción | | :------------------ | :------ | :----------------------------------------------------------- | | **product_id** | String | El identificador único del producto (SKU) comprado en Play Store. | | **purchase_token** | String | Token generado por Google Play para identificar de forma única esta transacción de compra. | | **is_subscription** | Boolean | Indica si el producto comprado es una suscripción (`true`) o una compra única (`false`). | ### Propiedades del evento \{#event-properties\} Las propiedades de los eventos pueden variar según el tipo de evento e incluso entre eventos del mismo tipo. Por ejemplo, un evento que proviene de App Store no incluirá propiedades específicas de Android como `base_plan_id`. El evento [Nivel de acceso actualizado](webhook-event-types-and-fields#for-access-level-updated-event) tiene propiedades específicas, por lo que le hemos dedicado una sección aparte. Del mismo modo, hemos separado las [Propiedades adicionales de eventos de impuestos e ingresos](webhook-event-types-and-fields#additional-tax-and-revenue-event-properties), ya que son exclusivas de ciertos tipos de eventos. #### Para la mayoría de los tipos de eventos \{#for-most-event-types\} Las propiedades de los eventos son consistentes en la mayoría de los tipos de eventos (excepto el evento **Access Level Updated**, que se describe en su propia sección). A continuación encontrarás una tabla completa con las propiedades y si pertenecen a eventos específicos. | Campo | Tipo | Descripción | |:------------------------------|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **ab_test_name** | String | Nombre de la [prueba A/B de Adapty](ab-tests) donde se originó la transacción. | | **ab_test_revision** | Integer | Revisión de la prueba A/B donde se originó la transacción. | | **base_plan_id** | String | [ID del plan base](https://support.google.com/googleplay/android-developer/answer/12154973) en Google Play Store o [ID de precio](https://docs.stripe.com/products-prices/how-products-and-prices-work#use-products-and-prices) en Stripe. | | **cancellation_reason** | String | <p>Posibles motivos de cancelación: `voluntarily_cancelled`, `billing_error`, `price_increase`, `product_was_not_available`, `refund`, `cancelled_by_developer`, `new_subscription_replace`, `upgraded`, `unknown`, `adapty_revoked`.</p><p>Presente en los siguientes tipos de evento:</p>`subscription_cancelled`, `subscription_refunded` y `trial_cancelled`. | | **cohort_name** | String | Nombre de la [audiencia](audience) que determinó qué paywall se mostró al usuario. | | **consecutive_payments** | Integer | Número de períodos que un usuario lleva suscrito sin interrupciones. Incluye el período actual. | | **currency** | String | Moneda local. | | **developer_id** | String | ID del [placement](placements) donde se originó la transacción. | | **environment** | String | Los valores posibles son `Sandbox` o `Production`. | | **event_datetime** | Fecha ISO 8601 | Fecha y hora del evento. Igual que en el nivel raíz del evento. | | **original_purchase_date** | Fecha ISO 8601 | En suscripciones recurrentes, la compra original es la primera transacción de la cadena; su ID, llamado ID de transacción original, vincula la cadena de renovaciones. Las transacciones posteriores son extensiones de ella. La fecha de compra original es la fecha y hora de esa primera transacción. | | **original_transaction_id** | String | <p>En suscripciones recurrentes, es el ID de transacción original que vincula la cadena de renovaciones. La transacción original es la primera de la cadena; las siguientes son extensiones de ella.</p><p>Si no hay extensiones, `original_transaction_id` coincide con store_transaction_id.</p> | | **paywall_name** | String | Nombre del paywall donde se originó la transacción. | | **paywall_revision** | String | Revisión del paywall donde se originó la transacción. El valor predeterminado es 1. | | **price_local** | Float | Precio del producto antes de la comisión de Apple/Google en moneda local. | | **price_usd** | Float | Precio del producto antes de la comisión de Apple/Google en USD. | | **profile_country** | String | Determinado por Adapty a partir de la IP del perfil. | | **profile_event_id** | UUID | ID de evento único que puede usarse para deduplicación. | | **profile_has_access_level** | Boolean | Valor booleano que indica si el perfil tiene un nivel de acceso activo. | | **profile_id** | UUID | ID de perfil generado por Adapty. Igual que en el nivel raíz del evento. | | **profile_ip_address** | String | IP del perfil (puede ser IPv4 o IPv6; se prefiere IPv4 cuando está disponible). `null` si **Collect users' IP addresses** está desactivado en los [ajustes de la app](https://app.adapty.io/settings/general). | | **profile_total_revenue_usd** | Float | Ingresos totales del perfil con los reembolsos descontados. | | **promotional_offer_id** | String | ID de Adapty de la [oferta promocional](offers) utilizada. Este ID lo defines tú al crear una oferta en el dashboard. | | **purchase_date** | Fecha ISO 8601 | Fecha y hora de la compra del producto. | | **rate_after_first_year** | Boolean | Booleano que indica que la suscripción cumple los requisitos para una comisión reducida (normalmente el 15%) tras un año de renovación continua. Las tasas de comisión varían según la elegibilidad del programa y el país. Consulta [Comisión del store e impuestos](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue) para más detalles. | | **store** | String | Store donde se realizó la compra. Valores estándar: **app_store**, **play_store**, **stripe**, **paddle**. <br/>Si configuras [transacciones de store personalizadas](api-adapty/operations/setTransaction) mediante la API del servidor, se usa el valor del parámetro **store**. | | **store_country** | String | País enviado por el app store. | | **store_offer_category** | String | Categoría de oferta aplicada. Los valores posibles son `introductory`, `promotional`, `winback`. | | **store_offer_discount_type** | String | Tipo de oferta aplicada. Los valores posibles son `free_trial`, `pay_as_you_go` y `pay_up_front`. | | **subscription_expires_at** | Fecha ISO 8601 | Fecha de vencimiento de la suscripción. Normalmente en el futuro. | | **transaction_id** | String | Identificador único de una transacción. | | **trial_duration** | String | Duración del período de prueba en días. Se envía con el formato "{} days", por ejemplo, "7 days". Presente solo en los tipos de evento relacionados con pruebas: `trial_started`, `trial_converted`, `trial_cancelled`. | | **variation_id** | UUID | ID único del paywall donde se realizó la compra. | | **vendor_product_id** | String | ID del producto en Apple App Store, Google Play Store o Stripe. | #### Propiedades adicionales de impuestos e ingresos en eventos \{#additional-tax-and-revenue-event-properties\} Las propiedades de evento relacionadas con impuestos e ingresos que se muestran a continuación son campos adicionales que solo aplican a ciertos tipos de eventos. Esto significa que los tipos de eventos listados incluyen las [Propiedades de evento para la mayoría de los tipos de eventos](webhook-event-types-and-fields#for-most-event-types), junto con los campos extra que se detallan a continuación. Tipos de eventos que tienen las propiedades de impuestos e ingresos: - `subscription_renewed` - `subscription_initial_purchase` - `subscription_refunded` - `non_subscription_purchase` | Campo | Tipo | Descripción | | :-------------------- | :---- | :----------------------------------------------------------- | | **net_revenue_local** | Float | Ingresos netos (ingresos tras la comisión de Apple/Google e impuestos) en moneda local. | | **net_revenue_usd** | Float | Ingresos netos (ingresos tras la comisión de Apple/Google e impuestos) en USD. | | **proceeds_local** | Float | Precio del producto tras la comisión de Apple/Google en moneda local. | | **proceeds_usd** | Float | Precio del producto tras la comisión de Apple/Google. | | **tax_amount_local** | Float | Importe de impuestos deducido en moneda local. | | **tax_amount_usd** | Float | Importe de impuestos deducido en USD. | #### Para el evento Access Level Updated El evento **Access Level Updated** es un evento de webhook específico que se genera únicamente cuando la integración de Webhook está activa y este tipo de evento está habilitado. Si está habilitado, se envía al Webhook configurado y aparece en el **Event Feed**. Si no está habilitado, el evento no se creará. Si has habilitado el [uso compartido de niveles de acceso](general#6-sharing-paid-access-between-user-accounts), el evento **access level updated** se enviará para todos los perfiles que compartan el nivel de acceso. :::tip Usa este evento para actualizar el nivel de acceso del usuario en tu base de datos, conceder o revocar funciones premium en tu backend y mantener el acceso sincronizado entre dispositivos o plataformas. ::: | Propiedad | Tipo | Descripción | | ---------------------------------- | ------------- | ------------------------------------------------------------ | | **ab_test_name** | String | Nombre de la prueba A/B donde se originó la transacción. | | **access_level_id** | String | El ID del nivel de acceso. | | **activated_at** | ISO 8601 date | Fecha y hora de la última activación del acceso. | | **active_introductory_offer_type** | String | Tipo de oferta introductoria aplicada. Los valores posibles son `free_trial`, `pay_as_you_go` y `pay_up_front`. | | **active_promotional_offer_id** | String | ID de la oferta promocional indicada en la sección Product del Adapty Dashboard. | | **active_promotional_offer_type** | String | Tipo de oferta promocional aplicada. Los valores posibles son `free_trial`, `pay_as_you_go` y `pay_up_front`. | | **base_plan_id** | String | [ID del plan base](https://support.google.com/googleplay/android-developer/answer/12154973) en Google Play Store o [ID de precio](https://docs.stripe.com/products-prices/how-products-and-prices-work#use-products-and-prices) en Stripe. | | **billing_issue_detected_at** | ISO 8601 date | Fecha y hora del problema de facturación. | | **cancellation_reason** | String | Motivos posibles de cancelación: `voluntarily_cancelled`, `billing_error`, `price_increase`, `product_was_not_available`, `refund`, `cancelled_by_developer`, `new_subscription_replace`, `upgraded`, `unknown`, `adapty_revoked`. | | **cohort_name** | String | Nombre de la audiencia a la que pertenece el perfil. | | **currency** | String | Moneda local (por defecto USD). | | **developer_id** | String | El ID del placement donde se originó la transacción. | | **environment** | String | Los valores posibles son `Sandbox` o `Production`. | | **event_datetime** | ISO 8601 date | La fecha y hora del evento. | | **expires_at** | ISO 8601 date | Fecha y hora en que expirará el acceso. | | **is_active** | Boolean | Booleano que indica si el nivel de acceso está activo. | | **is_in_grace_period** | Boolean | Booleano que indica si el perfil está en el período de gracia. | | **is_lifetime** | Boolean | Booleano que indica si el nivel de acceso es de por vida. | | **is_refund** | Boolean | Booleano que indica si la transacción es un reembolso. | | **original_purchase_date** | ISO 8601 date | En suscripciones recurrentes, la compra original es la primera transacción de la cadena, cuyo ID (llamado original transaction ID) vincula la cadena de renovaciones; las transacciones posteriores son extensiones de ella. La fecha de compra original es la fecha y hora de esta primera transacción. | | **original_transaction_id** | String | <p>En suscripciones recurrentes, es el ID de transacción original que vincula la cadena de renovaciones. La transacción original es la primera de la cadena; las posteriores son extensiones de ella.</p><p>Si no hay extensiones, `original_transaction_id` coincide con store_transaction_id.</p>El identificador de transacción de la compra original. | | **paywall_name** | String | Nombre del paywall donde se originó la transacción. | | **paywall_revision** | String | Revisión del paywall donde se originó la transacción. El valor por defecto es 1. | | **profile_country** | String | Determinado por Adapty a partir de la IP del perfil. | | **profile_event_id** | UUID | ID de evento único que puede usarse para deduplicación. | | **profile_has_access_level** | Boolean | Booleano que indica si el perfil tiene un nivel de acceso activo. | | **profile_id** | UUID | ID interno de perfil de usuario de Adapty. | | **profile_ip_address** | String | Dirección IP del perfil del usuario. | | **profile_total_revenue_usd** | Float | Ingresos totales del perfil, incluidos los reembolsos. | | **purchase_date** | ISO 8601 date | La fecha y hora de compra del producto. | | **renewed_at** | ISO 8601 date | Fecha y hora en que se renovará el acceso. | | **starts_at** | ISO 8601 date | Fecha y hora en que comienza el nivel de acceso. | | **store** | String | Store donde se compró el producto. Valores estándar: **app_store**, **play_store**, **stripe**, **paddle**. <br/>Si configuras [transacciones de store personalizadas](api-adapty/operations/setTransaction) mediante la API del servidor, se usa el valor del parámetro **store**. | | **store_country** | String | País enviado a Adapty por el store. | | **subscription_expires_at** | ISO 8601 date | Fecha de expiración de la suscripción. | | **transaction_id** | String | Identificador único de una transacción. | | **trial_duration** | String | Duración del período de prueba en días (p. ej., "7 days"). | | **variation_id** | UUID | Identificador de una variante, usado para atribuir compras a este paywall. | | **vendor_product_id** | String | ID del producto en el store (Apple/Google/Stripe). | | **will_renew** | Boolean | Indica si el nivel de acceso de pago se renovará. | :::warning Ten en cuenta que esta estructura puede crecer con el tiempo, ya que nosotros o los terceros con los que trabajamos podemos añadir nuevos datos. Asegúrate de que el código que la procesa sea lo suficientemente robusto y se base en campos específicos en lugar de en la estructura completa. ::: --- # File: set-up-webhook-integration --- --- title: "Configurar la integración de webhook" description: "Configura la integración de webhook en Adapty para automatizar el seguimiento de eventos." --- La [integración de webhook](webhook) de Adapty consta de los siguientes pasos: <img src="/assets/shared/img/webhook-setup.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '300px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <p> </p> 1. **Configura tu endpoint:** 1. Asegúrate de que tu servidor pueda procesar las solicitudes de Adapty con la cabecera **Content-Type** establecida en `application/json`. 2. Configura tu servidor para recibir la solicitud de verificación de Adapty y responder con cualquier estado `2xx` y un cuerpo JSON. 3. [Gestiona los eventos de suscripción](#subscription-events) una vez verificada la conexión. 2. **Configura y activa la integración de webhook** en el [Adapty Dashboard](#configure-webhook-integration-in-the-adapty-dashboard). También puedes [mapear los eventos de Adapty a nombres de eventos personalizados](#configure-webhook-integration-in-the-adapty-dashboard). Te recomendamos probar en el entorno **Sandbox** antes de pasar a producción. 3. **Adapty envía una solicitud de verificación** a tu servidor. 4. **Tu servidor responde** con un estado `2XX` y un cuerpo JSON. 5. **Una vez que Adapty recibe una respuesta válida, empieza a enviar eventos de suscripción.** ## Configura tu servidor para procesar las solicitudes de Adapty \{#set-up-your-server-to-process-adapty-requests\} Adapty enviará a tu endpoint de webhook 2 tipos de solicitudes: 1. [Solicitud de verificación](#verification-request): la solicitud inicial para verificar que la conexión está configurada correctamente. Esta solicitud no contendrá ningún evento y se enviará en el momento en que hagas clic en el botón **Save** en la integración de Webhook del Adapty Dashboard. Para confirmar que tu endpoint recibió correctamente la solicitud de verificación, tu endpoint debe responder con la respuesta de verificación. 2. [Evento de suscripción](#subscription-events): una solicitud estándar que el servidor de Adapty envía cada vez que se crea un evento en él. Tu servidor no necesita responder con ninguna respuesta específica. Lo único que necesita el servidor de Adapty es recibir una respuesta HTTP estándar con código 200 si recibe el mensaje correctamente. ### Solicitud de verificación \{#verification-request\} Después de activar la integración de webhook en el Adapty Dashboard, Adapty enviará una solicitud POST de verificación que contiene un objeto JSON vacío `{}` como cuerpo. Configura tu endpoint para que tenga la **cabecera Content-Type** como `application/json`, es decir, el endpoint de tu servidor debe esperar que la solicitud de webhook entrante tenga su carga útil formateada como JSON. Tu servidor debe responder con un código de estado 2xx y enviar cualquier respuesta JSON válida, por ejemplo: ```json title="Json" {} ``` Una vez que Adapty recibe la respuesta de verificación en el formato correcto y con un código de estado 2xx, tu integración de webhook de Adapty está completamente configurada. ### Eventos de suscripción \{#subscription-events\} Los eventos de suscripción se envían con la cabecera **Content-Type** establecida en `application/json` y contienen datos del evento en formato JSON. Para conocer los posibles tipos de eventos y las estructuras de solicitud, consulta [Tipos de eventos y campos del webhook](webhook-event-types-and-fields). ## Configurar la integración de webhook en el Adapty Dashboard \{#configure-webhook-integration-in-the-adapty-dashboard\} En Adapty, puedes configurar flujos separados para los eventos de producción y los eventos de prueba recibidos desde el entorno sandbox de Apple o Stripe, o desde la cuenta de prueba de Google. Para los eventos de producción, usa el campo **Production endpoint URL** especificando la URL a la que se enviarán los callbacks. Además, configura el campo **Authorization header value for production endpoint**: la cabecera que tu servidor usará para autenticar los eventos de Adapty. Ten en cuenta que usaremos el valor especificado en el campo **Authorization header value for production endpoint** como cabecera `Authorization` exactamente como se proporcionó, sin ningún cambio ni adición. Para los eventos de prueba, usa los campos **Sandbox endpoint URL** y **Authorization header value for sandbox endpoint** según corresponda. Para configurar la integración de webhook: 1. Abre [Integrations -> Webhook](https://app.adapty.io/integrations/customwebhook) en tu Adapty Dashboard. <img src="/assets/shared/img/webhook_integration.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Activa el interruptor para iniciar la integración. 4. Rellena los campos de la integración: | Campo | Descripción | | ------------------------------------------------------ | ------------------------------------------------------------ | | **Production endpoint URL** | La URL que Adapty usa para enviar solicitudes HTTP POST de eventos en producción. | | **Authorization header value for production endpoint** | <p>La cabecera que tu servidor usará para autenticar las solicitudes de Adapty en producción. Ten en cuenta que usaremos el valor especificado en este campo como cabecera `Authorization` exactamente como se proporcionó, sin ningún cambio ni adición.</p><p></p><p>Aunque no es obligatorio, se recomienda encarecidamente para mayor seguridad.</p> | Además, para tus necesidades de prueba en el entorno sandbox, hay otros dos campos disponibles: | Campo de prueba | Descripción | | --------------------------------------------------- | ------------------------------------------------------------ | | **Sandbox endpoint URL** | La URL que Adapty usa para enviar solicitudes HTTP POST de eventos en el entorno sandbox. | | **Authorization header value for sandbox endpoint** | <p>La cabecera que tu servidor usará para autenticar las solicitudes de Adapty durante las pruebas en el entorno sandbox. Ten en cuenta que usaremos el valor especificado en este campo como cabecera `Authorization` exactamente como se proporcionó, sin ningún cambio ni adición.</p><p></p><p>Aunque no es obligatorio, se recomienda encarecidamente para mayor seguridad.</p> | 4. (Opcional) Elige los eventos que quieres recibir y mapea sus nombres. Consulta nuestros [Flujos de eventos](event-flows) para ver qué eventos se activan en cada situación. Si los IDs de tus eventos difieren de los usados en Adapty, mantén los IDs de tu sistema tal como están y reemplaza los IDs de eventos predeterminados de Adapty con los tuyos en la sección **Events names** de la página [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook). El ID del evento puede ser cualquier cadena; simplemente asegúrate de que el ID del evento en tu servidor de procesamiento de webhooks coincida con el que introdujiste en el Adapty Dashboard. No puedes dejar el ID del evento vacío para los eventos habilitados. <img src="/assets/shared/img/86942b8-event_names_renaming.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Los campos y opciones adicionales no son obligatorios; úsalos según sea necesario: | Ajuste | Descripción | | :--------------------------------- | :----------------------------------------------------------- | | **Send Trial Price** | Cuando está habilitado, Adapty incluirá el precio de la suscripción en los campos `price_local` y `price_usd` para el evento **Trial Started**. | | **Exclude Historical Events** | Elige excluir los eventos que ocurrieron antes de que el usuario instalara la app con el SDK de Adapty. Esto evita la duplicación de eventos y garantiza un reporting preciso. Por ejemplo, si un usuario activó una suscripción mensual el 10 de enero y actualizó la app con el SDK de Adapty el 6 de marzo, Adapty omitirá los eventos anteriores al 6 de marzo y conservará los eventos posteriores. | | **Send user attributes** | Activa esta opción para enviar atributos específicos del usuario, como las preferencias de idioma. Estos atributos aparecerán en el campo `user_attributes`. Consulta [Campos del evento](webhook-event-types-and-fields#event-fields) para más información. | | **Send attribution** | Activa esta opción para incluir información de atribución (por ejemplo, datos de AppsFlyer) en el campo `attributions`. Consulta la sección [Datos de atribución](webhook-event-types-and-fields#attributions) para más detalles. | | **Send Play Store purchase token** | Activa esta opción para recibir el token de Play Store necesario para la revalidación de compras, si es necesario. Al activarlo, se añadirá el parámetro `play_store_purchase_token` al evento. Para más detalles sobre su contenido, consulta la sección [Token de compra de Play Store](webhook-event-types-and-fields#play-store-purchase-token). | 6. Recuerda hacer clic en el botón **Save** para confirmar los cambios. En el momento en que hagas clic en el botón **Save**, Adapty enviará una solicitud de verificación y esperará la respuesta de verificación de tu servidor. ### Elegir los eventos a enviar y mapear los nombres de los eventos \{#choose-events-to-send-and-map-event-names\} Elige los eventos que quieres recibir en tu servidor activando el interruptor correspondiente. Si los nombres de tus eventos difieren de los usados en Adapty y necesitas mantener tus nombres tal como están, puedes configurar el mapeo reemplazando los nombres de eventos predeterminados de Adapty con los tuyos en la sección **Events names** de la página [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook). <img src="/assets/shared/img/86942b8-event_names_renaming.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> El nombre del evento puede ser cualquier cadena. No puedes dejar los campos vacíos para los eventos habilitados. Si eliminaste accidentalmente el nombre de un evento de Adapty, siempre puedes copiarlo desde el artículo [Eventos para enviar a integraciones de terceros](events). ## Gestionar los eventos de webhook \{#handle-webhook-events\} Los webhooks suelen entregarse entre 5 y 60 segundos después de que ocurre el evento. Sin embargo, los eventos de cancelación pueden tardar hasta 2 horas en entregarse después de que un usuario cancele su suscripción. Si el código de estado de respuesta de tu servidor está fuera del rango 200-404, Adapty reintentará enviar el evento hasta 9 veces en 24 horas con retroceso exponencial. Te recomendamos configurar tu webhook para realizar solo una validación básica del cuerpo del evento de Adapty antes de responder. Si tu servidor no puede procesar el evento y no quieres que Adapty reintente, usa un código de estado dentro del rango 200-404. Además, gestiona cualquier tarea que consuma tiempo de forma asíncrona y responde a Adapty rápidamente. Si Adapty no recibe una respuesta en 10 segundos, considerará el intento como fallido y reintentará. --- # File: test-webhook --- --- title: "Probar la integración de webhook" description: "Prueba las integraciones de webhook en Adapty para automatizar el seguimiento de eventos de suscripción." --- Una vez que hayas configurado tu integración, es momento de probarla. Puedes probar tanto la integración en sandbox como la de producción. Te recomendamos empezar con la de sandbox y validar al máximo en ella: - Los eventos se envían y se entregan correctamente. - Configuraste correctamente las opciones para eventos históricos, el precio de la suscripción para el evento **Trial started**, la atribución, los atributos de usuario y el token de compra de Google Play Store para que se envíen o no con un evento. - Mapeaste los nombres de los eventos correctamente y tu servidor puede procesarlos. ## Cómo probar \{#how-to-test\} Antes de empezar a probar una integración, asegúrate de haber: 1. Configurado la integración de webhook según se describe en el tema [Configurar la integración de webhook](set-up-webhook-integration). 2. Configurado el entorno según se describe en los temas [Probar compras in-app en Apple App Store](test-purchases-in-sandbox) y [Probar compras in-app en Google Play Store](testing-on-android). Asegúrate de haber compilado tu app de prueba en el entorno sandbox y no en producción. 3. Realizado una compra / iniciado una prueba / solicitado un reembolso que genere el evento que elegiste enviar al webhook. Por ejemplo, para obtener el evento **Subscription started**, realiza una nueva suscripción. ## Validación del resultado \{#validation-of-the-result\} ### Resultado exitoso al enviar eventos \{#successful-sending-events-result\} En caso de una integración exitosa, el evento aparecerá en la sección **Last sent events** de la integración y tendrá el estado **Success**. <img src="/assets/shared/img/6ccc3bb-webhook_integration_success.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Resultado fallido al enviar eventos \{#unsuccessful-sending-events-result\} | Problema | Solución | |-----|--------| | El evento no apareció | Tu compra no se realizó y, por lo tanto, el evento no se creó. Consulta el tema [Solución de problemas con compras de prueba](troubleshooting-test-purchases) para encontrar la solución. | | El evento apareció y tiene el estado **Sending failed** | <p>Determinamos la entregabilidad en función del estado HTTP y consideramos todo lo que esté **fuera del rango 200-399** como un fallo.</p><p>Para obtener más información sobre el problema, pasa el cursor sobre el estado **Sending failed** de tu evento fallido como se muestra a continuación.</p> | <img src="/assets/shared/img/12ff189-hover_sending_failed.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: handle-integration-errors --- --- title: "Gestionar errores en integraciones" description: "Gestionar errores en integraciones" --- Al usar cualquier integración de atribución, mensajería o analíticas, puede que encuentres algunos errores comunes. Consulta esta guía para los casos de solución de problemas. ## Discrepancia de datos \{#data-discrepancy\} **Motivo**: Esto puede ocurrir porque no todos tus usuarios usan la versión de la app que tiene el SDK de Adapty. **Solución**: Para garantizar la consistencia de los datos, puedes obligar a tus usuarios a actualizar la app a una versión con el SDK de Adapty. ## Errores de red \{#network-errors\} **Motivo**: Lo más probable es que no haya habido conexión a internet entre el servidor de Adapty y el servidor de la integración. **Solución**: Estos problemas normalmente no duran mucho y solo afectan a un pequeño número de eventos. ## El servidor de integración no pudo procesar el evento \{#integration-server-failed-to-process-the-event\} **Motivo**: La integración está configurada incorrectamente. **Solución**: Consulta el artículo sobre la integración en nuestra documentación. Asegúrate de haber completado todos los pasos de configuración tanto en el Adapty Dashboard, como en el lado de la herramienta de terceros y en el código de tu app. ## Datos de integración faltantes \{#missing-integration-data\} **Motivo**: Al perfil le falta algún ID específico de la integración. Esto puede ocurrir cuando la integración no está configurada correctamente en el código de la app. **Solución**: Consulta el artículo sobre la integración en nuestra documentación. Asegúrate de haber implementado los métodos de los fragmentos de código en el código de tu app, y de que estos métodos interactúen realmente con los perfiles de tus usuarios. ## Credenciales de integración faltantes \{#missing-integration-credentials\} **Motivo**: Faltan algunas credenciales de integración o son incorrectas. **Solución**: Comprueba todas las credenciales de esa integración en el Adapty Dashboard. El problema puede deberse a un desajuste de versión o de entorno. ## El evento ha expirado \{#the-event-has-expired\} **Motivo**: La opción **Exclude historical events** está habilitada en la configuración de la integración, y la fecha de creación del evento es anterior a la fecha de creación del perfil en nuestro sistema. Esto puede ocurrir si una cadena de transacciones que comenzó hace muchos años llega a Adapty mediante la validación de recibos para un perfil creado recientemente. **Solución**: Asegúrate de que esto no ocurra con nuevos eventos. Si quieres enviar eventos históricos a la integración, desactiva **Exclude historical events**. ## Tipo de evento desactivado/no compatible \{#disabledunsupported-event-type\} **Motivo**: El evento no está soportado por esta integración, o lo desactivaste al configurarla. Por ejemplo, los eventos `access_level_updated` no están soportados por la mayoría de las integraciones. **Solución**: Consulta en la documentación de la integración si esta soporta ese tipo de evento. Si es así, en el Adapty Dashboard, asegúrate de que ese tipo de evento esté habilitado en la configuración de la integración. --- # File: test-purchases-in-sandbox --- --- title: "Pruebas en sandbox" description: "Prueba compras en el entorno sandbox para garantizar transacciones sin problemas." --- Una vez que hayas configurado todo en el Adapty Dashboard y en tu aplicación móvil, es hora de realizar pruebas de compras in-app. **Nota:** ninguna de las herramientas de prueba cobra a los usuarios cuando prueban la compra de un producto. App Store no envía correos electrónicos por compras o reembolsos realizados en entornos de prueba. --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="note"> **Las transacciones en sandbox se excluyen de todos los gráficos de análisis.** Siguen apareciendo en las páginas de perfil individuales y en el feed de eventos. </Callout> :::info Para proceder con las pruebas de compras in-app, asegúrate de: - Haber completado las guías de [inicio rápido](quickstart) sobre integración con el store, añadir productos e integración del SDK de Adapty. - Que tu producto esté marcado como [**Ready to submit**](InvalidProductIdentifiers#step-2-check-products) en App Store Connect. ::: ## Pruebas en sandbox \{#sandbox-testing\} <div style={{ maxWidth: '560px', margin: '0 auto 2rem', position: 'relative', aspectRatio: '16/9', width: '100%' }}> <iframe style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} src="https://www.youtube.com/embed/hq4PRU-vuik?si=m5F5Sj6iLEJ-2q6n" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen /> </div> :::info Recomendamos probar las compras in-app con un dispositivo real. Aunque las compras en sandbox pueden ejecutarse en simuladores, los dispositivos reales son necesarios para probar todos los flujos en su totalidad, incluidos los diálogos de pago y las solicitudes biométricas. ::: Tienes dos formas principales de probar las compras in-app: - **Compilar en Xcode y ejecutar en un dispositivo de prueba**: conveniente para desarrolladores y QA engineers. - **Usar una cuenta de prueba sandbox con TestFlight**: adecuado para cualquier otra persona. Ambas opciones se explican en la guía a continuación. ### Paso 1. Crear una cuenta de prueba Sandbox en App Store Connect \{#step-1-create-sandbox-test-account-in-app-store-connect\} :::warning Crea una nueva cuenta de prueba Sandbox para asegurarte de que tu historial de compras esté limpio. Si reutilizas una cuenta existente, los productos comprados anteriormente seguirán disponibles y no podrás probar comprarlos de nuevo. ::: Puedes crear una nueva cuenta de prueba Sandbox en pocos clics: 1. Ve a [**Users and Access** > **Sandbox** > **Test Accounts**](https://appstoreconnect.apple.com/access/users/sandbox) en App Store Connect y haz clic en **+**. <img src="/assets/shared/img/add-sandbox-user.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Introduce los datos del usuario de prueba. Asegúrate de definir el **Country or Region** que planeas probar, ya que afecta la disponibilidad del producto para la región y la moneda de compra. :::tip - Si usas Gmail o iCloud, puedes reutilizar tu dirección de correo existente con el [subdireccionamiento con signo más](https://www.wikihow.com/Use-Plus-Addressing-in-Gmail). - Puedes usar una dirección de correo aleatoria que ni siquiera exista, pero asegúrate de rechazar la autenticación de dos factores (2FA) cuando inicies sesión en un dispositivo de prueba más adelante. ::: <img src="/assets/shared/img/57c3a7c-apple_new_test_account.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Create**. ### Paso 2. Activar el modo Desarrollador \{#step-2-enable-the-developer-mode\} :::note Omite este paso si el modo Desarrollador ya está **activado** en tu dispositivo de prueba o si **no tienes un dispositivo Mac**. ::: Necesitarás un Mac con Xcode instalado y el cable de tu dispositivo de prueba: 1. Abre Xcode en tu Mac. Si vas a probar las compras in-app con TestFlight, solo necesitas tener XCode instalado; no es necesario tener una app ahí. 2. Conecta tu dispositivo de prueba al Mac mediante el cable. 3. Ve a **Settings > Privacy & Security > Developer Mode** en tu dispositivo de prueba y activa el **Developer Mode**. ### Paso 3. Descargar la app desde TestFlight \{#step-3-download-the-app-from-testflight\} :::info Este paso solo aplica si estás probando con TestFlight. Si estás compilando la app en Xcode, omite este paso. ::: Para más detalles sobre cómo enviar tu app a TestFlight, consulta la [documentación de Apple](https://developer.apple.com/documentation/StoreKit/testing-in-app-purchases-with-sandbox#Prepare-for-sandbox-testing). Antes de descargar la app de TestFlight, en tu dispositivo de prueba, asegúrate de haber iniciado sesión con tu Apple Account de producción. Luego descarga la app que vayas a probar desde TestFlight. :::danger No abras la app una vez descargada. Continúa directamente con los siguientes pasos. Si la abres accidentalmente, elimínala de tu dispositivo de prueba y descárgala de nuevo. De lo contrario, tu historial de compras puede no estar limpio y las pruebas de compras in-app generarán errores. ::: ### Paso 4. Cambiar a la cuenta de prueba Sandbox \{#step-4-switch-to-sandbox-test-account\} <Details> <summary>¿No usas Mac? Aquí tienes una alternativa</summary> Si no trabajas en macOS, no puedes cambiar a una cuenta sandbox desde Xcode. Sin embargo, puedes hacerlo directamente en tu dispositivo de prueba: 1. Ve a **Settings > Your Apple Account > Media & Purchases** en tu dispositivo de prueba. 2. Selecciona **Sign Out** en el menú emergente. 3. Abre la app descargada desde TestFlight e intenta comprar un producto. 4. Cuando se te pida iniciar sesión, introduce las credenciales de tu cuenta sandbox para cambiar al entorno sandbox. </Details> Para cambiar a tu cuenta sandbox: 1. Ve a **Settings > Your Apple Account > Media & Purchases** en tu dispositivo de prueba. 2. Selecciona **Sign Out** en el menú emergente. 3. Ve a **Settings > Developer**. Si la opción **Developer** no está disponible, asegúrate de haberla [activado en el paso 2](#step-2-enable-the-developer-mode). <img src="/assets/shared/img/devmode.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hacia abajo hasta la sección **Sandbox Apple Account** y toca **Sign In**. <img src="/assets/shared/img/sandbox-acc.png" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Inicia sesión con las credenciales de tu Sandbox Apple Account. ### Paso 5. Borrar el historial de compras \{#step-5-clear-purchase-history\} Si acabas de crear una nueva cuenta de prueba Sandbox y has cambiado a ella, puedes omitir este paso, ya que solo aplica cuando repites pruebas con la misma cuenta de prueba Sandbox. 1. Ve a **Settings > Developer > Sandbox Apple Account** en tu dispositivo de prueba. 2. Selecciona **Manage** en el menú emergente. 3. Ve a **Account Settings** y toca **Clear Purchase History**. :::danger Este paso es necesario cada vez que repitas las pruebas con la misma cuenta de prueba Sandbox. En ese caso, también deberás [cerrar sesión de tu cuenta de prueba Sandbox](#step-4-switch-to-sandbox-test-account) y volver a iniciarla para limpiar la caché del historial de compras en el dispositivo de prueba. ::: ### Paso 6. Compilar en Xcode y ejecutar \{#step-6-build-in-xcode-and-run\} :::info Este paso solo aplica si estás probando con una compilación de Xcode. Si usas TestFlight, omite este paso. ::: 1. Conecta tu dispositivo de prueba al Mac. 2. Abre Xcode. 3. Haz clic en **Run** en la barra de herramientas o elige **Product > Run** para compilar y ejecutar la app en el dispositivo conectado. Si la compilación es exitosa, Xcode lanzará la app en tu dispositivo y abrirá una sesión de depuración en el área de debug. Tu app ya está lista para pruebas en el dispositivo. ### Paso 7. Realizar una compra de prueba \{#step-7-make-test-purchase\} Abre la app y realiza tu compra de prueba a través de un paywall. Una vez hecho, ve al artículo sobre [validar compras de prueba](validate-test-purchases) para revisar los resultados. ### Paso 8. Seguir probando \{#step-8-keep-testing\} Ahora tu entorno de pruebas está listo. Si quieres volver a probar, [borra el historial de compras de la cuenta sandbox](https://developer.apple.com/help/app-store-connect/test-in-app-purchases/manage-sandbox-apple-account-settings/). ## Problemas de prueba \{#testing-issues\} A continuación se describen los problemas más comunes que puedes encontrar al probar una app. ### Problemas con TestFlight \{#testflight-issues\} No puedes borrar tu historial de compras **si usas TestFlight sin la cuenta de prueba Sandbox**, lo que genera varios problemas y resultados de prueba incorrectos. Si olvidaste accidentalmente [cambiar a la cuenta de prueba Sandbox](#step-4-switch-to-sandbox-test-account) y abriste la app aunque sea una vez, TestFlight asocia tu historial de compras a tu Apple Account de producción, lo que genera problemas inesperados. Para solucionarlo, sigue estos pasos: 1. Elimina la app del dispositivo de prueba. 2. Sigue los pasos para las [pruebas en sandbox](#sandbox-testing). :::note Es importante no solo reinstalar la app, sino también cambiar a la cuenta de prueba Sandbox, borrar el historial de compras y lanzarla con la cuenta de prueba Sandbox. ::: ### Problemas con niveles de acceso compartidos \{#shared-access-levels-issues\} Si repites las pruebas con la misma cuenta de prueba Sandbox, puedes encontrar comportamiento inesperado con los [niveles de acceso compartidos](sharing-paid-access-between-user-accounts) para el usuario de prueba. Para verificar si el usuario tiene un nivel de acceso heredado, ve a [Profiles & Segments](https://app.adapty.io/profiles/users) desde el Adapty Dashboard y abre el perfil del usuario. <img src="/assets/shared/img/profile-access-level-origin.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Si el usuario tiene un nivel de acceso heredado, sigue estos pasos para obtener resultados de prueba precisos: 1. Elimina el perfil principal. 2. Elimina la app del dispositivo de prueba. 3. [Descarga la app desde TestFlight](#step-3-download-the-app-from-testflight). 4. [Cambia a la cuenta de prueba Sandbox](#step-4-switch-to-sandbox-test-account). 5. [Borra el historial de compras](#step-5-clear-purchase-history). 6. [Abre la app y realiza tu compra de prueba](#step-6-make-test-purchase). ### Actualización de la app en TestFlight \{#updating-app-in-testflight\} Si la app de TestFlight ha sido actualizada: 1. Elimina la app del dispositivo de prueba. 2. [Descarga la app desde TestFlight](#step-3-download-the-app-from-testflight). 3. [Cambia a la cuenta de prueba Sandbox](#step-4-switch-to-sandbox-test-account). 4. [Borra el historial de compras](#step-5-clear-purchase-history). 5. [Abre la app y realiza tu compra de prueba](#step-6-make-test-purchase). ### Autorización durante el proceso de compra \{#authorization-during-the-purchase-process\} Si has descargado la app de TestFlight y no has [iniciado sesión en la cuenta sandbox desde la configuración del dispositivo](#step-4-switch-to-sandbox-test-account), intentar hacerlo durante el proceso de compra no funcionará. Para que la compra sea exitosa, debes iniciar sesión en tu cuenta sandbox desde la configuración del dispositivo antes de intentar realizar una compra. ## Suscripciones de prueba \{#test-subscriptions\} Al probar la app con la cuenta de prueba Sandbox, puedes configurar la tasa de renovación de la suscripción para cada tester en sandbox. Consulta más información sobre la edición de las tasas de renovación de suscripciones en la [documentación oficial de Apple](https://developer.apple.com/help/app-store-connect/test-in-app-purchases/manage-sandbox-apple-account-settings). De forma predeterminada, las suscripciones se renuevan hasta 12 veces antes de detenerse, según el siguiente calendario: | Duración de la suscripción | 1 semana | 1 mes | 2 meses | 3 meses | 6 meses | 1 año | | :---------------------------------- | :--------- | :--------- | :--------- | :--------- | :--------- | :--------- | | Velocidad de renovación | 3 minutos | 5 minutos | 10 minutos | 15 minutos | 30 minutos | 1 hora | | Duración del reintento de cobro | 10 minutos | 10 minutos | 10 minutos | 10 minutos | 10 minutos | 10 minutos | | Duración del período de gracia | 3 minutos | 5 minutos | 5 minutos | 5 minutos | 5 minutos | 5 minutos | :::note Ten en cuenta que las transacciones de prueba pueden tardar hasta 10 minutos en aparecer en el [Event feed](validate-test-purchases). ::: ## Ofertas de prueba \{#test-offers\} Para probar ofertas es necesario que todos los recibos del usuario estén eliminados para que la elegibilidad funcione correctamente. La forma más fiable de probar ofertas es usar una [cuenta de prueba Sandbox](#step-1-create-sandbox-test-account-in-app-store-connect) completamente nueva. Repetir las pruebas con la misma cuenta de prueba Sandbox puede causar comportamiento inesperado. :::danger Si repites las pruebas con la misma cuenta de prueba Sandbox, asegúrate de [borrar el historial de compras](#step-5-clear-purchase-history) para evitar problemas relacionados con la elegibilidad. ::: --- # File: local-sk-files --- --- title: "Pruebas con StoreKit en Xcode" description: "Prueba compras en el entorno sandbox para garantizar transacciones fluidas." --- Las pruebas con StoreKit en Xcode te permiten probar compras in-app de forma local sin necesidad de configurar una cuenta sandbox. Para este tipo de pruebas, necesitas: 1. [Crear un producto en Adapty](quickstart-products) y asignarle un **App Store product ID**. 2. En Xcode, crea un [archivo de configuración de StoreKit](https://developer.apple.com/documentation/xcode/setting-up-storekit-testing-in-xcode) local y añade un producto. El ID del producto debe coincidir con el **App Store product ID** en Adapty. 3. Añade el archivo de configuración de StoreKit a tu esquema de compilación y compila la app. Ejecútala en el emulador o en tu dispositivo. ## ¿Debería usar las pruebas con StoreKit en Xcode? \{#should-i-use-storekit-testing-in-xcode\} Esta forma de probar es la más cómoda si eres desarrollador de la app y quieres probar la compilación sobre la marcha o reproducir distintos escenarios de compra usando las herramientas de Xcode. Sin embargo, ten en cuenta que este tipo de pruebas es local, por lo que ningún cambio aparecerá en el Adapty Dashboard. Antes de lanzar tu app en producción, te recomendamos que pruebes el [trabajo con perfiles](ios-quickstart-identify) usando el [entorno sandbox](test-purchases-in-sandbox). **Deberías** usar las pruebas con StoreKit si quieres: - Probar la lógica de compra - Reproducir distintos escenarios de compra con las herramientas de Xcode (por ejemplo, pago cancelado o reembolso) - Probar usando el emulador **No deberías** usar las pruebas con StoreKit si quieres: - Probar la lógica relacionada con perfiles - Verificar que tus acciones en la app aparecen en el Adapty Dashboard - Compartir tu app con equipos que no sean de desarrollo para pruebas ## Paso 1. Crea un archivo de configuración de StoreKit \{#step-1-create-a-storekit-configuration-file\} Para crear un archivo de configuración de StoreKit en Xcode: 1. Haz clic en **File > New > File from template**. Luego selecciona **StoreKit Configuration File** y haz clic en **Next**. 2. Ponle un nombre. Luego, dependiendo de si ya tienes los productos en App Store Connect: - Selecciona **Sync this file with an app in App Store Connect**: para crear un archivo de configuración que contendrá todos tus productos de App Store Connect y poder probarlos localmente. - No selecciones **Sync this file with an app in App Store Connect**: para crear un archivo de configuración vacío donde tendrás que añadir los productos manualmente. Haz clic en **Next**. 3. No añadas tu app como destino. Continúa. Si estás trabajando con productos sincronizados desde App Store Connect, ve al [Paso 2](#step-2-add-the-configuration-file-to-the-build-scheme). 4. Si tus productos no están sincronizados desde App Store Connect, haz clic en **+** en la parte inferior izquierda y selecciona un tipo de producto. 5. Introduce un nombre para el grupo de suscripción y haz clic en **Next**. 6. Introduce un nombre de referencia. En el campo **Product ID**, introduce el **App Store product ID** de tu producto en Adapty. 7. Configura el precio, las ofertas y otros ajustes del producto en el archivo de configuración. O añade más productos. ## Paso 2. Añade el archivo de configuración al esquema de compilación \{#step-2-add-the-configuration-file-to-the-build-scheme\} Para compilar la app usando este archivo de configuración, necesitas añadirlo a un esquema de compilación. La buena práctica es separar los esquemas de prueba y de producción, así que te sugerimos crear un nuevo esquema para pruebas: 1. En la parte superior, haz clic en el nombre de tu app y selecciona **New scheme**. 2. Introduce un nombre para el esquema y haz clic en **OK**. 3. Haz clic de nuevo en el nombre de la app y selecciona **Edit scheme**. En **StoreKit configuration**, selecciona tu archivo de configuración local para que se use al compilar. ## Paso 3. Compila y prueba \{#step-3-build--test\} Ahora puedes compilar la app y probar compras in-app sin conectarte al backend de App Store. Puedes realizar compras y obtener niveles de acceso de forma local. Estos cambios no se reflejarán en el Adapty Dashboard, pero aun así puedes probar el desbloqueo de funciones de pago localmente. [Lee más](https://developer.apple.com/documentation/xcode/testing-in-app-purchases-with-storekit-transaction-manager-in-code) sobre otras funciones disponibles con las pruebas de StoreKit en Xcode. --- # File: testing-on-android --- --- title: "Probar compras in-app en Google Play Store" description: "Prueba compras de suscripción en Android usando Adapty." --- Probar las compras in-app (IAPs) en tu app Android es un paso fundamental antes de publicarla. Las pruebas en sandbox son una forma segura y eficiente de probar IAPs sin cobrar dinero real a tus usuarios. En esta guía, te explicamos el proceso de pruebas en sandbox de IAPs en Google Play Store para Android. --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="note"> **Las transacciones en sandbox se excluyen de todos los gráficos de análisis.** Siguen apareciendo en las páginas de perfil individuales y en el feed de eventos. </Callout> ## Entorno de pruebas \{#testing-environment\} Para garantizar el rendimiento óptimo de tu app Android, se recomienda probarla en un dispositivo real en lugar de un emulador. Aunque hemos probado con éxito en emuladores, Google recomienda usar un dispositivo real. Si decides usar un emulador, asegúrate de que tenga Google Play instalado. Esto ayudará a garantizar que tu app funcione correctamente. ## 1. Configura una cuenta de prueba para probar la app \{#1-set-up-test-account-for-app-testing\} Para facilitar las pruebas en fases posteriores del desarrollo, necesitarás configurar un usuario de prueba para las pruebas de compras in-app. Este usuario será la primera cuenta con la que inicies sesión en tu dispositivo Android de pruebas. Ten en cuenta que la cuenta principal de un dispositivo Android solo puede cambiarse realizando un restablecimiento de fábrica, lo que borra todos tus datos. Por eso es importante configurar correctamente tu cuenta de usuario de prueba para evitar tener que hacer un restablecimiento. :::important La forma de configurar una cuenta de prueba dependerá del dispositivo que uses: - Si tienes un dispositivo dedicado para pruebas, crea una **cuenta de prueba separada (una nueva cuenta de Gmail)**. - Si no tienes un dispositivo dedicado para pruebas, puedes usar tu propia **cuenta personal** y activar temporalmente las **License testing** para ella. - Si no tienes ningún dispositivo Android, puedes **crear una cuenta de prueba separada y usarla con un emulador**. Sin embargo, este enfoque no se recomienda ya que no permite detectar todos los posibles problemas de dispositivos reales. ::: ## 2. Activa License testing \{#2-enable-license-testing\} Una vez que hayas configurado una cuenta de usuario de prueba, deberás configurar las pruebas de licencias para tu app. Para hacerlo, sigue estos pasos: 1. En la barra lateral de Google Play Console, ve a **Settings** y selecciona **License testing** en la sección **Monetization**. <img src="/assets/shared/img/android-license-testing.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Selecciona una lista de probadores de licencias existente o crea una nueva. <img src="/assets/shared/img/android-testers.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Añade la cuenta que usarás para las pruebas a la lista y guarda los cambios. Si los miembros de tu equipo también necesitan probar la app, puedes añadir sus correos electrónicos a la lista para que todo el grupo tenga acceso. <img src="/assets/shared/img/android-list.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## 3. Crea una pista cerrada y añade la cuenta de prueba \{#3-create-closed-track-and-add-test-account-to-it\} Para empezar a probar, necesitas publicar una versión firmada de tu app en una pista cerrada: 1. Abre tu app y selecciona **Test and release > Testing > Closed testing** en el menú. Allí, haz clic en **Create track**. <img src="/assets/shared/img/android-closed-testing.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Introduce el nombre de la pista de pruebas cerrada y haz clic en **Create track**. 3. Añade una lista de probadores a la pista. 4. En la sección **How testers join your test**, copia el enlace y envíalo al dispositivo que ha iniciado sesión con la cuenta de prueba. Abre el enlace en tu dispositivo de pruebas para convertir al usuario en probador. <img src="/assets/shared/img/android-link.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning Ten en cuenta lo siguiente para garantizar que las pruebas funcionen correctamente: - Abrir la URL de participación marca tu cuenta de Play para pruebas. Si no completas este paso, los productos no se cargarán. - Con frecuencia, los desarrolladores usan un ID de aplicación diferente para sus versiones de prueba. Esto puede causarte problemas, ya que Google Play Services utiliza el ID de aplicación para encontrar tus compras in-app. - Hay casos en los que un usuario de prueba puede estar autorizado a comprar consumibles pero no suscripciones si el dispositivo de prueba no tiene PIN. Esto puede manifestarse con un críptico mensaje de "Something went wrong". Asegúrate de que el dispositivo de prueba tenga un PIN y de que esté conectado a Google Play Store. ::: ## 4. Sube un APK firmado a la pista cerrada \{#4-upload-a-signed-apk-to-the-closed-track\} Genera un APK firmado o usa Android App Bundle para subir un APK firmado a la pista cerrada que acabas de crear. Ni siquiera necesitas lanzar el release. Solo sube el APK. Puedes encontrar más información al respecto en [este](https://support.google.com/googleplay/android-developer/answer/9859348?visit_id=638929100639477968-3849460621&rd=1) artículo de soporte. :::important Si tu app es nueva, es posible que tengas que hacerla disponible en tu país o región. Para ello, ve a **Testing > Closed testing**, haz clic en tu pista de pruebas y ve a **Countries/regions** para añadir los países y regiones deseados. ::: ## 5. Prueba las compras in-app \{#5-test-in-app-purchases\} Después de subir el APK, espera unos minutos para que se procese el release. Luego, abre tu dispositivo de pruebas e inicia sesión con la cuenta de correo electrónico que añadiste a la lista de probadores. A continuación, podrás probar las compras in-app como lo harías en una app de producción. <img src="/assets/shared/img/a8d2da9-image.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Más información \{#read-more\} Consulta los siguientes recursos para saber más sobre cómo probar compras in-app en apps Android: - [Períodos de renovación en sandbox](https://developer.android.com/google/play/billing/test#subs) - [Probar compras únicas](https://developer.android.com/google/play/billing/test#one-time) --- # File: validate-test-purchases --- --- title: "Validar compras de prueba" description: "Valida las compras de prueba en Adapty para garantizar transacciones sin problemas." --- Antes de publicar tu app en producción, es fundamental probar las compras in-app a fondo. Consulta nuestros artículos [Probar compras in-app en Apple App Store](test-purchases-in-sandbox) y [Probar compras in-app en Google Play Store](testing-on-android) para obtener una guía detallada sobre cómo hacerlo. Una vez que empieces a probar, necesitas verificar que las compras de prueba se hayan completado correctamente. Cada vez que realices una compra de prueba en tu dispositivo móvil, comprueba la transacción correspondiente en el [**Event Feed**](https://app.adapty.io/event-feed) del Adapty Dashboard. Si la compra no aparece en el **Event Feed**, significa que Adapty no la está registrando. ## La compra de prueba es exitosa \{#test-purchase-is-successful\} Si la compra de prueba se realiza correctamente, el evento de transacción aparecerá en el **Event Feed**: <img src="/assets/shared/img/9ade2d5-event_feed_sandbox.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Si las transacciones funcionan como se espera, pasa al [Release checklist](release-checklist) y continúa con la publicación de la app. ## La compra de prueba no es exitosa \{#test-purchase-is-not-successful\} Si no ves ningún evento de transacción en 10 minutos o encuentras un error en la app, consulta el artículo de [Solución de problemas](troubleshooting-test-purchases) y los artículos sobre manejo de errores [para iOS](ios-sdk-error-handling), [para Android](android-sdk-error-handling), [para React Native](react-native-handle-errors), [para Flutter](error-handling-on-flutter-react-native-unity), [para Unity](unity-handle-errors) y [Kotlin Multiplatform](kmp-handle-errors) para encontrar posibles soluciones. <img src="/assets/shared/img/31a79b2-no_events.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- # File: troubleshooting-test-purchases --- --- title: "Solución de problemas con compras de prueba" description: "Soluciona problemas con compras de prueba en Adapty y resuelve incidencias comunes en transacciones in-app." --- Si encuentras problemas con las transacciones, asegúrate primero de haber completado todos los pasos del [checklist de lanzamiento](release-checklist). Si ya los completaste y sigues teniendo problemas, sigue las indicaciones a continuación para resolverlos: ## Se devuelve un error en la app móvil \{#an-error-is-returned-in-the-mobile-app\} Consulta la lista de errores de tu plataforma: [para iOS](ios-sdk-error-handling), [para Android](android-sdk-error-handling), [para React Native](react-native-troubleshoot-purchases), [Flutter](error-handling-on-flutter-react-native-unity) y [Unity](unity-troubleshoot-purchases), y sigue nuestras recomendaciones para resolver el problema. ## La transacción no aparece en el Event Feed aunque no se devuelve ningún error en la app móvil \{#transaction-is-absent-from-the-event-feed-although-no-error-is-returned-in-the-mobile-app\} Para resolver este problema, verifica lo siguiente: 1. **Para iOS**: Asegúrate de usar un dispositivo real y no un simulador. 2. Asegúrate de que el `Bundle ID`/`Package name` de tu app coincida con el que figura en [**App settings**](https://app.adapty.io/settings/general). 3. Asegúrate de que el `PUBLIC_SDK_KEY` de tu app coincida con la **Public SDK key** del Adapty Dashboard: [**App settings** -> pestaña **General** -> subsección **API keys**](https://app.adapty.io/settings/general). 4. Asegúrate de estar usando una cuenta sandbox y no un [archivo de configuración local de StoreKit](local-sk-files). Si antes usaste un archivo de configuración local de StoreKit para pruebas, verifica que no lo estés usando en la compilación actual. ## No hay ningún evento en mi perfil de prueba \{#no-event-is-present-in-my-testing-profile\} Esto es un comportamiento normal. En Adapty se crea automáticamente un nuevo registro de perfil de usuario cuando: - Un usuario ejecuta tu app por primera vez - Un usuario cierra sesión en tu app **Por qué ocurre:** Todas las transacciones y eventos están vinculados al perfil que generó la primera transacción. Esto mantiene todo el historial de transacciones (pruebas, compras, renovaciones) asociado al mismo perfil. **Lo que verás:** Puede que aparezcan nuevos registros de perfil (llamados "perfiles no originales") sin eventos, pero conservarán los niveles de acceso. Es posible que veas eventos `access_level_updated`. Esto es un comportamiento esperado. **Para pruebas:** Para evitar la creación de múltiples perfiles, crea una nueva cuenta de prueba (Sandbox Apple ID) cada vez que reinstales la app. Para más detalles, consulta [Creación de perfiles](how-profiles-work#profile-creation). A continuación se muestra un ejemplo de perfil no original. Observa la ausencia de eventos en **User history** y la presencia de un nivel de acceso. <img src="/assets/shared/img/98d0dad-non-original_profile.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Los precios no reflejan los precios reales configurados en App Store Connect \{#prices-do-not-reflect-the-actual-prices-set-in-app-store-connect\} Tanto en Sandbox como en TestFlight, que usa el entorno sandbox para las compras in-app, lo importante es verificar que el flujo de compra funcione correctamente, no que los precios sean exactos. Cabe destacar que la API de Apple puede proporcionar datos inexactos en ocasiones, especialmente cuando los dispositivos o cuentas tienen distintas regiones configuradas. Como los precios provienen directamente del Store y el backend de Adapty no afecta en ningún modo a los precios de compra, puedes ignorar cualquier inexactitud en los precios durante las pruebas de compras a través de Adapty. Por tanto, prioriza la prueba del flujo de compra en sí sobre la exactitud de los precios para asegurarte de que funciona como se espera. ## La hora de la transacción en el Event Feed es incorrecta \{#the-transaction-time-in-the-event-feed-is-incorrect\} El **Event Feed** utiliza la zona horaria configurada en **App Settings**. Para alinear la zona horaria de los eventos con tu hora local, ajusta la **Reporting timezone** en [**App settings** -> pestaña **General**](https://app.adapty.io/settings/general). ## Los paywalls y los productos tardan mucho en cargarse \{#paywalls-and-products-take-a-long-time-to-load\} Este problema puede ocurrir si tu cuenta de prueba tiene un historial de transacciones muy largo. Te recomendamos encarecidamente crear una nueva cuenta de prueba cada vez, tal como se describe en la sección [Crear una cuenta de prueba en Sandbox (Sandbox Apple ID) en App Store Connect](test-purchases-in-sandbox#step-1-create-sandbox-test-account-in-app-store-connect). Si no puedes crear una cuenta nueva, puedes borrar el historial de transacciones de tu cuenta actual siguiendo estos pasos en tu dispositivo iOS: 1. Abre **Configuración** y toca **App Store**. 2. Toca tu **Sandbox Apple ID**. 3. En el popup, selecciona **Manage**. 4. En la página **Account Settings**, toca **Clear Purchase History**. Para más detalles, consulta la [documentación para desarrolladores de Apple](https://developer.apple.com/documentation/storekit/testing-in-app-purchases-with-sandbox). --- # File: test-devices --- --- title: "Dispositivos de prueba" description: "Aprende a gestionar los dispositivos de prueba en Adapty para un testing eficiente." --- Para hacer pruebas, puedes marcar tu dispositivo como dispositivo de prueba, lo que desactiva el caché y garantiza que los cambios se reflejen de inmediato. :::note Los dispositivos de prueba están disponibles a partir de las siguientes versiones del SDK: - iOS: 2.11.1 - Android: 2.11.3 - React Native: 2.11.1 La compatibilidad con Flutter y Unity se añadirá más adelante. ::: ## Marca tu dispositivo como dispositivo de prueba \{#mark-your-device-as-test\} 1. Abre **[App settings](https://app.adapty.io/settings/general)** en el Adapty Dashboard. 2. Desplázate hacia abajo hasta la sección **Test devices** en la pestaña **General**. <img src="/assets/shared/img/14c581d-test_device_add.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en el botón **Add test device**. <img src="/assets/shared/img/f86d5e2-test_users_add_device.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En la ventana **Add test device**, introduce: | Campo | Descripción | |:-----------------------------------------| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Test device name** | Nombre del dispositivo o dispositivos de prueba para tu referencia. | | **ID used to identify this test device** | Elige el tipo de identificador que usarás para identificar el dispositivo o dispositivos de prueba. Consulta nuestras recomendaciones en la sección [Qué identificador deberías usar](test-devices#which-identifier-you-should-use) para elegir la mejor opción. | | **ID value** | Introduce el valor del identificador. | 5. Recuerda hacer clic en el botón **Add test device** para guardar los cambios. ## Qué identificador deberías usar \{#which-identifier-you-should-use\} Para identificar un dispositivo puedes usar varios identificadores. Recomendamos los siguientes: - **Customer User ID** tanto para dispositivos iOS como Android si <InlineTooltip tooltip="identificas a tus usuarios en Adapty">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users) y [Unity](unity-identifying-users)</InlineTooltip>. Es la mejor opción, especialmente si tienes más de un dispositivo de prueba para una misma cuenta en tu app. Si se usa el Customer User ID como **ID used to identify this test device**, todos los dispositivos vinculados a esa cuenta se marcarán como dispositivos de prueba. - **IDFA (iOS)** y **Advertising ID (Android)**: Estos identificadores publicitarios son la opción ideal para dispositivos iOS y Android respectivamente si ya solicitas el consentimiento de tus usuarios para acceder a ellos. Aunque tengas un Customer User ID, puede que prefieras usar identificadores publicitarios si cambias de cuenta en tu app durante las pruebas. Además, estos identificadores son útiles cuando la misma cuenta tiene tanto dispositivos de prueba como personales y no quieres que los dispositivos personales se marquen como dispositivos de prueba. Existen otras opciones, como el Adapty Profile ID, el IDFV y el Android ID, que son menos cómodas pero pueden usarse si no puedes utilizar el Customer User ID, el IDFA ni el Advertising ID. A continuación, revisamos todas las opciones posibles en detalle. ### Identificadores para todas las plataformas \{#identifiers-for-all-platforms\} | Identificador | Uso | |----------|-----| | Customer User ID | <p>Un identificador único que tú asignas para identificar a tus usuarios en tu sistema. Puede ser el correo electrónico del usuario, tu ID interno o cualquier otra cadena de texto. Para usar esta opción, debes <InlineTooltip tooltip="identificar a tus usuarios en Adapty">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users) y [Unity](unity-identifying-users)</InlineTooltip>.</p><p></p><p>Es la mejor opción para identificar un dispositivo de prueba, especialmente si usas varios dispositivos para la misma cuenta. Todos los dispositivos con esa cuenta se considerarán dispositivos de prueba.</p> | | Adapty profile ID | <p>Un identificador único para el [perfil de usuario](profiles-crm) en Adapty.</p><p></p><p>Úsalo si no puedes usar el Customer User ID, el IDFA para iOS ni el Advertising ID para Android. Ten en cuenta que el Adapty Profile ID puede cambiar si reinstalar la app o vuelves a iniciar sesión.</p> | #### Cómo obtener el Customer User ID y el Adapty profile ID \{#how-to-obtain-customer-user-id-and-adapty-profile-id\} Ambos identificadores se pueden obtener en los detalles del **Profile** en el Adapty Dashboard: 1. Busca el perfil del usuario en la pestaña [**Adapty Profiles** -> **Event feed**](https://app.adapty.io/event-feed). :::note Para encontrar el perfil exacto, realiza un tipo de transacción poco frecuente. Cuando la transacción aparezca en el [**Event Feed**](https://app.adapty.io/event-feed), podrás identificarla fácilmente. ::: 2. Copia los valores de los campos **Customer user ID** y **Adapty ID** en los detalles del perfil: <img src="/assets/shared/img/345d308-test_users_CUID_adapty_ID.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Identificadores de Apple \{#apple-identifiers\} | Identificador | Uso | |----------|-----| | IDFA | <p>El Identifier for Advertisers (IDFA) es un identificador único de dispositivo que Apple asigna al dispositivo del usuario.</p><p></p><p>Es ideal para dispositivos iOS, ya que no cambia por sí solo, aunque puedes restablecerlo manualmente.</p><p>**Nota**: Desde la introducción de iOS 14.5, los anunciantes deben solicitar el consentimiento del usuario para acceder al IDFA. Asegúrate de pedirlo en tu app y de haberlo concedido en tu dispositivo de prueba.</p> | | IDFV | El Identifier for Vendors (IDFV) es un identificador alfanumérico único que Apple asigna a todas las apps de un mismo dispositivo pertenecientes al mismo editor o proveedor. Puede cambiar si reinstalar o actualizas tu app. | #### Cómo obtener el IDFA \{#how-to-obtain-the-idfa\} Apple no proporciona el IDFA por defecto. Obtenlo a través de la atribución del perfil en el Adapty Dashboard: 1. Busca el perfil del usuario en la pestaña [**Adapty Profiles** -> **Event feed**](https://app.adapty.io/event-feed). :::note Para encontrar el perfil exacto, realiza un tipo de transacción poco frecuente. Cuando la transacción aparezca en el [**Event Feed**](https://app.adapty.io/event-feed), podrás identificarla fácilmente. ::: 2. Abre los detalles del perfil y copia el valor del campo **IDFA** en la sección **Attributes**: <img src="/assets/shared/img/ce4a63f-test_users_idfa.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> También puedes [buscar en el App Store una app que te muestre tu IDFA](https://www.apple.com/us/search/idfa?src=globalnav). #### Cómo obtener el Identifier for Vendors (IDFV) \{#how-to-obtain-the-identifier-for-vendors-idfv\} Para obtener el IDFV, pide a tu desarrollador que lo solicite usando el siguiente método en tu app y que muestre el identificador recibido en los logs o en el panel de depuración. ```swift showLineNumbers title="Swift" UIDevice.current.identifierForVendor ``` ### Identificadores de Google \{#google-identifiers\} | Identificador | Uso | |----------|-----| | Advertising ID | <p>El Advertising ID es un identificador único de dispositivo que Google asigna al dispositivo del usuario.</p><p>Es ideal para dispositivos Android, ya que no cambia por sí solo, aunque puedes restablecerlo manualmente.</p><p> **Nota**: Para usarlo, desactiva la opción **Opt out of Ads Personalization** en la configuración de **Ads** si usas Android 12 o superior.</p>| | Android ID | El Android ID es un identificador único para cada combinación de clave de firma de la app, usuario y dispositivo. Está disponible en Android 8.0 y versiones superiores. | #### Cómo obtener el Advertising ID \{#how-to-obtain-advertising-id\} Para encontrar el Advertising ID de tu dispositivo: 1. Abre la app **Settings** en tu dispositivo Android. 2. Toca **Google**. 3. Selecciona **Ads** en **Services**. Tu Advertising ID aparecerá en la parte inferior de la pantalla. #### Cómo obtener el Android ID \{#how-to-obtain-android-id\} Para obtener el Android ID, pide a tu desarrollador que solicite el [ANDROID_ID](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID) usando el siguiente método en tu app y que muestre el identificador recibido en los logs o en el panel de depuración. ```kotlin showLineNumbers title="Kotlin/Java" android.provider.Settings.Secure.getString(contentResolver, android.provider.Settings.Secure.ANDROID_ID); ``` --- # File: release-checklist --- --- title: "Lista de verificación para el lanzamiento" description: "Sigue la lista de verificación de Adapty para garantizar un proceso de actualización de tu app sin problemas." --- ¡Nos alegra que hayas elegido Adapty! Esperamos que la implementación haya ido bien. Esta guía te llevará paso a paso para asegurarte de que tu app esté lista para publicarse en los stores y de que el flujo de monetización funcione correctamente. ## Elementos esenciales antes del lanzamiento \{#pre-flight-essentials\} Lo que necesitas antes de empezar la validación: - Un dispositivo real con una cuenta sandbox - Acceso al Adapty Dashboard - Acceso a App Store Connect / Google Play Console :::note Aunque las compras sandbox pueden ejecutarse en simuladores, necesitas dispositivos reales para probar todos los flujos, incluidos los diálogos de pago y las solicitudes biométricas. ::: <Button id="test-purchases-in-sandbox"> Guía de pruebas para App Store </Button> <Button id="testing-on-android"> Guía de pruebas para Google Play </Button> ## Validaciones universales \{#universal-validations\} - [ ] **Conexión con el store**: Asegúrate de haber conectado Adapty a App Store y/o Google Play: - [ ] [App Store](initial_ios) - [ ] [Google Play](initial-android) - [ ] **Entrega de eventos de suscripción**: Confirma que las notificaciones del servidor estén configuradas: - [ ] [Notificaciones del servidor de App Store](enable-app-store-server-notifications) - [ ] [Notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn) - [ ] **Identificación de perfiles**: Valida la lógica de identificación de usuarios y asegúrate de que las compras se asignen al perfil correcto: - [ ] [Comprueba que la lógica de identificación en tu código coincide con tu caso de uso](ios-quickstart-identify) - [ ] [Asegúrate de entender la lógica padre/heredero para compartir acceso de pago entre perfiles de usuario](sharing-paid-access-between-user-accounts) - [ ] **Ofertas**: Si tienes ofertas promocionales de App Store en la app, asegúrate de haber [añadido tu clave de compra in-app](app-store-connection-configuration#step-4-for-trials-and-special-offers--set-up-promotional-offers) tanto en el campo principal como en la sección **App Store promotional offers**. - [ ] **Recopilación de datos**: Garantiza el cumplimiento de la privacidad: - [ ] Si necesitas cumplir con normativas de privacidad como el RGPD o la CCPA, o si tu app está destinada a menores, controla si [habilitas la recopilación y el uso compartido de IDFA e IP](sdk-installation-ios#data-policies). - [ ] Si tu app usa AppTrackingTransparency, asegúrate de [enviar el estado de autorización a Adapty](ios-deal-with-att). - [ ] **Etiquetas de privacidad**: [Más información](apple-app-privacy) sobre los datos que recopila Adapty y qué indicadores debes configurar para la revisión. ## Validaciones de compras \{#purchase-validations\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Antes de publicar, asegúrate de que las compras in-app de tu app funcionan correctamente y de que tu paywall está listo para la revisión del store. La forma de validar las compras in-app depende de cómo las hayas implementado: - Muestras un paywall creado en el Adapty Paywall Builder - Has implementado tu propio paywall y usas el método `makePurchase` dentro de él para gestionar las compras - Usas Adapty en modo observador (ya sea con el Adapty Paywall Builder o con tu paywall personalizado) <Tabs groupId="paywall" queryString> <TabItem value="builder" label="Adapty Paywall Builder" default> **Objetivo**: Adapty renderiza el paywall, los usuarios pueden comprar productos, el acceso se desbloquea y el flujo de restauración funciona. - [ ] Tu app [muestra el paywall](ios-present-paywalls) desde el mismo placement que vas a publicar. - [ ] El paywall se muestra en pantalla. Si la carga tarda demasiado (por ejemplo, si tú o tus usuarios tenéis una conexión inestable), considera [ajustar tu política de obtención](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder). - [ ] El paywall coincide con la variante esperada (audiencia/idioma si aplica). Puedes [cambiar la prioridad de la audiencia](change-audience-priority) si es necesario. - [ ] Los productos y precios aparecen en el paywall. Ten en cuenta que la API de Apple puede ocasionalmente mostrar precios incorrectos durante las pruebas (especialmente con configuraciones de región distintas), así que prioriza probar la funcionalidad del flujo de compra sobre la exactitud de los precios, ya que Adapty no afecta a los precios del store. - [ ] La compra sandbox se completa con éxito. Se recibe el callback de compra exitosa. - [ ] El acceso se desbloquea y persiste. Confirma que [el acceso de pago se concede según el perfil de Adapty actual](ios-check-subscription-status#connect-profile-with-paywall-logic). - [ ] Tras la compra, el perfil de Adapty tiene un nivel de acceso activo. - [ ] Las funciones de pago se desbloquean cuando el perfil contiene ese nivel de acceso (no solo en el callback de compra). - [ ] La restauración de compras funciona. Cuando reinstales la app o la instales en un dispositivo nuevo, la restauración automática de compras funciona según la configuración de [Compartir acceso de pago](sharing-paid-access-between-user-accounts). Si no tienes autenticación en el backend, las compras se restauran automáticamente independientemente de la configuración. En otros casos, asegúrate de que los usuarios puedan restaurar sus compras tras reinstalar la app. - [ ] Requisitos para la revisión del store: - [ ] El botón **Restore purchases** está en el paywall. Puedes añadirlo en el paywall builder y procesará las restauraciones de compras automáticamente al pulsarlo. - [ ] Los Términos de uso y la Política de privacidad son accesibles desde la pantalla del paywall, y al hacer clic en estos enlaces se abren en un navegador. </TabItem> <TabItem value="makepurchase" label="Custom paywall (makePurchase)" default> **Objetivo**: Tú renderizas la interfaz; Adapty gestiona las compras, las actualizaciones del perfil y las restauraciones. - [ ] Los IDs de productos no están codificados en el código de tu app. Solo codificas los IDs de [placement](placements). - [ ] Tu app [obtiene los productos](fetch-paywalls-and-products) desde el mismo placement que vas a publicar. - [ ] La lista de productos se carga correctamente. Si la carga tarda demasiado (por ejemplo, si tú o tus usuarios tenéis una conexión inestable), considera [ajustar tu política de obtención](fetch-paywalls-and-products#fetch-paywall-information). - [ ] Los productos obtenidos coinciden con la variante esperada (audiencia/idioma si aplica). Puedes [cambiar la prioridad de la audiencia](change-audience-priority) si es necesario. - [ ] Los productos y precios aparecen en el paywall. Ten en cuenta que la API de Apple puede ocasionalmente mostrar precios incorrectos durante las pruebas (especialmente con configuraciones de región distintas), así que prioriza probar la funcionalidad del flujo de compra sobre la exactitud de los precios, ya que Adapty no afecta a los precios del store. - [ ] La compra sandbox con [makePurchase](making-purchases) se completa con éxito: - [ ] El resultado de compra exitosa se gestiona correctamente. - [ ] Los resultados pendientes/fallidos/cancelados se gestionan de forma adecuada. - [ ] Si [usas un Remote Config](present-remote-config-paywalls), sus valores se obtienen correctamente en tu paywall. - [ ] Cuando se muestra un paywall, se llama al [método `logShowPaywall`](present-remote-config-paywalls#track-paywall-view-events). - [ ] La compra sandbox se completa con éxito. Se recibe el callback de compra exitosa. - [ ] El acceso se desbloquea y persiste. Confirma que [el acceso de pago se concede según el perfil de Adapty actual](ios-check-subscription-status#connect-profile-with-paywall-logic). - [ ] Tras la compra, el perfil de Adapty tiene un nivel de acceso activo. - [ ] Las funciones de pago se desbloquean cuando el perfil contiene ese nivel de acceso (no solo en el callback de compra). - [ ] La restauración de compras funciona. Cuando reinstales la app o la instales en un dispositivo nuevo, la restauración automática de compras funciona según la configuración de [Compartir acceso de pago](sharing-paid-access-between-user-accounts). Si no tienes autenticación en el backend, las compras se restauran automáticamente independientemente de la configuración. En otros casos, asegúrate de que los usuarios puedan restaurar sus compras tras reinstalar la app. - [ ] Requisitos para la revisión del store: - [ ] El botón **Restore purchases** es accesible y [gestiona las restauraciones](restore-purchase). - [ ] Los Términos de uso y la Política de privacidad son accesibles desde la pantalla del paywall, y al hacer clic en estos enlaces se abren en un navegador. </TabItem> <TabItem value="observer" label="Observer mode"> **Objetivo**: Tú gestionas las compras, las actualizaciones del perfil y las restauraciones; Adapty recibe el reporte de transacciones. - [ ] **Tu app completa las compras usando tu propio flujo de compra** (StoreKit / BillingClient / backend): - [ ] La compra sandbox se completa con éxito en la interfaz del store. - [ ] Los resultados pendientes/fallidos/cancelados se gestionan de forma adecuada en tu app. - [ ] **Las transacciones se reportan a Adapty**. - [ ] El modo observador está [habilitado en el código de tu app](implement-observer-mode). - [ ] La compra aparece en el Event Feed de Adapty. - [ ] Las renovaciones, cancelaciones y reembolsos se reflejan con el tiempo (según corresponda). - [ ] **Las vistas del paywall se rastrean**. El [método `logShowPaywall`](present-remote-config-paywalls#track-paywall-view-events) se llama cuando se muestra un paywall. - [ ] **La restauración de compras funciona en tu implementación**. Reinstalar la app o cambiar de dispositivo restaura el acceso correctamente. - [ ] **Requisitos para la revisión del store**: - [ ] La acción **Restore purchases** es accesible y activa tu flujo de restauración. - [ ] Los Términos de uso y la Política de privacidad son accesibles desde el paywall o la pantalla de compra y se abren en un navegador. </TabItem> </Tabs> Si tienes alguna pregunta sobre la integración del SDK de Adapty, usa el chatbot de IA en la esquina inferior derecha o contáctanos en [support@adapty.io](mailto:support@adapty.io). --- # File: submit-app-to-app-store --- --- title: "Envía tu app de iOS a la App Store" description: "Sube tu build a App Store Connect y envía tu app de iOS con suscripciones para revisión de Apple." --- Una vez que tu integración con Adapty esté probada y funcionando, ya puedes subir tu build a App Store Connect y enviar tu app para revisión de Apple. :::tip Antes de enviar, asegúrate de haber completado la [Lista de verificación para el lanzamiento](release-checklist) para verificar tu integración con Adapty, los flujos de compra y los requisitos de revisión de la tienda. ::: ## Sube tu build a App Store Connect \{#upload-your-build-to-app-store-connect\} ### Paso 1. Archiva tu app en Xcode y súbela a App Store Connect \{#step-1-archive-your-app-in-xcode-and-upload-it-to-app-store-connect\} 1. En Xcode, establece el destino de compilación en **Any iOS Device (arm64)**. <img src="/assets/shared/img/build-target.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> 2. Selecciona **Product** > **Archive** en la barra de menú superior. <img src="/assets/shared/img/xcode-archive.webp" style={{ border: '1px solid #727272', width: '500px', display: 'block', margin: '0 auto' }} /> 3. Espera a que finalice el proceso de archivado. La ventana **Organizer** se abre automáticamente. Selecciona tu archivo y haz clic en **Distribute App**. <img src="/assets/shared/img/distribute-app.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> 4. Elige **App Store Connect** como método de distribución. Sigue los pasos para completar la subida. :::note La subida puede fallar si faltan recursos obligatorios, como el icono de la app o la pantalla de inicio. Consulta el registro de errores de Xcode para más detalles. ::: <img src="/assets/shared/img/distribution-method.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> ### Paso 2. Comprueba el build en App Store Connect \{#step-2-check-the-build-in-app-store-connect\} 1. Ve a [App Store Connect](https://appstoreconnect.apple.com) y abre tu app. 2. Desplázate hasta la sección **Build**. Asegúrate de que el build que acabas de subir aparece ahí. :::note Puede tardar unos minutos en aparecer el build en App Store Connect tras la subida. ::: <img src="/assets/shared/img/app-store-build.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> ## Envía tu app y productos a revisión \{#submit-your-app-and-products-for-review\} Una vez que el build aparezca en la sección **Build**, adjunta tus suscripciones in-app y envía la app para revisión de Apple. ### Paso 1. Adjunta los productos al envío \{#step-1-attach-products-to-the-submission\} Cada suscripción debe tener el estado **Ready to Submit** en App Store Connect antes de poder adjuntarla. Si una suscripción está en borrador o le falta información, no aparecerá en la lista. 1. En la misma página, desplázate hasta la sección **In-App Purchases and Subscriptions**. 2. Haz clic en **Select in-app purchases or subscriptions**. <img src="/assets/shared/img/app-store-select-products.webp" style={{ border: '1px solid #727272', width: '700px', display: 'block', margin: '0 auto' }} /> 3. Selecciona todos los productos que quieras incluir en este envío y haz clic en **Done**. ### Paso 2. Envía para revisión \{#step-2-submit-for-review\} 1. Completa todos los campos obligatorios de la página (descripción, capturas de pantalla, palabras clave, etc.). 2. En la sección **App Store Version Release**, selecciona si quieres publicar tu app automáticamente, manualmente o de forma programada tras su aprobación. 3. Haz clic en **Add for Review** y luego en **Submit to App Review**. Apple revisa las apps en un plazo de 1 a 2 días, aunque los tiempos pueden variar. ## Verifica tu app en producción \{#verify-your-app-in-production\} Tras la aprobación de Apple: 1. Realiza una compra real (o espera a que tu primer usuario compre). 2. Abre el [**Event Feed**](https://app.adapty.io/event-feed) en el Adapty Dashboard y confirma que aparecen los eventos de transacciones en producción. 3. Comprueba que los eventos de suscripción (renovaciones, cancelaciones) llegan correctamente; esto depende de que las [notificaciones del servidor de App Store](enable-app-store-server-notifications) estén configuradas. Si los eventos de producción no aparecen, verifica la [configuración de tu conexión con App Store](app-store-connection-configuration). ## Próximos pasos \{#next-steps\} Tu app ya está en producción. Empieza a hacer crecer tus ingresos por suscripción: - **[Pruebas A/B](ab-tests)**: Experimenta con distintos paywalls para encontrar el que mejor convierte. - **[Analíticas](charts)**: Monitoriza métricas de suscripción como MRR, churn y conversión. - **Integraciones**: Envía eventos de suscripción a plataformas de [analíticas](analytics-integration) y de [atribución](attribution-integration). --- # File: general --- --- title: "Configuración de la app" description: "Explora la configuración general de Adapty para un uso sin complicaciones." --- Puedes navegar a la pestaña General de la página App Settings para gestionar el comportamiento, la apariencia y el reparto de ingresos de tu app. Aquí puedes personalizar el nombre e icono de tu app, gestionar las claves del SDK y la API de Adapty, configurar tu estado en el Small Business Program y elegir la zona horaria para los análisis y gráficos de tu app. ## 1. Detalles de la app \{#1-app-details\} <img src="/assets/shared/img/8fa2929-CleanShot_2023-04-21_at_15.16.222x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Elige un nombre e icono únicos que representen tu app en la interfaz de Adapty. Ten en cuenta que el nombre y el icono de la app no afectarán al nombre ni al icono en el App Store o Google Play. Además, asegúrate de seleccionar una categoría de app adecuada que refleje con precisión el propósito y el contenido de tu app. Esto ayudará a los usuarios a descubrirla y garantizará que aparezca en las categorías correctas de la store. ## 2\. Miembro del Small Business Program y tarifa de servicio reducida \{#2-member-of-small-business-program-and-reduced-service-fee\} <img src="/assets/shared/img/825e2be-CleanShot_2023-04-19_at_13.43.292x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Si tu organización está inscrita en el [Small Business Program](app-store-small-business-program) de Apple o en el programa de [tarifa de servicio reducida](google-reduced-service-fee) de Google, tus apps están sujetas a una comisión reducida de la store. Notifica a Adapty si tu app está inscrita en un programa de comisión reducida. Para garantizar cálculos correctos, especifica el estado de estos programas en la sección "Reduced Store Fee". La configuración de tarifa reducida solo se aplica a las transacciones futuras. Cambia tu estado **antes** de que entre en vigor y Adapty ajustará la tasa de comisión. :::warning * Si extiendes tu participación en un programa de tarifa reducida, **añade un período de elegibilidad adicional**. * Si pierdes la membresía en el programa, **cambia la fecha de vencimiento** de tu período de elegibilidad actual. ::: Los siguientes artículos profundizan en este tema: * [App Store Small Business Program](app-store-small-business-program) * [Google Reduced Service Fee](google-reduced-service-fee) ## 3\. Zona horaria de informes \{#3-reporting-timezone\} <img src="/assets/shared/img/47227f9-CleanShot_2023-04-19_at_13.45.302x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Elige la zona horaria que corresponda a tu ubicación o a la zona donde los análisis y gráficos de tu app sean más relevantes. Recomendamos usar la misma zona horaria que tu cuenta de App Store Connect o Google Play Console para mantener la coherencia. Ten en cuenta que esta configuración de zona horaria no afecta a las integraciones de terceros en el sistema de Adapty, que utilizan la zona horaria UTC. Puedes acceder a la configuración de zona horaria en la sección "Reported timezone" de la pestaña General en la página App Settings. También puedes aplicar la misma zona horaria a todas las apps de tu cuenta de Adapty marcando la casilla correspondiente. ## 4\. Definición de instalaciones para análisis \{#4-installs-definition-for-analytics\} Elige qué se considera un nuevo evento de instalación en los análisis: | Base | Descripción | |------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Nuevos device_ids | <p>(Recomendado) Cada instalación de la app desde la store en un dispositivo se cuenta como una nueva instalación. Esto incluye tanto las instalaciones por primera vez como las reinstalaciones.</p><p>Las instalaciones se cuentan por ID de dispositivo y no se ven afectadas por la autenticación del usuario. Crear un perfil (al activar el SDK o al cerrar sesión), iniciar sesión o actualizar la app no genera eventos de instalación adicionales.</p><p>Por ejemplo, si la misma app está instalada en 5 dispositivos diferentes, verás 5 instalaciones en los análisis.</p> | | Nuevos customer_user_ids | <p>Esta opción está pensada para apps que <InlineTooltip tooltip="identifican usuarios en Adapty">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), [Unity](unity-identifying-users), [Kotlin Multiplatform](kmp-quickstart-identify), [Capacitor](capacitor-quickstart-identify)</InlineTooltip>. </p><p>Para los usuarios con sesión iniciada, solo la primera instalación asociada a un customer user ID se cuenta como instalación. Las instalaciones en dispositivos adicionales no se contabilizan como nuevas instalaciones. </p><p>Los usuarios anónimos (usuarios que no han iniciado sesión) no se contabilizan en los análisis. </p><p>Reinstalar la app o volver a iniciar sesión no crea instalaciones adicionales.</p> <p>Las stores y las plataformas de atribución (como App Store Connect, Google Play Console y AppsFlyer) utilizan un enfoque basado en dispositivos para contar instalaciones. Si cuentas instalaciones por customer user IDs en Adapty, los números pueden diferir de los de estos servicios externos.</p><p>⚠️ Si no identificas usuarios en Adapty, no se contabilizará ninguna instalación con esta opción activada.</p> | | Nuevos perfiles en Adapty | (Heredado) Cada instalación, reinstalación de la app y los perfiles anónimos creados durante los cierres de sesión se cuentan como nuevas instalaciones. | Ten en cuenta que esta opción solo afecta a la página [**Analytics**](https://app.adapty.io/analytics) y no tiene impacto en la página [**Overview**](https://app.adapty.io/overview), donde puedes configurar la vista por separado. ## 5. Lógica de aumento de precio en el App Store \{#5-app-store-price-increase-logic\} Para mantener datos precisos y evitar discrepancias entre los análisis de Adapty y los resultados de App Store Connect, es importante seleccionar la opción adecuada al ajustar las configuraciones relacionadas con los aumentos de precio en App Store Connect. Así puedes elegir la lógica que se aplicará a los aumentos de precio de las suscripciones en Adapty: <img src="/assets/shared/img/b766c8b-CleanShot_2023-07-18_at_19.28.18_22x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> - **El precio de la suscripción para los usuarios existentes se mantiene:** Al seleccionar esta opción, el precio actual se conservará para tus suscriptores existentes, aunque realices cambios en el precio en App Store Connect. Esto significa que los suscriptores existentes seguirán siendo facturados al precio original de su suscripción. - **Cuando el precio de la suscripción cambia en App Store Connect, también cambia para los suscriptores existentes:** Si eliges esta opción, cualquier cambio de precio realizado en App Store Connect se aplicará también a tus suscriptores existentes. Esto significa que los suscriptores existentes serán cobrados al nuevo precio que refleja la actualización establecida en App Store Connect. :::warning Es importante tener en cuenta que la opción seleccionada no solo afecta a los análisis en Adapty, sino que también impacta en las integraciones y en el comportamiento general del procesamiento de transacciones. ::: Asegúrate de seleccionar la opción adecuada que se alinee con tu enfoque deseado para gestionar los precios de las suscripciones para los suscriptores existentes. Esto ayudará a mantener datos precisos y la sincronización entre los análisis de Adapty y los resultados obtenidos de App Store Connect. ## 6. Compartir el acceso de pago entre cuentas de usuario \{#6-sharing-paid-access-between-user-accounts\} :::link Artículo principal: [Compartir el acceso de pago entre cuentas de usuario](sharing-paid-access-between-user-accounts) ::: La configuración **Sharing paid access between user accounts** determina qué hace Adapty cuando más de un [perfil de usuario](identifying-users) intenta acceder a la misma compra. Puedes especificar una configuración de compartición de acceso independiente para el [entorno sandbox](test-purchases-in-sandbox). --- no_index: true --- **Habilitado (predeterminado)** Los usuarios identificados (aquellos con un [Customer User ID](identifying-users#set-customer-user-id-on-configuration)) pueden compartir el mismo [nivel de acceso](access-level) proporcionado por Adapty si su dispositivo está vinculado al mismo Apple/Google ID. Esto es útil cuando un usuario reinstala la app e inicia sesión con un correo diferente: seguirá teniendo acceso a su compra anterior. Con esta opción, varios usuarios identificados pueden compartir el mismo nivel de acceso. Aunque el nivel de acceso se comparte, todas las transacciones pasadas y futuras se registran como eventos en el Customer User ID original para mantener análisis coherentes y conservar un historial completo de transacciones, incluyendo períodos de prueba, compras de suscripción, renovaciones y más, vinculados al mismo perfil. **Transferir acceso al nuevo usuario** Los usuarios identificados pueden seguir accediendo al [nivel de acceso](access-level) proporcionado por Adapty, incluso si inician sesión con un [Customer User ID](identifying-users#set-customer-user-id-on-configuration) diferente o reinstalan la app, siempre que el dispositivo esté vinculado al mismo Apple/Google ID. A diferencia de la opción anterior, Adapty transfiere la compra entre usuarios identificados. Esto garantiza que el contenido adquirido esté disponible, pero solo un usuario puede tener acceso a la vez. Por ejemplo, si UserA compra una suscripción y UserB inicia sesión en el mismo dispositivo y restaura las transacciones, UserB obtendrá acceso a la suscripción y se le revocará a UserA. Si uno de los usuarios (ya sea el nuevo o el anterior) no está identificado, el nivel de acceso seguirá compartiéndose entre esos perfiles en Adapty. Aunque el nivel de acceso se transfiere, todas las transacciones pasadas y futuras se registran como eventos en el Customer User ID original para mantener análisis coherentes y conservar un historial completo de transacciones, incluyendo períodos de prueba, compras de suscripción, renovaciones y más, vinculados al mismo perfil. Después de cambiar a **Transferir acceso al nuevo usuario**, los niveles de acceso no se transferirán entre perfiles de inmediato. El proceso de transferencia para cada nivel de acceso específico se activa solo cuando Adapty recibe un evento del store, como la renovación de una suscripción, una restauración o al validar una transacción. **Deshabilitado** El primer perfil de usuario identificado que obtenga un nivel de acceso lo conservará para siempre. Esta es la mejor opción si la lógica de negocio de tu app requiere que las compras estén vinculadas a un único Customer User ID. Ten en cuenta que los niveles de acceso siguen compartiéndose entre usuarios anónimos. Puedes "desvincular" una compra [eliminando el perfil del usuario propietario](ss-delete-profile). Tras la eliminación, el nivel de acceso queda disponible para el primer perfil de usuario que lo reclame, ya sea anónimo o identificado. Deshabilitar el uso compartido solo afecta a los nuevos usuarios. Las suscripciones que ya se comparten entre usuarios seguirán compartiéndose incluso después de deshabilitar esta opción. :::warning Apple y Google exigen que las compras in-app se compartan o transfieran entre usuarios porque se basan en el Apple/Google ID para asociar la compra. Sin el uso compartido, restaurar compras podría no funcionar tras reinstalaciones posteriores. Deshabilitar el uso compartido puede impedir que los usuarios recuperen el acceso al iniciar sesión. Recomendamos deshabilitar el uso compartido solo si tus usuarios **deben iniciar sesión** antes de realizar una compra. De lo contrario, un usuario identificado podría comprar una suscripción, iniciar sesión en otra cuenta y perder el acceso de forma permanente. ::: ### ¿Qué opción debo elegir? \{#which-setting-should-i-choose\} | Mi app... | Opción a elegir | | ------------------------------------------------------------ | ------------------------------------------------------------ | | No tiene sistema de inicio de sesión y solo usa los IDs de perfil anónimos de Adapty. | Usa la opción predeterminada, ya que los niveles de acceso siempre se comparten entre IDs de perfil anónimos en las tres opciones. | | Tiene un sistema de inicio de sesión opcional y permite a los clientes realizar compras antes de crear una cuenta. | Elige **Transferir acceso al nuevo usuario** para garantizar que los clientes que compren sin una cuenta puedan restaurar sus transacciones más adelante. | | Requiere que los clientes creen una cuenta antes de comprar, pero permite que las compras estén vinculadas a varios Customer User IDs. | Elige **Transferir acceso al nuevo usuario** para garantizar que solo un Customer User ID tenga acceso a la vez, permitiendo a los usuarios iniciar sesión con un Customer User ID diferente sin perder su acceso de pago. | | Requiere que los clientes creen una cuenta antes de comprar, con reglas estrictas que vinculan las compras a un único Customer User ID. | Elige **Deshabilitado** para garantizar que las transacciones nunca se transfieran entre cuentas. | ## 7. Claves del SDK y la API \{#7-sdk-and-api-keys\} Usa una clave SDK pública para integrar los SDK de Adapty en tu app, y una clave secreta para acceder a la API del servidor de Adapty. Puedes generar nuevas claves o revocar las existentes según sea necesario. Para crear tokens para el Developer CLI, ve a **Settings → Developer API**. Consulta [Authentication](developer-cli-authentication). ## 8. Dispositivos de prueba \{#8-test-devices\} Especifica los dispositivos que se usarán para pruebas para asegurarte de que reciben actualizaciones instantáneas de los cambios en paywalls o placements, sin demoras de caché. Para más información, consulta [Testing devices](test-devices). ## 9. Persistencia de variante entre placements \{#9-cross-placement-variation-stickiness\} Define cuánto tiempo después de finalizar una prueba un usuario sigue viendo las variantes de esa prueba. Esto afecta a la precisión de los análisis y a la experiencia del usuario, ya que mostrarle una oferta diferente a la que vio anteriormente puede influir en su decisión de compra. El período máximo y predeterminado de persistencia es de 90 días. :::warning Ten en cuenta lo siguiente: - Cambiar esta configuración afectará a todos los usuarios que previamente recibieron una variante. Inmediatamente podrán ver un nuevo paywall cuando accedan a un placement, lo que puede distorsionar los resultados de tus pruebas A/B en curso. - Si el período de persistencia ha expirado para un usuario, puede recibir un nuevo paywall o prueba A/B. Sin embargo, incluso en ese caso, no podrá formar parte de ninguna otra prueba entre placements en ningún momento futuro. ::: ## 10. Eliminar la app \{#10-delete-the-app\} Si ya no necesitas una app, puedes eliminarla de Adapty. :::warning Ten en cuenta que esta acción es irreversible y no podrás restaurar la app ni sus datos. ::: --- # File: ios-settings --- --- title: "Credenciales de Apple App Store" description: "Configura los ajustes de iOS en Adapty para una gestión de suscripciones sin interrupciones." --- Para configurar las credenciales de App Store y garantizar el funcionamiento óptimo del SDK de iOS de Adapty, ve a la pestaña [iOS SDK](https://app.adapty.io/settings/ios-sdk) dentro de la página App Settings del Adapty Dashboard. Luego, configura los siguientes parámetros: <img src="/assets/shared/img/3d4087e-CleanShot_2023-06-26_at_13.27.042x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> | Campo | Descripción | |----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Bundle ID** | El [bundle ID de tu app](app-store-connection-configuration#step-1-provide-bundle-id-and-apple-app-id). | | **In-app purchase API (StoreKit 2)** | [Claves](app-store-connection-configuration#step-2-provide-issuer-id-and-key-id) para habilitar la autenticación segura y la validación del historial de transacciones de compras in-app. | | **App Store Server Notifications** | URL que se utiliza para habilitar las [notificaciones server2server](enable-app-store-server-notifications) de App Store y monitorizar los cambios en el estado de suscripción de los usuarios. | | **App Store Promotional Offers** | Claves de suscripción para crear [ofertas promocionales](generate-in-app-purchase-key) en Adapty para productos específicos. | | **Apple app ID** | El ID de tu app en App Store. Para encontrarlo, abre la página de tu app en App Store Connect, accede a la página **App Information** desde el menú izquierdo y copia el **Apple ID**. | | **App Store Connect shared secret (LEGACY)** | <p>**Clave heredada para el SDK de Adapty anterior a v.2.9.0**</p><p></p><p>[Una clave](app-store-connection-configuration#step-5-enter-app-store-shared-secret) para la validación de recibos y la prevención de fraude en tu app.</p> | --- # File: google-play-store-connection-configuration --- --- title: "Configurar la integración con Google Play Store" description: "Configura la conexión con Google Play Store en Adapty para gestionar las compras in-app sin problemas." --- Esta sección describe el proceso de integración de tu aplicación móvil distribuida a través de Google Play con Adapty. Tendrás que introducir los datos de configuración de tu app desde la Play Store en el Adapty Dashboard. Este paso es fundamental para validar las compras y recibir actualizaciones de suscripciones desde la Play Store dentro de Adapty. Puedes completar este proceso durante el onboarding inicial o realizar cambios posteriormente en los **App Settings** del Adapty Dashboard. :::danger Los cambios de configuración solo son válidos antes de publicar tu aplicación móvil con los paywalls de Adapty integrados. Modificar la configuración tras el lanzamiento romperá la integración y los paywalls dejarán de mostrarse en tu aplicación. ::: ## Paso 1. Proporciona el Package name \{#step-1-provide-package-name\} El Package name es el identificador único de tu app en Google Play Store. Es necesario para el funcionamiento básico de Adapty, como el procesamiento de suscripciones. 1. Abre la [Google Play Developer Console](https://play.google.com/console/u/0/developers). 2. Selecciona la app cuyo ID necesitas. Se abrirá la ventana **Dashboard**. <img src="/assets/shared/img/7889edb-package_name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Busca el ID del producto bajo el nombre de la aplicación y cópialo. 4. Abre los [**App settings**](https://app.adapty.io/settings/android-sdk) desde el menú superior de Adapty. <img src="/assets/shared/img/b00066c-package_name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. En la pestaña **Android SDK** de la ventana **App settings**, pega el **Package name** copiado. ## Paso 2. Sube el archivo de clave de cuenta \{#step-2-upload-the-account-key-file\} 1. Sube el archivo de clave privada de cuenta de servicio en formato JSON que creaste en el paso [Crear archivo de clave de cuenta de servicio](create-service-account) en el área **Service account key file**. <img src="/assets/shared/img/20fdba1-service_key_file.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> No olvides hacer clic en el botón **Save** para confirmar los cambios. **Próximos pasos** - [Activar las notificaciones en tiempo real para desarrolladores (RTDN) en la Google Play Console](enable-real-time-developer-notifications-rtdn) --- # File: enable-real-time-developer-notifications-rtdn --- --- title: "Habilitar notificaciones en tiempo real para desarrolladores (RTDN) en Google Play Console" description: "Mantente informado sobre eventos críticos y garantiza la exactitud de los datos habilitando las Notificaciones en Tiempo Real para Desarrolladores (RTDN) en Google Play Console para Adapty. Aprende a configurar RTDN para recibir actualizaciones instantáneas sobre reembolsos y otros eventos importantes de la Play Store" --- Configurar las notificaciones en tiempo real para desarrolladores (RTDN) es fundamental para garantizar la exactitud de los datos, ya que te permite recibir actualizaciones al instante desde la Play Store, incluyendo información sobre reembolsos y otros eventos. ## Habilitar notificaciones \{#enable-notifications\} 1. Asegúrate de tener **Google Cloud Pub/Sub** habilitado. Abre [este enlace](https://console.cloud.google.com/flows/enableapi?apiid=pubsub) y selecciona el proyecto de tu app. Si todavía no has habilitado **Google Cloud Pub/Sub**, debes hacerlo aquí. <img src="/assets/shared/img/pubsub.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Ve a [**App settings > Android SDK**](https://app.adapty.io/settings/android-sdk) desde el menú superior de Adapty y copia el contenido del campo **Enable Pub/Sub API** que aparece junto al título **Google Play RTDN topic name**. <img src="/assets/shared/img/a72ff2d-copy_topic.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <p> </p> :::note Si el contenido del campo **Enable Pub/Sub API** tiene un formato incorrecto (el formato correcto empieza por `projects/...`), consulta la sección [Corregir el formato incorrecto en el campo Enable Pub/Sub API](enable-real-time-developer-notifications-rtdn#fixing-incorrect-format-in-enable-pubsub-api-field) para obtener ayuda. ::: 3. Abre la [Google Play Console](https://play.google.com/console/), elige tu app y ve a **Monetize with Play** -> **Monetization setup**. En la sección **Google Play Billing**, marca la casilla **Enable real-time notifications**. 4. Pega el contenido del campo **Enable Pub/Sub API** que copiaste en los **App Settings** de Adapty en el campo **Topic name**. 5. Haz clic en **Save changes** en la Google Play Console. <img src="/assets/shared/img/e55ba0e-paste_topic_name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Probar las notificaciones \{#test-notifications\} Para comprobar si te has suscrito correctamente a las notificaciones en tiempo real para desarrolladores: 1. Guarda los cambios en la configuración de Google Play Console. 2. Debajo del campo **Topic name** en Google Play Console, haz clic en **Send test notification**. <img src="/assets/shared/img/rtdn-test.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Ve a [**App settings > Android SDK**](https://app.adapty.io/settings/android-sdk) en Adapty. Si se ha enviado una notificación de prueba, verás su estado encima del nombre del topic. <img src="/assets/shared/img/rtdn-adapty-test.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Corregir el formato incorrecto en el campo Enable Pub/Sub API \{#fixing-incorrect-format-in-enable-pubsub-api-field\} Si el contenido del campo **Enable Pub/Sub API** tiene un formato incorrecto (el formato correcto empieza por `projects/...`), sigue estos pasos para solucionar el problema: ### 1. Verificar la habilitación de la API y los permisos \{#1-verify-api-enablement-and-permissions\} Comprueba detenidamente que todas las APIs necesarias estén habilitadas y que los permisos estén correctamente concedidos a la cuenta de servicio. Aunque ya hayas completado estos pasos, es importante revisarlos de nuevo para asegurarte de que no se omitió ninguno. Repite los pasos de las siguientes secciones: 1. [Habilitar las APIs de desarrollador en Google Play Console](enabling-of-devepoler-api) 2. [Crear una cuenta de servicio en Google Cloud Console](create-service-account) 3. [Conceder permisos a la cuenta de servicio en Google Play Console](grant-permissions-to-service-account) 4. [Generar el archivo de clave de la cuenta de servicio en Google Play Console](create-service-account-key-file) 5. [Configurar la integración con Google Play Store](google-play-store-connection-configuration) ### 2. Ajustar las políticas de dominio \{#2-adjust-domain-policies\} Cambia las políticas **Domain restricted contacts** y **Domain restricted sharing**: 1. Abre la [Google Cloud Console](https://console.cloud.google.com/) y selecciona el proyecto donde creaste la cuenta de servicio para gestionar tu app. 2. En la sección **Quick Access**, elige **IAM & Admin**. <img src="/assets/shared/img/google-cloud-IAM-and-Admin.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En el panel izquierdo, elige **Organization Policies**. 4. Busca la política **Domain restricted contacts**. <img src="/assets/shared/img/google-cloud-policy-action.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en el botón de puntos suspensivos en la columna **Actions** y elige **Edit policy**. 6. En la ventana de edición de la política: 1. En **Policy source**, selecciona el botón de opción **Override parent's policy**. 2. En **Policy enforcement**, selecciona el botón de opción **Replace**. 3. En **Rules**, haz clic en el botón **ADD A RULE**. <img src="/assets/shared/img/google-cloud-edit-policy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En **New rule** -> **Policy values**, elige **Allow All**. <img src="/assets/shared/img/google-cloud-allow-all-policy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **SET POLICY**. 7. Repite los pasos 4-6 para la política **Domain restricted sharing**. Por último, vuelve a generar el contenido del campo **Enable Pub/Sub API** situado junto al título **Google Play RTDN topic name**. El campo tendrá ahora el formato correcto. Asegúrate de cambiar **Policy source** de vuelta a **Inherit parent's policy** para las políticas actualizadas una vez que hayas habilitado correctamente las Notificaciones en Tiempo Real para Desarrolladores (RTDN). ## Reenvío de eventos sin procesar \{#raw-events-forwarding\} En algunos casos, puede que quieras seguir recibiendo eventos S2S sin procesar de Google. Para continuar recibiéndolos mientras usas Adapty, simplemente añade tu endpoint en el campo **URL for forwarding raw Google events** y enviaremos los eventos tal cual los recibimos de Google. <img src="/assets/shared/img/e388892-001774-September-22-GhkjOFbT.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> --- **Próximos pasos** Configura el SDK de Adapty para: - [Android](sdk-installation-android) - [React Native](sdk-installation-reactnative) - [Flutter](sdk-installation-flutter) - [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform) - [Unity](sdk-installation-unity) --- # File: apple-search-ads --- --- title: "Apple Ads" description: "Integra Apple Ads con Adapty para optimizar las conversiones de suscripción." --- :::important La integración de Apple Ads en **App settings** se usa únicamente para análisis básicos y para las integraciones de SplitMetrics Acquire y Asapty. [Apple Ads Manager](adapty-ads-manager) utiliza una conexión independiente. Conecta tu cuenta de Apple Ads en los [ajustes de Apple Ads Manager](adapty-ads-manager-get-started). ::: Adapty puede ayudarte a obtener datos de atribución de Apple Ads y analizar tus métricas con segmentación por campaña y palabra clave. Adapty recopila automáticamente los datos de atribución de Apple Ads a través de su SDK y el framework AdServices. Una vez que hayas configurado la integración con Apple Ads, Adapty comenzará a recibir datos de atribución de Apple Ads. Puedes acceder a estos datos fácilmente y consultarlos en la página de perfiles. <img src="/assets/shared/img/ba4a3e9-CleanShot_2023-08-21_at_15.14.592x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Configurar la integración \{#set-up-integration\} ### Conectar Adapty al framework AdServices \{#connect-adapty-to-the-adservices-framework\} Apple Ads a través de [AdServices](https://developer.apple.com/documentation/adservices) requiere cierta configuración en el Adapty Dashboard, y también deberás habilitarlo en la parte de la app. Para configurar Apple Ads usando el framework AdServices a través de Adapty, sigue estos pasos: #### Paso 1: Configurar Info.plist \{#step-1-configure-infoplist\} Añade `AdaptyAppleSearchAdsAttributionCollectionEnabled` al archivo `Info.plist` de la app y establece su valor en `YES` (booleano). #### Paso 2: Obtener la clave pública \{#step-2-obtain-public-key\} En el Adapty Dashboard, ve a [Settings -> Apple Ads.](https://app.adapty.io/settings/apple-search-ads) Localiza la clave pública pregenerada (Adapty te proporciona un par de claves) y cópiala. <img src="/assets/shared/img/baa5998-CleanShot_2023-08-21_at_14.55.542x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::note Si usas un servicio alternativo o tu propia solución para la atribución de Apple Ads, puedes subir tu propia clave privada. ::: #### Paso 3: Configurar la gestión de usuarios en Apple Ads \{#step-3-configure-user-management-on-apple-ads\} En tu [cuenta de Apple Ads](https://ads.apple.com/app-store), ve a la página **Settings > User Management**. Para que Adapty pueda obtener los datos de atribución, debes invitar a otra cuenta de Apple ID y concederle acceso como API Account Manager. Puedes usar cualquier cuenta a la que tengas acceso o crear una nueva específicamente para este fin. Lo importante es que debes poder iniciar sesión en Apple Ads con ese Apple ID. <img src="/assets/shared/img/ec183b2-kdjsfldsfjkdsfdfd.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### Paso 4: Generar las credenciales de API \{#step-4-generate-api-credentials\} A continuación, inicia sesión en Apple Ads con la cuenta recién añadida. Ve a Settings -> API en la interfaz de Apple Ads. Pega la clave pública que copiaste anteriormente en el campo correspondiente. Genera nuevas credenciales de API. #### Paso 5: Configurar Adapty con las credenciales de Apple Ads \{#step-5-configure-adapty-with-apple-ads-credentials\} Copia los campos Client ID, Team ID y Key ID de los ajustes de Apple Ads. En el Adapty Dashboard, pega estas credenciales en los campos correspondientes. <img src="/assets/shared/img/7356113-CleanShot_2023-08-21_at_15.08.512x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Conectar tu app a la red AdServices \{#connect-your-app-to-the-adservices-network\} Una vez que completes [la configuración del framework AdServices](#connect-the-adservices-framework), Adapty empieza a recopilar automáticamente los datos de atribución de Apple Search Ads. No necesitas añadir ningún código al SDK. En aplicaciones iOS, estos datos de atribución **siempre** tendrán prioridad sobre los datos de otras fuentes. Si este comportamiento no es el deseado, *desactiva* la atribución de ASA siguiendo las instrucciones a continuación. ## Desactivar la integración \{#disable-integration\} Para desactivar la atribución de Apple Search Ads, abre la pestaña [**App Settings** -> **Apple Search Ads**](https://app.adapty.io/settings/apple-search-ads) y desactiva el interruptor **Receive Apple Search Ads attribution**. <img src="/assets/shared/img/asa-disable.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::warning Ten en cuenta que desactivar esto detendrá completamente la recepción de análisis de ASA. Como resultado, ASA dejará de usarse en los análisis y no se enviará a las integraciones. Además, SplitMetrics Acquire y Asapty dejarán de funcionar, ya que dependen de la atribución de ASA para operar correctamente. La atribución recibida antes de este cambio no se verá afectada. ::: ## Subir tus propias claves \{#uploading-your-own-keys\} :::note Opcional Estos pasos no son necesarios para la atribución de Apple Ads, solo para trabajar con otros servicios como Asapty o tu propia solución. ::: Puedes usar tu propio par de claves pública-privada si estás utilizando otros servicios o una solución propia para la atribución de ASA. ### Paso 1 \{#step-1\} Genera la clave privada en el Terminal ```text showLineNumbers title="Text" openssl ecparam -genkey -name prime256v1 -noout -out private-key.pem ``` Súbela en Adapty Settings -> Apple Ads (botón Upload private key) ### Paso 2 \{#step-2\} Genera la clave pública en el Terminal ```text showLineNumbers title="Text" openssl ec -in private-key.pem -pubout -out public-key.pem ``` Puedes usar esta clave pública en los ajustes de Apple Ads de la cuenta con el rol API Account Manager. Así podrás usar los valores generados de Client ID, Team ID y Key ID tanto en Adapty como en otros servicios. --- # File: account --- --- title: "Detalles de la cuenta y facturación" description: "Gestiona tu cuenta de Adapty y optimiza la configuración para un mejor seguimiento de suscripciones." --- La página **Account** te permite gestionar tu perfil, los miembros del equipo y la facturación. La página tiene tres pestañas: - [General](#general-settings) - [Subscription & Billing](#billing-info) - [Members](#members) Para acceder a la configuración de tu cuenta, haz clic en **Account** en la parte superior derecha o ve a [app.adapty.io/account](https://app.adapty.io/account). <img src="/assets/shared/img/account-info.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Configuración general \{#general-settings\} La pestaña General contiene tu perfil, la configuración de la cuenta, las preferencias de visualización y la configuración de informes. - **Profile**: Introduce tu nombre, apellidos y nombre de empresa. El nombre de empresa puede tener hasta 256 caracteres. - **Account settings**: Consulta tu dirección de correo electrónico registrada y cambia tu contraseña. - **Date & Time formats**: Elige cómo se muestran las fechas y horas en Adapty: - **American format**: January 31, 2022 y hora en formato de 12 horas (AM/PM) - **European format**: 31 January, 2022 y hora en formato de 24 horas (16:00) - **Email reports**: Configura informes diarios, semanales o mensuales para una o todas tus apps. Recibe informes resumidos de todas las apps a la vez, o un informe detallado de cada app seleccionada. <img src="/assets/shared/img/account-info.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Suscripción y facturación \{#billing-info\} La pestaña **Subscription & Billing** te permite gestionar tu información de pago y el acceso a funciones: - Añadir o actualizar los datos de pago - Revisar la información de facturación - Adquirir funciones adicionales de pago Más información sobre [funciones y precios](https://adapty.io/pricing). ## Miembros \{#members\} Puedes gestionar los miembros de tu equipo desde la configuración de la cuenta. Para añadir miembros, invítalos por su correo electrónico y asígnales un rol. Lee más sobre cómo gestionar los miembros del equipo y sus permisos de acceso [aquí](members-settings). --- # File: members-settings --- --- title: "Members" description: "Gestiona la configuración y los permisos de los miembros en el dashboard de Adapty." --- :::note Esta página trata sobre los miembros del Adapty Dashboard Si quieres otorgar distintos niveles de acceso a los usuarios de tu app, consulta [Nivel de acceso](access-level). ::: El sistema de miembros del Adapty Dashboard te permite conceder diferentes niveles de acceso a Adapty y especificar las aplicaciones para cada miembro. ## Roles \{#roles\} Los siguientes roles están disponibles para los miembros en el Adapty Dashboard: | Rol | Acceso a Facturación | Añadir nuevos miembros | Modificar cualquier cosa | Acceso a todas las secciones | |-------------|----------------------|------------------------|--------------------------|------------------------------| | Owner | ✅ | ✅ | ✅ | ✅ | | Admin | ❌ | ✅ | ✅ | ✅ | | Developer | ❌ | ❌ | ✅ | ❌ | | Viewer | ❌ | ❌ | ❌ | ✅ | | Support | ❌ | ❌ | ❌ | ❌ | | ASA manager | ❌ | ❌ | ❌ | ❌ | - **Owner:** El Owner es el creador original de la cuenta de Adapty y tiene el nivel más alto de acceso y control. Los Owners tienen acceso completo a la facturación de Adapty, lo que les permite gestionar la información de pago y los planes de suscripción. Además, solo los Owners y los Admins pueden especificar el acceso a aplicaciones para los nuevos miembros. Solo puede haber un Owner por cuenta de Adapty. - **Admin:** Los miembros con el rol Admin tienen acceso completo a las aplicaciones seleccionadas. Pueden realizar diversas tareas de gestión, como crear y modificar paywalls, realizar pruebas A/B, analizar métricas y gestionar miembros dentro de esas aplicaciones. - **Developer:** Los miembros con el rol Developer tienen acceso completo a todas las entidades, excepto a las métricas y a los miembros de la cuenta. No pueden acceder a ninguna configuración de facturación. Este rol está pensado para quienes configuran paywalls, pruebas A/B y otras entidades e integran Adapty en tu app, pero no deben ver datos financieros. - **Viewer:** Los miembros con el rol Viewer tienen acceso de solo lectura a las aplicaciones seleccionadas. Pueden ver la información, pero no pueden crear ni modificar paywalls, pruebas A/B u otras funciones, invitar a nuevos usuarios, crear nuevas apps ni cambiar la configuración de la app. - **Support:** Los miembros con el rol Support solo tienen acceso a los perfiles de usuario en las aplicaciones seleccionadas. Sin embargo, no pueden añadir nuevos miembros ni acceder a ninguna otra sección de Adapty. Este rol es especialmente adecuado para equipos de soporte o personas que necesitan ayudar a los clientes con consultas o problemas relacionados con suscripciones. - **ASA manager:** Los miembros con el rol ASA manager solo tienen acceso al dashboard de [Apple Ads Manager](adapty-ads-manager). ## Añadir un miembro \{#add-a-member\} En Adapty puedes invitar hasta 256 miembros del equipo. Añadir nuevos miembros es gratuito. :::note Solo puedes invitar direcciones de correo electrónico que aún no estén registradas en Adapty. Si tu colega tiene una cuenta independiente, invita a una dirección de correo diferente o contacta con el soporte de Adapty para eliminar su cuenta existente. ::: Para añadir un miembro del equipo: 1. Haz clic en **Account** en la parte superior derecha y abre la pestaña **Members**. 2. Haz clic en **Invite member**. <img src="/assets/shared/img/invite-member.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Introduce la dirección de correo electrónico del miembro. 4. Selecciona un [rol](#roles) de la lista. 5. Selecciona las apps a las que quieres dar acceso. 6. (Opcional) Activa **Always allow access to new apps** para conceder acceso automáticamente a las apps futuras. 7. Haz clic en **Save**. <img src="/assets/shared/img/add-member.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '500px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Transferir la propiedad de la cuenta \{#transfer-account-ownership\} Si necesitas transferir la **propiedad completa de la cuenta**, contacta con nuestro equipo de soporte en [support@adapty.io](mailto:support@adapty.io). Si necesitas transferir la **propiedad de la app**, consulta la [guía específica](transfer-apps) para más información. --- # File: set-up-app-store-connect --- --- title: "Configurar App Store Connect" description: "Guía para desarrolladores primerizos sobre cómo inscribirse en el Apple Developer Program y configurar App Store Connect para las compras in-app." --- Si estás **creando tu primera app para iOS**, debes configurar tu cuenta de Apple Developer y App Store Connect antes de integrar Adapty. :::note Si ya tienes una cuenta de Apple Developer y una app registrada en App Store Connect, puedes saltarte esta guía e ir directamente a [Integración inicial con el App Store](initial_ios). ::: ## Paso 1. Inscríbete en el Apple Developer Program \{#step-1-enroll-in-apple-developer-program\} Para distribuir apps en el App Store y vender compras in-app, debes unirte al [Apple Developer Program](https://developer.apple.com/programs/). ### Elige el tipo de inscripción \{#choose-enrollment-type\} Apple ofrece dos tipos de inscripción: | | Individual | Organización | |----------------------------------------|------------------------|------------------------------------| | **Para quién es** | Desarrolladores solos | Empresas, equipos, organizaciones sin ánimo de lucro | | **Requiere número D-U-N-S** | No | Sí | | **Apps publicadas bajo** | Tu nombre personal | El nombre de tu organización | | **Gestión de equipo** | No disponible | Disponible | :::tip Si te inscribes como organización, necesitas un **número D-U-N-S** — un identificador único de nueve dígitos emitido por Dun & Bradstreet. Puedes [comprobar si tu organización ya tiene uno](https://developer.apple.com/enroll/duns-lookup/) o solicitar uno nuevo — el enlace está al final de la página de búsqueda. Un número D-U-N-S puede tardar hasta 5 días hábiles en llegar. ::: ### Inscríbete \{#enroll\} 1. Ve a la [página de inscripción del Apple Developer Program](https://developer.apple.com/programs/enroll/). 2. Inicia sesión con tu Apple ID. Si no tienes uno, créalo primero. 3. Sigue los pasos según tu tipo de inscripción (individual u organización). 4. Paga la cuota anual. Una vez que Apple procese tu inscripción, tendrás acceso a [App Store Connect](https://appstoreconnect.apple.com). La inscripción suele tardar hasta 48 horas. Para las organizaciones, puede tardar más si se requiere verificación del D-U-N-S. ## Paso 2. Configura tu app en App Store Connect \{#step-2-set-up-your-app-in-app-store-connect\} Antes de poder vender compras in-app, completa la configuración inicial en App Store Connect. Esto incluye firmar acuerdos, añadir datos de pago y registrar tu app. ### Firma el Paid Applications Agreement \{#sign-the-paid-applications-agreement\} Apple requiere que firmes el Paid Applications Agreement antes de poder vender en el App Store. Esto aplica tanto a apps de pago como a compras in-app en apps gratuitas. 1. Ve a la página **Business** en [App Store Connect](https://appstoreconnect.apple.com/business). 2. Encuentra el acuerdo **Paid Apps** y haz clic en **Review and Agree**. 3. Completa la información requerida: - **Banking information**: Añade una cuenta bancaria donde Apple enviará tus ingresos. - **Tax information**: Rellena los formularios fiscales de los países donde quieres vender. - **Contact information**: Proporciona tus datos de contacto. :::important Debes completar las tres secciones (banking, tax, contact) para que el acuerdo entre en vigor. Hasta que el acuerdo esté activo, no podrás vender compras in-app. ::: ### Crea un Bundle ID \{#create-a-bundle-id\} Un Bundle ID identifica tu app de forma única en todo el ecosistema de Apple. Lo necesitas para registrar tu app en App Store Connect y para configurar la integración con Adapty. 1. Abre el [portal de Apple Developer](https://developer.apple.com/account). 2. Ve a **Certificates, Identifiers & Profiles** → **Identifiers**. 3. Haz clic en **+** para registrar un nuevo identificador. 4. Selecciona **App IDs** y haz clic en **Continue**. 5. Selecciona **App** como tipo y haz clic en **Continue**. 6. Rellena los campos: - **Description**: Un nombre para identificar este Bundle ID (p. ej., "My Subscription App"). - **Bundle ID**: Elige **Explicit** e introduce un identificador único en formato de dominio invertido (p. ej., `com.yourcompany.yourapp`). 7. En la sección **Capabilities**, desplázate hacia abajo y marca **In-App Purchase**. 8. Haz clic en **Continue** y luego en **Register**. ### Registra tu app en App Store Connect \{#register-your-app-in-app-store-connect\} 1. Ve a la página **Apps** en [App Store Connect](https://appstoreconnect.apple.com/apps). 2. Haz clic en **+** → **New App**. 3. Rellena los campos obligatorios: - **Platforms**: Selecciona **iOS**. - **Name**: El nombre de tu app tal como aparecerá en el App Store. - **Primary language**: El idioma predeterminado para los metadatos de tu app. - **Bundle ID**: Selecciona el Bundle ID que creaste en el paso anterior. - **SKU**: Un identificador único para tu app (no visible para los usuarios). Por ejemplo, `my_subscription_app_2025`. 4. Haz clic en **Create**. Tu app ya está registrada en App Store Connect y lista para la integración con Adapty. ## Qué hacer a continuación \{#whats-next\} - [Integración inicial con el App Store](initial_ios): Conecta tu app del App Store a Adapty - [Integración del SDK](quickstart-sdk): Integra el SDK de Adapty en el código de tu app - [Pruebas en sandbox](test-purchases-in-sandbox): Prueba tus compras in-app antes del lanzamiento - [Envía tu app iOS al App Store](submit-app-to-app-store): Sube tu build y envíala para revisión de Apple - [App Store Small Business Program](app-store-small-business-program): Reduce tu comisión del App Store del 30% al 15% --- # File: app-store-products --- --- title: "Producto en App Store" description: "Gestiona los productos de App Store de forma eficiente con las herramientas de suscripción de Adapty." --- Esta página explica cómo crear un producto en App Store Connect. Aunque esta información no está directamente relacionada con la funcionalidad de Adapty, puede servirte de ayuda si tienes problemas al crear productos en tu cuenta de App Store Connect. Para crear un producto que se vinculará a Adapty: 1. Abre **App Store Connect**. Ve a la sección [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) en el menú lateral izquierdo. <img src="/assets/shared/img/148c3b5-subscriptions.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Si aún no has creado un grupo de suscripciones, haz clic en el botón **Create** bajo el título **Subscription Groups** para iniciar el proceso. Los [Subscription Groups](https://developer.apple.com/help/app-store-connect/manage-subscriptions/offer-auto-renewable-subscriptions) en App Store Connect organizan y gestionan tus productos, lo que permite a los usuarios cambiar entre diferentes ofertas sin problemas. Ten en cuenta que no es posible crear una suscripción fuera de un grupo. 3. En la ventana **Create Subscription Group** que se abre, introduce un nombre para el nuevo grupo de suscripciones en el campo **Reference Name**. El nombre de referencia es una etiqueta o identificador que tú defines para distinguir y gestionar los distintos grupos de suscripciones dentro de tu app. El nombre de referencia no es visible para los usuarios; es exclusivamente para tu uso interno y organización. Te permite identificar y referirte fácilmente a grupos de suscripciones específicos al gestionarlos en la interfaz de App Store Connect. Esto resulta especialmente útil si tienes varias ofertas de suscripción o quieres categorizarlas de una forma que tenga sentido para la estructura de tu app. <img src="/assets/shared/img/3f93c44-create_subscription_group.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en el botón **Create** para confirmar la creación del grupo de suscripciones. 5. El grupo de suscripciones se crea y se abre. Ahora puedes crear suscripciones dentro del grupo. Haz clic en el botón **Create** bajo el título **Subscriptions**. Si añades una nueva suscripción a un grupo existente, haz clic en el botón **Plus** junto al título **Subscriptions**. <img src="/assets/shared/img/22fc643-add_subscription.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. En la ventana **Create Subscription** que se abre, introduce el nombre en el campo **Reference Name** y el código único de la suscripción en el campo **Product ID**. El Reference Name actúa como identificador exclusivo dentro de App Store Connect para tu suscripción in-app. No es visible para los usuarios en el App Store. Recomendamos usar una descripción clara y legible que represente con precisión la suscripción que quieres crear. Ten en cuenta que este nombre no puede superar los 64 caracteres. El Product ID es un identificador alfanumérico único imprescindible para acceder a tu producto durante la fase de desarrollo y para sincronizarlo con Adapty. En el Product ID solo se permiten caracteres alfanuméricos, puntos y guiones bajos. <img src="/assets/shared/img/04aca55-create_subscription.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Haz clic en el botón **Create** para confirmar la creación de la suscripción. 8. La suscripción se crea y se abre. Ahora selecciona la duración de la suscripción en la lista **Subscription Duration**. Aunque la duración ya esté indicada en el nombre de la suscripción, recuerda completar el campo **Subscription Duration**. <img src="/assets/shared/img/f56cf0f-subscription_duration.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 9. Ahora toca configurar el precio de la suscripción. Para ello, haz clic en el botón **Add Subscription Price** bajo el título Subscription Prices. Es posible que tengas que desplazarte hacia abajo para encontrarlo. 10. En la ventana **Subscription Price** que se abre, selecciona el país base en la lista **Country or Region** y la moneda base en la lista **Price**. Más adelante, Apple calculará automáticamente los precios para los 175 países o regiones basándose en este precio base y los tipos de cambio más recientes. <img src="/assets/shared/img/de1cec8-subscription_price.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 11. Haz clic en el botón **Next**. En la ventana **Price by Country or Region** que se abre, verás los precios recalculados automáticamente para todos los países. Puedes modificarlos si lo deseas. <img src="/assets/shared/img/2a047a6-price_by_country.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 12. Tras actualizar los precios regionales, continúa haciendo clic en el botón **Next** en la parte inferior de la ventana. 13. En la ventana **Confirm Subscription Price?** que se abre, revisa detenidamente los precios finales. Si necesitas corregirlos, puedes hacer clic en el botón **Back** para volver a la ventana **Price by Country or Region** y actualizarlos. Cuando estés conforme con los precios, haz clic en el botón **Confirm**. <img src="/assets/shared/img/d2b2031-confirm_prices.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 14. Después de cerrar la ventana **Confirm Subscription Price?**, recuerda hacer clic en el botón **Save** en la ventana de tu suscripción. Sin este paso, la suscripción no se creará y todos los datos introducidos se perderán. Ten en cuenta que los pasos descritos hasta ahora se centran en configurar una suscripción de renovación automática. Sin embargo, si quieres configurar otros tipos de compras in-app, puedes hacer clic en la pestaña **In-App Purchases** en la barra lateral, en lugar de "Subscriptions". Esto te llevará a la sección donde puedes gestionar y crear distintos tipos de compras in-app. <img src="/assets/shared/img/5663d85-in-app_purchases.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Añadir productos a Adapty \{#add-products-to-adapty\} Una vez que hayas terminado de añadir tus compras in-app, suscripciones y ofertas en App Store Connect, el siguiente paso es [añadir estos productos a Adapty](create-product). --- # File: apple-app-privacy --- --- title: "Privacidad de aplicaciones de Apple" description: "Entiende las políticas de privacidad de aplicaciones de Apple y su impacto en tu app de suscripciones." --- Apple exige una declaración de privacidad para todas las apps nuevas y actualizaciones tanto en la sección **App Privacy** de App Store Connect como en el archivo de manifiesto de la app. Adapty es una dependencia de terceros en tu app, por lo que debes declarar cómo usas Adapty en relación con los datos de usuario. ## Manifiesto de privacidad de aplicaciones de Apple \{#apple-app-privacy-manifest\} El [archivo de manifiesto de privacidad](https://developer.apple.com/documentation/bundleresources/describing-data-use-in-privacy-manifests), llamado `PrivacyInfo.xcprivacy`, describe qué datos privados utiliza tu app y por qué. Como propietario de la app, debes crear un archivo de manifiesto para ella. Además, si integras SDKs adicionales, asegúrate de que los archivos de manifiesto de aquellos incluidos en la lista de [SDKs que requieren manifiesto de privacidad y firma](https://developer.apple.com/support/third-party-SDK-requirements/) estén incluidos. Al compilar la app, Xcode tomará todos estos archivos de manifiesto y los fusionará en uno solo. Aunque Adapty no figura en la lista de [SDKs que requieren manifiesto de privacidad y firma](https://developer.apple.com/support/third-party-SDK-requirements/), las versiones 2.10.2 y superiores del SDK de Adapty lo incluyen para tu comodidad. Asegúrate de actualizar el SDK para obtener el manifiesto. Si bien Adapty no requiere que se incluya ningún dato en el archivo de manifiesto (también llamado informe de privacidad de la app), si usas el `customerUserId` de Adapty para el seguimiento, es necesario especificarlo en tu archivo de manifiesto de la siguiente manera: 1. Añade un diccionario al array `NSPrivacyCollectedDataTypes` en tu archivo de información de privacidad. 2. Añade las claves `NSPrivacyCollectedDataType`, `NSPrivacyCollectedDataTypeLinked` y `NSPrivacyCollectedDataTypeTracking` al diccionario. 3. Añade la cadena `NSPrivacyCollectedDataTypeUserID` (identificador del tipo de dato `UserID` en la [lista de categorías y tipos de datos que se deben declarar en el archivo de manifiesto](https://developer.apple.com/documentation/bundleresources/describing-data-use-in-privacy-manifests#Describe-the-data-your-app-or-third-party-SDK-collects)) para la clave `NSPrivacyCollectedDataType` en tu diccionario `NSPrivacyCollectedDataTypes`. 4. Añade `true` para las claves `NSPrivacyCollectedDataTypeTracking` y `NSPrivacyCollectedDataTypeLinked` en tu diccionario `NSPrivacyCollectedDataTypes`. 5. Usa la cadena `NSPrivacyCollectedDataTypePurposeProductPersonalization` como valor para la clave `NSPrivacyCollectedDataTypePurposes` en tu diccionario `NSPrivacyCollectedDataTypes`. Si segmentas tus paywalls a audiencias con atributos personalizados, considera detenidamente qué atributos personalizados usas y si coinciden con las [categorías y tipos de datos que se deben declarar en el archivo de manifiesto](https://developer.apple.com/documentation/bundleresources/describing-data-use-in-privacy-manifests). En ese caso, repite los pasos anteriores para cada tipo de dato. Una vez que hayas declarado todos los tipos y categorías de datos que recopilas, crea el informe de privacidad de tu app tal como se describe en la [documentación de Apple](https://developer.apple.com/documentation/bundleresources/describing-data-use-in-privacy-manifests#Create-your-apps-privacy-report). ## Declaración de privacidad de aplicaciones de Apple en App Store Connect \{#apple-app-privacy-disclosure-in-app-store-connect\} 1. En [App Store Connect](https://appstoreconnect.apple.com/), abre tu app y ve a **App Privacy**. Haz clic en **Get Started**. <img src="/assets/shared/img/app-privacy-get-started.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Selecciona **Yes, we collect data from this app** y haz clic en **Next**. <img src="/assets/shared/img/app-privacy-data-collection.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Tipos de datos \{#data-types\} La tabla siguiente lista los tipos de datos que Apple exige declarar e indica cuáles necesita Adapty. **Esto solo cubre Adapty.** Si tu app recopila datos adicionales a través de otros SDKs o tu propio código, selecciona también esos tipos de datos. ✅ = Requerido por Adapty 👀 = Puede ser requerido \(consulta los detalles a continuación\) ❌ = No requerido por Adapty — selecciona si tu app recopila estos datos por otros medios | Tipo de dato | Requerido | Nota | |-----------------------------------------------------------------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Identifiers | ✅ | <p>Si identificas usuarios con un customerUserId, selecciona 'User ID'.</p><p></p><p>Adapty recopila IDFA, por lo que debes seleccionar 'Device ID'.</p> | | Purchases | ✅ | Adapty recopila el historial de compras de los usuarios. | | Contact Info, incluyendo nombre, número de teléfono o dirección email | 👀 | Requerido si pasas datos personales como nombre, número de teléfono o dirección de email usando el método **`updateProfile`**. | | Usage Data | 👀 | Si usas SDKs de analítica como Amplitude, Mixpanel, AppMetrica o Firebase, puede ser necesario. | | Location | ❌ | Adapty no recopila datos de ubicación precisa. Selecciona si tu app los recopila. | | Health & Fitness | ❌ | Adapty no recopila datos de salud ni actividad física. Selecciona si tu app los recopila. | | Sensitive Info | ❌ | Adapty no recopila información sensible. Selecciona si tu app la recopila. | | User Content | ❌ | Adapty no recopila contenido de usuario. Selecciona si tu app lo recopila. | | Diagnostics | ❌ | Adapty no recopila datos de diagnóstico. Selecciona si tu app los recopila. | | Browsing History | ❌ | Adapty no recopila el historial de navegación. Selecciona si tu app lo recopila. | | Search History | ❌ | Adapty no recopila el historial de búsqueda. Selecciona si tu app lo recopila. | | Contacts | ❌ | Adapty no recopila listas de contactos. Selecciona si tu app las recopila. | | Financial Info | ❌ | Adapty no recopila información financiera. Selecciona si tu app la recopila. | ### Tipos de datos requeridos \{#required-data-types\} #### Purchases \{#purchases\} Al usar Adapty, debes declarar que tu app recopila **Purchase History**. <img src="/assets/shared/img/feb3b9f-CleanShot_2023-08-25_at_12.32.552x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> #### Identifiers \{#identifiers\} Al usar Adapty, debes declarar los siguientes identificadores: - **Device ID** — Adapty recopila IDFA. - **User ID** — requerido si identificas usuarios con **`customerUserId`**. <img src="/assets/shared/img/93f3daa-CleanShot_2023-08-25_at_12.35.272x.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Uso de datos \{#data-usage\} Después de guardar los **Data types**, deberás indicar cómo se usan los datos: 1. Haz clic en **Set up purchase history** dentro del bloque **Purchases**. <img src="/assets/shared/img/purchase-privacy.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Cuando Apple pregunte cómo se usan los datos del historial de compras, selecciona lo siguiente para Adapty: - **Analytics** — Adapty usa el historial de compras para analíticas de ingresos, cohortes y métricas. - **Product Personalization** — Adapty usa los datos de compras para la segmentación de audiencias y la segmentación de paywalls. - **App Functionality** — Adapty valida las compras, gestiona los niveles de acceso y hace seguimiento del estado de la suscripción. Selecciona propósitos adicionales si tu app usa los datos de compras de otras formas (por ejemplo, si envías eventos de compra a plataformas publicitarias mediante integraciones de Adapty). <img src="/assets/shared/img/purchase-history.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Next**. 4. Para **Device ID** y **User ID** (si se usa): 1. Haz clic en **Set up user/device ID** dentro del bloque **User/Device ID**. 2. Cuando Apple pregunte cómo se usan los datos de identificadores, selecciona lo siguiente para Adapty: - **App Functionality** — Adapty usa los identificadores para gestionar perfiles de usuario, vincular compras y hacer seguimiento de los niveles de acceso. Si envías datos de atribución a plataformas de terceros mediante integraciones de Adapty (como AppsFlyer o Adjust), selecciona también **Third-Party Advertising**. Selecciona propósitos adicionales si tu app usa los identificadores de otras formas. <img src="/assets/shared/img/user-id-privacy.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **Next**. --- # File: apple-family-sharing --- --- title: "Apple Family Sharing" description: "Habilita Apple Family Sharing en Adapty para admitir suscripciones compartidas." --- La función de Family Sharing de Apple permite distribuir compras in-app entre los miembros de una familia, lo que ofrece a los usuarios de apps orientadas a grupos —como servicios de streaming de vídeo o apps para niños— una forma cómoda de compartir suscripciones sin tener que compartir su Apple ID. Al permitir que hasta cinco miembros de la familia usen una suscripción, [Family Sharing](https://developer.apple.com/documentation/storekit/supporting-family-sharing-in-your-app) puede mejorar la fidelización y el engagement de los usuarios en tu app. En esta guía, explicamos cómo activar el uso compartido familiar para suscripciones y cómo Adapty gestiona las compras compartidas dentro de una familia. Para empezar a habilitar Family Sharing para un producto concreto, ve a [App Store Connect](https://appstoreconnect.apple.com/). Family Sharing está desactivado por defecto tanto para las compras in-app nuevas como para las existentes, por lo que es necesario habilitarlo individualmente para cada compra in-app. Puedes hacerlo fácilmente accediendo a la **página de tu app**, navegando a la página de la compra in-app correspondiente y seleccionando la opción **Turn On** en la sección Family Sharing. Ten en cuenta que una vez que actives Family Sharing para un producto, **no podrás desactivarlo**, ya que esto interrumpiría la experiencia de los usuarios que ya han compartido la suscripción con sus familiares. Además, ten en cuenta que solo los productos no consumibles y las suscripciones pueden compartirse. <img src="/assets/shared/img/6db165a-CleanShot_2023-03-28_at_17.15.342x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> En el modal que aparece, haz clic en el botón **Confirm** para finalizar el proceso. Tras hacerlo, la sección Family Sharing debería actualizarse y mostrar el mensaje "This subscription can be shared by everyone in a family group." Esto confirma que la suscripción ya está habilitada para Family Sharing y puede compartirse con hasta cinco miembros de la familia. Adapty facilita la compatibilidad con Family Sharing sin ningún esfuerzo adicional. Solo tienes que [configurar tus productos](app-store-products) desde App Store y, una vez que lo **actives** desde App Store Connect, **Family Sharing** estará disponible automáticamente en **Adapty** y se recibirá como un evento en el webhook. :::note Ten en cuenta que Family Sharing no es compatible con el entorno sandbox. ::: Ten en cuenta que cuando un usuario adquiere una suscripción y la comparte con sus familiares, puede haber un **retraso de hasta una hora** antes de que esté disponible para ellos. Apple diseñó este retraso para dar al usuario tiempo de cambiar de opinión y retirar el acceso si lo desea. Sin embargo, si la suscripción se renueva, los familiares reciben acceso sin ningún retraso. Cuando un usuario compra un producto in-app de Family Sharing, la transacción aparecerá en su recibo como de costumbre, pero con un nuevo campo llamado `in_app_ownership_type` con el valor `PURCHASED.` Además, se creará una nueva transacción para todos los miembros de la familia, que tendrá un `web_order_line_item_id` y un `original_transaction_id` diferentes a los de la compra original, así como un campo `in_app_ownership_type` con el valor `FAMILY_SHARED.` Para garantizar un cálculo de ingresos preciso, solo se contabilizan en los análisis de Adapty las transacciones con un `in_app_ownership_type` de `PURCHASED`. Las transacciones `FAMILY_SHARED` quedan excluidas de las métricas de ingresos y conversión. **Eventos enviados para transacciones de Family Sharing.** Las transacciones `FAMILY_SHARED` solo activan el evento **Access level updated**. Los eventos de suscripción por producto no se activan para los miembros de la familia. | Evento | `FAMILY_SHARED` | `PURCHASED` | | --- | --- | --- | | **Nivel de acceso actualizado** | Sí | Sí | | **Suscripción iniciada** | No | Sí | | **Prueba iniciada** | No | Sí | | **Suscripción renovada** | No | Sí | | **Suscripción expirada** | No | Sí | | **Suscripción reembolsada** | No | Sí | | **Problema de facturación detectado** | No | Sí | Si tu analítica downstream se basa en **Suscripción iniciada**, los miembros familiares no aparecerán ahí. Usa **Nivel de acceso actualizado** para detectar miembros familiares activos. Para identificar a los demás miembros de la familia en Adapty, puedes encontrarlos en los detalles del evento. Primero, localiza la transacción de compra familiar original. Luego, examina los detalles del evento para esa transacción, buscando específicamente el mismo producto, fecha de compra y fecha de vencimiento. Analizando los detalles del evento, puedes identificar otras transacciones de membresía familiar asociadas a la compra original. --- # File: app-store-small-business-program --- --- title: "App Store Small Business Program" description: "Comprende el Small Business Program de Apple, su impacto en tus ingresos y los análisis de Adapty" --- :::link Para el programa equivalente en Play Store, consulta [Google Reduced Service Fee](google-reduced-service-fee). ::: Las organizaciones que reciben hasta 1 millón de USD en ingresos anuales del App Store pueden participar en el [programa Small Business](https://developer.apple.com/app-store/small-business-program/) de Apple. Si te inscribes, la comisión estándar del 30% se reduce al **15%**. Los miembros del programa deben **cambiar su configuración en Adapty** para garantizar cálculos de ingresos correctos y el manejo adecuado de los eventos de integración. Este artículo describe: * [Cómo configurar Adapty](#configure-adapty) si tu app está inscrita en el Small Business Program * [Cómo inscribirte en el programa](#apply-for-the-program) si quieres reducir tu comisión del store ## Configurar Adapty \{#configure-adapty\} Adapty puede aplicar la comisión reducida a tus [análisis](analytics) y [eventos de integración](analytics-integration). Para activarlo, especifica tu estado en el Small Business Program por app. :::warning Configura tu estado en el SBP en Adapty **en cuanto recibas la aprobación**. Los cambios tardíos no pueden reescribir los eventos de webhook ya entregados ([detalles](#retroactive-setting-changes)). ::: 1. Abre [**App Settings** → **General**](https://app.adapty.io/account) 2. Busca la sección **Small Business Program**. 3. Haz clic en **Add period**. 4. Selecciona la fecha de inicio de la membresía. 5. Selecciona una fecha de fin, o activa la casilla **At the current moment** para extender este estado indefinidamente. Si en el futuro [pierdes la elegibilidad](#losing-eligibility), puedes modificar la fecha de fin. 6. Haz clic en **Apply**. Si tu organización sigue siendo elegible para el programa, la membresía se renueva automáticamente al siguiente año natural. Sin embargo, el estado de membresía solo se aplica **al rango de fechas que especifiques**. * Haz clic en **Add period** para añadir un nuevo período de membresía. * Para extender este estado indefinidamente, activa la casilla **At the current moment**. Para verificar tu configuración, abre el [gráfico de Revenue](revenue) y selecciona **Proceeds after store commission**. Confirma que los ingresos mostrados reflejan la comisión reducida. ## Inscribirse en el programa \{#apply-for-the-program\} ### Requisitos de elegibilidad \{#eligibility-requirements\} Apple determina la elegibilidad para el SBP en función de tus **ingresos anuales** — las ventas del año natural anterior **después** de la comisión del store e impuestos. Para ser elegible, los ingresos anuales de tu organización y sus <InlineTooltip tooltip="Cuentas de desarrollador asociadas">Cuentas en las que tú o tu organización tenéis una participación mayoritaria (>50%) o autoridad de toma de decisiones.</InlineTooltip> deben sumar 1 millón de USD o menos. Las organizaciones recién creadas son automáticamente elegibles para solicitar el programa. ### Antes de solicitar \{#before-you-apply\} Asegúrate de que: - Eres el titular de la cuenta en el Apple Developer Program - Has aceptado el último contrato de Paid Applications en App Store Connect - Puedes listar todas tus cuentas de desarrollador asociadas ### Inscripción \{#enrollment\} 1. Ve a la [página de inscripción del App Store Small Business Program](https://developer.apple.com/app-store/small-business-program/). 2. Haz clic en **Enroll** e inicia sesión con tu cuenta de Apple Developer. 3. Revisa la información precargada (nombre, correo electrónico, Team ID) y envíala. ### Revisión \{#review\} El proceso de revisión puede tardar más de un mes. Si cumples los requisitos, recibirás un correo de aprobación de Apple. Tras la aprobación, hay un período de espera. La comisión reducida entra en vigor el día 15 del [siguiente período fiscal de Apple](https://adapty.io/apple-fiscal-calendar/). No se aplica a transacciones anteriores. ### Pérdida de elegibilidad \{#losing-eligibility\} Cuando tus ingresos totales del año natural superan 1 millón de USD, pierdes la membresía del programa y Apple comienza a aplicar la comisión estándar del 30%. :::important Si tu negocio abandona el Small Business Program, **cambia inmediatamente la fecha de salida** en tu configuración. De lo contrario, Adapty seguirá calculando la comisión con la tasa reducida. ::: Puedes volver a calificar para el programa **el año siguiente** a que tus ingresos anuales caigan por debajo de 1 millón de USD. Lee los [términos oficiales del programa](https://developer.apple.com/app-store/small-business-program/) para más detalles. ## Cambios retroactivos en la configuración \{#retroactive-setting-changes\} --- no_index: true --- Cuando cambias el estado de comisión reducida en Adapty con una fecha de efecto retroactiva, la nueva tasa de comisión aparece en los datos de Adapty según distintos calendarios: | Dónde aparece la tasa | Qué ocurre después de cambiar la tasa | | --- | --- | | Dashboard de analíticas (Revenue, Proceeds, MRR, ARR) | Adapty aplica la nueva tasa en un plazo de 24 horas, cuando se ejecuta el recálculo diario. | | Exportaciones a S3, GCS y BigQuery | Adapty aplica la nueva tasa en la siguiente exportación programada. | | Eventos de webhook ya entregados | Adapty no puede modificar los eventos de webhook tras su entrega. Conservan la tasa anterior. | Si tu almacén de datos guarda ingresos procedentes de eventos de webhook, esos registros mantienen la tasa de comisión antigua. Para reconciliarlos, recupera el período afectado desde el dashboard de analíticas o genera una exportación nueva a S3, GCS o BigQuery. --- # File: android-products --- --- title: "Producto en Play Store" description: "Gestiona productos Android con Adapty, simplifica las compras in-app y optimiza las estrategias de monetización." --- Esta página proporciona orientación para crear un producto en Play Store. Aunque esta información puede no estar directamente relacionada con la funcionalidad de Adapty, resulta un recurso valioso si encuentras dificultades al crear productos en Google Play Console. Un producto hace referencia a un artículo o servicio digital que ofreces dentro de tu app en Play Store, normalmente disponible para su compra. Puede incluir productos in-app como compras únicas, suscripciones u otros bienes digitales que los usuarios pueden adquirir mientras usan tu aplicación. En el [sistema de facturación de Google](https://developer.android.com/google/play/billing/compatibility), las suscripciones pueden incorporar múltiples planes base, cada uno con distintos descuentos u ofertas. Esta estructura está compuesta por tres componentes principales: - **Suscripciones:** Representan conjuntos de beneficios que los usuarios pueden disfrutar durante un período específico (los artículos que se venden). Por ejemplo, un "nivel Gold" que ofrece funciones premium a los suscriptores. - **Planes base:** Representan configuraciones específicas de períodos de facturación, tipos de renovación y precios (cómo se venden los artículos). Por ejemplo, "anual con renovación automática" o "mensual prepago". - **Ofertas:** Implican descuentos disponibles para usuarios elegibles que modifican el precio del plan base. Por ejemplo, "prueba gratuita de 14 días para nuevos usuarios". ## ¿Cómo crear un producto en Play Store? \{#how-to-create-a-product-in-play-store\} Un producto hace referencia a un artículo o servicio digital que ofreces dentro de tu app, normalmente disponible para su compra. Puede incluir productos in-app como compras únicas, suscripciones u otros bienes digitales que los usuarios pueden adquirir mientras usan tu aplicación. Para configurar un producto para dispositivos Android: 1. Abre la sección [**Monetize** -> **Subscriptions**](https://console.cloud.google.com/iam-admin/serviceaccounts) o [**Monetize** -> **In-app products**](https://console.cloud.google.com/iam-admin/serviceaccounts) en el menú izquierdo de Google Play Console. <img src="/assets/shared/img/6eff1d1-subscription_GP.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el botón **Create subscription**. <img src="/assets/shared/img/af7fe02-create_subscription_GP.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Create subscription** que se abre, introduce el ID de la suscripción en el campo **Product ID** y el nombre de la suscripción en el campo **Name**. El ID del producto debe ser único, comenzar con un número o letra minúscula, y puede contener guiones bajos (\_) y puntos (.). Se utiliza para acceder a tu producto durante el desarrollo y sincronizarlo con Adapty. Una vez que se asigna un Product ID a un producto en Google Play Console, no puede reutilizarse en ninguna otra app, incluso si el producto se elimina. Al elegir el ID del producto, es recomendable seguir un formato estandarizado. Te recomendamos usar un enfoque más conciso y nombrar el producto `<nombre de suscripción>.<nivel de acceso>`. Así puedes controlar la duración y la frecuencia de facturación mediante planes base como semanal, mensual, etc. El nombre es solo para tu referencia; aparecerá en tu ficha de Google Play Store, así que puedes usar cualquier nombre descriptivo que necesites. Tiene un límite de 55 caracteres. 4. Haz clic en el botón **Create** para confirmar la creación de la suscripción. :::note Productos de suscripción de Google Play en Adapty Los productos de Adapty corresponden a los planes base de las suscripciones de Google Play, ya que esos son los productos disponibles para que los clientes compren. Adapty gestiona sin problemas la migración de las suscripciones existentes de Google Play junto con sus planes base correspondientes en los productos, sin que tengas que hacer nada adicional. Sin embargo, cuando añades un nuevo producto en Adapty, deberás proporcionar tanto el ID del plan base como el ID del producto. ::: ### Crear un plan base \{#create-a-base-plan\} Para los productos de suscripción, necesitarás añadir un plan base. Los planes base determinan el período de facturación, el precio y el tipo de renovación para que los clientes adquieran tu suscripción. Ten en cuenta que los clientes no compran directamente un producto de suscripción, sino que siempre adquieren un plan base dentro de una suscripción. Para crear un plan base: 1. Abre la sección [**Monetize** -> **Subscriptions**](https://console.cloud.google.com/iam-admin/serviceaccounts) en el menú izquierdo de Google Play Console. Una vez allí, localiza la suscripción a la que deseas añadir un plan base. 2. Haz clic en el botón **View subscription** junto a la suscripción. <img src="/assets/shared/img/4072a2a-subscriptions_GP.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Una vez que se abran los detalles de la suscripción, haz clic en el botón **Add base plan** que aparece bajo el título **Base plans and offers**. Es posible que tengas que desplazarte hacia abajo para encontrarlo. <img src="/assets/shared/img/b493b60-add_base_plan.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En la ventana **Add base plan** que se abre, introduce un identificador único para el plan base en el campo **Plan ID**. Debe comenzar con un número o letra minúscula, y puede contener números (0-9), letras minúsculas (a-z) y guiones (-). Completa también los campos obligatorios. <img src="/assets/shared/img/8146763-CleanShot_2023-07-20_at_16.51.412x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Especifica los precios por región. <img src="/assets/shared/img/8b26e1d-prices.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Haz clic en el botón **Save** para finalizar la configuración. 7. Haz clic en el botón **Activate** para activar el plan base. Ten en cuenta que los productos de suscripción solo pueden tener un único plan base con duración y tipo de renovación consistentes en Adapty. ### Productos de respaldo \{#fallback-products\} :::warning Compatibilidad con planes base no retrocompatibles Las versiones antiguas de los SDK de Adapty no son compatibles con las funciones de Google Billing Library v5+, concretamente con múltiples planes base por producto de suscripción y ofertas. Solo los planes base marcados como **[backwards compatible](https://support.google.com/googleplay/android-developer/answer/12124625?hl=en#backwards_compatible)** en Google Play Console son accesibles con estas versiones del SDK. Ten en cuenta que solo un plan base por suscripción puede marcarse como retrocompatible. ::: <img src="/assets/shared/img/b5e70cb-CleanShot_2023-07-20_at_17.03.252x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Para aprovechar al máximo las configuraciones y funciones mejoradas de suscripciones de Google en Adapty, ofrecemos la posibilidad de configurar un producto de respaldo retrocompatible. Este producto de respaldo se utiliza exclusivamente para apps que usan versiones antiguas del SDK de Adapty. Al crear productos de Google Play, ahora tienes la opción de indicar si el producto debe marcarse como retrocompatible en Play Console. Adapty utiliza esta información para determinar si el producto puede ser comprado por versiones antiguas del SDK (versiones 2.5 e inferiores). Supongamos que tienes una suscripción llamada `subscription.premium` que ofrece dos planes base: semanal (retrocompatible) y mensual. Si añades el producto `subscription.premium:weekly` a Adapty, no necesitas indicar un producto retrocompatible. Sin embargo, en el caso del producto `subscription.premium:monthly`, deberás especificar un producto retrocompatible. No hacerlo podría provocar una compra no deseada del producto `subscription.premium:weekly` en la biblioteca de facturación 4 de Google. Para resolver este escenario, debes crear un producto separado donde el plan base también sea mensual y esté marcado como retrocompatible. Esto garantiza que los usuarios que seleccionen la opción `subscription.premium:monthly` sean facturados correctamente con la frecuencia prevista. ## Añadir productos a Adapty \{#add-products-to-adapty\} Una vez que hayas completado la incorporación de tus compras in-app, suscripciones y ofertas en App Store Connect, el siguiente paso es [añadir estos productos a Adapty](create-product). --- # File: google-play-data-safety --- --- title: "Seguridad de datos de Google Play" description: "Garantiza el cumplimiento de las políticas de seguridad de datos de Google Play en Adapty." --- La sección de seguridad de datos disponible en Google Play ofrece a los desarrolladores un método sencillo para informar a los usuarios sobre los datos que su app recopila o comparte, así como destacar las medidas críticas de privacidad y seguridad. Esta información permite a los usuarios tomar decisiones más informadas al elegir qué apps descargar y usar. Aquí tienes una guía breve sobre los datos que Adapty recopila para ayudarte a proporcionar la información requerida a Google Play. ## Recopilación y seguridad de datos \{#data-collection-and-security\} <img src="/assets/shared/img/3508c24-image4.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> **¿Tu app recopila o comparte alguno de los tipos de datos de usuario requeridos?** Selecciona 'Sí', ya que Adapty recopila el historial de compras del cliente. **¿Todos los datos de usuario recopilados por tu app están cifrados en tránsito?** Selecciona 'Sí', ya que Adapty cifra los datos en tránsito. **¿Ofreces a los usuarios una forma de solicitar la eliminación de sus datos?** Si seleccionas 'Sí', asegúrate de que tus clientes tengan una forma de contactar con tu equipo de soporte para solicitar la eliminación de sus datos. Podrás eliminar al cliente directamente desde el Adapty Dashboard o mediante la API REST. ## Tipos de datos \{#data-types\} A continuación encontrarás la lista de tipos de datos que Google requiere para el reporte, junto con la especificación de si Adapty recopila cada tipo de dato en particular. | Tipo de dato | Detalles | | :----------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Ubicación | Adapty no lo recopila | | Salud y fitness | Adapty no lo recopila | | Fotos y vídeos | Adapty no lo recopila | | Archivos y documentos | Adapty no lo recopila | | Calendario | Adapty no lo recopila | | Contactos | Adapty no lo recopila | | Contenido del usuario | Adapty no lo recopila | | Historial de navegación | Adapty no lo recopila | | Historial de búsqueda | Adapty no lo recopila | | Información y rendimiento de la app | Adapty no lo recopila | | Navegación web | Adapty no lo recopila | | Información de contacto | Adapty no lo recopila | | Información financiera | Adapty recopila el historial de compras de los usuarios | | Información personal e identificadores | Adapty recopila el ID de usuario y otra información de contacto identificable, como nombre, dirección de correo electrónico, número de teléfono, etc., si los pasas explícitamente al SDK de Adapty. | | Identificadores de dispositivo y otros | Adapty recopila datos sobre el ID del dispositivo. | ## Uso y tratamiento de datos \{#data-usage-and-handling\} ### IDs de usuario \{#user-ids\} **1. ¿Estos datos se recopilan, se comparten o ambas cosas?** Adapty recopila estos datos. Si usas integraciones entre Adapty y terceros que no se consideran proveedores de servicios, puede que también tengas que indicar "Compartido" aquí. **2. ¿Estos datos se procesan de forma efímera?** Selecciona 'No'. **3. ¿Estos datos son obligatorios para tu app o los usuarios pueden elegir si se recopilan?** La recopilación de estos datos es obligatoria y no se puede desactivar. <img src="/assets/shared/img/2c60161-image5.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> **4. ¿Por qué se recopilan estos datos de usuario? / ¿Por qué se comparten estos datos de usuario?** Marca las casillas 'Funcionalidad de la app' y 'Análisis'. <img src="/assets/shared/img/07a3c9e-image2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Información financiera \{#financial-info\} Si usas Adapty, debes declarar que tu app recopila información sobre el 'Historial de compras' en la sección de tipos de datos de Google Play Console. <img src="/assets/shared/img/1057870-image7.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Identificadores de dispositivo u otros \{#device-or-other-ids\} <img src="/assets/shared/img/d10f132-CleanShot_2023-03-01_at_17.55.312x.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <img src="/assets/shared/img/ccb1a2a-image5.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Pasos siguientes \{#next-steps\} Una vez que hayas completado las selecciones de seguridad de datos, Google mostrará una vista previa de la sección de privacidad de tu app. Si has seleccionado "Información financiera" e "Identificadores de dispositivo u otros" como se mencionó anteriormente, la información de privacidad debería aparecer de forma similar al siguiente ejemplo. <img src="/assets/shared/img/e8d9b73-image3.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Si estás listo para enviar tu app a revisión, consulta nuestro documento [Lista de verificación para el lanzamiento](release-checklist) para obtener más orientación sobre cómo preparar tu app para el envío. --- # File: google-reduced-service-fee --- --- title: "Tarifa de Servicio Reducida de Google" description: "Comprende la Tarifa de Servicio Reducida de Google, su impacto en tus ingresos y los análisis de Adapty" --- :::link Para el programa equivalente en App Store, consulta el [Programa para Pequeñas Empresas de App Store](app-store-small-business-program). ::: El [programa de Tarifa de Servicio Reducida](https://support.google.com/googleplay/android-developer/answer/112622?hl=en) de Google Play reduce la comisión sobre tu primer millón de USD en ganancias anuales del 30% al **15%**. Las ganancias que superen el millón de USD en el mismo año calendario se cobran a la tarifa estándar del 30%. :::note Desde el 1 de enero de 2022, Google cobra el 15% en todas las suscripciones de renovación automática independientemente de este programa. La Tarifa de Servicio Reducida beneficia principalmente a las compras in-app que no son suscripciones y a las apps de pago. ::: Los miembros del programa deben **cambiar su configuración en Adapty** para garantizar cálculos de ingresos correctos y el manejo adecuado de los eventos de integración. Este artículo describe: * [Cómo configurar Adapty](#configure-adapty) si tu app está inscrita en el programa de Tarifa de Servicio Reducida * [Cómo inscribirse en el programa](#enroll-in-the-program) si quieres reducir tu comisión del store ## Configurar Adapty \{#configure-adapty\} Adapty puede aplicar la tarifa de comisión reducida a tus [análisis](analytics) y [eventos de integración](analytics-integration). Para habilitarlo, especifica tu estado en la Tarifa de Servicio Reducida para cada app por separado. :::warning Configura tu estado de Tarifa de Servicio Reducida en Adapty **en cuanto te inscribas**. Los cambios posteriores no pueden reescribir los eventos de webhook ya entregados ([detalles](#retroactive-setting-changes)). ::: 1. Abre [**App Settings** → **General**](https://app.adapty.io/account). 2. Busca la sección **Reduced Service Fee**. 3. Haz clic en **Add period**. 4. Selecciona la fecha de inicio de la membresía. 5. Selecciona una fecha de fin, o activa la casilla **At the current moment** para extender este estado indefinidamente. Si tus [ganancias anuales superan el millón de USD](#exceeding-the-threshold), puedes modificar la fecha de fin. 6. Haz clic en **Apply**. El estado de membresía solo se aplica **al rango de fechas que especifiques**. El programa se reinicia cada año calendario. * Haz clic en **Add period** para añadir un nuevo período de membresía. * Para extender este estado indefinidamente, activa la casilla **At the current moment**. Para verificar tu configuración, abre el [gráfico de Ingresos](revenue) y selecciona **Proceeds after store commission**. Confirma que los ingresos mostrados reflejan la tarifa de comisión reducida. ## Inscribirse en el programa \{#enroll-in-the-program\} ### Requisitos de elegibilidad \{#eligibility-requirements\} Google determina la elegibilidad en función de tus **ganancias anuales** en todas las cuentas de tu <InlineTooltip tooltip="Grupo de Cuentas">Un grupo de cuentas de desarrollador cuyas ganancias se contabilizan conjuntamente. Debes designar tu cuenta de desarrollador como la cuenta de desarrollador principal y vincular las cuentas asociadas al grupo.</InlineTooltip>. La tarifa del 15% se aplica al primer millón de USD en ganancias anuales combinadas. Cualquier ganancia por encima de ese umbral se cobra al 30%. ### Antes de inscribirte \{#before-you-enroll\} Asegúrate de que: - Tienes un [perfil de pago](https://support.google.com/googleplay/android-developer/answer/10632485) configurado - Puedes listar todas tus cuentas de desarrollador asociadas ### Inscripción \{#enrollment\} 1. Ve a la [Google Play Console](https://play.google.com/console/). 2. Crea un Grupo de Cuentas y establece tu cuenta de desarrollador como la cuenta de desarrollador principal. 3. Vincula las cuentas de desarrollador asociadas al grupo. 4. Acepta los términos del programa de Tarifa de Servicio Reducida. Tras completar estos pasos, Google te inscribe automáticamente. No hay revisión manual ni correo de aprobación. Para instrucciones detalladas, consulta la [guía de inscripción](https://support.google.com/googleplay/android-developer/answer/10632485) de Google. ### Superar el umbral \{#exceeding-the-threshold\} Cuando tus ganancias anuales combinadas superen el millón de USD, Google cobra el 30% sobre la parte que excede el umbral durante el resto de ese año calendario. :::important Si tus ganancias anuales superan el millón de USD, **cambia inmediatamente la fecha de salida** en tu configuración de Adapty. De lo contrario, Adapty seguirá calculando la comisión a la tarifa reducida. ::: El programa se reinicia cada año calendario. Si tus ganancias superan el millón de USD en un año, la tarifa del 15% se aplica automáticamente de nuevo a tu primer millón de USD el año siguiente. No es necesario volver a inscribirse. Lee los [términos oficiales del programa](https://support.google.com/googleplay/android-developer/answer/112622?hl=en) para más detalles. ## Cambios retroactivos en la configuración \{#retroactive-setting-changes\} --- no_index: true --- Cuando cambias el estado de comisión reducida en Adapty con una fecha de efecto retroactiva, la nueva tasa de comisión aparece en los datos de Adapty según distintos calendarios: | Dónde aparece la tasa | Qué ocurre después de cambiar la tasa | | --- | --- | | Dashboard de analíticas (Revenue, Proceeds, MRR, ARR) | Adapty aplica la nueva tasa en un plazo de 24 horas, cuando se ejecuta el recálculo diario. | | Exportaciones a S3, GCS y BigQuery | Adapty aplica la nueva tasa en la siguiente exportación programada. | | Eventos de webhook ya entregados | Adapty no puede modificar los eventos de webhook tras su entrega. Conservan la tasa anterior. | Si tu almacén de datos guarda ingresos procedentes de eventos de webhook, esos registros mantienen la tasa de comisión antigua. Para reconciliarlos, recupera el período afectado desde el dashboard de analíticas o genera una exportación nueva a S3, GCS o BigQuery. --- # File: google-play-quota-increase --- --- title: "Solicitar aumento de cuota de la Google Play Developer API" description: "Solicita un aumento de cuota para la Google Play Developer API si superas el límite predeterminado durante importaciones históricas o con una base de suscriptores grande." --- Adapty utiliza la [Google Play Developer API](https://developers.google.com/android-publisher) para validar compras y sincronizar datos de suscripciones. La cuota predeterminada para esta API es de 3.000 consultas por minuto. Si tu app supera este límite, Google te envía una notificación por correo electrónico. Esto ocurre habitualmente durante las [importaciones de datos históricos](importing-historical-data-to-adapty) o en apps con un gran número de suscriptores activos. Para evitar interrupciones, solicita un aumento de cuota a Google antes de realizar una importación grande o si recibiste una notificación de cuota superada. ## Antes de empezar \{#before-you-start\} Activa las [notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn) si aún no lo has hecho. Las RTDN entregan actualizaciones de suscripciones mediante notificaciones push en lugar de sondeo, lo que reduce el consumo de la API. Google puede rechazar las solicitudes de aumento de cuota si las RTDN no están habilitadas. ## Reúne la información necesaria \{#gather-required-information\} Antes de abrir el formulario de solicitud, recopila los siguientes datos: - **ID de cuenta de desarrollador**: Para encontrarlo, en la [Google Play Console](https://play.google.com/console/), ve a **Settings > Developer account > Account details**. El ID aparece en la parte superior de la página. - **Nombre del paquete de la app**: El nombre del paquete de tu app Android (por ejemplo, `com.example.app`). Encuéntralo en la Google Play Console en la página **Dashboard** de tu app. - **Número de proyecto de Google Cloud**: Para encontrarlo, en la [Google Cloud Console](https://console.cloud.google.com/), selecciona tu proyecto. El número de proyecto aparece en la página **Dashboard**. ## Solicita el aumento de cuota \{#request-the-quota-increase\} 1. Abre el [formulario de solicitud de aumento de cuota de la Google Play Developer API](https://support.google.com/googleplay/android-developer/contact/apiqr). 2. Introduce tu ID de cuenta de desarrollador, el nombre del paquete de la app y el número de proyecto de Google Cloud. 3. Selecciona la API y el bloque de cuota que necesita un aumento. Si recibiste un correo de Google sobre el exceso de cuota, en él se especifica qué bloque es. 4. En el campo de justificación, explica que utilizas un servicio de gestión de suscripciones de terceros que requiere acceso a la API para validar compras y sincronizar datos de suscripciones. 5. Para la cuota solicitada, introduce la cantidad que necesitas. Si no estás seguro de cuánto solicitar, revisa tu uso actual en la [Google Cloud Console](https://console.cloud.google.com/) en **IAM & Admin > Quotas** (filtra por "Google Play Android Developer API"); luego envía tus datos de uso y el número de entradas históricas que planeas importar a [support@adapty.io](mailto:support@adapty.io) para que podamos ayudarte a determinar la cantidad correcta. 6. Envía el formulario. Google normalmente procesa las solicitudes de aumento de cuota en unos pocos días hábiles. --- # File: prepare-your-app-for-store-review --- --- title: "Prepara tu app para la revisión en las stores" description: "Consejos para que tu aplicación sea aprobada en App Store y Google Play Store" --- Este artículo describe el proceso que siguen las stores al revisar las aplicaciones enviadas, y ofrece consejos para conseguir una aprobación más rápida. La información se basa en las guías oficiales de envío: * [Guías de envío de App Store](https://developer.apple.com/app-store/review/guidelines/) * [Guías de envío de Google Play Store](https://play.google/developer-content-policy/) :::important Ambas stores siguen un proceso de revisión similar. Cuando una política solo aplica a una de ellas, el artículo menciona la store por su nombre. ::: Los usuarios de Adapty deben prestar especial atención al cumplimiento de los requisitos relacionados con [paywalls y compras in-app](#iap-related-requirements), ya que son de los motivos de rechazo más frecuentes. ## Antes de empezar \{#before-you-begin\} Confirma que tu aplicación está lista para el envío. Adapty ofrece una [lista de verificación para el lanzamiento](release-checklist) para preparar tu app para su publicación. Google Play Store exige a los editores nuevos que [prueben la app](https://support.google.com/googleplay/android-developer/answer/14151465?hl=en) antes de enviarla. La prueba debe contar con al menos 12 personas y durar un mínimo de 14 días consecutivos. Este requisito se introdujo en 2025 para reducir el número de apps con errores que llegan al equipo de revisión de Google. ## Resumen del proceso de revisión \{#review-process-overview\} #### Paso 1: El análisis automatizado \{#step-1-the-automated-screening\} Tanto App Store como Google Play Store siguen un proceso de revisión en dos pasos. Justo después del envío, tu app pasa por un análisis automatizado que puede tardar varias horas. Ambas stores analizan tu app en busca de malware, y Google hace especial hincapié en este proceso. Busca indicadores de comportamiento malicioso, como contacto con servidores sospechosos y acceso injustificado a datos del usuario. Si tu aplicación se considera potencialmente dañina, se marca y se pasa a un analista de seguridad humano. La [documentación de Google Play Protect](https://developers.google.com/android/play-protect/cloud-based-protections#machine-learning) contiene una lista aproximada de las comprobaciones realizadas en este paso. Las stores también verifican la presencia de los metadatos necesarios, la ausencia de dependencias dañinas o muy desactualizadas, y la integridad de tu build. #### Paso 2: La revisión humana \{#step-2-the-human-review\} Una vez que tu app supera el análisis automatizado, la examina un revisor humano. Este paso puede tardar varios días, según la complejidad de tu app y la cola de revisión en ese momento. Las apps que procesan datos sensibles tardan más en revisarse. ## Requisitos generales \{#general-requirements\} ### Estabilidad \{#stability\} Las aplicaciones que se cierran inesperadamente durante la revisión son rechazadas. Los revisores pueden simular intencionalmente condiciones de red poco fiables, por lo que la app debe ser capaz de gestionarlas correctamente. ### Completitud \{#completeness\} Tanto Apple como Google imponen un requisito de *completitud* ("funcionalidad mínima") sobre el contenido de la store. * Los marcadores de posición, las pantallas de "próximamente" y las funcionalidades rotas dan lugar a rechazos en apps de iOS. * Google es [más flexible](https://support.google.com/googleplay/android-developer/answer/9898783?hl=en), especialmente si tu aplicación está en [Acceso Anticipado](https://knowledge.workspace.google.com/admin/users/access/turn-early-access-apps-on-or-off-for-users). * Ambas stores **rechazan apps** con poca o ninguna funcionalidad. Esto incluye apps que muestran una sola imagen, un archivo PDF o una página web. La falta de contenido entra en la misma categoría. * Si la app no cumple lo que anuncias, será rechazada. * Si configuras una compra in-app en tu dashboard pero no la incluyes en el build, la app será rechazada. ### Precisión de los metadatos \{#metadata-accuracy\} La información engañosa, inexacta o inconsistente en la descripción, capturas de pantalla y otros metadatos puede llevar al rechazo. No uses tu ficha en la store para anunciar funcionalidades futuras de la app. Si la app no está diseñada para el público en general, el revisor buscará documentación adicional que explique sus flujos de trabajo. Incluye instrucciones claras en los metadatos de la app. ### Clasificación de contenido \{#content-rating\} El contenido de tu app debe coincidir con la clasificación que has declarado. ### Aspectos legales \{#legal-aspects\} * La política de privacidad de tu app debe ser accesible desde dentro de la app. Puedes usar el [botón de enlace](paywall-buttons#links) del Paywall Builder. * Exige a los usuarios que lean y acepten cualquier acuerdo legal **antes** de que entre en vigor. * Indica la presencia de publicidad en tu app. No hacerlo puede resultar en un rechazo. * Si tu app de iOS incluye compras in-app, debes aceptar el **Paid Apps Agreement** en tu dashboard de App Store Connect. ### Autenticación \{#authentication\} Si parte del contenido de tu app solo está disponible tras autenticarse, proporciona credenciales de acceso válidas al revisor de la store. No poder acceder al contenido de forma completa justifica un rechazo. Si tu app permite a los usuarios crear una cuenta, también debe permitirles eliminarla. Dirigir a los usuarios a soporte por correo electrónico o a un sitio web no cumple este requisito. ### Acceso y privacidad \{#access-and-privacy\} Los metadatos de la app deben indicar claramente el motivo de cada permiso solicitado. Los permisos más sensibles (por ejemplo, acceso a mensajes de texto y registros de llamadas) pueden requerir una demostración en vídeo. El mismo principio aplica a los datos sensibles del usuario: si los solicitas, explica por qué. ## Requisitos relacionados con las compras in-app \{#iap-related-requirements\} Las infracciones de política de negocio son uno de los motivos de rechazo más habituales. Si los principales métodos de monetización de tu aplicación son las suscripciones y las compras in-app, estará sujeta a un escrutinio mayor. ### Requisitos del paywall \{#paywall-requirements\} Los revisores de apps esperan paywalls sencillos y fáciles de entender. Si se sospecha que estás manipulando a los usuarios, la app es rechazada. Si varias revisiones encuentran evidencias de prácticas engañosas, tu cuenta puede ser desactivada y tu app [suspendida](https://support.google.com/googleplay/android-developer/community-guide/287283557/app-suspended-for-repeated-rejections?hl=en). Google Play utiliza un [sistema de sanciones](https://support.google.com/googleplay/android-developer/answer/9899234?hl=en) que puede llevar a que todas tus apps sean eliminadas. Sigue estas prácticas en el diseño de tu paywall: - **Sé transparente y directo.** Muestra el precio exacto, la frecuencia de facturación, los beneficios y las condiciones de cancelación de los productos antes de invitar al usuario a comprar. Diferencia claramente entre compras únicas y productos que requieren pagos recurrentes. Si un producto incluye una prueba gratuita, indica claramente su duración y condiciones. No uses un lenguaje intencionalmente confuso para engañar al usuario. - **Sé coherente.** Los precios de los productos deben coincidir en la ficha de App Store, las pantallas dentro de la app, las pantallas de gestión de suscripciones y el contenido de marketing. Cualquier discrepancia de precios, por pequeña que sea, es motivo de rechazo. El Paywall Builder de Adapty sincroniza automáticamente los precios entre tu paywall y tu producto en App Store Connect. Si tu paywall está codificado manualmente, debes [obtener el precio de cada producto](fetch-paywalls-and-products) desde su array de datos. - **Muestra todos los niveles en igualdad de condiciones.** No preselecciones la opción más cara ni ocultes las más baratas. - **Evita los "dark patterns".** No generes una falsa sensación de urgencia o escasez. No fuerces a los usuarios a realizar compras haciendo que las funcionalidades gratuitas sean incómodas o difíciles de encontrar intencionalmente. ### Garantía de acceso \{#access-guarantee\} La aplicación debe garantizar el derecho de los usuarios a acceder a sus compras. * **Acceso inmediato** Una compra exitosa debe desbloquear el acceso al producto de forma inmediata, sin retrasos visibles. Los estados intermedios de autorización de pago no deben generar errores ni romper la experiencia de usuario. Una compra exitosa debe ocultar el paywall de inmediato. Si sigues mostrando el paywall tras una compra, impides que el usuario acceda al contenido que ha pagado. * **Restauración del acceso** El usuario debe poder restaurar el acceso al producto desde un nuevo dispositivo. Coloca el botón de restaurar en un lugar visible. Si creaste el paywall con el [Paywall Builder](adapty-paywall-builder), el botón de restaurar activa automáticamente el proceso de restauración. Si [implementaste un paywall manual](ios-implement-paywalls-manually), añade código que ejecute el método [restorePurchases](restore-purchase). Adapty restaurará el nivel de acceso del usuario, **salvo** que uses el SDK en [modo observador](observer-vs-full-mode). La app debe ser capaz de reconocer las compras in-app realizadas desde la página de la store del producto o desde cualquier otro lugar de la store. ### Métodos de pago apropiados \{#appropriate-payment-methods\} Ambas stores prohíben la venta de bienes físicos con compras in-app y exigen la facturación dentro de la store para la mayoría de los bienes digitales. El requisito de facturación dentro de la store no aplica en algunas jurisdicciones geográficas, incluidas EE. UU. y la UE. Según el país, puede que puedas [saltarte la facturación dentro de la store por completo](https://support.google.com/googleplay/android-developer/answer/16497028) o [presentar al usuario una elección](https://support.google.com/googleplay/android-developer/answer/13821247) entre la facturación de la store y la facturación alternativa. Algunas categorías de apps (como lectores de e-books o apps de citas) pueden ser elegibles para métodos de pago alternativos incluso fuera de estas regiones. Consulta las guías oficiales de las stores para más detalles. :::tip A diferencia de [Google](https://support.google.com/googleplay/android-developer/answer/13821247), Apple no ofrece una lista definitiva de países que permiten métodos de facturación alternativos. A medida que nuevas jurisdicciones aprueben leyes similares, la disponibilidad irá aumentando. Lee la documentación correspondiente a tu país en particular antes de continuar. ::: Ten en cuenta que ambas stores aplican sus propias normas para las integraciones con proveedores de pago, y siguen cobrando comisiones por las transacciones que usan estos servicios. ## Cómo gestionar un rechazo \{#handling-rejection\} Si tu aplicación es rechazada, el revisor indicará qué directriz(ces) ha infringido. Lee la directriz completa y corrígela: * [Guías de envío de App Store](https://developer.apple.com/app-store/review/guidelines/) * [Guías de envío de Google Play Store](https://play.google/developer-content-policy/) Si crees que el rechazo fue injusto, tienes derecho a apelar. Aporta pruebas de cumplimiento y contacta con la store. * No actualices la app mientras está siendo revisada. * Cada vez que envíes tu aplicación para revisión, puede que te toque un revisor diferente. Esto puede jugar a tu favor o en tu contra. * No corrijas los problemas uno a uno. Envía la app para revisión cuando todas las correcciones estén listas. * Si Google Play rechazó tu aplicación por infracciones de política, actualiza los datos en cuestión en todos los canales, incluso si están pausados o inactivos. * Las revisiones posteriores suelen tardar menos que la primera. * La revisión urgente puede estar disponible para errores críticos y plazos inminentes — úsala con moderación. ## Después de la revisión: supervisión continua \{#after-the-review-continuous-monitoring\} Ambas stores continúan supervisando tu aplicación incluso después de que supere el proceso de revisión. Si la función de tu app cambia tras la aprobación (por ejemplo, debido a código cargado de forma dinámica), será marcada y dada de baja. Una avalancha de comentarios negativos de los usuarios también es motivo de un escrutinio adicional. Entre 2024 y 2025, Google [eliminó el 47% de las apps de su Play Store](https://techcrunch.com/2025/04/29/google-play-sees-47-decline-in-apps-since-start-of-last-year/) para aumentar su calidad media. Abandonar tu app también conlleva riesgos. Tanto [Google](https://www.cnet.com/tech/mobile/google-play-store-will-hide-apps-that-havent-been-updated-in-years/) como [Apple](https://developer.apple.com/support/app-store-improvements/#:~:text=Developers%20of%20apps%20that%20have,launch%20will%20be%20removed%20immediately.) dan de baja apps que no reciben actualizaciones ni descargas. ## Ver también \{#see-also\} * [Pruebas en sandbox](test-purchases-in-sandbox) * [Lista de verificación para el lanzamiento](release-checklist) --- # File: firebase-apps --- --- title: "Aplicaciones Firebase" description: "Integra Firebase con Adapty para mejorar el análisis de usuarios y el seguimiento de suscripciones en tu app móvil." --- Esta página explica cómo integrar Adapty en tu app si funciona con Firebase. :::note Primeros pasos Estos no son todos los pasos necesarios para que Adapty funcione, solo algunos consejos útiles para la integración con Firebase. Si quieres integrar Adapty en tu app, lee primero la [Guía de inicio rápido](quickstart). ::: ## Identificación de usuarios \{#user-identification\} Si usas Firebase Auth, este fragmento de código puede ayudarte a mantener tus usuarios sincronizados entre Firebase y Adapty. Ten en cuenta que es solo un ejemplo y debes considerar las particularidades de autenticación de tu app. <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS with Firebase" default> ```swift showLineNumbers @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Configure Adapty before Firebase Adapty.activate("YOUR_API_KEY") Adapty.delegate = self // Configure Firebase FirebaseApp.configure() // Add state change listener for Firebase Authentication Auth.auth().addStateDidChangeListener { (auth, user) in if let uid = user?.uid { // identify Adapty SDK with new Firebase user Adapty.identify(uid) { error in if let e = error { print("Sign in error: \(e.localizedDescription)") } else { print("User \(uid) signed in") } } } } return true } } extension AppDelegate: AdaptyDelegate { // MARK: - Adapty delegate func didReceiveUpdatedPurchaserInfo(_ purchaserInfo: PurchaserInfoModel) { // You can optionally post to the notification center whenever // purchaser info changes. // You can subscribe to this notification throughout your app // to refresh tableViews or change the UI based on the user's // subscription status NotificationCenter.default.post(name: NSNotification.Name(rawValue: "com.Adapty.PurchaserInfoUpdatedNotification"), object: purchaserInfo) } } ``` </TabItem> <TabItem value="kotlin" label="Android with Firebase" default> ```kotlin showLineNumbers class App : Application() { override fun onCreate() { super.onCreate() // Configure Adapty Adapty.activate(this, "YOUR_API_KEY") Adapty.setOnPurchaserInfoUpdatedListener(object : OnPurchaserInfoUpdatedListener { override fun onPurchaserInfoReceived(purchaserInfo: PurchaserInfoModel) { // handle any changes to subscription state } }) // Add state change listener for Firebase Authentication FirebaseAuth.getInstance().addAuthStateListener { auth -> val currentUserId = auth.currentUser?.uid if (currentUserId != null) { // identify Adapty SDK with new Firebase user Adapty.identify(currentUserId) { error -> if (error == null) { //success } } } else { Adapty.logout { } } } } } ``` </TabItem> </Tabs> --- # File: refund-saver --- --- title: "Refund Saver" description: "Usa Adapty Refund Saver para minimizar los reembolsos y maximizar los ingresos." --- El Refund Saver ayuda a los usuarios de Adapty a gestionar eficazmente las solicitudes de reembolso de la App Store de Apple mediante automatización. Al simplificar este proceso, reduce la pérdida de ingresos y ahorra tiempo. Con notificaciones en tiempo real e información accionable, esta herramienta garantiza que atiendas las solicitudes de reembolso de forma efectiva cumpliendo las directrices de Apple. <img src="/assets/shared/img/refunds-chart.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '500px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## ¿Qué es el Refund Saver? \{#what-is-the-refund-saver\} Cuando los usuarios solicitan reembolsos en la App Store, Apple evalúa los datos de consumo relacionados con la compra in-app para decidir si aprobar o denegar la solicitud. Por ejemplo, si un usuario compra una suscripción, la usa intensamente durante la mayor parte del período de suscripción y luego solicita un reembolso, es probable que Apple lo apruebe a menos que aportes datos de uso que demuestren que la suscripción se consumió activamente. Apple [anima a los desarrolladores](https://developer.apple.com/documentation/appstoreserverapi/send-consumption-information-v1) a compartir estos datos para garantizar que las decisiones sobre reembolsos sean justas. El Refund Saver de Adapty automatiza este proceso cumpliendo plenamente con las [directrices](https://developer.apple.com/documentation/appstoreserverapi/send-consumption-information-v1) de la App Store. Así es como funciona: - Cuando un usuario inicia una solicitud de reembolso, la App Store envía una notificación pidiendo detalles sobre la transacción y el uso. - Si ignoras o demoras la respuesta, es probable que Apple apruebe el reembolso por defecto. - Adapty procesa automáticamente estas notificaciones y proporciona a Apple los datos necesarios. Esta automatización reduce la probabilidad de reembolsos innecesarios, ahorrando tiempo y protegiendo tus ingresos. :::info Con el Refund Saver puedes recuperar hasta el 40% de los ingresos procedentes de solicitudes de reembolso. ::: ## Requisitos para usar el Refund Saver \{#requirements-to-use-refund-saver\} Para usar esta función, asegúrate de cumplir los siguientes requisitos previos: 1. **Actualiza tu Política de Privacidad en App Store Connect:** La Política de Privacidad de tu app debe revelar la recopilación y el uso de datos de consumo. Esto garantiza que los usuarios entiendan las prácticas de privacidad de tu app antes de descargarla. Consulta los [detalles de privacidad de apps de Apple](https://developer.apple.com/app-store/app-privacy-details/) como referencia. 2. **Obtén el consentimiento del usuario para compartir datos en tu app:** Apple exige que obtengas un consentimiento válido del usuario antes de compartir sus datos personales con Apple. Como desarrollador, eres responsable de obtener ese consentimiento, ya que serás tú quien comparta los datos del usuario con Apple. Consulta las [directrices](https://developer.apple.com/documentation/appstoreserverapi/send-consumption-information) de Apple para más detalles. 3. **Activa las notificaciones de servidor V2:** Asegúrate de que las notificaciones de servidor V2 estén activadas en tu cuenta de Apple Developer y configuradas correctamente en Adapty, ya que las notificaciones V1 no son compatibles. Si aún no las has activado, sigue los pasos de la guía [Activar notificaciones de servidor de App Store](enable-app-store-server-notifications). ## Activar el Refund Saver \{#turn-on-refund-saver\} 1. Abre la sección [Refund Saver](https://app.adapty.io/refund-saver) en el Adapty Dashboard. <img src="/assets/shared/img/refund-off.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en **Turn on Refund Saver** para activar la función. ## Configurar el comportamiento predeterminado de reembolso \{#set-a-default-refund-behavior\} Apple permite a los desarrolladores especificar un resultado preferido para cada solicitud de reembolso al responderla. El propósito de esta configuración es encontrar el equilibrio adecuado entre rechazar y aceptar solicitudes de reembolso, de modo que solo se concedan los reembolsos justos. Ten en cuenta que esta configuración solo sirve para influir en el resultado, pero la decisión final sigue siendo de Apple. Adapty permite configurar esta preferencia, aunque se usará el mismo valor para todas las solicitudes de reembolso. 1. Para cambiar tu preferencia, haz clic en **Edit refund preference**. <img src="/assets/shared/img/refund-saver-preference.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la ventana **Edit refund preference**, elige tu opción de **Default refund request preference**: | Opción | Descripción | | ----------------------------------------------------- | ------------------------------------------------------------ | | Always decline | (predeterminado) Esta es la opción predeterminada y suele dar los mejores resultados para minimizar los reembolsos. | | Decline first refund request, grant all next | Para cada transacción que encuentre el Refund Saver, inicialmente pedirá a Apple que rechace el reembolso. Sin embargo, si la misma transacción vuelve a aparecer, el Refund Saver siempre recomendará conceder el reembolso. Este enfoque ayuda a minimizar la frustración de los usuarios por rechazos injustos: simplemente pueden volver a solicitar el reembolso y probablemente lo recibirán. | | Always refund | Sugiere que Apple apruebe todas las solicitudes de reembolso. | | No preference | No proporciona ninguna recomendación a Apple. En este caso, Apple determinará el resultado del reembolso basándose en sus políticas internas y el historial del usuario, sin ninguna influencia de tu configuración. Esta opción ofrece el enfoque más neutral. | ## Configurar el comportamiento de reembolso para un usuario específico en el dashboard \{#set-refund-behavior-for-a-specific-user-in-the-dashboard\} Aunque hayas configurado el comportamiento predeterminado del Refund Saver para toda la app, es posible que quieras establecer preferencias individuales para usuarios concretos. En el Adapty Dashboard, puedes hacerlo desde el perfil del usuario. Usa la sección **Refund Saver Preferences** que se encuentra en la parte inferior izquierda. <img src="/assets/shared/img/refund-saver-profile-preference.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Configurar el comportamiento de reembolso para un usuario específico en el SDK \{#set-refund-behavior-for-a-specific-user-in-the-sdk\} Puedes establecer la preferencia de reembolso en el código de tu app de forma individual para cada instalación según las acciones del usuario. Usa el fragmento de código siguiente para configurar la preferencia: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (3.4.1+)" default> ```swift showLineNumbers code do { try await Adapty.updateRefundPreference(<PREFERENCE_VALUE>) // possible values: .noPreference, .grant, .decline } catch { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter (3.4.0+)" default> ```javascript showLineNumbers code try { // possible values: AdaptyRefundPreference.noPreference, AdaptyRefundPreference.grant, AdaptyRefundPreference.decline await Adapty().updateRefundPreference(<PREFERENCE_VALUE>); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="rn" label="React Native (3.4.0+)" default> ```typescript showLineNumbers try { await adapty.updateRefundPreference(<PREFERENCE_VALUE>); // possible values: RefundPreference.NoPreference, RefundPreference.Grant, RefundPreference.Decline } catch (error) { // handle the `AdaptyError` } ``` </TabItem> <TabItem value="unity" label="Unity (3.3.0+)" default> ```csharp showLineNumbers Adapty.UpdateAppStoreRefundPreference(<PREFERENCE_VALUE>, (error) => { if (error != null) { // handle the error return; } }); ``` </TabItem> </Tabs> :::note También puedes usar la API del lado del servidor para [establecer una preferencia de reembolso individual](api-adapty/operations/setRefundSaverSettings): - Usa el SDK cuando la configuración de preferencia está directamente vinculada a las interacciones del cliente, como cuando los usuarios hacen clic en un botón para configurar su preferencia. - Usa la API cuando necesitas realizar procesamiento del lado del servidor o cuando se adapta mejor a la arquitectura de tu aplicación. ::: ## Obtener el consentimiento del usuario \{#obtain-user-consent\} La forma de recopilar el consentimiento del usuario para compartir datos depende de ti, pero Apple exige un consentimiento válido del usuario antes de compartir cualquier dato personal con ellos. Apple recomienda usar un **enfoque de opt-in**, que implica avisos dentro de la app que explican cómo se usarán los datos y requieren una acción explícita del usuario para dar su consentimiento. Si un usuario ignora o rechaza el aviso, no se considera que haya dado su consentimiento. Para más detalles, consulta las [directrices](https://developer.apple.com/documentation/appstoreserverapi/send-consumption-information) de Apple. Si el consentimiento explícito no es práctico para tu app, puedes considerar un **enfoque de opt-out**. Esto implica incluir una cláusula de uso compartido de datos en tus Términos de Servicio, explicando que los usuarios aceptan el uso compartido de datos al aceptar los términos. Asegúrate de indicar claramente cómo pueden revocar su consentimiento. A continuación tienes un ejemplo de cláusula para el enfoque opt-out, que incluye los tipos de datos que podrías compartir. Es solo un modelo para orientarte al redactar tu propio texto. Eres responsable de asegurarte de que tu versión final cumpla con todas las leyes aplicables y los requisitos de Apple. *"Si recibimos una solicitud de reembolso de una compra in-app, podemos proporcionar a Apple información sobre la actividad de compras in-app del usuario. Esto puede incluir detalles como el tiempo transcurrido desde la instalación de la app, el tiempo total de uso de la app, un identificador de cuenta anónimo, si la compra in-app fue completamente consumida, si incluyó un período de prueba, el importe total gastado y el importe total reembolsado."* Según el enfoque elegido, configura la opción **Default consent policy** en el menú **Edit refund preferences**: <img src="/assets/shared/img/refund-saver-consent.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <p> </p> | Opción | Descripción | | ------- | ------------------------------------------------------------ | | Opt-out | (predeterminado) Si Adapty no conoce el estado de consentimiento del usuario, asume que el consentimiento **fue dado** y el Refund Saver **compartirá** los datos relacionados con el reembolso con Apple. | | Opt-in | Si Adapty no conoce el estado de consentimiento del usuario, asume que el consentimiento **no fue dado** y el Refund Saver **no compartirá** ningún dato con Apple. Este es el enfoque recomendado por Apple. | ## Actualizar el consentimiento del usuario en el SDK \{#update-user-consent-in-the-sdk\} Para indicarle a Adapty si un usuario específico ha dado su consentimiento, usa el método `updateCollectingRefundDataConsent`: <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS (3.4.1+)" default> ```swift showLineNumbers do { try await Adapty.updateCollectingRefundDataConsent(<CONSENT_VALUE>) // true = consent is explicitly provided, false = consent is explicitly revoked } catch { // handle the error } ``` </TabItem> <TabItem value="flutter" label="Flutter (3.4.0+)" default> ```dart showLineNumbers try { // true = user gave consent, false = user revoked consent await Adapty().updateCollectingRefundDataConsent(<CONSENT_VALUE>); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` </TabItem> <TabItem value="rn" label="React Native (3.4.0+)" default> ```typescript showLineNumbers try { await adapty.updateCollectingRefundDataConsent(<CONSENT_VALUE>); // true = consent is explicitly provided, false = consent is explicitly revoked } catch (error) { // handle the `AdaptyError` } ``` </TabItem> <TabItem value="unity" label="Unity (3.3.0+)" default> ```csharp showLineNumbers Adapty.UpdateAppStoreCollectingRefundDataConsent(<CONSENT_VALUE>, (error) => { if (error != null) { // handle the error return; } }); ``` </TabItem> </Tabs> :::note También puedes usar la API del lado del servidor para [establecer una preferencia individual de uso compartido de datos](api-adapty/operations/setRefundSaverSettings): - Usa el SDK cuando la configuración de preferencia está directamente vinculada a las interacciones del cliente, como cuando los usuarios hacen clic en un botón para configurar su preferencia. - Usa la API cuando necesitas realizar procesamiento del lado del servidor o cuando se adapta mejor a la arquitectura de tu aplicación. ::: ## Comprobar el consentimiento del usuario \{#check-user-consent\} Puedes comprobar el estado de consentimiento actual de un usuario en cualquier momento. En el Adapty Dashboard, simplemente abre el perfil del usuario y busca la configuración **Allow data sharing** en la sección **Refund Saver Preferences**, en la parte inferior izquierda. <img src="/assets/shared/img/refund-saver-profile-consent.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::note También puedes usar la API del lado del servidor para [obtener las preferencias individuales de reembolso y uso compartido](api-adapty/operations/getRefundSaverSettings). ::: ## Limitaciones \{#limitations\} - **Solo para la App Store de Apple:** El Refund Saver solo está disponible para solicitudes de reembolso realizadas en la App Store de Apple. Google Play no ofrece análisis de datos de consumo para reembolsos. Las decisiones de reembolso en Google Play se basan únicamente en las políticas de Google y en la información proporcionada por el usuario. - **Solo para suscripciones de renovación automática y consumibles:** El Refund Saver funciona con suscripciones de renovación automática y compras in-app consumibles, ya que Apple solo proporciona la información necesaria para estos tipos de compra. - **Requiere notificaciones de servidor V2:** El Refund Saver no es compatible con las notificaciones de servidor de App Store V1. Si actualmente usas V1 en Adapty, debes cambiar a V2; consulta la guía [Enviar notificaciones de servidor de App Store a Adapty](enable-app-store-server-notifications) para más detalles. Cambiar a V2 también mejorará tu análisis en Adapty al proporcionar datos más precisos y completos. --- # File: meta-create-campaign --- --- title: "Publicita tu app en Meta Ads" --- En esta guía paso a paso aprenderás cómo crear y configurar anuncios para tu app en Meta, para optimizarlos y hacer un seguimiento de su rendimiento fácilmente. ## Cómo se estructuran los anuncios en Meta \{#how-ads-in-meta-are-structured\} Al anunciarte en Meta Ads, necesitas configurar tres niveles jerárquicos: - **Campaign**: Las campañas definen tus objetivos publicitarios. - **Ad set**: Los conjuntos de anuncios especifican tu audiencia objetivo y los placements, determinando dónde y a quién se mostrarán tus anuncios. Cada campaña puede contener varios conjuntos de anuncios. - **Ads**: Los anuncios son los creativos reales que los usuarios ven e interactúan con ellos. Cada conjunto de anuncios puede contener varios anuncios; sin embargo, se recomienda limitarlo a no más de cinco anuncios por conjunto para un rendimiento óptimo. ## Paso 1. Crea una cuenta en Meta Ads Manager \{#step-1-create-meta-ads-manager-account\} Para empezar con Meta Ads, necesitas tener una página de Facebook Business, ya que no puedes publicar anuncios desde tu cuenta personal. Por lo tanto, debes vincular tu página de empresa a tu portfolio de negocio de Meta Ads: 1. Ve a [business.facebook.com](https://business.facebook.com/). Si todavía no tienes una página de empresa en el portfolio de negocio, tendrás que añadirla. Haz clic en **Go to settings**. 2. Ve a **Account > Pages** en la barra lateral izquierda. Haz clic en **Add** y selecciona **Add an existing Facebook page** o **Create a new Facebook page**. Consulta la [guía para crear una página de empresa](https://www.facebook.com/business/help/473994396650734) si todavía no tienes una. 3. Opcionalmente, vincula tu cuenta de Instagram en la página **Account > Instagram accounts** dentro de los ajustes. Una vez conectada tu página de empresa, ya puedes continuar. ## Paso 2. Añade el píxel de Meta \{#step-2-add-meta-pixel\} Necesitarás un píxel de Meta para conectar los datos de tu campaña con los ingresos y obtener mejores resultados. Antes de conectar datos y crear un píxel, necesitarás: - Una página de empresa: añádela a tu portfolio de negocio en [**Settings > Accounts > Pages**](https://business.facebook.com/latest/settings/pages) - Una cuenta de Business Manager: debes tener control total sobre el portfolio de negocio - Un correo electrónico empresarial: configúralo en [**Settings > Business info**](https://business.facebook.com/latest/settings/business_info) - Una cuenta publicitaria: añádela a tu portfolio de negocio en [**Settings > Accounts > Ad accounts**](https://business.facebook.com/latest/settings/ad_accounts) Cuando estés listo, crea un píxel: 1. Ve al [**Events Manager**](https://www.facebook.com/events_manager2). Haz clic en **Connect data**. 2. Selecciona **Web** como tipo de fuente de datos. 3. Dale un nombre a tu dataset y haz clic en **Create**. 4. Para [Adapty User Acquisition](adapty-user-acquisition), no necesitarás completar la instalación completa del píxel. Por lo tanto, cuando te pregunten sobre la integración, puedes hacer clic en **x** en la ventana de configuración y tu píxel seguirá apareciendo en la lista tras actualizar la página. 5. Cuando tu dataset aparezca en la lista, puedes continuar con la creación de una campaña. ## Paso 3. Crea una campaña \{#step-3-create-campaign\} Para crear una campaña en Meta Ads Manager: 1. Ve al [Meta Ads Manager](https://adsmanager.facebook.com/adsmanager/manage). En la pestaña **Campaign**, haz clic en **Create**. 2. Selecciona **Sales** como objetivo de la campaña y haz clic en **Continue**. 3. Ponle nombre a tu campaña en la sección **Campaign name**. 4. En la sección **Budget**, dentro de **Budget strategy**, selecciona cómo quieres controlar el presupuesto: - **Campaign budget**: La opción más sencilla si no tienes claro qué oportunidades funcionarán mejor. Si la seleccionas, Meta Ads detectará automáticamente los mejores resultados para asignar más presupuesto a los conjuntos de anuncios con mejor rendimiento. A continuación, elige entre presupuesto **Daily** o **Lifetime** e introduce el límite en tu moneda. El presupuesto **Daily** te da más flexibilidad mientras estás aprendiendo, para que puedas empezar con cantidades pequeñas e irlas ajustando sobre la marcha. También puedes seleccionar **Schedule budget increase** y establecer reglas para aumentar automáticamente el presupuesto por un importe o porcentaje determinado. - **Ad set budget**: Selecciona esta opción si quieres definir manualmente qué audiencias recibirán más o menos presupuesto de la campaña. Si no tienes muy claro cómo hacerlo, puedes seleccionar **Share some of your budget with other ad sets** para permitir que Meta ajuste automáticamente los presupuestos de los conjuntos de anuncios hasta en un 20% si esto beneficia el rendimiento del anuncio. 5. En **Campaign bid strategy**, selecciona la mejor opción para tus objetivos: - **Highest volume (default)**: La opción más sencilla para empezar. Si la seleccionas, dejas que Meta optimice el coste por clic para obtener los mejores resultados con tu presupuesto. - **Cost per result goal**: Apunta a un coste por resultado concreto si ya conoces tus referencias. - **Bid cap**: Establece el coste máximo que estás dispuesto a pujar. 6. Adapty te permite llevar a cabo [pruebas A/B](ab-tests) completas. Sin embargo, también puedes activar pruebas A/B en Meta Ads si lo necesitas. Lee más sobre las pruebas A/B en Meta Ads Manager [aquí](https://www.facebook.com/business/help/1159714227408868). 7. Ahora toca añadir el primer conjunto de anuncios a tu campaña. Haz clic en **Next** para continuar. ## Paso 4. Crea un conjunto de anuncios \{#step-4-create-ad-set\} Para crear un conjunto de anuncios: 1. Dale un nombre a tu conjunto de anuncios en el campo **Ad set name**. 2. En el desplegable **Conversion location**, selecciona **Website**. 3. En el campo **Performance goal**, selecciona **Maximize number of landing page views** si tienes una landing page, o **Maximize number of link clicks** si usas un enlace inteligente que dirige a los usuarios directamente a la store. 4. En el campo **Dataset**, selecciona el dataset que creaste en el [Paso 2](#step-2-add-meta-pixel). 5. Selecciona un **Conversion event**. En nuestro caso, probablemente será **Purchase** o **Start trial**. No te preocupes si ves un aviso indicando que tu dataset aún no tiene eventos: simplemente significa que el dataset es nuevo. 6. Si al configurar la campaña seleccionaste **Ad set budget**, elige entre presupuesto **Daily** o **Lifetime** e introduce el límite en tu moneda. El presupuesto **Daily** te da más flexibilidad mientras estás aprendiendo, para que puedas empezar con cantidades pequeñas e irlas ajustando sobre la marcha. Establece las fechas de inicio y, si aplica, de fin del conjunto de anuncios. Por ejemplo, si quieres publicitar una oferta promocional en tu app, es fundamental que el período del conjunto de anuncios coincida con el de la oferta. 7. En la sección **Audience controls**, configura los ajustes de audiencia: - **Location**: Las ubicaciones pueden ser tan amplias o específicas como necesites. Puedes limitar las **Locations** en el conjunto de anuncios para adaptarte a las características de cada región en tus anuncios. - **Minimum age**: Selecciona la edad mínima de los usuarios que verán tu anuncio. Para algunos anuncios puede ser un requisito legal. No puedes seleccionar una edad mínima inferior a 18 años a nivel global ni inferior a 20 en Tailandia. - **Language**: Configura el **Language** solo si no es el idioma más común en los países seleccionados. Por ejemplo, no necesitarás seleccionar **English** en Estados Unidos, pero si te diriges a personas hispanohablantes que viven allí, puede que quieras seleccionar **Spanish**. 8. Por defecto, Meta encuentra automáticamente grupos de personas más pequeños a los que tu anuncio puede resultar relevante. Sin embargo, si añades una sugerencia de audiencia, puedes orientar a Meta hacia las personas que crees que tienen más probabilidades de responder. En la sección **Advantage+ audience**, puedes ajustar: - **Age**: Establece un rango de edad específico para orientar mejor tu anuncio a los distintos grupos de edad. - **Gender**: Muestra tu anuncio a todos los usuarios o segméntalos por género. - **Detailed targeting**: Este ajuste te da el control más específico sobre la audiencia para tu anuncio y/o app. Aquí puedes formar grupos basándote en **Demographics**, **Interests** o **Behaviors**. Dependiendo de lo que haga tu app, por ejemplo, puedes centrarte en distintas profesiones, seguidores de bandas de música concretas, padres de recién nacidos o personas con hábitos de compra online frecuentes. :::note Los ajustes de **Detailed targeting** se aplican con el operador **Or**. Si quieres aplicar condiciones con el operador **And**, haz clic en **Define further** y selecciona nuevas condiciones. ::: 9. En la sección **Placements**, puedes seleccionar dónde aparecerá tu anuncio. Por defecto, se selecciona **Advantage+**, lo que permite a Meta distribuir el presupuesto de tu conjunto de anuncios entre varios placements en función de dónde es más probable que rindan mejor. Te recomendamos usar esta opción si no tienes claro dónde colocar tu anuncio. Si quieres seleccionar placements específicos manualmente, elige **Manual placements** y personalízalos. Lee más [aquí](https://www.facebook.com/business/help/965529646866485). 10. **Recomendado**: La segmentación por dispositivo te ayuda a optimizar tu gasto. En la sección **Placements**, haz clic en **Show more settings**. En la subsección **Devices and operating system**, selecciona qué dispositivos, sistemas operativos y versiones de SO deben incluirse en tu audiencia. Esto garantiza que tus anuncios solo se muestren a usuarios relevantes. Por ejemplo, los usuarios de escritorio no verán tu anuncio y se excluirá a los usuarios con versiones de SO antiguas que tu app no admite. 11. Cuando estés listo, haz clic en **Next** para continuar. ## Paso 5. Crea anuncios \{#step-5-create-ads\} Para crear un anuncio en Meta Ads Manager: 1. Dale un nombre a tu anuncio en el campo **Ad name**. 2. En la sección **Identity**, selecciona la página de Facebook que se usará para publicar los anuncios. Si tienes una cuenta de Instagram específica para tu app y la has conectado en Meta Business Suite en el [Paso 1](#step-1-create-meta-ads-manager-account), selecciónala en el desplegable **Instagram account**. De lo contrario, selecciona **Use Facebook page** para que los anuncios de Instagram se publiquen usando la página de Facebook. 3. En **Ad setup**, selecciona cómo quieres publicar tu anuncio. Al publicitar apps, recomendamos seleccionar **Create ad** para que tu publicación redirija a los usuarios a tu app en lugar de a la página de Facebook. En el campo **Format**, selecciona una opción según los creativos que tengas y cómo quieras mostrarlos. 4. En la sección **Destination**, mantén **Website** seleccionado como **Main destination**. En el campo **Website URL**, pega `https://api-ua.adapty.io/api/v1/attribution/click`. En [Adapty User Acquisition](adapty-user-acquisition), [crea una campaña web](ua-facebook) y pega el contenido de **Click link** después de `https://api-ua.adapty.io/api/v1/attribution/click` en el campo **URL parameters** de la sección **Tracking**. 5. En la sección **Ad creative**, haz clic en **Set up creative** y selecciona **Image ad** o **Video ad**. Se abrirá una nueva ventana donde podrás subir archivos multimedia, recortarlos y añadir textos. 6. Si quieres traducir automáticamente los textos de tu anuncio, en la sección **Languages**, haz clic en **Add languages**. Luego, añade un idioma principal: extraerá automáticamente los textos de tu creativo. Después, añade los idiomas de traducción para la traducción automática. 7. Cuando estés listo, haz clic en **Publish** para lanzar tu anuncio. ## Qué viene después \{#whats-next\} Para activar tu anuncio, necesitarás añadir un método de pago si todavía no lo has hecho. Después, puedes [explorar cómo la campaña afecta a los ingresos de tu app en el dashboard de Adapty User Acquisition](adapty-user-acquisition). ¿Todavía no usas Adapty User Acquisition? [Reserva una llamada con nosotros](https://calendly.com/tnurutdinov-adapty/30min) para descubrir cómo puede ayudarte a hacer seguimiento y optimizar tus campañas publicitarias. --- # File: tiktok-create-campaign --- --- title: "Anuncia tu app en TikTok for Business" --- En esta guía paso a paso aprenderás a crear y configurar anuncios para tu app en TikTok for Business, para que puedas optimizarlos y hacer seguimiento de su rendimiento fácilmente. ## Paso 1. Añade la información de tu negocio \{#step-1-add-business-info\} Si estás empezando con TikTok for Business, primero tienes que añadir la información de tu negocio: 1. Ve a [https://ads.tiktok.com](https://ads.tiktok.com/business/) y haz clic en **Get started**. 2. Regístrate con tu correo electrónico o tu cuenta de TikTok. 3. Introduce los datos de tu negocio y sigue las instrucciones en pantalla. Una vez aprobada tu cuenta de negocio, serás redirigido a la creación de tu primera campaña. ## Paso 2. Crea un píxel \{#step-2-create-a-pixel\} Necesitarás un píxel de TikTok para conectar los datos de tu campaña con los ingresos y obtener mejores resultados: 1. Ve al [**Events Manager**](https://ads.tiktok.com/i18n/events_manager/home). Haz clic en **Connect data source**. 2. Selecciona **Web** como tipo de fuente de datos. 3. En la ventana **Add your website**, haz clic en **Skip**. 4. Selecciona **Manual setup** y haz clic en **Next**. 5. Selecciona **TikTok pixel + Events API** y haz clic en **Next**. 6. Dale un nombre a tu píxel y haz clic en **Create**. 7. En [Adapty User Acquisition](adapty-user-acquisition), no necesitarás completar la instalación completa del píxel. Puedes cerrar la ventana de configuración y tu píxel aparecerá en la lista. 8. Para que este píxel esté disponible en las campañas, tienes que enviarle un evento de prueba desde [Adapty User Acquisition](adapty-user-acquisition): 1. [Crea una nueva campaña de TikTok](ua-tiktok). 2. Despliega la sección específica de la plataforma, por ejemplo, iOS. 3. Selecciona un píxel en el desplegable. 4. Haz clic en **Send test event**. 5. En el desplegable, selecciona el evento que usarás para optimización en el anuncio. 6. En TikTok for Business, abre tu píxel y ve a la pestaña **Test events**. Copia el `test_event_code`. 7. Pégalo en el campo **Test event code** de Adapty y haz clic en **Send**. 9. El evento de prueba aparecerá en TikTok en unos minutos. Cuando lo veas en los detalles de tu píxel, podrás continuar con la configuración de la campaña en TikTok Ads Manager. ## Paso 3. Selecciona el objetivo de la campaña \{#step-3-select-the-campaign-objective\} :::important Este tutorial usa la vista Quick setup de TikTok Ads Manager. Algunas configuraciones recomendadas solo aparecen en la vista Full, lo cual indicamos en los pasos correspondientes. ::: Ve a la [página de creación de anuncios](https://ads.tiktok.com/i18n/nb_creation/create/objectives) en el Ads Manager. En la primera pantalla, selecciona el objetivo publicitario y haz clic en **Continue**. Selecciona **Sales > Website conversion**. ## Paso 4. Rellena la información de la campaña \{#step-4-fill-in-the-campaign-info\} A continuación, rellena la información de la campaña: 1. Ponle nombre a tu campaña en el campo **Campaign name**. 2. En el campo **Optimization goal**, selecciona **Conversion**. 3. Selecciona tu píxel activo en el desplegable y elige un **Optimization event**. Ten en cuenta que solo los eventos activos están disponibles para seleccionar. Si el evento que necesitas no está disponible, envía un evento de prueba siguiendo las instrucciones del [Paso 2](#step-2-create-a-pixel). 4. Tu anuncio se mostrará en el feed y la búsqueda de TikTok. Para una configuración adicional, haz clic en **Advanced settings**. En **Placements**, configura los ajustes de placement: - **User comment**: Selecciónalo si quieres mostrar tu anuncio también en la sección de comentarios. TikTok recomienda mantener los comentarios activados para que tus anuncios consigan más impresiones. - **Allow video download**: Permite que los espectadores descarguen tu anuncio. - **Allow video sharing**: Permite que los espectadores compartan tu anuncio. 5. Haz clic en **Continue**. ## Paso 5. Añade el contenido del anuncio \{#step-5-add-ad-content\} Ahora es el momento de configurar tus creatividades y la URL de destino: 1. En el campo **TikTok account**, selecciona la cuenta que se usará para publicar. 2. En [Adapty User Acquisition](adapty-user-acquisition), [crea una campaña web](ua-tiktok) y pega el **Click link** en el campo **Destination URL**. 3. En la sección **Creatives**, haz clic en **+ Videos and images**. 4. Si quieres usar tus publicaciones de TikTok como creatividades, selecciónalas en la pestaña **TikTok post**. De lo contrario, cambia a la pestaña **Creative library** y haz clic en **Upload**. Los archivos que subas allí estarán accesibles desde esta pestaña más adelante, para que puedas reutilizarlos en otras campañas. 5. Recorta las creatividades para que se adapten al formato de TikTok y elige si se usarán como anuncios individuales o como un carrusel. 6. Despliega la creatividad subida y haz clic en **+** junto a **No music selected**. Puedes subir tus propios archivos mp3. Añadir música es obligatorio. 7. En el campo **Add text**, introduce el texto que se usará como descripción. 8. Selecciona **Place the ads on this TikTok account as a post** si quieres publicar este anuncio en tu cuenta de TikTok. 9. En el campo **Call to action**, selecciona o elimina las llamadas a la acción relevantes para tu anuncio. TikTok las añadirá automáticamente a tu anuncio. 10. Haz clic en **Continue**. ## Paso 6. Configura la segmentación y el presupuesto \{#step-6-configure-targeting-and-budget\} Por último, define quién debe ver tu anuncio y cuánto planeas pagar por él: 1. En la sección **Targeting**, selecciona **Automatic** o **Custom**. La opción **Automatic** es la más sencilla si todavía no conoces bien a tu audiencia. Sin embargo, si seleccionas **Custom**, puedes optimizar tu gasto eligiendo los grupos de usuarios que tienen más probabilidades de responder a tu anuncio. 2. Si has seleccionado **Custom**, configura: - **Location**: La ubicación predeterminada es la de tu cuenta publicitaria. Si seleccionas más de un país o región, los resultados de revisión del anuncio se devolverán por separado para cada ubicación. La entrega real de los anuncios también puede variar según las ubicaciones compatibles con los distintos placements. - **Languages**: Por defecto, todos los idiomas están seleccionados. Elige el idioma de segmentación según el que se use con más frecuencia en la ubicación seleccionada. - **Gender**: Por defecto, todos los géneros están seleccionados. :::tip Si cambias al modo Full, encontrarás una sección adicional **Device** bajo **Targeting**. Aquí puedes limitar tu audiencia por tipo de dispositivo, sistema operativo y versión del SO, algo útil si tu app requiere una versión mínima. ::: 3. En la sección **Budget**, selecciona una de las opciones sugeridas o elige **Custom**. 4. Si has seleccionado **Custom**, elige si necesitas un presupuesto **Daily** o **Lifetime** e introduce el límite en tu moneda. El presupuesto **Daily** te da más flexibilidad mientras todavía estás aprendiendo, así que puedes empezar con importes más pequeños e irlos ajustando sobre la marcha. 5. En la sección **Schedule**, selecciona **Continue for at least 7 days** o **Custom**. Te recomendamos establecer un horario **Custom** si tu anuncio es sensible al tiempo, para no perder el momento en que necesitas detenerlo. 6. Si has seleccionado **Custom**, establece la fecha y hora de inicio, o de inicio y fin, de los anuncios. Ten en cuenta que se usará la zona horaria de tu cuenta. 7. Haz clic en **Publish**. Cuando termines, se creará una nueva campaña con un grupo de anuncios. El grupo de anuncios contendrá un anuncio si has configurado un carrusel, o varios anuncios si has añadido las creatividades como anuncios separados. ## Paso 7. Introduce los datos de pago \{#step-7-enter-payment-details\} Para empezar a publicar el anuncio, tras configurar la segmentación y el presupuesto, introduce tus datos de pago. ¡Y ya está todo listo! ## Qué hacer a continuación \{#whats-next\} Ahora puedes [explorar cómo la campaña afecta a los ingresos de tu app en el dashboard de Adapty User Acquisition](adapty-user-acquisition). ¿Todavía no usas Adapty User Acquisition? [Reserva una llamada con nosotros](https://calendly.com/tnurutdinov-adapty/30min) para conocer cómo puede ayudarte a hacer seguimiento y optimizar tus campañas publicitarias. --- # File: getting-started-with-server-side-api --- --- title: "API del servidor" description: "Primeros pasos con la API del servidor de Adapty para la gestión de suscripciones." --- Con la API puedes: 1. Consultar el estado de suscripción de un usuario. 2. Activar la suscripción de un usuario con un nivel de acceso. 3. Recuperar los atributos de un usuario. 4. Establecer los atributos de un usuario. 5. Obtener y actualizar configuraciones de paywalls. <img src="/assets/shared/img/server.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> <p> </p> :::note Para hacer seguimiento de eventos de suscripción, usa la integración de [Webhook](webhook) en Adapty o intégralo directamente con tu servicio existente. ::: ## Caso 1: Sincronizar suscriptores entre web y móvil \{#case-1-sync-subscribers-between-web-and-mobile\} Si usas proveedores de pago web como Stripe, ChargeBee u otros, puedes sincronizar tus suscriptores fácilmente. Así es cómo: 1. <InlineTooltip tooltip="Asigna un ID único a cada usuario">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip>. 2. [Consulta su estado de suscripción](api-adapty/operations/getProfile) usando la API. 3. Si un usuario está en un plan freemium, muestra un paywall en tu sitio web. 4. Tras un pago exitoso, [actualiza el estado de la suscripción](api-adapty/operations/setTransaction) en Adapty a través de la API. 5. Tus suscriptores se mantendrán automáticamente sincronizados con tu app móvil. ## Caso 2: Conceder una suscripción \{#case-2-grant-a-subscription\} :::note Por razones de seguridad, no es posible conceder una suscripción a través del SDK. ::: Si vendes a través de tu propia tienda online, Amazon Appstore, Microsoft Store u cualquier otra plataforma distinta de Google Play y App Store, necesitarás sincronizar esas transacciones con Adapty para otorgar acceso y registrar la transacción en los análisis. 1. <InlineTooltip tooltip="Asigna un ID único a cada usuario">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip>. 2. [Configura una tienda personalizada para tus productos en el Adapty Dashboard](custom-store). 3. Sincroniza la transacción con Adapty usando la petición de API [Set transaction](api-adapty/operations/setTransaction). ## Caso 3: Conceder un nivel de acceso \{#case-3-grant-an-access-level\} Supongamos que estás ejecutando una promoción que ofrece una prueba gratuita de 7 días y quieres que la experiencia sea coherente en todas las plataformas. Para sincronizarlo con la app móvil: 1. <InlineTooltip tooltip="Asigna un ID único a cada usuario">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip>. 2. Usa la API para [conceder acceso premium](api-adapty/operations/grantAccessLevel) durante 7 días. Transcurridos los 7 días, los usuarios que no se suscriban pasarán al nivel gratuito. ## Caso 4: Sincronizar propiedades y atributos personalizados de usuarios \{#case-4-sync-users-properties-and-custom-attributes\} Si tienes atributos personalizados para tus usuarios —como el número de palabras aprendidas en una app de idiomas— también puedes sincronizarlos. 1. <InlineTooltip tooltip="Asigna un ID único a cada usuario">[iOS](identifying-users), [Android](android-identifying-users), [React Native](react-native-identifying-users), [Flutter](flutter-identifying-users), y [Unity](unity-identifying-users)</InlineTooltip>. 2. [Actualiza el atributo](api-adapty/operations/updateProfile) a través de la API o el SDK. Estos atributos personalizados se pueden usar para crear segmentos y ejecutar pruebas A/B. ## Caso 5: Gestionar configuraciones de paywalls \{#case-5-manage-paywall-configurations\} Puedes [actualizar los Remote Configs en los paywalls](api-adapty/operations/updatePaywall) para ajustar dinámicamente la apariencia y el comportamiento de tu paywall sin redesplegar tu app. --- **A continuación:** - Continúa con la [autorización para la API del servidor](ss-authorization) - Peticiones: - [Obtener perfil](api-adapty/operations/getProfile) - [Crear perfil](api-adapty/operations/createProfile) - [Actualizar perfil](api-adapty/operations/updateProfile) - [Eliminar perfil](api-adapty/operations/deleteProfile) - [Conceder nivel de acceso](api-adapty/operations/grantAccessLevel) - [Revocar nivel de acceso](api-adapty/operations/revokeAccessLevel) - [Establecer transacción](api-adapty/operations/setTransaction) - [Validar compra, proporcionar nivel de acceso al cliente e importar su historial de transacciones](api-adapty/operations/validateStripePurchase) - [Añadir identificadores de integración](api-adapty/operations/setIntegrationIdentifiers) - [Obtener paywall](api-adapty/operations/getPaywall) - [Listar paywalls](api-adapty/operations/listPaywalls) - [Actualizar paywall](api-adapty/operations/updatePaywall) --- # File: flutterflow --- --- title: "Plugin de Adapty para FlutterFlow" description: "Integra FlutterFlow con Adapty para una gestión de suscripciones mejorada." --- Adapty es una plataforma versátil diseñada para ayudar a las apps móviles a crecer. Tanto si estás empezando como si ya tienes miles de usuarios, Adapty te permite ahorrar meses en la integración de compras in-app y duplicar los ingresos por suscripciones con la gestión de paywalls. El plugin de Adapty para FlutterFlow te permite aprovechar todas las funciones de Adapty sin escribir ni una línea de código. Puedes diseñar páginas de paywall en FlutterFlow, habilitar las compras en ellas y luego controlar de forma remota qué productos se muestran, incluyendo la segmentación por grupos de usuarios o las pruebas A/B. Y una vez que publiques tu app, tendrás acceso inmediato a analíticas detalladas de las compras de tus clientes directamente en nuestro dashboard. ¿Quieres actualizar los productos disponibles en tu paywall? ¡Es muy sencillo! Haz los cambios en unos pocos clics dentro del Adapty Dashboard y tus clientes verán los nuevos productos de inmediato, sin necesidad de publicar una nueva versión de la app. Qué más te ofrece Adapty: - **Suscripciones y compras in-app**: Adapty se encarga de la validación de recibos en el servidor y sincroniza a tus clientes en todas las plataformas, incluida la web. - **Pruebas A/B para paywalls**: Prueba diferentes precios, duraciones, períodos de prueba y elementos visuales para optimizar tus ofertas de suscripción y de compra única. - **Analíticas potentes**: Accede a métricas detalladas para entender mejor la monetización de tu app y mejorarla. - **Integraciones**: Adapty se conecta sin problemas con herramientas de analítica de terceros como Amplitude, AppsFlyer, Adjust, Branch, Mixpanel, Facebook Ads, AppMetrica, Webhooks personalizados y mucho más. --- # File: ff-getting-started --- --- title: "Primeros pasos" description: "Empieza con los Feature Flags de Adapty para personalizar los flujos de suscripción." --- Con Adapty puedes crear y ejecutar paywalls y pruebas A/B en distintos momentos del recorrido del usuario en tu app móvil, como en el onboarding, en los ajustes, etc. Estos puntos se llaman [Placements](placements). Un placement en tu app puede gestionar varios paywalls o [pruebas A/B](ab-tests) a la vez, cada uno diseñado para un grupo concreto de usuarios, lo que denominamos [Audiencias](audience). Además, puedes experimentar con los paywalls, sustituyendo uno por otro a lo largo del tiempo sin publicar una nueva versión de la app. Lo único que tienes que escribir directamente en el código de la app es el ID del placement. <img src="/assets/shared/img/audience.jpg" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> La biblioteca de Adapty mantiene tu paywall actualizado con los últimos productos de tu Adapty Dashboard. [Obtiene los datos de los productos](ff-action-flow) y [los muestra en tu paywall](ff-add-variables-to-paywalls), [gestiona las compras](ff-make-purchase) y [comprueba el nivel de acceso del usuario](ff-check-subscription-status) para determinar si debe recibir contenido de pago. Para empezar, solo tienes que [añadir la biblioteca de Adapty](ff-getting-started#add-the-adapty-library-as-a-dependency) a tu proyecto de FlutterFlow e [inicializarla](ff-getting-started#initiate-adapty-plugin) como se muestra a continuación. :::warning Antes de comenzar, ten en cuenta las siguientes limitaciones: - La biblioteca de Adapty para FlutterFlow no es compatible con aplicaciones web. Evita compilar aplicaciones web con ella. - La biblioteca de Adapty para FlutterFlow no es compatible con paywalls creados con el Paywall Builder de Adapty. Tienes que diseñar tu propio paywall en FlutterFlow antes de habilitar las compras con Adapty. ::: ## Añadir la biblioteca de Adapty como dependencia \{#add-the-adapty-library-as-a-dependency\} 1. En el [FlutterFlow Dashboard](https://app.flutterflow.io/dashboard), abre tu proyecto y haz clic en **Settings and Integrations** en el menú de la izquierda. En la sección **Project setup** a la izquierda, selecciona **Project dependencies**. <img src="/assets/shared/img/main_settings.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la sección **FlutterFlow Libraries**, haz clic en **Add Library** e introduce `adapty-xtuel0`. Haz clic en **Add**. 3. Ahora debes asociar tu clave SDK con la biblioteca. Haz clic en **View details** junto a la biblioteca. <img src="/assets/shared/img/ff_view_details.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Copia la **Public SDK key** desde la pestaña [**App Settings** -> **General**](https://app.adapty.io/settings/general) en el Adapty Dashboard. <img src="/assets/shared/FF_img/adaptyapikey.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Pega la clave en **AdaptyApiKey** en FlutterFlow. <img src="/assets/shared/img/ff_apikey.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> La biblioteca de Adapty FF se añadirá como dependencia a tu proyecto. En la ventana de la biblioteca **Adapty** FF encontrarás todos los recursos de Adapty que se han importado a tu proyecto. ## Llamar a la nueva acción de activación al iniciar la aplicación \{#call-the-new-activation-action-at-application-launch\} 1. Ve a la sección **Custom Code** en el menú de la izquierda y abre `main.dart`. <img src="/assets/shared/img/ff_dartmain.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en **+** y selecciona `activate (Adapty)`. <img src="/assets/shared/img/ff_activate.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Save**. ## Inicializar el plugin de Adapty \{#initiate-adapty-plugin\} Para que el Adapty Dashboard reconozca tu app, deberás proporcionar una clave especial en FlutterFlow. 1. En tu proyecto de FlutterFlow, ve a **Settings and Integrations > Permissions** en el menú de la izquierda. 2. En la ventana **Permissions** que se abre, haz clic en el botón **Add Permission**. 3. En los campos **iOS Permission Key** y **Android Permission Key**, pega `AdaptyPublicSdkKey`. 4. Para el campo **Permission Message**, copia la **Public SDK key** desde la pestaña [**App Settings** -> **General**](https://app.adapty.io/settings/general) en el Adapty Dashboard. Cada app tiene su propia clave SDK, así que si tienes varias apps, asegúrate de coger la correcta. <img src="/assets/shared/img/ff_permissions.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Tras completar estos pasos, podrás mostrar tu paywall en tu app de FlutterFlow y habilitar las compras a través de él. ## ¿Qué sigue? \{#whats-next\} 1. [Crea un flujo de acción](ff-action-flow) para gestionar los productos del paywall de Adapty y sus datos en FlutterFlow. 2. [Mapea los datos recibidos en el paywall](ff-add-variables-to-paywalls) que diseñaste en FlutterFlow. 3. [Configura el botón de compra](ff-make-purchase) en tu paywall para procesar las transacciones a través de Adapty cuando se pulse. 4. Por último, [añade comprobaciones del estado de la suscripción](ff-check-subscription-status) para determinar si mostrar contenido de pago al usuario. --- # File: ff-action-flow --- --- title: "Paso 1. Crear el flujo para mostrar los datos del paywall" description: "Configura los flujos de acción de feature flags en Adapty para personalizar los journeys de suscripción de los usuarios." --- :::important Al usar el plugin de FlutterFlow, no puedes usar paywalls creados en el Paywall Builder de Adapty. Debes implementar tu propia página de paywall en FlutterFlow y conectarla a Adapty. ::: Después de añadir la librería de Adapty como dependencia a tu proyecto de FlutterFlow, es momento de construir el flujo que **recupera los datos del paywall y los productos de Adapty, y los muestra en el paywall que has diseñado en FlutterFlow**. Primero necesitamos recibir los datos del paywall desde Adapty. Empezaremos solicitando el paywall de Adapty, luego sus productos asociados, y finalmente comprobaremos si los datos se recibieron correctamente. Si es así, mostraremos el título y el precio del producto en la página del paywall. En caso contrario, mostraremos un mensaje de error. Antes de continuar, asegúrate de haber hecho lo siguiente: 1. [Crear al menos un paywall y añadir al menos un producto](create-paywall) en el Adapty Dashboard. 2. [Crear al menos un placement](create-placement) y [añadir tu paywall](add-audience-paywall-ab-test) en el Adapty Dashboard. ¡Empecemos! ## Paso 1.1. Solicitar el paywall de Adapty \{#step-11-request-adapty-paywall\} Como se mencionó, para mostrar datos en tu paywall de FlutterFlow, primero necesitamos recuperarlos desde Adapty. El primer paso es obtener el paywall de Adapty. Así se hace: 1. Abre tu pantalla de paywall y cambia a la sección **Actions** en el panel derecho. Ahí, abre el **Action Flow Editor**. <img src="/assets/shared/img/ff_action_flow.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la ventana **Select Action Trigger**, selecciona **On Page Load**. <img src="/assets/shared/img/ff_action_trigger.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Add Action**. Luego, busca la acción personalizada `getPaywall` y selecciónala. <img src="/assets/shared/img/ff_getpaywall.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. En la sección **Set Actions Arguments**, introduce el ID real del [placement que has creado](create-placement) en el Adapty Dashboard que incluye el paywall. En este ejemplo es `monthly`. ¡Asegúrate de usar tu ID de placement real! <img src="/assets/shared/img/ff_placementid.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Si has [localizado](localizations-and-locale-codes) tu paywall en el dashboard de Adapty, también puedes configurar el argumento **locale**. 6. En **Action Output Variable Name**, crea una nueva variable y nómbrala `getPaywallResult`. La usaremos en el siguiente paso para referenciar el paywall de Adapty y solicitar sus productos. <img src="/assets/shared/img/ff_getpaywallresult.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 1.2. Solicitar los productos del paywall de Adapty \{#step-12-request-adapty-paywall-products\} ¡Genial! Ya hemos recuperado el paywall de Adapty. Ahora, obtengamos los productos asociados a este paywall: 1. Haz clic en **+** bajo la acción creada y selecciona **Add Action**. Esta acción recibirá los productos del paywall de Adapty. Para ello, busca y selecciona `getPaywallProducts`. 2. En la sección **Set Actions Arguments**, selecciona la variable `getPaywallResult` creada anteriormente. <img src="/assets/shared/img/ff_getpaywallproduct.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Rellena los demás campos de la siguiente manera: - **Available Options**: Data Structured Field - **Select Field**: value - **Available Options**: No further changes <img src="/assets/shared/img/ff_getpaywallresult2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en **Confirm**. 5. En **Action Output Variable Name**, crea una nueva variable y nómbrala `getPaywallProductsResult`. La usaremos para vincular el paywall que diseñaste en FlutterFlow con los datos del paywall de Adapty. <img src="/assets/shared/img/ff_getpaywallproductsresult.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 1.3. Añadir comprobación de si el paywall se cargó correctamente \{#step-13-add-check-if-the-paywall-uploaded-successfully\} Antes de continuar, verifiquemos que el paywall de Adapty se recibió correctamente. Si es así, podemos actualizar el paywall con los datos de los productos. Si no, gestionaremos el error. Así se añade la comprobación: 1. Haz clic en **+** y luego en **Add Conditional**. <img src="/assets/shared/img/ff-add-conditional.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. En la sección **Action Output**, selecciona la variable de salida de acción creada anteriormente (`getPaywallResult` en nuestro ejemplo). <img src="/assets/shared/img/ff-getpaywallresult.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Para verificar que el paywall de Adapty se recibió, comprueba la presencia de un campo con valor. Rellena los campos de la siguiente manera: - **Available Options**: Has Field - **Field (AdaptyGetPaywallResult)**: value 4. Haz clic en **Confirm** para finalizar la condición. ## Paso 1.4. Registrar la visualización del paywall \{#step-14-log-the-paywall-review\} Para asegurarte de que los análisis de Adapty registran la visualización del paywall, necesitamos registrar este evento. Sin este paso, la visualización no se contabilizará en los análisis. Así se hace: 1. Haz clic en **+** bajo la etiqueta **TRUE** y haz clic en **Add Action**. 2. En el campo **Select Action**, busca y elige **logShowPaywall**. <img src="/assets/shared/img/ff-logshowpaywall.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Value** en el área **Set Action Arguments** y elige la variable `getPaywallResult` que hemos creado. Esta variable contiene los datos del paywall. 4. Rellena los campos de la siguiente manera: - **Available Options**: Data Structured Field - **Select Field**: value 5. Haz clic en **Confirm**. <img src="/assets/shared/img/ff-lohsgowpaywallresult.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 1.5. Mostrar error si el paywall no se recibe \{#step-15-show-error-if-paywall-not-received\} Si el paywall de Adapty no se recibe, necesitas [gestionar el error](error-handling-on-flutter-react-native-unity#system-storekit-codes). En este ejemplo, simplemente mostraremos un mensaje de alerta. 1. Añade una acción **Informational Dialog** a la etiqueta **FALSE**. 2. En el campo **Title**, añade el texto que quieras ver como título del diálogo. En este ejemplo, es **Error**. 3. Haz clic en **Value** en el cuadro **Message**. 4. Rellena los campos de la siguiente manera: - **Set Variable**: variable `getPaywallProductResult` que hemos creado - **Available Options**: Data Structure Field - **Select Field**: error - **Available Options**: Data Structure Field - **Select Field**: errorMessage <img src="/assets/shared/img/ff-error.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **Confirm**. 6. Añade una acción **Terminate action** al flujo **FALSE**. <img src="/assets/shared/img/ff-terminate.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Haz clic en **Close** en la esquina superior derecha. ¡Felicidades! Has recibido correctamente los datos del producto. Ahora, [vincúlalos al paywall que has diseñado en FlutterFlow](ff-add-variables-to-paywalls). --- # File: ff-add-variables-to-paywalls --- --- title: "Paso 2. Añadir datos a la página del paywall" description: "Añade variables de Feature Flag a los paywalls en Adapty." --- Una vez que hayas [recibido todos los datos de producto necesarios](ff-action-flow), es hora de mapearlos al bonito paywall que diseñaste en FlutterFlow. En este ejemplo, mapearemos el título del producto y su precio. ## Paso 2.1. Añadir el título del producto a la página del paywall \{#step-21-add-product-title-to-paywall-page\} 1. Haz doble clic en el texto del producto en tu página del paywall. En la ventana **Set from Variable**, busca la variable `getPaywallProductResult` y selecciónala. <img src="/assets/shared/img/ff-paywall-text.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Rellena los campos de la siguiente manera: - **Available Options**: Data Structured Field - **Select Field**: value - **Available Options**: Item at Index - **List Index Options**: First - **Available Options**: Data Structured Field - **Select Field**: localizedTitle - **Default Variable Value**: null - **UI Builder Display Value**: Cualquier valor; en el ejemplo es `product.title` <img src="/assets/shared/img/ff-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Confirm** para guardar los cambios. ## Paso 2.2. Añadir el texto del precio a la página del paywall \{#step-22-add-price-text-to-paywall-page\} Repite los pasos del Paso 2.1 para el texto del precio como se muestra a continuación: 1. Haz doble clic en el texto del precio en tu página del paywall. En la ventana **Set from Variable**, busca la variable `getPaywallProductResult` y selecciónala. <img src="/assets/shared/img/ff-price.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Rellena los campos de la siguiente manera: - **Available Options**: Data Structured Field - **Select Field**: value - **Available Options**: Item at Index - **List Index Options**: First - **Available Options**: Data Structured Field - **Select Field**: price - **Default Variable Value**: null - **UI Builder Display Value**: Cualquier valor; en el ejemplo es `product.price` 3. Haz clic en el botón **Confirm** para guardar los cambios. ### Añadir el precio en moneda local a la página del paywall \{#add-price-in-local-currency-to-paywall-page\} 1. Haz doble clic en el precio en tu página del paywall. En la ventana **Set from Variable**, busca la variable `getPaywallProductResult` y selecciónala. 2. Rellena los campos de la siguiente manera: - **Available Options**: Data Structured Field - **Select Field**: value - **Available Options**: Item at Index - **List Index Options**: First - **Available Options**: Data Structured Field - **Select Field**: price - **Available Options**: Data Structured Field - **Select Field**: amount - **Available Options**: Decimal - **Decimal Type**: Automatic - **Default Variable Value**: null - **UI Builder Display Value**: Cualquier valor; en el ejemplo es `price.amount` 3. Haz clic en **Confirm** para guardar los cambios. ¡Y voilà! Ahora, al lanzar tu app, mostrará los datos del producto del paywall de Adapty directamente en tu página del paywall. Es hora de [permitir que tus usuarios compren este producto](ff-make-purchase). --- # File: ff-make-purchase --- --- title: "Paso 3. Habilitar la compra" description: "Aprende a realizar compras usando el sistema de Feature Flags de Adapty." --- ¡Enhorabuena! Ya has [configurado tu paywall para mostrar datos de productos de Adapty](ff-add-variables-to-paywalls), incluyendo el título y el precio del producto. Ahora vamos al paso final: permitir que los usuarios realicen una compra a través del paywall. ## Paso 3.1. Permitir que los usuarios realicen compras \{#step-31-enable-users-to-make-purchases\} 1. Haz doble clic en el botón de compra de tu página del paywall. En el panel derecho, abre la sección **Actions** si no está ya abierta. 2. Abre el **Action Flow Editor**. <img src="/assets/shared/img/ff-action-flow-editor.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Select Action Trigger**, elige **On Tap**. 4. En la ventana **No Actions Created**, haz clic en **Add Action**. Busca la acción `makePurchase` y selecciónala. <img src="/assets/shared/img/ff-makepurchase.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. En la sección **Set Actions Arguments**, selecciona la variable `getPaywallProductsResult` creada anteriormente. 6. Rellena los campos de la siguiente manera: - **Available Options**: Data Structure Field - **Select Field**: value - **Available Options**: Item at Index - **List Index Options**: First <img src="/assets/shared/img/ff-makepurchase-value.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 7. Haz clic en `subscriptionUpdateParameters`, busca `AdaptySubscriptionUpdateParameters` y selecciónalo. Haz clic en **Confirm**. :::info Por defecto, puedes dejar todos los campos del objeto vacíos. Necesitarás rellenarlos para reemplazar una suscripción por otra en apps de Android. Lee más [aquí](https://android.adapty.io/adapty/com.adapty.models/-adapty-subscription-update-parameters/). ::: <img src="/assets/shared/img/ff-subupdate.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 8. Haz clic en **Confirm**. 9. En **Action Output Variable Name**, crea una nueva variable y nómbrala `makePurchaseResult`; se usará más adelante para confirmar que la compra fue exitosa. <img src="/assets/shared/img/ff-makepurchaseresult.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 3.2. Comprobar si la compra fue exitosa \{#step-32-check-if-the-purchase-was-successful\} Ahora configuremos una comprobación para ver si la compra se realizó correctamente. 1. Haz clic en **+** y luego en **Add Conditional**. 2. En **Set Condition for Action**, selecciona la variable `makePurchaseResult`. 3. En la ventana **Set Variable**, rellena los campos de la siguiente manera: - **Available Options**: Has Field - **Select Field**: profile <img src="/assets/shared/img/ff-makepurchaseresult-conditional.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en **Confirm**. ## Paso 3.3. Abrir el contenido de pago \{#step-33-open-paid-content\} Si la compra es exitosa, puedes desbloquear el contenido de pago. Aquí te explicamos cómo configurarlo: 1. Haz clic en **+** bajo la etiqueta **TRUE** y haz clic en **Add Action**. 2. En el campo **Define Action**, busca y selecciona la página que quieres abrir en la lista **Navigate To**. En este ejemplo, la página es **Questions**. <img src="/assets/shared/img/ff-questions.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 3.4. Mostrar un mensaje de error si la compra falla \{#step-34-show-error-message-if-purchase-failed\} Si la compra falla, vamos a mostrar una alerta al usuario. 1. Añade una acción **Informational Dialog** a la etiqueta **FALSE**. 2. En el campo **Title**, introduce el texto que quieras para el título del diálogo, como **Purchase Failed**. <img src="/assets/shared/img/ff-purchase-fail.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Haz clic en **Value** en el cuadro **Message**. En la ventana **Set from Variable**, busca `makePurchaseResult` y selecciónalo. Rellena los campos de la siguiente manera: - **Available Options**: Data Structure Field - **Select Field**: error - **Available Options**: Data Structure Field - **Select Field**: errorMessage <img src="/assets/shared/img/ff-fail-message.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Haz clic en **Confirm**. 5. Añade una acción **Terminate** al flujo **FALSE**. <img src="/assets/shared/img/ff-terminate-purchase.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Por último, haz clic en **Close** en la esquina superior derecha. ¡Enhorabuena! Tus usuarios ya pueden comprar tus productos. Como paso adicional, vamos a [configurar una comprobación del acceso del usuario al contenido de pago](ff-check-subscription-status) en otros puntos de la app para decidir si mostrarles el contenido de pago o el paywall. --- # File: ff-check-subscription-status --- --- title: "Paso 4. Verificar el acceso al contenido de pago" description: "Aprende a verificar el estado de la suscripción usando los feature flags de Adapty para una mejor segmentación de usuarios." --- Para determinar si un usuario tiene acceso a contenido de pago específico, debes verificar su nivel de acceso. Esto implica comprobar si el usuario tiene al menos un nivel de acceso y si ese nivel es el requerido. Puedes hacerlo consultando el perfil del usuario, que contiene todos los niveles de acceso disponibles. Ahora, vamos a permitir que los usuarios compren tu producto: 1. Haz doble clic en el botón que debe mostrar el contenido de pago y abre la sección **Actions** en el panel derecho si no está ya abierta. 2. Abre el **Action Flow Editor**. <img src="/assets/shared/img/ff-open-paid-content.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. En la ventana **Select Action Trigger**, elige **On Tap**. 4. En la ventana **No Actions Created**, haz clic en el botón **Add Conditional Action**. 5. Haz clic en **UNSET** para establecer los argumentos de la acción y elige la variable `currentProfile`. Esta es la variable de Adapty que almacena los datos del perfil del usuario actual. <img src="/assets/shared/img/ff-currentprofile.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '300px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 6. Rellena los campos de la siguiente manera: - **Available Options**: Data Structure Field - **Select Field**: accessLevels - **Available Options**: Filter List Items - **Filter Conditions**: 1. Selecciona **Conditions -> Single Condition** y haz clic en **UNSET**. 2. En el campo **First value**, selecciona **Item in list** como **Source** y rellena los campos así: - **Available Options**: Data Structure Field - **Select Field**: accessLevelIdentifier 3. Establece el operador de filtro en **Equal to**. 4. Haz clic en **UNSET** junto a **Second value** y en el campo **Value**, introduce el ID de tu nivel de acceso; en nuestro ejemplo usamos `premium`. <img src="/assets/shared/img/ff-filter.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '500px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Haz clic en **Confirm** y continúa rellenando los demás campos. - **Available Options**: Item at Index - **List Index Options**: First - **Available Options**: Data Structure Field - **Select Field**: accessLevel - **Available Options**: Data Structure Field - **Select Field**: isActive 7. Haz clic en **Confirm**. Ahora, añade las acciones para lo que ocurre a continuación: si el usuario tiene la suscripción correcta o no. Llévalo a la página disponible para suscriptores premium o abre la página del paywall para que pueda comprar el acceso. --- # File: ff-resources --- --- title: "Adapty FlutterFlow plugin actions and data types" description: "Access Adapty's feature flag resources to streamline subscription-based features." --- ## Custom Actions Below are Adapty methods delivered to FlutterFlow with Adapty plugin. They can be used as custom actions in FlutterFlow. | Custom Action | Description | Action Arguments | Adapty Data Types - Action Output Variable | |---|----|--------|----| | activate | Initializes the Adapty SDK | None || | <p id="getPaywall">getPaywall</p> | Retrieves a paywall. It does not return paywall products. Use the `getPaywallProducts` action to get the actual products | <ul><li>[Placement_ID](placements)</li><li>[Locale](localizations-and-locale-codes)</li></ul> | [AdaptyGetPaywallResult](ff-resources#adaptygetpaywallresult)| | <p id="getPaywallProducts">getPaywallProducts</p> | Returns a list of actual paywall products | [AdaptyPaywall](ff-resources#adaptypaywall) | [AdaptyGetProductsResult](ff-resources#adaptygetproductsresult) | | <p id="getproductsintroductoryoffereligibility">getProductsIntroductoryOfferEligibility</p> | Checks if the user qualifies for an introductory iOS subscription offer | [AdaptyPaywallProduct](product) | [AdaptyGetIntroEligibilitiesResult](ff-resources#adaptygetintroeligibilitiesresult) | | <p id="makePurchase">makePurchase</p> | Completes a purchase and unlocks content. If a paywall has a promotional offer, Adapty automatically applies it at checkout| <ul><li> **product**: an AdaptyPaywallProduct object retrieved from the paywall.</li><li> **subscriptionUpdateParams**: an [`AdaptySubscriptionUpdateParameters`](ff-resources#adaptysubscriptionupdateparameters) object used to upgrade or downgrade a subscription (use for Android).</li><li>**isOfferPersonalized**: Specifies whether the offer is personalized to the buyer (use for Android).</li></ul> | [AdaptyMakePurchaseResult](ff-resources#adaptymakepurchaseresult) | | <p id="getprofile">getProfile</p> | <p>Retrieves the current app user's profile. This allows you to set access levels and other parameters</p><p>If it fails (e.g., due to no internet), cached data will be returned. Adapty regularly updates the profile cache to ensure the information stays as current as possible</p> | None| [AdaptyGetProfileResult](ff-resources#adaptygetprofileresult) | | updateProfile | Changes optional attributes of the current user profile such as email, phone number, etc. You can later use attributes to create user [segments](segments) or just view them in CRM | The ID and any parameters that need to be updated for the [AdaptyProfile](ff-resources#adaptyprofile) | [AdaptyError](ff-resources#adaptyerror) (Optional) | | restorePurchases | Restores any purchases the user has made | None | [AdaptyGetProfileResult](ff-resources#adaptygetprofileresult) | | logShowPaywall | Logs when a specific paywall is shown to the user | [AdaptyPaywall](ff-resources#adaptypaywall) | [AdaptyError](ff-resources#adaptyerror) (Optional) | | identify | Identifies the user using your system's `customerUserId` | customerUserId | [AdaptyError](ff-resources#adaptyerror) (Optional) | | logout | Logs the current user out of your app | None | [AdaptyError](ff-resources#adaptyerror) (Optional)| | presentCodeRedemptionSheet | Displays a sheet that allows users to redeem codes (iOS only) | None | None | ## Data Types Adapty data types (collections of data values) delivered to FlutterFlow with Adapty plugin. ### AdaptyAccessLevel Information about the user's [access level](access-level). | Field Name | Type | Description | |--------------------------|----------|-------------| | activatedAt | DateTime | The time when this access level was activated | | activeIntroductoryOfferType | String | The type of an active introductory offer. If set, it means an offer was applied during this subscription period | | activePromotionalOfferId | String | The ID of an active promotional offer (purchased from iOS) | | activePromotionalOfferType | String | The type of an active promotional offer (purchased from iOS). If set, it means an offer was applied during this subscription period | | billingIssueDetectedAt | DateTime | The time when a billing issue was detected. The subscription can still be active. Set to null if payment is successfully processed | | cancellationReason | String | The reason why the subscription was canceled | | expiresAt | DateTime | The access level expiration time (could be in the past or not set for lifetime access) | | id | String | The identifier of the access level | | isActive | Boolean | True if this access level is active. Generally, you can check this property to determine if a user has an access to premium features | | isInGracePeriod | Boolean | True if this auto-renewable subscription is in the [grace period](https://developer.apple.com/help/app-store-connect/manage-subscriptions/enable-billing-grace-period-for-auto-renewable-subscriptions) | | isLifetime | Boolean | True if this access level is active for a lifetime (no expiration date) | | isRefund | Boolean | True if this purchase was refunded | | offerId | String | The ID of an active promotional offer (purchased from Android) | | renewedAt | DateTime | The time when the access level was last renewed | | startsAt | DateTime | The start time of this access level (could be in the future) | | store | String | The store where the purchase was made | | unsubscribedAt | DateTime | The time when auto-renewal was turned off for the subscription. The subscription can still be active. If not set, the user reactivated the subscription | | vendorProductId | String | The product ID from the store that unlocked this access level | | willRenew | Boolean | True if this auto-renewable subscription is set to renew | ### AdaptyAccessLevelIdentifiers This struct is intended to replace key-value pair for `Map<String, AdaptyAccessLevel` [AdaptyAccessLevel](ff-resources#adaptyaccesslevel). | Field Name | Type | Description | |------------|------|-------------| | accessLevelIdentifier | String | The ID of the access level | | accessLevel | Data ([AdaptyAccessLevel](ff-resources#adaptyaccesslevel)) | The associated [AdaptyAccessLevel](ff-resources#adaptyaccesslevel) | ### AdaptyCustomDoubleAttribute Information on custom double attributes defined for the [user](ff-resources#adaptyprofile). | Field Name | Type | Description | |------------|------|-------------| | key | String | The ID of the custom double attribute | | value | Double | The value of the custom double attribute | ### AdaptyCustomStringAttribute Information on custom string attributes defined for the [user](ff-resources#adaptyprofile). | Field Name | Type | Description | |------------|------|-------------| | key | String | The ID of the custom string attribute | | value | String | The value of the custom string attribute | ### AdaptyError Contains details about an error. For a complete list of error codes, refer to [React Native, Flutter, Unity - Handle errors](error-handling-on-flutter-react-native-unity). | Field Name | Type | Description | |--------------------------|----------|-------------| | errorMessage | String | A human-readable description of the error | | errorCode | Integer | Numeric code identifying the error | ### AdaptyGetIntroEligibilitiesResult Contains the result of the `getProductsIntroductoryOfferEligibility` custom action. | Field Name | Type | Description | |--------------------------|----------|-------------| | value | List < Data ([AdaptyProductIntroEligibility](ff-resources#adaptyproductintroeligibility)) > | List of the user's eligibility for promotional offers | | error | Data ([AdaptyError](ff-resources#adaptyerror)) | Contains details about the error via [`AdaptyError`](ff-resources#adaptyerror) | ### AdaptyGetPaywallResult Contains the result of the `getPaywall` custom action. | Field Name | Type | Description | |--------------------------|----------|-------------| | value | Data ([AdaptyPaywall](ff-resources#adaptypaywall)) | Contains a list of [AdaptyPaywall](ff-resources#adaptypaywall) objects | | error | Data ([AdaptyError](ff-resources#adaptyerror)) | Contains error information via [AdaptyError](ff-resources#adaptyerror) | ### AdaptyGetProductsResult Contains the result of the `getPaywallProducts` custom action. | Field Name | Type | Description | |--------------------------|----------|-------------| | value | List < Data ([AdaptyPaywallProduct](product)) > | Contains a list of [AdaptyPaywallProducts](product) | | error | Data ([AdaptyError](ff-resources#adaptyerror)) | Contains error information via [AdaptyError](ff-resources#adaptyerror) | ### AdaptyGetProfileResult Contains the result of the `getProfile` custom action. | Field Name | Type | Description | |--------------------------|----------|-------------| | value | Data ([AdaptyProfile](ff-resources#adaptyprofile)) | Contains the user profile as an [AdaptyProfile](ff-resources#adaptyprofile) | | error | Data (AdaptyError) | Contains error information via [AdaptyError](ff-resources#adaptyerror) | ### AdaptyMakePurchaseResult Contains the result of the `makePurchase` custom action. | Field Name | Type | Description | |--------------------------|----------|-------------| | value | Data ([AdaptyProfile](ff-resources#adaptyprofile)) | Contains the user's profile as an [AdaptyProfile](ff-resources#adaptyprofile) | | error | Data ([AdaptyError](ff-resources#adaptyerror)) | Contains error information via [AdaptyError](ff-resources#adaptyerror) | ### AdaptyNonSubscription Information about non-subscription purchases. These can be one-time (consumable) products, unlocks (like new map unlock in the game), etc. | Field Name | Type | Description | |--------------------------|----------|-------------| | isConsumable | Boolean | Indicates whether the product is consumable | | isOneTime | Boolean | Indicates if the product is a one-time purchase (e.g., if true, the purchase is processed only once) | | isRefund | Boolean | Indicates if the product has been refunded | | isSandbox | Boolean | Indicates if the product was purchased in a sandbox environment | | purchasedAt | DateTime | The time when the product was purchased | | purchaseId | String | The ID of the purchase in Adapty. Can be used for tracking one-time products | | store | String | The store where the product was purchased (e.g., App Store, Google Play) | | vendorProductId | String | ID of the product in vendor's system | | vendorTransactionId | String | Transaction ID in the vendor's system | ### AdaptyPaywall Information about a [paywall](paywalls). | Field Name | Type | Description | |----------------------|----------|-------------| | abTestName | String | The name of the parent A/B test | | hasViewConfiguration | Boolean | Indicates if there is a view configuration for the paywall | | locale | String | The locale ID of the paywall | | name | String | Paywall name | | placement.id | String | The ID of the parent placement | | remoteConfigString | String | A custom dictionary from Adapty Dashboard associated with this paywall | | placement.revision | Integer | The current revision/version of the paywall. Every change generates a new revision | | variationId | String | The variation ID used to attribute purchases to this paywall | | vendorProductIds | String | Array of product IDs related to the paywall | ### AdaptyPaywallProduct Information about [product](product). | Field Name | Type | Description | | -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | vendorProductId | String | The ID of a product from an app store | | localizedDescription | String | A description of the product in the user's language | | localizedTitle | String | The name of the product in the user's language | | regionCode | String | The region code of the locale used to format the price of the product (use for iOS) | | isFamilyShareable | Boolean | A Boolean value that indicates whether the product is available for family sharing in App Store Connect. Will be always FALSE for iOS version below 14.0 and macOS version below 11.0 (use for iOS) | | paywallVariationId | String | The ID of a variation, used to attribute purchases to this paywall | | paywallABTestName | String | Parent A/B test name | | paywallName | String | Parent paywall name | | price | Data ([AdaptyPriceData](#adaptyprice) | The price of the product | | subscriptionDetails | Data ([AdaptySubscriptionDetails](#adaptysubscriptiondetails)) | Information on subscription | ### AdaptyPrice Information about product price. | Field Name | Type | Description | | --------------- | ------ | ------------------------------------------ | | amount | Double | The numeric value of the price | | currencyCode | String | The code of the price currency | | currencySymbol | String | The symbol used for the currency | | localizedString | String | The price displayed in the user's language | ### AdaptyProductIntroEligibility Defines if the user qualifies for an introductory offer for an iOS subscription. | Field Name | Type | Description | | --------------- | ----------------------------------------------------------- | ------------------------------------------------------------ | | vendorProductId | String | The ID of a product from an app store | | eligibility | [AdaptyEligibilityEnum](ff-resources#adaptyeligibilityenum) | Definition if the user qualifies for an introductory offer for an iOS subscription | ### AdaptyProductNonsubscriptions Details of the active non-subscription tied to this product. | Field Name | Type | Description | | ---------------- | ----------------------------------------------------------- | ------------------------------------------------------------ | | productId | String | The ID of the product from an app store | | nonsubscriptions | [AdaptyNonSubscription](ff-resources#adaptynonsubscription) | Information about non-subscription purchases. These can be one-time (consumable) products, unlocks (like new map unlock in the game), etc. | ### AdaptyProductSubscriptions Details of the active subscription tied to this product. | Field Name | Type | Description | | ------------ | ----------------------------------------------------- | ---------------------------------------- | | productId | String | The ID of the product from an app store | | subscription | [AdaptySubscription](ff-resources#adaptysubscription) | Information about subscription purchases | ### AdaptyProfile Information on the user's profile | Field Name | Type | Description | | ---------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | accessLevels | List < Data ([AdaptyAccessLevelIdentifiers](ff-resources#adaptyaccesslevelidentifiers)) > | List of all access levels that belong to the user | | profileId | String | The ID of the user profile | | customerUserId | String | The ID of the user in the vendor's system | | subscriptions | List < Data ([MapKeySubscriptions](#mapkeysubscriptions)) > | The list of all subscriptions purchased by the user | | nonSubscriptions | List < Data ([MapKeyNonSubscriptions](#mapkeynonsubscriptions)) > | The list of all non-subscription products purchased by the user | ### AdaptyProfileParameters Information on the user. | Field Name | Type | Description | | ----------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | firstName | String | The first name of the user | | lastName | String | The last name of the user | | gender | [AdaptyGenderEnum](#adaptygenderenum) | The gender of the user | | birthday | String | The birthday of the user | | email | String | The email of the user | | phoneNumber | String | The phone number of the user | | facebookAnonymousId | String | The ID of the user in [Facebook Ads integration](facebook-ads) | | amplitudeUserId | String | The ID of the user in [Amplitude integration](amplitude) | | amplitudeDeviceId | String | The ID of the user's device in [Amplitude integration](amplitude) | | mixpanelUserId | String | The ID of the user in [Mixpanel integration](mixpanel) | | appmetricaProfileId | String | The ID of the user in [AppMetrica integration](appmetrica) | | appmetricaDeviceId | String | The ID of the user's device in [AppMetrica integration](appmetrica) | | oneSignalPlayerId | String | The ID of the user in [OneSignal integration](onesignal) | | pushwooshHWID | String | The ID of the user's device in [Pushwoosh integration](pushwoosh) | | firebaseAppInstanceId | String | The ID of the user in [Firebase integration](firebase-and-google-analytics) | | airbridgeDeviceId | String | The ID of the user's device in [Airbridge integration](airbridge) | | appTrackingTransparencyStatus | AdaptyATTStatus | The status of the access to IDFA (use for iOS) | | analyticsDisabled | Boolean | Definition if the external [analytics is opted out for the user](analytics-integration#disabling-external-analytics-for-a-specific-customer) | | customStringAttributes | List < Data ([AdaptyCustomStringAttribute](ff-resources#adaptycustomstringattribute)) > | List of custom string attributes of the user | | customDoubleAttributes | List < Data ([AdaptyCustomDoubleAttribute](ff-resources#adaptycustomdoubleattribute)) > | List of custom double attributes of the user | ### AdaptySubscription Information on existing user subscription. | Field Name | Type | Description | | --------------------------- | -------- | ------------------------------------------------------------ | | activatedAt | DateTime | The time when this subscription was activated | | activeIntroductoryOfferType | String | The type of an active introductory offer. If set, it means an offer was applied during this subscription period | | activePromotionalOfferId | String | The ID of an active promotional offer (use for iOS) | | activePromotionalOfferType | String | The type of an active promotional offer (use for iOS). If set, it means an offer was applied during this subscription period | | cancellationReason | String | The reason why the subscription was canceled | | expiresAt | DateTime | The subscription expiration time | | renewedAt | DateTime | The time when the subscription was last renewed | | unsubscribedAt | DateTime | The time when auto-renewal was turned off for the subscription. The subscription can still be active. If not set, the user reactivated the subscription | | billingIssueDetectedAt | DateTime | The time when a billing issue was detected. The subscription can still be active. Set to null if payment is successfully processed | | isActive | Boolean | True if this subscription is active. Generally, you can check this property to determine if a user has access to premium features | | isInGracePeriod | Boolean | True if this auto-renewable subscription is in the [grace period](https://developer.apple.com/help/app-store-connect/manage-subscriptions/enable-billing-grace-period-for-auto-renewable-subscriptions) | | isLifetime | Boolean | True if this subscription is active for a lifetime (no expiration date) | | isRefund | Boolean | True if this purchase was refunded | | isSandbox | Boolean | Indicates if the product was purchased in a sandbox environment | | offerId | String | The ID of an active promotional offer (use for Android) | | startsAt | DateTime | The start time of this access level (could be in the future) | | store | String | The store where the product was purchased (e.g., App Store, Google Play) | | vendorOriginalTransactionId | String | ID of the initial subscription in vendor's system | | vendorProductId | String | ID of the product in vendor's system | | vendorTransactionId | String | Transaction ID in the vendor's system | | willRenew | Boolean | True if this auto-renewable subscription is set to renew | ### AdaptySubscriptionDetails Scheme of a Subscription object as a part of the [AdaptyPaywallProduct](product). | Field Name | Type | Description | | ----------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | androidBasePlanId | String | [Base plan ID](https://support.google.com/googleplay/android-developer/answer/12154973) in the Google Play Store or [price ID](https://docs.stripe.com/products-prices/how-products-and-prices-work#use-products-and-prices) in Stripe. | | androidIntroductoryOfferEligibility | [AdaptyEligibilityEnum](ff-resources#adaptyeligibilityenum) | Definition if the user qualifies for an introductory offer for an iOS subscription | | androidOfferId | String | The ID of an active promotional offer (use for Android) | | androidOfferTags | List < String > | List of [custom tags](https://developers.google.com/android-publisher/api-ref/rest/v3/OfferTag) specified for base plans and subscription offers. | | introductoryOffer | List < Data ([AdaptySubscriptionPhase](ff-resources#adaptysubscriptionphase)) > | The ID of an introductory offer (use for iOS) | | localizedSubscriptionPeriod | String | The period of the subscription in the user's language | | promotionalOffer | Data ([AdaptySubscriptionPhase](ff-resources#adaptysubscriptionphase)) | The promotional offer details (use for iOS) | | promotionalOfferEligibility | Boolean | Definition if the user qualifies for an promotional offer for an iOS subscription | | promotionalOfferId | String | The ID of the promotional offer (use for iOS) | | renewalType | [AdaptyRenewalTypeEnum](#adaptyrenewaltypeenum) | Defines if the subscription is auto-renewable or not via [AdaptyRenewalTypeEnum](ff-resources#adaptyrenewaltypeenum) | | subscriptionGroupIdentifier | String | The ID of the product group the product belongs to (use for iOS) | | subscriptionPeriod | Data ([AdaptySubscriptionPeriod](#adaptysubscriptionperiod)) | The duration of the subscription | ### AdaptySubscriptionPeriod The duration of the subscription. | Field Name | Type | Description | | ------------- | --------------------------------------------- | ----------------------------------------------------------- | | numberOfUnits | Integer | Number of days/weeks/months/years the subscription lasts. | | unit | [AdaptyPeriodUnitEnum](#adaptyperiodunitenum) | Measurement unit of the period: days, weeks, months, years. | ### AdaptySubscriptionPhase Represents a subscription phase, such as a free trial or an introductory offer period. | Field Name | Type | Description | | --------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | identifier | String | The ID of the phase | | localizedNumberOfPeriods | String | The length of the phase. For example, a 6-month offer would display as `6 months` in the user's language. | | localizedSubscriptionPeriod | String | The subscription duration in the user's language, like `3 months`. | | numberOfPeriods | Integer | The number of subscription periods in this phase. For instance, a 6-month offer would have two 3-month periods. | | paymentMode | [AdaptyPaymentModeEnum](#adaptypaymentmodeenum) | The payment model used for this phase. | | price | Data ([AdaptyPrice](#adaptyprice)) | The price of this phase. | | subscriptionPeriod | Data ([AdaptySubscriptionPeriod](#adaptysubscriptionperiod)) | The subscription period on which this phase is based. | ### AdaptySubscriptionUpdateParameters (*Android only*) Parameters for replacing one subscription with another. | Field Name | Type | Description | | ---------- | ------------------------------------------------------------ | ---------- | | oldSubVendorProductId | String | The ID of the current subscription in the Play Store that you want to replace. | | replacementMode | [AdaptySubscriptionUpdateReplacementMode](ff-resources#adaptysubscriptionupdatereplacementmode) | Enum that corresponds to [`BillingFlowParams.ProrationMode`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode) values. | ### MapKeyNonSubscriptions Replacement for a dictionary for [AdaptyNonSubscription](ff-resources#adaptynonsubscription). | Field Name | Type | | ---------- | ------------------------------------------------------------ | | key | String | | value | List < Data ([AdaptyNonSubscription](ff-resources#adaptynonsubscription)) > | ### MapKeySubscriptions Replacement for a dictionary for [AdaptySubscription](ff-resources#adaptysubscription). | Field Name | Type | | ---------- | ------------------------------------------------------------ | | key | String | | value | List < Data ([AdaptySubscription](ff-resources#adaptysubscription)) > | ## Enums Adapty enums (variables that are sets of predefined constants) delivered to FlutterFlow with the Adapty plugin. ### AdaptyEligibilityEnum Defines if the user qualifies for an introductory offer for an iOS subscription. | Field Name | Description | |--------------------------|-------------| | eligible | The user is eligible for an intro offer, it is safe to reflect this info in your UI | | ineligible | The user is not eligible to get any offer, you shouldn't present it in your UI | | notApplicable | This product is not configured to have an offer | ### AdaptyGenderEnum Defines user's gender. | Field Name | Description | | ---------- | -------------------------------------------- | | none | The gender is not set | | female | User's gender is female | | male | User's gender is male | | Other | The user has defined their gender as "other" | ### AdaptyPaymentModeEnum Defines the payment model. | Field Name | Description | | ---------- | ------------------------------------------------------------ | | payAsYouGo | A pricing model where customers are billed based on their actual usage or consumption of a product/service, rather than paying for a fixed fee upfront | | payUpFront | A pricing model where customers are billed before they receive the product/service. | | freeTrial | User is on a free trial period | | unknown | Pricing model is not defined | ### AdaptyPeriodUnitEnum Defines the units in which the periods are measured. | Field Name | Description | | ---------- | ----------- | | day | In days | | week | In weeks | | month | In months | | year | In years | | unknown | Not defined | ### AdaptyRenewalTypeEnum Defines if the subscription is auto-renewable or not. | Field Name | Description | | ------------- | --------------------------------------------------- | | prepaid | The subscription is prepaid and not auto-renewable. | | autorenewable | The subscription is auto-renewable | ### AdaptySubscriptionUpdateReplacementMode Defines the subscription update mode for Android. | Field Name | Description | | ------------- | --------------------------------------------------- | | withTimeProration | (default) The new plan takes effect immediately, and the remaining time will be prorated and credited to the user. | | chargeProratedPrice | The new plan takes effect immediately, and the billing cycle remains the same. The price for the remaining period will be charged. This option is only available for subscription upgrades. | | withoutProration | The new plan takes effect immediately, and the new price will be charged on the next recurrence time. The billing cycle stays the same. | | deferred | The new purchase takes effect immediately, and the new plan will take effect when the old item expires. | | chargeFullPrice | The new plan takes effect immediately, and the billing cycle remains the same. The price for the remaining period will be charged. This option is only available for subscription upgrades. | ### App States App state variables are specific variables that hold the current state of an application. They can be accessed and modified throughout the entire application across all pages and components. This type of variable can be useful for storing data that needs to be shared between different parts of the app, such as user preferences and authentication tokens. | Field Name | Data Type | Persisted | Description | | -------------- | -------------------------------------------------- | --------- | ------------------------------------------------------------ | | currentProfile | Data ([AdaptyProfile](ff-resources#adaptyprofile)) | False | The variable that contains the information on the current user profile. Keep it up-to-date. | --- # End of Documentation _Generated on: 2026-05-15T20:13:57.783Z_ _Successfully processed: 252/252 files_ # UNITY - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: es Generated on: 2026-05-15T20:13:57.789Z Total files: 39 --- # File: sdk-installation-unity --- --- title: "Instalar y configurar el SDK de Unity" description: "Guía paso a paso para instalar Adapty SDK en Unity para apps basadas en suscripciones." --- El SDK de Adapty incluye dos módulos clave para una integración fluida en tu app de Unity: - **Core Adapty**: Este SDK esencial 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 visual sin código para crear paywalls multiplataforma fácilmente. :::tip ¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Consulta nuestra [app de ejemplo](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Assets), que muestra la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Requisitos \{#requirements\} El SDK de Adapty es compatible con iOS 13.0+, pero requiere iOS 15.0+ para trabajar con paywalls creados en el paywall builder. :::info Adapty es compatible con Google Play Billing Library hasta la versión 8.x. Por defecto, Adapty usa Google Play Billing Library v7.0.0. Para usar una versión más reciente, [sobreescribe la dependencia de Billing](https://developer.android.com/google/play/billing/integrate#dependency) en tu build de Android. ::: --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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. </Callout> ## Instalar el SDK de Adapty \{#install-adapty-sdk\} [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-Unity.svg?style=flat&logo=unity)](https://github.com/adaptyteam/AdaptySDK-Unity/releases) 1. Descarga el [`adapty-unity-plugin-*.unitypackage`](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Releases) desde GitHub e impórtalo en tu proyecto. <img src="/assets/shared/img/456bd98-adapty-unity-plugin.webp" style={{ border: 'none', /* border width and color */ width: '400px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Descarga e importa el [plugin External Dependency Manager](https://github.com/googlesamples/unity-jar-resolver). 3. El SDK usa el plugin "External Dependency Manager" para gestionar las dependencias de iOS Cocoapods y las dependencias de Android gradle. Tras la instalación, puede que necesites invocar el gestor de dependencias: `Assets -> External Dependency Manager -> Android Resolver -> Force Resolve` y `Assets -> External Dependency Manager -> iOS Resolver -> Install Cocoapods` 4. Al compilar tu proyecto de Unity para iOS, obtendrás el archivo `Unity-iPhone.xcworkspace`, que debes abrir en lugar de `Unity-iPhone.xcodeproj`; de lo contrario, las dependencias de Cocoapods no se usarán. ## Activar el módulo Adapty del SDK de Adapty \{#activate-adapty-module-of-adapty-sdk\} Activa el SDK de Adapty en el código de tu app. :::note El SDK de Adapty solo necesita activarse una vez en tu app. ::: 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. :::important - Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API 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. ::: ```csharp showLineNumbers title="C#" using UnityEngine; using AdaptySDK; public class AdaptyListener : MonoBehaviour, AdaptyEventListener { void Start() { DontDestroyOnLoad(this.gameObject); Adapty.SetEventListener(this); var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY"); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); } public void OnLoadLatestProfile(AdaptyProfile profile) { } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } } ``` :::important Espera al callback de finalización de `Activate` antes de llamar a cualquier otro método del SDK de Adapty. Consulta [Orden de llamadas en el SDK de Unity](unity-sdk-call-order) para ver la secuencia completa. ::: ## Configurar la escucha de eventos \{#set-up-event-listening\} Crea un script para escuchar los eventos de Adapty. Nómbralo `AdaptyListener` en tu escena. Te recomendamos usar el método `DontDestroyOnLoad` para este objeto, de modo que persista durante toda la vida útil de la aplicación. <img src="/assets/shared/img/2ccd564-create_adapty_listener.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Adapty usa el namespace `AdaptySDK`. Al principio de los archivos de script que usen el SDK de Adapty, puedes añadir: ```csharp showLineNumbers title="C#" using AdaptySDK; ``` Suscríbete a los eventos de Adapty: ```csharp showLineNumbers title="C#" using UnityEngine; using AdaptySDK; public class AdaptyListener : MonoBehaviour, AdaptyEventListener { public void OnLoadLatestProfile(AdaptyProfile profile) { // handle updated profile data } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } } ``` Te recomendamos ajustar el Script Execution Order para colocar el AdaptyListener antes de Default Time. Esto garantiza que Adapty se inicialice lo antes posible. <img src="/assets/shared/img/activate_unity.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Añadir el plugin de Kotlin a tu proyecto \{#add-kotlin-plugin-to-your-project\} :::warning Este paso es obligatorio. Si lo omites, tu app móvil puede fallar cuando se muestre el paywall. ::: 1. En **Player Settings**, asegúrate de que las opciones **Custom Launcher Gradle Template** y **Custom Base Gradle Template** estén seleccionadas. <img src="/assets/shared/img/kotlin-plugin1.webp" style={{ border: 'none', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Añade la siguiente línea a `/Assets/Plugins/Android/launcherTemplate.gradle`: ```groovy showLineNumbers apply plugin: 'com.android.application' // highlight-next-line apply plugin: 'kotlin-android' apply from: 'setupSymbols.gradle' apply from: '../shared/keepUnitySymbols.gradle' ``` 3. Añade la siguiente línea a `/Assets/Plugins/Android/baseProjectTemplate.gradle`: ```groovy showLineNumbers plugins { // If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity // See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html // See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle // To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version id 'com.android.application' version '8.3.0' apply false id 'com.android.library' version '8.3.0' apply false // highlight-next-line id 'org.jetbrains.kotlin.android' version '1.8.0' apply false **BUILD_SCRIPT_DEPS** } ``` Ahora configura los paywalls en tu app: - Si usas el [Adapty Paywall Builder](adapty-paywall-builder), primero [activa el módulo AdaptyUI](#activate-adaptyui-module-of-adapty-sdk) a continuación y luego sigue la [guía de inicio rápido del Paywall Builder](unity-quickstart-paywalls). - Si construyes tu propia UI de paywall, consulta la [guía de inicio rápido para paywalls personalizados](unity-quickstart-manual). ## Activar el módulo AdaptyUI del SDK de Adapty \{#activate-adaptyui-module-of-adapty-sdk\} Si planeas usar el [Paywall Builder](adapty-paywall-builder) y has instalado el módulo AdaptyUI, necesitas que AdaptyUI esté activo. Puedes activarlo durante la configuración: ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetActivateUI(true); ``` ## Configuración opcional \{#optional-setup\} ### Registro de logs \{#logging\} #### Configurar el sistema de logs \{#set-up-the-logging-system\} Adapty registra errores y otra información importante para ayudarte a entender qué ocurre. Estos son los niveles disponibles: | Nivel | Descripción | | ---------- | ------------------------------------------------------------ | | `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 merecen atención | | `info` | Se registrarán los 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 durante la configuración de Adapty: ```csharp showLineNumbers title="C#" // 'verbose' is recommended for development and the first production release var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY"); builder.LogLevel = AdaptyLogLevel.Verbose; ``` También puedes cambiar el nivel de log en tiempo de ejecución: ```csharp showLineNumbers title="C#" Adapty.SetLogLevel(AdaptyLogLevel.Verbose, (error) => { // handle result }); ``` ### 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 adicionales de seguridad de datos para cumplir con las directrices del store o del país. #### Deshabilitar la recopilación y el uso compartido de la dirección IP \{#disable-ip-address-collection-and-sharing\} Al activar el módulo Adapty, establece `SetIPAddressCollectionDisabled` en `true` para deshabilitar 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 las regulaciones regionales de protección de datos (como el RGPD o la CCPA), o reducir la recopilación de datos innecesaria cuando las funciones basadas en IP no son necesarias para tu app. ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetIPAddressCollectionDisabled(true); ``` #### Deshabilitar la recopilación y el uso compartido del ID publicitario \{#disable-advertising-id-collection-and-sharing\} Al activar el módulo Adapty, establece `SetAppleIDFACollectionDisabled` y/o `SetGoogleAdvertisingIdCollectionDisabled` 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/Google Play, evitar activar el prompt de App Tracking Transparency, o si tu app no requiere atribución publicitaria ni analíticas basadas en IDs publicitarios. ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetAppleIDFACollectionDisabled(true) .SetGoogleAdvertisingIdCollectionDisabled(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 la caché proporcionando una configuración personalizada. Usa `SetAdaptyUIMediaCache` para sobreescribir la configuración predeterminada de la caché: ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetAdaptyUIMediaCache( 100 * 1024 * 1024, // MemoryStorageTotalCostLimit 100MB null, // MemoryStorageCountLimit 100 * 1024 * 1024 // DiskStorageSizeLimit 100MB ); ``` Parámetros: | Parámetro | Obligatorio | Descripción | |-----------------------------|-------------|----------------------------------------------------------------------------------| | memoryStorageTotalCostLimit | opcional | Tamaño total de la caché en memoria en bytes. Por defecto, valor específico de la plataforma. | | memoryStorageCountLimit | opcional | El límite de número de elementos en el almacenamiento en memoria. Por defecto, valor específico de la plataforma. | | diskStorageSizeLimit | opcional | El límite de tamaño de archivo en disco en bytes. Por defecto, valor específico 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 `SetGoogleLocalAccessLevelAllowed` en `true`: ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetGoogleLocalAccessLevelAllowed(true); ``` ### Limpiar datos al restaurar desde copia de seguridad \{#clear-data-on-backup-restore\} Cuando `SetAppleClearDataOnBackup` 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 del producto 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 permanecen sin cambios. ::: ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetAppleClearDataOnBackup(true); ``` ## Solución de problemas \{#troubleshooting\} #### Reglas de copia de seguridad de Android (configuración de Auto Backup) \{#android-backup-rules-auto-backup-configuration\} 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 `<manifest>` incluya tools: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.app"> ... </manifest> ``` #### 2. Sobreescribe los atributos de copia de seguridad en `<application>` \{#2-override-backup-attributes-in-application\} En el mismo archivo `AndroidManifest.xml`, actualiza la etiqueta `<application>` para que tu app proporcione los valores definitivos e indique al fusionador de manifiestos que reemplace los valores de las librerías: ```xml <application android:name=".App" android:allowBackup="true" android:fullBackupContent="@xml/sample_backup_rules" android:dataExtractionRules="@xml/sample_data_extraction_rules" tools:replace="android:fullBackupContent,android:dataExtractionRules"> ... </application> ``` 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" <?xml version="1.0" encoding="utf-8"?> <data-extraction-rules> <cloud-backup> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </cloud-backup> <device-transfer> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="appsflyer-purchase-data"/> <exclude domain="database" path="afpurchases.db"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> </device-transfer> </data-extraction-rules> ``` **Para Android 11 e inferior** (usa el formato legado de contenido de copia de seguridad completa): ```xml title="sample_backup_rules.xml" <?xml version="1.0" encoding="utf-8"?> <full-backup-content> <exclude domain="sharedpref" path="appsflyer-data"/> <exclude domain="sharedpref" path="AdaptySDKPrefs.xml"/> :::important En Unity, aplica estos cambios en `Assets/Plugins/Android/AndroidManifest.xml` y crea los archivos de reglas de copia de seguridad en `Assets/Plugins/Android/res/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 flujo de compra usa un `launchMode` no predeterminado, Android puede recrearla o reutilizarla incorrectamente 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, usa solo los modos de lanzamiento `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 <activity android:name=".MainActivity" android:launchMode="standard" /> ``` --- # File: unity-quickstart-paywalls --- --- title: "Habilitar compras usando paywalls en Unity SDK" description: "Aprende cómo presentar paywalls en tu aplicación Unity con el SDK de Adapty." --- Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - Los [**paywalls**](paywalls) son configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar ofertas, precios y combinaciones de productos sin tocar el código de tu app. - Los [**placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para los placements en el dashboard y luego los solicitas por ID de placement en tu código. Esto facilita la ejecución de pruebas A/B y mostrar diferentes paywalls a distintos usuarios. Adapty te ofrece tres formas de habilitar compras en tu app. Selecciona una según los requisitos de tu aplicación: | Implementación | Complejidad | Cuándo usarla | |---------------------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Paywall Builder | ✅ Fácil | [Creas un paywall completo y listo para compras en el editor 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 entre bastidores. | | Paywalls creados manualmente | 🟡 Medio | Implementas la UI de tu paywall en el código de tu app, pero igualmente obtienes el objeto paywall de Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](unity-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 paywall creado en el Adapty Paywall Builder.** Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](unity-making-purchases). ::: Para mostrar un paywall creado en el Adapty Paywall Builder, en el código de tu app solo necesitas: 1. **Obtener el paywall**: Obtener el paywall de Adapty. 2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app. 3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con el paywall con la respuesta de tu app a ellas. Por ejemplo, abrir enlaces o cerrar el paywall cuando los usuarios pulsan 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-unity) 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. Obtener el paywall \{#1-get-the-paywall\} Tus paywalls están asociados a placements configurados en el dashboard. Los placements te permiten ejecutar distintos paywalls para diferentes audiencias o realizar [pruebas A/B](ab-tests). Para obtener un paywall creado en el Adapty Paywall Builder, necesitas: 1. Obtener el objeto `paywall` por el ID del [placement](placements) usando el método `GetPaywall` y comprobar si fue creado en el builder mediante la propiedad `HasViewConfiguration`. 2. Crear la vista del paywall usando el método `CreatePaywallView`. La vista contiene los elementos de UI y el estilo necesarios para mostrar el paywall. :::important Para obtener la configuración de la vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará. ::: ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", (paywall, error) => { if(error != null) { // handle the error return; } // Create paywall view parameters var parameters = new AdaptyUICreatePaywallViewParameters(); // Create the paywall view AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { if(error != null) { // handle the error return; } // view - the paywall view ready to be presented }); }); ``` :::info Este inicio rápido proporciona la configuración mínima necesaria para mostrar un paywall. Para detalles de configuración avanzada, consulta nuestra [guía sobre cómo obtener paywalls](unity-get-pb-paywalls). ::: ## 2. Mostrar el paywall \{#2-display-the-paywall\} Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo. Para mostrar el paywall, usa el método `view.Present()` en el `view` creado por el método `CreatePaywallView`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `CreatePaywallView` otra vez para crear una nueva instancia de `view`. ```csharp showLineNumbers title="Unity" view.Present((error) => { // handle the error }); ``` :::info Para más detalles sobre cómo mostrar un paywall, consulta nuestra [guía](unity-present-paywalls). ::: ## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\} Cuando los usuarios pulsan botones en el paywall, el SDK de Unity gestiona automáticamente las compras y la restauración. Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren gestionar las acciones en tu código. Por ejemplo, tu paywall probablemente tenga un botón de cerrar y URLs que abrir (p. ej., términos de uso y política de privacidad). Para gestionar estas acciones, tu clase debe implementar la interfaz `AdaptyPaywallsEventsListener` y registrarse como listener. :::tip Lee nuestras guías sobre cómo gestionar [acciones](unity-handle-paywall-actions) y [eventos](unity-handling-events) de botones. ::: ```csharp showLineNumbers title="Unity" public class YourClass : MonoBehaviour, AdaptyPaywallsEventsListener { void Start() { // Register this class as the paywall events listener Adapty.SetPaywallsEventsListener(this); } // AdaptyPaywallsEventsListener method - handles button actions public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Close: view.Dismiss(null); break; case AdaptyUIUserActionType.OpenUrl: Application.OpenURL(action.Value); break; default: break; } } } ``` ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> 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](unity-check-subscription-status) para asegurarte de que muestras un paywall o das acceso a las funciones de pago a los usuarios correctos. ## Ejemplo completo \{#full-example\} Así es como todos esos pasos pueden integrarse juntos en tu app. ```csharp showLineNumbers using System; using UnityEngine; using AdaptySDK; public class PaywallManager : MonoBehaviour, AdaptyPaywallsEventsListener { [SerializeField] private string placementId = "YOUR_PLACEMENT_ID"; private AdaptyUIPaywallView currentPaywallView; void Start() { // Register for paywall events Adapty.SetPaywallsEventsListener(this); GetAndDisplayPaywall(); } private void GetAndDisplayPaywall() { Adapty.GetPaywall(placementId, (paywall, error) => { if (error != null) { Debug.LogError("Error getting paywall: " + error.Message); return; } if (paywall.HasViewConfiguration) { CreateAndPresentPaywallView(paywall); } else { Debug.LogWarning("Paywall was not created using the builder"); } }); } private void CreateAndPresentPaywallView(AdaptyPaywall paywall) { var parameters = new AdaptyUICreatePaywallViewParameters(); AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { if (error != null) { Debug.LogError("Error creating paywall view: " + error.Message); return; } currentPaywallView = view; view.Present((presentError) => { if (presentError != null) { Debug.LogError("Error presenting paywall: " + presentError.Message); return; } Debug.Log("Paywall presented successfully"); }); }); } // AdaptyPaywallsEventsListener implementation public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Close: Debug.Log("Close button pressed"); view.Dismiss(null); break; case AdaptyUIUserActionType.OpenUrl: Application.OpenURL(action.Value); break; default: break; } } // Required interface methods (implement as needed) public void PaywallViewDidAppear(AdaptyUIPaywallView view) { } public void PaywallViewDidDisappear(AdaptyUIPaywallView view) { } public void PaywallViewDidSelectProduct(AdaptyUIPaywallView view, string productId) { } public void PaywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) { } public void PaywallViewDidFinishPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyPurchaseResult purchasedResult) { } public void PaywallViewDidFailPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error) { } public void PaywallViewDidStartRestore(AdaptyUIPaywallView view) { } public void PaywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) { } public void PaywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) { } public void PaywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) { } public void PaywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) { } public void PaywallViewDidFinishWebPaymentNavigation(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error) { } public void ShowPaywall() { GetAndDisplayPaywall(); } void OnDestroy() { if (currentPaywallView != null) { currentPaywallView.Dismiss(null); } } } ``` --- # File: unity-check-subscription-status --- --- title: "Comprobar el estado de la suscripción en el SDK de Unity" description: "Aprende cómo comprobar el estado de la suscripción en tu app de Unity con Adapty." --- Para decidir si los usuarios pueden acceder al contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil. Este artículo te muestra cómo acceder al estado del perfil para decidir qué necesitan ver los usuarios: si mostrarles un paywall o darles acceso a las funciones de pago. ## Obtener el estado de la suscripción \{#get-subscription-status\} Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones: - Llama a `GetProfile` si necesitas los datos más recientes del perfil de inmediato (como al iniciar la app) o quieres forzar una actualización. - Configura **actualizaciones automáticas del perfil** para mantener una copia local que se actualiza automáticamente cada vez que cambia el estado de la suscripción. ### Obtener el perfil \{#get-profile\} La forma más sencilla de obtener el estado de la suscripción es usar el método `GetProfile` para acceder al perfil: ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // check the access }); ``` ### Escuchar actualizaciones de la suscripción \{#listen-to-subscription-updates\} Para recibir actualizaciones del perfil automáticamente en tu app: 1. Extiende `AdaptyEventListener` e implementa el método `OnLoadLatestProfile`: Adapty llamará a este método automáticamente cada vez que cambie el estado de la suscripción del usuario. 2. Almacena los datos del perfil actualizado cuando se llame a este método, para poder usarlos en toda la app sin realizar peticiones de red adicionales. ```csharp public class SubscriptionManager : MonoBehaviour, AdaptyEventListener { private AdaptyProfile currentProfile; void Start() { // Register this object as an Adapty event listener Adapty.SetEventListener(this); } // Store the profile when it updates public void OnLoadLatestProfile(AdaptyProfile profile) { currentProfile = profile; // Update UI, unlock content, etc. } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } // Use stored profile instead of calling getProfile() public bool HasAccess() { if (currentProfile?.AccessLevels != null && currentProfile.AccessLevels.ContainsKey("premium")) { return currentProfile.AccessLevels["premium"].IsActive; } return false; } } ``` :::note Adapty llama automáticamente a `OnLoadLatestProfile` cuando se inicia tu app, proporcionando datos de suscripción en caché incluso si el dispositivo está sin conexión. ::: ## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\} Cuando necesitas tomar decisiones inmediatas sobre mostrar paywalls o dar acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en situaciones como el inicio de la app, al entrar en secciones premium o antes de mostrar contenido específico. ```csharp private void CheckAccessLevel() { Adapty.GetProfile((profile, error) => { if (error != null) { Debug.LogError("Error checking access level: " + error.Message); // Show paywall if access check fails return; } var accessLevel = profile.AccessLevels["YOUR_ACCESS_LEVEL"]; if (accessLevel == null || !accessLevel.IsActive) { // Show paywall if no access } }); } private void InitializePaywall() { LoadPaywall(); CheckAccessLevel(); } ``` ## Próximos pasos \{#next-steps\} Ahora que sabes cómo rastrear el estado de la suscripción, aprende a [trabajar con perfiles de usuario](unity-quickstart-identify) para asegurarte de que pueden acceder a lo que han pagado. --- # File: unity-quickstart-identify --- --- title: "Identificar usuarios en el SDK de Unity" description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app en Unity." --- :::important Esta guía es para ti si tienes tu propio sistema de autenticación. Aquí aprenderás a trabajar con perfiles de usuario en Adapty para que se integre con tu sistema de autenticación existente. ::: La forma en que gestionas las compras de los usuarios depende del modelo de autenticación de tu app: - Si tu app no utiliza autenticación de backend y no almacena datos de usuario, consulta la [sección sobre usuarios anónimos](#anonymous-users). - Si tu app tiene (o tendrá) autenticación de backend, consulta la [sección sobre usuarios identificados](#identified-users). **Conceptos clave**: - Los **perfiles** son las entidades necesarias para que funcione el SDK. Adapty los crea automáticamente. - Pueden ser anónimos **(sin customer user ID)** o identificados **(con customer user ID)**. - Proporcionas el **customer user ID** para cruzar los perfiles de Adapty con tu sistema de autenticación interno. Estas son las diferencias entre usuarios anónimos e identificados: | | Usuarios anónimos | Usuarios identificados | |-------------------------------|----------------------------------------------------------|-------------------------------------------------------------------------------------| | **Gestión de compras** | Restauración de compras a nivel de store | Mantienen el historial de compras en todos los dispositivos mediante su customer user ID | | **Gestión de perfiles** | Nuevos perfiles en cada reinstalación | El mismo perfil en todas las sesiones y dispositivos | | **Persistencia de datos** | Los datos de usuarios anónimos están vinculados a la instalación de la app | Los datos de usuarios identificados persisten entre instalaciones | ## Usuarios anónimos \{#anonymous-users\} Si no tienes autenticación de backend, **no necesitas gestionar la autenticación en el código de la app**: 1. Cuando el SDK se activa en el primer arranque de la app, Adapty **crea un nuevo perfil para el usuario**. 2. Cuando el usuario realiza una compra en la app, esta compra queda **asociada a su perfil de Adapty y a su cuenta del store**. 3. Cuando el usuario **reinstala** la app o la instala en un **nuevo dispositivo**, Adapty **crea un nuevo perfil anónimo en la activación**. 4. Si el usuario ya había realizado compras en tu app, por defecto, sus compras se sincronizan automáticamente desde el App Store al activar el SDK. Con usuarios anónimos se crearán nuevos perfiles en cada instalación, pero eso no es un problema porque, en las analíticas de Adapty, puedes [configurar qué se considerará una nueva instalación](general#4-installs-definition-for-analytics). Para los usuarios anónimos, debes contar las instalaciones por **IDs de dispositivo**. En este caso, cada instalación de la app en un dispositivo se cuenta como una instalación, incluidas las reinstalaciones. ## Usuarios identificados \{#identified-users\} Tienes dos opciones para identificar a los usuarios en la app: - [**Durante el inicio de sesión/registro:**](#during-loginsignup) Si los usuarios inician sesión después de que arranca tu app, llama a `identify()` con un customer user ID cuando se autentiquen. - [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando arranca la app, envíalo al llamar a `activate()`. :::important Por defecto, cuando Adapty recibe una compra de un Customer User ID que actualmente está asociado a otro Customer User ID, el nivel de acceso se comparte, de modo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o deshabilitar el uso compartido por completo. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles. ::: <img src="/assets/shared/img/identify-diagram.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ### Durante el inicio de sesión/registro \{#during-loginsignup\} Si identificas a los usuarios después del arranque de la app (por ejemplo, después de que inicien sesión o se registren), usa el método `identify` para establecer su customer user ID. - Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual. - Si **ya has usado este customer user ID para identificar al usuario**, Adapty cambiará al perfil asociado a ese customer user ID. :::important Los customer user IDs deben ser únicos para cada usuario. Si hardcodeas el valor del parámetro, todos los usuarios se considerarán como uno solo. ::: Espera el callback de finalización de `Identify` antes de llamar a otros métodos del SDK. Las llamadas concurrentes producen `#3006 profileWasChanged` o aterrizan en el perfil anónimo. Consulta [Orden de llamadas en el SDK de Unity](unity-sdk-call-order). ```csharp showLineNumbers Adapty.Identify("YOUR_USER_ID", (error) => { // Único para cada usuario if(error == null) { // identificación correcta } }); ``` ### Durante la activación del SDK \{#during-the-sdk-activation\} Si ya conoces un customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado. Si conoces un customer user ID pero lo estableces solo después de la activación, eso significará que, al activarse, Adapty creará un nuevo perfil anónimo y cambiará al existente solo después de que llames a `identify`. Puedes pasar un customer user ID existente (uno que hayas usado antes) o uno nuevo. Si pasas uno nuevo, el nuevo perfil creado al activarse se vinculará automáticamente al customer user ID. :::note Por defecto, la creación de perfiles anónimos no afecta a los dashboards de analíticas, ya que las instalaciones se cuentan por IDs de dispositivo. Un ID de dispositivo representa una única instalación de la app desde el store en un dispositivo y solo se regenera tras reinstalar la app. No depende de si es una primera instalación o una reinstalación, ni de si se usa un customer user ID existente. Crear un perfil (al activar el SDK o al cerrar sesión), iniciar sesión o actualizar la app sin reinstalarla no genera eventos de instalación adicionales. Si quieres contar las instalaciones por usuarios únicos en lugar de dispositivos, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics). ::: ```csharp showLineNumbers using UnityEngine; using AdaptySDK; var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetCustomerUserId("YOUR_USER_ID"); // Los customer user IDs deben ser únicos para cada usuario. Si hardcodeas el valor del parámetro, todos los usuarios se considerarán como uno solo. Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); ``` ### Cerrar sesión de usuarios \{#log-users-out\} Si tienes un botón para cerrar la sesión de los usuarios, usa el método `logout`. :::important Cerrar la sesión de los usuarios crea un nuevo perfil anónimo para el usuario. ::: ```csharp showLineNumbers Adapty.Logout((error) => { if(error == null) { // cierre de sesión correcto } }); ``` :::info Para volver a iniciar sesión en la app, usa el método `identify`. ::: ### Permitir compras sin inicio de sesión \{#allow-purchases-without-login\} Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, debes asegurarte de que mantengan el acceso tras iniciar sesión: 1. Cuando un usuario sin sesión iniciada realiza una compra, Adapty la vincula a su ID de perfil anónimo. 2. Cuando el usuario inicia sesión en su cuenta, Adapty cambia al perfil identificado. - Si es un nuevo customer user ID (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que se mantiene todo el historial de compras. - Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), necesitas obtener el nivel de acceso actual tras el cambio de perfil. Puedes llamar a [`getProfile`](unity-check-subscription-status) justo después de la identificación, o [escuchar las actualizaciones del perfil](unity-check-subscription-status) para que los datos se sincronicen automáticamente. ## Próximos pasos \{#next-steps\} ¡Enhorabuena! Has implementado la lógica de pago in-app en tu app. ¡Te deseamos todo el éxito con la monetización de tu app! Para sacar aún más partido a Adapty, puedes explorar estos temas: - [**Pruebas**](troubleshooting-test-purchases): Asegúrate de que todo funciona como se espera - [**Onboardings**](onboardings): Engancha a los usuarios con onboardings e impulsa la retención - [**Integraciones**](configuration): Intégrate con servicios de atribución de marketing y analíticas con solo una línea de código - [**Establecer atributos de perfil personalizados**](unity-setting-user-attributes): Añade atributos personalizados a los perfiles de usuario y crea segmentos para lanzar pruebas A/B o mostrar diferentes paywalls a distintos usuarios --- # File: adapty-cursor-unity --- --- title: "Integra Adapty en tu app de Unity con IA" description: "Una guía paso a paso para integrar Adapty en tu app de Unity usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA." --- Esta página describe dos formas de integrar Adapty en tu app de Unity. Usa la skill de integración del SDK para un flujo automatizado de extremo a extremo, o sigue el recorrido manual más abajo. ## Usa la skill de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\} La [skill adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza la integración de principio a fin: configuración del dashboard, instalación del SDK, paywall y verificación por etapas. El recorrido manual más abajo es el plan B si tu herramienta no admite el formato Claude Skills. **Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI. ### Instalación \{#install\} Elige el formato según tu herramienta. La lista completa está en el [README de la skill](https://github.com/adaptyteam/adapty-sdk-integration-skill). - **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y luego `claude plugin install adapty-sdk-integration@adapty` desde tu terminal. - **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`. - **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`. - **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de skills de tu herramienta. ### Ejecución \{#run\} En tu proyecto, ejecuta `/adapty-sdk-integration`. La skill detecta tu plataforma y hace algunas preguntas de configuración. Luego te guía por la configuración del dashboard, la instalación del SDK, el paywall y la verificación, consultando la documentación de Adapty en cada etapa. :::note La skill está en beta. Si se detiene o se comporta de forma inesperada, el recorrido manual de más abajo cubre cada etapa paso a paso. ::: ## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\} Adapty requiere cierta configuración en el dashboard antes de escribir código. Puedes hacerlo con una skill interactiva de LLM o manualmente desde el Dashboard. ### Con la skill (recomendado) \{#skill-approach-recommended\} La skill de la CLI de Adapty permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin abrir el Dashboard en cada paso. Solo necesitas [conectar tus stores](integrate-payments) en el Dashboard. ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` Una vez añadida la skill, ejecuta `/adapty-cli` en tu agente. Te guiará por cada paso, incluido cuándo abrir el Dashboard para conectar tus stores. ### Con el Dashboard \{#dashboard-approach\} Si prefieres configurar todo manualmente, esto es lo que necesitas antes de escribir código. Tu LLM no puede buscar los valores del dashboard por ti — tendrás que proporcionárselos. 1. **Conecta tus app stores**: En el Adapty Dashboard, ve a **App settings → General**. Conecta tanto App Store como Google Play si tu app de Unity apunta a ambas plataformas. Esto es obligatorio para que las compras funcionen. [Conectar app stores](integrate-payments) 2. **Copia tu clave pública del SDK**: En el Adapty Dashboard, ve a **App settings → General** y busca la sección **API keys**. En el código, es la cadena que pasas al constructor de configuración de Adapty. 3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No haces referencia a los productos directamente en el código — Adapty los entrega a través de los paywalls. [Añadir productos](quickstart-products) 4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `Adapty.GetPaywall("YOUR_PLACEMENT_ID")`. [Crear paywall](quickstart-paywalls) 5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena que se comprueba en `profile.AccessLevels["premium"]?.IsActive`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago acceden a funciones distintas según el producto (por ejemplo, un plan `basic` frente a un plan `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar. :::tip Con estos cinco elementos listos, ya puedes escribir código. Dile a tu LLM: "Mi clave pública del SDK es X, mi ID de placement es Y" para que pueda generar el código correcto de inicialización y obtención del paywall. ::: ### Configura cuando estés listo \{#set-up-when-ready\} Estos elementos no son necesarios para empezar a programar, pero los querrás a medida que tu integración madure: - **Pruebas A/B**: Configúralas en la página **Placements**. No requieren cambios en el código. [Pruebas A/B](ab-tests) - **Paywalls y placements adicionales**: Añade más llamadas a `GetPaywall` con distintos IDs de placement. - **Integraciones de analíticas**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta [integraciones de analíticas](analytics-integration) e [integraciones de atribución](attribution-integration). ## Proporciona la documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\} ### Usa Context7 (recomendado) \{#use-context7-recommended\} [Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. El LLM obtiene automáticamente la documentación adecuada según lo que le preguntes — sin necesidad de pegar URLs manualmente. Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta: ``` npx ctx7 setup ``` Esto detecta tu editor y configura el servidor Context7. Para la configuración manual, consulta el [repositorio de Context7 en GitHub](https://github.com/upstash/context7). Una vez configurado, haz referencia a la biblioteca de Adapty en tus prompts: ``` Use the adaptyteam/adapty-docs library to look up how to install the Unity SDK ``` :::warning Aunque Context7 elimina la necesidad de pegar enlaces de documentación manualmente, el orden de implementación sigue siendo importante. Sigue el [recorrido de implementación](#implementation-walkthrough) paso a paso para asegurarte de que todo funciona correctamente. ::: ### Usa la documentación en texto plano \{#use-plain-text-docs\} Puedes acceder a cualquier artículo de Adapty en texto plano Markdown. Añade `.md` al final de su URL o haz clic en **Copy for LLM** debajo del título del artículo. Por ejemplo: [adapty-cursor-unity.md](https://adapty.io/docs/es/adapty-cursor-unity.md). Cada etapa del [recorrido de implementación](#implementation-walkthrough) incluye un bloque "Envía esto a tu LLM" con enlaces `.md` para pegar. Para acceder a más documentación a la vez, consulta los [archivos índice y subconjuntos por plataforma](#plain-text-doc-index-files) más abajo. ## Recorrido de implementación \{#implementation-walkthrough\} El resto de esta guía recorre la integración de Adapty en orden de implementación. Cada etapa incluye la documentación que debes enviar a tu LLM, lo que deberías ver al terminar y los problemas más comunes. ### Planifica tu integración \{#plan-your-integration\} Antes de ponerte a programar, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA admite un modo de planificación (como el de Cursor o Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir código. Indica a tu LLM qué enfoque usas para las compras — esto determina las guías que debe seguir: - [**Adapty Paywall Builder**](adapty-paywall-builder): Creas paywalls en el editor no-code de Adapty y el SDK los renderiza automáticamente. - [**Paywalls creados manualmente**](unity-making-purchases): Construyes tu propia interfaz de paywall en código, pero usas Adapty para obtener los productos y gestionar las compras. - [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analíticas e integraciones. ¿No sabes cuál elegir? Lee la [tabla comparativa del quickstart](unity-quickstart-paywalls). ### Instala y configura el SDK \{#install-and-configure-the-sdk\} Añade el paquete del SDK de Adapty a través del Unity Package Manager y actívalo con tu clave pública del SDK. Esta es la base — nada más funciona sin ella. **Guía:** [Instalar y configurar el SDK de Adapty](sdk-installation-unity) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/sdk-installation-unity.md ``` :::tip[Punto de control] - **Esperado:** El proyecto compila y se ejecuta. La consola de Unity muestra el log de activación de Adapty. - **Problema frecuente:** "Public API key is missing" → comprueba que hayas reemplazado el marcador de posición con tu clave real de App settings. ::: ### Muestra paywalls y gestiona las compras \{#show-paywalls-and-handle-purchases\} Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras. Prueba cada compra en el sandbox a medida que avances — no esperes al final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración. <Tabs groupId="paywall-approach"> <TabItem value="builder" label="Paywall Builder" default> **Guías:** - [Habilitar compras con paywalls (quickstart)](unity-quickstart-paywalls) - [Obtener paywalls del Paywall Builder y su configuración](unity-get-pb-paywalls) - [Mostrar paywalls](unity-present-paywalls) - [Gestionar eventos de paywall](unity-handling-events) - [Responder a acciones de botones](unity-handle-paywall-actions) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/unity-quickstart-paywalls.md - https://adapty.io/docs/es/unity-get-pb-paywalls.md - https://adapty.io/docs/es/unity-present-paywalls.md - https://adapty.io/docs/es/unity-handling-events.md - https://adapty.io/docs/es/unity-handle-paywall-actions.md ``` :::tip[Punto de control] - **Esperado:** El paywall aparece con tus productos configurados. Al pulsar un producto se abre el diálogo de compra en sandbox. - **Problema frecuente:** Paywall vacío o error en `GetPaywall` → verifica que el ID de placement coincide exactamente con el del dashboard y que el placement tiene una audiencia asignada. ::: </TabItem> <TabItem value="manual" label="Paywalls manuales"> **Guías:** - [Habilitar compras en tu paywall personalizado (quickstart)](unity-quickstart-manual) - [Obtener paywalls y productos](fetch-paywalls-and-products-unity) - [Renderizar paywalls diseñados con Remote Config](present-remote-config-paywalls-unity) - [Realizar compras](unity-making-purchases) - [Restaurar compras](unity-restore-purchase) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/unity-quickstart-manual.md - https://adapty.io/docs/es/fetch-paywalls-and-products-unity.md - https://adapty.io/docs/es/present-remote-config-paywalls-unity.md - https://adapty.io/docs/es/unity-making-purchases.md - https://adapty.io/docs/es/unity-restore-purchase.md ``` :::tip[Punto de control] - **Esperado:** Tu paywall personalizado muestra los productos obtenidos de Adapty. Al pulsar un producto se abre el diálogo de compra en sandbox. - **Problema frecuente:** Array de productos vacío → verifica que el paywall tiene productos asignados en el dashboard y que el placement tiene una audiencia. ::: </TabItem> <TabItem value="observer" label="Observer mode"> **Guías:** - [Descripción general del modo Observer](observer-vs-full-mode) - [Implementar el modo Observer](implement-observer-mode-unity) - [Reportar transacciones en modo Observer](report-transactions-observer-mode-unity) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/observer-vs-full-mode.md - https://adapty.io/docs/es/implement-observer-mode-unity.md - https://adapty.io/docs/es/report-transactions-observer-mode-unity.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox usando tu flujo de compras existente, la transacción aparece en el **Event Feed** del dashboard de Adapty. - **Problema frecuente:** Sin eventos → verifica que estás reportando las transacciones a Adapty y que las notificaciones del servidor están configuradas para ambos stores. ::: </TabItem> </Tabs> ### Comprueba el estado de la suscripción \{#check-subscription-status\} Tras una compra, comprueba el perfil de usuario para ver si hay un nivel de acceso activo que permita el acceso al contenido premium. **Guía:** [Comprobar el estado de la suscripción](unity-check-subscription-status) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/unity-check-subscription-status.md ``` :::tip[Punto de control] - **Esperado:** Tras una compra en sandbox, `profile.AccessLevels["premium"]?.IsActive` devuelve `true`. - **Problema frecuente:** `AccessLevels` vacío tras la compra → comprueba que el producto tiene un nivel de acceso asignado en el dashboard. ::: ### Identifica a los usuarios \{#identify-users\} Vincula las cuentas de usuario de tu app con los perfiles de Adapty para que las compras persistan entre dispositivos. :::important Omite este paso si tu app no tiene autenticación. ::: **Guía:** [Identificar usuarios](unity-quickstart-identify) Envía esto a tu LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/es/unity-quickstart-identify.md ``` :::tip[Punto de control] - **Esperado:** Tras llamar a `Adapty.Identify("your-user-id")`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado. - **Problema frecuente:** Llama a `Identify` después de la activación pero antes de obtener los paywalls para evitar problemas de atribución con perfiles anónimos. ::: ### Prepárate para el lanzamiento \{#prepare-for-release\} Una vez que tu integración funcione en el sandbox, revisa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción. **Guía:** [Lista de verificación para el lanzamiento](release-checklist) Envía esto a tu LLM: ``` Read these Adapty docs before releasing: - https://adapty.io/docs/es/release-checklist.md ``` :::tip[Punto de control] - **Esperado:** Todos los elementos de la lista confirmados: conexiones de stores, notificaciones del servidor, flujo de compras, comprobaciones de nivel de acceso y requisitos de privacidad. - **Problema frecuente:** Notificaciones del servidor no configuradas → configura las App Store Server Notifications en **App settings → iOS SDK** y las Google Play Real-Time Developer Notifications en **App settings → Android SDK**. ::: ## Archivos índice de documentación en texto plano \{#plain-text-doc-index-files\} Si necesitas dar a tu LLM un contexto más amplio más allá de páginas individuales, ofrecemos archivos índice que listan o combinan toda la documentación de Adapty: - [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con enlaces `.md`. Es un [estándar emergente](https://llmstxt.org/) para hacer los sitios web accesibles a los LLMs. Ten en cuenta que para algunos agentes de IA (por ejemplo, ChatGPT) necesitarás descargar `llms.txt` y subirlo al chat como archivo. - [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación del sitio de Adapty combinada en un único archivo. Muy grande — úsalo solo cuando necesites el panorama completo. - Subconjuntos específicos de Unity: [`unity-llms.txt`](https://adapty.io/docs/es/unity-llms.txt) y [`unity-llms-full.txt`](https://adapty.io/docs/es/unity-llms-full.txt): Subconjuntos específicos de la plataforma que ahorran tokens en comparación con el sitio completo. --- # File: unity-get-pb-paywalls --- --- title: "Obtener paywalls del Paywall Builder y su configuración en el SDK de Unity" description: "Aprende a recuperar paywalls de PB en Adapty para un mejor control de suscripciones en tu app de Unity." --- Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación. :::warning El nuevo Paywall Builder funciona con el SDK de Unity versión 3.3.0 o superior. ::: Ten en cuenta que este tema hace referencia a paywalls personalizados con Paywall Builder. Si estás implementando tus paywalls manualmente, consulta el tema [Obtener paywalls y productos para paywalls de Remote Config en tu app móvil](fetch-paywalls-and-products-unity). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a mostrar paywalls en tu app móvil (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en ellos](create-placement) en el Adapty Dashboard. 4. Instala el [SDK de Adapty](sdk-installation-unity) en tu app móvil. </details> ## Obtener el paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\} Si has [diseñado un paywall con el Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app móvil para mostrárselo al usuario. Este tipo de paywall contiene tanto lo que debe mostrarse como la forma en que debe mostrarse. Aun así, necesitas obtener su ID a través del placement, su configuración de vista, y luego presentarlo en tu app móvil. Para garantizar un rendimiento óptimo, es fundamental recuperar el paywall y su [configuración de vista](unity-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dejando tiempo suficiente para que las imágenes se descarguen antes de presentarlas al usuario. Para obtener un paywall, usa el método `GetPaywall`: ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", "en", (paywall, error) => { if(error != null) { // handle the error return; } // paywall - the resulting object }); ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | requerido | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma, la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls localmente en dos capas: la caché de actualización regular descrita arriba y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para que siempre obtengas la versión más reciente de tus paywalls, garantizando fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el respaldo local.</p><p>Ten en cuenta que en casos excepcionales este método puede agotar el tiempo ligeramente después de lo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.</p> | Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html) con una lista de IDs de productos, el identificador del paywall, Remote Config y varias otras propiedades. | ## Obtener la configuración de vista del paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important Asegúrate de activar el toggle **Show on device** en el Paywall Builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperar. ::: Después de obtener el paywall, comprueba si incluye una `ViewConfiguration`, lo que indica que fue creado con Paywall Builder. Esto te orientará sobre cómo mostrar el paywall. Si la `ViewConfiguration` está presente, trátalo como un paywall de Paywall Builder; si no, [trátalo como un paywall de Remote Config](present-remote-config-paywalls-unity). En el SDK de Unity, llama directamente al método `CreatePaywallView` sin necesidad de obtener primero la configuración de vista manualmente. :::warning El resultado del método `CreatePaywallView` solo puede usarse una vez. Si necesitas usarlo de nuevo, vuelve a llamar al método `CreatePaywallView`. Llamarlo dos veces sin recrearlo puede resultar en el error `AdaptyUIError.viewAlreadyPresented`. ::: ```csharp showLineNumbers var parameters = new AdaptyUICreatePaywallViewParameters() .SetPreloadProducts(preloadProducts) .SetLoadTimeout(new TimeSpan(0, 0, 3)); AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { // handle the result }); ``` Parámetros: | Parámetro | Presencia | Descripción | | :------------------ | :------------- | :----------------------------------------------------------- | | **paywall** | requerido | Un objeto `AdaptyPaywall` para obtener un controlador para el paywall deseado. | | **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el respaldo local. Ten en cuenta que en casos excepcionales este método puede agotar el tiempo ligeramente después de lo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente. | | **PreloadProducts** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el tiempo de visualización de los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. | | **CustomTags** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas actúan como marcadores de posición en el contenido del paywall, reemplazados dinámicamente por cadenas específicas para contenido personalizado. Consulta el tema [Etiquetas personalizadas en el Paywall Builder](custom-tags-in-paywall-builder) para más detalles. | | **CustomTimers** | opcional | Define un diccionario de temporizadores personalizados y sus fechas de finalización. Los temporizadores personalizados te permiten mostrar cuentas regresivas en tu paywall. | :::note Si usas varios idiomas, aprende cómo añadir una [localización del Paywall Builder](add-paywall-locale-in-adapty-paywall-builder) y cómo usar los códigos de idioma correctamente [aquí](localizations-and-locale-codes). ::: Una vez que tengas la vista, [presenta el paywall](unity-present-paywalls). ## Personalizar recursos \{#customize-assets\} Para personalizar imágenes y vídeos en tu paywall, implementa los recursos personalizados. Las imágenes y vídeos hero tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de recursos personalizado, apuntas a estos elementos por sus IDs y personalizas su comportamiento. Para otras imágenes y vídeos, necesitas [establecer un ID personalizado](custom-media) en el dashboard de Adapty. Por ejemplo, puedes: - Mostrar una imagen o vídeo diferente a algunos usuarios. - Mostrar una imagen de vista previa local mientras se carga la imagen principal remota. - Mostrar una imagen de vista previa antes de reproducir un vídeo. :::important Para usar esta función, actualiza el SDK de Adapty para Unity a la versión 3.8.0 o superior. ::: A continuación se muestra un ejemplo de cómo proporcionar recursos personalizados mediante un diccionario simple: ```csharp showLineNumbers var customAssets = new Dictionary<string, AdaptyCustomAsset> { { "custom_image", AdaptyCustomAsset.LocalImageFile("custom_assets/images/custom_image.png") }, { "hero_video", AdaptyCustomAsset.LocalVideoFile("custom_assets/videos/custom_video.mp4") } }; var parameters = new AdaptyUICreatePaywallViewParameters() .SetCustomAssets(customAssets) .SetLoadTimeout(new TimeSpan(0, 0, 3)); AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { // handle the result }); ``` :::note Si no se encuentra un recurso, el paywall usará su apariencia predeterminada. ::: ## Configurar temporizadores definidos por el desarrollador \{#set-up-developer-defined-timers\} Para usar temporizadores personalizados en tu app de Unity, puedes pasar un diccionario de IDs de temporizadores y sus fechas de finalización directamente al método `SetCustomTimers`. Aquí tienes un ejemplo: ```csharp showLineNumbers var customTimers = new Dictionary<string, DateTime> { { "CUSTOM_TIMER_6H", DateTime.Now.AddHours(6) }, { "CUSTOM_TIMER_NY", new DateTime(2025, 1, 1) } }; var parameters = new AdaptyUICreatePaywallViewParameters() .SetCustomTimers(customTimers) .SetLoadTimeout(new TimeSpan(0, 0, 3)); AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { // handle the result }); ``` En este ejemplo, `CUSTOM_TIMER_NY` y `CUSTOM_TIMER_6H` son los **Timer ID** de los temporizadores definidos por el desarrollador que configuraste en el Adapty Dashboard. El resolvedor de temporizadores garantiza que tu app actualice dinámicamente cada temporizador con el valor correcto. Por ejemplo: - `CUSTOM_TIMER_NY`: El tiempo restante hasta el final del temporizador, como el Año Nuevo. - `CUSTOM_TIMER_6H`: El tiempo restante en un período de 6 horas que comenzó cuando el usuario abrió el paywall. ## Acelerar la obtención del paywall con el paywall de audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\} Normalmente, los paywalls se obtienen casi al instante, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos en los que tengas numerosas audiencias y paywalls, y tus usuarios tengan una conexión a internet débil, obtener un paywall puede tardar más de lo deseable. En esas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún paywall. Para abordar esto, puedes usar el método `GetPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener paywall](#fetch-paywall) anterior. :::warning Considera usar `GetPaywall` en lugar de `GetPaywallForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: Puede crear problemas al dar soporte a varias versiones de la app, requiriendo diseños compatibles con versiones anteriores o aceptando que las versiones más antiguas puedan mostrarse incorrectamente. - **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación por país, atribución o atributos personalizados. Si la obtención más rápida supera estos inconvenientes para tu caso de uso, usa `GetPaywallForDefaultAudience` como se muestra a continuación. De lo contrario, usa `GetPaywall` como se describe [arriba](#fetch-paywall). ::: ```csharp showLineNumbers Adapty.GetPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", (paywall, error) => { if(error != null) { // handle the error return; } // paywall - the resulting object }); ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | requerido | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del paywall. Se espera que este parámetro sea un código de idioma compuesto por uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma, la segunda para la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls localmente en dos capas: la caché de actualización regular descrita arriba y los paywalls de respaldo. También usamos CDN para obtener paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para que siempre obtengas la versión más reciente de tus paywalls, garantizando fiabilidad incluso cuando la conexión a internet es escasa.</p> | --- # File: unity-present-paywalls --- --- title: "Mostrar paywalls" description: "Aprende cómo mostrar paywalls en tu app de Unity con el SDK de Adapty." --- Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese paywall contiene tanto lo que debe mostrarse como la forma en que debe hacerse. :::warning Esta guía cubre el **nuevo Paywall Builder**, que requiere el SDK de Adapty 3.3.0 o posterior. Para mostrar paywalls con Remote Config, consulta [Renderizar paywalls diseñados con remote config](present-remote-config-paywalls). ::: Para mostrar un paywall, usa el método `view.Present()` sobre el `view` creado por el método [`CreatePaywallView`](unity-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `CreatePaywallView` otra vez para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo puede provocar el error `AdaptyUIError.viewAlreadyPresented`. ::: ```csharp showLineNumbers title="Unity" view.Present((error) => { // handle the error }); ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Mostrar diálogo \{#show-dialog\} Usa este método en lugar de los diálogos de alerta nativos cuando hay un paywall visible en Android. En Android, las alertas normales aparecen detrás del paywall y el usuario no puede verlas. Este método garantiza que el diálogo se muestre correctamente por encima del paywall en todas las plataformas. ```csharp showLineNumbers title="Unity" var dialog = new AdaptyUIDialogConfiguration() .SetTitle("Close paywall?") .SetContent("You will lose access to exclusive offers.") .SetDefaultActionTitle("Stay") .SetSecondaryActionTitle("Close"); AdaptyUI.ShowDialog(view, dialog, (action, error) => { if (error == null) { if (action == AdaptyUIDialogActionType.Secondary) { // User confirmed - close the paywall view.Dismiss(); } // If primary - do nothing, user stays } }); ``` ## Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el paywall en iOS pasando el parámetro `iosPresentationStyle` al método `Present()`. El parámetro acepta los valores `AdaptyUIIOSPresentationStyle.FullScreen` (predeterminado) o `AdaptyUIIOSPresentationStyle.PageSheet`. ```csharp showLineNumbers title="Unity" view.Present(AdaptyUIIOSPresentationStyle.PageSheet, (error) => { // handle the error }); ``` --- # File: unity-handle-paywall-actions --- --- title: "Responder a acciones de botones en el SDK de Unity" description: "Gestiona las acciones de botones de paywall en Unity usando Adapty para una mejor monetización de la app." --- Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente: 1. Añade un [botón en el Paywall Builder](paywall-buttons) y asígnale una acción existente o crea un ID de acción personalizado. 2. Escribe código en tu app para gestionar cada acción que hayas asignado. Esta guía muestra cómo gestionar acciones personalizadas y predefinidas en tu código. :::warning **Solo las compras y restauraciones se gestionan automáticamente.** El resto de acciones de botones, como cerrar paywalls o abrir enlaces, requieren implementar respuestas específicas en el código de la app. ::: ## Cerrar paywalls \{#close-paywalls\} Para añadir un botón que cierre tu paywall: 1. En el Paywall Builder, añade un botón y asígnale la acción **Close**. 2. En el código de tu app, implementa un handler para la acción `close` que descarte el paywall. ```csharp showLineNumbers title="Unity" public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Close: view.Dismiss(null); break; default: // handle other events break; } } ``` ## Abrir URLs desde paywalls \{#open-urls-from-paywalls\} :::tip Si quieres añadir un grupo de enlaces (p. ej., términos de uso y restauración de compras), añade un elemento **Link** en el Paywall Builder y gestíonalo igual que los botones con la acción **Open URL**. ::: Para añadir un botón que abra un enlace desde tu paywall (p. ej., **Terms of use** o **Privacy policy**): 1. En el Paywall Builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir. 2. En el código de tu app, implementa un handler para la acción `openUrl` que abra la URL recibida en un navegador. ```csharp showLineNumbers title="Unity" public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.OpenUrl: var urlString = action.Value; if(!string.IsNullOrWhiteSpace(urlString)) { Application.OpenURL(urlString); } break; default: // handle other events break; } } ``` ## Iniciar sesión en la app \{#log-into-the-app\} Para añadir un botón que permita a los usuarios iniciar sesión en tu app: 1. En el Paywall Builder, añade un botón y asígnale la acción **Custom** con el ID `login`. 2. En el código de tu app, implementa un handler para la acción personalizada `login` que identifique a tu usuario. ```csharp showLineNumbers title="Unity" public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Custom: if (action.Value == "login") { // Navigate to login scene SceneManager.LoadScene("LoginScene"); } break; default: // handle other events break; } } ``` ## Gestionar acciones personalizadas \{#handle-custom-actions\} Para añadir un botón que gestione cualquier otra acción: 1. En el Paywall Builder, añade un botón, asígnale la acción **Custom** y asígnale un ID. 2. En el código de tu app, implementa un handler para el ID de acción que hayas creado. Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall: ```csharp showLineNumbers title="Unity" public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Custom: if (action.Value == "openNewPaywall") { // Display another paywall ShowAlternativePaywall(); } break; default: // handle other events break; } } private void ShowAlternativePaywall() { // Implement your logic to show alternative paywall } ``` --- # File: unity-handling-events --- --- title: "Gestionar eventos del paywall" description: "Aprende a gestionar eventos del paywall en tu aplicación Unity con el SDK de Adapty." --- :::important Esta guía cubre la gestión de eventos para compras, restauraciones, selección de productos y renderizado del paywall. También debes implementar el manejo de botones (cerrar paywall, abrir enlaces, etc.). Consulta nuestra [guía sobre el manejo de acciones de botones](unity-handle-paywall-actions) para más detalles. ::: Los paywalls configurados con el [Paywall Builder](adapty-paywall-builder) no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan ciertos eventos a los que tu aplicación puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.), así como notificaciones sobre acciones relacionadas con compras realizadas en el paywall. A continuación aprenderás cómo responder a estos eventos. :::warning Esta guía es **exclusivamente para paywalls del nuevo Paywall Builder**, que requieren el SDK de Adapty v3.3.0 o posterior. ::: :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Gestión de eventos \{#handling-events\} Para controlar o monitorear los procesos que ocurren en la pantalla del paywall dentro de tu aplicación móvil, implementa la interfaz `AdaptyPaywallsEventsListener`: ```csharp showLineNumbers title="Unity" using UnityEngine; using AdaptySDK; public class PaywallEventsHandler : MonoBehaviour, AdaptyPaywallsEventsListener { void Start() { Adapty.SetPaywallsEventsListener(this); } // Implement all required interface methods below } ``` ### Eventos generados por el usuario \{#user-generated-events\} #### Paywall mostrado \{#paywall-appeared\} Se invoca cuando la vista del paywall aparece en pantalla. :::note En iOS, también se invoca cuando el usuario pulsa el [botón del web paywall](web-paywall#step-2a-add-a-web-purchase-button) dentro de un paywall y el web paywall se abre en un navegador integrado. ::: ```csharp showLineNumbers title="Unity" public void PaywallViewDidAppear(AdaptyUIPaywallView view) { } ``` #### Paywall ocultado \{#paywall-disappeared\} Se invoca cuando la vista del paywall desaparece de la pantalla. :::note En iOS, también se invoca cuando un [web paywall](web-paywall#step-2a-add-a-web-purchase-button) abierto desde un paywall en un navegador integrado desaparece de la pantalla. ::: ```csharp showLineNumbers title="Unity" public void PaywallViewDidDisappear(AdaptyUIPaywallView view) { } ``` #### Selección de producto \{#product-selection\} Se invoca cuando se selecciona un producto para comprar (por el usuario o por el sistema). ```csharp showLineNumbers title="Unity" public void PaywallViewDidSelectProduct( AdaptyUIPaywallView view, string productId ) { } ``` <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```javascript { "productId": "premium_monthly" } ``` </Details> #### Compra iniciada \{#started-purchase\} Se invoca cuando el usuario inicia el proceso de compra. ```csharp showLineNumbers title="Unity" public void PaywallViewDidStartPurchase( AdaptyUIPaywallView view, AdaptyPaywallProduct product ) { } ``` <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ``` </Details> #### Compra exitosa, cancelada o pendiente \{#successful-canceled-or-pending-purchase\} Si la compra se completa correctamente, el usuario la cancela, o queda en estado pendiente, se invocará este método. Las cancelaciones del usuario y los pagos pendientes (como los que requieren aprobación parental) activan este método, no `PaywallViewDidFailPurchase`. ```csharp showLineNumbers title="Unity" public void PaywallViewDidFinishPurchase( AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyPurchaseResult purchasedResult ) { } ``` <Details> <summary>Ejemplos de eventos (Haz clic para expandir)</summary> ```javascript // Successful purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "Success", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } } } // Cancelled purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "UserCancelled" } } // Pending purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "Pending" } } ``` </Details> Recomendamos cerrar la pantalla en ese caso. #### Compra fallida \{#failed-purchase\} Si una compra falla debido a un error, se invocará este método. Esto incluye errores de StoreKit/Google Play Billing (restricciones de pago, productos inválidos, fallos de red), errores de verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan `PaywallViewDidFinishPurchase` con un resultado de cancelación, y los pagos pendientes no activan este método. ```csharp showLineNumbers title="Unity" public void PaywallViewDidFailPurchase( AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error ) { } ``` <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" } } } ``` </Details> #### Restauración iniciada \{#started-restore\} Se invoca cuando el usuario inicia el proceso de restauración: ```csharp showLineNumbers title="Unity" public void PaywallViewDidStartRestore(AdaptyUIPaywallView view) { } ``` #### Restauración exitosa \{#successful-restore\} Se invoca cuando la restauración de compras se completa correctamente: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFinishRestore( AdaptyUIPaywallView view, AdaptyProfile profile ) { } ``` <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```javascript { "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } ``` </Details> Recomendamos cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de la suscripción](unity-listen-subscription-changes) para aprender cómo verificarlo. #### Restauración fallida \{#failed-restore\} Se invoca cuando la restauración de compras falla: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFailRestore( AdaptyUIPaywallView view, AdaptyError error ) { } ``` <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```javascript { "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } ``` </Details> #### Navegación web de pago finalizada \{#finished-web-payment-navigation\} Después de intentar abrir un [web paywall](web-paywall) para realizar una compra (tanto si tuvo éxito como si falló), se invocará este método: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFinishWebPaymentNavigation( AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error ) { } ``` **Parámetros:** - `product`: El producto para el que se abrió (o intentó abrir) el web paywall - `error`: `null` si el web paywall se abrió correctamente, o un `AdaptyError` si falló <Details> <summary>Ejemplos de eventos (Haz clic para expandir)</summary> ```javascript // Successful navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": null } // Failed navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "wrong_param", "message": "Current method is not available for this product", "details": { "underlyingError": "Product not configured for web purchases" } } } ``` </Details> ### Obtención de datos y renderizado \{#data-fetching-and-rendering\} #### Errores de carga de productos \{#product-loading-errors\} Se invoca cuando falla la carga de productos y proporciona un `AdaptyError`. Si no pasaste el array de productos durante la inicialización, AdaptyUI recuperará los objetos necesarios del servidor por sí solo. Esta operación puede fallar, y AdaptyUI reportará el error invocando este método: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFailLoadingProducts( AdaptyUIPaywallView view, AdaptyError error ) { } ``` <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```javascript { "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } ``` </Details> #### Errores de renderizado \{#rendering-errors\} Se invoca cuando ocurre un error durante el renderizado de la interfaz y proporciona un `AdaptyError`: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFailRendering( AdaptyUIPaywallView view, AdaptyError error ) { } ``` <Details> <summary>Ejemplo de evento (Haz clic para expandir)</summary> ```javascript { "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } ``` </Details> En condiciones normales, estos errores no deberían producirse, por lo que si encuentras alguno, por favor haznos saber. --- # File: unity-web-paywalls --- --- title: "Implementar web paywalls en Unity SDK" description: "Configura un web paywall para cobrar sin las comisiones y auditorías del App Store." --- :::important Antes de comenzar, asegúrate de haber [configurado tu web paywall en el dashboard](web-paywall) y de tener instalada la versión 3.14 o posterior del SDK de Adapty. ::: ## Abrir web paywalls \{#open-web-paywalls\} Si trabajas con un paywall que has desarrollado tú mismo, necesitas gestionar los web paywalls mediante el método del SDK. El método `Adapty.OpenWebPaywall`: 1. Genera una URL única que permite a Adapty vincular un paywall concreto mostrado a un usuario con la página web a la que se le redirige. 2. Detecta cuando tus usuarios vuelven a la app y, a continuación, llama a `Adapty.GetProfile` en intervalos cortos para determinar si se han actualizado los derechos de acceso del perfil. De este modo, si el pago se ha realizado con éxito y los derechos de acceso se han actualizado, la suscripción se activa en la app casi de inmediato. ```csharp showLineNumbers title="Unity" Adapty.OpenWebPaywall( product, (error) => { if (error != null) { Debug.LogError($"Failed to open web paywall: {error.Message}"); } else { Debug.Log("Web paywall opened successfully"); } } ); ``` :::note Existen dos versiones del método `OpenWebPaywall`: 1. `OpenWebPaywall(product)`, que genera URLs a partir del paywall y también añade los datos del producto a las URLs. 2. `OpenWebPaywall(paywall)`, que genera URLs a partir del paywall sin añadir los datos del producto a las URLs. Úsalo cuando los productos de tu paywall en Adapty sean distintos a los del web paywall. ::: #### Gestionar errores \{#handle-errors\} | Código de error | Descripción | Acción recomendada | |-----------|--------------------------------------------------------|---------------------------------------------------------------------------| | `AdaptyErrorCode.WrongParam` | El paywall o el producto no tiene configurada una URL de compra web, o no se pudo abrir la URL en el navegador | Revisa el mensaje de error para más detalles. Verifica la configuración del paywall/producto en el Adapty Dashboard, o comprueba la configuración del dispositivo. | | `AdaptyErrorCode.DecodingFailed` | No se pudieron codificar correctamente los parámetros en la URL | Verifica que los parámetros de la URL sean válidos y estén correctamente formateados | :::note Consulta la propiedad `Message` del error para obtener detalles concretos sobre qué salió mal, ya que `WrongParam` puede indicar varios problemas (URL de compra ausente, error al abrir el navegador, etc.). ::: ## Abrir web paywalls en un navegador in-app \{#open-web-paywalls-in-an-in-app-browser\} :::important La apertura de web paywalls en un navegador in-app está disponible a partir del SDK de Adapty v. 3.15. ::: Por defecto, los web paywalls se abren en el navegador externo, lo que lleva a los usuarios fuera de tu app. Para ofrecer una experiencia de usuario fluida, puedes abrir los web paywalls en un navegador in-app. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin cambiar de app. Para habilitarlo, pasa `AdaptyWebPresentation.InAppBrowser` al método `OpenWebPaywall`: ```csharp showLineNumbers title="Unity" Adapty.OpenWebPaywall( product, AdaptyWebPresentation.InAppBrowser, // default — ExternalBrowser (error) => { if (error != null) { Debug.LogError($"Failed to open web paywall: {error.Message}"); } else { Debug.Log("Web paywall opened successfully"); } } ); ``` --- # File: unity-use-fallback-paywalls --- --- title: "Unity - Usar paywalls de respaldo" description: "Gestiona los casos en los que los usuarios están sin conexión o los servidores de Adapty no están disponibles" --- :::warning Los paywalls de respaldo son compatibles con Unity SDK v2.11 y versiones posteriores. ::: Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet. * **Si la aplicación no puede acceder a los servidores de Adapty:** Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding. * **Si la aplicación no puede acceder a internet:** Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar. :::important Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty. ::: ## Configuración \{#configuration\} 1. Añade los archivos de configuración de respaldo al directorio común `Assets/StreamingAssets` de tu proyecto. 2. Llama al método `.setFallback` **antes** de obtener el paywall o el onboarding de destino. ```csharp using UnityEngine; using AdaptySDK; #if UNITY_IOS string fileName = "ios_fallback.json"; #elif UNITY_ANDROID string fileName = "android_fallback.json"; #else // Optional: handle Editor or other platforms string fileName = "fallback.json"; #endif Adapty.SetFallback(fileName, (error) => { if (error != null) { Debug.LogError($"Failed to set fallback: {error}"); return; } // Fallback set successfully }); ``` Parámetros: | Parámetro | Descripción | |:-------------|:-----------------------------------------------------| | **fileName** | La cadena con el nombre del archivo de configuración de respaldo. | --- # File: unity-troubleshoot-paywall-builder --- --- title: "Solucionar problemas del Paywall Builder en el SDK de Unity" description: "Solucionar problemas del Paywall Builder en el SDK de Unity" --- Esta guía te ayuda a resolver los problemas más comunes al usar paywalls diseñados en el Adapty Paywall Builder con el SDK de Unity. ## Falla al obtener la configuración de un paywall \{#getting-a-paywall-configuration-fails\} **Problema**: El método `CreateView` no puede obtener la configuración del paywall. **Causa**: El paywall no está habilitado para mostrarse en el dispositivo desde el Paywall Builder. **Solución**: Activa el interruptor **Show on device** en el Paywall Builder. <img src="/assets/shared/img/show-on-device.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## El número de vistas del paywall es demasiado alto \{#the-paywall-view-number-is-too-big\} **Problema**: El contador de vistas del paywall muestra el doble del número esperado. **Causa**: Es posible que estés llamando a `LogShowPaywall` en tu código, lo que duplica el contador de vistas cuando se usa el Paywall Builder. Para los paywalls diseñados con el Paywall Builder, el seguimiento de análisis es automático, por lo que no es necesario usar este método. **Solución**: Asegúrate de no llamar a `LogShowPaywall` en tu código si estás usando el Paywall Builder. ## Otros problemas \{#other-issues\} **Problema**: Estás experimentando otros problemas relacionados con el Paywall Builder que no se han cubierto anteriormente. **Solución**: Si es necesario, migra el SDK a la versión más reciente usando las [guías de migración](unity-sdk-migration-guides). Muchos problemas se resuelven en versiones más nuevas del SDK. --- # File: unity-quickstart-manual --- --- title: "Habilitar compras en tu paywall personalizado con Unity SDK" description: "Integra el SDK de Adapty en tus paywalls personalizados de Unity para habilitar compras in-app." --- Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall, mientras el SDK de Adapty obtiene los productos, gestiona las nuevas compras y restaura las anteriores. :::important **Esta guía está dirigida a desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Adapty Paywall Builder](unity-quickstart-paywalls). Con Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compras automáticamente y puedes probar distintos diseños sin volver a publicar tu app. ::: ## Antes de empezar \{#before-you-start\} ### Configura los productos \{#set-up-products\} Para habilitar las compras in-app, necesitas entender tres conceptos clave: - [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida) - [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de obtener productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app. - [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para 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 distintos paywalls a diferentes usuarios. Asegúrate de entender estos conceptos incluso si trabajas con tu paywall personalizado. Básicamente, son solo tu forma de gestionar los productos que vendes en tu app. Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite obtener tus productos. Para saber qué debes hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart). ### Gestiona usuarios \{#manage-users\} Puedes trabajar con o sin autenticación de backend en tu lado. Sin embargo, el SDK de Adapty gestiona de forma diferente a los usuarios anónimos e identificados. Lee la [guía de inicio rápido de identificación](unity-quickstart-identify) para entender las particularidades y asegurarte de que trabajas correctamente con los usuarios. ## Paso 1. Obtén los productos \{#step-1-get-products\} Para obtener los productos de tu paywall personalizado, necesitas: 1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`. 2. Obtener el array de productos para este paywall usando el método `getPaywallProducts`. ```csharp showLineNumbers using AdaptySDK; void LoadPaywall() { Adapty.GetPaywall("YOUR_PLACEMENT_ID", (paywall, error) => { if (error != null) { // Handle the error return; } Adapty.GetPaywallProducts(paywall, (products, productsError) => { if (productsError != null) { // Handle the error return; } // Use products to build your custom paywall UI }); }); } ``` ## Paso 2. Acepta compras \{#step-2-accept-purchases\} Cuando un usuario toca un producto en tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado. ```csharp showLineNumbers using AdaptySDK; void PurchaseProduct(AdaptyPaywallProduct product) { Adapty.MakePurchase(product, (result, error) => { if (error != null) { // Handle the error return; } switch (result.Type) { case AdaptyPurchaseResultType.Success: var profile = result.Profile; // Purchase successful, profile updated break; case AdaptyPurchaseResultType.UserCancelled: // User canceled the purchase break; case AdaptyPurchaseResultType.Pending: // Purchase is pending (e.g., user will pay offline with cash) break; } }); } ``` ## Paso 3. Restaura compras \{#step-3-restore-purchases\} Los app stores requieren que todas las apps con suscripciones ofrezcan una forma de que los usuarios puedan restaurar sus compras. Llama al método `restorePurchases` cuando el usuario toque el botón de restaurar. Esto sincronizará su historial de compras con Adapty y devolverá el perfil actualizado. ```csharp showLineNumbers using AdaptySDK; void RestorePurchases() { Adapty.RestorePurchases((profile, error) => { if (error != null) { // Handle the error return; } // Restore successful, profile updated }); } ``` ## Próximos pasos \{#next-steps\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Callout type="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! </Callout> Tu paywall está listo para mostrarse en la app. Prueba tus compras en el [sandbox de 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. A continuación, [comprueba si los usuarios han completado su compra](unity-check-subscription-status) para determinar si mostrar el paywall o dar acceso a las funciones de pago. --- # File: fetch-paywalls-and-products-unity --- --- title: "Obtener paywalls y productos para paywalls con Remote Config en Unity SDK" description: "Obtén paywalls y productos en el SDK de Unity de Adapty para mejorar la monetización de los usuarios." --- Antes de mostrar paywalls con Remote Config y personalizados, necesitas obtener la información sobre ellos. Ten en cuenta que este tema hace referencia a paywalls con Remote Config y personalizados. Para obtener orientación sobre cómo recuperar paywalls creados con Paywall Builder, consulta [Obtener paywalls de Paywall Builder y su configuración](unity-get-pb-paywalls). :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: <details> <summary>Antes de empezar a obtener paywalls y productos en tu app (haz clic para expandir)</summary> 1. [Crea tus productos](create-product) en el Adapty Dashboard. 2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard. 3. [Crea placements e incorpora tu paywall en el placement](create-placement) en el Adapty Dashboard. 4. [Instala el SDK de Adapty](sdk-installation-unity) en tu app. </details> ## Obtener información del paywall \{#fetch-paywall-information\} En Adapty, un [producto](product) es una combinación de productos del App Store y Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos en placements específicos de tu app. Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`. :::important **No escribas los IDs de producto en el código.** El único ID que debes incluir en el código es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe gestionar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana tres, muéstralos todos sin cambiar el código. ::: ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", "en", (paywall, error) => { if(error != null) { // handle the error return; } // paywall - the resulting object }); ``` | Parámetro | Presencia | Descripción | |---------|--------|-----------| | **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p></p><p>Consulta [Localizaciones y códigos de idioma](unity-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen conexiones a internet inestables, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](unity-use-fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no esté disponible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el respaldo local.</p><p></p><p>Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede constar de diferentes solicitudes internamente.</p> | ¡No escribas los IDs de producto en el código! Dado que los paywalls se configuran de forma remota, los productos disponibles, el número de productos y las ofertas especiales (como pruebas gratuitas) pueden cambiar con el tiempo. Asegúrate de que tu código gestione estos escenarios. Por ejemplo, si inicialmente obtienes 2 productos, tu app debería mostrar esos 2 productos. Sin embargo, si más adelante obtienes 3 productos, tu app debería mostrar los 3 sin requerir cambios en el código. Lo único que tienes que incluir en el código es el ID del placement. Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objeto [`AdaptyPaywall`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html) con: una lista de IDs de producto, el identificador del paywall, el Remote Config y varias otras propiedades. | ## Obtener productos \{#fetch-products\} Una vez que tienes el paywall, puedes consultar el array de productos correspondiente: ```csharp showLineNumbers Adapty.GetPaywallProducts(paywall, (products, error) => { if(error != null) { // handle the error return; } // products - the requested products array }); ``` Parámetros de respuesta: | Parámetro | Descripción | | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Products | Lista de objetos [`AdaptyPaywallProduct`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall_product.html) con: identificador del producto, nombre del producto, precio, moneda, duración de la suscripción y varias otras propiedades. | Al implementar tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall_product.html). A continuación se muestran las propiedades más utilizadas, pero consulta el documento enlazado para obtener todos los detalles de las propiedades disponibles. | Propiedad | Descripción | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | Para mostrar el título del producto, usa `product.LocalizedTitle`. Ten en cuenta que la localización se basa en el país del store seleccionado por el usuario, no en el idioma del propio dispositivo. | | **Price** | Para mostrar una versión localizada del precio, usa `product.Price.LocalizedString`. Esta localización se basa en la configuración regional del dispositivo. También puedes acceder al precio como número usando `product.Price.Amount`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda asociado, usa `product.Price.CurrencySymbol`. | | **Subscription Period** | Para mostrar el período (p. ej., semana, mes, año, etc.), usa `product.Subscription?.LocalizedPeriod`. Esta localización se basa en la configuración regional del dispositivo. Para obtener el período de suscripción mediante programación, usa `product.Subscription?.Period`. Desde ahí puedes acceder al enum `Unit` para obtener la duración (es decir, `AdaptySubscriptionPeriodUnit.Day`, `AdaptySubscriptionPeriodUnit.Week`, `AdaptySubscriptionPeriodUnit.Month`, `AdaptySubscriptionPeriodUnit.Year` o `AdaptySubscriptionPeriodUnit.Unknown`). El valor `NumberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral, verías `AdaptySubscriptionPeriodUnit.Month` en la propiedad Unit y `3` en la propiedad NumberOfUnits. | | **Introductory Offer** | Para mostrar un distintivo u otro indicador de que una suscripción incluye una oferta introductoria, revisa la propiedad `product.Subscription?.Offer?.Phases`. Esta es una lista que puede contener hasta dos fases de descuento: la fase de prueba gratuita y la fase de precio introductorio. Dentro de cada objeto de fase están las siguientes propiedades útiles:<br/>• `PaymentMode`: un enum con los valores `AdaptyPaymentMode.FreeTrial`, `AdaptyPaymentMode.PayAsYouGo`, `AdaptyPaymentMode.PayUpFront` y `AdaptyPaymentMode.Unknown`. Las pruebas gratuitas serán del tipo `AdaptyPaymentMode.FreeTrial`.<br/>• `Price`: El precio con descuento como número. Para las pruebas gratuitas, busca `0` aquí.<br/>• `LocalizedNumberOfPeriods`: una cadena localizada según el idioma del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `"3 days"` en este campo.<br/>• `SubscriptionPeriod`: Alternativamente, puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que la sección anterior describe.<br/>• `LocalizedSubscriptionPeriod`: Un período de suscripción formateado del descuento para la configuración regional del usuario. | ## Acelerar la obtención de paywalls con el paywall de audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\} Normalmente, los paywalls se obtienen casi al instante, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos donde tienes numerosas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseado. En estas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún paywall. Para resolver esto, puedes usar el método `GetPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener información del paywall](#fetch-paywall-information) anterior. :::warning Considera usar `GetPaywall` en lugar de `GetPaywallForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: Puede crear problemas al dar soporte a múltiples versiones de la app, requiriendo diseños retrocompatibles o aceptando que las versiones más antiguas puedan mostrarse incorrectamente. - **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación basada en país, atribución o atributos personalizados. Si una obtención más rápida compensa estos inconvenientes para tu caso de uso, usa `GetPaywallForDefaultAudience` como se muestra a continuación. De lo contrario, usa `GetPaywall` como se describe [anteriormente](#fetch-paywall-information). ::: ```csharp showLineNumbers Adapty.GetPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", (paywall, error) => { if(error != null) { // handle the error return; } // paywall - the resulting object }); ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del paywall. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen conexiones a internet inestables, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.</p><p></p><p>El SDK de Adapty almacena los paywalls localmente en dos capas: la caché actualizada regularmente descrita anteriormente y los paywalls de respaldo. También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no esté disponible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | --- # File: present-remote-config-paywalls-unity --- --- title: "Renderizar paywall diseñado con Remote Config en Unity SDK" description: "Descubre cómo presentar paywalls con Remote Config en Adapty Unity SDK para personalizar la experiencia de usuario." --- Si has personalizado un paywall usando Remote Config, necesitarás implementar el renderizado en el código de tu app para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú decides qué incluir y cómo se ve tu paywall. Proporcionamos un método para obtener la configuración remota, dándote la autonomía de mostrar tu paywall personalizado configurado a través de Remote Config. ## Obtener el Remote Config del paywall y presentarlo \{#get-paywall-remote-config-and-present-it\} Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores que necesites. ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", (paywall, error) => { if (error != null) { // handle the error return; } // Access remote config dictionary var dictionary = paywall.RemoteConfig?.Dictionary; var headerText = dictionary?["header_text"] as string; // Or access raw JSON data var jsonData = paywall.RemoteConfig?.Data; }); ``` En este punto, una vez que hayas recibido todos los valores necesarios, es momento de renderizarlos y ensamblarlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a distintos tamaños de pantalla y orientaciones de dispositivos móviles, garantizando una experiencia fluida y amigable en todos los dispositivos. :::warning Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls-unity#track-paywall-view-events) tal como se describe a continuación, para que los análisis de Adapty puedan recopilar información para los embudos y las pruebas A/B. ::: Una vez que hayas terminado de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.MakePurchase()` con el producto de tu paywall. Para más detalles sobre el método `.MakePurchase()`, consulta [Realizar compras](unity-making-purchases). Te recomendamos [crear un paywall de respaldo denominado paywall de respaldo](unity-use-fallback-paywalls). Este respaldo se mostrará al usuario cuando no haya conexión a internet ni caché disponible, garantizando una experiencia fluida incluso en esas situaciones. ## Registrar eventos de visualización del paywall \{#track-paywall-view-events\} Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque recopilamos datos de compras automáticamente, registrar las visualizaciones del paywall requiere tu intervención, ya que solo tú sabes cuándo un usuario ve un paywall. Para registrar un evento de visualización del paywall, simplemente llama a `.LogShowPaywall(paywall)` y se reflejará en las métricas de tu paywall en los embudos y las pruebas A/B. :::important No es necesario llamar a `.LogShowPaywall(paywall)` si estás mostrando paywalls creados en el [Paywall Builder](adapty-paywall-builder). ::: ```csharp showLineNumbers Adapty.LogShowPaywall(paywall, (error) => { // handle the error }); ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :-------- |:------------------------------------------------------------------| | **paywall** | obligatorio | Un objeto [`AdaptyPaywall`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html). | --- # File: unity-making-purchases --- --- title: "Realizar compras in-app en Unity SDK" description: "Guía sobre cómo gestionar compras in-app y suscripciones usando Adapty." --- Mostrar paywalls en tu app es un paso fundamental para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, mostrarlos es suficiente para gestionar las compras solo si usas el [Paywall Builder](adapty-paywall-builder) para personalizar tus paywalls. Si no usas el Paywall Builder, debes usar un método independiente llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método es la puerta de entrada para que los usuarios interactúen con los paywalls y completen sus transacciones. Si tu paywall tiene una oferta promocional activa para el producto que el usuario intenta comprar, Adapty la aplicará automáticamente en el momento de la compra. :::warning Ten en cuenta que la oferta introductoria se aplicará automáticamente solo si usas paywalls configurados con el Paywall Builder. En otros casos, deberás [verificar la elegibilidad del usuario para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Omitir este paso puede provocar que tu app sea rechazada durante la revisión. Además, podría suponer cobrar el precio completo a usuarios que tienen derecho a una oferta introductoria. ::: Asegúrate de haber completado la [configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras. ## Realizar una compra \{#make-purchase\} :::note **¿Usas el [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente; puedes saltarte este paso. **¿Buscas una guía paso a paso?** Consulta la [guía de inicio rápido](unity-implement-paywalls-manually) para instrucciones de implementación completas con todo el contexto. ::: ```csharp showLineNumbers using AdaptySDK; void MakePurchase(AdaptyPaywallProduct product) { Adapty.MakePurchase(product, (result, error) => { switch (result.Type) { case AdaptyPurchaseResultType.Pending: // handle pending purchase break; case AdaptyPurchaseResultType.UserCancelled: // handle purchase cancellation break; case AdaptyPurchaseResultType.Success: var profile = result.Profile; // handle successfull purchase break; default: break; } }); } ``` Parámetros de la solicitud: | Parámetro | Presencia | Descripción | | :---------- | :------- |:------------------------------------------------------------------------------------------------------| | **Product** | obligatorio | Un objeto [`AdaptyPaywallProduct`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall_product.html) obtenido del paywall. | Parámetros de la respuesta: | Parámetro | Descripción | |---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** | <p>Si la solicitud fue exitosa, la respuesta incluye este objeto. Un objeto [AdaptyProfile](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_profile.html) proporciona información completa sobre los niveles de acceso, suscripciones y compras únicas del usuario dentro de la app.</p><p>Verifica el estado del nivel de acceso para confirmar si el usuario tiene el acceso requerido a la app.</p> | :::warning **Nota:** si todavía usas la versión de StoreKit de Apple inferior a v2.0 y la versión del SDK de Adapty inferior a v.2.9.0, debes proporcionar el [secreto compartido de App Store de Apple](app-store-connection-configuration#step-5-enter-app-store-shared-secret) en su lugar. Este método está actualmente deprecado por Apple. ::: ## Cambiar la suscripción al realizar una compra \{#change-subscription-when-making-a-purchase\} Cuando un usuario elige una nueva suscripción en lugar de renovar la actual, el funcionamiento depende del store: - En el App Store, la suscripción se actualiza automáticamente dentro del grupo de suscripciones. Si un usuario compra una suscripción de un grupo mientras ya tiene una suscripción de otro, ambas suscripciones estarán activas al mismo tiempo. - En Google Play, la suscripción no se actualiza automáticamente. Deberás gestionar el cambio en el código de tu app como se describe a continuación. Para reemplazar la suscripción por otra en Android, llama al método `.makePurchase()` con el parámetro adicional: ```csharp showLineNumbers // Create subscription update parameters var subscriptionUpdateParams = new AdaptySubscriptionUpdateParameters( "old_product_id", // Product ID of the current subscription AdaptySubscriptionUpdateReplacementMode.WithTimeProration ); Adapty.MakePurchase(product, subscriptionUpdateParams, (profile, error) => { if(error != null) { // Handle the error return; } // successful cross-grade }); ``` Parámetro adicional de la solicitud: | Parámetro | Presencia | Descripción | | :--------------------------- | :------- |:-------------------------------------------------------------------------------------------------------| | **subscriptionUpdateParams** | obligatorio | Un objeto [`AdaptySubscriptionUpdateParameters`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_subscription_update_parameters.html). | Puedes leer más sobre suscripciones y modos de reemplazo en la documentación para desarrolladores de Google: - [Acerca de los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-modes) - [Recomendaciones de Google para los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-recommendations) - Modo de reemplazo [`CHARGE_PRORATED_PRICE`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#CHARGE_PRORATED_PRICE()). Nota: este método solo está disponible para actualizaciones de suscripción. No se admiten degradaciones. - Modo de reemplazo [`DEFERRED`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#DEFERRED()). Nota: el cambio real de suscripción solo ocurrirá cuando finalice el período de facturación de la suscripción actual. ## Canjear códigos de oferta en iOS \{#redeem-offer-codes-in-ios\} --- no_index: true --- import Callout from '../../../components/Callout.astro'; <Details> <summary>Sobre los códigos de oferta</summary> Los códigos de oferta te permiten dar descuentos o períodos de prueba gratuitos a usuarios concretos. A diferencia de las ofertas habituales, que se aplican de forma automática, los códigos de oferta se distribuyen fuera de la app — por email, redes sociales o materiales impresos. Los usuarios los canjean introduciendo el código en el App Store, accediendo a una URL de canje o a través de un diálogo dentro de la app. Para configurar códigos de oferta, abre una suscripción en App Store Connect y ve a su sección **Offer Codes**. Puedes crear [tres tipos](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes) de códigos de oferta: - **Free** — la suscripción es gratuita durante un período determinado y la siguiente renovación se cobra al precio completo. - **Pay as you go** — el usuario paga un precio reducido en cada ciclo de facturación durante un período determinado y, después, la suscripción se renueva al precio completo. - **Pay up front** — el usuario paga un precio único reducido por toda la duración de la oferta y, después, la suscripción se renueva al precio completo. No es necesario añadir los códigos de oferta a Adapty. Apple etiqueta cada transacción durante el período de la oferta con la categoría del código de oferta. Esto incluye el canje inicial y todas las renovaciones con descuento posteriores. Adapty detecta la etiqueta y registra cada transacción con la categoría de oferta `offer_code`. Cuando termina el período de oferta y la suscripción se renueva al precio completo, la etiqueta deja de estar presente. Puedes filtrar los análisis por el tipo de oferta **Offer Code** en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds). #### Resolución de discrepancias en los ingresos \{#revenue-discrepancy-troubleshooting\} Si observas que una transacción con código de oferta aparece en Adapty al precio completo del producto en lugar del precio reducido, verifica lo siguiente en App Store Connect: - El código de oferta tiene los precios correctos configurados para todas las regiones donde los usuarios pueden canjearlo. - El precio de la oferta está configurado para el país o región específica del usuario. Apple envía el precio regional en la transacción. Si no hay ningún precio regional configurado para la oferta, Apple puede enviar el precio completo del producto. Puedes filtrar y verificar las transacciones con código de oferta en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds) mediante los filtros de tipo de oferta **Offer Code** y **Offer Discount Type**. #### Códigos promocionales heredados (obsoletos) \{#legacy-promo-codes-deprecated\} <Callout type="warning"> Apple dejó obsoletos los códigos promocionales para compras in-app en marzo de 2026. Los códigos de oferta los sustituyen con más funcionalidades: elegibilidad configurable, fechas de expiración y hasta 1 millón de códigos por trimestre. Si antes usabas códigos promocionales para compras in-app, migra a los códigos de oferta en App Store Connect. </Callout> Los códigos promocionales heredados (limitados a 100 por app y versión) daban acceso gratuito a una suscripción. A diferencia de los códigos de oferta, Apple no incluía información de descuento en las transacciones con código promocional — enviaba el precio completo del producto en el recibo. Por ello, Adapty registraba estas transacciones al precio completo, lo que generaba discrepancias entre los análisis de Adapty y App Store Connect. Si ves transacciones históricas al precio completo que deberían haber sido gratuitas, es probable que provengan de códigos promocionales heredados. Como estos códigos ya están obsoletos, migra a los códigos de oferta para un seguimiento preciso de los ingresos. </Details> Para mostrar la hoja de canje de códigos en tu app: ```csharp showLineNumbers Adapty.PresentCodeRedemptionSheet((error) => { // handle the error }); ``` :::danger Según nuestras observaciones, la hoja de canje de códigos de oferta puede no funcionar de forma fiable en algunas apps. Recomendamos redirigir al usuario directamente al App Store. Para ello, debes abrir la URL con el siguiente formato: `https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}` ::: ## Gestionar planes prepago (Android) \{#manage-prepaid-plans-android\} Si los usuarios de tu app pueden comprar [planes prepago](https://developer.android.com/google/play/billing/subscriptions#prepaid-plans) (por ejemplo, comprar una suscripción no renovable por varios meses), puedes habilitar las [transacciones pendientes](https://developer.android.com/google/play/billing/subscriptions#pending) para planes prepago. ```csharp showLineNumbers title="Unity" using UnityEngine; using AdaptySDK; var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetGoogleEnablePendingPrepaidPlans(true); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); ``` --- # File: unity-restore-purchase --- --- title: "Restaurar compras en la app móvil con Unity SDK" description: "Aprende cómo restaurar compras en Adapty para garantizar una experiencia de usuario sin interrupciones." --- Restaurar compras tanto en iOS como en Android es una funcionalidad que permite a los usuarios recuperar el acceso a contenido comprado previamente, como suscripciones o compras in-app, sin que se les vuelva a cobrar. Esta funcionalidad es especialmente útil para usuarios que hayan desinstalado y reinstalado la app, o que hayan cambiado de dispositivo y quieran acceder a su contenido ya adquirido sin pagar de nuevo. :::note En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin que tengas que añadir código adicional. Si ese es tu caso, puedes saltarte este paso. ::: Para restaurar una compra cuando no usas [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`: ```csharp showLineNumbers Adapty.RestorePurchases((profile, error) => { if (error != null) { // handle the error return; } var accessLevel = profile.AccessLevels["YOUR_ACCESS_LEVEL"]; if (accessLevel != null && accessLevel.IsActive) { // restore access } }); ``` Parámetros de respuesta: | Parámetro | Descripción | |---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** | <p>Un objeto [`AdaptyProfile`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_profile.html). Este modelo contiene información sobre los niveles de acceso, suscripciones y compras únicas.</p><p>Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app.</p> | :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: --- # File: implement-observer-mode-unity --- --- title: "Implementar el modo Observer en el SDK de Unity" description: "Implementa el modo observer en Adapty para registrar eventos de suscripción de usuarios en el SDK de Unity." --- Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analíticas. Si esto cubre tus necesidades, solo tienes que: 1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. Sigue las instrucciones de configuración para [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk). 2. [Reportar transacciones](report-transactions-observer-mode-unity) desde tu infraestructura de compras existente a Adapty. ### Configuración del modo Observer \{#observer-mode-setup\} Activa el modo Observer si gestionas las compras y el estado de suscripción por tu cuenta y utilizas Adapty para enviar eventos de suscripción y analíticas. :::important Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlo tú mismo. ::: ```csharp showLineNumbers title="C#" using UnityEngine; using AdaptySDK; public class AdaptyListener : MonoBehaviour, AdaptyEventListener { void Start() { DontDestroyOnLoad(this.gameObject); Adapty.SetEventListener(this); var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetObserverMode(true); // Enable observer mode Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); } public void OnLoadLatestProfile(AdaptyProfile profile) { } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } } ``` Parámetros: | Parámetro | Descripción | |--------------|----------------------------------------------------------------------------------------------------------------| | observerMode | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor por defecto es `false`. | ## Usar los paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\} Si también quieres usar los paywalls y las funciones de pruebas A/B de Adapty, puedes hacerlo, pero requiere algo de configuración adicional en el modo Observer. Esto es lo que necesitarás hacer además de los pasos anteriores: 1. Muestra los paywalls de la forma habitual para [paywalls con Remote Config](present-remote-config-paywalls-unity). 3. [Asocia los paywalls](report-transactions-observer-mode-unity) con las transacciones de compra. --- # File: report-transactions-observer-mode-unity --- --- title: "Reportar transacciones en Observer Mode en el SDK de Unity" description: "Reporta transacciones de compras en el Observer Mode de Adapty para obtener información sobre usuarios y seguimiento de ingresos en el SDK de Unity." --- <Tabs groupId="sdk-version" queryString> <TabItem value="current" label="Adapty SDK v3.4+ (current)" default> En Observer Mode, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas reportar las transacciones desde tu app store. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` para reportar explícitamente cada transacción y que Adapty la reconozca. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `ReportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra con el paywall que la originó, garantizando análisis precisos del paywall. ```csharp showLineNumbers Adapty.ReportTransaction( "YOUR_TRANSACTION_ID", "PAYWALL_VARIATION_ID", // optional (error) => { // handle the error }); ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | transactionId | requerido | <ul><li> Para iOS: Identificador de la transacción.</li><li> Para Android: Identificador de cadena `purchase.getOrderId` de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.</li></ul> | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html). | </TabItem> <TabItem value="old" label="Adapty SDK 3.3.x (legacy)" default> En Observer Mode, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas reportar las transacciones desde tu app store o restaurarlas. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis. Usa `reportTransaction` en ambas plataformas para reportar explícitamente cada transacción, y usa `restorePurchases` en Android como paso adicional para asegurarte de que Adapty la reconozca. :::warning **¡No omitas el reporte de transacciones ni la restauración de compras!** Si no llamas a estos métodos, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: Si usas paywalls de Adapty, incluye el `PAYWALL_VARIATION_ID` al reportar una transacción. Esto vincula la compra con el paywall que la originó, garantizando análisis precisos del paywall. ```csharp showLineNumbers // every time when calling transasction.finish() #if UNITY_ANDROID && !UNITY_EDITOR Adapty.RestorePurchases((profile, error) => { // handle the error }); #endif Adapty.ReportTransaction( "YOUR_TRANSACTION_ID", "PAYWALL_VARIATION_ID", // optional (error) => { // handle the error }); ``` Parámetros: | Parámetro | Presencia | Descripción | | ------------- | --------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | transactionId | requerido | <ul><li> Para iOS, StoreKit 1: un objeto [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction).</li><li> Para iOS, StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).</li><li> Para Android: Identificador de cadena (`purchase.getOrderId`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.</li></ul> | | variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html). | </TabItem> <TabItem value="old2" label="Adapty SDK up to 3.2.x (legacy)" default> <Tabs groupId="current-os" queryString> <TabItem value="swift" label="iOS" default> **Reporte de transacciones** - Las versiones hasta la 3.1.x escuchan automáticamente las transacciones en la App Store, por lo que no es necesario reportarlas manualmente. - La versión 3.2 no admite Observer Mode. </TabItem> <TabItem value="kotlin" label="Android and Android-based cross-platforms" default> **Reporte de transacciones** Usa `restorePurchases` para reportar una transacción a Adapty en Observer Mode, tal como se explica en la página [Restaurar compras en código móvil](unity-restore-purchase). :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `restorePurchases`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: </TabItem> </Tabs> **Asociar paywalls a transacciones** El SDK de Adapty no puede determinar el origen de las compras, ya que eres tú quien las procesa. Por lo tanto, si tienes pensado usar paywalls y/o pruebas A/B en Observer Mode, necesitas asociar la transacción proveniente de tu app store con el paywall correspondiente en el código de tu app. Es importante hacerlo correctamente antes de publicar tu app; de lo contrario, generará errores en los análisis. ```csharp Adapty.SetVariationForTransaction("<variationId>", "<transactionId>", (error) => { if(error != null) { // handle the error return; } // successful binding }); ``` | Parámetro | Presencia | Descripción | | ------------------------------------------------------ | --------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | transactionId | requerido | <p>Para iOS, StoreKit 1: un objeto [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction).</p><p>Para iOS, StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).</p><p>Para Android: Identificador de cadena (purchase.getOrderId de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación).</p> | | variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html). | </TabItem> </Tabs> --- # File: unity-troubleshoot-purchases --- --- title: "Solucionar problemas de compras en Unity SDK" description: "Solucionar problemas de compras en Unity SDK" --- Esta guía te ayuda a resolver problemas comunes al implementar compras manualmente en el SDK de Unity. ## makePurchase se llama correctamente, pero el perfil no se actualiza \{#makepurchase-is-called-successfully-but-the-profile-is-not-being-updated\} **Problema**: El método `makePurchase` se completa correctamente, pero el perfil del usuario y el estado de la suscripción no se actualizan en Adapty. **Causa**: Esto suele indicar una configuración incompleta de Google Play Store o problemas de configuración. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## makePurchase se invoca dos veces \{#makepurchase-is-invoked-twice\} **Problema**: El método `makePurchase` se está llamando varias veces para la misma compra. **Causa**: Esto ocurre normalmente cuando el flujo de compra se activa varias veces debido a problemas de gestión del estado de la interfaz o a interacciones rápidas del usuario. **Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android). ## AdaptyError.cantMakePayments en modo observador \{#adaptyerror-cantmakepayments-in-observer-mode\} **Problema**: Estás obteniendo `AdaptyError.cantMakePayments` al usar `makePurchase` en modo observador. **Causa**: En el modo observador, debes gestionar las compras por tu cuenta, no usar el método `makePurchase` de Adapty. **Solución**: Si usas `makePurchase` para las compras, desactiva el modo observador. Debes elegir entre usar `makePurchase` o gestionar las compras por tu cuenta en el modo observador. Consulta [Implementar el modo observador](implement-observer-mode-unity) para más detalles. ## Error de Adapty: (code: 103, message: Play Market request failed on purchases updated: responseCode=3, debugMessage=Billing Unavailable, detail: null) \{#adapty-error-code-103-message-play-market-request-failed-on-purchases-updated-responsecode3-debugmessagebilling-unavailable-detail-null\} **Problema**: Estás recibiendo un error de facturación no disponible de Google Play Store. **Causa**: Este error no está relacionado con Adapty. Es un error de la biblioteca de facturación de Google Play que indica que la facturación no está disponible en el dispositivo. **Solución**: Este error no está relacionado con Adapty. Puedes consultar más información en la documentación de Play Store: [Handle BillingResult response codes](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) | Play Billing | Android Developers. ## No se encuentran makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\} **Problema**: Estás encontrando problemas porque no se encuentran los `makePurchasesCompletionHandlers`. **Causa**: Esto suele estar relacionado con problemas en las pruebas de sandbox. **Solución**: Crea un nuevo usuario de sandbox e inténtalo de nuevo. Esto generalmente resuelve los problemas con los manejadores de finalización de compra en sandbox. ## Otros problemas \{#other-issues\} **Problema**: Estás experimentando otros problemas relacionados con las compras que no se tratan más arriba. **Solución**: Migra el SDK a la última versión siguiendo las [guías de migración](unity-sdk-migration-guides) si es necesario. Muchos problemas se resuelven en versiones más recientes del SDK. --- # File: unity-identifying-users --- --- title: "Identificar usuarios en Unity SDK" description: "Aprende cómo identificar usuarios en tu app de Unity con el SDK de Adapty." --- Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, deberías establecer tu propio Customer User ID. Puedes encontrar usuarios por su Customer User ID en la sección [Perfiles](profiles-crm) y utilizarlo en la [API del lado del servidor](getting-started-with-server-side-api), que se enviará a todas las integraciones. ### Configurar el ID de usuario en la inicialización \{#setting-customer-user-id-on-configuration\} Si ya tienes un ID de usuario durante la configuración, pásalo como parámetro `customerUserId` al método `.activate()`: ```csharp showLineNumbers using UnityEngine; using AdaptySDK; var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetCustomerUserId("YOUR_USER_ID"); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); ``` :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Establecer el ID de usuario después de la configuración \{#setting-customer-user-id-after-configuration\} Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo en cualquier momento con el método `.identify()`. Los casos más comunes para usar este método son tras el registro o la autenticación, cuando el usuario pasa de ser anónimo a estar autenticado. ```csharp showLineNumbers Adapty.Identify("YOUR_USER_ID", (error) => { if(error == null) { // successful identify } }); ``` Parámetros de la solicitud: - **Customer User ID** (obligatorio): un identificador de usuario de tipo string. :::warning Reenvío de datos significativos del usuario En algunos casos, como cuando un usuario inicia sesión de nuevo en su cuenta, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si enviaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, deberás reenviar esos datos para el usuario identificado. También es importante tener en cuenta que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes. ::: ### Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\} Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`: ```csharp showLineNumbers Adapty.Logout((error) => { if(error == null) { // successful logout } }); ``` Luego puedes iniciar sesión con el usuario usando el método `.identify()`. ## Asignar `appAccountToken` (iOS) [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un **UUID** que te permite vincular las transacciones del App Store con la identidad interna de tu usuario. StoreKit asocia este token con cada transacción, de modo que tu backend puede relacionar los datos del App Store con tus usuarios. Usa un UUID estable generado por usuario y reutilízalo para la misma cuenta en todos los dispositivos. Así te aseguras de que las compras y las notificaciones del App Store permanezcan correctamente vinculadas. Puedes configurar el token de dos formas: durante la activación del SDK o al identificar al usuario. :::important Siempre debes pasar `appAccountToken` junto con `customerUserId`. Si solo pasas el token, no se incluirá en la transacción. ::: ```csharp showLineNumbers title="Unity" using UnityEngine; using AdaptySDK; using System; // During configuration: var appAccountToken = new Guid("YOUR_APP_ACCOUNT_TOKEN"); var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetCustomerUserId("YOUR_USER_ID", appAccountToken); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); // Or when identifying users Adapty.Identify("YOUR_USER_ID", appAccountToken, (error) => { if (error == null) { // successful identify } }); ``` ## Establecer IDs de cuenta ofuscados (Android) \{#set-obfuscated-account-ids-android\} Google Play requiere IDs de cuenta ofuscados en ciertos casos de uso para mejorar la privacidad y seguridad de los usuarios. Estos IDs ayudan a Google Play a identificar compras sin exponer información del usuario, lo que es especialmente importante para la prevención de fraude y los análisis. Es posible que necesites establecer estos IDs si tu app maneja datos sensibles de usuarios o si debes cumplir con normativas de privacidad específicas. Los IDs ofuscados permiten a Google Play rastrear compras sin revelar los identificadores reales de los usuarios. ```csharp showLineNumbers title="Unity" using UnityEngine; using AdaptySDK; // Durante la configuración: var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetCustomerUserId("YOUR_USER_ID", null, "YOUR_OBFUSCATED_ACCOUNT_ID"); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // manejar el error return; } }); // O al identificar usuarios Adapty.Identify("YOUR_USER_ID", null, "YOUR_OBFUSCATED_ACCOUNT_ID", (error) => { if (error == null) { // identificación exitosa } }); ``` ## Detectar usuarios en múltiples dispositivos \{#detect-users-across-devices\} --- no_index: true --- Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`. Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID. **Lo que Adapty puede detectar entre dispositivos** | Tu configuración | Lo que Adapty detecta | Lo que debes hacer | | --- | --- | --- | | Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. | | Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. | | Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. | | Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. | **Restaurar compras en un dispositivo nuevo** Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK. No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado. --- # File: unity-setting-user-attributes --- --- title: "Establecer atributos de usuario en el SDK de Unity" description: "Aprende a actualizar atributos de usuario y datos de perfil en tu app de Unity con el SDK de Adapty." --- Puedes añadir atributos opcionales como email, número de teléfono, etc., al usuario de tu app. Luego puedes usarlos para crear [segmentos](segments) de usuarios o simplemente consultarlos en el CRM. ### Establecer atributos de usuario \{#setting-user-attributes\} Para establecer atributos de usuario, llama al método `.updateProfile()`: ```csharp showLineNumbers var builder = new Adapty.ProfileParameters.Builder() .SetFirstName("John") .SetLastName("Appleseed") .SetBirthday(new DateTime(1970, 1, 3)) .SetGender(ProfileGender.Female) .SetEmail("example@adapty.io"); Adapty.UpdateProfile(builder.Build(), (error) => { if(error != nil) { // handle the error } }); ``` Ten en cuenta que los atributos que hayas establecido previamente con el método `updateProfile` no se restablecerán. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ### Lista de claves permitidas \{#the-allowed-keys-list\} Las claves permitidas `<Key>` de `AdaptyProfileParameters.Builder` y sus valores `<Value>` se listan a continuación: | Key | Value | |---|-----| | <p>email</p><p>phoneNumber</p><p>firstName</p><p>lastName</p> | String | | gender | Enum, los valores permitidos son: `female`, `male`, `other` | | birthday | Date | ### Atributos de usuario personalizados \{#custom-user-attributes\} Puedes definir tus propios atributos personalizados. Normalmente están relacionados con el uso de tu app. Por ejemplo, en apps de fitness podrían ser el número de ejercicios por semana; en apps de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes utilizarlos en segmentos para crear paywalls y ofertas segmentadas, y también en análisis para identificar qué métricas de producto influyen más en los ingresos. ```csharp showLineNumbers try { builder = builder.SetCustomStringAttribute("string_key", "string_value"); builder = builder.SetCustomDoubleAttribute("double_key", 123.0f); } catch (Exception e) { // handle the exception } ``` Para eliminar una clave existente, usa el método `.withRemoved(customAttributeForKey:)`: ```csharp showLineNumbers try { builder = builder.RemoveCustomAttribute("key_to_remove"); } catch (Exception e) { // handle the exception } ``` A veces necesitas saber qué atributos personalizados ya se han establecido. Para ello, usa el campo `customAttributes` del objeto `AdaptyProfile`. :::warning Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor pueden haber cambiado desde la última sincronización. ::: ### Límites \{#limits\} - Hasta 30 atributos personalizados por usuario - Los nombres de clave pueden tener hasta 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.` - El valor puede ser una cadena de texto o un número flotante con un máximo de 50 caracteres. --- # File: unity-listen-subscription-changes --- --- title: "Comprobar el estado de la suscripción en Unity SDK" description: "Rastrea y gestiona el estado de la suscripción del usuario en Adapty para mejorar la retención de clientes en tu aplicación Unity." --- Con Adapty, hacer seguimiento del estado de la suscripción es muy sencillo. No tienes que insertar manualmente los IDs de producto en tu código. En cambio, puedes confirmar fácilmente el estado de suscripción de un usuario comprobando si tiene un [nivel de acceso](access-level) activo. <details> <summary>Antes de empezar a comprobar el estado de la suscripción (haz clic para ampliar)</summary> - Para iOS, configura las [Notificaciones del servidor de App Store](enable-app-store-server-notifications) - Para Android, configura las [Notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn) </details> ## El nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptyprofile-object\} Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_profile.html). Te recomendamos obtener el perfil cuando tu app arranca, por ejemplo al [identificar a un usuario](unity-identifying-users#setting-customer-user-id-on-configuration), y actualizarlo cada vez que se produzcan cambios. Así podrás usar el objeto de perfil sin necesidad de solicitarlo repetidamente. Para recibir notificaciones sobre actualizaciones del perfil, suscríbete a los cambios de perfil como se describe en la sección [Escuchar actualizaciones del estado de la suscripción](#listening-for-subscription-status-updates) más abajo. :::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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas. ::: ## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\} Para obtener el nivel de acceso desde el servidor, usa el método `.GetProfile()`: ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // check the access }); ``` Parámetros de respuesta: | Parámetro | Descripción | | --------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Profile | <p>Un objeto [AdaptyProfile](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_profile.html). En general, solo tienes que comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app.</p><p></p><p>El método `.getProfile` proporciona el resultado más actualizado, ya que siempre intenta consultar la API. Si por algún motivo (por ejemplo, sin conexión a internet) el SDK de Adapty no puede obtener información del servidor, se devuelven los datos de la caché. También es importante tener en cuenta que el SDK de Adapty actualiza la caché de `AdaptyProfile` de forma periódica para mantener esta información lo más actualizada posible.</p> | El método `.getProfile()` te proporciona el perfil del usuario a partir del cual puedes obtener el estado del nivel de acceso. Puedes tener múltiples niveles de acceso por app. Por ejemplo, si tienes una app de noticias y vendes suscripciones a diferentes temáticas de forma independiente, puedes crear los niveles de acceso "sports" y "science". Sin embargo, la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes usar simplemente el nivel de acceso "premium" predeterminado. Aquí tienes un ejemplo para comprobar el nivel de acceso "premium" predeterminado: ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // "premium" is an identifier of default access level var accessLevel = profile.AccessLevels["premium"]; if (accessLevel != null && accessLevel.IsActive) { // grant access to premium features } }); ``` ### Escuchar actualizaciones del estado de la suscripción \{#listening-for-subscription-status-updates\} Cada vez que la suscripción del usuario cambia, Adapty lanza un evento. Para recibir mensajes de Adapty, necesitas hacer una configuración adicional: ```csharp showLineNumbers // Extend `AdaptyEventListener ` with `OnLoadLatestProfile ` method: public class AdaptyListener : MonoBehaviour, AdaptyEventListener { public void OnLoadLatestProfile(AdaptyProfile profile) { // handle any changes to subscription state } } ``` Adapty también lanza un evento al inicio de la aplicación. En ese caso, se pasará el estado de suscripción almacenado en caché. ### Caché del estado de la suscripción \{#subscription-status-cache\} La caché implementada en el SDK de Adapty almacena el estado de suscripción del perfil. Esto significa que, incluso si el servidor no está disponible, se puede acceder a los datos en caché para obtener información sobre el estado de suscripción del perfil. No obstante, hay que tener en cuenta que no es posible solicitar datos directamente desde la caché. El SDK consulta periódicamente el servidor cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si hay modificaciones, como nuevas transacciones u otras actualizaciones, se enviarán a los datos en caché para mantenerlos sincronizados con el servidor. --- # File: unity-deal-with-att --- --- title: "Gestionar ATT en el SDK de Unity" description: "Comienza con Adapty en Unity para simplificar la configuración y gestión de suscripciones." --- Si tu aplicación utiliza el framework AppTrackingTransparency y presenta al usuario una solicitud de autorización de seguimiento de la app, debes enviar el [estado de autorización](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/) a Adapty. ```csharp showLineNumbers var builder = new Adapty.ProfileParameters.Builder() .SetAppTrackingTransparencyStatus(IOSAppTrackingTransparencyStatus.Authorized); Adapty.UpdateProfile(builder.Build(), (error) => { if(error != null) { // handle the error } }); ``` :::warning Te recomendamos encarecidamente que envíes este valor lo antes posible cuando cambie; solo así los datos se enviarán a tiempo a las integraciones que hayas configurado. ::: --- # File: kids-mode-unity --- --- title: "Kids Mode en Unity SDK" description: "Activa fácilmente el Modo Niños para cumplir con las políticas de Apple y Google. No se recopilan IDFA, GAID ni datos publicitarios en Unity SDK." --- Si tu aplicación Unity está destinada a niños, debes seguir las políticas de [Apple](https://developer.apple.com/kids/) y [Google](https://support.google.com/googleplay/android-developer/answer/9893335). Si usas el SDK de Adapty, unos pocos pasos sencillos te ayudarán a configurarlo para cumplir con estas políticas y superar las revisiones de las tiendas. ## ¿Qué se necesita? \{#whats-required\} Debes configurar el SDK de Adapty para desactivar la recopilación de: - [IDFA (Identifier for Advertisers)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers) (iOS) - [Android Advertising ID (AAID/GAID)](https://support.google.com/googleplay/android-developer/answer/6048248) (Android) - [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) Además, te recomendamos usar el ID de usuario del cliente con cuidado. Un ID en formato `<NombreApellido>` se considerará claramente como recopilación de datos personales, al igual que usar un correo electrónico. En el Modo Niños, la mejor práctica es usar identificadores aleatorios o anonimizados (por ejemplo, IDs hasheados o UUIDs generados por el dispositivo) para garantizar el cumplimiento normativo. ## Activar el Modo Niños \{#enabling-kids-mode\} ### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\} En el Adapty Dashboard, debes desactivar la recopilación de direcciones IP. Para ello, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** en **Collect users' IP address**. ### Cambios en el código de tu app \{#updates-in-your-mobile-app-code\} ¡La compatibilidad con el Modo Niños en Unity estará disponible próximamente! Por ahora, puedes seguir las guías de plataformas nativas: - [Kids Mode en iOS SDK](kids-mode) para la configuración en iOS - [Kids Mode en Android SDK](kids-mode-android) para la configuración en Android --- # File: unity-get-onboardings --- --- title: "Obtener onboardings en el SDK de Unity" description: "Aprende cómo recuperar onboardings en Adapty para Unity." --- Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el editor en el Adapty Dashboard, puedes mostrarlo en tu app de Unity. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, tal como se describe a continuación. Antes de empezar, asegúrate de que: 1. Has instalado el [SDK de Unity de Adapty](sdk-installation-unity) en su versión 3.14.0 o superior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). ## Obtener el onboarding y crear la vista \{#fetch-onboarding-and-create-view\} Cuando creas un [onboarding](onboardings) con nuestro editor sin código, se almacena como un contenedor con una configuración que tu app necesita obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a cuestionarios o entradas de formularios). El contenedor también registra automáticamente los eventos de analíticas, por lo que no necesitas implementar un seguimiento de vistas por separado. Para obtener el mejor rendimiento, obtén la configuración del onboarding con antelación para dar tiempo suficiente a que las imágenes se descarguen antes de mostrárselas a los usuarios. Para obtener un onboarding, usa el método `GetOnboarding`: ```csharp showLineNumbers Adapty.GetOnboarding("YOUR_PLACEMENT_ID", (onboarding, error) => { if (error != null) { // handle the error return; } // the requested onboarding }); ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto de uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p><p>Consulta [Localizaciones y códigos de localización](flutter-localizations-and-locale-codes) para más información sobre los códigos de localización y cómo recomendamos usarlos.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, es posible que los usuarios no obtengan los datos absolutamente más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo inestable que sea su conexión. La caché se actualiza con regularidad, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché actualizada periódicamente descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | | **loadTimeout** | por defecto: 5 seg | <p>Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el respaldo local.</p><p>Ten en cuenta que, en casos excepcionales, este método puede agotar el tiempo de espera un poco más tarde de lo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internas.</p> | Parámetros de respuesta: | Parámetro | Descripción | |:----------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | Un objeto [`AdaptyOnboarding`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_onboarding.html) con: el identificador y la configuración del onboarding, Remote Config y otras propiedades. | Tras obtener el onboarding, llama al método `CreateOnboardingView`. :::warning El resultado del método `CreateOnboardingView` solo puede usarse una vez. Si necesitas usarlo de nuevo, llama al método `CreateOnboardingView` otra vez. Llamarlo dos veces sin volver a crearlo puede provocar el error `AdaptyUIError.viewAlreadyPresented`. ::: ```csharp showLineNumbers AdaptyUI.CreateOnboardingView(onboarding, (view, error) => { // handle the result }); ``` Parámetros: | Parámetro | Presencia | Descripción | |:---------------| :------------- |:-----------------------------------------------------------------------------| | **onboarding** | obligatorio | Un objeto `AdaptyOnboarding` para obtener una vista del onboarding deseado. | | **externalUrlsPresentation** | <p>opcional</p><p>por defecto: `InAppBrowser`</p> | <p>Controla cómo se abren los enlaces en el onboarding. Opciones disponibles:</p><p>- `AdaptyWebPresentation.InAppBrowser` - Abre los enlaces en un navegador dentro de la app (por defecto)</p><p>- `AdaptyWebPresentation.ExternalBrowser` - Abre los enlaces en el navegador externo del dispositivo</p><p>Consulta [Personalizar cómo se abren los enlaces en los onboardings](unity-present-onboardings#customize-how-links-open-in-onboardings) para ver ejemplos de uso.</p> | Una vez que hayas cargado correctamente el onboarding y su configuración de vista, puedes [mostrarlo en tu app móvil](unity-present-onboardings). ## Acelerar la obtención del onboarding con el onboarding de audiencia por defecto \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} Normalmente, los onboardings se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en los casos en que tienes numerosas audiencias y onboardings, y tus usuarios tienen una conexión a internet débil, obtener un onboarding puede tardar más de lo que te gustaría. En estas situaciones, puede que quieras mostrar un onboarding por defecto para garantizar una experiencia de usuario fluida en lugar de no mostrar ninguno. Para resolver esto, puedes usar el método `GetOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, tal como se detalla en la sección [Obtener el onboarding](#fetch-onboarding) anterior. :::warning Considera usar `GetOnboarding` en lugar de `GetOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes: - **Problemas de compatibilidad**: Puede generar problemas al admitir varias versiones de la app, lo que requiere diseños compatibles con versiones anteriores o aceptar que las versiones más antiguas puedan mostrarse incorrectamente. - **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación basada en país, atribución o atributos personalizados. Si una obtención más rápida supera estos inconvenientes para tu caso de uso, usa `GetOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `GetOnboarding` como se describe [arriba](#fetch-onboarding). ::: ```csharp showLineNumbers Adapty.GetOnboardingForDefaultAudience("YOUR_PLACEMENT_ID", (onboarding, error) => { if (error != null) { // handle the error return; } // the requested onboarding }); ``` Parámetros: | Parámetro | Presencia | Descripción | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. | | **locale** | <p>opcional</p><p>por defecto: `en`</p> | <p>El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto de uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.</p><p></p><p>Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.</p> | | **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | <p>Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.</p><p></p><p>Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, es posible que los usuarios no obtengan los datos absolutamente más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo inestable que sea su conexión. La caché se actualiza con regularidad, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.</p><p></p><p>Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.</p><p></p><p>El SDK de Adapty almacena los onboardings localmente en dos capas: la caché actualizada periódicamente descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.</p> | --- # File: unity-present-onboardings --- --- title: "Presentar onboardings en Unity SDK" description: "Aprende a presentar onboardings de manera efectiva para impulsar más conversiones." --- Si has personalizado un onboarding usando el builder, no necesitas preocuparte por renderizarlo en el código de tu app Unity para mostrárselo al usuario. Dicho onboarding contiene tanto lo que debe mostrarse dentro del onboarding como la forma en que debe mostrarse. Antes de empezar, asegúrate de que: 1. Tienes instalado el [SDK de Adapty para Unity](sdk-installation-unity) 3.14.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). Para mostrar un onboarding, usa el método `view.Present()` sobre el `view` creado por el método `CreateOnboardingView`. Cada `view` solo puede usarse una vez. Si necesitas mostrar el paywall de nuevo, llama a `CreateOnboardingView` otra vez para crear una nueva instancia de `view`. :::warning Reutilizar el mismo `view` sin recrearlo puede producir un error `AdaptyUIError.viewAlreadyPresented`. ::: ```csharp showLineNumbers title="Unity" view.Present((presentError) => { if (presentError != null) { // handle the error } }; ``` ## Configurar el estilo de presentación en iOS \{#configure-ios-presentation-style\} Configura cómo se presenta el onboarding en iOS pasando el parámetro `iosPresentationStyle` al método `Present()`. El parámetro acepta los valores `AdaptyUIIOSPresentationStyle.FullScreen` (predeterminado) o `AdaptyUIIOSPresentationStyle.PageSheet`. ```csharp showLineNumbers title="Unity" view.Present(AdaptyUIIOSPresentationStyle.PageSheet, (error) => { // handle the error }); ``` ## Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\} :::important La personalización de cómo se abren los enlaces en los onboardings está disponible a partir del SDK de Adapty v. 3.15. ::: Por defecto, los enlaces en los onboardings se abren en un navegador integrado en la app, lo que ofrece una experiencia fluida al mostrar las páginas web dentro de tu aplicación sin cambiar de app. Para abrir los enlaces en un navegador externo en su lugar, pasa `AdaptyWebPresentation.ExternalBrowser` al método `CreateOnboardingView`: ```csharp showLineNumbers title="Unity" AdaptyUI.CreateOnboardingView( onboarding, AdaptyWebPresentation.ExternalBrowser, // default — InAppBrowser (view, error) => { if (error != null) { // handle the error return; } // present the onboarding view view.Present((presentError) => { if (presentError != null) { // handle the error } }); } ); ``` Opciones disponibles: - `AdaptyWebPresentation.InAppBrowser` - Abre los enlaces en un navegador integrado en la app (predeterminado) - `AdaptyWebPresentation.ExternalBrowser` - Abre los enlaces en el navegador externo del dispositivo --- # File: unity-handling-onboarding-events --- --- title: "Gestionar eventos de onboarding en Unity SDK" description: "Gestiona eventos relacionados con el onboarding en Unity usando Adapty." --- Antes de empezar, asegúrate de que: 1. Tienes instalado el [SDK de Adapty para Unity](sdk-installation-unity) 3.14.0 o posterior. 2. Has [creado un onboarding](create-onboarding). 3. Has añadido el onboarding a un [placement](placements). Los onboardings configurados con el builder generan eventos a los que tu app puede responder. A continuación aprenderás cómo responder a esos eventos. Para controlar o monitorizar los procesos que ocurren en la pantalla de onboarding dentro de tu app de Unity, implementa la interfaz `AdaptyOnboardingsEventsListener`. ## Acciones personalizadas \{#custom-actions\} En el builder puedes añadir una acción **custom** a un botón y asignarle un ID. <img src="/assets/shared/img/ios-events-1.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Luego puedes usar ese ID en tu código y gestionarlo como una acción personalizada. Por ejemplo, si un usuario pulsa un botón personalizado como **Login** o **Allow notifications**, se activará el método `OnboardingViewOnCustomAction` con el parámetro `actionId` igual al **Action ID** definido en el builder. Puedes crear tus propios IDs, como "allowNotifications". Para gestionar los eventos del onboarding, implementa la interfaz `AdaptyOnboardingsEventsListener`: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { void Start() { Adapty.SetOnboardingsEventsListener(this); } public void OnboardingViewOnCustomAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string actionId ) { if (actionId == "allowNotifications") { // request notification permissions } } public void OnboardingViewDidFailWithError( AdaptyUIOnboardingView view, AdaptyError error ) { // handle errors } // Implement other required interface methods (see examples below) } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "actionId": "allowNotifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ``` </Details> ## Cerrar el onboarding \{#closing-onboarding\} El onboarding se considera cerrado cuando el usuario pulsa un botón con la acción **Close** asignada. <img src="/assets/shared/img/ios-events-2.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> :::important Ten en cuenta que debes gestionar qué ocurre cuando el usuario cierra el onboarding. Por ejemplo, necesitas dejar de mostrar el onboarding en sí. ::: Implementa el método `OnboardingViewOnCloseAction` en tu clase: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnCloseAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string actionId ) { view.Dismiss((error) => { if (error != null) { // handle the error } }); } // ... other interface methods } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> ## Abrir un paywall \{#opening-a-paywall\} :::tip Gestiona este evento para abrir un paywall si quieres abrirlo dentro del onboarding. Si prefieres abrir el paywall después de que este se cierre, hay una forma más directa: gestiona [`OnboardingViewOnCloseAction`](#closing-onboarding) y abre el paywall sin depender de los datos del evento. ::: Si un usuario pulsa un botón que abre un paywall, recibirás el ID de acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más fluida de trabajar con paywalls en onboardings es hacer que el ID de acción sea igual al ID de placement del paywall. Así, tras el evento `OnboardingViewOnPaywallAction`, puedes usar el ID de placement para obtener y abrir el paywall de inmediato. Ten en cuenta que, en iOS, solo puede mostrarse una vista (paywall u onboarding) en pantalla a la vez. Si presentas un paywall encima de un onboarding, no podrás controlar el onboarding en segundo plano de forma programática. Si intentas cerrar el onboarding, se cerrará el paywall en su lugar, dejando visible el onboarding. Para evitarlo, cierra siempre la vista del onboarding antes de presentar el paywall. ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnPaywallAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string actionId ) { // Dismiss onboarding before presenting paywall view.Dismiss((dismissError) => { if (dismissError != null) { // handle the error return; } Adapty.GetPaywall(actionId, (paywall, error) => { if (error != null) { // handle the error return; } AdaptyUI.CreatePaywallView(paywall, (paywallView, createError) => { if (createError != null) { // handle the error return; } paywallView.Present((presentError) => { if (presentError != null) { // handle the error } }); }); }); }); } // ... other interface methods } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ``` </Details> ## Finalización de la carga del onboarding \{#finishing-loading-onboarding\} Cuando el onboarding termina de cargarse, implementa el método `OnboardingViewDidFinishLoading`: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewDidFinishLoading( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta ) { // handle loading completion } // ... other interface methods } ``` <Details> <summary>Ejemplo de evento (haz clic para expandir)</summary> ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ``` </Details> ## Seguimiento de la navegación \{#tracking-navigation\} El método `OnboardingViewOnAnalyticsEvent` se invoca cuando ocurren distintos eventos de analítica durante el flujo del onboarding. El objeto `analyticsEvent` puede ser uno de los siguientes tipos: | Tipo | Descripción | |------------|-------------| | `AdaptyOnboardingsAnalyticsEventOnboardingStarted` | Cuando el onboarding ha sido cargado | | `AdaptyOnboardingsAnalyticsEventScreenPresented` | Cuando se muestra cualquier pantalla | | `AdaptyOnboardingsAnalyticsEventScreenCompleted` | Cuando se completa una pantalla. Incluye el `ElementId` opcional (identificador del elemento completado) y `Reply` opcional (respuesta del usuario). Se activa cuando el usuario realiza cualquier acción para salir de la pantalla. | | `AdaptyOnboardingsAnalyticsEventSecondScreenPresented` | Cuando se muestra la segunda pantalla | | `AdaptyOnboardingsAnalyticsEventUserEmailCollected` | Se activa cuando se recoge el correo electrónico del usuario mediante el campo de entrada | | `AdaptyOnboardingsAnalyticsEventOnboardingCompleted` | Se activa cuando el usuario llega a una pantalla con el ID `final`. Si necesitas este evento, [asigna el ID `final` a la última pantalla](design-onboarding). | | `AdaptyOnboardingsAnalyticsEventUnknown` | Para cualquier tipo de evento no reconocido. Incluye `Name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) | Cada evento incluye información de `meta` con los siguientes campos: | Campo | Descripción | |------------|-------------| | `OnboardingId` | Identificador único del flujo de onboarding | | `ScreenClientId` | Identificador de la pantalla actual | | `ScreenIndex` | Posición de la pantalla actual en el flujo | | `ScreensTotal` | Número total de pantallas en el flujo | Aquí tienes un ejemplo de cómo usar los eventos de analítica para el seguimiento: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnAnalyticsEvent( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, AdaptyOnboardingsAnalyticsEvent analyticsEvent ) { switch (analyticsEvent) { case AdaptyOnboardingsAnalyticsEventOnboardingStarted: // track onboarding start TrackEvent("onboarding_started", meta); break; case AdaptyOnboardingsAnalyticsEventScreenPresented: // track screen presentation TrackEvent("screen_presented", meta); break; case AdaptyOnboardingsAnalyticsEventScreenCompleted screenCompleted: // track screen completion with user response TrackEvent("screen_completed", meta, screenCompleted.ElementId, screenCompleted.Reply); break; case AdaptyOnboardingsAnalyticsEventOnboardingCompleted: // track successful onboarding completion TrackEvent("onboarding_completed", meta); break; case AdaptyOnboardingsAnalyticsEventUnknown unknownEvent: // handle unknown events TrackEvent(unknownEvent.Name, meta); break; // handle other cases as needed } } // ... other interface methods } ``` :::note El método `TrackEvent` es un marcador de posición que debes implementar tú mismo para enviar analítica a tu servicio preferido. ::: <Details> <summary>Ejemplos de eventos (haz clic para expandir)</summary> ```javascript // onboardingStarted { "name": "onboarding_started", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } // screenPresented { "name": "screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 4 } } // screenCompleted { "name": "screen_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 }, "params": { "element_id": "profile_form", "reply": "success" } } // secondScreenPresented { "name": "second_screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // userEmailCollected { "name": "user_email_collected", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // onboardingCompleted { "name": "onboarding_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ``` </Details> --- # File: unity-onboarding-input --- --- title: "Procesar datos de onboardings en Unity SDK" description: "Guarda y usa datos de onboardings en tu app Unity con Adapty SDK." --- Cuando tus usuarios responden a una pregunta de quiz o introducen datos en un campo de texto, se invocará el método `OnboardingViewOnStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código. Implementa el método `OnboardingViewOnStateUpdatedAction` en tu clase: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string elementId, AdaptyOnboardingsStateUpdatedParams @params ) { switch (@params) { case AdaptyOnboardingsSelectParams selectParams: // handle single selection break; case AdaptyOnboardingsMultiSelectParams multiSelectParams: // handle multiple selections break; case AdaptyOnboardingsInputParams inputParams: // handle text input break; case AdaptyOnboardingsDatePickerParams datePickerParams: // handle date selection break; } } // ... other interface methods } ``` Los parámetros incluyen: | Parámetro | Descripción | |----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `elementId` | Un identificador único para el elemento de entrada. Puedes usarlo para asociar preguntas con respuestas al guardarlas. | | `@params` | El objeto con los datos de entrada del usuario. Puede ser uno de los siguientes tipos. | | `AdaptyOnboardingsSelectParams` | Selección única entre opciones. Contiene `Id`, `Value`, `Label` | | `AdaptyOnboardingsMultiSelectParams` | Selecciones múltiples entre opciones. Contiene una lista de `Params` (cada uno con `Id`, `Value`, `Label`)<br/>• `input`: Objeto con `type`, `value`<br/>• `datePicker`: Objeto con `day`, `month`, `year` | | `AdaptyOnboardingsInputParams` | Campo de entrada de texto. Contiene `Input`, que puede ser `AdaptyOnboardingsTextInput`, `AdaptyOnboardingsEmailInput` o `AdaptyOnboardingsNumberInput` | | `AdaptyOnboardingsDatePickerParams` | Selección de fecha. Contiene `Day`, `Month`, `Year` opcionales | <Details> <summary>Ejemplos de datos guardados (pueden variar según tu implementación)</summary> ```javascript // Example of a saved select action { "elementId": "preference_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "preferences_screen", "screenIndex": 1, "screensTotal": 3 }, "params": { "type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Example of a saved multi-select action { "elementId": "interests_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 3 }, "params": { "type": "multiSelect", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Example of a saved input action { "elementId": "name_input", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "input", "value": { "type": "text", "value": "John Doe" } } } // Example of a saved date picker action { "elementId": "birthday_picker", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "datePicker", "value": { "day": 15, "month": 6, "year": 1990 } } } ``` </Details> ## Casos de uso \{#use-cases\} ### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\} Si quieres vincular inmediatamente los datos de entrada con el perfil del usuario y evitar preguntarle dos veces por la misma información, necesitas [actualizar el perfil del usuario](unity-setting-user-attributes) con los datos de entrada al gestionar la acción. Por ejemplo, pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name` y quieres establecer el valor de ese campo como su nombre de pila. También les pides que introduzcan su correo electrónico en el campo `email`. En el código de tu app, puede verse así: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string elementId, AdaptyOnboardingsStateUpdatedParams @params ) { if (@params is AdaptyOnboardingsInputParams inputParams) { var builder = new AdaptyProfileParameters.Builder(); switch (elementId) { case "name": if (inputParams.Input is AdaptyOnboardingsTextInput textInput) { builder.SetFirstName(textInput.Value); } break; case "email": if (inputParams.Input is AdaptyOnboardingsEmailInput emailInput) { builder.SetEmail(emailInput.Value); } break; } Adapty.UpdateProfile(builder.Build(), (error) => { if (error != null) { // handle the error } }); } } // ... other interface methods } ``` ### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\} Usando quizzes en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios después de que completen el onboarding. Por ejemplo, puedes preguntarles sobre su experiencia con el deporte y mostrar diferentes CTAs y productos a distintos grupos de usuarios. 1. [Añade un quiz](onboarding-quizzes) en el editor de onboarding y asigna IDs significativos a sus opciones. 2. Gestiona las respuestas del quiz según sus IDs y [establece atributos personalizados](unity-setting-user-attributes) para los usuarios. ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string elementId, AdaptyOnboardingsStateUpdatedParams @params ) { if (@params is AdaptyOnboardingsSelectParams selectParams) { var builder = new AdaptyProfileParameters.Builder(); switch (elementId) { case "experience": // set custom attribute 'experience' with the selected value (beginner, amateur, pro) builder.SetCustomStringAttribute("experience", selectParams.Value); break; } Adapty.UpdateProfile(builder.Build(), (error) => { if (error != null) { // handle the error } }); } } // ... other interface methods } ``` 3. [Crea segmentos](segments) para cada valor de atributo personalizado. 4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado. 5. [Muestra un paywall](unity-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](unity-handling-onboarding-events#opening-a-paywall). --- # File: unity-sdk-call-order --- --- title: "Orden de llamadas en Unity SDK" description: "Evita perder acceso premium, atribución faltante y errores intermitentes #2002 llamando a los métodos del SDK de Adapty en el orden correcto." --- `Adapty.Activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que se dispare su callback de finalización, el SDK no tiene estado. Cualquier llamada realizada antes o en paralelo con `Activate()` falla con [`#2002 notActivated`](unity-handle-errors#custom-network-codes). Si tu app autentica usuarios y obtienes un customer user ID después del lanzamiento, llama a `Adapty.Identify()` en ese momento. No llames a métodos de acción del usuario hasta que se dispare el callback de `Identify`. Las llamadas que compiten con él o bien fallan con [`#3006 profileWasChanged`](unity-handle-errors#custom-network-codes), o aterrizan en el perfil anónimo creado durante la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la propiedad de instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `Identify` y sigue trabajando con el perfil anónimo. Los SDKs de MMP y analíticas (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera a sus callbacks de UID antes de llamar a `Adapty.Activate`. De lo contrario, el ID del MMP aterriza en un perfil anónimo temporal y no siempre se transfiere al identificado. Para los detalles específicos de AppsFlyer, consulta [AppsFlyer](appsflyer). ## El orden correcto \{#the-correct-order\} Tu ruta depende de dos cosas: cuándo conoces el customer user ID y si usas un SDK de MMP o analíticas. - **Pasos 2 y 5**: Obligatorios para toda app. Activa el SDK y luego llama a los métodos del SDK. - **Pasos 1 y 3**: Necesarios solo si integras un SDK de MMP o analíticas (AppsFlyer, Adjust, Branch, PostHog). - **Paso 4**: Necesario solo si tu app autentica usuarios y obtiene el customer user ID después del lanzamiento. Si tienes el customer user ID al iniciar la app, pásalo directamente a `Activate()` (paso 2a). Esta ruta nunca crea un perfil anónimo, por lo que el paso 4 es innecesario. | Paso | Llamada | Cuándo | Notas | |------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | Inicializa tu SDK de MMP o analíticas (AppsFlyer, Adjust, PostHog, Branch) | Al lanzar la app, lo primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerId`. | | 2a | `Adapty.Activate(builder.Build(), ...)` con `SetCustomerUserId` configurado en el builder | Al lanzar la app, después del paso 1, si tienes el customer user ID | Recomendado. Nunca se crea un perfil anónimo. | | 2b | `Adapty.Activate(builder.Build(), ...)` sin `SetCustomerUserId` | Al lanzar la app, después del paso 1, si no tienes el customer user ID (o nunca lo obtienes) | Adapty crea un perfil anónimo. | | 3 | `Adapty.SetIntegrationIdentifier(key, value, callback)` para cada MMP | Después del paso 2, antes de cualquier llamada de acción del usuario | Necesario para que los IDs del MMP aterricen en el perfil correcto. | | 4 | `Adapty.Identify("YOUR_USER_ID", callback)` | Después del paso 3 (o paso 2 si no hay MMP), antes del paso 5 — solo en la ruta 2b con autenticación | Espera el callback de finalización. Las llamadas concurrentes durante `Identify` producen `#3006 profileWasChanged`. | | 5 | `GetPaywall`, `GetPaywallProducts`, `RestorePurchases`, `MakePurchase`, `UpdateAttribution`, `UpdateProfile` | Después del paso 4 si llamas a `Identify`; en caso contrario, después del paso 3 (o paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. | :::important Saltarse estos pasos provoca pérdida de acceso premium para usuarios recurrentes, `appsflyer_id` ausente en los perfiles y paywalls devueltos para la audiencia incorrecta. ::: ## Instalaciones web2app y web-funnel \{#web2app-and-web-funnel-installs\} Si los usuarios compran en un checkout web (Stripe, Paddle) y luego instalan la app nativa, el primer `Activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el customer user ID antes del lanzamiento de la app (desde tu flujo de autenticación o el referrer de instalación), pásalo directamente a `Activate()`. De lo contrario, la compra web es invisible en el dispositivo hasta que llames a `Identify("YOUR_USER_ID")` y luego a `RestorePurchases`. Para los metadatos que debes enviar con cada checkout web, consulta: - [Stripe](stripe) - [Paddle](paddle) --- # File: unity-optimize-paywall-fetching --- --- title: "Optimizar la carga de paywalls en Unity SDK" description: "Carga paywalls de Adapty de forma fiable: timing, caché y patrones de respaldo para Unity." --- Una carga de paywall fiable en Unity hace tres cosas: renderiza rápido, devuelve el paywall dirigido a la audiencia correcta y recurre al respaldo cuando la red es lenta. Las reglas a continuación cubren el timing, la caché y los patrones de respaldo para conseguirlo. :::tip Las reglas asumen que `Adapty.Activate()` y `Adapty.Identify()` ya han finalizado. Consulta [Orden de llamadas en Unity SDK](unity-sdk-call-order). ::: ## Reglas y errores comunes \{#rules-and-pitfalls\} | Haz esto | No hagas esto | Por qué | |---|---|---| | Carga el placement que vas a mostrar. | Precarga todos los placements de forma concurrente al iniciar. | La precarga masiva bloquea el hilo principal y produce una pantalla en negro durante la ráfaga. | | Llama a `GetPaywall` después de que la atribución haya tenido tiempo de resolverse — por ejemplo, 1–2 segundos después de `Activate` o cuando se dispare `OnLoadLatestProfile`. | Llama a `GetPaywall` en `Awake()`. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia predeterminada y omite silenciosamente los segmentos y la personalización de ASA. | | Establece un `loadTimeout` y configura un [paywall de respaldo](fallback-paywalls) para cada placement. | Esperes indefinidamente a que `GetPaywall` responda. | Sin un timeout, los usuarios con conectividad deficiente ven una pantalla en blanco hasta que la red responde — o cierran la app. | Consulta [Cargar paywalls y productos](fetch-paywalls-and-products-unity) para la referencia de los parámetros `fetchPolicy` y `loadTimeout`, y [Placements](placements) para elegir el placement adecuado. ## Ajustar para conectividad deficiente \{#tune-for-poor-connectivity\} Para mercados con conectividad sistemáticamente deficiente (zonas rurales, transporte, regiones con problemas de enrutamiento): - Establece `fetchPolicy` a `AdaptyPlacementFetchPolicy.ReturnCacheDataElseLoad` en cada carga excepto la primera. - Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard. - Establece `loadTimeout` entre 3 y 5 segundos y acepta el paywall de respaldo cuando se agote el tiempo. - No condicionales la visualización del paywall a `GetProfile`. Llama a `GetPaywall` de forma independiente para que un perfil lento no bloquee la interfaz. --- # File: unity-test --- --- title: "Prueba y lanzamiento con Unity SDK" description: "Aprende a probar y lanzar tu app Unity con el SDK de Adapty." --- Si ya has implementado el SDK de Adapty en tu app de Unity, querrás verificar que todo está configurado correctamente y que las compras funcionan como se espera en las plataformas iOS y Android. Esto implica probar tanto la integración del SDK como el flujo de compra real con el entorno sandbox de Apple y el entorno de pruebas de Google Play. ## Prueba tu app \{#test-your-app\} Para realizar pruebas completas de tus compras in-app, consulta nuestras guías de pruebas específicas para cada plataforma: [guía de pruebas para iOS](test-purchases-in-sandbox) y [guía de pruebas para Android](testing-on-android). ## Prepárate para el lanzamiento \{#prepare-for-release\} Antes de enviar tu app al store, sigue el [checklist de lanzamiento](release-checklist) para confirmar: - La conexión con el store y las notificaciones del servidor están configuradas - Las compras se completan y se reportan a Adapty - El acceso se desbloquea y se restaura correctamente - Se cumplen los requisitos de privacidad y revisión --- # File: InvalidProductIdentifiers-unity --- --- title: "Solución para el error Code-1000 noProductIDsFound en Unity SDK" description: "Resuelve errores de identificador de producto no válido al gestionar suscripciones en Adapty." --- El error con código 1000, `noProductIDsFound`, indica que ninguno de los productos que solicitaste en el paywall está disponible para comprar en el App Store, aunque aparezcan listados allí. A veces este error va acompañado de una advertencia `InvalidProductIdentifiers`. Si la advertencia aparece sin el error, puedes ignorarla sin problema. Si estás viendo el error `noProductIDsFound`, sigue estos pasos para resolverlo: ## Paso 1. Comprueba el bundle ID \{#step-2-check-bundle-id\} --- no_index: true --- 1. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu app y ve a la sección **General** → **App Information**. 2. Copia el **Bundle ID** en la subsección **General Information**. <Zoom> <img src="/docs/img/afd5012-bundle_id_apple.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 3. Abre la pestaña [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) desde el menú superior de Adapty y pega el valor copiado en el campo **Bundle ID**. <Zoom> <img src="/docs/img/2d64163-bundle_id.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> </Zoom> 4. Vuelve a la página **App information** en App Store Connect y copia el **Apple ID** desde allí. 5. En la página [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) del Adapty Dashboard, pega el ID en el campo **Apple app ID**. ## Paso 2. Comprueba los productos \{#step-3-check-products\} 1. Ve a **App Store Connect** y navega a [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) en el menú de la izquierda. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción. Verás tus productos listados en la sección **Subscriptions**. 3. Asegúrate de que el producto que estás probando aparece como **Ready to Submit**. <img src="/assets/shared/img/ready-to-submit.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Compara el ID del producto de la tabla con el que aparece en la pestaña [**Products**](https://app.adapty.io/products) del Adapty Dashboard. Si los IDs no coinciden, copia el ID del producto de la tabla y [crea un producto](create-product) con ese ID en el Adapty Dashboard. <img src="/assets/shared/img/product-id-copy.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 3. Comprueba la disponibilidad del producto \{#step-4-check-product-availability\} 1. Vuelve a **App Store Connect** y abre la misma sección **Subscriptions**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción para ver tus productos. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hasta la sección **Availability** y comprueba que todos los países y regiones requeridos están en la lista. <img src="/assets/shared/img/product-availability.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 4. Comprueba los precios del producto \{#step-5-check-product-prices\} 1. De nuevo, ve a la sección **Monetization** → **Subscriptions** en **App Store Connect**. <img src="/assets/shared/img/subscription_group_open.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Haz clic en el nombre del grupo de suscripción. 3. Selecciona el producto que estás probando. <img src="/assets/shared/img/click-product.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 4. Desplázate hacia abajo hasta **Subscription Pricing** y despliega la sección **Current Pricing for New Subscribers**. <img src="/assets/shared/img/check-prices.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 5. Asegúrate de que todos los precios requeridos están en la lista. <img src="/assets/shared/img/product-pricing.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> ## Paso 5. Comprueba que el estado de pago de la app, la cuenta bancaria y los formularios fiscales están activos \{#step-5-check-app-paid-status-bank-account-and-tax-forms-are-active\} 1. En la página de inicio de [**App Store Connect**](https://appstoreconnect.apple.com/), haz clic en **Business**. <img src="/assets/shared/img/business.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 2. Selecciona el nombre de tu empresa. <img src="/assets/shared/img/business-name.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> 3. Desplázate hacia abajo y comprueba que tu **Paid Apps Agreement**, **Bank Account** y **Tax forms** aparecen todos como **Active**. <img src="/assets/shared/img/appstore-connect-status.webp" style={{ border: '1px solid #727272', /* border width and color */ width: '700px', /* image width */ display: 'block', /* for alignment */ margin: '0 auto' /* center alignment */ }} /> Siguiendo estos pasos deberías poder resolver la advertencia `InvalidProductIdentifiers` y publicar tus productos en el store. ## Paso 6. Vuelve a crear el producto si está bloqueado \{#step-6-recreate-the-product-if-its-stuck\} Puede que los pasos 1 a 5 pasen todos correctamente — estado `Approved`, Bundle ID correcto, API key válida — y aun así el SDK siga devolviendo `1000 noProductIDsFound`. En ese caso, es posible que el producto esté bloqueado en el registro de Apple. El registro de productos de Apple puede entrar en un estado en el que un producto existe en la interfaz de App Store Connect pero no está expuesto en la ruta de búsqueda de StoreKit. Elimina el producto en App Store Connect y vuelve a crearlo con el mismo ID de producto. Espera hasta 24 horas después de volver a crearlo para que los cambios se propaguen. --- # File: cantMakePayments-unity --- --- title: "Solución para el error Code-1003 cantMakePayment en el SDK de Unity" description: "Resuelve el error de pagos al gestionar suscripciones en Adapty." --- El error 1003, `cantMakePayments`, indica que no es posible realizar compras in-app en este dispositivo. Si encuentras el error `cantMakePayments`, normalmente se debe a una de estas razones: - Restricciones del dispositivo: El error no está relacionado con Adapty. Consulta las soluciones más abajo. - Configuración del modo Observer: El método `makePurchase` y el modo Observer no pueden usarse al mismo tiempo. Consulta la sección más abajo. ## Problema: Restricciones del dispositivo \{#issue-device-restrictions\} | Problema | Solución | |-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| | Restricciones de Screen Time | Desactiva las restricciones de compras in-app en [Screen Time](https://support.apple.com/en-us/102470) | | Cuenta suspendida | Contacta con el soporte de Apple para resolver problemas con la cuenta | | Restricciones regionales | Usa una cuenta de App Store de una región compatible | ## Problema: Usar el modo Observer y makePurchase a la vez \{#issue-using-both-observer-mode-and-makepurchase\} Si usas `makePurchases` para gestionar las compras, no necesitas el modo Observer. El [modo Observer](observer-vs-full-mode) solo es necesario si implementas la lógica de compra tú mismo. Por lo tanto, si usas `makePurchase`, puedes eliminar sin problema la activación del modo Observer del código de inicialización del SDK. --- # File: migration-to-unity-sdk-314 --- --- title: "Migrar el SDK de Adapty para Unity a la v. 3.14" description: "Migra al SDK de Adapty para Unity v3.14 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK de Adapty 3.14.0 es una versión mayor que incorpora mejoras que pueden requerir algunos pasos de migración: 1. Listener de eventos independiente para eventos de paywall. 2. Renombrar `AdaptyUI.CreateView` a `AdaptyUI.CreatePaywallView` y métodos relacionados. 3. Actualizar el método `MakePurchase` para usar `AdaptyPurchaseParameters` en lugar de parámetros individuales. 4. Reemplazar `SetFallbackPaywalls` por el método `SetFallback`. 5. Actualizar el acceso a propiedades del paywall para usar `AdaptyPlacement`. 6. Actualizar el acceso a la configuración remota usando el objeto `AdaptyRemoteConfig`. 7. Reemplazar `VendorProductIds` por `ProductIdentifiers` en el modelo `AdaptyPaywall`. 8. Actualizar la política de obtención en `GetPaywall` para usar `AdaptyFetchPolicy`. ## Listener de eventos independiente para eventos de paywall \{#separate-event-listener-for-paywall-events\} Si muestras paywalls diseñados con el [Paywall Builder](adapty-paywall-builder), los eventos de la vista de paywall ahora usan la interfaz dedicada `AdaptyPaywallsEventsListener` y el método `SetPaywallsEventsListener`. La interfaz principal `AdaptyEventListener` sigue siendo la encargada de las actualizaciones de perfil y los detalles de instalación. ```diff showLineNumbers using UnityEngine; using AdaptySDK; public class AdaptyListener : MonoBehaviour, - AdaptyEventListener { + AdaptyEventListener, + AdaptyPaywallsEventsListener { void Start() { Adapty.SetEventListener(this); + Adapty.SetPaywallsEventsListener(this); } // AdaptyEventListener methods public void OnLoadLatestProfile(AdaptyProfile profile) { } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } + // AdaptyPaywallsEventsListener methods + // Implement paywall event handlers here } ``` [Más información sobre cómo gestionar eventos de paywall](unity-handling-events). ## Renombrar los métodos de creación y presentación de vistas \{#rename-view-creation-and-presentation-methods\} Los métodos de creación y presentación de vistas han sido renombrados: ```diff showLineNumbers using AdaptySDK; - AdaptyUI.CreateView(paywall, parameters, (view, error) => { + AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { if (error != null) { // handle the error return; } - AdaptyUI.PresentView(view, (error) => { + AdaptyUI.PresentPaywallView(view, (error) => { // handle the error }); }); } ``` Del mismo modo, el método de cierre ha sido renombrado: ```diff showLineNumbers - AdaptyUI.DismissView(view, (error) => { + AdaptyUI.DismissPaywallView(view, (error) => { // handle the error }); ``` ## Actualizar el método MakePurchase \{#update-makepurchase-method\} El método `MakePurchase` ahora usa `AdaptyPurchaseParameters` en lugar de los argumentos individuales `subscriptionUpdateParams` e `isOfferPersonalized`. Esto mejora la seguridad de tipos y permite ampliar los parámetros de compra en el futuro. ```diff showLineNumbers using AdaptySDK; void MakePurchase( AdaptyPaywallProduct product, AdaptySubscriptionUpdateParameters subscriptionUpdate, bool? isOfferPersonalized ) { - Adapty.MakePurchase(product, subscriptionUpdate, isOfferPersonalized, (result, error) => { + var parameters = new AdaptyPurchaseParametersBuilder() + .SetSubscriptionUpdateParams(subscriptionUpdate) + .SetIsOfferPersonalized(isOfferPersonalized) + .Build(); + + Adapty.MakePurchase(product, parameters, (result, error) => { switch (result.Type) { case AdaptyPurchaseResultType.Pending: // handle pending purchase break; case AdaptyPurchaseResultType.UserCancelled: // handle purchase cancellation break; case AdaptyPurchaseResultType.Success: var profile = result.Profile; // handle successful purchase break; default: break; } }); } ``` Si no necesitas parámetros adicionales, puedes usar simplemente: ```csharp showLineNumbers using AdaptySDK; void MakePurchase(AdaptyPaywallProduct product) { Adapty.MakePurchase(product, (result, error) => { // handle purchase result }); } ``` ## Actualizar el método de respaldo \{#update-fallback-method\} :::important Al actualizar al SDK de Unity 3.14, deberás descargar los nuevos archivos de respaldo desde el Adapty Dashboard y reemplazar los existentes en tu proyecto. ::: El método para configurar los paywall de respaldo ha sido actualizado. El método `SetFallbackPaywalls` ha sido renombrado a `SetFallback`: ```diff showLineNumbers using AdaptySDK; void SetFallBackPaywalls() { #if UNITY_IOS var assetId = "adapty_fallback_ios.json"; #elif UNITY_ANDROID var assetId = "adapty_fallback_android.json"; #else var assetId = ""; #endif - Adapty.SetFallbackPaywalls(assetId, (error) => { + Adapty.SetFallback(assetId, (error) => { // handle the error }); } ``` Consulta el ejemplo de código final en la página [Usar paywalls de respaldo en Unity](unity-use-fallback-paywalls). ## Actualizar el acceso a propiedades del paywall \{#update-paywall-property-access\} Las siguientes propiedades se han movido de `AdaptyPaywall` a `AdaptyPlacement`: ```diff showLineNumbers using AdaptySDK; void ProcessPaywall(AdaptyPaywall paywall) { - var abTestName = paywall.ABTestName; - var audienceName = paywall.AudienceName; - var revision = paywall.Revision; - var placementId = paywall.PlacementId; + var abTestName = paywall.Placement.ABTestName; + var audienceName = paywall.Placement.AudienceName; + var revision = paywall.Placement.Revision; + var placementId = paywall.Placement.Id; } ``` ## Actualizar el acceso a la configuración remota \{#update-remote-config-access\} Las propiedades de Remote Config se han reestructurado en un objeto `AdaptyRemoteConfig` para una mejor organización: ```diff showLineNumbers using AdaptySDK; void ProcessRemoteConfig(AdaptyPaywall paywall) { - var remoteConfigString = paywall.RemoteConfigString; - var locale = paywall.Locale; - var remoteConfigDict = paywall.RemoteConfig; + var remoteConfigString = paywall.RemoteConfig.Data; + var locale = paywall.RemoteConfig.Locale; + var remoteConfigDict = paywall.RemoteConfig.Dictionary; } ``` ## Actualizar el uso del modelo AdaptyPaywall \{#update-adaptyPaywall-model-usage\} La propiedad `VendorProductIds` ha quedado obsoleta en favor de `ProductIdentifiers`. La nueva propiedad devuelve objetos `AdaptyProductIdentifier` en lugar de cadenas simples, lo que proporciona información de producto más estructurada. ```diff showLineNumbers using AdaptySDK; void ProcessPaywallProducts(AdaptyPaywall paywall) { - var productIds = paywall.VendorProductIds; - foreach (var vendorId in productIds) { - // use vendorId - } + var productIdentifiers = paywall.ProductIdentifiers; + foreach (var productId in productIdentifiers) { + var vendorId = productId.VendorProductId; + // use vendorId + } } ``` El objeto `AdaptyProductIdentifier` da acceso al ID del producto del proveedor a través de la propiedad `VendorProductId`, manteniendo la misma funcionalidad y ofreciendo una mejor estructura para futuras mejoras. ## Actualizar la política de obtención en GetPaywall \{#update-getpaywall-fetch-policy\} El tipo del parámetro `fetchPolicy` en el método `GetPaywall` ha cambiado de `AdaptyPaywallFetchPolicy` a `AdaptyPlacementFetchPolicy`. Este cambio unifica el uso de la política de obtención en todo el SDK. ```diff showLineNumbers using AdaptySDK; void GetPaywall(string placementId) { - Adapty.GetPaywall(placementId, AdaptyPaywallFetchPolicy.ReloadRevalidatingCacheData, null, (paywall, error) => { + Adapty.GetPaywall(placementId, AdaptyPlacementFetchPolicy.ReloadRevalidatingCacheData, null, (paywall, error) => { // handle the result }); } ``` --- # File: migration-to-unity-sdk-34 --- --- title: "Migrar Adapty Unity SDK a v. 3.4" description: "Migra al Adapty Unity SDK v3.4 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- Adapty SDK 3.4.0 es una versión mayor que incluye mejoras que requieren pasos de migración por tu parte. ## Actualizar los archivos de paywall de respaldo \{#update-fallback-paywall-files\} Actualiza los archivos de paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK: 1. [Descarga los archivos de paywall de respaldo actualizados](fallback-paywalls) desde el Adapty Dashboard. 2. [Reemplaza los paywalls de respaldo existentes en tu aplicación móvil](unity-use-fallback-paywalls) con los nuevos archivos. ## Actualizar la implementación del modo Observer \{#update-implementation-of-observer-mode\} Si usas el modo Observer, asegúrate de actualizar su implementación. Anteriormente, se usaban distintos métodos para reportar transacciones a Adapty. En la nueva versión, el método `reportTransaction` debe usarse de forma consistente tanto en Android como en iOS. Este método reporta explícitamente cada transacción a Adapty, garantizando que sea reconocida. Si se usó un paywall, pasa el ID de variación para vincular la transacción a él. :::warning **¡No omitas el reporte de transacciones!** Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones. ::: ```diff showLineNumbers - #if UNITY_ANDROID && !UNITY_EDITOR - Adapty.RestorePurchases((profile, error) => { - // handle the error - }); - #endif Adapty.ReportTransaction( "YOUR_TRANSACTION_ID", "PAYWALL_VARIATION_ID", // optional (error) => { // handle the error }); ``` --- # File: migration-to-unity330 --- --- title: "Migrar Adapty Unity SDK a la versión 3.3" description: "Migra al Adapty Unity SDK v3.3 para obtener mejor rendimiento y nuevas funciones de monetización." --- Adapty SDK 3.3.0 es una versión mayor que incorpora mejoras que pueden requerir algunos pasos de migración por tu parte. 1. Actualiza a Adapty SDK v3.3.x. 2. Se han renombrado varias clases, propiedades y métodos en los módulos Adapty y AdaptyUI del SDK de Adapty. 3. A partir de ahora, el método `SetLogLevel` acepta un callback como argumento. 4. A partir de ahora, el método `PresentCodeRedemptionSheet` acepta un callback como argumento. 5. Cambia la forma en que se crea la vista del paywall. 6. Elimina el método `GetProductsIntroductoryOfferEligibility`. 7. Guarda los paywalls de respaldo en archivos separados (uno por plataforma) en `Assets/StreamingAssets/` y pasa los nombres de archivo al método `SetFallbackPaywalls`. 8. Actualiza la lógica de compra. 9. Actualiza el manejo de eventos del Paywall Builder. 10. Actualiza el manejo de errores del paywall del Paywall Builder. 11. Actualiza las configuraciones de integración para Adjust, Amplitude, AppMetrica, Appsflyer, Branch, Firebase and Google Analytics, Mixpanel, OneSignal, Pushwoosh. 13. Actualiza la implementación del modo Observer. 14. Actualiza la inicialización del plugin de Unity con una llamada explícita a `Activate`. ## Actualizar el Adapty Unity SDK a la versión 3.3.x \{#upgrade-adapty-unity-sdk-to-33x\} Hasta esta versión, Adapty SDK era el SDK principal e imprescindible para el funcionamiento correcto de Adapty en tu app, mientras que AdaptyUI SDK era un SDK opcional que solo se necesitaba si usabas el Adapty Paywall Builder. A partir de la versión 3.3.0, AdaptyUI SDK queda obsoleto y AdaptyUI se fusiona con el Adapty SDK como módulo. Debido a estos cambios, debes eliminar AdaptyUISDK y reinstalar AdaptySDK. 1. Elimina las dependencias de los paquetes **AdaptySDK** y **AdaptyUISDK** de tu proyecto. 2. Borra las carpetas **AdaptySDK** y **AdaptyUISDK**. 3. Importa de nuevo el paquete AdaptySDK tal como se describe en la página [Instalación y configuración del SDK de Adapty para Unity](sdk-installation-unity). ## Renombramientos \{#renamings\} 1. Renombramientos en el módulo Adapty: | Versión anterior | Nueva versión | | ------------------------- | ------------------------ | | Adapty.sdkVersion | Adapty.SDKVersion | | Adapty.LogLevel | AdaptyLogLevel | | Adapty.Paywall | AdaptyPaywall | | Adapty.PaywallFetchPolicy | AdaptyPaywallFetchPolicy | | PaywallProduct | AdaptyPaywallProduct | | Adapty.Profile | AdaptyProfile | | Adapty.ProfileParameters | AdaptyProfileParameters | | ProfileGender | AdaptyProfileGender | | Error | AdaptyError | 2. Renombramientos en el módulo AdaptyUI: | Versión anterior | Nueva versión | | ------------------ | ------------------ | | CreatePaywallView | CreateView | | PresentPaywallView | PresentView | | DismissPaywallView | DismissView | | AdaptyUI.View | AdaptyUIView | | AdaptyUI.Action | AdaptyUIUserAction | ## Cambiar el método SetLogLevel \{#change-the-setloglevel-method\} A partir de ahora, el método `SetLogLevel` acepta un callback como argumento. ```diff showLineNumbers - Adapty.SetLogLevel(Adapty.LogLevel.Verbose); + Adapty.SetLogLevel(Adapty.LogLevel.Verbose, null); // or you can pass the callback to handle the possible error ``` ## Cambiar el método PresentCodeRedemptionSheet \{#change-the-presentcoderedemptionsheet-method\} A partir de ahora, el método `PresentCodeRedemptionSheet` acepta un callback como argumento. ```diff showLineNumbers - Adapty.PresentCodeRedemptionSheet(); + Adapty.PresentCodeRedemptionSheet(null); // or you can pass the callback to handle the possible error ``` ## Cambiar la forma en que se crea la vista del paywall \{#change-how-the-paywall-view-is-created\} Para ver el ejemplo de código completo, consulta [Obtener la configuración de vista del paywall diseñado con el Paywall Builder](unity-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). ```diff showLineNumbers + var parameters = new AdaptyUICreateViewParameters() + .SetPreloadProducts(true); - AdaptyUI.CreatePaywallView( + AdaptyUI.CreateView( paywall, - preloadProducts: true, + parameters, (view, error) => { // use the view }); ``` ## Eliminar el método GetProductsIntroductoryOfferEligibility \{#remove-the-getproductsintroductoryoffereligibility-method\} Antes de Adapty iOS SDK 3.3.0, el objeto producto siempre incluía las ofertas, independientemente de si el usuario era elegible. Había que comprobar la elegibilidad manualmente antes de usar la oferta. Ahora, el objeto producto solo incluye una oferta si el usuario es elegible. Esto significa que ya no es necesario comprobar la elegibilidad: si hay una oferta disponible, el usuario es elegible. ## Actualizar el método para proporcionar paywalls de respaldo \{#update-method-for-providing-fallback-paywalls\} Hasta esta versión, los paywalls de respaldo se pasaban como JSON serializado. A partir de la versión 3.3.0, el mecanismo ha cambiado: 1. Guarda los paywalls de respaldo en archivos dentro de `/Assets/StreamingAssets/`, un archivo para Android y otro para iOS. 2. Pasa los nombres de archivo al método `SetFallbackPaywalls`. Tu código cambiará de la siguiente manera: ```diff showLineNumbers using AdaptySDK; void SetFallBackPaywalls() { + #if UNITY_IOS + var assetId = "adapty_fallback_ios.json"; + #elif UNITY_ANDROID + var assetId = "adapty_fallback_android.json"; + #else + var assetId = ""; + #endif - Adapty.SetFallbackPaywalls("FALLBACK_PAYWALLS_JSON_STRING", (error) => { + Adapty.SetFallbackPaywalls(assetId, (error) => { // handle the error }); } ``` Consulta el ejemplo de código completo en la página [Usar paywalls de respaldo en Unity](unity-use-fallback-paywalls). ## Actualizar la lógica de compra \{#update-making-purchase\} Anteriormente, las compras canceladas y pendientes se consideraban errores y devolvían los códigos `PaymentCancelled` y `PendingPurchase`, respectivamente. Ahora se usa una nueva clase `AdaptyPurchaseResultType` para procesar las compras canceladas, exitosas y pendientes. Actualiza el código de compra de la siguiente manera: ```diff showLineNumbers using AdaptySDK; void MakePurchase(AdaptyPaywallProduct product) { - Adapty.MakePurchase(product, (profile, error) => { - // handle successfull purchase + Adapty.MakePurchase(product, (result, error) => { + switch (result.Type) { + case AdaptyPurchaseResultType.Pending: + // handle pending purchase + break; + case AdaptyPurchaseResultType.UserCancelled: + // handle purchase cancellation + break; + case AdaptyPurchaseResultType.Success: + var profile = result.Profile; + // handle successful purchase + break; + default: + break; } }); } ``` Consulta el ejemplo de código completo en la página [Realizar compras en la app móvil](unity-making-purchases). ## Actualizar el manejo de eventos del Paywall Builder \{#update-handling-of-paywall-builder-events\} Las compras canceladas y pendientes ya no se consideran errores; todos estos casos se procesan con el método `PaywallViewDidFinishPurchase`. 1. Elimina el procesamiento del evento de compra cancelada. 2. Actualiza el manejo del evento de compra exitosa de la siguiente manera: ```diff showLineNumbers - public void OnFinishPurchase( - AdaptyUI.View view, - Adapty.PaywallProduct product, - Adapty.Profile profile - ) { } + public void PaywallViewDidFinishPurchase( + AdaptyUIView view, + AdaptyPaywallProduct product, + AdaptyPurchaseResult purchasedResult + ) { } ``` 3. Actualiza el manejo de acciones: ```diff showLineNumbers - public void OnPerformAction( - AdaptyUI.View view, - AdaptyUI.Action action - ) { + public void PaywallViewDidPerformAction( + AdaptyUIView view, + AdaptyUIUserAction action + ) { switch (action.Type) { - case AdaptyUI.ActionType.Close: + case AdaptyUIUserActionType.Close: view.Dismiss(null); break; - case AdaptyUI.ActionType.OpenUrl: + case AdaptyUIUserActionType.OpenUrl: var urlString = action.Value; if (urlString != null { Application.OpenURL(urlString); } default: // handle other events break; } } ``` 4. Actualiza el manejo del inicio de compra: ```diff showLineNumbers - public void OnSelectProduct( - AdaptyUI.View view, - Adapty.PaywallProduct product - ) { } + public void PaywallViewDidSelectProduct( + AdaptyUIView view, + string productId + ) { } ``` 5. Actualiza el manejo de compra fallida: ```diff showLineNumbers - public void OnFailPurchase( - AdaptyUI.View view, - Adapty.PaywallProduct product, - Adapty.Error error - ) { } + public void PaywallViewDidFailPurchase( + AdaptyUIView view, + AdaptyPaywallProduct product, + AdaptyError error + ) { } ``` 6. Actualiza el manejo del evento de restauración exitosa: ```diff showLineNumbers - public void OnFailRestore( - AdaptyUI.View view, - Adapty.Error error - ) { } + public void PaywallViewDidFailRestore( + AdaptyUIView view, + AdaptyError error + ) { } ``` Consulta el ejemplo de código completo en la página [Manejar eventos del paywall](unity-handling-events). ## Actualizar el manejo de errores del paywall del Paywall Builder \{#update-handling-of-paywall-builder-paywall-errors\} El manejo de errores también ha cambiado; actualiza tu código según las indicaciones a continuación. 1. Actualiza el manejo de errores de carga de productos: ```diff showLineNumbers - public void OnFailLoadingProducts( - AdaptyUI.View view, - Adapty.Error error - ) { } + public void PaywallViewDidFailLoadingProducts( + AdaptyUIView view, + AdaptyError error + ) { } ``` 2. Actualiza el manejo de errores de renderizado: ```diff showLineNumbers - public void OnFailRendering( - AdaptyUI.View view, - Adapty.Error error - ) { } + public void PaywallViewDidFailRendering( + AdaptyUIView view, + AdaptyError error + ) { } ``` ## Actualizar la configuración del SDK de integraciones de terceros \{#update-third-party-integration-sdk-configuration\} A partir de Adapty Unity SDK 3.3.0, hemos actualizado la API pública del método `updateAttribution`. Anteriormente, aceptaba un diccionario `[AnyHashable: Any]`, lo que permitía pasar objetos de atribución directamente desde varios servicios. Ahora requiere un `[String: any Sendable]`, por lo que tendrás que convertir los objetos de atribución antes de pasarlos. Para garantizar que las integraciones funcionen correctamente con Adapty Unity SDK 3.3.0 y versiones posteriores, actualiza las configuraciones de tu SDK para las siguientes integraciones tal como se describe en las secciones a continuación. ### Adjust Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con Adjust](adjust#connect-your-app-to-adjust). ```diff showLineNumbers - using static AdaptySDK.Adapty; using AdaptySDK; Adjust.GetAdid((adid) => { - Adjust.GetAttribution((attribution) => { - Dictionary<String, object> data = new Dictionary<String, object>(); - - data["network"] = attribution.Network; - data["campaign"] = attribution.Campaign; - data["adgroup"] = attribution.Adgroup; - data["creative"] = attribution.Creative; - - String attributionString = JsonUtility.ToJson(data); - Adapty.UpdateAttribution(attributionString, AttributionSource.Adjust, adid, (error) => { - // handle the error - }); + if (adid != null) { + Adapty.SetIntegrationIdentifier( + "adjust_device_id", + adid, + (error) => { + // handle the error + }); } }); Adjust.GetAttribution((attribution) => { Dictionary<String, object> data = new Dictionary<String, object>(); data["network"] = attribution.Network; data["campaign"] = attribution.Campaign; data["adgroup"] = attribution.Adgroup; data["creative"] = attribution.Creative; String attributionString = JsonUtility.ToJson(data); - Adapty.UpdateAttribution(attributionString, AttributionSource.Adjust, adid, (error) => { + Adapty.UpdateAttribution(attributionString, "adjust", (error) => { // handle the error }); }); ``` ### Amplitude Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con Amplitude](amplitude#sdk-configuration). ```diff showLineNumbers using AdaptySDK; - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetAmplitudeUserId("YOUR_AMPLITUDE_USER_ID"); - builder.SetAmplitudeDeviceId(amplitude.getDeviceId()); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); + Adapty.SetIntegrationIdentifier( + "amplitude_user_id", + "YOUR_AMPLITUDE_USER_ID", + (error) => { + // handle the error + }); + Adapty.SetIntegrationIdentifier( + "amplitude_device_id", + amplitude.getDeviceId(), + (error) => { + // handle the error + }); ``` ### AppMetrica Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con AppMetrica](appmetrica#sdk-configuration). ```diff showLineNumbers using AdaptySDK; - var deviceId = AppMetrica.GetDeviceId(); - if (deviceId != null { - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetAppmetricaProfileId("YOUR_ADAPTY_CUSTOMER_USER_ID"); - builder.SetAppmetricaDeviceId(deviceId); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); - } + var deviceId = AppMetrica.GetDeviceId(); + if (deviceId != null { + Adapty.SetIntegrationIdentifier( + "appmetrica_device_id", + deviceId, + (error) => { + // handle the error + }); + + Adapty.SetIntegrationIdentifier( + "appmetrica_profile_id", + "YOUR_ADAPTY_CUSTOMER_USER_ID", + (error) => { + // handle the error + }); + } ``` ### AppsFlyer Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con AppsFlyer](appsflyer#connect-your-app-to-appsflyer). ```diff showLineNumbers using AppsFlyerSDK; using AdaptySDK; // before SDK initialization AppsFlyer.getConversionData(this.name); // in your IAppsFlyerConversionData void onConversionDataSuccess(string conversionData) { // It's important to include the network user ID - string appsFlyerId = AppsFlyer.getAppsFlyerId(); - Adapty.UpdateAttribution(conversionData, AttributionSource.Appsflyer, appsFlyerId, (error) => { + string appsFlyerId = AppsFlyer.getAppsFlyerId(); + + Adapty.SetIntegrationIdentifier( + "appsflyer_id", + appsFlyerId, + (error) => { // handle the error }); + + Adapty.UpdateAttribution( + conversionData, + "appsflyer", + (error) => { + // handle the error + }); } ``` ### Branch Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con Branch](branch#connect-your-app-to-branch). ```diff showLineNumbers using AdaptySDK; - class YourBranchImplementation { - func initializeBranch() { - Branch.getInstance().initSession(launchOptions: launchOptions) { (data, error) in - if let data { - Adapty.updateAttribution(data, source: .branch) - } - } - } - } + Branch.initSession(delegate(Dictionary<string, object> parameters, string error) { + string attributionString = JsonUtility.ToJson(parameters); + + Adapty.UpdateAttribution( + attributionString, + "branch", + (error) => { + // handle the error + }); + }); ``` ### Firebase and Google Analytics Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con Firebase and Google Analytics](firebase-and-google-analytics). ```diff showLineNumbers // We suppose FirebaseAnalytics Unity Plugin is already installed using AdaptySDK; Firebase.Analytics .FirebaseAnalytics .GetAnalyticsInstanceIdAsync() .ContinueWithOnMainThread((task) => { if (!task.IsCompletedSuccessfully) { // handle error return; } var firebaseId = task.Result var builder = new Adapty.ProfileParameters.Builder(); - builder.SetFirebaseAppInstanceId(firebaseId); - - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error + Adapty.SetIntegrationIdentifier( + "firebase_app_instance_id", + firebaseId, + (error) => { + // handle the error }); }); ``` ### Mixpanel Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con Mixpanel](mixpanel#sdk-configuration). ```diff showLineNumbers using AdaptySDK; - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetMixpanelUserId(Mixpanel.DistinctId); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); + var distinctId = Mixpanel.DistinctId; + if (distinctId != null) { + Adapty.SetIntegrationIdentifier( + "mixpanel_user_id", + distinctId, + (error) => { + // handle the error + }); + } ``` ### OneSignal Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con OneSignal](onesignal#sdk-configuration). ```diff showLineNumbers using AdaptySDK; - using OneSignalSDK; - var pushUserId = OneSignal.Default.PushSubscriptionState.userId; - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetOneSignalPlayerId(pushUserId); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); + var distinctId = Mixpanel.DistinctId; + if (distinctId != null) { + Adapty.SetIntegrationIdentifier( + "mixpanel_user_id", + distinctId, + (error) => { + // handle the error + }); + } ``` ### Pushwoosh Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [Configuración del SDK para la integración con Pushwoosh](pushwoosh#sdk-configuration). ```diff showLineNumbers using AdaptySDK; - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetPushwooshHWID(Pushwoosh.Instance.HWID); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); + Adapty.SetIntegrationIdentifier( + "pushwoosh_hwid", + Pushwoosh.Instance.HWID, + (error) => { + // handle the error + }); ``` ## Actualizar la implementación del modo Observer \{#update-observer-mode-implementation\} Actualiza cómo vinculas los paywalls a las transacciones. Anteriormente, usabas el método `setVariationId` para asignar el `variationId`. Ahora puedes incluir el `variationId` directamente al registrar la transacción mediante el nuevo método `reportTransaction`. Consulta el ejemplo de código completo en [Asociar paywalls con transacciones de compra en el modo Observer](report-transactions-observer-mode-unity). ```diff showLineNumbers // every time when calling transaction.finish() - Adapty.SetVariationForTransaction("<variationId>", "<transactionId>", (error) => { - if(error != null) { - // handle the error - return; - } - - // successful binding - }); + Adapty.ReportTransaction( + "YOUR_TRANSACTION_ID", + "PAYWALL_VARIATION_ID", // optional + (error) => { + // handle the error + }); ``` ## Actualizar la inicialización del plugin de Unity \{#update-the-unity-plugin-initialization\} A partir de Adapty Unity SDK 3.3.0, es obligatorio llamar al método `Activate` de forma explícita durante la inicialización del plugin: ```csharp showLineNumbers Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); ``` --- # File: migration-to-unity-sdk-v3 --- --- title: "Migrar Adapty Unity SDK a v. 3.0" description: "Migra al Adapty Unity SDK v3.0 para mejorar el rendimiento y acceder a nuevas funciones de monetización." --- El SDK v.3.0 de Adapty incluye soporte para el nuevo [Adapty Paywall Builder](adapty-paywall-builder), la versión actualizada de la herramienta no-code para crear paywalls de forma visual. Con su máxima flexibilidad y ricas capacidades de diseño, tus paywalls serán más efectivos y rentables. ## Proceso de actualización \{#upgrade-process\} El proceso de actualización para Unity sigue los mismos pasos que en otras plataformas: 1. Actualiza al SDK de Adapty v3.x 2. Migra tus paywalls existentes al nuevo Paywall Builder Para instrucciones de migración específicas de Unity, consulta la [guía de instalación del SDK de Unity](sdk-installation-unity) y sigue los pasos generales de migración descritos en la guía de migración principal. --- # File: unity-migration-guide --- --- title: "Guía de migración del SDK" description: "Guías de migración para el SDK de Adapty para Unity." --- ## Guías de migración \{#migration-guides\} ### [Guía de migración al SDK de Unity Adapty 3.x](unity-sdk-migration-guides) Aprende a migrar desde versiones anteriores al SDK de Unity Adapty 3.x. ## Novedades \{#whats-new\} ### Versión 3.x \{#version-3x\} - Presentación de paywall mejorada - Gestión de errores mejorada - Mejor compatibilidad con C# - Optimizaciones de rendimiento ### Versión 2.x \{#version-2x\} - Nuevas funciones de onboarding - Analíticas mejoradas - Flujo de compra mejorado - Correcciones de errores y mejoras de estabilidad ## Cambios incompatibles \{#breaking-changes\} ### Versión 3.x \{#version-3x-breaking\} - API de observer actualizada - Métodos de presentación de paywall modificados - Estructura de gestión de errores modificada ### Versión 2.x \{#version-2x-breaking\} - API de onboarding actualizada - Estructura de perfil modificada - Flujo de compra modificado ## Lista de comprobación para la migración \{#migration-checklist\} Al migrar a una nueva versión: - [ ] Revisar los cambios incompatibles - [ ] Actualizar las llamadas a la API - [ ] Probar todas las funcionalidades - [ ] Actualizar la gestión de errores - [ ] Verificar el seguimiento de analíticas - [ ] Probar en todas las plataformas --- # End of Documentation _Generated on: 2026-05-15T20:13:57.814Z_ _Successfully processed: 39/39 files_