Gérer les événements de flow et de paywall - Kotlin Multiplatform
Ce guide couvre la gestion des événements pour les achats, les restaurations, la sélection de produits et le rendu des flows. Vous devez également implémenter la gestion des boutons (fermeture du flow, ouverture de liens, etc.). Consultez notre guide sur la gestion des actions de flow pour plus de détails.
Les flows et les paywalls configurés avec le Flow Builder ou le Paywall Builder n’ont pas besoin de code supplémentaire pour effectuer ou restaurer des achats. Ils génèrent cependant des événements auxquels votre application peut réagir. Ces événements comprennent les appuis sur des boutons (boutons de fermeture, URLs, sélections de produits, etc.) ainsi que des notifications sur les actions liées aux achats. Découvrez ci-dessous comment répondre à ces événements.
Pour contrôler ou surveiller les processus qui se produisent sur l’écran du flow dans votre application mobile, implémentez les méthodes de l’interface AdaptyUIFlowsEventsObserver et enregistrez votre observer avec AdaptyUI.setFlowsEventsObserver(). Certaines méthodes disposent d’implémentations par défaut qui gèrent automatiquement les scénarios courants — ne surchargez donc que les méthodes que vous souhaitez modifier :
AdaptyUI.setFlowsEventsObserver(object : AdaptyUIFlowsEventsObserver {
// override only the methods you want to change
})Ces méthodes sont l’endroit où vous ajoutez votre logique personnalisée pour répondre aux événements de flow. Vous pouvez utiliser view.dismiss() pour fermer le flow, ou implémenter tout autre comportement personnalisé dont vous avez besoin. Notez que dismiss() est une fonction suspend — dans un callback, lancez-la sur le mainUiScope de l’observer : mainUiScope.launch { view.dismiss() }.
Événements générés par l’utilisateur
Apparition et disparition du flow
Quand un flow apparaît ou disparaît, ces méthodes sont invoquées :
override fun flowViewDidAppear(view: AdaptyUIFlowView) {
// Handle flow appearance
// You can track analytics or update UI here
}
override fun flowViewDidDisappear(view: AdaptyUIFlowView) {
// Handle flow disappearance
// You can track analytics or update UI here
}- Sur iOS,
flowViewDidAppearest également invoqué lorsqu’un utilisateur appuie sur le bouton de paywall web à l’intérieur d’un flow, et qu’un paywall web s’ouvre dans un navigateur intégré. - Sur iOS,
flowViewDidDisappearest également invoqué lorsqu’un paywall web ouvert depuis un flow dans un navigateur intégré disparaît de l’écran.
Exemples d’événements (Cliquez pour développer)
// Flow appeared
{
// No additional data
}
// Flow disappeared
{
// No additional data
} Sélection du produit
Si un utilisateur sélectionne un produit à acheter, cette méthode sera invoquée :
override fun flowViewDidSelectProduct(view: AdaptyUIFlowView, productId: String) {
// Handle product selection
// You can update UI or track analytics here
}Exemple d’événement (cliquer pour développer)
{
"productId": "premium_monthly"
} Achat lancé
Si un utilisateur lance le processus d’achat, cette méthode sera invoquée :
override fun flowViewDidStartPurchase(view: AdaptyUIFlowView, product: AdaptyPaywallProduct) {
// Handle purchase start
// You can show loading indicators or track analytics here
}En mode Observateur, les achats démarrés depuis un flow sont transmis à votre AdaptyUIObserverModeResolver à la place.
Exemple d’événement (Cliquer pour développer)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
} Achat réussi, annulé ou en attente
Cette méthode est appelée lorsqu’un achat se termine. Par défaut, elle ne fait rien — le flow reste ouvert après l’achat jusqu’à ce que vous le fermiez, alors appelez view.dismiss() vous-même dès que l’utilisateur obtient l’accès :
override fun flowViewDidFinishPurchase(
view: AdaptyUIFlowView,
product: AdaptyPaywallProduct,
purchaseResult: AdaptyPurchaseResult
) {
when (purchaseResult) {
is AdaptyPurchaseResult.Success -> {
// Check if user has access to premium features
if (purchaseResult.profile.accessLevels["premium"]?.isActive == true) {
mainUiScope.launch { view.dismiss() }
}
}
AdaptyPurchaseResult.Pending -> {
// Handle pending purchase (e.g., user will pay offline with cash)
}
AdaptyPurchaseResult.UserCanceled -> {
// Handle user cancellation
}
}
}Exemples d’événements (cliquez pour développer)
// 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"
}
}
}
}
}
// 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"
}
}
// User canceled purchase
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"purchaseResult": {
"type": "UserCanceled"
}
} Nous recommandons de fermer l’écran du flow en cas d’achat réussi.
Échec d’achat
Cette méthode est invoquée lorsqu’un achat échoue en raison d’une erreur. Cela inclut les erreurs StoreKit/Google Play Billing (restrictions de paiement, produits invalides, échecs réseau), les échecs de vérification des transactions et les erreurs système. Notez que les annulations de l’utilisateur déclenchent flowViewDidFinishPurchase avec un résultat annulé à la place, et les paiements en attente ne déclenchent pas cette méthode.
override fun flowViewDidFailPurchase(
view: AdaptyUIFlowView,
product: AdaptyPaywallProduct,
error: AdaptyError
) {
// Add your purchase failure handling logic here
// For example: show error message, retry option, or custom error handling
}Exemple d’événement (Cliquer pour développer)
{
"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"
}
}
} Restauration lancée
Si un utilisateur lance le processus de restauration, cette méthode sera invoquée :
override fun flowViewDidStartRestore(view: AdaptyUIFlowView) {
// Handle restore start
// You can show loading indicators or track analytics here
}Restauration réussie
Si la restauration d’un achat réussit, cette méthode sera invoquée. Par défaut, elle ne fait rien — le flow reste ouvert après la restauration jusqu’à ce que vous le fermiez :
override fun flowViewDidFinishRestore(view: AdaptyUIFlowView, profile: AdaptyProfile) {
// Add your successful restore handling logic here
// For example: show success message, update UI, or dismiss the flow
// Check if user has access to premium features
if (profile.accessLevels["premium"]?.isActive == true) {
mainUiScope.launch { view.dismiss() }
}
}Exemple d’événement (Cliquer pour développer)
{
"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"
}
]
}
} Nous recommandons de fermer l’écran si l’utilisateur possède le accessLevel requis. Consultez la rubrique Statut de l’abonnement pour savoir comment le vérifier.
Échec de la restauration
Si Adapty.restorePurchases() échoue, cette méthode sera invoquée :
override fun flowViewDidFailRestore(view: AdaptyUIFlowView, error: AdaptyError) {
// Add your restore failure handling logic here
// For example: show error message, retry option, or custom error handling
}Exemple d’événement (Cliquez pour développer)
{
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
} Fin de navigation de paiement web
Si un utilisateur lance le processus d’achat via un paywall web, cette méthode sera invoquée :
override fun flowViewDidFinishWebPaymentNavigation(
view: AdaptyUIFlowView,
product: AdaptyPaywallProduct?,
error: AdaptyError?
) {
if (error != null) {
// Handle web payment navigation error
} else {
// Handle successful web payment navigation
}
}Exemples d’événements (Cliquer pour développer)
// Successful web payment 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 web payment navigation
{
"product": null,
"error": {
"code": "web_payment_failed",
"message": "Web payment navigation failed",
"details": {
"underlyingError": "Network connection error"
}
}
} Récupération et rendu des données
Erreurs de chargement des produits
Si vous ne transmettez pas les produits lors de l’initialisation, AdaptyUI récupèrera lui-même les objets nécessaires depuis le serveur. Si cette opération échoue, AdaptyUI signalera l’erreur en appelant cette méthode :
override fun flowViewDidFailLoadingProducts(view: AdaptyUIFlowView, error: AdaptyError) {
// Add your product loading failure handling logic here
// For example: show error message, retry option, or custom error handling
}Exemple d’événement (Cliquez pour développer)
{
"error": {
"code": "products_loading_failed",
"message": "Failed to load products from the server",
"details": {
"underlyingError": "Network timeout"
}
}
} Erreurs de rendu et d’exécution
Si une erreur survient lors du rendu de l’interface, ou si une autre erreur d’exécution non liée à un achat se produit, elle sera signalée par cette méthode. Par défaut, le flow est fermé en cas d’erreur — surchargez la méthode pour le maintenir ouvert ou ajouter votre propre gestion :
override fun flowViewDidReceiveError(view: AdaptyUIFlowView, error: AdaptyError) {
// Handle the error
// The default implementation dismisses the flow;
// once you override this method, dismissal is up to you
}Exemple d’événement (Cliquez pour développer)
{
"error": {
"code": "rendering_failed",
"message": "Failed to render flow interface",
"details": {
"underlyingError": "Invalid flow configuration"
}
}
} En situation normale, de telles erreurs ne devraient pas se produire, donc si vous en rencontrez une, veuillez nous en informer.
Événements analytiques
override fun flowViewDidReceiveAnalyticEvent(
view: AdaptyUIFlowView,
name: String,
paramsJsonString: String
) {
}Kotlin Multiplatform vous transmet les paramètres d’événement sous forme de chaîne JSON brute, donc décodez paramsJsonString avant de les lire.
Un flow envoie flow_screen_showed chaque fois qu’un utilisateur ouvre l’un de ses écrans. Adapty comptabilise ces événements dans ses propres analytics de flow et les transmet également à votre application, afin que vous puissiez reconstituer le même funnel dans vos propres analytics.
| Paramètre | Description |
|---|---|
instanceId | L’ID de l’écran que l’utilisateur a ouvert. |
screen_order | La position de l’écran dans le flow. |
is_last_screen | true quand l’écran n’a nulle part où aller ensuite. Un flow avec des embranchements peut se terminer sur plusieurs écrans différents, et chacun d’eux renvoie true. |
isBackendEvent et isCustomerEvent sont tous les deux true pour cet événement : Adapty continue de le comptabiliser, et votre application le reçoit également.
Consultez Suivre les vues d’écran de flow pour savoir quoi en faire.
Navigation
Bouton retour système Android
Par défaut, un flow ne peut pas être fermé avec le bouton retour système Android ni avec le geste de retour — l’implémentation par défaut de flowViewDidPerformAction ferme le flow uniquement sur CloseAction et ignore AndroidSystemBackAction, ainsi l’utilisateur quitte le flow via un chemin que vous définissez, comme un bouton Close ou une action on_device_back dans le builder. Si vous souhaitez que le bouton retour système ferme le flow, gérez vous-même l’action :
override fun flowViewDidPerformAction(view: AdaptyUIFlowView, action: AdaptyUIAction) {
when (action) {
is AdaptyUIAction.CloseAction ->
mainUiScope.launch { view.dismiss() } // default behavior
is AdaptyUIAction.AndroidSystemBackAction ->
mainUiScope.launch { view.dismiss() } // not handled by default
is AdaptyUIAction.OpenUrlAction ->
AdaptyUI.openWebUrl(action.url, action.openIn) // default behavior
else -> Unit
}
}Consultez le guide de gestion des actions de flow pour la liste complète des actions.
Les paywalls configurés avec le Paywall Builder n’ont pas besoin de code supplémentaire pour effectuer et restaurer des achats. Cependant, ils génèrent certains événements auxquels votre application peut réagir. Ces événements incluent les pressions sur des boutons (boutons de fermeture, URL, sélections de produits, etc.) ainsi que des notifications sur les actions liées aux achats effectuées sur le paywall. Découvrez comment répondre à ces événements ci-dessous.
Ce guide concerne uniquement les paywalls du nouveau Paywall Builder.
Pour contrôler ou surveiller les processus se déroulant sur l’écran du paywall dans votre application mobile, implémentez les méthodes de l’interface AdaptyUIPaywallsEventsObserver. Certaines méthodes ont des implémentations par défaut qui gèrent automatiquement les scénarios courants.
Ces méthodes sont l’endroit où vous ajoutez votre logique personnalisée pour répondre aux événements du paywall. Vous pouvez utiliser view.dismiss() pour fermer le paywall, ou implémenter tout autre comportement personnalisé dont vous avez besoin.
Événements générés par l’utilisateur
Apparition et disparition du paywall
Lorsqu’un paywall apparaît ou disparaît, ces méthodes sont invoquées :
override fun paywallViewDidAppear(view: AdaptyUIPaywallView) {
// Handle paywall appearance
// You can track analytics or update UI here
}
override fun paywallViewDidDisappear(view: AdaptyUIPaywallView) {
// Handle paywall disappearance
// You can track analytics or update UI here
}- Sur iOS,
paywallViewDidAppearest également invoqué lorsqu’un utilisateur appuie sur le bouton de paywall web dans un paywall, et qu’un paywall web s’ouvre dans un navigateur intégré. - Sur iOS,
paywallViewDidDisappearest également invoqué lorsqu’un paywall web ouvert depuis un paywall dans un navigateur intégré disparaît de l’écran.
Exemples d’événements (cliquez pour développer)
// Paywall appeared
{
// No additional data
}
// Paywall disappeared
{
// No additional data
} Sélection du produit
Si un utilisateur sélectionne un produit pour l’achat, cette méthode sera invoquée :
override fun paywallViewDidSelectProduct(view: AdaptyUIPaywallView, productId: String) {
// Handle product selection
// You can update UI or track analytics here
}Exemple d’événement (cliquez pour développer)
{
"productId": "premium_monthly"
} Achat démarré
Si un utilisateur lance le processus d’achat, cette méthode sera invoquée :
override fun paywallViewDidStartPurchase(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct) {
// Handle purchase start
// You can show loading indicators or track analytics here
}Exemple d’événement (Cliquer pour développer)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
} Achat réussi, annulé ou en attente
Si un achat réussit, cette méthode sera invoquée. Par défaut, elle ferme automatiquement le paywall, sauf si l’achat a été annulé par l’utilisateur :
override fun paywallViewDidFinishPurchase(
view: AdaptyUIPaywallView,
product: AdaptyPaywallProduct,
purchaseResult: AdaptyPurchaseResult
) {
when (purchaseResult) {
is AdaptyPurchaseResult.Success -> {
// Check if user has access to premium features
if (purchaseResult.profile.accessLevels["premium"]?.isActive == true) {
view.dismiss()
}
}
AdaptyPurchaseResult.Pending -> {
// Handle pending purchase (e.g., user will pay offline with cash)
}
AdaptyPurchaseResult.UserCanceled -> {
// Handle user cancellation
}
}
}Exemples d’événements (Cliquez pour développer)
// 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"
}
}
}
}
}
// 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"
}
}
// User canceled purchase
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"purchaseResult": {
"type": "UserCanceled"
}
} Nous recommandons de fermer l’écran du paywall en cas d’achat réussi.
Échec d’un achat
Si un achat échoue en raison d’une erreur, cette méthode est appelée. Cela inclut les erreurs StoreKit/Google Play Billing (restrictions de paiement, produits invalides, problèmes réseau), les échecs de vérification des transactions et les erreurs système. À noter que les annulations par l’utilisateur déclenchent paywallViewDidFinishPurchase avec un résultat annulé, et les paiements en attente ne déclenchent pas cette méthode.
override fun paywallViewDidFailPurchase(
view: AdaptyUIPaywallView,
product: AdaptyPaywallProduct,
error: AdaptyError
) {
// Add your purchase failure handling logic here
// For example: show error message, retry option, or custom error handling
}Exemple d’événement (cliquer pour développer)
{
"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"
}
}
} Restauration démarrée
Si un utilisateur lance le processus de restauration, cette méthode sera invoquée :
override fun paywallViewDidStartRestore(view: AdaptyUIPaywallView) {
// Handle restore start
// You can show loading indicators or track analytics here
}Restauration réussie
Si la restauration d’un achat réussit, cette méthode sera invoquée :
override fun paywallViewDidFinishRestore(view: AdaptyUIPaywallView, profile: AdaptyProfile) {
// Add your successful restore handling logic here
// For example: show success message, update UI, or dismiss paywall
// Check if user has access to premium features
if (profile.accessLevels["premium"]?.isActive == true) {
view.dismiss()
}
}Exemple d’événement (Cliquer pour développer)
{
"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"
}
]
}
} Nous recommandons de fermer l’écran si l’utilisateur possède le accessLevel requis. Consultez la rubrique Statut de l’abonnement pour savoir comment le vérifier.
Échec de la restauration
Si Adapty.restorePurchases() échoue, cette méthode sera invoquée :
override fun paywallViewDidFailRestore(view: AdaptyUIPaywallView, error: AdaptyError) {
// Add your restore failure handling logic here
// For example: show error message, retry option, or custom error handling
}Exemple d’événement (cliquer pour développer)
{
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
} Fin de navigation de paiement web
Si un utilisateur lance le processus d’achat via un paywall web, cette méthode sera invoquée :
override fun paywallViewDidFinishWebPaymentNavigation(
view: AdaptyUIPaywallView,
product: AdaptyPaywallProduct?,
error: AdaptyError?
) {
if (error != null) {
// Handle web payment navigation error
} else {
// Handle successful web payment navigation
}
}Exemples d’événements (cliquez pour développer)
// Successful web payment 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 web payment navigation
{
"product": null,
"error": {
"code": "web_payment_failed",
"message": "Web payment navigation failed",
"details": {
"underlyingError": "Network connection error"
}
}
} Récupération et rendu des données
Erreurs de chargement des produits
Si vous ne transmettez pas les produits lors de l’initialisation, AdaptyUI les récupère lui-même depuis le serveur. En cas d’échec, AdaptyUI signale l’erreur en appelant cette méthode :
override fun paywallViewDidFailLoadingProducts(view: AdaptyUIPaywallView, error: AdaptyError) {
// Add your product loading failure handling logic here
// For example: show error message, retry option, or custom error handling
}Exemple d’événement (cliquez pour agrandir)
{
"error": {
"code": "products_loading_failed",
"message": "Failed to load products from the server",
"details": {
"underlyingError": "Network timeout"
}
}
} Erreurs de rendu
Si une erreur survient lors du rendu de l’interface, elle sera signalée par cette méthode :
override fun paywallViewDidFailRendering(view: AdaptyUIPaywallView, error: AdaptyError) {
// Handle rendering error
// In a normal situation, such errors should not occur
// If you come across one, please let us know
}Exemple d’événement (Cliquer pour agrandir)
{
"error": {
"code": "rendering_failed",
"message": "Failed to render paywall interface",
"details": {
"underlyingError": "Invalid paywall configuration"
}
}
} Dans une situation normale, de telles erreurs ne devraient pas se produire. Si vous en rencontrez une, merci de nous en informer.