Đơn hàng
Tổng quan
| Method + path | Scope | Dùng để |
|---|---|---|
GET /orders | orders_list | Liệt kê đơn để đồng bộ (keyset), hoặc tra một đơn theo orderCode |
GET /orders/:id | orders_detail | Một đơn kèm dòng hàng và địa chỉ giao |
GET /order-statuses | orders_list | Danh mục trạng thái đơn của cửa hàng |
PUT /orders | orders_create + orders_update | Tạo hoặc cập nhật tối đa 50 đơn mỗi lần |
Chuẩn bị dữ liệu để tạo đơn
| Cần | Lấy ở |
|---|---|
| Khách của đơn | GET /customers, hoặc tạo trước bằng POST /customers |
variantId / sku của dòng hàng | GET /products |
shippingProvinceCode / shippingWardCode | GET /address/provinces, GET /address/wards |
statusId | GET /order-statuses |
Đơn tạo qua Open API không đi qua bước duyệt và không bắn thông báo nội bộ như đơn tạo trên giao diện; nguồn đơn ghi là api:<tên key>.
Đối tượng đơn hàng
{
"id": "b7c2…",
"orderCode": "DH-1001",
"customerId": "1f0c6a7e-5d1b-4a8e-9a63-2c1f0f6b9d10",
"statusId": "5a21…",
"paymentStatus": "unpaid",
"subtotal": 380000,
"taxAmount": 6400,
"shippingFee": 30000,
"discountAmount": 0,
"totalAmount": 416400,
"paidAmount": 0,
"debtAmount": 416400,
"currency": "VND",
"shipping": {
"name": "Nguyễn Thị Lan",
"phone": "0901234567",
"address": "12 Lý Thường Kiệt",
"provinceCode": "01",
"province": "Thành phố Hà Nội",
"wardCode": "00070",
"ward": "Phường Hoàn Kiếm"
},
"assignedStaffId": "7a2d…",
"source": "api:ERP Kho",
"items": [
{
"id": "e1f0…", "productId": "c3a9…", "variantId": "3e8b…",
"productName": "Áo thun basic", "variantName": "Trắng / M", "sku": "AO-THUN-M",
"quantity": 2, "unitPrice": 150000, "discountAmount": 0, "vatRate": 0,
"taxAmount": 0, "totalPrice": 300000, "notes": null
}
],
"createdAt": "2026-10-08T03:00:00.000Z",
"updatedAt": "2026-10-08T03:12:45.123Z"
}
source là nguồn tạo đơn (ui hoặc api:<tên key>). Tên tỉnh / phường do hệ thống tra từ mã, không lấy từ dữ liệu gửi lên.
Đọc đơn
GET /api/open/v1/orders?updatedSince=2026-10-01T00:00:00Z&updatedUntil=2026-10-08T00:00:00Z&limit=50&excludeSource=api:ERP%20Kho
GET /api/open/v1/orders?orderCode=DH-1001
GET /api/open/v1/orders/b7c2…
- Liệt kê theo keyset, trả
{ "data": [đơn…], "nextCursor": …, "window": {…} }— tối đa 50 đơn / lần, cửa sổ tối đa 30 ngày.excludeSourcebỏ các đơn được tạo từ nguồn đó. ?orderCode=trả{ "data": đơn | null }.GET /orders/:idtrả{ "data": đơn }, hoặc404 OPEN_API_ORDER_NOT_FOUND.
Key chỉ thấy những đơn mà người tạo key được thấy (theo phạm vi phân quyền đơn hàng).
Danh mục trạng thái
GET /api/open/v1/order-statuses
{
"data": [
{ "id": "0d6f…", "label": "Mới", "isInitial": true, "isCompleted": false, "isCancelled": false },
{ "id": "5a21…", "label": "Đang giao", "isInitial": false, "isCompleted": false, "isCancelled": false },
{ "id": "9c40…", "label": "Hoàn thành", "isInitial": false, "isCompleted": true, "isCancelled": false },
{ "id": "e117…", "label": "Đã huỷ", "isInitial": false, "isCompleted": false, "isCancelled": true }
]
}
Gửi statusId lấy từ danh mục này. Đơn mới luôn bắt đầu ở trạng thái isInitial.
Tạo hoặc cập nhật đơn
PUT /api/open/v1/orders
- Request Body
- Response
{
"orders": [
{
"orderCode": "DH-1001",
"customer": { "matchField": "customerCode", "value": "KH-001" },
"items": [
{ "sku": "AO-THUN-M", "quantity": 2, "unitPrice": 150000 },
{ "variantId": "3e8b…", "quantity": 1, "unitPrice": 90000, "discountAmount": 10000, "vatRate": 8 }
],
"shippingFee": 30000,
"shippingName": "Nguyễn Thị Lan",
"shippingPhone": "0901234567",
"shippingAddress": "12 Lý Thường Kiệt",
"shippingProvinceCode": "01",
"shippingWardCode": "00070",
"statusId": "5a21…"
},
{
"customer": { "id": "1f0c6a7e-5d1b-4a8e-9a63-2c1f0f6b9d10" },
"items": [{ "sku": "AO-THUN-L", "quantity": 1, "unitPrice": 150000 }]
}
]
}
{
"results": [
{ "index": 0, "status": "updated", "id": "b7c2…", "orderCode": "DH-1001" },
{ "index": 1, "status": "created", "id": "c90a…", "orderCode": "ORD-20261008-K3P9QZ" }
]
}
Nhận diện đơn bằng orderCode
| Gửi | Kết quả |
|---|---|
orderCode trùng một đơn đang có | Cập nhật đơn đó |
orderCode chưa có | Tạo đơn mới với đúng mã này |
Không gửi orderCode | Luôn tạo mới, mã tự sinh ORD-YYYYMMDD-XXXXXX |
Mỗi dòng kết quả trả orderCode — lưu lại để lần sau cập nhật đúng đơn.
Không gửi orderCode thì gửi lại (retry) sẽ tạo đơn trùng. Đặt orderCode = mã đơn bên hệ thống của bạn để mọi lần gửi đều idempotent. Mã đã dùng cho một đơn — kể cả đơn đã xoá — không dùng lại được (ORDER_CODE_DUPLICATE).
Trường của một đơn
| Trường | Kiểu | Ghi chú |
|---|---|---|
orderCode | chuỗi ≤ 50 | Mã đơn, duy nhất trong cửa hàng |
customer | object | Bắt buộc khi tạo: { "id": "<id khách>" } hoặc { "matchField": "phone" | "email" | "customerCode" | "<mã trường duy nhất>", "value": "…" }. Bỏ qua khi cập nhật |
items | mảng ≤ 200 | Bắt buộc khi tạo. Khi cập nhật: gửi ⇒ thay toàn bộ dòng hàng; không gửi ⇒ giữ nguyên |
statusId | uuid | Chuyển sang trạng thái này (đã đúng trạng thái thì bỏ qua) |
shippingFee | số ≥ 0 | |
shippingName, shippingPhone, shippingAddress | chuỗi | shippingAddress là số nhà, tên đường |
shippingProvinceCode | chuỗi | Mã tỉnh / thành — xem danh mục |
shippingWardCode | chuỗi | Mã phường / xã, phải thuộc tỉnh ở trên. Khi cập nhật: gửi một trong hai mã là thay cả cặp (mã vắng ⇒ xoá) |
Một dòng hàng:
| Trường | Kiểu | Ghi chú |
|---|---|---|
variantId hoặc sku | uuid / chuỗi | Phải có trong danh mục sản phẩm của cửa hàng, nếu không ⇒ OPEN_API_ITEM_NOT_FOUND |
quantity | số nguyên ≥ 1 | |
unitPrice | số ≥ 0 | |
discountAmount | số ≥ 0 | Giảm giá trên dòng |
vatRate | số ≥ 0 | % VAT |
productName | chuỗi | Mặc định lấy tên sản phẩm trong danh mục |
notes | chuỗi ≤ 1000 | Ghi chú của dòng hàng |
Tổng tiền, thuế và công nợ do hệ thống tính lại từ dòng hàng và phí giao hàng.
Đơn ở trạng thái hoàn thành hoặc đã huỷ không cập nhật được nữa — dòng đó trả status: "error". Chuyển trạng thái cũng tuân theo quy tắc của cửa hàng (trạng thái kết thúc bị khoá, bước cần duyệt).