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

<Details>
<summary>Ejemplo de evento (haz clic para expandir)</summary>

```javascript
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}
```
</Details>

#### Compra iniciada \{#started-purchase\}

Si un usuario inicia el proceso de compra, se invocará este método:

```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseStarted(
    product: AdaptyPaywallProduct,
    context: Context,
) {}
```

<Details>
<summary>Ejemplo de evento (haz clic para expandir)</summary>

```javascript
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}
```
</Details>

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()
}
```

<Details>
<summary>Ejemplos de eventos (haz clic para expandir)</summary>

```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"
  }
}
```
</Details>

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

<Details>
<summary>Ejemplo de evento (haz clic para expandir)</summary>

```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"
  }
}
```
</Details>

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

<Details>
<summary>Ejemplos de eventos (haz clic para expandir)</summary>

```javascript
// Successful navigation
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": null
}

// Failed navigation
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "web_navigation_failed",
    "message": "Failed to open web paywall",
    "details": {
      "underlyingError": "Browser unavailable"
    }
  }
}
```
</Details>

#### 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,
) {}
```

<Details>
<summary>Ejemplo de evento (haz clic para expandir)</summary>

```javascript
{
  "profile": {
    "accessLevels": {
      "premium": {
        "id": "premium",
        "isActive": true,
        "expiresAt": "2024-02-15T10:30:00Z"
      }
    },
    "subscriptions": [
      {
        "vendorProductId": "premium_monthly",
        "isActive": true,
        "expiresAt": "2024-02-15T10:30:00Z"
      }
    ]
  }
}
```
</Details>

Recomendamos cerrar la pantalla si el usuario tiene el `accessLevel` requerido. Consulta el tema [Estado de la suscripción](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,
) {}
```

<Details>
<summary>Ejemplo de evento (haz clic para expandir)</summary>

```javascript
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}
```
</Details>

#### Actualizar suscripción \{#upgrade-subscription\}

<Tabs groupId="current-os" queryString>
<TabItem value="new" label="SDK version 3.10.0 or later" default>

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()
```

</TabItem>
<TabItem value="old" label="SDK version earlier than 3.10.0" default>

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(...))
}
```

</TabItem>
</Tabs>

<Details>
<summary>Ejemplo de evento (haz clic para expandir)</summary>

```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"
  }
}
```
</Details>

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

<Details>
<summary>Ejemplo de evento (haz clic para expandir)</summary>

```javascript
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}
```
</Details>

Si devuelves `true`, AdaptyUI repetirá la solicitud 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,
) {}
```

<Details>
<summary>Ejemplo de evento (haz clic para expandir)</summary>

```javascript
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}
```
</Details>

En una situación normal, estos errores no deberían producirse, así que si te encuentras con alguno, comunícanoslo.