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

طلبات النقاط

الخلفية​

طلب النقاط هو هدية نقاط من عميل إلى آخر، أو نقاط يشتريها العميل لنفسه. يختار العميل عرضًا، واختياريًا مناسبة، ومستلمًا، ثم يدفع بالنقاط أو بالبطاقة. وعلى Shukah يفعل التطبيق ذلك عبر POST /maeda/point-order و POST /maeda/point-order/get-description واستدعاءَي دفع على /maeda/order/checkout-payment-*. وفي Locate route لكل واحد من هذه الأربعة، إضافةً إلى route جديد يقرأ طلبًا واحدًا. وثلاثة من الأربعة adapt: يحتاج Create وrouteا الدفع إلى تغييرات في التطبيق. أما get-description فهو drop-in: لا يتغير سوى الـ base URL والمسار، لكن في TL;DR الخاص به بنودًا للمراقبة (المعرّفات والـ token). وستة أمور تستحق العناية:

  • الطلب الذي يستلمه التطبيق له مفاتيح مختلفة: فالمعرّف UUID اسمه correlation_id، والسعر هو money_amount. وتسمّي routes الدفع الطلب بـ correlation_id في المسار.
  • النقاط المصروفة على جانب لا تُخصم من الجانب الآخر (خلال ثواني التشغيل؛ ولم يُفحص هل يصل لاحقًا). فعلى dev في 2026-10-02 لم يخفض طلبٌ مدفوع بالنقاط إلا رصيد الجانب الذي أنشأه، لذا يجب أن يقرأ التطبيق الرصيد والسجل من الجانب الذي يُنشئ فيه الطلبات.
  • يقبل Create فقط payment_type بالقيمة point أو checkout. وقد يرسل التطبيق أيضًا payfort أو hyperpay، وتجيب Locate عن كليهما بـ 400.
  • يقع الدفع بالنقاط وrouteا الدفع خلف flagين على الخادم. وكان كلاهما مفعّلًا على dev في 2026-10-01. أما production فغير مؤكَّد.
  • ليس لـ Create مفتاح idempotency، وبعد 504 لا يستطيع التطبيق إيجاد الطلب الذي ربما أنشأه.
  • عدة استدعاءات أخرى للهدايا في التطبيق ليس لها route في Locate: قائمة الهدايا المرسَلة، واستدعاء token الدفع، واستدعاء STC Pay، واستدعاءات PayFort.

الميزة​

أين تحدث الاستدعاءات في التطبيق​

في نسخة التطبيق اللحظية بتاريخ 2026-02-26 يقع تدفق الهدية في نموذج إرسال الهدية وفي شاشة دفع الهدية. وتحتاج routes Locate الخمسة كلها إلى token، وعلى dev اليوم يجب أن يكون token من Shukah: فهي تستخدم فحص الـ token نفسه الذي تستخدمه routes النقاط الأخرى، والذي أعطى JWT من Locate 401 في 2026-10-02 (انظر Which tokens work). وفي 2026-10-01 عمل الـ token كلاهما على route الرصيد.

الخطوةالاستدعاءماذا يفعل التطبيق
1. كتابة الرسالةDescriptionبينما يملأ العميل النموذج، يطلب التطبيق الرسالة التي سيراها المستلم. وقد رصدت وثائق خدمة النقاط أنه يستدعيه مرتين لطلب واحد: مرة بالعرض، ومرة أخرى بالعرض والمناسبة.
2. إيجاد المستلمالبحث عن عميلعند الإرسال، يبحث التطبيق عن المستلم برقم الهاتف ويأخذ userId بوصفه to_client_id.
3. إنشاء الطلبCreateيعيد التطبيق أولًا تحميل الإعدادات العامة. ويرسل payment_type بالقيمة point عندما يدفع العميل بالنقاط، وإلا قيمة payment من تلك الإعدادات: payfort أو hyperpay أو checkout.
4أ. الدفع بالنقاطلا شيءيكون الطلب مدفوعًا بالفعل عند عودة create. فيعيد التطبيق تحميل السجل ويفتح تبويب الهدايا المرسَلة (Sent gifts) ويعرض "Gift sent successfully".
4ب. بطاقة محفوظةالدفع بالبطاقةيرسل التطبيق معرّف البطاقة المحفوظة وCVV. وتبدأ الإجابة خطوة 3-D Secure.
4ج. بطاقة جديدة أو Apple Pay أو STC Payالدفع بالجلسةيطلب التطبيق جلسة دفع ويعطيها لـ Checkout SDK. أما payment_type الذي يرسله (applePay أو stcpay أو card في النسخة اللحظية) فتتجاهله Locate. وقد أرسل التطبيق 2.31.5 القيمة applePay هنا في 2026-10-02، وأرسل STC Pay إلى route checkout-payment-stcpay الخاص بـ Shukah نفسها، وليس له route في Locate.
5. بعد الدفع بالبطاقةلا شيءعند نجاح الـ SDK يعيد التطبيق تحميل السجل ويفتح تبويب الهدايا المرسَلة.
لا تستخدمه أي شاشةقراءة الطلب (Get order)لدى التطبيق استدعاء لطلب واحد من Shukah، لكن لا شاشة تستخدمه.

وعندما لا تقول إعدادات العميل checkout، يدفع التطبيق عبر PayFort أو Hyperpay بدلًا من ذلك، باستدعاءات ليس لها route في Locate. وينطبق الأمر نفسه على استدعاء Frames-token في التطبيق، واستدعاء STC Pay لديه، واستدعاء CVV-token إلى خدمة الدفع (انظر «التفاصيل»).

دورة الحياة​

يبدأ الطلب الجديد بالحالة initial_order أو، عند الدفع بالنقاط، بالحالة successful. والحالات هي كلمات Shukah نفسها، محفوظة كما هي.

statuspayment_statusالمعنىكيف تُبلَغ
initial_ordernullأُنشئ ولم يُدفع.Create مع checkout. ويبقى الطلب هنا حتى تُخبر البوابة خدمة النقاط بالنتيجة. والطلب الذي لا يدفعه أحد يُترك كما هو.
successfulnullدُفع بالنقاط. خُصم من المرسِل وأُضيف للمستلم.Create مع point.
successfulCAPTUREDدُفع بالبطاقة. أُضيف للمستلم.تُبلّغ البوابة عن دفعة ملتقَطة (captured)، وعادةً خلال ثوانٍ من استدعاء الدفع.
payment_failedFAILEDرُفضت البطاقة. وهذا نهائي: فالـ routeان للدفع يجيبان الآن بـ 409، لذا على العميل إنشاء طلب جديد.تُبلّغ البوابة عن دفعة مرفوضة.
paidAUTHORIZEDلا يحدث إلا في الطلبات المنسوخة من Shukah. ولا تضبطه Locate أبدًا.لا يُبلَغ من routes Locate.

لا يوجد route للتحديث (refresh). فبعد استدعاء الدفع يراقب التطبيق قراءة الطلب (Get order) أو سجل النقاط حتى تتغير الحالة.

من يستطيع فعل ماذا​

الاستدعاءمنالجميع الآخرون
Createالمستدعي هو الدافع دائمًا. ويؤخذ الدافع من الـ token، لا من الـ body أبدًا.—
قراءة الطلب (Get order)الدافع أو المستلم.404 point order not found، وهي الإجابة نفسها لطلب مفقود.
الدفع بالبطاقة، الدفع بالجلسةالدافع فقط.404، وللمستلم أيضًا.
Descriptionأي عميل. والمُهدي في الجملة هو المستدعي.—

لا مفتاح idempotency، وما يجب فعله بعد 504​

ليس لـ Create مفتاح idempotency. فكل استدعاء ينشئ طلبًا جديدًا بـ correlation_id جديد. وإعادة المحاولة بعد انتهاء المهلة قد تنشئ إذن طلبًا ثانيًا.

و504 تعني أن النتيجة مجهولة. وتقول وثائق order-v2 نفسها إن على التطبيق قراءة الطلب بدل إعادة المحاولة عشوائيًا. وما تسمح به الشيفرة (قراءة للشيفرة، لا ما تنص عليه الوثائق):

  • القراءات، وdescription، والدفع بالجلسة. آمنة لإعادة المحاولة. فاستدعاء الدفع بالجلسة المكرر لا ينشئ إلا جلسة أخرى.
  • الدفع بالبطاقة. يعرف التطبيق correlation_id. اقرأ الطلب أولًا ولا تستدعِ الدفع بالبطاقة مرة أخرى حتى يصبح الطلب successful أو payment_failed. فالبوابة لا تجعل مرجع الدفع فريدًا، لذا لا يُثار التقاط ثانٍ إلا كتنبيه (alert) ولا يُسترد.
  • Create مع checkout. لم يحصل التطبيق على correlation_id، ولا يوجد route لسرد الطلبات ولا مفتاح idempotency، لذا لا يستطيع إيجاد الطلب. والطلب المكرر غير المدفوع غير ضار: فالطلبات التي لا تُدفع أبدًا تُترك كما هي.
  • Create مع point. الشيء نفسه، لكن التكرار يخصم من المرسِل مرتين إذا كان الاستدعاء الأول قد اكتمل. افحص الرصيد والسجل قبل إعادة المحاولة.

وكيف ينبغي للتطبيق إيجاد الطلب بعد 504 في create: غير موثّق بعد.

الـ Flags​

مفتاحان في خدمة النقاط يحددان هل يعمل الدفع أصلًا. وقد فُحص كلاهما على dev في 2026-10-01 باستدعاءات لا تكتب شيئًا.

الـ Flagما يتحكم فيهDev، فُحص 2026-10-01Productionما يراه التطبيق عند تعطيله
POINT_ORDER_POINT_ENABLEDCreate مع payment_type بالقيمة point. ولا يتأثر checkout.مفعّل. أجاب body دفع بالنقاط إلى نفسك بـ 400 a point order cannot be paid with points to yourself. ومع تعطيل الـ flag سيكون 403.غير مؤكَّد.403 {"success":false,"error":{"code":"FORBIDDEN","message":"paying a point order with points is disabled"}}، قبل أي بحث أو كتابة.
POINT_ORDER_CARD_ENABLEDالـ routeان للدفع كليهما.مفعّل. أجاب pay/session على معرّف طلب عشوائي بـ 404 point order not found. ومع تعطيل الـ flag سيكون 403.غير مؤكَّد.403 {"success":false,"error":{"code":"FORBIDDEN","message":"card payment for point orders is disabled"}}، قبل أي قراءة: حتى معرّف طلب مجهول يحصل على 403.

