Gestionar eventos de flow y paywall - Capacitor

Important

Esta guía cubre el manejo de eventos para compras, restauraciones, selección de productos y renderizado de flows. También puedes configurar el manejo de botones (cerrar el flow, abrir enlaces, acciones personalizadas, etc.). Consulta nuestra guía sobre el manejo de acciones de botones para más detalles.

Los flows y paywalls creados con el Flow Builder no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan algunos eventos a los que tu app puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selección de productos, etc.), así como notificaciones sobre acciones relacionadas con compras realizadas en el flow. A continuación se explica cómo responder a estos eventos.

Para controlar o monitorizar los procesos que ocurren en la pantalla del flow dentro de tu app, implementa el método view.setEventHandlers:

Important

Solo puedes establecer un handler por evento: llamar a setEventHandlers varias veces sobreescribirá los handlers que proporciones, reemplazando tanto los predeterminados como los establecidos previamente para esos eventos específicos. Los handlers que no establezcas conservarán su comportamiento predeterminado. setEventHandlers devuelve una función para cancelar la suscripción, y view.dismiss() elimina todos los handlers.


const view = await createFlowView(flow);

const unsubscribe = await view.setEventHandlers({
  onCloseButtonPress() {
    return true; // close the flow (default behavior)
  },
  onAndroidSystemBack() {
    return true; // close the flow; by default, it stays open
  },
  onPurchaseCompleted(purchaseResult, product) {
    return purchaseResult.type === 'success'; // close the flow on a successful purchase, keep it open for cancelled or pending purchases
  },
  onPurchaseStarted(product) { /***/ },
  onPurchaseFailed(error, product) { /***/ },
  onRestoreCompleted(profile) { /***/ },
  onRestoreFailed(error) { /***/ },
  onProductSelected(productId) { /***/ },
  onError(error) { /***/ },
  onLoadingProductsFailed(error) { /***/ },
  onUrlPress(url, openIn) {
    adapty.openWebUrl({ url, openIn }).catch(console.warn); // same as the SDK default
    return false; // keep the flow open
  },
  onAppeared(appearedView) { /***/ },
  onDisappeared() { /***/ },
  onWebPaymentNavigationFinished() { /***/ },
});
Ejemplos de eventos (haz clic para expandir)

Los ejemplos a continuación muestran las propiedades disponibles en cada manejador, con valores ilustrativos en los comentarios.

// onUrlPress
url;    // 'https://example.com/terms'
openIn; // 'browser_in_app' or 'browser_out_app'

// onCustomAction
actionId; // 'login'

// onProductSelected
productId; // 'premium_monthly'

// onAppeared
appearedView.id;          // '3f8a1c7e-9b24-4d51-8e30-6c5b2a9f1d47'
appearedView.placementId; // 'onboarding_paywall'
appearedView.variationId; // 'd21c4b6a-57e8-4f39-b0a2-8c7e13f5d94b'
appearedView.locale;      // 'es'

// onPurchaseStarted, onPurchaseCompleted, onPurchaseFailed
product.vendorProductId;        // 'premium_monthly'
product.localizedTitle;         // 'Premium Monthly'
product.localizedDescription;   // 'Premium subscription for 1 month'
product.price?.amount;          // 9.99
product.price?.currencyCode;    // 'USD'
product.price?.localizedString; // '$9.99'

// onPurchaseCompleted
purchaseResult.type; // 'success', 'pending', or 'user_cancelled'
if (purchaseResult.type === 'success') {
  purchaseResult.profile.accessLevels['premium']?.isActive; // true
}

// onRestoreCompleted
profile.accessLevels['premium']?.isActive; // true

// onPurchaseFailed, onRestoreFailed, onError, onLoadingProductsFailed
error.message; // 'Purchase failed due to insufficient funds'

Puedes registrar solo los manejadores de eventos que necesites y omitir los que no. De este modo, no se crearán listeners para eventos no utilizados. No hay manejadores de eventos obligatorios.

Los manejadores de eventos devuelven un booleano. Si devuelven true, el proceso de visualización se considera completado, por lo que la pantalla del flow se cierra y los listeners de eventos de esa vista se eliminan. Algunos manejadores de eventos tienen un comportamiento predeterminado que puedes modificar si lo necesitas:

  • onCloseButtonPress: cierra el flow cuando se pulsa el botón de cerrar.
  • onUrlPress: abre la URL pulsada en el navegador nativo mediante adapty.openWebUrl, respetando la opción Open in definida en el builder, y mantiene el flow abierto.
  • onAndroidSystemBack: mantiene el flow abierto cuando se pulsa el botón Back. Devuelve true para cerrarlo.
  • onPurchaseCompleted: mantiene el flow abierto tras completar una compra. Devuelve true para cerrarlo.
  • onRestoreCompleted: mantiene el flow abierto tras una restauración exitosa. Devuelve true para cerrarlo.
  • onError: cierra el flow si falla su renderizado.

