---
title: "Gérer les événements de flow et de paywall - React Native"
description: "Gérez les événements de flow et de paywall dans votre application React Native avec le SDK d'Adapty."
---

:::important
Ce guide couvre la gestion des événements pour les achats, les restaurations, la sélection de produits et le rendu des flows. Vous pouvez également configurer la gestion des boutons (fermeture du flow, ouverture de liens, actions personnalisées, etc.). Consultez notre [guide sur la gestion des actions de boutons](react-native-handle-paywall-actions) pour plus de détails.
:::

Les flows et paywalls créés avec le Flow 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épondre. Ces événements incluent des 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 dans le flow. Découvrez ci-dessous comment répondre à ces événements.

Pour contrôler ou surveiller les processus se déroulant sur l'écran du flow dans votre application mobile, implémentez des gestionnaires d'événements :

<Tabs groupId="presentation-method" queryString>
<TabItem value="platform" label="React component" default>

Pour un composant React, vous gérez les événements via des props de gestionnaire d'événements individuelles dans le composant `AdaptyFlowView` :

```typescript showLineNumbers title="React Native (TSX)"

function MyFlow({ flow }) {
  const onCloseButtonPress = useCallback<FlowEventHandlers['onCloseButtonPress']>(() => {}, []);
  const onProductSelected = useCallback<FlowEventHandlers['onProductSelected']>((productId) => {}, []);
  const onPurchaseStarted = useCallback<FlowEventHandlers['onPurchaseStarted']>((product) => {}, []);
  const onPurchaseCompleted = useCallback<FlowEventHandlers['onPurchaseCompleted']>((purchaseResult, product) => {}, []);
  const onPurchaseFailed = useCallback<FlowEventHandlers['onPurchaseFailed']>((error, product) => {}, []);
  const onRestoreStarted = useCallback<FlowEventHandlers['onRestoreStarted']>(() => {}, []);
  const onRestoreCompleted = useCallback<FlowEventHandlers['onRestoreCompleted']>((profile) => {}, []);
  const onRestoreFailed = useCallback<FlowEventHandlers['onRestoreFailed']>((error) => {}, []);
  const onAppeared = useCallback<FlowEventHandlers['onAppeared']>(() => {}, []);
  const onError = useCallback<FlowEventHandlers['onError']>((error) => {}, []);
  const onLoadingProductsFailed = useCallback<FlowEventHandlers['onLoadingProductsFailed']>((error) => {}, []);
  const onUrlPress = useCallback<FlowEventHandlers['onUrlPress']>((url, openIn) => {
    adapty.openWebUrl(url, openIn);
    return false;
  }, []);
  const onCustomAction = useCallback<FlowEventHandlers['onCustomAction']>((actionId) => {}, []);
  const onWebPaymentNavigationFinished = useCallback<FlowEventHandlers['onWebPaymentNavigationFinished']>(() => {}, []);

  return (
    <AdaptyFlowView
      flow={flow}
      style={styles.container}
      onCloseButtonPress={onCloseButtonPress}
      onProductSelected={onProductSelected}
      onPurchaseStarted={onPurchaseStarted}
      onPurchaseCompleted={onPurchaseCompleted}
      onPurchaseFailed={onPurchaseFailed}
      onRestoreStarted={onRestoreStarted}
      onRestoreCompleted={onRestoreCompleted}
      onRestoreFailed={onRestoreFailed}
      onAppeared={onAppeared}
      onError={onError}
      onLoadingProductsFailed={onLoadingProductsFailed}
      onUrlPress={onUrlPress}
      onCustomAction={onCustomAction}
      onWebPaymentNavigationFinished={onWebPaymentNavigationFinished}
    />
  );
}
```

</TabItem>
<TabItem value="standalone" label="Modal presentation">

Pour une présentation modale, implémentez la méthode des gestionnaires d'événements.

:::important
Appeler `setEventHandlers` plusieurs fois remplacera les gestionnaires que vous fournissez, en remplaçant à la fois les gestionnaires par défaut et ceux définis précédemment pour ces événements spécifiques.
:::

