تخط وانتقل إلى المحتوى الرئيسي

الطلبات

إنشاء وقراءة الطلبات والانتقال بين حالاتها وإلغاؤها. دعم كامل للمتغيرات، سلال متعددة الأسطر، تسعير معتمد على الخادم.

بقلم: Support

الطلبات هي قلب المنصة. كل طلب:

  • ينتمي إلى متجر واحد فقط (مقيّد بمفتاحك — لا يمكنك أبدًا الوصول إلى بيانات تاجر آخر بالخطأ).

  • له دورة حياة من 7 حالات.

  • مرتبط بعميل (يُزال التكرار باستخدام الهاتف داخل المتجر).

  • يحتوي على عنصر واحد أو أكثر، كل منها يمكن أن يحمل متغيرات. إضافات المنتج (add-ons، أي الحقول المخصصة المدفوعة) غير مدعومة في واجهة v1 — انظر الإضافات.

  • له حالة دفع (pending, paid, refunded) مستقلّة عن حالة التسليم.

دورة الحياة {#lifecycle}

المسار الرئيسي   pending → confirmed → processing → shipped → delivered
قفزات مسموحة     pending → processing        confirmed → shipped
إلغاء            pending | confirmed | processing → cancelled   (نهائية)
إرجاع            shipped | delivered → returned                 (نهائية)

وهذه خريطة الانتقالات التي تفرضها الواجهة البرمجية بالضبط، لكل حالة:

من

الحالات التالية المسموح بها

pending

confirmed، processing، cancelled

confirmed

processing، shipped، cancelled

processing

shipped، cancelled

shipped

delivered، returned

delivered

returned

cancelled

— (نهائية)

returned

— (نهائية)

الإلغاء مشروع فقط من pending وconfirmed وprocessing — الطلب الذي بلغ shipped أو delivered لم يعد يمكن إلغاؤه.

وتعديل الطلب بـ PATCH إلى الحالة التي هو عليها أصلًا هو عملية 200 بلا أثر.

الحالة

المعنى

المخزون

pending

أُنشئ من الواجهة أو الـ API. ينتظر تأكيد التاجر.

لم يُحجز

confirmed

أكّده التاجر (اتصل بالعميل، راجع السلة).

مُلتزَم (تم خصمه)

processing

يُجهَّز / يُحضَّر للتسليم لشركة التوصيل.

مُلتزَم

shipped

سُلِّم لشركة التوصيل.

مُلتزَم

delivered

استلمه العميل ووقّع.

مُلتزَم

cancelled

أُلغي الطلب — يعود المخزون إن كان مُلتزَمًا.

مُسترَد

returned

أرجع العميل المنتج — يعود المخزون.

مُسترَد

cancelled و returned حالتان نهائيتان — لا يمكن الخروج منهما.

POST /v1/orders — إنشاء طلب

أنشئ طلبًا جديدًا للمتجر المنادي. يستعمله الثيمات المخصصة والتطبيقات المحمولة والموزّعون وأي واجهة headless ترسل الطلبات من خادمها الخاص بدل استعمال checkout واجهة المتجر المدمجة.

المصادقة: مفتاح منصة بصلاحية orders:write. يتطلب Idempotency-Key.

يُنشأ الطلب بحالة pending. لا يُلتزَم المخزون عند الإنشاء — أول انتقال إلى حالة مُلتزِمة (confirmed أو processing أو shipped أو delivered) هو من يخصم المخزون، تمامًا كتدفّق اللوحة. الهدف من ذلك: ترك مساحة لفريق العمليات لتصفية الطلبات الوهمية والمكررة وغير المُجاب عنها قبل لمس المخزون.

الجسم

{
  "customer": {
    "name":      "Sarra Benali",
    "phone":     "0555000111",
    "email":     "[email protected]",
    "wilaya_id": 16,
    "commune":   "Bab Ezzouar",
    "address":   "12 Rue X, Apt 3"
  },
  "delivery": {
    "type":      "home",
    "desk_id":    null,
    "desk_name":  null
  },
  "items": [
    {
      "product_id": 26,
      "quantity":   2,
      "variants": [
        { "group_name": "Color", "option_name": "Red",  "color_code": "#ff0000", "price_adjustment": 0 },
        { "group_name": "Size",  "option_name": "L",    "color_code": null,      "price_adjustment": 200 }
      ]
    }
  ],
  "shipping_cost":  600,
  "discount":       0,
  "payment_fee":    0,
  "payment_method": "cod",
  "notes":          "Please call before delivery"
}

مرجع الحقول

customer (كائن، إلزامي)

الحقل

النوع

إلزامي

ملاحظات

name

string (1–255)

الاسم الكامل

phone

string

^\+?[0-9 ]{6,20}$ — جزائري أو دولي

email

string | null

إن وُجد، يُحفظ في سجل العميل

wilaya_id

int 1–58

كود الولاية الجزائرية

commune

string (1–100)

نص حر، مثل "Bab Ezzouar"

address

string

شارع + شقة؛ يمكن تركه فارغًا للاستلام بالمكتب

العملاء يُزال تكرارهم بالمتجر + رقم الهاتف. إن وُجد عميل بهذا الهاتف في متجرك، يُحدَّث (الاسم، الولاية، البلدية، العنوان، البريد) ويُعاد استخدامه. وإلا يُنشَأ سجل جديد.

delivery (كائن، اختياري)

الحقل

النوع

الافتراضي

ملاحظات

type

home | desk | digital

home

digital للمنتجات الرقمية فقط

desk_id

int | null

null

إلزامي إن كان type = desk وأردت مكتبًا محددًا

desk_name

string | null

null

تسمية بشرية اختيارية

items (مصفوفة، إلزامية، 1–50 سطرًا)

الحقل

النوع

إلزامي

ملاحظات

product_id

int

يجب أن ينتمي لمتجرك (الـ IDs من متاجر أخرى تُرفض بـ 400)

quantity

int 1–9999

variants

مصفوفة من كائنات المتغيرات

انظر أدناه

هام — تسعير معتمد على الخادم. أنت لا تُحدّد سعر السطر. يُستعمل دائمًا سعر المنتج الحالي في الكتالوج. إن أرسلت حقل price فهو يُتجاهل.

وprice_adjustment لكل متغيّر معتمد على الخادم أيضًا: لكل زوج (group_name, option_name) يطابق خيارًا حقيقيًا على المنتج، تستبدل DZBuild قيمة price_adjustment الخاصة بالكتالوج. قيمتك تبقى فقط للأزواج غير الموجودة في الكتالوج — وهو تساهُل مقصود للتكاملات القديمة — لذا فأي "خصم" سالب تخترعه يُهمَل بصمت لأي خيار حقيقي. عامل price_adjustment عند الإدخال على أنه معلوماتي فقط: أعِد إرسال القيمة كما وردت من GET /v1/products/{id} كي يطابق إجماليك المحسوب عند العميل إجمالي الخادم.

items[].variants (مصفوفة، اختيارية)

كل كائن متغيّر يصف خيارًا مختارًا لمجموعة متغيرات على المنتج:

الحقل

النوع

ملاحظات

group_name

string

مثل "Color" أو "Size" أو "Material"

option_name

string

مثل "Red" أو "L" أو "Cotton"

color_code

string | null

لون hex (لمتغيرات اللون فقط)

price_adjustment

number

يُضاف للسعر الأساسي. تُستبدل بقيمة الكتالوج كلما وُجد زوج المجموعة/الخيار على المنتج — انظر ملاحظة التسعير أعلاه

أرسل كائن متغيّر واحد لكل مجموعة مختارة على هذا السطر. فـ "تيشيرت أحمر مقاس L" يصبح إدخالين (واحد لـ Color/Red وآخر لـ Size/L). تعرضها DZBuild على صفحة الطلب في اللوحة كما لو اختارها العميل من الواجهة تمامًا.

للمنتجات التي تستخدم متغيرات لكل قطعة (مثل عرض "اشترِ 3 تيشيرتات، اختر لونًا لكل قطعة")، استخدم quantity = 1 لكل سطر وأنشئ سطرًا لكل قطعة — هذا أنظف ربط.

حقول المال على المستوى الأعلى

الحقل

النوع

الافتراضي

ملاحظات

shipping_cost

number ≥ 0

0

تحسبه من جانب العميل بناء على الولاية + نوع التسليم

discount

number ≥ 0

0

قيمة كود خصم، خصم يدوي…

payment_fee

number ≥ 0

0

رسوم بوابة الدفع الإلكتروني

payment_method

cod | free_digital | digital_payment

تلقائي

الافتراضي cod للمادي، free_digital للرقمي

notes

string ≤ 1000

null

ملاحظات العميل، تظهر على صفحة الطلب في اللوحة

الإجمالي يُحسب على الخادم: subtotal + shipping_cost - discount + payment_fee (لا يقل عن 0). و subtotal نفسه = sum(items[].quantity × (price + Σ variants.price_adjustment)).

تُؤخذ الحقول shipping_cost وdiscount وpayment_fee كما أُرسلت تمامًا — والتحقق الوحيد هو أن كلًّا منها ≥ 0. وهي لا تُقارَن بأسعار الشحن أو أكواد الخصم المعرَّفة في متجرك، لذلك يجب استدعاء POST /v1/orders من خادم موثوق فقط، ولا يجوز إطلاقًا استدعاؤه من كود متصفح أو تطبيق يمكن للزبون العبث به.

الطلب

curl -X POST 'https://api.dzbuild.app/v1/orders' \
  -H "Authorization: Bearer $DZ_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "customer": {
      "name":      "Sarra Benali",
      "phone":     "0555000111",
      "wilaya_id": 16,
      "commune":   "Bab Ezzouar",
      "address":   "12 Rue X"
    },
    "items": [
      { "product_id": 26, "quantity": 1,
        "variants": [
          { "group_name": "Duration", "option_name": "30 days", "price_adjustment": 0 }
        ]
      }
    ],
    "shipping_cost":  600,
    "payment_method": "cod"
  }'

