> ## 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 통제권 증명

> 메시지 서명, Satoshi 테스트, 스크린샷, 자가 선언으로 고객의 암호화폐 지갑 통제권을 증명하는 Didit 호스팅 위젯입니다. Travel Rule 대응용.

지갑 소유권 위젯은 고객이 특정 암호화폐 지갑 주소를 실제로 관리하고 있음을 증명하는 호스팅 페이지입니다. EU TFR에 따라 VASP는 자체 보관(비호스팅) 지갑으로 선라이즈 임계값을 초과하는 자금을 보내거나 받기 전에 해당 지갑을 통제하고 있음을 확인해야 합니다. 이 위젯은 서명 흐름을 직접 구현하지 않고도 Didit이 그 증명을 수집하는 방법입니다.

검증된 증명이 있으면 일치하는 [지갑 주소록 항목](https://docs.didit.me/ko/transaction-monitoring/travel-rule.md#지갑-주소록)에 `is_ownership_verified: true`로 설정되고, 이에 연결된 [Travel Rule 이체](https://docs.didit.me/ko/transaction-monitoring/travel-rule.md)는 `UNCONFIRMED_OWNERSHIP` 상태에서 벗어나 진행할 수 있습니다.

## Didit이 위젯을 자동으로 발급하는 경우

직접 세션을 발급할 필요는 거의 없습니다.
[Travel Rule 설정](https://docs.didit.me/ko/transaction-monitoring/travel-rule.md#설정)에서 `auto_wallet_verification`이 켜져 있으면(기본값), 이체에 최종 사용자의 지갑 통제 증명이 필요할 때 Didit이 거래 생성 시점에 위젯 세션을 자동으로 발급합니다.

* 이체가 `UNCONFIRMED_OWNERSHIP` 상태입니다. 제출 시 거래 상대방 지갑이 [자체 보관 지갑](https://docs.didit.me/ko/transaction-monitoring/travel-rule.md#선언된-자체-보관-지갑)으로 선언되었습니다.
* 이체가 발신 거래이며 `COUNTERPARTY_VASP_NOT_FOUND` 상태입니다. 거래 상대방 VASP를 식별하지 못했으므로 소유권 증명이 거래를 완료하기 위해 남은 유일한 경로입니다.

자동 발급된 세션은 거래의 `action_required` 블록(`type: "wallet_ownership"`, 열어야 할 `url` 포함)으로 반환되며, SDK는 [클라이언트 측 제출](https://docs.didit.me/ko/transaction-monitoring/sdk-transaction-submission.md#필수-사용자-작업) 시 이를 자동으로 실행합니다.
Didit이 대신 생성하는 수신 이체(선라이즈 등록, 수신 `TRP` 조회, `INTERNAL` 레일의 수신 구간)에는 `UNCONFIRMED_OWNERSHIP` 상태가 되더라도 자동 발급 세션이 생성되지 않습니다.
이체가 존재하기 전에 지갑을 미리 검증하려는 경우, `auto_wallet_verification`을 끈 경우, 또는 위와 같은 수신 이체의 경우에는 `POST /v3/travel-rule/widget-session/`으로 직접 발급하십시오.

## 증명 내용

이 위젯은 최대 네 가지 증명 방법을 제공합니다. 어떤 방법이 표시되는지는 세션을 발급한 방식과 [Travel Rule 설정](https://docs.didit.me/ko/transaction-monitoring/travel-rule.md#설정)에 따라 달라집니다:

| 방법 | 작동 방식 | 제공 조건 |
| - | - | - |
| `MESSAGE_SIGNING` | 고객이 지갑으로 Didit이 발급한 챌린지 문자열에 서명합니다. EVM, Solana, Bitcoin, Tron 지갑을 지원하며(Bitcoin은 레거시, 중첩 SegWit, 네이티브 SegWit, Taproot 주소 포함), Didit은 서명을 주소에 대해 검증합니다. 검증 대상과 다른 계정으로 서명하면 계정 전환을 안내하는 메시지와 함께 거부됩니다. EVM 스마트 컨트랙트 계정(ERC-4337 스마트 지갑)도 서명할 수 있습니다. Didit은 지갑이 등록된 체인에서 해당 계정의 컨트랙트에 서명이 유효한지 확인하고(ERC-1271), 아직 배포되지 않은 계정이라면 지갑이 반환한 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, 최대 10MB)을 업로드합니다. 다른 방법과 달리 자동으로 검증되는 경우가 없습니다. 컴플라이언스 팀이 스크린샷 검토 대기열([`GET /v3/travel-rule/wallet-proofs/`](https://docs.didit.me/ko/management-api/travel-rule/list-wallet-proofs))에서 검토하고 승인하거나 거부합니다([`POST .../decision/`](https://docs.didit.me/ko/management-api/travel-rule/decide-wallet-proof)). 또는 콘솔에서 거래의 **Travel Rule** 탭을 통해, 또는 [`POST .../travel-rule/ownership/`](https://docs.didit.me/ko/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/ko/transaction-monitoring/travel-rule#소유권-검증-모드)가 연결된 이체에 대한 진술을 허용하는 경우에만 제공됩니다. `FULL` 모드에서는 절대 허용되지 않으며, `SIMPLIFIED` 모드에서는 `simplified_validation_threshold` 미만인 경우에만 허용됩니다. 모드가 허용하지 않는 진술을 시작하면 `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`가 제출되면 백그라운드 작업이 5분마다 대기 중인 입금을 다시 확인하고, 트랜잭션이 확정된 후 증명을 완료합니다. 따라서 고객이 `txid`를 다시 제출할 필요는 없습니다.
`satoshi_deposit_address`를 전달하면, 위젯 세션의 `expires_at`이 최소 24시간인 Satoshi 테스트 기간까지 연장됩니다. 세션 자체가 만료되기 전에 입금이 확정될 시간을 확보하기 위해서입니다.

## 흐름

**1. 위젯 세션 발급**

검증할 지갑으로 `POST /v3/travel-rule/widget-session/`을 호출합니다. 선택 사항으로, 소유권이 증명되면 이체가 자동으로 진행되도록 이체와 연결할 수 있습니다.

**2. 반환된 URL 열기**

고객을 응답의 `url`로 보냅니다. 고객은 그곳에서 증명을 완료하며, Didit 계정이나 API 키는 필요하지 않습니다. URL에 포함된 토큰이 인증 역할을 합니다.

**3. 완료 감지**

증명이 검증되면 위젯이 주소록 항목을 검증됨으로 표시하고, 세션이 이체와 연결되어 있으면 해당 이체를 진행시킵니다. `travel_rule.status.updated`(연결된 이체)를 확인하거나 위젯 컨텍스트를 폴링합니다.

### 위젯 세션 발급

이 예제는 세션을 Travel Rule 가이드의 [선언된 자체 보관 이체](https://docs.didit.me/ko/transaction-monitoring/travel-rule.md#선언된-자체-보관-지갑)와 연결하므로, `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`). 이체에 사용하는 것과 같은 토큰(`ethereum`이 아니라 `ETH`)을 사용하십시오. 위젯은 이 `chain` 값으로 주소록 항목을 저장하며, 다른 체인의 이체는 이 항목과 일치하지 않습니다. 메시지 서명에는 Didit이 인식하는 체인이 필요합니다. |
| `holder_name` | 문자열 | 수취인 이름 일치 확인에 사용되는 지갑 보유자의 이름입니다. |
| `vendor_data` | 문자열 | 보유자를 식별하는 내부 식별자이며, 생성된 주소록 항목에 함께 저장됩니다. |
| `transaction_id` | 문자열(uuid) | 선택 사항입니다. 증명이 검증될 때 진행시킬 Travel Rule 이체의 Didit 거래 UUID입니다. 알 수 없는 UUID는 `404`를 반환하며, 이체의 거래 상대방 지갑이 아닌 지갑에 대한 증명은 `400`으로 거부됩니다. 지갑을 단독으로 검증하려면 생략합니다. |
| `satoshi_deposit_address` | 문자열 | 선택 사항입니다. 귀사가 관리하는 입금 주소입니다. 이 값을 제공하면 `SATOSHI_TEST` 방식을 사용할 수 있습니다. |
| `callback_url` | 문자열 | 선택 사항입니다. 완료 후 위젯이 고객을 돌려보낼 주소입니다. |
| `expires_in_minutes` | 정수 | 선택 사항입니다. 발급된 링크의 유효 기간(분 단위)으로, `5`에서 `10080` 사이입니다(기본값 `60`). `satoshi_deposit_address`를 전달하면 Satoshi 테스트 기간인 24시간 이상으로 자동 늘어납니다. |

```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을 만들지 마십시오.

### 반환된 URL 열기

> [!WARNING]
>
> **이번 릴리스에서 위젯은 웹에서만 동작합니다. `url`은 시스템 브라우저나 커스텀 탭에서 여십시오. Didit 모바일 SDK 웹뷰 안에서는 절대 열지 마십시오.**
>
> 메시지 서명은 고객의 브라우저에 설치된 지갑과 함께 작동하며, 위젯은 이 지갑을 EIP-6963과 Wallet Standard 프로토콜로 찾습니다.
> QR 코드 등으로 별도의 모바일 지갑 앱을 연결하는 기능은 현재 제공되지 않으므로, 위젯은 다른 앱으로 넘기지 않습니다.
> Didit Web/Flutter/React Native/iOS/Android SDK 웹뷰처럼 앱 내 웹뷰는 **고객의 브라우저 지갑에 접근할 수 없으므로**, 그 안에서 열린 위젯에서는 고객이 서명할 방법이 없습니다.
>
> 모바일에서는 웹뷰로 렌더링하지 말고, OS 브라우저(`SFSafariViewController` / Chrome Custom Tabs 또는 기본 브라우저)로 `url`을 여십시오. 웹에서는 일반 탭이나 팝업을 사용해도 됩니다.

### 완료 감지

위젯은 토큰으로 조회하는 공개 읽기 엔드포인트를 제공합니다. 이를 폴링해 진행 상황을 확인하거나, 세션이 이체와 연결된 경우 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`에 값이 설정됩니다(null이 아님).
* `proofs`의 각 항목에는 해당 방식에 필요한 필드가 포함됩니다. `SATOSHI_TEST` 증명에는 `deposit_address`, `expected_amount`, 그리고 자체 `expires_at`이 추가됩니다. `MESSAGE_SIGNING` 증명에는 고객이 서명할 문자열인 `challenge`가 추가됩니다.
* `SATOSHI_TEST` 증명은 입금이 온체인에서 확인되었지만 아직 확정되지 않은 동안 `status: "PENDING"` 상태에 머물 수 있습니다. 이때 귀사나 고객이 할 일은 없습니다. 위의 [Satoshi 테스트 검증 계약](#satoshi-테스트-검증-계약)을 참고하십시오.
* 세션이 `transaction_id`와 함께 발급된 경우, 증명이 검증되면 연결된 이체가 진행되며, 라이브 애플리케이션에서는 [`travel_rule.status.updated`](https://docs.didit.me/ko/integration/webhooks#이벤트-유형) webhook이 발생합니다.
  폴링보다 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/ko/transaction-monitoring/travel-rule#지갑-소유권-확인)로 승인하며, 이 엔드포인트는 소유권 확인을 기다리는 이체만 받습니다.
  따라서 연결된 이체가 없는 세션이나 `COUNTERPARTY_VASP_NOT_FOUND` 상태의 발신 이체와 연결된 세션에서는 스크린샷 증명을 승인할 방법이 없습니다. 이런 세션에서 스크린샷 증명을 제공하면 안 된다면 `allow_screenshot_proof`를 끄십시오.
* 증명이 거부된 후에도 남은 시도 횟수가 있으면 세션에서 새 시도를 할 수 있습니다.
  `max_attempts`, `attempts_used`, `attempts_remaining`이 시도 횟수를 나타내며, 거부된 증명에는 `rejection_reason`이 포함됩니다. `attempts_remaining`이 `0`이면 새 증명을 시작할 때 `409`가 반환되므로, 새 세션을 발급해야 합니다.
* 컨텍스트 엔드포인트는 알 수 없는 토큰에 대해 `404`를, 완료되지 않은 채 세션이 만료되면 `410`을 반환합니다.

> [!NOTE]
>
> 여기에는 일회성 의존성이 없습니다. 위젯과 귀사의 백엔드는 같은 영속 상태(검증된 주소록 항목과 이체 상태)로 수렴합니다. 따라서 webhook이 누락되거나 탭이 닫히더라도, 이체 또는 위젯 컨텍스트를 다시 읽어 언제든 복구할 수 있습니다.

## 현지화 및 브랜딩

위젯은 **54개 언어**로 렌더링되며, 링크의 언어 경로(또는 브라우저 기본값)를 따릅니다.

앱에서 화이트 라벨이 활성화되어 있으면 위젯은 인증 플로우의 브랜딩을 자동으로 상속합니다. 로고, 브랜드 색상, 버튼 및 패널 스타일, 사용자 지정 글꼴, 파비콘, 제목에 표시되는 앱의 공개 이름이 여기에 포함됩니다.
별도의 설정은 필요 없습니다. KYC 세션에 설정한 것과 같은 커스터마이징이 여기에도 적용됩니다.

이미 완료된 링크로 다시 접속한 사용자에게는 완료 화면이 다시 표시됩니다. `callback_url`을 전달한 경우 위젯은 사용자를 해당 URL로 되돌려 보냅니다.
만료된 링크에는 브랜드가 적용된 만료 화면이 표시되며, 사용자에게 새 링크를 요청하도록 안내합니다.
사용자를 위해 새 세션을 생성하려면 `POST /v3/travel-rule/widget-session/`을 사용하세요. `action: "resend"`로 전송을 재전송해도 세션은 생성되지 않습니다.

## 다음 단계

> [card] [Travel Rule](https://docs.didit.me/ko/transaction-monitoring/travel-rule.md)
>
> 전체 송금 수명 주기, 결제 경로, 설정 및 수취 플로우를 확인할 수 있습니다.

> [card] [지갑 주소록](https://docs.didit.me/ko/transaction-monitoring/travel-rule.md#지갑-주소록)
>
> 직접 관리하는 지갑을 등록하면 인바운드 전송이 이 지갑을 기준으로 확인됩니다.

> [card] [Webhooks](https://docs.didit.me/ko/integration/webhooks.md)
>
> travel\_rule.status.updated를 구독하면 증명으로 전송이 진행될 때 이에 대응할 수 있습니다.

> [card] [네이티브 SDK](https://docs.didit.me/ko/integration/native-sdks/overview.md)
>
> 위젯 URL은 SDK 웹뷰가 아닌 시스템 브라우저에서 여세요.


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