跳轉到

裝置註冊與綁定

PAYMENT 先建立短效 enrollment token;現場人員在 FarmAgent 輸入 token 完成綁定。 Token 明文只在 POST /v1/enrollments 回應出現一次;不得當 URL 熵值依賴。

新裝置註冊(device_create)

sequenceDiagram
  participant P as PAYMENT
  participant F as Farm
  participant A as FarmAgent/Field
  P->>F: POST /v1/enrollments(不帶 device_code)
  F-->>P: 201 enrollment_id + device_code + token(只一次)
  Note over P: 保存 device_code;token 交現場
  A->>F: POST /v1/enrollments/redeem(token 即憑證)
  F-->>A: 200 bound
  F-->>P: webhook device.bound(best-effort)
  P->>F: GET /v1/enrollments/{id}(callback 遺失時)
  F-->>P: state=bound, device_code
  Note over P: 權威保存 device_code

既有裝置加銀行(bank_add)

sequenceDiagram
  participant P as PAYMENT
  participant F as Farm
  participant A as FarmAgent/Field
  P->>F: POST /v1/enrollments(帶既有 device_code)
  F-->>P: 201 token + mode=bank_add
  A->>F: POST /v1/enrollments/redeem
  F-->>A: 200 bound(sealed PIN 寫入 bank binding)
  F-->>P: webhook bank.bound(best-effort)
  P->>F: GET enrollment + GET device
  F-->>P: bank + pin_configured 確認

POST /v1/enrollments

POST /v1/enrollments
Authorization: Bearer $FARM_API_KEY
Content-Type: application/json

完整 request 欄位(EnrollIn

欄位 必填 說明
region bank_code 須命中 banks 主資料且 enabled;正規化為大寫。缺 → 422
bank_code 銀行代碼(如 SCB)。空 → 422
bank_payload object,預設 {}。典型含 pin(六位);密文暫存,永不出現在 callback
device_code 省略 → 新機 mode=device_create(Farm 配置 device_code);有值 → 既有機 mode=bank_add
label 可選標籤
allowed_apps 字串陣列,預設 []
ttl_seconds 預設 1800;合法範圍 1–7200。超出 → 400
device_type 可選 ios / android(小寫)。僅 device_create 寫入 devices,之後不可改。非法 → 422

最小 request(新機)

curl -sS -X POST "$FARM_BASE/v1/enrollments" \
  -H "Authorization: Bearer $FARM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "region": "TH",
    "bank_code": "SCB",
    "bank_payload": {"pin": "000000"},
    "label": "desk-placeholder",
    "ttl_seconds": 1800
  }'

最小 request(既有機加銀行)

{
  "region": "TH",
  "bank_code": "KTB",
  "bank_payload": {"pin": "000000"},
  "device_code": "TH-XXXX"
}

成功 response(201)

{
  "enrollment_id": "enr_…",
  "device_code": "TH-XXXX",
  "device_id": "TH-XXXX",
  "token": "A3K9MP",
  "mode": "device_create",
  "region": "TH",
  "bank_code": "SCB",
  "status": "pending",
  "expires_at": 0
}

Token 規則:6 字元,字集 23456789ABCDEFGHJKMNPQRSTUVWXYZ(無 0/O/1/I/L)。 Redeem 會正規化為大寫。安全依賴短 TTL + 單次使用 + 現場實體配對,不是 URL 熵。

常見錯誤

狀況 HTTP
ttl_seconds 超出 1..7200 400
region/bank 未啟用 422
device_type 非 ios/android 422
bank_add 但 device_code 非本 tenant 404
secrets key 未設定 503

GET /v1/enrollments/{enrollment_id}

Callback 遺失時的權威狀態。跨 tenant → 404。

{
  "enrollment_id": "enr_…",
  "tenant_id": "…",
  "device_id": "TH-XXXX",
  "device_code": "TH-XXXX",
  "mode": "device_create",
  "region": "TH",
  "bank_code": "SCB",
  "device_type": "ios",
  "state": "bound",
  "udid_bound": true,
  "label": "desk-placeholder",
  "allowed_apps": [],
  "created_at": 0,
  "expires_at": 0
}

POST /v1/enrollments/redeem(Field/FarmAgent)

PAYMENT 不直接呼叫

此端點以 token 為憑證(無 Bearer)。文件僅說明完整 FLOW。 正式現場路徑經 FarmAgent UI/job;勿在 PAYMENT 後端持有 redeem 邏輯或假造 Bearer 範例。

請求形狀(供理解 FLOW):

{ "token": "A3K9MP", "udid": null, "install_id": null }

成功 → status=bound,並觸發 device.boundbank.bound。 無效/過期 token → 404;無可用裝置 → 409;猜測過頻 → 429。

Webhook:device.bound / bank.bound

{
  "event": "device.bound",
  "idempotency_key": "enr_…",
  "enrollment_id": "enr_…",
  "device_id": "TH-XXXX",
  "device_code": "TH-XXXX",
  "bank_code": "SCB",
  "region": "TH",
  "tenant_id": "…",
  "label": "desk-placeholder",
  "allowed_apps": []
}
  • mode=bank_addeventbank.bound
  • 永不帶 PIN
  • 投遞:best-effort,無持久化重送。失敗時以 GET /v1/enrollments/{id} 恢復。
  • 接收端去重鍵:enrollment_id(同 idempotency_key)。

PAYMENT 必須保存

欄位 用途
enrollment_id GET fallback、與 bound event 對帳
device_code 後續 devices/tasks 唯一公開裝置鍵
mode / bank_code 區分新機 vs 加銀行

是否可重試

情況 決策
POST enrollments 網路超時、未拿到 body 可重送 enrollment(會發新 token/可能新 device_code);或先依業務側記錄查
已拿到 enrollment_id、等 bound 不要重發 enrollment;輪詢 GET enrollment
redeem 由現場處理 PAYMENT 不重試 redeem