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

واجهة التكامل البرمجية

تتيح هذه الواجهة لـموصّل متجر إلكتروني قراءة كتالوج Nadigit IMS ومخزونه، وحجز مخزون لسلة شراء، وتسجيل الطلبات. وفي المقابل، يُبلغ Nadigit IMS الموصّل عبر webhooks موقّعة. هذه هي الواجهة التي يستخدمها موصّل Bagisto، وهي التي يُبنى عليها موصّل لأي منصّة أخرى.

لا توثّق هذه الصفحة إلا واجهة التكامل: الواجهات البرمجية الأخرى في Nadigit IMS مخصّصة للواجهة الويب.

قبل البدء​

حقل الإعداداتالاستعمال
backendUrlالعنوان الأساسي للواجهة: تُضاف إليه المسارات أدناه
keycloakTokenUrlعنوان الحصول على الرموز
keycloakClientId، keycloakClientSecretبيانات اعتماد الموصّل
webhookSecretسرّ التحقق من الـ webhooks

المصادقة​

يحصل الموصّل على رمز وصول عبر تدفّق OAuth 2.0 ‏client credentials، ثم يرسله في ترويسة Authorization مع كل طلب.

curl -s -X POST "$KEYCLOAK_TOKEN_URL" \
-d grant_type=client_credentials \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET"
GET /api/ecommerce/catalog HTTP/1.1
Authorization: Bearer <access_token>
  • الرمز قصير المدة (expires_in بالثواني): احتفظ به وجدّده قبيل انتهاء صلاحيته، بدل طلب رمز جديد مع كل استدعاء.
  • النطاق. لا يصل الموصّل إلا إلى واجهة التكامل، ولا يخصّ إلا المستودع والمتجر المرتبطين بالتكامل. المستودع ليس أبدًا معاملًا في الطلب: تحدّده هوية الموصّل.

الاصطلاحات​

  • التبادل بصيغة JSON وترميز UTF-8.
  • SKU هو مرجع الصنف في Nadigit IMS، وهو فريد داخل المستودع.
  • تُعبَّر الكميات بـوحدة البيع الخاصة بالصنف، مع كسور عشرية للأصناف التي تُباع بالوزن أو القياس (fractional).
  • المبالغ بعملة المنظمة.

نقاط الوصول​

الطريقةالمسارالدور
GET/api/ecommerce/catalogالكتالوج القابل للنشر، مقسّم إلى صفحات
GET/api/ecommerce/catalog/{sku}صنف واحد
GET/api/ecommerce/stock?skus=…مخزون 200 صنف كحدّ أقصى
PUT/api/ecommerce/reservations/{cartId}حجز مخزون سلة
POST/api/ecommerce/ordersتسجيل طلب
POST/api/ecommerce/orders/{reference}/cancelإلغاء طلب

الكتالوج​

GET /api/ecommerce/catalog?page=0&size=100
GET /api/ecommerce/catalog/ACC-POIG-BL

يضمّ الكتالوج الأصناف النشطة والمادية في المستودع المرتبط، وتُستثنى الخدمات. القائمة مقسّمة إلى صفحات (page من 0، وsize من 1 إلى 200، و100 افتراضيًا) ومرتّبة حسب SKU. ويعيد SKU غير معروف أو غير نشط أو من مستودع آخر الرمز 404.

{
"sku": "ACC-POIG-BL",
"name": "Poignée de fenêtre à crémone – blanc",
"description": "…",
"category": "Accessoires",
"categoryPath": ["Accessoires"],
"price": 45.0,
"taxRate": 20.0,
"taxClass": "…",
"taxInclusive": false,
"allocatable": 146.0,
"measureUnit": "Unité",
"fractional": false,
"quantityPrecision": 0,
"primaryImageUrl": "https://…",
"imageUrls": ["https://…"],
"barcodes": ["…"]
}

