` para que tu app proporcione los valores finales e indique al fusionador de manifiestos que reemplace los valores de la biblioteca:
```xml
...
```
Si algún SDK también establece `android:allowBackup`, inclúyelo en `tools:replace`:
```xml
tools:replace="android:allowBackup,android:fullBackupContent,android:dataExtractionRules"
```
#### 3. Crear archivos de reglas de copia de seguridad combinados \{#3-create-merged-backup-rules-files\}
Crea archivos XML en `app/src/main/res/xml/` que combinen las reglas de Adapty con las reglas de otros SDKs. Android utiliza distintos formatos de reglas de copia de seguridad según la versión del SO, por lo que crear ambos archivos garantiza la compatibilidad con todas las versiones de Android que admite tu app.
:::note
Los ejemplos a continuación muestran AppsFlyer como SDK de terceros de muestra. Reemplaza o añade reglas para cualquier otro SDK que estés utilizando en tu app.
:::
**Para Android 12 y superior** (usa el nuevo formato de reglas de extracción de datos):
```xml title="sample_data_extraction_rules.xml"
```
**Para Android 11 y versiones anteriores** (usa el formato heredado de copia de seguridad completa):
```xml title="sample_backup_rules.xml"
```
Con esta configuración:
- Las exclusiones de copia de seguridad de Adapty (`AdaptySDKPrefs.xml`) se conservan.
- Las exclusiones de otros SDKs (por ejemplo, `appsflyer-data`) también se aplican.
- El combinador de manifiestos usa la configuración de tu app y ya no falla con atributos de copia de seguridad en conflicto.
#### Las compras fallan al volver desde otra app \{#purchases-fail-after-returning-from-another-app\}
Si la Activity que inicia el flujo de compra usa un `launchMode` distinto al predeterminado, Android puede recrearla o reutilizarla incorrectamente cuando el usuario vuelve desde Google Play, una app bancaria o un navegador. Esto puede provocar que el resultado de la compra se pierda o se trate como cancelado.
Para que las compras funcionen correctamente, usa únicamente los modos de lanzamiento `standard` o `singleTop` en la Activity que inicia el flujo de compra, y evita cualquier otro modo.
En tu `AndroidManifest.xml`, asegúrate de que la Activity que inicia el flujo de compra esté configurada como `standard` o `singleTop`:
```xml
```
---
# File: android-quickstart-paywalls
---
---
title: "Habilitar compras usando paywalls en el SDK de Android"
description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app."
---
Para habilitar las compras in-app, necesitas entender tres conceptos clave:
- [**Productos**](product) – todo lo que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida)
- Los [**paywalls**](paywalls) son configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar ofertas, precios y combinaciones de productos sin tocar el código de tu app.
- Los [**placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para los placements en el dashboard y luego los solicitas por ID de placement en tu código. Esto facilita ejecutar pruebas A/B y mostrar diferentes paywalls a distintos usuarios.
Adapty te ofrece tres formas de habilitar las compras en tu app. Selecciona una según los requisitos de tu aplicación:
| Implementación | Complejidad | Cuándo usarla |
|------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Adapty Paywall Builder | ✅ Fácil | [Creas un paywall completo y listo para compras en el editor sin código](quickstart-paywalls). Adapty lo renderiza automáticamente y gestiona todo el flujo de compra, la validación de recibos y la gestión de suscripciones en segundo plano. |
| Paywalls creados manualmente | 🟡 Medio | Implementas la UI de tu paywall en el código de tu app, pero sigues obteniendo el objeto paywall de Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](android-quickstart-manual). |
| Modo observador | 🔴 Difícil | Ya tienes tu propia infraestructura de gestión de compras y quieres seguir usándola. Ten en cuenta que el modo observador tiene sus limitaciones en Adapty. Consulta el [artículo](observer-vs-full-mode). |
:::important
**Los pasos a continuación muestran cómo implementar un paywall creado en el Paywall Builder de Adapty.**
Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](android-making-purchases).
:::
Para mostrar un paywall creado en el Paywall Builder de Adapty, en el código de tu app solo necesitas:
1. **Obtener el paywall**: Obtén el paywall desde Adapty.
2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app.
3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con el paywall a las respuestas de tu app. Por ejemplo, abrir enlaces o cerrar el paywall cuando los usuarios pulsan botones.
## Antes de empezar \{#before-you-start\}
Antes de empezar, completa estos pasos:
1. [Conecta tu app a Google Play](initial-android) en el Adapty Dashboard.
2. [Crea tus productos](create-product) en Adapty.
3. [Crea un paywall y añade productos](create-paywall).
4. [Crea un placement y añade tu paywall](create-placement).
5. [Instala y activa el SDK de Adapty](sdk-installation-android) en el código de tu app.
:::tip
La forma más rápida de completar estos pasos es seguir la [guía de inicio rápido](quickstart) o crear paywalls y placements usando el [CLI para desarrolladores](developer-cli-quickstart).
:::
## 1. Obtener el paywall \{#1-get-the-paywall\}
Tus paywalls están asociados a placements configurados en el dashboard. Los placements te permiten ejecutar diferentes paywalls para distintas audiencias o para correr [pruebas A/B](ab-tests).
Para obtener un paywall creado en el Paywall Builder de Adapty, debes:
1. Obtener el objeto `paywall` por el ID del [placement](placements) usando el método `getPaywall` y verificar si se trata de un paywall creado en el builder.
2. Obtener la configuración de vista del paywall usando el método `getViewConfiguration`. La configuración de vista contiene los elementos de UI y el estilo necesarios para mostrar el paywall.
:::important
Para obtener la configuración de vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará.
:::
```kotlin showLineNumbers
Adapty.getPaywall("YOUR_PLACEMENT_ID") { result ->
if (result is AdaptyResult.Success) {
val paywall = result.value
if (!paywall.hasViewConfiguration) {
return@getPaywall
}
AdaptyUI.getViewConfiguration(paywall) { configResult ->
if (configResult is AdaptyResult.Success) {
val viewConfiguration = configResult.value
}
}
}
}
```
```java showLineNumbers
Adapty.getPaywall("YOUR_PLACEMENT_ID", result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue();
if (!paywall.hasViewConfiguration()) {
return;
}
AdaptyUI.getViewConfiguration(paywall, configResult -> {
if (configResult instanceof AdaptyResult.Success) {
AdaptyUI.LocalizedViewConfiguration viewConfiguration =
((AdaptyResult.Success) configResult).getValue();
// use loaded configuration
}
});
}
});
```
## 2. Mostrar el paywall \{#2-display-the-paywall\}
Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo.
Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, llama al método `AdaptyUI.getPaywallView()` o crea el `AdaptyPaywallView` directamente:
```kotlin showLineNumbers
val paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
null, // products = null means auto-fetch
eventListener,
)
```
```kotlin showLineNumbers
val paywallView =
AdaptyPaywallView(activity) // or retrieve it from xml
...
with(paywallView) {
showPaywall(
viewConfiguration,
null, // products = null means auto-fetch
eventListener,
)
}
```
```java showLineNumbers
AdaptyPaywallView paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
null, // products = null means auto-fetch
eventListener,
);
```
```java showLineNumbers
AdaptyPaywallView paywallView =
new AdaptyPaywallView(activity); //add to the view hierarchy if needed, or you receive it from xml
...
paywallView.showPaywall(viewConfiguration, products, eventListener);
```
```xml showLineNumbers
```
Una vez creada la vista correctamente, puedes añadirla a la jerarquía de vistas y mostrarla en la pantalla del dispositivo.
:::tip
Para más detalles sobre cómo mostrar un paywall, consulta nuestra [guía](android-present-paywalls).
:::
## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\}
Cuando los usuarios pulsan botones en el paywall, el SDK de Android gestiona automáticamente las compras, la restauración, el cierre del paywall y la apertura de enlaces.
Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren gestionar sus acciones en tu código. También puedes querer sobrescribir su comportamiento predeterminado.
Por ejemplo, aquí está el comportamiento predeterminado del botón de cierre. No necesitas añadirlo en el código, pero aquí puedes ver cómo hacerlo si fuera necesario.
:::tip
Lee nuestras guías sobre cómo gestionar [acciones](android-handle-paywall-actions) y [eventos](android-handling-events) de los botones.
:::
```kotlin showLineNumbers title="Kotlin"
override fun onActionPerformed(action: AdaptyUI.Action, context: Context) {
when (action) {
AdaptyUI.Action.Close -> (context as? Activity)?.onBackPressed() // default behavior
}
}
```
```java showLineNumbers
@Override
public void onActionPerformed(@NonNull AdaptyUI.Action action, @NonNull Context context) {
if (action instanceof AdaptyUI.Action.Close) {
if (context instanceof Activity) {
((Activity) context).onBackPressed();
}
}
}
```
## Próximos pasos \{#next-steps\}
---
no_index: true
---
import Callout from '../../../components/Callout.astro';
¿Tienes preguntas o estás teniendo algún problema? Consulta nuestro [foro de soporte](https://adapty.featurebase.app/) donde encontrarás respuestas a preguntas frecuentes o podrás plantear las tuyas. ¡Nuestro equipo y la comunidad están aquí para ayudarte!
Tu paywall está listo para mostrarse en la app. [Prueba tus compras en Google Play Store](testing-on-android) para asegurarte de que puedes completar una compra de prueba desde el paywall.
A continuación, debes [comprobar el nivel de acceso de los usuarios](android-check-subscription-status) para asegurarte de que muestras un paywall o das acceso a las funciones de pago a los usuarios correctos.
## Ejemplo completo \{#full-example\}
Aquí se muestra cómo integrar todos estos pasos en tu app.
```kotlin showLineNumbers title="Kotlin"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Adapty.getPaywall("YOUR_PLACEMENT_ID") { paywallResult ->
if (paywallResult is AdaptyResult.Success) {
val paywall = paywallResult.value
if (!paywall.hasViewConfiguration) {
// Use custom logic
return@getPaywall
}
AdaptyUI.getViewConfiguration(paywall) { configResult ->
if (configResult is AdaptyResult.Success) {
val viewConfiguration = configResult.value
val paywallView = AdaptyUI.getPaywallView(
this,
viewConfiguration,
null, // products = null means auto-fetch
object : AdaptyUIEventListener {
override fun onActionPerformed(action: AdaptyUI.Action, context: Context) {
when (action) {
is AdaptyUI.Action.Close -> {
(context as? Activity)?.onBackPressed()
}
}
}
}
)
setContentView(paywallView)
}
}
}
}
}
}
```
```java showLineNumbers
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Adapty.getPaywall("YOUR_PLACEMENT_ID", paywallResult -> {
if (paywallResult instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success) paywallResult).getValue();
if (!paywall.hasViewConfiguration()) {
// Use custom logic
return;
}
AdaptyUI.getViewConfiguration(paywall, configResult -> {
if (configResult instanceof AdaptyResult.Success) {
AdaptyUI.LocalizedViewConfiguration viewConfiguration =
((AdaptyResult.Success) configResult).getValue();
AdaptyPaywallView paywallView = AdaptyUI.getPaywallView(
this,
viewConfiguration,
null, // products = null means auto-fetch
new AdaptyUIEventListener() {
@Override
public void onActionPerformed(@NonNull AdaptyUI.Action action, @NonNull Context context) {
if (action instanceof AdaptyUI.Action.Close) {
if (context instanceof Activity) {
((Activity) context).onBackPressed();
}
}
}
}
);
setContentView(paywallView);
}
});
}
});
}
}
```
---
# File: android-check-subscription-status
---
---
title: "Comprobar el estado de la suscripción en el SDK de Android"
description: "Aprende cómo comprobar el estado de la suscripción en tu app de Android con Adapty."
---
Para decidir si los usuarios pueden acceder al contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil.
Este artículo muestra cómo acceder al estado del perfil para decidir qué deben ver los usuarios: si mostrarles un paywall o darles acceso a las funciones de pago.
## Obtener el estado de la suscripción \{#get-subscription-status\}
Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones:
- Llama a `getProfile` si necesitas los datos más recientes del perfil de inmediato (por ejemplo, al iniciar la app) o quieres forzar una actualización.
- Configura **actualizaciones automáticas del perfil** para mantener una copia local que se refresca automáticamente cada vez que cambia el estado de la suscripción.
### Obtener el perfil \{#get-profile\}
La forma más sencilla de obtener el estado de la suscripción es usar el método `getProfile` para acceder al perfil:
```kotlin showLineNumbers
Adapty.getProfile { result ->
when (result) {
is AdaptyResult.Success -> {
val profile = result.value
// check the access
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.getProfile(result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyProfile profile = ((AdaptyResult.Success) result).getValue();
// check the access
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
### Escuchar actualizaciones de la suscripción \{#listen-to-subscription-updates\}
Para recibir automáticamente actualizaciones del perfil en tu app:
1. Usa `Adapty.setOnProfileUpdatedListener()` para escuchar los cambios en el perfil: Adapty llamará automáticamente a este método cada vez que cambie el estado de la suscripción del usuario.
2. Guarda los datos del perfil actualizado cuando se llame a este método, para poder usarlos en toda tu app sin realizar peticiones de red adicionales.
```kotlin
class SubscriptionManager {
private var currentProfile: AdaptyProfile? = null
init {
// Listen for profile updates
Adapty.setOnProfileUpdatedListener { profile ->
currentProfile = profile
// Update UI, unlock content, etc.
}
}
// Use stored profile instead of calling getProfile()
fun hasAccess(): Boolean {
return currentProfile?.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true
}
}
```
```java
public class SubscriptionManager {
private AdaptyProfile currentProfile;
public SubscriptionManager() {
// Listen for profile updates
Adapty.setOnProfileUpdatedListener(profile -> {
this.currentProfile = profile;
// Update UI, unlock content, etc.
});
}
// Use stored profile instead of calling getProfile()
public boolean hasAccess() {
if (currentProfile == null) {
return false;
}
AdaptyAccessLevel premiumAccess = currentProfile.getAccessLevels().get("YOUR_ACCESS_LEVEL");
return premiumAccess != null && premiumAccess.isActive();
}
}
```
:::note
Adapty llama automáticamente al listener de actualización del perfil cuando tu app arranca, proporcionando datos de suscripción en caché incluso si el dispositivo está sin conexión.
:::
## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\}
Cuando necesitas tomar decisiones inmediatas sobre mostrar paywalls o conceder acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en situaciones como el inicio de la app, al entrar en secciones premium o antes de mostrar contenido específico.
```kotlin
private fun initializePaywall() {
loadPaywall { paywallView ->
checkAccessLevel { result ->
when (result) {
is AdaptyResult.Success -> {
if (!result.value && paywallView != null) {
setContentView(paywallView) // Show paywall if no access
}
}
is AdaptyResult.Error -> {
if (paywallView != null) {
setContentView(paywallView) // Show paywall if access check fails
}
}
}
}
}
}
private fun checkAccessLevel(callback: ResultCallback) {
Adapty.getProfile { result ->
when (result) {
is AdaptyResult.Success -> {
val hasAccess = result.value.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true
callback.onResult(AdaptyResult.Success(hasAccess))
}
is AdaptyResult.Error -> {
callback.onResult(AdaptyResult.Error(result.error))
}
}
}
}
```
```java
private void initializePaywall() {
loadPaywall(paywallView -> {
checkAccessLevel(result -> {
if (result instanceof AdaptyResult.Success) {
boolean hasAccess = ((AdaptyResult.Success) result).getValue();
if (!hasAccess && paywallView != null) {
setContentView(paywallView); // Show paywall if no access
}
} else if (result instanceof AdaptyResult.Error) {
if (paywallView != null) {
setContentView(paywallView); // Show paywall if access check fails
}
}
});
});
}
private void checkAccessLevel(ResultCallback callback) {
Adapty.getProfile(result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyProfile profile = ((AdaptyResult.Success) result).getValue();
AdaptyAccessLevel premiumAccess = profile.getAccessLevels().get("YOUR_ACCESS_LEVEL");
boolean hasAccess = premiumAccess != null && premiumAccess.isActive();
callback.onResult(AdaptyResult.success(hasAccess));
} else if (result instanceof AdaptyResult.Error) {
callback.onResult(AdaptyResult.error(((AdaptyResult.Error) result).getError()));
}
});
}
```
## Pasos siguientes \{#next-steps\}
Ahora que sabes cómo hacer seguimiento del estado de la suscripción, aprende a [trabajar con perfiles de usuario](android-quickstart-identify) para asegurarte de que los usuarios pueden acceder a lo que han pagado.
---
# File: android-quickstart-identify
---
---
title: "Identificar usuarios en el SDK de Android"
description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app en Android."
---
:::important
Esta guía es para ti si tienes tu propio sistema de autenticación. Aquí aprenderás a trabajar con perfiles de usuario en Adapty para asegurarte de que se alinee con tu sistema de autenticación existente.
:::
La forma en que gestionas las compras de los usuarios depende del modelo de autenticación de tu app:
- Si tu app no usa autenticación de backend y no almacena datos de usuario, consulta la [sección sobre usuarios anónimos](#anonymous-users).
- Si tu app tiene (o tendrá) autenticación de backend, consulta la [sección sobre usuarios identificados](#identified-users).
**Conceptos clave**:
- Los **perfiles** son las entidades necesarias para que funcione el SDK. Adapty los crea automáticamente.
- Pueden ser anónimos **(sin customer user ID)** o identificados **(con customer user ID)**.
- Tú proporcionas el **customer user ID** para cruzar los perfiles de Adapty con tu sistema de autenticación interno.
Estas son las diferencias entre usuarios anónimos e identificados:
| | Usuarios anónimos | Usuarios identificados |
|------------------------------|------------------------------------------------------------|--------------------------------------------------------------------------------------|
| **Gestión de compras** | Restauración de compras a nivel de store | Mantienen el historial de compras entre dispositivos gracias a su customer user ID |
| **Gestión de perfiles** | Nuevos perfiles en cada reinstalación | El mismo perfil en todas las sesiones y dispositivos |
| **Persistencia de datos** | Los datos de usuarios anónimos están ligados a la instalación de la app | Los datos de usuarios identificados persisten entre instalaciones de la app |
## Usuarios anónimos \{#anonymous-users\}
Si no tienes autenticación de backend, **no necesitas gestionar la autenticación en el código de la app**:
1. Cuando el SDK se activa en el primer arranque de la app, Adapty **crea un nuevo perfil para el usuario**.
2. Cuando el usuario compra algo en la app, esa compra queda **asociada a su perfil de Adapty y a su cuenta de la store**.
3. Cuando el usuario **reinstala** la app o la instala en un **nuevo dispositivo**, Adapty **crea un nuevo perfil anónimo al activarse**.
4. Si el usuario ya había realizado compras en tu app, por defecto se sincronizan automáticamente desde el App Store al activar el SDK.
Con usuarios anónimos se crean nuevos perfiles en cada instalación, pero eso no es un problema porque, en los análisis de Adapty, puedes [configurar qué se considera una nueva instalación](general#4-installs-definition-for-analytics).
Para usuarios anónimos, debes contar las instalaciones por **ID de dispositivo**. En este caso, cada instalación de la app en un dispositivo se cuenta como una instalación, incluidas las reinstalaciones.
## Usuarios identificados \{#identified-users\}
Tienes dos opciones para identificar usuarios en la app:
- [**Durante el login/registro:**](#during-loginsignup) Si los usuarios inician sesión después de que arranca tu app, llama a `identify()` con un customer user ID cuando se autentiquen.
- [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando se lanza la app, envíalo al llamar a `activate()`.
:::important
Por defecto, cuando Adapty recibe una compra de un Customer User ID que en ese momento está asociado a otro Customer User ID, el nivel de acceso se comparte, de modo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o desactivar completamente el uso compartido. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles.
:::
### Durante el login/registro \{#during-loginsignup\}
Si identificas a los usuarios después del arranque de la app (por ejemplo, tras iniciar sesión o registrarse), usa el método `identify` para establecer su customer user ID.
- Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual.
- Si **ya has usado este customer user ID para identificar al usuario antes**, Adapty pasará a trabajar con el perfil asociado a ese customer user ID.
:::important
Los customer user IDs deben ser únicos para cada usuario. Si escribes el valor del parámetro directamente en el código, todos los usuarios se considerarán como uno solo.
:::
Espera a que se ejecute el callback de finalización de `identify` antes de llamar a otros métodos del SDK. Las llamadas concurrentes pueden acabar en el perfil anónimo en lugar del identificado. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order).
```kotlin showLineNumbers
Adapty.identify("YOUR_USER_ID") { error -> // Unique for each user
if (error == null) {
// successful identify
}
}
```
```java showLineNumbers
// User IDs must be unique for each user
Adapty.identify("YOUR_USER_ID", error -> {
if (error == null) {
// successful identify
}
});
```
### Durante la activación del SDK \{#during-the-sdk-activation\}
Si ya conoces el customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado.
Si conoces el customer user ID pero lo estableces solo después de la activación, eso significa que, al activarse, Adapty creará un nuevo perfil anónimo y cambiará al existente solo cuando llames a `identify`.
Puedes pasar un customer user ID existente (uno que hayas usado antes) o uno nuevo. Si pasas uno nuevo, el perfil creado al activarse se vinculará automáticamente a ese customer user ID.
:::note
Por defecto, crear perfiles anónimos no afecta a los dashboards de análisis, porque las instalaciones se cuentan por ID de dispositivo.
Un ID de dispositivo representa una única instalación de la app desde la store en un dispositivo y se regenera solo cuando la app se reinstala.
No depende de si es una primera o repetida instalación, ni de si se usa un customer user ID existente.
Crear un perfil (al activar el SDK o al cerrar sesión), iniciar sesión o actualizar la app sin reinstalarla no genera eventos de instalación adicionales.
Si quieres contar instalaciones por usuarios únicos en lugar de por dispositivos, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics).
:::
```kotlin showLineNumbers
AdaptyConfig.Builder("PUBLIC_SDK_KEY")
.withCustomerUserId("user123") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one.
.build()
```
```java showLineNumbers
new AdaptyConfig.Builder("PUBLIC_SDK_KEY")
.withCustomerUserId("user123") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one.
.build();
```
### Cerrar sesión de usuarios \{#log-users-out\}
Si tienes un botón para cerrar la sesión de los usuarios, usa el método `logout`.
:::important
Cerrar la sesión de un usuario crea un nuevo perfil anónimo para ese usuario.
:::
```kotlin showLineNumbers
Adapty.logout { error ->
if (error == null) {
// successful logout
}
}
```
```java showLineNumbers
Adapty.logout(error -> {
if (error == null) {
// successful logout
}
});
```
:::info
Para volver a iniciar sesión de los usuarios en la app, usa el método `identify`.
:::
### Permitir compras sin iniciar sesión \{#allow-purchases-without-login\}
Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, debes asegurarte de que mantengan el acceso después de iniciar sesión:
1. Cuando un usuario sin sesión realiza una compra, Adapty la vincula a su ID de perfil anónimo.
2. Cuando el usuario inicia sesión en su cuenta, Adapty pasa a trabajar con su perfil identificado.
- Si es un customer user ID nuevo (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que se mantiene todo el historial de compras.
- Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), debes obtener el nivel de acceso actual después del cambio de perfil. Puedes llamar a [`getProfile`](android-check-subscription-status) justo después de la identificación, o [escuchar las actualizaciones de perfil](android-check-subscription-status) para que los datos se sincronicen automáticamente.
## Próximos pasos \{#next-steps\}
¡Enhorabuena! Has implementado la lógica de pago in-app en tu app. ¡Te deseamos mucho éxito con la monetización de tu app!
Para sacar aún más partido a Adapty, puedes explorar estos temas:
- [**Pruebas**](troubleshooting-test-purchases): Asegúrate de que todo funciona como se espera
- [**Onboardings**](android-onboardings): Engancha a los usuarios con onboardings e impulsa la retención
- [**Integraciones**](configuration): Integra con servicios de atribución de marketing y análisis con una sola línea de código
- [**Establecer atributos de perfil personalizados**](android-setting-user-attributes): Añade atributos personalizados a los perfiles de usuario y crea segmentos para lanzar pruebas A/B o mostrar distintos paywalls a diferentes usuarios
---
# File: adapty-cursor-android
---
---
title: "Integra Adapty en tu app Android con ayuda de IA"
description: "Una guía paso a paso para integrar Adapty en tu app Android usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA."
---
Esta página explica dos formas de integrar Adapty en tu app Android. Usa la skill de integración del SDK que encontrarás a continuación para un flujo automatizado de principio a fin, o sigue el recorrido manual más adelante.
## Usa la skill de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\}
La [skill adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza la integración completa: configuración del dashboard, instalación del SDK, paywall y verificación por etapas. El recorrido manual que aparece más abajo es la alternativa si tu herramienta no admite el formato Claude Skills.
**Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI.
### Instalación \{#install\}
Elige el formato para tu herramienta. La lista completa está en el [README de la skill](https://github.com/adaptyteam/adapty-sdk-integration-skill).
- **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y luego `claude plugin install adapty-sdk-integration@adapty` desde tu terminal.
- **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`.
- **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`.
- **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de skills de tu herramienta.
### Ejecución \{#run\}
En tu proyecto, ejecuta `/adapty-sdk-integration`. La skill detecta tu plataforma y hace algunas preguntas de configuración. Luego guía el proceso de configuración del dashboard, instalación del SDK, paywall y verificación — consultando la documentación de Adapty relevante en cada etapa.
:::note
La skill está en beta. Si se detiene o se comporta de forma inesperada, el recorrido manual que aparece a continuación cubre cada etapa paso a paso.
:::
## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\}
Adapty requiere cierta configuración en el dashboard antes de escribir código con el SDK. Puedes hacerlo con una skill de LLM interactiva o manualmente desde el Dashboard.
### Con la skill (recomendado) \{#skill-approach-recommended\}
La skill de la CLI de Adapty permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin necesidad de abrir el Dashboard en cada paso. Solo necesitas [conectar tu store](integrate-payments) desde el Dashboard.
```
npx skills add adaptyteam/adapty-cli --skill adapty-cli
```
Una vez añadida la skill, ejecuta `/adapty-cli` en tu agente. Te guiará en cada paso, incluido cuándo abrir el Dashboard para conectar tu store.
### Con el Dashboard \{#dashboard-approach\}
Si prefieres configurarlo todo manualmente, esto es lo que necesitas antes de escribir código. Tu LLM no puede consultar los valores del dashboard por ti — tendrás que proporcionárselos.
1. **Conecta tu store**: En el Adapty Dashboard, ve a **App settings → General**. Es obligatorio para que las compras funcionen.
[Conectar Google Play](integrate-payments)
2. **Copia tu Public SDK key**: En el Adapty Dashboard, ve a **App settings → General** y busca la sección **API keys**. En el código, es la cadena que pasas al constructor de configuración de Adapty.
3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No referenciarás los productos directamente en el código — Adapty los entrega a través de los paywalls.
[Añadir productos](quickstart-products)
4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `Adapty.getPaywall("YOUR_PLACEMENT_ID")`.
[Crear paywall](quickstart-paywalls)
5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena se comprueba en `profile.accessLevels["premium"]?.isActive`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago tienen acceso a diferentes funciones según el producto (por ejemplo, un plan `basic` frente a un plan `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar.
:::tip
Una vez que tengas los cinco, estás listo para escribir código. Dile a tu LLM: "Mi Public SDK key es X, mi placement ID es Y" para que pueda generar el código correcto de inicialización y obtención del paywall.
:::
### Configura cuando estés listo \{#set-up-when-ready\}
No son obligatorios para empezar a programar, pero los necesitarás a medida que tu integración madure:
- **Pruebas A/B**: Configúralas en la página **Placements**. No requieren cambios en el código.
[Pruebas A/B](ab-tests)
- **Paywalls y placements adicionales**: Añade más llamadas a `getPaywall` con diferentes IDs de placement.
- **Integraciones de analíticas**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta [integraciones de analíticas](analytics-integration) e [integraciones de atribución](attribution-integration).
## Dale documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\}
### Usa Context7 (recomendado) \{#use-context7-recommended\}
[Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. El LLM obtiene automáticamente la documentación correcta según lo que le preguntes — sin necesidad de pegar URLs manualmente.
Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta:
```
npx ctx7 setup
```
Esto detecta tu editor y configura el servidor Context7. Para la configuración manual, consulta el [repositorio de Context7 en GitHub](https://github.com/upstash/context7).
Una vez configurado, referencia la biblioteca de Adapty en tus prompts:
```
Use the adaptyteam/adapty-docs library to look up how to install the Android SDK
```
:::warning
Aunque Context7 elimina la necesidad de pegar enlaces a la documentación manualmente, el orden de implementación importa. Sigue el [recorrido de implementación](#implementation-walkthrough) que aparece a continuación paso a paso para asegurarte de que todo funcione.
:::
### Usa la documentación en texto plano \{#use-plain-text-docs\}
Puedes acceder a cualquier artículo de Adapty en texto plano Markdown. Añade `.md` al final de su URL o haz clic en **Copy for LLM** debajo del título del artículo. Por ejemplo: [adapty-cursor-android.md](https://adapty.io/docs/es/adapty-cursor-android.md).
Cada etapa del [recorrido de implementación](#implementation-walkthrough) incluye un bloque "Envía esto a tu LLM" con enlaces `.md` para pegar.
Para obtener más documentación a la vez, consulta los [archivos de índice y subconjuntos específicos por plataforma](#plain-text-doc-index-files) que aparecen más abajo.
## Recorrido de implementación \{#implementation-walkthrough\}
El resto de esta guía recorre la integración de Adapty en el orden de implementación. Cada etapa incluye la documentación que debes enviar a tu LLM, qué deberías ver al terminar y los problemas más comunes.
### Planifica tu integración \{#plan-your-integration\}
Antes de escribir código, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA admite un modo de planificación (como el modo plan de Cursor o Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir cualquier código.
Indícale a tu LLM qué enfoque usas para las compras — esto afecta a las guías que debe seguir:
- [**Adapty Paywall Builder**](adapty-paywall-builder): Creas los paywalls en el editor no-code de Adapty y el SDK los renderiza automáticamente.
- [**Paywalls creados manualmente**](android-making-purchases): Construyes tu propia interfaz de paywall en el código, pero sigues usando Adapty para obtener los productos y gestionar las compras.
- [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analíticas e integraciones.
¿No sabes cuál elegir? Lee la [tabla comparativa en la guía de inicio rápido](android-quickstart-paywalls).
### Instala y configura el SDK \{#install-and-configure-the-sdk\}
Añade la dependencia del SDK de Adapty mediante Gradle en Android Studio y actívalo con tu Public SDK key. Esta es la base — sin ella nada más funciona.
**Guía:** [Instalar y configurar el SDK de Adapty](sdk-installation-android)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/sdk-installation-android.md
```
:::tip[Punto de control]
- **Esperado:** La app compila y se ejecuta. Logcat muestra el log de activación de Adapty.
- **Problema frecuente:** "Public API key is missing" → comprueba que has reemplazado el placeholder con tu clave real de App settings.
:::
### Muestra los paywalls y gestiona las compras \{#show-paywalls-and-handle-purchases\}
Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras.
Prueba cada compra en sandbox a medida que avanzas — no esperes al final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración.
**Guías:**
- [Habilitar compras con paywalls (inicio rápido)](android-quickstart-paywalls)
- [Obtener paywalls del Paywall Builder y su configuración](android-get-pb-paywalls)
- [Mostrar paywalls](android-present-paywalls)
- [Gestionar eventos del paywall](android-handling-events)
- [Responder a acciones de botones](android-handle-paywall-actions)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/android-quickstart-paywalls.md
- https://adapty.io/docs/es/android-get-pb-paywalls.md
- https://adapty.io/docs/es/android-present-paywalls.md
- https://adapty.io/docs/es/android-handling-events.md
- https://adapty.io/docs/es/android-handle-paywall-actions.md
```
:::tip[Punto de control]
- **Esperado:** El paywall aparece con los productos configurados. Al pulsar un producto se activa el diálogo de compra en sandbox.
- **Problema frecuente:** Paywall vacío o error en `getPaywall` → verifica que el ID del placement coincide exactamente con el del dashboard y que el placement tiene una audiencia asignada.
:::
**Guías:**
- [Habilitar compras en tu paywall personalizado (inicio rápido)](android-quickstart-manual)
- [Obtener paywalls y productos](fetch-paywalls-and-products-android)
- [Renderizar un paywall diseñado con Remote Config](present-remote-config-paywalls-android)
- [Realizar compras](android-making-purchases)
- [Restaurar compras](android-restore-purchase)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/android-quickstart-manual.md
- https://adapty.io/docs/es/fetch-paywalls-and-products-android.md
- https://adapty.io/docs/es/present-remote-config-paywalls-android.md
- https://adapty.io/docs/es/android-making-purchases.md
- https://adapty.io/docs/es/android-restore-purchase.md
```
:::tip[Punto de control]
- **Esperado:** Tu paywall personalizado muestra los productos obtenidos de Adapty. Al pulsar un producto se activa el diálogo de compra en sandbox.
- **Problema frecuente:** Array de productos vacío → verifica que el paywall tiene productos asignados en el dashboard y que el placement tiene una audiencia.
:::
**Guías:**
- [Descripción general del modo Observer](observer-vs-full-mode)
- [Implementar el modo Observer](implement-observer-mode-android)
- [Informar transacciones en el modo Observer](report-transactions-observer-mode-android)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/observer-vs-full-mode.md
- https://adapty.io/docs/es/implement-observer-mode-android.md
- https://adapty.io/docs/es/report-transactions-observer-mode-android.md
```
:::tip[Punto de control]
- **Esperado:** Tras una compra en sandbox usando tu flujo de compras existente, la transacción aparece en el **Event Feed** del dashboard de Adapty.
- **Problema frecuente:** Sin eventos → verifica que estás reportando las transacciones a Adapty y que las notificaciones en tiempo real de Google Play Developer están configuradas.
:::
### Comprueba el estado de la suscripción \{#check-subscription-status\}
Tras una compra, comprueba el perfil del usuario para verificar si tiene un nivel de acceso activo y así restringir el contenido premium.
**Guía:** [Comprobar el estado de la suscripción](android-check-subscription-status)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/android-check-subscription-status.md
```
:::tip[Punto de control]
- **Esperado:** Tras una compra en sandbox, `profile.accessLevels["premium"]?.isActive` devuelve `true`.
- **Problema frecuente:** `accessLevels` vacío tras la compra → comprueba que el producto tiene un nivel de acceso asignado en el dashboard.
:::
### Identifica a los usuarios \{#identify-users\}
Vincula las cuentas de usuario de tu app con los perfiles de Adapty para que las compras persistan entre dispositivos.
:::important
Omite este paso si tu app no tiene autenticación.
:::
**Guía:** [Identificar usuarios](android-quickstart-identify)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/android-quickstart-identify.md
```
:::tip[Punto de control]
- **Esperado:** Tras llamar a `Adapty.identify("your-user-id")`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado.
- **Problema frecuente:** Llama a `identify` después de la activación pero antes de obtener los paywalls para evitar la atribución a perfiles anónimos.
:::
### Prepárate para el lanzamiento \{#prepare-for-release\}
Una vez que tu integración funcione en sandbox, revisa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción.
**Guía:** [Lista de verificación de lanzamiento](release-checklist)
Envía esto a tu LLM:
```
Read these Adapty docs before releasing:
- https://adapty.io/docs/es/release-checklist.md
```
:::tip[Punto de control]
- **Esperado:** Todos los elementos de la lista confirmados: conexión al store, notificaciones del servidor, flujo de compras, comprobaciones del nivel de acceso y requisitos de privacidad.
- **Problema frecuente:** Faltan las notificaciones en tiempo real de Google Play → configúralas en **App settings → Android SDK** o los eventos no aparecerán en el dashboard.
:::
## Archivos de índice de documentación en texto plano \{#plain-text-doc-index-files\}
Si necesitas darle a tu LLM un contexto más amplio más allá de páginas individuales, ofrecemos archivos de índice que listan o combinan toda la documentación de Adapty:
- [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con enlaces `.md`. Un [estándar emergente](https://llmstxt.org/) para hacer los sitios web accesibles a los LLMs. Ten en cuenta que para algunos agentes de IA (p. ej., ChatGPT) necesitarás descargar `llms.txt` y subirlo al chat como archivo.
- [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación de Adapty combinada en un único archivo. Muy grande — úsalo solo cuando necesites el panorama completo.
- Subconjuntos específicos de Android: [`android-llms.txt`](https://adapty.io/docs/es/android-llms.txt) y [`android-llms-full.txt`](https://adapty.io/docs/es/android-llms-full.txt): Subconjuntos específicos de la plataforma que ahorran tokens en comparación con el sitio completo.
---
# File: android-get-pb-paywalls
---
---
title: "Obtener paywalls del Paywall Builder y su configuración en el SDK de Android"
description: "Aprende a recuperar paywalls de PB en Adapty para un mejor control de suscripciones en tu app de Android."
---
Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación.
:::warning
El nuevo Paywall Builder funciona con el SDK de Android versión 3.0 o superior.
:::
Ten en cuenta que este tema hace referencia a paywalls personalizados con Paywall Builder. Si estás implementando tus paywalls manualmente, consulta el tema [Obtener paywalls y productos para paywalls con Remote Config en tu app móvil](fetch-paywalls-and-products-android).
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
Antes de empezar a mostrar paywalls en tu app móvil (haz clic para expandir)
1. [Crea tus productos](create-product) en el Adapty Dashboard.
2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard.
3. [Crea placements e incorpora tu paywall en ellos](create-placement) en el Adapty Dashboard.
4. Instala el [SDK de Adapty](sdk-installation-android) en tu app móvil.
## Obtener el paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\}
Si has [diseñado un paywall usando el Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Este tipo de paywall contiene tanto lo que se debe mostrar como la forma en que debe mostrarse. Aun así, necesitas obtener su ID a través del placement, su configuración de vista, y luego presentarlo en tu app móvil.
Para garantizar un rendimiento óptimo, es fundamental recuperar el paywall y su [configuración de vista](android-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dando tiempo suficiente para que las imágenes se descarguen antes de presentárselas al usuario.
Para obtener un paywall, usa el método `getPaywall`:
```kotlin showLineNumbers
...
Adapty.getPaywall("YOUR_PLACEMENT_ID", locale = "en", loadTimeout = 10.seconds) { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
// the requested paywall
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
...
Adapty.getPaywall("YOUR_PLACEMENT_ID", "en", TimeInterval.seconds(10), result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue();
// the requested paywall
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
Parámetros:
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. |
| **locale** | opcional
por defecto: `en`
| El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | Por defecto, el SDK intentará cargar datos del servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo irregular que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante limpieza manual.
El SDK de Adapty almacena paywalls localmente en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN sea inaccesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.
|
| **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local.
Ten en cuenta que en casos excepcionales este método puede exceder ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.
Para Android: puedes crear `TimeInterval` con funciones de extensión (como `5.seconds`, donde `.seconds` es de `import com.adapty.utils.seconds`), o `TimeInterval.seconds(5)`. Para no establecer ningún límite, usa `TimeInterval.INFINITE`.
|
Parámetros de respuesta:
| Parámetro | Descripción |
| :-------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Paywall | Un objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/) con una lista de IDs de productos, el identificador del paywall, Remote Config y otras propiedades. |
## Obtener la configuración de vista del paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\}
:::important
Asegúrate de activar el toggle **Show on device** en el Paywall Builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperar.
:::
Después de obtener el paywall, comprueba si incluye una `ViewConfiguration`, lo que indica que fue creado con Paywall Builder. Esto te indicará cómo mostrar el paywall. Si la `ViewConfiguration` está presente, trátalo como un paywall de Paywall Builder; si no, [manéjalo como un paywall de Remote Config](present-remote-config-paywalls).
Usa el método `getViewConfiguration` para cargar la configuración de vista.
```kotlin showLineNumbers
if (!paywall.hasViewConfiguration) {
// use your custom logic
return
}
AdaptyUI.getViewConfiguration(paywall, loadTimeout = 10.seconds) { result ->
when(result) {
is AdaptyResult.Success -> {
val viewConfiguration = result.value
// use loaded configuration
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
| Parámetro | Presencia | Descripción |
| :-------------- | :------------- | :----------------------------------------------------------- |
| **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. |
| **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local. Ten en cuenta que en casos excepcionales este método puede exceder ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente. |
Usa el método `getViewConfiguration` para cargar la configuración de vista.
```java showLineNumbers
if (!paywall.hasViewConfiguration()) {
// use your custom logic
return;
}
AdaptyUI.getViewConfiguration(paywall, TimeInterval.seconds(10), result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyUI.LocalizedViewConfiguration viewConfiguration =
((AdaptyResult.Success) result).getValue();
// use loaded configuration
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
| Parámetro | Presencia | Descripción |
| :----------------------- | :------------- | :----------------------------------------------------------- |
| **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. |
| **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el fallback local. Ten en cuenta que en casos excepcionales este método puede exceder ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente. |
:::note
Si usas varios idiomas, aprende cómo añadir una [localización en Paywall Builder](add-paywall-locale-in-adapty-paywall-builder) y cómo usar los códigos de idioma correctamente [aquí](android-localizations-and-locale-codes).
:::
Una vez cargado, [presenta el paywall](android-present-paywalls).
## Obtener un paywall para la audiencia predeterminada y cargarlo más rápido \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\}
Normalmente, los paywalls se obtienen casi de forma instantánea, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, si tienes muchas audiencias y paywalls y tus usuarios tienen una conexión a internet lenta, obtener un paywall puede tardar más de lo deseado. En esos casos, puede que quieras mostrar un paywall por defecto para garantizar una buena experiencia de usuario en lugar de no mostrar ninguno.
Para resolver esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es importante entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, tal como se detalla en la sección [Obtener el paywall diseñado con Paywall Builder](#fetch-paywall-designed-with-paywall-builder) anterior.
:::warning
Por qué recomendamos usar `getPaywall`
El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes:
- **Posibles problemas de compatibilidad hacia atrás**: si necesitas mostrar paywalls diferentes para distintas versiones de la app (actual y futuras), puedes encontrarte con dificultades. Tendrás que diseñar paywalls que sean compatibles con la versión actual (legacy) o asumir que los usuarios con esa versión pueden tener problemas con paywalls que no se renderizan.
- **Pérdida de segmentación**: todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluyendo la basada en países, atribución de marketing o tus propios atributos personalizados).
Si estás dispuesto a aceptar estos inconvenientes a cambio de una carga más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, utiliza `getPaywall` como se describe [arriba](#fetch-paywall-designed-with-paywall-builder).
:::
```kotlin showLineNumbers
Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", locale = "en") { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
// the requested paywall
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue();
// the requested paywall
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
:::note
El método `getPaywallForDefaultAudience` está disponible a partir del SDK de Android 2.11.3
:::
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. |
| **locale** | opcional
por defecto: `en`
| El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | Por defecto, el SDK intentará cargar datos del servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo irregular que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante limpieza manual.
|
## Personalizar assets \{#customize-assets\}
Para personalizar imágenes y vídeos en tu paywall, implementa los assets personalizados.
Las imágenes y vídeos hero tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de assets personalizado, apuntas a estos elementos por sus IDs y personalizas su comportamiento.
Para otras imágenes y vídeos, necesitas [establecer un ID personalizado](custom-media) en el dashboard de Adapty.
Por ejemplo, puedes:
- Mostrar una imagen o vídeo diferente a algunos usuarios.
- Mostrar una imagen de vista previa local mientras se carga la imagen principal remota.
- Mostrar una imagen de vista previa antes de reproducir un vídeo.
:::important
Para usar esta funcionalidad, actualiza el SDK de Android de Adapty a la versión 3.7.0 o superior.
:::
Aquí tienes un ejemplo de cómo puedes proporcionar assets personalizados mediante un diccionario simple:
```kotlin showLineNumbers
val customAssets = AdaptyCustomAssets.of(
"hero_image" to
AdaptyCustomImageAsset.remote(
url = "https://example.com/image.jpg",
preview = AdaptyCustomImageAsset.file(
FileLocation.fromAsset("images/hero_image_preview.png"),
)
),
"hero_video" to
AdaptyCustomVideoAsset.file(
FileLocation.fromResId(requireContext(), R.raw.custom_video),
preview = AdaptyCustomImageAsset.file(
FileLocation.fromResId(requireContext(), R.drawable.video_preview),
),
),
)
val paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
products,
eventListener,
insets,
customAssets,
)
```
:::note
Si un asset no se encuentra, el paywall volverá a su apariencia predeterminada.
:::
---
# File: android-present-paywalls
---
---
title: "Android - Presentar paywalls con el nuevo Paywall Builder"
description: "Aprende a presentar paywalls en Android para una monetización efectiva."
---
Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese paywall ya contiene tanto lo que se muestra como la forma en que se muestra.
:::warning
Esta guía es solo para **paywalls del nuevo Paywall Builder** que requieren SDK v3.0. El proceso para mostrar paywalls es diferente según si fueron diseñados con versiones anteriores del Paywall Builder, si son paywalls con Remote Config, o si usas el [modo Observer](observer-vs-full-mode).
- Para presentar **paywalls con Remote Config**, consulta [Renderizar paywalls diseñados con Remote Config](present-remote-config-paywalls).
- Para presentar **paywalls en modo Observer**, consulta [Android - Presentar paywalls de Paywall Builder en modo Observer](android-present-paywall-builder-paywalls-in-observer-mode)
:::
Para obtener el objeto `viewConfiguration` utilizado a continuación, consulta [Obtener paywalls de Paywall Builder y su configuración](android-get-pb-paywalls).
Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, llama al método `AdaptyUI.getPaywallView()` o crea el `AdaptyPaywallView` directamente:
```kotlin showLineNumbers
val paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
products,
eventListener,
insets,
personalizedOfferResolver,
tagResolver,
timerResolver,
)
```
```kotlin showLineNumbers
val paywallView =
AdaptyPaywallView(activity) // or retrieve it from xml
...
with(paywallView) {
showPaywall(
viewConfiguration,
products,
eventListener,
insets,
personalizedOfferResolver,
tagResolver,
timerResolver,
)
}
```
```java showLineNumbers
AdaptyPaywallView paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
products,
eventListener,
insets,
personalizedOfferResolver,
tagResolver,
timerResolver
);
```
```java showLineNumbers
AdaptyPaywallView paywallView =
new AdaptyPaywallView(activity); //añade a la jerarquía de vistas si es necesario, o recíbela desde xml
...
paywallView.showPaywall(viewConfiguration, products, eventListener, insets, personalizedOfferResolver, tagResolver, timerResolver);
```
```xml showLineNumbers
```
Después de crear la vista correctamente, puedes añadirla a la jerarquía de vistas y mostrarla en la pantalla del dispositivo.
Si obtienes `AdaptyPaywallView` _sin_ llamar a `AdaptyUI.getPaywallView()`, también tendrás que llamar al método `.showPaywall()`.
Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, utiliza esta función composable:
```kotlin showLineNumbers
AdaptyPaywallScreen(
viewConfiguration,
products,
eventListener,
insets,
personalizedOfferResolver,
tagResolver,
timerResolver,
)
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :---------------------------- | :------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **viewConfiguration** | obligatorio | Proporciona un objeto `AdaptyUI.LocalizedViewConfiguration` con los detalles visuales del paywall. Usa el método `Adapty.getViewConfiguration(paywall)` para cargarlo. Consulta el tema [Fetch the visual configuration of paywall](android-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) para más detalles. |
| **products** | opcional | Proporciona un array de `AdaptyPaywallProduct` para optimizar el momento en que se muestran los productos en pantalla. Si se pasa `null`, AdaptyUI obtendrá automáticamente los productos necesarios. |
| **eventListener** | opcional | Proporciona un `AdaptyUiEventListener` para observar los eventos del paywall. Se recomienda extender `AdaptyUiDefaultEventListener` para facilitar su uso. Consulta el tema [Handling paywall events](android-handling-events) para más detalles. |
| **insets** | opcional | Los insets son los espacios alrededor del paywall que evitan que los elementos interactivos queden ocultos detrás de las barras del sistema.
Por defecto: `UNSPECIFIED`, lo que significa que Adapty ajustará automáticamente los insets. Esto funciona bien para paywalls de borde a borde.
Si tu paywall no es de borde a borde, puede que quieras definir insets personalizados. Consulta la sección [Change paywall insets](android-present-paywalls#change-paywall-insets) más abajo para saber cómo hacerlo.
|
| **personalizedOfferResolver** | opcional | Para indicar precios personalizados ([más información](https://developer.android.com/google/play/billing/integrate#personalized-price)), implementa `AdaptyUiPersonalizedOfferResolver` y proporciona tu propia lógica que mapee `AdaptyPaywallProduct` a `true` si el precio del producto es personalizado, o a `false` en caso contrario. |
| **tagResolver** | opcional | Usa `AdaptyUiTagResolver` para resolver etiquetas personalizadas dentro del texto del paywall. Este resolver recibe un parámetro de etiqueta y lo resuelve a la cadena correspondiente. Consulta el tema [Custom tags in Paywall Builder](custom-tags-in-paywall-builder) para más detalles. |
| **timerResolver** | opcional | Pasa el resolver aquí si vas a usar funcionalidad de temporizador personalizado. |
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
## Cambiar los márgenes del paywall \{#change-paywall-insets\}
Los márgenes son los espacios alrededor del paywall que evitan que los elementos interactivos queden ocultos detrás de las barras del sistema. Por defecto, Adapty ajusta automáticamente estos márgenes, lo que funciona perfectamente para paywalls de borde a borde.
Si tu paywall no es de borde a borde, puede que quieras definir márgenes personalizados:
- Si ni la barra de estado ni la barra de navegación se superponen con `AdaptyPaywallView`, usa `AdaptyPaywallInsets.NONE`.
- Para configuraciones más personalizadas, como cuando tu paywall se superpone con la barra de estado superior pero no con la inferior, puedes establecer solo el `bottomInset` a `0`, como se muestra en el ejemplo a continuación:
```kotlin showLineNumbers
//create extension function
fun View.onReceiveSystemBarsInsets(action: (insets: Insets) -> Unit) {
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
val systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
ViewCompat.setOnApplyWindowInsetsListener(this, null)
action(systemBarInsets)
insets
}
}
//and then use it with the view
paywallView.onReceiveSystemBarsInsets { insets ->
val paywallInsets = AdaptyPaywallInsets.vertical(insets.top, 0)
paywallView.showPaywall(
viewConfiguration,
products,
eventListener,
paywallInsets,
personalizedOfferResolver,
tagResolver,
timerResolver,
)
}
```
```java showLineNumbers
...
ViewCompat.setOnApplyWindowInsetsListener(paywallView, (view, insets) -> {
Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());
ViewCompat.setOnApplyWindowInsetsListener(paywallView, null);
AdaptyPaywallInsets paywallInsets =
AdaptyPaywallInsets.of(systemBarInsets.top, 0);
paywallView.showPaywall(paywall, products, viewConfiguration, paywallInsets, productTitleResolver);
return insets;
});
```
## Usar temporizador definido por el desarrollador \{#use-developer-defined-timer\}
Para usar temporizadores definidos por el desarrollador en tu app, crea un objeto `timerResolver` —un diccionario o mapa que asocia temporizadores personalizados con los valores de cadena que los reemplazarán cuando se renderice el paywall. Aquí tienes un ejemplo:
```kotlin showLineNumbers
...
val customTimers = mapOf(
"CUSTOM_TIMER_NY" to Calendar.getInstance(TimeZone.getDefault()).apply { set(2025, 0, 1) }.time, // New Year 2025
)
val timerResolver = AdaptyUiTimerResolver { timerId ->
customTimers.getOrElse(timerId, { Date(System.currentTimeMillis() + 3600 * 1000L) /* in 1 hour */ } )
}
```
```java showLineNumbers
...
Map customTimers = new HashMap<>();
customTimers.put(
"CUSTOM_TIMER_NY",
new Calendar.Builder().setTimeZone(TimeZone.getDefault()).setDate(2025, 0, 1).build().getTime()
);
AdaptyUiTimerResolver timerResolver = new AdaptyUiTimerResolver() {
@NonNull
@Override
public Date timerEndAtDate(@NonNull String timerId) {
Date date = customTimers.get(timerId);
return date != null ? date : new Date(System.currentTimeMillis() + 3600 * 1000L); /* en 1 hora */
}
};
```
En este ejemplo, `CUSTOM_TIMER_NY` es el **Timer ID** del temporizador definido por el desarrollador que configuraste en el Adapty dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente el temporizador con el valor correcto, como `13d 09h 03m 34s` (calculado como la hora de fin del temporizador, por ejemplo el Año Nuevo, menos la hora actual).
## Usa etiquetas personalizadas \{#use-custom-tags\}
Para usar etiquetas personalizadas en tu app, crea un objeto `tagResolver`—un diccionario o mapa que asocia etiquetas personalizadas con los valores de cadena que las reemplazarán cuando se renderice el paywall. Aquí tienes un ejemplo:
```kotlin showLineNumbers
val customTags = mapOf("USERNAME" to "John")
val tagResolver = AdaptyUiTagResolver { tag -> customTags[tag] }
```
```java showLineNumbers
Map customTags = new HashMap<>();
customTags.put("USERNAME", "John");
AdaptyUiTagResolver tagResolver = customTags::get;
```
En este ejemplo, `USERNAME` es una etiqueta personalizada que introdujiste en el Adapty dashboard como ``. El `tagResolver` se encarga de que tu app reemplace dinámicamente esta etiqueta personalizada con el valor especificado, como `John`.
Te recomendamos crear y rellenar el `tagResolver` justo antes de mostrar tu paywall. Una vez listo, pásalo al método de AdaptyUI que uses para presentar el paywall.
## Cambiar el color del indicador de carga del paywall \{#change-paywall-loading-indicator-color\}
Puedes sobrescribir el color predeterminado del indicador de carga de la siguiente manera:
```xml showLineNumbers title = "XML"
```
---
# File: android-handle-paywall-actions
---
---
title: "Responder a acciones de botones en el SDK de Android"
description: "Gestiona las acciones de los botones del paywall en Android usando Adapty para una mejor monetización de la app."
---
Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente:
1. Añade un [botón en el Paywall Builder](paywall-buttons) y asígnale una acción predefinida o crea un ID de acción personalizado.
2. Escribe código en tu app para gestionar cada acción que hayas asignado.
Esta guía explica cómo gestionar acciones personalizadas y predefinidas en tu código.
:::warning
**Solo las compras, restauraciones, cierres de paywall y apertura de URLs se gestionan automáticamente.** Todas las demás acciones de botones requieren una implementación de respuesta adecuada en el código de la app.
:::
## Cerrar paywalls \{#close-paywalls\}
Para añadir un botón que cierre tu paywall:
1. En el Paywall Builder, añade un botón y asígnale la acción **Close**.
2. En el código de tu app, implementa un manejador para la acción `close` que descarte el paywall.
:::info
En el SDK de Android, la acción `close` cierra el paywall por defecto. Sin embargo, puedes sobreescribir este comportamiento en tu código si lo necesitas. Por ejemplo, cerrar un paywall podría abrir otro.
:::
```kotlin
override fun onActionPerformed(action: AdaptyUI.Action, context: Context) {
when (action) {
AdaptyUI.Action.Close -> (context as? Activity)?.onBackPressed() // default behavior
}
}
```
## Abrir URLs desde paywalls \{#open-urls-from-paywalls\}
:::tip
Si quieres añadir un grupo de enlaces (por ejemplo, términos de uso y restauración de compras), añade un elemento **Link** en el Paywall Builder y gestiónalo de la misma forma que los botones con la acción **Open URL**.
:::
Para añadir un botón que abra un enlace desde tu paywall (por ejemplo, **Términos de uso** o **Política de privacidad**):
1. En el Paywall Builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir.
2. En el código de tu app, implementa un manejador para la acción `openUrl` que abra la URL recibida en un navegador.
:::info
En el SDK de Android, la acción `openUrl` abre la URL por defecto. Sin embargo, puedes sobreescribir este comportamiento en tu código si lo necesitas.
:::
```kotlin
override fun onActionPerformed(action: AdaptyUI.Action, context: Context) {
when (action) {
is AdaptyUI.Action.OpenUrl -> {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(action.url)) // default behavior
context.startActivity(intent)
}
}
}
```
## Iniciar sesión en la app \{#log-into-the-app\}
Para añadir un botón que permita a los usuarios iniciar sesión en tu app:
1. En el Paywall Builder, añade un botón y asígnale la acción **Login**.
2. En el código de tu app, implementa un manejador para la acción `login` que identifique a tu usuario.
```kotlin
override fun onActionPerformed(action: AdaptyUI.Action, context: Context) {
when (action) {
AdaptyUI.Action.Login -> {
val intent = Intent(context, LoginActivity::class.java)
context.startActivity(intent)
}
}
}
```
## Gestionar acciones personalizadas \{#handle-custom-actions\}
Para añadir un botón que gestione cualquier otra acción:
1. En el Paywall Builder, añade un botón, asígnale la acción **Custom** y asígnale un ID.
2. En el código de tu app, implementa un manejador para el ID de acción que hayas creado.
Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall:
```kotlin
override fun onActionPerformed(action: AdaptyUI.Action, context: Context) {
when (action) {
is AdaptyUI.Action.Custom -> {
if (action.customId == "openNewPaywall") {
// Display another paywall
}
}
}
}
```
---
# File: android-handling-events
---
---
title: "Android - Gestionar eventos del paywall"
description: "Gestiona eventos de suscripción en Android de forma eficiente con las herramientas de seguimiento de eventos de Adapty."
---
:::important
Esta guía cubre la gestión de eventos para compras, restauraciones, selección de productos y renderizado del paywall. También debes implementar la gestión de botones (cerrar el paywall, abrir enlaces, etc.). Consulta nuestra [guía sobre la gestión de acciones de botones](android-handle-paywall-actions) para más detalles.
:::
Los paywalls configurados con el [Paywall Builder](adapty-paywall-builder) no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan algunos eventos a los que tu app puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.), así como notificaciones sobre acciones relacionadas con compras en el paywall. A continuación, aprende cómo responder a estos eventos.
:::warning
Esta guía es **exclusivamente para paywalls del nuevo Paywall Builder** que requieren el SDK de Adapty v3.0 o posterior.
:::
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
Si necesitas controlar o monitorizar los procesos que tienen lugar en la pantalla de compra, implementa los métodos de `AdaptyUiEventListener`.
Si quieres mantener el comportamiento predeterminado en algunos casos, puedes extender `AdaptyUiDefaultEventListener` y sobreescribir solo los métodos que desees cambiar.
A continuación se muestran los valores predeterminados de `AdaptyUiDefaultEventListener`.
### Eventos generados por el usuario \{#user-generated-events\}
#### Selección de producto \{#product-selection\}
Si se selecciona un producto para la compra (por el usuario o por el sistema), se invocará este método:
```kotlin showLineNumbers title="Kotlin"
public override fun onProductSelected(
product: AdaptyPaywallProduct,
context: Context,
) {}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
```
#### Compra iniciada \{#started-purchase\}
Si un usuario inicia el proceso de compra, se invocará este método:
```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseStarted(
product: AdaptyPaywallProduct,
context: Context,
) {}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
```
Este método no se invocará en el modo Observer. Consulta el tema [Android - Presentar paywalls del Paywall Builder en modo Observer](android-present-paywall-builder-paywalls-in-observer-mode) para más detalles.
#### Compra exitosa, cancelada o pendiente \{#successful-canceled-or-pending-purchase\}
Si la compra se realiza correctamente, se invocará este método:
```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseFinished(
purchaseResult: AdaptyPurchaseResult,
product: AdaptyPaywallProduct,
context: Context,
) {
if (purchaseResult !is AdaptyPurchaseResult.UserCanceled)
context.getActivityOrNull()?.onBackPressed()
}
```
Ejemplos de eventos (haz clic para expandir)
```javascript
// Successful purchase
{
"purchaseResult": {
"type": "Success",
"profile": {
"accessLevels": {
"premium": {
"id": "premium",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
}
}
},
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
// Cancelled purchase
{
"purchaseResult": {
"type": "UserCanceled"
},
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
// Pending purchase
{
"purchaseResult": {
"type": "Pending"
},
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
```
En ese caso, recomendamos cerrar la pantalla.
Este método no se invocará en el modo Observer. Consulta el tema [Android - Presentar paywalls del Paywall Builder en modo Observer](android-present-paywall-builder-paywalls-in-observer-mode) para más detalles.
#### Compra fallida \{#failed-purchase\}
Si una compra falla debido a un error, se invocará este método. Esto incluye errores de Google Play Billing (restricciones de pago, productos no válidos, fallos de red), fallos en la verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan `onPurchaseFinished` con un resultado cancelado, y los pagos pendientes no activan este método.
```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseFailure(
error: AdaptyError,
product: AdaptyPaywallProduct,
context: Context,
) {}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"error": {
"code": "purchase_failed",
"message": "Purchase failed due to insufficient funds",
"details": {
"underlyingError": "Insufficient funds in account"
}
},
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
```
Este método no se invocará en el modo Observer. Consulta el tema [Android - Presentar paywalls del Paywall Builder en modo Observer](android-present-paywall-builder-paywalls-in-observer-mode) para más detalles.
#### Navegación web de pago finalizada \{#finished-web-payment-navigation\}
Este método se invoca tras un intento de abrir un [web paywall](web-paywall) para un producto específico. Esto incluye tanto intentos de navegación exitosos como fallidos:
```kotlin showLineNumbers title="Kotlin"
public override fun onFinishWebPaymentNavigation(
product: AdaptyPaywallProduct?,
error: AdaptyError?,
context: Context,
) {}
```
**Parámetros:**
| Parámetro | Descripción |
|:------------|:---------------------------------------------------------------------------------------------------|
| **product** | Un `AdaptyPaywallProduct` para el que se abrió el web paywall. Puede ser `null`. |
| **error** | Un objeto `AdaptyError` si la navegación al web paywall falló; `null` si la navegación fue exitosa. |
Ejemplos de eventos (haz clic para expandir)
```javascript
// Successful navigation
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"error": null
}
// Failed navigation
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"error": {
"code": "web_navigation_failed",
"message": "Failed to open web paywall",
"details": {
"underlyingError": "Browser unavailable"
}
}
}
```
#### Restauración exitosa \{#successful-restore\}
Si la restauración de una compra se realiza correctamente, se invocará este método:
```kotlin showLineNumbers title="Kotlin"
public override fun onRestoreSuccess(
profile: AdaptyProfile,
context: Context,
) {}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"profile": {
"accessLevels": {
"premium": {
"id": "premium",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
},
"subscriptions": [
{
"vendorProductId": "premium_monthly",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
]
}
}
```
Recomendamos cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de la suscripción](android-listen-subscription-changes) para saber cómo comprobarlo.
#### Restauración fallida \{#failed-restore\}
Si `Adapty.restorePurchases()` falla, se invocará este método:
```kotlin showLineNumbers title="Kotlin"
public override fun onRestoreFailure(
error: AdaptyError,
context: Context,
) {}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
}
```
#### Actualizar suscripción \{#upgrade-subscription\}
Cuando un usuario intenta comprar una nueva suscripción mientras otra está activa, puedes controlar cómo debe gestionarse la nueva compra sobreescribiendo este método. Tienes dos opciones:
1. **Reemplazar la suscripción actual** por la nueva:
```kotlin showLineNumbers title="Kotlin"
public override fun onAwaitingPurchaseParams(
product: AdaptyPaywallProduct,
context: Context,
onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback,
): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked {
onPurchaseParamsReceived(
AdaptyPurchaseParameters.Builder()
.withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...))
.build()
)
return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked
}
```
2. **Mantener ambas suscripciones** (añadir la nueva por separado):
```kotlin showLineNumbers title="Kotlin"
public override fun onAwaitingPurchaseParams(
product: AdaptyPaywallProduct,
context: Context,
onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback,
): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked {
onPurchaseParamsReceived(AdaptyPurchaseParameters.Empty)
return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked
}
```
:::note
Si no sobreescribes este método, el comportamiento predeterminado es mantener ambas suscripciones activas (equivalente a usar `AdaptyPurchaseParameters.Empty`).
:::
También puedes establecer parámetros de compra adicionales si es necesario:
```kotlin
AdaptyPurchaseParameters.Builder()
.withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...)) // opcional - para reemplazar la suscripción actual
.withOfferPersonalized(true) // opcional - si se usan precios personalizados
.build()
```
Si se compra una nueva suscripción mientras otra sigue activa, sobreescribe este método para reemplazar la actual por la nueva. Si la suscripción activa debe permanecer activa y la nueva se añade por separado, llama a `onSubscriptionUpdateParamsReceived(null)`:
```kotlin showLineNumbers title="Kotlin"
public override fun onAwaitingSubscriptionUpdateParams(
product: AdaptyPaywallProduct,
context: Context,
onSubscriptionUpdateParamsReceived: SubscriptionUpdateParamsCallback,
) {
onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters(...))
}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"product": {
"vendorProductId": "premium_yearly",
"localizedTitle": "Premium Yearly",
"localizedDescription": "Premium subscription for 1 year",
"localizedPrice": "$99.99",
"price": 99.99,
"currencyCode": "USD"
},
"subscriptionUpdateParams": {
"replacementMode": "with_time_proration"
}
}
```
### Obtención de datos y renderizado \{#data-fetching-and-rendering\}
#### Errores al cargar productos \{#product-loading-errors\}
Si no pasas los productos durante la inicialización, AdaptyUI recuperará los objetos necesarios del servidor por sí solo. Si esta operación falla, AdaptyUI notificará el error invocando este método:
```kotlin showLineNumbers title="Kotlin"
public override fun onLoadingProductsFailure(
error: AdaptyError,
context: Context,
): Boolean = false
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"error": {
"code": "products_loading_failed",
"message": "Failed to load products from the server",
"details": {
"underlyingError": "Network timeout"
}
}
}
```
Si devuelves `true`, AdaptyUI repetirá la solicitud en 2 segundos.
#### Errores de renderizado \{#rendering-errors\}
Si se produce un error durante el renderizado de la interfaz, se notificará llamando a este método:
```kotlin showLineNumbers title="Kotlin"
public override fun onRenderingError(
error: AdaptyError,
context: Context,
) {}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"error": {
"code": "rendering_failed",
"message": "Failed to render paywall interface",
"details": {
"underlyingError": "Invalid paywall configuration"
}
}
}
```
En una situación normal, estos errores no deberían producirse, así que si te encuentras con alguno, comunícanoslo.
---
# File: android-use-fallback-paywalls
---
---
title: "Android - Usar paywalls de respaldo"
description: "Gestiona los casos en que los usuarios están sin conexión o los servidores de Adapty no están disponibles."
---
:::warning
Los paywalls de respaldo son compatibles con Android SDK v2.11 y versiones posteriores.
:::
Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet.
* **Si la aplicación no puede acceder a los servidores de Adapty:**
Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding.
* **Si la aplicación no puede acceder a internet:**
Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar.
:::important
Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty.
:::
## Configuración \{#configuration\}
1. Mueve el archivo de configuración de respaldo al directorio `assets` o `res/raw` de tu proyecto Android.
2. Llama al método `.setFallback` **antes** de obtener el paywall o el onboarding de destino.
```kotlin showLineNumbers
//if you put the 'android_fallback.json' file to the 'assets' directory
val location = FileLocation.fromAsset("android_fallback.json")
//or `FileLocation.fromAsset("/android_fallback.json")` if you placed it in a child folder of 'assets')
//if you put the 'android_fallback.json' file to the 'res/raw' directory
val location = FileLocation.fromResId(context, R.raw.android_fallback)
//you can also pass a file URI
val fileUri: Uri = //get Uri for the file with fallback paywalls
val location = FileLocation.fromFileUri(fileUri)
//pass the file location
Adapty.setFallback(location, callback)
```
```java showLineNumbers
//if you put the 'android_fallback.json' file to the 'assets' directory
FileLocation location = FileLocation.fromAsset("android_fallback.json");
//or `FileLocation.fromAsset("/android_fallback.json");` if you placed it in a child folder of 'assets')
//if you put the 'android_fallback.json' file to the 'res/raw' directory
FileLocation location = FileLocation.fromResId(context, R.raw.android_fallback);
//you can also pass a file URI
Uri fileUri = //get Uri for the file with fallback paywalls
FileLocation location = FileLocation.fromFileUri(fileUri);
//pass the file location
Adapty.setFallback(location, callback);
```
Parámetros:
| Parámetro | Descripción |
| :----------- | :----------------------------------------------------------- |
| **location** | El objeto [FileLocation](https://android.adapty.io/adapty/com.adapty.utils/-file-location/-companion/) para el archivo de configuración de respaldo |
---
# File: android-localizations-and-locale-codes
---
---
title: "Usar localizaciones y códigos de idioma en el SDK de Android"
description: "Gestiona las localizaciones de tu app y los códigos de idioma para llegar a una audiencia global (Android)."
---
## Por qué es importante \{#why-this-is-important\}
Hay varios escenarios en los que los códigos de idioma entran en juego; por ejemplo, cuando intentas obtener el paywall correcto para la localización actual de tu app.
Como los códigos de idioma son complejos y pueden variar de una plataforma a otra, nos basamos en un estándar interno para todas las plataformas que admitimos. Sin embargo, precisamente por esa complejidad, es muy importante que entiendas exactamente qué le estás enviando a nuestro servidor para obtener la localización correcta y qué ocurre después, de modo que siempre recibas lo que esperas.
## Estándar de códigos de idioma en Adapty \{#locale-code-standard-at-adapty\}
Para los códigos de idioma, Adapty utiliza una versión ligeramente modificada del [estándar BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag): cada código se compone de subetiquetas en minúsculas separadas por guiones. Algunos ejemplos: `en` (inglés), `pt-br` (portugués de Brasil), `zh` (chino simplificado), `zh-hant` (chino tradicional).
## Coincidencia de códigos de idioma \{#locale-code-matching\}
Cuando Adapty recibe una llamada del SDK con el código de idioma y empieza a buscar la localización correspondiente de un paywall, ocurre lo siguiente:
1. La cadena de idioma entrante se convierte a minúsculas y todos los guiones bajos (`_`) se reemplazan por guiones (`-`).
2. A continuación, buscamos la localización cuyo código de idioma coincida exactamente.
3. Si no se encuentra ninguna coincidencia, tomamos la subcadena antes del primer guion (`pt` para `pt-br`) y buscamos la localización que coincida.
4. Si tampoco se encuentra ninguna coincidencia, devolvemos la localización predeterminada `en`.
De este modo, un dispositivo iOS que envíe `'pt_BR'`, un dispositivo Android que envíe `pt-BR` y otro dispositivo que envíe `pt-br` obtendrán el mismo resultado.
## Implementar localizaciones: la forma recomendada \{#implementing-localizations-recommended-way\}
Si te estás preguntando sobre las localizaciones, probablemente ya estés trabajando con los archivos de cadenas localizadas de tu proyecto. En ese caso, te recomendamos añadir un par clave-valor con el código de idioma de Adapty correspondiente en cada uno de esos archivos. Luego, extrae el valor de esa clave al llamar a nuestro SDK, así:
```kotlin showLineNumbers
// 1. Modify your strings.xml files
/*
strings.xml - Spanish
*/
es
/*
strings.xml - Portuguese (Brazil)
*/
pt-br
// 2. Extract and use the locale code
val localeCode = context.getString(R.string.adapty_paywalls_locale)
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method
```
Así te aseguras de tener un control total sobre qué localización se recuperará para cada usuario de tu app.
## Implementar localizaciones: la otra forma \{#implementing-localizations-the-other-way\}
Puedes obtener resultados similares (aunque no idénticos) sin definir explícitamente códigos de idioma para cada localización. Esto implica extraer el código de idioma de otros objetos que proporciona tu plataforma, así:
```kotlin showLineNumbers
val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
context.resources.configuration.locales[0]
else
context.resources.configuration.locale
val localeCode = locale.toLanguageTag()
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method
```
Ten en cuenta que no recomendamos este enfoque, ya que es difícil predecir exactamente qué recibirá el servidor de Adapty.
Si aun así decides usarlo, asegúrate de haber cubierto todos los casos de uso relevantes.
---
# File: android-web-paywall
---
---
title: "Implementar web paywalls en Android SDK"
description: "Configura un web paywall para cobrar sin las comisiones y revisiones de Play Store."
---
:::important
Antes de empezar, asegúrate de haber [configurado tu web paywall en el dashboard](web-paywall) e instalado Adapty SDK versión 3.15 o posterior.
:::
## Abrir web paywalls \{#open-web-paywalls\}
Si estás trabajando con un paywall que desarrollaste tú mismo, necesitas gestionar los web paywalls mediante el método del SDK. El método `.openWebPaywall`:
1. Genera una URL única que permite a Adapty vincular un paywall concreto mostrado a un usuario específico con la página web a la que es redirigido.
2. Detecta cuando tus usuarios vuelven a la app y luego solicita `.getProfile` en intervalos cortos para determinar si los derechos de acceso del perfil se han actualizado.
De esta forma, si el pago fue exitoso y los derechos de acceso se actualizaron, la suscripción se activa en la app casi de inmediato.
:::note
Cuando los usuarios vuelvan a la app, actualiza la interfaz para reflejar los cambios del perfil. Adapty recibirá y procesará los eventos de actualización del perfil.
:::
```kotlin showLineNumbers
Adapty.openWebPaywall(
activity = activity,
product = product,
) { error ->
if (error == null) {
// the web paywall was opened successfully
} else {
// handle the error
}
}
```
:::note
Existen dos versiones del método `openWebPaywall`:
1. `openWebPaywall(product)`, que genera las URLs por paywall y también añade los datos del producto a las URLs.
2. `openWebPaywall(paywall)`, que genera las URLs por paywall sin añadir los datos del producto a las URLs. Úsalo cuando tus productos en el paywall de Adapty sean distintos a los del web paywall.
:::
## Abrir web paywalls en un navegador in-app \{#open-web-paywalls-in-an-in-app-browser\}
Por defecto, los web paywalls se abren en el navegador externo.
Para ofrecer una experiencia de usuario fluida, puedes abrir los web paywalls en un navegador in-app. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin cambiar de app.
Para habilitarlo, establece el parámetro `presentation` en `AdaptyWebPresentation.InAppBrowser`:
```kotlin showLineNumbers
Adapty.openWebPaywall(
activity = activity,
product = product,
presentation = AdaptyWebPresentation.InAppBrowser,
) { error ->
if (error == null) {
// the web paywall was opened successfully
} else {
// handle the error
val adaptyError = error
}
}
```
---
# File: android-troubleshoot-paywall-builder
---
---
title: "Solucionar problemas del Paywall Builder en el SDK de Android"
description: "Solucionar problemas del Paywall Builder en el SDK de Android"
---
Esta guía te ayuda a resolver problemas comunes al usar paywalls diseñados en el Paywall Builder de Adapty en el SDK de Android.
## La obtención de la configuración del paywall falla \{#getting-a-paywall-configuration-fails\}
**Problema**: El método `getViewConfiguration` no puede recuperar la configuración del paywall.
**Causa**: El paywall no está habilitado para mostrarse en el dispositivo en el Paywall Builder.
**Solución**: Activa el interruptor **Show on device** en el Paywall Builder.
## El número de vistas del paywall es demasiado alto \{#the-paywall-view-number-is-too-big\}
**Problema**: El contador de vistas del paywall muestra el doble del número esperado.
**Causa**: Es posible que estés llamando a `logShowPaywall` en tu código, lo que duplica el contador de vistas si estás usando el Paywall Builder. Para los paywalls diseñados con el Paywall Builder, las analíticas se registran automáticamente, por lo que no necesitas usar este método.
**Solución**: Asegúrate de no llamar a `logShowPaywall` en tu código si estás usando el Paywall Builder.
## Otros problemas \{#other-issues\}
**Problema**: Estás experimentando otros problemas relacionados con el Paywall Builder que no se tratan más arriba.
**Solución**: Migra el SDK a la versión más reciente usando las [guías de migración](android-sdk-migration-guides) si es necesario. Muchos problemas se resuelven en versiones más recientes del SDK.
---
# File: android-quickstart-manual
---
---
title: "Habilitar compras en tu paywall personalizado en Android SDK"
description: "Integra el SDK de Adapty en tus paywalls personalizados de Android para habilitar las compras in-app."
---
Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall, mientras el SDK de Adapty obtiene los productos, gestiona las nuevas compras y restaura las anteriores.
:::important
**Esta guía es para desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Adapty Paywall Builder](android-quickstart-paywalls). Con Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compras automáticamente y puedes probar distintos diseños sin volver a publicar tu app.
:::
## Antes de empezar \{#before-you-start\}
### Configurar productos \{#set-up-products\}
Para habilitar las compras in-app, necesitas entender tres conceptos clave:
- [**Productos**](product) – todo lo que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida)
- [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de obtener productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app.
- [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para los placements en el dashboard y luego los solicitas por ID de placement en tu código. Esto facilita ejecutar pruebas A/B y mostrar diferentes paywalls a distintos usuarios.
Asegúrate de entender estos conceptos aunque trabajes con tu paywall personalizado. Básicamente, son tu forma de gestionar los productos que vendes en tu app.
Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite obtener tus productos. Para entender qué debes hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart).
### Gestionar usuarios \{#manage-users\}
Puedes trabajar con o sin autenticación de backend en tu lado.
Sin embargo, el SDK de Adapty gestiona de forma diferente a los usuarios anónimos e identificados. Lee la [guía de inicio rápido de identificación](android-quickstart-identify) para entender las particularidades y asegurarte de trabajar correctamente con los usuarios.
## Paso 1. Obtener productos \{#step-1-get-products\}
Para obtener los productos de tu paywall personalizado, necesitas:
1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`.
2. Obtener el array de productos para este paywall usando el método `getPaywallProducts`.
```kotlin showLineNumbers
fun loadPaywall() {
Adapty.getPaywall("YOUR_PLACEMENT_ID") { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
Adapty.getPaywallProducts(paywall) { productResult ->
when (productResult) {
is AdaptyResult.Success -> {
val products = productResult.value
// Use products to build your custom paywall UI
}
is AdaptyResult.Error -> {
val error = productResult.error
// Handle the error
}
}
}
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
}
```
```java showLineNumbers
public void loadPaywall() {
Adapty.getPaywall("YOUR_PLACEMENT_ID", result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue();
Adapty.getPaywallProducts(paywall, productResult -> {
if (productResult instanceof AdaptyResult.Success) {
List products = ((AdaptyResult.Success>) productResult).getValue();
// Use products to build your custom paywall UI
} else if (productResult instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) productResult).getError();
// Handle the error
}
});
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// Handle the error
}
});
}
```
## Paso 2. Aceptar compras \{#step-2-accept-purchases\}
Cuando un usuario pulsa un producto en tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado.
```kotlin showLineNumbers
fun purchaseProduct(activity: Activity, product: AdaptyPaywallProduct) {
Adapty.makePurchase(activity, product) { result ->
when (result) {
is AdaptyResult.Success -> {
when (val purchaseResult = result.value) {
is AdaptyPurchaseResult.Success -> {
val profile = purchaseResult.profile
// Purchase successful, profile updated
}
is AdaptyPurchaseResult.UserCanceled -> {
// User canceled the purchase
}
is AdaptyPurchaseResult.Pending -> {
// Purchase is pending (e.g., user will pay offline with cash)
}
}
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
}
```
```java showLineNumbers
public void purchaseProduct(Activity activity, AdaptyPaywallProduct product) {
Adapty.makePurchase(activity, product, null, result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPurchaseResult purchaseResult = ((AdaptyResult.Success) result).getValue();
if (purchaseResult instanceof AdaptyPurchaseResult.Success) {
AdaptyProfile profile = ((AdaptyPurchaseResult.Success) purchaseResult).getProfile();
// Purchase successful, profile updated
} else if (purchaseResult instanceof AdaptyPurchaseResult.UserCanceled) {
// User canceled the purchase
} else if (purchaseResult instanceof AdaptyPurchaseResult.Pending) {
// Purchase is pending (e.g., user will pay offline with cash)
}
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// Handle the error
}
});
}
```
## Paso 3. Restaurar compras \{#step-3-restore-purchases\}
Google Play y otras tiendas de aplicaciones exigen que todas las apps con suscripciones ofrezcan una forma de que los usuarios puedan restaurar sus compras.
Llama al método `restorePurchases` cuando el usuario pulse el botón de restaurar. Esto sincronizará su historial de compras con Adapty y devolverá el perfil actualizado.
```kotlin showLineNumbers
fun restorePurchases() {
Adapty.restorePurchases { result ->
when (result) {
is AdaptyResult.Success -> {
val profile = result.value
// Restore successful, profile updated
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
}
```
```java showLineNumbers
public void restorePurchases() {
Adapty.restorePurchases(result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyProfile profile = ((AdaptyResult.Success) result).getValue();
// Restore successful, profile updated
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// Handle the error
}
});
}
```
## Siguientes pasos \{#next-steps\}
---
no_index: true
---
import Callout from '../../../components/Callout.astro';
¿Tienes preguntas o estás teniendo algún problema? Consulta nuestro [foro de soporte](https://adapty.featurebase.app/) donde encontrarás respuestas a preguntas frecuentes o podrás plantear las tuyas. ¡Nuestro equipo y la comunidad están aquí para ayudarte!
Tu paywall ya está listo para mostrarse en la app. [Prueba tus compras en Google Play Store](testing-on-android) para asegurarte de que puedes completar una compra de prueba desde el paywall. Para ver cómo funciona en una implementación lista para producción, consulta [ProductListFragment.kt](https://github.com/adaptyteam/AdaptySDK-Android/blob/master/app/src/main/java/com/adapty/example/ProductListFragment.kt) en nuestra app de ejemplo, que muestra el manejo de compras con gestión adecuada de errores, retroalimentación en la interfaz y administración de suscripciones.
A continuación, [comprueba si los usuarios han completado su compra](android-check-subscription-status) para determinar si mostrar el paywall o conceder acceso a las funciones de pago.
---
# File: fetch-paywalls-and-products-android
---
---
title: "Obtener paywalls y productos para paywalls de Remote Config en el SDK de Android"
description: "Obtén paywalls y productos en el SDK de Android de Adapty para mejorar la monetización de usuarios."
---
Antes de mostrar paywalls de Remote Config y personalizadas, necesitas obtener la información sobre ellas. Ten en cuenta que este tema hace referencia a paywalls de Remote Config y personalizadas. Para obtener orientación sobre cómo obtener paywalls configuradas con Paywall Builder, consulta [Obtener paywalls del Paywall Builder y su configuración](android-get-pb-paywalls).
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
Antes de empezar a obtener paywalls y productos en tu app (haz clic para expandir)
1. [Crea tus productos](create-product) en el Adapty Dashboard.
2. [Crea un paywall e incorpora los productos en tu paywall](create-paywall) en el Adapty Dashboard.
3. [Crea placements e incorpora tu paywall en el placement](create-placement) en el Adapty Dashboard.
4. [Instala el SDK de Adapty](sdk-installation-android) en tu app.
## Obtener información del paywall \{#fetch-paywall-information\}
En Adapty, un [producto](product) es una combinación de productos tanto de App Store como de Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos dentro de placements específicos de tu app.
Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`.
:::important
**No escribas los IDs de producto en el código.** El único ID que debes escribir directamente en el código es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe gestionar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana devuelve tres, debes mostrarlos todos sin modificar el código.
:::
```kotlin showLineNumbers
Adapty.getPaywall("YOUR_PLACEMENT_ID", locale = "en") { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
// the requested paywall
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.getPaywall("YOUR_PLACEMENT_ID", "en", result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue();
// the requested paywall
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. |
| **locale** | opcional
por defecto: `en`
| El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto de una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](android-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos utilizarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | Por defecto, el SDK intentará cargar datos del servidor y devolverá los datos en caché en caso de error. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.
El SDK de Adapty almacena los paywalls en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](android-use-fallback-paywalls). También usamos CDN para obtener paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.
|
| **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera de este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el respaldo local.
Ten en cuenta que en casos excepcionales este método puede agotar el tiempo de espera un poco más tarde de lo especificado en `loadTimeout`, ya que la operación puede consistir en diferentes solicitudes internamente.
|
¡No escribas los IDs de producto en el código! Como los paywalls se configuran de forma remota, los productos disponibles, el número de productos y las ofertas especiales (como pruebas gratuitas) pueden cambiar con el tiempo. Asegúrate de que tu código gestione estos escenarios.
Por ejemplo, si inicialmente obtienes 2 productos, tu app debería mostrar esos 2 productos. Sin embargo, si más adelante obtienes 3 productos, tu app debería mostrar los 3 sin necesidad de cambios en el código. Lo único que debes escribir directamente en el código es el ID del placement.
Parámetros de respuesta:
| Parámetro | Descripción |
| :-------- | :----------- |
| Paywall | Un objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/) con: una lista de IDs de producto, el identificador del paywall, Remote Config y varias otras propiedades. |
## Obtener productos \{#fetch-products\}
Una vez que tienes el paywall, puedes consultar el array de productos que le corresponde:
```kotlin showLineNumbers
Adapty.getPaywallProducts(paywall) { result ->
when (result) {
is AdaptyResult.Success -> {
val products = result.value
// the requested products
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.getPaywallProducts(paywall, result -> {
if (result instanceof AdaptyResult.Success) {
List products = ((AdaptyResult.Success>) result).getValue();
// the requested products
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
Parámetros de respuesta:
| Parámetro | Descripción |
| :-------- | :----------- |
| Products | Lista de objetos [`AdaptyPaywallProduct`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall-product/) con: identificador del producto, nombre del producto, precio, moneda, duración de la suscripción y varias otras propiedades. |
Al implementar tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall-product/). A continuación se muestran las propiedades más utilizadas, pero consulta el documento enlazado para obtener detalles completos sobre todas las propiedades disponibles.
| Propiedad | Descripción |
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Title** | Para mostrar el título del producto, usa `product.localizedTitle`. Ten en cuenta que la localización se basa en el país de la store seleccionado por el usuario, no en el idioma del dispositivo. |
| **Price** | Para mostrar una versión localizada del precio, usa `product.price.localizedString`. Esta localización se basa en la información de idioma del dispositivo. También puedes acceder al precio como número usando `product.price.amount`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda asociado, usa `product.price.currencySymbol`. |
| **Subscription Period** | Para mostrar el período (p. ej., semana, mes, año, etc.), usa `product.subscriptionDetails?.localizedSubscriptionPeriod`. Esta localización se basa en el idioma del dispositivo. Para obtener el período de suscripción de forma programática, usa `product.subscriptionDetails?.subscriptionPeriod`. Desde ahí puedes acceder al enum `unit` para obtener la duración (es decir, DAY, WEEK, MONTH, YEAR, o UNKNOWN). El valor `numberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral, verás `MONTH` en la propiedad unit y `3` en la propiedad numberOfUnits. |
| **Introductory Offer** | Para mostrar un distintivo u otro indicador de que una suscripción incluye una oferta introductoria, consulta la propiedad `product.subscriptionDetails?.introductoryOfferPhases`. Es una lista que puede contener hasta dos fases de descuento: la fase de prueba gratuita y la fase de precio introductorio. Dentro de cada objeto de fase están las siguientes propiedades útiles:
• `paymentMode`: un enum con los valores `FREE_TRIAL`, `PAY_AS_YOU_GO`, `PAY_UPFRONT` y `UNKNOWN`. Las pruebas gratuitas serán del tipo `FREE_TRIAL`.
• `price`: el precio con descuento como número. Para las pruebas gratuitas, busca `0` aquí.
• `localizedNumberOfPeriods`: una cadena localizada usando el idioma del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `3 days` en este campo.
• `subscriptionPeriod`: alternativamente, puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que la sección anterior describe.
• `localizedSubscriptionPeriod`: un período de suscripción formateado del descuento para el idioma del usuario. |
## Acelera la obtención de paywalls con el paywall de audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\}
Normalmente, los paywalls se obtienen casi al instante, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos donde tienes numerosas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseado. En estas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún paywall.
Para solucionar esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener información del paywall](fetch-paywalls-and-products-android#fetch-paywall-information) anterior.
:::warning
Por qué recomendamos usar `getPaywall`
El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes:
- **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar diferentes paywalls para distintas versiones de la app (actual y futuras), puede que encuentres problemas. Tendrás que diseñar paywalls que sean compatibles con la versión actual (legacy) o aceptar que los usuarios con la versión actual (legacy) puedan encontrar problemas con paywalls que no se renderizan.
- **Pérdida de targeting**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes el targeting personalizado (incluido el basado en países, atribución de marketing o tus propios atributos personalizados).
Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención de paywalls más rápida, usa el método `getPaywallForDefaultAudience` tal como se indica a continuación. De lo contrario, sigue usando el método `getPaywall` descrito [anteriormente](fetch-paywalls-and-products-android#fetch-paywall-information).
:::
```kotlin showLineNumbers
Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", locale = "en") { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
// the requested paywall
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue();
// the requested paywall
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
:::note
El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.3 del SDK de Android.
:::
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. |
| **locale** | opcional
por defecto: `en`
| El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto de una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](android-localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos utilizarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | Por defecto, el SDK intentará cargar datos del servidor y devolverá los datos en caché en caso de error. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.
|
---
# File: present-remote-config-paywalls-android
---
---
title: "Renderizar un paywall diseñado con Remote Config en Android SDK"
description: "Descubre cómo presentar paywalls de Remote Config en el SDK de Android de Adapty para personalizar la experiencia del usuario."
---
Si has personalizado un paywall usando Remote Config, tendrás que implementar el renderizado en el código de tu app móvil para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú controlas qué se incluye y cómo se ve tu paywall. Te proporcionamos un método para obtener la configuración remota, dándote autonomía para mostrar tu paywall personalizado configurado mediante Remote Config.
## Obtener el Remote Config del paywall y presentarlo \{#get-paywall-remote-config-and-present-it\}
Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores necesarios.
```kotlin showLineNumbers
Adapty.getPaywall("YOUR_PLACEMENT_ID") { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
val headerText = paywall.remoteConfig?.dataMap?.get("header_text") as? String
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.getPaywall("YOUR_PLACEMENT_ID", result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success) result).getValue();
AdaptyPaywall.RemoteConfig remoteConfig = paywall.getRemoteConfig();
if (remoteConfig != null) {
if (remoteConfig.getDataMap().get("header_text") instanceof String) {
String headerText = (String) remoteConfig.getDataMap().get("header_text");
}
}
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
En este punto, una vez que hayas recibido todos los valores necesarios, es el momento de renderizarlos y ensamblarlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a distintas pantallas y orientaciones de teléfonos móviles, ofreciendo una experiencia fluida y cómoda en todos los dispositivos.
:::warning
Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls-android#track-paywall-view-events) tal como se describe a continuación, para que Adapty Analytics capture la información de embudos y pruebas A/B.
:::
Una vez que hayas terminado de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.makePurchase()` con el producto de tu paywall. Para más detalles sobre el método `.makePurchase()`, consulta [Realizar compras](android-making-purchases).
Te recomendamos [crear un paywall de respaldo llamado fallback paywall](android-use-fallback-paywalls). Este paywall de respaldo se mostrará al usuario cuando no haya conexión a internet ni caché disponible, garantizando una experiencia fluida incluso en esas situaciones.
## Registrar eventos de visualización del paywall \{#track-paywall-view-events\}
Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque los datos de compras se recopilan automáticamente, registrar las visualizaciones de paywalls requiere tu intervención, ya que solo tú sabes cuándo un usuario ve un paywall.
Para registrar un evento de visualización de paywall, simplemente llama a `.logShowPaywall(paywall)`, y se reflejará en las métricas de tu paywall en embudos y pruebas A/B.
:::important
No es necesario llamar a `.logShowPaywall(paywall)` si estás mostrando paywalls creados en el [Paywall Builder](adapty-paywall-builder).
:::
```kotlin showLineNumbers
Adapty.logShowPaywall(paywall)
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :---------- | :------- |:------------------------------------------------------------------------------------------------------------|
| **paywall** | obligatorio | Un objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
---
# File: android-making-purchases
---
---
title: "Realizar compras in-app en la app para Android SDK"
description: "Guía para gestionar compras in-app y suscripciones con Adapty."
---
Mostrar paywalls en tu app es un paso esencial para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, con solo presentar estos paywalls es suficiente para gestionar las compras únicamente si usas [Paywall Builder](adapty-paywall-builder) para personalizar tus paywalls.
Si no usas el Paywall Builder, debes utilizar un método independiente llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método es la puerta de entrada para que los usuarios interactúen con los paywalls y procedan con sus transacciones.
Si tu paywall tiene una oferta promocional activa para el producto que el usuario intenta comprar, Adapty la aplicará automáticamente en el momento de la compra.
:::warning
Ten en cuenta que la oferta introductoria solo se aplicará automáticamente si usas paywalls configurados con el Paywall Builder.
En otros casos, deberás [verificar la elegibilidad del usuario para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Saltarte este paso puede provocar que tu app sea rechazada durante la publicación. Además, podría generar cargos al precio completo a usuarios que son elegibles para una oferta introductoria.
:::
Asegúrate de haber completado la [configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras.
## Realizar una compra \{#make-purchase\}
:::note
**¿Usas [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente: puedes saltarte este paso.
**¿Buscas una guía paso a paso?** Consulta la [guía de inicio rápido](android-implement-paywalls-manually) para instrucciones de implementación completas con todo el contexto.
:::
```kotlin showLineNumbers
Adapty.makePurchase(activity, product, null) { result ->
when (result) {
is AdaptyResult.Success -> {
when (val purchaseResult = result.value) {
is AdaptyPurchaseResult.Success -> {
val profile = purchaseResult.profile
if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) {
// Grant access to the paid features
}
}
is AdaptyPurchaseResult.UserCanceled -> {
// Handle the case where the user canceled the purchase
}
is AdaptyPurchaseResult.Pending -> {
// Handle deferred purchases (e.g., the user will pay offline with cash)
}
}
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
```
```java showLineNumbers
Adapty.makePurchase(activity, product, null, result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPurchaseResult purchaseResult = ((AdaptyResult.Success) result).getValue();
if (purchaseResult instanceof AdaptyPurchaseResult.Success) {
AdaptyProfile profile = ((AdaptyPurchaseResult.Success) purchaseResult).getProfile();
AdaptyProfile.AccessLevel premium = profile.getAccessLevels().get("YOUR_ACCESS_LEVEL");
if (premium != null && premium.isActive()) {
// Grant access to the paid features
}
} else if (purchaseResult instanceof AdaptyPurchaseResult.UserCanceled) {
// Handle the case where the user canceled the purchase
} else if (purchaseResult instanceof AdaptyPurchaseResult.Pending) {
// Handle deferred purchases (e.g., the user will pay offline with cash)
}
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// Handle the error
}
});
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :---------- | :-------- | :-------------------------------------------------------------------------------------------------- |
| **Product** | requerido | Un objeto [`AdaptyPaywallProduct`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall-product/) obtenido del paywall. |
Parámetros de la respuesta:
| Parámetro | Descripción |
|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Profile** | Si la solicitud se realizó correctamente, la respuesta contiene este objeto. Un objeto [AdaptyProfile](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/) proporciona información completa sobre los niveles de acceso, suscripciones y compras no recurrentes de un usuario dentro de la app.
Comprueba el estado del nivel de acceso para determinar si el usuario tiene el acceso requerido a la app.
|
:::warning
**Nota:** si aún usas una versión de StoreKit de Apple inferior a v2.0 y una versión del SDK de Adapty inferior a v.2.9.0, debes proporcionar el [secreto compartido de App Store de Apple](app-store-connection-configuration#step-5-enter-app-store-shared-secret). Apple ha deprecado este método.
:::
## Cambiar la suscripción al realizar una compra \{#change-subscription-when-making-a-purchase\}
Cuando un usuario elige una nueva suscripción en lugar de renovar la actual, el funcionamiento depende del store. En Google Play, la suscripción no se actualiza automáticamente. Tendrás que gestionar el cambio en el código de tu app tal como se describe a continuación.
Para reemplazar una suscripción por otra en Android, llama al método `.makePurchase()` con el parámetro adicional:
```kotlin showLineNumbers
Adapty.makePurchase(
activity,
product,
AdaptyPurchaseParameters.Builder()
.withSubscriptionUpdateParams(subscriptionUpdateParams)
.build()
) { result ->
when (result) {
is AdaptyResult.Success -> {
when (val purchaseResult = result.value) {
is AdaptyPurchaseResult.Success -> {
val profile = purchaseResult.profile
// successful cross-grade
}
is AdaptyPurchaseResult.UserCanceled -> {
// user canceled the purchase flow
}
is AdaptyPurchaseResult.Pending -> {
// the purchase has not been finished yet, e.g. user will pay offline by cash
}
}
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
```
Parámetro adicional de la solicitud:
| Parámetro | Presencia | Descripción |
| :--------------------------- | :-------- | :----------------------------------------------------------- |
| **subscriptionUpdateParams** | requerido | un objeto [`AdaptySubscriptionUpdateParameters`](https://android.adapty.io/adapty/com.adapty.models/-adapty-subscription-update-parameters/). |
```java showLineNumbers
Adapty.makePurchase(
activity,
product,
new AdaptyPurchaseParameters.Builder()
.withSubscriptionUpdateParams(subscriptionUpdateParams)
.build(),
result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPurchaseResult purchaseResult = ((AdaptyResult.Success) result).getValue();
if (purchaseResult instanceof AdaptyPurchaseResult.Success) {
AdaptyProfile profile = ((AdaptyPurchaseResult.Success) purchaseResult).getProfile();
// successful cross-grade
} else if (purchaseResult instanceof AdaptyPurchaseResult.UserCanceled) {
// user canceled the purchase flow
} else if (purchaseResult instanceof AdaptyPurchaseResult.Pending) {
// the purchase has not been finished yet, e.g. user will pay offline by cash
}
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// Handle the error
}
});
```
Parámetro adicional de la solicitud:
| Parámetro | Presencia | Descripción |
| :--------------------------- | :-------- | :----------------------------------------------------------- |
| **subscriptionUpdateParams** | requerido | un objeto [`AdaptySubscriptionUpdateParameters`](https://android.adapty.io/adapty/com.adapty.models/-adapty-subscription-update-parameters/). |
Puedes leer más sobre las suscripciones y los modos de reemplazo en la documentación para desarrolladores de Google:
- [Acerca de los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-modes)
- [Recomendaciones de Google sobre los modos de reemplazo](https://developer.android.com/google/play/billing/subscriptions#replacement-recommendations)
- Modo de reemplazo [`CHARGE_PRORATED_PRICE`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#CHARGE_PRORATED_PRICE()). Nota: este método solo está disponible para actualizaciones de suscripción. No se admiten degradaciones.
- Modo de reemplazo [`DEFERRED`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#DEFERRED()). Nota: el cambio real de suscripción solo se producirá cuando finalice el período de facturación actual.
### Gestionar planes prepagados \{#manage-prepaid-plans\}
Si los usuarios de tu app pueden adquirir [planes prepagados](https://developer.android.com/google/play/billing/subscriptions#prepaid-plans) (por ejemplo, comprar una suscripción no renovable por varios meses), puedes habilitar las [transacciones pendientes](https://developer.android.com/google/play/billing/subscriptions#pending) para planes prepagados.
```kotlin showLineNumbers
AdaptyConfig.Builder("PUBLIC_SDK_KEY")
.withEnablePendingPrepaidPlans(true)
.build()
```
```java showLineNumbers
new AdaptyConfig.Builder("PUBLIC_SDK_KEY")
.withEnablePendingPrepaidPlans(true)
.build();
```
---
# File: android-restore-purchase
---
---
title: "Restaurar compras en una app para Android con el SDK"
description: "Aprende cómo restaurar compras en Adapty para garantizar una experiencia de usuario fluida."
---
Restaurar compras es una función que permite a los usuarios recuperar el acceso a contenido adquirido previamente, como suscripciones o compras in-app, sin que se les vuelva a cobrar. Esta función es especialmente útil para usuarios que han desinstalado y reinstalado la app o que han cambiado de dispositivo y quieren acceder a su contenido sin pagar de nuevo.
:::note
En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin que tengas que añadir código adicional. Si es tu caso, puedes saltarte este paso.
:::
Para restaurar una compra cuando no usas [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`:
```kotlin showLineNumbers
Adapty.restorePurchases { result ->
when (result) {
is AdaptyResult.Success -> {
val profile = result.value
if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) {
// successful access restore
}
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.restorePurchases(result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyProfile profile = ((AdaptyResult.Success) result).getValue();
if (profile != null) {
AdaptyProfile.AccessLevel premium = profile.getAccessLevels().get("YOUR_ACCESS_LEVEL");
if (premium != null && premium.isActive()) {
// successful access restore
}
}
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
Parámetros de respuesta:
| Parámetro | Descripción |
|---------|-----------|
| **Profile** | Un objeto [`AdaptyProfile`](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/). Este modelo contiene información sobre los niveles de acceso, suscripciones y compras únicas.
Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app.
|
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
---
# File: implement-observer-mode-android
---
---
title: "Implementar el modo Observer en el SDK de Android"
description: "Implementa el modo Observer en Adapty para rastrear eventos de suscripción de usuarios en el SDK de Android."
---
Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analíticas.
Si esto cubre tus necesidades, solo tienes que:
1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`. Sigue las instrucciones de configuración para [Android](sdk-installation-android#activate-adapty-module-of-adapty-sdk).
2. [Reportar transacciones](report-transactions-observer-mode-android) desde tu infraestructura de compras existente a Adapty.
## Configuración del modo Observer \{#observer-mode-setup\}
Activa el modo Observer si gestionas las compras y el estado de la suscripción por tu cuenta y usas Adapty para enviar eventos de suscripción y analíticas.
:::important
Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlas tú mismo.
:::
```kotlin showLineNumbers
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Adapty.activate(
applicationContext,
AdaptyConfig.Builder("PUBLIC_SDK_KEY")
.withObserverMode(true) //default false
.build()
)
}
```
```java showLineNumbers
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Adapty.activate(
applicationContext,
new AdaptyConfig.Builder("PUBLIC_SDK_KEY")
.withObserverMode(true) //default false
.build()
);
}
```
Parámetros:
| Parámetro | Descripción |
| --------------------------- | ------------------------------------------------------------ |
| observerMode | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor por defecto es `false`. |
## Usar los paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\}
Si también quieres usar los paywalls y las funciones de pruebas A/B de Adapty, puedes hacerlo, pero requiere una configuración adicional en el modo Observer. Esto es lo que necesitas hacer además de los pasos anteriores:
1. Muestra los paywalls de la forma habitual para [paywalls con Remote Config](present-remote-config-paywalls-android). Para los paywalls del Paywall Builder, sigue las guías de configuración específicas para [Android](android-present-paywall-builder-paywalls-in-observer-mode).
3. [Asocia los paywalls](report-transactions-observer-mode-android) con las transacciones de compra.
---
# File: report-transactions-observer-mode-android
---
---
title: "Reportar transacciones en Observer Mode en el SDK de Android"
description: "Reporta transacciones de compra en el Observer Mode de Adapty para obtener información de usuario y seguimiento de ingresos en el SDK de Android."
---
En Observer Mode, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Debes reportar las transacciones desde tu app store. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis.
Usa `reportTransaction` para reportar explícitamente cada transacción y que Adapty la reconozca.
:::warning
**¡No omitas el reporte de transacciones!**
Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones.
:::
Si usas paywalls de Adapty, incluye el `variationId` al reportar una transacción. Esto vincula la compra con el paywall que la generó, garantizando análisis precisos del paywall.
```kotlin showLineNumbers
val transactionInfo = TransactionInfo.fromPurchase(purchase)
Adapty.reportTransaction(transactionInfo, variationId) { result ->
if (result is AdaptyResult.Success) {
// success
}
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
| --------------- | --------- | ------------------------------------------------------------ |
| transactionInfo | requerido | El TransactionInfo de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. |
| variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
```java showLineNumbers
TransactionInfo transactionInfo = TransactionInfo.fromPurchase(purchase);
Adapty.reportTransaction(transactionInfo, variationId, result -> {
if (result instanceof AdaptyResult.Success) {
// success
}
});
```
Parámetros:
| Parámetro | Presencia | Descripción |
| --------------- | --------- | ------------------------------------------------------------ |
| transactionInfo | requerido | El TransactionInfo de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. |
| variationId | opcional | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
En Observer Mode, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Debes reportar las transacciones desde tu app store o restaurarlas. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis.
Usa `restorePurchases` para reportar la transacción a Adapty.
:::warning
**¡No omitas la restauración de compras!**
Si no llamas a `restorePurchases`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones.
:::
Si usas paywalls de Adapty, vincula tu transacción con el paywall que originó la compra mediante el método `setVariationId`. Esto garantiza que la compra se atribuya correctamente al paywall que la desencadenó para un análisis preciso. Este paso solo es necesario si usas paywalls de Adapty.
```kotlin showLineNumbers
Adapty.restorePurchases { result ->
if (result is AdaptyResult.Success) {
// success
}
}
Adapty.setVariationId(transactionId, variationId) { error ->
if (error == null) {
// success
}
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
| ------------- | --------- | ------------------------------------------------------------ |
| transactionId | requerido | Identificador de cadena (`purchase.getOrderId`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. |
| variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
```java showLineNumbers
Adapty.restorePurchases(result -> {
if (result instanceof AdaptyResult.Success) {
// success
}
});
Adapty.setVariationId(transactionId, variationId, error -> {
if (error == null) {
// success
}
});
```
Parámetros:
| Parámetro | Presencia | Descripción |
| ------------- | --------- | ------------------------------------------------------------ |
| transactionId | requerido | Identificador de cadena (`purchase.getOrderId`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. |
| variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
**Reporte de transacciones**
Usa `restorePurchases` para reportar una transacción a Adapty en Observer Mode, tal como se explica en la página [Restaurar compras en el código móvil](android-restore-purchase).
:::warning
**¡No omitas el reporte de transacciones!**
Si no llamas a `restorePurchases`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones.
:::
**Asociar paywalls a transacciones**
El SDK de Adapty no puede determinar el origen de las compras, ya que eres tú quien las procesa. Por lo tanto, si tienes intención de usar paywalls y/o pruebas A/B en Observer Mode, debes asociar la transacción proveniente de tu app store con el paywall correspondiente en el código de tu app móvil. Es importante hacerlo bien antes de publicar tu app, de lo contrario generará errores en los análisis.
```kotlin
Adapty.setVariationId(transactionId, variationId) { error ->
if (error == null) {
// success
}
}
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| ------------- | --------- | ------------------------------------------------------------ |
| transactionId | requerido | Identificador de cadena (purchase.getOrderId de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. |
| variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
```java
Adapty.setVariationId(transactionId, variationId, error -> {
if (error == null) {
// success
}
});
```
| Parámetro | Presencia | Descripción |
| ------------------------------------------------- | --------- | ------------------------------------------------------------ |
| transactionId | requerido | Identificador de cadena (purchase.getOrderId de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación. |
| variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
---
# File: android-present-paywall-builder-paywalls-in-observer-mode
---
---
title: "Mostrar paywalls de Paywall Builder en modo Observer en el SDK de Android"
description: "Aprende cómo mostrar paywalls en modo observer usando el Paywall Builder de Adapty."
---
Si has personalizado un paywall con el Paywall Builder, no tienes que preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Este tipo de paywall incluye tanto qué mostrar como cómo mostrarlo.
:::warning
Esta sección es exclusivamente para el [modo Observer](observer-vs-full-mode). Si no trabajas en modo Observer, consulta el tema [Android - Mostrar paywalls creados con Paywall Builder](android-present-paywalls).
:::
Antes de empezar a mostrar paywalls (haz clic para expandir)
1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios).
2. Instala y configura el SDK de Adapty. Asegúrate de establecer el parámetro `observerMode` en `true`. Consulta nuestras instrucciones específicas por framework [para Android](sdk-installation-android).
3. [Crea productos](create-product) en el Adapty Dashboard.
4. [Configura los paywalls, asígnales productos](create-paywall) y personalízalos usando el Paywall Builder en el Adapty Dashboard.
5. [Crea placements y asígnales tus paywalls](create-placement) en el Adapty Dashboard.
6. [Obtén los paywalls del Paywall Builder y su configuración](android-get-pb-paywalls) en el código de tu aplicación móvil.
1. Implementa el `AdaptyUiObserverModeHandler`.
El evento `onPurchaseInitiated` te informará de que el usuario ha iniciado una compra. Puedes activar tu flujo de compra personalizado en respuesta a este callback:
```kotlin showLineNumbers
val observerModeHandler =
AdaptyUiObserverModeHandler { product, paywall, paywallView, onStartPurchase, onFinishPurchase ->
onStartPurchase()
yourBillingClient.makePurchase(
product,
onSuccess = { purchase ->
onFinishPurchase()
//handle success
},
onError = {
onFinishPurchase()
//handle error
},
onCancel = {
onFinishPurchase()
//handle cancel
}
)
}
```
```java showLineNumbers
AdaptyUiObserverModeHandler observerModeHandler = (product, paywall, paywallView, onStartPurchase, onFinishPurchase) -> {
onStartPurchase.invoke();
yourBillingClient.makePurchase(
product,
purchase -> {
onFinishPurchase.invoke();
//handle success
},
error -> {
onFinishPurchase.invoke();
//handle error
},
() -> { //cancellation
onFinishPurchase.invoke();
//handle cancel
}
);
};
```
Para manejar las restauraciones en el modo Observer, sobreescribe `getRestoreHandler()`. Por defecto devuelve `null`, lo que utiliza el flujo integrado de `Adapty.restorePurchases()` de Adapty. Para proporcionar tu propia implementación de restauración:
```kotlin showLineNumbers
val observerModeHandler = object : AdaptyUiObserverModeHandler {
// onPurchaseInitiated implementation (see above)
override fun getRestoreHandler() =
AdaptyUiObserverModeHandler.RestoreHandler { onStartRestore, onFinishRestore ->
onStartRestore()
yourBillingClient.restorePurchases(
onSuccess = { restoredPurchases ->
onFinishRestore()
//handle successful restore
},
onError = {
onFinishRestore()
//handle error
}
)
}
}
```
```java showLineNumbers
AdaptyUiObserverModeHandler observerModeHandler = new AdaptyUiObserverModeHandler() {
// onPurchaseInitiated implementation (see above)
@Override
public RestoreHandler getRestoreHandler() {
return (onStartRestore, onFinishRestore) -> {
onStartRestore.invoke();
yourBillingClient.restorePurchases(
restoredPurchases -> {
onFinishRestore.invoke();
//handle successful restore
},
error -> {
onFinishRestore.invoke();
//handle error
}
);
};
}
};
```
Recuerda invocar los siguientes callbacks para notificar a AdaptyUI sobre el proceso de compra o restauración. Esto es necesario para que el paywall funcione correctamente, por ejemplo, para mostrar el loader:
| Callback | Descripción |
| :----------------- |:-----------------------------------------------------------------------------------------------------|
| onStartPurchase() | El callback debe invocarse para notificar a AdaptyUI que la compra ha comenzado. |
| onFinishPurchase() | El callback debe invocarse para notificar a AdaptyUI que la compra ha finalizado. |
| onStartRestore() | Opcional. El callback puede invocarse para notificar a AdaptyUI que la restauración ha comenzado. |
| onFinishRestore() | Opcional. El callback puede invocarse para notificar a AdaptyUI que la restauración ha finalizado. |
2. Para mostrar el paywall visual en la pantalla del dispositivo, primero debes configurarlo.
Para ello, llama al método `AdaptyUI.getPaywallView()` o crea el `AdaptyPaywallView` directamente:
```kotlin showLineNumbers
val paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
products,
eventListener,
personalizedOfferResolver,
tagResolver,
timerResolver,
observerModeHandler,
)
```
```kotlin showLineNumbers
val paywallView =
AdaptyPaywallView(activity) // or retrieve it from xml
...
with(paywallView) {
showPaywall(
viewConfiguration,
products,
eventListener,
personalizedOfferResolver,
tagResolver,
timerResolver,
observerModeHandler,
)
}
```
```java showLineNumbers
AdaptyPaywallView paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
products,
eventListener,
personalizedOfferResolver,
tagResolver,
timerResolver,
observerModeHandler
);
```
```java showLineNumbers
AdaptyPaywallView paywallView =
new AdaptyPaywallView(activity); //añadir a la jerarquía de vistas si es necesario, o recibirlo desde xml
...
paywallView.showPaywall(viewConfiguration, products, eventListener, personalizedOfferResolver, tagResolver, timerResolver, observerModeHandler);
```
```xml showLineNumbers
```
Después de crear la vista correctamente, puedes añadirla a la jerarquía de vistas y mostrarla.
Para hacerlo, utiliza esta función composable:
```kotlin showLineNumbers
AdaptyPaywallScreen(
viewConfiguration,
products,
eventListener,
personalizedOfferResolver,
tagResolver,
timerResolver,
)
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **Products** | opcional | Proporciona un array de `AdaptyPaywallProduct` para optimizar el momento de visualización de los productos en pantalla. Si se pasa `null`, AdaptyUI obtendrá automáticamente los productos necesarios. |
| **ViewConfiguration** | obligatorio | Proporciona un objeto `AdaptyViewConfiguration` que contiene los detalles visuales del paywall. Usa el método `Adapty.getViewConfiguration(paywall)` para cargarlo. Consulta el tema [Obtener la configuración visual del paywall](#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) para más detalles. |
| **EventListener** | opcional | Proporciona un `AdaptyUiEventListener` para observar los eventos del paywall. Se recomienda extender `AdaptyUiDefaultEventListener` para mayor comodidad. Consulta el tema [Gestionar eventos del paywall](android-handling-events) para más detalles. |
| **PersonalizedOfferResolver** | opcional | Para indicar precios personalizados ([leer más](https://developer.android.com/google/play/billing/integrate#personalized-price)), implementa `AdaptyUiPersonalizedOfferResolver` y añade tu propia lógica que mapee `AdaptyPaywallProduct` a `true` si el precio del producto es personalizado, o a `false` en caso contrario. |
| **TagResolver** | opcional | Usa `AdaptyUiTagResolver` para resolver etiquetas personalizadas dentro del texto del paywall. Este resolver recibe un parámetro de etiqueta y lo resuelve en la cadena de texto correspondiente. Consulta el tema [Etiquetas personalizadas en Paywall Builder](custom-tags-in-paywall-builder) para más detalles. |
| **ObserverModeHandler** | obligatorio en modo Observer | El `AdaptyUiObserverModeHandler` que implementaste en el paso anterior. |
| **variationId** | obligatorio | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
| **transaction** | obligatorio | Para iOS, StoreKit 1: un objeto [`SKPaymentTransaction`](https://developer.apple.com/documentation/storekit/skpaymenttransaction).
Para iOS, StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).
Para Android: identificador de cadena (`purchase.getOrderId()`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.
|
Antes de empezar a mostrar paywalls (Haz clic para ampliar)
1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios).
2. Instala y configura el SDK de Adapty. Asegúrate de establecer el parámetro `observerMode` en `true`. Consulta las instrucciones específicas para cada framework: [Android](sdk-installation-android), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk) y [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk).
3. [Crea productos](create-product) en el Adapty Dashboard.
4. [Configura los paywalls, asígnales productos](create-paywall) y personalízalos con el Paywall Builder en el Adapty Dashboard.
5. [Crea placements y asígnales tus paywalls](create-placement) en el Adapty Dashboard.
6. [Obtén los paywalls del Paywall Builder y su configuración](android-get-pb-paywalls) en el código de tu aplicación móvil.
1. Implementa el `AdaptyUiObserverModeHandler`. El callback de `AdaptyUiObserverModeHandler` (`onPurchaseInitiated`) te avisa cuando el usuario inicia una compra. Puedes activar tu flujo de compra personalizado en respuesta a este callback así:
```kotlin showLineNumbers
val observerModeHandler =
AdaptyUiObserverModeHandler { product, paywall, paywallView, onStartPurchase, onFinishPurchase ->
onStartPurchase()
yourBillingClient.makePurchase(
product,
onSuccess = { purchase ->
onFinishPurchase()
//handle success
},
onError = {
onFinishPurchase()
//handle error
},
onCancel = {
onFinishPurchase()
//handle cancel
}
)
}
```
```java showLineNumbers
AdaptyUiObserverModeHandler observerModeHandler = (product, paywall, paywallView, onStartPurchase, onFinishPurchase) -> {
onStartPurchase.invoke();
yourBillingClient.makePurchase(
product,
purchase -> {
onFinishPurchase.invoke();
//handle success
},
error -> {
onFinishPurchase.invoke();
//handle error
},
() -> { //cancellation
onFinishPurchase.invoke();
//handle cancel
}
);
};
```
También recuerda invocar estos callbacks en AdaptyUI. Esto es necesario para el correcto funcionamiento del paywall, como mostrar el loader, entre otras cosas:
| Callback en Kotlin | Callback en Java | Descripción |
| :----------------- | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| onStartPurchase() | onStartPurchase.invoke() | Este callback debe invocarse para notificar a AdaptyUI que la compra ha comenzado. |
| onFinishPurchase() | onFinishPurchase.invoke() | Este callback debe invocarse para notificar a AdaptyUI que la compra ha finalizado correctamente, que ha fallado, o que ha sido cancelada. |
2. Para mostrar el paywall visual, primero debes inicializarlo. Para ello, llama al método `AdaptyUI.getPaywallView()` o crea el `AdaptyPaywallView` directamente:
```kotlin showLineNumbers
val paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
products,
AdaptyPaywallInsets.of(topInset, bottomInset),
eventListener,
personalizedOfferResolver,
tagResolver,
observerModeHandler,
)
//======= OR =======
val paywallView =
AdaptyPaywallView(activity) // or retrieve it from xml
...
with(paywallView) {
setEventListener(eventListener)
setObserverModeHandler(observerModeHandler)
showPaywall(
viewConfiguration,
products,
AdaptyPaywallInsets.of(topInset, bottomInset),
personalizedOfferResolver,
tagResolver,
)
}
```
```java showLineNumbers
AdaptyPaywallView paywallView = AdaptyUI.getPaywallView(
activity,
viewConfiguration,
products,
AdaptyPaywallInsets.of(topInset, bottomInset),
eventListener,
personalizedOfferResolver,
tagResolver,
observerModeHandler
);
//======= OR =======
AdaptyPaywallView paywallView =
new AdaptyPaywallView(activity); //add to the view hierarchy if needed, or you receive it from xml
...
paywallView.setEventListener(eventListener);
paywallView.setObserverModeHandler(observerModeHandler);
paywallView.showPaywall(viewConfiguration, products, AdaptyPaywallInsets.of(topInset, bottomInset), personalizedOfferResolver);
```
```xml showLineNumbers
```
Tras crear la vista correctamente, puedes añadirla a la jerarquía de vistas y mostrarla.
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
|---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Products** | opcional | Proporciona un array de `AdaptyPaywallProduct` para optimizar el tiempo de visualización de los productos en pantalla. Si se pasa `null`, AdaptyUI obtendrá automáticamente los productos necesarios. |
| **ViewConfiguration** | requerido | Suministra un objeto `AdaptyViewConfiguration` que contiene los detalles visuales del paywall. Usa el método `Adapty.getViewConfiguration(paywall)` para cargarlo. Consulta el tema [Obtener la configuración visual del paywall](android-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) para más detalles. |
| **Insets** | requerido | Define un objeto `AdaptyPaywallInsets` con información sobre el área solapada por las barras del sistema, creando márgenes verticales para el contenido. Si ni la barra de estado ni la barra de navegación solapan el `AdaptyPaywallView`, pasa `AdaptyPaywallInsets.NONE`. En modo pantalla completa donde las barras del sistema solapan parte de tu interfaz, obtén los insets como se muestra debajo de la tabla. |
| **EventListener** | opcional | Proporciona un `AdaptyUiEventListener` para observar los eventos del paywall. Se recomienda extender `AdaptyUiDefaultEventListener` para facilitar su uso. Consulta el tema [Gestión de eventos del paywall](android-handling-events) para más detalles. |
| **PersonalizedOfferResolver** | opcional | Para indicar precios personalizados ([leer más](https://developer.android.com/google/play/billing/integrate#personalized-price)), implementa `AdaptyUiPersonalizedOfferResolver` y pasa tu propia lógica que mapea `AdaptyPaywallProduct` a `true` si el precio del producto es personalizado, o `false` en caso contrario. |
| **TagResolver** | opcional | Usa `AdaptyUiTagResolver` para resolver etiquetas personalizadas dentro del texto del paywall. Este resolver toma un parámetro de etiqueta y lo resuelve en la cadena correspondiente. Consulta el tema [Etiquetas personalizadas en Paywall Builder](custom-tags-in-paywall-builder) para más detalles. |
| **ObserverModeHandler** | requerido en modo Observer | El `AdaptyUiObserverModeHandler` que implementaste en el paso anterior. |
| **variationId** | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/). |
| **transaction** | requerido | Para iOS, StoreKit 1: un objeto [`SKPaymentTransaction`](https://developer.apple.com/documentation/storekit/skpaymenttransaction).
Para iOS, StoreKit 2: un objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).
Para Android: el identificador de cadena (`purchase.getOrderId()`) de la compra, donde la compra es una instancia de la clase [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) de la biblioteca de facturación.
|
Para el modo de pantalla completa, donde las barras del sistema se superponen a parte de tu UI, obtén los insets de la siguiente manera:
```kotlin showLineNumbers
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
//create extension function
fun View.onReceiveSystemBarsInsets(action: (insets: Insets) -> Unit) {
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
val systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
ViewCompat.setOnApplyWindowInsetsListener(this, null)
action(systemBarInsets)
insets
}
}
//and then use it with the view
paywallView.onReceiveSystemBarsInsets { insets ->
val paywallInsets = AdaptyPaywallInsets.of(insets.top, insets.bottom)
paywallView.setEventListener(eventListener)
paywallView.setObserverModeHandler(observerModeHandler)
paywallView.showPaywall(viewConfig, products, paywallInsets, personalizedOfferResolver, tagResolver)
}
```
```java showLineNumbers
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
...
ViewCompat.setOnApplyWindowInsetsListener(paywallView, (view, insets) -> {
Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());
ViewCompat.setOnApplyWindowInsetsListener(paywallView, null);
AdaptyPaywallInsets paywallInsets =
AdaptyPaywallInsets.of(systemBarInsets.top, systemBarInsets.bottom);
paywallView.setEventListener(eventListener);
paywallView.setObserverModeHandler(observerModeHandler);
paywallView.showPaywall(viewConfiguration, products, paywallInsets, personalizedOfferResolver, tagResolver);
return insets;
});
```
Devuelve:
| Objeto | Descripción |
| :------------------ | :------------------------------------------------- |
| `AdaptyPaywallView` | objeto que representa la pantalla del paywall solicitado. |
:::warning
No olvides [Asociar paywalls a transacciones de compra](report-transactions-observer-mode-android). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra.
:::
---
# File: android-troubleshoot-purchases
---
---
title: "Solucionar problemas de compras en el SDK de Android"
description: "Solucionar problemas de compras en el SDK de Android"
---
Esta guía te ayuda a resolver los problemas más comunes al implementar compras manualmente en el SDK de Android.
## makePurchase se llama correctamente, pero el perfil no se actualiza \{#makepurchase-is-called-successfully-but-the-profile-is-not-being-updated\}
**Problema**: El método `makePurchase` se completa correctamente, pero el perfil del usuario y el estado de la suscripción no se actualizan en Adapty.
**Causa**: Esto generalmente indica una configuración incompleta de Google Play Store.
**Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android).
## makePurchase se invoca dos veces \{#makepurchase-is-invoked-twice\}
**Problema**: El método `makePurchase` se llama varias veces para la misma compra.
**Causa**: Esto suele ocurrir cuando el flujo de compra se activa varias veces debido a problemas en la gestión del estado de la interfaz o a interacciones muy rápidas del usuario.
**Solución**: Asegúrate de haber completado todos los [pasos de configuración de Google Play](initial-android).
## AdaptyError.cantMakePayments en el modo observador \{#adaptyerrorcantmakepayments-in-observer-mode\}
**Problema**: Recibes `AdaptyError.cantMakePayments` al usar `makePurchase` en el modo observador.
**Causa**: En el modo observador, debes gestionar las compras por tu cuenta y no usar el método `makePurchase` de Adapty.
**Solución**: Si usas `makePurchase` para las compras, desactiva el modo observador. Tienes que elegir entre usar `makePurchase` o gestionar las compras por tu cuenta en el modo observador. Consulta [Implementar el modo observador](implement-observer-mode-android) para más detalles.
## Error de Adapty: (code: 103, message: Play Market request failed on purchases updated: responseCode=3, debugMessage=Billing Unavailable, detail: null) \{#adapty-error-code-103-message-play-market-request-failed-on-purchases-updated-responsecode3-debugmessagebilling-unavailable-detail-null\}
**Problema**: Recibes un error de facturación no disponible de Google Play Store.
**Causa**: Este error no está relacionado con Adapty. Es un error de la biblioteca de facturación de Google Play que indica que la facturación no está disponible en el dispositivo.
**Solución**: Este error no está relacionado con Adapty. Puedes encontrar más información en la documentación de Play Store: [Handle BillingResult response codes](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) | Play Billing | Android Developers.
## No se encuentran makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\}
**Problema**: Tienes problemas porque no se encuentran los `makePurchasesCompletionHandlers`.
**Causa**: Esto suele estar relacionado con problemas en las pruebas en sandbox.
**Solución**: Crea un nuevo usuario de sandbox e inténtalo de nuevo. Esto suele resolver los problemas con los manejadores de finalización de compras en entornos sandbox.
## Otros problemas \{#other-issues\}
**Problema**: Experimentas otros problemas relacionados con compras que no se tratan anteriormente.
**Solución**: Si es necesario, migra el SDK a la versión más reciente siguiendo las [guías de migración](android-sdk-migration-guides). Muchos problemas se resuelven en versiones más nuevas del SDK.
---
# File: android-identifying-users
---
---
title: "Identificar usuarios en el SDK de Android"
description: "Identifica usuarios en Adapty para mejorar las experiencias de suscripción personalizadas (Android)."
---
Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, deberías definir tu propio Customer User ID. Puedes buscar usuarios por su Customer User ID en la sección [Perfiles](profiles-crm) y usarlo en la [API server-side](getting-started-with-server-side-api), que se enviará a todas las integraciones.
### Configurar el ID de usuario en la configuración \{#setting-customer-user-id-on-configuration\}
Si tienes un ID de usuario durante la configuración, pásalo directamente como parámetro `customerUserId` al método `.activate()`:
```kotlin showLineNumbers
Adapty.activate(applicationContext, "PUBLIC_SDK_KEY", customerUserId = "YOUR_USER_ID")
```
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
### Establecer el ID de usuario personalizado tras la configuración \{#setting-customer-user-id-after-configuration\}
Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo más tarde en cualquier momento con el método `.identify()`. Los casos más habituales para usar este método son después del registro o la autenticación, cuando el usuario pasa de ser anónimo a estar autenticado.
```kotlin showLineNumbers
Adapty.identify("YOUR_USER_ID") { error ->
if (error == null) {
// successful identify
}
}
```
```java showLineNumbers
Adapty.identify("YOUR_USER_ID", error -> {
if (error == null) {
// successful identify
}
});
```
Parámetros de la solicitud:
- **Customer User ID** (obligatorio): un identificador de usuario de tipo string.
:::warning
Reenvío de datos significativos del usuario
En algunos casos, como cuando un usuario inicia sesión en su cuenta de nuevo, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si pasaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, debes volver a enviar esos datos para el usuario identificado.
También es importante tener en cuenta que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes.
:::
### Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\}
Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`:
```kotlin showLineNumbers
Adapty.logout { error ->
if (error == null) {
// successful logout
}
}
```
```java showLineNumbers
Adapty.logout(error -> {
if (error == null) {
// successful logout
}
});
```
Luego puedes iniciar sesión con el usuario usando el método `.identify()`.
### Detectar usuarios en varios dispositivos \{#detect-users-across-devices\}
---
no_index: true
---
Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`.
Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID.
**Lo que Adapty puede detectar entre dispositivos**
| Tu configuración | Lo que Adapty detecta | Lo que debes hacer |
| --- | --- | --- |
| Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. |
| Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. |
| Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. |
| Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. |
**Restaurar compras en un dispositivo nuevo**
Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK.
No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado.
---
# File: android-setting-user-attributes
---
---
title: "Establecer atributos de usuario en el SDK de Android"
description: "Aprende a establecer atributos de usuario en Adapty para mejorar la segmentación de audiencias."
---
Puedes añadir atributos opcionales como email, número de teléfono, etc., al usuario de tu app. Luego puedes usar esos atributos para crear [segmentos](segments) de usuarios o simplemente consultarlos en el CRM.
### Configurar atributos de usuario \{#setting-user-attributes\}
Para establecer atributos de usuario, llama al método `.updateProfile()`:
```kotlin showLineNumbers
val builder = AdaptyProfileParameters.Builder()
.withEmail("email@email.com")
.withPhoneNumber("+18888888888")
.withFirstName("John")
.withLastName("Appleseed")
.withGender(AdaptyProfile.Gender.OTHER)
.withBirthday(AdaptyProfile.Date(1970, 1, 3))
Adapty.updateProfile(builder.build()) { error ->
if (error != null) {
// handle the error
}
}
```
```java showLineNumbers
AdaptyProfileParameters.Builder builder = new AdaptyProfileParameters.Builder()
.withEmail("email@email.com")
.withPhoneNumber("+18888888888")
.withFirstName("John")
.withLastName("Appleseed")
.withGender(AdaptyProfile.Gender.OTHER)
.withBirthday(new AdaptyProfile.Date(1970, 1, 3));
Adapty.updateProfile(builder.build(), error -> {
if (error != null) {
// handle the error
}
});
```
Ten en cuenta que los atributos que hayas establecido previamente con el método `updateProfile` no se restablecerán.
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
### Lista de claves permitidas \{#the-allowed-keys-list\}
Las claves `` permitidas de `AdaptyProfileParameters.Builder` y sus valores `` son los siguientes:
| Clave | Valor |
|---|-----|
| email
phoneNumber
firstName
lastName
| String |
| gender | Enum, los valores permitidos son: `female`, `male`, `other` |
| birthday | Date |
### Atributos personalizados de usuario \{#custom-user-attributes\}
Puedes definir tus propios atributos personalizados, que normalmente están relacionados con el uso de tu app. Por ejemplo, en aplicaciones de fitness pueden ser el número de ejercicios por semana; en apps de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes usarlos en segmentos para crear paywalls y ofertas segmentadas, y también en analíticas para identificar qué métricas de producto influyen más en los ingresos.
```kotlin showLineNumbers
builder.withCustomAttribute("key1", "value1")
```
```java showLineNumbers
builder.withCustomAttribute("key1", "value1");
```
Para eliminar una clave existente, usa el método `.withRemoved(customAttributeForKey:)`:
```kotlin showLineNumbers
builder.withRemovedCustomAttribute("key2")
```
```java showLineNumbers
builder.withRemovedCustomAttribute("key2");
```
A veces necesitas saber qué atributos personalizados ya se han establecido. Para ello, usa el campo `customAttributes` del objeto `AdaptyProfile`.
:::warning
Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor podrían haber cambiado desde la última sincronización.
:::
### Límites \{#limits\}
- Hasta 30 atributos personalizados por usuario
- Los nombres de las claves tienen un máximo de 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.`
- El valor puede ser una cadena de texto o un número decimal con un máximo de 50 caracteres.
---
# File: android-listen-subscription-changes
---
---
title: "Comprobar el estado de la suscripción en el SDK de Android"
description: "Rastrea y gestiona el estado de la suscripción del usuario en Adapty para mejorar la retención de clientes en tu app de Android."
---
Con Adapty, hacer seguimiento del estado de la suscripción es muy sencillo. No necesitas insertar manualmente los IDs de producto en tu código. En su lugar, puedes confirmar fácilmente el estado de la suscripción de un usuario comprobando si tiene un [nivel de acceso](access-level) activo.
Antes de empezar a comprobar el estado de la suscripción, configura las [notificaciones en tiempo real para desarrolladores (RTDN)](enable-real-time-developer-notifications-rtdn).
## Nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptyprofile-object\}
Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/). Te recomendamos recuperar el perfil cuando tu app arranque, por ejemplo al [identificar a un usuario](android-identifying-users#setting-customer-user-id-on-configuration), y luego actualizarlo cada vez que se produzcan cambios. Así podrás usar el objeto de perfil sin tener que solicitarlo repetidamente.
Para recibir notificaciones sobre actualizaciones del perfil, escucha los cambios tal como se describe en la sección [Escuchar actualizaciones del perfil, incluidos los niveles de acceso](android-listen-subscription-changes) más abajo.
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras [apps de ejemplo](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\}
Para obtener el nivel de acceso desde el servidor, usa el método `.getProfile()`:
```kotlin showLineNumbers
Adapty.getProfile { result ->
when (result) {
is AdaptyResult.Success -> {
val profile = result.value
// check the access
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.getProfile(result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyProfile profile = ((AdaptyResult.Success) result).getValue();
// check the access
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
Parámetros de respuesta:
| Parámetro | Descripción |
| --------- | ------------------------------------------------------------ |
| Profile | Un objeto [AdaptyProfile](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/). En general, solo necesitas comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app.
El método `.getProfile` devuelve el resultado más actualizado posible, ya que siempre intenta consultar la API. Si por algún motivo (p. ej., sin conexión a internet) el SDK de Adapty no puede obtener información del servidor, se devolverán los datos de la caché. También es importante tener en cuenta que el SDK de Adapty actualiza la caché de `AdaptyProfile` con regularidad para mantener esta información lo más actualizada posible.
|
El método `.getProfile()` te proporciona el perfil del usuario, a partir del cual puedes obtener el estado del nivel de acceso. Puedes tener varios niveles de acceso por app. Por ejemplo, si tienes una app de periódico y vendes suscripciones a distintos temas de forma independiente, puedes crear los niveles de acceso "sports" y "science". Sin embargo, la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes utilizar simplemente el nivel de acceso "premium" predeterminado.
A continuación se muestra un ejemplo para comprobar el nivel de acceso "premium" predeterminado:
```kotlin showLineNumbers
Adapty.getProfile { result ->
when (result) {
is AdaptyResult.Success -> {
val profile = result.value
if (profile.accessLevels["premium"]?.isActive == true) {
// grant access to premium features
}
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
```java showLineNumbers
Adapty.getProfile(result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyProfile profile = ((AdaptyResult.Success) result).getValue();
AdaptyProfile.AccessLevel premium = profile.getAccessLevels().get("premium");
if (premium != null && premium.isActive()) {
// grant access to premium features
}
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// handle the error
}
});
```
### Escuchar actualizaciones del estado de la suscripción \{#listening-for-subscription-status-updates\}
Cada vez que cambia la suscripción del usuario, Adapty lanza un evento.
Para recibir mensajes de Adapty, necesitas hacer alguna configuración adicional:
```kotlin showLineNumbers
Adapty.setOnProfileUpdatedListener { profile ->
// handle any changes to subscription state
}
```
```java showLineNumbers t
Adapty.setOnProfileUpdatedListener(profile -> {
// handle any changes to subscription state
});
```
Adapty también lanza un evento al iniciar la aplicación. En ese caso, se pasará el estado de la suscripción en caché.
### Caché del estado de la suscripción \{#subscription-status-cache\}
La caché implementada en el SDK de Adapty almacena el estado de la suscripción del perfil. Esto significa que, aunque el servidor no esté disponible, se puede acceder a los datos en caché para obtener información sobre el estado de la suscripción del perfil.
No obstante, hay que tener en cuenta que no es posible solicitar datos directamente desde la caché. El SDK consulta periódicamente el servidor cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si existen modificaciones, como nuevas transacciones u otras actualizaciones, se enviarán a los datos en caché para mantenerlos sincronizados con el servidor.
---
# File: kids-mode-android
---
---
title: "Modo Kids en Android SDK"
description: "Activa fácilmente el Modo Kids para cumplir las políticas de Google. Sin GAID ni datos publicitarios recopilados en Android SDK."
---
Si tu aplicación Android está destinada a niños, debes seguir las políticas de [Google](https://support.google.com/googleplay/android-developer/answer/9893335). Si usas el SDK de Adapty, unos pocos pasos sencillos te ayudarán a configurarlo para cumplir estas políticas y superar las revisiones de la tienda.
## ¿Qué se requiere? \{#whats-required\}
Necesitas configurar el SDK de Adapty para deshabilitar la recopilación de:
- [Android Advertising ID (AAID/GAID)](https://support.google.com/googleplay/android-developer/answer/6048248)
- [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf)
Además, te recomendamos usar el ID de usuario personalizado con cuidado. Un ID de usuario con formato `` se considerará claramente como recopilación de datos personales, al igual que usar un correo electrónico. Para el Modo Kids, la práctica recomendada es usar identificadores aleatorios o anonimizados (por ejemplo, IDs hasheados o UUIDs generados por el dispositivo) para garantizar el cumplimiento.
## Activar el Modo Kids \{#enabling-kids-mode\}
### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\}
En el Adapty Dashboard, necesitas deshabilitar la recopilación de direcciones IP. Para ello, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** en **Collect users' IP address**.
### Cambios en el código de tu aplicación \{#updates-in-your-mobile-app-code\}
Para cumplir con las políticas, debes deshabilitar la recopilación del Android Advertising ID (AAID/GAID) y la dirección IP al inicializar el SDK de Adapty:
**Kotlin:**
```kotlin showLineNumbers
override fun onCreate() {
super.onCreate()
Adapty.activate(
applicationContext,
AdaptyConfig.Builder("PUBLIC_SDK_KEY")
// highlight-start
.withAdIdCollectionDisabled(true) // set to `true`
.withIpAddressCollectionDisabled(true) // set to `true`
// highlight-end
.build()
)
}
```
**Java:**
```java showLineNumbers
@Override
public void onCreate() {
super.onCreate();
Adapty.activate(
applicationContext,
new AdaptyConfig.Builder("PUBLIC_SDK_KEY")
// highlight-start
.withAdIdCollectionDisabled(true) // set to `true`
.withIpAddressCollectionDisabled(true) // set to `true`
// highlight-end
.build()
);
}
```
---
# File: android-get-onboardings
---
---
title: "Obtener onboardings en el SDK de Android"
description: "Aprende cómo recuperar onboardings en Adapty para Android."
---
Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el builder en el Adapty Dashboard, puedes mostrarlo en tu app de Android. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, tal como se describe a continuación.
Antes de empezar, asegúrate de que:
1. Tienes instalado el [SDK de Adapty para Android](sdk-installation-android) en la versión 3.8.0 o superior.
2. Has [creado un onboarding](create-onboarding).
3. Has añadido el onboarding a un [placement](placements).
## Obtener el onboarding \{#fetch-onboarding\}
Cuando creas un [onboarding](onboardings) con nuestro builder sin código, se almacena como un contenedor con una configuración que tu app necesita obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a cuestionarios o entradas de formularios). El contenedor también registra automáticamente los eventos de analítica, por lo que no necesitas implementar un seguimiento de vistas por separado.
Para un mejor rendimiento, obtén la configuración del onboarding con antelación para que las imágenes tengan tiempo suficiente de descargarse antes de mostrárselas a los usuarios.
Para obtener un onboarding, usa el método `getOnboarding`:
```kotlin showLineNumbers
Adapty.getOnboarding("YOUR_PLACEMENT_ID") { result ->
when (result) {
is AdaptyResult.Success -> {
val onboarding = result.value
// the requested onboarding
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
|---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. |
| **locale** | opcional
por defecto: `en`
| El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.
Ejemplo: `en` significa inglés; `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra al desinstalarla o mediante limpieza manual.
El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no esté disponible. Este sistema está diseñado para asegurarse de que siempre obtengas la versión más reciente de tus onboardings garantizando la fiabilidad incluso cuando la conexión a internet es limitada.
|
| **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera de este método. Si se alcanza el timeout, se devolverán los datos en caché o el fallback local.
Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes solicitudes internamente.
Para Android: puedes crear `TimeInterval` con funciones de extensión (como `5.seconds`, donde `.seconds` es de `import com.adapty.utils.seconds`), o `TimeInterval.seconds(5)`. Para no establecer ningún límite, usa `TimeInterval.INFINITE`.
|
Parámetros de respuesta:
| Parámetro | Descripción |
|:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| Onboarding | Un objeto [`AdaptyOnboarding`](https://android.adapty.io/adapty/com.adapty.models/-adapty-onboarding/) con: el identificador y la configuración del onboarding, Remote Config y varias otras propiedades. |
## Acelerar la obtención del onboarding con el onboarding de la audiencia predeterminada \{#speed-up-onboarding-fetching-with-default-audience-onboarding\}
Normalmente, los onboardings se obtienen casi al instante, por lo que no tienes que preocuparte por acelerar este proceso. Sin embargo, cuando tienes muchas audiencias y onboardings y tus usuarios tienen una conexión a internet débil, obtener un onboarding puede tardar más de lo deseable. En esas situaciones, puede que quieras mostrar un onboarding por defecto para garantizar una experiencia fluida en lugar de no mostrar ninguno.
Para abordar esto, puedes usar el método `getOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, tal como se detalla en la sección [Obtener onboarding](#fetch-onboarding) anterior.
:::warning
Considera usar `getOnboarding` en lugar de `getOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes:
- **Problemas de compatibilidad**: puede crear problemas al dar soporte a múltiples versiones de la app, lo que requiere diseños compatibles con versiones anteriores o asumir que las versiones más antiguas podrían mostrarse incorrectamente.
- **Sin personalización**: solo muestra contenido para la audiencia "All Users", eliminando la segmentación por país, atribución o atributos personalizados.
Si la mayor velocidad de obtención compensa estos inconvenientes para tu caso de uso, utiliza `getOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getOnboarding` como se describe [arriba](#fetch-onboarding).
:::
```kotlin
Adapty.getOnboardingForDefaultAudience("YOUR_PLACEMENT_ID") { result ->
when (result) {
is AdaptyResult.Success -> {
val onboarding = result.value
// Handle successful onboarding retrieval
}
is AdaptyResult.Error -> {
val error = result.error
// Handle error case
}
}
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
|---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. |
| **locale** | opcional
por defecto: `en`
| El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.
Ejemplo: `en` significa inglés; `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` | Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, los usuarios puede que no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra al desinstalarla o mediante limpieza manual.
El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita anteriormente y los onboardings de respaldo. También usamos CDN para obtener onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no esté disponible. Este sistema está diseñado para asegurarse de que siempre obtengas la versión más reciente de tus onboardings garantizando la fiabilidad incluso cuando la conexión a internet es limitada.
|
---
# File: android-present-onboardings
---
---
title: "Presentar onboardings en Android SDK"
description: "Aprende cómo presentar onboardings en Android para una interacción efectiva con el usuario."
---
Antes de comenzar, asegúrate de que:
1. Has instalado el [SDK de Adapty para Android](sdk-installation-android) 3.8.0 o posterior.
2. Has [creado un onboarding](create-onboarding).
3. Has añadido el onboarding a un [placement](placements).
Si has personalizado un onboarding con el Onboarding Builder, no necesitas preocuparte por renderizarlo en el código de tu app móvil para mostrárselo al usuario. Ese onboarding ya contiene tanto qué mostrar como cómo mostrarlo.
Para mostrar el onboarding visual en la pantalla del dispositivo, primero debes configurarlo. Para ello, llama al método `AdaptyUI.getOnboardingView()` o crea el `OnboardingView` directamente:
```kotlin
val onboardingView = AdaptyUI.getOnboardingView(
activity = this,
viewConfig = onboardingConfig,
eventListener = eventListener
)
```
```kotlin
val onboardingView = AdaptyOnboardingView(activity)
onboardingView.show(
viewConfig = onboardingConfig,
delegate = eventListener
)
```
```java
AdaptyOnboardingView onboardingView = AdaptyUI.getOnboardingView(
activity,
onboardingConfig,
eventListener
);
```
```java
AdaptyOnboardingView onboardingView = new AdaptyOnboardingView(activity);
onboardingView.show(onboardingConfig, eventListener);
```
```xml
```
Una vez que la vista se haya creado correctamente, puedes añadirla a la jerarquía de vistas y mostrarla en la pantalla del dispositivo.
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :-------- | :------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **viewConfig** | requerido | La configuración del onboarding obtenida de `AdaptyUI.getOnboardingConfiguration()` |
| **eventListener** | requerido | Una implementación de `AdaptyOnboardingEventListener` para gestionar los eventos del onboarding. Consulta [Gestión de eventos del onboarding](android-handle-onboarding-events) para más detalles. |
## Cambiar el color del indicador de carga \{#change-loading-indicator-color\}
Puedes sobreescribir el color por defecto del indicador de carga de la siguiente manera:
```xml
```
## Añadir transiciones suaves entre la pantalla de inicio y el onboarding \{#add-smooth-transitions-between-the-splash-screen-and-onboarding\}
Por defecto, entre la pantalla de inicio y el onboarding verás la pantalla de carga hasta que el onboarding esté completamente cargado. Sin embargo, si quieres que la transición sea más fluida, puedes personalizarla y bien extender la pantalla de inicio o mostrar algo diferente.
Para ello, crea `adapty_onboarding_placeholder_view.xml` en `res/layout` y define ahí un placeholder (lo que se mostrará mientras se carga el onboarding).
Si defines un placeholder, el onboarding se cargará en segundo plano y se mostrará automáticamente cuando esté listo.
## Desactivar los márgenes de área segura \{#disable-safe-area-paddings\}
Por defecto, la vista del onboarding aplica automáticamente márgenes de área segura para evitar elementos del sistema como la barra de estado y la barra de navegación. Sin embargo, si quieres desactivar este comportamiento y tener control total sobre el diseño, puedes hacerlo estableciendo el parámetro `safeAreaPaddings` en `false`.
```kotlin
val onboardingView = AdaptyUI.getOnboardingView(
activity = this,
viewConfig = onboardingConfig,
eventListener = eventListener,
safeAreaPaddings = false
)
```
```kotlin
val onboardingView = AdaptyOnboardingView(activity)
onboardingView.show(
viewConfig = onboardingConfig,
delegate = eventListener,
safeAreaPaddings = false
)
```
```java
AdaptyOnboardingView onboardingView = AdaptyUI.getOnboardingView(
activity,
onboardingConfig,
eventListener,
false
);
```
```java
AdaptyOnboardingView onboardingView = new AdaptyOnboardingView(activity);
onboardingView.show(onboardingConfig, eventListener, false);
```
También puedes controlar este comportamiento de forma global añadiendo un recurso booleano a tu app:
```xml
false
```
Cuando `safeAreaPaddings` está en `false`, el onboarding se extenderá a pantalla completa sin ajustes automáticos de márgenes, dándote control total sobre el diseño y permitiendo que el contenido del onboarding use todo el espacio de la pantalla.
## Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\}
:::important
La personalización de cómo se abren los enlaces en los onboardings está disponible a partir del SDK de Adapty v. 3.15.1.
:::
Por defecto, los enlaces en los onboardings se abren en un navegador integrado en la app. Esto ofrece una experiencia fluida al mostrar páginas web dentro de tu aplicación, sin que el usuario tenga que cambiar de app.
Si prefieres abrir los enlaces en un navegador externo, puedes personalizar este comportamiento estableciendo el parámetro `externalUrlsPresentation` en `AdaptyWebPresentation.ExternalBrowser`:
```kotlin
val onboardingConfig = AdaptyUI.getOnboardingConfiguration(
onboarding = onboarding,
externalUrlsPresentation = AdaptyWebPresentation.ExternalBrowser // default – InAppBrowser
)
```
```java
AdaptyOnboardingConfiguration onboardingConfig = AdaptyUI.getOnboardingConfiguration(
onboarding,
AdaptyWebPresentation.ExternalBrowser // default – InAppBrowser
);
```
---
# File: android-handle-onboarding-events
---
---
title: "Gestionar eventos de onboarding en el SDK de Android"
description: "Gestiona los eventos relacionados con el onboarding en Android usando Adapty."
---
Antes de empezar, asegúrate de que:
1. Has instalado el [SDK de Adapty para Android](sdk-installation-android) 3.8.0 o posterior.
2. Has [creado un onboarding](create-onboarding).
3. Has añadido el onboarding a un [placement](placements).
Los onboardings configurados con el builder generan eventos a los que tu app puede responder. A continuación se explica cómo reaccionar ante estos eventos.
Para controlar o monitorizar los procesos que ocurren en la pantalla de onboarding dentro de tu app Android, implementa la interfaz `AdaptyOnboardingEventListener`.
## Acciones personalizadas \{#custom-actions\}
En el builder, puedes añadir una acción **personalizada** a un botón y asignarle un ID. Luego puedes usar ese ID en tu código y gestionarlo como una acción personalizada.
Por ejemplo, si un usuario toca un botón personalizado como **Login** o **Allow notifications**, se activará el método delegado `onCustomAction` con el ID de acción definido en el builder. Puedes crear tus propios IDs, como "allowNotifications".
```kotlin showLineNumbers
class YourActivity : AppCompatActivity() {
private val eventListener = object : AdaptyOnboardingEventListener {
override fun onCustomAction(action: AdaptyOnboardingCustomAction, context: Context) {
when (action.actionId) {
"allowNotifications" -> {
// Request notification permissions
}
}
}
override fun onError(error: AdaptyOnboardingError, context: Context) {
// Handle errors
}
// ... other required delegate methods
}
}
```
Ejemplo de evento (haz clic para expandir)
```json
{
"actionId": "allowNotifications",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
}
}
```
## Cerrar el onboarding \{#closing-onboarding\}
El onboarding se considera cerrado cuando el usuario toca un botón con la acción **Close** asignada. Debes gestionar qué ocurre cuando el usuario cierra el onboarding. Por ejemplo:
:::important
Debes gestionar qué ocurre cuando el usuario cierra el onboarding. Por ejemplo, debes dejar de mostrar el onboarding.
:::
Por ejemplo:
```kotlin
override fun onCloseAction(action: AdaptyOnboardingCloseAction, context: Context) {
// Dismiss the onboarding screen
(context as? Activity)?.onBackPressed()
}
```
Ejemplo de evento (haz clic para expandir)
```json
{
"action_id": "close_button",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "final_screen",
"screen_index": 3,
"total_screens": 4
}
}
```
## Abrir un paywall \{#opening-a-paywall\}
:::tip
Gestiona este evento para abrir un paywall si quieres mostrarlo dentro del onboarding. Si prefieres abrir el paywall una vez que el onboarding se haya cerrado, hay una forma más sencilla: gestiona [`AdaptyOnboardingCloseAction`](#closing-onboarding) y abre el paywall sin depender de los datos del evento.
:::
Si un usuario hace clic en un botón que abre un paywall, recibirás el ID de la acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más fluida de trabajar con paywalls en onboardings es hacer que el ID de acción coincida con el ID del placement del paywall. Así, tras el evento `AdaptyOnboardingOpenPaywallAction`, puedes usar el ID del placement para obtener y abrir el paywall directamente:
```kotlin
override fun onOpenPaywallAction(action: AdaptyOnboardingOpenPaywallAction, context: Context) {
// Get the paywall using the placement ID from the action
Adapty.getPaywall(placementId = action.actionId) { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
// Get the paywall configuration
AdaptyUI.getViewConfiguration(paywall) { result ->
when(result) {
is AdaptyResult.Success -> {
val paywallConfig = result.value
// Create and present the paywall
val paywallView = AdaptyUI.getPaywallView(
activity = this,
viewConfig = paywallConfig,
products,
eventListener = paywallEventListener
)
// Add the paywall view to your layout
binding.container.addView(paywallView)
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
}
```
Ejemplo de evento (haz clic para expandir)
```json
{
"action_id": "premium_offer_1",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "pricing_screen",
"screen_index": 2,
"total_screens": 4
}
}
```
## Finalización de la carga del onboarding \{#finishing-loading-onboarding\}
Cuando el onboarding termina de cargarse, se invocará este método:
```kotlin
override fun onFinishLoading(action: AdaptyOnboardingLoadedAction, context: Context) {
// Handle loading completion
}
```
Ejemplo de evento (haz clic para expandir)
```json
{
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "welcome_screen",
"screen_index": 0,
"total_screens": 4
}
}
```
## Eventos de navegación \{#navigation-events\}
El método `onAnalyticsEvent` se llama cuando ocurren distintos eventos de analítica durante el flujo del onboarding.
El objeto `event` puede ser de uno de los siguientes tipos:
| Tipo | Descripción |
|------------|-------------|
| `OnboardingStarted` | Cuando el onboarding ha terminado de cargarse |
| `ScreenPresented` | Cuando se muestra cualquier pantalla |
| `ScreenCompleted` | Cuando se completa una pantalla. Incluye un `elementId` opcional (identificador del elemento completado) y un `reply` opcional (respuesta del usuario). Se activa cuando el usuario realiza cualquier acción para salir de la pantalla. |
| `SecondScreenPresented` | Cuando se muestra la segunda pantalla |
| `UserEmailCollected` | Se activa cuando se recoge el email del usuario mediante el campo de entrada |
| `OnboardingCompleted` | Se activa cuando el usuario llega a una pantalla con el ID `final`. Si necesitas este evento, asigna el ID `final` a la última pantalla. |
| `Unknown` | Para cualquier tipo de evento no reconocido. Incluye `name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) |
Cada evento incluye información `meta` con los siguientes campos:
| Campo | Descripción |
|------------|-------------|
| `onboardingId` | Identificador único del flujo de onboarding |
| `screenClientId` | Identificador de la pantalla actual |
| `screenIndex` | Posición de la pantalla actual en el flujo |
| `totalScreens` | Número total de pantallas en el flujo |
A continuación se muestra un ejemplo de cómo puedes usar los eventos de analítica para el seguimiento:
```kotlin
override fun onAnalyticsEvent(event: AdaptyOnboardingAnalyticsEvent, context: Context) {
when (event) {
is AdaptyOnboardingAnalyticsEvent.OnboardingStarted -> {
// Track onboarding start
trackEvent("onboarding_started", event.meta)
}
is AdaptyOnboardingAnalyticsEvent.ScreenPresented -> {
// Track screen presentation
trackEvent("screen_presented", event.meta)
}
is AdaptyOnboardingAnalyticsEvent.ScreenCompleted -> {
// Track screen completion with user response
trackEvent("screen_completed", event.meta, event.elementId, event.reply)
}
is AdaptyOnboardingAnalyticsEvent.OnboardingCompleted -> {
// Track successful onboarding completion
trackEvent("onboarding_completed", event.meta)
}
is AdaptyOnboardingAnalyticsEvent.Unknown -> {
// Handle unknown events
trackEvent(event.name, event.meta)
}
// Handle other cases as needed
}
}
```
Ejemplos de eventos (haz clic para expandir)
```javascript
// OnboardingStarted
{
"name": "onboarding_started",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "welcome_screen",
"screen_index": 0,
"total_screens": 4
}
}
// ScreenPresented
{
"name": "screen_presented",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "interests_screen",
"screen_index": 2,
"total_screens": 4
}
}
// ScreenCompleted
{
"name": "screen_completed",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "profile_screen",
"screen_index": 1,
"total_screens": 4
},
"params": {
"element_id": "profile_form",
"reply": "success"
}
}
// SecondScreenPresented
{
"name": "second_screen_presented",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "profile_screen",
"screen_index": 1,
"total_screens": 4
}
}
// UserEmailCollected
{
"name": "user_email_collected",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "profile_screen",
"screen_index": 1,
"total_screens": 4
}
}
// OnboardingCompleted
{
"name": "onboarding_completed",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "final_screen",
"screen_index": 3,
"total_screens": 4
}
}
```
---
# File: android-onboarding-input
---
---
title: "Procesar datos de onboardings en el SDK de Android"
description: "Guarda y usa datos de onboardings en tu app de Android con el SDK de Adapty."
---
Cuando tus usuarios responden a una pregunta de quiz o introducen datos en un campo de texto, se invocará el método `onStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código.
Por ejemplo:
```kotlin
override fun onStateUpdatedAction(action: AdaptyOnboardingStateUpdatedAction, context: Context) {
// Store user preferences or responses
when (val params = action.params) {
is AdaptyOnboardingStateUpdatedParams.Select -> {
// Handle single selection
}
is AdaptyOnboardingStateUpdatedParams.MultiSelect -> {
// Handle multiple selections
}
is AdaptyOnboardingStateUpdatedParams.Input -> {
// Handle text input
}
is AdaptyOnboardingStateUpdatedParams.DatePicker -> {
// Handle date selection
}
}
}
```
Consulta el formato de la acción [aquí](https://android.adapty.io/adapty-ui/com.adapty.ui.onboardings.actions/-adapty-onboarding-state-updated-action/).
Ejemplos de datos guardados (el formato puede variar según tu implementación)
```javascript
// Example of a saved select action
{
"elementId": "preference_selector",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "preferences_screen",
"screenIndex": 1,
"screensTotal": 3
},
"params": {
"type": "select",
"value": {
"id": "option_1",
"value": "premium",
"label": "Premium Plan"
}
}
}
// Example of a saved multi-select action
{
"elementId": "interests_selector",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "interests_screen",
"screenIndex": 2,
"screensTotal": 3
},
"params": {
"type": "multiSelect",
"value": [
{
"id": "interest_1",
"value": "sports",
"label": "Sports"
},
{
"id": "interest_2",
"value": "music",
"label": "Music"
}
]
}
}
// Example of a saved input action
{
"elementId": "name_input",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
},
"params": {
"type": "input",
"value": {
"type": "text",
"value": "John Doe"
}
}
}
// Example of a saved date picker action
{
"elementId": "birthday_picker",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
},
"params": {
"type": "datePicker",
"value": {
"day": 15,
"month": 6,
"year": 1990
}
}
}
```
## Casos de uso \{#use-cases\}
### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\}
Si quieres vincular inmediatamente los datos introducidos con el perfil del usuario y evitar pedirle la misma información dos veces, necesitas [actualizar el perfil del usuario](android-setting-user-attributes) con los datos del campo al gestionar la acción.
Por ejemplo, pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name`, y quieres establecer el valor de ese campo como el nombre de pila del usuario. También les pides que introduzcan su correo electrónico en el campo `email`. En el código de tu app, podría verse así:
```kotlin showLineNumbers
override fun onStateUpdatedAction(action: AdaptyOnboardingStateUpdatedAction, context: Context) {
// Store user preferences or responses
when (val params = action.params) {
is AdaptyOnboardingStateUpdatedParams.Input -> {
// Handle text input
val builder = AdaptyProfileParameters.Builder()
// Map elementId to appropriate profile field
when (action.elementId) {
"name" -> {
when (val inputParams = params.params) {
is AdaptyOnboardingInputParams.Text -> {
builder.withFirstName(inputParams.value)
}
}
}
"email" -> {
when (val inputParams = params.params) {
is AdaptyOnboardingInputParams.Email -> {
builder.withEmail(inputParams.value)
}
}
}
}
Adapty.updateProfile(builder.build()) { error ->
if (error != null) {
// handle the error
}
}
}
}
}
```
### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\}
Con los quizzes en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios tras completar el onboarding.
Por ejemplo, puedes preguntarles sobre su experiencia deportiva y mostrar diferentes CTAs y productos a distintos grupos de usuarios.
1. [Añade un quiz](onboarding-quizzes) en el constructor de onboarding y asigna IDs significativos a sus opciones.
2. Gestiona las respuestas del quiz según sus IDs y [establece atributos personalizados](android-setting-user-attributes) para los usuarios.
```kotlin showLineNumbers
override fun onStateUpdatedAction(action: AdaptyOnboardingStateUpdatedAction, context: Context) {
// Handle quiz responses and set custom attributes
when (val params = action.params) {
is AdaptyOnboardingStateUpdatedParams.Select -> {
// Handle quiz selection
val builder = AdaptyProfileParameters.Builder()
// Map quiz responses to custom attributes
when (action.elementId) {
"experience" -> {
// Set custom attribute 'experience' with the selected value (beginner, amateur, pro)
builder.withCustomAttribute("experience", params.params.value)
}
}
Adapty.updateProfile(builder.build()) { error ->
if (error != null) {
// handle the error
}
}
}
}
}
```
3. [Crea segmentos](segments) para cada valor de atributo personalizado.
4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado.
5. [Muestra un paywall](android-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](android-handle-onboarding-events#opening-a-paywall).
---
# File: android-sdk-call-order
---
---
title: "Orden de llamadas en el SDK de Android"
description: "Evita perder el acceso premium, que falten datos de atribución y errores intermitentes de ADAPTY_NOT_INITIALIZED llamando a los métodos del SDK de Adapty en el orden correcto."
---
`Adapty.activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que finalice, el SDK no tiene estado. Cualquier llamada emitida antes o en paralelo con `activate()` falla con [`ADAPTY_NOT_INITIALIZED`](android-sdk-error-handling).
Si tu app autentica usuarios y recoges un ID de usuario personalizado después del lanzamiento, llama a `Adapty.identify()` en ese momento. No llames a métodos de acción de usuario hasta que se ejecute el callback de finalización de `identify`. Las llamadas que compiten con él devuelven un error en su callback, o recaen sobre el perfil anónimo creado en la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la propiedad de la instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `identify` y sigue trabajando con el perfil anónimo.
Los SDKs de MMP y analíticas (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera sus callbacks de UID antes de llamar a `Adapty.activate`. De lo contrario, el ID del MMP queda en un perfil anónimo temporal y no siempre se transfiere al perfil identificado. Para los detalles específicos de AppsFlyer, consulta [AppsFlyer](appsflyer).
## El orden correcto \{#the-correct-order\}
Tu camino depende de dos cosas: cuándo conoces el ID de usuario personalizado y si usas un SDK de MMP o analíticas.
- **Pasos 2 y 5**: Obligatorios para toda app. Activa el SDK y luego llama a los métodos del SDK.
- **Pasos 1 y 3**: Necesarios solo si integras un SDK de MMP o analíticas (AppsFlyer, Adjust, Branch, PostHog).
- **Paso 4**: Necesario solo si tu app autentica usuarios y recoge el ID de usuario personalizado después del lanzamiento.
Si tienes el ID de usuario personalizado al lanzar la app, pásalo en el `AdaptyConfig.Builder` antes de llamar a `activate()` (paso 2a). Esta ruta nunca crea un perfil anónimo, por lo que el paso 4 no es necesario.
| Paso | Llamada | Cuándo | Notas |
|------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| 1 | Inicializa tu SDK de MMP o analíticas (AppsFlyer, Adjust, PostHog, Branch) | Al lanzar la app, primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerUID`. |
| 2a | `Adapty.activate(context, AdaptyConfig.Builder("KEY").withCustomerUserId(...).build())` | Al lanzar la app, tras el paso 1, si tienes el ID de usuario personalizado | Recomendado. Nunca se crea un perfil anónimo. |
| 2b | `Adapty.activate(context, AdaptyConfig.Builder("KEY").build())` sin `customerUserId` | Al lanzar la app, tras el paso 1, si no tienes el ID de usuario personalizado (o nunca lo recoges) | Adapty crea un perfil anónimo. |
| 3 | `Adapty.setIntegrationIdentifier("appsflyer_id", uid)` para cada MMP | Tras el paso 2, antes de cualquier llamada de acción de usuario | Necesario para que los IDs de MMP queden en el perfil correcto. |
| 4 | `Adapty.identify("YOUR_USER_ID") { error -> ... }` | Tras el paso 3 (o el paso 2 si no hay MMP), antes del paso 5 — solo en la ruta 2b con autenticación | Usa el callback de finalización. Las llamadas concurrentes durante `identify` pueden recaer en el perfil anónimo. |
| 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Tras el paso 4 si llamas a `identify`; si no, tras el paso 3 (o el paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. |
:::important
Saltarse estos pasos provoca pérdida de acceso premium para usuarios que regresan, que falte `appsflyer_id` en los perfiles, y que se devuelvan paywalls para la audiencia equivocada.
:::
## Instalaciones web2app y de embudo web \{#web2app-and-web-funnel-installs\}
Si los usuarios compran en un checkout web (Stripe, Paddle) y luego instalan la app nativa, el primer `activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el ID de usuario personalizado antes del lanzamiento de la app (desde tu flujo de autenticación o el referrer de instalación), pásalo directamente en el `AdaptyConfig.Builder`. De lo contrario, la compra web será invisible en el dispositivo hasta que llames a `identify("YOUR_USER_ID")` y luego a `restorePurchases`.
Para los metadatos que enviar con cada checkout web, consulta:
- [Stripe](stripe)
- [Paddle](paddle)
---
# File: android-optimize-paywall-fetching
---
---
title: "Optimizar la obtención de paywalls en el SDK de Android"
description: "Obtén paywalls de Adapty de forma fiable: sincronización, caché y patrones de respaldo para Android."
---
Una obtención fiable de paywalls en Android hace tres cosas: renderiza rápido, devuelve el paywall dirigido a la audiencia correcta y tiene un respaldo elegante cuando la red es lenta. Las reglas a continuación cubren los patrones de sincronización, caché y respaldo para conseguirlo.
:::tip
Las reglas asumen que `Adapty.activate()` y `Adapty.identify()` ya se han resuelto. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order).
:::
## Reglas y errores comunes \{#rules-and-pitfalls\}
| Haz esto | No hagas esto | Por qué |
|---|---|---|
| Obtén el placement que vas a mostrar. | No precargues todos los placements de forma concurrente al iniciar. | La precarga masiva bloquea el hilo principal y produce una pantalla en negro durante la ráfaga. |
| Llama a `getPaywall` después de que la atribución haya tenido oportunidad de resolverse — por ejemplo, 1–2 segundos después de `activate` o tras que se dispare `setOnProfileUpdatedListener`. | No llames a `getPaywall` en `Application.onCreate()`. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia por defecto y omite silenciosamente los segmentos y la personalización de ASA. |
| Establece un `loadTimeout` y configura un [paywall de respaldo](fallback-paywalls) para cada placement. | No esperes a `getPaywall` indefinidamente. | Sin un tiempo de espera, los usuarios con conectividad deficiente ven una pantalla en blanco hasta que la red responde — o cierran la app. |
Consulta [Obtener paywalls y productos](fetch-paywalls-and-products-android) para la referencia de parámetros `fetchPolicy` y `loadTimeout`, y [Placements](placements) para elegir el placement adecuado.
## Ajustar para conectividad deficiente \{#tune-for-poor-connectivity\}
Para mercados con conectividad consistentemente deficiente (zonas rurales, transporte, regiones afectadas por enrutamiento):
- Establece `fetchPolicy` en `AdaptyPlacementFetchPolicy.ReturnCacheDataElseLoad` en cada obtención excepto la primera.
- Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard.
- Establece `loadTimeout` entre 3 y 5 segundos y acepta el respaldo cuando se agote el tiempo.
- No condicionales la visualización del paywall a `getProfile`. Llama a `getPaywall` de forma independiente para que un perfil lento no bloquee la interfaz.
---
# File: android-test
---
---
title: "Prueba y lanzamiento en Android SDK"
description: "Aprende a verificar el estado de suscripción en tu app Android con Adapty."
---
Si ya has implementado el SDK de Adapty en tu app Android, querrás comprobar que todo está configurado correctamente y que las compras funcionan como se espera. Esto implica probar tanto la integración del SDK como el flujo de compra real con el entorno sandbox de Google Play.
## Prueba tu app \{#test-your-app\}
Para realizar pruebas exhaustivas de tus compras in-app, incluyendo pruebas en sandbox y validación en closed track, consulta nuestra [guía de pruebas](testing-on-android).
## Prepárate para el lanzamiento \{#prepare-for-release\}
Antes de enviar tu app al store, sigue la [Lista de verificación para el lanzamiento](release-checklist) para confirmar que:
- La conexión al store y las notificaciones del servidor están configuradas
- Las compras se completan y se reportan a Adapty
- El acceso se desbloquea y se restaura correctamente
- Se cumplen los requisitos de privacidad y revisión
---
# File: android-sdk-error-handling
---
---
title: "Gestionar errores en el SDK de Android"
description: "Gestiona los errores del SDK de Android de forma eficaz con la guía de solución de problemas de Adapty."
---
Cada error devuelto por el SDK es de tipo `AdaptyError`.
:::tip
**Activa los logs detallados antes de depurar.** La mayoría de los `AdaptyError` envuelven un error subyacente de Play Billing, de red o del backend. Con los logs detallados activados (`Adapty.logLevel = AdaptyLogLevel.VERBOSE` — consulta [Logging](sdk-installation-android#logging)), ese error envuelto se imprime en la consola, lo que normalmente te indica la causa real.
:::
:::important
Si estas soluciones no resuelven tu problema, consulta [Otros problemas](#other-issues) para ver los pasos que debes seguir antes de contactar con el soporte y así ayudarnos a asistirte de forma más eficiente.
:::
| Error | Solución |
|----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| UNKNOWN | Este error indica que ocurrió un error desconocido o inesperado. |
| [ITEM_UNAVAILABLE](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#ITEM_UNAVAILABLE()) | Este error ocurre principalmente durante la fase de pruebas. Puede significar que los productos no están en producción o que el usuario no pertenece al grupo de Testers en Google Play. |
| ADAPTY_NOT_INITIALIZED | El SDK de Adapty no está activado.
Lo más habitual es que ocurra cuando una pantalla de inicio u otro hook de UI temprano llama a métodos de Adapty antes de que `Adapty.activate` haya terminado. El síntoma es intermitente y puede no reproducirse en un emulador porque los tiempos en dispositivos reales son distintos. Espera a que `Adapty.activate` finalice antes de ejecutar cualquier otra llamada al SDK. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order) para ver la secuencia completa. También debes [configurar el SDK de Adapty](sdk-installation-android#activate-adapty-module-of-adapty-sdk) correctamente usando el método `Adapty.activate`. |
| PROFILE_WAS_CHANGED | El perfil del usuario cambió durante la operación.
Esto ocurre cuando se llama a un método mientras `Adapty.identify` todavía está en curso: la llamada en vuelo aterriza en un perfil que está a punto de ser reemplazado y el SDK la rechaza. Espera a que `Adapty.identify` finalice antes de ejecutar otras llamadas al SDK. Consulta [Orden de llamadas en el SDK de Android](android-sdk-call-order). |
| PRODUCT_NOT_FOUND | Este error indica que el producto solicitado para la compra no está disponible en el store. |
| INVALID_JSON | El JSON del paywall de respaldo local no es válido.
Corrige tu paywall en inglés predeterminado y luego reemplaza los paywalls locales no válidos. Consulta el tema [Personalizar el paywall con Remote Config](customize-paywall-with-remote-config) para saber cómo corregir un paywall, y [Definir paywalls de respaldo locales](fallback-paywalls) para saber cómo reemplazar los paywalls locales.
|
| CURRENT_SUBSCRIPTION_TO_UPDATE
\_NOT_FOUND_IN_HISTORY
| La suscripción original que debe reemplazarse no se encontró en las suscripciones activas. |
| [BILLING_SERVICE_TIMEOUT](https://developer.android.com/google/play/billing/errors#service_timeout_error_code_-3) | Este error indica que la solicitud alcanzó el tiempo de espera máximo antes de que Google Play pudiera responder. Puede deberse, por ejemplo, a un retraso en la ejecución de la acción solicitada por la llamada a la Play Billing Library. |
| [FEATURE_NOT_SUPPORTED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#FEATURE_NOT_SUPPORTED()) | La función solicitada no es compatible con la Play Store en el dispositivo actual. |
| [BILLING_SERVICE_DISCONNECTED](https://developer.android.com/google/play/billing/errors#service_disconnected_error_code_-1) | Este error indica que la conexión de la app cliente con el servicio de Google Play Store a través del `BillingClient` se ha interrumpido. |
| [BILLING_SERVICE_UNAVAILABLE](https://developer.android.com/google/play/billing/errors#service_unavailable_error_code_2) | Este error indica que el servicio de facturación de Google Play no está disponible en este momento. En la mayoría de los casos significa que hay un problema de conexión de red entre el dispositivo cliente y los servicios de Google Play Billing. |
| [BILLING_UNAVAILABLE](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) | Este error indica que ocurrió un problema de facturación durante el proceso de compra. Las posibles causas son:
1. La app de Play Store en el dispositivo del usuario no está instalada o está desactualizada.
2. El usuario se encuentra en un país no compatible.
3. El usuario forma parte de una cuenta empresarial cuyo administrador ha desactivado las compras.
4. Google Play no pudo cargar el método de pago del usuario (por ejemplo, una tarjeta de crédito caducada).
5. El usuario no ha iniciado sesión en la app de Play Store.
|
| [DEVELOPER_ERROR](https://developer.android.com/google/play/billing/errors#developer_error) | Este error indica que estás usando una API de forma incorrecta. |
| [BILLING_ERROR](https://developer.android.com/google/play/billing/errors#error_error_code_6) | Este error indica un problema interno del propio Google Play. |
| [ITEM_ALREADY_OWNED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#ITEM_ALREADY_OWNED()) | El producto ya fue comprado anteriormente. |
| [ITEM_NOT_OWNED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#ITEM_NOT_OWNED()) | Este error indica que la acción solicitada sobre el ítem falló porque el usuario no es su propietario. |
| [BILLING_NETWORK_ERROR](https://developer.android.com/google/play/billing/errors#network_error_error_code_12) | Este error indica que hubo un problema con la conexión de red entre el dispositivo y los sistemas de Play. |
| NO_PRODUCT_IDS_FOUND | Este error indica que ninguno de los productos del paywall está disponible en el store.
Si encuentras este error, sigue los pasos a continuación para resolverlo:
- Comprueba que todos los productos se hayan añadido al Adapty Dashboard.
- Asegúrate de que el **Package name** de tu app coincide con el de Google Play Console.
- Verifica que los identificadores de producto de los stores coincidan con los que has añadido al Dashboard. Ten en cuenta que los identificadores no deben incluir el Bundle ID, a menos que ya esté incluido en el store.
- Confirma que el estado de pago de la app sea **Active** en la configuración fiscal de Google. Asegúrate de que tu información fiscal esté actualizada y que tus certificados sean válidos.
- Comprueba que haya una cuenta bancaria vinculada a la app para que sea elegible para la monetización.
- Verifica si los productos están disponibles en tu región.
- Asegúrate de que tu app esté en uno de los tracks de prueba. El track **Internal testing** es la opción más sencilla, ya que no requiere revisión y mantiene la app oculta para los clientes.
|
| NO_PURCHASES_TO_RESTORE | Este error indica que Google Play no encontró ninguna compra para restaurar. |
| AUTHENTICATION_ERROR | Debes [configurar el SDK de Adapty](sdk-installation-android#activate-adapty-module-of-adapty-sdk) correctamente usando el método `Adapty.activate`. |
| BAD_REQUEST | Solicitud incorrecta.
Asegúrate de haber completado todos los pasos necesarios para [integrarte con Google Play](google-play-store-connection-configuration). |
| SERVER_ERROR | Error del servidor. |
| REQUEST_FAILED | Este error indica un problema de red que no se puede definir con precisión. |
| DECODING_FAILED | No fue posible decodificar la respuesta.
Revisa tu código y asegúrate de que los parámetros que envías son válidos. Por ejemplo, este error puede indicar que estás usando una clave API no válida. |
| ANALYTICS_DISABLED | No podemos gestionar eventos de análisis porque los has [desactivado](analytics-integration#disabling-external-analytics-for-a-specific-customer). |
| WRONG_PARAMETER | Este error indica que alguno de tus parámetros no es correcto: está en blanco cuando no puede estarlo, es del tipo incorrecto, etc. |
## Otros problemas \{#other-issues\}
Si todavía no has encontrado una solución, los siguientes pasos pueden ser:
- **Actualizar el SDK a la última versión**: Siempre recomendamos actualizar a las últimas versiones del SDK, ya que son más estables e incluyen correcciones de problemas conocidos.
- **Contactar al equipo de soporte u obtener ayuda de otros desarrolladores** en el [foro de soporte](https://adapty.featurebase.app/).
- **Contactar al equipo de soporte a través de [support@adapty.io](mailto:support@adapty.io) o por el chat**: Si no puedes actualizar el SDK o la actualización no resolvió el problema, contacta a nuestro equipo de soporte. Ten en cuenta que tu problema se resolverá más rápido si [activas el registro detallado](sdk-installation-android#logging) y compartes los logs con el equipo. También puedes adjuntar fragmentos de código relevantes.
---
# File: migration-to-android-312
---
---
title: "Migrar el SDK de Adapty Android a v. 3.12"
description: "Migra al SDK de Adapty Android v3.12 para mejorar el rendimiento y acceder a nuevas funciones de monetización."
---
En el SDK de Adapty 3.12.0, hemos eliminado el método `logShowOnboarding` del SDK.
Si has estado usando este método, dejará de estar disponible al actualizar el SDK a la versión 3.12 o posterior.
En su lugar, puedes [crear onboardings en el constructor de onboardings sin código de Adapty](onboardings). El análisis de estos onboardings se registra automáticamente y dispones de numerosas opciones de personalización.
---
# File: migration-to-android-310
---
---
title: "Guía de migración al SDK de Adapty para Android 3.10.0"
description: ""
---
El SDK de Adapty 3.10.0 es una versión mayor que introduce mejoras que pueden requerir algunos pasos de migración de tu parte:
1. `AdaptyUiPersonalizedOfferResolver` ha sido eliminado. Si lo estás usando, pásalo en el callback `onAwaitingPurchaseParams`.
2. Actualiza la firma del método `onAwaitingSubscriptionUpdateParams` para los paywalls del Paywall Builder.
## Actualizar el callback de parámetros de compra \{#update-purchase-parameters-callback\}
El método `onAwaitingSubscriptionUpdateParams` ha sido renombrado a `onAwaitingPurchaseParams` y ahora usa `AdaptyPurchaseParameters` en lugar de `AdaptySubscriptionUpdateParameters`. Esto te permite especificar parámetros de reemplazo de suscripción (crossgrade) e indicar si el precio es personalizado ([más información](https://developer.android.com/google/play/billing/integrate#personalized-price)), junto con otros parámetros de compra.
```diff showLineNumbers
- override fun onAwaitingSubscriptionUpdateParams(
- product: AdaptyPaywallProduct,
- context: Context,
- onSubscriptionUpdateParamsReceived: SubscriptionUpdateParamsCallback,
- ) {
- onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters(...))
- }
+ override fun onAwaitingPurchaseParams(
+ product: AdaptyPaywallProduct,
+ context: Context,
+ onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback,
+ ): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked {
+ onPurchaseParamsReceived(
+ AdaptyPurchaseParameters.Builder()
+ .withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...))
+ .withOfferPersonalized(true)
+ .build()
+ )
+ return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked
+ }
```
Si no se necesitan parámetros adicionales, puedes usar simplemente:
```kotlin showLineNumbers
+ override fun onAwaitingPurchaseParams(
product: AdaptyPaywallProduct,
context: Context,
onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback,
): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked {
onPurchaseParamsReceived(AdaptyPurchaseParameters.Empty)
return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked
}
```
---
# File: migration-to-android-sdk-34
---
---
title: "Migrar el SDK de Adapty para Android a la versión 3.4"
description: "Migra al SDK de Adapty para Android v3.4 para obtener mejor rendimiento y nuevas funciones de monetización."
---
El SDK de Adapty 3.4.0 es una versión principal que introduce mejoras que requieren pasos de migración por tu parte.
## Actualizar los archivos de paywall de respaldo \{#update-fallback-paywall-files\}
Actualiza los archivos de paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK:
1. [Descarga los archivos de paywall de respaldo actualizados](fallback-paywalls) desde el Adapty Dashboard.
2. [Reemplaza los paywalls de respaldo existentes en tu app](android-use-fallback-paywalls) con los nuevos archivos.
## Actualizar la implementación del Modo Observador \{#update-implementation-of-observer-mode\}
Si usas el Modo Observador, asegúrate de actualizar su implementación.
En versiones anteriores, tenías que restaurar las compras para que Adapty pudiera reconocer las transacciones realizadas a través de tu propia infraestructura, ya que Adapty no tenía acceso directo a ellas en el Modo Observador. Si usabas paywalls, también debías asociar manualmente cada transacción con el paywall que la había iniciado.
En la nueva versión, debes reportar explícitamente cada transacción para que Adapty pueda reconocerla. Si usas paywalls, también necesitas pasar el ID de variación para vincular la transacción con el paywall utilizado.
:::warning
**¡No omitas el reporte de transacciones!**
Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones.
:::
```diff showLineNumbers
- Adapty.restorePurchases { result ->
- if (result is AdaptyResult.Success) {
- // success
- }
- }
-
- Adapty.setVariationId(transactionId, variationId) { error ->
- if (error == null) {
- // success
- }
- }
+ val transactionInfo = TransactionInfo.fromPurchase(purchase)
+
+ Adapty.reportTransaction(transactionInfo, variationId) { result ->
+ if (result is AdaptyResult.Success) {
+ // success
+ }
+ }
```
```diff showLineNumbers
- Adapty.restorePurchases(result -> {
- if (result instanceof AdaptyResult.Success) {
- // success
- }
- });
-
- Adapty.setVariationId(transactionId, variationId, error -> {
- if (error == null) {
- // success
- }
- });
+ TransactionInfo transactionInfo = TransactionInfo.fromPurchase(purchase);
+
+ Adapty.reportTransaction(transactionInfo, variationId, result -> {
+ if (result instanceof AdaptyResult.Success) {
+ // success
+ }
+ });
```
---
# File: migration-to-android330
---
---
title: "Migrar el SDK de Adapty para Android a la v. 3.3"
description: "Migra al SDK de Adapty para Android v3.3 para mejorar el rendimiento y acceder a nuevas funciones de monetización."
---
El SDK de Adapty 3.3.0 es una versión mayor que incluye mejoras que, no obstante, pueden requerir algunos pasos de migración por tu parte.
1. Actualiza cómo gestionas las compras en paywalls que no se han creado con Paywall Builder. Deja de procesar los códigos de error `USER_CANCELED` y `PENDING_PURCHASE`. Una compra cancelada ya no se considera un error y ahora aparecerá en los resultados de compra sin error.
2. Sustituye los eventos `onPurchaseCanceled` y `onPurchaseSuccess` por el nuevo evento `onPurchaseFinished` para paywalls creados con Paywall Builder. Este cambio se debe al mismo motivo: las compras canceladas ya no se tratan como errores y se incluirán en los resultados de compra sin error.
3. Cambia la firma del método `onAwaitingSubscriptionUpdateParams` para paywalls de Paywall Builder.
4. Actualiza el método utilizado para proporcionar paywalls de respaldo si pasas el URI del archivo directamente.
5. Actualiza las configuraciones de integración para Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase y Google Analytics, Mixpanel, OneSignal, Pushwoosh.
## Actualizar la compra \{#update-making-purchase\}
Las compras canceladas y pendientes anteriormente se consideraban errores y devolvían los códigos `USER_CANCELED` y `PENDING_PURCHASE`, respectivamente.
Ahora se usa una nueva clase `AdaptyPurchaseResult` para indicar compras canceladas, exitosas y pendientes. Actualiza el código de compra de la siguiente manera:
```diff
Adapty.makePurchase(activity, product) { result ->
when (result) {
is AdaptyResult.Success -> {
- val info = result.value
- val profile = info?.profile
-
- if (profile?.accessLevels?.get("YOUR_ACCESS_LEVEL")?.isActive == true) {
- // Grant access to the paid features
- }
+ when (val purchaseResult = result.value) {
+ is AdaptyPurchaseResult.Success -> {
+ val profile = purchaseResult.profile
+ if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) {
+ // Grant access to the paid features
+ }
+ }
+
+ is AdaptyPurchaseResult.UserCanceled -> {
+ // Handle the case where the user canceled the purchase
+ }
+
+ is AdaptyPurchaseResult.Pending -> {
+ // Handle deferred purchases (e.g., the user will pay offline with cash
+ }
+ }
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
```
Para ver el ejemplo de código completo, consulta la página [Realizar compras en la app móvil](android-making-purchases#make-purchase).
## Modificar eventos de compra en el Paywall Builder \{#modify-paywall-builder-purchase-events\}
1. Añade el evento `onPurchaseFinished`:
```diff showLineNumbers
+ public override fun onPurchaseFinished(
+ purchaseResult: AdaptyPurchaseResult,
+ product: AdaptyPaywallProduct,
+ context: Context,
+ ) {
+ when (purchaseResult) {
+ is AdaptyPurchaseResult.Success -> {
+ // Otorgar acceso a las funciones de pago
+ }
+ is AdaptyPurchaseResult.UserCanceled -> {
+ // Manejar el caso en que el usuario canceló la compra
+ }
+ is AdaptyPurchaseResult.Pending -> {
+ // Manejar compras diferidas (p. ej., el usuario pagará en efectivo fuera de línea)
+ }
+ }
+ }
```
Para ver el ejemplo de código completo, consulta [Compra exitosa, cancelada o pendiente](android-handling-events#successful-canceled-or-pending-purchase) y la descripción del evento.
2. Elimina el procesamiento del evento `onPurchaseCancelled`:
```diff showLineNumbers
- public override fun onPurchaseCanceled(
- product: AdaptyPaywallProduct,
- context: Context,
- ) {}
```
3. Elimina `onPurchaseSuccess`:
```diff showLineNumbers
- public override fun onPurchaseSuccess(
- profile: AdaptyProfile?,
- product: AdaptyPaywallProduct,
- context: Context,
- ) {
- // Your logic on successful purchase
- }
```
## Cambiar la firma del método onAwaitingSubscriptionUpdateParams \{#change-the-signature-of-on-awaiting-subscription-update-params-method\}
Ahora, si se compra una nueva suscripción mientras otra sigue activa, llama a `onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters...))` si la nueva suscripción debe reemplazar a la que está activa, o a `onSubscriptionUpdateParamsReceived(null)` si la suscripción activa debe permanecer activa y la nueva debe añadirse por separado:
```diff showLineNumbers
- public override fun onAwaitingSubscriptionUpdateParams(
- product: AdaptyPaywallProduct,
- context: Context,
- ): AdaptySubscriptionUpdateParameters? {
- return AdaptySubscriptionUpdateParameters(...)
- }
+ public override fun onAwaitingSubscriptionUpdateParams(
+ product: AdaptyPaywallProduct,
+ context: Context,
+ onSubscriptionUpdateParamsReceived: SubscriptionUpdateParamsCallback,
+ ) {
+ onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters(...))
+ }
```
Consulta la sección [Actualizar suscripción](android-handling-events#upgrade-subscription) de la documentación para ver el ejemplo de código completo.
## Actualización del método para proporcionar paywalls de respaldo \{#update-providing-fallback-paywalls\}
Si pasas una URI de archivo para proporcionar paywalls de respaldo, actualiza la forma en que lo haces de la siguiente manera:
```diff showLineNumbers
val fileUri: Uri = // Get the URI for the file with fallback paywalls
- Adapty.setFallbackPaywalls(fileUri, callback)
+ Adapty.setFallbackPaywalls(FileLocation.fromFileUri(fileUri), callback)
```
```diff showLineNumbers
Uri fileUri = // Obtén el URI del archivo con los paywalls de respaldo
- Adapty.setFallbackPaywalls(fileUri, callback);
+ Adapty.setFallbackPaywalls(FileLocation.fromFileUri(fileUri), callback);
```
## Actualizar la configuración del SDK de integraciones de terceros \{#update-third-party-integration-sdk-configuration\}
Para garantizar que las integraciones funcionen correctamente con el SDK de Android de Adapty 3.3.0 y versiones posteriores, actualiza las configuraciones del SDK para las siguientes integraciones tal como se describe en las secciones a continuación.
### Adjust
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Adjust](adjust#connect-your-app-to-adjust).
```diff showLineNumbers
- Adjust.getAttribution { attribution ->
- if (attribution == null) return@getAttribution
-
- Adjust.getAdid { adid ->
- if (adid == null) return@getAdid
-
- Adapty.updateAttribution(attribution, AdaptyAttributionSource.ADJUST, adid) { error ->
- // Handle the error
- }
- }
- }
+ Adjust.getAdid { adid ->
+ if (adid == null) return@getAdid
+
+ Adapty.setIntegrationIdentifier("adjust_device_id", adid) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
+ }
+
+ Adjust.getAttribution { attribution ->
+ if (attribution == null) return@getAttribution
+
+ Adapty.updateAttribution(attribution, "adjust") { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
+ }
```
```diff showLineNumbers
val config = AdjustConfig(context, adjustAppToken, environment)
config.setOnAttributionChangedListener { attribution ->
attribution?.let { attribution ->
- Adapty.updateAttribution(attribution, AdaptyAttributionSource.ADJUST) { error ->
+ Adapty.updateAttribution(attribution, "adjust") { error ->
if (error != null) {
// Handle the error
}
}
}
}
Adjust.onCreate(config)
```
### AirBridge
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AirBridge](airbridge#connect-your-app-to-airbridge).
```diff showLineNumbers
Airbridge.getDeviceInfo().getUUID(object: AirbridgeCallback.SimpleCallback() {
override fun onSuccess(result: String) {
- val params = AdaptyProfileParameters.Builder()
- .withAirbridgeDeviceId(result)
- .build()
- Adapty.updateProfile(params) { error ->
- if (error != null) {
- // Handle the error
- }
- }
+ Adapty.setIntegrationIdentifier("airbridge_device_id", result) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
}
override fun onFailure(throwable: Throwable) {
}
})
```
### Amplitude
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Amplitude](amplitude#sdk-configuration).
```diff showLineNumbers
// For Amplitude maintenance SDK (obsolete)
val amplitude = Amplitude.getInstance()
val amplitudeDeviceId = amplitude.getDeviceId()
val amplitudeUserId = amplitude.getUserId()
//for actual Amplitude Kotlin SDK
val amplitude = Amplitude(
Configuration(
apiKey = AMPLITUDE_API_KEY,
context = applicationContext
)
)
val amplitudeDeviceId = amplitude.store.deviceId
val amplitudeUserId = amplitude.store.userId
//
- val params = AdaptyProfileParameters.Builder()
- .withAmplitudeDeviceId(amplitudeDeviceId)
- .withAmplitudeUserId(amplitudeUserId)
- .build()
- Adapty.updateProfile(params) { error ->
- if (error != null) {
- // Handle the error
- }
- }
+ Adapty.setIntegrationIdentifier("amplitude_user_id", amplitudeUserId) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
+ Adapty.setIntegrationIdentifier("amplitude_device_id", amplitudeDeviceId) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
```
### AppMetrica
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AppMetrica](appmetrica#sdk-configuration).
```diff showLineNumbers
val startupParamsCallback = object: StartupParamsCallback {
override fun onReceive(result: StartupParamsCallback.Result?) {
val deviceId = result?.deviceId ?: return
- val params = AdaptyProfileParameters.Builder()
- .withAppmetricaDeviceId(deviceId)
- .withAppmetricaProfileId("YOUR_ADAPTY_CUSTOMER_USER_ID")
- .build()
- Adapty.updateProfile(params) { error ->
- if (error != null) {
- // Handle the error
- }
- }
+ Adapty.setIntegrationIdentifier("appmetrica_device_id", deviceId) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
+
+ Adapty.setIntegrationIdentifier("appmetrica_profile_id", "YOUR_ADAPTY_CUSTOMER_USER_ID") { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
}
override fun onRequestError(
reason: StartupParamsCallback.Reason,
result: StartupParamsCallback.Result?
) {
// Handle the error
}
}
AppMetrica.requestStartupParams(context, startupParamsCallback, listOf(StartupParamsCallback.APPMETRICA_DEVICE_ID))
```
### AppsFlyer
Actualiza el código de tu app como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con AppsFlyer](appsflyer#connect-your-app-to-appsflyer).
```diff showLineNumbers
val conversionListener: AppsFlyerConversionListener = object : AppsFlyerConversionListener {
override fun onConversionDataSuccess(conversionData: Map) {
- Adapty.updateAttribution(
- conversionData,
- AdaptyAttributionSource.APPSFLYER,
- AppsFlyerLib.getInstance().getAppsFlyerUID(context)
- ) { error ->
- if (error != null) {
- // Handle the error
- }
- }
+ val uid = AppsFlyerLib.getInstance().getAppsFlyerUID(context)
+ Adapty.setIntegrationIdentifier("appsflyer_id", uid) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
+ Adapty.updateAttribution(conversionData, "appsflyer") { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
}
}
```
### Branch
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Branch](branch#connect-your-app-to-branch).
```diff showLineNumbers
// Login and update attribution
Branch.getAutoInstance(this)
.setIdentity("YOUR_USER_ID") { referringParams, error ->
referringParams?.let { data ->
- Adapty.updateAttribution(data, AdaptyAttributionSource.BRANCH) { error ->
- if (error != null) {
- // Handle the error
- }
- }
+ Adapty.updateAttribution(data, "branch") { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
}
}
// Logout
Branch.getAutoInstance(context).logout()
```
### Facebook Ads
Actualiza el código de tu aplicación móvil como se indica a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Facebook Ads](facebook-ads#connect-your-app-to-facebook-ads).
```diff showLineNumbers
- val builder = AdaptyProfileParameters.Builder()
- .withFacebookAnonymousId(AppEventsLogger.getAnonymousAppDeviceGUID(context))
-
- Adapty.updateProfile(builder.build()) { error ->
- if (error != null) {
- // Handle the error
- }
- }
+ Adapty.setIntegrationIdentifier(
+ "facebook_anonymous_id",
+ AppEventsLogger.getAnonymousAppDeviceGUID(context)
+ ) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
```
### Firebase y Google Analytics \{#firebase-and-google-analytics\}
Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Firebase y Google Analytics](firebase-and-google-analytics).
```diff showLineNumbers
// After Adapty.activate()
FirebaseAnalytics.getInstance(context).appInstanceId.addOnSuccessListener { appInstanceId ->
- Adapty.updateProfile(
- AdaptyProfileParameters.Builder()
- .withFirebaseAppInstanceId(appInstanceId)
- .build()
- ) { error ->
- if (error != null) {
- // Handle the error
- }
- }
+ Adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
}
```
```diff showLineNumbers
// Después de Adapty.activate()
- FirebaseAnalytics.getInstance(context).getAppInstanceId().addOnSuccessListener(appInstanceId -> {
- AdaptyProfileParameters params = new AdaptyProfileParameters.Builder()
- .withFirebaseAppInstanceId(appInstanceId)
- .build();
-
- Adapty.updateProfile(params, error -> {
- if (error != null) {
- // Handle the error
- }
- });
- });
+ FirebaseAnalytics.getInstance(context).getAppInstanceId().addOnSuccessListener(appInstanceId -> {
+ Adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId, error -> {
+ if (error != null) {
+ // Handle the error
+ }
+ });
+ });
```
### Mixpanel
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con Mixpanel](mixpanel#sdk-configuration).
```diff showLineNumbers
- val params = AdaptyProfileParameters.Builder()
- .withMixpanelUserId(mixpanelAPI.distinctId)
- .build()
-
- Adapty.updateProfile(params) { error ->
- if (error != null) {
- // Handle the error
- }
- }
+ Adapty.setIntegrationIdentifier("mixpanel_user_id", mixpanelAPI.distinctId) { error ->
+ if (error != null) {
+ // Handle the error
+ }
+ }
```
### OneSignal
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo de código completo, consulta la [configuración del SDK para la integración con OneSignal](onesignal#sdk-configuration).
```diff showLineNumbers
// SubscriptionID
val oneSignalSubscriptionObserver = object: IPushSubscriptionObserver {
override fun onPushSubscriptionChange(state: PushSubscriptionChangedState) {
- val params = AdaptyProfileParameters.Builder()
- .withOneSignalSubscriptionId(state.current.id)
- .build()
-
- Adapty.updateProfile(params) { error ->
+ Adapty.setIntegrationIdentifier("one_signal_subscription_id", state.current.id) { error ->
if (error != null) {
// Handle the error
}
}
}
}
```
```diff showLineNumbers
// SubscriptionID
IPushSubscriptionObserver oneSignalSubscriptionObserver = state -> {
- AdaptyProfileParameters params = new AdaptyProfileParameters.Builder()
- .withOneSignalSubscriptionId(state.getCurrent().getId())
- .build();
- Adapty.updateProfile(params, error -> {
+ Adapty.setIntegrationIdentifier("one_signal_subscription_id", state.getCurrent().getId(), error -> {
if (error != null) {
// Handle the error
}
});
};
```
```diff showLineNumbers
// PlayerID
val osSubscriptionObserver = OSSubscriptionObserver { stateChanges ->
stateChanges?.to?.userId?.let { playerId ->
- val params = AdaptyProfileParameters.Builder()
- .withOneSignalPlayerId(playerId)
- .build()
-
- Adapty.updateProfile(params) { error ->
+ Adapty.setIntegrationIdentifier("one_signal_player_id", playerId) { error ->
if (error != null) {
// Handle the error
}
- }
}
}
```
```diff showLineNumbers
// PlayerID
OSSubscriptionObserver osSubscriptionObserver = stateChanges -> {
OSSubscriptionState to = stateChanges != null ? stateChanges.getTo() : null;
String playerId = to != null ? to.getUserId() : null;
if (playerId != null) {
- AdaptyProfileParameters params1 = new AdaptyProfileParameters.Builder()
- .withOneSignalPlayerId(playerId)
- .build();
-
- Adapty.updateProfile(params1, error -> {
+ Adapty.setIntegrationIdentifier("one_signal_player_id", playerId, error -> {
if (error != null) {
// Handle the error
}
- });
}
};
```
### Pushwoosh
Actualiza el código de tu app móvil como se muestra a continuación. Para ver el ejemplo completo de código, consulta la [configuración del SDK para la integración con Pushwoosh](pushwoosh#sdk-configuration).
```diff showLineNumbers
- val params = AdaptyProfileParameters.Builder()
- .withPushwooshHwid(Pushwoosh.getInstance().hwid)
- .build()
- Adapty.updateProfile(params) { error ->
+ Adapty.setIntegrationIdentifier("pushwoosh_hwid", Pushwoosh.getInstance().hwid) { error ->
if (error != null) {
// Handle the error
}
}
```
```diff showLineNumbers
- AdaptyProfileParameters params = new AdaptyProfileParameters.Builder()
- .withPushwooshHwid(Pushwoosh.getInstance().getHwid())
- .build();
-
- Adapty.updateProfile(params, error -> {
+ Adapty.setIntegrationIdentifier("pushwoosh_hwid", Pushwoosh.getInstance().getHwid(), error -> {
if (error != null) {
// Handle the error
}
});
```
---
# File: migration-to-android-sdk-v3
---
---
title: "Migrar el SDK de Adapty para Android a la v. 3.0"
description: "Migra al SDK de Adapty para Android v3.0 para mejorar el rendimiento y acceder a nuevas funciones de monetización."
---
El SDK de Adapty v.3.0 incluye soporte para el nuevo y emocionante [Adapty Paywall Builder](adapty-paywall-builder), la nueva versión de la herramienta visual sin código para crear paywalls. Con su máxima flexibilidad y ricas capacidades de diseño, tus paywalls serán más efectivos y rentables.
Los SDKs de Adapty se distribuyen como BoM (Bill of Materials), lo que garantiza que las versiones del SDK de Adapty y del SDK de AdaptyUI en tu aplicación se mantengan sincronizadas.
Para migrar a la v3.0, actualiza tu código de la siguiente manera:
```diff showLineNumbers
dependencies {
...
- implementation 'io.adapty:android-sdk:2.11.5'
- implementation 'io.adapty:android-ui:2.11.3'
+ implementation platform('io.adapty:adapty-bom:3.0.4')
+ implementation 'io.adapty:android-sdk'
+ implementation 'io.adapty:android-ui'
}
```
```diff showLineNumbers
dependencies {
...
- implementation("io.adapty:android-sdk:2.11.5")
- implementation("io.adapty:android-ui:2.11.3")
+ implementation(platform("io.adapty:adapty-bom:3.0.4"))
+ implementation("io.adapty:android-sdk")
+ implementation("io.adapty:android-ui")
}
```
```diff showLineNumbers
//libs.versions.toml
[versions]
..
- adapty = "2.11.5"
- adaptyUi = "2.11.3"
+ adaptyBom = "3.0.4"
[libraries]
..
- adapty = { group = "io.adapty", name = "android-sdk", version.ref = "adapty" }
- adapty-ui = { group = "io.adapty", name = "android-ui", version.ref = "adaptyUi" }
+ adapty-bom = { module = "io.adapty:adapty-bom", version.ref = "adaptyBom" }
+ adapty = { module = "io.adapty:android-sdk" }
+ adapty-ui = { module = "io.adapty:android-ui" }
//module-level build.gradle.kts
dependencies {
...
+ implementation(libs.adapty.bom)
implementation(libs.adapty)
implementation(libs.adapty.ui)
}
```
---
# End of Documentation
_Generated on: 2026-05-15T20:13:57.456Z_
_Successfully processed: 39/39 files_