> ## 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.

# Vérifier le Statut par ID

> Vérifier le statut d'une recharge mobile en utilisant l'ID interne

## Vue d'Ensemble

Suivez le statut d'une recharge mobile en utilisant le `topupId` retourné depuis l'endpoint d'envoi. En général, nos serveurs traiteront votre requête en environ 5 à 30 secondes.

<Warning>
  **Important :** Il est obligatoire de comprendre tous les statuts possibles pour gérer correctement la mise à jour et la notification de vos utilisateurs.
</Warning>

## Paramètres de Chemin

<ParamField path="id" type="string" required>
  ID interne de la recharge depuis la réponse `/mobile/send`
</ParamField>

## Réponse

<ResponseField name="success" type="boolean" required>
  Indique si la requête a réussi
</ResponseField>

<ResponseField name="data" type="object" required>
  <Expandable title="properties">
    <ResponseField name="_id" type="string" required>
      Identifiant de la recharge
    </ResponseField>

    <ResponseField name="ref" type="string">
      Votre référence personnalisée (si fournie)
    </ResponseField>

    <ResponseField name="status" type="string" required>
      Statut actuel : `PENDING`, `HANDLING`, `FULFILLED`, `REFUNDED`, ou
      `UNKNOWN_ERROR`
    </ResponseField>

    <ResponseField name="plan_code" type="string" required>
      Code de forfait utilisé
    </ResponseField>

    <ResponseField name="MSSIDN" type="string" required>
      Numéro de téléphone
    </ResponseField>

    <ResponseField name="topup_amount" type="number" required>
      Montant de la recharge en DZD
    </ResponseField>

    <ResponseField name="balance_amount" type="number" required>
      Montant déduit de votre solde
    </ResponseField>

    <ResponseField name="created_at" type="string" required>
      Horodatage de création (ISO 8601)
    </ResponseField>

    <ResponseField name="refund_message" type="string">
      Message en arabe pour l'utilisateur final (uniquement si REFUNDED ou UNKNOWN\_ERROR)
    </ResponseField>

    <ResponseField name="suggested_offers" type="array">
      Forfaits alternatifs en cas de non-correspondance (uniquement si REFUNDED)

      <Expandable title="properties">
        <ResponseField name="typename" type="string">
          Nom d'affichage du forfait suggéré
        </ResponseField>

        <ResponseField name="plan_code" type="string">
          Code de forfait à utiliser
        </ResponseField>

        <ResponseField name="amount" type="number">
          Montant suggéré
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="follow_orderid" type="string">
      ID de commande de suivi si la commande a été redirigée
    </ResponseField>

    <ResponseField name="retry_orderid" type="string">
      ID de commande de nouvelle tentative si la commande a été réessayée
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta" type="object" required>
  <Expandable title="properties">
    <ResponseField name="timestamp" type="string" required>
      Horodatage de la réponse au format ISO 8601
    </ResponseField>
  </Expandable>
</ResponseField>

## Valeurs de Statut

<AccordionGroup>
  <Accordion title="PENDING" icon="clock">
    Le statut `PENDING` indique que la commande a été créée et est actuellement en file d'attente, en attente de traitement par l'un de nos serveurs. Elle sera traitée dès que possible.

    **Durée typique :** 2 à 15 secondes

    **Action :** Continuez à interroger toutes les 5 à 10 secondes
  </Accordion>

  <Accordion title="HANDLING" icon="gear">
    Le statut `HANDLING` indique que la commande est actuellement en cours de traitement par l'un de nos serveurs.

    **Durée typique :** 3 à 8 secondes

    **Action :** Continuez à interroger toutes les 5 à 10 secondes
  </Accordion>

  <Accordion title="FULFILLED" icon="circle-check">
    Le statut `FULFILLED` indique que la recharge mobile a été envoyée avec succès. ✅

    **Action :** Mettez à jour le statut de la commande comme terminée, notifiez l'utilisateur
  </Accordion>

  <Accordion title="REFUNDED" icon="rotate-left">
    Le statut `REFUNDED` indique que la recharge mobile a été remboursée. ❌

    **Champs disponibles :**

    * `refund_message` : Affichez ceci à l'utilisateur (en arabe)
    * `suggested_offers` : Forfaits alternatifs (en cas de non-correspondance)

    **Action :** Remboursez l'utilisateur, affichez le message, proposez des alternatives
  </Accordion>

  <Accordion title="UNKNOWN_ERROR" icon="triangle-exclamation">
    Le statut `UNKNOWN_ERROR` indique que la recharge mobile peut avoir réussi ou échoué. Le statut se mettra à jour dans 1 à 12 heures en FULFILLED ou REFUNDED après vérification manuelle par notre équipe de support. Nous serons immédiatement notifiés et aucune action de votre part n'est requise. ⚠️

    De telles erreurs surviennent en raison de la nature des commandes AT et des comportements inattendus des opérateurs (nouveaux messages, problèmes réseau, etc.).

    <Warning>
      **VOUS NE DEVEZ PAS REMBOURSER LE MONTANT** à votre utilisateur à ce statut !
    </Warning>

    **Action :**

    * Affichez `refund_message` à l'utilisateur
    * NE remboursez PAS immédiatement
    * Configurez une tâche cron quotidienne pour vérifier les commandes UNKNOWN\_ERROR et les mettre à jour en FULFILLED ou REFUNDED
    * Vérifiez à nouveau après 24 heures
    * Gérez ensuite les remboursements si le statut devient REFUNDED
  </Accordion>
