---
type: tutorial
title: サイトにウィジェットを追加する
summary: React の問い合わせフォームにウィジェットを追加し、ダッシュボードで本物の検証を確認してから、Node バックエンドでそれを強制します。
---

[前のレッスン](/docs/getting-started/create-your-account) で BananaSeed の公開鍵を手に入れました。次は React の問い合わせフォームにウィジェットを置き、本物の訪問者がそれをクリアしたことをダッシュボードが確認するのを見届け、そしてそれを本物の保護に変える 1 つのサーバー呼び出しを追加します。

## 最後にあなたが手にするもの

- 問い合わせフォームの上のウィジェットが、ブラウザで訪問者を検証している状態。
- ダッシュボードがそれらの検証を表示している状態。
- 有効なトークンを持たないあらゆる送信を、Node バックエンドが捨てている状態。

## 1. React フォームにウィジェットを追加する

パッケージをインストールします。これは `<caputchin-widget>` 要素（と、次のレッスン用の `<caputchin-game>` 要素）を登録します：

```bash
npm install @caputchin/widget
```

起動時に一度、たとえばアプリのエントリーポイント（`main.jsx`）でインポートします：

```js
import "@caputchin/widget";
```

では、ウィジェットを BananaSeed の問い合わせフォームに置きましょう。チェックボックスがクリアされると、ウィジェットはフォームに隠しの `caputchin-token` フィールドを追加するので、送信時にあなた自身のフィールドとまったく同じように読み取れます。`cpt_pub_...` をあなたの公開鍵に置き換えてください：

```jsx
export function ContactForm() {
  async function handleSubmit(e) {
    e.preventDefault();
    const data = new FormData(e.currentTarget);
    await fetch("/api/contact", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        email: data.get("email"),
        message: data.get("message"),
        token: data.get("caputchin-token"),
      }),
    });
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <caputchin-widget sitekey="cpt_pub_..." />
      <button type="submit">Send</button>
    </form>
  );
}
```

ビルド手順のない素の HTML ページ上、あるいはネイティブのモバイルアプリの中で使う場合は？[script タグ（CDN）連携](/docs/integration-guides/cdn) と [モバイルアプリガイド](/docs/integration-guides/mobile-apps) を参照してください。

## 2. 検証されるのを見る

アプリを実行して問い合わせフォームを開きます。Caputchin のチェックボックスが現れ、クリックも要らず、ひとりでに完了します。フォームをまだ送信する必要すらありません。それはすでに本物の検証であり、Caputchin に届いています。

## 3. ダッシュボードで確認する

ダッシュボードに戻り、チームを開いてサイトキーへ入ります。その統計はファネルを示します：**セッション開始**、**クライアント側で完了**、**サーバー側で検証済み**、そして **脅威をブロック**。ページを更新すると、**セッション開始** と **クライアント側で完了** が両方とも少なくとも `1` になります。それがあなたの訪問です。ウィジェットが読み込まれ（開始）、チャレンジがブラウザで解かれた（クライアント側で完了）ので、バックエンドはまったく関与していません。

**サーバー側で検証済み** はまだ `0` です。あなたのサーバー上のものがまだトークンを 1 つも確認していないからです。そのギャップが次のステップです。

<Callout type="success">ウィジェットはライブで、ダッシュボードはバックエンドのコードを 1 行も書く前から、すでに本物の検証を記録しています。🎉</Callout>

## 4. バックエンドでトークンを検証する

クライアント側で完了は、訪問者がチャレンジをクリアしたことを意味しますが、サーバーがトークンを確認するまで、あなたのフォームは保護されていません。ボットはウィジェットを完全に無視して、エンドポイントへ直接 POST できます。だからこそ、バックエンドはリクエストを信頼する前に、すべてのトークンを Caputchin で確認します。あなたのシークレット（`cpt_sec_...`）は環境変数に住み、決してブラウザにはありません：