الاستجابة 200

إنشاء الطلب يُرجع HTTP 200 وليس 201 — لا تتفرّع على رمز الحالة؛ تحقّق من data.id / data.order_number بدلًا من ذلك. الجسم بنفس شكل GET /v1/orders/{id} — معبّأ بالكامل بالإجماليات المحسوبة، وكتلة العميل المعيارية، وسطور العناصر التي أنشأتها للتو (مع متغيراتها).

صيغة order_number هي ORD-{store_id}-{YYYYMMDD}-{8 أحرف hex كبيرة}. وطلبات صفحات الهبوط تستعمل LP-{8 أحرف hex كبيرة}. أما الطلبات المُنشأة قبل 2026-06-02 فتحمل لاحقة قديمة من 4 أحرف hex، لذا يجب أن يقبل المحلّل الطولين.

الأخطاء

الكود

السبب

bad_request "Body must be valid JSON"

Content-Type خاطئ أو JSON تالف

bad_request "customer object is required"

كائن customer مفقود

bad_request "customer.name is required (1-255 chars)"

الاسم مفقود أو طويل جدًا

bad_request "customer.phone is required (digits, optional leading +)"

الهاتف لم يطابق التعبير النمطي

bad_request "customer.wilaya_id must be 1-58"

ولاية غير صالحة

