Sau khi cài đặt Zalo Sandbox, bạn cần cấu hình thông tin OA giả lập, chuẩn bị người dùng thử và lấy access token trước khi gọi các API.

Trước khi bắt đầu

Hướng dẫn này giả định rằng bạn đã tải Zalo Sandbox từ chợ EzyPlatform, cài đặt thành công cả admin plugin và web plugin, sau đó khởi động lại EzyPlatform.
Plugin phụ thuộc vào EzyChat. Hãy bảo đảm EzyChat đã được cài đặt và hoạt động trước khi sử dụng Zalo Sandbox.
Trong các ví dụ bên dưới, <WEB_URL> là địa chỉ web EzyPlatform của bạn, chẳng hạn http://localhost:8080 hoặc https://example.com. Khi thực hành, hãy thay toàn bộ <WEB_URL> bằng địa chỉ tương ứng và không thêm dấu / ở cuối.

Kiểm tra sau khi cài đặt

Đăng nhập trang quản trị EzyPlatform và mở menu Zalo Sandbox. Plugin cung cấp ba khu vực chính:
  • Messages: xem tin nhắn đã gửi vào sandbox và phát webhook thử nghiệm;
  • Users: quản lý danh sách người dùng được xem là follower của OA sandbox;
  • Settings: cấu hình App ID, secret, token, callback URL, webhook URL và lỗi giả lập.
Plugin phụ thuộc vào hệ thống chat của EzyPlatform. Khi khởi động thành công, một kênh chat OA và vai trò người dùng OA sandbox sẽ được tạo nếu chưa tồn tại.
Bạn cũng có thể mở trang Swagger tại:
<WEB_URL>/zalo-sandbox/swagger
Trang này cho phép xem nhanh những endpoint, request và response đang được sandbox hỗ trợ.

Cấu hình sandbox lần đầu

Mở Zalo Sandbox → Settings và điền các trường sau:
TrườngCách thiết lập
Zalo OA App IDNhập một chuỗi đại diện cho ứng dụng hoặc bấm Random
Zalo OA App Secret KeyNhập application secret hoặc bấm Random
Zalo OA Webhook Secret KeySecret dùng để ký webhook gửi từ sandbox
Zalo OA Access TokenToken sandbox sẽ trả về sau bước đổi authorization code
Zalo OA Refresh TokenRefresh token dùng cho luồng làm mới token
Token Expire In SecondsThời gian hiệu lực tính bằng giây; mặc định tương đương 7 ngày
Token Expired AtMốc hết hạn hiện tại tính bằng milliseconds từ Unix epoch
Auth Code ChallengeCó thể để trống và truyền challenge khi bắt đầu OAuth
Auth Callback URLsDanh sách callback URL được phép
OA Webhook URLsDanh sách URL nhận webhook để chọn trên trang quản trị
Send Message Response ErrorMã lỗi muốn giả lập cho API gửi tin thường
Send Message Template Response ErrorMã lỗi muốn giả lập cho API gửi tin template
Screenshot 2026-09-12 at 10.42.58.png
Screenshot 2026-09-12 at 10.43.15.png
Các trường App ID, App Secret Key, Access Token, Refresh Token, thời gian hiệu lực và callback URL là bắt buộc. Callback URL và webhook URL được nhập dưới dạng danh sách phân tách bằng dấu phẩy.
Nhấn Save để lưu. Bạn cũng có thể dùng Ctrl+S trên Windows/Linux hoặc Command+S trên macOS khi con trỏ đang ở trong biểu mẫu.
Callback URL phải khớp hoàn toàn với redirect_uri gửi lên endpoint cấp quyền, bao gồm protocol, host, port, path và dấu gạch chéo cuối nếu có. Ví dụ:
<WEB_URL>/oa/ZALO_SANDBOX/auth-callback

Chuẩn bị người dùng OA thử nghiệm

Người dùng OA sandbox là tài khoản EzyPlatform được gán thêm vai trò OA sandbox, không phải một loại tài khoản độc lập.
Để thêm người dùng:
  1. Tạo tài khoản trong khu vực quản lý người dùng của EzyPlatform nếu chưa có.
  2. Mở Zalo Sandbox → Users.
  3. Nhấn nút thêm mới.
  4. Tìm và chọn một tài khoản đã tồn tại.
  5. Kiểm tra tài khoản xuất hiện trong danh sách Users của Zalo Sandbox.