تعيد القائمة هذه الكائنات داخل صفحة (content، totalElements، totalPages، number، size). لا يوفّر الكتالوج بعدُ مرشّح «معدَّل منذ»: زامنه كاملًا ثم اعتمد على الـ webhooks للتغييرات.

المخزون​

GET /api/ecommerce/stock?skus=ACC-POIG-BL,SIL-NEU-280,UNKNOWN
[
{ "sku": "ACC-POIG-BL", "found": true, "sellable": 148.0, "reservedByOthers": 2.0, "allocatable": 146.0 },
{ "sku": "UNKNOWN", "found": false, "sellable": null, "reservedByOthers": null, "allocatable": null }
]
الحقلالمعنى
sellableالمخزون القابل للبيع في المستودع
reservedByOthersالكمية المحجوزة لسلال أخرى أو مبيعات جارية
allocatableما يمكن بيعه بعد: الكمية التي تُعرض
foundfalse لـ SKU غير معروف: اسحبه من البيع

حتى 200 SKU في كل استدعاء، مفصولة بفواصل.

الحجوزات​

يمنع حجز مخزون السلة بيع صنف نفد فعلًا عبر الإنترنت. استدعِ نقطة الوصول هذه عند كل تعديل للسلة وقبيل الدفع.

PUT /api/ecommerce/reservations/cart-8841
Content-Type: application/json

{ "lines": [ { "sku": "ACC-POIG-BL", "quantity": 2 } ] }
  • 204: يُستبدل حجز السلة بهذه الأسطر.
  • 409: يجب منع الدفع.
{
"code": "insufficient_stock",
"message": "…",
"lines": [ { "sku": "ACC-POIG-BL", "requested": 2, "allocatable": 1 } ]
}

قيمة code هي insufficient_stock (مخزون غير كافٍ، سطرًا بسطر) أو reservations_disabled (الحجوزات المرنة معطّلة في Nadigit IMS). ينتهي الحجز تلقائيًا: السلة المتروكة تحرّر مخزونها دون أي تدخل من الموصّل.

الطلبات​

POST /api/ecommerce/orders
Content-Type: application/json

{
"reference": "WEB-1000231",
"cartId": "cart-8841",
"orderDate": "2026-09-24T18:40:00",
"customer": { "email": "customer@example.ma", "firstName": "Salma", "lastName": "Idrissi", "phone": "+212600000000" },
"lines": [ { "sku": "ACC-POIG-BL", "quantity": 2, "unitPrice": 45.0 } ],
"declaredTotal": 108.0,
"paymentMethod": "card",
"paymentReference": "ch_3Q…"
}
الحقلإلزاميملاحظة
referenceنعممرجع الطلب في المتجر، 64 حرفًا كحدّ أقصى. يُستخدم مفتاحًا للتفرّد
cartIdلاالسلة التي يُستهلك حجزها
customerلايُعثر على العميل ببريده، أو يُنشأ
linesنعمSKU والكميات. unitPrice استرشادي
shipping، declaredTotalلااسترشاديان، يُقارنان بالإجمالي المُعاد حسابه
paymentMethodلاانظر أدناه
paymentReferenceلامرجع المعاملة، يُحفظ مع الدفعة

السلوك:

  • يعيد Nadigit IMS حساب الأسعار والضرائب. إذا اختلف declaredTotal، يُقبل الطلب وتصبح قيمة priceMismatch هي true.
  • يُخصم الطلب من مخزون المستودع ويُربط بالمتجر المرتبط.
  • عدم التكرار: إعادة إرسال reference نفسه تعيد الطلب الموجود (alreadyExisted: true) دون إنشاء نسخة ثانية. يمكن للموصّل إعادة إرسال طلب بأمان.
  • paymentMethod: القيم card وwallet وstripe وpaypal وonline وtransfer وcash تسجّل دفعة، فيصبح الطلب مدفوعًا. أما cod (الدفع عند التسليم) أو قيمة فارغة أو غير معروفة فلا تسجّل شيئًا، ويبقى الطلب غير مدفوع.
{
"reference": "WEB-1000231",
"status": "Processing",
"paymentStatus": "PAID",
"totalAmount": 108.0,
"dutyFreeAmount": 90.0,
"taxAmount": 18.0,
"alreadyExisted": false,
"priceMismatch": false,
"declaredTotal": 108.0
}

