Skip to main content
ORCA
MES
VN

Yêu cầu đơn bán hàng

Chỉ nhận field có thể ánh xạ từ RegistContract schema hiện tại và lookup mã đối tác/sản phẩm. Thiếu mapping sẽ lỗi rõ ràng.

Endpoint

POST https://open-api.orca.partners/v1/sales-orders

Xác thực

Lưu Customer API key trong secret phía server và gửi bằng Bearer header. Customer key không có scope mới hoặc tùy chọn.

  • Authorization: Bearer <customer-api-key>
  • Idempotency-Key: <external-order-derived-key>
  • Content-Type: application/json

Yêu cầu đơn bán hàng

Chỉ nhận field có thể ánh xạ từ RegistContract schema hiện tại và lookup mã đối tác/sản phẩm. Thiếu mapping sẽ lỗi rõ ràng.

{
  "source_type": "own_mall",
  "source_channel_id": "42",
  "external_order_id": "ORDER-2026-00042",
  "order_name": "Own mall ORDER-2026-00042",
  "ordered_at": "2026-08-18T00:00:00Z",
  "expected_delivery_at": "2026-08-22T00:00:00Z",
  "vendor_code": "ONLINE-CUSTOMER",
  "currency": "KRW",
  "shipping_address": "110 Sejong-daero, Seoul",
  "items": [
    {
      "item_code": "FINISHED-001",
      "quantity": 2,
      "unit_price": 12000
    }
  ]
}

Backend tạo đơn cửa hàng riêng

Gọi hàm Node.js/Axios đúng một lần sau khi commit DB đơn hàng thành công. Không đưa key vào browser, app, repository công khai hoặc DB đơn hàng.

// Node.js backend only — run after the order DB commit.
const axios = require("axios");
const { createHash } = require("crypto");

async function sendCommittedOrderToOrca(order) {
  const apiKey = process.env.ORCA_API_KEY;
  const sourceChannelId = process.env.ORCA_SOURCE_CHANNEL_ID;
  if (!apiKey || !sourceChannelId) {
    throw new Error(
      "ORCA_CONFIG_ERROR: ORCA_API_KEY and ORCA_SOURCE_CHANNEL_ID are required"
    );
  }

  const startedAt = Date.now();
  const idempotencyKey = `own-mall:${createHash("sha256")
    .update(String(order.id))
    .digest("hex")}`;
  const payload = {
    source_type: "own_mall",
    source_channel_id: sourceChannelId,
    external_order_id: String(order.id),
    order_name: `Own mall ${order.id}`,
    ordered_at: order.orderedDate,
    expected_delivery_at: order.expectedDeliveryDate,
    vendor_code: order.orcaVendorCode,
    currency: order.currency,
    shipping_address: order.deliveryAddress || undefined,
    items: order.items.map((item) => ({
      item_code: item.orcaItemCode,
      quantity: item.quantity,
      unit_price: item.unitPrice,
    })),
  };

  try {
    const response = await axios.post(
      "https://open-api.orca.partners/v1/sales-orders",
      payload,
      {
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Idempotency-Key": idempotencyKey,
          "Content-Type": "application/json",
        },
        timeout: 10000,
      }
    );
    return response.data;
  } catch (error) {
    const status = error.response?.status ?? null;
    const code =
      error.response?.data?.err?.errorCode ??
      (error.code === "ECONNABORTED"
        ? "ORCA_TIMEOUT"
        : "ORCA_REQUEST_FAILED");
    const failure = new Error(
      `ORCA_ORDER_CREATE_FAILED stage=post-commit status=${status ?? "none"} ` +
        `code=${code} elapsed_ms=${Date.now() - startedAt}`
    );
    failure.cause = error;
    throw failure;
  }
}

module.exports = { sendCommittedOrderToOrca };

Quy tắc vận hành

  • Idempotency-Key là bắt buộc nhưng chưa bảo đảm replay bền vững hay chống trùng đồng thời.
  • Sau timeout/5xx, xác nhận kết quả MES trước khi gửi lại thủ công.
  • Giới hạn 100 sản phẩm và 256 KiB mỗi request; 1 request/giây mỗi IP, burst 10.