تقول وثائق أقدم (2026-09-21 و09-22 و09-25) إن الـ flagين غير مضبوطين في كل بيئة. وتقول وثيقة بتاريخ 2026-09-17 إن شرط الدفع بالنقاط تحقق على dev فقط، وإن production يبقى غير مضبوط حتى طرحه. وهذه الأقوال أقدم من الفحص، لذا ففي dev يسود الفحص. وبالنسبة إلى production: غير موثّق بعد. وتُضبط القيم في بيئة خدمة النقاط وتُدمج في الـ image الخاص بها، لذا يحتاج التغيير إلى build جديد للـ image.

المهل (Timeouts)​

ينتظر order-v2 خدمة النقاط 10 ثوانٍ، وينتظر 20 ثانية على routeَي الدفع. والحد الخاص بالتطبيق لـ order-v2 هو 20 ثانية، لذا قد يستسلم التطبيق على routes الدفع في اللحظة نفسها تقريبًا التي يُصدر فيها order-v2 504. وتواصل routes الدفع إلى خدمة الدفع، التي لها افتراضيها الخاص البالغ 10 ثوانٍ.

POST /api/v2/client/point-orders​

يستبدل ShukahPOST /maeda/point-order✅ تم الاستبدال🚩 POINT_ORDER_POINT_ENABLED
يحتاج تعديل التطبيق6 breaking · 4 للمراقبة
  • 🔴 لا يوجد point_order_id: يبني التطبيق pointOrderId = String(undefined) = "undefined"؛ واسم amount صار money_amount.
  • 🔴 وتغيب أيضًا: client_id وto_client_id وpoint_occasion_id وoccasion وdescription وpayment_point_order_id.
  • 🔴 تحتاج routes الدفع إلى correlation_id بصيغة UUID في المسار؛ احتفظ به من create وإلا أعطى الدفع 404.
  • 🔴 لم تُخصم النقاط المصروفة على جانب من الجانب الآخر خلال ثوانٍ (ولم يُفحص ما بعدها): اقرأ الرصيد والسجل من الجانب الذي يُنشئ الطلبات.
  • 🔴 القيمتان payment_type بـ payfort أو hyperpay (يرسل التطبيق قيمة الإعدادات) تعطيان 400؛ ولا تعمل إلا point أو checkout.
  • 🔴 يجب أن يكون point_offer_id وpoint_occasion_id معرّفَي hex من 24 حرفًا من قوائم Locate؛ والأعداد الصحيحة من Shukah تعطي 400.
  • 🟡 تجيب Shukah بـ 201 وLocate بـ 200؛ وتضيف Locate الحقول correlation_id وorder_no وpayment_status وorder_type.
  • 🟡 payment_type بقيمة point إلى معرّفك أنت يعطي 400؛ وإهداء النفس عبر checkout مسموح (وتسمح Shukah بكليهما).
  • 🟡 يحتاج point إلى POINT_ORDER_POINT_ENABLED (403 عند التعطيل)؛ وعلى dev لا يفتح أي 403 خطأً عامًا.
  • 🟡 لا يوجد idempotency key؛ وبعد 504 لا يعرف التطبيق معرّف الطلب أبدًا.

flag في خدمة النقاط لـ payment_type "point" (403 FORBIDDEN عند التعطيل). مفعّل على dev، وفُحص في 2026-10-01؛ وproduction غير مؤكَّد.

تقبل payment_type بالقيمة point أو checkout فقط (لا payfort ولا hyperpay) وتُرجع طلبًا بشكل مختلف (correlation_id وmoney_amount وغيرهما).

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <token>: token من Shukah على dev اليوم. أما JWT من Locate فيحصل على 401 عند فحص الـ token نفسه الذي تستخدمه routes النقاط الأخرى (2026-10-02): انظر Which tokens work.token الدافع.
Content-Typeheaderstringنعمapplication/json.الـ body هو JSON.
point_offer_idbodystringنعم24 حرف hex، تُفحص ولا تُحوَّل أبدًا. والعدد الصحيح من Shukah أو رقم أو "3" تعطي 400.id العرض من قائمة العروض.
to_client_idbodyintegerنعمعدد صحيح موجب. ويُقبل رقم JSON أو سلسلة رقمية. و" 7 " تصبح 7 وtrue تصبح 1. أما "" وnull والمفتاح المفقود و"abc" و"12.5" فتعطي 400.معرّف عميل المستلم، كما يُرجعه البحث عن عميل.
payment_typebodystringنعمpoint أو checkout بالضبط. وcard وpayfort وhyperpay تعطي 400.point: الدفع بنقاط الدافع في هذا الاستدعاء. checkout: إنشاء طلب غير مدفوع ليُدفع بالبطاقة.
point_occasion_idbodystring أو nullلا24 حرف hex. والمفتاح المفقود وnull و0 و"0" و"" كلها تعني بلا مناسبة. وأي قيمة أخرى غير hex (" " و5) تعطي 400.id المناسبة من قائمة المناسبات.
occasionbodystring أو nullلاتُحذف المسافات الزائدة، من 1 إلى 255 حرفًا. و"" والمسافات وnull تعني لا شيء. وغير السلسلة أو 256 حرفًا تعطي 400.نص حر، لمناسبة مخصصة.
أي مفتاح آخرbodyanyلايُسقَط.—

الـ body الذي ليس كائن JSON يفشل بـ 400 مع details بالقيمة {"formErrors":["Expected object, received null"],"fieldErrors":{}}.

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

200 مع كائن مجرد (وليس 201). وله تسعة مفاتيح. وتعود المفاتيح التسعة نفسها من قراءة الطلب (Get order).

الحقلالنوعيقبل nullالصيغة / القيودالوصف
correlation_idstringلاUUID v4، من 36 حرفًا.معرّف الطلب. ويوضع في مسار routes get والدفع.
order_nointegerنعمالقيمة الأولى 100000. وnull فقط في الطلبات المنسوخة من Shukah.رقم الطلب المقروء للبشر.
statusstringلاinitial_order أو paid أو payment_failed أو successful. وطلب checkout الجديد هو initial_order، والطلب المدفوع بالنقاط هو successful.حالة الطلب.
payment_statusstringنعمCAPTURED أو FAILED أو AUTHORIZED أو null. وnull عند الإنشاء.حالة البوابة.
point_amountnumberلاقيمة point_amount للعرض.النقاط التي يحصل عليها المستلم.
purchased_point_amountnumberنعمقيمة العرض. وnull عندما لا يكون للعرض سعر بالنقاط.النقاط التي تُخصم من الدافع عند الدفع بالنقاط.
money_amountstringلاخانتان عشريتان بالضبط، مثل "40.00".الريال (SAR) الذي تُخصم به البطاقة.
payment_typestringلاpoint أو checkout.صدى للطلب.
order_typestringلاgift عندما يُعثر على صف مناسبة، وإلا transfer. ولا يحدده النص الحر أبدًا.نوع الطلب.
{
"correlation_id": "2755e9a8-a6b5-4244-9f3d-777efe89ef80",
"order_no": 100010,
"status": "initial_order",
"payment_status": null,
"point_amount": 100,
"purchased_point_amount": 110,
"money_amount": "40.00",
"payment_type": "checkout",
"order_type": "transfer"
}

التُقط على dev، 2026-10-01 (العميل 356259): طلب checkout للعرض الأول، أُرسل إلى معرّف العميل نفسه، دون مناسبة. ولم يُدفع قط ويبقى على dev، لأنه لا يوجد route يحذف طلبًا.

checkout وpoint. طلب checkout يكتب صف طلب واحدًا ولا شيء غيره: فلا نقاط تتحرك ولا يُرسل شيء إلى Shukah أو خدمة الدفع. أما طلب point فيُسوّى في الاستدعاء نفسه. إذ يخصم من الدافع purchased_point_amount، ويضيف للمستلم point_amount، ويحفظ الطلب successful. وكل ذلك يحدث معًا أو لا يحدث إطلاقًا. وفي العروض الحية يُخصم من الدافع أكثر مما يحصل عليه المستلم، مثل 110 نقاط مقابل 100.

الهدايا الذاتية. قد يساوي to_client_id المستدعي في checkout: فشراء نقاط لنفسك هو الحالة الشائعة على Shukah (814 من 1,228 طلبًا). أما point فترفضه بـ 400.

الأخطاء​

تعمل الفحوص بهذا الترتيب لـ point: الـ flag، فالدفع لنفسه، فالعرض، فالمستلم، فالمناسبة، فـ purchased_point_amount، فملف الدافع، ثم الخصم. ولـ checkout: العرض، فالمستلم، فالمناسبة، ثم يُحفظ الطلب.

HTTPBodyمتى
400{"success":false,"error":{"code":"VALIDATION_ERROR","message":"Invalid point-order body","details":{"formErrors":[],"fieldErrors":{"payment_type":["Invalid enum value. Expected 'point' | 'checkout', received 'card'"]}}}}فشل الـ body في الفحص. والمثال هو payment_type: "card"، من تشغيل محلي لـ schema الخدمة، ولم يُلتقط مباشرة.
400{"success":false,"error":{"code":"VALIDATION_ERROR","message":"a point order cannot be paid with points to yourself"}}payment_type هو point وto_client_id هو المستدعي. التُقط على dev، 2026-10-01.
400الشكل نفسه، message بالقيمة to_client_id <id> does not exist.المستلم ليس عميلًا معروفًا. لم يُلتقط.
400الشكل نفسه، message بالقيمة point occasion <id> not found or inactive.معرّف المناسبة مجهول أو غير نشط. لم يُلتقط.
400الشكل نفسه، message بالقيمة point offer <id> cannot be paid with points: it has no purchased_point_amount.point على عرض بلا سعر بالنقاط. لم يُلتقط.
400الشكل نفسه، message بالقيمة client <id> does not exist.ليس للدافع ملف (لا يُبلَغ إلا مع point). لم يُلتقط.
403{"success":false,"error":{"code":"FORBIDDEN","message":"paying a point order with points is disabled"}}payment_type هو point والـ flag معطّل. يُفحص قبل أي بحث. لم يُلتقط، لأن الـ flag كان مفعّلًا.
404{"success":false,"error":{"code":"NOT_FOUND","message":"point offer <id> not found or inactive"}}العرض مجهول أو غير نشط أو محذوف. لم يُلتقط.
422{"success":false,"error":{"code":"INSUFFICIENT_POINTS","message":"insufficient points for client <id>"}}point، والرصيد أقل من purchased_point_amount. ولا يُنشأ طلب. لم يُلتقط.
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 سيئ)، أو أجابت دون معرّف عميل. لم يُلتقط على هذا الـ route. وأعطت routes النقاط الأخرى Invalid or expired token لـ JWT من Locate في 2026-10-02.
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 ثوانٍ. من order-v2. قد يكون الطلب موجودًا بالفعل: انظر «لا مفتاح idempotency» أعلاه. لم يُلتقط.

