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

محفظة النقاط

الخلفية​

على Shukah يقرأ التطبيق رصيد النقاط عبر GET /maeda/client/points، وجدول معدلات الكسب عبر GET /maeda/point-rate، ويسترد كود بطاقة هدية عبر GET /maeda/giftcard-code/use. وفي Locate route لكل منها، ويصنّف تدقيق التكافؤ الثلاثة كلها drop-in: لا يحتاج أيٌّ منها إلى تغيير في التطبيق سوى الـ base URL والمسار. ومع ذلك يحوي TL;DR كلٍّ منها بنودًا للمراقبة، لأن الـ method أو الـ token أو البيانات قد تختلف:

  • الرصيد (drop-in). يمرّر GET /api/v2/client/points في order-v2 الاستدعاء إلى خدمة النقاط. ويُرجع المفاتيح الأربعة نفسها كما في Shukah. وللمراقبة: يمكن أن تحمل points أربع خانات عشرية، وعلى dev لم يكن الرصيد وتاريخ الانتهاء كما في Shukah (في 2026-10-01 وفي 2026-10-02)، وعلى dev اليوم لا يقبل إلا token من Shukah، لا JWT من Locate.
  • معدلات الكسب وكود بطاقة الهدية (drop-in). يخدم order-v2 هذين الـ routes بنفسه. ولا يمرّرهما إلى خدمة النقاط. وللمراقبة: جدول معدلات الكسب على dev ليس جدول Shukah، وكود بطاقة الهدية صار الآن POST مع الكود في الـ body، وهو استدعاء موجود في التطبيق بالفعل. ولا يستدعي التطبيق الاثنين إلا في وضع Merchant.

الميزة​

المحفظة هي الشاشة التي يرى فيها العميل رصيد النقاط وآخر معاملات النقاط، وخانة لإدخال كود بطاقة هدية، والأسئلة الشائعة عن النقاط. وفي نسخة التطبيق اللحظية بتاريخ 2026-02-26 تحدث الاستدعاءات في هذه الأماكن:

الاستدعاءأين في التطبيق
الرصيد (#points)عندما يحمّل التطبيق معلومات العميل (عند البدء وبعد تسجيل الدخول)، وعندما تفتح شاشة الدفع (checkout) في المطعم خيار طريقة الدفع (لمعرفة هل يستطيع العميل الدفع بالنقاط). وتعرض شاشة المحفظة عدد النقاط وقيمتها بالريال (SAR) وتاريخ الانتهاء.
معدلات الكسب (#point-rates)في بطاقة "Earn Points" في سلة المطعم وسلة التاجر (merchant)، التي تعرض ما يكسبه إجمالي السلة.
كود بطاقة الهدية (#gift-card-code)عندما يدخل العميل كودًا في الخانة الموجودة في شاشة المحفظة. وعند النجاح يعرض التطبيق رسالة بالكود والنقاط، ثم يعيد تحميل الرصيد والسجل. وعند الخطأ يعرض رسالة بنص الخطأ.

يحتاج الرصيد وكود بطاقة الهدية إلى token العميل نفسه. أما جدول معدلات الكسب فعام. وبالنسبة إلى الرصيد، لا يعمل على dev اليوم إلا token من Shukah: فقد حصل JWT من Locate على 401 في 2026-10-02، مع أن الـ token كليهما عملا في 2026-10-01 (انظر «أي tokens تعمل» في «التفاصيل»). أما كود بطاقة الهدية فيستخدم فحص الـ token نفسه (عبر /api/v1/users)، لذا يحتاج هو أيضًا إلى token من Shukah (قراءة شيفرة؛ لم يُستدعَ مع token، لأنه يضيف نقاطًا).

وضع Merchant. لا يستدعي التطبيق routes Locate لمعدلات الكسب وكود بطاقة الهدية إلا عندما تقول إعدادات العميل enableMerchantService: true. وبدونها يواصل التطبيق استدعاء Shukah للاثنين. ويقرأ التطبيق ذلك الإعداد من خدمة client-settings في Locate عند بدئه. أما استدعاء الرصيد فلا نسخة له في Locate داخل التطبيق اليوم: فهو يستدعي Shukah دائمًا، في الوضعين، لذا يجب تغيير التطبيق ليستدعي GET /api/v2/client/points.

رصيدان. الرصيد الذي يُرجعه هذا الـ route يأتي من خدمة النقاط. وعندما يدفع العميل طلبًا بالنقاط، يظل order-v2 يفحص الرصيد على Shukah (GET /maeda/client/points بـ token العميل)، لا هذا الرصيد. وأثناء التشغيل المتوازي قد يختلف الاثنان، فتعرض شاشة المحفظة وشاشة الدفع (checkout) أرقامًا مختلفة. وهل هذا مقصود: غير موثّق بعد.

الصرف غير متزامن. لم يتبع السجلان (ledgers) أحدهما الآخر عند صرف النقاط (خلال ثواني التشغيل؛ ولم يُفحص هل يصل لاحقًا). فعلى dev في 2026-10-02 خفّض طلب هدية مدفوع بالنقاط على Shukah رصيد Shukah بمقدار 110 وترك رصيد Locate كما هو؛ والطلب نفسه على Locate، بعد ثوانٍ، لم يخفض إلا رصيد Locate (انظر طلبات النقاط). فإذا أنشأ التطبيق طلبات النقاط على جانب، فعليه أن يقرأ الرصيد والسجل من ذلك الجانب، وإلا عرضت شاشة المحفظة رقمًا خاطئًا.

GET /api/v2/client/points​

يستبدل ShukahGET /maeda/client/points✅ تم الاستبدال
استبدال مباشر0 breaking · 4 للمراقبة
  • 🟡 يحصل JWT من Locate على 401 "Invalid or expired token" على dev؛ أرسل token من Shukah.
  • 🟡 الرصيد "514405.6788" مقابل "514853.64" في Shukah (Locate أقل بـ 447.96)؛ والانتهاء 2026-11-28 مقابل 2026-11-30.
  • 🟡 قد تحمل points حتى 4 خانات عشرية؛ ويستخدم التطبيق Number(points) || 0 فيُحلَّل بنجاح؛ وعلى الشاشات التقريب عند العرض.
  • 🟡 body الـ 401 هو {success,error:{code,message}} بلا message في المستوى الأعلى؛ وسيعرض التطبيق سلسلة JSON الخام.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <token>. يعمل token من Shukah على dev. أما JWT من Locate فيحصل على 401 Invalid or expired token (التُقط على dev، 2026-10-02؛ وكان يعمل في 2026-10-01): انظر «أي tokens تعمل» في «التفاصيل».الـ token الخاص بالعميل نفسه. ومنه يُؤخذ معرّف العميل، لذا لا يرسل التطبيق معرّفًا أبدًا.
أي مفتاح queryquery—لالا يُقرأ. ولا يمرّر order-v2 أي query في هذا الـ route، لذا فإن language الذي يضيفه التطبيق يُسقَط.—

الاستجابة (Response)​

200 مع كائن مجرد (بلا غلاف status أو data):

الحقلالنوعيقبل nullالصيغة / القيودالوصف
idstringلامعرّف العميل كنص، مثل "356259".معرّف العميل.
user_idstringلاالقيمة نفسها كما في id.معرّف العميل.
pointsstringلاعدد عشري في نص. تُحذف الأصفار اللاحقة والنقطة الأخيرة، وتبقى حتى أربع خانات عشرية: "514405.6788". و"0" عندما لا يكون للعميل حساب نقاط. ويمكن أن يكون سالبًا: فالمزامنة الواردة من Shukah لا تفحص السحب على المكشوف (overdraft).الرصيد الحالي.
pointsExpirationDatestringنعمYYYY-MM-DD، تاريخ بتوقيت Asia/Riyadh. وnull ما لم تنطبق قاعدة الانتهاء أدناه.اليوم الذي ينتهي فيه الرصيد بسبب عدم النشاط.

تأتي المفاتيح بهذا الترتيب: id وuser_id وpoints وpointsExpirationDate.

{
"id": "356259",
"user_id": "356259",
"points": "514405.6788",
"pointsExpirationDate": "2026-11-28"
}

التُقط على dev، 2026-10-01 (العميل 356259)، مع token من Shukah. وأعاد الاستدعاء نفسه مع JWT من Locate الـ body نفسه في ذلك اليوم. وفي 2026-10-02 حصل token من Shukah على القيم نفسها، وحصل JWT من Locate على 401.

صيغة points. تحتفظ Locate بالنقاط بأربع خانات عشرية ولا تقرّبها. ولا يفعل المنسِّق (formatter) سوى حذف الأصفار اللاحقة، فتصبح "511712.9600" هي "511712.96" وتصبح "0.0000" هي "0". وعلى dev أعطى العميل نفسه في الدقيقة نفسها "514405.6788" على Locate و"128712.41" على Shukah في 2026-10-01، و"514405.6788" و"514853.64" في 2026-10-02. ويقرأ التطبيق القيمة بـ Number(points) || 0، لذا تُحلَّل الصيغتان.

قاعدة pointsExpirationDate. يكون التاريخ null عندما تقول إعدادات النقاط إن الانتهاء never، أو عندما لا تكون الإعدادات مضبوطة، أو عندما لا يكون للعميل صف كسب محسوب. وإلا فهو تاريخ أحدث صف كسب محسوب للعميل مضافًا إليه عدد أيام عدم النشاط المضبوط، بتوقيت Asia/Riyadh. ولا تفرض Locate التاريخ إلا عندما تكون مهمة الانتهاء (expiry job) مفعّلة (انظر «التفاصيل»). وقد أرجع dev القيمة 2026-11-28.

الأخطاء​

HTTPBodyمتى
401{"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}}لا يوجد header باسم Authorization، أو لا يبدأ بـ Bearer . التُقط على dev، 2026-10-01، دون token.
401{"success":false,"error":{"code":"UNAUTHORIZED","message":"Invalid or expired token"}}، أو الشكل نفسه مع message بالقيمة auth service unreachable: <error text> أو auth response missing numeric client id.رفضت خدمة المصادقة الـ token، أو تعذّر الوصول إليها (فعطل خدمة المصادقة يبدو كـ token سيئ)، أو أجابت دون معرّف عميل. وقد التُقط الـ body الأول على 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":"…"}} (ويُضاف مفتاح details لأخطاء التحقق). أما body النجاح فلا غلاف له.

جرّبه​

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" in Details
curl -s "$ORDER_BASE_URL/api/v2/client/points" -H "Authorization: Bearer $SHUKAH_TOKEN"

المتوقع: 200 والكائن المبيّن تحت «الاستجابة». استخدم token من Shukah على dev اليوم: فـ JWT من Locate يحصل على 401 Invalid or expired token المبيّن تحت «الأخطاء» (2026-10-02). وبلا token تحصل على 401 Missing or invalid authorization header.

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
URLGET $SHUKAH_BASE_URL/maeda/client/pointsGET $ORDER_BASE_URL/api/v2/client/pointsغيّر base URL والمسار.
الـ Tokentoken معتم (opaque) من Shukah.token من Shukah فقط على dev اليوم. أما JWT من Locate فيحصل على 401 (التُقط على dev، 2026-10-02)؛ وفي 2026-10-01 حصل الاثنان على 200.يجب أن يواصل التطبيق إرسال token من Shukah هنا، حتى بعد أن يحمل JWT من Locate لخدمة المصادقة. وللبيئات الأخرى: غير موثّق بعد.
صيغة pointsخانتان عشريتان: "514853.64" (التُقط على dev، 2026-10-02).حتى أربع خانات عشرية: "514405.6788" (العميل نفسه، اليوم نفسه).يحلّل التطبيق بـ Number(points) || 0، لذا يعمل. وأي شاشة تفترض خانتين عشريتين يجب أن تقرّب.
الرصيد للعميل 356259"514853.64" في 2026-10-02 (و"128712.41" في 2026-10-01: فقد تغيرت بيانات Shukah على dev بينهما)."514405.6788" في اليومين: أي أقل من Shukah بـ 447.96 نقطة، نحو 0.09%، في 2026-10-02.سينخفض الرصيد قليلًا في شاشة المحفظة عند التحوّل. وأحدث صف في سجل Locate أقدم بنحو يومين من أحدث صف في Shukah، وهذا يلائم الفجوة (استنتاج). تأكد من أن التغذية من Shukah حديثة قبل التحوّل.
pointsExpirationDate"2026-11-30"."2026-11-28". كل تاريخ يقع بعد 60 يومًا من أحدث صف في السجل (ledger) على ذلك الجانب، وأحدث صف في Locate أقدم بيومين (استنتاج).قد يتحرك التاريخ الذي يعرضه التطبيق بأيام. وهل تشغّل Locate مهمة الانتهاء على dev: غير موثّق بعد.
body الخطأ{"name":"Unauthorized","message":"Your request was made with invalid credentials.","code":0,"status":401,"type":"Illuminate\\Auth\\AuthenticationException"} (التُقط على dev، 2026-10-01، بلا token).غلاف خدمة النقاط الوارد تحت «الأخطاء».يجب تغيير أي شيء يطابق نص الخطأ أو name أو type.
Content-Typeapplication/jsonapplication/json; charset=utf-8لا شيء.

GET /api/v2/client/system_configs/point_rates​

يستبدل ShukahGET /maeda/point-rate✅ تم الاستبدال🚩 enableMerchantService
استبدال مباشر0 breaking · 4 للمراقبة
  • 🟡 الشرائح مختلفة: Locate 70-140:2 و140-251:4 و251-451:10 و451+:16؛ وShukah 0-139:2 ... 451+:30 (5 صفوف).
  • 🟡 قيمة amount_to في Locate تساوي amount_from التالية (140 و251 و451)؛ وتنتهي شرائح Shukah عند 139 و153 و250 و450.
  • 🟡 يبدأ الصف الأول في Locate عند 70 وفي Shukah عند 0؛ وتحت 70 يعطي الصف الأول الذي يضيفه التطبيق 0 نقطة.
  • 🟡 عام على الطرفين (200 بلا token)؛ ولا يستدعي التطبيق route Locate إلا عندما تكون enableMerchantService بقيمة true.

مفتاح من جانب التطبيق: لا يستدعي التطبيق route Locate هذا إلا في وضع Merchant (إعدادات العميل enableMerchantService: true)؛ وإلا فما زال يستدعي Shukah.

يخدم order-v2 هذا الـ route بنفسه ولا يمرّره إلى خدمة النقاط. لذا فإن قواعد الـ proxy وأخطاء 502 و504 في صفحات النقاط الأخرى لا تنطبق عليه. ولا يستدعيه التطبيق إلا في وضع Merchant.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
Authorizationheaderstringلاغير مطلوب: فقد أجاب dev بـ 200 بلا token ومعه. والـ token المُرسَل لا يُقرأ.—
أي مفتاح queryquery—لايُقبل ويُتجاهل.—

الاستجابة (Response)​

200 مع مصفوفة مجردة، مرتبة بحسب amount_from من الأدنى إلى الأعلى (وnull تُعدّ 0). وهي [] عندما لا يكون خيار المعدل (rate option) مضبوطًا:

الحقلالنوعيقبل nullالصيغة / القيودالوصف
amount_fromnumberنعمإجمالي الطلب الذي يبدأ عنده الصف.بداية النطاق، بالريال (SAR).
amount_tonumberنعمnull في الصف الأخير. وعلى dev ينتهي كل صف عند القيمة التي يبدأ عندها الصف التالي (140 و251 و451).نهاية النطاق، بالريال (SAR).
point_earnnumberلاعدد صحيح على dev.النقاط المكتسبة عندما يقع إجمالي الطلب في النطاق.
[
{"amount_from": 70, "amount_to": 140, "point_earn": 2},
{"amount_from": 140, "amount_to": 251, "point_earn": 4},
{"amount_from": 251, "amount_to": 451, "point_earn": 10},
{"amount_from": 451, "amount_to": null, "point_earn": 16}
]

التُقط على dev، 2026-10-01، بلا token (ومرة أخرى مع token من Shukah).

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

الأخطاء​

HTTPBodyمتى
500body خطأ من Nest. لم تُلتقط مفاتيحه بالضبط.تعذّرت قراءة خيار المعدل، أو قيمته ليست قائمة. لم يُلتقط.

لا يوجد 401: فليس للـ route حارس. وتُتجاهل مفاتيح query والـ headers المجهولة.

جرّبه​

export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
curl -s "$ORDER_BASE_URL/api/v2/client/system_configs/point_rates"

المتوقع: 200 والمصفوفة المبيّنة تحت «الاستجابة». ولا حاجة إلى token.

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
URLGET $SHUKAH_BASE_URL/maeda/point-rateGET $ORDER_BASE_URL/api/v2/client/system_configs/point_ratesغيّر base URL والمسار. ولدى التطبيق هذا الاستدعاء بالفعل لوضع Merchant.
الـ Tokenلا حاجة إليه (200 بلا token على dev، 2026-10-01).لا حاجة إليه (200 بلا token على dev، 2026-10-01).لا شيء.
الصفوف على devخمسة صفوف: 0–139: 2 و140–153: 12 و154–250: 16 و251–450: 20 و451 and up: 30 (التُقط على dev، 2026-10-01).أربعة صفوف: 70–140: 2 و140–251: 4 و251–451: 10 و451 and up: 16.ستعرض بطاقة "Earn Points" شرائح مختلفة بعد التحوّل. وأي الجدولين هو الصحيح: غير موثّق بعد.
نهايات النطاقاتلا تتداخل النطاقات: 139 ثم 140.تشترك النطاقات في قيمة النهاية: فـ 140 تنهي صفًا وتبدأ التالي.إذا كان التطبيق يعدّ amount_to مشمولًا، فإجمالي 140 أو 251 بالضبط يطابق صفين.
الصف الأوليبدأ عند 0.يبدأ عند 70. ويضيف التطبيق صفًا للإجماليات الأقل من الصف الأول، بـ point_earn يساوي 0.الإجمالي الأقل من 70 يطابق ذلك الصف المضاف، فتعرض البطاقة 0 نقطة على Locate.
إجابة فارغةغير موثّق بعد.[] عندما لا يكون الخيار مضبوطًا.تقرأ شيفرة التطبيق الصف الأول دون فحص، لذا تجعل المصفوفة الفارغة الاستدعاء يفشل بخطأ 422 الخاص بالتطبيق نفسه.
قاعدة الإضافة (credit)لا ينطبق.ما زال order-v2 يحدد النقاط التي يكسبها الطلب من جدول Shukah، لا من هذا الجدول.قد تعرض البطاقة معدلًا يختلف عن النقاط التي يكسبها الطلب فعلًا.

POST /api/v2/client/gift-card-code​

يستبدل ShukahGET /maeda/giftcard-code/use✅ تم الاستبدال🚩 enableMerchantService
استبدال مباشر0 breaking · 5 للمراقبة
  • 🟡 يتحول GET ?code= إلى POST {code}؛ ولدى التطبيق الاستدعاءان بالفعل ويختار حسب الوضع (points.service.ts:128).
  • 🟡 فحص token نفسه كما في routes النقاط (user-profile-auth.guard.ts:60-62)، لذا يحصل تطبيق يملك JWT فقط على 401.
  • 🟡 لا يُعتدّ إلا بالأكواد التي يحتفظ بها order-v2؛ وقد يعطي كود صادر عن Shukah الرد 404 "The code not exist".
  • 🟡 يذهب الرصيد إلى add-point في Shukah ما لم يكن usePointService مفعّلًا، لذا يتحرك رصيد Locate لاحقًا.
  • 🟡 الأخطاء بصيغة Nest {message,statusCode}؛ ويعرض التطبيق message لذا يجب أن يصلح النص للمستخدمين.

مفتاح من جانب التطبيق: لا يستدعي التطبيق route Locate هذا إلا في وضع Merchant (إعدادات العميل enableMerchantService: true)؛ وإلا فما زال يستدعي Shukah.

تقبل Locate الطلب POST مع {code} بدل GET ?code= وتفحص الأكواد مقابل أكواد بطاقات الهدايا التي تحتفظ بها Locate بنفسها (وغير معروف هل الأكواد الصادرة عن Shukah موجودة هناك)؛ ويستدعيه التطبيق بالفعل في وضع Merchant. 0 استدعاء في جدول حركة production بتاريخ 2026-10-02 (3 أيام).

يخدم order-v2 هذا الـ route بنفسه ولا يمرّره إلى خدمة النقاط. لذا فإن قواعد الـ proxy وأخطاء 502 و504 في صفحات النقاط الأخرى لا تنطبق عليه. ولا يستدعيه التطبيق إلا في وضع Merchant.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <token>. أجاب dev بـ 401 بلا token. وهو يستخدم فحص الـ token نفسه الذي تستخدمه routes النقاط (عبر /api/v1/users مع x-auth-from: SHUKAH)، لذا فعلى dev اليوم يعمل token من Shukah ويحصل JWT من Locate على 401 (قراءة شيفرة؛ لم يُستدعَ مع token).الـ token الخاص بالعميل نفسه. ومنه يُؤخذ معرّف العميل.
Content-Typeheaderstringنعمapplication/json.الـ body هو JSON.
codebodystringنعمسلسلة غير فارغة. وcode المفقود أو الفارغ أو غير النصي يُرفض بـ 400 في خطوة التحقق.كود بطاقة الهدية.

الاستجابة (Response)​

المتوقع عند النجاح 201، لكنه لم يُلتقط: فالاستدعاء يستهلك كودًا ويضيف نقاطًا، لذا لم يُشغَّل. ولا يضبط الـ handler أي حالة أخرى، والقيمة الافتراضية في الـ framework لـ POST هي 201. وللـ body ستة مفاتيح:

الحقلالنوعيقبل nullالصيغة / القيودالوصف
total_pointsnumberلامبلغ الكود مضروبًا في سعر الصرف. وسعر الصرف هو إعداد exchanged_points (point_rate مقسومًا على riyad_rate)، أو 4 عندما يكون ذلك الإعداد مفقودًا أو غير مقروء.النقاط المضافة.
date_createdstringلاyyyy-mm-dd hh:mm:ss بالتوقيت العربي (UTC+3).وقت استرداد الكود.
descriptionstringلاPoints received for the <code> gift card.نص للسجل.
messagestringلاالنص العربي الثابت تم شحن الرصيد بنجاح.رسالة النجاح.
points_typestringلادائمًا earn.نوع الصف.
trans_typestringلادائمًا adjustment.نوع المعاملة.
{
"total_points": 40,
"date_created": "2026-10-01 12:00:00",
"description": "Points received for the ABC12345 gift card",
"message": "تم شحن الرصيد بنجاح",
"points_type": "earn",
"trans_type": "adjustment"
}

توضيحي، لم يُلتقط: مبني من نوع الاستجابة في locate-order-v2-api@d9e4e5f6. فالاستدعاء الحي سيستهلك كود بطاقة هدية ويضيف نقاطًا. والقيم مختلقة.

الأخطاء​

HTTPBodyمتى
401{"message":"Unauthorized","statusCode":401}لا يوجد token صالح للاستخدام. التُقط على dev، 2026-10-01، بلا token. وهذا شكل Nest، لا غلاف خدمة النقاط.
400خطأ تحقق من Nest. لم يُلتقط.code مفقود أو فارغ أو ليس سلسلة.
404الرسالة The code not exist، بشكل أخطاء Nest. لم تُلتقط.لا يوجد كود بتلك القيمة، أو الكود ما زال مسودة (draft).
400الرسالة This code has already been used، بشكل أخطاء Nest. لم تُلتقط.للكود عميل بالفعل أو هو معلَّم بأنه مستخدم.
400الرسالة The code has expired، بشكل أخطاء Nest. لم تُلتقط.الوقت الحالي تجاوز الساعة 24:00 بالتوقيت العربي (21:00 UTC) من تاريخ expired_at للكود.
500شكل أخطاء Nest. لم يُلتقط.فشلت إضافة النقاط ثلاث مرات متتالية، بفاصل ثانية واحدة.

جرّبه​

يستهلك هذا الاستدعاء كود بطاقة هدية ويضيف نقاطًا. استخدم كودًا أُنشئ للاختبار.

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" in Details
curl -s -X POST "$ORDER_BASE_URL/api/v2/client/gift-card-code" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d '{"code":"<test-code>"}'

المتوقع: الـ body ذو المفاتيح الستة المبيّن تحت «الاستجابة». استخدم token من Shukah على dev اليوم: فـ JWT من Locate يُرفض بفحص الـ token نفسه الذي تستخدمه routes النقاط (قراءة شيفرة). وبلا token تحصل على 401 المبيّن تحت «الأخطاء».

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
الـ method والكودGET $SHUKAH_BASE_URL/maeda/giftcard-code/use?code=<code>.POST $ORDER_BASE_URL/api/v2/client/gift-card-code مع {"code":"<code>"} في الـ body.غيّر الـ method وURL ومكان الكود. ولدى التطبيق الاستدعاءان بالفعل ويختار أحدهما بحسب وضعه.
الـ Tokentoken معتم (opaque) من Shukah.فحص الـ token نفسه الذي تستخدمه routes النقاط (عبر /api/v1/users)، أي token من Shukah على dev اليوم؛ أما JWT من Locate فيحصل على 401 (قراءة شيفرة؛ لم يُستدعَ مع token).يرسل التطبيق token Shukah الخاص به بالفعل في وضع Merchant. فأبقِه إلى أن يقبل فحص الـ token JWT من Locate.
مخزون الأكوادأكواد بطاقات الهدية الخاصة بـ Shukah.الأكواد الموجودة في قاعدة بيانات التاجر (merchant) في order-v2. وهل الأكواد الصادرة عن Shukah موجودة هناك أيضًا: غير موثّق بعد.قد يُرفض في Locate كود يعمل على Shukah.
الاستجابةغير موثّق بعد: لا يوجد التقاط من Shukah، لأن الاستدعاء يضيف نقاطًا.ستة مفاتيح: total_points وdate_created وdescription وmessage وpoints_type وtrans_type.يحلّل التطبيق المفاتيح الستة نفسها في الوضعين. قارن القيم قبل التحوّل.
أين تقع الإضافة (credit)لا ينطبق.Shukah أو خدمة النقاط، بحسب flag على الخادم. فيرسل الإضافة إلى Shukah عندما يكون الـ flag معطّلًا (وهو الافتراضي). وقيمته على dev: غير موثّق بعد.مع تعطيل الـ flag تصل الإضافة إلى Shukah أولًا، فلا يتحرك رصيد Locate إلا بعد المزامنة.
body الخطأغير موثّق بعد.401 هو {"message":"Unauthorized","statusCode":401} الخاص بـ Nest.يجب تغيير أي شيء يطابق نص الخطأ.

تنبيه​

  • يحتاج رصيد النقاط إلى token من Shukah على dev اليوم (فـ JWT من Locate يحصل على 401)، ولرقمه أربع خانات عشرية، وفي 2026-10-02 كان أقل من رصيد Shukah بـ 447.96 نقطة للعميل نفسه.
  • ما زال الدفع (checkout) يفحص رصيد Shukah عندما يدفع العميل طلبًا بالنقاط، والنقاط المصروفة على جانب لا تُخصم من الآخر (dev، 2026-10-02، خلال ثوانٍ؛ ولم يُفحص ما بعدها): فقد تختلف شاشة المحفظة عن شاشة الدفع، لذا اقرأ الرصيد من الجانب الذي تُنشأ فيه الطلبات.
  • جدول معدلات الكسب على dev يختلف عن جدول Shukah، وتفشل شيفرة التطبيق عند مصفوفة فارغة، وهو ما يُرجعه هذا الـ route عندما يكون الخيار مفقودًا.

التفاصيل (للـ backend)​

فُحص dev فقط، وعبر order-v2 فقط على https://dev.v2.api.order.locate.sa، والبادئة /api/v2/client. وخدمة النقاط على https://dev-point-api.locate.sa (مذكور في وثائق خدمة النقاط نفسها). أما أن order-v2 على dev يستخدم ذلك الـ host فغير مثبت: فالالتقاطات تُظهر خدمة نقاط عاملة خلفه، والـ URL نفسه غير موثّق بعد. الـ hosts في UAT وproduction: غير موثّق بعد.

في هذا القسم تعني OV2: المستودع locate-order-v2-api@d9e4e5f6 (2026-10-01) وتعني PS: المستودع locate-point-service@2c44ce3 (2026-10-01) وتعني AUTH: المستودع locate-authentication-service@6757aa4 (وكلها origin/develop، قُرئت مجددًا في 2026-10-02). أما استشهادات الجوال فتذكر المستودع كاملًا.

كيف تعمل الاستدعاءات الممرَّرة​

ينطبق هذا القسم على استدعاء الرصيد، ومع routes أخرى على صفحات النقاط الأخرى في هذه الوحدة. أما route معدلات الكسب وبطاقة الهدية الواردان بعده فغير ممرَّرين.

  • الـ Route. يستدعي التطبيق order-v2، الذي ليس له حارس ولا تحقق ولا نوع body في هذه الـ routes. ويمرّر الاستدعاء إلى خدمة النقاط ويُرجع الحالة والـ body دون تغيير (OV2: src/modules/client/point/point.controller.ts:17-29، OV2: src/modules/client/point/point.service.ts:64). ويذهب استدعاء الرصيد إلى GET /api/client/points في خدمة النقاط، بحد زمني 10 ثوانٍ (PS: services/api/src/routes/client.routes.ts:17، PS: services/api/src/handlers/client-points.ts:6-10).
  • ما يُمرَّر. فقط Authorization (كما استُلم) وAccept: application/json وفي POST الـ Content-Type: application/json. وما لا يُمرَّر: x-from-application وfrom-application وx-from-version وAccept-Language ومعرّفات الأجهزة وملفات الـ cookies، مع أن التطبيق يرسل عدة منها (OV2: src/modules/client/point/point.service.ts:54-60، mobile/locate-client@e960c3a: src/shared/api/helpers/getCommonHeaders.ts:4-11). واستدعاء الرصيد لا يمرّر أي query.
  • الخطأان اللذان ينشئهما order-v2. خطأ النقل (transport error) بالرمز ECONNABORTED أو ETIMEDOUT يصبح 504. وأي خطأ نقل آخر يصبح 502. وإذا لم يكن POINT_SERVICE_URL مضبوطًا، يكون كل استدعاء 502 (OV2: src/modules/client/point/point.service.ts:11-16,74-96). ويحمل body الـ 502 أيضًا message، وهو ما تتركه وثيقة مرجع order-v2 (OV2: docs/locate-point-client-api-reference.md:15-16).
  • أخطاء الـ framework قبل تشغيل الـ proxy (من إعادة إنتاج محلية على Nest 10.4.22؛ وإصدار runtime على dev غير معروف): الـ body المشوَّه بصيغة JSON يعطي 400 بشكل Nest {"message":"…","error":"Bad Request","statusCode":400}، والـ body الذي يتجاوز نحو 100 kB يعطي 413. وهذه ليست غلاف خدمة النقاط.
  • فحص الـ token. تشترط خدمة النقاط Authorization: Bearer <token> وتستدعي GET {AUTH_SERVICE_URL}/api/v1/users بذلك الـ token وبقيمة x-auth-from هي SHUKAH افتراضيًا ويمكن تغييرها بإعداد AUTH_VERIFY_APPLICATION. ولا يُمرَّر x-auth-from الخاص بالمستدعي أبدًا. وأي إجابة غير 2xx تصبح 401. ومعرّف العميل هو أول ما يوجد من client_id وuser_id وid في الإجابة، ويجب أن يكون عددًا صحيحًا موجبًا (PS: services/api/src/middleware/client-auth.ts:7-18، PS: services/api/src/services/client-auth.service.ts:15-37). وتعذّر الوصول إلى خدمة المصادقة هو 401 أيضًا (auth service unreachable: …)، لا 5xx، مع أن وثيقة مرجع order-v2 تقول 500 (PS: services/api/src/services/client-auth.service.ts:26-28، OV2: docs/locate-point-client-api-reference.md:201).
  • تُسجَّل أجسام الطلبات في log. يسجّل order-v2 أجسام الطلبات دون إخفاء، بما في ذلك cvv في استدعاء الدفع بالبطاقة (OV2: src/main.ts:38، OV2: src/shared/lib/base.lib.ts:32-43).

غلاف الأخطاء. كل خطأ في خدمة النقاط هو {"success":false,"error":{"code":…,"message":…,"details"?:…}}. وبالنسبة إلى VALIDATION_ERROR الصادر عن الـ schema، فإن details هو {"formErrors":[…],"fieldErrors":{"<field>":["<message>"]}}.

error.codeHTTP
VALIDATION_ERROR400
UNAUTHORIZED401
FORBIDDEN403
NOT_FOUND404
CONFLICT409
INSUFFICIENT_POINTS422
INTERNAL_ERROR500

(PS: packages/core/src/errors/error.ts:19-26,42-47.) وأي استثناء ليس AppError يصبح INTERNAL_ERROR مع Internal server error (PS: services/api/src/middleware/error-handler.ts:18-22).

أي tokens تعمل​

على dev، في 2026-10-02، أعاد GET /api/v2/client/points القيمة 200 لـ token معتم من Shukah، و401 {"success":false,"error":{"code":"UNAUTHORIZED","message":"Invalid or expired token"}} لـ JWT من Locate. وأعطت routes السجل والعروض والمناسبات والأسئلة الشائعة الزوج نفسه. وأجاب route معدل الكسب بـ 200 للاثنين، لأنه بلا حارس. وفي 2026-10-01 كان route الرصيد قد أجاب بـ 200 مع body متطابق للـ token كليهما، وكذلك GET /api/v1/users مع x-auth-from: SHUKAH.

لماذا يفشل JWT من Locate الآن:

  1. لا يفحص order-v2 الـ token على الـ routes الممرَّرة. بل يمرّر Authorization (OV2: src/modules/client/point/point.controller.ts:17-29، OV2: src/modules/client/point/point.service.ts:54-60).
  2. تسأل خدمة النقاط خدمة المصادقة GET /api/v1/users مع x-auth-from: SHUKAH، ما لم يكن AUTH_VERIFY_APPLICATION مضبوطًا، وتحوّل أي إجابة ليست 2xx إلى 401 Invalid or expired token (PS: services/api/src/services/client-auth.service.ts:17,22-29). والرسالة تطابق الالتقاط، فالرفض صادر عن خدمة المصادقة، لا عن غياب header. ويجري الحارس الخاص بـ order-v2 على route بطاقة الهدية الاستدعاء نفسه مع SHUKAH مكتوبة في الشيفرة (OV2: src/auth/guards/user-profile-auth.guard.ts:60-62).
  3. مع x-auth-from: SHUKAH تقرأ خدمة المصادقة auth_source (AUTH: src/middlewares/user.js:35-37,101؛ AUTH: src/services/setting.js:46-50). وقد أعادت إعدادات العميل العامة على dev القيمة "auth_source":"shukah" في 2026-10-02. وقد يتجاوزها متغير البيئة AUTH_SOURCE ولا يمكن قراءته من الخارج، فهذا مرجَّح جدًّا وليس مثبتًا. وفي ذلك الفرع يُمرَّر الـ token إلى /maeda/api/verify-token في Shukah، وأي فشل هو 401 (AUTH: src/middlewares/user.js:132-136,178-185). وقبلت Shukah على dev الـ token الخاص بـ Shukah و رفضت JWT من Locate في 2026-10-02 (استنتاج: لم يُستدعَ verify-token في Shukah مع الـ JWT نفسه). وفي 2026-10-01 كانت لا تزال تقبل الـ JWT، على الأرجح عبر دفع JWTs الجديدة من Locate إلى Shukah (انظر تفاصيل المستخدم).
  4. ومع ضبط auth_source على locate، سيحصل كل JWT من Locate على 401 أيضًا: فـ AUTH: src/services/client.js:12-17 يستدعي .cacheQuery على الـ Promise الذي تُرجعه Client.findByToken من نوع async (AUTH: src/models/client.js:163).

لذا لا يعمل JWT من Locate على هذه الـ routes إلا إذا قبلته Shukah على dev مرة أخرى، أو إذا صار auth_source هو locate وأُصلح ذلك العيب. وإلى ذلك الحين يحتاج التطبيق إلى token من Shukah لكل route محروس في صفحات النقاط. وأي tokens تقبلها هذه الـ routes في UAT وproduction: غير موثّق بعد.

route الرصيد​

  • يقرأ ثلاثة أشياء معًا: حساب نقاط العميل، وإعدادات النقاط، وأحدث صف كسب محسوب للعميل. ولا يكتب شيئًا، ولا يرسل push، ولا يستدعي Shukah (PS: services/api/src/services/client-points.service.ts:14-24).
  • لماذا أربع خانات عشرية. يخزّن السجل (ledger) النقاط بأربع خانات عشرية ويُكتب الرصيد كنص بأربع خانات عشرية. ولا تفعل formatShukahPoints سوى حذف الأصفار اللاحقة والنقطة الأخيرة، لذا لا تقرّب أبدًا (PS: packages/core/src/money/minor-units.ts:3-4,21-23، PS: services/api/src/services/client-points.mappers.ts:3-8؛ ويثبّتها PS: services/api/src/services/client-points.mappers.test.ts:31-41). والقيمة "514405.6788" هي الرصيد المخزَّن كما هو. وأي صفوف في السجل تعطيه الجزء .6788: غير موثّق بعد.
  • الفجوة مع Shukah. في 2026-10-01 اختلف الرصيد بمعامل يقارب 3.9966 (514405.6788 مقابل 128712.41). وفي 2026-10-02 أعادت Shukah 514853.64، أي أن بيانات Shukah على dev تغيرت، وكانت Locate أقل بـ 447.96 نقطة. وأحدث صف كسب في Locate (shukah_id 1351722، 2026-09-29 12:45:07) متأخر بـ 45 معرّفًا عن أحدث صف في Shukah (1351767، 2026-10-01 17:00:01)، والذيل الناقص يلائم الفجوة (استنتاج). وكانت تقارير التطابق في يوليو تضع الجانبين عند نحو 511,700 (PS: docs/reports/2026-07-17-client-api-parity.md:39-40).
  • قاعدة الانتهاء. null ما لم تقل الإعدادات inactivity_days ويوجد صف كسب محسوب. عندها يكون التاريخ هو وقت إنشاء أحدث صف كسب محسوب مضافًا إليه expiration_inactivity_days يومًا، معروضًا كتاريخ بتوقيت Asia/Riyadh (PS: services/api/src/services/client-points.mappers.ts:30-39؛ الاختبار PS: services/api/src/services/client-points.mappers.test.ts:18-29: 2026-07-16 زائد 60 يومًا يعطي 2026-09-14). وصفوف الانتهاء القديمة التي كتبتها Shukah ككسب سالب لا تُستثنى من «أحدث كسب»، لذا قد يدفع أحدها التاريخ إلى الأمام. أما الانتهاءات الجديدة فتُخزَّن كمصروفات ولا تفعل ذلك (PS: services/api/src/services/client-points.service.ts:17-21، PS: packages/core/src/points/sync-shukah-points.ts:97-102).
  • إعدادات dev (استنتاج). أرجع dev القيمة 2026-11-28. وأحدث صف في سجل Locate للعميل هو 2026-09-29، و2026-09-29 زائد 60 يومًا هو 2026-11-28، لذا يتصرف dev وكأن عدد أيام عدم النشاط 60. ولا يمكن قراءة وثيقة الإعدادات الحقيقية إلا عبر route المسؤول بمفتاح خدمة: غير موثّق بعد. وتُخزَّن الإعدادات مؤقتًا (cache) نحو 10 ثوانٍ (PS: packages/core/src/db/point-config.singleton.ts:20,89).
  • هل يُفرض التاريخ؟ مهمة الانتهاء تشغيل تجريبي (dry run) (تحاكي ولا تكتب شيئًا) عندما لا يكون مصدر النقاط merchant، أو عندما لا يكون نوع الانتهاء inactivity_days، أو عندما تكون النافذة أقل من يوم واحد. وإذا كان المصدر merchant ولم تكن MERCHANT_FIRST_ENABLED بقيمة true، فإن المهمة لا تحاكي: بل ترفض العمل وتفشل بـ INTERNAL_ERROR (PS: packages/core/src/points/expire-points.ts:245-254، PS: packages/core/src/points/merchant-first-guard.ts:21-28). وما زالت Shukah تكتب انتهاءاتها بنفسها. وعندما تنطبق المهمة، ينتهي الرصيد حالما يكون التاريخ اليوم أو أقدم بتوقيت Asia/Riyadh، وتعمل المهمة يوميًا عند 01:00 بتوقيت Asia/Riyadh (PS: packages/core/src/points/expire-points.ts:63-66، PS: docs/plans/2026-09-16-point-expiration-job.md:6). وهل تنطبق على dev: غير موثّق بعد.
  • رصيدان. لتسعير السلة والدفع (checkout) عندما يقبل المتجر الدفع بالنقاط، يستدعي order-v2 الرابط GET {SHUKAH_BASE_URL}/maeda/client/points بـ token العميل. ويستخدم 0 إذا انقضى تاريخ انتهاء Shukah أو فشل الاستدعاء. ولا يعتمد ذلك على الـ flag usePointService (OV2: src/shared/lib/handle-points.lib.ts:326-350، OV2: src/modules/client/orders/orders.service.ts:1128-1136,5036-5047).
  • الصرف غير متزامن. استدعاءات حية على dev، 2026-10-02 (العميل 356259): Shukah 514853.64 و Locate 514405.6788 قبل؛ و514743.64 و514405.6788 بعد طلب point على Shukah (الطلب 1257)؛ و514743.64 و514295.6788 بعد الطلب نفسه على Locate (الطلب 100012). وخصم كل جانب purchased_point_amount (110) من سجله فقط. ويضع طلب point في Locate طرفي Shukah في outbox لا شيء يفرّغه على k8s اليوم (انظر طلبات النقاط، التفاصيل). ولم يُفحص هل يصل صرف Shukah إلى Locate لاحقًا عبر التغذية الواردة.

route معدلات الكسب​

  • الـ controller هو @Controller('system_configs') مع @Get('/point_rates') ودون حارس (OV2: src/modules/client/system-configuration-options/system-configs.controller.ts:7-13).
  • تقرأ الخدمة الخيار point_rates لـ merchantId: 0 من قاعدة بيانات merchant-v2، عبر Redis hash cache. وتحلّل JSON، وترتّب بحسب amount_from (وnull تُعدّ 0)، وتُرجع [] عندما يكون الخيار مفقودًا. والنص غير الصالح كـ JSON يعطي [] أيضًا، لأن المحلّل يرجع إلى قائمة فارغة. أما 500 فيحتاج إلى فشل في القراءة، أو JSON ليس قائمة (فيُلقي الترتيب استثناءً) (OV2: src/modules/client/system-configuration-options/system-configs.service.ts:18-40، OV2: src/services/redis/system-config-cache.service.ts:35-62، OV2: src/shared/lib/base.lib.ts:58-75). ولا يستدعي خدمة النقاط ولا Shukah.
  • ليس هو الجدول الذي يحتسب order-v2 منه. تقرأ getPointsEarn الرابط /maeda/point-rate في Shukah (OV2: src/shared/lib/handle-points.lib.ts:25-43,98-107، OV2: src/services/points/points.service.ts:160). وليس في خدمة النقاط route للمعدلات، بل نموذج point_rates غير مستخدم (PS: packages/core/src/db/models/point-settings.model.ts:14-19).
  • مجموعة ثالثة من الشرائح. يقول نص الأسئلة الشائعة عن النقاط في الخدمتين إن 35–69 SR تكسب نقطة واحدة، و70–139 تكسب نقطتين، و140–250 تكسب 4، و251–450 تكسب 8، و451 فأكثر تكسب 16. وهذا لا يطابق أيًّا من الجدولين (انظر الأسئلة الشائعة عن النقاط). وأي الجدولين هو الصحيح: غير موثّق بعد.
  • التطبيق. في وضع Merchant يستدعي التطبيق هذا الـ route؛ وتجري المعالجة اللاحقة نفسها على الجدولين. فيضيف صفًا أول بـ amount_from تساوي null وamount_to تساوي amount_from للصف الأول ناقص واحد. والاختبار الخاص بذلك الصف صحيح دائمًا (إذ كُتب بـ ||)، لذا يُضاف الصف دائمًا. ويقرأ response.data?.[0].amount_from دون حماية، لذا فإن [] تُلقي استثناءً (mobile/locate-client@e960c3a: src/entities/points/model/api/services/points.service.ts:78-126).

route بطاقة الهدية​

  • @UseGuards(UserProfileGuard) و@Post()، مع نوع body هو {code} (@IsString()، @IsNotEmpty()) (OV2: src/modules/client/gift-card-code/gift-card-code.controller.ts:8-24، OV2: src/modules/client/gift-card-code/dtos/apply-code.dto.ts:4-9). ويجري الحارس استدعاء المصادقة نفسه الذي تجريه routes النقاط، مع x-auth-from المضمَّن في الشيفرة بالقيمة SHUKAH، والـ token المفقود يعطي {"message":"Unauthorized","statusCode":401} الخاص بـ Nest (OV2: src/auth/guards/user-profile-auth.guard.ts:58-72).
  • تبحث applyCode عن الكود في قاعدة بيانات merchant-v2 بحالة ليست DRAFT. والكود المجهول 404. والكود الذي له عميل أو بحالة USED يعطي 400. وينتهي الكود عند 24:00 بالتوقيت العربي (21:00 UTC) من تاريخ expired_at الخاص به، وهذا 400. والنقاط هي المبلغ مضروبًا في المعدل، مع المعدل من إعداد exchanged_points أو 4. وتمر الإضافة عبر PointsService.addPoints، بثلاث محاولات على الأكثر بفاصل ثانية، ثم 500. ثم يُعلَّم الكود بأنه مستخدم (OV2: src/modules/core/gift-card-code/gift-card-code.service.ts:40-94,96-114، OV2: src/modules/core/gift-card-code/constants/exchange-point-rate.ts:1).
  • أين تذهب الإضافة. ترسلها addPoints إلى /maeda/client/add-point في Shukah عندما يكون الـ flag usePointService بقيمة false (الافتراضي، ويُستخدم أيضًا عندما يتعذّر قراءة الـ flag)، وإلى خدمة النقاط عندما يكون true (OV2: src/services/points/points.service.ts:29-73، OV2: src/services/user-points/user-points.service.ts:39-52). وتقول وثائق خدمة النقاط إن الـ flag بقيمة false في كل بيئة وإنه لا خطة لتفعيله في production (PS: docs/integrations/2026-07-30-use-point-service-flag-routing.md:13-16,37). ولم تُقرأ قيمته على dev: غير موثّق بعد.
  • ليس لخدمة النقاط route لبطاقة الهدية، ولا تذكر وثائقها واحدًا.
  • تحاكي الاستجابة شكل Shukah: يحلّل التطبيق المفاتيح الستة نفسها في الوضعين (mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:215-222). ويستدعي التطبيق Shukah بـ GET مع code في query، وLocate بـ POST مع {code}، ويُختار ذلك بحسب وضع التطبيق (mobile/locate-client@e960c3a: src/entities/points/model/api/services/points.service.ts:128-168).

حيث تختلف الوثائق عن الشيفرة (القول للشيفرة)​

  • تقول وثائق الخدمة إن points هو الرصيد كما هو مخزَّن، وإنه "Shukah-formatted" (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:92، OV2: docs/locate-point-client-api-reference.md:97). وهذا يصح على الأصفار اللاحقة فقط: فالشيفرة تحتفظ بأربع خانات عشرية.
  • تقول الوثيقتان إن pointsExpirationDate تكون null إلى أن تُضبط إعدادات النقاط (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:463-467، OV2: docs/locate-point-client-api-reference.md:90,98,222-224). ويُرجع dev الآن تاريخًا.
  • تقول وثيقة خدمة النقاط إن الـ token المعتم يُمرَّر دائمًا إلى Shukah (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:56-58). وفي شيفرة المصادقة هذا فرع واحد فقط (انظر «أي tokens تعمل»).
  • يقول دليل الاختبار اليدوي بتاريخ 2026-09-21 إن order-v2 على dev يستدعي خدمة نقاط على localhost:4010، فيكون كل استدعاء 502 (PS: docs/plans/2026-09-21-point-order-manual-test-guide.md:24-32). وفي 2026-10-01 أجاب الـ proxy على dev بـ 200 مع بيانات السجل، لذا فالدليل قديم.
  • 2026-10-02: قالت هذه الصفحة إن token من Shukah وJWT من Locate عملا كلاهما على route الرصيد، وإن قسم «جرّبه» فيها استخدم JWT من Locate. وفي 2026-10-02 حصل الـ JWT على 401؛ وتذكر الصفحة الآن السبب، ويستخدم قسم «جرّبه» token من Shukah.
  • 2026-10-02: قالت هذه الصفحة إن رصيد Locate نحو أربعة أضعاف رصيد Shukah ("128712.41"). ورصيد Shukah على dev الآن "514853.64"، أي أعلى من رصيد Locate بـ 447.96 نقطة.

Sources​

  • locate-order-v2-api@d9e4e5f6: src/modules/client/point/point.controller.ts:17-29، src/modules/client/point/point.service.ts:11-16,54-60,64,74-96, src/main.ts:38، src/shared/lib/base.lib.ts:32-43,58-75، src/modules/client/system-configuration-options/system-configs.controller.ts:7-13، src/modules/client/system-configuration-options/system-configs.service.ts:18-40، src/services/redis/system-config-cache.service.ts:35-62، src/modules/client/gift-card-code/gift-card-code.controller.ts:8-24، src/modules/client/gift-card-code/dtos/apply-code.dto.ts:4-9، src/modules/core/gift-card-code/gift-card-code.service.ts:40-114، src/modules/core/gift-card-code/constants/exchange-point-rate.ts:1، src/auth/guards/user-profile-auth.guard.ts:58-72, src/services/points/points.service.ts:29-73,160، src/services/user-points/user-points.service.ts:39-52، src/shared/lib/handle-points.lib.ts:25-43,98-107,326-350، src/modules/client/orders/orders.service.ts:1128-1136,5036-5047
  • وثائق locate-order-v2-api@d9e4e5f6: docs/locate-point-client-api-reference.md:15-16,90,97,98,201,222-224
  • locate-point-service@2c44ce3: services/api/src/routes/client.routes.ts:17، services/api/src/handlers/client-points.ts:6-10, services/api/src/middleware/client-auth.ts:7-18، services/api/src/services/client-auth.service.ts:15-37، services/api/src/middleware/error-handler.ts:18-22, packages/core/src/errors/error.ts:19-26,42-47، services/api/src/services/client-points.service.ts:14-24، services/api/src/services/client-points.mappers.ts:3-8,30-39، services/api/src/services/client-points.mappers.test.ts:18-29,31-41، packages/core/src/money/minor-units.ts:3-4,21-23, packages/core/src/db/point-config.singleton.ts:20,89، packages/core/src/points/expire-points.ts:63-66,245-254، packages/core/src/points/merchant-first-guard.ts:21-28، packages/core/src/points/sync-shukah-points.ts:97-102، packages/core/src/db/models/point-settings.model.ts:14-19
  • وثائق locate-point-service@2c44ce3: docs/integrations/2026-07-16-client-facing-point-apis.md:56-58,92,463-467، docs/integrations/2026-07-30-use-point-service-flag-routing.md:13-16,37، docs/plans/2026-09-16-point-expiration-job.md:6، docs/plans/2026-09-21-point-order-manual-test-guide.md:24-32، docs/reports/2026-07-17-client-api-parity.md:39-40
  • نسخة الجوال اللحظية mobile/locate-client@e960c3a (الفرع development، 2026-02-26؛ شيفرة التطبيق نفسه، وليست من Locate): platforms/mobile/products/locate/screens/home/mainInner/Wallet/ui/Wallet/Wallet.tsx:78-85، src/features/PointsWallet/ui/PointsWallet/PointsWallet.tsx:29، src/entities/user/model/services/fetchUserInfo/fetchUserInfo.ts:55، src/features/selectRestaurantPaymentMethod/ui/SelectRestaurantPaymentMethod/SelectRestaurantPaymentMethod.container.tsx:64، platforms/mobile/products/locate/screens/restaurant/RestaurantCart/ui/RestaurantCart/RestaurantCart.tsx:301، platforms/mobile/shared/modules/core/cart/components/public/MerchantCartGeneralInfo/components/MerchantCartGeneralInfo/MerchantCartGeneralInfo.tsx:66، src/widgets/ApplyPointsPromoCode/model/services/applyPointsPromoCode.ts:27-50، src/entities/points/model/api/services/points.service.ts:30-44,78-168، src/entities/points/model/api/controllers/points.controller.ts:95-126، src/entities/points/model/api/dto/points.dto.ts:90-102,185-222، src/entities/settings/model/services/getServiceAvailable/getServiceAvailable.ts:48-54، src/shared/features/AppModeConfigs.ts:5-62, src/shared/api/helpers/getCommonHeaders.ts:4-11
  • locate-authentication-service@6757aa4: src/middlewares/user.js:35-37,101,132-136,178-185، src/services/setting.js:46-50، src/services/client.js:12-17، src/models/client.js:163
  • locate-point-service@2c44ce3، فحص الـ token: services/api/src/services/client-auth.service.ts:17,22-29؛ locate-order-v2-api@d9e4e5f6: src/auth/guards/user-profile-auth.guard.ts:60-62
  • التقاطات dev، 2026-10-02 (parity audit): points--shukah وpoints--locate-st وpoints--locate-jwt و point-rate--shukah وpoint-rate--locate-st وpoint-rate--locate-jwt؛ وإعدادات العميل على dev قُرئت من https://dev.go.locate.sa/api/v1/settings/client
  • التقاطات dev، 2026-10-01: locate-points وlocate-points-locate-token وshukah-points و probe-locate-points وprobe-shukah-points وlocate-point-rates وprobe-locate-point-rates و shukah-point-rate وprobe-shukah-point-rate وprobe-locate-gift-card-code و locate-points-faq وshukah-points-faq