Обзор
Отправка с клиентской стороны основана на трёх гарантиях:- Защищённая от спуфинга привязка субъекта. Токен привязывается к одному
vendor_data(идентификатору вашего пользователя) при выпуске. Идентичность субъекта каждой транзакции, отправленной с этим токеном, принудительно устанавливается на стороне сервера на основе этой привязки. Поэтому подменённый клиент не может отправлять транзакции от имени другого пользователя, а payload никогда не должен содержать учётные данные. - Автоматический сбор данных об устройстве. SDK собирает отпечаток устройства и отправляет его вместе с транзакцией, а Didit определяет IP-адрес и user agent на стороне сервера. Совокупные данные об устройстве прикрепляются к субъекту транзакции без дополнительных библиотек, отдельного токена устройства и какой-либо настройки с вашей стороны. Они используются для правил на основе устройства и для корреляции сессий и транзакций, связанных через устройство.
- Автоматически запускаемое подтверждение владения кошельком. Если созданная транзакция требует, чтобы конечный пользователь подтвердил контроль над кошельком, ответ содержит блок
action_requiredтипаwallet_ownership. SDK сразу открывает этот сценарий, а после его завершения обновляет транзакцию. Не нужно создавать циклы опроса или самостоятельно отправлять ссылки. Обязательное действиеverification_sessionустроено иначе: оно никогда не запускается автоматически и возвращается в том же блоке, чтобы ваше приложение запустило его через собственную интеграцию верификации Didit.
Серверный
POST /v3/transactions/ остаётся правильным выбором, когда конечного пользователя нет: бэкофисные переводы, пакетный импорт или события, передаваемые вашими собственными системами.
Оба пути создают одни и те же записи транзакций, выполняют одни и те же правила и отправляют одни и те же webhook.Процесс
1
Выпустите токен транзакции
Ваш бэкенд вызывает
POST /v3/transactions/sdk-token/ с вашим API-ключом и vendor_data пользователя и передаёт возвращённый sdk_token вашему приложению.2
Отправка с устройства
Приложение вызывает
submitTransaction в SDK, который отправляет payload на POST /v1/transactions/ с заголовком X-Transaction-Token и автоматическим обогащением данными об устройстве.3
Обработайте обязательное действие
Если
action_required.type равен wallet_ownership, SDK автоматически открывает виджет, а затем обновляет транзакцию через GET /v1/transactions/{transaction_id}/.
Если это verification_session, SDK ничего не запускает: прочитайте action_required и начните собственный сценарий верификации с его session_token (или url в веб-версии).Выпуск токена транзакции
Выпускайте токены со своего бэкенда черезPOST /v3/transactions/sdk-token/. Аутентификация по API-ключу такая же, как у остальных эндпоинтов транзакций /v3/.
Как и в остальном API
/v3/, принимаются также псевдонимы в camelCase (vendorData, ttlSeconds, maxUses).
Отправка с устройства
SDK отправляют запрос наPOST /v1/transactions/ с токеном в заголовке X-Transaction-Token.
Payload на уровне протокола передаётся в формате camelCase. Методы submitTransaction формируют его за вас, поэтому работать с ним напрямую нужно только при вызове эндпоинта из собственного HTTP-клиента:
POST /v3/transactions/:
Ответ содержит те же данные транзакции в snake_case, что и эндпоинты
/v3/, а также блок action_required:
Объявление некастодиального кошелька
В примере выше получатель объявлен собственным некастодиальным кошельком пользователя:paymentMethod.type контрагента равен unhosted_wallet.
Это объявление меняет работу обмена Travel Rule:
- Didit полностью пропускает маршрутизацию через VASP-контрагента, так как за некастодиальным кошельком нет VASP, и создаёт перевод сразу в статусе
UNCONFIRMED_OWNERSHIP. - Если в настройках Travel Rule включён
auto_wallet_verification(по умолчанию), сессия виджета подтверждения владения кошельком создаётся автоматически и возвращается вaction_required, как в ответе выше. - SDK автоматически запускает виджет. Когда пользователь подтверждает контроль над кошельком, для перевода выполняется проверка совпадения имени, после чего он переходит в
COMPLETED, либо вCOUNTERPARTY_MISMATCHED_DATA, если имя не совпадает с владельцем адреса в адресной книге кошелька.
Что задаёт токен
Три вещи всегда определяются токеном, а не payload:- Приложение, в котором создаётся транзакция.
- Личность субъекта:
subject.externalUserIdв payload игнорируется и заменяется значениемvendor_dataиз токена. - Контекст устройства субъекта (см. ниже); данные, полученные на сервере, имеют приоритет над любыми значениями в
subject.device.
401 от этого эндпоинта означает, что токен отклонён: он отсутствует, неизвестен, отозван, исчерпал max_uses или истёк.
В ответах с истекшим токеном в описании ошибки всегда есть слово expired. По нему SDK отличают ошибку expired_token от invalid_token.
Чтение транзакции
GET /v1/transactions/{transaction_id}/ возвращает те же данные с той же аутентификацией X-Transaction-Token.
SDK вызывают его автоматически после завершения запущенного потока действия или по закрытии его окна, поэтому итоговое состояние никогда не зависит от одного события или уведомления.
Вы можете сами проверить status и action_required в любой момент, пока токен действителен. С вашего бэкенда та же запись возвращается через GET /v3/transactions/{transaction_id}/.
Автоматический анализ устройства
Каждая отправка содержит данные об устройстве без каких-либо дополнительных действий с вашей стороны:- SDK собирает отпечаток устройства, безопасный с точки зрения конфиденциальности (схема
didit-fp-v2: сигналы платформы и navigator, постоянный идентификатор устройства, оценка бота и составной хеш), и передаёт его вместе с запросом. - Didit определяет IP-адрес, user agent и accept-language на сервере по самому соединению, поэтому их нельзя подделать через payload.
/v3/, отправленные с сервера, по-прежнему могут передавать явные объекты device_context для каждого участника. На пути SDK вы получаете более полную версию тех же данных без дополнительной работы.
Требуемые действия пользователя
Некоторые транзакции не могут завершиться, пока конечный пользователь не выполнит действие. Правило может требовать сессию верификации, а для перевода по Travel Rule может понадобиться подтверждение того, что целевой кошелёк действительно принадлежит пользователю. Оба случая отображаются в одном месте: в блокеaction_required ответа на отправку (и в каждом последующем чтении транзакции):
Если оба варианта могут относиться к одной транзакции, приоритет у
wallet_ownership, поскольку отсутствие подтверждения владения блокирует сам обмен по Travel Rule.
action_required становится null, когда ожидающее действие разрешено.
Поле remediation сохранено для обратной совместимости. Каноническим блоком является action_required.
Контракт автозапуска
autoLaunchAction (по умолчанию true) автоматически запускает только действие wallet_ownership:
- Веб - открывается во встроенном модальном окне SDK. Завершение отслеживается через сообщения с проверкой origin и ограниченный опрос транзакции (никогда не зависит только от одного события).
- iOS / Android (а также обёртки React Native и Flutter, которые передают работу нативным SDK) - показывается как нативный интерфейс подтверждения владения кошельком SDK (SwiftUI / Jetpack Compose), если
useNativeWalletOwnershipимеет значениеtrue(по умолчанию). При необходимости используетсяSFSafariViewControllerили Chrome Custom Tab, а в крайнем случае браузер по умолчанию. После закрытия потока или возврата приложения на передний план SDK автоматически повторно читает транзакцию.
verification_session никогда не запускается автоматически: оно всегда возвращается в action_required, и ваше приложение запускает его через собственную интеграцию верификации Didit.
После того как автоматически запущенный SDK поток подтверждения владения кошельком завершится или будет закрыт, SDK повторно читает транзакцию и передаёт обновлённый результат в ваш callback onActionCompleted.
Действие verification_session не входит в этот цикл, поскольку его запускает ваше приложение. См. следующий раздел.
Запуск сессии верификации
Когдаaction_required.type имеет значение verification_session, прочитайте блок из result.actionRequired и передайте его в существующую интеграцию верификации Didit:
GET /v1/transactions/{transaction_id}/ (или дождитесь webhook транзакции), чтобы получить обновлённый статус транзакции.
Пример: повторная биометрическая проверка для крупного вывода средств
Это наиболее эффективный сценарий: рискованные транзакции (крупные суммы, новые получатели, новые устройства) требуют повторной биометрической проверки того же человека, которого вы верифицировали при онбординге. Захват учётной записи выглядит именно так: верный пароль, верный код 2FA (выманенный фишингом или перехваченный в результате SIM-свопа) и вывод на кошелёк, который вы никогда не видели. К этому моменту все защиты на основе учётных данных уже не сработали. Но лицо - нет: злоумышленник не может пройти проверку живости по селфи, которое должно совпасть с лицом, снятым при онбординге настоящего владельца в рамках KYC.1
Создайте правило один раз в консоли
Transactions → Rules → Create Rule: условие
amount gte 5000, действие Change status → AWAITING_USER, и на этом действии выберите ваш сценарий Биометрическая аутентификация.
Добавьте любые другие условия риска, которые вам нужны, например контрагента, с которым проводится первая транзакция, новый отпечаток устройства или смену страны IP-адреса, как дополнительные условия или дополнительные правила.2
Отправьте вывод как обычно
Код отправки не меняется. Когда срабатывает правило, ответ возвращается со значением
status: "AWAITING_USER" и блоком action_required типа verification_session:3
Запустите повторную проверку
Передайте
session_token (или url) в вашу существующую интеграцию верификации Didit точно так же, как в разделе Запуск сессии верификации.
Поскольку сессия создаётся для того же vendor_data, что и субъект транзакции, у Didit уже есть проверенный эталонный снимок лица пользователя из онбординга: одобренное селфи с проверкой живости, фото с чипа паспорта или портрет из документа.
Пользователь делает одно селфи в реальном времени, а Didit выполняет проверку живости и сравнение лиц 1:1 с сохранённым эталоном. Повторное сканирование документа и повторный онбординг не требуются. Небольшое неудобство длится несколько секунд и возникает только для транзакций, которые этого заслуживают.4
Определите итог
Когда сессия завершится, транзакция автоматически переоценивается:
APPROVED, если повторная проверка пройдена, или DECLINED, если нет. В любом случае отправляется webhook transaction.status.updated (reason: remediation_completed в истории транзакции).
Ваше приложение получает результат через callback в стиле onActionCompleted, через GET /v1/transactions/{transaction_id}/ или через webhook.Web SDK
submitTransaction уже доступен в синглтоне Web SDK:
{ transactionId, status, travelRuleStatus?, actionRequired? }.
В payload на уровне SDK вы передаёте category, details и customProperties; SDK сам сопоставляет их с псевдонимами протокола (type, info, props).
Ошибки типизированы через DiditTransactionError:
Нативные SDK
Нативный API
submitTransaction входит в состав iOS и Android SDK 4.1.0, а также релизов React Native и Flutter, которые используют эту версию.
В отличие от Web SDK, в нативных payload напрямую используются имена полей протокола: type, info и props.getTransaction(transactionToken, transactionId) для повторного чтения транзакции с тем же токеном. SDK вызывают его автоматически после запуска потока действий, а вы можете вызвать его самостоятельно в любой момент.
На мобильных устройствах механизм передачи отпечатка устройства уже используется для всех сетевых вызовов SDK, поэтому отправки транзакций автоматически передают те же данные об устройстве.
Без SDK
Всё, что делают SDK, выполняется посредством обычных HTTPS-запросов, и ничто здесь не требует SDK. Есть два равноценных варианта интеграции. Только серверная сторона (без токенов вообще). Отправьте транзакцию черезPOST /v3/transactions/ с вашего бэкенда, используя API-ключ. Ответ (и каждый последующий GET /v3/transactions/{transaction_id}/) содержит тот же блок action_required. Также можно передавать явный контекст device для каждого участника, поскольку собрать его за вас некому: SDK в этом варианте нет.
Клиентская сторона с собственным HTTP-стеком. Выпустите токен через POST /v3/transactions/sdk-token/, затем вызывайте POST /v1/transactions/ и GET /v1/transactions/{transaction_id}/ с устройства, передав заголовок X-Transaction-Token. Та же привязка к субъекту и то же серверное определение IP и user-agent действуют и здесь.
Далее обрабатывайте action_required самостоятельно:
И определяйте завершение без клиентских обратных вызовов:
- Webhooks (рекомендуется).
transaction.status.updatedсрабатывает, когда ожидающее действие завершает обработку транзакции, аtravel_rule.status.updatedсрабатывает при каждом изменении состояния обмена. Оба события содержат достаточно данных, чтобы обновить ваши записи без опроса. - Опрос. Перечитывайте транзакцию (
/v3/с вашего бэкенда или/v1/с токеном, пока он действителен):action_requiredвозвращается кnullпосле завершения, аstatusиtravel_rule.statusсодержат результат.
Webhook
Вашему бэкенду не нужно, чтобы приложение передавало результат. Когда отправленная транзакция требует действия пользователя, webhook транзакции (transaction.created или transaction.status.updated) содержит тот же блок action_required. Ваши системы узнают об ожидающем действии, даже если приложение закрыто до завершения потока.
Действие verification_session сопровождается статусом AWAITING_USER, но действие wallet_ownership само по себе не меняет статус транзакции, поэтому ориентируйтесь на action_required, а не на статус.
Переходы Travel Rule дополнительно генерируют событие travel_rule.status.updated.
Следующие шаги
Отправка транзакций
Серверный API Create Transaction и полный справочник payload.
Travel Rule
Управляемый обмен данными между отправителем и получателем, настройки и жизненный цикл.
Виджет подтверждения владения кошельком
Размещённый поток подтверждения контроля, на котором основаны действия wallet_ownership.
Webhook
transaction.created, transaction.status.updated и проверка подписи.
Правила
Действия правил, включая те, что создают сессии верификации.
Статусы
Что означает AWAITING_USER в жизненном цикле транзакции.