Controladores de eventos

Manejador de eventosDescripción
onCustomActionSe invoca cuando el usuario realiza una acción personalizada, p. ej., hace clic en un botón personalizado.
onUrlPressSe invoca cuando el usuario hace clic en una URL de tu flow.
onAndroidSystemBackSe invoca cuando el usuario pulsa el botón Back del sistema Android. El flow permanece abierto por defecto; devuelve true para cerrarlo.
onCloseButtonPressSe invoca cuando el botón de cierre es visible y el usuario lo pulsa. Se recomienda cerrar la pantalla del flow en este manejador.
onPurchaseCompletedSe invoca cuando la compra finaliza, ya sea con éxito, cancelada por el usuario o pendiente de aprobación. En caso de compra exitosa, proporciona un AdaptyProfile actualizado. Las cancelaciones del usuario y los pagos pendientes (p. ej., requieren aprobación parental) activan este evento, no onPurchaseFailed.
onPurchaseStartedSe invoca cuando el usuario pulsa el botón de acción “Purchase” para iniciar el proceso de compra.
onPurchaseFailedSe invoca cuando una compra falla por errores (p. ej., restricciones de pago, productos no válidos, fallos de red, fallos de verificación de transacciones). No se invoca por cancelaciones del usuario ni pagos pendientes, que activan onPurchaseCompleted en su lugar.
onRestoreStartedSe invoca cuando el usuario inicia un proceso de restauración de compras.
onRestoreCompletedSe invoca cuando la restauración de compras se realiza correctamente y proporciona un AdaptyProfile actualizado. Se recomienda cerrar la pantalla si el usuario tiene el accessLevel requerido. Consulta el tema Estado de la suscripción para saber cómo comprobarlo.
onRestoreFailedSe invoca cuando el proceso de restauración falla y proporciona un AdaptyError.
onProductSelectedSe invoca cuando se selecciona cualquier producto en la vista del flow, lo que permite monitorizar qué selecciona el usuario antes de la compra.
onErrorSe invoca cuando ocurre un error durante el renderizado de la vista y proporciona un AdaptyError. Estos errores no deberían producirse; si te encuentras con uno, por favor, comunícanoslo.
onLoadingProductsFailedSe invoca cuando la carga de productos falla y proporciona un AdaptyError. Si no has establecido prefetchProducts: true en la creación de la vista, AdaptyUI recuperará los objetos necesarios del servidor por sí mismo.
onAppearedSe invoca cuando el flow se muestra al usuario y proporciona la vista que apareció; consulta El argumento view. 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 un navegador in-app.
onDisappearedSe invoca cuando el usuario cierra el flow. En iOS, también se invoca cuando un web paywall abierto desde un flow en un navegador in-app desaparece de la pantalla.
onWebPaymentNavigationFinishedSe invoca tras intentar abrir un web paywall para realizar una compra, con independencia de si tuvo éxito o no.
onRequestAppReviewReservado para solicitudes de valoración de la app desde un flow. Los flows aún no generan solicitudes de valoración, por lo que no es necesario implementarlo.
onAnalyticsSe invoca cuando un flow reporta un evento de analíticas, como una vista de pantalla. Consulta Eventos de analíticas más abajo.
onRequestPermissionReservado para solicitudes de permisos del sistema (como notificaciones push o acceso a la cámara) desde un flow. Los flows aún no generan solicitudes de permisos, por lo que no es necesario implementarlo.
onObserverPurchaseInitiatedSolo modo observador: se invoca cuando el usuario pulsa el botón de compra en un flow. Adapty no realiza la compra; ejecútala con tu propio código de compra y luego notifica la transacción a Adapty. Consulta Gestionar compras en modo observador más abajo.
onObserverRestoreInitiatedSolo modo observador: se invoca cuando el usuario pulsa el botón de restauración en un flow. Adapty no realiza la restauración; ejecútala tú mismo y luego notifica las transacciones restauradas. Consulta Gestionar compras en modo observador más abajo.

El argumento view

El argumento view requiere Capacitor SDK 4.0.2-beta.1 o posterior. De todos los manejadores de flow, solo onAppeared recibe una descripción de la propia vista: un objeto FlowEventView con estos campos:

