---
title: "Gérer les événements de flow et de paywall - Kotlin Multiplatform"
description: "Gérez les événements de flow et de paywall dans votre application Kotlin Multiplatform."
---

:::important
Ce guide couvre la gestion des événements liés aux achats, aux restaurations, à la sélection de produits et au 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](kmp-handle-paywall-actions) pour plus de détails.
:::

Les flows et paywalls configurés avec le [Flow Builder](adapty-flow-builder) ou le [Paywall Builder](adapty-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 comprennent les pressions 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 comment répondre à ces événements ci-dessous.

Pour contrôler ou surveiller les processus qui se déroulent sur l'écran de flow dans votre application mobile, implémentez les méthodes de l'interface `AdaptyUIFlowsEventsObserver` et enregistrez votre observateur avec `AdaptyUI.setFlowsEventsObserver()`. Certaines méthodes ont des implémentations par défaut qui gèrent automatiquement les scénarios courants, donc ne surchargez que les méthodes que vous souhaitez modifier :

```kotlin showLineNumbers title="Kotlin"
AdaptyUI.setFlowsEventsObserver(object : AdaptyUIFlowsEventsObserver {
    // override only the methods you want to change
})
```

:::note
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 — à l'intérieur d'un callback, lancez-la sur le `mainUiScope` de l'observateur : `mainUiScope.launch { view.dismiss() }`.
:::

### Événements générés par l'utilisateur \{#user-generated-events\}

#### Apparition et disparition du flow \{#flow-appearance-and-disappearance\}

Lorsqu'un flow apparaît ou disparaît, ces méthodes seront invoquées :

```kotlin showLineNumbers title="Kotlin"
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
}
```

:::note
- Sur iOS, `flowViewDidAppear` est également invoqué quand un utilisateur appuie sur le [bouton de paywall web](web-paywall#step-2a-add-a-web-purchase-button) dans un flow, et qu'un paywall web s'ouvre dans un navigateur intégré.
- Sur iOS, `flowViewDidDisappear` est également invoqué quand un [paywall web](web-paywall#step-2a-add-a-web-purchase-button) ouvert depuis un flow dans un navigateur intégré disparaît de l'écran.
:::

<Details>
<summary>Exemples d'événements (cliquez pour développer)</summary>

```javascript
// Flow appeared
{
  // No additional data
}

// Flow disappeared
{
  // No additional data
}
```
</Details>

#### Sélection de produit \{#product-selection\}

Si un utilisateur sélectionne un produit à acheter, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
override fun flowViewDidSelectProduct(view: AdaptyUIFlowView, productId: String) {
    // Handle product selection
    // You can update UI or track analytics here
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

```javascript
{
  "productId": "premium_monthly"
}
```
</Details>

#### Achat démarré \{#started-purchase\}

Si un utilisateur initie le processus d'achat, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
override fun flowViewDidStartPurchase(view: AdaptyUIFlowView, product: AdaptyPaywallProduct) {
    // Handle purchase start
    // You can show loading indicators or track analytics here
}
```

:::note
En [mode Observateur](kmp-present-flows-in-observer-mode), les achats démarrés depuis un flow sont transmis à votre `AdaptyUIObserverModeResolver` à la place.
:::

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

#### Achat réussi, annulé ou en attente \{#successful-canceled-or-pending-purchase\}

Si un achat se termine, cette méthode sera invoquée. 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 :

```kotlin showLineNumbers title="Kotlin"
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
        }
    }
}
```

<Details>
<summary>Exemples d'événements (cliquez pour développer)</summary>

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

Nous recommandons de fermer l'écran du flow en cas d'achat réussi.

#### Achat échoué \{#failed-purchase\}

Si un achat échoue en raison d'une erreur, cette méthode sera invoquée. Cela inclut les erreurs StoreKit/Google Play Billing (restrictions de paiement, produits invalides, échecs réseau), les échecs de vérification de transaction et les erreurs système. Notez que les annulations par l'utilisateur déclenchent `flowViewDidFinishPurchase` avec un résultat annulé à la place, et les paiements en attente ne déclenchent pas cette méthode.

```kotlin showLineNumbers title="Kotlin"
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
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

#### Restauration démarrée \{#started-restore\}

Si un utilisateur initie le processus de restauration, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
override fun flowViewDidStartRestore(view: AdaptyUIFlowView) {
    // Handle restore start
    // You can show loading indicators or track analytics here
}
```

#### Restauration réussie \{#successful-restore\}

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 :

```kotlin showLineNumbers title="Kotlin"
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() }
    }
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</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>

