Hướng dẫn

Đưa đơn hàng sang ERP / kế toán

Nhận đơn hàng ngay khi hoàn tất hoặc bị huỷ, ghi sang hệ thống của bạn, không trùng, không sót.

  1. 1Đăng ký webhook Bán hàng

    Trong app: Thiết lập › Ứng dụng & API › Webhooks → chọn Bán hàng → dán URL → Xác minh và lưu → bật order.completed, order.cancelled, order.status_changed.

    Webhooks →order.completed →

  2. 2Verify signature rồi trả 2xx thật nhanh

    Kiểm X-NextX-Signature-256 trên body gốc, trả 200 ngay, đưa việc ghi ERP vào hàng đợi của bạn. Chậm quá hạn hoặc lỗi thì sự kiện được giao lại.

    Node.js (Express)
    import crypto from "crypto";
    
    app.post("/webhooks/orders", express.raw({ type: "application/json" }), (req, res) => {
      const event = JSON.parse(req.body.toString("utf8"));
      // the URL verification ping uses a one-time secret: answer it BEFORE checking the signature
      if (event.type === "endpoint.verification") return res.json({ challenge: event.challenge });
    
      const ts = req.header("X-NextX-Timestamp");
      const sig = req.header("X-NextX-Signature-256") ?? "";
      const expected = "sha256=" + crypto
        .createHmac("sha256", process.env.WEBHOOK_SECRET)
        .update(ts + "." + req.body.toString("utf8"))
        .digest("hex");
      const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
      if (!fresh || sig.length !== expected.length ||
          !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) return res.sendStatus(401);
    
      queue.add(event);          // write to the ERP in the background
      res.sendStatus(200);
    });

    Verify signature →

  3. 3Chống trùng theo id sự kiện

    Một sự kiện có thể tới hơn một lần. Lưu id (evt_…) đã xử lý; gặp lại thì bỏ qua. Dùng orderId làm khoá bên ERP.

    worker
    async function handle(event) {
      if (await db.processed.exists(event.id)) return;      // "evt_..."
      const order = event.data.object;                       // orderId, orderCode, total, items...
      if (event.type === "order.completed") await erp.upsertInvoice(order.orderId, order);
      if (event.type === "order.cancelled") await erp.voidInvoice(order.orderId, order.cancelReason);
      await db.processed.insert(event.id);
    }
  4. 4Đối soát định kỳ

    Mỗi đêm đọc lại đơn trong ngày để bù sự kiện bị lỡ khi máy chủ của bạn ngừng lâu.

    curl
    curl "https://api.nextx.vn/api/v1/sales/orders?dateFrom=2026-10-27&dateTo=2026-10-27&pageNumber=1&pageSize=100" \
      -H "Authorization: Bearer <ACCESS_TOKEN>"

    Tham chiếu API →