bad_request "customer.commune is required (1-100 chars)"

البلدية مفقودة أو طويلة جدًا

bad_request "items must be a non-empty array"

سلة فارغة

bad_request "items: max 50 lines per order"

أكثر من 50 سطرًا (قسّمها لطلبات متعددة)

bad_request "items[N].product_id is required"

product_id مفقود

bad_request "Product N does not belong to this store"

منتج من متجر آخر

bad_request "items[N].quantity must be 1-9999"

كمية غير صالحة

bad_request "delivery.type must be home, desk, or digital"

نوع تسليم غير صالح

bad_request "payment_method must be cod, free_digital, or digital_payment"

طريقة دفع غير صالحة

bad_request "shipping_cost, discount, payment_fee must be ≥ 0"

حقل مالي سالب

bad_request "Monthly order limit reached for this store plan"

سقف الخطة Free — انظر أدناه

سقف الطلبات الشهري. الخطة Free محدودة بـ 30 طلبًا في الشهر التقويمي؛ أما Pro و Unlimited و Enterprise فبلا سقف. واسم خطة غير معروف يعود أيضًا إلى سقف 30/شهر المجاني. العدّاد شهري تقويمي ويشمل كل مصادر الطلبات (واجهة المتجر + صفحة الهبوط + لوحة التحكم + الواجهة البرمجية).

