تخطَّ إلى المحتوى

Webhooks

تدفع الـ Webhooks إشعارات فورية إلى خادمك عند حدوث أحداث في متجرك.

أنواع الأحداث

الحدثالمحفّز
order.createdطلب جديد (واجهة العميل أو نقطة البيع)
order.status_changedتحديث حالة الطلب
refund.createdاسترداد طلب من نقطة البيع — جزئي أو كامل
manager_override.approvedالموافقة على إجراء حساس برمز المدير (استرداد كبير، إلغاء طلب، خصم ≥ ٢٠٪، تبديل كاشير…)
product.createdإضافة منتج جديد
product.updatedتغيير تفاصيل المنتج
customer.createdتسجيل عميل جديد
inventory.changedتغيير مستوى المخزون

صيغة الحمولة

{
"id": "delivery-uuid",
"event": "order.created",
"entity_type": "order",
"entity_id": "order-uuid-123",
"data": { "id": "order-uuid-123", "status": "pending", "total": 80.0 },
"timestamp": "2026-04-20T14:30:00Z"
}

حمولة order.created من نقطة البيع

عندما يصدر الطلب من نقطة البيع تحتوي data على حقول إضافية تفيد أنظمة الرواتب والمحاسبة:

{
"source": "pos",
"order_id": "order-uuid",
"total": 120.0,
"payment_method": "cash",
"status": "completed",
"customer_name": "أحمد محمد",
"customer_phone": "+218910000123",
"created_by": "session-owner-profile-uuid",
"actual_cashier_id": "pin-switched-profile-uuid",
"item_count": 3,
"discount": 10.0
}

actual_cashier_id غير فارغ فقط عندما يستخدم الكاشير ميزة تبديل PIN — وفي هذه الحالة يجب اعتماده على created_by لاحتساب العمولات.

حمولة refund.created

{
"refund_id": "refund-uuid",
"order_id": "order-uuid",
"amount": 25.0,
"refund_type": "partial",
"reason": "العلبة مكسورة",
"reason_code": "damaged",
"items": [{ "product_id": "...", "variant_id": null, "quantity": 1, "amount": 25.0 }],
"created_by": "cashier-profile-uuid"
}

reason_code قيم ثابتة: damaged · wrong_item · customer_changed_mind · price_match · expired · other (أو null إذا لم يحدّد الكاشير سبباً).

حمولة manager_override.approved

{
"action": "refund",
"cashier_id": "cashier-profile-uuid",
"manager_id": "manager-profile-uuid",
"manager_name": "مدير الفرع",
"context": {
"orderId": "order-uuid",
"amount": 150.0,
"refundType": "full"
}
}

قيم action المحتملة: refund · partial_refund · void · discount · negative_stock · price_override · cashier_switch · other. context يختلف حسب الإجراء — استخدم action للتفريع.

الرؤوس

الرأسالوصف
X-durj-Signatureتوقيع HMAC-SHA256
X-durj-Eventنوع الحدث
X-durj-Deliveryمعرف التسليم

التحقق من التوقيع (Python)

import hmac, hashlib
def verify_signature(payload: bytes, secret: str, signature: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)

سياسة إعادة المحاولة

المحاولةالتأخير
الأولى1 دقيقة
الثانية5 دقائق
الثالثة30 دقيقة
الرابعةساعتان
الخامسة12 ساعة

بعد 5 محاولات فاشلة، يُنقل إلى قائمة الرسائل الميتة (DLQ).

التكرار الآمن (مطلوب)

كل تسليم — بما فيه إعادات المحاولة لنفس الحدث — يحمل ترويسة X-durj-Delivery. قيمتها ثابتة عبر المحاولات: إذا أعاد المرسل التسليم بعد فشل أو مهلة، يُرسل نفس الحمولة بنفس X-durj-Delivery تماماً.

يجب على المتلقي إلغاء التكرار بناءً على هذه الترويسة — لا الاعتماد على تشابه المحتوى:

async function handleWebhook(req, res) {
const deliveryId = req.headers['x-durj-delivery']
if (await alreadyProcessed(deliveryId)) {
return res.status(200).json({ deduplicated: true })
}
await processEvent(req.body)
await markProcessed(deliveryId)
return res.status(200).json({ ok: true })
}

السبب: أي خطأ مؤقت (5xx) من طرفك يُعيد المحاولة حتى 5 مرات. بدون إلغاء التكرار، ستحاسب الزبون 5 مرات أو ترسل 5 رسائل لنفس الحدث.