Sau khi cài đặt Viettel Post Sandbox, bạn có thể tích hợp API theo cùng luồng nghiệp vụ thường dùng với Viettel Post: đăng nhập, lấy dịch vụ phù hợp, lấy thông tin cửa hàng và tạo vận đơn.
Các API trong tài liệu này chạy trên EzyPlatform của bạn, không kết nối đến Viettel Post và không tạo vận đơn thật.

Chuẩn bị

Trong các ví dụ bên dưới:
  • <WEB_URL> là địa chỉ web EzyPlatform, ví dụ http://localhost:8080 hoặc https://example.com;
  • <TOKEN_TAM> là token trả về từ API /user/login;
  • <TOKEN_DAI_HAN> là token trả về từ API /user/ownerconnect.
Base URL của sandbox là:
<WEB_URL>/viettel-post-sandbox/v2
Bạn có thể xem request và response đầy đủ của các API tại:
<WEB_URL>/viettel-post-sandbox/swagger

Luồng tạo vận đơn

flowchart TD
    A[Chuẩn bị WEB_URL] --> B[Gọi user/login]
    B --> C{Đăng nhập thành công?}
    C -- Không --> D[Kiểm tra USERNAME và PASSWORD]
    D --> B
    C -- Có --> E[Nhận token tạm]
    E --> F[Gọi user/ownerconnect]
    F --> G[Nhận token dài hạn]
    G --> H[Gọi order/getPriceAllNlp]
    H --> I[Chọn mã dịch vụ và lấy ID địa chỉ]
    I --> J[Gọi user/listInventory]
    J --> K[Chọn GROUPADDRESS_ID và CUS_ID]
    K --> L[Gọi order/createOrder]
    L --> M{Tạo đơn thành công?}
    M -- Không --> N[Kiểm tra token và dữ liệu bắt buộc]
    N --> L
    M -- Có --> O[Kiểm tra vận đơn trên trang quản lý]
Kết quả của mỗi bước được dùng cho bước tiếp theo:
  • token từ user/login được gửi đến user/ownerconnect;
  • token từ user/ownerconnect được gửi trong header Token của các API còn lại;
  • mã dịch vụ và ID địa chỉ từ order/getPriceAllNlp được dùng để tạo đơn;
  • GROUPADDRESS_IDCUS_ID từ user/listInventory được dùng để tạo đơn.

Đăng nhập để lấy token

Bạn không cần tạo tài khoản sandbox trước. Ở lần đăng nhập đầu tiên, plugin tự động tạo tài khoản từ USERNAMEPASSWORD được gửi lên.
Trên môi trường sandbox:
  • USERNAMEPASSWORD có thể là giá trị giả lập nhưng không được để trống;
  • nên dùng một số điện thoại giả lập làm USERNAME để gần với dữ liệu Viettel Post;
  • không nên sử dụng tài khoản hoặc mật khẩu thật;
  • token mặc định có hiệu lực trong 7 ngày.

Lấy token tạm

Gọi API /user/login:
curl --request POST '<WEB_URL>/viettel-post-sandbox/v2/user/login' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "USERNAME": "0987654321",
    "PASSWORD": "sandbox-password"
  }'
Response có dạng:
{
  "status": 200,
  "error": false,
  "message": "OK",
  "data": {
    "userId": 123,
    "partner": 123,
    "phone": "0987654321",
    "token": "<TOKEN_TAM>",
    "expired": 1750000000000,
    "source": 5
  }
}
Trường expired là Unix timestamp theo millisecond cho biết thời điểm token hết hạn.

Lấy token dài hạn

Tiếp theo, gọi API /user/ownerconnect với cùng username, password và token tạm trong header Token:
curl --request POST '<WEB_URL>/viettel-post-sandbox/v2/user/ownerconnect' \
  --header 'Content-Type: application/json' \
  --header 'Token: <TOKEN_TAM>' \
  --data-raw '{
    "USERNAME": "0987654321",
    "PASSWORD": "sandbox-password"
  }'
