跳轉到

裝置、PIN 與解鎖

Device read contract

Payment DTO 不含 udidagent_id、WDA URL 等 ops 路由欄位。

GET /v1/devices

curl -sS "$FARM_BASE/v1/devices" \
  -H "Authorization: Bearer $FARM_API_KEY"
{
  "devices": [
    {
      "device_id": "TH-XXXX",
      "device_code": "TH-XXXX",
      "label": "desk-placeholder",
      "device_type": "ios",
      "online": true,
      "connection": "usb",
      "wda_status": "up",
      "locked": false,
      "lock_reason": null,
      "locked_at": null,
      "banks": [
        {"bank_code": "SCB", "pin_configured": true, "variant": "standard", "updated_at": 0}
      ]
    }
  ]
}

GET /v1/devices/{device_id}

單筆同上 DTO。跨 tenant/不存在 → 404

欄位 說明
online / connection 依 agent heartbeat(TTL 內)
wda_status up / down / unknown
locked / lock_reason / locked_at 鎖卡狀態;locked_at 即 unlock 用的 generation
banks[] bank_codepin_configuredvariantupdated_at

PIN 更新

PUT /v1/devices/{device_code}/banks/{bank_code}/secret
Authorization: Bearer $FARM_API_KEY

欄位(BankSecretIn

欄位 必填 說明
pin 恰 6 位數字。否則 422
variant 可選腳本變體(如 standard)。省略 = 不改既有綁定
curl -sS -X PUT "$FARM_BASE/v1/devices/TH-XXXX/banks/SCB/secret" \
  -H "Authorization: Bearer $FARM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pin":"000000","variant":"standard"}'
{
  "device_code": "TH-XXXX",
  "bank_code": "SCB",
  "pin_configured": true,
  "variant": "standard",
  "updated_at": 0
}

PIN 機密

  • 進 log、進 audit、 echo 回 response
  • Farm 沒有任何讀回明文 PIN 的 API
  • bank/country 必須在主資料 enabled,否則 422
  • script catalog 不可用 → 503;variant 未部署 → 422

解綁銀行

DELETE /v1/devices/{device_code}/banks/{bank_code}
結果 HTTP
刪除成功 204(無 body)
無此 binding/跨 tenant/裝置不存在 404
curl -sS -o /dev/null -w "%{http_code}\n" -X DELETE \
  "$FARM_BASE/v1/devices/TH-XXXX/banks/SCB" \
  -H "Authorization: Bearer $FARM_API_KEY"

解鎖 FLOW

sequenceDiagram
  participant P as PAYMENT
  participant F as Farm
  P->>F: GET /v1/devices/{id}
  F-->>P: locked_at generation
  P->>F: PUT bank secret(修正 PIN)
  P->>F: POST unlock(expected_locked_at)
  alt generation 相符或已未鎖
    F-->>P: 200
    F-->>P: device.unlocked(outbox 重試至 2xx)
  else generation 過期
    F-->>P: 409 stale_lock_generation
    P->>F: 重新 GET 後決定
  end

POST /v1/devices/{device_code}/unlock

欄位 必填 說明
expected_locked_at 來自 GET 的 locked_at(float)。缺欄 → 422
note 可選;只進 audit 與 device.unlocked 通知
curl -sS -X POST "$FARM_BASE/v1/devices/TH-XXXX/unlock" \
  -H "Authorization: Bearer $FARM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"expected_locked_at":1754712345.6,"note":"PIN fixed placeholder"}'

Generation matrix

裝置狀態 結果
目前未鎖 200was_locked=false(重送安全;意圖已達成)
已鎖且 locked_at == expected_locked_at 200was_locked=true,清 lock 欄位
已鎖且 generation 不符 409 detail.error=stale_lock_generation,帶目前 lock_reason/locked_at不解鎖
跨 tenant/不存在 404
{"device_code":"TH-XXXX","was_locked":true,"locked_at":null,"lock_reason":null}

不解鎖不重開排程

Unlock 只清 lock 欄位,不碰 schedule_enabled。 Ops 刻意停用的裝置不會因上游解鎖而自動恢復排程。

Unlock 不做「PIN 是否真的修好」的前置探測;若仍錯,下一次任務會再鎖。

Timeout/callback 遺失

  • device.locked / device.unlockedDB outbox,重試至 2xx
  • 仍建議以 GET devicelockedlocked_at 為權威。