Client lookup
Bối cảnh
Trên Shukah, app tìm một client theo số điện thoại bằng GET /maeda/client?contact_phone=…. Auth
service có cùng lookup đó tại GET /api/v1/clients. Nó trả về cùng bốn field, nhưng
cần JWT của Locate và che last name.
Tính năng
Gửi một số điện thoại và nhận lại ai đang giữ số đó: id, phone, first name của client và last name đã được che. Câu trả lời là một mảng có một dòng, hoặc mảng rỗng khi không có client active nào có phone đó.
Trong snapshot app ngày 2026-02-26, app chỉ dùng nó để tìm người nhận của một quà tặng điểm, từ form tặng quà và lần nữa ở bước checkout quà tặng. Nó chỉ đọc dòng đầu tiên, và mảng rỗng trở thành lỗi "User not found" trong app.
Lời gọi cần một phiên đã đăng nhập, nhưng số điện thoại bạn tra không nhất thiết phải là của bạn. Token chỉ xác định người gọi. Phone trong query quyết định ai được trả về.
GET /api/v1/clients
GET /maeda/client✅ Đã thay thế- 🔴 Token Shukah nhận 401 "Unauthorization"; hãy gửi JWT của Locate (lấy từ exchange-token; giữ lại token Shukah).
- 🟡 Locate che last_name ("User" thành "U**r"); chưa rõ Shukah có che không (bản capture đã được làm sạch).
- 🟡 Số điện thoại chưa tồn tại trả 200 [] trên Locate (theo code); app biến [] thành "User not found" (user.service.ts:49-51).
- 🟡 Body 401 của Locate là {status,message}, không phải body Laravel; app chỉ đọc message.
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <locate-jwt>, token của người gọi. Token Shukah bị từ chối với 401. | Phiên của người gọi. |
contact_phone | query | string | có | Khoảng trắng bị bỏ. Nó được so khớp với phone đã lưu có hoặc không có dấu + ở đầu. Trống hoặc thiếu cho 400. Một dấu + nguyên văn trong URL bị đọc thành khoảng trắng và bị bỏ, nên +966… không encode vẫn khớp. | Số điện thoại cần tra. Có thể thuộc về bất kỳ ai, không chỉ người gọi. |
key, language và mọi query key khác | query | string | không | Được chấp nhận và bỏ qua. | Giữ lại để query string Shukah cũ vẫn chạy. |
Response
200 với một mảng gồm không hoặc một dòng:
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
client_id | integer | không | — | Id của client tìm được. |
contact_phone | string | có | Phone như đã lưu, không bị che. | Số điện thoại của client. |
first_name | string | có | Đã trim. Trống thành null. Không bị che. | Tên. |
last_name | string | có | Bị che: giữ ký tự đầu và cuối và mọi ký tự giữa chúng thành * (User thành U**r). Tên một hoặc hai ký tự được trả nguyên. Trống thành null. | Họ, đã che. |
Khi không có client active, chưa bị xóa nào có phone đó, câu trả lời là 200 với []. Client đang
suspended, blocked, pending hoặc expired cũng nhận [].
[
{
"client_id": 356259,
"contact_phone": "+966500000000",
"first_name": "Test",
"last_name": "User"
}
]
Capture trên dev, 2026-10-01 (client 356259); các field cá nhân đã được thay thế. Capture đã thay last_name bằng User, nên dạng đã che của giá trị thật không hiển thị ở đây. Quy tắc che đến từ code và unit test của nó: User sẽ thành U**r.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
400 | {"name":"Bad Request","message":"Phone number cannot be blank.","code":0,"status":400,"type":"yii\\web\\BadRequestHttpException"} | contact_phone thiếu hoặc trống. |
401 | {"status":401,"message":"Unauthorization"} | Token thiếu, sai định dạng hoặc không phải JWT của Locate, client của người gọi không tồn tại, hoặc client của người gọi không active. |
Body 401 được capture trên dev vào 2026-10-01 khi không có token. Body 400 có cùng shape với
cái đã capture cho reset-otp (xem OTP login). Nó chưa được capture trên
route này, vì lần chạy capture không có lời gọi lookup nào với token và phone rỗng.
Thử ngay
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
export PHONE='<phone-to-look-up>' # the answer shows that client's first name and phone
curl -s -G "$AUTH_BASE_URL/api/v1/clients" --data-urlencode "contact_phone=$PHONE" -H "Authorization: Bearer $TOKEN"
Kỳ vọng: 200 và một mảng có một dòng, như ở mục Response. Phone không ai có cho [].
Khác biệt so với Shukah
Body 200 có cùng bốn key ở cả hai phía: vào 2026-10-01 cả hai service trả client_id,
contact_phone, first_name và last_name cho client 356259.
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| Token | Token Shukah (capture dùng một token như vậy). | Chỉ JWT của Locate. Token Shukah nhận 401 ở cùng guard với verify-token. | Exchange token Shukah lấy JWT trước khi chuyển, và giữ token Shukah cho order-v2. |
| URL | GET $SHUKAH_BASE_URL/maeda/client?contact_phone=…&key=maeda&language=en-GB (lời gọi đã capture). | GET $AUTH_BASE_URL/api/v1/clients?contact_phone=…. Locate bỏ qua key và language. | Đổi base URL và path. App có thể tiếp tục gửi key và language. |
last_name | Chưa được tài liệu hóa: capture trên dev đã thay last_name, nên không thấy được việc che. | Bị che: giữ ký tự đầu và cuối và phần giữa thành *. | App copy last_name từ dòng đầu tiên, và Locate trả nó đã che. Hãy kiểm tra flow tặng quà dùng tên như thế nào. |
| Phone không tồn tại | Chưa được tài liệu hóa. | 200 với [], cũng áp dụng cho client không active. | App đã biến mảng rỗng thành "User not found", nên [] không cần đổi gì. |
Body 401 | {"name":"Unauthorized","message":"Your request was made with invalid credentials.","code":0,"status":401,"type":"Illuminate\\Auth\\AuthenticationException"} (không token, capture trên dev, 2026-10-01). | {"status":401,"message":"Unauthorization"} (không token, capture trên dev, 2026-10-01). | Mọi chỗ so khớp theo text lỗi, name hay type đều phải đổi. status là 401 ở cả hai. |
Lưu ý
last_nametrả về đã che (UserthànhU**r), nhưngfirst_namevàcontact_phonetrả về đầy đủ.- Phone không tồn tại và client không active đều nhận
200với[], mà app biến thành lỗi "User not found", nên không phân biệt được hai trường hợp. - Token và phone độc lập với nhau: bất kỳ người gọi đã đăng nhập nào cũng có thể tra bất kỳ phone nào và nhận first name và phone của client đó.
Chi tiết (cho backend)
Chỉ kiểm tra dev: host https://dev.authentication.locate.sa, prefix /api/v1. Host và prefix của UAT
và production: Chưa được tài liệu hóa.
Route và lookup
GET /clientsđược đăng ký trước mọi route có tham số, phía sau middlewareclient, nên nó cần JWT Locate của một client active giống như verify-token (src/routes/client.js:14-16,src/middlewares/client.js:8-42).- Handler đọc
contact_phonevà trả400qua lỗi Yii khi nó trống (src/controllers/client/lookup/index.js:33-34). - Lookup là
findClientByPhone, giống reset-otp: phone đã lưu với+tùy chọn, chưa bị xóa, statusACTIVE,client_idmới nhất trước, không cache (src/controllers/client/reset-otp/handler.js:24-30,src/shared/helpers/filter.js:1-4). Nó trả về một kết quả khớp tốt nhất, nên mảng có tối đa một dòng (src/controllers/client/lookup/index.js:36-40). - Một dấu
+nguyên văn trong URL được decode thành khoảng trắng, vànormalizePhonesau đó bỏ nó, nên+966…không encode vẫn khớp (src/sync/lib/casts.js:40-41; đọc code). - Design doc nêu các flow merchant giữ một JWT client của Locate làm use case
(
docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:9-11). Snapshot app chỉ gọi nó để resolve người nhận quà (xem Sources). - Lời gọi chỉ đọc, không có cache riêng, không phân trang và không có side effect
(
docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:25-28).
Map dòng và che dữ liệu
toLookupRow dựng từng dòng (src/controllers/client/lookup/index.js:23-28):
client_idlàclient_idđã lưu.contact_phonelà phone đã lưu, hoặcnull.first_nameđược trim, và trống thànhnull(src/controllers/client/lookup/index.js:17-21).last_nameđi quamaskLastName: trim,nullhoặc trống chonull, độ dài hai trở xuống thì giữ nguyên, còn lại là ký tự đầu,length - 2dấu sao và ký tự cuối (src/controllers/client/lookup/index.js:8-15). Unit test chốtUserthànhU**r(src/controllers/client/__tests__/lookup.test.js:32).- Việc che
first_namehaycontact_phonenằm ngoài phạm vi trong design doc (docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:93). Vì vậy bất kỳ người gọi đã đăng nhập nào cũng nhận chúng đầy đủ cho bất kỳ phone nào, và lời gọi không phân biệt được phone không tồn tại với phone không active.
Chưa được tài liệu hóa
- Shukah có che
last_namehay không: capture đã thay giá trị. - Shukah trả gì cho phone không tồn tại hoặc phone trống: cả hai đều chưa được capture.
- Lookup của Shukah có nhận thêm các filter danh sách hay không. Design doc liệt kê "Pagination /
status/pagelist filters from upstream" là ngoài phạm vi của Locate (docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:91), và capture chỉ dùngcontact_phone,keyvàlanguage. - Có môi trường nào có route này hay không:
origin/productiontại4f3bc96(2026-09-21) không đăng ký nó (src/routes/client.jscủa nó không importlookup), và có thứ gì chạy từ ref đó hay không thì chưa rõ.
Khi tài liệu và code mâu thuẫn (code thắng)
- Ví dụ che trong design doc có 13 dấu sao (
U*************)choUser (500…000)) (docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:30). Code và unit test cho 12 (src/controllers/client/__tests__/lookup.test.js:33), vì quy tắc làlength - 2. Khác biệt này chỉ là hình thức.
Sources
locate-authentication-service@6757aa4:src/routes/client.js:14-16,src/middlewares/client.js:8-42,src/controllers/client/lookup/index.js:8-41,src/controllers/client/__tests__/lookup.test.js:32-33,src/controllers/client/reset-otp/handler.js:24-30,src/shared/helpers/filter.js:1-4,src/sync/lib/casts.js:40-41,src/shared/errors/yii-bad-request-error.js:12-23,src/middlewares/error-handler.js:10,src/shared/constant/error-message.js:59- Tài liệu của
locate-authentication-service@6757aa4:docs/plans/2026-08-04-client-lookup-by-phone-api-local.md:9-11,25-30,56-59,90-93(2026-08-04) - Snapshot mobile
mobile/locate-client@e960c3a(branchdevelopment, 2026-02-26; code của chính app, không phải Locate):src/entities/user/model/api/services/user.service.ts:38-47,49-51,58-60,src/entities/user/model/api/dto/user.dto.ts:126-155,src/widgets/SendPointGiftForm/model/hooks/sendPointGiftForm.ts:64,src/widgets/PointGiftsCheckout/model/hooks/usePointGiftsCheckout.ts:376 - Capture dev, 2026-10-01:
locate-client-lookup,shukah-client-lookup,probe-locate-client-lookup,probe-shukah-client-lookup,probe-locate-reset-otp - Bằng chứng phụ, không phải sha đã ghim:
locate-authentication-serviceorigin/productiontại4f3bc96(2026-09-21),src/routes/client.js