> ## 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">
  ## نظرة عامة

  إرسال شحن هاتف محمول فوري إلى جميع شبكات الهاتف الجزائرية (Mobilis وDjezzy وOoredoo وPixx) مع تتبع الحالة في الوقت الفعلي.

  <Warning>
    لا تعيد محاولة الطلبات الفاشلة دون التحقق من الحالة أولًا باستخدام
    [check-by-ref](/ar/api-reference/mobile/check-by-ref) أو
    [check-by-id](/ar/api-reference/mobile/check-by-id). يمكن أن تتسبب طلبات
    الإرسال المكررة في خصم مزدوج.
  </Warning>

  ## متن الطلب

  <ParamField body="plan_code" type="string" required>
    رمز الخطة من [list-plans](/ar/api-reference/mobile/list-plans) (مثلًا: `PREPAID_DJEZZY`، `MIX1000_OOREDOO`)
  </ParamField>

  <ParamField body="MSSIDN" type="string" required>
    رقم الهاتف المحمول الجزائري بصيغة `0XXXXXXXXX` (10 أرقام تبدأ بـ 05، 06، أو 07)
  </ParamField>

  <ParamField body="amount" type="number">
    مبلغ الشحن بالدينار الجزائري. **مطلوب فقط للخطط الديناميكية** (حيث `min_amount` و`max_amount` محددان). غير مطلوب للخطط الثابتة.
  </ParamField>

  <ParamField body="ref" type="string">
    مرجع داخلي فريد (معرّف طلبك). يُستخدم لمنع الطلبات المكررة والتحقق من الحالة لاحقًا.
  </ParamField>

  <Note>
    استخدم `ref` دائمًا إذا كنت ترسل طلبات من جانب الخادم. يمنع الخصم المزدوج إذا فشل طلب الشبكة.
  </Note>

  ## الاستجابة

  <ResponseField name="success" type="boolean" required>
    يشير إلى نجاح الطلب
  </ResponseField>

  <ResponseField name="data" type="object" required>
    <Expandable title="properties">
      <ResponseField name="topupId" type="string">
        معرّف الشحن الداخلي. استخدمه مع [check-by-id](/ar/api-reference/mobile/check-by-id) للتتبع.
      </ResponseField>

      <ResponseField name="topupRef" type="string">
        مرجعك الداخلي (من حقل `ref` في الطلب). يُرجع `null` إذا لم تُحدد `ref`.
      </ResponseField>

      <ResponseField name="newBalance" type="number">
        رصيد حسابك بعد إرسال الشحن
      </ResponseField>
    </Expandable>
  </ResponseField>

  <ResponseField name="meta" type="object" required>
    <Expandable title="properties">
      <ResponseField name="timestamp" type="string">
        طابع زمني للاستجابة بتنسيق ISO 8601
      </ResponseField>
    </Expandable>
  </ResponseField>

  ## الأمثلة

  ### خطة ديناميكية (يحدد المبلغ)

  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST https://api.oneclickdz.com/v3/mobile/send \
      -H "X-Access-Token: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "plan_code": "PREPAID_DJEZZY",
        "MSSIDN": "0776543210",
        "amount": 500,
        "ref": "order-001"
      }'
    ```

    ```javascript Node.js theme={null}
    const response = await fetch("https://api.oneclickdz.com/v3/mobile/send", {
      method: "POST",
      headers: {
        "X-Access-Token": "YOUR_API_KEY",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        plan_code: "PREPAID_DJEZZY",
        MSSIDN: "0776543210",
        amount: 500,
        ref: "order-001",
      }),
    });
    const data = await response.json();
    ```

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

    response = requests.post(
        'https://api.oneclickdz.com/v3/mobile/send',
        headers={'X-Access-Token': 'YOUR_API_KEY', 'Content-Type': 'application/json'},
        json={
            'plan_code': 'PREPAID_DJEZZY',
            'MSSIDN': '0776543210',
            'amount': 500,
            'ref': 'order-001'
        }
    )
    data = response.json()
    ```
  </CodeGroup>

  ### خطة ثابتة (لا مبلغ)

  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST https://api.oneclickdz.com/v3/mobile/send \
      -H "X-Access-Token: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "plan_code": "MIX1000_OOREDOO",
        "MSSIDN": "0554926570",
        "ref": "order-002"
      }'
    ```

    ```javascript Node.js theme={null}
    const response = await fetch("https://api.oneclickdz.com/v3/mobile/send", {
      method: "POST",
      headers: {
        "X-Access-Token": "YOUR_API_KEY",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        plan_code: "MIX1000_OOREDOO",
        MSSIDN: "0554926570",
        ref: "order-002",
      }),
    });
    const data = await response.json();
    ```

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

    response = requests.post(
        'https://api.oneclickdz.com/v3/mobile/send',
        headers={'X-Access-Token': 'YOUR_API_KEY'},
        json={
            'plan_code': 'MIX1000_OOREDOO',
            'MSSIDN': '0554926570',
            'ref': 'order-002'
        }
    )
    data = response.json()
    ```
  </CodeGroup>

  ### مثال على الاستجابة

  ```json theme={null}
  {
    "success": true,
    "data": {
      "topupId": "6901616fe9e88196b4eb64b0",
      "topupRef": "order-001",
      "newBalance": 826.13
    },
    "meta": {
      "timestamp": "2025-10-29T00:35:59.454Z"
    }
  }
  ```

  ## استجابات الخطأ

  <AccordionGroup>
    <Accordion title="400 - خطأ في التحقق">
      ```json theme={null}
      {
        "success": false,
        "error": {
          "code": "ERR_VALIDATION",
          "message": "Validation error",
          "details": {
            "amount": "Amount must be between 100 and 10000 for this plan"
          }
        }
      }
      ```
    </Accordion>

    <Accordion title="400 - رقم هاتف غير صالح">
      ```json theme={null}
      {
        "success": false,
        "error": {
          "code": "ERR_PHONE",
          "message": "Invalid phone number"
        }
      }
      ```
    </Accordion>

    <Accordion title="403 - رصيد غير كافٍ">
      ```json theme={null}
      {
        "success": false,
        "error": {
          "code": "NO_BALANCE",
          "message": "Insufficient balance"
        }
      }
      ```
    </Accordion>

    <Accordion title="403 - مرجع مكرر">
      ```json theme={null}
      {
        "success": false,
        "error": {
          "code": "DUPLICATED_REF",
          "message": "This reference ID is already in use"
        }
      }
      ```

      <Note>
        إذا تلقيت هذا الخطأ، **لا ترسل طلبًا جديدًا**. بدلًا من ذلك، تحقق من حالة الطلب الموجود باستخدام `ref`.
      </Note>
    </Accordion>

    <Accordion title="500 - خطأ داخلي">
      ```json theme={null}
      {
        "success": false,
        "error": {
          "code": "INTERNAL_SERVER_ERROR",
          "message": "Developer was notified and will check shortly"
        }
      }
      ```

      <Warning>
        **لا تسترد الأموال فورًا.** ابقَ على تتبع الطلب لمدة 24 ساعة. نظام الشحن غالبًا ما يُكمل العملية رغم هذا الخطأ.
      </Warning>
    </Accordion>
  </AccordionGroup>

  ## منع الطلبات المكررة

  <Info>
    يضمن حقل `ref` معالجة الشحن مرة واحدة فقط حتى في حالات مشكلات الشبكة.
  </Info>

  ```javascript theme={null}
  async function sendTopUpSafe(planCode, phone, amount, orderId) {
    // Use orderId as the ref for idempotency
    const ref = `topup-${orderId}`;

    try {
      const response = await fetch("https://api.oneclickdz.com/v3/mobile/send", {
        method: "POST",
        headers: {
          "X-Access-Token": API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          plan_code: planCode,
          MSSIDN: phone,
          amount,
          ref,
        }),
      });

      const data = await response.json();

      if (data.success) {
        return { success: true, topupId: data.data.topupId };
      }

      // Handle duplicate ref - order already exists
      if (data.error.code === "DUPLICATED_REF") {
        const status = await checkTopUpByRef(ref);
        return { success: true, topupId: status.data._id, fromExisting: true };
      }

      throw new Error(`${data.error.code}: ${data.error.message}`);
    } catch (networkError) {
      // Network error - check if order was created
      const status = await checkTopUpByRef(ref);
      if (status.success) {
        return { success: true, topupId: status.data._id, fromExisting: true };
      }
      throw networkError;
    }
  }
  ```

  ## نموذج تدفق كامل

  ```javascript theme={null}
  async function processTopUp(userId, planCode, phone, amount) {
    // 1. Validate inputs
    if (!/^0[567]\d{8}$/.test(phone)) {
      throw new Error('Invalid Algerian phone number format');
    }

    // 2. Check balance
    const balance = await getBalance();
    if (balance < amount * 1.05) { // Add buffer for safety
      throw new Error('Insufficient balance');
    }

    // 3. Generate unique ref
    const ref = `topup-${userId}-${Date.now()}`;

    // 4. Send topup
    const result = await sendTopUp({
      plan_code: planCode,
      MSSIDN: phone,
      amount,
      ref
    });

    // 5. Store result
    await saveTransaction({
      ref,
      topupId: result.topupId,
      status: 'PENDING',
      createdAt: new Date()
    });

    // 6. Return success
    return result;
  }
  ```

  ## تنسيق رقم الهاتف

  <Warning>
    يجب أن يكون رقم الهاتف **نصاً (string)**، وليس رقماً. الصفر البادئ مطلوب.
  </Warning>

  **تنسيقات صحيحة:**

  * ✅ `"0778037340"` (نص مع صفر بادئ)
  * ✅ `"0665983439"`
  * ✅ `"0556121212"`

  **تنسيقات غير صحيحة:**

  * ❌ `778037340` (الصفر البادئ مفقود)
  * ❌ `0778037340` (رقم بدل نص)
  * ❌ `"+213778037340"` (تنسيق دولي)
  * ❌ `"0778 037 340"` (يحتوي على مسافات)

  ## دورة حياة الحالة

  بعد إرسال الشحن، يمر بالحالات التالية:

  <Steps>
    <Step title="PENDING">
      تم إنشاء الطلب وإضافته للمعالجة **المدة:** 2-15 ثانية (عادةً)
    </Step>

    <Step title="HANDLING">
      جارٍ المعالجة من قِبَل المشغّل **المدة:** 3-8 ثوانٍ (عادةً)
    </Step>

    <Step title="الحالة النهائية">
      **FULFILLED:** اكتمل بنجاح ✅ **REFUNDED:** فشل واسترداد المبلغ ❌ **UNKNOWN\_ERROR:** حالة غير محددة (تُحسم خلال 24 ساعة) ⚠️
    </Step>
  </Steps>

  [تعرّف على تتبع الحالة →](/ar/api-reference/mobile/check-by-ref)

  ## اختبار Sandbox

  فعّل وضع Sandbox للاختبار دون معاملات حقيقية:

  <CardGroup cols={2}>
    <Card title="التدفق الطبيعي" icon="check">
      **استخدم أي رقم عادي** (مثل `0778037340`) التدفق: PENDING (5ث) → HANDLING (15ث) → FULFILLED
    </Card>

    <Card title="استرداد مع رسالة" icon="message">
      **استخدم:** `0600000001` النتيجة: REFUNDED مع `refund_message`
    </Card>

    <Card title="عدم تطابق الخطة" icon="list">
      **استخدم:** `0600000002` النتيجة: REFUNDED مع `suggested_offers`
    </Card>

    <Card title="خطأ غير معروف" icon="question">
      **استخدم:** `0600000003` النتيجة: حالة UNKNOWN\_ERROR
    </Card>
  </CardGroup>

  <Tip>
    راجع [دليل سير عمل شحن الهاتف](../../mobile-topup-guides/4-status-polling) للحصول على تعليمات اختبار Sandbox الكاملة.
  </Tip>

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

  <CardGroup cols={2}>
    <Card title="استخدم مراجع فريدة" icon="fingerprint">
      قدّم دائماً `ref` فريداً لمنع التكرار وتسهيل التتبع
    </Card>

    <Card title="تحقق قبل الإرسال" icon="circle-check">
      تحقق من الخطة وتنسيق الهاتف والرصيد من جهة العميل لتقليل الأخطاء
    </Card>

    <Card title="اعمل بشكل غير متزامن" icon="clock">
      نفّذ طلبات API بشكل غير متزامن لتجنب إيقاف طلبات المستخدمين
    </Card>

    <Card title="نفّذ منطق إعادة المحاولة" icon="arrows-rotate">
      أعد المحاولة عند فشل الطلبات مع تأخير أسي لأخطاء الشبكة
    </Card>
  </CardGroup>

  ## Endpoints ذات الصلة

  <CardGroup cols={3}>
    <Card title="قائمة الخطط" icon="list" href="/ar/api-reference/mobile/list-plans">
      استعرض الخطط المتاحة
    </Card>

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

    <Card title="التحقق من الحالة بالمعرّف" icon="fingerprint" href="/ar/api-reference/mobile/check-by-id">
      تتبع باستخدام topupId
    </Card>
  </CardGroup>
</div>
