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

# Подтверждение владения кошельком - виджет Travel Rule

> Виджет Didit подтверждает контроль клиента над криптокошельком: подписью сообщения, Satoshi test, скриншотом или самодекларацией. Для Travel Rule.

Виджет подтверждения владения кошельком - это размещённая страница, которая подтверждает, что ваш клиент контролирует конкретный адрес криптокошелька. По правилам EU TFR VASP должен проверить владение некастодиальным кошельком до того, как перевести средства на него или с него сверх порога sunrise. Виджет позволяет Didit собрать это подтверждение без того, чтобы вы сами создавали процессы подписания.

Проверенное подтверждение помечает соответствующую [запись в адресной книге кошельков](https://docs.didit.me/ru/transaction-monitoring/travel-rule.md#адресная-книга-кошельков) как `is_ownership_verified: true`, что позволяет связанному [переводу по Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule.md) перейти из `UNCONFIRMED_OWNERSHIP`.

## Когда Didit создаёт её для вас

Обычно вам не нужно создавать сессию самостоятельно.
Если в ваших [настройках Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule.md#настройка) включён параметр `auto_wallet_verification` (по умолчанию он включён), Didit автоматически создаёт сессию виджета при создании транзакции, когда для перевода нужно подтверждение контроля кошелька со стороны конечного пользователя:

* Перевод находится в статусе `UNCONFIRMED_OWNERSHIP` - кошелёк контрагента был [заявлен как некастодиальный](https://docs.didit.me/ru/transaction-monitoring/travel-rule.md#объявленные-некастодиальные-кошельки) при отправке.
* Перевод исходящий и находится в статусе `COUNTERPARTY_VASP_NOT_FOUND` - VASP контрагента не удалось определить, поэтому подтверждение владения является единственным оставшимся путём к завершению.

Автоматически созданная сессия возвращается в блоке `action_required` транзакции (`type: "wallet_ownership"`, с `url` для открытия), а SDK запускают её автоматически при [отправке на стороне клиента](https://docs.didit.me/ru/transaction-monitoring/sdk-transaction-submission.md#требуемые-действия-пользователя).
Входящие переводы, которые Didit создаёт для вас (регистрация sunrise, входящий запрос `TRP` или входящий этап по каналу `INTERNAL`), никогда не получают автоматически созданную сессию, даже если они попадают в `UNCONFIRMED_OWNERSHIP`.
Создавайте сессию вручную с помощью `POST /v3/travel-rule/widget-session/`, если хотите заранее подтвердить кошелёк до появления какого-либо перевода, если параметр `auto_wallet_verification` у вас выключен или для одного из таких входящих переводов.

## Что это подтверждает

Виджет предлагает до четырёх способов подтверждения. Какие из них появятся, зависит от того, как была создана сессия, и от ваших [настроек Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule.md#настройка):

| Способ | Как это работает | Когда предлагается |
| - | - | - |
| `MESSAGE_SIGNING` | Клиент подписывает строку-челлендж, выданную Didit, своим кошельком. Поддерживаются кошельки EVM, Solana, Bitcoin и Tron (для Bitcoin: адреса legacy, nested SegWit, native SegWit и Taproot). Didit проверяет подпись по адресу. Подпись, выполненная не тем аккаунтом, который проверяется, отклоняется, и появляется понятная подсказка о переключении аккаунта. Аккаунты смарт-контрактов EVM (смарт-кошельки ERC-4337) тоже могут подписывать: Didit запрашивает у контракта аккаунта, в сети, где объявлен кошелёк, действительна ли подпись (ERC-1271). Для аккаунта, который ещё не развёрнут, Didit проверяет подпись ERC-6492, которую возвращает кошелёк, не развёртывая аккаунт и не отправляя никаких транзакций. Аккаунты смарт-контрактов проверяются во всех поддерживаемых сетях EVM. См. [Контракт проверки подписи сообщения](#контракт-проверки-подписи-сообщения) ниже. Кошельки, установленные в браузере клиента, обнаруживаются по протоколам EIP-6963 и Wallet Standard, поэтому каждое расширение отображается как отдельный вариант подписи. Подключение отдельного мобильного приложения кошелька, например по QR-коду, сейчас недоступно. | Всегда, для поддерживаемых семейств сетей. |
| `SATOSHI_TEST` | Клиент отправляет с кошелька небольшую сумму в сети на адрес депозита, который контролируете вы. Наблюдатель Didit проверяет отправителя (для аккаунта смарт-контракта EVM - сам аккаунт, в том числе когда транзакцию отправляет бандлер ERC-4337), сумму и то, что депозит подтверждён в сети. После подтверждения депозит автоматически завершает доказательство владения кошельком. См. [Контракт проверки тестового депозита Satoshi](#контракт-проверки-тестового-депозита-satoshi) ниже. | Только если при создании передан `satoshi_deposit_address`, а сеть относится к EVM, Bitcoin или Solana. Никогда не появляется в автоматически созданной сессии. |
| `SCREENSHOT` | Клиент загружает скриншот кошелька с адресом (JPEG/PNG/WebP, до 10 МБ). В отличие от других способов, он никогда не проверяется автоматически. Ваша команда комплаенса проверяет скриншот в очереди проверки скриншотов ([`GET /v3/travel-rule/wallet-proofs/`](https://docs.didit.me/ru/management-api/travel-rule/list-wallet-proofs)) и одобряет или отклоняет его ([`POST .../decision/`](https://docs.didit.me/ru/management-api/travel-rule/decide-wallet-proof)). Либо подтверждает перевод на вкладке **Travel Rule** транзакции в консоли или через [`POST .../travel-rule/ownership/`](https://docs.didit.me/ru/management-api/travel-rule/confirm-ownership) (для перевода в статусе `UNCONFIRMED_OWNERSHIP` или входящего перевода, находящегося на удержании). | Только если в ваших настройках `allow_screenshot_proof` имеет значение `true`. |
| `SELF_DECLARATION` | Клиент подтверждает владение без криптографического доказательства. Это крайний вариант, допустимый там, где юрисдикция это разрешает. | Только если `allow_self_declaration` имеет значение `true`, а ваш [`ownership_validation_mode`](https://docs.didit.me/ru/transaction-monitoring/travel-rule#режимы-проверки-владения) принимает декларацию для связанного перевода: никогда в режиме `FULL` и в режиме `SIMPLIFIED` только ниже `simplified_validation_threshold`. Если инициировать такой способ, который режим не принимает, API вернёт `403`. |

### Контракт проверки подписи сообщения

После отправки подписи возвращается `200` со `status: "VERIFIED"`, когда подпись доказывает владение кошельком.
Если подпись не подтверждена, возвращается `400`, доказательство остаётся открытым, и клиент может подписать сообщение снова. Возможные значения `code`:

| `code` | Значение |
| - | - |
| `SIGNATURE_INVALID` | Подпись не доказывает владение кошельком: подписал другой аккаунт или ключ либо сообщение отличается. Для аккаунта смарт-контракта его контракт ответил, что подпись не принадлежит ему. |
| `SIGNATURE_UNSUPPORTED_SCHEME` | Кошелёк подписал в формате, который Didit не проверяет, например мультиподпись Bitcoin или полную подпись BIP-322. |
| `SIGNATURE_LOOKUP_FAILED` | Кошелёк может быть аккаунтом смарт-контракта, а его сеть сейчас не удалось проверить. В этом ответе доказательство не подтверждается; повторная подпись запускает проверку заново. |

### Контракт проверки тестового депозита Satoshi

Проверка тестового депозита Satoshi выполняется асинхронно, потому что транзакция должна появиться в сети и затем получить подтверждение.
После отправки `txid` депозита возвращается `200` со `status: "PENDING"`, пока депозит не подтверждён в сети. `txid` сохраняется, а не отбрасывается.
Когда депозит подтверждён, возвращается `200` со `status: "VERIFIED"`.
Для действительно неверного депозита возвращается `400` со `status: "REJECTED"`: отправлен с другого кошелька, сумма на адрес депозита не совпадает с ожидаемой, транзакция старше челленджа или уже подтвердила другой кошелёк. Доказательство закрывается, и клиент может начать новое, пока в сессии остались попытки.
Если депозит ещё не проиндексирован, также возвращается `200` со `status: "PENDING"`. Если поиск завершился ошибкой на стороне Didit, возвращается `400` с кодом `DEPOSIT_LOOKUP_FAILED`. В обоих случаях доказательство остаётся открытым, и Didit продолжает проверку.
Возвращается `410`, если тестовое окно истекло раньше, чем был обнаружен действительный депозит.

После отправки `txid` фоновое задание каждые пять минут перепроверяет депозит, ожидающий подтверждения, и завершает доказательство после подтверждения транзакции. Клиенту не нужно повторно отправлять `txid`.
Если вы передаёте `satoshi_deposit_address`, срок действия самой сессии, указанный в `expires_at`, продлевается как минимум до окончания 24-часового окна тестового депозита Satoshi. Это даёт депозиту время подтвердиться до истечения самой сессии.

## Процесс

**1. Создайте сессию виджета**

Вызовите `POST /v3/travel-rule/widget-session/` с кошельком, который нужно верифицировать. По желанию привяжите сессию к переводу, чтобы перевод автоматически переходил к следующему статусу после подтверждения владения.

**2. Откройте возвращённый URL**

Отправьте клиента по `url` из ответа. Он проходит подтверждение там, и ему не нужны аккаунт Didit или API-ключ: token в URL и есть аутентификация.

**3. Определите завершение**

Когда подтверждение проверено, виджет помечает запись в адресной книге как верифицированную и, если сессия привязана к переводу, продвигает перевод. Следите за событием `travel_rule.status.updated` (для привязанных переводов) или опрашивайте контекст виджета.

### Создание сессии виджета

В этом примере сессия привязывается к [объявленному переводу на некастодиальный кошелёк](https://docs.didit.me/ru/transaction-monitoring/travel-rule.md#объявленные-некастодиальные-кошельки) из руководства по Travel Rule, поэтому `wallet_address` указывает на кошелёк назначения этого перевода.
Сессия, привязанная к переводу, принимает подтверждения только для кошелька контрагента этого перевода.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/widget-session/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
    "chain": "ETH",
    "holder_name": "Jane Doe",
    "vendor_data": "user-042",
    "transaction_id": "99999999-aaaa-bbbb-cccc-dddddddddddd",
    "satoshi_deposit_address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
    "callback_url": "https://yourapp.example/wallet-verified",
    "expires_in_minutes": 60
  }'
```

| Поле | Тип | Описание |
| - | - | - |
| `wallet_address` | строка | Адрес кошелька для верификации. |
| `chain` | строка | Идентификатор сети для адреса (например `ETH`, `BTC`, `SOL`). Используйте тот же token, что и в ваших переводах (`ETH`, а не `ethereum`): виджет сохраняет запись в адресной книге с этим `chain`, и перевод в другой сети с ней не совпадёт. Подписание сообщения требует сети, распознаваемой Didit. |
| `holder_name` | строка | Имя владельца кошелька, используется для сверки имени бенефициара. |
| `vendor_data` | строка | Ваш внутренний идентификатор владельца, переносится в созданную запись в адресной книге. |
| `transaction_id` | строка (uuid) | Необязательный. UUID транзакции Didit для перевода Travel Rule, который нужно продвинуть после успешной проверки подтверждения. Неизвестный UUID возвращает `404`, а подтверждение для любого кошелька, кроме кошелька контрагента этого перевода, отклоняется с `400`. Опустите параметр, чтобы проверить кошелёк отдельно. |
| `satoshi_deposit_address` | строка | Необязательный. Адрес депозита, которым вы управляете; при его передаче включается метод `SATOSHI_TEST`. |
| `callback_url` | строка | Необязательный. Куда виджет возвращает клиента после завершения. |
| `expires_in_minutes` | целое число | Необязательный. Срок действия созданной ссылки в минутах, от `5` до `10080` (по умолчанию `60`); автоматически увеличивается до как минимум 24-часового окна теста Satoshi, если передан `satoshi_deposit_address`. |

```json theme={null}
{
  "widget_session_id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb",
  "token": "3s9x...urlsafe",
  "url": "https://verify.didit.me/wallet-ownership/3s9x...urlsafe",
  "expires_at": "2026-07-07T13:00:00Z"
}
```

Всегда открывайте `url` из ответа как есть, не формируйте его самостоятельно.

### Откройте возвращённый URL

> [!WARNING]
>
> **Виджет в этом релизе работает только в вебе. Открывайте `url` в системном браузере или в пользовательской вкладке, но никогда внутри webview мобильного SDK Didit.**
>
> Подписание сообщений работает с кошельками, установленными в браузере клиента; виджет находит их по протоколам EIP-6963 и Wallet Standard.
> Подключение отдельного мобильного приложения-кошелька, например по QR-коду, сейчас недоступно, поэтому виджет не передаёт управление другому приложению.
> Webview внутри приложения, например webview SDK Didit для Web, Flutter, React Native, iOS или Android, **не может обратиться к кошелькам в браузере клиента**, поэтому виджет, открытый там, не даёт клиенту возможности подписать сообщение.
>
> На мобильных устройствах открывайте `url` в системном браузере (`SFSafariViewController` / Chrome Custom Tabs или браузер по умолчанию), а не отображайте его в webview. В вебе подходит обычная вкладка или всплывающее окно.

### Определите завершение

Виджет предоставляет публичный endpoint чтения, доступный по token. Опрашивайте его, чтобы видеть прогресс, или полагайтесь на webhook, если сессия привязана к переводу:

```bash theme={null}
curl https://verification.didit.me/v3/travel-rule/widget/{token}/
```

```json theme={null}
{
  "vasp_name": "Origin CASP SL",
  "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
  "chain": "ETH",
  "holder_name": "Jane Doe",
  "allowed_methods": ["MESSAGE_SIGNING", "SCREENSHOT", "SATOSHI_TEST", "SELF_DECLARATION"],
  "transfer_status": "UNCONFIRMED_OWNERSHIP",
  "callback_url": "https://yourapp.example/wallet-verified",
  "expires_at": "2026-07-07T13:00:00Z",
  "completed_at": null,
  "proofs": [
    {
      "proof_id": "88888888-9999-aaaa-bbbb-cccccccccccc",
      "method": "SATOSHI_TEST",
      "status": "PENDING",
      "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
      "chain": "ETH",
      "deposit_address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "expected_amount": "0.000042817305521963",
      "expires_at": "2026-07-07T13:05:00.412731+00:00",
      "created_at": "2026-07-06T13:05:00.412731Z"
    }
  ],
  "customization": null
}
```

* `completed_at` становится ненулевым, как только подтверждение проверено.
* Каждая запись в `proofs` содержит поля, нужные её методу: `SATOSHI_TEST` добавляет `deposit_address`, `expected_amount` и собственный `expires_at`; `MESSAGE_SIGNING` добавляет `challenge`, строку, которую подписывает клиент.
* Подтверждение `SATOSHI_TEST` может оставаться в `status: "PENDING"`, пока депозит виден ончейн, но ещё не подтверждён; от вас и клиента действий не требуется. См. [контракт проверки Satoshi test](#контракт-проверки-тестового-депозита-satoshi) выше.
* Если сессия создана с `transaction_id`, проверка подтверждения продвигает привязанный перевод, а в продакшн-приложении срабатывает webhook [`travel_rule.status.updated`](https://docs.didit.me/ru/integration/webhooks#типы-событий).
  Предпочитайте webhook опросу.
  Для входящего запроса `TRP` одобрение отправляется обратно отправителю, и запрос переходит в `COMPLETED`, когда это одобрение доставлено.
  Любой другой перевод в `UNCONFIRMED_OWNERSHIP` (или входящий перевод в `ON_HOLD`) переходит в `COMPLETED`, либо в `COUNTERPARTY_MISMATCHED_DATA`, если имя бенефициара не совпадает с владельцем кошелька, а завершившийся урегулированный sunrise-депозит продолжает переход в `FINISHED`.
  Исходящий перевод в `COUNTERPARTY_VASP_NOT_FOUND` переходит в `COMPLETED`.
* Подтверждение `SCREENSHOT` в этом контексте остаётся в `status: "PENDING"`, даже после того как вы подтвердили владение.
  Одобряете его через [`POST .../travel-rule/ownership/`](https://docs.didit.me/ru/transaction-monitoring/travel-rule#подтверждение-владения-кошельком), который принимает только перевод, ожидающий подтверждения владения.
  Поэтому скриншот невозможно одобрить в сессии без привязанного перевода или в сессии, привязанной к исходящему переводу в `COUNTERPARTY_VASP_NOT_FOUND`: отключите `allow_screenshot_proof`, если такие сессии не должны его предлагать.
* После отклонённого подтверждения сессия даёт новую попытку, пока попытки остаются.
  `max_attempts`, `attempts_used` и `attempts_remaining` показывают счётчик, отклонённое подтверждение содержит `rejection_reason`, а когда `attempts_remaining` равно `0`, при запуске новой попытки возвращается `409`, и тогда нужно создать новую сессию.
* Endpoint контекста возвращает `404` для неизвестного token и `410`, если сессия истекла, не завершившись.

> [!NOTE]
>
> Здесь нет разовых зависимостей: виджет и ваш бэкенд сходятся к одному и тому же устойчивому состоянию (верифицированная запись в адресной книге и статус перевода), поэтому потерянный webhook или закрытая вкладка всегда восстанавливаются повторным чтением перевода или контекста виджета.

## Локализация и брендинг

Виджет поддерживает **54 языка**. Язык выбирается по пути в ссылке или по языку браузера по умолчанию.

Если для вашего приложения включён White label, виджет автоматически наследует брендинг вашего потока верификации: логотип, фирменные цвета, стиль кнопок и панелей, пользовательский шрифт, фавикон и публичное название приложения в заголовке страницы. Отдельная настройка не нужна: применяется та же кастомизация, которую вы задали для KYC-сессий.

Пользователь, который возвращается по уже завершённой ссылке, снова увидит экран завершения. Если вы передали `callback_url`, виджет перенаправит его туда. Истёкшие ссылки показывают брендированный экран истечения срока действия с предложением запросить новую ссылку. Создайте для него новую сессию через `POST /v3/travel-rule/widget-session/`: повторная отправка перевода с `action: "resend"` новую сессию не создаёт.

## Следующие шаги

> [card] [Travel Rule](https://docs.didit.me/ru/transaction-monitoring/travel-rule.md)
>
> Полный жизненный цикл обмена, каналы, настройки и потоки получения.

> [card] [Адресная книга кошельков](https://docs.didit.me/ru/transaction-monitoring/travel-rule.md#адресная-книга-кошельков)
>
> Зарегистрируйте кошельки, которыми вы управляете, чтобы входящие переводы сопоставлялись с ними.

> [card] [Webhooks](https://docs.didit.me/ru/integration/webhooks.md)
>
> Подпишитесь на travel\_rule.status.updated, чтобы реагировать, когда подтверждение продвигает перевод на следующий этап.

> [card] [Нативные SDK](https://docs.didit.me/ru/integration/native-sdks/overview.md)
>
> Открывайте URL виджета в системном браузере, а не в WebView SDK.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.