API Nhà cung cấp: Nhận đơn hàng thủ công mà không cần cạnh tranh
Phiên bản đã xuất bản của hướng dẫn này bảo bạn gọi GET /orders. Endpoint đó trả cùng một đơn hàng cho mọi poller bạn chạy. Có một endpoint nhận đơn hàng một cách nguyên tử, một lease bạn phải tự lấy, và hai giới hạn tốc độ không ai ghi chép. Đây là vòng lặp thực sự hoạt động ổn định dưới tải.
Sarah JohnsonNếu bạn xử lý đơn hàng thủ công qua API, quyết định đầu tiên là chọn endpoint nào để nhận việc, và đây cũng là quyết định mà hầu hết tích hợp đều làm sai. GET /orders chỉ là thao tác đọc. Nó trả về các đơn hàng khớp cho bất kỳ ai gọi và không thay đổi gì cả. Chạy hai worker, hoặc một worker có retry, cả hai sẽ nhận cùng một đơn hàng và cả hai sẽ cố giao hàng.
Endpoint thực sự nhận việc là POST /orders-pull. Nó chọn các đơn hàng thủ công đang chờ của bạn, chuyển từng đơn từ trạng thái pending sang processing bằng cơ chế compare-and-set, và chỉ trả về những đơn mà việc chuyển trạng thái thực sự thành công. Nếu một worker khác gọi đến chỉ một mili giây sau đó, phép so sánh sẽ thất bại và đơn hàng đó không xuất hiện trong phản hồi của nó. Ghi chú nguồn của endpoint này mô tả nó tương thích PerfectPanel, đây là định dạng mà hầu hết các tích hợp panel đã quen dùng.
Vòng lặp, theo đúng thứ tự nên chạy
- Nhận việc.
POST /orders-pull. Body tùy chọnlimit, mặc định 100, giới hạn cứng ở 500. Đơn cũ nhất được xử lý trước. - Giữ chỗ, nếu worker của bạn xử lý chậm.
PATCH /orders/{id}/last-process-timevới body trống sẽ ghi nhận thời điểm hiện tại, giúp ẩn đơn hàng khỏi các lần poll của chính bạn trong ba mươi phút. - Giao hàng.
POST /orders-updatevới các dòng thông tin tài khoản, hoặcPOST /orders/{id}/accounts-bulkcho các đơn lớn. - Đóng đơn. Chính lệnh gọi
orders-updateđó mang theostatus: "completed", hoặc"partial"nếu bạn mới giao một phần.
Xác thực là một header trên mọi lệnh gọi, và có hai dạng được chấp nhận:
curl -X POST "https://api.hstockplus.com/api/admin/v2/orders-pull" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"limit": 50}'
# tương đương
curl -X POST "https://api.hstockplus.com/api/admin/v2/orders-pull" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"limit": 50}'
Tạo khóa tại trang API Settings trong dashboard của bạn. Thiếu header sẽ trả về API_KEY_REQUIRED; sai khóa sẽ trả về INVALID_API_KEY. Đây là hai mã lỗi khác nhau và chúng chỉ ra hai lỗi khác nhau.
Cửa sổ ba mươi phút là cơ chế giữ chỗ và nó là tùy chọn
Cả hai endpoint liệt kê đều bỏ qua mọi đơn hàng có lastProcessTime nằm trong ba mươi phút gần nhất. Nghe có vẻ như tự động chống trùng lặp, nhưng thực ra không phải, vì không endpoint nào ghi trường này. Không gì ghi nó ngoài bạn, thông qua PATCH /orders/{id}/last-process-time.
Vậy trường này là một cơ chế giữ chỗ hợp tác. Ghi dấu khi bạn nhận một đơn và nó sẽ biến mất khỏi lần poll tiếp theo trong nửa giờ, điều bạn mong muốn nếu việc giao hàng mất một lúc và poller của bạn chạy mỗi phút. Gửi một timestamp ISO tường minh để đặt thời gian hết hạn khác, hoặc null để giải phóng ngay lập tức. Bỏ qua endpoint này hoàn toàn thì cửa sổ đó không bao giờ áp dụng cho bạn.
orders-pull đã bảo vệ bạn khỏi việc nhận trùng qua cơ chế chuyển trạng thái. Cơ chế giữ chỗ dành cho lỗi thứ hai: worker của bạn đã nhận đơn, vẫn đang xử lý, và bạn không muốn thấy nó lần nữa trong lúc đó.
Đọc đơn hàng, với các giá trị mặc định của bộ lọc dễ gây bất ngờ
GET /orders vẫn là endpoint phù hợp để đối soát, backfill và dashboard. Hai giá trị mặc định của nó đáng để biết trước khi bạn xây dựng dựa trên nó.
- Trạng thái thanh toán mặc định là hai giá trị, không phải một. Nếu không có tham số
paymentStatus, bạn nhận các đơn có trạng tháicompletedhoặcpartial. Partial nghĩa là đã hoàn tiền một phần, và những đơn đó vẫn là việc cần xử lý. Tham số này chấp nhận danh sách phân tách bằng dấu phẩy gồm pending, completed, failed, refunded, processing và partial. - Loại sản phẩm là tùy chọn và ở dạng số nhiều. Nó không bắt buộc cho đơn hàng thủ công. Nó chấp nhận
manual,inventoryvàauto, phân tách bằng dấu phẩy, nênproductType=manual,autolà hợp lệ.
Bộ lọc trạng thái đơn hàng chấp nhận pending, processing, completed, refunded và error. partial không nằm trong số đó; nó là trạng thái thanh toán. Các bộ lọc khác: entityType là product hoặc smm_service, subCategory theo tên, createdFrom và createdTo dưới dạng ISO instant, limit tối đa 500, và offset.
curl -X GET "https://api.hstockplus.com/api/admin/v2/orders\
?status=pending,processing&productType=manual&paymentStatus=completed,partial&limit=50" \
-H "X-Api-Key: YOUR_API_KEY"
Điều gì xảy ra với các dòng thông tin tài khoản bạn gửi
Đây là nơi phát sinh nhiều ticket hỗ trợ nhất về API, vì ba phép biến đổi được chạy trước khi bất cứ thứ gì được lưu trữ và không phép nào trong số đó hiển thị trong phản hồi.
- Mọi phần tử đều được tách theo dòng mới. Một mục trong mảng có thể chứa cả một file.
["a:1\nb:2\nc:3"]và["a:1","b:2","c:3"]là cùng một cách gửi. Dòng trống và khoảng trắng xung quanh sẽ bị loại bỏ. - Các phần tử trùng lặp bị loại, không phân biệt hoa thường. Trong cùng một batch, và so với mọi thứ đã giao trên đơn đó. Gửi lại cùng một batch sau timeout là an toàn và sẽ không giao trùng bất cứ thứ gì.
- Các dòng vượt quá số lượng đơn hàng bị loại bỏ âm thầm. Lần gửi bị cắt xuống còn số chỗ trống. Nó không báo lỗi, vì vậy hãy gửi đúng số lượng.
Nội dung một dòng là gì hoàn toàn do bạn quyết định. Nền tảng lưu nó như một chuỗi không trong suốt và không bao giờ phân tích nó, nên một cặp phân tách bằng dấu hai chấm, một bộ ba phân tách bằng dấu pipe kèm mã xác thực hai lớp, một token, một blob phiên: tất cả chỉ là văn bản. Bất kỳ quy ước nào mô tả listing của bạn hứa hẹn chính là quy ước người mua sẽ mong đợi, và không có gì ép buộc điều đó thay cho bạn.
Vì các phần tử trùng với lần giao trước bị loại bỏ thay vì bị từ chối, giao một phần qua nhiều lần gọi là một mẫu được hỗ trợ. Gửi những gì bạn có, đánh dấu đơn là partial, gửi phần còn lại sau, rồi đánh dấu completed.
Hai giới hạn tốc độ, cả hai đều chưa từng được ghi chép cho đến bây giờ
orders-update có khoảng thời gian tối thiểu giữa các lần gọi theo từng API key, mặc định 200 mili giây, và giới hạn đồng thời theo người dùng là hai yêu cầu đang xử lý. Vượt quá một trong hai sẽ trả về HTTP 429 kèm độ trễ có thể đọc bằng máy:
{ "error": "RATE_LIMIT", "message": "Too many order update requests",
"retry_after_ms": 137 }
{ "error": "CONCURRENCY_LIMIT", "message": "Too many concurrent sync requests",
"retry_after_ms": 5000 }
Đọc retry_after_ms và chờ đúng khoảng đó. Backoff cố định sẽ hoặc quá chậm cho trường hợp 200 ms hoặc quá nhanh cho trường hợp đồng thời. Ngoài ra còn có mutex theo từng đơn: hai lần giao hàng chạy đua trên cùng một đơn sẽ cho ORDER_DELIVERY_BUSY, cũng là 429, và phản hồi đúng là retry ngắn thay vì gửi lại thông tin tài khoản khác.
Các mã lỗi đáng xử lý riêng biệt
API_KEY_REQUIREDvàINVALID_API_KEY: thiếu header so với header mà cache không nhận diện.ORDER_NOT_FOUND: id đơn hàng không có trong cache. Lưu ý rằng một nhà cung cấp thông thường cũng có thể không thấy gì cho một đơn auto mới tạo do độ trễ hiển thị ngắn, nên mã 404 vài giây sau khi tạo không nhất thiết là vĩnh viễn.ACCESS_DENIED: đơn hàng không chứa dòng nào thuộc về bạn.PAYMENT_NOT_COMPLETED: chưa thanh toán hoặc đã hoàn tiền toàn bộ. Bạn không thể giao hàng vào đơn này và không nên retry.INVALID_ACCOUNTS, thông báo "No valid accounts provided": mảng bị làm phẳng thành rỗng. Thường là chuỗi rỗng hoặc mảng chứa phần tử không phải chuỗi.RATE_LIMIT,CONCURRENCY_LIMIT,ORDER_DELIVERY_BUSY: back off và retry lại đúng yêu cầu đó.
Hai trường cho việc ghi sổ của riêng bạn
orders-update chấp nhận external_id cho mã tham chiếu nội bộ của bạn, và cũng chấp nhận supplierOrderId như một bí danh nếu đó là định dạng panel của bạn đã gửi. Nó chấp nhận external_price, một con số ghi lại chi phí đơn hàng ở phía upstream của bạn. Con số này được lưu để tham khảo và không ảnh hưởng đến tổng số trên nền tảng. Gửi null hoặc chuỗi rỗng sẽ giữ nguyên giá trị hiện có, nên các lần cập nhật lặp lại sẽ không xóa nó.
Manual thực sự nghĩa là gì, và nó nhanh đến mức nào
Loại sản phẩm manual nghĩa là nhà cung cấp tải hàng lên sau khi đơn hàng đến. Đây không phải là cài đặt tốc độ giao hàng, và bản ghi nói rõ điều đó. Trên toàn bộ các đơn hàng sản phẩm manual đã hoàn thành trên trang web, 6.552 đơn, thời gian trung vị từ thanh toán đến hoàn thành dưới ba mươi giây, ba phần tư hoàn thành trong mười phút, và chỉ khoảng một trong bốn mươi đơn mất hơn một ngày.
Đơn hàng inventory gần như tức thì vì các dòng dữ liệu đã nằm sẵn đó. Đơn auto nằm giữa hai loại kia. Lý do manual trông gần như nhanh tương đương chính là vì các nhà cung cấp xử lý khối lượng lớn trên loại này đang chạy API này với chu kỳ poll ngắn thay vì nhìn dashboard. Nếu tích hợp của bạn thêm vài phút, bạn chính là phần đuôi chậm, không phải chuẩn chung.
Một mốc thời gian cần biết nếu bạn bán dịch vụ tăng trưởng thay vì tài khoản: một đơn dịch vụ tăng trưởng có đúng ba ngày cửa sổ hậu mãi tính từ thời điểm giao hàng, bất kể listing nói gì. Listing tài khoản có chế độ bảo hành riêng, đặt theo từng listing.
Một worker hoạt động đúng cách
const BASE = 'https://api.hstockplus.com/api/admin/v2';
const HEAD = { 'X-Api-Key': process.env.API_KEY, 'Content-Type': 'application/json' };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function call(method, path, body) {
const res = await fetch(BASE + path, {
method,
headers: HEAD,
body: body ? JSON.stringify(body) : undefined,
});
const json = await res.json().catch(() => ({}));
if (res.status === 429) {
await sleep(json.retry_after_ms ?? 1000);
return call(method, path, body);
}
if (!res.ok) throw Object.assign(new Error(json.message || res.status), { code: json.error });
return json;
}
async function tick() {
const { orders = [] } = await call('POST', '/orders-pull', { limit: 50 });
for (const order of orders) {
// Đơn đã ở trạng thái 'processing' và thuộc về bạn. Giữ chỗ nếu bạn xử lý chậm.
await call('PATCH', '/orders/' + order.id + '/last-process-time', {});
const accounts = await prepareCredentials(order); // hệ thống của bạn
await call('POST', '/orders-update', {
order: order.id,
status: accounts.length >= order.quantity ? 'completed' : 'partial',
accounts,
supplierOrderId: 'SUP-' + order.id,
});
}
}
Ba điều vòng lặp đó làm mà cách cũ không làm: nó nhận việc thay vì chỉ đọc, nó tôn trọng độ trễ retry của máy chủ thay vì đoán, và nó báo partial một cách trung thực thay vì đóng một đơn mà nó không thể giao đủ.
Tài liệu tham chiếu endpoint đầy đủ, bao gồm tạo sản phẩm, ticket, hoàn tiền và tác vụ bulk tài khoản, nằm trên trang API nhà cung cấp. Nếu bạn vẫn đang cân nhắc một listing nên là manual, inventory hay auto ngay từ đầu, lựa chọn đó được trình bày trong hướng dẫn quản lý sản phẩm.



