---
type: tutorial
title: 用手动模式自己驱动验证
summary: 手动模式让你自己的代码驱动验证生命周期。它存在于两个元素上（复选框组件和游戏宿主），各有不同的活儿；这一页把两者都构建出来，并展示何时用哪一个。
---

到这篇教程结束时，你将从你自己的代码、而非内置 UI 来驱动 Caputchin 验证生命周期，在两个元素中适合你情况的那一个上。**手动模式**（`trigger="manual"`）存在于两个不同的元素上，而它们干的活儿确实不同：

| 元素 | 手动模式给你什么 | 你驱动 |
|---|---|---|
| `<caputchin-widget>` | 内置复选框被隐藏；你从你 **自己的触发**（一次表单提交、一个自定义按钮）触发 proof-of-work 求解。无游戏。 | `start()`（然后求解通常自行结清） |
| `<caputchin-game>` | 无 iframe；你把 **你自己的游戏标记** 嵌进组件的布局外壳，并从玩法决定结果。 | `pass()` / `fail()`（无 `start()`；回合一挂载即上线） |

按你要回答的问题来选：

- *“我想要正常的隐形/复选框验证，但由我自己的手势触发，且不带默认复选框。”* → 下面的 **复选框组件**。
- *“我想要我自己的交互式挑战渲染在我自己的 DOM 里，而非一个 iframe 游戏，且由我决定通过/失败。”* → 下面的 **游戏宿主**。

游戏路径适配在哪里，见 [跑你自己的游戏](/docs/custom-game-development/overview)。你的页面上需要已经有组件；如果还没有，就先去 [添加组件](/docs/getting-started/add-the-widget)。

两者共有的一个局限：一个手动模式的回合 **无法满足一个 [游戏关卡](/docs/site-keys/games)**。复选框元素根本从不给一个密钥设关卡（只有 `<caputchin-game>` 能），而一个手动的 `<caputchin-game>` 回合没有可回放的轨迹，所以一个设了关卡的密钥拒绝它。手动模式是给未设关卡的密钥（proof-of-work 验证）或纯游戏用途的。

## 路径 A：复选框组件，你自己的触发

当你想要标准验证、但想自己触发它时用这个，例如只在访客提交一个表单时跑求解，或者放在你自己的按钮后面而非内置复选框。

### 1. 把元素标为手动

```html
<caputchin-widget id="cap" sitekey="cpt_pub_..." trigger="manual"></caputchin-widget>
<button id="go" type="button">Verify and continue</button>
```

有了 `trigger="manual"`，内置复选框 UI 被隐藏；元素等你来开始求解。

### 2. 从你的手势开始求解

手动模式下的复选框元素暴露单一一个方法，`start()`：

- `start()` 踢动 proof-of-work 求解，取代复选框点击。在 `trigger="manual"` 下有效。

复选框组件没有 `pass()` / `fail()`；你一旦调用 `start()`，求解就自行结清并发出 `pass` 事件。（释放/中止把手在游戏宿主上，在路径 B 里讲到。）

```js
const widget = document.getElementById("cap");

document.getElementById("go").addEventListener("click", () => {
  widget.start(); // begin verification now
});

widget.addEventListener("pass", () => {
  // token issued; submit your form / continue
});
```

当求解结清时，元素完成验证，并（在一个表单里）像任何其他组件那样注入 `caputchin-token` 字段。你的后端 [核验那个令牌](/docs/integration-guides/verify-on-your-backend) 一如往常；手动模式在服务端什么都不改。

## 路径 B：游戏宿主，你自己的 DOM

当你想把你自己的交互式挑战渲染在你自己的标记里、而非加载一个 iframe 游戏，并自己决定结果时用这个。

### 1. 把元素标为手动并嵌入你的标记

在 `<caputchin-game>` 上放 `trigger="manual"`，并把你自己的标记放进它里面。在其他每个模式里组件都忽略 light-DOM 子元素；手动模式是它通过一个 `<slot>` 把它们投影进它布局外壳的那唯一一个模式。

```html
<caputchin-game id="cap" sitekey="cpt_pub_..." trigger="manual">
  <!-- Your own markup. The widget renders it inside its shell. -->
  <div id="my-game">
    <p>Tap the banana three times.</p>
    <button id="banana" type="button">🍌</button>
  </div>
</caputchin-game>
```

在有一个 sitekey 存在、且密钥未设关卡时，组件在后台跑它的 proof-of-work 检查；你的交互是看得见的那部分。在没有 sitekey（或 `no-verify`）时，它是纯游戏：你的交互跑起来，没有什么可验证。

### 2. 从你的代码驱动结果

手动模式下的游戏宿主暴露 `pass()` 和 `fail()`。**没有 `start()`**：回合在元素一挂载时就上线（工厂被挂载就是那个开始信号）。

- `pass(payload?)` 释放验证：访客成功了。调用它一次；之后的调用被忽略（裁定已锁定）。
- `fail(payload?)` 中止回合。可选；一个未完成的回合无论如何都被当作非通过。

那些载荷：

- `pass({ trace })`：`trace` 是你的交互产出的那条不透明的回合记录。在一个设了关卡的回合上，服务器重放它以得裁定；在一个未设关卡或纯游戏的密钥上，它被原样接受，所以当没有什么可回放时传一个空字符串（或一个简单的标记）。这里没有客户端提供的分数。
- `fail({ code?, message? })`：一个可选的诊断 `code` 和 `message`，两者都是客户端元数据，绝非一个信任信号。

```js
const widget = document.getElementById("cap");
let taps = 0;

document.getElementById("banana").addEventListener("click", () => {
  taps += 1;
  if (taps >= 3) {
    widget.pass({ trace: `banana:${taps}` }); // visitor won
  }
});

// Optional: give up explicitly.
// widget.fail({ code: "gave-up", message: "closed the prompt" });
```

和复选框路径一样，`pass()` 完成验证并注入令牌；你的后端以同样的方式核验它。

## 对结果作出反应（两条路径）

监听组件的事件来更新你自己的 UI：

```js
widget.addEventListener("pass", () => {
  // verification released
});
widget.addEventListener("error", (e) => {
  console.warn("verification error", e.detail);
});
```

那个信任决定始终是你的后端确认令牌，而非那个事件；事件是给 UX 的。

## 手动模式做不到什么

- **给一个站点密钥设关卡。** 一个打开了 [要求一个游戏](/docs/site-keys/games) 的密钥根本无法被复选框元素满足，而且拒绝一个手动的 `<caputchin-game>`（它在搭建时抛出一个配置错误），因为没有可回放的轨迹。手动模式是给未设关卡或纯游戏的密钥的。
- **重放一个自定义游戏的回合。** 如果你需要一个设关卡的游戏，就把它渲染成一个 [自托管 iframe 游戏](/docs/custom-game-development/self-hosted-game) 并产出一条轨迹，而不是手动模式。

## 另见

- [组件方法与事件](/docs/references/widget-methods-and-events)：`start()` / `pass()` / `fail()` 以及它们发出的每个事件的完整参考。
- [自托管 iframe 游戏](/docs/custom-game-development/self-hosted-game)：一个能设关卡的、沙箱化的、可回放的游戏。
- [跑你自己的游戏](/docs/custom-game-development/overview)：两种交付模式的对比。
- [在你的后端核验](/docs/integration-guides/verify-on-your-backend)：确认一次通过产出的那个令牌。