CampoDescripción
idEl identificador de esta instancia de vista. Es interno al SDK y no coincide con nada en el Adapty Dashboard.
placementIdEl placement para el que se obtuvo el flow.
variationIdLa variante a la que se resolvió el flow, para atribuir tus propios análisis a una prueba A/B.
localeLa localización del flow con la que se construyó la vista. Difiere de la que solicitaste cuando el flow no tiene esa localización. La vista devuelta por createFlowView indica el mismo valor en su propiedad locale. Consulta Usar localizaciones y códigos de idioma.

Eventos de Analytics

view.setEventHandlers({
  onAnalytics(name, params) {
    return false; // keep the flow open
  },
});

Un flow reporta flow_screen_showed cada vez que un usuario abre una de sus pantallas. Adapty contabiliza estos eventos en sus propios análisis de flows y también los envía a tu aplicación, para que puedas construir el mismo embudo en tus propias herramientas de análisis.

ParámetroDescripción
instanceIdEl ID de la pantalla que el usuario abrió.
screen_orderLa posición de la pantalla en el flow.
is_last_screentrue cuando la pantalla no tiene a dónde seguir. Un flow con ramificaciones puede terminar en varias pantallas distintas, y cada una reporta true.

Tanto isBackendEvent como isCustomerEvent son true en este evento: Adapty sigue contabilizándolo y tu aplicación también lo recibe.

Consulta Rastrear vistas de pantallas del flow para saber qué hacer con ellas.

Gestionar compras en modo observer

Si activaste el SDK en modo Observer (observerMode: true) y muestras un flow renderizado por Adapty, el SDK no realiza las compras por ti. Cuando un usuario pulsa el botón de compra o restauración, el SDK invoca onObserverPurchaseInitiated o onObserverRestoreInitiated, para que puedas realizar la compra o restauración con tu propio código. Consulta Presentar flows en modo Observer para ver la configuración completa.

Important

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

Los paywall configurados con el Paywall Builder no necesitan código adicional para realizar ni 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 te explicamos cómo responder a estos eventos.

Para controlar o monitorizar los procesos que ocurren en la pantalla del paywall dentro de tu app, implementa el método view.setEventHandlers:


const view = await createPaywallView(paywall);

const unsubscribe = view.setEventHandlers({
  onCloseButtonPress() {
    console.log('User closed paywall');
    return true; // Allow the paywall to close
  },
  onAndroidSystemBack() {
    console.log('User pressed back button');
    return true; // Allow the paywall to close
  }, 
  onAppeared() {
    console.log('Paywall appeared');
    return false; // Don't close the paywall
  }, 
  onDisappeared() {
    console.log('Paywall disappeared');
  },
  onPurchaseCompleted(purchaseResult, product) {
    console.log('Purchase completed:', purchaseResult);
    return purchaseResult.type !== 'user_cancelled'; // Close if not cancelled
  },
  onPurchaseStarted(product) {
    console.log('Purchase started:', product);
    return false; // Don't close the paywall
  },
  onPurchaseFailed(error, product) {
    console.error('Purchase failed:', error);
    return false; // Don't close the paywall
  },
  onRestoreCompleted(profile) {
    console.log('Restore completed:', profile);
    return true; // Close the paywall after successful restore
  },
  onRestoreFailed(error) {
    console.error('Restore failed:', error);
    return false; // Don't close the paywall
  },
  onProductSelected(productId) {
    console.log('Product selected:', productId);
    return false; // Don't close the paywall
  },
  onRenderingFailed(error) {
    console.error('Rendering failed:', error);
    return false; // Don't close the paywall
  },
  onLoadingProductsFailed(error) {
    console.error('Loading products failed:', error);
    return false; // Don't close the paywall
  },
  onUrlPress(url) {
    window.open(url, '_blank');
    return false; // Don't close the paywall
  },
});
Ejemplos de eventos (haz clic para ampliar)
// onCloseButtonPress
{
  "event": "close_button_press"
}

// onAndroidSystemBack
{
  "event": "android_system_back"
}

// onAppeared
{
  "event": "paywall_shown"
}

// onDisappeared
{
  "event": "paywall_closed"
}

// onUrlPress
{
  "event": "url_press",
  "url": "https://example.com/terms"
}

// onCustomAction
{
  "event": "custom_action",
  "actionId": "login"
}

// onProductSelected
{
  "event": "product_selected",
  "productId": "premium_monthly"
}

