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

# Migration de v2 vers v3

> Guide de mise à niveau simple pour migrer de l'API v2 vers v3

## Vue d'ensemble

Ce guide vous montre exactement ce qu'il faut modifier lors de la migration de l'API OneClickDz Flexy v2 vers v3. Chaque exemple présente votre code v2 actuel, liste les changements à effectuer et montre le résultat en v3.

**Disponible en plusieurs langages** : exemples JavaScript/Node.js, PHP et Python inclus.

<Warning>
  **Dépréciation de l'API v2** : L'API v2 sera dépréciée le **30 octobre 2026**.
  Veuillez migrer avant cette date pour éviter toute interruption de service.
</Warning>

## Nouveautés de v3

<CardGroup cols={2}>
  <Card title="Réponses standardisées" icon="layer-group">
    Toutes les réponses encapsulées dans la structure `{(success, data, meta, requestId)}`
  </Card>

  <Card title="Meilleure gestion des erreurs" icon="shield-check">
    Erreurs structurées avec codes, messages et détails pour un débogage plus facile
  </Card>

  <Card title="Débogage amélioré" icon="info-circle">
    Chaque réponse inclut des horodatages et des IDs de requête uniques
  </Card>

  <Card title="Endpoints plus clairs" icon="route">
    Regroupement logique : `/mobile/*`, `/internet/*`, `/gift-cards/*`, `/account/*`
  </Card>

  <Card title="Clés API séparées" icon="key">
    Générez des clés sandbox dédiées pour les tests tout en conservant votre clé de production
  </Card>

  <Card title="Liste blanche d'IP" icon="shield-halved">
    Ajoutez des restrictions IP directement depuis la page des paramètres pour une sécurité renforcée
  </Card>
</CardGroup>

## Liste de contrôle de migration rapide

<Steps>
  <Step title="Générer une clé API sandbox (optionnel)">
    Créez une clé API sandbox depuis la page des paramètres de votre tableau de bord pour tester v3 avant de migrer la production
  </Step>

  <Step title="Configurer la liste blanche d'IP (optionnel)">
    Ajoutez des restrictions IP pour une sécurité renforcée directement depuis les paramètres
  </Step>

  <Step title="Mettre à jour l'en-tête d'authentification">
    Passez de `authorization` à `X-Access-Token` dans toutes les requêtes API
  </Step>

  <Step title="Mettre à jour la gestion des réponses">
    Accédez aux données via `result.data` au lieu d'y accéder directement depuis la réponse
  </Step>

  <Step title="Mettre à jour la gestion des erreurs">
    Vérifiez le booléen `result.success` et gérez l'objet structuré `result.error`
  </Step>

  <Step title="Mettre à jour les chemins des endpoints">
    Mappez les chemins v2 vers les nouveaux chemins v3 (voir le tableau de référence complet ci-dessous)
  </Step>

  <Step title="Mettre à jour la logique de pagination">
    Accédez aux informations de pagination depuis `result.data.pagination` au lieu du niveau racine
  </Step>
</Steps>

## Choisissez votre stratégie de migration

<Info>
  **Bonne nouvelle** : Les APIs v2 et v3 fonctionnent simultanément jusqu'à la dépréciation de v2.
  Votre clé API de production actuelle fonctionne avec les endpoints v2 et v3, vous offrant une flexibilité dans votre migration.
</Info>

<Note>
  **Changement d'URL de base** : v3 utilise une nouvelle URL de base : - v2 :
  `https://flexy-api.oneclickdz.com/v2` - v3 : `https://api.oneclickdz.com/v3`
</Note>

<Info>
  **Nouvelles fonctionnalités de sécurité** : v3 introduit la gestion de clés API sandbox séparées pour les tests, ainsi que la liste blanche d'IP directement depuis la page des paramètres de votre tableau de bord. Votre clé de production continue de fonctionner avec v2 et v3.
</Info>

Choisissez l'approche qui correspond à vos besoins :

<CardGroup cols={2}>
  <Card title="🆕 Nouveau départ" icon="rocket">
    **Idéal pour** : Refonte complète ou nouveaux projets

    * Générez une clé sandbox pour les tests depuis les paramètres
    * Configurez la liste blanche d'IP pour la clé de production
    * Testez tout en sandbox avec v3
    * Utilisez la clé de production quand vous êtes prêt

    ✅ Table rase, pas de code hérité
    ✅ Sécurité renforcée avec les restrictions IP
  </Card>

  <Card title="🔄 Migration progressive" icon="arrows-rotate">
    **Idéal pour** : Systèmes de production existants

    * Continuez d'utiliser la clé de production actuelle pour les endpoints v2
    * Générez une clé sandbox pour tester v3 en toute sécurité
    * Migrez les endpoints un par un vers v3
    * Exécutez v2 et v3 en parallèle avec la même clé de production

    ✅ Zéro temps d'arrêt, moins de risques
    ✅ Migrez à votre rythme
  </Card>
</CardGroup>

**Recommandé** : La plupart des équipes choisissent la **migration progressive** pour minimiser les risques. Vous pouvez d'abord mettre à jour les endpoints critiques tout en maintenant les autres sur v2.

### Migration intelligente : Commencez par les cartes cadeaux

<Info>
  **Conseil pro** : Si vous êtes intéressé par les cartes cadeaux, intégrez-les d'abord sur v3 ! Vous n'avez pas besoin de migrer vos recharges mobiles et internet existantes pour commencer à utiliser les cartes cadeaux sur v3. C'est une façon peu risquée de tester v3 tout en maintenant vos opérations critiques sur v2. Utilisez votre clé sandbox pour tester, puis passez en production quand vous êtes prêt.
</Info>

**Ordre de migration suggéré** :

1. **Phase 1** : Intégrer les cartes cadeaux sur v3 (si applicable)
   * Générez une clé sandbox pour les tests
   * Utilisez les endpoints `/v3/gift-cards/*` en sandbox
   * Testez soigneusement, puis utilisez la clé de production
   * Maintenez mobile/internet sur v2

2. **Phase 2** : Migrer les endpoints de compte et de validation
   * `/v3/validate`
   * `/v3/account/balance`
   * `/v3/account/transactions`

3. **Phase 3** : Migrer les recharges mobiles progressivement
   * Testez d'abord avec des opérations à faible volume
   * Surveillez les problèmes
   * Augmentez progressivement la migration