الإلغاء​

POST /api/ecommerce/orders/WEB-1000231/cancel

يلغي الطلب ويعيد مخزونه إلى المستودع. الطلب الملغى مسبقًا يعيد نجاحًا مع alreadyExisted: true، والمرجع غير المعروف في المستودع يعيد 404. الاسترداد، إن وُجد، ليس تلقائيًا.

الـ Webhooks​

يستدعي Nadigit IMS الموصّل على رابط المتجر + مسار الـ webhook المسجّلين في التكامل، بطريقة POST، بعد كل تغيير مُسجَّل.

الأحداث​

product.updated: تغيّر صنف (المخزون، السعر، الحالة).

{ "event": "product.updated", "action": "upsert", "sku": "ACC-POIG-BL", "occurredAt": "2026-09-24T18:41:07Z" }
  • action: "upsert": أعد قراءة الصنف عبر GET /api/ecommerce/catalog/{sku} وطبّقه.
  • action: "unpublish": اسحب الصنف من البيع (معطّل، نافد، أو لم يعد في المستودع).

order.status: تغيّرت حالة طلب ويب.

{ "event": "order.status", "reference": "WEB-1000231", "status": "Delivered", "paymentStatus": "PAID", "occurredAt": "2026-09-25T10:02:13Z" }
statusالمعنى
Processingقيد التحضير (الحالة عند التسجيل)
Delivered، Completedمُسلَّم، مكتمل
Canceledملغى
Return_Pending، Returned، Partial_Returnإرجاع قيد المعالجة، مُرجَع، مُرجَع جزئيًا

قيمة paymentStatus هي UNPAID أو PARTIALLY_PAID أو PAID.

التحقق من التوقيع​

يحمل كل webhook ترويستين:

  • X-Timestamp: وقت الإرسال بالثواني منذ 1 يناير 1970؛
  • X-Signature: ‏HMAC-SHA256(webhookSecret, X-Timestamp + "." + body) بصيغة ست عشرية بأحرف صغيرة.

أعد حساب التوقيع على النص الخام المستلم، وقارنه في زمن ثابت، وارفض أي طابع زمني قديم جدًا (أكثر من خمس دقائق مثلًا).

$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $ts . '.' . $body, $webhookSecret);
if (!hash_equals($expected, $sig) || abs(time() - (int) $ts) > 300) {
http_response_code(401);
exit;
}
const crypto = require('crypto');
function isValid(rawBody, ts, sig, secret) {
const expected = crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
return sig.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
&& Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
}

الرد وإعادة المحاولة​

أجب بـ 2xx بسرعة، ثم عالج الـ webhook. عند الفشل أو غياب الرد، يعيد Nadigit IMS المحاولة عدة مرات مع مباعدة المحاولات. تُسجَّل كل محاولة لدى Nadigit IMS. عالج الـ webhooks بطريقة لا تتأثر بالتكرار: قد يصل الحدث نفسه مرتين.

رموز الأخطاء​

الرمزالسبب
400طلب غير صالح (حقل ناقص أو غير صحيح)؛ يفصّل النص الحقول
401رمز غائب أو منتهي الصلاحية أو غير صالح: جدّد الرمز
403وصول مرفوض، أو وظيفة غير مشمولة في باقة التثبيت
404SKU أو طلب غير معروف في المستودع المرتبط
409مخزون غير كافٍ أو حجوزات معطّلة (انظر الحجوزات)
503تثبيت دون ترخيص نشط

تعيد الأخطاء غير 409 نصًا على هذا الشكل:

{ "timestamp": "…", "status": 403, "error": "Forbidden", "message": "…", "path": "/api/ecommerce/catalog", "errorCode": "…" }

انظر أيضًا​