للمطوّرين

كيف يصلنا الطلب — عقد استقبال طلبات المتاجر

لمن: مطوّر المتجر أو المنصة أو الإضافة التي ستُرسل لنا الطلبات. الحالة: POST /partner/v1/shipments متاح اليوم. العقد أدناه (/partner/v1/store-orders) هو الطريق الرسمي للمتاجر، ويُبنى في المرحلة الأولى من خطة التكامل. ما دام غير منشور، استعمل shipments.


1. بابان للدخول، اختر واحداً

متى تستعمله المسار
أ. الشكل الموحّد (مفضّل) تتحكّم بشكل الرسالة: إضافة ووكومرس، متجر مخصّص، منصة تبني تكاملاً معنا POST /partner/v1/store-orders
ب. الحمولة الأصلية لا تستطيع تغيير شكل الـwebhook (سلة، زِد، ووكومرس القياسي) POST /integrations/{platform}/{connection_id}

الباب (أ) يعطيك تحقّقاً فورياً ورسائل خطأ مفهومة. الباب (ب) نستقبل فيه حمولتك كما هي ونحوّلها عندنا. كلاهما ينتهي إلى نفس المسار الداخلي، فلا فرق في النتيجة.


2. المصادقة والترويسات

POST /partner/v1/store-orders HTTP/1.1
Host: api.rawafedsystems.example
X-API-Key: tw_live_ab12.9f3c…            ← {prefix}.{secret} من بوابة التاجر، يُعرض مرة واحدة
Idempotency-Key: wc-1042-ready-01          ← إلزامي عملياً، اقرأ القاعدة 6
Content-Type: application/json; charset=utf-8
  • الصلاحية المطلوبة: shipments:write. الطلب بمفتاح بلا هذه الصلاحية يُرفض 403 مع تسمية الصلاحية الناقصة.
  • حد المعدل: 60 طلباً/دقيقة لكل مفتاح.
  • الترميز: UTF-8 دائماً. الأسماء والعناوين عربية، ولا نقبل ترميزاً آخر.

للباب (ب): لا نرسل مفتاح API، بل نتحقق من توقيع المنصة نفسها بالسر المخزّن مع الاتصال (X-WC-Webhook-Signature لووكومرس، وتوقيع سلة/زِد لكل منهما). توقيع خاطئ = 401 ولا نحفظ شيئاً.


3. الشكل الموحّد

{
  "external_id": "1042",
  "external_number": "#1042",
  "status": "processing",
  "placed_at": "2026-09-17T12:03:00+03:00",

  "recipient": {
    "name": "سامر خليل",
    "phone": "0944000002",
    "alt_phone": "0999888777"
  },

  "delivery": {
    "tawseel_code": "DMS-MZH-8UQ2",
    "address_text": "المزة، جانب جامع الحسن، بناء أزرق",
    "city": "دمشق",
    "zone": "المزة",
    "lat": 33.5061,
    "lng": 36.2601,
    "notes": "الطابق الثالث، الباب على اليمين"
  },

  "payment": {
    "method": "cod",
    "cod_amount": 85000,
    "currency": "SYP"
  },

  "parcel": {
    "pieces": 1,
    "weight_kg": 1.5,
    "service_level": "intra_city",
    "items": [
      { "sku": "TSH-BLK-M", "name": "تيشيرت أسود M", "qty": 2, "price": 25000 }
    ]
  },

  "pickup": { "branch_code": "DMS-01" },
  "meta": { "store_url": "https://shop.example.sy", "plugin_version": "1.2.0" }
}

الحقول