كل خطأ صادر عن خدمة النقاط له الغلاف (envelope) {"success":false,"error":{"code":"…","message":"…"}}.

جرّبه​

ينشئ هذا الاستدعاء طلبًا على dev لا يمكن حذفه. استخدم checkout: فـ point يحرّك النقاط.

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>'
export TO_CLIENT_ID='<recipient-client-id>'
curl -s -X POST "$ORDER_BASE_URL/api/v2/client/point-orders" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d "{\"point_offer_id\":\"$OFFER_ID\",\"to_client_id\":\"$TO_CLIENT_ID\",\"payment_type\":\"checkout\"}"

المتوقع: 200 والـ body ذو المفاتيح التسعة المبيّن تحت «الاستجابة»، مع status بالقيمة initial_order (التُقط على dev، 2026-10-02، بـ token من Shukah). أما JWT من Locate فيحصل على 401 عند فحص الـ token (قراءة شيفرة: لم يُستدعَ create بالـ JWT). وبلا token تحصل على 401 المبيّن تحت «الأخطاء».

الفروق عن Shukah​

استُدعي الجانبان على dev في 2026-10-02 (العميل 356259، بـ token من Shukah) بالعرض والمستلم وpayment_type نفسها: مرة بـ checkout ومرة بـ point، إلى عميل dev آخر. وأجابت Shukah بـ 201 مع صف طلبها (الـ body في صفحة المقارنة). وأجابت Locate بـ 200. وهذا طلب point على Locate:

{
"correlation_id": "add67555-e1e4-4fac-ae71-9675a98efffe",
"order_no": 100012,
"status": "successful",
"payment_status": null,
"point_amount": 100,
"purchased_point_amount": 110,
"money_amount": "40.00",
"payment_type": "point",
"order_type": "transfer"
}

التُقط على dev، 2026-10-02. ولطلب checkout (order_no 100011) المفاتيح التسعة نفسها، مع status بالقيمة initial_order وpayment_type بالقيمة checkout. ويبقى الطلبان على dev.

الجانبShukahLocateالأثر على التطبيق
URLPOST $SHUKAH_BASE_URL/maeda/point-orderPOST $ORDER_BASE_URL/api/v2/client/point-ordersغيّر base URL والمسار.
الـ Tokentoken معتم (opaque) من Shukah.token من Shukah فقط على dev اليوم: ونجح مع create في 2026-10-02. أما JWT من Locate فيحصل على 401 عند فحص الـ token في خدمة النقاط، كما في routes النقاط الأخرى (2026-10-02؛ ولم يُجرَّب مع create).يجب أن يواصل التطبيق إرسال token من Shukah هنا. وللبيئات الأخرى: غير موثّق بعد.
حالة HTTP201.200 (التُقط على dev، 2026-10-02).لا شيء إذا قبل التطبيق أي 2xx. ويجب تغيير الشيفرة التي تتحقق من 201 بالضبط.
payment_typeيرسل التطبيق point، أو قيمة payment من الإعدادات العامة: payfort أو hyperpay أو checkout.point أو checkout فقط. وpayfort وhyperpay تعطيان 400.إذا قالت الإعدادات payfort أو hyperpay، يفشل create. وهل تُسقَط تلك البوابات لطلبات النقاط: غير موثّق بعد. وعلى Shukah استخدم payfort في 1,126 من 1,225 طلبًا (وثيقة خدمة النقاط بتاريخ 2026-07-29).
المعرّفات في الـ bodyأعداد صحيحة كنص.point_offer_id وpoint_occasion_id سلسلتان من 24 حرفًا. وto_client_id دون تغيير.أرسل المعرّفات من قوائم Locate. والعدد الصحيح من Shukah يعطي 400.
مفاتيح الاستجابةاثنا عشر مفتاحًا: point_order_id وpayment_point_order_id وclient_id وto_client_id وpoint_amount وpurchased_point_amount وamount وstatus وpayment_type وpoint_occasion_id وoccasion وdescription (create، التُقط على dev، 2026-10-02؛ وهي مفاتيح صفوف طلبات Shukah نفسها في قائمة الهدايا المرسَلة). ويعرّف التطبيق occasion_id بدل point_occasion_id، فـ occasionId لديه دائمًا undefined.تسعة مفاتيح: correlation_id وorder_no وstatus وpayment_status وpoint_amount وpurchased_point_amount وmoney_amount وpayment_type وorder_type.لا يوجد على الجانبين إلا point_amount وpurchased_point_amount وstatus وpayment_type. واسم amount على Locate هو money_amount، وتغيب point_order_id وclient_id وto_client_id وpoint_occasion_id وoccasion وdescription وpayment_point_order_id. ويقرأ التطبيق point_order_id وamount وpayment_point_order_id ويمرّر معرّف الطلب إلى كل استدعاء دفع، لذا يجب إعادة بناء كائن الطلب من المفاتيح الجديدة.
معرّف الطلبpoint_order_id: رقم (1256 و1257 من create، 2026-10-02)، يُعاد إرساله كسلسلة ("1252") في أجسام الدفع من التطبيق 2.31.5 (2026-10-02).UUID، correlation_id.يأخذه route الدفع وroute القراءة في المسار. ويجيب create دون point_order_id، فيصبح pointOrderId في التطبيق النص "undefined" ويحصل كل استدعاء دفع على 404.
هدية لنفسهمسموحة.مسموحة لـ checkout، و400 لـ point.لا شيء لـ checkout.
كلمات الحالةinitial_order وpayment_failed وsuccessful وpaid (كما يعرّفها التطبيق).الكلمات نفسها. وطلب Locate لا يبلغ إلا initial_order وsuccessful وpayment_failed.لا شيء.
Idempotencyلا شيء.لا شيء.الخطر نفسه على الجانبين.
body الخطأغير موثّق بعد.غلاف خدمة النقاط الوارد تحت «الأخطاء».يجب تغيير أي شيء يطابق نص الخطأ.
رؤية Shukah للطلبلا ينطبق.طلب point يضع تحديثين في طابور لـ Shukah، لكن لا شيء يرسل ذلك الطابور على k8s اليوم. وطلب checkout لا يضع شيئًا.لا ترى Shukah طلبات Locate إلى أن يوجد مُفرِّغ (drainer). ويقرأ تبويب الهدايا المرسَلة من Shukah.
الرصيد بعد طلب pointخفّض الطلب 1257 رصيد Shukah بمقدار 110 (من 514853.64 إلى 514743.64). ولم يتحرك رصيد Locate.خفّض الطلب 100012، بعد ثوانٍ، رصيد Locate بمقدار 110 (من 514405.6788 إلى 514295.6788). ولم يتحرك رصيد Shukah. على dev، 2026-10-02.كاسر (breaking). السجلان (ledgers) غير متزامنين عند الصرف، على الأقل ليس خلال ثوانٍ؛ ولم يُفحص هل يصل الصرف لاحقًا. فإذا أنشأ التطبيق الطلبات على Locate وقرأ الرصيد أو السجل من Shukah (أو العكس)، عرض رصيدًا خاطئًا.

GET /api/v2/client/point-orders/:correlation_id​

endpoint جديد — لا يوجد ما يقابله في Shukah.

route جديد. تقول وثائق خدمة النقاط فقط "Read-through, no settle". ولدى التطبيق استدعاء لطلب Shukah واحد (GET /maeda/point-order/{id})، لكن لا شاشة تستخدمه، لذا فليس لهذا الـ route نظير في Shukah ضمن تدفق التطبيق. وأي استدعاء من Shukah يستبدله، إن وُجد: غير موثّق بعد.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <token>: token من Shukah على dev اليوم. أما JWT من Locate فيحصل على 401 عند فحص الـ token نفسه الذي تستخدمه routes النقاط الأخرى (2026-10-02): انظر Which tokens work.token الدافع أو المستلم.
correlation_idpathstringنعمأي سلسلة غير فارغة؛ ولا فحص للصيغة. وGET …/point-orders/description تُقرأ بوصفها المعرّف description.الـ correlation_id الذي أرجعه create.
أي مفتاح queryquery—لايمرّره order-v2 وتتجاهله خدمة النقاط.—

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

200 مع المفاتيح التسعة نفسها كما في استجابة create. وبالنسبة إلى طلب منسوخ من Shukah، يكون order_no هو null وتحمل status وpayment_status قيم Shukah نفسها.

{
"correlation_id": "2755e9a8-a6b5-4244-9f3d-777efe89ef80",
"order_no": 100010,
"status": "initial_order",
"payment_status": null,
"point_amount": 100,
"purchased_point_amount": 110,
"money_amount": "40.00",
"payment_type": "checkout",
"order_type": "transfer"
}

التُقط على dev، 2026-10-01 (العميل 356259): الطلب الذي أنشأته عينة create، وقُرئ مباشرة بعدها.

الاستدعاء للقراءة فقط. فلا يسوّي طلبًا ولا يحدّثه: وتتغير الحالة فقط عندما تُبلّغ البوابة بنتيجة. ولا توجد طريقة لسرد الطلبات، لذا لا يستطيع التطبيق قراءة إلا طلب يملك معرّفه بالفعل.

الأخطاء​

HTTPBodyمتى
404{"success":false,"error":{"code":"NOT_FOUND","message":"point order not found"}}لا يوجد طلب بهذا المعرّف للمستدعي بوصفه دافعًا أو مستلمًا. والطلب المفقود وطلب شخص آخر يعطيان الإجابة نفسها. لم يُلتقط.
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 سيئ)، أو أجابت دون معرّف عميل. لم يُلتقط على هذا الـ route. وأعطت routes النقاط الأخرى Invalid or expired token لـ JWT من Locate في 2026-10-02.
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 CORRELATION_ID='<correlation_id-from-create>'
curl -s "$ORDER_BASE_URL/api/v2/client/point-orders/$CORRELATION_ID" -H "Authorization: Bearer $SHUKAH_TOKEN"

المتوقع: 200 والـ body ذو المفاتيح التسعة المبيّن تحت «الاستجابة». والمعرّف الذي ليس لك يعطي 404 المبيّن تحت «الأخطاء». استخدم token من Shukah على dev اليوم، كما في كل route للنقاط.

POST /api/v2/client/point-orders/description​

يستبدل ShukahPOST /maeda/point-order/get-description✅ تم الاستبدال
استبدال مباشر0 breaking · 4 للمراقبة
  • 🟡 تُجيب Shukah بـ {"description":""} وLocate بجملة كاملة؛ للعرض فقط لذا لا يكسر شيئًا.
  • 🟡 يجب أن تكون المعرّفات معرّفات hex من 24 حرفًا من Locate؛ وتعطي "1" و"1" من Shukah الرد 400 VALIDATION_ERROR في الحقلين.
  • 🟡 الجملة بالإنجليزية دائمًا وتستخدم اسم المستدعي وهاتفه المقنَّع؛ وهي بيانات شخصية فلا تُسجَّل في السجلات.
  • 🟡 لم يُجرَّب JWT على هذا الـ route؛ ويرفضه الفحص نفسه على كل route نقاط آخر.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <token>: token من Shukah على dev اليوم. أما JWT من Locate فيحصل على 401 عند فحص الـ token نفسه الذي تستخدمه routes النقاط الأخرى (2026-10-02): انظر Which tokens work.token المُهدي.
