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

الطلبات التي تنشئها التطبيقات

إنشاء طلبات بالأسعار التي وافق عليها التاجر، والبنود المخصصة، والملاحظة والوسوم وخصائص التطبيق، وملف المتجر، والبحث في المنتجات.

آخر تحديث 10 أيلول 2026قراءة 8 د
في هذه الصفحة

هذه الصفحة لمن يحوّل تطبيقه عرض سعر أو فاتورة أو محادثة إلى طلب حقيقي: أدوات عروض الأسعار، وطلبات الجملة، ومساعدو المبيعات. في نهايتها تعرف كيف تنشئ طلباً بالأسعار التي اتفقت عليها، وما تفعله المنصّة به، وما يراه التاجر، وكيف تبقي الطلب مرتبطاً بسجلّك أنت بعد ذلك. المراجعة 2026-09-10 من واجهة المنصّة v1، وكلها إضافية.

ما يفتحه هذا#

  • POST /orders ينشئ طلباً بأسعار الوحدة التي ترسلها، لخيارات من الكتالوج ولبنود مخصصة لا وجود لها في الكتالوج أصلاً. تعيد المنصّة احتساب المجاميع، وتخصم المخزون، وتربط العميل، وتنبّه التاجر، وتطلق order.created بقيمة source: "app"، تماماً كطلب من المتجر.
  • PATCH /orders/{id} يحدّث ملاحظة الطلب، ويضيف وسوماً أو يزيلها، ويدمج خصائص تعود لتطبيقك وحده. الخاصية reference تصير شريحة في صفحة الطلب عند التاجر.
  • GET /store يعطيك عملات المتجر، وجدول الصرف الذي تطبّقه صفحة الدفع، وطرق الدفع المفعّلة، وقائمة المحافظات، من دون أي نطاق.
  • GET /products?q= يبحث في الكتالوج وGET /products/variants?ids= يعيد التحقق من الخيارات خلف بنودك قبل أن تلتزم.

النطاقات#

النطاق الجملة التي يراها التاجر ما يفتحه
orders:create إنشاء طلبات بأسعار يحددها التطبيق POST /orders
orders:write تحديث حالة الطلب وملاحظته ووسومه PATCH /orders/{id}، PATCH /orders/{id}/status
products:read قراءة المنتجات وخياراتها GET /products، GET /products/variants

orders:create منفصل عن orders:write عن قصد: التاجر الذي وافق على تحديث الحالة والملاحظة والوسوم لا يكتسب إنشاء الطلبات بصمت أبداً. ولا يتضمّن قراءة الطلبات أيضاً؛ جسم 201 هو القراءة الوحيدة التي يمنحها، فاطلب orders:read كذلك إن كنت ستعرض الطلبات أو تجلبها لاحقاً. تعرض شاشة الموافقة تحت النطاق العبارة «تظهر هذه الطلبات في لوحة التحكم موسومة باسم التطبيق، ويُخصم المخزون كأي طلب آخر.» الجدول الكامل في مرجع النطاقات.

أنشئ طلباً#

يتطلب orders:create وIdempotency-Key. اشتقّ المفتاح من سجلّك أنت، معرّف عرض السعر أو الفاتورة، فتعيد المهمة المكررة الطلب المسجّل بدلاً من إنشاء طلب ثانٍ.

Shell
curl -X POST https://dukkan.one/platform-api/v1/orders \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Idempotency-Key: quote-Q-1042-accepted" \
  -H "Content-Type: application/json" \
  -d @order.json
