> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Droits de Fonctions de Fonctionnalité

> Gérez les fonctionnalités de votre application à partir d'un achat. Les droits de fonction de fonctionnalité offrent une capacité booléenne instantanément lors du paiement et la révoquent automatiquement lors de l'annulation.

<Info>
  Un droit de fonction de fonctionnalité transforme Dodo Payments en un magasin de fonctions de fonctionnalité conscient de la facturation. Attachez une fonction comme `advanced_reports` à un produit, et chaque client payant obtiendra une concession que votre application peut vérifier via API ou synchroniser avec des webhooks. Pas de plateforme externe, pas d'OAuth, pas d'étape de livraison — la concession elle-même est la capacité.
</Info>

## Ce qui est livré

Rien ne quitte Dodo Payments — la concession **est** le livrable :

* Lors de l'achat, la concession est créée et passe directement à `delivered`. Il n'y a pas de phase `pending`, pas d'action du client, et pas de moyen pour que la livraison échoue.
* La concession transporte une charge utile typée `feature` : `{ "feature_type": "boolean", "feature_id": "advanced_reports" }`. Votre application lit `feature_id` pour décider ce qu'il faut débloquer.
* L'annulation, le remboursement ou la révocation manuelle déplace la concession vers `revoked`, et votre application voit la fonction disparaître.

Les utilisations courantes incluent la gestion des fonctionnalités basées sur des plans (Pro débloque les analyses), les capacités supplémentaires (une mise à niveau "accès API"), et les programmes d'accès anticipé vendus sous forme d'achats uniques.

<Note>
  `feature_id` est un identifiant choisi par le commerçant, non unique à travers les droits. Deux droits peuvent conférer la même `feature_id` — par exemple, un plan Pro mensuel et annuel accordant tous deux `advanced_reports`.
</Note>

## Créer une fonction de fonctionnalité