Content-Typeheaderstringنعمapplication/json.الـ body هو JSON.
point_offer_idbodystringنعم24 حرف hex. والعدد الصحيح من Shukah أو الرقم يعطي 400.id العرض.
point_occasion_idbodystring أو nullلا24 حرف hex. والمفتاح المفقود وnull و"" و"0" و0 تعني لا شيء. وأي قيمة أخرى غير hex تعطي 400.id المناسبة.
occasionbodystring أو nullلاتُحذف المسافات الزائدة، من 1 إلى 255 حرفًا. و"" والمسافات وnull تعني لا شيء. النص الحر يغلب: فعندما لا يكون فارغًا، لا يُبحث عن معرّف المناسبة أصلًا.نص حر، لمناسبة مخصصة.

لا يوجد حقل للمستلم. فالجملة هي منظور المستلم، مبنية من المستدعي.

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

200 مع مفتاح واحد:

الحقلالنوعيقبل nullالصيغة / القيودالوصف
descriptionstringلاYou have received a gift {occasion} of {points} points from {giver name} ({masked phone}). انظر القواعد أدناه.النص الذي يعرضه التطبيق بينما يكتب العميل الهدية.
{
"description": "You have received a gift Birthday of 100 points from Test User (*****0000)"
}

التُقط على dev، 2026-10-01 (العميل 356259)، مع العرض الأول ومناسبة Birthday؛ واستُبدل اسم المُهدي وأرقام هاتفه بـ Test User و*****0000.

قواعد الجملة:

  • النص إنجليزي دائمًا. ولا يوجد مُدخل للغة.
  • occasion هو النص الحر عندما لا يكون فارغًا بعد حذف المسافات الزائدة. وإلا فهو التسمية الإنجليزية للمناسبة عند إعطاء point_occasion_id. وإلا فهو فارغ.
  • عند غياب المناسبة تحتفظ الجملة بمسافة مزدوجة: a gift of 400 points. وتحتفظ بها خدمة النقاط عن قصد، كما تفعل Shukah، حتى يستطيع التطبيق تبديل الـ hosts دون تغيير أي نص. لا تحذفها.
  • points هي point_amount للعرض، أي ما يحصل عليه المستلم، لا purchased_point_amount.
  • اسم المُهدي هو الاسم الأول واسم العائلة للمستدعي مفصولين بمسافة، أو Locate user عندما يكون كلاهما فارغًا.
  • الهاتف المقنَّع هو ***** يتبعها آخر أربعة أرقام من هاتف المستدعي، أو ***** فقط عندما لا توجد أرقام.
{
"description": "You have received a gift of 400 points from Test User (*****0000)"
}

من وثيقة خدمة النقاط locate-point-service@2c44ce3: docs/integrations/2026-07-16-client-facing-point-apis.md (آخر تغيير 2026-09-22، الأسطر 397-404، وقورنت هناك بـ Shukah على dev في 2026-09-21)، مع استبدال الاسم والأرقام. وليست التقاطًا من Locate: فلم يُجرَ أي استدعاء بلا مناسبة في 2026-10-01.

تحتوي الجملة الاسم الحقيقي للمستدعي وآخر أربعة أرقام من الهاتف. تعامل معها كبيانات شخصية عند تسجيلها في log.

الأخطاء​

HTTPBodyمتى
400{"success":false,"error":{"code":"VALIDATION_ERROR","message":"Invalid description body","details":{"formErrors":[],"fieldErrors":{"point_offer_id":["Required"]}}}}فشل الـ body في الفحص. والمثال هو {}، من تشغيل محلي لـ schema الخدمة، ولم يُلتقط مباشرة.
400الشكل نفسه، message بالقيمة point occasion <id> not found or inactive.لا يوجد نص حر، ومعرّف المناسبة مجهول أو غير نشط. لم يُلتقط.
400الشكل نفسه، message بالقيمة client <id> does not exist.ليس للمستدعي ملف. لم يُلتقط.
404{"success":false,"error":{"code":"NOT_FOUND","message":"point offer <id> not found or inactive"}}العرض مجهول أو غير نشط أو محذوف. لم يُلتقط.
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 سيئ)، أو أجابت دون معرّف عميل. لم يُلتقط على هذا الـ route. وأعطت routes النقاط الأخرى Invalid or expired token لـ JWT من Locate في 2026-10-02.
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>'
export OCCASION_ID='<id-from-the-occasions-list>'
curl -s -X POST "$ORDER_BASE_URL/api/v2/client/point-orders/description" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d "{\"occasion\":\"\",\"point_offer_id\":\"$OFFER_ID\",\"point_occasion_id\":\"$OCCASION_ID\"}"

المتوقع: 200 وdescription المبيّن تحت «الاستجابة»، مع اسم المستدعي نفسه وأرقام هاتفه. وبلا token تحصل على 401 المبيّن تحت «الأخطاء». استخدم token من Shukah على dev اليوم: فقد حصل على 200 في 2026-10-02، أما JWT من Locate فيحصل على 401 على routes النقاط الأخرى.

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
URLPOST $SHUKAH_BASE_URL/maeda/point-order/get-descriptionPOST $ORDER_BASE_URL/api/v2/client/point-orders/descriptionغيّر base URL والمسار.
الـ Tokentoken معتم (opaque) من Shukah.token من Shukah (200 على dev، 2026-10-02). ولم يُجرَّب JWT من Locate على هذا الـ route؛ والفحص نفسه يعطيه 401 على routes النقاط الأخرى.يجب أن يواصل التطبيق إرسال token من Shukah هنا.
المعرّفات في الـ bodyأعداد صحيحة كنص، مثل "1".سلاسل من 24 حرفًا. والعدد الصحيح من Shukah يعطي 400.أرسل المعرّفات من قوائم Locate.
الإجابة على dev{"description":""} لكل body جُرّب في 2026-10-01 (معرّفات كنص وكأرقام، ومع نص حر)، ومرة أخرى في 2026-10-02.الجملة الكاملة (التُقطت على dev، 2026-10-01).مع Shukah ستكون خانة الرسالة فارغة اليوم. وتقول وثيقة خدمة النقاط إن الجانبين تطابقا في 2026-09-21؛ أما في 2026-10-01 فلم يتطابقا. ولماذا تُرجع Shukah "" الآن: غير موثّق بعد.
الجملةالصياغة نفسها، مع المسافة المزدوجة عند غياب المناسبة (بحسب وثيقة خدمة النقاط، 2026-09-21).الصياغة نفسها والمسافة المزدوجة نفسها.لا شيء إذا لم تكن الإجابة فارغة.
body الخطأغير موثّق بعد.غلاف خدمة النقاط الوارد تحت «الأخطاء».يجب تغيير أي شيء يطابق نص الخطأ.

POST /api/v2/client/point-orders/:correlation_id/pay/card​

يستبدل ShukahPOST /maeda/order/checkout-payment-standalone✅ تم الاستبدال🚩 POINT_ORDER_CARD_ENABLED
يحتاج تعديل التطبيق2 breaking · 3 للمراقبة
  • 🔴 ينتقل معرّف الطلب من point_order_id في body (سلسلة "1252") إلى المسار بصيغة correlation_id من نوع UUID.
  • 🔴 يحتفظ التطبيق بـ pointOrderId فقط؛ ومن create في Locate تكون قيمته "undefined" فيعطي الاستدعاء 404 "point order not found".
  • 🟡 الـ body {id, cvv} نفسه؛ ويُتجاهل point_order_id وpayment_type (pay-point-order-card.ts:7-15).
  • 🟡 تتحول cvv بقيمة null إلى النص "null"؛ وإجابة 3DS هي body الخاص بـ payment-api بينما يتوقع نوع التطبيق 3ds.downgraded.
  • 🟡 يحتاج إلى POINT_ORDER_CARD_ENABLED (403 FORBIDDEN عند التعطيل)؛ وتجيب Shukah dev بـ 422 "not configured properly".

flag في خدمة النقاط لـ routes الدفع (403 FORBIDDEN عند التعطيل). مفعّل على dev، وفُحص في 2026-10-01؛ وproduction غير مؤكَّد.

لطلبات النقاط فقط: الـ body نفسه (id وcvv)، لكن الطلب يُسمّى في المسار بـ correlation_id لا point_order_id.

يدفع هذا الـ route طلب checkout ببطاقة حفظها العميل. والانتقال من checkout-payment-standalone في Shukah هو تغيير في URL ومعرّف طلب جديد، لا body جديد. وroute Shukah نفسه يُستخدم أيضًا لطلبات المطاعم؛ وتغطي هذه الصفحة استخدامه للهدايا فقط.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <token>: token من Shukah على dev اليوم. أما JWT من Locate فيحصل على 401 عند فحص الـ token نفسه الذي تستخدمه routes النقاط الأخرى (2026-10-02): انظر Which tokens work. ويُمرَّر الـ token الخام إلى خدمة الدفع. وهل تقبل خدمة الدفع JWT من Locate: غير موثّق بعد.token الدافع. والمستلم يحصل على 404.
Content-Typeheaderstringنعمapplication/json.الـ body هو JSON.
correlation_idpathstringنعمتُحذف المسافات الزائدة. ومسافات فقط تعطي 400 correlation_id is required.correlation_id الخاص بالطلب.
idbodystringنعمتُحذف المسافات الزائدة، حرف واحد على الأقل. وغير السلسلة تعطي 400.معرّف البطاقة المحفوظة (src_…). وهو card.tokenName في التطبيق.
cvvbodystringلاسلسلة أو رقم، تُحذف المسافات الزائدة، من 1 إلى 8 محارف. و"" تُعدّ مفقودة. وnull تصبح النص "null". وتحتاج إليه البوابة لبطاقات MADA. ويُمرَّر ولا يُخزَّن.رمز أمان البطاقة.
point_order_id وpayment_type وأي مفتاح آخرbodyanyلايُقبل ويُتجاهل. فالطلب يُسمّى بالمسار.يُبقى حتى يظل body Shukah القديم يعمل.

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

200 مع body خدمة الدفع، دون تغيير. ولم يُلتقط مباشرة: فالاستدعاء يذهب إلى بوابة الدفع وينشئ دفعة. ويبدأ التطبيق 3-D Secure من هذا الكائن. ولا تقرأ خدمة النقاط شيئًا منه، ويبقى الطلب initial_order حتى تُبلّغ البوابة بنتيجة.