order-create-request.jsonJSON
{
  "currency": "SYP",
  "status": "placed",
  "items": [
    {
      "variant_id": "c0ffee00-0001-4e8f-9b70-2d1c3e4f5a6b",
      "quantity": 2,
      "unit_price_minor": 225000
    },
    {
      "variant_id": null,
      "name": "تغليف هدية مع بطاقة إهداء",
      "quantity": 1,
      "unit_price_minor": 15000
    }
  ],
  "discount": {
    "amount_minor": 15000,
    "title": "خصم عرض السعر Q-1042"
  },
  "shipping": {
    "amount_minor": 25000,
    "method": "delivery"
  },
  "customer": {
    "name": "ليان الحلبي",
    "phone": "+963944123456",
    "email": null
  },
  "shipping_address": {
    "address": "شارع بغداد، بناء 22، الطابق الثالث",
    "province": "damascus",
    "city": "دمشق",
    "notes": null
  },
  "payment_method": "cod",
  "exchange_rate": null,
  "note": "عرض السعر Q-1042 مقبول عبر واتساب",
  "tags": [
    "عرض سعر",
    "جملة"
  ],
  "attributes": {
    "reference": {
      "kind": "quote",
      "number": "Q-1042",
      "url": "https://offers.example.com/quotes/Q-1042"
    },
    "quote_id": "q_8f3a"
  },
  "inventory": "decrement"
}

استجابة 201 هي الطلب كاملاً، مع kind على كل بند، وsource: "app"، وملاحظتك ووسومك وخصائصك، وبيانات العميل التي أرسلتها. تُعاد هذه البيانات بصرف النظر عن clients:read، لأنك أنت من أرسلها.

order-create-response.jsonJSON
{
  "data": {
    "id": "6f1d2c3b-4a59-4e8f-9b70-2d1c3e4f5a6c",
    "order_number": "1043",
    "status": "placed",
    "payment_status": "unpaid",
    "source": "app",
    "line_pricing": "gross",
    "subtotal": {
      "amount_minor": 465000,
      "currency": "SYP",
      "decimals": 0
    },
    "discount": {
      "amount_minor": 15000,
      "currency": "SYP",
      "decimals": 0
    },
    "shipping": {
      "amount_minor": 25000,
      "currency": "SYP",
      "decimals": 0
    },
    "tax": {
      "amount_minor": 0,
      "currency": "SYP",
      "decimals": 0
    },
    "total": {
      "amount_minor": 475000,
      "currency": "SYP",
      "decimals": 0
    },
    "paid": {
      "amount_minor": 0,
      "currency": "SYP",
      "decimals": 0
    },
    "refunded": {
      "amount_minor": 0,
      "currency": "SYP",
      "decimals": 0
    },
    "exchange_rate": null,
    "tags": [
      "عرض سعر",
      "جملة"
    ],
    "created_at": "2026-09-10T09:30:00.412Z",
    "updated_at": "2026-09-10T09:30:00.412Z",
    "items": [
      {
        "id": "a1b2c3d4-0002-4e8f-9b70-2d1c3e4f5a6b",
        "variant_id": "c0ffee00-0001-4e8f-9b70-2d1c3e4f5a6b",
        "kind": "variant",
        "name": "عطر العود الملكي — 50 مل",
        "quantity": 2,
        "unit_price": {
          "amount_minor": 225000,
          "currency": "SYP",
          "decimals": 0
        },
        "total": {
          "amount_minor": 450000,
          "currency": "SYP",
          "decimals": 0
        }
      },
      {
        "id": "a1b2c3d4-0003-4e8f-9b70-2d1c3e4f5a6b",
        "variant_id": null,
        "kind": "custom",
        "name": "تغليف هدية مع بطاقة إهداء",
        "quantity": 1,
        "unit_price": {
          "amount_minor": 15000,
          "currency": "SYP",
          "decimals": 0
        },
        "total": {
          "amount_minor": 15000,
          "currency": "SYP",
          "decimals": 0
        }
      }
    ],
    "fulfillments": [],
    "refunds": [],
    "note": "عرض السعر Q-1042 مقبول عبر واتساب",
    "attributes": {
      "reference": {
        "kind": "quote",
        "number": "Q-1042",
        "url": "https://offers.example.com/quotes/Q-1042"
      },
      "quote_id": "q_8f3a"
    },
    "customer": {
      "first_name": "ليان",
      "last_name": "الحلبي",
      "email": null,
      "phone": "+963944123456"
    },
    "shipping_address": {
      "address": "شارع بغداد، بناء 22، الطابق الثالث",
      "province": "damascus",
      "city": "دمشق",
      "notes": null
    }
  }
}

الجسم#

