التجارة السعودية · معمارية التكاملات

Webhooks سلة وزد: كيف تبني مزامنة طلبات تتحمل الأحداث المفقودة؟

معمارية عملية لتطبيقات سلة وزد تجمع بين التحقق من Webhooks وسجل استقبال دائم ومعالجة idempotent ومطابقة دورية عبر API.

المسار المرتبطهندسة التجارة الإلكترونية
رسم تصوري أصلي لمساري أحداث من متجرين يدخلان سجل استقبال دائم مع حلقة مطابقة؛ لا يمثل البنية الداخلية لأي منصة.
تصوّر بصري للفكرة — يتبعه شرح ومخطط تنفيذي داخل المقال.

الـWebhook إشارة سريعة، لكنه ليس دفتر الطلبات. إذا كان تطبيقك على سلة أو زد يرسل الطلبات إلى ERP أو مستودع أو شركة شحن، فالنجاح لا يعني استقبال callback واحد في المسار المثالي. يجب أن يتحمل التكامل التكرار، وتأخر المحاولات، وفترة قد لا يصل فيها أي حدث أصلًا.

ماذا تقول عقود المنصتين فعلًا؟

تسرد وثائق Webhooks في سلة أحداث إنشاء الطلب وتحديثه والدفع والإلغاء والاسترجاع وتغيّرات الشحنة. وتوثّق التحقق عبر Signature أو Token، ومدة تقارب 30 ثانية لبدء الاتصال والحصول على الاستجابة، وثلاث محاولات بفاصل يقارب خمس دقائق عندما لا يعيد endpoint استجابة ناجحة. هذه المحاولات تحسن التسليم، لكنها لا تجعل قاعدة بياناتك نسخة مضمونة من سلة.

أما وثائق Webhooks في زد فتعرض أحداث إنشاء الطلب وتغيّر حالته وحالة الدفع، إلى جانب أحداث المنتجات والعملاء. الشروط موثقة حاليًا لأحداث `order.create` و`order.status.update`، ما يسمح بتصفية الاشتراك عندما يناسب ذلك حالة الاستخدام. تختلف أسماء الأحداث والـpayloads والشروط عن سلة؛ لذلك يجب توحيد المعنى بعد التحقق من كل مزود، لا افتراض أن العقدين متطابقان.

Circuit Breaker في زد يغيّر سؤال التعافي

توثق زد أيضًا تتبّع صحة Webhooks. ينتقل endpoint إلى degraded بعد 10 إخفاقات أو أكثر خلال نافذة ساعة، وإلى broken بعد 30 أو أكثر. الاستجابة الناجحة تعيد العداد، بينما لا يُحسب HTTP 429 فشلًا؛ بل إشارة إلى أن endpoint يعمل لكنه محدود بالـrate limit.

التفصيل التشغيلي الأهم يأتي بعد broken: تقول زد إن إرسال الأحداث الجديدة يتوقف وإن الأحداث المعلقة غير المسلّمة تُحذف. والتعافي صريح ويتطلب target URL جديدًا. كما تعيد زد محاولة الحدث حتى ثلاث مرات بعد دقيقة ثم خمس ثم خمس عشرة دقيقة، وبعد نفادها يُحتسب الإخفاق ضمن عداد الصحة.

هذه حماية مفيدة للمنصة، لكنها تعني أن خطة التعافي لا يمكن أن تكون «أعد تشغيل worker وانتظر». يجب أن يكتشف تطبيقك الفجوة ويعيد بناء الحالة من APIs الموثوقة.

ضع Durable Inbox قبل منطق العمل

اجعل handler العام صغيرًا: تحقّق من توقيع المزود أو بياناته باستخدام raw request، وحدد التاجر، وافحص الحد الأدنى من envelope، واحفظ التسليم في durable inbox، ثم أعد 2xx. يتولى queue worker العمل البطيء. لا تتصل بـERP ولا تنشئ AWB ولا ترسل حملة داخل request نفسها.

استخدم مفتاح uniqueness يقدمه المزود عندما يتضمن العقد event identifier ثابتًا. وإذا لم يتوفر، ابنِ fallback واضح الإصدار من المزود والتاجر ونوع الحدث ومعرّف المصدر الثابت؛ ويمكن أن يفيد hash للـpayload في اكتشاف التكرار، لكنه ليس هوية تجارية عامة. احتفظ بالـpayload الخام بصلاحيات وسياسة retention، وافصل أخطاء parsing عن أخطاء تنفيذ قواعد العمل.

webhook -> verify -> durable inbox -> 2xx
                         |
                         v
                       queue -> normalized order projection
                         ^                 |
                         |                 v
                 reconciliation <- platform API

على worker تنفيذ upsert باستخدام `(platform, merchant_id, source_order_id)` وحفظ وقت تحديث المصدر عندما يكون موثوقًا. وكل side effect يحتاج حد idempotency مستقلًا. إعادة تشغيل تحديث الطلب قد تحدّث نسخته المحلية، لكنها يجب ألا تنشئ شحنة ثانية أو تصدر refund مكررًا.