<Steps>
  <Step title="Open Entitlements">
    Dans votre tableau de bord Dodo Payments, allez à **Droits** et cliquez sur **+** pour commencer un nouveau droit, puis choisissez **Fonctions de Fonctionnalité**.
  </Step>

  <Step title="Name the flag">
    Donnez à la fonction un **Nom d'Affichage** pour votre tableau de bord, un **ID de Fonctionnalité** que votre application vérifiera (le tableau de bord en suggère un à partir du nom), et une **Description** pour que votre équipe sache ce qu'elle contrôle.

    <Frame caption="Creating a feature flag. The Feature ID is what your application checks; Meta Data attaches limits alongside the flag.">
      <img src="https://mintcdn.com/dodopayments/oS2MTbJuY6MeBjjs/images/entitlements/feature-flags/create.png?fit=max&auto=format&n=oS2MTbJuY6MeBjjs&q=85&s=08d8fc2cfee102ff08fd150c76b5fcf9" alt="Formulaire de nouvelle fonction de fonctionnalité avec nom d'affichage, ID de fonctionnalité, description et entrées de métadonnées" style={{ maxHeight: '500px', width: 'auto' }} width="1196" height="776" data-path="images/entitlements/feature-flags/create.png" />
    </Frame>
  </Step>

  <Step title="Optionally add metadata">
    Basculez **Meta Data** pour attacher une configuration clé-valeur — limites, noms de niveaux, quotas — qui est livrée à votre application en même temps que la fonction. Voir [Attacher des limites avec des métadonnées](#attach-limits-with-metadata).
  </Step>

  <Step title="Confirm">
    Cliquez sur **Confirmer**. La fonction apparaît dans votre liste de droits, prête à attacher à des produits.

    <Frame caption="The created feature flag. The right pane tracks every customer grant issued from it.">
      <img src="https://mintcdn.com/dodopayments/oS2MTbJuY6MeBjjs/images/entitlements/feature-flags/list.png?fit=max&auto=format&n=oS2MTbJuY6MeBjjs&q=85&s=02eedcbbf7ece9f67d375c984c5515b9" alt="Tableau de bord des droits avec la fonction de fonctionnalité Rapports Avancés et son panneau d'activité de concession" style={{ maxHeight: '500px', width: 'auto' }} width="1316" height="898" data-path="images/entitlements/feature-flags/list.png" />
    </Frame>
  </Step>
</Steps>

## Attacher à un produit

Ouvrez un produit (ou créez-en un), trouvez la carte **Droits**, et cliquez sur **+** pour attacher des droits existants. Sélectionnez votre fonction de fonctionnalité et cliquez sur **Terminé**.

<Frame caption="Attaching the feature flag to a product. One product can deliver multiple entitlements.">
  <img src="https://mintcdn.com/dodopayments/oS2MTbJuY6MeBjjs/images/entitlements/feature-flags/attach-picker.png?fit=max&auto=format&n=oS2MTbJuY6MeBjjs&q=85&s=da2137873e285cc6ce0ce234fe899ed3" alt="Panneau d'attachement des droits avec la fonction de fonctionnalité Rapports Avancés sélectionnée" style={{ maxHeight: '500px', width: 'auto' }} width="1316" height="898" data-path="images/entitlements/feature-flags/attach-picker.png" />
</Frame>

La fonction attachée s'affiche sur le formulaire de produit, et la prévisualisation du paiement la répertorie sous **Comprend**.

<Frame caption="The product now includes the feature flag. Every successful purchase or active subscription grants it.">
  <img src="https://mintcdn.com/dodopayments/oS2MTbJuY6MeBjjs/images/entitlements/feature-flags/attach-to-product.png?fit=max&auto=format&n=oS2MTbJuY6MeBjjs&q=85&s=3556181389997edddde9c396d0ce408d" alt="Formulaire de produit avec la fonction de fonctionnalité Rapports Avancés attachée dans la carte Droits" style={{ maxHeight: '500px', width: 'auto' }} width="1316" height="898" data-path="images/entitlements/feature-flags/attach-to-product.png" />
</Frame>

## Configuration requise

| Champ          | Requis | Description                                                                                                 |
| -------------- | ------ | ----------------------------------------------------------------------------------------------------------- |
| `feature_id`   | Oui    | Identifiant que votre application vérifie, par exemple `advanced_reports`. Non unique à travers les droits. |
| `feature_type` | Oui    | Type de capacité conférée. Seul `boolean` est pris en charge aujourd'hui.                                   |

### Créer via API

<CodeGroup>
  ```typescript TypeScript theme={null} theme={null}
  import DodoPayments from 'dodopayments';

  const client = new DodoPayments({
    bearerToken: process.env['DODO_PAYMENTS_API_KEY'],
    environment: 'test_mode',
  });

  const entitlement = await client.entitlements.create({
    name: 'Advanced Reports',
    integration_type: 'feature_flag',
    integration_config: {
      feature_type: 'boolean',
      feature_id: 'advanced_reports',
    },
    metadata: {
      tier: 'pro',
      monthly_report_limit: 100,
    },
  });
  ```

  ```python Python theme={null} theme={null}
  client.entitlements.create(
      name="Advanced Reports",
      integration_type="feature_flag",
      integration_config={
          "feature_type": "boolean",
          "feature_id": "advanced_reports",
      },
      metadata={
          "tier": "pro",
          "monthly_report_limit": 100,
      },
  )
  ```

  ```go Go theme={null} theme={null}
  client.Entitlements.New(ctx, dodopayments.EntitlementNewParams{
    Name:            dodopayments.F("Advanced Reports"),
    IntegrationType: dodopayments.F(dodopayments.EntitlementIntegrationTypeFeatureFlag),
    IntegrationConfig: dodopayments.F[dodopayments.IntegrationConfigUnionParam](
      dodopayments.IntegrationConfigFeatureFlagConfigParam{
        FeatureType: dodopayments.F(dodopayments.FeatureTypeBoolean),
        FeatureID:   dodopayments.F("advanced_reports"),
      },
    ),
  })
  ```
</CodeGroup>

***

## Attacher des limites avec des métadonnées

Une fonction booléenne répond à la question "ce client a-t-il la fonctionnalité ?". Les métadonnées répondent "avec quelle configuration ?". Les métadonnées de droits acceptent les valeurs de type chaîne, entier, nombre et booléen, et chaque concession prend un **instantané figé** des métadonnées du droit au moment de sa création.

Ce comportement d'instantané est ce qui rend les métadonnées sûres à utiliser pour les limites de plan :

* Modifier ultérieurement les métadonnées du droit n'affecte que les futures concessions. Les clients conservent les limites avec lesquelles ils ont acheté.
* L'instantané est renvoyé sur chaque concession sous son champ `metadata`, donc un appel API vous donne à la fois la fonction et sa configuration.

Par exemple, une fonction `advanced_reports` avec `{ "tier": "pro", "monthly_report_limit": 100 }` permet à votre application de débloquer le tableau de bord *et* d'appliquer le quota de 100 rapports sans une deuxième recherche. Si vous augmentez plus tard la limite à 250, les clients existants restent à 100 jusqu'à ce qu'ils reçoivent une nouvelle concession (par exemple, après un changement de plan).

<Tip>
  Utilisez les métadonnées pour les limites et la configuration ; utilisez `feature_id` uniquement pour l'identité. Le codage des limites dans l'id (`advanced_reports_100`) force une nouvelle fonction pour chaque changement de limite et perturbe les vérifications de votre application.
</Tip>

***

## Vérifier les fonctionnalités d'un client

Répertoriez les concessions de fonctions de fonctionnalité livrées à un client et construisez l'ensemble des fonctionnalités activées. Le point de terminaison retourne une ligne par concession à travers tous les droits, filtrable par `integration_type` et `status`.

<CodeGroup>
  ```typescript TypeScript theme={null} theme={null}
  const features = new Map<string, Record<string, unknown>>();

  for await (const grant of client.customers.listEntitlementGrants('cus_abc123', {
    integration_type: 'feature_flag',
    status: 'Delivered',
  })) {
    if (grant.feature) {
      features.set(grant.feature.feature_id, grant.metadata ?? {});
    }
  }

  if (features.has('advanced_reports')) {
    const limit = features.get('advanced_reports')?.monthly_report_limit;
    // unlock the dashboard, enforce the limit
  }
  ```

  ```python Python theme={null} theme={null}
  page = client.customers.list_entitlement_grants(
      customer_id="cus_abc123",
      integration_type="feature_flag",
      status="Delivered",
  )

  features = {
      grant.feature.feature_id: grant.metadata
      for grant in page.items
      if grant.feature
  }

  if "advanced_reports" in features:
      limit = features["advanced_reports"].get("monthly_report_limit")
  ```

  ```go Go theme={null} theme={null}
  page, _ := client.Customers.ListEntitlementGrants(
    ctx, "cus_abc123",
    dodopayments.CustomerListEntitlementGrantsParams{
      IntegrationType: dodopayments.F("feature_flag"),
      Status:          dodopayments.F("Delivered"),
    },
  )

  features := map[string]bool{}
  for _, grant := range page.Items {
    if grant.Feature.FeatureID != "" {
      features[grant.Feature.FeatureID] = true
    }
  }
  ```
</CodeGroup>

<Note>
  La charge utile `feature` est remplie uniquement sur `feature_flag` concessions; elle est `null` pour chaque autre type d'intégration. Voir la référence API [Lister les concessions du client](/api-reference/entitlements/list-customer-grants) pour la structure de réponse complète.
</Note>

Vérifier l'API à chaque requête ajoute de la latence à votre chemin critique. Mettez en cache l'ensemble des fonctionnalités par client avec un TTL court (minutes, pas heures), et invalidez le cache depuis votre gestionnaire de webhook lorsqu'une concession change d'état — cette combinaison maintient les vérifications rapides et les révocations quasi instantanées.

***

## Cycle de vie

Les concessions de fonctions de fonctionnalité suivent le [cycle de vie des concessions](/features/entitlements/introduction#how-grants-work) standard avec une simplification : il n'y a pas d'étape de livraison, donc les concessions ne se retrouvent jamais dans `pending` et ne passent jamais à `failed`.

| Déclencheur                                        | Effet                                                                                                         |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Paiement unique réussi / abonnement devient actif  | Concession créée avec `status: delivered` et `delivered_at` définis.                                          |
| Abonnement mis en attente, annulé ou expiré        | Concession révoquée avec le correspondant `revocation_reason`.                                                |
| Remboursement sur un paiement unique               | Concession révoquée avec `revocation_reason: refund`.                                                         |
| Abonnement récupéré (par exemple, relance réussie) | La concession révoquée est restaurée à `delivered` — même concession `id`, champs de révocation effacés.      |
| Révocation manuelle via API                        | Concession révoquée avec `revocation_reason: manual`. Pas de restauration automatique lors du renouvellement. |

Les concessions sont idempotentes par droit et par client : tant qu'un client a une concession non révoquée pour une fonction, les achats répétés et les renouvellements ne créent pas de doublons.

***

## Webhooks

Abonnez-vous aux événements [`entitlement_grant.*`](/developer-resources/webhooks/intents/entitlement-grant) pour refléter les fonctions dans votre propre base de données au lieu de les interroger :

* `entitlement_grant.created` — arrive déjà `delivered` avec la charge utile `feature`. Activez la fonctionnalité.
* `entitlement_grant.delivered` — s'exécute lorsqu'une concession précédemment révoquée est restaurée. Réactivez la fonctionnalité.
* `entitlement_grant.revoked` — accès retiré. Désactivez la fonctionnalité et vérifiez `revocation_reason` pour décider de votre messagerie.

```typescript TypeScript theme={null} theme={null}
app.post('/webhooks/dodo', async (req, res) => {
  const event = req.body;

  if (event.type.startsWith('entitlement_grant.') && event.data.feature) {
    const { customer_id, feature } = event.data;
    const enabled = event.type !== 'entitlement_grant.revoked';

    await db.customerFeatures.upsert({
      customerId: customer_id,
      featureId: feature.feature_id,
      enabled,
      config: event.data.metadata ?? {},
    });
  }

  res.sendStatus(200);
});
```

Il n'y a pas de `entitlement_grant.failed` pour les fonctions de fonctionnalité — la livraison se fait entièrement à l'intérieur de Dodo Payments et ne peut pas échouer.

***

## Exemple : Pro plan débloque les rapports avancés

1. **Créez la fonction.** `feature_id: advanced_reports` with metadata `{ "tier": "pro", "monthly_report_limit": 100 }`.
2. **Attachez-la** à votre produit d'abonnement Pro Plan.
3. **Un client s'abonne.** Dodo Payments crée une concession `delivered` et déclenche `entitlement_grant.created` ; votre gestionnaire de webhook active `advanced_reports` pour le client avec une limite de 100.
4. **Votre application contrôle la fonctionnalité.** Lors du chargement du tableau de bord, vérifiez l'ensemble des fonctionnalités en cache (ou appelez `listEntitlementGrants`) et affichez l'onglet des rapports uniquement lorsque `advanced_reports` est présent.
5. **Le client annule.** Dodo Payments révoque la concession et déclenche `entitlement_grant.revoked` ; votre gestionnaire désactive la fonctionnalité. Si le client récupère plus tard via la relance, `entitlement_grant.delivered` la rétablit — aucun changement de code n'est nécessaire.

***

## Meilleures pratiques

* **Utilisez des ids de fonctionnalité stables en snake\_case.** Votre code d'application vérifie ces chaînes ; en renommer une est un changement de rupture des deux côtés.
* **Une fonction par capacité.** Préférez `advanced_reports` + `api_access` en tant que deux droits plutôt qu'une seule `pro_bundle` — les révocations et les mélanges de plans restent propres.
* **Gérez l'état à partir des webhooks, vérifiez avec l'API.** Les webhooks maintiennent votre base de données à jour ; le point de terminaison de la liste est la source de vérité pour les tâches de réconciliation et les ratés de cache.
* **Traitez `revoked` comme immédiat.** Une fonction révoquée signifie que le client ne paie plus pour la fonctionnalité. Vérifiez à la prochaine requête, pas à la prochaine session.
* **Placez les limites dans les métadonnées, pas dans le code.** Changer un quota ne nécessite alors que de modifier le droit — les nouveaux clients le récupèrent automatiquement tandis que les concessions existantes conservent leur instantané acheté.
