> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fluz.app/llms.txt
> Use this file to discover all available pages before exploring further.

# 終止 Widget 工作階段

> 取消你已經交給 widget 的付款，使使用者無法再完成該筆交易。

你產生一個 `patToken`、把它交給 widget，而使用者走開了——或是你的系統在一分鐘後取消了訂單。該權杖會一直有效直到你簽署的 `exp` 到期為止，所以如果沒有撤銷的方法，使用者之後仍可能回來完成你已經不希望發生的付款。

`terminateWidgetSession` 會在伺服器端結束該工作階段。之後使用者在 widget 內的下一次操作會被拒絕，且任何針對該工作階段的撥款嘗試都會被拒絕。

<Info>
  **先決條件：** 你的應用程式 API Key（用於 Basic 認證），以及你簽發的 operator token 或其 `jti`。
</Info>

## 認證

此 mutation 使用你的「應用程式」憑證進行認證，而不是使用者的 access token——與你用於 [`generateUserAccessToken`](/get-started/api-credentials) 的 `Basic` 標頭相同：

```text theme={null}
Authorization: Basic <YOUR_API_KEY>
```

從開發者主控台原樣送出 API Key；它已經過 base64 編碼，解碼後為伺服器檢查的 `app_id:apiSecret` 配對。請使用與簽署該 operator token 的 `apiSecret` 相同應用程式所對應的金鑰。

<Warning>
  Staging 與正式環境是分開的應用程式，且有各自的憑證。使用錯誤環境的金鑰會回傳 `401`，訊息與格式錯誤的金鑰相同——完整檢查清單請參見 [If the token request returns 401](/get-started/api-credentials)。
</Warning>

你只能終止屬於你所認證之應用程式的工作階段。由其他應用程式簽發的 `jti` 不會受到你的呼叫影響——你會收到一個成功回應且 `wasActive: false`，而該應用程式的工作階段會繼續執行。

狀態為 `PERSONAL` 的應用程式不支援 Basic 認證。

## 辨識工作階段

請提供「operator token 或其 `jti`」其中之一。至少需要一個。

<Tabs>
  <Tab title="依 token（建議）">
    傳入你交給 widget 的相同 `patToken`。它會以你的應用程式密鑰進行驗證，因此不屬於你的 token 會被直接拒絕。

    已過期的 token 仍會被接受——終止一個已過期的工作階段是無害的，而且代表你無需在呼叫前自行追蹤到期。

    token 自身的 `exp` 也會界定終止紀錄被保留的時長，這也是在你仍持有 token 時，這種作法較佳的原因。
  </Tab>

  <Tab title="依 jti">
    當你已不再持有 token 時使用。`jti` 是你在簽署 `patToken` 時所產生的 UUID v4——請見 [Set Up Your Server](/developers/setting-up-your-server)。

    它也會以 `fluz_jti` 查詢參數回傳至你的 callback URL，因此即使未儲存 token，你也可以從回呼中終止工作階段。

    因為沒有 token 可讀取 `exp`，僅以 `jti` 終止時，系統會以固定期間記憶該終止，而不是精確等同於該工作階段原本可使用的時長。
  </Tab>
</Tabs>

同時提供兩者是允許的，只要它們描述的是同一個工作階段。以 token 為準，而 `jti` 會被視為對它的聲明——若 `jti` 與 token 內的值不一致，將被拒絕而非被靜默忽略，因此不會因混淆而終止錯誤的工作階段。

## 參數

* **`input`**（`TerminateWidgetSessionInput!`）：用於識別要終止的工作階段。

### TerminateWidgetSessionInput 欄位

| 欄位      | 型別       | 說明                                                     | 必填                   |
| :------ | :------- | :----------------------------------------------------- | :------------------- |
| `token` | `String` | 你交給使用者開啟 widget 的 operator token（`patToken`）。          | `token` 與 `jti` 至少擇一 |
| `jti`   | `String` | 該 token 的 `jti` 宣告。與送達你 callback URL 的 `fluz_jti` 值相同。 | `token` 與 `jti` 至少擇一 |

## 範例 mutation

```graphql theme={null}
mutation {
  terminateWidgetSession(input: { jti: "11111111-1111-4111-8111-111111111111" }) {
    jti
    wasActive
    terminatedAt
  }
}
```

## cURL 範例

```curl theme={null}
curl -X POST \
  https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Basic YOUR_API_KEY' \
  -d '{
    "query": "mutation { terminateWidgetSession(input: { jti: \"11111111-1111-4111-8111-111111111111\" }) { jti wasActive terminatedAt } }"
  }'
```