الحقل المعنى
currency إحدى عملات المتجر المسموح بها (GET /store، الحقل storefront_currencies.allowed). وإلا 422 برمز currency_not_allowed مع details.allowed.
status placed (الافتراضي) يترك للتاجر مراجعة الطلب في لوحة التحكم؛ وapproved يسجّل قبولاً صريحاً أعطاه العميل لك مسبقاً. كلاهما يتبع دورة الحياة العادية بعد ذلك.
items[] من 1 إلى 200 بند. variant_id يسمّي خياراً من الكتالوج؛ وnull يجعل البند مخصصاً وعندها يلزم name. الكمية quantity من 1 إلى 100,000، وunit_price_minor سعر الوحدة المتفق عليه بالوحدات الصغرى لعملة currency؛ والصفر مسموح.
discount مبلغ على مستوى الطلب مع عنوان اختياري، لا يتجاوز مجموع البنود.
shipping المبلغ وmethod: إما delivery أو pickup.
customer الاسم، ورقم الهاتف الدولي الكامل (يُخزَّن منسّقاً)، والبريد اختياري. لا تخترع المنصّة بريداً أبداً حين تتركه null.
shipping_address مطلوب مع delivery. الحقل province إحدى القيم التي يعرضها GET /store تحت provinces.
payment_method إما cod أو shamcash_manual؛ يجب أن يكون مفعّلاً في المتجر، وإلا 409 برمز payment_method_disabled.
exchange_rate مطلوب عندما تختلف currency عن عملة التسعير، وممنوع في غير ذلك.
note وtags وattributes الحقول نفسها التي يحرّرها PATCH /orders/{id}، راجع أدناه.
inventory إما decrement (الافتراضي) أو skip، راجع المخزون والحالة.

المجاميع والعملة#

كل حقل مالي عدد صحيح بالوحدات الصغرى لعملة الطلب، مقيساً بالأس العشري الذي تعتمده دكان لتلك العملة (SYP صفر، USD اثنان، JOD ثلاثة). ترسل أسعار الوحدة ومبلغَي الخصم والشحن؛ وتعيد المنصّة احتساب كل ما عداها:

  • المجموع الفرعي = Σ quantity × unit_price_minor
  • الإجمالي = المجموع الفرعي − الخصم + الشحن
  • الضريبة = 0

حين لا يكون الطلب بعملة تسعير المتجر، أرسل السعر الذي طبّقته بالشكل { "scaled", "scale" } حيث السعر = scaled ÷ 10^scale، ووحدة واحدة من عملة التسعير = السعر من وحدات عملة الطلب. خذه من fx.rates في GET /store؛ فذلك الجدول يتضمّن تعديلات التاجر أصلاً وهو بالضبط ما تطبّقه صفحة الدفع. قرّب كل سعر وحدة بعد التحويل، لا الإجمالي، لتطابق أرقامك ما تكلّفه السلة نفسها في المتجر. تفعل الحزمة ذلك عنك بـ exchangeRateFor وbuildCreateOrderLines؛ راجع صفحة الحزمة. قواعد المال عموماً في المبالغ بالوحدات الصغرى.

المخزون والحالة#

  • inventory: "decrement" يخصم المخزون لكل بند من الكتالوج وفق سياسة المخزون في المتجر. عندما يكون بند ناقصاً يفشل الطلب كله بـ 409 ولا يُكتب شيء. أما inventory: "skip" فلا يكتب أي حركة، للطلبات التي لم تكن بضاعتها في مخزون هذا المتجر أصلاً.
  • البنود المخصصة (kind: "custom") لا رابط لها بالكتالوج: لا تمسّ المخزون أبداً، والاسترداد عليها بالمبلغ فقط، ولا يكون مع restock. راجع حركات المخزون.
  • status: "placed" هو الافتراضي الصحيح عندما ينبغي أن يؤكّد التاجر قبل أن يُشحن شيء. استخدم approved فقط حين جمع تطبيقك قبولاً صريحاً من العميل؛ تعرض لوحة التحكم «الحالة عند الإنشاء: مقبول» في سجل الطلب ليعرف التاجر سبب تجاوز خطوة المراجعة.

الأخطاء#