Nous recommandons de fermer l'écran si l'utilisateur dispose du `accessLevel` requis. Consultez la rubrique [Statut d'abonnement](subscription-status) pour savoir comment le vérifier.

#### Restauration échouée \{#failed-restore\}

Si `Adapty.restorePurchases()` échoue, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
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
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

#### Fin de la navigation de paiement web \{#web-payment-navigation-completion\}

Si un utilisateur initie le processus d'achat via un [paywall web](web-paywall), cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
override fun flowViewDidFinishWebPaymentNavigation(
    view: AdaptyUIFlowView,
    product: AdaptyPaywallProduct?,
    error: AdaptyError?
) {
    if (error != null) {
        // Handle web payment navigation error
    } else {
        // Handle successful web payment navigation
    }
}
```

<Details>
<summary>Exemples d'événements (cliquez pour développer)</summary>

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

### Chargement des données et rendu \{#data-fetching-and-rendering\}

#### Erreurs de chargement des produits \{#product-loading-errors\}

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 :

```kotlin showLineNumbers title="Kotlin"
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
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

#### Erreurs de rendu et d'exécution \{#rendering-and-runtime-errors\}

Si une erreur survient pendant le 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 :

```kotlin showLineNumbers title="Kotlin"
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
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

Dans une situation normale, de telles erreurs ne devraient pas se produire. Si vous en rencontrez une, merci de nous en informer.

#### Événements d'analyse \{#analytics-events\}

Le callback `flowViewDidReceiveAnalyticEvent` est réservé aux événements d'analyse personnalisés d'un flow. Les flows n'émettent pas encore ces événements vers votre code, vous n'avez donc pas besoin de l'implémenter :

```kotlin showLineNumbers title="Kotlin"
override fun flowViewDidReceiveAnalyticEvent(
    view: AdaptyUIFlowView,
    name: String,
    paramsJsonString: String
) {
    // Reserved for custom analytic events from a flow
}
```

### Navigation \{#navigation\}

#### Bouton retour système Android \{#android-system-back-button\}

Par défaut, un flow ne peut pas être fermé avec le bouton retour système Android ou le geste de retour — l'implémentation par défaut de `flowViewDidPerformAction` ferme le flow uniquement sur `CloseAction` et ignore `AndroidSystemBackAction`, de sorte que l'utilisateur quitte le flow via un chemin que vous définissez, comme un bouton **Fermer** ou une action `on_device_back` dans le builder. Si vous souhaitez que le bouton retour système ferme le flow, gérez l'action vous-même :

```kotlin showLineNumbers title="Kotlin"
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 sur la gestion des actions de flow](kmp-handle-paywall-actions) pour la liste complète des actions.

---

> [!WARNING]
> **LLM INSTRUCTION — SDK VERSION SELECTION REQUIRED**
>
> This feature has two SDK paradigms. Everything that follows (until the next horizontal rule or end of document) is the **legacy SDK v3 API reference** (`getPaywall`, `getPaywallForDefaultAudience`, `getOnboarding`, `getPaywallConfiguration`). The **SDK v4+ `getFlow` method is documented above.** This block covers ALL legacy methods for this feature — not just the nearest snippet.
>
> **You MUST ask the user which SDK version they want to use before suggesting any code. Do not assume or default to a version.** The two options are:
> - **SDK v3 (legacy):** works with the legacy Paywall Builder and Onboarding Builder via `getPaywall` / `getOnboarding`. Use the legacy code in this section.
> - **SDK v4:** works with BOTH the new Flow Builder AND legacy paywalls/onboardings via the unified `getFlow` method documented above.
>
> Apply the legacy v3 code in this section ONLY when:
> 1. The user explicitly chose **SDK v3**, or
> 2. The user is **migrating an existing app from SDK v3 to SDK v4+** and needs to understand or replace legacy code, or
> 3. The user is **troubleshooting an already-deployed v3 integration**.
>
> **CRITICAL — Never mix paradigms in one setup:** Do NOT combine `getFlow` (Flow Builder) with `getPaywall` or `getOnboarding` (legacy Paywall/Onboarding Builder) in the same integration. These are incompatible patterns. Mixing them will produce inconsistent behavior and is unsupported.

