Webhooks API

Webhook 允許您的應用程式在 Mercozy 商店發生事件時接收即時 HTTP 通知。無需輪詢 API,只需註冊一個 URL,Mercozy 會自動將事件資料 POST 到該位址。

快速開始

3 個步驟即可開始接收事件:

步驟 1:註冊您的端點

從以下位置取得 API 金鑰 設定 > 整合 > API 金鑰. 需要管理員權限。Webhook 簽名密鑰(whsec_xxx)在建立端點時自動產生,且僅顯示一次。

bash
curl -X POST "https://api.mercozy.com/api/v1/external/webhooks" \
  -H "X-API-Key: mk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My ERP Integration",
    "url": "https://your-app.com/webhooks/mercozy",
    "events": ["order.created", "order.cancelled", "payment.received"]
  }'
步驟 2:處理傳入的事件
javascript
app.post('/webhooks/mercozy', (req, res) => {
  // 1. Verify signature (see below)
  const signature = req.headers['x-webhook-signature'];
  if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // 2. Process the event
  const { type, data } = req.body;
  switch (type) {
    case 'order.created':
      syncOrderToERP(data);
      break;
    case 'payment.received':
      updateAccountingSystem(data);
      break;
  }

  // 3. Respond quickly with 200
  res.status(200).json({ received: true });
});
步驟 3:測試您的端點
bash
curl -X POST "https://api.mercozy.com/api/v1/external/webhooks/{id}/test" \
  -H "X-API-Key: mk_live_your_key_here"

端點列表

GET

/webhooks

webhooks:read

列出所有已註冊的 Webhook 端點。

POST

/webhooks

webhooks:write

註冊新的 Webhook 端點以接收事件通知。

PUT

/webhooks/:id

webhooks:write

更新現有 Webhook 端點(URL、事件或啟用狀態)。

DELETE

/webhooks/:id

webhooks:write

刪除 Webhook 端點。事件將不再推送。

欄位參考
欄位類型必填描述

id

string

唯一 Webhook 識別碼(唯讀)

name

string

端點的描述性名稱

url

string

接收 Webhook 事件的 HTTPS 端點 URL

events

string[]

要訂閱的事件類型陣列

secret

string

用於驗證負載的簽名金鑰(唯讀,建立時產生)

isActive

boolean

Webhook 是否啟用(預設:true)

createdAt

datetime

ISO 8601 時間戳(唯讀)


可用事件類型

Mercozy 支援 6 個類別共 18 種 Webhook 事件類型。只需訂閱您需要的事件。

訂單
  • order.created

    建立新訂單時觸發

  • order.updated

    訂單資訊修改時觸發

  • order.cancelled

    訂單取消時觸發

  • order.completed

    訂單標記為已完成時觸發

  • order.status_changed

    訂單狀態變更時觸發

付款
  • payment.received

    收款成功或貨到付款收款時觸發

  • payment.refunded

    退款或取消付款時觸發

商品
  • product.created

    新增商品時觸發

  • product.updated

    商品資訊變更時觸發

  • product.deleted

    刪除商品時觸發

庫存
  • stock.low

    庫存低於閾值時觸發

  • stock.updated

    庫存數量變更時觸發

客戶
  • customer.created

    新客戶註冊時觸發

  • customer.updated

    客戶資訊更新時觸發

配送
  • delivery.batch_started

    配送批次發車時觸發

  • delivery.batch_completed

    批次中所有站點完成時觸發

  • delivery.completed

    單筆配送完成時觸發

  • delivery.failed

    配送嘗試失敗時觸發


Webhook 負載

每次 Webhook 推送包含事件類型、時間戳和相關資源資料。

