DZBuild POS
DZBuild POS هو برنامج صندوق مجاني على ويندوز للمحلات التي تبيع أيضاً عبر الإنترنت. يعمل دون اتصال، وبعد ربطه يبقى متزامناً مع متجر DZBuild واحد أو أكثر. تذكر هذه الصفحة النداءات التي يرسلها الصندوق المرتبط، ليعرف فريق الدعم والشركاء ما يستطيع الصندوق فعله وما لا يستطيعه.
لا تجيب هذه النقاط إلا الرموز الصادرة للصندوق. مفتاح API الشخصي أو رمز التطبيق يتلقى 403 على المسارات الخاصة بالصندوق، ورمز الصندوق يتلقى 403 على كل مسار خارج قائمته.
لمن هو
- لأصحاب المتاجر الذين يبيعون في محل وفي متجرهم على DZBuild. صاحب المتجر وحده يستطيع ربط صندوق؛ أعضاء الفريق لا يستطيعون.
- لكل الخطط. الصندوق ليس مفتاح API، فلا يحتاج خطة Enterprise ولا يأخذ مكان مفتاح.
- بعد تسجيل أول صندوق تظهر إضافة DZBuild POS في لوحة التحكم مع الصناديق المرتبطة وزر الفصل وآخر المبيعات والمرتجعات وإغلاقات Z. الرابط
https://dzbuild.com/dashboard/connected-devicesيفتح هذه الصفحة.
كيف يرتبط الصندوق
- الاكتشاف:
GET https://dzbuild.com/.well-known/oauth-authorization-server(RFC 8414). - الصندوق عميل OAuth 2.0 عام اسمه
dzbuild-pos-windows، بلا سرّ. يُقبلS256وحده في PKCE، والتحويل يكون إلىhttp://127.0.0.1:PORT/oauth/callbackعلى أي منفذ. - يدخل صاحب المتجر من متصفح النظام ويختار المتاجر التي يستعملها الصندوق، حتى 10 متاجر. ويمكن ربط الصندوق برمز أيضاً: يعرض الصندوق رمزاً، ويكتبه صاحب المتجر في
https://dzbuild.com/deviceمن هاتفه. - رمز الوصول يبدأ بـ
dzpos_ويدوم 15 دقيقة. رمز التحديث يُستبدل عند كل استعمال وينتهي بعد 30 يوماً دون استعمال أو 180 يوماً في المجموع. إرسال رمز تحديث قديم من جديد ينهي الربط. - كل نداء يحمل
X-DZ-Storeمع رقم المتجر، ما عداGET /v1/me. المتجر الذي لم يختره صاحبه يُرجع403. - كل
POSTوPATCHوDELETEيحملIdempotency-Key، بالقواعد المذكورة في التكرار الآمن. - الحدود: 120 طلباً في الدقيقة لكل صندوق و600 لكل متجر. حصة الواجهة البرمجية الشهرية لا تنطبق على الصناديق.
- فصل الصندوق من لوحة التحكم ينهي الربط: التحديث التالي يُرجع
400 invalid_grantونبضة الاتصال التالية410 device_revoked.
الصلاحيات الـ 19
يطلب الصندوق الصلاحيات الـ 19 دائماً، وتمنحها DZBuild كلها دائماً.
| الصلاحيات | ما يستطيعه الصندوق |
|---|---|
openid وprofile وoffline_access | معرفة من ربطه والبقاء مرتبطاً |
store:read | قراءة متاجر الربط ولغتها وخطتها وحدّ منتجاتها |
products:read وproducts:write | قراءة المنتجات وإنشاؤها وتعديلها على دفعات وإضافة الصور |
inventory:read وinventory:write | قراءة مخزون المنتجات وتعديله |
orders:read وorders:write | استقبال طلبات المتجر وأخذ طلب وتحريكه وإلغاؤه |
customers:read | التحقق من رقم هاتف قبل البيع |
pos:sales:read وpos:sales:write | تسجيل المبيعات والمرتجعات وإغلاقات Z |
locations:read وlocations:write | تسجيل المحل الذي يوجد فيه الصندوق |
backups:read وbackups:write | محجوزة: النسخ الاحتياطية غير متاحة |
devices:self | تسجيل الصندوق وإرسال نبضات الاتصال وفك الربط |
events:read | قراءة تدفق التغييرات |
النقاط
| الطريقة والمسار | ما تفعله |
|---|---|
GET /v1/me | صاحب المتجر ومتاجر الربط |
GET /v1/store | المتجر المختار: الاسم واللغة والعملة DZD وتوقيت خصم المخزون والخطة وحدّ المنتجات |
POST /v1/devices وGET /v1/devices | تسجيل الصندوق (سطر واحد لكل صندوق ومتجر) وعرض الصناديق |
POST /v1/devices/{id}/heartbeat | كل 15 دقيقة: الإصدار والإرسالات المنتظرة والفاشلة |
DELETE /v1/devices/{id} | فك ربط هذا الصندوق |
GET /v1/locations وPOST /v1/locations | المحل الذي يوجد فيه الصندوق |
POST /v1/products/batch | إنشاء أو تعديل حتى 100 منتج |
GET /v1/products وGET /v1/products/{id} | المنتجات المعدّلة منذ تاريخ معيّن، ومنتج واحد |
POST /v1/media/uploads وPOST /v1/products/{id}/images | تذكرة الصورة، ثم ربط الصورة المرفوعة |
POST /v1/inventory/adjustments/batch | حتى 500 تثبيت أو تغيير للمخزون |
POST /v1/pos/sales وPOST /v1/pos/sales/{sale_id}/refunds وPOST /v1/pos/closures | المبيعات والمرتجعات وإغلاقات Z |
GET /v1/orders وGET /v1/orders/{id} | طلبات المتجر بصيغة الصندوق |
POST /v1/orders/{id}/claim وPATCH /v1/orders/{id} وPOST /v1/orders/{id}/cancel | أخذ طلب وتحريكه وإلغاؤه |
GET /v1/customers?phone= | مؤشرات رقم هاتف واحد |
GET /v1/events | تدفق التغييرات |
بعض المسارات مشتركة مع مفاتيح API (المنتجات والطلبات والزبائن). الصندوق يتلقى الصيغ المذكورة في هذه الصفحة، ومفتاح API يحتفظ بالصيغ المذكورة في قسم الموارد.
دفعة المنتجات
تأخذ POST /v1/products/batch الحقل items، حتى 100 عنصر. لكل عنصر external_id الخاص بالصندوق وname وsku وbarcode اختياريان وpricing.price كنص بخانتين عشريتين ("4500.00") وinventory.track_stock وstatus (active أو draft أو archived) وcategory اختيارية لها external_id وname خاصان بها.
- يعدّل العنصر المنتج المرتبط مسبقاً بـ
external_idالخاص به. وإن لم يوجد، يتبنّى منتجاً بلا متغيرات له نفسskuوغير مرتبط بعد. وإن لم يوجد، ينشئ منتجاً بمخزون 0. - يُعثر على الفئة بـ
external_idالخاص بها، أو تُنشأ من اسمها. - تذكر الاستجابة كل عنصر:
external_idوidوstatus(createdأوupdated) وerror: null. العنصر المرفوض يحمل كائنerrorولا يحملidولاstatus. - حدّ الخطة: عندما يملك المتجر عدد المنتجات النشطة الذي تسمح به خطته، فإن الدفعة التي تنشئ منتجاً، مسودة كان أو لا، تُرجع
402 product_limit_reached. العناصر التي سبقته تبقى مكتوبة. وتحويل منتج موجود إلىactiveبعد الحدّ خطأ على ذلك العنصر وحده. - الدفعة لا تضبط المخزون أبداً. المخزون يمرّ عبر نداء التعديلات.
الصور
POST /v1/media/uploadsمعfilenameوcontent_type(image/jpegأوimage/pngأوimage/webp) وsize(حتى 8 ميغابايت) وsha256. تحمل الاستجابةmedia_idورابطاً موقّعاًupload_urlصالحاً 10 دقائق.- يرسل الصندوق البايتات الخام بـ
PUTإلىupload_url، دون ترويسات DZBuild. POST /v1/products/{id}/imagesمعmedia_idوpositionيربط الصورة. يجب أن يطابق الحجم وsha256التذكرة، وإلا يُرجع النداء422 media_mismatch. التذكرة المجهولة أو المنتهية تُرجع404 media_not_found. يحمل المنتج حتى 20 صورة.
المخزون
تأخذ POST /v1/inventory/adjustments/batch حتى 500 عنصر. يذكر كل عنصر product_external_id وtarget: "product" وإما set (قطع كاملة) وإما delta، وreason (pos_sale أو pos_return أو restock أو count أو loss) وref فريداً.
- لا يُعدَّل إلا مخزون المنتجات التي تتابع المخزون على مستوى المنتج. المنتج الذي له مخزون لكل متغير يُرجع خطأ العنصر
variant_product، والذي لا يتابع المخزونnot_tracked، والمنتج الذي لم يعد موجوداًunknown_product. - تذكر الاستجابة كل عنصر بـ
refمعstatusإماokوإماerror. - تغييرات مخزون الصندوق لا تظهر أبداً في سجل تغييرات لوحة التحكم ولا تعرض تراجعاً.
وثائق الصندوق
تسجّل POST /v1/pos/sales تذكرة أو فاتورة أو وصل تسليم، وPOST /v1/pos/sales/{sale_id}/refunds وصل إرجاع أو إشعاراً دائناً على ذلك البيع، وPOST /v1/pos/closures إغلاق Z بمجاميعه وبصماته.
- تُحفظ الوثائق كما أُرسلت ولا تتغير أبداً: لا يوجد مسار تعديل أو حذف.
- يُرجع البيع
201مع{"id": 99120, "stock_applied": false}. المخزون يمرّ عبر نداء التعديلات، ولا يمرّ أبداً عبر وثيقة. - وثائق الصندوق منفصلة عن الطلبات. لا تُحسب في حدّ الطلبات الشهري للمتجر، ولا تُرسل أي إشعار، ولا تصل أبداً إلى Google Sheets ولا إلى webhooks.
- المبالغ نصوص بخانتين عشريتين، والكميات أرقام بثلاث خانات عشرية على الأكثر، مع حتى 500 سطر و20 دفعة في الوثيقة.
- إرسال وثيقة سُجّل
external_idالخاص بها من قبل يُرجع409 already_existsمع الرقم المسجّل فيerror.details.id. يعدّ الصندوق ذلك نجاحاً. - المرتجع على بيع متجر آخر يُرجع
404 not_found.
الطلبات
- تعطي
GET /v1/orders?updated_since=...وGET /v1/orders/{id}طلبات المتجر بصيغة الصندوق مع منتجاتها.order_numberهو الرقم القصير للمتجر إن وُجد. POST /v1/orders/{id}/claimمعdevice_idوterminal: أول صندوق يأخذ الطلب. الصندوق نفسه إذا أخذه من جديد يتلقى200، وأي صندوق آخر يتلقى409 order_claimed.PATCH /v1/orders/{id}معstatusيتطلب أخذ الطلب (409 claim_requiredفي غير ذلك). الانتقالات المسموحة: منpendingأوconfirmedإلىprocessingأوshippedأوdelivered، ومنprocessingإلىshippedأوdelivered، ومنshippedإلىdelivered. أي انتقال آخر يُرجع409 transition_not_allowed.- تأخذ
POST /v1/orders/{id}/cancelالحقلreason(out_of_stockأوcustomer_unreachableأوduplicateأوother) وnoteاختيارية حتى 500 حرف. يعود المخزون حسب قواعد مخزون المتجر. إذا كان صندوق آخر قد أخذ الطلب، يُرجع الإلغاء409 order_claimed. - تغيير الحالة من الصندوق يشغّل نفس الخطوات التي يشغّلها تغيير من لوحة التحكم، ومنها الإشعارات وتحديث Google Sheets.
الزبائن
تُرجع GET /v1/customers?phone=0550123456 صفحة فيها عنصر واحد على الأكثر: id وis_banned وfraud_score. تبحث في زبائن المتجر وحدهم، وتقبل الرقم مع +213 أو بدونه، ولا تعطي اسماً ولا عنواناً ولا سجلاً. الرقم المجهول يعطي صفحة فارغة.
الأحداث
تُرجع GET /v1/events?wait=25&limit=200&cursor=... الحقول items وnext_cursor وhas_more. الحقل next_cursor موجود دائماً، حتى في صفحة فارغة، ويعيده الصندوق في النداء التالي.
- تحتفظ الحافة بالنداء حتى 25 ثانية وتجيب فور حدوث تغيير.
- النداء الأول، دون مؤشر، يبدأ بحدث
order.updatedلكل طلب في حالةpendingأوconfirmedأوprocessing. - الأنواع:
order.createdوorder.updatedوproduct.updatedوproduct.deletedوinventory.level_changedوcustomer.updatedوdevice.revoked. لكل حدثidثابت، فيستطيع الصندوق تجاهل التكرار. - يحمل
inventory.level_changedالحقولoldوnewوdeltaوsource:orderعندما يحرّك طلبٌ المخزون (مع رقم الطلب)، وdashboardلأي تغيير آخر حدث خارج الصندوق، وposعندما يغيّره صندوق آخر في المتجر نفسه (يتجاهل الصندوق هذا المصدر). - كتابات الصندوق نفسه على المنتجات والمخزون لا تُرسل إليه من جديد.
- يحمل
device.revokedالحقلdevice_idكنص، مرة واحدة، بعد فصل الصندوق. - المؤشر الذي لم تُصدره هذه الواجهة يُرجع
400 bad_request.
النسخ الاحتياطية
لا تحفظ DZBuild النسخ الاحتياطية للصناديق. تُرجع GET /v1/backups وPOST /v1/backups وPOST /v1/backups/{id}/complete وGET /v1/backups/{id}/download دائماً 501 not_implemented، ويحتفظ الصندوق بنسخه على الحاسوب.
رموز الأخطاء
| الحالة | code | متى |
|---|---|---|
| 400 | bad_request | updated_since خاطئ أو مؤشر لم تُصدره هذه الواجهة |
| 401 | unauthorized | رمز مفقود أو خاطئ أو منتهٍ، أو ربط منتهٍ |
| 402 | product_limit_reached | دفعة تنشئ منتجاً بعد حدّ الخطة |
| 403 | forbidden | مسار خارج قائمة الصندوق، أو متجر لم يختره صاحبه |
| 404 | not_found | منتج أو بيع أو طلب ليس في هذا المتجر |
| 404 | device_not_found | رقم صندوق ليس هذا الصندوق في هذا المتجر |
| 404 | media_not_found | تذكرة صورة مجهولة أو منتهية |
| 409 | already_exists | وثيقة بهذا external_id مسجّلة؛ رقمها في details.id |
| 409 | order_claimed | صندوق آخر أخذ الطلب |
| 409 | claim_required | تحريك طلب لم يأخذه هذا الصندوق |
| 409 | transition_not_allowed | الانتقال غير مسموح من حالة الطلب |
| 410 | device_revoked | فُصل الصندوق |
| 413 | payload_too_large | جسم أكبر من 1 ميغابايت |
| 422 | validation_error | حقل خاطئ؛ يذكره details.field |
| 422 | media_mismatch | الصورة المرفوعة لا تطابق تذكرتها |
| 422 | idempotency_key_reuse | نفس Idempotency-Key مع جسم مختلف |
| 429 | rate_limited | تجاوز حدّ؛ انتظر Retry-After |
| 501 | not_implemented | مسارات النسخ الاحتياطية |
| 503 | storage_unavailable | تخزين الصور غير متاح؛ أعد المحاولة لاحقاً |