4. **Phase 4** : Migrer les recharges internet
   * Complétez avant la date limite de dépréciation

Cette approche vous permet de **migrer à votre rythme** jusqu'à la date de dépréciation tout en profitant des nouvelles fonctionnalités v3.

***

## Les 3 changements principaux

### 1. En-tête d'authentification

**Ce qu'il faut modifier** : Renommez l'en-tête de `authorization` à `X-Access-Token`

<Info>
  **Nouvelle gestion des clés** : v3 vous permet de générer une clé API sandbox séparée pour les tests depuis les paramètres de votre tableau de bord. Vous pouvez également ajouter des restrictions de liste blanche d'IP pour une sécurité renforcée. Votre clé API de production existante fonctionne avec les endpoints v2 et v3.
</Info>

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    // Votre code v2 actuel :
    fetch("https://flexy-api.oneclickdz.com/v2/plans/listAll", {
      headers: {
        authorization: "YOUR_API_KEY",
      },
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    // Votre code v2 actuel :
    $ch = curl_init('https://flexy-api.oneclickdz.com/v2/plans/listAll');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'authorization: YOUR_API_KEY'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Votre code v2 actuel :
    import requests

    response = requests.get(
    'https://flexy-api.oneclickdz.com/v2/plans/listAll',
    headers={'authorization': 'YOUR_API_KEY'}
    )
    ```
  </Tab>
</Tabs>

**Modifications nécessaires** :

* ❌ Supprimer : `authorization: "YOUR_API_KEY"`
* ✅ Ajouter : `X-Access-Token: "YOUR_API_KEY"`
* ✅ Mettre à jour : URL de base vers `https://api.oneclickdz.com/v3`

<Info>
  Le mécanisme d'authentification reste le même - seul le nom de l'en-tête change. Votre clé API existante fonctionne avec v2 et v3.
</Info>

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    // Code v3 mis à jour :
    fetch("https://api.oneclickdz.com/v3/mobile/plans", {
      headers: {
        "X-Access-Token": "YOUR_API_KEY",
      },
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    // Code v3 mis à jour :
    $ch = curl_init('https://api.oneclickdz.com/v3/mobile/plans');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'X-Access-Token: YOUR_API_KEY'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Code v3 mis à jour :
    import requests

    response = requests.get(
    'https://api.oneclickdz.com/v3/mobile/plans',
    headers={'X-Access-Token': 'YOUR_API_KEY'}
    )
    ```
  </Tab>
</Tabs>

***

### 2. Structure des réponses

**Ce qu'il faut modifier** : Toutes les réponses sont maintenant encapsulées dans une structure standard

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    // Votre code v2 actuel :
    const response = await fetch("https://flexy-api.oneclickdz.com/v2/account/balance", {
      headers: { authorization: API_KEY },
    });
    const data = await response.json();
    console.log("Balance:", data.balance); // Accès direct
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    // Votre code v2 actuel :
    $ch = curl_init('https://flexy-api.oneclickdz.com/v2/account/balance');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['authorization: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $data = json_decode($response, true);
    echo "Balance: " . $data['balance']; // Accès direct
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Votre code v2 actuel :
    response = requests.get(
        'https://flexy-api.oneclickdz.com/v2/account/balance',
        headers={'authorization': API_KEY}
    )
    data = response.json()
    print(f"Balance: {data['balance']}")  # Accès direct
    ```
  </Tab>
</Tabs>

**Modifications nécessaires** :

* ❌ Supprimer : L'accès direct à `data.balance`
* ✅ Ajouter : Vérifiez d'abord `result.success`
* ✅ Ajouter : Accédez via `result.data.balance`
* ✅ Bonus : Utilisez `result.requestId` pour le débogage
* ✅ Bonus : Utilisez `result.meta.timestamp` pour les informations de timing

<Warning>
  **Changement majeur** : En v3, vous devez toujours vérifier le booléen `success` avant
  d'accéder aux données. L'accès direct aux champs causera des erreurs si la requête a échoué.
</Warning>

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    // Code v3 mis à jour :
    const response = await fetch("https://api.oneclickdz.com/v3/account/balance", {
      headers: { "X-Access-Token": API_KEY },
    });
    const result = await response.json();

    if (result.success) {
      console.log("Balance:", result.data.balance);
      console.log("Request ID:", result.requestId); // Pour le débogage
      console.log("Timestamp:", result.meta.timestamp);
    } else {
      console.error("Error:", result.error.message);
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    // Code v3 mis à jour :
    $ch = curl_init('https://api.oneclickdz.com/v3/account/balance');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Access-Token: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    if ($result['success']) {
        echo "Balance: " . $result['data']['balance'] . "\n";
        echo "Request ID: " . $result['requestId'] . "\n";
        echo "Timestamp: " . $result['meta']['timestamp'] . "\n";
    } else {
        error_log("Error: " . $result['error']['message']);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Code v3 mis à jour :
    response = requests.get(
        'https://api.oneclickdz.com/v3/account/balance',
        headers={'X-Access-Token': API_KEY}
    )
    result = response.json()

    if result['success']:
        print(f"Balance: {result['data']['balance']}")
        print(f"Request ID: {result['requestId']}")  # Pour le débogage
        print(f"Timestamp: {result['meta']['timestamp']}")
    else:
        print(f"Error: {result['error']['message']}")
    ```
  </Tab>
</Tabs>

***

### 3. Gestion des erreurs

**Ce qu'il faut modifier** : Les erreurs sont maintenant des objets structurés avec des codes

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    // Votre code v2 actuel :
    const response = await fetch("https://flexy-api.oneclickdz.com/v2/topup/sendTopup", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        authorization: API_KEY,
      },
      body: JSON.stringify(data),
    });

    const result = await response.json();
    if (result.error) {
      console.error("Error:", result.message); // Chaîne simple
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    // Votre code v2 actuel :
    $ch = curl_init('https://flexy-api.oneclickdz.com/v2/topup/sendTopup');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'authorization: ' . $apiKey
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    if (isset($result['error'])) {
        error_log("Error: " . $result['message']); // Chaîne simple
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Votre code v2 actuel :
    response = requests.post(
        'https://flexy-api.oneclickdz.com/v2/topup/sendTopup',
        headers={
            'Content-Type': 'application/json',
            'authorization': API_KEY
        },
        json=data
    )
    result = response.json()

    if 'error' in result:
        print(f"Error: {result['message']}")  # Chaîne simple
    ```
  </Tab>
</Tabs>

**Modifications nécessaires** :

* ❌ Supprimer : La vérification de la chaîne `result.error`
* ✅ Ajouter : Vérifiez `result.success === false`
* ✅ Ajouter : Accédez à `result.error.code` et `result.error.message`
* ✅ Ajouter : Gérez les différents codes d'erreur avec des switch/if
* ✅ Ajouter : Journalisez `result.requestId` pour les tickets de support

<Info>
  **Conseil pro** : Sauvegardez toujours le `requestId` lors de la journalisation des erreurs. Notre équipe de support en a besoin pour tracer les problèmes dans notre système.
</Info>

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    // Code v3 mis à jour :
    const response = await fetch("https://api.oneclickdz.com/v3/mobile/send", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Access-Token": API_KEY,
      },
      body: JSON.stringify(data),
    });

    const result = await response.json();

    if (!result.success) {
      console.error(`Error [${result.error.code}]: ${result.error.message}`);
      console.log("Request ID:", result.requestId);

      switch (result.error.code) {
        case "INSUFFICIENT_BALANCE":
          alert("Veuillez recharger votre compte");
          break;
        case "ERR_PHONE":
          alert(`Numéro de téléphone invalide : ${result.error.message}`);
          break;
        case "DUPLICATED_REF":
          alert("Cette référence de commande a déjà été utilisée");
          break;
        default:
          alert("Une erreur s'est produite. Veuillez réessayer.");
      }
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    // Code v3 mis à jour :
    $ch = curl_init('https://api.oneclickdz.com/v3/mobile/send');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'X-Access-Token: ' . $apiKey
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    if (!$result['success']) {
        $errorCode = $result['error']['code'];
        $errorMessage = $result['error']['message'];
        error_log("Error [{$errorCode}]: {$errorMessage}");
        error_log("Request ID: " . $result['requestId']);

        switch ($errorCode) {
            case 'INSUFFICIENT_BALANCE':
                echo "Veuillez recharger votre compte\n";
                break;
            case 'ERR_PHONE':
                echo "Numéro de téléphone invalide : {$errorMessage}\n";
                break;
            case 'DUPLICATED_REF':
                echo "Cette référence de commande a déjà été utilisée\n";
                break;
            default:
                echo "Une erreur s'est produite. Veuillez réessayer\n";
        }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Code v3 mis à jour :
    response = requests.post(
        'https://api.oneclickdz.com/v3/mobile/send',
        headers={
            'Content-Type': 'application/json',
            'X-Access-Token': API_KEY
        },
        json=data
    )
    result = response.json()

    if not result['success']:
        error_code = result['error']['code']
        error_message = result['error']['message']
        print(f"Error [{error_code}]: {error_message}")
        print(f"Request ID: {result['requestId']}")

        if error_code == 'INSUFFICIENT_BALANCE':
            print("Veuillez recharger votre compte")
        elif error_code == 'ERR_PHONE':
            print(f"Numéro de téléphone invalide : {error_message}")
        elif error_code == 'DUPLICATED_REF':
            print("Cette référence de commande a déjà été utilisée")
        else:
            print("Une erreur s'est produite. Veuillez réessayer.")
    ```
  </Tab>
</Tabs>

***

## Exemples de migration étape par étape

### Exemple 1 : Récupérer les forfaits mobiles

**Étape 1 - Votre code v2 actuel :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch("https://flexy-api.oneclickdz.com/v2/plans/listAll", {
      headers: {
        authorization: API_KEY,
      },
    });

    const data = await response.json();
    const dynamicPlans = data.dymanicPlans; // Note: typo in v2
    const fixedPlans = data.fixedPlans;
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://flexy-api.oneclickdz.com/v2/plans/listAll');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['authorization: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $data = json_decode($response, true);

    $dynamicPlans = $data['dymanicPlans']; // Note: typo in v2
    $fixedPlans = $data['fixedPlans'];
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.get(
        'https://flexy-api.oneclickdz.com/v2/plans/listAll',
        headers={'authorization': API_KEY}
    )
    data = response.json()

    dynamic_plans = data['dymanicPlans']  # Note: typo in v2
    fixed_plans = data['fixedPlans']
    ```
  </Tab>
</Tabs>

**Étape 2 - Ce qu'il faut changer :**

1. Changer l'URL de base : `https://flexy-api.oneclickdz.com` → `https://api.oneclickdz.com`
2. Changer l'endpoint : `/v2/plans/listAll` → `/v3/mobile/plans`
3. Changer l'en-tête : `authorization` → `X-Access-Token`
4. Ajouter la vérification de succès : `if (result.success)`
5. Accéder via data : `data.dymanicPlans` → `result.data.dynamicPlans` (faute corrigée !)

<Warning>
  **OBLIGATOIRE** : v3 corrige la faute de frappe dans "dymanicPlans" en "dynamicPlans". Mettez à jour votre code pour utiliser l'orthographe correcte.
</Warning>

**Étape 3 - Votre nouveau code v3 :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch("https://api.oneclickdz.com/v3/mobile/plans", {
      headers: {
        "X-Access-Token": API_KEY,
      },
    });

    const result = await response.json();
    if (result.success) {
      const dynamicPlans = result.data.dynamicPlans; // Faute corrigée !
      const fixedPlans = result.data.fixedPlans;
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://api.oneclickdz.com/v3/mobile/plans');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Access-Token: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    if ($result['success']) {
        $dynamicPlans = $result['data']['dynamicPlans']; // Faute corrigée !
        $fixedPlans = $result['data']['fixedPlans'];
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.get(
        'https://api.oneclickdz.com/v3/mobile/plans',
        headers={'X-Access-Token': API_KEY}
    )
    result = response.json()

    if result['success']:
        dynamic_plans = result['data']['dynamicPlans']  # Faute corrigée !
        fixed_plans = result['data']['fixedPlans']
    ```
  </Tab>
</Tabs>

***

### Exemple 2 : Envoyer une recharge mobile

**Étape 1 - Votre code v2 actuel :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch("https://flexy-api.oneclickdz.com/v2/topup/sendTopup", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        authorization: API_KEY,
      },
      body: JSON.stringify({
        plan_code: "PREPAID_DJEZZY",
        MSSIDN: "0778037340",
        amount: 500,
        ref: "order-123",
      }),
    });

    const data = await response.json();
    console.log("Topup ID:", data.topupId);
    console.log("New Balance:", data.newBalance);
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $data = [
        'plan_code' => 'PREPAID_DJEZZY',
        'MSSIDN' => '0778037340',
        'amount' => 500,
        'ref' => 'order-123'
    ];

    $ch = curl_init('https://flexy-api.oneclickdz.com/v2/topup/sendTopup');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'authorization: ' . $apiKey
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    echo "Topup ID: " . $result['topupId'] . "\n";
    echo "New Balance: " . $result['newBalance'] . "\n";
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    data = {
        'plan_code': 'PREPAID_DJEZZY',
        'MSSIDN': '0778037340',
        'amount': 500,
        'ref': 'order-123'
    }

    response = requests.post(
        'https://flexy-api.oneclickdz.com/v2/topup/sendTopup',
        headers={
            'Content-Type': 'application/json',
            'authorization': API_KEY
        },
        json=data
    )
    result = response.json()

    print(f"Topup ID: {result['topupId']}")
    print(f"New Balance: {result['newBalance']}")
    ```
  </Tab>
</Tabs>

**Étape 2 - Ce qu'il faut changer :**

1. Changer l'URL de base : `https://flexy-api.oneclickdz.com` → `https://api.oneclickdz.com`
2. Changer l'endpoint : `/v2/topup/sendTopup` → `/v3/mobile/send`
3. Changer l'en-tête : `authorization` → `X-Access-Token`
4. Ajouter la vérification de succès : `if (result.success)`
5. Accéder via data : `data.topupId` → `result.data.topupId`
6. Ajouter une gestion complète des erreurs

<Warning>
  **Important** : Gérez toujours les erreurs lors de l'envoi de recharges. Les problèmes réseau, le solde insuffisant ou des données invalides peuvent causer des échecs. Sauvegardez le `requestId` pour le suivi.
</Warning>

**Étape 3 - Votre nouveau code v3 :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch("https://api.oneclickdz.com/v3/mobile/send", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Access-Token": API_KEY,
      },
      body: JSON.stringify({
        plan_code: "PREPAID_DJEZZY",
        MSSIDN: "0778037340",
        amount: 500,
        ref: "order-123",
      }),
    });

    const result = await response.json();
    if (result.success) {
      console.log("✅ Recharge envoyée avec succès !");
      console.log("Topup ID:", result.data.topupId);
      console.log("Nouveau solde:", result.data.newBalance);
      console.log("Request ID:", result.requestId);
    } else {
      console.error(`❌ Erreur [${result.error.code}]: ${result.error.message}`);
      console.error("Request ID:", result.requestId);
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://api.oneclickdz.com/v3/mobile/send');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'X-Access-Token: ' . $apiKey
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    if ($result['success']) {
        echo "✅ Recharge envoyée avec succès !\n";
        echo "Topup ID: " . $result['data']['topupId'] . "\n";
        echo "Nouveau solde: " . $result['data']['newBalance'] . "\n";
        echo "Request ID: " . $result['requestId'] . "\n";
    } else {
        error_log("❌ Erreur [{$result['error']['code']}]: {$result['error']['message']}");
        error_log("Request ID: " . $result['requestId']);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.post(
        'https://api.oneclickdz.com/v3/mobile/send',
        headers={
            'Content-Type': 'application/json',
            'X-Access-Token': API_KEY
        },
        json=data
    )
    result = response.json()

    if result['success']:
        print("✅ Recharge envoyée avec succès !")
        print(f"Topup ID: {result['data']['topupId']}")
        print(f"Nouveau solde: {result['data']['newBalance']}")
        print(f"Request ID: {result['requestId']}")
    else:
        print(f"❌ Erreur [{result['error']['code']}]: {result['error']['message']}")
        print(f"Request ID: {result['requestId']}")
    ```
  </Tab>
</Tabs>

***

### Exemple 3 : Vérifier le statut d'une recharge

**Étape 1 - Votre code v2 actuel :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch(
      "https://flexy-api.oneclickdz.com/v2/topup/checkStatus/REF/order-123",
      {
        headers: { authorization: API_KEY },
      }
    );

    const data = await response.json();
    console.log("Statut:", data.topup.status);
    console.log("Téléphone:", data.topup.MSSIDN);
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://flexy-api.oneclickdz.com/v2/topup/checkStatus/REF/order-123');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['authorization: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $data = json_decode($response, true);

    echo "Statut: " . $data['topup']['status'] . "\n";
    echo "Téléphone: " . $data['topup']['MSSIDN'] . "\n";
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.get(
        'https://flexy-api.oneclickdz.com/v2/topup/checkStatus/REF/order-123',
        headers={'authorization': API_KEY}
    )
    data = response.json()

    print(f"Statut: {data['topup']['status']}")
    print(f"Téléphone: {data['topup']['MSSIDN']}")
    ```
  </Tab>
</Tabs>

**Étape 2 - Ce qu'il faut changer :**

1. Changer l'URL de base : `https://flexy-api.oneclickdz.com` → `https://api.oneclickdz.com`
2. Changer l'endpoint : `/v2/topup/checkStatus/REF/:ref` → `/v3/mobile/check-ref/:ref`
3. Changer l'en-tête : `authorization` → `X-Access-Token`
4. Ajouter la vérification de succès : `if (result.success)`
5. Accéder directement : `data.topup.status` → `result.data.status` (plus d'objet `topup` imbriqué)
6. Gérer les nouveaux champs d'information sur le remboursement

<Info>
  **Nouvelle fonctionnalité** : v3 inclut des informations détaillées sur les remboursements avec des offres alternatives suggérées quand une recharge est remboursée.
</Info>

**Étape 3 - Votre nouveau code v3 :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch(
      "https://api.oneclickdz.com/v3/mobile/check-ref/order-123",
      {
        headers: { "X-Access-Token": API_KEY },
      }
    );

    const result = await response.json();
    if (result.success) {
      console.log("Statut:", result.data.status);
      console.log("Téléphone:", result.data.MSSIDN);

      // Nouveau en v3 : informations de remboursement améliorées
      if (result.data.status === "REFUNDED") {
        console.log("❌ La recharge a été remboursée");
        console.log("Raison:", result.data.refund_message);
        if (result.data.suggested_offers) {
          console.log("💡 Essayez ces offres à la place :");
          console.log(result.data.suggested_offers);
        }
      } else if (result.data.status === "FULFILLED") {
        console.log("✅ Recharge complétée avec succès");
      }
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://api.oneclickdz.com/v3/mobile/check-ref/order-123');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Access-Token: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    if ($result['success']) {
        echo "Statut: " . $result['data']['status'] . "\n";
        echo "Téléphone: " . $result['data']['MSSIDN'] . "\n";

        // Nouveau en v3 : informations de remboursement améliorées
        if ($result['data']['status'] === 'REFUNDED') {
            echo "❌ La recharge a été remboursée\n";
            echo "Raison: " . $result['data']['refund_message'] . "\n";
            if (isset($result['data']['suggested_offers'])) {
                echo "💡 Essayez ces offres à la place :\n";
                print_r($result['data']['suggested_offers']);
            }
        } elseif ($result['data']['status'] === 'FULFILLED') {
            echo "✅ Recharge complétée avec succès\n";
        }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.get(
        'https://api.oneclickdz.com/v3/mobile/check-ref/order-123',
        headers={'X-Access-Token': API_KEY}
    )
    result = response.json()

    if result['success']:
        print(f"Statut: {result['data']['status']}")
        print(f"Téléphone: {result['data']['MSSIDN']}")

        # Nouveau en v3 : informations de remboursement améliorées
        if result['data']['status'] == 'REFUNDED':
            print("❌ La recharge a été remboursée")
            print(f"Raison: {result['data']['refund_message']}")
            if 'suggested_offers' in result['data']:
                print("💡 Essayez ces offres à la place :")
                print(result['data']['suggested_offers'])
        elif result['data']['status'] == 'FULFILLED':
            print("✅ Recharge complétée avec succès")
    ```
  </Tab>
</Tabs>

***

### Exemple 4 : Lister les transactions avec pagination

**Étape 1 - Votre code v2 actuel :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch(
      "https://flexy-api.oneclickdz.com/v2/account/transactions?page=1&pageSize=20",
      {
        headers: { authorization: API_KEY },
      }
    );

    const data = await response.json();
    console.log("Transactions:", data.transactions);
    console.log("Total:", data.totalResults);
    console.log("Page:", data.currentPage);
    console.log("Pages:", data.totalPages);
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://flexy-api.oneclickdz.com/v2/account/transactions?page=1&pageSize=20');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['authorization: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $data = json_decode($response, true);

    echo "Transactions: " . count($data['transactions']) . "\n";
    echo "Total: " . $data['totalResults'] . "\n";
    echo "Page: " . $data['currentPage'] . "\n";
    echo "Pages: " . $data['totalPages'] . "\n";
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.get(
        'https://flexy-api.oneclickdz.com/v2/account/transactions?page=1&pageSize=20',
        headers={'authorization': API_KEY}
    )
    data = response.json()

    print(f"Transactions: {len(data['transactions'])}")
    print(f"Total: {data['totalResults']}")
    print(f"Page: {data['currentPage']}")
    print(f"Pages: {data['totalPages']}")
    ```
  </Tab>
</Tabs>

**Étape 2 - Ce qu'il faut changer :**

1. Changer l'URL de base : `https://flexy-api.oneclickdz.com` → `https://api.oneclickdz.com`
2. Changer la version : `/v2/account/transactions` → `/v3/account/transactions`
3. Changer l'en-tête : `authorization` → `X-Access-Token`
4. Ajouter la vérification de succès : `if (result.success)`
5. Accéder aux éléments : `data.transactions` → `result.data.items`
6. Accéder à la pagination : Champs au niveau racine → objet `result.data.pagination`

<Warning>
  **Changement cassant** : La structure de pagination a été déplacée du niveau racine vers `data.pagination`. Mettez à jour toute la logique de liste/pagination.
</Warning>

**Étape 3 - Votre nouveau code v3 :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch(
      "https://api.oneclickdz.com/v3/account/transactions?page=1&pageSize=20",
      {
        headers: { "X-Access-Token": API_KEY },
      }
    );

    const result = await response.json();
    if (result.success) {
      console.log("Transactions:", result.data.items);
      console.log("Total:", result.data.pagination.totalResults);
      console.log("Page:", result.data.pagination.page);
      console.log("Pages:", result.data.pagination.totalPages);
      console.log("Taille de page:", result.data.pagination.pageSize);
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://api.oneclickdz.com/v3/account/transactions?page=1&pageSize=20');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Access-Token: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    if ($result['success']) {
        echo "Transactions: " . count($result['data']['items']) . "\n";
        echo "Total: " . $result['data']['pagination']['totalResults'] . "\n";
        echo "Page: " . $result['data']['pagination']['page'] . "\n";
        echo "Pages: " . $result['data']['pagination']['totalPages'] . "\n";
        echo "Taille de page: " . $result['data']['pagination']['pageSize'] . "\n";
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.get(
        'https://api.oneclickdz.com/v3/account/transactions?page=1&pageSize=20',
        headers={'X-Access-Token': API_KEY}
    )
    result = response.json()

    if result['success']:
        print(f"Transactions: {len(result['data']['items'])}")
        print(f"Total: {result['data']['pagination']['totalResults']}")
        print(f"Page: {result['data']['pagination']['page']}")
        print(f"Pages: {result['data']['pagination']['totalPages']}")
        print(f"Taille de page: {result['data']['pagination']['pageSize']}")
    ```
  </Tab>
</Tabs>

***

### Exemple 5 : Vérifier les produits internet

**Étape 1 - Votre code v2 actuel :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch(
      "https://flexy-api.oneclickdz.com/v2/internet/checkCards/ADSL",
      {
        headers: { authorization: API_KEY },
      }
    );

    const cards = await response.json(); // Direct array
    cards.forEach((card) => {
      console.log(`${card.value} DA - Disponible: ${card.available}`);
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://flexy-api.oneclickdz.com/v2/internet/checkCards/ADSL');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['authorization: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $cards = json_decode($response, true); // Direct array

    foreach ($cards as $card) {
        echo $card['value'] . " DA - Disponible: " . $card['available'] . "\n";
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.get(
        'https://flexy-api.oneclickdz.com/v2/internet/checkCards/ADSL',
        headers={'authorization': API_KEY}
    )
    cards = response.json()  # Direct array

    for card in cards:
        print(f"{card['value']} DA - Disponible: {card['available']}")
    ```
  </Tab>
</Tabs>

**Étape 2 - Ce qu'il faut changer :**

1. Changer l'URL de base : `https://flexy-api.oneclickdz.com` → `https://api.oneclickdz.com`
2. Changer l'endpoint : `/v2/internet/checkCards/:type` → `/v3/internet/products?type=:type`
3. Passer du param de chemin au param de requête : `/ADSL` → `?type=ADSL`
4. Changer l'en-tête : `authorization` → `X-Access-Token`
5. Ajouter la vérification de succès : `if (result.success)`
6. Accéder au tableau : Tableau direct → `result.data.products`

<Info>
  **Conception API** : v3 utilise des paramètres de requête au lieu de paramètres de chemin pour la sélection du type, facilitant l'ajout de filtres à l'avenir.
</Info>

**Étape 3 - Votre nouveau code v3 :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const response = await fetch(
      "https://api.oneclickdz.com/v3/internet/products?type=ADSL",
      {
        headers: { "X-Access-Token": API_KEY },
      }
    );

    const result = await response.json();
    if (result.success) {
      result.data.products.forEach((card) => {
        console.log(`${card.value} DA - Disponible: ${card.available}`);
      });
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://api.oneclickdz.com/v3/internet/products?type=ADSL');
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Access-Token: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $result = json_decode($response, true);

    if ($result['success']) {
        foreach ($result['data']['products'] as $card) {
            echo $card['value'] . " DA - Disponible: " . $card['available'] . "\n";
        }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.get(
        'https://api.oneclickdz.com/v3/internet/products?type=ADSL',
        headers={'X-Access-Token': API_KEY}
    )
    result = response.json()

    if result['success']:
        for card in result['data']['products']:
            print(f"{card['value']} DA - Disponible: {card['available']}")
    ```
  </Tab>
</Tabs>

***

## Migrer votre client API

Voici comment mettre à jour votre wrapper de client API :

**Étape 1 - Votre client v2 actuel :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    class FlexyAPI {
      constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseUrl = "https://flexy-api.oneclickdz.com";
      }

      async request(endpoint, options = {}) {
        const response = await fetch(`${this.baseUrl}${endpoint}`, {
          ...options,
          headers: {
            ...options.headers,
            authorization: this.apiKey,
          },
        });
        return response.json();
      }

      async getBalance() {
        const data = await this.request("/v2/account/balance");
        return data.balance;
      }

      async sendTopup(params) {
        return await this.request("/v2/topup/sendTopup", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(params),
        });
      }
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    class FlexyAPI {
        private $apiKey;
        private $baseUrl = 'https://flexy-api.oneclickdz.com';

        public function __construct($apiKey) {
            $this->apiKey = $apiKey;
        }

        private function request($endpoint, $options = []) {
            $ch = curl_init($this->baseUrl . $endpoint);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'authorization: ' . $this->apiKey
            ]);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

            if (isset($options['method']) && $options['method'] === 'POST') {
                curl_setopt($ch, CURLOPT_POST, true);
                curl_setopt($ch, CURLOPT_POSTFIELDS, $options['body']);
            }

            $response = curl_exec($ch);
            return json_decode($response, true);
        }

        public function getBalance() {
            $data = $this->request('/v2/account/balance');
            return $data['balance'];
        }

        public function sendTopup($params) {
            return $this->request('/v2/topup/sendTopup', [
                'method' => 'POST',
                'body' => json_encode($params)
            ]);
        }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests

    class FlexyAPI:
        def __init__(self, api_key):
            self.api_key = api_key
            self.base_url = 'https://flexy-api.oneclickdz.com'

        def request(self, endpoint, method='GET', data=None):
            headers = {'authorization': self.api_key}
            url = f"{self.base_url}{endpoint}"

            if method == 'GET':
                response = requests.get(url, headers=headers)
            else:
                headers['Content-Type'] = 'application/json'
                response = requests.post(url, headers=headers, json=data)

            return response.json()

        def get_balance(self):
            data = self.request('/v2/account/balance')
            return data['balance']

        def send_topup(self, params):
            return self.request('/v2/topup/sendTopup', method='POST', data=params)
    ```
  </Tab>
</Tabs>

**Étape 2 - Ce qu'il faut changer :**

1. Changer l'URL de base : `https://flexy-api.oneclickdz.com` → `https://api.oneclickdz.com`
2. Changer l'en-tête : `authorization` → `X-Access-Token`
3. Ajouter la gestion de l'encapsuleur de réponse dans la méthode `request()`
4. Vérifier `result.success` et gérer les erreurs
5. Mettre à jour tous les chemins d'endpoint vers v3
6. Lever des erreurs structurées avec des codes et des IDs de requête

<Note>
  **Bonne pratique** : Centralisez la gestion des erreurs dans votre client API. Cela facilite l'ajout de journalisation, de surveillance et de notifications utilisateur.
</Note>

**Étape 3 - Votre nouveau client v3 :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    class FlexyAPI {
      constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseUrl = "https://api.oneclickdz.com";
      }

      async request(endpoint, options = {}) {
        const response = await fetch(`${this.baseUrl}${endpoint}`, {
          ...options,
          headers: {
            ...options.headers,
            "X-Access-Token": this.apiKey,
          },
        });

        const result = await response.json();

        if (!result.success) {
          const error = new Error(result.error.message);
          error.code = result.error.code;
          error.details = result.error.details;
          error.requestId = result.requestId;
          throw error;
        }

        return result.data;
      }

      async getBalance() {
        const data = await this.request("/v3/account/balance");
        return data.balance;
      }

      async sendTopup(params) {
        return await this.request("/v3/mobile/send", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(params),
        });
      }
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    class FlexyAPI {
        private $apiKey;
        private $baseUrl = 'https://api.oneclickdz.com';

        public function __construct($apiKey) {
            $this->apiKey = $apiKey;
        }

        private function request($endpoint, $options = []) {
            $ch = curl_init($this->baseUrl . $endpoint);
            $headers = ['X-Access-Token: ' . $this->apiKey];

            if (isset($options['method']) && $options['method'] === 'POST') {
                curl_setopt($ch, CURLOPT_POST, true);
                curl_setopt($ch, CURLOPT_POSTFIELDS, $options['body']);
                $headers[] = 'Content-Type: application/json';
            }

            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exec($ch);
            $result = json_decode($response, true);

            if (!$result['success']) {
                throw new Exception(json_encode([
                    'message' => $result['error']['message'],
                    'code' => $result['error']['code'],
                    'details' => $result['error']['details'] ?? null,
                    'requestId' => $result['requestId']
                ]));
            }

            return $result['data'];
        }

        public function getBalance() {
            $data = $this->request('/v3/account/balance');
            return $data['balance'];
        }

        public function sendTopup($params) {
            return $this->request('/v3/mobile/send', [
                'method' => 'POST',
                'body' => json_encode($params)
            ]);
        }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests

    class FlexyAPIError(Exception):
        def __init__(self, message, code, details=None, request_id=None):
            super().__init__(message)
            self.code = code
            self.details = details
            self.request_id = request_id

    class FlexyAPI:
        def __init__(self, api_key):
            self.api_key = api_key
            self.base_url = 'https://api.oneclickdz.com'

        def request(self, endpoint, method='GET', data=None):
            headers = {'X-Access-Token': self.api_key}
            url = f"{self.base_url}{endpoint}"

            if method == 'GET':
                response = requests.get(url, headers=headers)
            else:
                headers['Content-Type'] = 'application/json'
                response = requests.post(url, headers=headers, json=data)

            result = response.json()

            if not result['success']:
                raise FlexyAPIError(
                    result['error']['message'],
                    result['error']['code'],
                    result['error'].get('details'),
                    result['requestId']
                )

            return result['data']

        def get_balance(self):
            data = self.request('/v3/account/balance')
            return data['balance']

        def send_topup(self, params):
            return self.request('/v3/mobile/send', method='POST', data=params)
    ```
  </Tab>
</Tabs>

***

## Référence des codes d'erreur

Erreurs v2 courantes et leurs équivalents v3 :

| Erreur v2                | Code d'erreur v3       | Ce qui a changé                                  |
| ------------------------ | ---------------------- | ------------------------------------------------ |
| `"NO-BALANCE"`           | `INSUFFICIENT_BALANCE` | Maintenant un code avec un message structuré     |
| `"DUPLICATED-REF"`       | `DUPLICATED_REF`       | Même code, maintenant avec un objet details      |
| `"ERR_VALIDATION"`       | `ERR_VALIDATION`       | Inclut `details.field` et `details.pattern`      |
| `"ERR_PHONE"`            | `ERR_PHONE`            | Inclut le pattern de validation dans les détails |
| `"ERR_STOCK"`            | `ERR_STOCK`            | Informations sur la disponibilité du produit     |
| `"Forbidden"`            | `ERR_AUTH`             | Plus descriptif, erreurs d'auth unifiées         |
| `"Access token missing"` | `ERR_AUTH`             | Même code pour toutes les erreurs d'auth         |
| `"NOT_FOUND"`            | `NOT_FOUND`            | Ressource non trouvée (commande, produit, etc.)  |

**Comment gérer les erreurs v3 :**

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    // Méthode v2 :
    if (result.error === "NO-BALANCE") {
      // gérer
    }

    // Méthode v3 :
    if (!result.success && result.error.code === "INSUFFICIENT_BALANCE") {
      console.log("Request ID:", result.requestId); // Sauvegarder pour le support
      if (result.error.details?.currentBalance) {
        console.log("Solde actuel:", result.error.details.currentBalance);
      }
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    // Méthode v2 :
    if ($result['error'] === 'NO-BALANCE') {
        // gérer
    }

    // Méthode v3 :
    if (!$result['success'] && $result['error']['code'] === 'INSUFFICIENT_BALANCE') {
        error_log("Request ID: " . $result['requestId']);
        if (isset($result['error']['details']['currentBalance'])) {
            echo "Solde actuel: " . $result['error']['details']['currentBalance'] . "\n";
        }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Méthode v2 :
    if result.get('error') == 'NO-BALANCE':
        # gérer

    # Méthode v3 :
    if not result['success'] and result['error']['code'] == 'INSUFFICIENT_BALANCE':
        print(f"Request ID: {result['requestId']}")
        if 'currentBalance' in result['error'].get('details', {}):
            print(f"Solde actuel: {result['error']['details']['currentBalance']}")
    ```
  </Tab>
</Tabs>

***

<Warning>
  **Délai critique** : Après le 30 octobre 2026, toutes les requêtes API v2 échoueront. Planifiez votre migration en conséquence pour éviter toute interruption de service.
</Warning>

***

## Problèmes de migration courants et solutions

<AccordionGroup>
  <Accordion title="Problème : Erreurs 401 Non autorisé" icon="circle-exclamation">
    **Problème** : Endpoint changé mais nom de l'en-tête non mis à jour.

    **Solution** : Remplacez `authorization` par `X-Access-Token` dans TOUTES les requêtes.

    ```javascript theme={null}
    // ❌ Incorrect
    headers: { authorization: API_KEY }

    // ✅ Correct
    headers: { "X-Access-Token": API_KEY }
    ```
  </Accordion>

  <Accordion title="Problème : Obtenir 'undefined' en accédant aux données" icon="circle-exclamation">
    **Problème** : Tentative d'accès direct aux données sans passer par `result.data`.

    **Solution** : Accédez toujours aux données de réponse via la propriété `data`.

    ```javascript theme={null}
    // ❌ Incorrect
    const balance = result.balance;

    // ✅ Correct
    const balance = result.data.balance;
    ```
  </Accordion>

  <Accordion title="Problème : La pagination ne fonctionne pas" icon="circle-exclamation">
    **Problème** : Recherche des champs de pagination au niveau racine.

    **Solution** : Accédez à la pagination via `data.pagination`.

    ```javascript theme={null}
    // ❌ Incorrect
    const total = result.totalResults;
    const page = result.currentPage;

    // ✅ Correct
    const total = result.data.pagination.totalResults;
    const page = result.data.pagination.page;
    ```
  </Accordion>

  <Accordion title="Problème : La gestion des erreurs ne fonctionne pas" icon="circle-exclamation">
    **Problème** : Vérification de l'ancien format d'erreur.

    **Solution** : Vérifiez le booléen `success` et accédez à l'objet d'erreur structuré.

    ```javascript theme={null}
    // ❌ Incorrect
    if (result.error) {
      console.log(result.message);
    }

    // ✅ Correct
    if (!result.success) {
      console.log(result.error.code, result.error.message);
      console.log("Request ID:", result.requestId);
    }
    ```
  </Accordion>

  <Accordion title="Problème : Erreurs 404 Non trouvé" icon="circle-exclamation">
    **Problème** : Utilisation des anciens chemins d'endpoint v2.

    **Solution** : Mettez à jour tous les chemins selon le tableau de référence ci-dessus.

    ```javascript theme={null}
    // ❌ Incorrect
    POST /v2/topup/sendTopup
    GET /v2/internet/checkCards/ADSL

    // ✅ Correct
    POST /v3/mobile/send
    GET /v3/internet/products?type=ADSL
    ```
  </Accordion>
</AccordionGroup>

***

***

## Référence complète des endpoints

| Service              | Endpoint v2                             | Endpoint v3                                   | Notes                                          |
| -------------------- | --------------------------------------- | --------------------------------------------- | ---------------------------------------------- |
| **Authentification** |                                         |                                               |                                                |
| En-tête              | `authorization: KEY`                    | `X-Access-Token: KEY`                         | Nom de l'en-tête modifié                       |
| Valider              | `GET /v2/validate`                      | `GET /v3/validate`                            | Réponse encapsulée                             |
| **Mobile**           |                                         |                                               |                                                |
| Liste des forfaits   | `GET /v2/plans/listAll`                 | `GET /v3/mobile/plans`                        | Chemin modifié, faute de frappe corrigée       |
| Envoyer recharge     | `POST /v2/topup/sendTopup`              | `POST /v3/mobile/send`                        | Chemin simplifié                               |
| Vérifier par réf     | `GET /v2/topup/checkStatus/REF/:ref`    | `GET /v3/mobile/check-ref/:ref`               | Chemin simplifié, infos remboursement ajoutées |
| Vérifier par ID      | `GET /v2/topup/checkStatus/ID/:id`      | `GET /v3/mobile/check-id/:id`                 | Chemin simplifié                               |
| Liste recharges      | `GET /v2/topup/list`                    | `GET /v3/mobile/list`                         | Pagination déplacée vers `data.pagination`     |
| **Internet**         |                                         |                                               |                                                |
| Produits             | `GET /v2/internet/checkCards/:type`     | `GET /v3/internet/products?type=`             | Param de chemin → param de requête             |
| Vérifier numéro      | `GET /v2/internet/checkNumber/:t/:n`    | `GET /v3/internet/check-number?type=&number=` | Params de chemin → params de requête           |
| Envoyer              | `POST /v2/internet/sendTopup`           | `POST /v3/internet/send`                      | Chemin simplifié                               |
| Vérifier par réf     | `GET /v2/internet/checkStatus/REF/:ref` | `GET /v3/internet/check-ref/:ref`             | Chemin simplifié                               |
| Vérifier par ID      | `GET /v2/internet/checkStatus/ID/:id`   | `GET /v3/internet/check-id/:id`               | Chemin simplifié                               |
| Liste                | `GET /v2/internet/list`                 | `GET /v3/internet/list`                       | Pagination déplacée                            |
| **Cartes cadeaux**   |                                         |                                               |                                                |
| Catalogue            | `GET /v2/gift-cards/catalogue`          | `GET /v3/gift-cards/catalog`                  | Orthographe corrigée                           |
| Vérifier produit     | `GET /v2/gift-cards/checkProduct/:id`   | `GET /v3/gift-cards/checkProduct/:id`         | Réponse encapsulée                             |
| Passer commande      | `POST /v2/gift-cards/placeOrder`        | `POST /v3/gift-cards/placeOrder`              | Réponse encapsulée                             |
| Vérifier commande    | `GET /v2/gift-cards/checkOrder/:id`     | `GET /v3/gift-cards/checkOrder/:id`           | Réponse encapsulée                             |
| Liste commandes      | `GET /v2/gift-cards/list`               | `GET /v3/gift-cards/list`                     | Pagination déplacée                            |
| **Compte**           |                                         |                                               |                                                |
| Solde                | `GET /v2/account/balance`               | `GET /v3/account/balance`                     | Réponse encapsulée                             |
| Transactions         | `GET /v2/account/transactions`          | `GET /v3/account/transactions`                | Pagination déplacée                            |

***

## Liste de vérification des tests

Avant de déployer en production, vérifiez :

* [ ] Tous les en-têtes d'authentification modifiés vers `X-Access-Token`
* [ ] Tous les chemins d'endpoints mis à jour vers v3
* [ ] Vérification succès/erreur implémentée partout
* [ ] Données accessibles via `result.data`
* [ ] Pagination accessible via `result.data.pagination`
* [ ] Codes d'erreur gérés avec `result.error.code`
* [ ] IDs de requête journalisés pour le débogage
* [ ] Testé en mode sandbox d'abord
* [ ] Toutes les fonctionnalités existantes fonctionnent encore
* [ ] Scénarios d'erreur testés (solde insuffisant, données invalides, etc.)

***

## Besoin d'aide ?

<CardGroup cols={2}>
  <Card title="Documentation API v3" href="/fr/quickstart" icon="book">
    Référence complète v3 et exemples
  </Card>

  <Card title="Guide de gestion des erreurs" href="/fr/api-reference/error-handling" icon="shield-check">
    Bonnes pratiques pour la gestion des erreurs
  </Card>

  <Card title="Paramètres du tableau de bord" icon="gear">
    **Accès** : Connectez-vous à votre tableau de bord

    **Générer une clé sandbox** : Créez une clé sandbox pour des tests sécurisés

    **Liste blanche d'IP** : Ajoutez des restrictions IP pour une sécurité renforcée
  </Card>

  <Card title="Support" icon="headset">
    **Email** : [support@oneclickdz.com](mailto:support@oneclickdz.com)

    **Important** : Incluez toujours votre ID de requête lors du signalement de problèmes
  </Card>
</CardGroup>

<Info>
  **Support à la migration** : Notre équipe est là pour vous aider ! Envoyez-nous un e-mail avec "Support migration API v3" en objet, et incluez : - Les détails de votre implémentation actuelle - Les erreurs spécifiques (avec les IDs de requête) - Des exemples de code illustrant le problème
</Info>
