Chuyển tới nội dung chính

Point offers

Bối cảnh​

Form tặng điểm cần hai danh sách: các gói điểm (offer) và các dịp (occasion). Trên Shukah app đọc chúng bằng GET /maeda/point-offer và GET /maeda/point-occasion. Locate có cùng hai route đó, và một route mới trả về một offer đơn lẻ. Các field giống của Shukah. Có ba điều cần chú ý: id của một offer và của một occasion là chuỗi 24 ký tự thay vì số, Locate cần token trong khi Shukah trả lời mà không cần, và ?id= của Shukah trả về toàn bộ danh sách.

Tính năng​

Một offer là một gói điểm mà client có thể mua bằng thẻ hoặc trả bằng điểm, để gửi cho client khác. Một occasion là lý do của món quà, như Birthday. Trong snapshot app ngày 2026-02-26 các lời gọi xảy ra trên form send-gift:

Lời gọiỞ đâu trong app
Danh sách offerKhi bộ chọn số điểm quà tặng xuất hiện trên form send-gift.
Một offer (Shukah ?id=)Trong gift summary, trong nút checkout và trong bộ chọn phương thức thanh toán. App hỏi Shukah với ?id= rồi tự chọn offer ra khỏi danh sách trả về.
Danh sách occasionKhi bộ chọn occasion xuất hiện trên form send-gift.
Một occasion (Shukah ?id=)Trong gift summary. Cùng cách tự chọn phía client.

Các id đi vào các lời gọi sau. App gửi id của offer làm point_offer_id và id của occasion làm point_occasion_id khi nó xin text của lời nhắn (Point order description) và khi nó tạo order (Create point order).

Tất cả đều cần token trên Locate, và trên dev hiện nay chỉ có token Shukah chạy: một JWT Locate nhận 401 Invalid or expired token trên danh sách offer và occasion vào 2026-10-02, dù cả hai token đều chạy trên route balance vào 2026-10-01 (xem Which tokens work). order-v2 chuyển từng lời gọi sang point service và trả câu trả lời không đổi.

Id: lưu chúng, gửi lại chúng, không bao giờ parse chúng. id của một offer hay occasion là chuỗi hex 24 ký tự của dòng database của nó. Nó khác nhau ở mỗi môi trường, nên app phải đọc id từ danh sách và không bao giờ viết một id vào code. Một integer Shukah gửi tới Locate thay cho id bị từ chối với 400. App đã biến id thành text (String(id)) và so sánh chúng dạng text, cách đó chạy được với các id mới, nhưng các TypeScript type của nó vẫn ghi number.

Quy tắc is_custom. is_custom là 0 hoặc 1, không bao giờ là boolean. Khi nó là 1 (trên dev: Thank và Other) app hiển thị một ô nhập text tự do và gửi những gì client đã gõ làm occasion, cạnh id của occasion.

GET /api/v2/client/point-offers​

Thay thế ShukahGET /maeda/point-offer✅ Đã thay thế
Thay thẳng0 breaking · 4 cần lưu ý
  • 🟡 id là chuỗi 24 ký tự hex (Shukah 1..5); app dùng String(id) nên chạy được; id của Shukah trả 400.
  • 🟡 Locate cần token; Shukah trả 200 khi không có token; app luôn gửi token.
  • 🟡 JWT của Locate nhận 401 trên dev; hãy gửi token của Shukah.
  • 🟡 Năm offer giống nhau ở point_amount, purchased_point_amount, amount ("40.00" dạng chuỗi) và order.

Bộ lọc ?id= bị bỏ qua: lệnh gọi vẫn trả toàn bộ danh sách.

Request​

FieldVị tríKiểuBắt buộcRàng buộcMô tả
AuthorizationheaderstringcóBearer <token>: một token Shukah trên dev hiện nay. Một JWT Locate nhận 401 Invalid or expired token (capture trên dev, 2026-10-02, trên hai danh sách): xem Which tokens work.Token của chính client.
idqueryanykhôngĐược chấp nhận và bỏ qua: toàn bộ danh sách luôn được trả về, như ?id= của Shukah.Giữ lại để lời gọi Shukah cũ vẫn chạy.
Bất kỳ query key nào khácquery—khôngĐược chấp nhận và bỏ qua, ví dụ language.—

