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

# التحقق من الحالة بالمعرّف

> التحقق من حالة شحن الهاتف المحمول باستخدام المعرّف الداخلي

<div dir="rtl">
  ## نظرة عامة

  يُرجع حالة شحن هاتف محمول باستخدام `topupId` المُرجع في استجابة [إرسال الشحن](/ar/api-reference/mobile/send-topup).

  <Note>
    إذا حددت `ref` عند إرسال الشحن، يُفضَّل استخدام [check-by-ref](/ar/api-reference/mobile/check-by-ref) إذ يعتمد على معرّفاتك الخاصة.
  </Note>

  ## المعلمات

  <ParamField path="topupId" type="string" required>
    المعرّف الداخلي للشحن المُرجع في استجابة [send-topup](/ar/api-reference/mobile/send-topup)
  </ParamField>

  ## الاستجابة

  <ResponseField name="success" type="boolean" required>
    `true` إذا تم العثور على الشحن وإرجاع حالته
  </ResponseField>

  <ResponseField name="data" type="object" required>
    <Expandable title="properties">
      <ResponseField name="_id" type="string">
        معرّف الشحن الداخلي
      </ResponseField>

      <ResponseField name="ref" type="string">
        مرجعك الداخلي (إذا أُوفِّر عند الإرسال)
      </ResponseField>

      <ResponseField name="status" type="string">
        الحالة الحالية للشحن:

        * `PENDING`: قيد المعالجة
        * `SUCCESS`: اكتمل الشحن
        * `FAILED`: فشل بشكل نهائي (الرصيد مُسترد)
        * `UNKNOWN_ERROR`: حالة غير محددة - انتظر، لا تسترد الأموال
      </ResponseField>

      <ResponseField name="MSSIDN" type="string">
        رقم الهاتف الذي تم شحنه
      </ResponseField>

      <ResponseField name="operator" type="string">
        مشغّل الشبكة: `Mobilis`، `Djezzy`، أو `Ooredoo`
      </ResponseField>

      <ResponseField name="planCode" type="string">
        رمز الخطة المستخدمة
      </ResponseField>

      <ResponseField name="planName" type="string">
        الاسم المقروء للخطة
      </ResponseField>

      <ResponseField name="amount" type="number">
        مبلغ الشحن بالدينار الجزائري
      </ResponseField>

      <ResponseField name="cost" type="number">
        التكلفة الفعلية من رصيدك
      </ResponseField>

      <ResponseField name="createdAt" type="string">
        طابع زمني للإنشاء (ISO 8601)
      </ResponseField>

      <ResponseField name="updatedAt" type="string">
        طابع زمني لآخر تحديث للحالة (ISO 8601)
      </ResponseField>
    </Expandable>
  </ResponseField>

  ## قيم الحالة

  | الحالة          | المعنى             | الإجراء                              |
  | --------------- | ------------------ | ------------------------------------ |
  | `PENDING`       | الشحن قيد المعالجة | تحقق مجددًا بعد 5-10 ثوانٍ           |
  | `SUCCESS`       | الشحن اكتمل بنجاح  | أبلغ المستخدم بالنجاح                |
  | `FAILED`        | الشحن فشل نهائيًا  | الرصيد مُسترد - يمكن المحاولة مجددًا |
  | `UNKNOWN_ERROR` | حالة غير محددة     | **انتظر 24 ساعة** - لا تسترد الأموال |

  <Warning>
    إذا رأيت `UNKNOWN_ERROR`، **لا تسترد الأموال فورًا** ولا ترسل الشحن مجددًا. تحقق من الحالة مرة أخرى بعد 24 ساعة. غالبًا ما تُكمل العملية في النهاية.
  </Warning>

  ## الأمثلة

  <CodeGroup>
    ```bash cURL theme={null}
    curl https://api.oneclickdz.com/v3/mobile/topup/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/topup/${topupId}`,
      {
        headers: { "X-Access-Token": "YOUR_API_KEY" },
      }
    );
    const data = await response.json();
    console.log("Status:", data.data.status);
    ```

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

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

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

  ### مثال على الاستجابة — تم التنفيذ (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"
  }
  ```

  ### مثال على الاستجابة — مُسترَد (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"
  }
  ```

  ### مُسترَد مع عروض مقترحة

  عندما لا يتطابق كود الخطة مع نوع رقم الهاتف، ستتلقى بدائل مقترحة:

  ```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>
    **ملاحظة:** `suggested_offers` مصفوفة ويمكن أن تحتوي على عدة عناصر، مثلاً عند إرسال خطط GETMENU.
  </Note>

  ## استراتيجية الاستعلام

  ### مزامنة API مع قاعدة بياناتك

  <Steps>
    <Step title="ابدأ الاستعلام">
      ابدأ التحقق من الحالة فور إرسال الشحن. يمكنك ضبط interval كل 5-10 ثوانٍ على واجهتك للتحقق من الحالة. عند الوصول إلى FULFILLED أو REFUNDED أو UNKNOWN\_ERROR، أوقف الاستعلام.
    </Step>

    <Step title="فترة الاستعلام">
      تحقق كل 5-10 ثوانٍ طالما الحالة PENDING أو HANDLING.

      ```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="معالجة النتيجة">
      حدّث الطلب بناءً على الحالة النهائية

      ```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>

  ## مثال الاستعلام الكامل

  <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();

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

        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):
        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']

            if data['status'] in ['FULFILLED', 'REFUNDED', 'UNKNOWN_ERROR']:
                return data

            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>

  ## معالجة UNKNOWN\_ERROR

  ```javascript theme={null}
  async function handleTopUpStatus(topupId) {
    const response = await fetch(
      `https://api.oneclickdz.com/v3/mobile/topup/${topupId}`,
      {
        headers: { "X-Access-Token": API_KEY },
      }
    );

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

    switch (status) {
      case "SUCCESS":
        await markOrderComplete(topupId);
        await notifyUser("success");
        break;

      case "FAILED":
        await markOrderFailed(topupId);
        await notifyUser("failed");
        // Balance is refunded - safe to retry
        break;

      case "UNKNOWN_ERROR":
        // DO NOT refund or retry immediately
        // Schedule a re-check after 24 hours
        await scheduleStatusRecheck(topupId, 24 * 60 * 60 * 1000);
        await notifyUser("pending_review");
        break;

      case "PENDING":
        // Still processing - check again later
        break;
    }
  }
  ```

  ## الاستطلاع مع Backoff الأسي

  ```javascript theme={null}
  async function pollTopUpByIdWithBackoff(topupId) {
    const delays = [5000, 10000, 20000, 30000, 60000]; // ms

    for (const delay of delays) {
      const response = await fetch(
        `https://api.oneclickdz.com/v3/mobile/topup/${topupId}`,
        {
          headers: { "X-Access-Token": API_KEY },
        }
      );

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

      if (status === "SUCCESS" || status === "FAILED" || status === "UNKNOWN_ERROR") {
        return data.data;
      }

      // Still PENDING - wait before next check
      await new Promise((resolve) => setTimeout(resolve, delay));
    }

    // After all retries, schedule a long-term check
    return { status: "TIMEOUT", requiresManualReview: true };
  }
  ```

  ## أفضل الممارسات

  <CardGroup cols={2}>
    <Card title="تخزين الحالة مؤقتاً" icon="database">
      خزّن الحالة في قاعدة البيانات لتقليل استدعاءات API. استعلم من API الخاصة بنا فقط عندما لا تكون الحالة نهائية (PENDING/HANDLING).
    </Card>

    <Card title="أوقف عند الحالة النهائية" icon="hand">
      لا تستمر بالاستعلام بعد FULFILLED أو REFUNDED أو UNKNOWN\_ERROR
    </Card>

    <Card title="حماية من المهلة الزمنية" icon="timer">
      حدد حداً أقصى لمحاولات الاستعلام (عادة 60 = 5 دقائق)
    </Card>

    <Card title="معالجة الأخطاء" icon="shield">
      تعامل مع أخطاء الشبكة بمنطق إعادة المحاولة
    </Card>

    <Card title="أظهر الرسائل بالعربية" icon="language">
      أظهر دائماً `refund_message` للمستخدمين — وهي بالعربية وتشرح المشكلة
    </Card>

    <Card title="عرض بدائل" icon="arrows-split">
      عند وجود `suggested_offers`، حدّث واجهتك لإظهار هذه الخطط بدلاً من جميع الخطط
    </Card>
  </CardGroup>

  ## معالجة الاسترداد

  سيُضاف `refund_message` (رسالة باللغة العربية) إلى عملية الشحن، ويجب عرضه كما هو للعميل.

  **أمثلة على السيناريوهات:**

  ### رقم هاتف خاطئ

  إذا أدخل العميل رقم هاتف خاطئ:

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

  ### عدم تطابق الخطة

  عند إرسال PREPAID\_DJEZZY إلى رقم فاتورة (postpaid):

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

  ### مصفوفة suggested\_offers

  تُظهر المصفوفة `suggested_offers` الخطط الصحيحة لرقم الهاتف. حدّث واجهة تطبيقك لعرض هذه الخطط الجديدة بدلاً من عرض جميع الخطط.

  <Note>
    مصفوفة `suggested_offers` يمكن أن تحتوي على عدة عناصر، خاصة عند إرسال طلبات GETMENU.
  </Note>

  ## الاختبار في بيئة Sandbox

  فعّل وضع Sandbox من [صفحة الإعدادات](https://app.oneclickdz.com/#/settings) لاختبار الطلبات دون التأثير على رصيدك.

  جميع العمليات ستحاكي عملية حقيقية:

  <Steps>
    <Step title="السير الطبيعي">
      **أي رقم هاتف عادي:**

      * أول 5 ثوانٍ: الحالة `PENDING`
      * أول 15 ثانية: الحالة `HANDLING`
      * ثم: الحالة `FULFILLED`

      بعد دمج سير FULFILLED، انتقل لاختبار الحالات الأخرى.
    </Step>

    <Step title="اختبار الاسترداد مع رسالة">
      **الهاتف: 0600000001**

      اختبار حالة REFUNDED مع `refund_message` يجب عرضه للعميل
    </Step>

    <Step title="اختبار الاسترداد مع اقتراحات">
      **الهاتف: 0600000002**

      اختبار حالة REFUNDED مع `refund_message` و`suggested_offers`
    </Step>

    <Step title="اختبار الخطأ غير المعروف">
      **الهاتف: 0600000003**

      اختبار حالة `UNKNOWN_ERROR` للتأكد من صحة منطق cron job اليومي
    </Step>
  </Steps>

  ## Endpoints ذات الصلة

  <CardGroup cols={2}>
    <Card title="التحقق من الحالة بالمرجع" icon="magnifying-glass" href="/ar/api-reference/mobile/check-by-ref">
      البحث باستخدام مرجعك الداخلي
    </Card>

    <Card title="إرسال الشحن" icon="paper-plane" href="/ar/api-reference/mobile/send-topup">
      إرسال شحن هاتف محمول
    </Card>
  </CardGroup>
</div>