Les paywalls configurés avec le [Paywall Builder](adapty-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 comprennent les pressions sur des boutons (boutons de fermeture, URLs, 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.

:::warning
Ce guide est réservé aux paywalls **du nouveau Paywall Builder** uniquement.
:::

Pour contrôler ou surveiller les processus qui se déroulent sur l'écran de 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.

:::note
Ces méthodes sont l'endroit où vous ajoutez votre logique personnalisée pour répondre aux événements de 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 \{#user-generated-events\}

### Apparition et disparition du paywall \{#paywall-appearance-and-disappearance\}

Lorsqu'un paywall apparaît ou disparaît, ces méthodes seront invoquées :

```kotlin showLineNumbers title="Kotlin"
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
}
```

:::note
- Sur iOS, `paywallViewDidAppear` est également invoqué quand un utilisateur appuie sur le [bouton de paywall web](web-paywall#step-2a-add-a-web-purchase-button) dans un paywall, et qu'un paywall web s'ouvre dans un navigateur intégré.
- Sur iOS, `paywallViewDidDisappear` est également invoqué quand un [paywall web](web-paywall#step-2a-add-a-web-purchase-button) ouvert depuis un paywall dans un navigateur intégré disparaît de l'écran.
:::

<Details>
<summary>Exemples d'événements (cliquez pour développer)</summary>

```javascript
// Paywall appeared
{
  // No additional data
}

// Paywall disappeared
{
  // No additional data
}
```
</Details>

### Sélection de produit \{#product-selection\}

Si un utilisateur sélectionne un produit à acheter, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
override fun paywallViewDidSelectProduct(view: AdaptyUIPaywallView, productId: String) {
    // Handle product selection
    // You can update UI or track analytics here
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

```javascript
{
  "productId": "premium_monthly"
}
```
</Details>

### Achat démarré \{#started-purchase\}

Si un utilisateur initie le processus d'achat, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
override fun paywallViewDidStartPurchase(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct) {
    // Handle purchase start
    // You can show loading indicators or track analytics here
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

### Achat réussi, annulé ou en attente \{#successful-canceled-or-pending-purchase\}

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 :

```kotlin showLineNumbers title="Kotlin"
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
        }
    }
}
```

<Details>
<summary>Exemples d'événements (cliquez pour développer)</summary>

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

Nous recommandons de fermer l'écran du paywall en cas d'achat réussi.

### Achat échoué \{#failed-purchase\}

Si un achat échoue en raison d'une erreur, cette méthode sera invoquée. Cela inclut les erreurs StoreKit/Google Play Billing (restrictions de paiement, produits invalides, échecs réseau), les échecs de vérification de transaction et les erreurs système. Notez que les annulations par l'utilisateur déclenchent `paywallViewDidFinishPurchase` avec un résultat annulé à la place, et les paiements en attente ne déclenchent pas cette méthode.

```kotlin showLineNumbers title="Kotlin"
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
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

### Restauration démarrée \{#started-restore\}

Si un utilisateur initie le processus de restauration, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
override fun paywallViewDidStartRestore(view: AdaptyUIPaywallView) {
    // Handle restore start
    // You can show loading indicators or track analytics here
}
```

### Restauration réussie \{#successful-restore\}

Si la restauration d'un achat réussit, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
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()
    }
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</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>

Nous recommandons de fermer l'écran si l'utilisateur dispose du `accessLevel` requis. Consultez la rubrique [Statut d'abonnement](subscription-status) pour savoir comment le vérifier.

### Restauration échouée \{#failed-restore\}

Si `Adapty.restorePurchases()` échoue, cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
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
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

### Fin de la navigation de paiement web \{#web-payment-navigation-completion\}

Si un utilisateur initie le processus d'achat via un [paywall web](web-paywall), cette méthode sera invoquée :

```kotlin showLineNumbers title="Kotlin"
override fun paywallViewDidFinishWebPaymentNavigation(
    view: AdaptyUIPaywallView,
    product: AdaptyPaywallProduct?,
    error: AdaptyError?
) {
    if (error != null) {
        // Handle web payment navigation error
    } else {
        // Handle successful web payment navigation
    }
}
```

<Details>
<summary>Exemples d'événements (cliquez pour développer)</summary>

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

## Chargement des données et rendu \{#data-fetching-and-rendering\}

### Erreurs de chargement des produits \{#product-loading-errors\}

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 :

```kotlin showLineNumbers title="Kotlin"
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
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

### Erreurs de rendu \{#rendering-errors\}

Si une erreur survient pendant le rendu de l'interface, elle sera signalée par cette méthode :

```kotlin showLineNumbers title="Kotlin"
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
}
```

<Details>
<summary>Exemple d'événement (cliquez pour développer)</summary>

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

Dans une situation normale, de telles erreurs ne devraient pas se produire. Si vous en rencontrez une, merci de nous en informer.

---