Idempotency

كل POST يجب أن يحمل رأس Idempotency-Key. إن أعدت المحاولة بنفس المفتاح ونفس المتجر خلال 24 ساعة نُعيد لك نفس الاستجابة — يُنشأ الطلب مرة واحدة فقط. انظر Idempotency.

# آمن للإعادة بالمفتاح نفسه
KEY="$(uuidgen)"
for i in 1 2 3; do
  curl -X POST 'https://api.dzbuild.app/v1/orders' \
    -H "Authorization: Bearer $DZ_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $KEY" \
    -d @order.json
done
# يُنشأ طلب واحد فقط لا غير

Webhook الذي يُطلق

إنشاء طلب يُطلق order.created لكل اشتراكات /v1/webhooks على هذا الحدث. انظر أحداث Webhook.

⚠️ تنبيه — /v1/webhooks ترى نشاط الواجهة البرمجية فقط

اشتراكات /v1/webhooks تُطلق فقط للتغييرات التي تمت عبر واجهة v1. أما الطلبات الموضوعة على واجهة المتجر أو على صفحة هبوط، وتغييرات الحالة من لوحة التحكم، فلا تُطلق order.created / order.confirmed / order.shipped.

للحصول على أحداث تغطي كل نشاط الطلبات، استعمل إضافة Webhooks (/dashboard/addons ← "Webhooks — Connect n8n, Make & Zapier"، تتطلب خطة Unlimited) وهي تلتقط كل طلب مهما كان مصدره — أو واصل الاستطلاع عبر GET /v1/orders?since=….

ما الذي يتخطّاه مسار الطلب عبر الواجهة البرمجية

الطلب المُنشأ عبر الواجهة البرمجية هو إدراج مبسّط. مقارنةً بطلب من واجهة المتجر أو صفحة هبوط أو لوحة التحكم، فهو لا:

  • يُرسل للتاجر أي إشعار بطلب جديد (بريد أو Telegram أو إشعار دفع). إن كان لا بد من تنبيه التاجر، اشترك في order.created ووزّع الإشعارات بنفسك.

  • يُنتج أي حدث Pixel أو CAPI لـ Meta أو TikTok.

  • يزيد عدّادي total_orders / total_spent للعميل.

  • يفحص قائمة حظر العملاء — فطلب العميل المحظور عبر الواجهة البرمجية يمرّ. اقرأ is_banned من العملاء وارفضه من جانب عميلك إن احتجت ذلك.

  • يطبّق فحوص مكافحة الإساءة التلقائية في checkout واجهة المتجر، ولا قواعد إضافة الحد الأدنى/الأقصى للكمية.

ولاحظ أيضًا أن العميل الذي تُنشئه الواجهة البرمجية تُخزَّن فيه سلسلة customer.name كاملةً في first_name ويُترك last_name فارغًا؛ أما العميل الموجود مسبقًا والمطابَق برقم الهاتف فيُستبدَل فيه first_name فقط، ويُترك أي last_name موجود دون تغيير.


GET /v1/orders

اسرد الطلبات. ترقيم بالمؤشّر.

