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

Đơn hàng

Tổng quan​

Method + pathScopeDùng để
GET /ordersorders_listLiệt kê đơn để đồng bộ (keyset), hoặc tra một đơn theo orderCode
GET /orders/:idorders_detailMột đơn kèm dòng hàng và địa chỉ giao
GET /order-statusesorders_listDanh mục trạng thái đơn của cửa hàng
PUT /ordersorders_create + orders_updateTạ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ầnLấy ở
Khách của đơnGET /customers, hoặc tạo trước bằng POST /customers
variantId / sku của dòng hàngGET /products
shippingProvinceCode / shippingWardCodeGET /address/provinces, GET /address/wards
statusIdGET /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. excludeSource bỏ các đơn được tạo từ nguồn đó.
  • ?orderCode= trả { "data": đơn | null }.
  • GET /orders/:id trả { "data": đơn }, hoặc 404 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
{
"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 }]
}
]
}

Nhận diện đơn bằng orderCode​

GửiKế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 orderCodeLuô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.

Nên luôn gửi orderCode

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ườngKiểuGhi chú
orderCodechuỗi ≤ 50Mã đơn, duy nhất trong cửa hàng
customerobjectBắ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
itemsmảng ≤ 200Bắ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
statusIduuidChuyển sang trạng thái này (đã đúng trạng thái thì bỏ qua)
shippingFeesố ≥ 0
shippingName, shippingPhone, shippingAddresschuỗishippingAddress là số nhà, tên đường
shippingProvinceCodechuỗiMã tỉnh / thành — xem danh mục
shippingWardCodechuỗiMã 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ườngKiểuGhi chú
variantId hoặc skuuuid / chuỗiPhả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
quantitysố nguyên ≥ 1
unitPricesố ≥ 0
discountAmountsố ≥ 0Giảm giá trên dòng
vatRatesố ≥ 0% VAT
productNamechuỗiMặc định lấy tên sản phẩm trong danh mục
noteschuỗi ≤ 1000Ghi 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 đã kết thúc

Đơ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).