← 所有文章
教學

Zalo QR 授權:處理 qrcode_expired 並接收簽章 Webhook

如果 Zalo QR 授權流程回傳 qrcode_expired,不要繼續使用舊 QR。重新呼叫 POST /v1/accounts/{account_id}/auth/qr/start,再輪詢 POST /v1/accounts/{account_id}/auth/qr/check。同時,Webhook 端點要先建立好,授權狀態與後續訊息才有投遞位置。帳號完成授權後,Zalo 入站訊息會以簽章的 message.received 事件送到你的服務。

重點

  • 在 UnifyPort 中,Zalo 授權是 QR-only:建立 Zalo messaging account 時設定 auth_mode: "qrcode",不需要先準備 provider 憑證。
  • 先註冊 Webhook。授權更新與後續訊息都會透過事件流投遞。
  • qrcode_expired 是可預期的重試狀態:重新呼叫 qr/start、顯示新的 QR 內容、繼續輪詢。
  • 在解析 JSON 前,先用 X-Device-Timestamp + "." + raw body 驗證 X-Device-Signature
  • 若產品需求是 Zalo Official Account 身分或 OA 原生能力,應評估官方 OA 路線;本文聚焦 UnifyPort 面向既有 messaging account 的非官方介面。

若還在比較帳號模型,先看 Zalo Official Account API 與個人帳號 Webhook。如果你的台灣或東南亞團隊同時處理 LINE、Zalo、X,也可參考 LINE、Zalo、X 共用一個 Webhook

這個流程不是官方 OA Webhook

Zalo 官方開發文件提供 Official Account API 與 OA Webhook,適合需要 OA 身分、OA 後台操作或官方平台關係的情境。

UnifyPort 的流程不同:用 QR 授權連接一個 Zalo messaging account,再由 UnifyPort 的 Webhook 投遞層輸出標準化事件。Zalo authorization 說明此流程使用 QR login,且需要 Webhook 端點接收授權與訊息事件。

步驟 1:掃描前先建立簽章 Webhook

curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://inbox.example.com/unifyport/zalo",
  "status": "active",
  "subscribed_events": ["account.auth.succeeded", "account.auth.required", "message.received"],
  "signing_secret": "zalo-support-2026"
}'

欄位細節見 Create webhook endpointurl 必須是絕對 URL,status 可為 activeinactivesubscribed_events 可使用明確的公共事件名稱或 ["*"]

步驟 2:建立 Zalo messaging account

curl -X POST https://api.unifyport.ai/v1/accounts \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Zalo Support Inbox",
  "provider": "zalo",
  "region": "global",
  "status": "active",
  "auth_mode": "qrcode",
  "capabilities": ["receive_message"],
  "provider_data": {},
  "metadata": { "workflow": "support-intake" }
}'

保存回傳的 account_id。後續 QR 授權端點會用到它,請不要把正式帳號 ID 貼到公開 issue 或 AI 對話裡。

步驟 3:啟動 QR 授權並處理 qrcode_expired

curl -X POST "https://api.unifyport.ai/v1/accounts/$ACCOUNT_ID/auth/qr/start" \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

接著輪詢:

curl -X POST "https://api.unifyport.ai/v1/accounts/$ACCOUNT_ID/auth/qr/check" \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

若狀態為 qrcode_expired,丟棄舊 QR 畫面並重新呼叫 qr/start。後台 UI 建議只呈現三種狀態:等待掃描、已過期需產生新 QR、已授權。

步驟 4:驗證簽章再處理事件

Webhook delivery 定義的簽章字串是:

<X-Device-Timestamp>.<raw request body>
import crypto from 'node:crypto';
import express from 'express';

const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;

app.post('/unifyport/zalo', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.get('X-Device-Timestamp') ?? '';
  const signature = req.get('X-Device-Signature') ?? '';

  const expected = crypto
    .createHmac('sha256', signingSecret)
    .update(timestamp + '.')
    .update(req.body)
    .digest('hex');

  if (signature.length !== expected.length) return res.sendStatus(401);
  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  if (event.type === 'message.received' && event.provider === 'zalo') {
    // 先保存 event.id、event.account_id、event.occurred_at 與 event.data,再路由。
  }

  res.sendStatus(202);
});

步驟 5:保存標準化 Zalo 事件

{
  "id": "evt_2f9c1a4b7e",
  "type": "message.received",
  "provider": "zalo",
  "account_id": "acc_8c21d0",
  "occurred_at": "2026-06-08T12:34:56Z",
  "data": {
    "conversation": { "id": "5005", "type": "user" },
    "sender": { "id": "4004", "type": "user", "name": "Minh Nguyen" },
    "message": {
      "id": "3003",
      "text": "Sản phẩm này còn hàng không?",
      "direction": "inbound",
      "sent_at": "2026-06-08T12:34:55Z"
    }
  }
}

先保存,再路由。Slack、CRM、AI 分類與人工分派都應在事件可持久查詢後再執行。通用收件架構可參考 Webhook-first inbound integration checklist

FAQ

Zalo 回傳 qrcode_expired 時該怎麼辦?

重新呼叫 auth/qr/start,顯示新的 QR 內容,並繼續輪詢 auth/qr/check。不要重用過期 QR。

這個流程需要 Zalo 開發者憑證嗎?

不需要先提供 provider 憑證。指定使用者掃描 QR 後,帳號身分才會被識別。

這和官方 Zalo OA Webhook 一樣嗎?

不一樣。官方 OA Webhook 屬於 Official Account 開發模型;本文描述 UnifyPort 非官方介面的標準化入站事件。

下一步

Zalo authorizationWebhook delivery 放在一起看,先連接測試帳號並送一則 Zalo 入站訊息,再接下游自動化。

來源

官方來源核對於 2026-09-04:

UnifyPort API

讓訊息接入變成一條穩定的產品管線。

先用統一 API 跑通傳送,再用標準事件把所有入站訊息接回業務系統。