訂單事件資料
json
{
  "id": "evt_clx1abc2def3",
  "type": "order.created",
  "timestamp": "2026-03-19T10:30:00Z",
  "data": {
    "id": "clx9order123",
    "orderNumber": "ORD-1042",
    "status": "PENDING",
    "total": 4999,
    "currency": "USD"
  }
}
付款事件資料
json
{
  "id": "evt_clx2pay4ghi5",
  "type": "payment.received",
  "timestamp": "2026-03-19T10:35:00Z",
  "data": {
    "orderId": "clx9order123",
    "orderNumber": "ORD-1042",
    "amount": 4999,
    "currency": "USD",
    "paymentMethod": "CARD",
    "status": "CAPTURED"
  }
}
商品事件資料
json
{
  "id": "evt_clx3prod6jkl",
  "type": "product.created",
  "timestamp": "2026-03-19T11:00:00Z",
  "data": {
    "id": "clx9prod789",
    "name": "Organic Coffee Beans",
    "sku": "COF-001",
    "price": 1299,
    "status": "ACTIVE"
  }
}
配送事件資料
json
{
  "id": "evt_clx4del7mno",
  "type": "delivery.completed",
  "timestamp": "2026-03-19T14:22:00Z",
  "data": {
    "orderId": "clx9order123",
    "orderNumber": "ORD-1042"
  }
}

HTTP 標頭

每個 Webhook 請求包含以下標頭:

欄位類型必填描述

Content-Type

string

application/json

X-Webhook-Signature

string

HMAC-SHA256 簽章:sha256={十六進位摘要}

X-Webhook-Timestamp

string

Unix 毫秒時間戳記

X-Webhook-ID

string

用於去重的唯一事件 ID

User-Agent

string

Mercozy-Webhook/1.0 (+https://www.mercozy.com/docs/webhooks)


簽名驗證

每個 Webhook 請求都包含 X-Webhook-Signature 標頭,其中包含 HMAC-SHA256 簽章。在處理事件前務必驗證此簽章,確保請求來自 Mercozy。

Node.js
javascript
const crypto = require('crypto');

function verifyWebhook(rawBody, signature, timestamp, secret) {
  // Signature is computed over "timestamp.body"
  const message = timestamp + '.' + rawBody;
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(message)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express middleware
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-webhook-signature'];
  const ts = req.headers['x-webhook-timestamp'];
  if (!verifyWebhook(req.body, sig, ts, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  // Process event...
  res.status(200).json({ received: true });
});
Python
python
import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
    # Signature is computed over "timestamp.body"
    message = (timestamp + '.').encode() + payload
    expected = 'sha256=' + hmac.new(
        secret.encode(),
        message,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

# Flask example
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
    sig = request.headers.get('X-Webhook-Signature')
    ts = request.headers.get('X-Webhook-Timestamp')
    if not verify_webhook(request.data, sig, ts, WEBHOOK_SECRET):
        return 'Invalid signature', 401
    event = request.get_json()
    # Process event...
    return {'received': True}, 200
PHP
php
<?php
function verifyWebhook(string $payload, string $signature, string $timestamp, string $secret): bool {
    // Signature is computed over "timestamp.body"
    $message = $timestamp . '.' . $payload;
    $expected = 'sha256=' . hash_hmac('sha256', $message, $secret);
    return hash_equals($expected, $signature);
}

$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
if (!verifyWebhook($payload, $signature, $timestamp, $webhookSecret)) {
    http_response_code(401);
    exit('Invalid signature');
}
$event = json_decode($payload, true);
// Process event...
http_response_code(200);
echo json_encode(['received' => true]);

重試策略

如果您的端點回傳非 2xx 狀態碼或逾時(10 秒),Mercozy 將使用指數退避進行重試:

欄位類型必填描述

第 1

立即

第 2

延遲 ~1s

第 3

延遲 ~4s

3 次重試失敗後,投遞將標記為失敗。您可以透過控制面板或 API 手動重試。


最佳實踐

  • 5 秒內回應立即回傳 200 狀態碼,非同步處理事件。Mercozy 逾時時間為 10 秒。

  • 始終驗證簽章處理事件前檢查 X-Webhook-Signature 標頭,防止偽造請求。

  • 處理重複(冪等性)使用 X-Webhook-ID 標頭去重。儲存已處理的事件 ID,跳過重複事件。

  • 使用 HTTPS 端點正式環境要求使用 HTTPS URL 以保護傳輸中的資料。

  • 記錄日誌並監控記錄所有傳入的 Webhook 並監控失敗情況。使用投遞日誌 API 檢查投遞狀態。