نمط تعافٍ محايد للمنصة: استقبل بسرعة، عالج بتكرار آمن، ثم طابق مع مصدر الحقيقة. هذا تصميم تحريري وليس بنية سلة أو زد الداخلية.
نمط تعافٍ محايد للمنصة: استقبل بسرعة، عالج بتكرار آمن، ثم طابق مع مصدر الحقيقة. هذا تصميم تحريري وليس بنية سلة أو زد الداخلية. اضغط لعرض أكبر

طابق مع API بدل التخمين

شغّل reconciliation مجدولًا لكل تاجر. اطلب نافذة زمنية محددة تتداخل مع آخر checkpoint ناجح، وقارن الطلبات الموثوقة بالنسخة المحلية، ثم أرسل الفروقات للإصلاح. هذا التداخل يعيد قراءة بعض السجلات عمدًا؛ وتجعل idempotent upserts ذلك آمنًا وتحمي من فرق الساعات والتحديثات المتأخرة.

توثق واجهة List Orders في سلة حاليًا pagination متسلسلًا، وحدًا أقصى `per_page=30`، ونافذة cache مدتها 15 دقيقة، وفلاتر بالتاريخ. لذلك قسّم الفترات الطويلة إلى نوافذ أصغر، وأكمل صفحات كل نافذة بالترتيب، ولا تحفظ checkpoint إلا بعد نجاحها كاملة، واحترم rate-limit responses. لا تقفز مباشرة إلى صفحة بعيدة ولا تعتمد على expanded response قديم.

في زد يمكن فصل فحصين: reconciliation التجاري يقارن حالة الطلب، بينما فحص Webhook Health يحدد هل الاشتراك healthy أو degraded أو broken. لا تخلط التنبيهين. قد يكون endpoint صحيًا لكن worker يحتوي bug، وقد يكون endpoint broken بينما يبدو آخر طلب عولج بنجاح طبيعيًا تمامًا.

اجعل اختلافات المزودين صريحة

أنشئ Salla adapter وZid adapter خلف عقد داخلي مثل `fetchChangedOrders` و`verifyWebhook` و`normalizeOrderEvent` و`inspectWebhookHealth`. وحّد فقط الحقول التي يملك منتجك معناها. حالات الدفع والطلبات المخصصة ومخزون الفروع والاسترجاع يجب أن تحتفظ بقيمة المصدر إلى جانب أي mapping داخلي.

يجب أن يصف الحدث الموحّد حقيقة لا أن يخترعها؛ `OrderPaymentStateObserved` أدق من `OrderPaid` عندما لا يثبت payload تسوية نهائية. ضع version للـnormalized payload حتى تستطيع إعادة المعالجة بعد تغيير mapping من دون إعادة كتابة التاريخ بصمت.

يكمل هذا الحد الخاص بكل مزود دليل معالجة Webhooks الموثوقة وقواعد عقود API وتصميم الأخطاء. كما يعطي المراقبة التشغيلية مؤشرات واضحة: عمر سجلات inbox، ونسبة التكرار، وتأخر workers، وفروقات reconciliation، واستجابات 429، وعدد الاشتراكات broken.

خطة حادثة للطلبات المفقودة

عند ظهور فجوة، أوقف الآثار الجانبية التدميرية أولًا، لا الاستقبال. تحقق من صحة endpoint والمصادقة، ثم سجّل آخر حدث ناجح وآخر reconciliation checkpoint. أصلح endpoint، واستعد الاشتراكات أو أعد إنشاءها حسب عقد المنصة، ثم نفّذ backfill للنوافذ المتأثرة عبر API.

قارن الأعداد ومعرّفات المصدر قبل إعادة فتح عمليات الشحن أو المال. وأعد تشغيل inbox records وإصلاحات المطابقة من خلال worker نفسه وحدود idempotency نفسها. لا تعدّل ERP يدويًا بينما replay الآلي يعمل إلا إذا سُجل كل تعديل يدوي كمفتاح deduplication.

أخيرًا، وثّق زمن الفجوة والـinvariants التي تحققت منها: كل طلب في المصدر موجود محليًا، وكل أثر شحن له سجل idempotency واحد، ولا توجد حالة محلية أحدث من حالة المصدر المؤكدة من دون تفسير.

القرار العملي

استخدم Webhooks للسرعة وAPIs للتعافي؛ لا يغني أحدهما عن الآخر. يمنحك durable inbox دليلًا على ما وصل، وتجعل idempotent workers إعادة التشغيل آمنة، وتكتشف reconciliation ما لم يصل، ويخبرك فحص صحة المزود عندما تتغير حالة قناة التسليم نفسها.

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

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

تدعم هذه المراجع سلوك الأدوات المذكورة. الأمثلة وقرارات التصميم توضيحية، ويجب تكييفها مع متطلبات المشروع وإصداراته.

إعداد: Noor Yasser

من القرار إلى التنفيذ

تعمل على تحدٍ تقني مشابه؟

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

احجز لقاءً لمدة ٣٠ دقيقةالخدمة المرتبطةتطبيقات سلة وزد وأدوات التجّارمشروع من الأعمالMember Plus