Point orders
Bối cảnh
Một point order là một món quà điểm từ client này tặng client khác, hoặc điểm mà một client mua cho
chính mình. Client chọn một offer, tùy chọn một occasion, và một người nhận, rồi trả bằng điểm
hoặc bằng thẻ. Trên Shukah app làm việc này bằng POST /maeda/point-order,
POST /maeda/point-order/get-description và hai lời gọi thanh toán trên /maeda/order/checkout-payment-*.
Locate có một route cho mỗi lời gọi trong bốn lời gọi đó, cộng một route mới đọc một order. Ba trong
bốn route là adapt: create và hai route thanh toán cần app thay đổi. Get-description là
drop-in: chỉ đổi base URL và path, nhưng TL;DR của nó có các dòng cần lưu ý (các id và token).
Có sáu điều cần chú ý:
- Order mà app nhận về có các key khác: id là một UUID gọi là
correlation_id, và giá làmoney_amount. Các route thanh toán chỉ định order bằngcorrelation_idtrong path. - Đ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). Trên dev ngày 2026-10-02, một order trả bằng điểm chỉ làm giảm số dư của bên tạo ra nó, nên app phải đọc số dư và lịch sử ở chính bên mà nó tạo order.
- Create chỉ chấp nhận
payment_typelàpointhoặccheckout. App cũng có thể gửipayforthoặchyperpay, và Locate trả400cho cả hai. - Trả bằng điểm và hai route thanh toán nằm sau hai server flag. Cả hai đều bật trên dev vào 2026-10-01. Production chưa được xác nhận.
- Create không có idempotency key, và sau một
504app không thể tìm order mà nó có thể đã tạo. - Một số lời gọi quà tặng khác của app không có route Locate: danh sách quà đã gửi, lời gọi payment-token, lời gọi STC Pay và các lời gọi PayFort.
Tính năng
Các lời gọi xảy ra ở đâu trong app
Trong snapshot app ngày 2026-02-26 flow quà tặng nằm trên form send-gift và gift checkout. Cả
năm route Locate đều cần token, và trên dev hiện nay nó phải là token Shukah: chúng dùng cùng bước kiểm tra token
như các route point khác, vốn nhận 401 cho một JWT Locate vào 2026-10-02 (xem
Which tokens work). Vào 2026-10-01 cả hai token vẫn chạy trên route
balance.
| Bước | Lời gọi | App làm gì |
|---|---|---|
| 1. Soạn lời nhắn | Description | Trong khi client điền form, app xin lời nhắn mà người nhận sẽ thấy. Tài liệu của point service thấy nó gọi hai lần cho một order: một lần với offer, và một lần nữa với offer và occasion. |
| 2. Tìm người nhận | Client lookup | Khi submit, app tra người nhận theo phone và lấy userId làm to_client_id. |
| 3. Tạo order | Create | App đầu tiên tải lại các common settings. Nó gửi payment_type là point khi client trả bằng điểm, và nếu không thì là giá trị payment từ các settings đó: payfort, hyperpay hoặc checkout. |
| 4a. Trả bằng điểm | không có | Order đã được thanh toán khi create trả về. App tải lại lịch sử, mở tab Sent gifts và hiển thị "Gift sent successfully". |
| 4b. Thẻ đã lưu | Pay with card | App gửi id của thẻ đã lưu và CVV. Câu trả lời bắt đầu bước 3-D Secure. |
| 4c. Thẻ mới, Apple Pay hoặc STC Pay | Pay with session | App xin một payment session và đưa nó cho Checkout SDK. payment_type mà nó gửi (applePay, stcpay hoặc card trong snapshot) bị Locate bỏ qua. App 2.31.5 đã gửi applePay ở đây vào 2026-10-02, và gửi STC Pay tới route checkout-payment-stcpay riêng của Shukah, vốn không có route Locate. |
| 5. Sau khi thanh toán bằng thẻ | không có | Khi SDK báo thành công app tải lại lịch sử và mở tab Sent gifts. |
| Không màn hình nào dùng | Get order | App có một lời gọi cho một order Shukah, nhưng không màn hình nào dùng nó. |
Khi client settings không ghi checkout, app trả bằng PayFort hoặc Hyperpay thay vào đó, với
các lời gọi không có route Locate. Điều tương tự áp dụng cho lời gọi Frames-token của app, lời gọi STC Pay và
lời gọi CVV-token tới payment service (xem Chi tiết).
Vòng đời
Một order mới bắt đầu là initial_order hoặc, khi trả bằng điểm, là successful. Các status là
từ ngữ của chính Shukah, giữ nguyên.
status | payment_status | Ý nghĩa | Đạt được bằng cách nào |
|---|---|---|---|
initial_order | null | Đã tạo, chưa thanh toán. | Create với checkout. Order ở lại đây cho đến khi gateway báo kết quả cho point service. Một order không ai trả được để yên. |
successful | null | Đã trả bằng điểm. Người gửi bị trừ và người nhận được cộng. | Create với point. |
successful | CAPTURED | Đã trả bằng thẻ. Người nhận được cộng. | Gateway báo một khoản thanh toán captured, thường trong vài giây sau lời gọi pay. |
payment_failed | FAILED | Thẻ bị từ chối. Đây là trạng thái cuối: cả hai route pay giờ trả 409, nên client phải tạo một order mới. | Gateway báo một khoản thanh toán bị từ chối. |
paid | AUTHORIZED | Chỉ xuất hiện trên các order copy từ Shukah. Locate không bao giờ set chúng. | Không đạt được từ các route Locate. |
Không có route refresh. Sau một lời gọi pay app theo dõi Get order hoặc Point history cho đến khi status đổi.
Ai được làm gì
| Lời gọi | Ai | Những người khác |
|---|---|---|
| Create | Người gọi luôn là người trả. Người trả lấy từ token, không bao giờ từ body. | — |
| Get order | Người trả hoặc người nhận. | 404 point order not found, cùng câu trả lời như với một order không tồn tại. |
| Pay with card, pay with session | Chỉ người trả. | 404, cũng cho người nhận. |
| Description | Bất kỳ client nào. Người tặng trong câu là người gọi. | — |
Không có idempotency key, và làm gì sau một 504
Create không có idempotency key. Mỗi lời gọi tạo một order mới với correlation_id mới. Một lần thử lại sau
timeout vì vậy có thể tạo một order thứ hai.
Một 504 nghĩa là kết quả không rõ. Tài liệu của chính order-v2 nói app phải đọc lại order thay vì
thử lại một cách mù quáng. Những gì code cho phép (đọc từ code, không phải điều tài liệu nêu):
- Read, description, pay with session. An toàn để thử lại. Một lời gọi pay-with-session lặp lại chỉ tạo thêm một session.
- Pay with card. App biết
correlation_id. Hãy đọc order trước và đừng gọi lại pay with card cho đến khi order làsuccessfulhoặcpayment_failed. Gateway không làm cho payment reference là duy nhất, nên một lần capture thứ hai chỉ được nâng thành cảnh báo và không được hoàn tiền. - Create với
checkout. App chưa bao giờ nhận đượccorrelation_id, không có route list và không có idempotency key, nên nó không thể tìm order. Một order trùng chưa thanh toán là vô hại: các order không bao giờ được thanh toán thì được để yên. - Create với
point. Tương tự, nhưng một order trùng sẽ trừ người gửi hai lần nếu lời gọi đầu đã hoàn tất. Hãy kiểm tra số dư và lịch sử trước khi thử lại.
App nên tìm order thế nào sau một create 504: Chưa được tài liệu hóa.
Flag
Hai công tắc trong point service quyết định thanh toán có chạy hay không. Cả hai đã được probe trên dev vào 2026-10-01 bằng các lời gọi không ghi gì.
| Flag | Nó điều khiển gì | Dev, probe 2026-10-01 | Production | App thấy gì khi nó tắt |
|---|---|---|---|---|
POINT_ORDER_POINT_ENABLED | Create với payment_type point. checkout không bị ảnh hưởng. | Bật. Một body trả-bằng-điểm cho chính mình trả 400 a point order cannot be paid with points to yourself. Khi flag tắt sẽ là 403. | Chưa xác nhận. | 403 {"success":false,"error":{"code":"FORBIDDEN","message":"paying a point order with points is disabled"}}, trước mọi lookup hay ghi. |
POINT_ORDER_CARD_ENABLED | Cả hai route pay. | Bật. pay/session trên một order id ngẫu nhiên trả 404 point order not found. Khi flag tắt sẽ là 403. | Chưa xác nhận. | 403 {"success":false,"error":{"code":"FORBIDDEN","message":"card payment for point orders is disabled"}}, trước mọi lần đọc: ngay cả một order id không tồn tại cũng nhận 403. |
Các tài liệu cũ hơn (2026-09-21, 09-22 và 09-25) nói cả hai flag chưa được đặt ở mọi môi trường. Một tài liệu ngày 2026-09-17 nói điều kiện tiên quyết cho trả-bằng-điểm chỉ được đáp ứng trên dev và production vẫn chưa đặt cho đến khi rollout. Các phát biểu đó cũ hơn lần probe, nên với dev thì probe thắng. Với production: Chưa được tài liệu hóa. Các giá trị được set trong môi trường của point service và được bake vào image của nó, nên một thay đổi cần một lần build image mới.
Timeout
order-v2 chờ point service 10 giây và 20 giây trên hai route pay. Giới hạn của chính app cho order-v2
là 20 giây, nên trên các route pay app có thể bỏ cuộc vào gần cùng lúc với 504 của order-v2.
Các route pay tiếp tục đi tới payment service, vốn có mặc định 10 giây riêng.
POST /api/v2/client/point-orders
POST /maeda/point-order✅ Đã thay thế🚩 POINT_ORDER_POINT_ENABLED- 🔴 Không có point_order_id: app tạo pointOrderId = String(undefined) = "undefined"; amount đổi tên thành money_amount.
- 🔴 Cũng thiếu: client_id, to_client_id, point_occasion_id, occasion, description, payment_point_order_id.
- 🔴 Các route thanh toán cần correlation_id dạng UUID trong path; hãy giữ nó từ create, nếu không pay trả 404.
- 🔴 Điểm tiêu ở một bên không bị trừ ở bên kia trong vài giây (về sau chưa kiểm tra): hãy đọc số dư và lịch sử ở bên tạo order.
- 🔴 payment_type payfort hoặc hyperpay (app gửi giá trị từ settings) trả 400; chỉ point hoặc checkout hoạt động.
- 🔴 point_offer_id và point_occasion_id phải là id 24 ký tự hex từ danh sách Locate; số nguyên của Shukah trả 400.
- 🟡 Shukah trả 201, Locate trả 200; Locate thêm correlation_id, order_no, payment_status và order_type.
- 🟡 payment_type point tới id của chính mình trả 400; checkout tự tặng quà thì được (Shukah cho phép cả hai).
- 🟡 point cần POINT_ORDER_POINT_ENABLED (403 khi tắt); trên dev, không có 403 nào mở lỗi toàn cục.
- 🟡 Không có idempotency key; sau lỗi 504 app không bao giờ biết id của order.
Flag của point-service cho payment_type "point" (403 FORBIDDEN khi tắt). BẬT trên dev, đã probe 2026-10-01; production chưa xác nhận.
Chỉ chấp nhận payment_type point hoặc checkout (không có payfort hay hyperpay) và trả về order có shape khác (correlation_id, money_amount, v.v.).
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <token>: một token Shukah trên dev hiện nay. Một JWT Locate nhận 401 ở cùng bước kiểm tra như các route point khác (2026-10-02): xem Which tokens work. | Token của người trả. |
Content-Type | header | string | có | application/json. | Body là JSON. |
point_offer_id | body | string | có | 24 ký tự hex, được kiểm tra và không bao giờ chuyển đổi. Một integer Shukah, một số hay "3" là 400. | id của offer từ danh sách offer. |
to_client_id | body | integer | có | Một số nguyên dương. Một số JSON hoặc một numeric string được chấp nhận. " 7 " thành 7 và true thành 1. "", null, key bị thiếu, "abc" và "12.5" là 400. | Client id của người nhận, như client lookup trả về. |
payment_type | body | string | có | Chính xác point hoặc checkout. card, payfort và hyperpay là 400. | point: trả bằng điểm của người trả ngay trong lời gọi này. checkout: tạo một order chưa thanh toán để trả bằng thẻ. |
point_occasion_id | body | string hoặc null | không | 24 ký tự hex. Key bị thiếu, null, 0, "0" và "" đều nghĩa là không có occasion. Mọi giá trị không phải hex khác (" ", 5) là 400. | id của occasion từ danh sách occasion. |
occasion | body | string hoặc null | không | Đã trim, 1 đến 255 ký tự. "", khoảng trắng và null nghĩa là không có. Không phải string hoặc 256 ký tự là 400. | Text tự do, cho một occasion custom. |
| Bất kỳ key nào khác | body | any | không | Bị bỏ. | — |
Một body không phải JSON object lỗi với 400 và details là {"formErrors":["Expected object, received null"],"fieldErrors":{}}.
Response
200 với một object trần (không phải 201). Nó có chín key. Cùng chín key đó trả về từ
Get order.
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
correlation_id | string | không | Một UUID v4, 36 ký tự. | Id của order. Nó nằm trong path của các route get và pay. |
order_no | integer | có | Giá trị đầu tiên 100000. null chỉ trên các order copy từ Shukah. | Số order dễ đọc. |
status | string | không | initial_order, paid, payment_failed hoặc successful. Một order checkout mới là initial_order, và một order trả-bằng-điểm là successful. | Status của order. |
payment_status | string | có | CAPTURED, FAILED, AUTHORIZED hoặc null. null khi tạo. | Trạng thái của gateway. |
point_amount | number | không | point_amount của offer. | Số điểm người nhận được. |
purchased_point_amount | number | có | Giá trị của offer. null khi offer không có giá bằng điểm. | Số điểm người trả bị trừ khi trả bằng điểm. |
money_amount | string | không | Chính xác hai chữ số thập phân, ví dụ "40.00". | Số SAR thẻ bị tính. |
payment_type | string | không | point hoặc checkout. | Echo của request. |
order_type | string | không | gift khi tìm thấy một dòng occasion, nếu không là transfer. Text tự do không bao giờ quyết định điều đó. | Loại order. |
{
"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"
}
Capture trên dev, 2026-10-01 (client 356259): một order checkout cho offer đầu tiên, gửi tới id của
chính client, không có occasion. Nó chưa bao giờ được thanh toán và ở lại trên dev, vì không route nào xóa một
order.
checkout và point. Một order checkout chỉ ghi một dòng order và không gì khác: không điểm nào di chuyển
và không có gì được gửi sang Shukah hay payment service. Một order point được thanh toán trong cùng lời gọi. Nó
trừ người trả purchased_point_amount, cộng người nhận point_amount, và lưu order là
successful. Tất cả diễn ra cùng nhau hoặc không gì cả. Trên các offer live người trả bị trừ
nhiều hơn người nhận được, ví dụ 110 điểm cho 100.
Tự tặng. to_client_id có thể bằng người gọi với checkout: mua điểm cho chính mình là
trường hợp phổ biến trên Shukah (814 trong 1,228 order). point từ chối nó với 400.
Lỗi
Các kiểm tra chạy theo thứ tự này với point: flag, tự thanh toán, offer, người nhận, occasion,
purchased_point_amount, hồ sơ người trả, rồi khoản trừ. Với checkout: offer, người nhận, occasion,
rồi order được lưu.
| HTTP | Body | Khi nào |
|---|---|---|
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 không qua được kiểm tra. Ví dụ là payment_type: "card", từ một lần chạy local của schema của service, không capture live. |
400 | {"success":false,"error":{"code":"VALIDATION_ERROR","message":"a point order cannot be paid with points to yourself"}} | payment_type là point và to_client_id là người gọi. Đã capture trên dev, 2026-10-01. |
400 | Cùng shape, message là to_client_id <id> does not exist. | Người nhận không phải client đã biết. Chưa capture. |
400 | Cùng shape, message là point occasion <id> not found or inactive. | Occasion id không tồn tại hoặc inactive. Chưa capture. |
400 | Cùng shape, message là point offer <id> cannot be paid with points: it has no purchased_point_amount. | point trên một offer không có giá bằng điểm. Chưa capture. |
400 | Cùng shape, message là client <id> does not exist. | Người trả không có hồ sơ (chỉ đạt tới ở point). Chưa capture. |
403 | {"success":false,"error":{"code":"FORBIDDEN","message":"paying a point order with points is disabled"}} | payment_type là point và flag tắt. Được kiểm tra trước mọi lookup. Chưa capture, vì flag đang bật. |
404 | {"success":false,"error":{"code":"NOT_FOUND","message":"point offer <id> not found or inactive"}} | Offer không tồn tại, inactive hoặc đã bị xóa. Chưa capture. |
422 | {"success":false,"error":{"code":"INSUFFICIENT_POINTS","message":"insufficient points for client <id>"}} | point, và số dư thấp hơn purchased_point_amount. Không có order nào được tạo. 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. |
401 | Cù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. Chưa capture trên route này. Các route point khác đã trả Invalid or expired token cho một JWT Locate vào 2026-10-02. |
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. Từ order-v2. Order có thể đã tồn tại: xem "Không có idempotency key" ở trên. Chưa capture. |
Mọi lỗi từ point service đều có envelope {"success":false,"error":{"code":"…","message":"…"}}.
Thử ngay
Lời gọi này tạo một order trên dev mà không thể xóa. Hãy dùng checkout: point làm di chuyển điểm.
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\"}"
Kỳ vọng: 200 và body chín key như ở mục Response, với status là initial_order (capture trên
dev, 2026-10-02, với token Shukah). Một JWT Locate nhận 401 ở bước kiểm tra token (đọc code: create
chưa được gọi với JWT). Không có token bạn nhận 401 như ở mục Lỗi.
Khác biệt so với Shukah
Cả hai bên được gọi trên dev ngày 2026-10-02 (client 356259, token Shukah) với cùng offer, người nhận
và payment_type: một lần với checkout và một lần với point, gửi tới một client dev khác. Shukah
trả 201 với dòng order của nó (body nằm ở
trang so sánh). Locate trả 200. Order point trên
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"
}
Capture trên dev, 2026-10-02. Order checkout (order_no 100011) có cùng chín key, với status là
initial_order và payment_type là checkout. Cả hai order vẫn nằm trên dev.
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | POST $SHUKAH_BASE_URL/maeda/point-order | POST $ORDER_BASE_URL/api/v2/client/point-orders | Đổi base URL và path. |
| Token | Token opaque của Shukah. | Chỉ token Shukah trên dev hiện nay: nó chạy với create vào 2026-10-02. Một JWT Locate nhận 401 ở bước kiểm tra token của point service, như trên các route point khác (2026-10-02; chưa thử với create). | App phải tiếp tục gửi token Shukah ở đây. Với các môi trường khác: Chưa được tài liệu hóa. |
| HTTP status | 201. | 200 (capture trên dev, 2026-10-02). | Không có khi app chấp nhận mọi 2xx. Code kiểm tra đúng 201 phải đổi. |
payment_type | App gửi point, hoặc giá trị payment từ common settings: payfort, hyperpay hoặc checkout. | Chỉ point hoặc checkout. payfort và hyperpay là 400. | Nếu settings ghi payfort hoặc hyperpay, create lỗi. Các gateway đó có bị bỏ cho point order hay không: Chưa được tài liệu hóa. Trên Shukah, 1,126 trong 1,225 order dùng payfort (tài liệu của point service ngày 2026-07-29). |
| Id trong body | Integer dạng text. | point_offer_id và point_occasion_id là chuỗi 24 ký tự. to_client_id không đổi. | Gửi các id từ các danh sách Locate. Một integer Shukah là 400. |
| Các key của response | Mười hai key: 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, capture trên dev, 2026-10-02; cùng các key với các dòng order của Shukah trong danh sách quà đã gửi). App khai báo occasion_id thay vì point_occasion_id, nên occasionId của nó luôn là undefined. | Chín key: correlation_id, order_no, status, payment_status, point_amount, purchased_point_amount, money_amount, payment_type, order_type. | Chỉ point_amount, purchased_point_amount, status và payment_type có ở cả hai. amount là money_amount trên Locate, còn point_order_id, client_id, to_client_id, point_occasion_id, occasion, description và payment_point_order_id bị thiếu. App đọc point_order_id, amount và payment_point_order_id và truyền order id cho mọi lời gọi pay, nên object order phải được dựng lại từ các key mới. |
| Order id | point_order_id: một số (1256 và 1257 từ create, 2026-10-02), được gửi lại dưới dạng chuỗi ("1252") trong các body payment của app 2.31.5 (2026-10-02). | Một UUID, correlation_id. | Các route pay và get nhận nó trong path. Create trả lời mà không có point_order_id, nên pointOrderId của app trở thành chuỗi "undefined" và mọi lời gọi pay nhận 404. |
| Tự tặng | Được phép. | Được phép với checkout, 400 với point. | Không có với checkout. |
| Từ status | initial_order, payment_failed, successful, paid (như app khai báo type). | Cùng các từ đó. Một order Locate chỉ đạt tới initial_order, successful và payment_failed. | Không có. |
| Idempotency | Không có. | Không có. | Cùng rủi ro ở cả hai. |
| Body lỗi | Chưa được tài liệu hóa. | Envelope của point-service ở mục Lỗi. | Mọi chỗ so khớp theo text lỗi đều phải đổi. |
| Góc nhìn của Shukah về order | Không áp dụng. | Một order point xếp hàng hai cập nhật cho Shukah, nhưng hiện nay không có gì gửi hàng đợi đó trên k8s. Một order checkout không xếp hàng gì. | Shukah không thấy các order Locate cho đến khi có một drainer. Tab Sent gifts đọc từ Shukah. |
Số dư sau một order point | Order 1257 làm số dư Shukah giảm 110 (514853.64 còn 514743.64). Số dư Locate không đổi. | Order 100012, vài giây sau, làm số dư Locate giảm 110 (514405.6788 còn 514295.6788). Số dư Shukah không đổi. Dev, 2026-10-02. | Breaking. Hai ledger không được đồng bộ khi tiêu điểm, ít nhất là không trong vài giây; việc tiêu điểm có tới sau đó hay không thì chưa được kiểm tra. Nếu app tạo order trên Locate nhưng đọc số dư hoặc lịch sử từ Shukah (hoặc ngược lại), nó hiển thị sai số dư. |
GET /api/v2/client/point-orders/:correlation_id
Route mới. Tài liệu của point service chỉ nói "Read-through, no settle". App có một lời gọi cho một
order Shukah đơn lẻ (GET /maeda/point-order/{id}), nhưng không màn hình nào dùng nó, nên route này không có
đối tác Shukah nào trong flow của app. Nó thay thế lời gọi Shukah nào, nếu có: Chưa được tài liệu hóa.
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <token>: một token Shukah trên dev hiện nay. Một JWT Locate nhận 401 ở cùng bước kiểm tra như các route point khác (2026-10-02): xem Which tokens work. | Token của người trả hoặc người nhận. |
correlation_id | path | string | có | Mọi chuỗi không rỗng; không có kiểm tra định dạng. GET …/point-orders/description được đọc là id description. | correlation_id mà create đã trả về. |
| Bất kỳ query key nào | query | — | không | Được order-v2 forward và point service bỏ qua. | — |
Response
200 với cùng chín key như response của create. Với một order copy từ Shukah, order_no là
null và status cùng payment_status mang giá trị của chính 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"
}
Capture trên dev, 2026-10-01 (client 356259): order được tạo bởi sample create, đọc ngay sau đó.
Lời gọi chỉ đọc. Nó không bao giờ settle hay làm mới một order: status chỉ đổi khi gateway báo một kết quả. Không có cách nào liệt kê các order, nên app chỉ có thể đọc một order mà nó đã giữ id.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
404 | {"success":false,"error":{"code":"NOT_FOUND","message":"point order not found"}} | Không có order nào với id đó cho người gọi với tư cách người trả hoặc người nhận. Một order không tồn tại và order của người khác cho cùng câu trả lời. 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. |
401 | Cù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. Chưa capture trên route này. Các route point khác đã trả Invalid or expired token cho một JWT Locate vào 2026-10-02. |
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 CORRELATION_ID='<correlation_id-from-create>'
curl -s "$ORDER_BASE_URL/api/v2/client/point-orders/$CORRELATION_ID" -H "Authorization: Bearer $SHUKAH_TOKEN"
Kỳ vọng: 200 và body chín key như ở mục Response. Một id không phải của bạn cho 404 như ở mục
Lỗi. Hãy dùng token Shukah trên dev hiện nay, như với mọi route point.
POST /api/v2/client/point-orders/description
POST /maeda/point-order/get-description✅ Đã thay thế- 🟡 Shukah trả {"description":""}, Locate trả một câu đầy đủ; chỉ để hiển thị nên không gây lỗi.
- 🟡 id phải là id 24 ký tự hex của Locate; "1" và "1" của Shukah trả 400 VALIDATION_ERROR ở cả hai field.
- 🟡 Câu luôn bằng tiếng Anh và dùng tên cùng số điện thoại đã che của người gọi; là dữ liệu cá nhân, đừng ghi log.
- 🟡 Chưa thử JWT trên route này; cùng bước kiểm tra đó từ chối JWT trên mọi route points khác.
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <token>: một token Shukah trên dev hiện nay. Một JWT Locate nhận 401 ở cùng bước kiểm tra như các route point khác (2026-10-02): xem Which tokens work. | Token của người tặng. |
Content-Type | header | string | có | application/json. | Body là JSON. |
point_offer_id | body | string | có | 24 ký tự hex. Một integer Shukah hoặc một số là 400. | id của offer. |
point_occasion_id | body | string hoặc null | không | 24 ký tự hex. Key bị thiếu, null, "", "0" và 0 nghĩa là không có. Mọi giá trị không phải hex khác là 400. | id của occasion. |
occasion | body | string hoặc null | không | Đã trim, 1 đến 255 ký tự. "", khoảng trắng và null nghĩa là không có. Text tự do thắng: khi nó không rỗng, occasion id hoàn toàn không được tra cứu. | Text tự do, cho một occasion custom. |
Không có field người nhận. Câu văn là góc nhìn của người nhận, được dựng từ người gọi.
Response
200 với một key:
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
description | string | không | You have received a gift {occasion} of {points} points from {giver name} ({masked phone}). Xem các quy tắc bên dưới. | Text mà app hiển thị khi client soạn quà. |
{
"description": "You have received a gift Birthday of 100 points from Test User (*****0000)"
}
Capture trên dev, 2026-10-01 (client 356259), với offer đầu tiên và occasion Birthday; tên và các chữ số phone của
người tặng được thay bằng Test User và *****0000.
Các quy tắc của câu văn:
- Text luôn là tiếng Anh. Không có input ngôn ngữ.
occasionlà text tự do khi nó không rỗng sau khi trim. Nếu không, nó là nhãn tiếng Anh của occasion khipoint_occasion_idđược đưa ra. Nếu không nữa thì nó rỗng.- Khi không có occasion câu văn giữ một khoảng trắng đôi:
a gift of 400 points. Point service giữ nó có chủ đích, như Shukah làm, để app có thể đổi host mà không phải đổi một chuỗi nào. Đừng trim nó. pointslàpoint_amountcủa offer, số người nhận được, không phảipurchased_point_amount.- Tên người tặng là first name và last name của người gọi nối bằng một khoảng trắng, hoặc
Locate userkhi cả hai đều rỗng. - Phone đã che là
*****theo sau là bốn chữ số cuối của phone người gọi, hoặc chỉ*****khi không có chữ số nào.
{
"description": "You have received a gift of 400 points from Test User (*****0000)"
}
Từ tài liệu của point service locate-point-service@2c44ce3: docs/integrations/2026-07-16-client-facing-point-apis.md
(thay đổi lần cuối 2026-09-22, dòng 397-404, được kiểm tra ở đó với Shukah dev vào 2026-09-21), với
tên và chữ số đã được thay thế. Đây không phải capture Locate: không có lời gọi không-occasion nào được thực hiện vào 2026-10-01.
Câu văn chứa tên thật của người gọi và bốn chữ số cuối của phone. Hãy coi nó là dữ liệu cá nhân khi bạn log nó.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
400 | {"success":false,"error":{"code":"VALIDATION_ERROR","message":"Invalid description body","details":{"formErrors":[],"fieldErrors":{"point_offer_id":["Required"]}}}} | Body không qua được kiểm tra. Ví dụ là {}, từ một lần chạy local của schema của service, không capture live. |
400 | Cùng shape, message là point occasion <id> not found or inactive. | Không có text tự do, và occasion id không tồn tại hoặc inactive. Chưa capture. |
400 | Cùng shape, message là client <id> does not exist. | Người gọi không có hồ sơ. Chưa capture. |
404 | {"success":false,"error":{"code":"NOT_FOUND","message":"point offer <id> not found or inactive"}} | Offer không tồn tại, inactive hoặc đã bị xóa. 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. |
401 | Cù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. Chưa capture trên route này. Các route point khác đã trả Invalid or expired token cho một JWT Locate vào 2026-10-02. |
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>'
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\"}"
Kỳ vọng: 200 và description như ở mục Response, với tên và các chữ số phone của chính người gọi.
Không có token bạn nhận 401 như ở mục Lỗi. Hãy dùng token Shukah trên dev hiện nay: nó nhận 200 vào
2026-10-02, và một JWT Locate nhận 401 trên các route point khác.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | POST $SHUKAH_BASE_URL/maeda/point-order/get-description | POST $ORDER_BASE_URL/api/v2/client/point-orders/description | Đổi base URL và path. |
| Token | Token opaque của Shukah. | Một token Shukah (200 trên dev, 2026-10-02). Một JWT Locate chưa được thử trên route này; cùng bước kiểm tra đó cho nó 401 trên các route point khác. | App phải tiếp tục gửi token Shukah ở đây. |
| Id trong body | Integer dạng text, ví dụ "1". | Chuỗi 24 ký tự. Một integer Shukah là 400. | Gửi các id từ các danh sách Locate. |
| Câu trả lời trên dev | {"description":""} cho mọi body đã thử vào 2026-10-01 (id dạng text và dạng số, và với text tự do), và lại vào 2026-10-02. | Câu văn đầy đủ (capture trên dev, 2026-10-01). | Với Shukah ô lời nhắn hiện nay sẽ rỗng. Tài liệu của point service nói hai phía khớp nhau vào 2026-09-21; vào 2026-10-01 thì không. Vì sao Shukah giờ trả "": Chưa được tài liệu hóa. |
| Câu văn | Cùng cách diễn đạt, với khoảng trắng đôi khi không có occasion (theo tài liệu của point service, 2026-09-21). | Cùng cách diễn đạt và cùng khoảng trắng đôi. | Không có nếu câu trả lời không rỗng. |
| Body lỗi | Chưa được tài liệu hóa. | Envelope của point-service ở mục Lỗi. | Mọi chỗ so khớp theo text lỗi đều phải đổi. |
POST /api/v2/client/point-orders/:correlation_id/pay/card
POST /maeda/order/checkout-payment-standalone✅ Đã thay thế🚩 POINT_ORDER_CARD_ENABLED- 🔴 Id của order chuyển từ point_order_id trong body (chuỗi "1252") sang path dưới dạng correlation_id UUID.
- 🔴 App chỉ giữ pointOrderId; từ create của Locate nó là "undefined" nên lệnh gọi trả 404 "point order not found".
- 🟡 Body {id, cvv} giống nhau; point_order_id và payment_type bị bỏ qua (pay-point-order-card.ts:7-15).
- 🟡 cvv null thành chuỗi "null"; kết quả 3DS là body của payment-api, kiểu của app mong đợi 3ds.downgraded.
- 🟡 Cần POINT_ORDER_CARD_ENABLED (403 FORBIDDEN khi tắt); Shukah dev trả 422 "not configured properly".
Flag của point-service cho các route thanh toán (403 FORBIDDEN khi tắt). BẬT trên dev, đã probe 2026-10-01; production chưa xác nhận.
Chỉ cho point order: body giống nhau (id, cvv), nhưng order được chỉ định trong path bằng correlation_id, không phải point_order_id.
Route này thanh toán một order checkout bằng một thẻ mà client đã lưu. Chuyển khỏi
checkout-payment-standalone của Shukah là đổi URL và một order id mới, không phải body mới. Cùng route Shukah
cũng được dùng cho order nhà hàng; trang này chỉ nói về việc dùng nó cho quà tặng.
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <token>: một token Shukah trên dev hiện nay. Một JWT Locate nhận 401 ở cùng bước kiểm tra như các route point khác (2026-10-02): xem Which tokens work. Token thô được forward sang payment service. Payment service có chấp nhận JWT Locate hay không: Chưa được tài liệu hóa. | Token của người trả. Người nhận nhận 404. |
Content-Type | header | string | có | application/json. | Body là JSON. |
correlation_id | path | string | có | Đã trim. Chỉ có khoảng trắng cho 400 correlation_id is required. | correlation_id của order. |
id | body | string | có | Đã trim, ít nhất 1 ký tự. Không phải string là 400. | Id của thẻ đã lưu (src_…). card.tokenName của app. |
cvv | body | string | không | Một string hoặc số, đã trim, 1 đến 8 ký tự. "" được coi là thiếu. null thành text "null". Gateway cần nó cho thẻ MADA. Nó được forward, không được lưu. | Mã bảo mật của thẻ. |
point_order_id, payment_type, bất kỳ key nào khác | body | any | không | Được chấp nhận và bỏ qua. Order được chỉ định bởi path. | Giữ lại để body Shukah cũ vẫn chạy. |
Response
200 với body của payment service, không đổi. Nó chưa được capture live: lời gọi đi tới payment
gateway và tạo một khoản thanh toán. App bắt đầu 3-D Secure từ object này. Point service không đọc
gì từ nó, và order ở lại initial_order cho đến khi gateway báo một kết quả.
{
"id": "pay_2c6e…",
"status": "Pending",
"reference": "lp-<correlation_id>",
"3ds": {"enrolled": "Y", "is_redirect": true},
"_links": {"redirect": {"href": "https://…/sessions-interceptor/sid_…"}}
}
Từ tài liệu của point service locate-point-service@2c44ce3: docs/integrations/2026-07-16-client-facing-point-apis.md
(mục 3.6, thay đổi lần cuối 2026-09-22), với các giá trị dài được chính tài liệu rút ngắn. Chưa capture
live, vì lời gọi tạo một khoản thanh toán tại gateway và kế hoạch cấm việc đó.
Một response trong 902 trả về đã Declined mà không có _links.redirect. App phải xử lý trường hợp
đó.
Lỗi
Các kiểm tra chạy theo thứ tự này: flag, order của người trả này, loại thanh toán, loại order,
successful, payment_failed, hồ sơ của người trả, rồi số tiền.
| HTTP | Body | Khi nào |
|---|---|---|
400 | {"success":false,"error":{"code":"VALIDATION_ERROR","message":"Invalid pay-by-card body","details":{…}}} | Body không qua được kiểm tra, ví dụ không có id. Chưa capture. |
400 | Cùng shape, message là this point order is not paid by card. | payment_type của order không phải checkout. Chưa capture. |
400 | Cùng shape, message là this point order is not payable by card. | Order được copy từ Shukah, hoặc không có credit id đứng sau nó. Chưa capture. |
400 | Cùng shape, message là client <id> does not exist, hoặc point order <correlation_id> has a non-positive money_amount. | Người trả không có hồ sơ, hoặc số tiền là không hoặc âm. Chưa capture. |
403 | {"success":false,"error":{"code":"FORBIDDEN","message":"card payment for point orders is disabled"}} | POINT_ORDER_CARD_ENABLED tắt. Được kiểm tra đầu tiên, trước mọi lần đọc. Chưa capture, vì flag đang bật. |
404 | {"success":false,"error":{"code":"NOT_FOUND","message":"point order not found"}} | Không có order như vậy cho người gọi với tư cách người trả. Người nhận cũng nhận cái này. Chưa capture. |
409 | {"success":false,"error":{"code":"CONFLICT","message":"point order is already paid"}} | Order là successful. Chưa capture. |
409 | Cùng shape, message là point order payment failed; create a new order. | Order là payment_failed. Một lần bị từ chối là cuối cùng. Chưa capture. |
| Status của payment service | Body riêng của payment service, không đổi. Ví dụ 422 source_id_invalid cho một thẻ đã lưu không còn dùng được, hoặc 400 cho CVV MADA bị thiếu. | Payment service trả lời bằng một 4xx. Không có khoản thanh toán nào tồn tại và order vẫn thanh toán được. Chưa capture. |
500 | {"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}, hoặc message là PAYMENT_API_URL not set. | Không kết nối được payment service, nó không trả lời trong 10 giây, hoặc trả lời 5xx: không có gì được ghi lại. 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. |
401 | Cù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. Chưa capture trên route này. Các route point khác đã trả Invalid or expired token cho một JWT Locate vào 2026-10-02. |
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 20 giây. Từ order-v2. Khoản thanh toán có thể đã tồn tại ở gateway: hãy đọc order, đừng gọi lại. Chưa capture. |
Thử ngay
Lời gọi này đi tới payment gateway và có thể tính tiền một thẻ. Chỉ chạy nó trên dev, với một thẻ test và một order bạn tự tạo.
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\"}"
Kỳ vọng: 200 và object payment như ở mục Response. Order ở lại initial_order cho đến khi
gateway báo kết quả. Hãy dùng token Shukah trên dev hiện nay, như với mọi route point.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | POST $SHUKAH_BASE_URL/maeda/order/checkout-payment-standalone | POST $ORDER_BASE_URL/api/v2/client/point-orders/{correlation_id}/pay/card | Đổi base URL và path, và đặt correlation_id của order vào đó. |
| Body | {id, cvv, point_order_id}. | {id, cvv}. point_order_id và payment_type được chấp nhận và bỏ qua. | Không có: cùng tên. |
| Order id | point_order_id trong body, một chuỗi như "1252" (các body payment của app 2.31.5, capture trên dev, 2026-10-02). | Một UUID, correlation_id, trong path. | App chỉ giữ pointOrderId. Nó phải giữ id mới từ create. |
| Token | Token opaque của Shukah. | Chỉ token Shukah trên dev hiện nay: một JWT Locate nhận 401 ở cùng bước kiểm tra như các route point khác (2026-10-02). Token thô cũng được gửi tới payment service. | App phải tiếp tục gửi token Shukah ở đây. |
| Shukah trên dev | 422 {"name":"Unprocessable Content","message":"Checkout payment is not configured properly.",…} cho các khoản thanh toán point-order (2026-10-02). | Không gọi live. | Shukah dev hiện không nhận được thanh toán point, nên hai phía không thể so sánh live. |
| Flag | Không có. | POINT_ORDER_CARD_ENABLED: bật trên dev (probe 2026-10-01), production chưa xác nhận. | Nếu flag tắt, mọi lời gọi là 403. |
| Lỗi thanh toán | 4xx riêng của payment service. | Được chuyển tiếp không đổi với cùng status và body. | Không có: app đã xử lý shape đó. |
| Response | Type của app có 3ds.downgraded. | Sample của tài liệu có 3ds.is_redirect, và một khoản thanh toán bị từ chối có thể trả về mà không có _links.redirect, điều mà type của app yêu cầu. | Hãy kiểm tra bước bắt đầu 3-D Secure với một response thật. |
| Các khoản thanh toán quà khác | App cũng dùng checkout-payment, checkout-payment-stcpay, các lời gọi PayFort và một lời gọi CVV-token. | Không có route cho chúng. Chỉ có cái này và pay with session. | Xem "Chi tiết" cho các lời gọi chưa được thay thế. |
| Body lỗi | Chưa được tài liệu hóa. | Envelope của point-service ở mục Lỗi. | Mọi chỗ so khớp theo text lỗi đều phải đổi. |
POST /api/v2/client/point-orders/:correlation_id/pay/session
POST /maeda/order/checkout-payment-flow✅ Đã thay thế🚩 POINT_ORDER_CARD_ENABLED- 🔴 Id của order chuyển từ point_order_id trong body (chuỗi "1253") sang path dưới dạng correlation_id UUID.
- 🔴 App chỉ giữ pointOrderId; từ create của Locate nó là "undefined" nên lệnh gọi trả 404.
- 🟡 payment_type "applePay" và point_order_id bị bỏ qua; session của Checkout tự đưa ra các ví.
- 🟡 Body là tùy chọn ({rememberMe}); chuỗi "true" hoặc "false" được ép kiểu, số thì trả 400.
- 🟡 Cần POINT_ORDER_CARD_ENABLED; Shukah dev trả 422 "Checkout payment is not configured properly."
Flag của point-service cho các route thanh toán (403 FORBIDDEN khi tắt). BẬT trên dev, đã probe 2026-10-01; production chưa xác nhận.
Chỉ cho point order: body giống nhau (rememberMe); point_order_id và payment_type bị bỏ qua và order được chỉ định bằng correlation_id trong path.
Route này bắt đầu một payment session cho một order checkout. Checkout SDK dùng nó cho thẻ mới,
Apple Pay, Google Pay và STC Pay. Bản thân nó không tính tiền gì cả. Cùng route Shukah cũng được dùng cho
order nhà hàng; trang này chỉ nói về việc dùng nó cho quà tặng.
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <token>: một token Shukah trên dev hiện nay. Một JWT Locate nhận 401 ở cùng bước kiểm tra như các route point khác (2026-10-02): xem Which tokens work. | Token của người trả. Người nhận nhận 404. |
Content-Type | header | string | không | application/json. Một body rỗng hoặc thiếu là hợp lệ và nghĩa là {}. | Body là JSON. |
correlation_id | path | string | có | Đã trim. Chỉ có khoảng trắng cho 400 correlation_id is required. | correlation_id của order. |
rememberMe | body | boolean | không | true, false, hoặc text "true" hay "false". Mặc định false. Một số, null hoặc text khác là 400. | Lưu thẻ, gắn với người trả, sau một khoản thanh toán thành công. |
point_order_id, payment_type, bất kỳ key nào khác | body | any | không | Được chấp nhận và bỏ qua. | Giữ lại để body Shukah cũ vẫn chạy. |
Response
200 với session của payment service, không đổi. Nó chưa được capture live: lời gọi đi tới payment
gateway. Checkout SDK bắt đầu từ object này. Một session có thể bị bỏ và tạo lại
trên cùng order; đã thấy chín session trên một reference, và không cái nào là lỗi.
{
"id": "ps_3J5G…",
"payment_session_secret": "<redacted-secret>",
"payment_session_token": "<redacted-token>",
"_links": {"self": {"href": "…"}}
}
Từ tài liệu của point service locate-point-service@2c44ce3: docs/plans/2026-09-21-point-order-manual-test-guide.md
(dòng 336-343, thay đổi lần cuối 2026-09-22), với secret và token đã được thay thế. Chưa capture live,
vì lời gọi mở một payment session tại gateway và kế hoạch cấm việc đó.
Lỗi
Cùng các guard như Pay with card, theo cùng thứ tự. Các dòng được liệt kê đầy đủ.
| HTTP | Body | Khi nào |
|---|---|---|
400 | {"success":false,"error":{"code":"VALIDATION_ERROR","message":"Invalid pay-by-session body","details":{…}}} | rememberMe không phải giá trị giống boolean. Chưa capture. |
400 | Cùng shape, message là this point order is not paid by card hoặc this point order is not payable by card. | Order không phải là một order checkout mà Locate đã tạo. Chưa capture. |
400 | Cùng shape, message là client <id> does not exist, hoặc point order <correlation_id> has a non-positive money_amount. | Người trả không có hồ sơ, hoặc số tiền là không hoặc âm. Chưa capture. |
403 | {"success":false,"error":{"code":"FORBIDDEN","message":"card payment for point orders is disabled"}} | POINT_ORDER_CARD_ENABLED tắt. Được kiểm tra đầu tiên, trước mọi lần đọc. Chưa capture, vì flag đang bật. |
404 | {"success":false,"error":{"code":"NOT_FOUND","message":"point order not found"}} | Không có order như vậy cho người gọi với tư cách người trả. Đã capture trên dev, 2026-10-01, với một order id ngẫu nhiên (đây là cách flag được probe). |
409 | {"success":false,"error":{"code":"CONFLICT","message":"point order is already paid"}}, hoặc message là point order payment failed; create a new order. | Order là successful, hoặc là payment_failed. Chưa capture. |
| Status của payment service | Body riêng của payment service, không đổi. | Payment service trả lời bằng một 4xx. Chưa capture. |
500 | {"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}, hoặc message là PAYMENT_API_URL not set. | Không kết nối được payment service, nó không trả lời trong 10 giây, hoặc trả lời 5xx. 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. |
401 | Cù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. Chưa capture trên route này. Các route point khác đã trả Invalid or expired token cho một JWT Locate vào 2026-10-02. |
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 20 giây. Từ order-v2. Một 504 ở đây an toàn để thử lại: một lời gọi lặp lại chỉ tạo thêm một session. Chưa capture. |
Thử ngay
Lời gọi này mở một payment session tại gateway. Chỉ chạy nó trên dev, với một order bạn tự tạo.
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}'
Kỳ vọng: 200 và object session như ở mục Response. Một correlation_id ngẫu nhiên cho 404
như ở mục Lỗi (khi flag bật). Hãy dùng token Shukah trên dev hiện nay, như với mọi route point.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | POST $SHUKAH_BASE_URL/maeda/order/checkout-payment-flow | POST $ORDER_BASE_URL/api/v2/client/point-orders/{correlation_id}/pay/session | Đổi base URL và path, và đặt correlation_id của order vào đó. |
| Body | {point_order_id, payment_type, rememberMe}. | {rememberMe}. point_order_id và payment_type được chấp nhận và bỏ qua. | Không có: cùng tên. |
payment_type | App 2.31.5 đã gửi applePay (capture trên dev, 2026-10-02). Type của snapshot cũng cho phép stcpay và card, nhưng app 2.31.5 gửi STC Pay tới route checkout-payment-stcpay riêng của Shukah. | Bị bỏ qua. Checkout session cung cấp các wallet. | Wallet không được chọn ở server. Hãy kiểm tra SDK hiển thị đúng wallet. STC Pay không có route Locate riêng. |
| Order id | Một chuỗi trong body: {"point_order_id":"1253",…} trong capture app 2.31.5 (2026-10-02). | Một UUID, trong path. | App phải giữ correlation_id từ create. |
| Shukah trên dev | App 2.31.5 đã gọi nó trên dev vào 2026-10-02 và nhận 422 Checkout payment is not configured properly. | Không gọi live. | Shukah dev hiện không nhận được thanh toán point, nên hai phía không thể so sánh live. |
| Flag | Không có. | POINT_ORDER_CARD_ENABLED: bật trên dev (probe 2026-10-01), production chưa xác nhận. | Nếu flag tắt, mọi lời gọi là 403. |
| Response | Chưa được tài liệu hóa (chưa capture). | Session của payment service, không đổi. | Type của app có id, payment_session_secret, payment_session_token và _links.self, cùng các key như sample của tài liệu. |
| Body lỗi | Chưa được tài liệu hóa. | 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 ý
- Order có các key mới (
correlation_id,money_amount) và các route pay nhậncorrelation_idtrong path, nên app phải giữ nó từ create. - Create không có idempotency key: sau một
504app không bao giờ biết order id, và một lần thử lại có thể tạo order thứ hai hoặc, vớipoint, trừ tiền hai lần. - Trả bằng điểm và cả hai route pay cần hai flag. Cả hai đều bật trên dev vào 2026-10-01; production chưa xác nhận, và flag tắt cho
403.
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 forward | Giới hạn |
|---|---|---|---|
POST /api/v2/client/point-orders | POST /api/client/point-orders | Authorization và body như đã nhận | 10 s |
GET /api/v2/client/point-orders/:correlation_id | GET /api/client/point-orders/<id>, với id đi qua encodeURIComponent | Authorization và toàn bộ query (upstream bỏ qua) | 10 s |
POST /api/v2/client/point-orders/description | POST /api/client/point-orders/description | Authorization và body | 10 s |
POST /api/v2/client/point-orders/:correlation_id/pay/card | POST /api/client/point-orders/<id>/pay/card | Authorization và body | 20 s |
POST /api/v2/client/point-orders/:correlation_id/pay/session | POST /api/client/point-orders/<id>/pay/session | Authorization và body | 20 s |
(OV2: src/modules/client/point/point.controller.ts:10,110-201,
PS: services/api/src/routes/client.routes.ts:23-29.) Route literal POST point-orders/description
được mount trước GET point-orders/:correlation_id, nên một GET …/point-orders/description lạc đường
được đọc là order id description và trả 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 giữ mặc định
10 giây, vì nó chỉ soạn text (OV2: src/modules/client/point/point.controller.ts:126-131).
Một POST giữ status của upstream: một 200 vẫn là 200, không phải 201 mặc định của Nest (thấy trong một
lần tái hiện local của pattern proxy; OV2: src/modules/client/point/point.service.ts:64). Các route nội bộ của point service, kể cả
webhook và …/refresh, không bao giờ được proxy
(OV2: docs/plans/2026-09-16-point-order-proxy-plan.md:21,
OV2: docs/locate-point-client-api-reference.md:46).
Create
- Handler kiểm tra body bằng một schema, rồi
createPointOrderchạy các kiểm tra theo thứ tự nêu ở mục Lỗi (PS: services/api/src/handlers/create-point-order.ts:10-47,PS: packages/core/src/points/create-point-order.ts:66-186). Cả hai id là chuỗi 24-hex và không bao giờ được chuyển đổi (PS: services/api/src/lib/object-id.ts:12-14). Người nhận phải tồn tại trong collectionclientsdùng chung (PS: packages/core/src/points/client-lookup.ts:20-35). checkout: một lần insert order và một lần tăng counter choorder_no. Không có dòng ledger và không có outbox row (PS: packages/core/src/points/create-point-order.ts:162-172). Order vẫn chưa thanh toán và được để yên nếu bị bỏ (PS: docs/plans/2026-09-10-point-order-write-path-design.md:692).point: một transaction ghi expense của người trả, earn của người nhận, order (successful,payment_statusnull) và hai outbox row gửi tới Shukah (mirror_reduce_point,mirror_add_point). Expense chạy trước, nên overdraft guard chính là kiểm tra số dư và không có gì bị bỏ lại khi lỗi (PS: packages/core/src/points/create-point-order.ts:193-258,PS: packages/core/src/points/apply-point-txn.ts:71-86). Hiện nay không có gì drain outbox trên k8s, nên Shukah không thấy các chân này cho đến khi có một drainer (PS: CLAUDE.md:202-205,PS: .env.example:95-97,PS: docs/plans/2026-09-10-point-order-write-path-design.md:811). Các lời gọi thật ngày 2026-10-02 khớp với điều này: một orderpointtrên Locate không làm đổi số dư Shukah, và một order trên Shukah không làm đổi số dư Locate, trong vài giây của lần chạy. Bên nào có bắt kịp về sau hay không thì chưa được kiểm tra.- Markup. Trên dev người trả trả 110 cho 100, rồi 240, 360, 480 và 600. Ví dụ "10 %
markup" của design doc là offer đầu tiên, và các offer khác mang 20 %
(
PS: docs/plans/2026-08-03-point-order-flow-design.md:31). - Order lưu gì. Một
offer_snapshotcủa các số tiền, nhãn occasion từ dòng occasion, mô tả tiếng Anh phía người gửi, và ba id cố định (expense_correlation_id,earn_correlation_id,transfer_id) (PS: packages/core/src/points/create-point-order.ts:119-160).order_nobắt đầu từ 100000 từ một counter, vàcorrelation_idlà một UUID v4 (PS: packages/core/src/points/order-no.ts:12,20-28,PS: packages/core/src/points/correlation-id.ts:8-10). - Text tự do.
occasionđi vào mô tả đã lưu và, vớipoint, vào cả hai chân ledger. Nó không được lưu trên order như một field. Với một ordercheckoutđược gateway hook credit sau đó, text của người nhận được dựng từ nhãn tiếng Anh của dòng occasion, không phải từ text tự do của client. Điểm cuối cùng đó được đọc từ code và không tài liệu nào nêu (PS: packages/core/src/points/create-point-order.ts:119-125,190,PS: packages/core/src/points/settle-point-order.ts:231). order_typelàgiftkhi tìm thấy một dòng occasion (theo integer Shukah hoặc id của chính nó) vàtransfernếu không (PS: packages/core/src/points/derive-order-type.ts:16-34). Sample trong tài liệu của point service hiển thịgiftcho một body có occasion. Lời gọicheckoutlive không có occasion, nên nó làtransfer.- Không có idempotency key. Mỗi lời gọi cấp phát một
correlation_idvàorder_nomới (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).
Settlement và gateway hook
Các status là giá trị mt_point_order của Shukah, giữ nguyên
(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). Các order do Locate tạo chỉ đạt tới
initial_order, successful và payment_failed
(PS: packages/core/src/points/settle-point-order.ts:183-185). Gateway báo kết quả tới
POST /internal/point-orders/webhooks/checkout, không phải route của app và cần một service key
(PS: services/api/src/handlers/checkout-webhook.ts:9-41).
- Một khoản thanh toán captured có số tiền và currency khớp sẽ credit người nhận một chân earn và ghi một
outbox row, và set
successfulvàCAPTURED. Expense của người trả không được ghi (PS: packages/core/src/points/settle-point-order.ts:117-119,195-328). - Một khoản thanh toán bị từ chối trên một
initial_ordersetpayment_failedvàFAILED(PS: packages/core/src/points/settle-point-order.ts:33-42,125-145). - Một lần capture trên order
payment_failedvẫn là failed, và một yêu cầu hoàn tiền được gửi (PS: packages/core/src/points/settle-point-order.ts:121-123,331-392). - Số tiền hoặc currency không khớp để order ở
initial_order, đánh dấu nó halted và nâng một cảnh báo. Nó không bao giờ được settle tự động (PS: packages/core/src/points/settle-point-order.ts:201-224). - Settlement an toàn để lặp lại: credit được claim bằng cách lật order ra khỏi
initial_order(PS: packages/core/src/points/settle-point-order.ts:50-55).
Hai route pay
- Cả hai bắt đầu bằng
loadPayableOrder: flag, rồi một lần tra order bao gồm id của người trả (nên một order không tồn tại và order của người khác cho cùng404), rồi loại thanh toán, kiểm tra merchant,successful,payment_failed, hồ sơ của người trả và số tiền (PS: packages/core/src/points/pay-point-order.ts:42-102). - Payment service là repo
payment-api; nó chưa được đọc cho trang này. - Card. Lời gọi outbound là
POST {PAYMENT_API_URL}/client/api/v1/checkout/lp-<correlation_id>/payments/standalone. Session. Nó làPOST {PAYMENT_API_URL}/client/api/v1/checkout/payment/lp-<correlation_id>/session. Cả hai gửi các headertoken(token thô của client),x-from-application: LOCATE_CLIENTvàx-transaction-type: POINT. Body cóamounttheo SAR,customerId(người trả),customerName,customerPhone, vàidcùngcvv(card) hoặcrememberMe(session). Người trả là customer, không bao giờ là người nhận. Reference của gateway là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). - Lần ghi duy nhất là một ghi chú debug (
payment_providervàpayment_reference), ghi theo kiểu best effort. Không có ghi ledger và không có đổi status nào xảy ra ở đây (PS: packages/core/src/points/pay-point-order.ts:104-130). cvvđược forward và không bao giờ được point service lưu hay log (PS: services/api/src/middleware/request-logger.ts:5). order-v2 log request body mà không che, kể cả cái này (xem mục proxy).- Lỗi từ payment service. Một
4xxđược chuyển tiếp với status và body riêng của nó. Một lỗi transport, một timeout hoặc một5xxkhông được catch và thànhINTERNAL_ERROR500, với không có gì được ghi lại (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). - Chuỗi timeout: app 20 s, order-v2 20 s, payment service mặc định 10 s
(
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). - Một lần pay with card lặp lại yêu cầu payment service một khoản thanh toán mới trên cùng reference. Gateway
không làm cho reference là duy nhất, nên một lần capture thứ hai chỉ được cảnh báo
(
PS: packages/core/src/points/settle-point-order.ts:169-181,PS: docs/plans/2026-09-21-point-order-manual-test-guide.md:446). - Payment service có chấp nhận một JWT Locate trong header
tokenhay không: Chưa được tài liệu hóa.
Các flag
POINT_ORDER_POINT_ENABLED được đọc là process.env.POINT_ORDER_POINT_ENABLED !== 'true' và
POINT_ORDER_CARD_ENABLED cùng cách đó, nên chỉ đúng text true mới bật chúng
(PS: packages/core/src/points/create-point-order.ts:69-72,
PS: packages/core/src/points/pay-point-order.ts:45-47). Các giá trị flag được bake vào image: lần
build copy /data/compose/point-api/.env vào đó (PS: Jenkinsfile-k8s:25-28,115-122,
PS: Dockerfile:22-23). Lần probe ngày 2026-10-01 dùng hai lời gọi không ghi gì: một body point với
một offer id toàn số 0 và người gọi là người nhận (kiểm tra flag và kiểm tra tự thanh toán đều chạy
trước mọi lookup), và một lời gọi pay/session trên một UUID ngẫu nhiên (kiểm tra flag chạy trước lần tra
order, và một body rỗng là hợp lệ) (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). Các khẳng định của tài liệu về trạng thái của
các flag nằm ở 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 và
PS: docs/integrations/2026-09-25-payment-api-hook-forwarding.md:199,748-753.
Description
Route soạn text và không ghi gì. Nó tra offer (status: 'active',
deleted_at: null), rồi occasion chỉ khi không có text tự do, rồi hồ sơ của người gọi
(PS: services/api/src/services/point-settings.service.ts:44-79). Cách diễn đạt là
composeGiftDescription, giữ khoảng trắng đôi của Shukah có chủ đích
(PS: services/api/src/services/point-settings.mappers.ts:60-78). Các text ledger và order được
dựng khi một order được tạo gộp khoảng trống đó thành một khoảng trắng, và không mang khoảng trắng đôi:
đó là một họ câu văn khác
(PS: packages/core/src/points/order-reason.ts:10-28). Tên và phần che phone theo
displayName và maskPhone (PS: packages/core/src/points/order-reason.ts:10-18). Tài liệu của point
service ghi nhận rằng cả hai body, có và không có occasion, khớp với Shukah vào 2026-09-21
(PS: docs/integrations/2026-07-16-client-facing-point-apis.md:393-404). Hướng dẫn manual-test
2026-09-21 nói route proxy cho nó chưa được merge; nó đã được merge tại d9e4e5f6
(PS: docs/plans/2026-09-21-point-order-manual-test-guide.md:444,
OV2: src/modules/client/point/point.controller.ts:132-146).
Các lời gọi của app không có route Locate
- Danh sách quà đã gửi. Tab "Sent gifts" của app đọc
GET /maeda/point-order(vớipagevàper-page, và các headerx-pagination-*). Cả point service lẫn order-v2 đều không có route list cho point order, và migration registry đánh dấu nó chưa thay thế. Gift checkout mở tab đó sau khi thanh toán, nên một order do Locate tạo sẽ không hiển thị ở đó (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). - Các lời gọi thanh toán khác.
order/checkout-payment(Frames token),order/checkout-payment-stcpay, các đường PayFort và Hyperpay, và lời gọicvv/tokentới payment service với order id Shukah không có đối tác Locate. Locate cung cấp một route thẻ đã lưu và một route session; STC Pay là một wallet bên trong session. Lời gọicvv/tokenkhông có tác dụng với một order Locate. App nên dùng các lời gọi Locate nào thay thế: Chưa được tài liệu hóa (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). - Một order đơn lẻ. App có
getPointGiftOrderByIdchoGET /maeda/point-order/{id}, nhưng không màn hình nào gọi nó (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).
App
createPointGiftOrder tải lại common settings và gửi payment_type point cho thanh toán bằng
điểm, hoặc giá trị payment từ các settings đó cho thanh toán online
(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). Order DTO
biến response thành order của app: nó dựng pointOrderId là String(point_order_id), đọc
amount, client_id, to_client_id và payment_point_order_id, và ánh xạ status qua một bảng
bốn từ của Shukah
(mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:23-31,138-183). Mọi
lời gọi pay đều gửi order.pointOrderId
(mobile/locate-client@e960c3a: src/widgets/PointGiftsCheckout/model/hooks/usePointGiftsCheckout.ts:51,80-120).
Checkout tra người nhận theo phone và truyền userId làm to_client_id, rồi tạo
order và thanh toán; handler thành công tải lại lịch sử và thay màn hình bằng tab Sent gifts
(mobile/locate-client@e960c3a: src/widgets/PointGiftsCheckout/model/hooks/usePointGiftsCheckout.ts:221-232,370-401).
Khi tài liệu và code mâu thuẫn (code thắng)
- Các tài liệu 2026-09-21, 09-22 và 09-25 nói cả hai flag chưa được đặt ở mọi môi trường. Lần probe
2026-10-01 cho thấy cả hai bật trên dev. Tài liệu 2026-09-25 nói không ai đọc flag trên một host đang chạy
(
PS: docs/integrations/2026-09-25-payment-api-hook-forwarding.md:748-753). - Hướng dẫn manual-test 2026-09-21 nói route proxy description chưa được merge và order-v2 trên dev
gọi
localhost:4010. Cả hai đều đã lỗi thời (PS: docs/plans/2026-09-21-point-order-manual-test-guide.md:24-32,444). - Tài liệu tham khảo của order-v2 nói một auth service không kết nối được cho
500. Point service trả401(OV2: docs/locate-point-client-api-reference.md:201,PS: services/api/src/services/client-auth.service.ts:26-28). - Tài liệu integration gọi create là "new capability, not a Shukah read-parity endpoint", và cũng nói một
POST được thử lại khớp với "Shukah's own order-creation endpoint"
(
PS: docs/integrations/2026-07-16-client-facing-point-apis.md:178-182,237-238). - 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. Chúng dùng chung
bước kiểm tra token của point service, vốn nhận
401cho một JWT Locate trên các route point khác vào 2026-10-02 (PS: services/api/src/services/client-auth.service.ts:22-29). Các lời gọi Thử ngay giờ dùng một token Shukah. - 2026-10-02: trang này nói order của Shukah mang
occasion_id, như app khai báo type. Các dòng order của Shukah cópoint_occasion_id(danh sách quà đã gửi, capture trên dev, 2026-10-02), nênoccasionIdcủa app luôn làundefined(mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:138-183). - 2026-10-02: trang này nói các lời gọi pay gửi
point_order_iddưới dạng integer. App 2.31.5 gửi một chuỗi ("1252","1253"; capture proxy trên dev, 2026-10-02). Locate bỏ qua nó dù thế nào (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: trang này dựng phía Shukah của create từ các type của app và tài liệu của point service,
vì create chưa từng được gọi. Cả hai bên được gọi trên dev ngày 2026-10-02: Shukah trả
201với mười hai key, Locate trả200với chín key, và một orderpointở bên nào cũng chỉ làm giảm số dư của bên đó (capturepoint-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- Tài liệu của
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- Tài liệu của
locate-point-service@2c44ce3:docs/integrations/2026-07-16-client-facing-point-apis.md:178-182,237-238,263-299,393-404(thay đổi lần cuối 2026-09-22),docs/plans/2026-09-21-point-order-manual-test-guide.md:24-32,336-343,444,446(thay đổi lần cuối 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 - Snapshot mobile
mobile/locate-client@e960c3a(branchdevelopment, 2026-02-26; code của chính app, không phải 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 - Capture 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; các request body của app 2.31.5 chocheckout-payment-flowvàcheckout-payment-stcpay(do proxy của người dùng capture). Các route pay không được gọi vào 2026-10-02. - Capture dev, 2026-10-02 (lời gọi bổ sung, được người dùng chấp thuận):
point-order-create-card--shukah,point-order-create-card--locate-st,point-order-create-point--shukah,point-order-create-point--locate-st, và cả hai số dư trước và sau. - Capture 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