Как Didit маршрутизирует обмен
Когда клиент Didit отправляет перевод, Didit проверяет эти каналы строго по приоритету и использует первый, который может до вас дойти:
Таблица выше описывает переводы, которые отправляет клиент Didit.
Когда вы отправляете перевод клиенту Didit, Didit может принять его только как
TRP-запрос о переводе, после того как публичный TRP-хост Didit станет доступен (сейчас он не отвечает, см. endpoint TRP), либо после расчёта как регистрация sunrise. Сегодня ни один сетевой канал и ни один канал электронной почты не доставляет входящий перевод клиенту Didit.
Сетевые каналы (GTR, TRUST, VERIFYVASP, SYGNA) работают через членство на стороне отправителя.
Сейчас это должно быть собственное подключённое членство клиента Didit: у Didit пока нет членства на платформе ни в одной из этих сетей, и он проходит онбординг как провайдер.
GTR по умолчанию не включён, а требование к членству одинаково для всех четырёх сетей.
Отправитель должен проверить объект networks в Получить настройки Travel Rule: didit означает, что членство Didit представляет это приложение в качестве собственного участника сети, own означает подключённое членство клиента, off означает, что маршрутизация через платформу отключена, а unavailable означает, что приложение пока не может отправлять по этой сети.
Пока у Didit нет членства на платформе, ни одна сеть не получает значение didit.
Включение Travel Rule не активирует членство в сети.
О том, какие направления доступны через каждую сеть сегодня, см. сети Travel Rule.
Если вы работаете в одной из этих сетей и клиент Didit имеет там собственное членство, передайте ему свой идентификатор сети: перевод дойдёт до вас по сети, в которой вы уже работаете.
По SYGNA ваш ответ на разрешение завершает или отклоняет перевод: Didit опрашивает статус перевода каждые 5 минут и также регистрирует https://trp.didit.me/sygna/callbacks/permission/{token}/ как обратный вызов для каждого запроса разрешения, с токеном, выпущенным для этого перевода.
Этот обратный вызов находится на публичном TRP-хосте Didit, который пока не обрабатывает запросы.
Значение ACCEPTED в permission_status переводит перевод отправителя в COMPLETED, а REJECTED переводит его в COUNTERPARTY_VASP_GENERAL_DECLINE или, если reject_code равен BVRC007 (имя получателя не совпадает), в COUNTERPARTY_MISMATCHED_DATA.
Callback требует вашей подписи: без корректной подписи он отвечает 401 с {"status": "INVALID_SIGNATURE"}, на неизвестный токен отвечает 404, а для решения, которое уже применено, снова возвращает 200.
Если у вас нет общих с Didit членств, планируйте путь через TRP: передайте клиенту Didit свой Travel Address, и его переводы будут приходить на ваш endpoint TRP.
Ответ, который вы дадите в ответе на запрос о переводе, сразу завершает или отклоняет перевод.
Ответ, который вы отправите позже на callback Didit, требует публичного TRP-хоста Didit, который пока не работает.
Когда ни один канал не может вас определить, Didit выполняет ещё один шаг: атрибуцию через блокчейн-аналитику.
Адрес кошелька назначения сопоставляется с данными блокчейн-аналитики, и если он атрибутирован вашей бирже или кастодиальной службе, Didit записывает ваше название в свой каталог VASP и повторяет проверку каналов с ним.
Это значит, что вас можно определить, даже если отправитель указал только адрес кошелька.
Переводы с атрибуцией направляются вам автоматически только после того, как в записи каталога появится маршрут: проверенный endpoint TRP, запись в сети или контакт по комплаенсу (см. получение записи в каталоге VASP Didit).
Полная цепочка определения выглядит так: клиент Didit, подтвердивший контроль над кошельком (INTERNAL), а также сохранённый отправителем Travel Address; затем каналы протокола и сети; затем блокчейн-аналитика.
Travel Address
У каждого VASP Didit с включённым Travel Rule есть Travel Address: компактная строка, которая начинается сta и кодирует URL его персонального endpoint для запросов TRP на передачу средств. Клиенты Didit берут свой Travel Address в настройках Travel Rule и передают его контрагентам. Если клиент Didit передал вам свой адрес, у вас уже есть всё необходимое, чтобы отправить ему запрос по TRP.
Кодирование соответствует конвенции TRP для Travel Address:
- Удалите префикс
ta. - Декодируйте остаток по схеме Base58Check (алфавит Bitcoin, контрольная сумма double-SHA256).
- Декодированные байты представляют собой URL запроса без схемы. Добавьте в начало
https://. - URL всегда содержит пару параметров
t=i(тип: запрос).
{profile_uuid} определяет размещённый профиль VASP. Отправляйте на этот URL методом POST ваши запросы на передачу, а сначала проверьте, с кем вы взаимодействуете, через соответствующий endpoint идентификации (endpoint идентификации).
Второй вид: Travel Address на уровне кошелька. Он указывает на один кошелёк клиента Didit (receiving_travel_address в его адресной книге), поэтому ваш запрос попадает на этот кошелёк без необходимости сопоставлять его по номеру счёта или имени владельца. Такой адрес возвращает 404, когда кошелёк удалён или зарегистрирован за другим VASP.
TRP endpoint
Didit работает с TRP API версии3.2.1 по базовому URL https://trp.didit.me.
Все endpoint обмениваются данными в формате JSON по HTTPS.
Важны два заголовка TRP:
api-version- версия протокола TRP, которую вы используете, например3.2.1. Обязателен в запросах на отправку inquiry и в обоих endpoint для обратных вызовов.request-identifier- ваш уникальный идентификатор запроса, который возвращается в заголовках ответа. Обязателен в запросах на отправку inquiry и в обоих endpoint для обратных вызовов.
400.
Каждый ответ от Didit содержит api-version: 3.2.1 и повторяет ваш request-identifier.
GET /identity
Возвращает идентификационные данные оператора платформы. Заголовки не требуются.GET /trp/identity//
Возвращает идентификационные данные конкретного VASP, с которым вы собираетесь обмениваться данными. Используйте его, чтобы проверить название и LEI до отправки персональных данных (PII). Didit выполняет ту же проверку в обратном направлении, когда инициирует обмен: если идентичность, которую возвращает ваш endpoint, не совпадает с ожидаемым LEI или названием, обмен прерывается.404, если профиль не существует или его настройка Travel Rule отключена.
POST /trp/inquiries//
Отправьте запрос на перевод: вы выступаете VASP-отправителем, а клиент Didit выступает VASP-получателем.
В этом канале активы определяются по DTI, и DTI идентифицирует токен в одном блокчейне.
Сейчас Didit сопоставляет
BTC (4H95J0R2X), ETH (X9J9K872S) и USDC в девяти сетях; другие активы добавляются со временем.
Полный список см. в разделе Идентификаторы активов TRP.
Запрос с DTI, который Didit не распознаёт, всё равно принимается и передаётся VASP-получателю с DTI в качестве кода актива.
Ответ 200 означает, что запрос принят в обработку; 400 означает отсутствующие заголовки TRP или некорректное тело запроса; 404 означает, что профиль (или кошелёк для Travel Address для отдельного кошелька) неизвестен или отключён; 422 означает, что запрос использует расширение TRP: Didit не поддерживает ни одного, поэтому заголовок api-extensions или ключ extensions в теле отклоняются.
Повторная отправка того же request-identifier идемпотентна: исходный запрос используется повторно, а не дублируется.
Затем Didit создаёт входящий перевод в приложении клиента и сверяет его с адресной книгой кошельков клиента и его политикой.
Получатель сопоставляется по кошельку, который указан в Travel Address для отдельного кошелька; если его нет, по первому адресу кошелька в IVMS101.beneficiary.accountNumber; а если и его нет, по имени из beneficiaryPersons[0].naturalPerson среди подтверждённых записей кошельков клиента.
Ответ 200 содержит решение, если Didit может дать его сразу:
{"approved": {"address": "...", "callback": "..."}}- когда запрос одобрен автоматически. Didit не отправляет это одобрение и на вашcallback.{"rejected": "NOT_ENOUGH_COUNTERPARTY_DATA: inquiry carries no beneficiary identification"}- когда в запросе не указаны ни получатель, ни адрес кошелька, поэтому всегда указывайте оба.{"version": "3.2.1"}- когда решение ожидает клиента Didit. Решение затем приходит позже POST-запросом на ваш URLcallbackс одним из двух тел, приведённых ниже.
originatingVASP клиент Didit видит как VASP-отправителя перевода.
Didit не устанавливает срок для отправленного вами запроса, а клиент Didit, который его отменяет, не присылает вам отказ, поэтому задайте собственный таймаут.
После получения одобрения и отправки перевода в блокчейн завершите обмен, выполнив POST-запрос на URL approved.callback, который вам передан:
{ "canceled": "reason" } на тот же URL.
txid переводит перевод на стороне получателя в статус FINISHED; canceled переводит его в статус CANCELLED.
Оба endpoint обратного вызова возвращают 204 после обработки, 404 для неизвестного токена или перевода, который больше не ожидает этого обратного вызова, и 400 для тела, в котором нет ни одного из двух полей обратного вызова.
Обработка запросов, которые Didit отправляет вам
Когда клиент Didit отправляет вам перевод по TRP, Didit:- Запрашивает
/identityв корне хоста вашего URL для запросов (https://{your-host}/identity) и проверяет указанные в ответе имя и LEI на соответствие ожиданиям клиента. Если маршрут получен из директории Didit,/identityдолжен возвращать LEI из этой записи директории. - Отправляет POST-запрос с телом перевода на ваш URL для запросов. Структура тела та же, что описана выше:
asset.dti,amountв базовых единицах в виде числа JSON,callbackи payloadIVMS101.
2xx. Ответ 3xx отклоняется, потому что перенаправления не отслеживаются. Любой другой ответ записывается в хронологию перевода клиента вместе с вашим кодом состояния, например refused by counterparty (HTTP 403): ....
Оба запроса содержат два заголовка TRP, и Didit не отслеживает HTTP-перенаправления в них, поэтому /identity и URL для запросов должны отвечать напрямую, без перенаправлений.
Если /identity недоступен, не возвращает LEI или возвращает LEI, отличный от указанного клиентом, либо возвращает имя, которое не совпадает, Didit останавливается до отправки запроса, и перевод клиента переходит в статус COUNTERPARTY_VASP_NOT_REACHABLE.
Если затем POST-запрос завершается ошибкой или ваш endpoint отвечает на него статусом 4xx или 5xx, перевод тоже переходит в статус COUNTERPARTY_VASP_NOT_REACHABLE.
callback в этом запросе указывает на endpoint разрешения Didit (/trp/callbacks/resolution/{token}/).
Ответьте POST-запросом с теми же телами разрешения, которые использует Didit: { "approved": { "address": "...", "callback": "..." } } для одобрения (ваш callback задаёт адрес, по которому Didit отправит подтверждение из сети) или { "rejected": "reason" } для отклонения.
Отправьте с ним оба заголовка TRP. После обработки Didit отвечает 204; если перевод больше не ожидает разрешения, Didit отвечает 404. Для одобрения без address или если callback не является публичным URL http(s), по которому Didit может обратиться, Didit отвечает 400.
При одобрении сторона Didit переходит в COMPLETED. Когда клиент сообщит хеш транзакции в сети, Didit отправит POST-запрос с { "txid": "..." } на URL подтверждения, который вы указали (или { "canceled": "reason" }, если перевод сначала будет отменён).
Токены обратного вызова: секреты одного назначения, непредсказуемые, которые выпускаются для каждого перевода. Относитесь к полным URL обратного вызова как к конфиденциальным и учитывайте, что разрешение принимается только пока перевод ещё ожидает его.
Получение по электронной почте
Если ни один протокольный канал не может с вами связаться, а клиент Didit указал ваш контактный email для комплаенса, Didit переходит к электронной почте: ваш контакт по комплаенсу получает письмо с названием VASP-отправителя и одноразовой защищённой ссылкой для получения. Учётная запись, API-ключ или интеграция не нужны: токен в ссылке и является аутентификацией. При открытии ссылки отображается обмен, ожидающий обработки. API, который лежит в основе страницы и вызывается ею за вас, является публичным:amount передаётся как сохранённое десятичное значение без изменений, поэтому содержит все 18 знаков после запятой.
decision принимает значения accept или decline. Для accept укажите имя получателя так, как оно записано у вас, чтобы сторона отправителя могла выполнить сверку имён, либо отправьте полный ответ IVMS 101 как ivms_response; тогда имя получателя из него имеет приоритет.
При decline можно передать decline_reason (data_mismatch, not_our_customer, wallet_not_hosted, sanctions, risk_appetite, jurisdiction, insufficient_data или other) и decline_detail длиной до 512 символов, которое обязательно для other.
Принятие без beneficiary_name записывается как несоответствие, если отправитель указал имя получателя, если только у отправителя не отключена проверка имён.
Ответ содержит итоговый {"status": "..."}: COMPLETED, если имена совпадают, COUNTERPARTY_MISMATCHED_DATA, если не совпадают, или COUNTERPARTY_VASP_GENERAL_DECLINE после отклонения.
Ответить можно один раз, пока перевод ожидает вашего ответа. После ответа, после отмены перевода или после срока подтверждения оба endpoint возвращают 410, а неизвестный токен возвращает 404.
Запрос показывает поля отправителя, которыми он делится, и ivms_request в формате IVMS 101 на языке вашей юрисдикции. Вам напоминают за 72, 24 и 6 часов до срока, пока вы не ответили.
Sunrise: переводы, исполненные до обмена по Travel Rule
Когда перевод поступает на кошелёк клиента Didit до какого-либо обмена по Travel Rule, потому что общего канала на тот момент не существовало, данные всё равно можно передать уже после расчёта. Отправьте клиенту Didit (вашему контрагенту-получателю) данные Travel Rule обычным каналом и попросите его зарегистрировать их: он вызываетPOST /v3/travel-rule/inbound/, передавая блокчейн депозита, txid, адрес кошелька, сумму, валюту и ваши данные об отправителе.
Укажите идентификационные данные вашего VASP (название, LEI и Travel Address) в поле originating_vasp этого payload, чтобы в переводе клиента Didit было видно, кто отправил депозит.
Эти данные остаются в этом переводе и никогда не создают и не меняют вашу запись в директории.
LEI должен быть действительным 20-символьным LEI, а Travel Address должен быть корректным, иначе регистрация отклоняется.
Как попасть в каталог VASP Didit
Didit ведёт каталог VASP, к которому обращается маршрутизация исходящих переводов его клиентов. Записи попадают в каталог из следующих источников:- Промежуточный реестр ESMA по MiCA для поставщиков услуг в сфере криптоактивов, который читается каждую неделю: ваше название, LEI и государство-член, в котором вы зарегистрированы, если вы в нём значитесь.
- Публичный каталог TRISA, который читается каждую неделю: кто вы, ваша страна и запись в TRISA.
- Глобальный реестр LEI, из которого берутся ваш LEI, другие юридические наименования и страна.
- Атрибуция в аналитике блокчейна: когда адрес назначения кошелька определяется как принадлежащий вашей бирже или кастодиальному сервису, Didit добавляет или сопоставляет вашу запись и сохраняет атрибутированное название как псевдоним.
- Сотрудники Didit, которые курируют записи: проверенные ими идентификаторы маршрутизации и записи в сетях, контакты комплаенса, псевдонимы, а также бренды вместе с их юридическими лицами.
originating_vasp при регистрации Sunrise или Travel Address в переводе, остаётся в этом переводе клиента и никогда не меняет вашу запись.
Запросы по переводам, которые вы отправляете клиенту Didit, тоже не создают и не обновляют вашу запись.
Ваша запись направляет перевод к вам, когда в ней есть проверенный endpoint TRP, запись в сети, которой может воспользоваться отправитель, или контакт комплаенса для резервного канала по электронной почте.
Адрес комплаенса на одном из ваших доменов, который отвечает на запрос через ссылку в ответе, становится контактом, которым может воспользоваться любой клиент Didit.
Пока в вашей записи нет маршрута, клиент Didit связывается с вами только через поля, которые он передаёт с каждым переводом: ваш Travel Address, ваш сетевой идентификатор (counterparty_gtr_vasp_code, counterparty_trust_membership_id, counterparty_verifyvasp_id или counterparty_sygna_vasp_code) или ваш адрес электронной почты комплаенса (counterparty_vasp_email).
Чтобы быть доступными уже сейчас, передайте Travel Address, сетевой идентификатор или адрес электронной почты комплаенса клиентам Didit, с которыми вы работаете.
Чек-лист для VASP-контрагентов
- Вы уже работаете по TRP: передайте клиенту Didit ваш Travel Address и LEI. Его переводы в
BTC,ETHиUSDCпоступят на ваш endpoint как стандартные запросы TRP. Ответьте на запрос: для последующего ответа потребуется публичный хост TRP Didit, который пока не запущен. - Вы работаете через GTR, TRUST или VerifyVASP: передайте ваш сетевой идентификатор; переводы будут приходить по этой сети, если у клиента Didit есть собственное членство в ней. Didit читает ваш ответ GTR или VerifyVASP из сети, но не ответ TRUST, поэтому опубликуйте также endpoint TRP, если обмен через TRUST должен завершаться. Сети Travel Rule объясняют, когда это меняется.
- Вы работаете через Sygna Bridge: передайте ваш код VASP в Sygna; если у клиента Didit есть собственное членство в Sygna, ваш ответ о разрешении завершает перевод или отклоняет его после того, как Didit получит его посредством опроса.
- У вас пока нет канала протокола: дайте клиенту Didit адрес электронной почты комплаенса, за которым ведётся мониторинг. Вы будете получать ссылки на получение и сможете отвечать с размещённой страницы без какой-либо интеграции.
- Перевод уже проведён: передайте данные через клиента Didit с помощью регистрации Sunrise.
- Вы также хотите отправлять переводы в VASP Didit: запросите Travel Address вашего контрагента в настройках Travel Rule в Didit, затем используйте POST /trp/inquiries после запуска публичного хоста TRP Didit.
Следующие шаги
Travel Rule
Продукт для клиентов: жизненный цикл, настройки, каналы и API /v3/.
Виджет подтверждения владения кошельком
Как собираются подтверждения владения на стороне получателя, когда кошелёк является некастодиальным.
Регистрация Sunrise
Регистрация данных Travel Rule для депозитов, которые уже проведены ончейн.
Webhook
Как клиенты Didit отслеживают каждое изменение статуса обмена на своей стороне.