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

User Migration

Locate đang chuyển user và điểm của họ từ hệ thống Shukah cũ sang các service của chính Locate. Module này tài liệu hóa, cho mobile app, cái gì đã thay thế từng API Shukah mà app gọi trong mảng user và điểm, theo từng field, và còn thiếu gì.

Bối cảnh​

Các ghi chú có ngày tháng về việc migrate đã đi tới đây như thế nào. Thông tin về các service được đọc vào 2026-10-01 và được kiểm tra lại bởi parity audit ngày 2026-10-02; danh sách những gì app gọi lấy từ snapshot app ngày 2026-02-26.

  • 2026-06-25 đến 2026-08-04, xây dựng auth. Copy hàng loạt user, địa chỉ và token từ Shukah vào auth service, rồi các route client kiểu Shukah dưới /api/v1/clients/* (chuyển từ /maeda/api/* vào 2026-07-10), rồi live sync hai chiều.
  • 2026-07-06, kế hoạch. Chuyển toàn bộ user và điểm của user từ Shukah sang nền tảng Locate (Merchant), với cả hai hệ thống chạy song song để việc chuyển diễn ra dần dần và có thể rollback. Kế hoạch ước tính 3 đến 4 tuần.
  • 2026-07-15 đến 2026-09-22, xây dựng points. Copy ledger và live sync (2026-07-15), các API đọc (2026-07-16), rồi đường ghi point-order theo từng slice (2026-09-17 đến 2026-09-22).
  • 2026-10-01, app gọi gì. Snapshot của app gọi các API Shukah được liệt kê trên Tiến độ migrate. Phần lớn đã có route Locate; một vài cái thì chưa, trong đó có việc xóa tài khoản (GET /maeda/client/delete), vẫn ở lại Shukah. Vui lòng xác nhận danh sách với app hiện tại.
  • 2026-10-01, popup cập nhật. App hiển thị popup "cập nhật app" dựa trên bốn response header (new_version, old_version, has_new_version, is_required_update). Không Locate service nào gửi chúng, nên popup sẽ ngừng hoạt động sau khi chuyển, nếu hiện nay Shukah có gửi chúng (Chưa được tài liệu hóa). Xem Verify token.
  • 2026-10-01, cả hai flag point-order đều bật trên dev. Thanh toán một point order bằng điểm và bằng thẻ mỗi cách được bật bởi một server flag. Cả hai đều trả lời là bật trên dev; production chưa xác nhận, và flag tắt sẽ trả 403. Xem Point orders.
  • 2026-10-01, token. App không bao giờ gọi exchange-token. Một user giữ token Shukah mà gọi verify-token của Locate sẽ nhận 401, rồi app xóa token đã lưu và đăng xuất user. Xem Token.
  • 2026-10-01, chỉ có dev được tài liệu hóa. Host, capture và các lần kiểm tra đều là của dev. Việc mỗi route Locate đã deploy lên production hay chưa là một câu hỏi riêng; phần lớn các trang đều nói rõ điều này.
  • 2026-10-02, Shukah đang được port sang Laravel. Shukah dev trả lời bằng các body lỗi của Laravel (các giá trị type như Illuminate\… và Symfony\…). Cột "Implemented in Laravel" trong bảng traffic production của team theo dõi bản port đó, ở cùng các URL. Nó không theo dõi việc chuyển sang Locate.
  • 2026-10-02, parity audit. Cùng một input được gửi tới Shukah và tới Locate trên dev cho mọi route có thể gọi an toàn. Phát hiện chính: không có token nào chạy được trên cả hai service (các route client của auth service, /api/v1/clients/*, chỉ nhận JWT của Locate, các route point của order-v2 chỉ nhận token Shukah, xem Token); việc copy địa chỉ giữa Locate và Shukah bị hỏng ở cả hai chiều (xem Address book); và create point-order của Locate trả lời mà không có id của order mà app giữ (đã xác nhận bằng một lời gọi thật cuối ngày hôm đó), nên các lời gọi pay không tìm thấy order (xem Point orders). Một lời gọi bổ sung cùng ngày cho thấy điểm tiêu ở một bên không bị trừ ở bên kia (trong vài giây của lần chạy; việc nó có tới sau đó hay không thì chưa được kiểm tra), nên app phải đọc số dư ở bên mà nó tạo order. Số dư trên dev: vào 2026-10-01 cùng client có "514405.6788" điểm trên Locate và "128712.41" trên Shukah; vào 2026-10-02 Shukah có "514853.64": dữ liệu Shukah dev đã thay đổi trong khoảng đó. Khi ấy Locate thấp hơn 447.96 điểm, khớp với việc feed từ Shukah sang Locate trễ khoảng hai ngày (suy luận; xem Points wallet). Các trang giờ đã mang những kết quả này, mỗi cái kèm ngày của nó.

Ai gọi cái gì​

Mobile app ──► Auth service ◄──────────► Shukah (legacy, /maeda/...)
│ login, token, profile, ▲ still serves delete-account,
│ address book │ restaurant orders, payments
│ │
└──────► order-v2 (/api/v2/client) ─────┤ order-v2 also calls Shukah
│ passes the point routes │ for some point calls
▼ │
Point service ◄────────────────┘ ledger sync (designed both ways; see Point orders)
(not exposed to the app)

Các service cũng gọi nhau: order-v2 và point service hỏi auth service GET /api/v1/users xem người gọi là ai, nên app không gửi header cho việc đó. Chi tiết của các lần sync nằm trên các trang ở mục Chi tiết (cho backend).

MảngApp gọiDo service nào phục vụShukah trước đây
Đăng nhập và đăng kýreset-otp, verify-otpAuth serviceGET /maeda/api/reset-otp, GET /maeda/api/verify-otp
Phiên và hồ sơverify-token, client lookup, update profile, push setting, add deviceAuth service/maeda/api/verify-token, /maeda/client, PUT /maeda/client/{id}, /maeda/client/add-device-id
Sổ địa chỉlist, get, create, update, deleteAuth service/maeda/address-book và /maeda/address-book/{id}
Đổi tokenexchange-token (app chưa gọi)Auth serviceKhông có: endpoint chỉ có ở Locate
Điểmbalance, history, FAQ, offers, occasionsorder-v2, chuyển tiếp sang point service/maeda/client/points, /maeda/client/point-history, /maeda/points-faq, /maeda/point-offer, /maeda/point-occasion
Point ordercreate, read, description, pay bằng thẻ hoặc sessionorder-v2 và point service/maeda/point-order và các route checkout-payment
Tỷ lệ tích điểm, gift-card codepoint rates, gift-card codechính order-v2/maeda/point-rate, /maeda/giftcard-code/use
Xóa tài khoảnChưa thay thếVẫn ở ShukahGET /maeda/client/delete

Tiến độ migrate có danh sách đầy đủ với trạng thái của từng route, và Shukah vs Locate đặt từng API Shukah cạnh API thay thế của nó.

Base URL​

App dựng một URL từ base URL, một dấu / và endpoint. Các trang Locate viết ví dụ curl với $AUTH_BASE_URL, $ORDER_BASE_URL và $SHUKAH_BASE_URL, chỉ là phần host. Prefix nằm trong path: /api/v1 trên host auth, /api/v2/client trên host order-v2 và /maeda trên Shukah.

Dev​

ServiceHostPrefix
Auth servicehttps://dev.authentication.locate.sa/api/v1
order-v2https://dev.v2.api.order.locate.sa/api/v2/client
Point servicehttps://dev-point-api.locate.sa (app không gọi)/api/client
Shukahhttps://dev-api.shukah.com/maeda

UAT​

ServiceHostPrefix
Auth serviceChưa được tài liệu hóa — hỏi backendChưa được tài liệu hóa — hỏi backend
order-v2https://dev.v2.api.order.locate.sa (giống dev trong cài đặt UAT của app)/api/v2/client
Point serviceChưa được tài liệu hóa — hỏi backendChưa được tài liệu hóa — hỏi backend
Shukahhttps://dev-api.shukah.com (giống dev trong cài đặt UAT của app)/maeda

Production​

ServiceHostPrefix
Auth serviceChưa được tài liệu hóa — hỏi backendChưa được tài liệu hóa — hỏi backend
order-v2https://v2.api.order.locate.sa/api/v2/client
Point serviceChưa được tài liệu hóa — hỏi backendChưa được tài liệu hóa — hỏi backend
Shukahhttps://api.shukah.com/maeda

Hiện nay app không có base URL cho auth service trong bất kỳ cài đặt môi trường nào, nên cần thêm một cái. Các host ở trên lấy từ cài đặt của chính app và tài liệu của các service; host UAT và production của auth service nằm trong deployment settings mà đợt nghiên cứu không thấy.

Token​

  • Token Shukah. Một chuỗi opaque. Shukah cấp nó khi đăng nhập.
  • Token Locate. Một JWT do auth service cấp. Nó không có thời hạn và không có gì thu hồi nó.

Cả hai đều đi trong Authorization: Bearer <token>. Trang nào nhận loại nào:

TrangToken
OTP login, Exchange token, và lời gọi add-device-id trên Profile and settingsKhông cần.
Verify token, Client lookup, Address book, và update profile cùng update setting trên Profile and settingsChỉ JWT của Locate. Token Shukah nhận 401.
Các trang điểm (Points wallet, Point history, Point offers, Point orders, Points FAQ)Chỉ token Shukah, trên dev hiện nay. Một JWT của Locate nhận 401 Invalid or expired token (capture trên dev, 2026-10-02; vào 2026-10-01 nó vẫn nhận 200). Route earn-rate là public. Mỗi trang sẽ nói khi một route không cần token.
User detailVới x-auth-from: SHUKAH, dev chuyển tiếp token sang Shukah, nên token Shukah chạy còn JWT của Locate được dự đoán sẽ nhận 401 (suy luận, 2026-10-02: các route điểm gọi tới nó đã từ chối JWT; hôm đó route này không được gọi với x-auth-from). Các route điểm phụ thuộc vào lời gọi này, đó là lý do JWT của Locate fail ở đó.

Vậy hiện nay app cần cả hai token: JWT của Locate cho auth service và token Shukah cho order-v2. Shukah vs Locate giải thích vì sao, và vì sao điều này quan trọng khi app chuyển đổi.

Lấy test token​

Mục Thử ngay của các trang auth cần một JWT của Locate trong $TOKEN, trừ User detail, trang cần một token Shukah. Mục Thử ngay của các trang điểm order-v2 hiện cần một token Shukah trong $SHUKAH_TOKEN, vì một JWT của Locate nhận 401 ở đó (dev, 2026-10-02). Chỉ dùng dev. Có hai cách để lấy một JWT của Locate (A và B bên dưới), và một cách để lấy token Shukah từ nó (C).

A. Đăng nhập bằng OTP​

Hai lời gọi: reset-otp yêu cầu gửi mã, verify-otp đổi mã lấy token.

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export PHONE='<test-mode-phone>' # placeholder: replace it with a test-mode number
curl -s -G "$AUTH_BASE_URL/api/v1/clients/reset-otp" --data-urlencode "contact_phone=$PHONE"
curl -s -G "$AUTH_BASE_URL/api/v1/clients/verify-otp" --data-urlencode "contact_phone=$PHONE" --data-urlencode "code=2022"

Câu trả lời thứ hai có token trong access_token. Giữ nó làm $TOKEN.

cảnh báo

reset-otp gửi SMS thật trừ khi tài khoản là tài khoản test-mode (account_mode: TEST). Tài khoản test-mode nhận mã cố định 2022 và không có SMS. Service không có API để biến một tài khoản thành test-mode: backend đặt nó trong database. Hãy xin backend một số test-mode; trên dev có số nào không: Chưa được tài liệu hóa.

Số được hard-code +966500000000 cũng chấp nhận 2022, nhưng service vẫn cố gửi SMS tới nó, nên đừng dùng. Không bao giờ đưa số của người thật vào một bài test.

Lưu ý: một mã có hiệu lực 3 phút, và khi một mã còn hiệu lực reset-otp không gửi mã mới (nó vẫn trả {"status":true}). Backend có thể đọc OTP mới nhất qua một route debug admin được bảo vệ bằng API key; route đó chỉ dành cho backend, nên hãy hỏi backend. Xem OTP login để biết đầy đủ các quy tắc.

B. Đổi một token Shukah có sẵn​

Nếu bạn đã có token Shukah của một client trên dev, hãy đổi nó lấy JWT của Locate. Token Shukah đến từ luồng đăng nhập Shukah hiện có; cách lấy nó mà không cần app: Chưa được tài liệu hóa.

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export SHUKAH_TOKEN='<shukah-token>'
curl -s -X POST "$AUTH_BASE_URL/api/v1/clients/exchange-token" -H "Content-Type: application/json" -d "{\"token\":\"$SHUKAH_TOKEN\",\"target_platform\":\"locate\"}"

JWT của Locate nằm trong data.token. Giữ nó làm $TOKEN.

Lời gọi này ghi dữ liệu: mỗi lần nó lưu một token mới cho client và đẩy sang Shukah. Đừng gửi hai lời gọi cho cùng một client trong cùng một giây. Xem Exchange token.

C. Lấy token Shukah từ một JWT của Locate​

Cùng lời gọi exchange-token với JWT của Locate của bạn và target_platform đặt là shukah trả về token Shukah được lưu cho client đó: curl -s -X POST "$AUTH_BASE_URL/api/v1/clients/exchange-token" -H "Content-Type: application/json" -d "{\"token\":\"$TOKEN\",\"target_platform\":\"shukah\"}".

Token Shukah nằm trong data.token. Giữ nó làm $SHUKAH_TOKEN. JWT được kiểm tra trước. Câu trả lời có thể là {"status":200,"data":{}} khi token Shukah đã lưu không có bản raw; khi đó cách này không chạy với client đó (xem Exchange token).

Các biến mà các trang dùng​

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_BASE_URL=https://dev-api.shukah.com
export TOKEN='<locate-jwt>'
export SHUKAH_TOKEN='<shukah-token>'

Thay hai placeholder token bằng token của bạn. Đừng commit token thật vào file hay dán nó vào ticket hoặc chat.

Cách đọc các trang này​

Một trang endpoint có dạng như sau:

  1. Bối cảnh và Tính năng: app hiện làm gì trên Shukah, và tính năng là gì, bằng lời đơn giản.
  2. Mỗi endpoint một block, theo thứ tự:
    • hộp TL;DR (tiêu đề "Replaces Shukah"): API Shukah mà nó thay thế và trạng thái của nó; verdict kèm số khác biệt breaking và cần lưu ý; mỗi khác biệt một dòng, 🔴 cho breaking và 🟡 cho cần lưu ý; service phục vụ nó, những token nào chạy được, được xác minh thế nào và khi nào, và một link tới phía Shukah. Chú giải của Tiến độ migrate giải thích các verdict và các dấu;
    • Request và Response: các bảng của mọi field;
    • Lỗi: các body thật;
    • Thử ngay: một lệnh curl dùng các biến ở trên;
    • Khác biệt so với Shukah: chỉ những khác biệt thật, mỗi cái kèm tác động lên app. Một route không có khác biệt nào ghi Drop-in — không phát hiện khác biệt, kèm ngày.
  3. Lưu ý: tối đa ba cảnh báo một dòng.
  4. Một dòng kẻ, rồi Chi tiết (cho backend): business rule, hazard và Sources, nêu tên repository, commit và file cho từng sự kiện.

Hộp TL;DR và bảng tiến độ dùng các trạng thái sau:

DấuÝ nghĩa
✅ Đã thay thếĐã có route Locate.
❌ Chưa thay thếChưa có route Locate.
➖ Không migrateAPI cố ý ở lại Shukah.
🚩 flagĐã triển khai, nhưng một công tắc (flag ở server hoặc setting của app) quyết định có dùng hay không.

Tiến độ migrate thêm cột Kiểm tra trên dev: một request tới route Locate trên dev không kèm token, vào ngày được hiển thị. Nó cho biết route đã deploy và có phản hồi, chứ không cho biết route chạy đúng với client đã đăng nhập.

Chưa được tài liệu hóa nghĩa là không có code, tài liệu hay capture nào cho thấy câu trả lời. Đó không phải là đoán và cũng không phải "không". Hãy coi nó là một câu hỏi cho team backend hoặc mobile.

Các sample nói rõ chúng đến từ đâu: capture trên dev vào một ngày nào đó (dữ liệu cá nhân được thay bằng giá trị minh họa), hoặc lấy từ tài liệu của một service, kèm path và ngày. Tên file capture trong Sources trỏ tới bộ capture dev ngày 2026-10-01 chưa được commit và bộ capture parity-audit ngày 2026-10-02 (các tên như points--shukah), do tác giả tài liệu giữ; chúng không nằm trong repository này.

Một ngoại lệ so với các module khác. Phần lớn các module không bao giờ nhắc lại tên field hay shape của response, vì OpenAPI được render của service là nguồn sự thật. Các service ở đây không công bố OpenAPI, nên các trang này tự tài liệu hóa các field, từ code. Vì vậy bảng field có thể lỗi thời: hãy kiểm tra commit được nêu ở Sources, và khi một trang và service bất đồng, service thắng.

Tag​

Các trang mang tag. Mỗi tag link tới mọi trang có nó.

TagÝ nghĩa
auth-serviceDo auth service phục vụ. App gọi trực tiếp.
order-v2Đi qua client API của order-v2.
point-serviceDo point service phục vụ, phía sau order-v2.
authenticationĐăng nhập, token và phiên làm việc.
profileHồ sơ, cài đặt và thiết bị của client.
address-bookCác địa chỉ giao hàng đã lưu của client.
pointsSố dư, lịch sử, ưu đãi, dịp tặng và FAQ.
point-ordersMua hoặc tặng điểm, và thanh toán cho việc đó.
shukah-replacementEndpoint của Locate thay thế một API Shukah.
new-endpointEndpoint của Locate không có endpoint tương đương trên Shukah.
overviewCác trang cấp module: bối cảnh, tiến độ và so sánh.
mobileViết cho team mobile app.
backendĐược các backend service gọi, hoặc viết cho backend engineer.
clientDo app client gọi, dùng token của chính client.
adminĐược gọi từ công cụ admin, không phải từ app client. Chưa có trang nào mang tag này.
server-to-serverDo một backend service khác gọi, không phải do app.

Trạng thái không phải là tag. Nó nằm ở Tiến độ migrate.

Các trang​

Bắt đầu từ đây nếu bạn đang chuyển app: Danh sách chuyển đổi.

Auth service​

  • OTP login: đăng nhập hoặc đăng ký bằng số điện thoại và mã 4 chữ số.
  • Verify token: kiểm tra token và lấy hồ sơ của client đang đăng nhập.
  • Exchange token: đổi token Shukah lấy token Locate, hoặc ngược lại.
  • User detail: resolve một token thành hồ sơ; route mà order-v2 và point service gọi.
  • Client lookup: tìm client theo số điện thoại.
  • Address book: liệt kê, đọc, tạo, sửa và xóa địa chỉ đã lưu.
  • Profile and settings: cập nhật hồ sơ, bật hoặc tắt push, đăng ký thiết bị.

Điểm​

  • Points wallet: số dư và hạn dùng, tỷ lệ tích điểm, gift-card code.
  • Point history: các giao dịch điểm của client.
  • Point offers: các gói mà client có thể mua hoặc tặng, và các dịp tặng.
  • Point orders: tạo, đọc và thanh toán một đơn tặng điểm.
  • Points FAQ: câu hỏi và câu trả lời bằng tiếng Anh và tiếng Ả Rập.