Screenshot 2026-09-12 at 10.51.47.png
Nên bổ sung tên hiển thị, số điện thoại và ảnh đại diện cho tài khoản thử. API chi tiết người dùng sử dụng tên hiển thị và ảnh đại diện; API gửi template tìm người nhận theo số điện thoại.
Nhấn biểu tượng xem chi tiết để lấy UUID của người dùng. UUID này được dùng làm user_id khi gọi API OA.

Lấy access token bằng OAuth và PKCE

Bạn có thể dùng trực tiếp token đã cấu hình, nhưng thực hiện đầy đủ luồng OAuth sẽ giúp kiểm tra tích hợp sát thực tế hơn.

Tạo code verifier và code challenge

Tạo một code_verifier, sau đó băm SHA-256 và mã hóa Base64 URL-safe không padding để thu được code_challenge.
Ví dụ trên macOS hoặc Linux:
CODE_VERIFIER="zalo-sandbox-verifier-0123456789-abcdefghijklmnopqrstuvwxyz"
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" \
  | openssl dgst -sha256 -binary \
  | openssl base64 -A \
  | tr '+/' '-_' \
  | tr -d '=')
printf '%sn' "$CODE_CHALLENGE"
Giữ lại CODE_VERIFIER; giá trị này sẽ được dùng khi đổi authorization code lấy token.

Bắt đầu luồng cấp quyền

Mở URL sau trên trình duyệt và thay các placeholder bằng cấu hình của bạn:
<WEB_URL>/zalo-sandbox/v4/oa/permission?app_id=<APP_ID>&redirect_uri=<CALLBACK_URL>&code_challenge=<CODE_CHALLENGE>
Nên URL-encode các query parameter khi tạo URL trong ứng dụng. Sandbox kiểm tra App ID và callback URL, lưu code challenge rồi chuyển hướng về callback theo dạng:
<CALLBACK_URL>?code_challenge=<CODE_CHALLENGE>&code=<AUTHORIZATION_CODE>
Lấy giá trị code từ URL callback.

Đổi authorization code lấy token

Gửi request sau:
curl --request POST \
  '<WEB_URL>/zalo-sandbox/v4/oa/access_token' \
  --header 'Content-Type: application/json' \
  --header 'secret_key: <APP_SECRET_KEY>' \
  --data '{
    "app_id": "<APP_ID>",
    "code": "<AUTHORIZATION_CODE>",
    "grant_type": "authorization_code",
    "code_verifier": "<CODE_VERIFIER>"
  }'
Response thành công có dạng:
{
  "access_token": "<ACCESS_TOKEN>",
  "refresh_token": "<REFRESH_TOKEN>",
  "expires_in": 604800
}
Thao tác cấp token cập nhật mốc hết hạn dựa trên thời gian hiện tại và expires_in. Giá trị access token và refresh token được lấy từ cấu hình sandbox, không được tạo mới sau mỗi request.

Làm mới token

Khi muốn thử luồng refresh token:
curl --request POST \
  '<WEB_URL>/zalo-sandbox/v4/oa/access_token' \
  --header 'Content-Type: application/json' \
  --header 'secret_key: <APP_SECRET_KEY>' \
  --data '{
    "grant_type": "refresh_token",
    "refresh_token": "<REFRESH_TOKEN>"
  }'

Thử API người dùng

Lấy danh sách người dùng OA

Tham số data là một chuỗi JSON chứa offsetcount:
curl --get \
  '<WEB_URL>/zalo-sandbox/v3.0/oa/user/getlist' \
  --header 'access_token: <ACCESS_TOKEN>' \
  --data-urlencode 'data={"offset":0,"count":20}'
Danh sách chỉ chứa những tài khoản đã được thêm trong Zalo Sandbox → Users. Mỗi phần tử trả về user_id, chính là UUID của tài khoản trong EzyPlatform.

Lấy chi tiết người dùng