```javascript showLineNumbers title="React Native (TSX)"

const view = await createFlowView(flow);

const unsubscribe = view.setEventHandlers({
  onCloseButtonPress() {
    return true;
  },
  onAndroidSystemBack() {
    return true;
  },
  onPurchaseCompleted(purchaseResult, product) {
    return purchaseResult.type !== 'user_cancelled';
  },
  onPurchaseStarted(product) { /***/},
  onPurchaseFailed(error, product) { /***/ },
  onRestoreCompleted(profile) { /***/ },
  onRestoreFailed(error) { /***/ },
  onProductSelected(productId) { /***/},
  onError(error) { /***/ },
  onLoadingProductsFailed(error) { /***/ },
  onUrlPress(url, openIn) {
      adapty.openWebUrl(url, openIn);
      return false; // Keep flow open
  },
  onAppeared() { /***/ },
  onDisappeared() { /***/ },
  onWebPaymentNavigationFinished() { /***/ },
});
```

</TabItem>
</Tabs>

<Details>
<summary>Exemples d'événements (Cliquez pour agrandir)</summary>

```javascript
// onCloseButtonPress
{
  //Record the event
}

// onAndroidSystemBack
{
  //Record the event
}

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

// onCustomAction
{
  "actionId": "login"
}

// onProductSelected
{
  "productId": "premium_monthly"
}

// onPurchaseStarted
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onPurchaseCompleted - Success
{
  "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",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

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

// onPurchaseFailed
{
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onRestoreCompleted
{
  "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
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

// onError
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render flow interface",
    "details": {
      "underlyingError": "Invalid flow configuration"
    }
  }
}

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

// onAppeared
{
  //Record the event
}

// onDisappeared
{
  //Record the event
}

// onWebPaymentNavigationFinished
{
  //Record the event
}
```
</Details>

Vous pouvez enregistrer uniquement les gestionnaires d'événements dont vous avez besoin et ignorer les autres. Ainsi, aucun écouteur d'événement inutile ne sera créé. Aucun gestionnaire d'événement n'est obligatoire.

Les gestionnaires d'événements renvoient un booléen. Si `true` est renvoyé, le processus d'affichage est considéré comme terminé : l'écran du flow se ferme et les écouteurs d'événements associés à cette vue sont supprimés.

Certains gestionnaires d'événements ont un comportement par défaut que vous pouvez remplacer si nécessaire :
- `onCloseButtonPress` : ferme le flow lorsque le bouton de fermeture est pressé.
- `onUrlPress` : ouvre l'URL tapée et maintient le flow ouvert.
- `onAndroidSystemBack` (uniquement pour la présentation modale) : maintient le flow ouvert lorsque le bouton **Back** est pressé. Retournez `true` pour le fermer.
- `onRestoreCompleted` : maintient le flow ouvert après une restauration réussie. Retournez `true` pour le fermer.
- `onPurchaseCompleted` : maintient le flow ouvert après la finalisation d'un achat. Retournez `true` pour le fermer.
- `onError` : ferme le flow si son rendu échoue.

### Gestionnaires d'événements \{#event-handlers\}