## 範例回應

```json theme={null}
{
  "data": {
    "terminateWidgetSession": {
      "jti": "11111111-1111-4111-8111-111111111111",
      "wasActive": true,
      "terminatedAt": "2026-09-01T14:32:07.412Z"
    }
  }
}
```

## 回應欄位

| 欄位             | 型別          | 說明                                                                                                  |
| :------------- | :---------- | :-------------------------------------------------------------------------------------------------- |
| `jti`          | `String!`   | 被終止的工作階段識別碼。                                                                                        |
| `wasActive`    | `Boolean!`  | 當工作階段擁有已被此呼叫撤銷的即時保留時為 `true`——代表使用者當時在 widget 內，或原本可進入。當沒有任何即時保留可撤銷時為 `false`。僅供資訊參考；無論如何工作階段都已被終止。 |
| `terminatedAt` | `DateTime!` | 記錄終止的時間。                                                                                            |

<Note>
  **`wasActive: false` 表示成功，非失敗。** 這是使用者從未開啟 widget 時的正常回應——而這也是最安全的取消時機。終止一個從未被開啟的工作階段是完全支援的，也是你在已經交付付款但想取消時的建議作法。
</Note>

## 何時會拒絕終止

終止操作具冪等性——對已被終止的工作階段再次終止也會成功。只有在以下兩種情況會被拒絕，且都代表與金流相關的問題已塵埃落定：

| 錯誤                              | 代碼           | 狀態    | 意義                                               |
| :------------------------------ | :----------- | :---- | :----------------------------------------------- |
| `WidgetSessionInProgress`       | `WIDGET-006` | `409` | 該工作階段的撥款目前正在執行中。請等待結果——不要盲目重試。無論成功或失敗，你都會收到事件通知。 |
| `WidgetSessionAlreadyCompleted` | `WIDGET-007` | `409` | 付款已經完成。沒有任何可被終止的內容。                              |

你可能看到的其他錯誤：

| 錯誤                         | 代碼           | 狀態    | 原因                                                      |
| :------------------------- | :----------- | :---- | :------------------------------------------------------ |
| `MissingParameter`         | `WIDGET-001` | `400` | 未提供 `token` 或 `jti`，或 token 缺少 `jti` 宣告。                |
| `InvalidParameter`         | `WIDGET-002` | `400` | token 無法以你的應用程式密鑰驗證、非為此應用程式簽發、或提供的 `jti` 與 token 內的不一致。 |
| `MisconfiguredApplication` | `WIDGET-003` | `400` | 該應用程式未設定為可開啟 widget 工作階段。                               |

## 使用者會看到什麼

終止會在使用者於 widget 內的「下一次導覽或重新整理」時生效。它不會直接關閉已經渲染的畫面。

當他們下一次操作時，會看到一則「Session ended」訊息，指明你的應用程式並告知他們關閉視窗並從你的產品重新開始。若他們已進入付款確認步驟，該次確認會以相同訊息被拒絕，且伺服器端會以 `WidgetSessionTerminated`（`WIDGET-005`，`410`）拒絕該撥款。

<Warning>
  僅執行終止本身「不會」停止已開始執行的撥款——此情況會回傳 `409`，你應等待完成或失敗事件，而不是假設金流已被攔停。
</Warning>

## 終止會被記住多久

被終止的工作階段，會在其原本仍可被使用的期間內被拒絕：

* 以「token 終止」——直到該 token 自身的 `exp`，且至少為一小時。
* 僅以「`jti` 終止」——為 30 天。operator token 的簽署時效並無上限，因此在沒有可讀取的 `exp` 時，會將終止紀錄保留遠超過任何可想見的工作階段壽命。

在此之後，紀錄會被丟棄。實務上 operator token 早已過期，因此無論如何該工作階段也無法再被使用。

## 後續步驟

<CardGroup cols={2}>
  <Card title="設定你的伺服器" icon="server" href="/developers/setting-up-your-server">
    產生此 mutation 需要的 `patToken` 與 `jti`。
  </Card>

  <Card title="嵌入 widget" icon="code" href="/developers/adding-the-js-widget-to-your-page">
    script 標籤、初始化呼叫、按鈕綁定。
  </Card>

  <Card title="嵌入式 Widgets 概觀" icon="book-copy" href="/developers/widgets">
    widget 工作階段、OAuth 授權與預先核准交易權杖如何協同運作。
  </Card>

  <Card title="冪等性" icon="repeat" href="/docs/idempotency-requests">
    為什麼每個移動資金的呼叫都需要唯一的 `jti`。
  </Card>
</CardGroup>