المصادقة: أي مفتاح منصة نشِط للمتجر (orders:read ممنوحة افتراضيًا وغير مطبَّقة بشكل منفصل في v1؛ المفحوصة هي orders:write فقط على نقاط الكتابة).

معاملات الاستعلام

المعامل

النوع

ملاحظات

limit

int 1–200

الافتراضي 50

cursor

string

غير شفاف

status

إحدى الحالات السبع

تصفية

since

سلسلة ISO 8601

created_at >= since

customer_phone

string

تطابق تام

قيمة status غير الصالحة أو since غير القابلة للتحليل تُتجاهَل بصمت — فتحصل على القائمة غير المصفّاة لا على 400. ولا يوجد فلتر payment_status في v1؛ صفِّ من جانب العميل.

الطلب

curl 'https://api.dzbuild.app/v1/orders?status=pending&limit=20' \
  -H "Authorization: Bearer $DZ_KEY"

الاستجابة 200

{
  "data": {
    "items": [
      {
        "id":              6894,
        "order_number":    "ORD-13-20260317-AD3C91F7",
        "status":          "pending",
        "payment_status":  "pending",
        "payment_method":  "cod",
        "total":           1000,
        "customer_name":   "John Doe",
        "customer_phone":  "0555000000",
        "wilaya_id":       16,
        "commune":         "Bab Ezzouar",
        "delivery_type":   "home",
        "created_at":      "2026-03-17 15:18:13"
      }
    ],
    "next_cursor": null,
    "has_more": false
  }
}

عرض القائمة مضغوط عمدًا (بدون عناصر ولا متغيرات). اطلب GET /v1/orders/{id} للتفاصيل الكاملة.


GET /v1/orders/{id}

تفاصيل كاملة مع سطور العناصر والمتغيرات وكتلة العميل.

المصادقة: أي مفتاح منصة نشِط للمتجر (orders:read غير مطبَّقة بشكل منفصل في v1).

الاستجابة 200

{
  "data": {
    "id":             6894,
    "order_number":   "ORD-13-20260317-AD3C91F7",
    "status":         "pending",
    "payment_status": "pending",
    "payment_method": "cod",
    "customer": {
      "id":         5578,
      "name":       "John Doe",
      "phone":      "0555000000",
      "email":      null,
      "wilaya_id":  16,
      "commune":    "Bab Ezzouar",
      "address":    "12 Rue X"
    },
    "delivery": { "type": "home", "desk_id": null, "desk_name": null },
    "amounts": {
      "subtotal":      1000,
      "shipping_cost": 0,
      "discount":      0,
      "payment_fee":   0,
      "total":         1000
    },
    "items": [
      {
        "id":         8421,
        "product_id": 26,
        "price":      1000,
        "quantity":   1,
        "variants": [
          { "order_item_id": 8421, "group_name": "Duration", "option_name": "30 days",
            "color_code": null, "price_adjustment": "0.00" }
        ]
      }
    ],
    "created_at": "2026-03-17 15:18:13",
    "updated_at": "2026-03-17 15:18:13"
  }
}

المتغيرات في الاستجابة

مصفوفة variants على كل عنصر هي المرجع لما اختاره العميل. كل إدخال يحمل order_item_id وgroup_name وoption_name وcolor_code (لمتغيرات اللون) وprice_adjustment (الإضافة لكل قطعة، وتُرجَع كسلسلة string في JSON لا كعدد). لعروض القطع المتعددة قد ترى عدة إدخالات متغيرات على نفس العنصر بتجميعات مختلفة — انظر ملاحظات القطع المتعددة أدناه.

ما لا تُرجعه استجابة التفاصيل

  • notes حقل للكتابة فقط في v1 — يُخزَّن على الطلب ويظهر في لوحة التحكم، لكن GET /v1/orders/{id} لا يُرجعه أبدًا.

  • كما أن product_name وsku للعنصر وtotal السطر تُلتقط عند الإنشاء لكنها لا تُرجَع أيضًا. ارجع إلى GET /v1/products/{id} إن احتجت الأسماء.

