الشحن المباشر

يحدّد منتج الشحن المباشر خدمة المستلم. أما الشحن المباشر فهو شراؤك لذلك المنتج. تدفع بالدينار الليبي من محفظتك، ثم يتولى موظفو أنيس تسليم المبلغ للمستلم. قد يستغرق التنفيذ وقتًا. هذا ليس استدعاءً لنقل المال بين محافظ أنيس.

اختيار المنتج

وقّع GET /v1/wallets/{walletId}/direct-recharge-products?type=external. تبقى billing ضمن القيم المسموحة، لكن منتجات الفواتير غير متاحة بعد؛ ولا يمكن طلب عرض سعر لها أو شراؤها. تحتاج القراءات إلى direct_recharges:read، وهي مشمولة في direct_recharges:create. ردود المنتجات والمواقع والسعر غير موقّعة. أعد nextCursor كما هي في cursor؛ وتحمل الصفحة الأخيرة nextCursor: null.

{
  "items": [
    {
      "id": "e7c2a9d4-1b3f-4a85-9c60-8d2e4f7b1a93",
      "title": "فودافون كاش مصر",
      "description": "تحويل إلى محافظ فودافون كاش",
      "type": "external",
      "category": {
        "id": "4b9e...",
        "name": "مصر",
        "icon": "https://cdn.anis.ly/circle/eg.png"
      },
      "payoutCurrency": {
        "name": "جنيه مصري"
      },
      "available": true,
      "limits": {
        "minimumTotal": {
          "amount": "10.000",
          "currency": "LYD"
        },
        "maximumTotal": {
          "amount": "5000.000",
          "currency": "LYD"
        },
        "minimumPayout": "50.000",
        "maximumPayout": "30000.000"
      }
    }
  ],
  "nextCursor": null
}

تعتمد الأسعار والحدود على شريحة محفظتك. تشمل available: false غياب سعر لهذه المحفظة أو اقتصاره على فزّاعة؛ ولا يستطيع الشركاء الشراء عبر فزّاعة. قد تغيب الحدود عند غياب شريحة مطابقة. عملة التسليم اسم للعرض، وليست رمز ISO: لا تحوّلها إلى Money ولا تخترع رمزًا لها.

جمع الحقول والمواقع

اقرأ GET /v1/wallets/{walletId}/direct-recharge-products/{productId} لعرض المنتج وحقوله المرتبة.

{
  "id": "e7c2a9d4-1b3f-4a85-9c60-8d2e4f7b1a93",
  "title": "فودافون كاش مصر",
  "type": "external",
  "payoutCurrency": {
    "name": "جنيه مصري"
  },
  "available": true,
  "limits": {
    "minimumTotal": {
      "amount": "10.000",
      "currency": "LYD"
    },
    "maximumTotal": {
      "amount": "5000.000",
      "currency": "LYD"
    },
    "minimumPayout": "50.000",
    "maximumPayout": "30000.000"
  },
  "recipientFields": [
    {
      "key": "recipient_number",
      "name": {
        "ar": "رقم المحفظة",
        "en": "Wallet number"
      },
      "kind": "text",
      "for": "recipient",
      "order": 1,
      "validation": "^01[0-9]{9}$",
      "validationMessage": {
        "ar": "رقم غير صحيح",
        "en": "Invalid number"
      }
    },
    {
      "key": "recipient_name",
      "name": {
        "ar": "اسم المستلم",
        "en": "Recipient name"
      },
      "kind": "text",
      "for": "recipient",
      "order": 2
    },
    {
      "key": "recipient_location",
      "name": {
        "ar": "المدينة",
        "en": "City"
      },
      "kind": "list",
      "for": "recipient",
      "order": 3
    }
  ]
}
الحقل ما تفعله
key احتفظ به في كل عنصر ترسله من مفتاح وقيمة
name اعرض التسمية العربية أو الإنجليزية
kind تعني text نصًا، وlist خيارات، وnote إرشادًا للعرض
for يكون recipient أو sender كما ورد
order اعرض الحقول بهذا الترتيب
validation وvalidationMessage طبّق القاعدة ورسالتها باللغة المناسبة إن وردتا