Response​

200 với một mảng trần các offer active. Nó có thể rỗng. Không có phân trang.

FieldKiểuNullableĐịnh dạng / ràng buộcMô tả
idstringkhôngChuỗi hex 24 ký tự, ví dụ "6a46104f3dd7b3dd6a261d90". Khác nhau ở mỗi môi trường. Shukah gửi một integer.Id của offer. Gửi lại nó làm point_offer_id.
point_amountnumberkhôngSố JSON.Số điểm người nhận được.
purchased_point_amountnumbercónull khi offer không có giá bằng điểm. Trên dev nó cao hơn point_amount.Số điểm người gửi trả khi trả bằng điểm.
amountstringkhôngChính xác hai chữ số thập phân, ví dụ "40.00".Giá bằng SAR cho thanh toán bằng thẻ.
[
{"id": "6a46104f3dd7b3dd6a261d90", "point_amount": 100, "purchased_point_amount": 110, "amount": "40.00"},
{"id": "6a46104f3dd7b3dd6a261d91", "point_amount": 200, "purchased_point_amount": 240, "amount": "60.00"},
{"id": "6a46104f3dd7b3dd6a261d94", "point_amount": 300, "purchased_point_amount": 360, "amount": "90.00"},
{"id": "6a46104f3dd7b3dd6a261d96", "point_amount": 400, "purchased_point_amount": 480, "amount": "120.00"},
{"id": "6a46104f3dd7b3dd6a261d98", "point_amount": 500, "purchased_point_amount": 600, "amount": "150.00"}
]

Capture trên dev, 2026-10-01 (client 356259): cả năm offer.

Người gửi trả nhiều điểm hơn số người nhận được: 110 cho 100, 240 cho 200, 360 cho 300, 480 cho 400 và 600 cho 500. Một offer có purchased_point_amount là null không thể trả bằng điểm: create point order trả 400 cho nó.

Thứ tự. Các offer được sắp theo số thứ tự do admin đặt, rồi theo Shukah id, rồi theo row id. Cách sắp đó được commit vào 2026-10-01. Dev có chạy nó khi lấy sample hay không: Chưa được tài liệu hóa.

Lỗi​