curl --get \
  '<WEB_URL>/zalo-sandbox/v3.0/oa/user/detail' \
  --header 'access_token: <ACCESS_TOKEN>' \
  --data-urlencode 'data={"user_id":"<USER_UUID>"}'
Response thành công có thể chứa user_id, display_name, user_aliasavatar. Nếu UUID không tồn tại, sandbox trả về lỗi tham số không hợp lệ kèm thông tin gỡ lỗi.

Gửi tin nhắn chăm sóc khách hàng

Gửi một tin văn bản đến UUID của người dùng OA:
curl --request POST \
  '<WEB_URL>/zalo-sandbox/v3.0/oa/message/cs' \
  --header 'Content-Type: application/json' \
  --header 'access_token: <ACCESS_TOKEN>' \
  --data '{
    "recipient": {
      "user_id": "<USER_UUID>"
    },
    "message": {
      "text": "Xin chào từ Zalo Sandbox"
    }
  }'
Response thành công:
{
  "error": 0,
  "message": "success",
  "debugMessage": null
}
Mở Zalo Sandbox → Messages để kiểm tra tin nhắn vừa được ghi vào kênh chat OA.
Nếu muốn gửi nội dung có attachment, thêm đối tượng attachment vào message. Sandbox lưu nguyên cấu trúc này dưới dạng JSON để phục vụ kiểm thử; nó không xử lý toàn bộ vòng đời attachment giống Zalo thật.

Upload ảnh và dùng attachment ID

Để upload ảnh:
curl --request POST \
  '<WEB_URL>/zalo-sandbox/v2.0/oa/upload/image' \
  --header 'access_token: <ACCESS_TOKEN>' \
  --form 'file=@/duong-dan/toi/anh.png'
Response trả về tên file đã lưu dưới dạng attachment ID:
{
  "data": {
    "attachment_id": "<ATTACHMENT_ID>"
  }
}
Ví dụ gửi tin nhắn có attachment:
curl --request POST \
  '<WEB_URL>/zalo-sandbox/v3.0/oa/message/cs' \
  --header 'Content-Type: application/json' \
  --header 'access_token: <ACCESS_TOKEN>' \
  --data '{
    "recipient": {
      "user_id": "<USER_UUID>"
    },
    "message": {
      "text": "Tin nhắn có ảnh",
      "attachment": {
        "type": "template",
        "payload": {
          "template_type": "media",
          "elements": [
            {
              "media_type": "image",
              "attachment_id": "<ATTACHMENT_ID>"
            }
          ]
        }
      }
    }
  }'
Upload chỉ hoạt động khi tính năng upload tài nguyên đã được bật trong web runtime của EzyPlatform.

Gửi tin nhắn template

Người nhận được tìm theo số điện thoại. Vì vậy, tài khoản thử phải có số điện thoại trước khi gọi API này.
curl --request POST \
  '<WEB_URL>/zalo-sandbox/message/template' \
  --header 'Content-Type: application/json' \
  --header 'access_token: <ACCESS_TOKEN>' \
  --data '{
    "phone": "0900000000",
    "template_id": "order-update",
    "template_data": {
      "customer_name": "Nguyen Van A",
      "order_code": "ORDER-001",
      "status": "Đang giao hàng"
    }
  }'
Sandbox cũng thử chuyển số bắt đầu bằng 84 sang dạng nội địa bắt đầu bằng 0 khi tìm người dùng. Tin template hợp lệ xuất hiện trong trang Messages cùng các tham số đã gửi.

Phát webhook từ trang quản trị

Trước tiên, thêm ít nhất một URL vào trường OA Webhook URLs trong Settings, ví dụ:
<WEB_URL>/api/v1/oa/ZALO_SANDBOX/webhook
Có hai cách gửi webhook tin nhắn thử:
  • Mở Users, chọn một người dùng, chọn webhook URL, nhập nội dung rồi nhấn Send.
  • Mở Messages, cuộn tới biểu mẫu gửi, chọn webhook URL, nhập username và nội dung rồi nhấn Send.