{
"id": "pay_2c6e…",
"status": "Pending",
"reference": "lp-<correlation_id>",
"3ds": {"enrolled": "Y", "is_redirect": true},
"_links": {"redirect": {"href": "https://…/sessions-interceptor/sid_…"}}
}

من وثيقة خدمة النقاط locate-point-service@2c44ce3: docs/integrations/2026-07-16-client-facing-point-apis.md (القسم 3.6، آخر تغيير 2026-09-22)، مع اختصار القيم الطويلة في الوثيقة نفسها. لم يُلتقط مباشرة، لأن الاستدعاء ينشئ دفعة في البوابة والخطة تمنع ذلك.

عادت إحدى الاستجابات من 902 وهي Declined بالفعل دون _links.redirect. وعلى التطبيق التعامل مع هذه الحالة.

الأخطاء​

تعمل الفحوص بهذا الترتيب: الـ flag، فالطلب لهذا الدافع، فنوع الدفع، فنوع الطلب، فـ successful، فـ payment_failed، فملف الدافع، ثم المبلغ.

HTTPBodyمتى
400{"success":false,"error":{"code":"VALIDATION_ERROR","message":"Invalid pay-by-card body","details":{…}}}فشل الـ body في الفحص، مثل غياب id. لم يُلتقط.
400الشكل نفسه، message بالقيمة this point order is not paid by card.payment_type للطلب ليس checkout. لم يُلتقط.
400الشكل نفسه، message بالقيمة this point order is not payable by card.نُسخ الطلب من Shukah، أو لا معرّف إضافة (credit id) وراءه. لم يُلتقط.
400الشكل نفسه، message بالقيمة client <id> does not exist أو point order <correlation_id> has a non-positive money_amount.ليس للدافع ملف، أو المبلغ صفر أو سالب. لم يُلتقط.
403{"success":false,"error":{"code":"FORBIDDEN","message":"card payment for point orders is disabled"}}POINT_ORDER_CARD_ENABLED معطّل. يُفحص أولًا، قبل أي قراءة. لم يُلتقط، لأن الـ flag كان مفعّلًا.
404{"success":false,"error":{"code":"NOT_FOUND","message":"point order not found"}}لا يوجد طلب كهذا للمستدعي بوصفه دافعًا. والمستلم يحصل على هذا أيضًا. لم يُلتقط.
409{"success":false,"error":{"code":"CONFLICT","message":"point order is already paid"}}الطلب successful. لم يُلتقط.
409الشكل نفسه، message بالقيمة point order payment failed; create a new order.الطلب payment_failed. والرفض نهائي. لم يُلتقط.
حالة خدمة الدفعbody خدمة الدفع نفسه، دون تغيير. مثل 422 مع source_id_invalid لبطاقة محفوظة ميتة، أو 400 لـ CVV مفقود في MADA.أجابت خدمة الدفع بـ 4xx. لا توجد دفعة ويبقى الطلب قابلًا للدفع. لم يُلتقط.
500{"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}، أو message بالقيمة PAYMENT_API_URL not set.تعذّر الوصول إلى خدمة الدفع، أو لم تجب خلال 10 ثوانٍ، أو أجابت 5xx: ولا يُسجَّل شيء. لم يُلتقط.
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 سيئ)، أو أجابت دون معرّف عميل. لم يُلتقط على هذا الـ route. وأعطت routes النقاط الأخرى Invalid or expired token لـ JWT من Locate في 2026-10-02.
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"}}لم تجب خدمة النقاط خلال 20 ثانية. من order-v2. قد تكون الدفعة موجودة بالفعل في البوابة: اقرأ الطلب ولا تستدعِ مرة أخرى. لم يُلتقط.

جرّبه​

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

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 CORRELATION_ID='<correlation_id-from-create>'
export CARD_ID='<saved-card-id>'
curl -s -X POST "$ORDER_BASE_URL/api/v2/client/point-orders/$CORRELATION_ID/pay/card" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d "{\"id\":\"$CARD_ID\"}"

المتوقع: 200 وكائن الدفعة المبيّن تحت «الاستجابة». ويبقى الطلب initial_order حتى تُبلّغ البوابة بالنتيجة. استخدم token من Shukah على dev اليوم، كما في كل route للنقاط.

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
URLPOST $SHUKAH_BASE_URL/maeda/order/checkout-payment-standalonePOST $ORDER_BASE_URL/api/v2/client/point-orders/{correlation_id}/pay/cardغيّر base URL والمسار، وضع فيه correlation_id الخاص بالطلب.
الـ Body{id, cvv, point_order_id}.{id, cvv}. ويُقبل point_order_id وpayment_type ويُتجاهلان.لا شيء: الأسماء نفسها.
معرّف الطلبpoint_order_id في الـ body، وهو سلسلة مثل "1252" (أجسام الدفع من التطبيق 2.31.5، التُقطت على dev، 2026-10-02).UUID، correlation_id، في المسار.يحتفظ التطبيق بـ pointOrderId فقط. وعليه أن يحتفظ بالمعرّف الجديد من create.
الـ Tokentoken معتم (opaque) من Shukah.token من Shukah فقط على dev اليوم: فـ JWT من Locate يحصل على 401 عند الفحص نفسه الذي تستخدمه routes النقاط الأخرى (2026-10-02). ويذهب الـ token الخام أيضًا إلى خدمة الدفع.يجب أن يواصل التطبيق إرسال token من Shukah هنا.
Shukah على dev422 {"name":"Unprocessable Content","message":"Checkout payment is not configured properly.",…} لمدفوعات طلبات النقاط (2026-10-02).لم يُستدعَ مباشرة.لا تستطيع Shukah على dev قبول دفعة نقاط اليوم، لذا لم يمكن مقارنة الجانبين مباشرة.
الـ Flagلا شيء.POINT_ORDER_CARD_ENABLED: مفعّل على dev (فُحص 2026-10-01)، وproduction غير مؤكَّد.إذا كان الـ flag معطّلًا، فكل استدعاء 403.
أخطاء الدفع4xx الخاص بخدمة الدفع نفسها.تُمرَّر دون تغيير بالحالة والـ body نفسيهما.لا شيء: فالتطبيق يتعامل مع هذا الشكل بالفعل.
الاستجابةنوع التطبيق فيه 3ds.downgraded.عينة الوثيقة فيها 3ds.is_redirect، وقد تعود دفعة مرفوضة دون _links.redirect، الذي يشترطه نوع التطبيق.افحص بدء 3-D Secure مقابل استجابة حقيقية.
مدفوعات الهدايا الأخرىيستخدم التطبيق أيضًا checkout-payment وcheckout-payment-stcpay واستدعاءات PayFort واستدعاء CVV-token.لا route لها. ولا يوجد سوى هذا والدفع بالجلسة.انظر «التفاصيل» للاستدعاءات التي لم تُستبدل.
body الخطأغير موثّق بعد.غلاف خدمة النقاط الوارد تحت «الأخطاء».يجب تغيير أي شيء يطابق نص الخطأ.

POST /api/v2/client/point-orders/:correlation_id/pay/session​

يستبدل ShukahPOST /maeda/order/checkout-payment-flow✅ تم الاستبدال🚩 POINT_ORDER_CARD_ENABLED
يحتاج تعديل التطبيق2 breaking · 3 للمراقبة
  • 🔴 ينتقل معرّف الطلب من point_order_id في body (سلسلة "1253") إلى المسار بصيغة correlation_id من نوع UUID.
  • 🔴 يحتفظ التطبيق بـ pointOrderId فقط؛ ومن create في Locate تكون قيمته "undefined" فيعطي الاستدعاء 404.
  • 🟡 يُتجاهل payment_type بقيمة "applePay" وpoint_order_id؛ وتعرض جلسة Checkout المحافظ الإلكترونية.
  • 🟡 الـ body اختياري ({rememberMe})؛ والسلسلة "true" أو "false" تُحوَّل، أما الرقم فيعطي 400.
  • 🟡 يحتاج إلى POINT_ORDER_CARD_ENABLED؛ وتجيب Shukah dev بـ 422 "Checkout payment is not configured properly."

flag في خدمة النقاط لـ routes الدفع (403 FORBIDDEN عند التعطيل). مفعّل على dev، وفُحص في 2026-10-01؛ وproduction غير مؤكَّد.

لطلبات النقاط فقط: الـ body نفسه (rememberMe)؛ ويُتجاهل point_order_id وpayment_type ويُسمّى الطلب بـ correlation_id في المسار.

يبدأ هذا الـ route جلسة دفع لطلب checkout. ويستخدمه Checkout SDK لبطاقة جديدة وApple Pay وGoogle Pay وSTC Pay. ولا يخصم شيئًا بنفسه. وroute Shukah نفسه يُستخدم أيضًا لطلبات المطاعم؛ وتغطي هذه الصفحة استخدامه للهدايا فقط.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <token>: token من Shukah على dev اليوم. أما JWT من Locate فيحصل على 401 عند فحص الـ token نفسه الذي تستخدمه routes النقاط الأخرى (2026-10-02): انظر Which tokens work.token الدافع. والمستلم يحصل على 404.
Content-Typeheaderstringلاapplication/json. والـ body الفارغ أو المفقود صالح ويعني {}.الـ body هو JSON.
correlation_idpathstringنعمتُحذف المسافات الزائدة. ومسافات فقط تعطي 400 correlation_id is required.correlation_id الخاص بالطلب.
rememberMebodybooleanلاtrue أو false أو النص "true" أو "false". الافتراضي false. والرقم أو null أو أي نص آخر يعطي 400.حفظ البطاقة، مسجَّلة باسم الدافع، بعد دفعة ناجحة.
point_order_id وpayment_type وأي مفتاح آخرbodyanyلايُقبل ويُتجاهل.يُبقى حتى يظل body Shukah القديم يعمل.

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

200 مع جلسة خدمة الدفع، دون تغيير. ولم تُلتقط مباشرة: فالاستدعاء يذهب إلى بوابة الدفع. ويبدأ Checkout SDK من هذا الكائن. ويمكن التخلي عن الجلسة وإنشاء أخرى على الطلب نفسه؛ وقد شوهدت تسع على مرجع واحد، ولا واحدة منها خطأ.

{
"id": "ps_3J5G…",
"payment_session_secret": "<redacted-secret>",
"payment_session_token": "<redacted-token>",
"_links": {"self": {"href": "…"}}
}

من وثيقة خدمة النقاط locate-point-service@2c44ce3: docs/plans/2026-09-21-point-order-manual-test-guide.md (الأسطر 336-343، آخر تغيير 2026-09-22)، مع استبدال السر (secret) والـ token. ولم تُلتقط مباشرة، لأن الاستدعاء يفتح جلسة دفع في البوابة والخطة تمنع ذلك.

