> ## 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.

# 终止一个小部件会话

> 取消你已经交给小部件处理的支付，这样用户将无法再完成它。

你生成了一个 `patToken`，交给小部件，结果用户走开了——或者你的系统在一分钟后取消了订单。该令牌会一直有效，直到你签发的 `exp` 到期，因此如果没有撤销的方法，用户之后可能回来并完成一个你不再需要的支付。

`terminateWidgetSession` 在服务端结束会话。下次用户在小部件内进行操作时会被拒绝，且任何针对该会话的付款尝试都会被拒绝。

<Info>
  **先决条件：** 你的应用程序用于 Basic Auth 的 API Key，以及你签发的操作员令牌或其 `jti`。
</Info>

## 认证

此变更使用你**应用程序**的凭据进行认证，而不是用户访问令牌——与您用于 [`generateUserAccessToken`](/get-started/api-credentials) 的 `Basic` 头相同：

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

从开发者控制台原样发送该 API Key；它已进行 base64 编码，并会解码为服务器校验的 `app_id:apiSecret` 对。请使用与签发该操作员令牌的 `apiSecret` 属于同一应用的密钥。

<Warning>
  Staging 和 live 是相互独立的应用与凭据。来自错误环境的密钥会返回 `401`，其消息与格式错误的密钥相同——完整检查清单参见 [If the token request returns 401](/get-started/api-credentials)。
</Warning>

你只能终止与你进行认证的应用所属的会话。由其他应用签发的 `jti` 不会受到你的调用影响——你将获得一个成功响应，其中 `wasActive: false`，而该应用的会话会继续运行。

Basic 认证不适用于状态为 `PERSONAL` 的应用。

## 标识会话

提供**操作员令牌或其 `jti` 二选一**。至少需要一个。

<Tabs>
  <Tab title="按令牌（首选）">
    传入你交给小部件的同一个 `patToken`。它会用你应用的密钥进行验证，因此不属于你的令牌会被直接拒绝。

    已过期的令牌仍会被接受——终止一个已过期的会话是无害的，这也意味着你在调用前无需自己跟踪过期时间。

    令牌自身的 `exp` 也会限定终止被记忆的时长，这就是在你仍持有令牌时这是更佳选项的原因。
  </Tab>

  <Tab title="按 jti">
    当你不再持有令牌时使用此方式。`jti` 是你在签发 `patToken` 时生成的 UUID v4——参见 [Set Up Your Server](/developers/setting-up-your-server)。

    它也会作为 `fluz_jti` 查询参数传回你的回调 URL，因此即便你未存储令牌，也可在回调中终止会话。

    因为没有令牌可读取 `exp`，仅凭 `jti` 的终止会被记忆为固定时长，而非恰好等同于该会话本可被使用的时长。
  </Tab>
</Tabs>

只要两者描述的是同一会话，同时提供也是允许的。若同时提供，以令牌为准，`jti` 被视为对其的断言——与令牌自身不一致的 `jti` 会被拒绝，而不是被静默忽略，从而避免混淆导致终止错误的会话。

## 参数

* **`input`**（`TerminateWidgetSessionInput!`）：标识要终止的会话。

### TerminateWidgetSessionInput 字段

| 字段      | 类型       | 描述                                        | 必填                   |
| :------ | :------- | :---------------------------------------- | :------------------- |
| `token` | `String` | 你交给用户用于打开小部件的操作员令牌（`patToken`）。           | `token` 或 `jti` 至少其一 |
| `jti`   | `String` | 该令牌的 `jti` 声明。与你回调 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`——用户在小部件内，或本可以在小部件内。为 `false` 则表示没有可撤销的有效保留。仅供参考；无论如何会话都会被终止。 |
| `terminatedAt` | `DateTime!` | 记录终止的时间。                                                                                   |

<Note>
  **`wasActive: false` 表示成功，而非失败。** 当用户从未打开过小部件时，这是常见答案——这也是取消的最安全时机。终止一个从未被打开的会话是完全支持的，并且是撤销你已交付的支付的推荐方式。
</Note>

## 何时会拒绝终止

终止是幂等的——再次终止已被终止的会话也会成功。仅在两种情况下会被拒绝，这两种情况都意味着资金问题已定：

| 错误                              | 代码           | 状态    | 含义                                         |
| :------------------------------ | :----------- | :---- | :----------------------------------------- |
| `WidgetSessionInProgress`       | `WIDGET-006` | `409` | 会话的付款正在执行中。请等待结果——不要盲目重试。无论如何你都会收到完成或失败事件。 |
| `WidgetSessionAlreadyCompleted` | `WIDGET-007` | `409` | 付款已完成。无事可终止。                               |

你可能看到的其他错误：

| 错误                         | 代码           | 状态    | 原因                                          |
| :------------------------- | :----------- | :---- | :------------------------------------------ |
| `MissingParameter`         | `WIDGET-001` | `400` | 未提供 `token` 或 `jti`，或该令牌没有 `jti` 声明。        |
| `InvalidParameter`         | `WIDGET-002` | `400` | 令牌无法用你应用的密钥验证、不是为该应用签发、或提供的 `jti` 与令牌自身不一致。 |
| `MisconfiguredApplication` | `WIDGET-003` | `400` | 该应用未配置为可打开小部件会话。                            |

## 用户所见

终止会在用户下一次在小部件内进行**导航或刷新**时生效。它不会关闭已渲染的界面。

当用户下一次操作时，会看到“Session ended”的消息，注明你的应用并告知他们关闭窗口并从你的产品重新开始。若他们已经进入确认支付的步骤，该确认会以同样的消息被拒绝，且该会话的付款会在服务端以 `WidgetSessionTerminated`（`WIDGET-005`，`410`）被拒绝。

<Warning>
  仅靠终止本身并**不能**阻止已开始执行的付款——这种情况会返回 `409`，你应等待完成或失败事件，而不是假定资金被拦截。
</Warning>

## 终止会被记忆多久

被终止的会话会在其本可被使用的时长内被拒绝：

* **按令牌终止**——直到令牌自身的 `exp`，且至少为一小时。
* **仅按 `jti` 终止**——为 30 天。操作员令牌的签发时长没有上限，因此在没有可读取的 `exp` 时，会将终止记录保留远超任何可行的会话生命周期。

在该时间窗口之后，记录会被丢弃。实际上此时操作员令牌早已过期，因此该会话无论如何都无法被使用。

## 后续步骤

<CardGroup cols={2}>
  <Card title="设置你的服务器" icon="server" href="/developers/setting-up-your-server">
    生成本变更所需的 `patToken` 和 `jti`。
  </Card>

  <Card title="嵌入小部件" icon="code" href="/developers/adding-the-js-widget-to-your-page">
    Script 标签、初始化调用、按钮绑定。
  </Card>

  <Card title="嵌入式小部件概览" icon="book-copy" href="/developers/widgets">
    小部件会话、OAuth 授权与预先批准的交易令牌如何协同工作。
  </Card>

  <Card title="幂等性" icon="repeat" href="/docs/idempotency-requests">
    为什么每个资金流动的调用都需要唯一的 `jti`。
  </Card>
</CardGroup>