Lưu giá trị data.token trong response làm <TOKEN_DAI_HAN>. Tất cả các bước tiếp theo sử dụng token này trong header Token.

Lấy danh sách dịch vụ vận chuyển

Gọi API /order/getPriceAllNlp với địa chỉ người gửi và người nhận ở dạng văn bản:
curl --request POST '<WEB_URL>/viettel-post-sandbox/v2/order/getPriceAllNlp' \
  --header 'Content-Type: application/json' \
  --header 'Token: <TOKEN_DAI_HAN>' \
  --data-raw '{
    "SENDER_ADDRESS": "61 K2 Cầu Diễn, Nam Từ Liêm, Hà Nội",
    "RECEIVER_ADDRESS": "Định Công, Hoàng Mai, Hà Nội",
    "PRODUCT_TYPE": "HH",
    "PRODUCT_WEIGHT": 500,
    "PRODUCT_PRICE": 200000,
    "MONEY_COLLECTION": 200000,
    "PRODUCT_LENGTH": 20,
    "PRODUCT_WIDTH": 15,
    "PRODUCT_HEIGHT": 10,
    "TYPE": 1
  }'
Đơn vị dữ liệu:
  • trọng lượng tính bằng gram;
  • kích thước tính bằng centimet;
  • giá trị hàng và tiền thu hộ tính bằng VND;
  • PRODUCT_TYPE nhận HH cho hàng hóa hoặc TH cho thư, tài liệu;
  • TYPE nhận 1 cho vận chuyển trong nước hoặc 0 cho quốc tế.
Ví dụ response rút gọn:
{
  "SENDER_ADDRESS": {
    "PROVINCE_ID": 1,
    "DISTRICT_ID": 25,
    "WARD_ID": 498
  },
  "RECEIVER_ADDRESS": {
    "PROVINCE_ID": 1,
    "DISTRICT_ID": 4,
    "WARD_ID": 74
  },
  "RESULT": [
    {
      "MA_DV_CHINH": "PHS",
      "TEN_DICHVU": "Nội tỉnh tiết kiệm",
      "GIA_CUOC": 16500,
      "THOI_GIAN": "24 giờ"
    }
  ]
}
Bạn cần lưu:
  • ID tỉnh, huyện và xã trong SENDER_ADDRESSRECEIVER_ADDRESS;
  • RESULT[].MA_DV_CHINH để truyền vào ORDER_SERVICE khi tạo đơn.
Nếu response trả về nhiều dịch vụ, ứng dụng tự chọn dịch vụ phù hợp theo mã dịch vụ, cước phí hoặc thời gian giao hàng.

Lấy thông tin cửa hàng

Gọi API /user/listInventory:
curl --request GET '<WEB_URL>/viettel-post-sandbox/v2/user/listInventory' \
  --header 'Token: <TOKEN_DAI_HAN>'
Ví dụ response:
{
  "status": 200,
  "error": false,
  "message": "OK",
  "data": [
    {
      "groupaddressId": 5818802,
      "cusId": 1456467,
      "name": "Cửa hàng sandbox",
      "phone": "0987654321",
      "address": "61 K2 Cầu Diễn",
      "provinceId": 1,
      "districtId": 25,
      "wardsId": 493
    }
  ]
}
Chọn một cửa hàng và sử dụng:
  • groupaddressId làm GROUPADDRESS_ID;
  • cusId làm CUS_ID.

Tạo vận đơn