الحالة الرمز المعنى
422 invalid_request فشل حقل في التحقق؛ يسرد details.fields المسارات، مثل items.1.name أو shipping_address.
422 currency_not_allowed currency ليست من العملات التي يبيع بها المتجر؛ يسرد details.allowed الرموز المقبولة.
404 variant_not_found variant_id غير موجود في هذا المتجر؛ يسمّيه details.variant_id.
409 variant_unavailable الخيار موقوف؛ يسمّيه details.variant_id.
409 payment_method_disabled طريقة الدفع المختارة معطّلة في هذا المتجر.
409 insufficient_stock بند أو أكثر من الكتالوج يتجاوز الكمية المتاحة؛ يسمّي details.lines[] كل بند ناقص بـ variant_id، ومعه requested وavailable عند قياس النقص مسبقاً. أما إذا سبقك طلب آخر إلى قفل الصف، فيعود variant_id وحده. لم يُكتب شيء.
403 forbidden التثبيت بلا orders:create فعلي، أو المتجر متجر عرض في السوق.
429 rate_limited دلو الكتابة المشترك، أو الحدود اليومية أدناه. احترم Retry-After.
order-insufficient-stock.jsonJSON
{
  "error": {
    "code": "insufficient_stock",
    "message": "Not enough stock for one or more lines",
    "request_id": "7d0c2b1a-5e4f-4a3b-8c9d-0e1f2a3b4c5e",
    "details": {
      "lines": [
        {
          "variant_id": "c0ffee00-0001-4e8f-9b70-2d1c3e4f5a6b",
          "requested": 2,
          "available": 1
        }
      ]
    }
  }
}

اقرأ قائمة lines كاملة، وأعد التسعير أو قسّم الطلب، ثم أرسل طلباً جديداً بمفتاح جديد. لا يسجّل 409 مفتاح التكرار أبداً، فالمفتاح نفسه متاح لإعادة الاستخدام متى زال السبب.

ما يراه التاجر#

  • قائمة الطلبات: يحمل الطلب شريحة «من تطبيق»، ويمكن للتاجر التصفية حسب النوع «تطبيق» وحسب الوسم.
  • صفحة الطلب: بطاقة «مرجع التطبيق» بشريحة من خاصية reference (مثل «عرض سعر Q-1042») تفتح الرابط الذي أعطيته؛ وبطاقة «ملاحظة الطلب»؛ و«بند مخصص» على كل بند مخصص؛ وسجل «سجل الطلب» أول صفوفه «أنشأ تطبيق «اسم تطبيقك» هذا الطلب» بالعبارة «الأسعار كما حددها التطبيق · الحالة عند الإنشاء: قيد المراجعة».
  • التحرير: الأسعار والخصم والشحن في الطلب الذي أنشأه تطبيق مقفلة في لوحة التحكم («يديرها التطبيق ولا يمكن تعديلها هنا»)؛ والكميات تبقى قابلة للتعديل.
  • الإشعارات: يتلقّى التاجر الدفعة والبريد نفسيهما كما في طلب من المتجر، وتستلم التطبيقات المشترِكة order.created بقيمة source: "app".
  • إن أُزيل تطبيقك لاحقاً يحتفظ الطلب بكل شيء؛ وتقول بطاقة المرجع «تمت إزالة التطبيق، البيانات محفوظة».

حدّث الملاحظة والوسوم والخصائص#

يتطلب orders:write وIdempotency-Key. المال والبنود والعميل والعنوان ثابتة بعد الإنشاء؛ تحرّر هذه النقطة الحقول الثلاثة التي تحيط بها.

Shell
curl -X PATCH https://dukkan.one/platform-api/v1/orders/ORDER_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Idempotency-Key: invoice-INV-2207-linked" \
  -H "Content-Type: application/json" \
  -d @update.json