لخيارات المواقع استخدم GET /v1/wallets/{walletId}/direct-recharge-products/{productId}/locations?search=القاهرة. البحث اختياري؛ وتابع الصفحات بواسطة cursor.

{
  "items": [
    {
      "id": "1a2b...",
      "name": "القاهرة"
    },
    {
      "id": "3c4d...",
      "name": "الإسكندرية"
    }
  ],
  "nextCursor": null
}

أرسل معرّف الموقع المختار في قيمة الحقل، وليس الاسم المعروض.

حساب الطرفين

يتحقق GET /v1/wallets/{walletId}/direct-recharge-products/{productId}/quote?amount=1500.000&amountIn=fx من السعر دون حجز المال. تعني fx الإدخال بعملة التسليم، وتعني lyd الإدخال بالدينار. يعرض الردّ الطرفين دائمًا.

{
  "productId": "e7c2a9d4-1b3f-4a85-9c60-8d2e4f7b1a93",
  "payout": {
    "amount": "1500.000",
    "currency": {
      "name": "جنيه مصري"
    }
  },
  "exchangeRate": "0.155000",
  "price": {
    "amount": "232.500",
    "currency": "LYD"
  },
  "commission": {
    "amount": "5.000",
    "currency": "LYD"
  },
  "total": {
    "amount": "237.500",
    "currency": "LYD"
  }
}

تمثّل total تكلفة المحفظة. تشمل price تعديلات الموقع وشريحة المحفظة المطبّقة، وتظهر العمولة منفصلة. المبالغ نصوص عشرية، وليست أعداد JSON عائمة. سعر الصرف أيضًا نص عشري، يعرضه المثال بست خانات بعد الفاصلة.

طلب الشحن المباشر

احفظ قيمة UUID والمحتوى كما سترسله قبل POST /v1/wallets/{walletId}/direct-recharges. تحتاج إلى direct_recharges:create. وقّع بصمة المحتوى ورقم استخدام واحد جديد وIdempotency-Key؛ فهو معرّف العملية. أرسل payoutAmount وexpectedTotal من عرض السعر. لا ترسل price أو priceWithCommission أو extraFee.

{
  "productId": "e7c2a9d4-1b3f-4a85-9c60-8d2e4f7b1a93",
  "payoutAmount": "1500.000",
  "expectedTotal": {
    "amount": "237.500",
    "currency": "LYD"
  },
  "recipient": [
    {
      "key": "recipient_number",
      "value": "01012345678"
    },
    {
      "key": "recipient_name",
      "value": "Ahmed Ali"
    },
    {
      "key": "recipient_location",
      "value": "1a2b..."
    }
  ],
  "note": "Family support",
  "externalReference": "POS-77-TR-551"
}

تحقّق من توقيع 202. قد تعيد العملية المقبولة Location: /v1/direct-recharges/{operationId} مع:

{
  "operationId": "7d4e1a90-5b2c-4e7f-8a13-0c9d6e2b4f81",
  "operationType": "directRecharge",
  "status": "processing"
}

فحص الرصيد دقيق حتى 999,999.999 LYD. ينطبق هذا الحدّ على رصيد المحفظة بالدينار الليبي، وليس على عملة التسليم المسماة.

تفحص أنيس الرصيد قبل تسجيل الشراء؛ ويعيد نقصه insufficient_balance و409. وقد يسبب خصم متزامن لاحقًا الحالة paymentFailed.