HTTPBodyKhi nào
401{"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}}Không có header Authorization, hoặc nó không bắt đầu bằng Bearer . Đã capture trên dev, 2026-10-01, khi không có token.
401Cùng shape, với message là Invalid or expired token, auth service unreachable: <error text> hoặc auth response missing numeric client id.Auth service từ chối token, không kết nối được (sự cố auth trông giống token xấu), hoặc trả lời mà không có client id. Invalid or expired token đã được capture trên dev, 2026-10-02, với một JWT Locate trên hai danh sách.
500{"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}Bất kỳ lỗi không mong đợi nào trong point service, ví dụ lỗi database. Message là AUTH_SERVICE_URL not set khi service bị cấu hình sai. Chưa capture.
502{"success":false,"error":{"code":"POINT_SERVICE_UNAVAILABLE","message":"Point service unavailable: <error text>"}}order-v2 không kết nối được point service. Body này đến từ order-v2, không phải từ point service. Chưa capture.
504{"success":false,"error":{"code":"POINT_SERVICE_TIMEOUT","message":"Point service did not respond in time; the request outcome is unknown"}}Point service không trả lời trong 10 giây. Body này đến từ order-v2. Chưa capture.

Mọi lỗi từ point service đều có envelope {"success":false,"error":{"code":"…","message":"…"}}.

Thử ngay​

export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_TOKEN='<shukah-token>' # a Locate JWT gets 401 here today; see "Which tokens work" on Points wallet
curl -s "$ORDER_BASE_URL/api/v2/client/point-offers" -H "Authorization: Bearer $SHUKAH_TOKEN"

Kỳ vọng: 200 và mảng như ở mục Response. Hãy dùng token Shukah trên dev hiện nay: một JWT Locate nhận 401 Invalid or expired token (2026-10-02). Không có token bạn nhận 401 như ở mục Lỗi.

Khác biệt so với Shukah​

Năm offer bằng nhau về point_amount, purchased_point_amount, amount và thứ tự key vào 2026-10-01 và lại vào 2026-10-02. Chỉ id khác.

Khía cạnhShukahLocateTác động lên app
URLGET $SHUKAH_BASE_URL/maeda/point-offerGET $ORDER_BASE_URL/api/v2/client/point-offersĐổi base URL và path.
TokenKhông cần: 200 khi không có token (capture trên dev, 2026-10-01).Bắt buộc: 401 khi không có token (capture trên dev, 2026-10-01). Chỉ token Shukah trên dev hiện nay: một JWT Locate nhận 401 (2026-10-02).App đã gửi token của nó trên mọi lời gọi, và phải giữ nó là token Shukah ở đây. Một màn hình hiển thị offer trước khi đăng nhập sẽ lỗi. Màn hình như vậy có tồn tại hay không: Chưa được tài liệu hóa.
idMột integer, 1 đến 5 (capture trên dev, 2026-10-01).Một chuỗi 24 ký tự.App biến id thành text, nên nó chạy được. Các type của nó ghi number. Đừng bao giờ trộn hai loại: Locate từ chối integer của Shukah.
?id=Trả về toàn bộ danh sách. App tự chọn offer ra.Được chấp nhận và bỏ qua: lại toàn bộ danh sách.Không có: cách tự chọn phía client của app chạy được với các id mới.
purchased_point_amountMột số (capture trên dev, 2026-10-01).Một số, hoặc null khi offer không có giá bằng điểm.App khai báo kiểu là number và copy nó sang costInPoints. Phải xử lý null.
Thứ tựChưa được tài liệu hóa.Theo số thứ tự do admin đặt, rồi Shukah id, rồi row id.Nếu app hiển thị danh sách theo thứ tự của server, thứ tự có thể đổi.
Body lỗiChưa được tài liệu hóa. Route này public, nên không có 401 để so sánh.Envelope của point-service ở mục Lỗi.Mọi chỗ so khớp theo text lỗi đều phải đổi.

GET /api/v2/client/point-offers/:id​

Endpoint mới — không có endpoint tương đương trên Shukah.

Route mới. Shukah không có route nào trả về một offer: ?id= của nó trả về toàn bộ danh sách, và app tự chọn offer ra khỏi danh sách đó. Hiện nay app không gọi route này.

Request​

FieldVị tríKiểuBắt buộcRàng buộcMô tả
AuthorizationheaderstringcóBearer <token>: một token Shukah trên dev hiện nay. Một JWT Locate nhận 401 Invalid or expired token (capture trên dev, 2026-10-02, trên hai danh sách): xem Which tokens work.Token của chính client.
idpathstringcóChính xác 24 ký tự hex (chữ hoa hoặc thường). Integer của Shukah bị từ chối, không được tra cứu.Id của offer từ danh sách.
Bất kỳ query key nàoquery—khôngĐược order-v2 forward và point service bỏ qua.—

Response​

200 với một object, cùng shape với một dòng của danh sách (id, point_amount, purchased_point_amount, amount; xem danh sách).

{"id": "6a46104f3dd7b3dd6a261d90", "point_amount": 100, "purchased_point_amount": 110, "amount": "40.00"}

Capture trên dev, 2026-10-01 (client 356259): offer đầu tiên.

Lỗi​

HTTPBodyKhi nào
400{"success":false,"error":{"code":"VALIDATION_ERROR","message":"point offer id must be a 24-character hex ObjectId"}}:id không phải 24 ký tự hex. Không có key details. Chưa capture.
404{"success":false,"error":{"code":"NOT_FOUND","message":"point offer <id> not found or inactive"}}Không có offer với id đó, hoặc nó inactive hay đã bị xóa. Một id 24 hex không tồn tại là 404, không phải 400. Chưa capture.
401{"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}}Không có header Authorization, hoặc nó không bắt đầu bằng Bearer . Đã capture trên dev, 2026-10-01, khi không có token.
401Cùng shape, với message là Invalid or expired token, auth service unreachable: <error text> hoặc auth response missing numeric client id.Auth service từ chối token, không kết nối được (sự cố auth trông giống token xấu), hoặc trả lời mà không có client id. Invalid or expired token đã được capture trên dev, 2026-10-02, với một JWT Locate trên hai danh sách.
500{"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}Bất kỳ lỗi không mong đợi nào trong point service, ví dụ lỗi database. Message là AUTH_SERVICE_URL not set khi service bị cấu hình sai. Chưa capture.
502{"success":false,"error":{"code":"POINT_SERVICE_UNAVAILABLE","message":"Point service unavailable: <error text>"}}order-v2 không kết nối được point service. Body này đến từ order-v2, không phải từ point service. Chưa capture.
504{"success":false,"error":{"code":"POINT_SERVICE_TIMEOUT","message":"Point service did not respond in time; the request outcome is unknown"}}Point service không trả lời trong 10 giây. Body này đến từ order-v2. Chưa capture.

Thử ngay​

export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_TOKEN='<shukah-token>' # a Locate JWT gets 401 here today; see "Which tokens work" on Points wallet
export OFFER_ID='<id-from-the-offers-list>'
curl -s "$ORDER_BASE_URL/api/v2/client/point-offers/$OFFER_ID" -H "Authorization: Bearer $SHUKAH_TOKEN"

Kỳ vọng: 200 và offer như ở mục Response. Một id không phải 24 ký tự hex cho 400 như ở mục Lỗi. Hãy dùng token Shukah trên dev hiện nay: route này có cùng bước kiểm tra token như các danh sách.

GET /api/v2/client/point-occasions​

Thay thế ShukahGET /maeda/point-occasion✅ Đã thay thế
Thay thẳng0 breaking · 4 cần lưu ý
  • 🟡 id là chuỗi 24 ký tự hex (Shukah 1..7); app dùng String(id); gửi id của Shukah vào create của Locate trả 400.
  • 🟡 Locate cần token; Shukah trả 200 khi không có token; app luôn gửi token.
  • 🟡 JWT của Locate nhận 401 trên dev; hãy gửi token của Shukah.
  • 🟡 Bảy dòng giống nhau ở occasion, occasion_en, is_custom (số 0/1) và thứ tự key.

Request​

FieldVị tríKiểuBắt buộcRàng buộcMô tả
AuthorizationheaderstringcóBearer <token>: một token Shukah trên dev hiện nay. Một JWT Locate nhận 401 Invalid or expired token (capture trên dev, 2026-10-02, trên hai danh sách): xem Which tokens work.Token của chính client.
languagequerystringkhôngSau khi trim và chuyển chữ thường, chính xác ar cho nhãn tiếng Ả Rập. Mọi giá trị khác, kể cả ar-SA và en-GB, và không có giá trị, cho tiếng Anh. Không bao giờ là lỗi.Ngôn ngữ của nhãn occasion.
idqueryanykhôngĐược chấp nhận và bỏ qua: toàn bộ danh sách được trả về.Giữ lại để lời gọi Shukah cũ vẫn chạy.

App thêm language=en-GB hoặc language=ar-SA vào mọi lời gọi, nên với các giá trị của chính app nhãn luôn là tiếng Anh. Có chủ đích hay không: Chưa được tài liệu hóa.

Response​

200 với một mảng trần các occasion active. Nó có thể rỗng. Không có phân trang.

FieldKiểuNullableĐịnh dạng / ràng buộcMô tả
idstringkhôngChuỗi hex 24 ký tự. Khác nhau ở mỗi môi trường. Shukah gửi một integer.Id của occasion. Gửi lại nó làm point_occasion_id.
occasionstringkhôngNhãn tiếng Anh, đã trim. Với language=ar, là nhãn tiếng Ả Rập, hoặc nhãn tiếng Anh khi không có tiếng Ả Rập.Nhãn để hiển thị.
occasion_enstringkhôngNhãn tiếng Anh, đã trim, luôn luôn.Nhãn tiếng Anh.
is_customintegerkhông0 hoặc 1, không bao giờ là boolean.1 nghĩa là app hiển thị một ô nhập text tự do.
[
{"id": "6a4610503dd7b3dd6a261dbf", "occasion": "Birthday", "occasion_en": "Birthday", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc0", "occasion": "Born", "occasion_en": "Born", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc1", "occasion": "Gift", "occasion_en": "Gift", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc3", "occasion": "Wedding", "occasion_en": "Wedding", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc5", "occasion": "Graduation", "occasion_en": "Graduation", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc6", "occasion": "Thank", "occasion_en": "Thank", "is_custom": 1},
{"id": "6a4610503dd7b3dd6a261dc7", "occasion": "Other", "occasion_en": "Other", "is_custom": 1}
]

Capture trên dev, 2026-10-01 (client 356259), không có language: cả bảy occasion.

App có một vấn đề về type cũng tồn tại trên Shukah: nó đọc nhãn tiếng Anh là occasionEn, nhưng cả hai server đều gửi occasion_en, nên nameEn của app hiện là undefined.

Lỗi​

HTTPBodyKhi nào
401{"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}}Không có header Authorization, hoặc nó không bắt đầu bằng Bearer . Đã capture trên dev, 2026-10-01, khi không có token.
401Cùng shape, với message là Invalid or expired token, auth service unreachable: <error text> hoặc auth response missing numeric client id.Auth service từ chối token, không kết nối được (sự cố auth trông giống token xấu), hoặc trả lời mà không có client id. Invalid or expired token đã được capture trên dev, 2026-10-02, với một JWT Locate trên hai danh sách.
500{"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}Bất kỳ lỗi không mong đợi nào trong point service, ví dụ lỗi database. Message là AUTH_SERVICE_URL not set khi service bị cấu hình sai. Chưa capture.
502{"success":false,"error":{"code":"POINT_SERVICE_UNAVAILABLE","message":"Point service unavailable: <error text>"}}order-v2 không kết nối được point service. Body này đến từ order-v2, không phải từ point service. Chưa capture.
504{"success":false,"error":{"code":"POINT_SERVICE_TIMEOUT","message":"Point service did not respond in time; the request outcome is unknown"}}Point service không trả lời trong 10 giây. Body này đến từ order-v2. Chưa capture.

Thử ngay​

export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_TOKEN='<shukah-token>' # a Locate JWT gets 401 here today; see "Which tokens work" on Points wallet
curl -s "$ORDER_BASE_URL/api/v2/client/point-occasions?language=en-GB" -H "Authorization: Bearer $SHUKAH_TOKEN"

Kỳ vọng: 200 và mảng như ở mục Response. Hãy dùng token Shukah trên dev hiện nay: một JWT Locate nhận 401 Invalid or expired token (2026-10-02). Không có token bạn nhận 401 như ở mục Lỗi.

Khác biệt so với Shukah​

Bảy dòng bằng nhau về occasion, occasion_en, is_custom và thứ tự key vào 2026-10-01 và lại vào 2026-10-02. Chỉ id khác.

Khía cạnhShukahLocateTác động lên app
URLGET $SHUKAH_BASE_URL/maeda/point-occasionGET $ORDER_BASE_URL/api/v2/client/point-occasionsĐổi base URL và path.
TokenKhông cần: 200 khi không có token (capture trên dev, 2026-10-01).Bắt buộc: 401 khi không có token (capture trên dev, 2026-10-01). Chỉ token Shukah trên dev hiện nay: một JWT Locate nhận 401 (2026-10-02).App đã gửi token của nó trên mọi lời gọi, và phải giữ nó là token Shukah ở đây.
idMột integer, 1 đến 7 (capture trên dev, 2026-10-01).Một chuỗi 24 ký tự.App biến id thành text, nên nó chạy được. Đừng bao giờ trộn hai loại.
?id=Trả về toàn bộ danh sách.Được chấp nhận và bỏ qua: lại toàn bộ danh sách.Không có: cách tự chọn phía client của app chạy được với các id mới.
Nhãn occasionCùng text tiếng Anh trong occasion và occasion_en (capture trên dev, 2026-10-01).Tiếng Anh, hoặc tiếng Ả Rập khi language chính xác là ar.App gửi ar-SA, nên không bao giờ nhận được nhãn tiếng Ả Rập.
Thứ tựChưa được tài liệu hóa.Theo số thứ tự do admin đặt, rồi Shukah id, rồi row id.Nếu app hiển thị danh sách theo thứ tự của server, thứ tự có thể đổi.
Body lỗiChưa được tài liệu hóa. Route này public, nên không có 401 để so sánh.Envelope của point-service ở mục Lỗi.Mọi chỗ so khớp theo text lỗi đều phải đổi.

Lưu ý​

  • Id của offer và occasion là chuỗi 24 ký tự khác nhau theo môi trường: lưu chúng, gửi lại chúng, không bao giờ parse chúng, và không bao giờ gửi integer của Shukah.
  • Locate cần token trên cả ba route, trong khi các danh sách của Shukah là public.
  • ?id= vẫn trả về toàn bộ danh sách, nên hãy chọn offer hoặc occasion ra khỏi mảng, hoặc dùng GET /point-offers/:id.

Chi tiết (cho backend)​

Chỉ kiểm tra dev, qua order-v2 tại https://dev.v2.api.order.locate.sa, prefix /api/v2/client. Host UAT và production: Chưa được tài liệu hóa. Trong mục này OV2: nghĩa là locate-order-v2-api@d9e4e5f6 và PS: nghĩa là locate-point-service@2c44ce3. Cách proxy, việc kiểm tra token và error envelope hoạt động được mô tả một lần, ở Các lời gọi proxy hoạt động thế nào.

Route và proxy​

Route của app (order-v2)Route upstream (point service)Được forwardGiới hạn
GET /api/v2/client/point-offersGET /api/client/point-offersAuthorization và toàn bộ query10 s
GET /api/v2/client/point-offers/:idGET /api/client/point-offers/<id>, với id đi qua encodeURIComponentAuthorization và toàn bộ query (upstream bỏ qua)10 s
GET /api/v2/client/point-occasionsGET /api/client/point-occasionsAuthorization và toàn bộ query, nên language đến nơi10 s

(OV2: src/modules/client/point/point.controller.ts:61-108, PS: services/api/src/routes/client.routes.ts:20-22, PS: services/api/src/handlers/client-point-offers.ts:12-23, PS: services/api/src/handlers/client-point-occasions.ts:12-14.)

Point service đọc gì​

  • Offer. Các dòng của point_offers với status: 'active' và deleted_at: null, sắp theo sequence, rồi shukah_id, rồi _id. Không có phân trang hay giới hạn. Một offer do Locate tạo không có Shukah id được liệt kê như bất kỳ offer nào khác (PS: services/api/src/services/point-settings.service.ts:10-25). Cách sắp được đổi vào 2026-10-01 (commit 105119c, merge 14:25 +07), trong khi tài liệu integration vẫn ghi "ordered by shukah_id" (PS: docs/plans/2026-10-01-app-language-and-arabic-reason.md:6-8, PS: docs/integrations/2026-07-16-client-facing-point-apis.md:321-322). Các capture được lấy lúc khoảng 17:19 +07. Build đó có trên dev hay không: Chưa được tài liệu hóa.
  • Một offer. Cùng filter trên _id. Một offer inactive hoặc đã xóa là 404 (PS: services/api/src/services/point-settings.service.ts:29-34). Việc kiểm tra id là ^[0-9a-fA-F]{24}$ (PS: services/api/src/lib/object-id.ts:12-14, PS: services/api/src/handlers/client-point-offers.ts:16,19-23).
  • Occasion. Các dòng của point_occasions với cùng filter, sắp cùng cách (PS: services/api/src/services/point-settings.service.ts:38-42). Quy tắc language (chính xác ar sau khi trim và chuyển chữ thường) được ship trong cùng commit 105119c (PS: services/api/src/handlers/client-point-occasions.ts:5-8, PS: docs/plans/2026-10-01-app-language-and-arabic-reason.md:10-17).
  • Id. Các id là giá trị _id riêng của các dòng, render dưới dạng hex chữ thường 24 ký tự. Chữ hoa được chấp nhận ở đầu vào (PS: services/api/src/services/point-settings.mappers.ts:25-58). Các integer mà Shukah dùng vẫn được lưu trên các dòng dưới dạng shukah_id, nhưng các route client không còn chấp nhận chúng: một point_offer_id không phải 24 ký tự hex lỗi với must be a 24-character hex ObjectId (kiểm tra bằng một lần chạy local của schema, zod 3.25.76).
  • Query ?id=. Handler không đọc id; toàn bộ danh sách được trả về, điều mà tài liệu của point service nói là khớp với Shukah, "measured on dev 2026-09-21" (PS: services/api/src/handlers/client-point-offers.ts:7-11, PS: docs/integrations/2026-07-16-client-facing-point-apis.md:324-328).
  • is_custom. Mapper ghi doc.is_custom ? 1 : 0 (PS: services/api/src/services/point-settings.mappers.ts:56). Một occasion custom là trường hợp duy nhất mà app gửi text tự do trong occasion cùng với id (PS: services/api/src/services/point-settings.service.ts:57-58).
  • Chỉnh sửa. Offer và occasion được thay đổi qua các route admin dưới /api/admin/… với một service key, không bao giờ bởi app (PS: services/api/src/routes/admin.routes.ts:34-44).

App​

Các type của app ghi id: number cho offer và occasion (mobile/locate-client@e960c3a: src/entities/points/model/types/pointGiftOption.server.ts:1-6, mobile/locate-client@e960c3a: src/entities/points/model/types/pointGiftOccasion.server.ts:1-6). Các DTO biến id thành text và tính cost là Number(amount), costInPoints là purchased_point_amount, và isCustom là String(is_custom) === '1' (mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:104-136). Các helper by-id lọc danh sách trả về bằng String(item.id) === request.id (mobile/locate-client@e960c3a: src/entities/points/model/api/services/points.service.ts:212-241,285-295). Language interceptor thêm language=en-GB hoặc language=ar-SA vào mọi lời gọi (mobile/locate-client@e960c3a: src/shared/api/interceptors/languageInterceptor.ts:5-15).

Khi tài liệu và code mâu thuẫn (code thắng)​

  • Tài liệu integration nói offer được sắp theo shukah_id với _id là tiebreak (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:321-322). Code sắp theo sequence trước.
  • 2026-10-02: trang này nói một token Shukah và một JWT Locate đều chạy trên các route này. Vào 2026-10-02 JWT nhận 401 trên danh sách offer và occasion, và các lời gọi Thử ngay giờ dùng một token Shukah (PS: services/api/src/services/client-auth.service.ts:22-29; xem Which tokens work).

Sources​

  • locate-order-v2-api@d9e4e5f6: src/modules/client/point/point.controller.ts:61-108
  • locate-point-service@2c44ce3: services/api/src/routes/client.routes.ts:20-22, services/api/src/handlers/client-point-offers.ts:7-23, services/api/src/handlers/client-point-occasions.ts:5-14, services/api/src/services/point-settings.service.ts:10-42,57-58, services/api/src/services/point-settings.mappers.ts:25-58, services/api/src/lib/object-id.ts:12-14, services/api/src/routes/admin.routes.ts:34-44
  • Tài liệu của locate-point-service@2c44ce3: docs/integrations/2026-07-16-client-facing-point-apis.md:321-328, docs/plans/2026-10-01-app-language-and-arabic-reason.md:6-17
  • Snapshot mobile mobile/locate-client@e960c3a (branch development, 2026-02-26; code của chính app, không phải Locate): src/entities/points/model/api/services/points.service.ts:190-263,285-295, src/entities/points/model/api/dto/points.dto.ts:104-136, src/entities/points/model/types/pointGiftOption.server.ts:1-6, src/entities/points/model/types/pointGiftOccasion.server.ts:1-6, src/features/SelectPointGift/model/hooks/useSelectPointGiff.ts:16, src/features/SelectPointGiftOccasion/model/hooks/useSelectPointGiftOccasion.ts:10, src/widgets/SendPointGiftForm/ui/SendPointGiftForm/SendPointGiftForm.tsx:149-185, src/features/PointGiftSummary/ui/PointGiftSummary/PointGiftSummary.tsx:34-37, src/features/PointGiftsCheckoutButton/ui/PointGiftsCheckoutButton/PointGiftsCheckoutButton.tsx:33, src/features/SelectPointGiftPaymentMethod/model/hooks/useSelectPointGiftPaymentMethod.ts:52, src/shared/api/interceptors/languageInterceptor.ts:5-15
  • Capture dev, 2026-10-02 (parity audit): point-offer--shukah, point-offer--locate-st, point-offer--locate-jwt, point-occasion--shukah, point-occasion--locate-st, point-occasion--locate-jwt
  • Capture dev, 2026-10-01: locate-point-offers, locate-point-offers-again, locate-point-offer, locate-point-occasions, locate-point-occasions-again, shukah-point-offer, shukah-point-occasion, probe-locate-point-offers, probe-locate-point-offer, probe-locate-point-occasions, probe-shukah-point-offer, probe-shukah-point-occasion