عروض النقاط
الخلفية
يحتاج نموذج إهداء النقاط إلى قائمتين: باقات النقاط (العروض) والمناسبات. وعلى Shukah يقرأهما
التطبيق عبر GET /maeda/point-offer وGET /maeda/point-occasion. وفي Locate الـ routes نفسهما،
وroute جديد واحد يُرجع عرضًا واحدًا. والحقول هي نفسها عند Shukah. وثلاثة
أمور تستحق العناية: أن id العرض والمناسبة سلسلة من 24 حرفًا بدل رقم، وأن Locate تحتاج token
حيث كانت Shukah تجيب بدونه، وأن ?id= في Shukah يُرجع القائمة كلها.
الميزة
العرض باقة نقاط يستطيع العميل شراءها ببطاقة أو الدفع لها بالنقاط، ليرسلها إلى عميل آخر. والمناسبة هي سبب الهدية، مثل Birthday. وفي نسخة التطبيق اللحظية بتاريخ 2026-02-26 تحدث الاستدعاءات في نموذج إرسال الهدية:
| الاستدعاء | أين في التطبيق |
|---|---|
| قائمة العروض | عندما تظهر أداة اختيار مبلغ الهدية في نموذج إرسال الهدية. |
عرض واحد (?id= في Shukah) | في ملخص الهدية، وفي زر الدفع (checkout)، وفي أداة اختيار طريقة الدفع. يطلب التطبيق من Shukah ?id= ثم يلتقط العرض من القائمة المُرجَعة بنفسه. |
| قائمة المناسبات | عندما تظهر أداة اختيار المناسبة في نموذج إرسال الهدية. |
مناسبة واحدة (?id= في Shukah) | في ملخص الهدية. الالتقاط نفسه من جانب العميل (client-side). |
تدخل المعرّفات في الاستدعاءات اللاحقة. يرسل التطبيق id العرض بوصفه point_offer_id وid المناسبة
بوصفه point_occasion_id عندما يطلب نص الرسالة
(وصف طلب النقاط) وعندما ينشئ الطلب
(إنشاء طلب نقاط).
كل هذه تحتاج إلى token في Locate، ولا يعمل على dev اليوم إلا token من Shukah: فقد حصل JWT من Locate على
401 Invalid or expired token على قائمتَي العروض والمناسبات في 2026-10-02، مع أن الـ token
كليهما عملا على route الرصيد في 2026-10-01 (انظر
Which tokens work). يمرّر order-v2 كل استدعاء إلى خدمة
النقاط ويُرجع الإجابة دون تغيير.
المعرّفات: خزّنها وأعِد إرسالها ولا تحلّلها أبدًا. id العرض أو المناسبة هو سلسلة hex من 24 حرفًا
لصفه في قاعدة البيانات. وهو مختلف في كل بيئة، لذا يجب على التطبيق قراءة المعرّفات من
القائمة وألا يكتب أيًّا منها في الشيفرة. والعدد الصحيح من Shukah المُرسَل إلى Locate بدل معرّف يُرفض
بـ 400. ويحوّل التطبيق المعرّفات إلى نص بالفعل (String(id)) ويقارنها كنصوص، وهذا يعمل
مع المعرّفات الجديدة، لكن أنواع TypeScript لديه ما زالت تقول number.
قاعدة is_custom. is_custom هي 0 أو 1، وليست boolean أبدًا. وعندما تكون 1 (على dev:
Thank وOther) يعرض التطبيق خانة لنص حر ويرسل ما كتبه العميل في occasion، بجانب معرّف المناسبة.
GET /api/v2/client/point-offers
GET /maeda/point-offer✅ تم الاستبدال- 🟡 id سلسلة hex من 24 حرفًا (في Shukah من 1 إلى 5)؛ ويستخدم التطبيق String(id) فيعمل؛ ومعرّف Shukah يعطي 400.
- 🟡 تحتاج Locate إلى token؛ وأجابت Shukah بـ 200 بدونه؛ ويرسل التطبيق token دائمًا.
- 🟡 يحصل JWT من Locate على 401 على dev؛ أرسل token من Shukah.
- 🟡 العروض الخمسة متطابقة في point_amount وpurchased_point_amount وamount ("40.00" كسلسلة نصية) وorder.
يُتجاهل المرشح ?id=؛ ولا يزال الاستدعاء يُرجع القائمة كلها.
الطلب (Request)
| الحقل | الموضع | النوع | مطلوب | القيود | الوصف |
|---|---|---|---|---|---|
Authorization | header | string | نعم | Bearer <token>: token من Shukah على dev اليوم. أما JWT من Locate فيحصل على 401 Invalid or expired token (التُقط على dev، 2026-10-02، على القائمتين): انظر Which tokens work. | الـ token الخاص بالعميل نفسه. |
id | query | any | لا | يُقبل ويُتجاهل: فالقائمة كلها تُرجَع دائمًا، كما يفعل ?id= في Shukah. | يُبقى حتى يظل استدعاء Shukah القديم يعمل. |
| أي مفتاح query آخر | query | — | لا | يُقبل ويُتجاهل، مثل language. | — |
الاستجابة (Response)
200 مع مصفوفة مجردة بالعروض النشطة. وقد تكون فارغة. ولا ترقيم للصفحات.
| الحقل | النوع | يقبل null | الصيغة / القيود | الوصف |
|---|---|---|---|---|
id | string | لا | سلسلة hex من 24 حرفًا، مثل "6a46104f3dd7b3dd6a261d90". وهي مختلفة في كل بيئة. وتُرسل Shukah عددًا صحيحًا. | معرّف العرض. أعِد إرساله بوصفه point_offer_id. |
point_amount | number | لا | رقم JSON. | النقاط التي يحصل عليها المستلم. |
purchased_point_amount | number | نعم | null عندما لا يكون للعرض سعر بالنقاط. وعلى dev هو أعلى من point_amount. | النقاط التي يدفعها المرسِل عند الدفع بالنقاط. |
amount | string | لا | خانتان عشريتان بالضبط، مثل "40.00". | السعر بالريال (SAR) عند الدفع بالبطاقة. |
[
{"id": "6a46104f3dd7b3dd6a261d90", "point_amount": 100, "purchased_point_amount": 110, "amount": "40.00"},
{"id": "6a46104f3dd7b3dd6a261d91", "point_amount": 200, "purchased_point_amount": 240, "amount": "60.00"},
{"id": "6a46104f3dd7b3dd6a261d94", "point_amount": 300, "purchased_point_amount": 360, "amount": "90.00"},
{"id": "6a46104f3dd7b3dd6a261d96", "point_amount": 400, "purchased_point_amount": 480, "amount": "120.00"},
{"id": "6a46104f3dd7b3dd6a261d98", "point_amount": 500, "purchased_point_amount": 600, "amount": "150.00"}
]
التُقط على dev، 2026-10-01 (العميل 356259): العروض الخمسة كلها.
يدفع المرسِل نقاطًا أكثر مما يحصل عليه المستلم: 110 مقابل 100، و240 مقابل 200، و360 مقابل 300، و480 مقابل
400، و600 مقابل 500. والعرض الذي قيمة purchased_point_amount فيه null لا يمكن الدفع له بالنقاط:
فـ إنشاء طلب نقاط يجيب عنه بـ 400.
الترتيب. تأتي العروض مرتبة برقم تسلسلي يضبطه المسؤول، ثم بمعرّف Shukah، ثم بمعرّف الصف. وقد أُودع هذا الترتيب في 2026-10-01. وهل كان dev يشغّله وقت أخذ العينة: غير موثّق بعد.
الأخطاء
| HTTP | Body | متى |
|---|---|---|
401 | {"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}} | لا يوجد header باسم Authorization، أو لا يبدأ بـ Bearer . التُقط على dev، 2026-10-01، دون token. |
401 | الشكل نفسه، مع message بالقيمة Invalid or expired token أو auth service unreachable: <error text> أو auth response missing numeric client id. | رفضت خدمة المصادقة الـ token، أو تعذّر الوصول إليها (فعطل خدمة المصادقة يبدو كـ token سيئ)، أو أجابت دون معرّف عميل. وقد التُقط Invalid or expired token على dev، 2026-10-02، مع JWT من Locate على القائمتين. |
500 | {"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}} | أي فشل غير متوقع في خدمة النقاط، مثل خطأ في قاعدة البيانات. وتكون الرسالة AUTH_SERVICE_URL not set عندما تكون الخدمة مضبوطة خطأً. لم يُلتقط. |
502 | {"success":false,"error":{"code":"POINT_SERVICE_UNAVAILABLE","message":"Point service unavailable: <error text>"}} | تعذّر على order-v2 الوصول إلى خدمة النقاط. وهذا الـ body صادر عن order-v2، لا عن خدمة النقاط. لم يُلتقط. |
504 | {"success":false,"error":{"code":"POINT_SERVICE_TIMEOUT","message":"Point service did not respond in time; the request outcome is unknown"}} | لم تجب خدمة النقاط خلال 10 ثوانٍ. وهذا الـ body صادر عن order-v2. لم يُلتقط. |
كل خطأ صادر عن خدمة النقاط له الغلاف (envelope) {"success":false,"error":{"code":"…","message":"…"}}.
جرّبه
export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_TOKEN='<shukah-token>' # a Locate JWT gets 401 here today; see "Which tokens work" on Points wallet
curl -s "$ORDER_BASE_URL/api/v2/client/point-offers" -H "Authorization: Bearer $SHUKAH_TOKEN"
المتوقع: 200 والمصفوفة المبيّنة تحت «الاستجابة». استخدم token من Shukah على dev اليوم: فـ JWT من Locate يحصل على
401 Invalid or expired token (2026-10-02). وبلا token تحصل على 401 المبيّن تحت «الأخطاء».
الفروق عن Shukah
العروض الخمسة متطابقة في point_amount وpurchased_point_amount وamount وترتيب المفاتيح في
2026-10-01 ومرة أخرى في 2026-10-02. ويختلف id فقط.
| الجانب | Shukah | Locate | الأثر على التطبيق |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/point-offer | GET $ORDER_BASE_URL/api/v2/client/point-offers | غيّر base URL والمسار. |
| الـ Token | لا حاجة إليه: 200 دون token (التُقط على dev، 2026-10-01). | مطلوب: 401 دون token (التُقط على dev، 2026-10-01). وtoken من Shukah فقط على dev اليوم: فـ JWT من Locate يحصل على 401 (2026-10-02). | يرسل التطبيق token الخاص به مع كل استدعاء بالفعل، ويجب أن يُبقيه token من Shukah هنا. أما شاشة تعرض العروض قبل تسجيل الدخول فستفشل. وهل توجد شاشة كهذه: غير موثّق بعد. |
id | عدد صحيح من 1 إلى 5 (التُقط على dev، 2026-10-01). | سلسلة من 24 حرفًا. | يحوّل التطبيق المعرّفات إلى نص، لذا يعمل. أما أنواعه فتقول number. لا تخلط النوعين أبدًا: فLocate ترفض العدد الصحيح من Shukah. |
?id= | يُرجع القائمة كلها. ويلتقط التطبيق العرض بنفسه. | يُقبل ويُتجاهل: القائمة كلها مجددًا. | لا شيء: فالالتقاط من جانب العميل في التطبيق يعمل مع المعرّفات الجديدة. |
purchased_point_amount | رقم (التُقط على dev، 2026-10-01). | رقم، أو null عندما لا يكون للعرض سعر بالنقاط. | يعرّفه التطبيق كرقم وينسخه إلى costInPoints. ويجب التعامل مع null. |
| الترتيب | غير موثّق بعد. | برقم تسلسلي يضبطه المسؤول، ثم معرّف Shukah، ثم معرّف الصف. | إذا كان التطبيق يعرض القائمة بترتيب الخادم، فقد يتغير الترتيب. |
| body الخطأ | غير موثّق بعد. الـ route عام (public)، فلا يوجد 401 للمقارنة. | غلاف خدمة النقاط الوارد تحت «الأخطاء». | يجب تغيير أي شيء يطابق نص الخطأ. |
GET /api/v2/client/point-offers/:id
route جديد. ليس لدى Shukah route يُرجع عرضًا واحدًا: فـ ?id= عندها يُرجع القائمة كلها، ويلتقط التطبيق
العرض من تلك القائمة بنفسه. ولا يستدعي التطبيق هذا الـ route اليوم.
الطلب (Request)
| الحقل | الموضع | النوع | مطلوب | القيود | الوصف |
|---|---|---|---|---|---|
Authorization | header | string | نعم | Bearer <token>: token من Shukah على dev اليوم. أما JWT من Locate فيحصل على 401 Invalid or expired token (التُقط على dev، 2026-10-02، على القائمتين): انظر Which tokens work. | الـ token الخاص بالعميل نفسه. |
id | path | string | نعم | 24 حرف hex بالضبط (أحرف كبيرة أو صغيرة). والعدد الصحيح من Shukah يُرفض ولا يُبحث عنه. | معرّف العرض من القائمة. |
| أي مفتاح query | query | — | لا | يمرّره order-v2 وتتجاهله خدمة النقاط. | — |
الاستجابة (Response)
200 مع كائن واحد، بشكل صف القائمة نفسه (id وpoint_amount و
purchased_point_amount وamount؛ انظر القائمة).
{"id": "6a46104f3dd7b3dd6a261d90", "point_amount": 100, "purchased_point_amount": 110, "amount": "40.00"}
التُقط على dev، 2026-10-01 (العميل 356259): العرض الأول.
الأخطاء
| HTTP | Body | متى |
|---|---|---|
400 | {"success":false,"error":{"code":"VALIDATION_ERROR","message":"point offer id must be a 24-character hex ObjectId"}} | :id ليس 24 حرف hex. ولا يوجد مفتاح details. لم يُلتقط. |
404 | {"success":false,"error":{"code":"NOT_FOUND","message":"point offer <id> not found or inactive"}} | لا يوجد عرض بذلك المعرّف، أو هو غير نشط أو محذوف. والمعرّف المكوَّن من 24 حرف hex غير الموجود يعطي 404 لا 400. لم يُلتقط. |
401 | {"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}} | لا يوجد header باسم Authorization، أو لا يبدأ بـ Bearer . التُقط على dev، 2026-10-01، دون token. |
401 | الشكل نفسه، مع message بالقيمة Invalid or expired token أو auth service unreachable: <error text> أو auth response missing numeric client id. | رفضت خدمة المصادقة الـ token، أو تعذّر الوصول إليها (فعطل خدمة المصادقة يبدو كـ token سيئ)، أو أجابت دون معرّف عميل. وقد التُقط Invalid or expired token على dev، 2026-10-02، مع JWT من Locate على القائمتين. |
500 | {"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}} | أي فشل غير متوقع في خدمة النقاط، مثل خطأ في قاعدة البيانات. وتكون الرسالة AUTH_SERVICE_URL not set عندما تكون الخدمة مضبوطة خطأً. لم يُلتقط. |
502 | {"success":false,"error":{"code":"POINT_SERVICE_UNAVAILABLE","message":"Point service unavailable: <error text>"}} | تعذّر على order-v2 الوصول إلى خدمة النقاط. وهذا الـ body صادر عن order-v2، لا عن خدمة النقاط. لم يُلتقط. |
504 | {"success":false,"error":{"code":"POINT_SERVICE_TIMEOUT","message":"Point service did not respond in time; the request outcome is unknown"}} | لم تجب خدمة النقاط خلال 10 ثوانٍ. وهذا الـ body صادر عن order-v2. لم يُلتقط. |
جرّبه
export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_TOKEN='<shukah-token>' # a Locate JWT gets 401 here today; see "Which tokens work" on Points wallet
export OFFER_ID='<id-from-the-offers-list>'
curl -s "$ORDER_BASE_URL/api/v2/client/point-offers/$OFFER_ID" -H "Authorization: Bearer $SHUKAH_TOKEN"
المتوقع: 200 والعرض المبيّن تحت «الاستجابة». والمعرّف الذي ليس 24 حرف hex يعطي 400
المبيّن تحت «الأخطاء». استخدم token من Shukah على dev اليوم: فهذا الـ route له فحص الـ token نفسه الخاص بالقائمتين.
GET /api/v2/client/point-occasions
GET /maeda/point-occasion✅ تم الاستبدال- 🟡 id سلسلة hex من 24 حرفًا (في Shukah من 1 إلى 7)؛ ويستخدم التطبيق String(id)؛ وإرسال معرّف Shukah إلى create في Locate يعطي 400.
- 🟡 تحتاج Locate إلى token؛ وأجابت Shukah بـ 200 بدونه؛ ويرسل التطبيق token دائمًا.
- 🟡 يحصل JWT من Locate على 401 على dev؛ أرسل token من Shukah.
- 🟡 الصفوف السبعة متطابقة في occasion وoccasion_en وis_custom (رقم 0/1) وترتيب المفاتيح.
الطلب (Request)
| الحقل | الموضع | النوع | مطلوب | القيود | الوصف |
|---|---|---|---|---|---|
Authorization | header | string | نعم | Bearer <token>: token من Shukah على dev اليوم. أما JWT من Locate فيحصل على 401 Invalid or expired token (التُقط على dev، 2026-10-02، على القائمتين): انظر Which tokens work. | الـ token الخاص بالعميل نفسه. |
language | query | string | لا | بعد حذف المسافات وتحويل الأحرف إلى صغيرة، القيمة ar بالضبط تعطي التسمية العربية. وأي قيمة أخرى، بما فيها ar-SA وen-GB، أو غياب القيمة، تعطي الإنجليزية. ولا يكون خطأً أبدًا. | لغة تسمية occasion. |
id | query | any | لا | يُقبل ويُتجاهل: فالقائمة كلها تُرجَع. | يُبقى حتى يظل استدعاء Shukah القديم يعمل. |
يضيف التطبيق language=en-GB أو language=ar-SA إلى كل استدعاء، لذا فبقيم التطبيق نفسه
تكون التسمية إنجليزية دائمًا. وهل هذا مقصود: غير موثّق بعد.
الاستجابة (Response)
200 مع مصفوفة مجردة بالمناسبات النشطة. وقد تكون فارغة. ولا ترقيم للصفحات.
| الحقل | النوع | يقبل null | الصيغة / القيود | الوصف |
|---|---|---|---|---|
id | string | لا | سلسلة hex من 24 حرفًا. مختلفة في كل بيئة. وتُرسل Shukah عددًا صحيحًا. | معرّف المناسبة. أعِد إرساله بوصفه point_occasion_id. |
occasion | string | لا | التسمية الإنجليزية، بعد حذف المسافات الزائدة. ومع language=ar التسمية العربية، أو الإنجليزية عندما لا توجد عربية. | التسمية المعروضة. |
occasion_en | string | لا | التسمية الإنجليزية، بعد حذف المسافات الزائدة، دائمًا. | التسمية الإنجليزية. |
is_custom | integer | لا | 0 أو 1، وليست boolean أبدًا. | 1 تعني أن التطبيق يعرض خانة لنص حر. |
[
{"id": "6a4610503dd7b3dd6a261dbf", "occasion": "Birthday", "occasion_en": "Birthday", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc0", "occasion": "Born", "occasion_en": "Born", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc1", "occasion": "Gift", "occasion_en": "Gift", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc3", "occasion": "Wedding", "occasion_en": "Wedding", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc5", "occasion": "Graduation", "occasion_en": "Graduation", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc6", "occasion": "Thank", "occasion_en": "Thank", "is_custom": 1},
{"id": "6a4610503dd7b3dd6a261dc7", "occasion": "Other", "occasion_en": "Other", "is_custom": 1}
]
التُقط على dev، 2026-10-01 (العميل 356259)، دون language: المناسبات السبع كلها.
في التطبيق مشكلة في الأنواع موجودة مع Shukah أيضًا: فهو يقرأ التسمية الإنجليزية بالاسم occasionEn،
لكن الخادمين يرسلان occasion_en، لذا فإن nameEn في التطبيق هي undefined اليوم.
الأخطاء
| HTTP | Body | متى |
|---|---|---|
401 | {"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}} | لا يوجد header باسم Authorization، أو لا يبدأ بـ Bearer . التُقط على dev، 2026-10-01، دون token. |
401 | الشكل نفسه، مع message بالقيمة Invalid or expired token أو auth service unreachable: <error text> أو auth response missing numeric client id. | رفضت خدمة المصادقة الـ token، أو تعذّر الوصول إليها (فعطل خدمة المصادقة يبدو كـ token سيئ)، أو أجابت دون معرّف عميل. وقد التُقط Invalid or expired token على dev، 2026-10-02، مع JWT من Locate على القائمتين. |
500 | {"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}} | أي فشل غير متوقع في خدمة النقاط، مثل خطأ في قاعدة البيانات. وتكون الرسالة AUTH_SERVICE_URL not set عندما تكون الخدمة مضبوطة خطأً. لم يُلتقط. |
502 | {"success":false,"error":{"code":"POINT_SERVICE_UNAVAILABLE","message":"Point service unavailable: <error text>"}} | تعذّر على order-v2 الوصول إلى خدمة النقاط. وهذا الـ body صادر عن order-v2، لا عن خدمة النقاط. لم يُلتقط. |
504 | {"success":false,"error":{"code":"POINT_SERVICE_TIMEOUT","message":"Point service did not respond in time; the request outcome is unknown"}} | لم تجب خدمة النقاط خلال 10 ثوانٍ. وهذا الـ body صادر عن order-v2. لم يُلتقط. |
جرّبه
export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_TOKEN='<shukah-token>' # a Locate JWT gets 401 here today; see "Which tokens work" on Points wallet
curl -s "$ORDER_BASE_URL/api/v2/client/point-occasions?language=en-GB" -H "Authorization: Bearer $SHUKAH_TOKEN"
المتوقع: 200 والمصفوفة المبيّنة تحت «الاستجابة». استخدم token من Shukah على dev اليوم: فـ JWT من Locate يحصل على
401 Invalid or expired token (2026-10-02). وبلا token تحصل على 401 المبيّن تحت «الأخطاء».
الفروق عن Shukah
الصفوف السبعة متطابقة في occasion وoccasion_en وis_custom وترتيب المفاتيح في 2026-10-01
ومرة أخرى في 2026-10-02. ويختلف id فقط.
| الجانب | Shukah | Locate | الأثر على التطبيق |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/point-occasion | GET $ORDER_BASE_URL/api/v2/client/point-occasions | غيّر base URL والمسار. |
| الـ Token | لا حاجة إليه: 200 دون token (التُقط على dev، 2026-10-01). | مطلوب: 401 دون token (التُقط على dev، 2026-10-01). وtoken من Shukah فقط على dev اليوم: فـ JWT من Locate يحصل على 401 (2026-10-02). | يرسل التطبيق token الخاص به مع كل استدعاء بالفعل، ويجب أن يُبقيه token من Shukah هنا. |
id | عدد صحيح من 1 إلى 7 (التُقط على dev، 2026-10-01). | سلسلة من 24 حرفًا. | يحوّل التطبيق المعرّفات إلى نص، لذا يعمل. لا تخلط النوعين أبدًا. |
?id= | يُرجع القائمة كلها. | يُقبل ويُتجاهل: القائمة كلها مجددًا. | لا شيء: فالالتقاط من جانب العميل في التطبيق يعمل مع المعرّفات الجديدة. |
تسمية occasion | النص الإنجليزي نفسه في occasion وoccasion_en (التُقط على dev، 2026-10-01). | إنجليزية، أو عربية عندما تكون language هي ar بالضبط. | يرسل التطبيق ar-SA، فلا يحصل على التسمية العربية أبدًا. |
| الترتيب | غير موثّق بعد. | برقم تسلسلي يضبطه المسؤول، ثم معرّف Shukah، ثم معرّف الصف. | إذا كان التطبيق يعرض القائمة بترتيب الخادم، فقد يتغير الترتيب. |
| body الخطأ | غير موثّق بعد. الـ route عام (public)، فلا يوجد 401 للمقارنة. | غلاف خدمة النقاط الوارد تحت «الأخطاء». | يجب تغيير أي شيء يطابق نص الخطأ. |
تنبيه
- معرّفات العروض والمناسبات سلاسل من 24 حرفًا تختلف بين البيئات: خزّنها وأعِد إرسالها ولا تحلّلها أبدًا، ولا ترسل عددًا صحيحًا من Shukah أبدًا.
- تتطلب Locate token على الـ routes الثلاثة، بينما كانت قوائم Shukah عامة.
- ما زال
?id=يُرجع القائمة كلها، لذا التقط العرض أو المناسبة من المصفوفة، أو استخدمGET /point-offers/:id.
التفاصيل (للـ backend)
فُحص dev فقط، عبر order-v2 على https://dev.v2.api.order.locate.sa، والبادئة /api/v2/client.
الـ hosts في UAT وproduction: غير موثّق بعد. في هذا القسم تعني OV2: المستودع
locate-order-v2-api@d9e4e5f6 وتعني PS: المستودع locate-point-service@2c44ce3. وكيف يعمل الـ proxy وفحص
الـ token وغلاف الأخطاء موصوف مرة واحدة، تحت
كيف تعمل الاستدعاءات الممرَّرة.
الـ Routes والـ proxy
| route التطبيق (order-v2) | route المصدر (خدمة النقاط) | ما يُمرَّر | الحد |
|---|---|---|---|
GET /api/v2/client/point-offers | GET /api/client/point-offers | Authorization والـ query كله | 10 ث |
GET /api/v2/client/point-offers/:id | GET /api/client/point-offers/<id>، مع تمرير المعرّف عبر encodeURIComponent | Authorization والـ query كله (تتجاهله خدمة النقاط) | 10 ث |
GET /api/v2/client/point-occasions | GET /api/client/point-occasions | Authorization والـ query كله، لذا تصل language | 10 ث |
(OV2: src/modules/client/point/point.controller.ts:61-108،
PS: services/api/src/routes/client.routes.ts:20-22،
PS: services/api/src/handlers/client-point-offers.ts:12-23،
PS: services/api/src/handlers/client-point-occasions.ts:12-14.)
ما تقرؤه خدمة النقاط
- العروض. صفوف
point_offersبحالةstatus: 'active'وdeleted_at: null، مرتبة بحسبsequence، ثمshukah_id، ثم_id. ولا ترقيم للصفحات ولا حد. والعرض المنشأ في Locate بلا معرّف في Shukah يُسرد كأي عرض آخر (PS: services/api/src/services/point-settings.service.ts:10-25). وقد تغيّر الترتيب في 2026-10-01 (commit105119c، دُمج 14:25 +07)، بينما ما زالت وثيقة التكامل تقول "ordered byshukah_id" (PS: docs/plans/2026-10-01-app-language-and-arabic-reason.md:6-8،PS: docs/integrations/2026-07-16-client-facing-point-apis.md:321-322). وأُخذت الالتقاطات نحو الساعة 17:19 +07. وهل كان ذلك الـ build على dev: غير موثّق بعد. - عرض واحد. المرشّح نفسه على
_id. والعرض غير النشط أو المحذوف يعطي404(PS: services/api/src/services/point-settings.service.ts:29-34). وفحص المعرّف هو^[0-9a-fA-F]{24}$(PS: services/api/src/lib/object-id.ts:12-14،PS: services/api/src/handlers/client-point-offers.ts:16,19-23). - المناسبات. صفوف
point_occasionsبالمرشّح نفسه، ومرتبة بالطريقة نفسها (PS: services/api/src/services/point-settings.service.ts:38-42). وقاعدةlanguage(arبالضبط بعد حذف المسافات وتحويل الأحرف إلى صغيرة) صدرت في الـ commit نفسه105119c(PS: services/api/src/handlers/client-point-occasions.ts:5-8،PS: docs/plans/2026-10-01-app-language-and-arabic-reason.md:10-17). - المعرّفات. المعرّفات هي قيم
_idالخاصة بالصفوف نفسها، معروضة كـ hex من 24 حرفًا بأحرف صغيرة. وتُقبل الأحرف الكبيرة في الإدخال (PS: services/api/src/services/point-settings.mappers.ts:25-58). أما الأعداد الصحيحة التي تستخدمها Shukah فما زالت مخزَّنة في الصفوف باسمshukah_id، لكن routes العميل لم تعد تقبلها: فـpoint_offer_idالذي ليس 24 حرف hex يفشل بالرسالةmust be a 24-character hex ObjectId(فُحص بتشغيل محلي للـ schema، zod 3.25.76). - الـ query المسمّى
?id=. لا يقرأ الـ handler أيid؛ وتُرجع القائمة كلها، وهو ما تقول وثيقة خدمة النقاط إنه يطابق Shukah، "measured on dev 2026-09-21" (PS: services/api/src/handlers/client-point-offers.ts:7-11،PS: docs/integrations/2026-07-16-client-facing-point-apis.md:324-328). is_custom. يكتب الـ mapperdoc.is_custom ? 1 : 0(PS: services/api/src/services/point-settings.mappers.ts:56). والمناسبة المخصصة (custom) هي الحالة الوحيدة التي يرسل فيها التطبيق نصًا حرًا فيoccasionمع المعرّف (PS: services/api/src/services/point-settings.service.ts:57-58).- التعديلات. تُغيَّر العروض والمناسبات عبر routes المسؤول تحت
/api/admin/…بمفتاح خدمة، ولا يفعل ذلك التطبيق أبدًا (PS: services/api/src/routes/admin.routes.ts:34-44).
التطبيق
تقول أنواع التطبيق id: number للعروض والمناسبات
(mobile/locate-client@e960c3a: src/entities/points/model/types/pointGiftOption.server.ts:1-6،
mobile/locate-client@e960c3a: src/entities/points/model/types/pointGiftOccasion.server.ts:1-6). وتحوّل
الـ DTOs المعرّف إلى نص وتحسب cost بـ Number(amount) وcostInPoints بـ
purchased_point_amount وisCustom بـ String(is_custom) === '1'
(mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:104-136). وتُرشّح
دوال by-id المساعدة القائمة المُرجَعة بـ String(item.id) === request.id
(mobile/locate-client@e960c3a: src/entities/points/model/api/services/points.service.ts:212-241,285-295).
ويضيف interceptor اللغة language=en-GB أو language=ar-SA إلى كل استدعاء
(mobile/locate-client@e960c3a: src/shared/api/interceptors/languageInterceptor.ts:5-15).
حيث تختلف الوثائق عن الشيفرة (القول للشيفرة)
- تقول وثيقة التكامل إن العروض مرتبة بحسب
shukah_idمع_idلكسر التعادل (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:321-322). أما الشيفرة فترتّب بحسبsequenceأولًا. - 2026-10-02: قالت هذه الصفحة إن token من Shukah وJWT من Locate عملا كلاهما على هذه الـ routes. وفي
2026-10-02 حصل الـ JWT على
401على قائمتَي العروض والمناسبات، وتستخدم استدعاءات «جرّبه» الآن token من Shukah (PS: services/api/src/services/client-auth.service.ts:22-29؛ انظر Which tokens work).
Sources
locate-order-v2-api@d9e4e5f6:src/modules/client/point/point.controller.ts:61-108locate-point-service@2c44ce3:services/api/src/routes/client.routes.ts:20-22،services/api/src/handlers/client-point-offers.ts:7-23،services/api/src/handlers/client-point-occasions.ts:5-14،services/api/src/services/point-settings.service.ts:10-42,57-58،services/api/src/services/point-settings.mappers.ts:25-58,services/api/src/lib/object-id.ts:12-14،services/api/src/routes/admin.routes.ts:34-44- وثائق
locate-point-service@2c44ce3:docs/integrations/2026-07-16-client-facing-point-apis.md:321-328،docs/plans/2026-10-01-app-language-and-arabic-reason.md:6-17 - نسخة الجوال اللحظية
mobile/locate-client@e960c3a(الفرعdevelopment، 2026-02-26؛ شيفرة التطبيق نفسه، وليست من Locate):src/entities/points/model/api/services/points.service.ts:190-263,285-295،src/entities/points/model/api/dto/points.dto.ts:104-136،src/entities/points/model/types/pointGiftOption.server.ts:1-6،src/entities/points/model/types/pointGiftOccasion.server.ts:1-6،src/features/SelectPointGift/model/hooks/useSelectPointGiff.ts:16،src/features/SelectPointGiftOccasion/model/hooks/useSelectPointGiftOccasion.ts:10،src/widgets/SendPointGiftForm/ui/SendPointGiftForm/SendPointGiftForm.tsx:149-185،src/features/PointGiftSummary/ui/PointGiftSummary/PointGiftSummary.tsx:34-37،src/features/PointGiftsCheckoutButton/ui/PointGiftsCheckoutButton/PointGiftsCheckoutButton.tsx:33،src/features/SelectPointGiftPaymentMethod/model/hooks/useSelectPointGiftPaymentMethod.ts:52،src/shared/api/interceptors/languageInterceptor.ts:5-15 - التقاطات dev، 2026-10-02 (parity audit):
point-offer--shukahوpoint-offer--locate-stوpoint-offer--locate-jwtوpoint-occasion--shukahوpoint-occasion--locate-stوpoint-occasion--locate-jwt - التقاطات dev، 2026-10-01:
locate-point-offersوlocate-point-offers-againوlocate-point-offerوlocate-point-occasionsوlocate-point-occasions-againوshukah-point-offerوshukah-point-occasionوprobe-locate-point-offersوprobe-locate-point-offerوprobe-locate-point-occasionsوprobe-shukah-point-offerوprobe-shukah-point-occasion