الحقل إلزامي ملاحظات
external_id ✅ رقم الطلب عندك. هويّته الدائمة — لا تغيّره ولا تعِد استخدامه
external_number — ما يراه التاجر والزبون (#1042)
status ✅ حالة الطلب عندك كما هي. نحن نقرّر هل هي حالة الإطلاق (القاعدة 2)
placed_at — وقت الطلب الأصلي بـISO 8601 مع الإزاحة الزمنية
recipient.name ✅
recipient.phone ✅ رقم سوري محلي (09xxxxxxxx). نحن نوحّد صيغته
delivery.tawseel_code ⬥ أفضل ما ترسله. إن وُجد، تجاهَل باقي حقول العنوان
delivery.address_text ⬥ مطلوب إن لم يوجد رمز توصيل
delivery.city / zone — يسرّع التوجيه للفرع الصحيح
delivery.lat / lng — إن كان متجرك يلتقط إحداثيات، أرسلها. تختصر مكالمة
delivery.notes — طابق، معلم، تعليمات
payment.method ✅ cod أو prepaid
payment.cod_amount ✅ عند cod ما يُحصَّل من الزبون عند الباب، رقم عشري بالوحدة الكبرى (لا قروش)
payment.currency ✅ SYP غالباً
parcel.weight_kg — افتراضنا 1 كغ
parcel.service_level — standard / intra_city. نستنتجه إن تركته
parcel.items[] — يُطبع على البوليصة، ويُستعمل لاحقاً لحجز المخزون إن كانت بضاعتك عندنا
pickup.branch_code / tawseel_code — من أين نستلم. الافتراضي إعداد الاتصال

⬥ = أحدهما مطلوب على الأقل.


4. القواعد

1. external_id هو الهوية. إرسال نفس الطلب مرتين = سجل واحد وشحنة واحدة. لا ترسل رقماً جديداً لطلب قديم بعد تعديله.

2. أرسل عند موافقة التاجر، وأرسل التغييرات أيضاً. «الموافقة» تختلف بين المنصات، لذلك حالة الإطلاق تُضبط لكل اتصال من عندنا. أرسل كل تغيّر حالة: ما ليس حالة إطلاق نحفظه ولا نشحنه، وإن غيّرها التاجر لاحقاً انطلق الطلب بلا حاجة لإرسال جديد.

3. الهاتف إلزامي. هو المعرّف الوحيد الموثوق لشخص في سوريا، وعليه نبني مطابقة العناوين السابقة وإشعارات التتبع ورابط تحديد الموقع.

4. العنوان: لا ترفض ولا تخترع. الترتيب المفضّل: رمز توصيل ← إحداثيات ← نص + مدينة/منطقة. إن لم نستطع حلّه، لا نرفض الطلب: نحفظه بحالة needs_address، ونرسل للمستلم رابطاً يضع فيه دبوسه بنفسه، ثم يكمل تلقائياً. لا تطلب من التاجر إحداثيات.

5. مبلغ COD هو ما يُقبض عند الباب، لا سعر المنتجات. طلب مدفوع مسبقاً يُرسل بـprepaid وcod_amount: 0. إرسال سعر البضاعة على طلب مدفوع يعني مطالبة الزبون بالدفع مرتين.

6. أرسل Idempotency-Key مع كل طلب، وأعد المحاولة بنفس المفتاح. نفس المفتاح بنفس الجسم يُعيد الاستجابة المحفوظة مع ترويسة Idempotent-Replay: true. نفس المفتاح بجسم مختلف يُرفض 422. اقتراح لتكوين المفتاح: {platform}-{external_id}-{status}.

7. لا تنتظرنا طويلاً. مهلتنا أقل من ثانيتين عادة. عامل أي 5xx أو انقطاع كإعادة محاولة بتباعد متزايد، لا كفشل نهائي.

8. لا ترسل بيانات لا نحتاجها. لا أرقام بطاقات، لا كلمات مرور، لا سلة تسوّق كاملة.


5. ماذا نعيد لك

الرمز المعنى تصرّفك
202 وصل وحُفظ. {"store_order_id": 918, "status": "received"} انتهى. لا تعِد الإرسال
200 + Idempotent-Replay: true تكرار لطلب سابق انتهى
400 / 422 حمولة ناقصة أو غير صالحة. الجسم يسمّي الحقل أصلح وأعد الإرسال بمفتاح جديد
401 مفتاح أو توقيع خاطئ لا تعِد المحاولة، راجع المفتاح
403 صلاحية ناقصة (تُسمّى في الرسالة) أضف الصلاحية من البوابة
429 تجاوزت 60/دقيقة انتظر واستأنف
5xx عطل عندنا أعد المحاولة بتباعد متزايد ونفس Idempotency-Key

نرد 202 قبل أي معالجة. أي فشل تحويل لاحق لا يظهر في ردّنا، لأن تعطيل متجرك للـwebhook بعد عدة إخفاقات يوقف طلبات التاجر بصمت. تتابع نتيجة المعالجة عبر الأحداث الراجعة أو لوحة التاجر.


6. ما نرسله لك (الاتجاه المعاكس)

متجر ووكومرس عبر الباب أ لا يحتاج شيئاً من هذا القسم. نكتب في طلبه مباشرة عبر REST API بمفاتيحه: ملاحظة برابط التتبع يراها الزبون، ملاحظة حين تخرج الشحنة، ثم completed عند التسليم، وملاحظات داخلية للفشل والإرجاع. يكفي مفتاح REST بصلاحية Read/Write في صفحة «ربط متجرك». ما يلي لمن يرسل عبر الباب الموحّد ويريد الأحداث إلى خادمه هو.

سجّل عنوانك مرة واحدة عبر POST /partner/v1/webhooks (صلاحية webhooks:manage) وتصلك الأحداث:

POST /your/endpoint
X-Tawseel-Signature: 9a1f…            ← HMAC-SHA256 بصيغة hex على الجسم الخام بسرّك
Content-Type: application/json

{ "event": "shipment.status_changed",
  "data": { "tracking_code": "TWS-8KQ2M4XP", "from_status": "received",
            "status": "out_for_delivery", "note": null } }

الأحداث: shipment.created · shipment.status_changed · shipment.delivered · shipment.returned · settlement.closed.

التحقق (PHP):

$expected = hash_hmac('sha256', $rawBody, $yourSecret);
if (! hash_equals($expected, $request->header('X-Tawseel-Signature'))) {
    abort(401);
}

نحاول 3 مرات بتباعد 10 ثوانٍ ثم دقيقة ثم 5 دقائق، ثم تُعاد المحاولة بمهمة مجدولة. ردّ بـ2xx بسرعة وضع عملك في طابور، وإلا اعتُبرت المحاولة فاشلة.


7. الإلغاء والتعديل

  • إلغاء: POST /partner/v1/store-orders/{external_id}/cancel مع سبب اختياري. ينجح ما دامت الشحنة لم تدخل شبكة التشغيل؛ بعدها يُرفض 422 ويحتاج قراراً من العمليات، لأن الطرد صار عند شخص.
  • تعديل العنوان أو الهاتف: أعد إرسال نفس external_id بالبيانات الجديدة قبل خروج الشحنة. بعد الخروج يُحفظ التعديل ولا يُطبَّق تلقائياً.
  • تعديل المبلغ بعد الخروج: غير مسموح. المندوب يحمل ورقة بمبلغ.

8. خريطة الحقول لكل منصة (للباب ب)

عندنا ووكومرس سلة زِد
external_id id order.id order.id
status status حالة الطلب/الشحنة order.status
recipient.name billing.first_name + last_name customer.first_name… customer.name
recipient.phone billing.phone customer.mobile customer.mobile
delivery.address_text shipping.address_1 + address_2 ship_to.street shipping.address
delivery.city shipping.city ship_to.city shipping.city
payment.cod_amount total عند cod amounts.total order.total
parcel.items[] line_items[] items[] products[]

الحدث الذي يعني «اشحنه»: ووكومرس حالة يختارها التاجر (processing غالباً) · سلة shipment.creating · زِد order.ready.


9. قبل أن تُطلق

  • مفتاح API بصلاحية shipments:write (وwebhooks:manage إن أردت الأحداث).
  • external_id ثابت ولا يُعاد استخدامه.
  • Idempotency-Key على كل إرسال، ويُعاد بنفسه عند إعادة المحاولة.
  • هاتف المستلم بصيغة محلية صحيحة.
  • cod_amount صفر على الطلبات المدفوعة مسبقاً.
  • تتعامل مع 5xx بإعادة محاولة متباعدة لا بإلغاء.
  • نقطة استقبالك ترد 2xx بسرعة وتتحقق من X-Tawseel-Signature.
  • جرّبت على بيئة الاختبار قبل ربط متجر حقيقي.

بيئة الاختبار: اطلب منّا مفتاح تجريبي (يُنشأ عندنا بأمر tawseel:sandbox). الطلبات عليه لا تُنشئ شحنات حقيقية ولا تُفوتر.