Kết hợp dịch vụ, địa chỉ đã chuẩn hóa và thông tin cửa hàng từ các bước trước để gọi /order/createOrder:
curl --request POST '<WEB_URL>/viettel-post-sandbox/v2/order/createOrder' \
  --header 'Content-Type: application/json' \
  --header 'Token: <TOKEN_DAI_HAN>' \
  --data-raw '{
    "ORDER_NUMBER": "ORDER-SANDBOX-001",
    "GROUPADDRESS_ID": 5818802,
    "CUS_ID": 1456467,
    "DELIVERY_DATE": "11/09/2026 10:00:00",
    "SENDER_FULLNAME": "Cửa hàng sandbox",
    "SENDER_ADDRESS": "61 K2 Cầu Diễn, Nam Từ Liêm, Hà Nội",
    "SENDER_PHONE": "0987654321",
    "SENDER_EMAIL": "sender@example.com",
    "SENDER_PROVINCE": 1,
    "SENDER_DISTRICT": 25,
    "SENDER_WARD": 498,
    "RECEIVER_FULLNAME": "Người nhận sandbox",
    "RECEIVER_ADDRESS": "Định Công, Hoàng Mai, Hà Nội",
    "RECEIVER_PHONE": "0912345678",
    "RECEIVER_EMAIL": "receiver@example.com",
    "RECEIVER_PROVINCE": 1,
    "RECEIVER_DISTRICT": 4,
    "RECEIVER_WARD": 74,
    "PRODUCT_NAME": "Sản phẩm thử nghiệm",
    "PRODUCT_DESCRIPTION": "Đơn hàng dùng để kiểm thử tích hợp",
    "PRODUCT_QUANTITY": 1,
    "PRODUCT_PRICE": 200000,
    "PRODUCT_WEIGHT": 500,
    "PRODUCT_LENGTH": 20,
    "PRODUCT_WIDTH": 15,
    "PRODUCT_HEIGHT": 10,
    "PRODUCT_TYPE": "HH",
    "ORDER_PAYMENT": 3,
    "ORDER_SERVICE": "PHS",
    "ORDER_SERVICE_ADD": "",
    "ORDER_NOTE": "Cho kiểm tra hàng",
    "MONEY_COLLECTION": 200000,
    "LIST_ITEM": [
      {
        "PRODUCT_NAME": "Sản phẩm thử nghiệm",
        "PRODUCT_QUANTITY": 1,
        "PRODUCT_PRICE": 200000,
        "PRODUCT_WEIGHT": 500
      }
    ]
  }'
Ý nghĩa của ORDER_PAYMENT:
  • 1: không thu tiền;
  • 2: thu tiền hàng và phí vận chuyển;
  • 3: thu tiền hàng;
  • 4: thu phí vận chuyển.
Khi thành công, response trả về data.ORDER_NUMBER cùng tiền thu hộ và các khoản phí mẫu.
Bạn có thể kiểm tra vận đơn vừa tạo tại:
<WEB_URL>/viettel-post-sandbox/delivery-orders

Lỗi thường gặp

Username hoặc password bắt buộc

Body đăng nhập phải dùng đúng tên trường viết hoa USERNAMEPASSWORD. Cả hai giá trị đều không được để trống.

Token không hợp lệ hoặc hết hạn

  • Gửi token trong header Token.
  • Sử dụng token trả về từ ownerconnect, không sử dụng webhook token.
  • Thực hiện lại luồng đăng nhập nếu token đã hết hạn.

Không tìm thấy dịch vụ phù hợp

Kiểm tra địa chỉ người gửi, người nhận, loại hàng, trọng lượng, kích thước, giá trị hàng và tiền thu hộ.

Request tạo đơn thiếu dữ liệu

Kiểm tra các trường bắt buộc, đặc biệt là:
  • ORDER_NUMBER, GROUPADDRESS_ID, CUS_ID, DELIVERY_DATE;
  • ID tỉnh, huyện và xã của người gửi, người nhận;
  • PRODUCT_NAME, PRODUCT_QUANTITY, PRODUCT_WEIGHT, PRODUCT_TYPE;
  • ORDER_PAYMENT, ORDER_SERVICE, MONEY_COLLECTION;
  • LIST_ITEM phải có ít nhất một sản phẩm.
Tham khảo Swagger để xem đầy đủ request, response và các API khác của phiên bản plugin đang sử dụng.