تكامل مرتجعات زد الآمن ليس طلب API واحدًا؛ بل Workflow دائم يفصل أهلية المنتج، وحساب المبلغ، وإنشاء الطلب العكسي، ورد المال، وإثبات التحويل، والشحن العكسي، وفحص المنتج عند وصوله. التصميم الصحيح يحفظ كل انتقال، ويمنع تكرار الأثر المالي، ويطابق المال والمخزون كلًا على حدة. يفيد هذا الدليل مطوري ERP وWMS وخدمة العملاء والمحاسبة المتكاملة مع متاجر زد.
هذا شرح عملي وليس خبرًا. ما تنص عليه وثائق زد موثق بالمراجع أدناه، بينما State Machine وسياسة إعادة المحاولة والمطابقة توصيات هندسية. جرى التحقق من الوثائق في 23 أكتوبر 2026.
أربع حقائق لا تضعها في حقل واحد
تؤثر المرتجعات في أربعة سجلات مختلفة:
- الطلب الأصلي يثبت ما بيع والكمية التي ما تزال قابلة للإرجاع.
- الطلب العكسي يسجل نية الإرجاع والمنتجات والموقع والسبب وبيانات الشحن.
- رد المال يسجل العملية المالية وطريقتها.
- نتيجة الفحص تسجل ما وصل سليمًا أو تالفًا أو لم يصل.
إذا اختصر ERP هذه الحالات في قيمة \returned=true\ فلن يستطيع معرفة هل خرج المال قبل وصول الشحنة، أو هل أُعيد منتج تالف إلى المخزون، أو هل أنشأت إعادة المحاولة Refund ثانيًا.
ما الذي توثقه Zid؟
قبل الإرجاع الجزئي، استخدم View for Return. يعيد فقط المنتجات التي بقيت لها كمية قابلة للإرجاع، ويعيد حساب الإجماليات بعد المرتجعات السابقة. عندما ينعكس الطلب كاملًا تصبح قائمة المنتجات فارغة والإجمالي صفرًا.
بعد ذلك يعرض Calculate Reverse Totals المجموع والخصومات وضريبة القيمة المضافة والإجمالي النهائي. توضح زد أن هذه عملية حساب للقراءة فقط: لا تنشئ طلبًا عكسيًا، ولا تغير المخزون، ولا ترد المال، ولا تغير حالة الطلب.
ينشئ Create Reverse Orders سجل الإرجاع الفعلي. يستقبل الطلب ونوع الإرجاع كاملًا أو جزئيًا، وموقع المخزون، والسبب، والمنتجات، ومعلومات الشحن. وتحتوي الاستجابة على معرف الطلب العكسي وبنوده ومبالغه وطرق رد المال المتاحة.
أما Create Refund فهو أثر مالي مستقل. تطلب زد قراءة \refund_total\ و\available_refund_payment_methods\ ثم اختيار مبلغ وطريقة صالحة. وإذا كانت الطريقة \zid_bank_transfer\ يصبح رفع إيصال التحويل إلزاميًا بعد إنشاء Refund، وتقبل الواجهة ملف صورة.
المعالجة المادية منفصلة أيضًا. يسجل Update Return Products الكمية المستلمة بحالة جيدة، وغير المستلمة، والتالفة. يجب ألا يتجاوز مجموعها \reversed_quantity\ لكل بند، وأن تكون أعدادًا صحيحة غير سالبة مع قيمة واحدة موجبة على الأقل. وتوثق زد أن التحديث Atomic: فشل بند واحد يرفض الطلب كله.
وأخيرًا ينشئ Reverse Orders Waybill بوليصة الإرجاع، وقد يعيد رابط الملصق ورقم التتبع وشركة الشحن ومستوى الخدمة.
State Machine داخلية مقترحة
لا تنسخ حالة خارجية إلى عمود واحد قابل للتعديل. أنشئ حالة محلية بمراحل واضحة:
| الحالة الداخلية | الدليل المطلوب | الأثر المسموح |
|---|---|---|
\DRAFT\ | الطلب الأصلي والكمية المتبقية | لا شيء |
\QUOTED\ | Hash للمدخلات وSnapshot للحساب | موافقة العميل أو الموظف |
\REVERSE_CREATED\ | معرف Reverse Order من زد | طلب Waybill |
\REFUND_REQUESTED\ | معرف Refund والمبلغ والطريقة | رفع الإيصال عند الحاجة |
\IN_TRANSIT\ | رقم تتبع أو إثبات إرجاع يدوي | تحديث التتبع |
\INSPECTED\ | السليم والتالف والمفقود لكل بند | قرار المخزون |
\RECONCILED\ | نجاح مطابقة المال والمخزون والطلب | إغلاق الحالة |
ترتيب موافقة الشحن ورد المال يعتمد على سياسة المتجر. فصل زد للعمليات لا يعني وجوب تنفيذ الأثر المالي فورًا. يمكن للمرتجعات مرتفعة القيمة أو المعرضة للاحتيال أن تتطلب موافقة الفحص قبل استدعاء Refund، مع الالتزام بسياسة المستهلك ومزود الدفع.
معمارية Command وEffect وReconciliation
استخدم ثلاثة سجلات دائمة أو ما يعادلها:
- \
return_cases\: رقم الطلب الأصلي، ومعرف الإرجاع في زد، والحالة، والمبالغ المطلوبة والمعتمدة، وطريقة رد المال والإصدار. - \
return_lines\: رقم بند الطلب، والكمية المطلوبة والمعكوسة، والمستلمة سليمة، والتالفة، وغير المستلمة. - \
return_effects\: اسم العملية، وHash الطلب، ووقت البدء، والمعرف الخارجي، وSnapshot الاستجابة، والنتيجة، وحالة المطابقة.
يجب أن تقبل واجهتك \idempotency_key\ داخليًا. وحتى إن لم توثق واجهة زد Idempotency أصلية لعملية كتابة معينة، يستطيع نظامك إعادة الحالة المحلية الموجودة بدل إرسال أثر خارجي جديد.
async function prepareReturn(command: ReturnCommand) {
const oldEffect = await effects.find(command.idempotencyKey);
if (oldEffect) return oldEffect.publicResult;
const eligible = await zid.viewForReturn(command.orderId);
validateRemainingQuantities(command.lines, eligible.products);
const quote = await zid.calculateReverseTotals(
command.orderId,
command.lines
);
await outbox.insert({
key: command.idempotencyKey,
operation: "create_reverse_order",
requestHash: stableHash(command),
quoteSnapshot: quote,
status: "prepared"
});
return { status: "awaiting_approval", quote };
}لا تترك Database Transaction مفتوحة أثناء انتظار الشبكة. المثال يوضح البيانات المطلوبة؛ في الإنتاج خزّن Command داخل معاملة قصيرة، ثم دع Outbox Worker يستدعي زد ويحفظ النتيجة.
تعامل مع النتيجة المجهولة بجدية
انتهاء المهلة لا يثبت فشل العملية؛ قد تقبل زد الكتابة ثم تضيع الاستجابة في الطريق. إعادة Refund بلا تحقق قد تكرر تحويل المال.
لكل عملية كتابة:
- خزّن حالة \
started\قبل الاستدعاء. - احفظ Hash للطلب دون Tokens أو بيانات شخصية غير لازمة.
- عند خطأ Validation مؤكد، ارفض العملية ولا تكرر المدخلات نفسها.
- عند Timeout أو انقطاع الاتصال، اجعل الحالة \
unknown\. - طابق النتيجة مع أدلة الطلب العكسي أو الطلب الأصلي أو Credit Note أو الدفع المتاحة قبل كتابة ثانية.
- انقل الغموض المالي غير المحسوم إلى Queue بشرية تعرض الطلب الأصلي والأدلة المرصودة.
إذا لم توفر API دليلًا كافيًا للتمييز بين النجاح والفشل، فلا توجد إعادة محاولة آمنة تلقائيًا. المراجعة اليدوية أفضل من Refund مكرر.
المال والمخزون يحتاجان Ledger مختلفًا
مبلغ الاسترداد وكمية Restock مترابطان لكنهما ليسا الشيء نفسه. قد يحصل العميل على Refund لمنتج تالف، بينما يجب عزل المنتج بدل إعادته للبيع. وقد توافق خدمة العملاء على رد استثنائي قبل وصول الطرد.
مثّل قرار المخزون صراحة:
| نتيجة الفحص | المسار المالي | مسار المخزون |
|---|---|---|
| وصل سليمًا | Refund حسب السياسة | Restock بعد مطابقة SKU والموقع |
| وصل تالفًا | Refund أو مراجعة | Quarantine أو إصلاح أو شطب |
| لم يصل | تعليق أو تحقيق | لا Restock |
| الكمية مختلفة | اعتماد جزئي أو مراجعة | مطابقة كل بند |
لا تزد مخزون ERP لمجرد إنشاء Reverse Order. اعتمد نتيجة الفحص، وحدد أي نظام يملك تعديل المخزون. إذا كانت زد وERP تستطيعان التعديل معًا، فاختر مصدرًا واحدًا للحقيقة واستورد نتيجة الطرف الآخر؛ وإلا قد تضاف الوحدة مرتين.
الأسعار والضريبة والتقريب
استخدم Invoice Lines والإجمالي المحسوبين من زد كعرض المنصة. لا تعيد بناء Refund من سعر الكتالوج الحالي؛ فالخصومات والضريبة والإضافات والمرتجعات السابقة قد تغير المبلغ المستحق.
خزّن المال كقيمة Decimal مع العملة، واحتفظ بـSnapshot الاستجابة التي بُنيت عليها الموافقة. قارن بدقة العملة لا بمساواة Floating Point. وقبل Refund تحقق أن المبلغ لا يتجاوز \refund_total\ وأن الطريقة موجودة داخل \available_refund_payment_methods\.
ضوابط الأمان والخصوصية
- اطلب Scope الضروري \
orders.read_write\فقط، ولا تضع Manager Tokens في السجلات أو التحليلات. - تعامل مع صور الإيصالات كملفات غير موثوقة: حد للحجم والنوع، وفحص، وتخزين خاص، ومدة احتفاظ محددة.
- قلل نسخ عنوان العميل وهاتفه، وأرسل لخدمة اللوجستيات ما يحتاجه الإرجاع فقط.
- افصل صلاحية إنشاء Reverse Order عن صلاحية إخراج المال أو تجاوز نتيجة الفحص.
- سجل الموظف والسبب والكميات قبل وبعد والمبلغ والمعرفات الخارجية.
Observability والمطابقة
راقب ثوابت العمل لا Status Code وحده:
- عدد الحالات المتوقفة في كل مرحلة وعمرها؛
- الآثار ذات النتيجة \
unknown\؛ - المبلغ المطلوب مقابل Quote زد مقابل المبلغ المسترد؛
- الكمية المعكوسة مقابل السليم والتالف وغير المستلم؛
- Waybill بلا حركة أو طرد مستلم بلا فحص؛
- تعديل مخزون بلا بند فحص مطابق؛
- Refund بتحويل بنكي دون الإيصال الإلزامي.
شغل Reconciliation دوريًا يقارن الحالات المحلية المفتوحة بأدلة زد. أرسل تنبيهًا عند الاختلاف، ولا تعد كتابة التاريخ المحاسبي بصمت.
أفضل الممارسات والأخطاء الشائعة
أفضل الممارسات
- حدّث \
view-for-return\مباشرة قبل حساب إرجاع جزئي. - احفظ المعرفات الخارجية وSnapshots غير القابلة للتعديل.
- استخدم Outbox وإعادات محدودة وDead Letter أو مراجعة يدوية.
- اجعل الفحص قرارًا لكل بند مع مصير مخزون واضح.
- اختبر الإرجاع الجزئي والمرتجعات السابقة والتلف والـTimeout والأوامر المكررة.
Anti-patterns
- إعادة Refund بعد Timeout دون مطابقة.
- اعتبار Reverse Order دليلًا على وصول الطرد.
- حساب الضريبة والخصم من سعر المنتج اليوم.
- زيادة المخزون القابل للبيع من الكمية المطلوبة للإرجاع.
- حفظ الإيصالات في Public Bucket أو تسجيل Tokens.
متى لا تؤتمت العملية كاملة؟
المتجر الصغير ذو المرتجعات النادرة قد يكون أكثر أمانًا مع تنفيذ بشري وفحص آلي فقط. المنتجات الرقمية والخدمات والطلبات المصنوعة خصيصًا تحتاج أهلية مختلفة. وإذا كان ERP لا يفرق بين التالف وغير المستلم والسليم، أصلح نموذج البيانات قبل تفعيل Restock تلقائي.
توثق صفحات زد سلوك كل Endpoint، لكنها لا تنص على Transaction واحدة Atomic تشمل الإرجاع والمال والشحن والمخزون، ولا توثق ضمان Idempotency شاملًا لكل كتابة. لذلك صمم للتنفيذ الجزئي والمطابقة.
قائمة تنفيذ مختصرة
- اربط الطلب وبنوده بالمعرفات الأصلية لا بـSKU وحده.
- حدّث الكمية التي ما تزال قابلة للإرجاع.
- احسب Quote زد واحفظ Snapshot.
- خذ موافقة السياسة ثم أنشئ Reverse Order واحدًا.
- سجل Refund وإيصال التحويل كأثرين مستقلين.
- أنشئ الشحن العكسي وتابعه عند الحاجة.
- افحص كل بند وقرر مصير مخزونه.
- طابق المال والمخزون وحالة الطلب قبل الإغلاق.
أدلة مرتبطة: مطابقة محافظ عملاء زد، وState Machine لحالات الطلب في سلة وزد، واستعادة Webhooks المعطلة في زد، ومكتبة المقالات الهندسية.
المراجع الأولية
- Zid — تفاصيل الطلب المهيأة للإرجاع
- Zid — حساب إجماليات الإرجاع
- Zid — إنشاء Reverse Order
- Zid — إنشاء Refund للطلب العكسي
- Zid — رفع إيصال التحويل البنكي
- Zid — تحديث حالة المنتجات المرتجعة
- Zid — إنشاء Waybill للمرتجع
- ترخيص Unsplash
- Guilherme Mendes — صورة المستودع المرخّصة
المراجع الرسمية
تدعم هذه المراجع سلوك الأدوات المذكورة. الأمثلة وقرارات التصميم توضيحية، ويجب تكييفها مع متطلبات المشروع وإصداراته.
إعداد: Noor Yasser
تعمل على تحدٍ تقني مشابه؟
أساعد الفرق على تحويل القرار المعماري إلى نطاق واضح وتنفيذ يمكن تشغيله ومراجعته بثقة.




