محفظة النقاط
الخلفية
على 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
GET /maeda/client/points✅ تم الاستبدال- 🟡 يحصل 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)
| الحقل | الموضع | النوع | مطلوب | القيود | الوصف |
|---|---|---|---|---|---|
Authorization | header | string | نعم | Bearer <token>. يعمل token من Shukah على dev. أما JWT من Locate فيحصل على 401 Invalid or expired token (التُقط على dev، 2026-10-02؛ وكان يعمل في 2026-10-01): انظر «أي tokens تعمل» في «التفاصيل». | الـ token الخاص بالعميل نفسه. ومنه يُؤخذ معرّف العميل، لذا لا يرسل التطبيق معرّفًا أبدًا. |
| أي مفتاح query | query | — | لا | لا يُقرأ. ولا يمرّر order-v2 أي query في هذا الـ route، لذا فإن language الذي يضيفه التطبيق يُسقَط. | — |
الاستجابة (Response)
200 مع كائن مجرد (بلا غلاف status أو data):
| الحقل | النوع | يقبل null | الصيغة / القيود | الوصف |
|---|---|---|---|---|
id | string | لا | معرّف العميل كنص، مثل "356259". | معرّف العميل. |
user_id | string | لا | القيمة نفسها كما في id. | معرّف العميل. |
points | string | لا | عدد عشري في نص. تُحذف الأصفار اللاحقة والنقطة الأخيرة، وتبقى حتى أربع خانات عشرية: "514405.6788". و"0" عندما لا يكون للعميل حساب نقاط. ويمكن أن يكون سالبًا: فالمزامنة الواردة من Shukah لا تفحص السحب على المكشوف (overdraft). | الرصيد الحالي. |
pointsExpirationDate | string | نعم | 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.
الأخطاء
| HTTP | Body | متى |
|---|---|---|
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
| الجانب | Shukah | Locate | الأثر على التطبيق |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/client/points | GET $ORDER_BASE_URL/api/v2/client/points | غيّر base URL والمسار. |
| الـ Token | token معتم (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-Type | application/json | application/json; charset=utf-8 | لا شيء. |
GET /api/v2/client/system_configs/point_rates
GET /maeda/point-rate✅ تم الاستبدال🚩 enableMerchantService- 🟡 الشرائح مختلفة: 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)
| الحقل | الموضع | النوع | مطلوب | القيود | الوصف |
|---|---|---|---|---|---|
Authorization | header | string | لا | غير مطلوب: فقد أجاب dev بـ 200 بلا token ومعه. والـ token المُرسَل لا يُقرأ. | — |
| أي مفتاح query | query | — | لا | يُقبل ويُتجاهل. | — |
الاستجابة (Response)
200 مع مصفوفة مجردة، مرتبة بحسب amount_from من الأدنى إلى الأعلى (وnull تُعدّ 0). وهي []
عندما لا يكون خيار المعدل (rate option) مضبوطًا:
| الحقل | النوع | يقبل null | الصيغة / القيود | الوصف |
|---|---|---|---|---|
amount_from | number | نعم | إجمالي الطلب الذي يبدأ عنده الصف. | بداية النطاق، بالريال (SAR). |
amount_to | number | نعم | null في الصف الأخير. وعلى dev ينتهي كل صف عند القيمة التي يبدأ عندها الصف التالي (140 و251 و451). | نهاية النطاق، بالريال (SAR). |
point_earn | number | لا | عدد صحيح على 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 مشمولًا: غير
موثّق بعد، لأن شيفرة التطبيق لم تُقرأ لهذا الغرض.
الأخطاء
| HTTP | Body | متى |
|---|---|---|
500 | body خطأ من 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
| الجانب | Shukah | Locate | الأثر على التطبيق |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/point-rate | GET $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
GET /maeda/giftcard-code/use✅ تم الاستبدال🚩 enableMerchantService- 🟡 يتحول 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)
| الحقل | الموضع | النوع | مطلوب | القيود | الوصف |
|---|---|---|---|---|---|
Authorization | header | string | نعم | Bearer <token>. أجاب dev بـ 401 بلا token. وهو يستخدم فحص الـ token نفسه الذي تستخدمه routes النقاط (عبر /api/v1/users مع x-auth-from: SHUKAH)، لذا فعلى dev اليوم يعمل token من Shukah ويحصل JWT من Locate على 401 (قراءة شيفرة؛ لم يُستدعَ مع token). | الـ token الخاص بالعميل نفسه. ومنه يُؤخذ معرّف العميل. |
Content-Type | header | string | نعم | application/json. | الـ body هو JSON. |
code | body | string | نعم | سلسلة غير فارغة. وcode المفقود أو الفارغ أو غير النصي يُرفض بـ 400 في خطوة التحقق. | كود بطاقة الهدية. |
الاستجابة (Response)
المتوقع عند النجاح 201، لكنه لم يُلتقط: فالاستدعاء يستهلك كودًا ويضيف نقاطًا،
لذا لم يُشغَّل. ولا يضبط الـ handler أي حالة أخرى، والقيمة الافتراضية في الـ framework لـ POST هي
201. وللـ body ستة مفاتيح:
| الحقل | النوع | يقبل null | الصيغة / القيود | الوصف |
|---|---|---|---|---|
total_points | number | لا | مبلغ الكود مضروبًا في سعر الصرف. وسعر الصرف هو إعداد exchanged_points (point_rate مقسومًا على riyad_rate)، أو 4 عندما يكون ذلك الإعداد مفقودًا أو غير مقروء. | النقاط المضافة. |
date_created | string | لا | yyyy-mm-dd hh:mm:ss بالتوقيت العربي (UTC+3). | وقت استرداد الكود. |
description | string | لا | Points received for the <code> gift card. | نص للسجل. |
message | string | لا | النص العربي الثابت تم شحن الرصيد بنجاح. | رسالة النجاح. |
points_type | string | لا | دائمًا earn. | نوع الصف. |
trans_type | string | لا | دائمًا 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. فالاستدعاء
الحي سيستهلك كود بطاقة هدية ويضيف نقاطًا. والقيم مختلقة.
الأخطاء
| HTTP | Body | متى |
|---|---|---|
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
| الجانب | Shukah | Locate | الأثر على التطبيق |
|---|---|---|---|
| الـ 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 ومكان الكود. ولدى التطبيق الاستدعاءان بالفعل ويختار أحدهما بحسب وضعه. |
| الـ Token | token معتم (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.code | HTTP |
|---|---|
VALIDATION_ERROR | 400 |
UNAUTHORIZED | 401 |
FORBIDDEN | 403 |
NOT_FOUND | 404 |
CONFLICT | 409 |
INSUFFICIENT_POINTS | 422 |
INTERNAL_ERROR | 500 |
(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 الآن:
- لا يفحص 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). - تسأل خدمة النقاط خدمة المصادقة
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). - مع
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 (انظر تفاصيل المستخدم). - ومع ضبط
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 أعادت Shukah514853.64، أي أن بيانات Shukah على dev تغيرت، وكانت Locate أقل بـ 447.96 نقطة. وأحدث صف كسب في Locate (shukah_id1351722، 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 أو فشل الاستدعاء. ولا يعتمد ذلك على الـ flagusePointService(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و Locate514405.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 عندما يكون الـ flagusePointServiceبقيمة 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:163locate-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