| Gestionnaire d'événements              | Description                                                                                                                                                                                                                                                                                                              |
|:---------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **onCustomAction**                     | Déclenché lorsqu'un utilisateur effectue une action personnalisée, par exemple en cliquant sur un [bouton personnalisé](paywall-buttons).                                                                                                                                                                                  |
| **onUrlPress**                         | Déclenché lorsqu'un utilisateur clique sur une URL dans votre flow.                                                                                                                                                                                                                                                       |
| **onAndroidSystemBack**                | Présentation modale uniquement : déclenché lorsqu'un utilisateur appuie sur le bouton système Android **Retour**.                                                                                                                                                                                                          |
| **onCloseButtonPress**                 | Déclenché lorsque le bouton de fermeture est visible et qu'un utilisateur appuie dessus. Il est recommandé de fermer l'écran du flow dans ce gestionnaire.                                                                                                                                                                 |
| **onPurchaseCompleted**                | Déclenché lorsque l'achat se termine, qu'il soit réussi, annulé par l'utilisateur ou en attente d'approbation. En cas d'achat réussi, il fournit un `AdaptyProfile` mis à jour. Les annulations et les paiements en attente (par exemple, approbation parentale requise) déclenchent cet événement, et non `onPurchaseFailed`. |
| **onPurchaseStarted**                  | Déclenché lorsqu'un utilisateur appuie sur le bouton d'action « Acheter » pour démarrer le processus d'achat.                                                                                                                                                                                                             |
| **onPurchaseFailed**                   | Déclenché lorsqu'un achat échoue en raison d'erreurs (par exemple, restrictions de paiement, produits invalides, échecs réseau, échecs de vérification de transaction). Non déclenché pour les annulations utilisateur ou les paiements en attente, qui déclenchent `onPurchaseCompleted` à la place.                      |
| **onRestoreStarted**                   | Déclenché lorsqu'un utilisateur lance un processus de restauration d'achat.                                                                                                                                                                                                                                               |
| **onRestoreCompleted**                 | Déclenché lorsque la restauration des achats réussit et fournit un `AdaptyProfile` mis à jour. Il est recommandé de fermer l'écran si l'utilisateur dispose du `accessLevel` requis. Consultez la rubrique [Statut de l'abonnement](react-native-listen-subscription-changes) pour savoir comment le vérifier.             |
| **onRestoreFailed**                    | Déclenché lorsque le processus de restauration échoue et fournit une `AdaptyError`.                                                                                                                                                                                                                                       |
| **onProductSelected**                  | Déclenché lorsqu'un produit de la vue du flow est sélectionné, vous permettant de surveiller ce que l'utilisateur sélectionne avant l'achat.                                                                                                                                                                               |
| **onError**                            | Déclenché lorsqu'une erreur survient pendant le rendu de la vue et fournit une `AdaptyError`. Ces erreurs ne devraient pas se produire ; si vous en rencontrez une, merci de nous en informer.                                                                                                                             |
| **onLoadingProductsFailed**            | Déclenché lorsque le chargement des produits échoue et fournit une `AdaptyError`. Si vous n'avez pas défini `prefetchProducts: true` lors de la création de la vue, AdaptyUI récupérera lui-même les objets nécessaires depuis le serveur.                                                                                  |
| **onAppeared**                         | Déclenché lorsque le flow est affiché à l'utilisateur. Sur iOS, également déclenché lorsqu'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é à l'application.                                       |
| **onDisappeared**                      | Présentation modale uniquement : déclenché lorsque le flow est fermé par l'utilisateur. Sur iOS, également déclenché lorsqu'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.                                                         |
| **onWebPaymentNavigationFinished**     | Déclenché après une tentative d'ouverture d'un [paywall web](web-paywall) pour un achat, qu'elle soit réussie ou non.                                                                                                                                                                                                     |
| **onAnalytics**                        | Réservé aux événements analytiques personnalisés provenant 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.                                                                                                                                        |
| **onRequestAppReview**                 | Réservé aux demandes d'avis sur l'application provenant d'un flow. Les flows ne déclenchent pas encore de demandes d'avis, vous n'avez donc pas besoin de l'implémenter.                                                                                                                                                  |
| **onRequestPermission**                | Réservé aux demandes d'autorisation système (telles que les notifications push ou l'accès à la caméra) provenant d'un flow. Les flows ne déclenchent pas encore de demandes d'autorisation, vous n'avez donc pas besoin de l'implémenter.                                                                                  |
| **onObserverPurchaseInitiated**        | Mode observateur uniquement : déclenché lorsqu'un utilisateur appuie sur le bouton d'achat dans un flow. Adapty n'effectue pas l'achat — réalisez-le avec votre propre code d'achat, puis signalez la transaction à Adapty. Voir [Gérer les achats en mode observateur](#handle-purchases-in-observer-mode) ci-dessous.    |
| **onObserverRestoreInitiated**         | Mode observateur uniquement : déclenché lorsqu'un utilisateur appuie sur le bouton de restauration dans un flow. Adapty n'effectue pas la restauration — faites-le vous-même, puis signalez les transactions restaurées. Voir [Gérer les achats en mode observateur](#handle-purchases-in-observer-mode) ci-dessous.       |

### Gérer les achats en mode observateur \{#handle-purchases-in-observer-mode\}

Si vous avez activé le SDK en [mode observateur](implement-observer-mode-react-native) (`observerMode: true`) et que vous affichez un flow rendu par Adapty, le SDK n'effectue pas les achats à votre place. Lorsqu'un utilisateur appuie sur le bouton d'achat ou de restauration, le SDK appelle `onObserverPurchaseInitiated` ou `onObserverRestoreInitiated` à la place. Effectuez l'achat ou la restauration avec votre propre code, pilotez l'indicateur de chargement du flow avec les callbacks fournis, puis [signalez la transaction](report-transactions-observer-mode-react-native) à Adapty ensuite.

```typescript showLineNumbers
const unsubscribe = view.setEventHandlers({
  onObserverPurchaseInitiated(product, onStartPurchase, onFinishPurchase) {
    onStartPurchase(); // show the flow's loading indicator
    myPurchaseApi(product.vendorProductId)
      .then((transactionId) => adapty.reportTransaction(transactionId))
      .finally(() => onFinishPurchase()); // hide the loading indicator
    return false; // keep the flow open; dismiss it yourself after success
  },
  onObserverRestoreInitiated(onStartRestore, onFinishRestore) {
    onStartRestore();
    myRestoreApi()
      .finally(() => onFinishRestore());
    return false;
  },
});
```

---

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

:::important
Ce guide couvre la gestion des événements pour les achats, les restaurations, la sélection de produits et le rendu des paywalls. Vous devez également implémenter la gestion des boutons (fermeture du paywall, ouverture de liens, etc.). Consultez notre [guide sur la gestion des actions de boutons](react-native-handle-paywall-actions) pour en savoir plus.
:::

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 appuis 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éagir à ces événements ci-dessous.

:::warning
Ce guide concerne uniquement les **paywalls créés avec le nouveau Paywall Builder**, qui nécessitent le SDK Adapty v3.0 ou une version ultérieure.
:::

Pour contrôler ou surveiller les processus qui se produisent sur l'écran paywall de votre application mobile, implémentez des gestionnaires d'événements :

<Tabs groupId="presentation-method" queryString>
<TabItem value="platform" label="React component" default>

Pour le composant React, vous gérez les événements via des props de gestionnaire d'événements individuels dans le composant `AdaptyPaywallView` :

```typescript showLineNumbers title="React Native (TSX)"

function MyPaywall({ paywall }) {
  const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => {}, []);
  const onProductSelected = useCallback<EventHandlers['onProductSelected']>((productId) => {}, []);
  const onPurchaseStarted = useCallback<EventHandlers['onPurchaseStarted']>((product) => {}, []);
  const onPurchaseCompleted = useCallback<EventHandlers['onPurchaseCompleted']>((purchaseResult, product) => {}, []);
  const onPurchaseFailed = useCallback<EventHandlers['onPurchaseFailed']>((error, product) => {}, []);
  const onRestoreStarted = useCallback<EventHandlers['onRestoreStarted']>(() => {}, []);
  const onRestoreCompleted = useCallback<EventHandlers['onRestoreCompleted']>((profile) => {}, []);
  const onRestoreFailed = useCallback<EventHandlers['onRestoreFailed']>((error) => {}, []);
  const onPaywallShown = useCallback<EventHandlers['onPaywallShown']>(() => {}, []);
  const onRenderingFailed = useCallback<EventHandlers['onRenderingFailed']>((error) => {}, []);
  const onLoadingProductsFailed = useCallback<EventHandlers['onLoadingProductsFailed']>((error) => {}, []);
  const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => {
    Linking.openURL(url);
  }, []);
  const onCustomAction = useCallback<EventHandlers['onCustomAction']>((actionId) => {}, []);
  const onWebPaymentNavigationFinished = useCallback<EventHandlers['onWebPaymentNavigationFinished']>(() => {}, []);

  return (
    <AdaptyPaywallView
      paywall={paywall}
      style={styles.container}
      onCloseButtonPress={onCloseButtonPress}
      onProductSelected={onProductSelected}
      onPurchaseStarted={onPurchaseStarted}
      onPurchaseCompleted={onPurchaseCompleted}
      onPurchaseFailed={onPurchaseFailed}
      onRestoreStarted={onRestoreStarted}
      onRestoreCompleted={onRestoreCompleted}
      onRestoreFailed={onRestoreFailed}
      onPaywallShown={onPaywallShown}
      onRenderingFailed={onRenderingFailed}
      onLoadingProductsFailed={onLoadingProductsFailed}
      onUrlPress={onUrlPress}
      onCustomAction={onCustomAction}
      onWebPaymentNavigationFinished={onWebPaymentNavigationFinished}
    />
  );
}
```

</TabItem>
<TabItem value="standalone" label="Modal presentation">

Pour la présentation modale, implémentez la méthode des gestionnaires d'événements.

:::important
Appeler `setEventHandlers` plusieurs fois écrasera les gestionnaires que vous fournissez, en remplaçant à la fois les gestionnaires par défaut et ceux précédemment définis pour ces événements spécifiques.
:::

```javascript showLineNumbers title="React Native (TSX)"

const view = await createPaywallView(paywall);

const unsubscribe = view.setEventHandlers({
  onCloseButtonPress() {
    return true;
  },
  onAndroidSystemBack() {
    return true;
  },
  onPurchaseCompleted(purchaseResult, product) {
    return purchaseResult.type !== 'user_cancelled';
  },
  onPurchaseStarted(product) { /***/},
  onPurchaseFailed(error) { /***/ },
  onRestoreCompleted(profile) { /***/ },
  onRestoreFailed(error) { /***/ },
  onProductSelected(productId) { /***/},
  onRenderingFailed(error) { /***/ },
  onLoadingProductsFailed(error) { /***/ },
  onUrlPress(url) {
      Linking.openURL(url);
      return false; // Keep paywall open
  },
  onPaywallShown() { /***/ },
  onPaywallClosed() { /***/ },
  onWebPaymentNavigationFinished() { /***/ },
});
```

</TabItem>
</Tabs>

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

```javascript
// onCloseButtonPress
{
  //Record the event
}

// onAndroidSystemBack
{
  //Record the event
}

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

// onCustomAction
{
  "actionId": "login"
}

// onProductSelected
{
  "productId": "premium_monthly"
}

// onPurchaseStarted
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onPurchaseCompleted - Success
{
  "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",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

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

// onPurchaseFailed
{
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onRestoreCompleted
{
  "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
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

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

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

// onPaywallShown
{
  //Record the event
}

// onPaywallClosed
{
  //Record the event
}

// onWebPaymentNavigationFinished
{
  //Record the event
}
```
</Details>

Vous pouvez enregistrer uniquement les gestionnaires d'événements dont vous avez besoin, et ignorer les autres. Dans ce cas, aucun écouteur d'événement inutile ne sera créé. Aucun gestionnaire d'événement n'est obligatoire.

Les gestionnaires d'événements renvoient un booléen. Si `true` est renvoyé, le processus d'affichage est considéré comme terminé : l'écran du paywall se ferme et les écouteurs d'événements associés à cette vue sont supprimés.

Certains gestionnaires d'événements ont un comportement par défaut que vous pouvez redéfinir si nécessaire :
- `onCloseButtonPress` : ferme le paywall quand le bouton de fermeture est appuyé.
- `onUrlPress` : ouvre l'URL touchée et garde le paywall ouvert.
- `onAndroidSystemBack` (uniquement pour la présentation modale) : ferme le paywall quand le bouton **Back** est appuyé.
- `onRestoreCompleted` : ferme le paywall après une restauration réussie.
- `onPurchaseCompleted` : ferme le paywall sauf si l'utilisateur a annulé.
- `onRenderingFailed` : ferme le paywall si son rendu échoue.

### Gestionnaires d'événements \{#event-handlers\}

| Gestionnaire d'événements          | Description                                                                                                                                                                                                                                                                                                                      |
|:-----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **onCustomAction**                 | Déclenché lorsqu'un utilisateur effectue une action personnalisée, par exemple en cliquant sur un [bouton personnalisé](paywall-buttons).                                                                                                                                                                                         |
| **onUrlPress**                     | Déclenché lorsqu'un utilisateur clique sur une URL dans votre paywall.                                                                                                                                                                                                                                                           |
| **onAndroidSystemBack**            | Présentation modale uniquement : déclenché lorsqu'un utilisateur appuie sur le bouton système Android **Back**.                                                                                                                                                                                                                  |
| **onCloseButtonPress**             | Déclenché lorsque le bouton de fermeture est visible et qu'un utilisateur appuie dessus. Il est recommandé de fermer l'écran du paywall dans ce gestionnaire.                                                                                                                                                                    |
| **onPurchaseCompleted**            | Déclenché lorsque l'achat se termine, qu'il soit réussi, annulé par l'utilisateur ou en attente d'approbation. En cas d'achat réussi, il fournit un `AdaptyProfile` mis à jour. Les annulations par l'utilisateur et les paiements en attente (ex. : approbation parentale requise) déclenchent cet événement, pas `onPurchaseFailed`. |
| **onPurchaseStarted**              | Déclenché lorsqu'un utilisateur appuie sur le bouton d'action « Acheter » pour lancer le processus d'achat.                                                                                                                                                                                                                      |
| **onPurchaseFailed**               | Déclenché lorsqu'un achat échoue en raison d'erreurs (ex. : restrictions de paiement, produits invalides, échecs réseau, échecs de vérification de transaction). Non déclenché pour les annulations utilisateur ou les paiements en attente, qui déclenchent `onPurchaseCompleted` à la place.                                   |
| **onRestoreStarted**               | Déclenché lorsqu'un utilisateur démarre un processus de restauration d'achat.                                                                                                                                                                                                                                                    |
| **onRestoreCompleted**             | Déclenché lorsque la restauration des achats réussit et fournit un `AdaptyProfile` mis à jour. Il est recommandé de fermer l'écran si l'utilisateur possède le `accessLevel` requis. Consultez la rubrique [Statut d'abonnement](react-native-listen-subscription-changes) pour savoir comment le vérifier.                       |
| **onRestoreFailed**                | Déclenché lorsque le processus de restauration échoue et fournit `AdaptyError`.                                                                                                                                                                                                                                                  |
| **onProductSelected**              | Déclenché lorsqu'un produit du paywall est sélectionné, ce qui vous permet de suivre ce que l'utilisateur choisit avant l'achat.                                                                                                                                                                                                 |
| **onRenderingFailed**              | Déclenché lorsqu'une erreur survient pendant le rendu de la vue et fournit `AdaptyError`. Ces erreurs ne devraient pas se produire ; si vous en rencontrez une, veuillez nous le signaler.                                                                                                                                       |
| **onLoadingProductsFailed**        | Déclenché lorsque le chargement des produits échoue et fournit `AdaptyError`. Si vous n'avez pas défini `prefetchProducts: true` lors de la création de la vue, AdaptyUI récupérera lui-même les objets nécessaires depuis le serveur.                                                                                            |
| **onPaywallShown**                 | Déclenché lorsque le paywall est affiché à l'utilisateur. Sur iOS, également déclenché lorsqu'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é.                                                       |
| **onPaywallClosed**                | Présentation modale uniquement : déclenché lorsque le paywall est fermé par l'utilisateur. Sur iOS, également déclenché lorsqu'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.                                                         |
| **onWebPaymentNavigationFinished** | Déclenché après une tentative d'ouverture d'un [paywall web](web-paywall) pour un achat, qu'elle soit réussie ou non.                                                                                                                                                                                                            |

---