Gestionar eventos de flow y paywall - Unity

Esta guía cubre el manejo de eventos para compras, restauraciones, selección de productos y renderizado de flows. También debes implementar el manejo de botones (cerrar el flow, abrir enlaces, etc.). Consulta nuestra guía sobre cómo gestionar las acciones del flow para más detalles.

Los flows y paywalls configurados con el Flow Builder o el Paywall Builder no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan ciertos eventos a los que tu aplicación puede reaccionar. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.), así como notificaciones sobre acciones relacionadas con compras. A continuación encontrarás cómo responder a estos eventos.

¿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, que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.

Gestión de eventos

Para controlar o monitorear los procesos que ocurren en la pantalla del flow dentro de tu aplicación móvil, implementa la interfaz IAdaptyFlowsEventsListener y regístrala con Adapty.SetFlowsEventsListener():

using UnityEngine;
using AdaptySDK;

public class FlowEventsHandler : MonoBehaviour, IAdaptyFlowsEventsListener
{
    void Start()
    {
        Adapty.SetFlowsEventsListener(this);
    }

    // Implement all interface methods below
}

Estos métodos son donde añades tu lógica personalizada para responder a los eventos del flow. El SDK no aplica ningún comportamiento predeterminado: una compra exitosa o un error no cierra la vista automáticamente — llama a view.Dismiss(...) tú mismo cuando corresponda.

Eventos generados por el usuario

Flow appeared

Se invoca cuando la vista del flow aparece en pantalla.

En iOS, también se invoca cuando el usuario pulsa el botón de web paywall dentro de un flow y se abre un web paywall en el navegador integrado.

public void FlowViewDidAppear(AdaptyUIFlowView view) { }

Flow disappeared

Se invoca cuando la vista del flow se cierra de la pantalla.

En iOS, también se invoca cuando un web paywall abierto desde un flow en un navegador in-app desaparece de la pantalla.

public void FlowViewDidDisappear(AdaptyUIFlowView view) { }

Selección de producto

Se invoca cuando se selecciona un producto para su compra (por el usuario o por el sistema).

public void FlowViewDidSelectProduct(
    AdaptyUIFlowView view,
    string productId
) { }
Ejemplo de evento (haz clic para expandir)
{
  "productId": "premium_monthly"
}

Compra iniciada

Se invoca cuando un usuario inicia el proceso de compra.

public void FlowViewDidStartPurchase(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product
) { }

En el modo Observer, las compras iniciadas desde un flow se entregan a tu IAdaptyUIObserverModeResolver en su lugar.

Ejemplo de evento (haz clic para expandir)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

Compra exitosa, cancelada o pendiente

Este método se invoca si la compra se realiza con éxito, el usuario la cancela o queda en estado pendiente. Las cancelaciones del usuario y los pagos pendientes (por ejemplo, cuando se requiere aprobación parental) activan este método, no FlowViewDidFailPurchase.

El flow permanece abierto tras la compra hasta que lo cierres manualmente, así que llama a view.Dismiss(...) cuando el usuario obtenga acceso:

public void FlowViewDidFinishPurchase(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product,
    AdaptyPurchaseResult purchasedResult
) {
    switch (purchasedResult.Type) {
        case AdaptyPurchaseResultType.Success:
            // Check if user has access to premium features
            if (purchasedResult.Profile != null
                && purchasedResult.Profile.AccessLevels.TryGetValue("premium", out var premium)
                && premium.IsActive) {
                view.Dismiss(null);
            }
            break;
        case AdaptyPurchaseResultType.Pending:
            // Handle pending purchase (e.g., user will pay offline with cash)
            break;
        case AdaptyPurchaseResultType.UserCancelled:
            // Handle user cancellation
            break;
        default:
            break;
    }
}
Ejemplos de eventos (haz clic para expandir)
// Successful purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "Success",
    "profile": {
      "accessLevels": {
        "premium": {
          "id": "premium",
          "isActive": true,
          "expiresAt": "2024-02-15T10:30:00Z"
        }
      }
    }
  }
}

// Cancelled purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "UserCancelled"
  }
}

// Pending purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "Pending"
  }
}

Recomendamos cerrar la pantalla del flow en caso de compra exitosa.

Compra fallida

Si una compra falla debido a un error, se invocará este método. Esto incluye errores de StoreKit/Google Play Billing (restricciones de pago, productos no válidos, fallos de red), errores de verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan FlowViewDidFinishPurchase con un resultado cancelado, y los pagos pendientes no activan este método.

public void FlowViewDidFailPurchase(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product,
    AdaptyError error
) { }
Ejemplo de evento (Haz clic para expandir)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  }
}

Restauración iniciada

Se invoca cuando el usuario inicia el proceso de restauración:

public void FlowViewDidStartRestore(AdaptyUIFlowView view) { }

Restauración exitosa

Se invoca cuando la restauración de compras se completa con éxito. El flow permanece abierto tras la restauración hasta que lo cierres:

public void FlowViewDidFinishRestore(
    AdaptyUIFlowView view,
    AdaptyProfile profile
) {
    // Check if user has access to premium features
    if (profile.AccessLevels.TryGetValue("premium", out var premium) && premium.IsActive) {
        view.Dismiss(null);
    }
}
Ejemplo de evento (haz clic para expandir)
{
  "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 para saber cómo comprobarlo.

Fallo en la restauración

Se invoca cuando la restauración de una compra falla:

public void FlowViewDidFailRestore(
    AdaptyUIFlowView view,
    AdaptyError error
) { }
Ejemplo de evento (haz clic para expandir)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

Navegación web de pago finalizada

Después de intentar abrir un paywall web para realizar una compra (tanto si tuvo éxito como si falló), se invocará este método:

public void FlowViewDidFinishWebPaymentNavigation(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product,
    AdaptyError error
) { }

Parámetros:

  • product: El producto para el que se abrió (o intentó abrir) el paywall web, o null
  • error: null si el paywall web se abrió correctamente, o un AdaptyError si falló
Ejemplos de eventos (haz clic para expandir)
// 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": null,
  "error": {
    "code": "wrong_param",
    "message": "Current method is not available for this product",
    "details": {
      "underlyingError": "Product not configured for web purchases"
    }
  }
}

Obtención de datos y renderizado

Errores de carga de productos

Se invoca cuando falla la carga de productos y proporciona un AdaptyError. Si no pasaste el array de productos durante la inicialización, AdaptyUI recuperará los objetos necesarios del servidor por sí solo. Esta operación puede fallar, y AdaptyUI reportará el error invocando este método:

public void FlowViewDidFailLoadingProducts(
    AdaptyUIFlowView view,
    AdaptyError error
) { }
Ejemplo de evento (Haz clic para expandir)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

Errores de renderizado y en tiempo de ejecución

Si ocurre un error durante el renderizado de la interfaz, o cualquier otro error en tiempo de ejecución no relacionado con una compra, este método lo reportará. La vista no se cierra automáticamente — llama a view.Dismiss(...) tú mismo si lo deseas:

public void FlowViewDidReceiveError(
    AdaptyUIFlowView view,
    AdaptyError error
) { }
Ejemplo de evento (haz clic para expandir)
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render flow interface",
    "details": {
      "underlyingError": "Invalid flow configuration"
    }
  }
}

En una situación normal, estos errores no deberían producirse, así que si te encuentras con alguno, háznos lo saber.

Eventos de análisis

FlowViewDidReceiveAnalyticEvent está reservado para eventos analíticos personalizados de un flow. Los flows aún no emiten estos eventos a tu código, así que deja el cuerpo del método vacío — IAdaptyFlowsEventsListener es una interfaz C#, por lo que el método debe estar presente igualmente:

public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IDictionary<string, object> @params
) { }

Gestión de solicitudes del sistema

IAdaptyUISystemRequestsHandler (registrado mediante Adapty.SetSystemRequestsHandler(...)) está reservado para solicitudes del sistema que provienen de un flow: solicitudes de permisos del SO (como notificaciones push o acceso a la cámara) y solicitudes de valoración de la app. Los flows todavía no generan estas solicitudes, por lo que no es necesario registrar un handler.

Botón de retroceso del sistema Android

El botón de retroceso del sistema Android (o el gesto de retroceso) se entrega a FlowViewDidPerformAction como una acción SystemBack y no cierra el flow por sí solo — el usuario abandona el flow a través de una ruta que tú defines, como un botón Close o una acción on_device_back en el builder. Si quieres que el botón de retroceso del sistema cierre el flow, gestiona la acción tú mismo:

public void FlowViewDidPerformAction(
    AdaptyUIFlowView view,
    AdaptyUIUserAction action
) {
    switch (action.Type) {
        case AdaptyUIUserActionType.Close:
        case AdaptyUIUserActionType.SystemBack:
            view.Dismiss(null);
            break;
        default:
            // handle other events
            break;
    }
}

Consulta la guía sobre cómo gestionar las acciones de flow para ver la lista completa de acciones.

Esta guía cubre el manejo de eventos para compras, restauraciones, selección de productos y renderización de paywalls. También debes implementar el manejo de botones (cerrar el paywall, abrir enlaces, etc.). Consulta nuestra guía sobre el manejo de acciones de botones para más detalles.

Los paywalls configurados con el Paywall Builder no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan ciertos eventos a los que tu app puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.), así como notificaciones sobre acciones relacionadas con compras realizadas en el paywall. A continuación aprenderás cómo responder a estos eventos.

Esta guía es exclusivamente para paywalls del nuevo Paywall Builder, que requieren la versión 3.3.0 o posterior del SDK de Adapty.

¿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, que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.

Gestión de eventos

Para controlar o monitorear los procesos que ocurren en la pantalla del paywall dentro de tu aplicación móvil, implementa la interfaz AdaptyPaywallsEventsListener:

using UnityEngine;
using AdaptySDK;

public class PaywallEventsHandler : MonoBehaviour, AdaptyPaywallsEventsListener
{
    void Start()
    {
        Adapty.SetPaywallsEventsListener(this);
    }

    // Implement all required interface methods below
}

Eventos generados por el usuario

Paywall mostrado

Se invoca cuando la vista del paywall aparece en pantalla.

En iOS, también se invoca cuando el usuario pulsa el botón del web paywall dentro de un paywall y el web paywall se abre en un navegador integrado.

public void PaywallViewDidAppear(AdaptyUIPaywallView view) { }

Paywall ocultado

Se invoca cuando la vista del paywall desaparece de la pantalla.

En iOS, también se invoca cuando un web paywall abierto desde un paywall en un navegador integrado desaparece de la pantalla.

public void PaywallViewDidDisappear(AdaptyUIPaywallView view) { }

Selección de producto

Se invoca cuando se selecciona un producto para comprar (por el usuario o por el sistema).

public void PaywallViewDidSelectProduct(
    AdaptyUIPaywallView view, 
    string productId
) { }
Ejemplo de evento (Haz clic para expandir)
{
  "productId": "premium_monthly"
}

Compra iniciada

Se invoca cuando el usuario inicia el proceso de compra.

public void PaywallViewDidStartPurchase(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product
) { }
Ejemplo de evento (Haz clic para expandir)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

Compra exitosa, cancelada o pendiente

Si la compra se completa correctamente, el usuario la cancela, o queda en estado pendiente, se invocará este método. Las cancelaciones del usuario y los pagos pendientes (como los que requieren aprobación parental) activan este método, no PaywallViewDidFailPurchase.

public void PaywallViewDidFinishPurchase(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product, 
    AdaptyPurchaseResult purchasedResult
) { }
Ejemplos de eventos (Haz clic para expandir)
// Successful purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "Success",
    "profile": {
      "accessLevels": {
        "premium": {
          "id": "premium",
          "isActive": true,
          "expiresAt": "2024-02-15T10:30:00Z"
        }
      }
    }
  }
}

// Cancelled purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "UserCancelled"
  }
}

// Pending purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "Pending"
  }
}

Recomendamos cerrar la pantalla en ese caso.

Compra fallida

Si una compra falla debido a un error, se invocará este método. Esto incluye errores de StoreKit/Google Play Billing (restricciones de pago, productos inválidos, fallos de red), errores de verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan PaywallViewDidFinishPurchase con un resultado de cancelación, y los pagos pendientes no activan este método.

public void PaywallViewDidFailPurchase(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product, 
    AdaptyError error
) { }
Ejemplo de evento (Clic para expandir)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  }
}

Restauración iniciada

Se invoca cuando un usuario inicia el proceso de restauración:

public void PaywallViewDidStartRestore(AdaptyUIPaywallView view) { }

Restauración exitosa

Se invoca cuando la restauración de compras se completa correctamente:

public void PaywallViewDidFinishRestore(
    AdaptyUIPaywallView view, 
    AdaptyProfile profile
) { }
Ejemplo de evento (Haz clic para expandir)
{
  "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 para aprender cómo verificarlo.

Restauración fallida

Se invoca cuando la restauración de compras falla:

public void PaywallViewDidFailRestore(
    AdaptyUIPaywallView view, 
    AdaptyError error
) { }
Ejemplo de evento (Haz clic para expandir)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

Navegación web de pago finalizada

Después de intentar abrir un web paywall para realizar una compra (tanto si tuvo éxito como si falló), se invocará este método:

public void PaywallViewDidFinishWebPaymentNavigation(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product, 
    AdaptyError error
) { }

Parámetros:

  • product: El producto para el que se abrió (o intentó abrir) el web paywall
  • error: null si el web paywall se abrió correctamente, o un AdaptyError si falló
Ejemplos de eventos (Haz clic para expandir)
// Successful navigation
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": null
}

// Failed navigation
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "wrong_param",
    "message": "Current method is not available for this product",
    "details": {
      "underlyingError": "Product not configured for web purchases"
    }
  }
}

Obtención y renderizado de datos

Errores al cargar productos

Se invoca cuando falla la carga de productos y proporciona AdaptyError. Si no pasaste el array de productos durante la inicialización, AdaptyUI recuperará los objetos necesarios del servidor por sí mismo. Esta operación puede fallar, y AdaptyUI notificará el error invocando este método:

public void PaywallViewDidFailLoadingProducts(
    AdaptyUIPaywallView view, 
    AdaptyError error
) { }
Ejemplo de evento (clic para expandir)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

Errores de renderizado

Se invoca cuando ocurre un error durante el renderizado de la interfaz y proporciona un AdaptyError:

public void PaywallViewDidFailRendering(
    AdaptyUIPaywallView view, 
    AdaptyError error
) { }
Ejemplo de evento (Haz clic para expandir)
{
  "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 uno, por favor, comunícanoslo.