// onPurchaseStarted
{
  "event": "purchase_started",
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

// onPurchaseCompleted - Success
{
  "event": "purchase_completed",
  "purchaseResult": {
    "type": "success",
    "profile": {
      "accessLevels": {
        "premium": {
          "id": "premium",
          "isActive": true,
          "expiresAt": "2024-02-15T10:30:00Z"
        }
      }
    }
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

// onPurchaseCompleted - Cancelled
{
  "event": "purchase_completed",
  "purchaseResult": {
    "type": "user_cancelled"
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

// onPurchaseFailed
{
  "event": "purchase_failed",
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  }
}

// onRestoreCompleted
{
  "event": "restore_completed",
  "profile": {
    "accessLevels": {
      "premium": {
        "id": "premium",
        "isActive": true,
        "expiresAt": "2024-02-15T10:30:00Z"
      }
    },
    "subscriptions": [
      {
        "vendorProductId": "premium_monthly",
        "isActive": true,
        "expiresAt": "2024-02-15T10:30:00Z"
      }
    ]
  }
}

// onRestoreFailed
{
  "event": "restore_failed",
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

// onRenderingFailed
{
  "event": "rendering_failed",
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}

// onLoadingProductsFailed
{
  "event": "loading_products_failed",
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

Puedes registrar solo los manejadores de eventos que necesites y omitir los que no uses. Así no se crean listeners innecesarios. No hay manejadores de eventos obligatorios.

Los manejadores de eventos devuelven un booleano. Si devuelven true, el proceso de visualización se considera completado, por lo que la pantalla del paywall se cierra y los listeners de eventos de esa vista se eliminan. Algunos manejadores de eventos tienen un comportamiento predeterminado que puedes sobrescribir si lo necesitas:

  • onCloseButtonPress: cierra el paywall cuando se pulsa el botón de cerrar.
  • onAndroidSystemBack: cierra el paywall cuando se pulsa el botón Back.
  • onRestoreCompleted: cierra el paywall tras una restauración exitosa.
  • onPurchaseCompleted: cierra el paywall salvo que el usuario haya cancelado.
  • onRenderingFailed: cierra el paywall si falla su renderizado.
  • onUrlPress: abre las URLs en el navegador del sistema y mantiene el paywall abierto.

Manejadores de eventos

Manejador de eventosDescripción
onCustomActionSe invoca cuando el usuario realiza una acción personalizada, p. ej., pulsa un botón personalizado.
onUrlPressSe invoca cuando el usuario pulsa una URL en tu paywall.
onAndroidSystemBackSe invoca cuando el usuario pulsa el botón de sistema Back de Android.
onCloseButtonPressSe invoca cuando el botón de cierre está visible y el usuario lo pulsa. Se recomienda cerrar la pantalla del paywall en este manejador.
onPurchaseCompletedSe invoca cuando la compra finaliza, ya sea con éxito, cancelada por el usuario o pendiente de aprobación. En caso de compra exitosa, proporciona un AdaptyProfile actualizado. Las cancelaciones del usuario y los pagos pendientes (p. ej., requieren aprobación parental) disparan este evento, no onPurchaseFailed.
onPurchaseStartedSe invoca cuando el usuario pulsa el botón de acción “Comprar” para iniciar el proceso de compra.
onPurchaseCancelledSe invoca cuando el usuario inicia el proceso de compra y lo interrumpe manualmente (cancela el diálogo de pago).
onPurchaseFailedSe invoca cuando una compra falla por errores (p. ej., restricciones de pago, productos no válidos, fallos de red, fallos en la verificación de la transacción). No se invoca para cancelaciones del usuario ni pagos pendientes, que en su lugar disparan onPurchaseCompleted.
onRestoreStartedSe invoca cuando el usuario inicia un proceso de restauración de compras.
onRestoreCompletedSe invoca cuando la restauración de compras se completa con éxito y proporciona un AdaptyProfile actualizado. Se recomienda cerrar la pantalla si el usuario tiene el accessLevel requerido. Consulta el tema Estado de suscripción para saber cómo comprobarlo.
onRestoreFailedSe invoca cuando el proceso de restauración falla y proporciona AdaptyError.
onProductSelectedSe invoca cuando se selecciona cualquier producto en la vista del paywall, lo que te permite monitorizar lo que el usuario elige antes de la compra.
onAppearedSe invoca cuando la vista del paywall aparece en pantalla. En iOS, también se invoca cuando el usuario pulsa el botón de paywall web dentro de un paywall y se abre un paywall web en un navegador integrado en la app.
onDisappearedSe invoca cuando la vista del paywall desaparece de la pantalla. En iOS, también se invoca cuando un paywall web abierto desde un paywall en un navegador integrado en la app desaparece de la pantalla.
onRenderingFailedSe invoca cuando ocurre un error durante el renderizado de la vista y proporciona AdaptyError. Estos errores no deberían producirse, así que si te encuentras con uno, comunícanoslo.
onLoadingProductsFailedSe invoca cuando la carga de productos falla y proporciona AdaptyError. Si no has establecido prefetchProducts: true en la creación de la vista, AdaptyUI recuperará los objetos necesarios del servidor por su cuenta.