```js
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/contact", async (req, res) => {
  const { email, message, token } = req.body;

  const verdict = await fetch("https://verify.caputchin.com/v1/siteverify", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      secret: process.env.CAPUTCHIN_SECRET,
      response: token,
    }),
  }).then((r) => r.json());

  if (!verdict.success) {
    return res.status(400).json({ error: "Could not verify you are human." });
  }

  // Verified. Handle the real message: store it, email it, whatever you do.
  res.json({ ok: true });
});

app.listen(3001);
```

その `verdict.success` の確認こそが要点です。`/api/contact` へ直接ポストするボットはトークンを持たず、`success` は `false` で返り、メッセージはあなたが触れる前に捨てられます。

バックエンドがない？問題ありません。[ホスト型認証](/docs/hosted-verification/overview) をオンにすれば、Caputchin がこの確認をあなたの代わりに実行し、検証済みの送信だけを webhook か受信トレイへ転送します。

## 5. サーバーの確認を検証する

エンドポイントが検証するようになった今、もう一度だけ問い合わせフォームを送信します。サイトキーの統計に戻って更新すると：**サーバー側で検証済み** も今や少なくとも `1` になり、開始とクライアント側で完了の隣に並びます。ブラウザからサーバーまで、ファネル全体が説明できます。

そのファネルを逆向きに読む方法（カウント間のギャップが、訪問者の離脱や連携ミスについて何を語るか）については、[統計とセッション](/docs/site-keys/statistics) を参照してください。

<Callout type="success">問い合わせフォームは今や本当に保護されています。すべての送信がサーバーで確認され、トークンを持たないボットは追い返されます。🎉</Callout>

## たった今起きたこと

| ステップ | 場所 | 結果 |
|---|---|---|
| フォームの `<caputchin-widget>` | React | チェックボックスがマウント時に検証し、一度きりの `caputchin-token` フィールドを追加します。 |
| ページを開く | ブラウザ | 開始とクライアント側で完了が進み、バックエンドは要りません。 |
| シークレット付きの `/siteverify` | Node から Caputchin | `success: true` がリクエストを通します。トークンがなければ捨てられ、サーバー側で検証済みが伸びます。 |

## よくある質問

### ウィジェットを使うのにバックエンドは必要ですか？

ブラウザのステップには不要です。本当の保護には必要です。ウィジェットはブラウザの中で自分だけで検証しますが、ボットはそれを無視してあなたのエンドポイントへ直接 POST できるので、送信を信頼する前にサーバーが `caputchin-token` を確認しなければなりません。バックエンドを動かさないなら、[ホスト型認証](/docs/hosted-verification/overview) をオンにすれば、Caputchin がその確認をあなたの代わりに行います。

### ウィジェットが読み込まれた後も、なぜサーバー側で検証済みがゼロのままなのですか？

あなたのサーバーがまだトークンを 1 つも確認していないからです。開始とクライアント側で完了はブラウザだけで伸びます。サーバー側で検証済みは、あなたのバックエンドが `/siteverify` を呼んだとき、つまり [ステップ 4](#4-バックエンドでトークンを検証する) で初めて伸びます。

### ウィジェットは React の外でも動きますか？

はい。標準のカスタム要素なので、同じ 1 つのタグで、素の HTML、Vue、Svelte、Angular、ネイティブアプリの WebView に収まります。[クライアント側の連携](/docs/integration-guides/cdn) と [モバイルアプリガイド](/docs/integration-guides/mobile-apps) を参照してください。

### 私のシークレットキーはどこへ置きますか？

あなたのサーバーだけ、環境変数（`cpt_sec_...`）の中です。それは `/siteverify` の呼び出しを認証するもので、決してブラウザへ出荷してはいけません。

## 次へ

チェックボックスが動きます。次は楽しいところ：それを本物の遊べるゲームに、バックエンドを一切変えずに差し替えます。

[マーケットプレイスからゲームを追加する](/docs/getting-started/add-a-game) へ進みましょう。