غياب القراءة لا يثبت عدم الإنشاء. أعد المعرّف والمحتوى المحفوظين نفسيهما. يسمح 202 processing بعد التكرار بتأخر القراءة؛ وإذا ظلّت قراءة التطبيق مفقودة بعد ستين ثانية منذ قبول العملية، تُغلق إعادة المحاولة بـidempotency_conflict و409. ولا يثبت هذا الرمز أن شيئًا لم يُخصم: فقد يكون الطلب الأول قُبل بالفعل، فاقرأ العملية قبل أي محاولة جديدة.

يستمر الدفع والتسليم بعد ذلك. إذا ضاع الردّ أو بقيت النتيجة مجهولة، فاقرأ العملية نفسها أو أعد المحتوى المحفوظ نفسه بالمفتاح نفسه وتوقيع جديد. لا تستخدم معرّفًا جديدًا لتجاوز النتيجة المجهولة.

متابعة الحالة وإعادة المال

وقّع GET /v1/direct-recharges/{operationId} بصلاحية القراءة أو الإنشاء وتحقّق من توقيع الردّ. يظهر الشحن المباشر لتطبيق آخر كما لو كان غير موجود.

{
  "commission": {
    "amount": "5.000",
    "currency": "LYD"
  },
  "completedAt": "2026-10-20T10:00:00Z",
  "exchangeRate": "0.155000",
  "externalReference": "POS-77-TR-551",
  "note": "Family support",
  "number": 30412,
  "operationId": "7d4e1a90-5b2c-4e7f-8a13-0c9d6e2b4f81",
  "payout": {
    "amount": "1500.000",
    "currency": {
      "name": "جنيه مصري"
    }
  },
  "price": {
    "amount": "232.500",
    "currency": "LYD"
  },
  "product": {
    "id": "e7c2a9d4-1b3f-4a85-9c60-8d2e4f7b1a93",
    "title": "فودافون كاش مصر"
  },
  "proofUrl": "https://cdn.anis.ly/circle/proofs/example.png",
  "recipient": [
    {
      "key": "recipient_name",
      "name": {
        "ar": "اسم المستلم",
        "en": "Recipient name"
      },
      "value": "Ahmed Ali"
    },
    {
      "key": "recipient_number",
      "name": {
        "ar": "رقم المحفظة",
        "en": "Wallet number"
      },
      "value": "01012345678"
    }
  ],
  "requestedAt": "2026-10-20T09:00:00Z",
  "status": "completed",
  "total": {
    "amount": "237.500",
    "currency": "LYD"
  },
  "walletId": "71825266-7182-4182-8182-718252667182"
}
status المعنى
awaitingPayment سُجّل الشحن المباشر ويجري الخصم
paymentFailed رُفض الدفع ولم يُخصم شيء (نهائية)
pending دُفعت؛ بانتظار الموظف
inProgress يتولى موظف تنفيذها
suspendedDataError تحتاج بيانات المستلم إلى تصحيح؛ يوضح reason السبب
completed سُلّمت (نهائية)
rejectedAwaitingRefund رُفضت والمال قيد الإعادة
rejectedWithRefund رُفضت وعاد المال (نهائية)

يظهر reason عند التعليق وفي حالتي الرفض. ويشير proofUrl، إن ورد، إلى إثبات التسليم المحفوظ. تحتفظ إعادة المال الجزئية المسوّاة بالحالة المدمجة نفسها التي يعرضها التطبيق وتضيف هذا الجزء، دون إنشاء حالة جديدة.

{
  "partialRefund": {
    "refundedPayout": "500.000",
    "refundedTotal": {
      "amount": "77.500",
      "currency": "LYD"
    },
    "remainingPayout": "1000.000",
    "remainingTotal": {
      "amount": "160.000",
      "currency": "LYD"
    }
  }
}

لا تحدث الإعادة الجزئية إلا إذا فُعّلت للمنتج؛ فجميع عمليات الشحن المباشر الجديدة الحالية، بما فيها ما أنشأته التطبيقات، غير مؤهلة لها. تمثّل refundedTotal المال الذي عاد فعلًا، وتمثّل remainingTotal تكلفة المحفظة المتبقية. مبالغ التسليم نصوص بعملة العرض. لا تعتبر إعادة المال مكتملة لمجرد طلبها. يأتي إشعار الرفض بعد تسوية الإعادة الكاملة، وإشعار الإعادة الجزئية بعد تسويتها.