الأخطاء​

الحراس أنفسهم الخاصون بـ الدفع بالبطاقة، بالترتيب نفسه. وتُسرد الصفوف كاملة.

HTTPBodyمتى
400{"success":false,"error":{"code":"VALIDATION_ERROR","message":"Invalid pay-by-session body","details":{…}}}rememberMe ليست قيمة تشبه boolean. لم يُلتقط.
400الشكل نفسه، message بالقيمة this point order is not paid by card أو this point order is not payable by card.الطلب ليس طلب checkout أنشأته Locate. لم يُلتقط.
400الشكل نفسه، message بالقيمة client <id> does not exist أو point order <correlation_id> has a non-positive money_amount.ليس للدافع ملف، أو المبلغ صفر أو سالب. لم يُلتقط.
403{"success":false,"error":{"code":"FORBIDDEN","message":"card payment for point orders is disabled"}}POINT_ORDER_CARD_ENABLED معطّل. يُفحص أولًا، قبل أي قراءة. لم يُلتقط، لأن الـ flag كان مفعّلًا.
404{"success":false,"error":{"code":"NOT_FOUND","message":"point order not found"}}لا يوجد طلب كهذا للمستدعي بوصفه دافعًا. التُقط على dev، 2026-10-01، بمعرّف طلب عشوائي (وبهذه الطريقة فُحص الـ flag).
409{"success":false,"error":{"code":"CONFLICT","message":"point order is already paid"}}، أو message بالقيمة point order payment failed; create a new order.الطلب successful، أو هو payment_failed. لم يُلتقط.
حالة خدمة الدفعbody خدمة الدفع نفسه، دون تغيير.أجابت خدمة الدفع بـ 4xx. لم يُلتقط.
500{"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}، أو message بالقيمة PAYMENT_API_URL not set.تعذّر الوصول إلى خدمة الدفع، أو لم تجب خلال 10 ثوانٍ، أو أجابت 5xx. لم يُلتقط.
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 سيئ)، أو أجابت دون معرّف عميل. لم يُلتقط على هذا الـ route. وأعطت routes النقاط الأخرى Invalid or expired token لـ JWT من Locate في 2026-10-02.
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"}}لم تجب خدمة النقاط خلال 20 ثانية. من order-v2. و504 هنا آمن لإعادة المحاولة: فالاستدعاء المكرر لا ينشئ إلا جلسة أخرى. لم يُلتقط.

جرّبه​

يفتح هذا الاستدعاء جلسة دفع في البوابة. شغّله على dev فقط، بطلب أنشأته بنفسك.

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 CORRELATION_ID='<correlation_id-from-create>'
curl -s -X POST "$ORDER_BASE_URL/api/v2/client/point-orders/$CORRELATION_ID/pay/session" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d '{"rememberMe":false}'

المتوقع: 200 وكائن الجلسة المبيّن تحت «الاستجابة». وcorrelation_id عشوائي يعطي 404 المبيّن تحت «الأخطاء» (مع تفعيل الـ flag). استخدم token من Shukah على dev اليوم، كما في كل route للنقاط.

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
URLPOST $SHUKAH_BASE_URL/maeda/order/checkout-payment-flowPOST $ORDER_BASE_URL/api/v2/client/point-orders/{correlation_id}/pay/sessionغيّر base URL والمسار، وضع فيه correlation_id الخاص بالطلب.
الـ Body{point_order_id, payment_type, rememberMe}.{rememberMe}. ويُقبل point_order_id وpayment_type ويُتجاهلان.لا شيء: الأسماء نفسها.
payment_typeأرسل التطبيق 2.31.5 القيمة applePay (التُقط على dev، 2026-10-02). ويسمح نوع النسخة اللحظية أيضًا بـ stcpay وcard، لكن التطبيق 2.31.5 يرسل STC Pay إلى route checkout-payment-stcpay الخاص بـ Shukah نفسها.يُتجاهل. وتعرض جلسة Checkout المحافظ (wallets).لا تُختار المحفظة على الخادم. افحص أن الـ SDK يعرض المحفظة الصحيحة. وليس لـ STC Pay route خاص في Locate.
معرّف الطلبسلسلة في الـ body: {"point_order_id":"1253",…} في التقاط التطبيق 2.31.5 (2026-10-02).UUID، في المسار.على التطبيق الاحتفاظ بـ correlation_id من create.
Shukah على devاستدعاها التطبيق 2.31.5 على dev في 2026-10-02 وحصل على 422 Checkout payment is not configured properly.لم يُستدعَ مباشرة.لا تستطيع Shukah على dev قبول دفعة نقاط اليوم، لذا لم يمكن مقارنة الجانبين مباشرة.
الـ Flagلا شيء.POINT_ORDER_CARD_ENABLED: مفعّل على dev (فُحص 2026-10-01)، وproduction غير مؤكَّد.إذا كان الـ flag معطّلًا، فكل استدعاء 403.
الاستجابةغير موثّق بعد (لم يُلتقط).جلسة خدمة الدفع، دون تغيير.نوع التطبيق فيه id وpayment_session_secret وpayment_session_token و_links.self، وهي المفاتيح نفسها في عينة الوثيقة.
body الخطأغير موثّق بعد.غلاف خدمة النقاط الوارد تحت «الأخطاء».يجب تغيير أي شيء يطابق نص الخطأ.

تنبيه​

  • للطلب مفاتيح جديدة (correlation_id وmoney_amount) وتأخذ routes الدفع correlation_id في المسار، لذا يجب على التطبيق الاحتفاظ به من create.
  • ليس لـ Create مفتاح idempotency: فبعد 504 لا يعرف التطبيق معرّف الطلب أبدًا، وقد تنشئ إعادة المحاولة طلبًا ثانيًا أو، مع point، تخصم مرتين.
  • يحتاج الدفع بالنقاط وrouteا الدفع إلى flagين. وكلاهما مفعّل على dev في 2026-10-01؛ وproduction غير مؤكَّد، والـ flag المعطّل يعطي 403.

