# 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'; 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\} [![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 `` incluya tools: ```xml ... ``` #### 2. Sobreescribe los atributos de copia de seguridad en `` \{#2-override-backup-attributes-in-application\} En el mismo archivo `AndroidManifest.xml`, actualiza la etiqueta `` para que tu app proporcione los valores definitivos e indique al fusionador de manifiestos que reemplace los valores de las librerías: ```xml ... ``` Si algún SDK también define `android:allowBackup`, inclúyelo en `tools:replace`: ```xml tools:replace="android:allowBackup,android:fullBackupContent,android:dataExtractionRules" ``` #### 3. Crea los archivos de reglas de copia de seguridad combinadas \{#3-create-merged-backup-rules-files\} Crea archivos XML en el directorio `res/xml/` de tu proyecto Android que combinen las reglas de Adapty con las de otros SDKs. Android utiliza distintos formatos de reglas de copia de seguridad según la versión del sistema operativo, por lo que crear ambos archivos garantiza la compatibilidad con todas las versiones de Android que admite tu app. :::note Los ejemplos a continuación usan AppsFlyer como SDK de terceros de muestra. Reemplaza o añade reglas para cualquier otro SDK que uses en tu app. ::: **Para Android 12 y superior** (usa el nuevo formato de reglas de extracción de datos): ```xml title="sample_data_extraction_rules.xml" ``` **Para Android 11 e inferior** (usa el formato legado de contenido de copia de seguridad completa): ```xml title="sample_backup_rules.xml" :::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 ``` #### 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'; ¿Tienes preguntas o estás teniendo algún problema? Consulta nuestro [foro de soporte](https://adapty.featurebase.app/) donde encontrarás respuestas a preguntas frecuentes o podrás plantear las tuyas. ¡Nuestro equipo y la comunidad están aquí para ayudarte! Tu paywall está listo para mostrarse en la app. Prueba tus compras en el [sandbox del App Store](test-purchases-in-sandbox) o en [Google Play Store](testing-on-android) para asegurarte de que puedes completar una compra de prueba desde el paywall. 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 (
); } ``` --- # 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. ::: ### 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. **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. ::: **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. ::: **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. ::: ### 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 :::tip To get started with the Adapty Paywall Builder paywalls quickly, see our [quickstart guide](capacitor-quickstart-paywalls). ::: ### Implement paywalls manually For more guides on implementing paywalls and handling purchases manually, see the [category](capacitor-implement-paywalls-manually). ## Useful features --- # 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).
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-capacitor) en tu app móvil.
## 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** |

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 de uno o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma, 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](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.

| | **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** |

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, 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](capacitor-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.

| | **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 = { '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 }, }); ```
Ejemplos de eventos (haz clic para expandir) ```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" } } } ```
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). ::: ## 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). ::: --- # 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'; ¿Tienes preguntas o estás teniendo algún problema? Consulta nuestro [foro de soporte](https://adapty.featurebase.app/) donde encontrarás respuestas a preguntas frecuentes o podrás plantear las tuyas. ¡Nuestro equipo y la comunidad están aquí para ayudarte! Tu paywall está listo para mostrarse en la app. Prueba tus compras en el [sandbox 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. :::
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 é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.
## 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** |

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](capacitor-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.

| | **params.fetchPolicy** |

opcional

por defecto: `'reload_revalidating_cache_data'`

|

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.

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.

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

| | **params.loadTimeoutMs** |

opcional

por defecto: 5000 ms

|

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.

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.

| **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:
• `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'`.
• `price`: El precio con descuento como número. Para pruebas gratuitas, busca `0` aquí.
• `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.
• `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.
• `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** |

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](capacitor-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.

| | **params.fetchPolicy** |

opcional

por defecto: `'reload_revalidating_cache_data'`

|

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.

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.

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

| --- # 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';
Sobre los códigos de oferta 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\} 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. 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.
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 |
  • Para iOS: Identificador de la transacción.
  • 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.
| | **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." --- --- # 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.
Antes de empezar a comprobar el estado de suscripción (haz clic para expandir) - 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)
## 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." --- --- # 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** |

opcional

predeterminado: `en`

|

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.

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

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.

| | **params.fetchPolicy** |

opcional

predeterminado: `'reload_revalidating_cache_data'`

|

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 `'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.

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.

| | **params.loadTimeoutMs** |

opcional

predeterminado: 5000 ms

|

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.

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.

| 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** |

opcional

predeterminado: `en`

|

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.

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

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.

| | **params.fetchPolicy** |

opcional

predeterminado: `'reload_revalidating_cache_data'`

|

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 `'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.

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.

| --- # 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. 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 }, }); ```
Ejemplo de evento (haz clic para expandir) ```json { "actionId": "allow_notifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ```
### 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); }, }); ```
Ejemplo de evento (haz clic para expandir) ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ```
### Cerrar el onboarding \{#closing-onboarding\} El onboarding se considera cerrado cuando el usuario pulsa un botón con la acción **Close** asignada. :::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 }, }); ```
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 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 } ```
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 } } ```
### 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 |
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: 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).
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](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." --- --- # 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 `` 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 |

Este código de error indica que el usuario canceló una solicitud de pago.

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.

| | paymentInvalid | 3 | Este error indica que uno de los parámetros de pago no fue reconocido por la store. | | paymentNotAllowed | 4 |

Este código de error indica que el usuario no tiene permiso para autorizar pagos. Posibles razones:

- Los pagos no están disponibles en el país del usuario.

- El usuario es menor de edad.

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

El identificador de oferta no es válido. Posibles razones:

- No has configurado una oferta con ese identificador en la App Store.

- Has revocado la oferta.

- Hay un error tipográfico en el ID de la oferta.

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

Este error indica problemas con la integración de Adapty o con las ofertas.

Consulta [Configure App Store integration](app-store-connection-configuration) y [Offers](offers) para más detalles sobre cómo configurarlas.

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

Este error indica que ocurrió un error de facturación del usuario durante el proceso de compra. Algunos ejemplos de cuándo puede ocurrir:

1\. La app de Play Store en el dispositivo del usuario está desactualizada.

2. El usuario está en un país no compatible.

3. El usuario es un empleado de empresa y su administrador ha deshabilitado las compras.

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.

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

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

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

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

1. Comprueba que todos los productos se han añadido al Adapty Dashboard.

2. Asegúrate de que el Bundle ID de tu app coincide con el de Apple Connect.

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.

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.

5. Comprueba que hay una cuenta bancaria vinculada a la app para que pueda ser elegible para la monetización.

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"**.

| | productRequestFailed | 1002 |

No se pueden obtener los productos disponibles en este momento. Posible razón:

- Aún no se ha creado ninguna caché y no hay conexión a internet al mismo tiempo.

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

No hay ningún recibo válido disponible en el dispositivo. Esto puede ser un problema durante las pruebas en sandbox.

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.

| | 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_