قد تعيد القراءة أيضًا رفضًا سُجّل لعملية الإنشاء بهذا المعرّف، مع Idempotency-Replayed: true: الحالة 409 (insufficient_balance أو price_changed أو direct_recharge_product_unavailable) أو الحالة 422 (amount_out_of_range أو بيانات مستلم رُفضت). لم يُخصم شيء. وعند القراءة يعني idempotency_conflict (409) أنه لا يوجد شحن مباشر لتطبيقك بهذا المعرّف؛ فلا تستخدم Idempotency-Key جديدًا إلا لعملية جديدة فعلًا.

البحث عن عملياتي

وقّع GET /v1/wallets/{walletId}/direct-recharges بصلاحية direct_recharges:read أو direct_recharges:create. الردّ غير موقّع. تظهر عمليات تطبيقك وحده في المحفظة المصرّح بها، من الأحدث إلى الأقدم. استخدم معًا ما تحتاجه من المرشّحات الاختيارية status وproductId (معرّف UUID) وfrom وto (بتوقيت UTC) وcursor وpageSize. قيم status هي awaitingPayment وpaymentFailed وpending وinProgress وsuspendedDataError وcompleted وrejectedAwaitingRefund وrejectedWithRefund. لا تتجاوز الفترة 31 يومًا؛ وعند ترك التاريخين تُعرض آخر 31 يومًا. حجم الصفحة الافتراضي 50 وحدّه الأقصى 100. مرّر nextCursor كما هو مع المرشّحات وحجم الصفحة نفسيهما حتى يصبح null. النتيجة الفارغة تحمل items: [] وnextCursor: null. عند validation_failed (422) صحّح المرشّحات أو المؤشّر؛ ولا تكرر عملية الشراء لاستعادة القائمة. الحالات مطابقة لقراءة التفاصيل، وتبيّن hasPartialRefund وجود إعادة جزئية دون حالة جديدة. بيانات المستلم والإثبات في قراءة التفاصيل الموقّعة.

{
  "items": [
    {
      "operationId": "7d4e1a90-5b2c-4e7f-8a13-0c9d6e2b4f81",
      "status": "completed",
      "number": 30412,
      "product": {
        "id": "e7c2a9d4-1b3f-4a85-9c60-8d2e4f7b1a93",
        "title": "فودافون كاش مصر"
      },
      "payout": {
        "amount": "1500.000",
        "currency": {
          "name": "جنيه مصري"
        }
      },
      "total": {
        "amount": "237.500",
        "currency": "LYD"
      },
      "externalReference": "POS-77-TR-551",
      "requestedAt": "2026-10-20T09:00:00Z",
      "completedAt": "2026-10-20T09:14:00Z",
      "hasPartialRefund": false
    }
  ],
  "nextCursor": null
}

تصحيح مستلم معلّق

فقط حين تكون status بالقيمة suspendedDataError، أرسل PATCH /v1/direct-recharges/{operationId}/recipient. تحتاج إلى صلاحية الإنشاء وبصمة موقّعة ورقم استخدام واحد جديد. هذا تغيير موقّع، وليس شراءً جديدًا؛ ولا ترسل معه Idempotency-Key. أرسل قائمة المستلم المصحّحة كاملة. تحلّ محل القائمة المحفوظة. أدرج كل حقل مطلوب وأي قيم اختيارية تريد الاحتفاظ بها، بما فيها recipient_location.

{
  "recipient": [
    {
      "key": "recipient_number",
      "value": "01098765432"
    },
    {
      "key": "recipient_name",
      "value": "Ahmed Ali Hassan"
    },
    {
      "key": "recipient_location",
      "value": "1a2b..."
    }
  ]
}

