---
type: tutorial
title: Добавь виджет на свой сайт
summary: Добавь виджет в свою React-контактную форму, подтверди настоящую проверку в панели, затем добейся её соблюдения на своём Node-бэкенде.
---

У тебя есть публичный ключ BananaSeed из [предыдущего урока](/docs/getting-started/create-your-account). Теперь мы поместим виджет на React-контактную форму, увидим, как панель подтверждает, что её прошёл настоящий посетитель, и затем добавим один серверный вызов, который превращает это в настоящую защиту.

## Что у тебя будет в конце

- Виджет на твоей контактной форме, проверяющий посетителей в браузере.
- Твоя панель, показывающая эти проверки.
- Твой 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-странице без шага сборки или внутри нативного мобильного приложения? Смотри [интеграцию через тег скрипта (CDN)](/docs/integration-guides/cdn) и [руководство по мобильным приложениям](/docs/integration-guides/mobile-apps).

## 2. Посмотри, как идёт проверка

Запусти приложение и открой контактную форму. Галочка Caputchin появляется и завершается сама, без всякого клика. Тебе даже не нужно пока отправлять форму: это уже настоящая проверка, и она дошла до Caputchin.

## 3. Подтверди это в своей панели

Вернувшись в панель, открой свою команду и зайди в свой ключ сайта. Его статистика показывает воронку: **Сессии начаты**, **Пройдено в браузере**, **Проверено на сервере** и **Угрозы заблокированы**. Обнови страницу; **Сессии начаты** и **Пройдено в браузере** теперь обе показывают как минимум `1`. Это твой визит: виджет загрузился (начато), и испытание было решено в браузере (пройдено в браузере), вообще без участия бэкенда.

**Проверено на сервере** всё ещё `0`, потому что ничего на твоём сервере пока не проверяло токен. Этот разрыв и есть следующий шаг.

<Callout type="success">Твой виджет работает, и панель уже логирует настоящие проверки, до того как ты написал хоть строку кода бэкенда. 🎉</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` и есть суть: бот, шлющий POST прямо на `/api/contact`, не несёт токена, `success` возвращается `false`, и сообщение отбрасывается ещё до того, как ты его коснёшься.

Нет бэкенда? Не беда. Включи [размещённую проверку](/docs/hosted-verification/overview), и Caputchin выполнит эту проверку за тебя, пересылая только проверенные отправки на вебхук или в твой почтовый ящик.

## 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 выполнит эту проверку за тебя.

### Почему «Проверено на сервере» всё ещё ноль после того, как виджет загрузился?

Потому что ничто на твоём сервере ещё не проверило токен. «Начато» и «Пройдено в браузере» тикают из одного браузера; «Проверено на сервере» растёт только тогда, когда твой бэкенд вызывает `/siteverify`, а это [шаг 4](#4-проверь-токен-на-своём-бэкенде).

### Работает ли виджет за пределами React?

Да. Это стандартный пользовательский элемент, так что он встаёт в чистый HTML, Vue, Svelte, Angular или WebView нативного приложения тем же одним тегом. Смотри [интеграцию на стороне клиента](/docs/integration-guides/cdn) и [руководство по мобильным приложениям](/docs/integration-guides/mobile-apps).

### Куда девается мой секретный ключ?

Только на твой сервер, в переменную окружения (`cpt_sec_...`). Он аутентифицирует вызов `/siteverify` и никогда не должен попадать в браузер.

## Дальше

Галочка работает. Теперь самое весёлое: поменяй её на настоящую играбельную игру, без изменения твоего бэкенда.

Переходи к [Добавь игру из маркетплейса](/docs/getting-started/add-a-game).