</AccordionGroup>

## Exemples

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.oneclickdz.com/v3/mobile/check-id/6901616fe9e88196b4eb64b0 \
    -H "X-Access-Token: YOUR_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const topupId = "6901616fe9e88196b4eb64b0";
  const response = await fetch(
    `https://api.oneclickdz.com/v3/mobile/check-id/${topupId}`,
    { headers: { "X-Access-Token": "YOUR_API_KEY" } }
  );
  const { data } = await response.json();
  console.log(data.status);
  ```

  ```python Python theme={null}
  import requests

  topup_id = '6901616fe9e88196b4eb64b0'
  response = requests.get(
      f'https://api.oneclickdz.com/v3/mobile/check-id/{topup_id}',
      headers={'X-Access-Token': 'YOUR_API_KEY'}
  )
  status = response.json()['data']['status']
  ```

  ```php PHP theme={null}
  <?php
  $topupId = '6901616fe9e88196b4eb64b0';
  $ch = curl_init("https://api.oneclickdz.com/v3/mobile/check-id/{$topupId}");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Access-Token: YOUR_API_KEY']);
  $response = json_decode(curl_exec($ch), true);
  $status = $response['data']['status'];
  ?>
  ```
</CodeGroup>

### Réponse FULFILLED

```json theme={null}
{
  "success": true,
  "data": {
    "_id": "6901616fe9e88196b4eb64b0",
    "ref": "order-12345",
    "status": "FULFILLED",
    "plan_code": "PREPAID_DJEZZY",
    "MSSIDN": "0778037340",
    "topup_amount": 500,
    "balance_amount": 490,
    "created_at": "2025-10-29T00:35:59.378Z"
  },
  "meta": {
    "timestamp": "2025-10-29T00:36:15.606Z"
  },
  "requestId": "req_1730160975_abc123"
}
```

### Réponse REFUNDED

```json theme={null}
{
  "success": true,
  "data": {
    "_id": "6901616fe9e88196b4eb64b0",
    "ref": "order-12345",
    "status": "REFUNDED",
    "plan_code": "PREPAID_DJEZZY",
    "MSSIDN": "0778037340",
    "topup_amount": 500,
    "balance_amount": 490,
    "created_at": "2025-10-29T00:35:59.378Z",
    "refund_message": "الرصيد المطلوب غير متوفر لهذا الرقم"
  },
  "meta": {
    "timestamp": "2025-10-29T00:36:15.606Z"
  },
  "requestId": "req_1730160975_def456"
}
```

### Réponse REFUNDED avec Offres Suggérées

Lorsque le code de forfait ne correspond pas au type de numéro de téléphone, vous recevrez des alternatives suggérées :

```json theme={null}
{
  "success": true,
  "data": {
    "_id": "6901616fe9e88196b4eb64b0",
    "ref": "order-12345",
    "status": "REFUNDED",
    "plan_code": "PREPAID_DJEZZY",
    "MSSIDN": "0778037340",
    "topup_amount": 500,
    "balance_amount": 490,
    "created_at": "2025-10-29T00:35:59.378Z",
    "refund_message": "هذا العرض غير متوافق مع هذا الرقم جرب فاتورة",
    "suggested_offers": [
      {
        "typename": "📋 FACTURE | فاتورة",
        "plan_code": "FACTURE_DJEZZY",
        "amount": 500
      }
    ]
  },
  "meta": {
    "timestamp": "2025-10-29T00:36:15.606Z"
  },
  "requestId": "req_1730160975_ghi789"
}
```

<Note>
  **VEUILLEZ NOTER :** `suggested_offers` est un tableau et peut contenir plusieurs éléments, par exemple lorsque vous envoyez des forfaits GETMENU.
</Note>

## Stratégie de Polling

### Synchroniser Notre API avec Votre Backend

<Steps>
  <Step title="Démarrer le Polling">
    Commencez à vérifier le statut immédiatement après l'envoi de la recharge. Une approche simple consiste à définir un intervalle de 5 à 10 secondes sur votre frontend pour vérifier le statut. Une fois le statut FULFILLED, REFUNDED ou UNKNOWN\_ERROR, effacez l'intervalle et arrêtez d'envoyer des requêtes.

    ```javascript theme={null}
    const topupId = response.data.topupId;
    const status = await pollStatus(topupId);
    ```
  </Step>

  <Step title="Intervalle de Polling">
    Vérifiez toutes les 5 à 10 secondes tant que PENDING ou HANDLING. Cette approche maintiendra les requêtes au minimum (5 à 10 par transaction).

    Pour chaque requête GET de vos utilisateurs, vous pouvez lire le statut dans votre base de données. Seulement si le statut n'est pas `FULFILLED`, `REFUNDED` ou `UNKNOWN_ERROR`, envoyez une requête de vérification à notre API, mettez à jour votre base de données, puis répondez à votre utilisateur.

    ```javascript theme={null}
    async function pollStatus(topupId, maxAttempts = 60) {
      for (let i = 0; i < maxAttempts; i++) {
        const { data } = await checkTopupStatus(topupId);
        
        if (['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR'].includes(data.status)) {
          return data;
        }
        
        await sleep(5000); // Wait 5 seconds
      }
      throw new Error('Timeout');
    }
    ```
  </Step>

  <Step title="Gérer le Résultat">
    Mettez à jour la commande en fonction du statut final

    ```javascript theme={null}
    if (status === 'FULFILLED') {
      await markOrderComplete(orderId);
    } else if (status === 'REFUNDED') {
      await refundUser(orderId);
      await notifyUser(refund_message);
    } else if (status === 'UNKNOWN_ERROR') {
      await markForReview(orderId);
      // DO NOT refund yet - wait for daily cronjob
    }
    ```
  </Step>
</Steps>

## Exemple de Polling Complet

<CodeGroup>
  ```javascript Node.js theme={null}
  async function pollTopupStatus(topupId) {
    const maxAttempts = 60; // 5 minutes max
    const pollInterval = 5000; // 5 seconds

    for (let i = 0; i < maxAttempts; i++) {
      const response = await fetch(
        `https://api.oneclickdz.com/v3/mobile/check-id/${topupId}`,
        { headers: { "X-Access-Token": process.env.API_KEY } }
      );

      const { data } = await response.json();

      // Final states
      if (["FULFILLED", "REFUNDED", "UNKNOWN_ERROR"].includes(data.status)) {
        return data;
      }

      // Still processing
      console.log(`Attempt ${i + 1}: Status is ${data.status}`);
      await new Promise((resolve) => setTimeout(resolve, pollInterval));
    }

    throw new Error("Polling timeout after 5 minutes");
  }

  // Usage
  try {
    const result = await pollTopupStatus("6901616fe9e88196b4eb64b0");
    console.log("Final status:", result.status);
  } catch (error) {
    console.error("Polling failed:", error.message);
  }
  ```

  ```python Python theme={null}
  import requests
  import time

  def poll_topup_status(topup_id, max_attempts=60, poll_interval=5):
      """Poll top-up status until completion or timeout"""
      url = f'https://api.oneclickdz.com/v3/mobile/check-id/{topup_id}'
      headers = {'X-Access-Token': 'YOUR_API_KEY'}

      for attempt in range(max_attempts):
          response = requests.get(url, headers=headers)
          data = response.json()['data']

          # Check if final state
          if data['status'] in ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR']:
              return data

          # Still processing
          print(f"Attempt {attempt + 1}: Status is {data['status']}")
          time.sleep(poll_interval)

      raise TimeoutError('Polling timeout after 5 minutes')

  # Usage
  try:
      result = poll_topup_status('6901616fe9e88196b4eb64b0')
      print(f"Final status: {result['status']}")
  except TimeoutError as e:
      print(f"Error: {e}")
  ```
</CodeGroup>

## Gestion de UNKNOWN\_ERROR

<Warning>NE remboursez PAS immédiatement lorsque le statut est UNKNOWN\_ERROR !</Warning>

Le statut `UNKNOWN_ERROR` indique une incertitude quant au succès ou à l'échec de la recharge. Cela se produit en raison de la nature des commandes AT et des comportements inattendus des opérateurs (nouveaux messages, problèmes réseau, etc.).

<Steps>
  <Step title="Afficher le Message">
    Affichez `refund_message` à l'utilisateur expliquant le délai
  </Step>

  <Step title="Marquer pour Révision">
    Signalez la commande pour révision manuelle ou revérification automatisée. Nous serons immédiatement notifiés et aucune action de votre part n'est requise.
  </Step>

  <Step title="Revérification Automatisée">
    Configurez une tâche cron quotidienne pour vérifier les commandes incertaines. Le statut se mettra à jour dans 1 à 12 heures (jusqu'à 24h) en FULFILLED ou REFUNDED après vérification manuelle par notre équipe de support.

    ```javascript theme={null}
    // Daily at midnight
    cron.schedule('0 0 * * *', async () => {
      const uncertainOrders = await db.orders.find({
        status: 'UNKNOWN_ERROR',
        createdAt: { $lt: new Date(Date.now() - 24*60*60*1000) }
      });
      
      for (const order of uncertainOrders) {
        const status = await checkTopupStatus(order.apiTopupId);
        await updateOrderStatus(order.id, status.data.status);
        
        // Now handle refund if status is REFUNDED
        if (status.data.status === 'REFUNDED') {
          await processRefund(order);
        }
      }
    });
    ```
  </Step>

  <Step title="Résolution Finale">
    Le statut se mettra à jour en FULFILLED ou REFUNDED dans les 24 heures. C'est seulement à ce moment que vous devrez traiter les remboursements si le statut final est REFUNDED.
  </Step>
</Steps>

## Bonnes Pratiques

<CardGroup cols={2}>
  <Card title="Mettre en Cache le Statut" icon="database">
    Stockez le statut dans votre base de données et ne vérifiez l'API que pour les commandes en attente
  </Card>

  <Card title="Éviter le Sur-polling" icon="clock">
    Ne vérifiez pas plus fréquemment que toutes les 5 secondes pour les commandes actives
  </Card>

  <Card title="Gérer UNKNOWN_ERROR" icon="triangle-exclamation">
    Ne remboursez jamais immédiatement - attendez la résolution dans les 24 heures
  </Card>

  <Card title="Notifier les Utilisateurs" icon="bell">
    Envoyez des notifications push ou e-mail lorsque le statut change en état final
  </Card>
</CardGroup>

## Gestion des Remboursements

Un `refund_message` (message en arabe) sera ajouté à l'opération de recharge, et vous devez l'afficher tel quel à votre client.

**Exemples de scénarios :**

### Mauvais Numéro de Téléphone

Si votre client saisit un mauvais numéro de téléphone :

```
refund_message: "رقم الهاتف خاطئ، يرجى التأكد مع العميل"
```

### Inadéquation du Plan

Si vous essayez d'envoyer PREPAID\_DJEZZY vers un numéro facture (postpayé) :

```json theme={null}
{
  "refund_message": "هذا العرض غير متوافق مع هذا الرقم جرب فاتورة",
  "suggested_offers": [
    {
      "typename": "📋 FACTURE | فاتورة",
      "plan_code": "FACTURE_DJEZZY",
      "amount": 500
    }
  ]
}
```

### Tableau des Offres Suggérées

Le tableau `suggested_offers` affiche les plans corrects pour le numéro de téléphone. Mettez à jour l'interface de votre application pour représenter ces nouveaux plans au lieu d'afficher tous les plans.

<Note>
  `suggested_offers` est un tableau et peut contenir plusieurs éléments, surtout lorsque vous envoyez des requêtes GETMENU.
</Note>

## Tests en Sandbox

Activez le sandbox sur votre [page de paramètres](https://app.oneclickdz.com/#/settings) pour tester les requêtes sans affecter votre solde.

Toutes les opérations simuleront une opération réelle :

<Steps>
  <Step title="Flux Normal">
    **N'importe quel numéro de téléphone normal :**

    * 5 premières secondes : statut `PENDING`
    * 15 premières secondes : statut `HANDLING`
    * Ensuite : statut `FULFILLED`

    Après avoir intégré le flux fulfilled, passez à tester les autres statuts.
  </Step>

  <Step title="Tester le Remboursement avec Message">
    **Téléphone : 0600000001**

    Tester le statut REFUNDED avec `refund_message` à afficher au client
  </Step>

  <Step title="Tester le Remboursement avec Suggestions">
    **Téléphone : 0600000002**

    Tester le statut REFUNDED avec `refund_message` et `suggested_offers`
  </Step>

  <Step title="Tester l'Erreur Inconnue">
    **Téléphone : 0600000003**

    Tester le statut `UNKNOWN_ERROR` pour s'assurer que votre logique de tâche quotidienne fonctionne correctement
  </Step>
</Steps>

## Endpoints Associés

<CardGroup cols={2}>
  <Card title="Vérifier par Référence" icon="tag" href="/fr/api-reference/mobile/check-by-ref">
    Suivre avec votre référence personnalisée
  </Card>

  <Card title="Envoyer une Recharge" icon="paper-plane" href="/fr/api-reference/mobile/send-topup">
    Créer une nouvelle recharge
  </Card>
</CardGroup>