تحقّق من توقيع الردّ. يعيد التصحيح الناجح 202 Accepted موقّعًا مع Location: /v1/direct-recharges/{operationId} وإقرار المعالجة القياسي: operationId وoperationType: "directRecharge" وstatus: "processing" فقط. لا يحمل بيانات الشحن المباشر أو طلب الشراء. اتبع Location لقراءة الشحن المباشر أو انتظر إشعارًا من نوع directRecharge.*. لا يعني القبول أن البيانات المصحّحة ظهرت في القراءة بعد. اقرأ قبل الإرسال مجددًا: قد يُرفض تصحيح طُبّق بالفعل بالرمز direct_recharge_not_suspended. ترويسات الردّ وإقرار المعالجة:

HTTP/1.1 202 Accepted
Location: /v1/direct-recharges/7d4e1a90-5b2c-4e7f-8a13-0c9d6e2b4f81
{
  "operationId": "7d4e1a90-5b2c-4e7f-8a13-0c9d6e2b4f81",
  "operationType": "directRecharge",
  "status": "processing"
}

تحقّق من الحقول المرسلة وفق قواعد المنتج. قد يحمل وصف مشكلة التحقق من المستلم الجزء التالي:

{"extensions":{"fields":{"recipient_number":"Invalid number"}}}

يرافق direct_recharge_not_suspended الردّ 409، ويرافق validation_failed الردّ 422، أما المعرّف غير الموجود أو التابع لتطبيق آخر فيعيد resource_not_found مع 404.

الرفض قبل الشراء

الرمز HTTP المعنى
price_changed 409 تغيّر السعر؛ اطلب عرضًا وأكّده مجددًا
direct_recharge_product_unavailable 409 هذه المحفظة لا تستطيع شراءه
amount_out_of_range 422 مبلغ التسليم أو الإجمالي خارج الحدود
validation_failed 422 صحّح الحقول المذكورة في extensions.fields
purchase_not_allowed 403 لا يمكنك شراء منتج الشحن المباشر الخاص بك
insufficient_balance 409 الرصيد غير كافٍ
spending_limit_exceeded 429 بُلغ سقف التطبيق المشترك
idempotency_conflict 409 تغيّر المحتوى المحفوظ، أو تكرار إنشاء ما زال معرّفه مجهولًا بعد مهلة ستين ثانية؛ وقد يكون الطلب الأول قُبل وخُصم مبلغه، فاقرأه أولًا

تتشارك المشتريات سقف الإنفاق اليومي وحدّ استدعاءات الشراء مع طلبات البطاقات وشحن العدادات. بعد رفض مؤكد يغلق العملية، يحتاج الشراء المصحّح إلى معرّف جديد. رفض الوصول أو التوقيع أو تعذّر الاتصال لا يثبت فشل محاولة سابقة.

لا يعني idempotency_conflict ولا replay_detected أن الطلب الأول فشل: فقد يكون قُبل وخُصم مبلغه، فلا تفترض أبدًا أنه لم يُخصم شيء. اقرأ العملية الأصلية عبر GET /v1/direct-recharges/{operationId} قبل أي محاولة جديدة. لتكرار القصد نفسه بعد replay_detected احتفظ بـ Idempotency-Key وأرسل nonce وتوقيعًا جديدين. وبعد idempotency_conflict أعد استخدام المفتاح للقصد المطابق فقط، واستخدم Idempotency-Key جديدًا لعملية جديدة فعلًا. أما insufficient_balance وprice_changed ورفض المنتج غير المتاح فتقع قبل تحرّك المال: لم يُخصم شيء.

استقبل النتائج عبر الإشعارات وتحقّق من كل إشعار قبل معالجته.

المنتجات · المنتج · المواقع · السعر · الإنشاء · القراءة · المستلم · البحث عن عملياتي