Screenshot 2026-09-12 at 10.54.01.png
Screenshot 2026-09-12 at 10.54.16.png
Sandbox gửi một HTTP POST chứa event_name, timestamp, sendermessage. Với biểu mẫu tin nhắn, sự kiện được gửi là user_send_text.
Request có header:
X-ZEvent-Signature: mac=<SHA256_HEX>
Chữ ký được tính từ chuỗi nối theo đúng thứ tự:
APP_ID + RAW_JSON_BODY + TIMESTAMP + WEBHOOK_SECRET_KEY
Ứng dụng nhận webhook nên tính lại SHA-256 ở dạng chữ thường rồi so sánh với phần sau mac=. Giá trị timestamp vừa nằm trong body vừa tham gia tạo chữ ký.

Giả lập các trường hợp lỗi

Trong Settings, chọn một mã tại Send Message Response Error hoặc Send Message Template Response Error, sau đó lưu cấu hình và gọi lại API tương ứng.
Với request hợp lệ, sandbox vẫn ghi tin nhắn vào kênh chat rồi trả mã lỗi đã chọn. Vì vậy chức năng này phù hợp để kiểm tra cách ứng dụng xử lý response lỗi, nhưng không mô phỏng transaction rollback hay việc Zalo từ chối lưu tin nhắn.
Sau khi thử xong, chọn None và lưu lại để API trở về response thành công.
Một số lỗi phổ biến khác có thể kiểm tra trực tiếp:
Tình huốngKết quả mong đợi
Thiếu hoặc sai access tokenLỗi access token không hợp lệ
Token đã quá hạnLỗi access token không hợp lệ, debug message cho biết token hết hạn
Sai App ID hoặc application secretRequest đổi token bị từ chối
Callback URL không nằm trong danh sách cho phépRequest permission bị từ chối
code_verifier không khớp challengeRequest đổi token bị từ chối
UUID người nhận không tồn tạiLỗi người dùng chưa theo dõi OA
Số điện thoại template không tìm thấyLỗi người dùng chưa theo dõi OA
JSON trong tham số data không hợp lệLỗi tham số không hợp lệ

Xử lý sự cố

Access token vừa cấu hình nhưng vẫn báo hết hạn

Nhập token trong Settings chưa tự đặt lại thời điểm hết hạn. Hãy thực hiện request POST /v4/oa/access_token để sandbox cập nhật mốc hết hạn, hoặc đặt Token Expired At thành một thời điểm milliseconds trong tương lai.

Người dùng không xuất hiện trong API danh sách

Kiểm tra tài khoản đã được thêm qua Zalo Sandbox → Users. Việc chỉ tạo tài khoản trong EzyPlatform chưa đủ; tài khoản cần có vai trò OA sandbox.

Gửi template báo không tìm thấy người dùng

Kiểm tra số điện thoại trong hồ sơ tài khoản và giá trị phone trong request. Sandbox chỉ thử hai dạng: chuỗi được gửi lên và dạng đổi tiền tố 84 thành 0.

Không upload được ảnh

Kiểm tra upload tài nguyên đã được bật, kích thước file không vượt giới hạn và định dạng file được bộ kiểm tra media chấp nhận.

Không nhận được webhook

Kiểm tra URL đã được lưu trong Settings, môi trường sandbox có thể kết nối đến URL đó và endpoint đích chấp nhận HTTP POST. Nếu webhook nằm trên máy khác hoặc trong container khác, localhost có thể trỏ sai máy.

Checklist hoàn tất

Sau khi hoàn thành cấu hình, bạn nên xác nhận:
  • trang Swagger mở được;
  • Settings đã có App ID, secret, token và callback URL;
  • ít nhất một người dùng đã xuất hiện trong danh sách OA sandbox;
  • luồng OAuth trả về token và cập nhật thời hạn;
  • API danh sách và chi tiết người dùng trả đúng dữ liệu;
  • tin nhắn thường và template xuất hiện trong Messages;
  • upload ảnh trả về attachment ID nếu dự án cần kiểm thử media;
  • webhook đến được ứng dụng và chữ ký xác minh thành công;
  • các mã lỗi giả lập đã được đưa về None sau khi kiểm thử.
Hoàn thành các bước trên nghĩa là Zalo Sandbox đã sẵn sàng thay thế endpoint Zalo OA trong môi trường phát triển và kiểm thử cục bộ.