التفاصيل (للـ 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 المصدر (خدمة النقاط)ما يُمرَّرالحد
POST /api/v2/client/point-ordersPOST /api/client/point-ordersAuthorization والـ body كما استُلم10 ث
GET /api/v2/client/point-orders/:correlation_idGET /api/client/point-orders/<id>، مع تمرير المعرّف عبر encodeURIComponentAuthorization والـ query كله (تتجاهله خدمة النقاط)10 ث
POST /api/v2/client/point-orders/descriptionPOST /api/client/point-orders/descriptionAuthorization والـ body10 ث
POST /api/v2/client/point-orders/:correlation_id/pay/cardPOST /api/client/point-orders/<id>/pay/cardAuthorization والـ body20 ث
POST /api/v2/client/point-orders/:correlation_id/pay/sessionPOST /api/client/point-orders/<id>/pay/sessionAuthorization والـ body20 ث

(OV2: src/modules/client/point/point.controller.ts:10,110-201، PS: services/api/src/routes/client.routes.ts:23-29.) يُركَّب route الحرفي POST point-orders/description قبل GET point-orders/:correlation_id، لذا فإن GET …/point-orders/description الشاردة تُقرأ بوصفها معرّف الطلب description وتجيب 404 point order not found (PS: services/api/src/routes/client.routes.ts:24-27، PS: services/api/src/routes/client.routes.test.ts:43-75). ويحتفظ route الـ description بالافتراضي 10 ثوانٍ، لأنه يركّب نصًا فقط (OV2: src/modules/client/point/point.controller.ts:126-131). وتحتفظ POST بحالة المصدر: فـ 200 تبقى 200، لا 201 الافتراضية في Nest (شوهد في إعادة إنتاج محلية لنمط الـ proxy؛ OV2: src/modules/client/point/point.service.ts:64). أما routes خدمة النقاط الداخلية، بما فيها الـ webhook و…/refresh، فلا تُمرَّر أبدًا (OV2: docs/plans/2026-09-16-point-order-proxy-plan.md:21، OV2: docs/locate-point-client-api-reference.md:46).

Create​

  • يفحص الـ handler الـ body بـ schema، ثم تشغّل createPointOrder الفحوص بالترتيب الوارد تحت «الأخطاء» (PS: services/api/src/handlers/create-point-order.ts:10-47، PS: packages/core/src/points/create-point-order.ts:66-186). المعرّفان كلاهما سلسلتا hex من 24 حرفًا ولا يُحوَّلان أبدًا (PS: services/api/src/lib/object-id.ts:12-14). ويجب أن يوجد المستلم في مجموعة clients المشتركة (PS: packages/core/src/points/client-lookup.ts:20-35).
  • checkout: إدراج صف طلب واحد وزيادة عدّاد لـ order_no. ولا صف في السجل (ledger) ولا صف في outbox (PS: packages/core/src/points/create-point-order.ts:162-172). ويبقى الطلب غير مدفوع ويُترك كما هو إن هُجر (PS: docs/plans/2026-09-10-point-order-write-path-design.md:692).
  • point: معاملة واحدة تكتب مصروفات الدافع، وكسب المستلم، والطلب (successful، وpayment_status بالقيمة null)، وصفّي outbox موجهين إلى Shukah (mirror_reduce_point وmirror_add_point). وتُنفَّذ المصروفات أولًا، فيكون حارس السحب على المكشوف (overdraft guard) هو فحص الرصيد، ولا يُترك شيء عند الفشل (PS: packages/core/src/points/create-point-order.ts:193-258، PS: packages/core/src/points/apply-point-txn.ts:71-86). ولا شيء يفرّغ الـ outbox على k8s اليوم، لذا لا ترى Shukah هذين الطرفين إلى أن يوجد مُفرِّغ (drainer) (PS: CLAUDE.md:202-205، PS: .env.example:95-97، PS: docs/plans/2026-09-10-point-order-write-path-design.md:811). وتتفق الاستدعاءات الحية في 2026-10-02 مع ذلك: فطلب point على Locate لم يغيّر رصيد Shukah، وطلب على Shukah لم يغيّر رصيد Locate، خلال ثواني التشغيل. ولم يُفحص هل يلحق أيٌّ من الجانبين لاحقًا.
  • الهامش (Markup). على dev يدفع الدافع 110 مقابل 100، ثم 240 و360 و480 و600. ومثال وثيقة التصميم "10 % markup" هو العرض الأول، وتحمل البقية 20 % (PS: docs/plans/2026-08-03-point-order-flow-design.md:31).
  • ما يخزّنه الطلب. offer_snapshot للمبالغ، وتسمية المناسبة من صف المناسبة، والوصف الإنجليزي من جهة المرسِل، وثلاثة معرّفات ثابتة (expense_correlation_id وearn_correlation_id وtransfer_id) (PS: packages/core/src/points/create-point-order.ts:119-160). ويبدأ order_no من 100000 من عدّاد، وcorrelation_id هو UUID v4 (PS: packages/core/src/points/order-no.ts:12,20-28، PS: packages/core/src/points/correlation-id.ts:8-10).
  • النص الحر. يدخل occasion في الوصف المخزَّن، وفي point يدخل في طرفي السجل. ولا يُخزَّن على الطلب كحقل. وبالنسبة إلى طلب checkout تضيف النقاطَ له لاحقًا خطّافُ البوابة (gateway hook)، يُبنى نص المستلم من التسمية الإنجليزية لصف المناسبة، لا من النص الحر للعميل. وهذه النقطة الأخيرة مقروءة من الشيفرة ولا تنص عليها أي وثيقة (PS: packages/core/src/points/create-point-order.ts:119-125,190، PS: packages/core/src/points/settle-point-order.ts:231).
  • order_type هو gift عندما يُعثر على صف مناسبة (بعدده الصحيح في Shukah أو بمعرّفه الخاص) و transfer خلاف ذلك (PS: packages/core/src/points/derive-order-type.ts:16-34). وتُظهر عينة وثيقة خدمة النقاط gift لـ body فيه مناسبة. أما استدعاء checkout الحي فلم تكن فيه مناسبة، لذا هو transfer.
  • لا مفتاح idempotency. كل استدعاء يخصّص correlation_id وorder_no جديدين (PS: packages/core/src/points/create-point-order.ts:109-113,165؛ PS: docs/integrations/2026-07-16-client-facing-point-apis.md:237-238).

التسوية وخطّاف البوابة​

الحالات هي قيم mt_point_order في Shukah محفوظة كما هي (PS: docs/plans/2026-07-29-point-orders-verbatim-shukah-values.md:19-34، PS: packages/core/src/db/models/point-order.model.ts:25,30-31). والطلبات المنشأة في Locate لا تبلغ إلا initial_order وsuccessful وpayment_failed (PS: packages/core/src/points/settle-point-order.ts:183-185). وتُبلّغ البوابة بالنتائج إلى POST /internal/point-orders/webhooks/checkout، وهو ليس route للتطبيق ويحتاج إلى مفتاح خدمة (PS: services/api/src/handlers/checkout-webhook.ts:9-41).

  • الدفعة الملتقَطة (captured) التي يطابق مبلغها وعملتها تضيف للمستلم طرف كسب واحدًا وتكتب صف outbox واحدًا، وتضبط successful وCAPTURED. ولا تُكتب مصروفات للدافع (PS: packages/core/src/points/settle-point-order.ts:117-119,195-328).
  • الدفعة المرفوضة على initial_order تضبط payment_failed وFAILED (PS: packages/core/src/points/settle-point-order.ts:33-42,125-145).
  • الالتقاط على طلب payment_failed يبقى فاشلًا، ويُطلب استرداد (refund) (PS: packages/core/src/points/settle-point-order.ts:121-123,331-392).
  • عدم تطابق المبلغ أو العملة يترك الطلب initial_order، ويعلّمه بأنه متوقف (halted) ويُثير تنبيهًا (alert). ولا يُسوّى تلقائيًا أبدًا (PS: packages/core/src/points/settle-point-order.ts:201-224).
  • التسوية آمنة للتكرار: فتُحجز الإضافة (credit) بنقل الطلب خارج initial_order (PS: packages/core/src/points/settle-point-order.ts:50-55).

الـ routeان للدفع​

  • كلاهما يبدأ بـ loadPayableOrder: الـ flag، ثم بحث عن الطلب يتضمن معرّف الدافع (فالطلب المفقود وطلب شخص آخر يعطيان 404 نفسه)، ثم نوع الدفع، وفحص التاجر (merchant)، وsuccessful، وpayment_failed، وملف الدافع، والمبلغ (PS: packages/core/src/points/pay-point-order.ts:42-102).
  • خدمة الدفع هي مستودع payment-api؛ ولم يُقرأ لهذه الصفحة.
  • البطاقة. الاستدعاء الصادر هو POST {PAYMENT_API_URL}/client/api/v1/checkout/lp-<correlation_id>/payments/standalone. الجلسة. هو POST {PAYMENT_API_URL}/client/api/v1/checkout/payment/lp-<correlation_id>/session. ويرسل كلاهما الـ headers token (الـ token الخام للعميل) وx-from-application: LOCATE_CLIENT و x-transaction-type: POINT. وفي الـ body amount بالريال (SAR)، وcustomerId (الدافع)، وcustomerName، وcustomerPhone، وid وcvv (البطاقة) أو rememberMe (الجلسة). والدافع هو العميل (customer)، لا المستلم أبدًا. ومرجع البوابة هو lp-<correlation_id> (PS: packages/core/src/points/payment-api-client.ts:125-164، PS: packages/core/src/points/pay-point-order.ts:29-33,134-174).
  • الكتابة الوحيدة ملاحظة لتصحيح الأخطاء (payment_provider وpayment_reference)، تُكتب بذل أقصى جهد (best effort). ولا تحدث هنا كتابة في السجل ولا تغيير في الحالة (PS: packages/core/src/points/pay-point-order.ts:104-130).
  • يُمرَّر cvv ولا تخزّنه خدمة النقاط ولا تسجّله في log أبدًا (PS: services/api/src/middleware/request-logger.ts:5). أما order-v2 فيسجّل أجسام الطلبات دون إخفاء، بما فيها هذا (انظر قسم الـ proxy).
  • أخطاء خدمة الدفع. يُمرَّر 4xx بحالته وbody الخاصين به. أما فشل النقل أو انتهاء المهلة أو 5xx فلا يُلتقط ويصبح INTERNAL_ERROR 500، دون تسجيل شيء (PS: services/api/src/handlers/pay-point-order-card.ts:38-44، PS: services/api/src/handlers/pay-point-order-session.ts:38-40، PS: packages/core/src/points/payment-api-client.ts:22-30,70-74,86-92,113-118، PS: docs/plans/2026-09-10-point-order-write-path-design.md:684).
  • سلسلة المهل: التطبيق 20 ث، وorder-v2 20 ث، وخدمة الدفع 10 ث افتراضيًا (PAYMENT_API_TIMEOUT_MS) (mobile/locate-client@e960c3a: src/shared/api/instances/orderApiV2.ts:16، PS: packages/core/src/points/payment-api-client.ts:7-20).
  • يطلب الدفع المكرر بالبطاقة من خدمة الدفع دفعة جديدة على المرجع نفسه. والبوابة لا تجعل المرجع فريدًا، لذا لا يُثار التقاط ثانٍ إلا كتنبيه (PS: packages/core/src/points/settle-point-order.ts:169-181، PS: docs/plans/2026-09-21-point-order-manual-test-guide.md:446).
  • وهل تقبل خدمة الدفع JWT من Locate في header الـ token: غير موثّق بعد.

الـ Flags​

يُقرأ POINT_ORDER_POINT_ENABLED بالشكل process.env.POINT_ORDER_POINT_ENABLED !== 'true' و POINT_ORDER_CARD_ENABLED بالطريقة نفسها، لذا لا يفعّلهما إلا النص true بالضبط (PS: packages/core/src/points/create-point-order.ts:69-72، PS: packages/core/src/points/pay-point-order.ts:45-47). وقيم الـ flags مدمجة في الـ image: إذ ينسخ الـ build الملف /data/compose/point-api/.env إليه (PS: Jenkinsfile-k8s:25-28,115-122، PS: Dockerfile:22-23). واستخدم الفحص في 2026-10-01 استدعاءين لا يكتبان شيئًا: body من نوع point بمعرّف عرض كله أصفار والمستدعي بوصفه المستلم (فحص الـ flag وفحص الدفع لنفسه كلاهما يعملان قبل أي بحث)، واستدعاء pay/session على UUID عشوائي (فحص الـ flag يعمل قبل البحث عن الطلب، والـ body الفارغ صالح) (PS: packages/core/src/points/create-point-order.ts:69-75، PS: packages/core/src/points/pay-point-order.ts:44-54، PS: services/api/src/handlers/pay-point-order-session.ts:22-24). وادعاءات الوثائق عن حالة الـ flags موجودة في PS: .env.example:74-80,91-94 وPS: CLAUDE.md:192-196 و PS: docs/plans/2026-09-17-shukah-sync-idempotent-key.md:35-38 و PS: docs/plans/2026-09-21-point-order-session-handoff.md:17 و PS: docs/plans/2026-09-22-o16-objectid-identity-design.md:286 و PS: docs/integrations/2026-09-25-payment-api-hook-forwarding.md:199,748-753.

Description​

يركّب الـ route نصًا ولا يكتب شيئًا. فيبحث عن العرض (status: 'active'، deleted_at: null)، ثم عن المناسبة فقط عند غياب النص الحر، ثم عن ملف المستدعي (PS: services/api/src/services/point-settings.service.ts:44-79). والصياغة هي composeGiftDescription، التي تحتفظ بمسافة Shukah المزدوجة عن قصد (PS: services/api/src/services/point-settings.mappers.ts:60-78). أما نصوص السجل والطلب التي تُبنى عند إنشاء طلب فتختزل تلك الفجوة إلى مسافة واحدة ولا تحمل المسافة المزدوجة: فهذه عائلة مختلفة من الجمل (PS: packages/core/src/points/order-reason.ts:10-28). ويتبع الاسم وقناع الهاتف displayName وmaskPhone (PS: packages/core/src/points/order-reason.ts:10-18). وتسجّل وثيقة خدمة النقاط أن الـ bodyين، بمناسبة وبدونها، تطابقا مع Shukah في 2026-09-21 (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:393-404). ويقول دليل الاختبار اليدوي بتاريخ 2026-09-21 إن route الـ proxy الخاص بها لم يُدمج بعد؛ وهو مدمج عند d9e4e5f6 (PS: docs/plans/2026-09-21-point-order-manual-test-guide.md:444، OV2: src/modules/client/point/point.controller.ts:132-146).

استدعاءات التطبيق التي لا route لها في Locate​

  • قائمة الهدايا المرسَلة. يقرأ تبويب "Sent gifts" في التطبيق GET /maeda/point-order (مع page و per-page، وheaders الـ x-pagination-*). ولا خدمة النقاط ولا order-v2 لديها route لسرد طلبات النقاط، ويعلّمه سجل الترحيل (migration registry) بأنه لم يُستبدل. وتفتح شاشة دفع الهدية ذلك التبويب بعد الدفع، لذا لن يظهر فيه طلب أُنشئ في Locate (mobile/locate-client@e960c3a: src/entities/points/model/api/services/points.service.ts:170-188، mobile/locate-client@e960c3a: src/features/SendedPointGifts/model/hooks/useSendedPointGifts.ts:20، mobile/locate-client@e960c3a: src/entities/points/model/api/controllers/points.controller.ts:151-152).
  • استدعاءات الدفع الأخرى. order/checkout-payment (الـ Frames token) وorder/checkout-payment-stcpay، ومساري PayFort وHyperpay، واستدعاء cvv/token إلى خدمة الدفع بمعرّف طلب Shukah لا نظير لها في Locate. وتوفّر Locate route لبطاقة محفوظة وroute لجلسة؛ أما STC Pay فمحفظة داخل الجلسة. واستدعاء cvv/token لا أثر له على طلب Locate. وأي استدعاءات Locate ينبغي للتطبيق استخدامها بدلًا منها: غير موثّق بعد (mobile/locate-client@e960c3a: src/widgets/PointGiftsCheckout/model/hooks/usePointGiftsCheckout.ts:80-120,288-352، PS: docs/plans/2026-09-10-point-order-write-path-design.md:382-398).
  • طلب واحد. لدى التطبيق getPointGiftOrderById لـ GET /maeda/point-order/{id}، لكن لا شاشة تستدعيها (mobile/locate-client@e960c3a: src/entities/points/model/api/services/points.service.ts:316-341، mobile/locate-client@e960c3a: src/entities/points/index.ts:21-22).

التطبيق​

تعيد createPointGiftOrder تحميل الإعدادات العامة وترسل payment_type بالقيمة point لدفع بالنقاط، أو قيمة payment من تلك الإعدادات لدفع إلكتروني (mobile/locate-client@e960c3a: src/entities/points/model/api/rtk/pointsApi.ts:408-421، mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:356-397). ويحوّل الـ DTO الخاص بالطلب الاستجابة إلى طلب التطبيق: فيبني pointOrderId بـ String(point_order_id)، ويقرأ amount وclient_id وto_client_id وpayment_point_order_id، ويحوّل status عبر جدول كلمات Shukah الأربع (mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:23-31,138-183). وكل استدعاءات الدفع ترسل order.pointOrderId (mobile/locate-client@e960c3a: src/widgets/PointGiftsCheckout/model/hooks/usePointGiftsCheckout.ts:51,80-120). ويبحث الدفع (checkout) عن المستلم بالهاتف ويمرّر userId بوصفه to_client_id، ثم ينشئ الطلب ويدفع؛ ويعيد معالج النجاح تحميل السجل ويستبدل الشاشة بتبويب الهدايا المرسَلة (Sent gifts) (mobile/locate-client@e960c3a: src/widgets/PointGiftsCheckout/model/hooks/usePointGiftsCheckout.ts:221-232,370-401).

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

  • تقول وثائق 2026-09-21 و09-22 و09-25 إن الـ flagين غير مضبوطين في كل بيئة. ويُظهر فحص 2026-10-01 كليهما مفعّلًا على dev. وتقول وثيقة 2026-09-25 إن أحدًا لم يقرأ الـ flag على host يعمل (PS: docs/integrations/2026-09-25-payment-api-hook-forwarding.md:748-753).
  • يقول دليل الاختبار اليدوي بتاريخ 2026-09-21 إن route الـ proxy الخاص بـ description لم يُدمج وإن order-v2 على dev يستدعي localhost:4010. وكلاهما قديم (PS: docs/plans/2026-09-21-point-order-manual-test-guide.md:24-32,444).
  • تقول وثيقة مرجع order-v2 إن تعذّر الوصول إلى خدمة المصادقة يعطي 500. أما خدمة النقاط فتجيب 401 (OV2: docs/locate-point-client-api-reference.md:201، PS: services/api/src/services/client-auth.service.ts:26-28).
  • تصف وثيقة التكامل create بأنه "new capability, not a Shukah read-parity endpoint"، وتقول أيضًا إن POST المعاد يطابق "Shukah's own order-creation endpoint" (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:178-182,237-238).
  • 2026-10-02: قالت هذه الصفحة إن token من Shukah وJWT من Locate عملا كلاهما على هذه الـ routes. وهي تشترك في فحص الـ token الخاص بخدمة النقاط، الذي أعطى JWT من Locate 401 على routes النقاط الأخرى في 2026-10-02 (PS: services/api/src/services/client-auth.service.ts:22-29). وتستخدم استدعاءات «جرّبه» الآن token من Shukah.
  • 2026-10-02: قالت هذه الصفحة إن طلب Shukah يحمل occasion_id، كما يعرّفه التطبيق. وصفوف طلبات Shukah فيها point_occasion_id (قائمة الهدايا المرسَلة، التُقطت على dev، 2026-10-02)، لذا فـ occasionId في التطبيق دائمًا undefined (mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:138-183).
  • 2026-10-02: قالت هذه الصفحة إن استدعاءات الدفع ترسل point_order_id كعدد صحيح. يرسل التطبيق 2.31.5 سلسلة ("1252" و"1253"؛ التقاطات الـ proxy على dev، 2026-10-02). وتتجاهله Locate في الحالين (PS: services/api/src/handlers/pay-point-order-card.ts:7-15، PS: services/api/src/handlers/pay-point-order-session.ts:9-16).
  • 2026-10-02: بنت هذه الصفحة جانب Shukah من create من أنواع التطبيق ووثائق خدمة النقاط، لأن create لم يكن قد استُدعي. ثم استُدعي الجانبان على dev في 2026-10-02: تجيب Shukah بـ 201 مع اثني عشر مفتاحًا، وLocate بـ 200 مع تسعة، وطلب point على أي جانب لم يخفض إلا رصيد ذلك الجانب (الالتقاطات point-order-create-card--shukah وpoint-order-create-card--locate-st و point-order-create-point--shukah وpoint-order-create-point--locate-st).

Sources​

  • locate-order-v2-api@d9e4e5f6: src/modules/client/point/point.controller.ts:6-10,110-201، src/modules/client/point/point.service.ts:64, src/main.ts:38
  • وثائق locate-order-v2-api@d9e4e5f6: docs/locate-point-client-api-reference.md:46,48,201، docs/plans/2026-09-16-point-order-proxy-plan.md:21
  • locate-point-service@2c44ce3: services/api/src/routes/client.routes.ts:23-29، services/api/src/routes/client.routes.test.ts:43-75، services/api/src/handlers/create-point-order.ts:10-47، services/api/src/handlers/get-point-order.ts:6-12، services/api/src/handlers/point-order-description.ts:8-38، services/api/src/handlers/pay-point-order-card.ts:7-46، services/api/src/handlers/pay-point-order-session.ts:7-43، services/api/src/handlers/checkout-webhook.ts:9-41، services/api/src/services/point-orders.service.ts:3-15، services/api/src/services/point-settings.service.ts:44-79، services/api/src/services/point-settings.mappers.ts:60-78, services/api/src/lib/object-id.ts:12-14، services/api/src/services/client-auth.service.ts:26-28، services/api/src/middleware/request-logger.ts:5، packages/core/src/points/create-point-order.ts:66-268، packages/core/src/points/pay-point-order.ts:29-174، packages/core/src/points/payment-api-client.ts:7-30,70-74,86-92,113-164، packages/core/src/points/settle-point-order.ts:33-55,117-145,169-392، packages/core/src/points/apply-point-txn.ts:71-86، packages/core/src/points/derive-order-type.ts:16-34, packages/core/src/points/order-no.ts:12,20-28، packages/core/src/points/correlation-id.ts:8-10, packages/core/src/points/client-lookup.ts:20-35، packages/core/src/points/order-reason.ts:10-28، packages/core/src/db/models/point-order.model.ts:25,30-31, .env.example:74-97, CLAUDE.md:192-205، Jenkinsfile-k8s:25-28,115-122, Dockerfile:22-23
  • وثائق locate-point-service@2c44ce3: docs/integrations/2026-07-16-client-facing-point-apis.md:178-182,237-238,263-299,393-404 (آخر تغيير 2026-09-22)، docs/plans/2026-09-21-point-order-manual-test-guide.md:24-32,336-343,444,446 (آخر تغيير 2026-09-22)، docs/plans/2026-09-10-point-order-write-path-design.md:382-398,513-518,684,692,811، docs/plans/2026-08-03-point-order-flow-design.md:31,33، docs/plans/2026-07-29-point-orders-verbatim-shukah-values.md:19-34، docs/plans/2026-09-17-shukah-sync-idempotent-key.md:35-38، docs/plans/2026-09-21-point-order-session-handoff.md:17، docs/plans/2026-09-22-o16-objectid-identity-design.md:286، docs/integrations/2026-09-25-payment-api-hook-forwarding.md:199,748-753
  • نسخة الجوال اللحظية mobile/locate-client@e960c3a (الفرع development، 2026-02-26؛ شيفرة التطبيق نفسه، وليست من Locate): src/entities/points/model/api/services/points.service.ts:170-188,265-283,316-361، src/entities/points/model/api/dto/points.dto.ts:23-31,138-183,312-397، src/entities/points/model/api/rtk/pointsApi.ts:408-421، src/entities/points/model/api/controllers/points.controller.ts:151-152، src/entities/points/index.ts:21-22، src/widgets/PointGiftsCheckout/model/hooks/usePointGiftsCheckout.ts:51,80-120,221-232,288-352,370-401، src/widgets/SendPointGiftForm/ui/SendPointGiftForm/SendPointGiftForm.tsx:149-185، src/features/SendedPointGifts/model/hooks/useSendedPointGifts.ts:20، src/shared/api/instances/orderApiV2.ts:16
  • التقاطات dev، 2026-10-02 (parity audit): point-order-description--shukah و point-order-description--locate-st وpoint-order-description-shukahids--locate-st و point-order-list--shukah؛ وأجسام طلبات التطبيق 2.31.5 لـ checkout-payment-flow و checkout-payment-stcpay (التقطها الـ proxy الخاص بالمستخدم). ولم تُستدعَ routes الدفع في 2026-10-02.
  • التقاطات dev، 2026-10-02 (استدعاءات المتابعة، بموافقة المستخدم): point-order-create-card--shukah و point-order-create-card--locate-st وpoint-order-create-point--shukah و point-order-create-point--locate-st، والرصيدان قبل وبعد.
  • التقاطات dev، 2026-10-01: locate-point-order-create-checkout وlocate-point-order-get و locate-point-order-description وshukah-point-order-description وlocate-flag-probe-pay-by-point و locate-flag-probe-pay-session وprobe-locate-point-order-create وprobe-locate-point-order-get و probe-locate-point-order-description وprobe-locate-point-order-pay-card و probe-locate-point-order-pay-session وlocate-point-offers وlocate-point-occasions