الإضافات (add-ons) غير مكشوفة {#add-ons-are-not-exposed}

إضافات المنتج (الحقول المخصصة المدفوعة التي يضبطها التاجر على منتج) ليس لها أي سطح في v1: لا يمكنك إرسالها في POST /v1/orders، وGET /v1/orders/{id} لا يُرجع أي إدخالات إضافات. أما الطلب الموضوع على واجهة المتجر مع إضافات فيُقرأ عبر الواجهة البرمجية وقيمة الإضافات مدمجة أصلًا في items[].price (وبالتالي في amounts.subtotal) — لكن بدون أي سطر إضافة ولا عنوان ولا نسبة ملف.


PATCH /v1/orders/{id} — تغيير الحالة

المصادقة: مفتاح منصة بصلاحية orders:write. يتطلب Idempotency-Key.

الجسم يجب أن يكون {"status": "<one of the 7>"}. نتحقق من الانتقال مقابل الجدول في دورة الحياة؛ وإلا نُرجع 400 مع الحالات التالية المسموح بها. وتعديل الطلب إلى حالته الحالية هو عملية 200 بلا أثر.

الطلب

curl -X PATCH 'https://api.dzbuild.app/v1/orders/6894' \
  -H "Authorization: Bearer $DZ_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: confirm-6894-$(date +%s)" \
  -d '{"status": "confirmed"}'

تُرجع 200 وتفاصيل الطلب المحدّث. الانتقالات المتزامنة على نفس الطلب تُنفَّذ بالتسلسل: إن غيّر طلبٌ آخر الحالة قبلك، يفشل طلبك بنظافة بـ bad_request ("order status changed concurrently; retry") بدل أن يُطبَّق مرتين.

آثار المخزون الجانبية

حالات الالتزام بالمخزون هي confirmed وprocessing وshipped وdelivered.

  • غير مُلتزَم → مُلتزَم — يُخصم المخزون أول مرة يدخل فيها الطلب أيًا من تلك الحالات، بما في ذلك القفزة pending → processing. ويرتفع كذلك عدّاد مبيعات المنتج.

  • مُلتزَم → cancelled / returned — يُسترَد المخزون.

  • الانتقالات الأخرى لا تمسّ المخزون.

المخزون لا يُتحقَّق منه: الخصم يتوقف عند الصفر، فلا يُرفض البيع الزائد أبدًا، وأي مشكلة في المخزون لا تُفشل تغيير الحالة ولا تتراجع عنه. ويتصرّف المخزون تمامًا كما يتصرّف مع الطلبات المُدارة من لوحة التحكم.

الأخطاء

الكود

السبب

bad_request "Field \"status\" is required"

الجسم لا يحوي status

bad_request "status must be one of: pending, confirmed, …"

سلسلة غير صالحة

bad_request "Transition X -> Y not allowed. From 'X' you can only go to: …"

غير مسموح بآلة الحالات (الواجهة تُصدر -> بأحرف ASCII عادية)

bad_request "order status changed concurrently; retry"

طلب آخر غيّر الحالة أثناء انتقالك. آمن لإعادة المحاولة بنفس مفتاح Idempotency.

not_found

معرّف الطلب غير معروف أو لمتجر آخر


POST /v1/orders/{id}/cancel

نقطة مريحة — تكافئ تمامًا PATCH /v1/orders/{id} مع {"status":"cancelled"}. نفس جدول الانتقالات، بلا أي صلاحية إضافية. الإلغاء مشروع فقط من pending وconfirmed وprocessing؛ ومن shipped أو delivered ستحصل على 400 bad_request (فتلك الحالتان لا تنتقلان إلا إلى delivered / returned).

المصادقة: مفتاح منصة بصلاحية orders:write. يتطلب Idempotency-Key.

curl -X POST 'https://api.dzbuild.app/v1/orders/6894/cancel' \
  -H "Authorization: Bearer $DZ_KEY" \
  -H "Idempotency-Key: cancel-6894-$(date +%s)"


المتغيرات — مرجع كامل

تجعل المتغيرات منتجًا واحدًا يغطي خيارات متعددة (لون، حجم، خامة، سعة…). على الواجهة يضغط العملاء بطاقات المتغيرات لاختيار ما يريدون؛ على الواجهة البرمجية ترسل أنت الخيارات المختارة كجزء من الطلب.

نموذج المتغيرات

للمنتج 0 أو أكثر من مجموعات المتغيرات. للمجموعة 0 أو أكثر من الخيارات. كل خيار يمكن أن يحمل:

  • value (الاسم المعروض)

  • color_code (لون hex، فقط لمجموعات اللون)

  • price_adjustment (يُضاف للسعر الأساسي)

  • image_id (صورة منتج مرتبطة كـ swatch)

اقرأ GET /v1/products/{id} لرؤية كل المجموعات والخيارات لمنتج:

{
  "variants": [
    {
      "id":   11,
      "name": "Color",
      "type": "color",
      "options": [
        { "id": 14, "value": "Red",  "color_code": "#ff0000", "image_id": 28, "stock": 12 },
        { "id": 15, "value": "Blue", "color_code": "#0000ff", "image_id": 29, "stock": 5  }
      ]
    },
    {
      "id":   12,
      "name": "Size",
      "type": "text",
      "options": [
        { "id": 16, "value": "S", "stock": 10 },
        { "id": 17, "value": "M", "stock": 10 },
        { "id": 18, "value": "L", "stock": 5  }
      ]
    }
  ]
}

منتج بـ 2 ألوان × 3 مقاسات لديه 6 تركيبات.

كيفية إرسال المتغيرات في POST /v1/orders

حوّل اختيار العميل إلى إدخال متغيّر واحد لكل مجموعة مختارة. لـ "تيشيرت أحمر مقاس L":

"variants": [
  { "group_name": "Color", "option_name": "Red", "color_code": "#ff0000", "price_adjustment": 0 },
  { "group_name": "Size",  "option_name": "L",   "color_code": null,      "price_adjustment": 0 }
]

الأسماء التي ترسلها تُحفظ كما هي على الطلب — يجب أن تطابق ما أعاده GET /v1/products/{id}. price_adjustment يُضاف لسعر السطر (فيكون final_price = product.price + Σ price_adjustment)، لكن لأي زوج موجود في الكتالوج يستبدل الخادم قيمته الخاصة، فأرسل رقم الكتالوج.

المخزون لكل متغيّر

إن فعّل التاجر variant_stock_enabled على منتج، يحمل كل خيار متغيّر عداد مخزونه. اقرأه من options[].stock. الواجهة لا تمنعك من إنشاء طلب بـ quantity > stock — هذا قرار التاجر. ويُخصم المخزون عند أول انتقال إلى حالة مُلتزِمة (confirmed، processing، shipped، delivered) ولا يُتحقَّق منه أبدًا: الخصم يتوقف عند الصفر، فيُسجَّل البيع الزائد بدل أن يُرفض.

المخزون لكل تركيبة

إن فُعّل combination_stock_enabled يُتتبع المخزون لكل تركيبة (Red+L = 5، Red+M = 8…). التركيبات لم تُكشف بعد على نقطة المنتجات العامة (ستُكشف في v1.1 كمصفوفة combinations[] على GET /v1/products/{id}/combinations). حاليًا يُطبَّق مخزون التركيبات عند تأكيد الطلب ويظهر في لوحة التحكم.

المتغيرات المتدرجة (Cascading)

تتيح إضافة Cascading Variants للتاجر جعل خيارات المجموعة B تعتمد على اختيار المجموعة A (مثل "علامة → موديل" — اختيار "Apple" للعلامة يُظهر فقط "iPhone 15" / "iPhone 14" للموديل). وهذه الإضافة تتطلب خطة Pro.

لا يكشف GET /v1/products/{id} أي معلومات عن التدرّج — فخريطة الأب/الابن لا تُطبَّق إلا في واجهة المتجر وفي لوحة التحكم. لا يستطيع عميلك اكتشاف قواعد التدرّج عبر الواجهة البرمجية في v1: ثبّتها في الكود أو اقرأها من لوحة التحكم. وإرسال تركيبة غير صالحة يُنشئ الطلب رغم ذلك (لا نمنعه) — لكن التاجر سيرفضه عند التأكيد.

متغيرات الصورة-نص

بعض التجار يستخدمون نوع image_text للمتغيرات (صورة مصغرة بجانب اسم الخيار). على الواجهة البرمجية ما زلت ترسل group_name + option_name — الصورة شأن واجهة فقط ولا تكون جزءًا من حمولة الطلب.

عروض القطع المتعددة

إن أطلق التاجر عرض "اشترِ 3، اختر ألوانًا"، يختار العميل متغيرًا مختلفًا لكل قطعة. على الواجهة البرمجية:

"items": [
  { "product_id": 26, "quantity": 1,
    "variants": [{ "group_name": "Color", "option_name": "Red" }] },
  { "product_id": 26, "quantity": 1,
    "variants": [{ "group_name": "Color", "option_name": "Blue" }] },
  { "product_id": 26, "quantity": 1,
    "variants": [{ "group_name": "Color", "option_name": "Green" }] }
]

ثلاثة سطور منفصلة، كل واحد quantity = 1. هكذا تعرض صفحة الطلب لون كل قطعة بنظافة.


أنماط شائعة

"إرسال طلب من واجهة React مخصصة"

تبني واجهة React/Vue/Next.js تتحدث مع الواجهة البرمجية بدلًا من ثيمات DZBuild المضمّنة. التدفّق:

  1. اقرأ GET /v1/products + GET /v1/products/{id} لعرض الكتالوج.

  2. يضيف المستخدم العناصر إلى سلة من جانب العميل.

  3. احسب shipping_cost من wilaya_id للعميل (يمكنك تثبيت الأسعار أو سؤال GET /v1/store لأسعار شحن المتجر — قادم في v1.1).

  4. POST /v1/orders بالسلة + كتلة العميل + سعر الشحن.

  5. أعرض على العميل order_number وصفحة "شكرًا".

  6. استطلع GET /v1/orders?since=… لمتابعة تقدّم التنفيذ. فأحداث order.confirmed / order.shipped في /v1/webhooks لا تُطلق إلا عندما تتغيّر الحالة عبر الواجهة البرمجية — وتأكيد التاجر من لوحة التحكم لا يُنتج أي حدث.

انظر دليل الثيمات والواجهات المخصصة لخطوات كاملة.

"مزامنة الطلبات الجديدة مع CRM كل دقيقة"

استخدم فلتر since:

curl 'https://api.dzbuild.app/v1/orders?since=2026-04-30T20:00:00Z&limit=200' \
  -H "Authorization: Bearer $DZ_KEY"

الاستطلاع هو الأداة الصحيحة هنا. فاشتراك /v1/webhooks على order.created لا يغطي إلا الطلبات التي أنشأها تكاملك أنت عبر الواجهة البرمجية — أما طلبات واجهة المتجر وصفحات الهبوط فلا تُطلقه أبدًا، ولذلك لا يمكنه أن يحلّ محل مسح since. انظر Webhooks.

"تأكيد كل الطلبات المعلّقة لعميل واحد"

PHONE="0555000000"
curl -sS "https://api.dzbuild.app/v1/orders?status=pending&customer_phone=$PHONE" \
  -H "Authorization: Bearer $DZ_KEY" \
| jq -r '.data.items[].id' \
| while read OID; do
    curl -sS -X PATCH "https://api.dzbuild.app/v1/orders/$OID" \
      -H "Authorization: Bearer $DZ_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: confirm-$OID" \
      -d '{"status":"confirmed"}'
  done
هل أجاب هذا عن سؤالك؟