تعيش الشحنة في ثلاثة أنظمة على الأقل: سلة، وشركة الشحن، وغالبًا ERP أوخدمة Fulfilment. يمكن أن يكون كل نظام صحيحًا محليًا بينما يرى التاجر تناقضًا. قد تقبل شركة الشحن البوليصة ثم تنتهي مهلة التكامل، وقد تعيد سلة إرسال Webhook، وقد يستقبل ERP حالة متأخرة قبل حالة أقدم، أوقد يُطلب الإلغاء بعد مغادرة الطرد المستودع. إذا عومل آخر Payload على أنه الحقيقة، تتحول هذه الظروف الطبيعية في الأنظمة الموزعة إلى بوالص مكررة وحالات تتراجع ورسائل عميل لايمكن تفسيرها.
توفر واجهات شحنات سلة الحالية عمليات الإنشاء والقائمة والتفاصيل والتحديث والإلغاء والإرجاع والتتبع. وتشمل أحداث المتجر `shipment.creating` و`shipment.created` و`shipment.updated` و`shipment.cancelled`. وتذكر وثائق Webhooks أن التسليم غير الناجح قد يُعاد ثلاث مرات بفاصل يقارب خمس دقائق. لذلك يكون التسليم At-least-once من منظور المستقبل، ويجب أن يتوقع التطبيق التكرار.
هذا شرح مبني على الوثائق الحالية، وليس إعلانًا عن إطلاق ميزة شحن جديدة اليوم. تم التحقق من وثائق API في 29 سبتمبر 2026. أما آلة الحالات وسجل الأوامر وحلقة المطابقة أدناه فهي توصيات معمارية مبنية حول العمليات الموثقة.
افصل الملاحظات عن الحالة والأوامر
لا تجعل Webhook Handler يقرر حالة الشحنة ويتصل بشركة الشحن في العملية نفسها. خزّن ثلاثة سجلات مختلفة. تسجل Observation ما أبلغت به سلة أوشركة الشحن كما هو، مع المصدر ووقت الحدث ووقت الاستلام وHash للـPayload الخام. وتخزن Shipment Projection آخر حالة أعمال مقبولة. ويسجل Command أثرًا خارجيًا مقصودًا، مثل إنشاء بوليصة أوإلغاء شحنة أوطلب إرجاع.
يحفظ هذا الفصل الدليل. يمكن تأكيد Webhook مكررة من `shipment.updated` من دون تطبيق انتقال ثانٍ. وتبقى الحالة المتأخرة متاحة للتدقيق من دون إعادة `delivered` إلى `in_transit`. ويبقى اتصال الناقل الذي انتهت مهلته في `outcome_unknown` حتى المطابقة بدل وصفه خطأً بأنه فشل.
استخدم هويات ثابتة. يجب أن يجمع مفتاح الشحنة المحلي بين التاجر ومعرف شحنة سلة بعد توفره. وقبل إرجاع سلة للمعرف، استخدم Operation Key فريدة مثل التاجر والطلب ونوع الشحنة ومحاولة التجهيز. خزّن مرجع طلب سلة ورقم تتبع الناقل والمعرفات الخارجية وعلاقة الإرجاع كمراجع، لاكمفاتيح أساسية قابلة للتبادل. قد يملك الطلب أكثر من شحنة، والإرجاع Lifecycle مستقلة.
وحّد الحالات من دون محو تفاصيل المصدر
يوثق نموذج تفاصيل الشحنة في سلة حاليًا حالات تشمل `created` و`in_progress` و`in_transit` و`received_at_final_hub` و`to_be_reattempted` و`reattempted` و`unable_to_deliver` و`delivering` و`delivered` و`partially_delivered` و`cancelled` و`lost` و`damaged` و`return_to_origin` و`return_in_progress`. وستستخدم شركات الشحن مفردات مختلفة. اربط الطرفين بنموذج داخلي مختصر، لكن احتفظ بالحالة الأصلية بجانبه.
قد يجمع النموذج الداخلي تجهيز البوليصة والتسليم للناقل والحركة واستثناء التسليم والتسليم النهائي والإلغاء والإرجاع. يفيد التجميع منطق المنتج، بينما تفيد التفاصيل الأصلية الدعم والتدقيق. لا تختصر `lost` و`damaged` و`unable_to_deliver` في Failure عامة تخفي الإجراء المطلوب؛ فرسالة العميل ومعالجة المخزون ومطالبة الناقل تختلف.
أصدر Mapping Table بإصدارات. عندما تضيف شركة الشحن حالة أوتوسع سلة Enum، يجب أن تبقى Observations القديمة مفسرة بالإصدار الذي استقبلها. ضع القيمة غير المعروفة في Quarantine Queue مرئية بدل تحويلها بصمت إلى `in_transit`.
طبّق سياسة انتقال صريحة
يجب ألا تتغير Projection إلا عبر دالة انتقال واضحة. تنص دورة تطبيق الشحن على أن الشحنة بعد وصولها إلى `shipped` أو`delivering` أو`delivered` لايمكن إعادتها إلى `created` أو`in_progress`. يجب أن تكون سياستك المحلية صارمة بالقدر نفسه على الأقل، وأن تحدد ثوابت أعمال إضافية.
apply(observation, current_state, mapping_version)
-> accepted(next_state, reason)
-> ignored_duplicate(reason)
-> quarantined(reason)
-> conflict(needs_review)لا تستخدم Source Sequence أوSource Timestamp لترتيب الأحداث إلا عندما تكون دلالتها موثقة وموثوقة. وقت الاستلام ليس وقت الحدث. وإذا لم توجد قيمة ترتيب موثوقة، فقارن الانتقال بالثوابت: التسليم نهائي للمسار الصادر، والإلغاء بعد التسليم للناقل يحتاج تأكيدًا، وتقدم الإرجاع ينتمي لمسار Return مرتبط بدل إرجاع حالة الشحنة الصادرة إلى الوراء.
سجل كل قرار مع Observation ID والحالة السابقة والحالة المقترحة وإصدار القاعدة والسبب. يسمح هذا التاريخ للدعم بتفسير تجاهل حدث متأخر، ويسمح للمهندسين بإعادة بناء Projection بعد إصلاح خطأ في Mapping.
اجعل كل أمر للناقل Idempotent
إنشاء الشحنة أخطر عملية غامضة. تستقبل واجهة Create Shipment بيانات الطلب والناقل مع المراجع الخارجية، وتطلب الوثيقة الحالية تفاصيل العنوان الوطني داخل `ship_to`. قبل الاتصال بالناقل، أدرج Command Row بمفتاح عملية فريد. وأرسل المفتاح نفسه بصفته Idempotency Key أومرجع العميل عندما تدعم شركة الشحن ذلك.
يجب أن تستأنف Retry الأمر نفسه، لا أن تنشئ نية أعمال جديدة. إذا انتهت مهلة HTTP، ضع الأمر في `outcome_unknown`. وابحث لدى الناقل بمفتاح العملية أومرجع الطلب. لا تنشئ مرة أخرى إلا بعد وجود دليل على غياب الشحنة. خزّن البوليصة ورقم التتبع وProvider Receipt الناتجة ذريًا مع حالة نجاح الأمر.
طبق البروتوكول نفسه على الإلغاء والإرجاع. لا تثبت استجابة `200` من Queue محلية أن الناقل أكمل العمل، كما أن Timeout لا تثبت الرفض. افصل بين `requested` و`dispatched` و`confirmed` و`rejected` و`outcome_unknown` حتى تخبر الواجهة التاجر بما هو معروف فعلًا.
عامل Webhooks كإشارات ثم طابق
تحقق من `X-Salla-Signature` مقابل Request Body غير المعدلة باستخدام مقارنة Timing-safe، ثم خزّن Observation ورد بسرعة. ضع عمل الناقل أوERP الثقيل على Durable Queue. يفيد سلوك إعادة المحاولة الموثق كوسيلة تعافٍ، لكنه ليس عقد Exactly-once ولايجب أن يكون آلية التعافي الوحيدة.
أنشئ حلقة Reconciliation مجدولة. تدعم واجهة List Shipments فلاتر مثل الطلب والناقل والحالة ونوع الشحنة والمدى الزمني وPagination. استخدم نوافذ محدودة ومتداخلة، ثم اجلب التفاصيل أوسجل التتبع للصفوف المشبوهة. قارن سلة والناقل وProjection المحلية حسب الهويات والحالات الحالية ومراجع التتبع والأوقات النهائية.
لا تجعل المطابقة تستبدل الحالة مباشرة. هي تُصدر Observations تمر بدالة الانتقال نفسها التي تستخدمها Webhooks. يحافظ ذلك على مجموعة ثوابت واحدة وينشئ أثر تدقيق. صنّف الانحراف: شحنة محلية مفقودة، أوProvider Receipt مفقود، أوخلاف حالة، أوعدم تطابق الهوية، أوحركة متوقفة، أوتعارض في حالة نهائية. وجّه كل فئة إلى Repair تلقائي أوHuman Queue.
صمّم الإلغاء والإرجاع كـSagas
الإلغاء ليس Boolean. يطلبه التاجر، ويتحقق التكامل من الدليل الحالي، وتقبله شركة الشحن أوترفضه، ثم تُحدّث سلة ويتبع ذلك المخزون أوتواصل العميل. تشير إرشادات سلة الحالية إلى أن شركة الشحن يمكنها رفض الإلغاء بعد إرسال الشحنة أوتسليمها. احتفظ بهذا الرفض كنتيجة أعمال، لاكـIntegration Error عامة.
يحتاج الإرجاع Shipment Leg مستقلة. توثق دورة الشحن في سلة `shipment.creating` مع `type: return`، وتعمل واجهة Return Shipment على Shipment ID محدد. اربط الإرجاع بالشحنة الصادرة، لكن امنحه بوليصة وتتبعًا وأوامر وProjection وحالة نهائية مستقلة. إرجاع الطرد لايمحو حقيقة أن التسليم الصادر قد حدث.
استخدم Compensating Actions عندما تستحيل الذرية. إذا ألغى الناقل وفشل تحديث سلة، يجب أن تعيد المطابقة محاولة تحديث Projection في سلة. وإذا سجلت سلة الإلغاء ورفضه الناقل، أظهر Conflict وامنع تحرير المخزون تلقائيًا. تحتاج كل خطوة إلى مالك وDeadline وإجراء تعافٍ.
احمِ حدود Schema والعنوان
تقول وثائق Create Shipment الحالية إن حقول العنوان الوطني إلزامية في `ship_to`، وتلغي تدريجيًا معرفات الدولة والمدينة القديمة لصالح الحقول المتداخلة الجديدة. تحقق من اكتمال العنوان قبل إنشاء الأمر الخارجي. يجب أن يفشل غياب Postal Code أوShort Address قبل شراء بوليصة من الناقل، مع رسالة قابلة للإصلاح للتاجر.
عامل قيود API كـContract Tests. تطلب وثيقة Update Shipment Details الحالية `status` وتحدد `tracking_link` و`pdf_label` بحد أقصى 300 حرف. احتفظ بـFixtures لأطوال الحقول القصوى والقيم الاختيارية ومبالغ COD والحزم المتعددة ونوعي outbound وreturn وكل حالة نهائية مدعومة. نبّه إلى الحقول غير المعروفة فقط عندما تؤثر في الصحة، واحتفظ بالـPayload المتوافقة مستقبلًا ضمن الدليل الخام.
احذف البوالص وأرقام الهواتف والعناوين وبيانات العملاء من Logs. تحتاج Observability إلى Correlation IDs ومعرفات شحن scoped للتاجر ومعرفات الأوامر وانتقالات الحالة وفئات الأخطاء، لاإلى Payloads كاملة. شفّر الدليل الخام المحتفظ به وطبّق سياسة حذف تناسب الاحتياج التشغيلي والقانوني.
قس التعافي لا نجاح Webhook فقط
قد تخفي نسبة Webhook ناجحة مسار Fulfilment معطلًا. قس الزمن من طلب التاجر حتى تأكيد البوليصة، والأوامر في `outcome_unknown`، والملاحظات المكررة التي مُنعت، والانتقالات في Quarantine، والشحنات التي لم تتحرك، والخلافات حسب الناقل، وزمن إصلاح المطابقة، وتعارضات الإلغاء، والمرتجعات غير المرتبطة بمسار صادر، ورسائل العملاء التي أُرسلت من حالة غير مؤكدة.
اختبر بقطع الاتصال بعد اعتماد الناقل وقبل حفظ Worker للاستجابة. أعد Webhook نفسها مرات عدة. أرسل الحالات بترتيب مختلف. أخّر الإلغاء حتى تصبح الشحنة `in_transit`. وأفشل تحديث سلة بعد تأكيد إرجاع الناقل. النتيجة المتوقعة ليست نجاح كل استدعاء؛ بل ظهور هوية شحنة واحدة وحالة واحدة قابلة للتفسير في النهاية.
القاعدة العملية بسيطة: تبلغ الأحداث عن Observations، وتقبل آلة حالات مصدرة الانتقالات، وتمتلك Command Records الآثار الخارجية، وتحل المطابقة عدم اليقين. يكلف ذلك أكثر من إسناد آخر Payload إلى عمود، لكنه يمنع أغلى نتيجة في عمليات التجارة: طرد يتحرك في الواقع بينما يروي كل نظام قصة مختلفة.
المراجع الرسمية
تدعم هذه المراجع سلوك الأدوات المذكورة. الأمثلة وقرارات التصميم توضيحية، ويجب تكييفها مع متطلبات المشروع وإصداراته.
- Salla Docs — Create Shipment, verified 29 September 2026
- Salla Docs — List Shipments, verified 29 September 2026
- Salla Docs — Update Shipment Details, verified 29 September 2026
- Salla Docs — Shipping Management App Cycle, verified 29 September 2026
- Salla Docs — Webhooks and shipment events, verified 29 September 2026
- Salla Docs — Shipping and Fulfilment API Change Log, verified 29 September 2026
- Salla Docs — Shipment Tracking, verified 29 September 2026
- Salla Docs — Cancel and Return Shipment endpoints, verified 29 September 2026
إعداد: Noor Yasser
تعمل على تحدٍ تقني مشابه؟
أساعد الفرق على تحويل القرار المعماري إلى نطاق واضح وتنفيذ يمكن تشغيله ومراجعته بثقة.