order-update-request.jsonJSON
{
  "note": "تم تأكيد الدفع عند الاستلام هاتفياً",
  "tags": {
    "add": [
      "مؤكد"
    ],
    "remove": [
      "جملة"
    ]
  },
  "attributes": {
    "reference": {
      "kind": "invoice",
      "number": "INV-2207",
      "url": "https://offers.example.com/invoices/INV-2207"
    },
    "quote_id": null
  }
}
  • note يستبدل ملاحظة الطلب؛ وnull يمسحها. حتى 2,000 حرف.
  • tags زوج من عمليات المجموعات، وليس استبدالاً أبداً، فتبقى وسوم التاجر ووسوم التطبيقات الأخرى. الوسم من 1 إلى 40 حرفاً من حروف Unicode والأرقام والمسافات و_ و-؛ تنسّقه المنصّة (NFC مع إزالة الفراغات) وتزيل التكرار دون تمييز حالة الأحرف؛ ويحمل الطلب 10 وسوم على الأكثر. تطابق remove دون تمييز حالة الأحرف. ويصفّي GET /orders?tag= حسب وسم واحد.
  • attributes تُدمج في مساحة تطبيقك الخاصة. لا ترى التطبيقات الأخرى مفاتيحك أبداً ولا ترى أنت مفاتيحها؛ ويعيد GET /orders/{id} مفاتيحك وحدها. تطابق المفاتيح [a-z0-9_.-]{1,64}، بحد 20 لكل تطبيق، والقيم المفردة حتى 1,000 بايت UTF-8، و8 KB إجمالاً. الحد بالبايت، لذا تصل القيمة العربية إليه عند نحو 500 حرف. في PATCH تحذف null المفتاح.

الاستجابة هي الطلب المحدّث، بشكل 200 نفسه الذي يعيده GET /orders/{id}.

الخاصية reference#

لخاصية واحدة شكل ثابت ومكان ثابت في لوحة التحكم: reference تربط الطلب بالسجل الذي جاء منه في تطبيقك.

الحقل المعنى
kind نوع السجل في تطبيقك، حتى 40 حرفاً، مثل quote أو invoice.
number الرقم المقروء، حتى 80 حرفاً، مثل Q-1042. يظهر على الشريحة.
url رابط عميق إلى تطبيقك، أو null. يجب أن يكون https على أصل تطبيقك المسجّل (أصل أحد عناوين إعادة التوجيه في إصدار التطبيق)؛ وأي شيء آخر يُرفض بـ 422. يفتحه التاجر في تبويب جديد.

أرسلها عند الإنشاء أو أضفها لاحقاً؛ واستبدالها بكائن جديد يبدّل الشريحة.

ملف المتجر#

لا يحتاج إلى نطاق؛ كل ما فيه علني في المتجر أصلاً.

Shell
curl https://dukkan.one/platform-api/v1/store \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
store.jsonJSON
{
  "data": {
    "id": "0d9e8f7a-6b5c-4d3e-2f1a-0b9c8d7e6f5a",
    "slug": "sham-perfumes",
    "name": "عطور الشام",
    "description": "عطور شرقية أصيلة من دمشق",
    "logo_url": "https://cdn.dukkan.one/stores/sham-perfumes/logo.png",
    "banner_urls": [],
    "storefront_url": "https://shamperfumes.com",
    "whatsapp_number": "+963944000000",
    "phone": null,
    "address": "دمشق، الحميدية",
    "accent_color": "#7A1F2B",
    "locale": "ar",
    "timezone": "Asia/Damascus",
    "country": "SY",
    "pricing_currency": {
      "code": "SYP",
      "decimals": 0
    },
    "storefront_currencies": {
      "allowed": [
        "SYP",
        "USD"
      ],
      "default": "SYP"
    },
    "fx": {
      "base": "SYP",
      "as_of": "2026-09-10T06:00:00Z",
      "rates": {
        "SYP": {
          "scaled": 100000000,
          "scale": 8
        },
        "USD": {
          "scaled": 7700,
          "scale": 8
        }
      }
    },
    "payment_methods": [
      {
        "id": "cod"
      },
      {
        "id": "shamcash_manual",
        "shamcash_id": "0944000000"
      }
    ],
    "provinces": [
      {
        "value": "damascus",
        "ar": "دمشق",
        "en": "Damascus"
      },
      {
        "value": "aleppo",
        "ar": "حلب",
        "en": "Aleppo"
      }
    ]
  }
}
  • pricing_currency هي العملة التي تُخزَّن بها أسعار الكتالوج، مع أسّها العشري. وstorefront_currencies.allowed هي العملات التي يقبلها POST /orders.
  • fx جدول الأسعار المعدّل من التاجر بدءاً من عملة التسعير، وnull حين لا جدول للمتجر. كل سعر هو scaled ÷ 10^scale، والعملة الأساس مضمّنة بسعر 1، وas_of يخبرك بحداثته.
  • payment_methods يسرد الطرق اليدوية المفعّلة؛ ويحمل shamcash_manual المعرّف shamcash_id الذي يظهر عند الدفع لتعرض أنت المعرّف نفسه.
  • provinces هي قيم shipping_address.province المقبولة مع تسميات عربية وإنجليزية.
  • storefront_url هو الأصل العلني؛ والنطاق المخصص الموصول يتقدّم على النطاق الفرعي للمنصّة.

خزّنه مؤقتاً لكل تثبيت بضع دقائق؛ فالأسعار تتحرّك بضع مرات في اليوم على الأكثر.

ابحث في المنتجات والخيارات#

كلاهما يحتاج إلى products:read.

Shell
curl "https://dukkan.one/platform-api/v1/products?q=oud&status=active" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
curl "https://dukkan.one/platform-api/v1/products/variants?ids=c0ffee00-0001-4e8f-9b70-2d1c3e4f5a6b" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
variants-response.jsonJSON
{
  "data": [
    {
      "id": "c0ffee00-0001-4e8f-9b70-2d1c3e4f5a6b",
      "product_id": "b0b0b0b0-0001-4e8f-9b70-2d1c3e4f5a6b",
      "sku": "OUD-50",
      "price": {
        "amount_minor": 250000,
        "currency": "SYP",
        "decimals": 0
      },
      "status": "active",
      "name": "50 مل",
      "attributes": {
        "الحجم": "50 مل"
      },
      "barcode": null,
      "compare_at_price": null,
      "image_url": "https://cdn.dukkan.one/stores/sham-perfumes/products/oud-50.jpg",
      "weight_gram": 180,
      "available_quantity": 12,
      "product_name": "عطر العود الملكي",
      "product_status": "active"
    }
  ]
}
  • GET /products?q= يطابق من 2 إلى 80 حرفاً، دون تمييز حالة الأحرف، مع اسم المنتج واسم الخيار وSKU وDSIN والباركود، بالعربية أو اللاتينية، ويرتّب النتيجة. يجيب البحث بصفحة مرتّبة واحدة من دون مؤشر؛ فضيّق الاستعلام بدلاً من التصفّح. يصفّي status وids (حتى 100، مفصولة بفواصل)، ويُجمعان مع updated_at_min للمزامنة.
  • GET /products/variants?ids= يأخذ حتى 100 معرّف ويعيد الخيارات الموجودة، لكل منها product_name وproduct_status وprice الحالي وcompare_at_price وimage_url. تُحذف المعرّفات غير المعروفة ولا تكون خطأ أبداً، فقارن المعرّفات المُعادة بما طلبته. ويظهر available_quantity فقط حين يحمل التثبيت inventory:read أيضاً.

أعد التحقق من الخيارات خلف عرض السعر قبل POST /orders مباشرة: الخيار الذي أوقف منذ العرض يجيب بـ 409 عند الإنشاء، وتغيّر السعر أمر قد تودّ عرضه على العميل أولاً.

التكرار الآمن والحدود#

  • تحمل كل كتابة Idempotency-Key. حين تعيد المنصّة استجابة مسجّلة تقول ذلك بالترويسة Idempotency-Replayed: true؛ وتظهرها الحزمة بالحقل replayed.
  • تتشارك الكتابات دلو التثبيت بحد 60 في الدقيقة. ويُحتسب إنشاء الطلبات إضافةً إلى ذلك ضمن 500 طلب لكل تثبيت يومياً و2,000 لكل متجر يومياً. كلاهما يجيب بـ 429 مع Retry-After.
  • ترفض متاجر العرض في السوق إنشاء الطلبات بـ 403.

الخطوات التالية#