> ## 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.

# 安卓 Kotlin 身份验证 SDK：KYC

> Didit 提供原生安卓 Kotlin SDK，支持身份验证、活体检测和 NFC 电子护照读取。每次完整 KYC 核验低至 $0.33，每月 $10 免费额度，无需签订合同。



Android SDK 集成提示
        将提示词复制到剪贴板



      
      

\<pre>

\`Integrate Didit identity verification into my Android app using the native Kotlin SDK.

\## Requirements
\- Android API 23+ (Android 6.0 Marshmallow). ML-based auto-detection (document/face) requires API 24+ and falls back to manual capture below that.
\- Kotlin 1.9+
\- NFC reading requires NFC-capable device hardware

\## Installation

\### Step 1: Add Maven Repository
In settings.gradle.kts:
dependencyResolutionManagement {
repositories {
    google()
    mavenCentral()
    maven { url = uri("https://raw.githubusercontent.com/didit-protocol/sdk-android/main/repository") }
}
}

\### Step 2: Add Dependency
(!) Regarding the version ensure you check the [GitHub repository](https://github.com/didit-protocol/sdk-android) for the latest version.
In app/build.gradle.kts:
dependencies {
implementation("me.didit:didit-sdk:4.13.2")
}

\### Step 3: Add Packaging Exclusion (required to avoid build conflicts)
In the android { } block of app/build.gradle.kts:
android {
packaging {
    resources {
        excludes += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
    }
}
}

\## Permissions (auto-merged from SDK manifest)
\- INTERNET (required) — API communication
\- ACCESS_NETWORK_STATE (required) — Network availability
\- CAMERA (required) — Document scanning and face verification
\- NFC (optional) — Read NFC chips in passports/IDs

Camera and NFC are declared as optional features (android:required="false"), so the app installs on all devices.

\## Initialization (required)
In your Application class:
import me.didit.sdk.DiditSdk

class MyApp : Application() {
override fun onCreate() {
    super.onCreate()
    DiditSdk.initialize(this)
}
}

Register in AndroidManifest.xml:
\<application android:name=".MyApp" ...>

\## Backend: Create Session (your server)
POST https://verification.didit.me/v3/session/
Headers: { "x-api-key": DIDIT_API_KEY, "Content-Type": "application/json" }
Body: { "workflow_id": DIDIT_WORKFLOW_ID, "vendor_data": "user-id" }
Response: { "session_id": "uuid", "session_token": "short-token", "url": "https://verify.didit.me/session/short-token", "status": "Not Started" }
Send session_token to the Android app (never expose DIDIT_API_KEY to the device).

\## Jetpack Compose Integration
import me.didit.sdk.DiditSdk
import me.didit.sdk.DiditSdkState
import me.didit.sdk.VerificationResult

// Observe SDK state and launch UI when ready
LaunchedEffect(Unit) {
DiditSdk.state.collect { state ->
    when (state) {
        is DiditSdkState.Ready -> DiditSdk.launchVerificationUI(activity)
        is DiditSdkState.Error -> Log.e("Didit", "Error: state.message")
        else -> { /* Loading, Idle, CreatingSession */ }
    }
}
}

// Start verification with session token from backend
import me.didit.sdk.VerificationStatus

fun startVerification(token: String) {
DiditSdk.startVerification(token = token) { result ->
    when (result) {
        is VerificationResult.Completed -> when (result.session.status) {
            VerificationStatus.APPROVED -> { /* grant access */ }
            VerificationStatus.DECLINED -> { /* show retry */ }
            VerificationStatus.PENDING  -> { /* under review */ }
        }
        is VerificationResult.Cancelled -> { /* user cancelled */ }
        is VerificationResult.Failed    -> { /* show error: result.error.message */ }
    }
}
}

\## Result Statuses (VerificationStatus enum; .rawValue gives "Approved" / "Pending" / "Declined")
\- VerificationStatus.APPROVED — user identity verified, grant access
\- VerificationStatus.DECLINED — verification failed, show retry or contact support
\- VerificationStatus.PENDING  — needs manual review by your compliance team

\## Full Verification Results
The SDK only returns the status. Full decision data (document fields, face match scores, AML hits, etc.) arrives via webhook to your backend.
Set up webhooks: https://docs.didit.me/integration/webhooks

\## Rate Limits (session creation)
\- 600 session-create requests / minute per x-api-key (see https://docs.didit.me/integration/rate-limiting)
\- On 429, retry honouring Retry-After / X-RateLimit-* headers

\## Environment Variables (backend only — never expose in the Android app)
\- DIDIT_API_KEY — from Didit Console > API & Webhooks
\- DIDIT_WORKFLOW_ID — from Didit Console > Workflows

\## Docs
\- Android SDK: https://docs.didit.me/integration/native-sdks/android-sdk
\- GitHub: https://github.com/didit-protocol/sdk-android
\- Webhooks: https://docs.didit.me/integration/webhooks
\- API Reference: https://docs.didit.me/api-reference/overview`

\</pre>



      
      

粘贴到

一款轻量级、由服务端驱动的 Android SDK，用于身份验证，只需极少的配置。> [card] [GitHub 代码仓库](https://github.com/didit-protocol/sdk-android)
>
> 在 GitHub 上查看源代码和示例> [!NOTE]
>
> ### **原生 SDK**：推荐 Android 应用采用此方式。原生 SDK 提供最佳的用户体验、经过优化的相机处理以及完整的 NFC 支持。***## 要求| 要求 | 最低版本 |
| - | - |
| Android API | 23+（Android 6.0 Marshmallow）。ML 自动检测需要 API 24+（见下文）。 |
| Kotlin | 1.9+ |
| Jetpack Compose | 作为传递依赖自动包含 |> [!NOTE]
>
> **按 API 级别划分的功能可用性**：证件和人脸自动检测需要 API 24+（Didit 自有的轻量检测器，包含在 `didit-sdk-core` 中）。在 API 23 的设备上，SDK 会回退为手动拍摄，并使用带定时功能的快门按钮。其他所有功能均支持完整的 API 23+ 范围。> [!NOTE]
>
> **生物识别同意需要原生 SDK 4.10.0 或更高版本。** 对于受伊利诺伊州、德克萨斯州或科罗拉多州法律约束的用户，Didit 可以在首次人脸采集前请求明确的[生物识别同意](https://docs.didit.me/zh/getting-started/biometric-consent.md)。Android SDK 4.10.0 及更高版本会显示该步骤。较早版本无法显示该步骤：其会话照常运行，决策结果（`biometric_consent.reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK"`）会将同意报告为未收集。如果您在这些州验证用户，请升级。***## 安装### 步骤 1 添加仓库在 `settings.gradle.kts` 的 `repositories` 块中添加 Didit Maven 仓库：```kotlin theme={null}
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://raw.githubusercontent.com/didit-protocol/sdk-android/main/repository") }
    }
}
```如果使用 `settings.gradle`（Groovy），请改用：```groovy theme={null}
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url "https://raw.githubusercontent.com/didit-protocol/sdk-android/main/repository" }
    }
}
```### 步骤 2 添加依赖在应用的 `build.gradle.kts` 中添加 SDK 依赖：
(!) 关于版本号，请务必到 [GitHub 仓库](https://github.com/didit-protocol/sdk-android) 查看最新版本。```kotlin theme={null}
dependencies {
    implementation("me.didit:didit-sdk:4.13.2")
}
```如果使用 `build.gradle`（Groovy），请改用：```groovy theme={null}
dependencies {
    implementation "me.didit:didit-sdk:4.13.2"
}
```#### SDK 变体Android SDK 以多个独立构件的形式发布，因此您的应用只需为所用功能承担相应的二进制体积。上文的 `me.didit:didit-sdk` 是完整包。请选择适合您应用的构件：| 构件 | 自动检测（ML） | NFC 芯片读取 | 适用场景 |
| - | :-: | :-: | - |
| `me.didit:didit-sdk-core` | ✓ | – | 体积最小的构建；使用 Didit 自有检测器自动采集，不支持护照或证件芯片读取 |
| `me.didit:didit-sdk-autodetection` | ✓ | – | 自 4.11.0 起已弃用且为空；仅依赖 `didit-sdk-core`，请改用 `didit-sdk-core` |
| `me.didit:didit-sdk-nfc` | ✓ | ✓ | 在 `didit-sdk-core` 之上增加芯片读取功能 |
| `me.didit:didit-sdk` | ✓ | ✓ | 所有功能（完整包） |自 `4.6.0` 起，原生 WalletConnect 钱包签名以独立的可选插件形式发布，即 `me.didit:didit-sdk-wallet`。完整包 `me.didit:didit-sdk` 已包含该插件，因此如果您安装完整包，无需任何改动。如果您使用精简构件之一，并希望钱包所有权确认在应用内以原生方式运行，请将 `me.didit:didit-sdk-wallet` 与之一同添加；若不添加该插件，SDK 会改为通过安全的浏览器流程完成钱包所有权确认，因此该功能无论如何都能正常使用。将 WalletConnect 移出 `didit-sdk-core` 后，所有不需要它的构建也都减少了数 MB 的钱包相关依赖。所有构件均以相同版本发布，因此请将它们都保持在 `4.13.2`。#### 控制下载体积自动采集所用的检测模型在运行时下载并缓存，因此不会计入应用体积。自动采集给应用增加的部分是 ML 运行时的原生库，其体积取决于 CPU 架构：`arm64-v8a` 约 10 MB，`armeabi-v7a` 约 7 MB，两种 x86 模拟器架构各约 13 MB。要只承担一次这部分成本：1) **发布 Android App Bundle（AAB）**。Google Play 只会投递设备自身的架构和语言，因此 arm64 手机安装的原生代码约为 10 MB（压缩后的下载体积约为 4 MB）。通用 APK 会打包全部四种架构，超过 40 MB，这是尺寸测量结果异常偏大的最常见原因。
2) **如果您分发单个 APK**，且目标用户是 ARM 手机和平板，请限制打包的架构：

   ```kotlin theme={null}
   android {
       defaultConfig {
           ndk { abiFilters += listOf("arm64-v8a", "armeabi-v7a") }
       }
   }
   ```

   请保留用户实际运行的所有 ABI：如果面向 Chromebook 或其他 x86 设备，请添加 `x86_64`；或者发布按 ABI 拆分的 APK，确保没有设备失去支持。
3) **启用 R8**：在 release 构建类型中设置 `isMinifyEnabled = true` 和 `isShrinkResources = true`（如果使用 Groovy 版 `build.gradle`，则设置 `minifyEnabled true` / `shrinkResources true`）。SDK 自带使用方规则，因此无需任何额外配置，未使用的 SDK 代码即会被移除。
4) 如果 APK 构建中不需要全部 48 个 SDK 本地化区域（locale），请使用 `resConfigs(...)` **限制打包的语言**；在 Google Play 上，AAB 语言拆分会自动处理这一点。### 步骤 3 添加打包排除规则在应用的 `android` 块中添加以下内容，以避免构建冲突：```kotlin theme={null}
android {
    packaging {
        resources {
            excludes += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
        }
    }
}
```就这样！Gradle 会自动解析所有传递依赖。***## 权限SDK 需要以下权限。这些权限在 SDK 的 `AndroidManifest.xml` 中声明，并会自动合并到您应用的清单文件中：| 权限 | 来源 AAR | 说明 | 是否必需 |
| - | - | - | - |
| `INTERNET` | `didit-sdk-core` | 用于 API 通信的网络访问 | 是 |
| `ACCESS_NETWORK_STATE` | `didit-sdk-core` | 检测网络是否可用 | 是 |
| `CAMERA` | `didit-sdk-core` | 证件扫描和人脸验证 | 是 |
| `NFC` | `didit-sdk-nfc` | 读取护照/身份证中的 NFC 芯片 | 从 `didit-sdk-nfc` 自动合并（`me.didit:didit-sdk` 的传递依赖） |### 相机与 NFC 功能SDK 将 `android.hardware.camera` 和 `android.hardware.nfc` 声明为可选功能（`android:required="false"`）。这样，即使设备没有相机或 NFC 硬件，您的应用也能安装；SDK 会在运行时妥善处理缺失的硬件。***## 快速开始### 步骤 1: 初始化 SDK在您的 `Application.onCreate()` 中初始化 SDK：```kotlin theme={null}
import me.didit.sdk.DiditSdk

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        DiditSdk.initialize(this)
    }
}
```### 步骤 2: 启动验证并处理结果```kotlin theme={null}
import me.didit.sdk.DiditSdk
import me.didit.sdk.DiditSdkState
import me.didit.sdk.VerificationResult

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Observe SDK state to launch UI when ready
        lifecycleScope.launch {
            DiditSdk.state.collect { state ->
                when (state) {
                    is DiditSdkState.Ready -> DiditSdk.launchVerificationUI(this@MainActivity)
                    is DiditSdkState.Error -> Log.e("Didit", "Error: ${state.message}")
                    else -> { /* Loading, Idle, CreatingSession */ }
                }
            }
        }
    }

    private fun startVerification() {
        // Method 1: UniLink (no backend required)
        DiditSdk.startVerification(
            workflowId = "your-workflow-id",
            vendorData = "user-123"
        ) { result ->
            handleResult(result)
        }

        // Method 2: Backend Session (recommended for production)
        // DiditSdk.startVerification(
        //     token = "your-session-token"
        // ) { result ->
        //     handleResult(result)
        // }
    }

    private fun handleResult(result: VerificationResult) {
        when (result) {
            is VerificationResult.Completed -> {
                Log.d("Didit", "Session: ${result.session.sessionId}")
                Log.d("Didit", "Status: ${result.session.status}")
            }
            is VerificationResult.Cancelled -> {
                Log.d("Didit", "User cancelled")
            }
            is VerificationResult.Failed -> {
                Log.e("Didit", "Failed: ${result.error.message}")
            }
        }
    }
}
```***## 集成方式SDK 支持两种集成方式：### 方式 1: UniLink (最简单)无需后端。SDK 直接使用您在 Didit Console 中获取的工作流 ID 创建会话。仅限于 `vendorData`。```kotlin theme={null}
DiditSdk.startVerification(
    workflowId = "your-workflow-id",
    vendorData = "user-123"
) { result ->
    handleResult(result)
}
```### 方式 2: 后端会话 (推荐用于生产环境)您的后端通过 [创建验证会话 API](https://docs.didit.me/zh/sessions-api/create-session.md)（`POST /v3/session/`）创建会话，该 API 支持完整的参数（`contact_details`、`expected_details`、`metadata`、`callback` 等）。然后，将 `session_token` 传递给 SDK。```kotlin theme={null}
// Your backend creates a session and returns the token
val sessionToken = yourBackend.createVerificationSession(userId = currentUser.id)

// Pass the token to the SDK
DiditSdk.startVerification(
    token = sessionToken
) { result ->
    handleResult(result)
}
```这种方式可让您全面控制以下内容：- 将会话与您的用户关联（`vendor_data`）
- 设置自定义元数据
- 为每个会话配置回调
- 提供联系方式和预期信息，用于交叉验证***## 配置自定义 SDK 行为：```kotlin theme={null}
import me.didit.sdk.Configuration
import me.didit.sdk.core.localization.SupportedLanguage

val configuration = Configuration(
    languageLocale = SupportedLanguage.SPANISH,  // Force Spanish language
    showLanguageSelector = true,                  // Let the user change language
    fontFamily = "my_custom_font",                // Custom font (must be in res/font/)
    loggingEnabled = true                         // Enable debug logging
)

DiditSdk.startVerification(
    token = "your-session-token",
    configuration = configuration
) { result ->
    handleResult(result)
}
```### 配置选项| 属性 | 类型 | 默认值 | 说明 |
| - | - | - | - |
| `languageLocale` | `SupportedLanguage?` | 设备语言区域 | 强制使用指定语言 |
| `showLanguageSelector` | `Boolean` | `false` | 在欢迎页面显示网页风格的语言选择器 |
| `fontFamily` | `String?` | 系统字体 | 自定义字体资源名称（来自 `res/font/`） |
| `loggingEnabled` | `Boolean` | `false` | 启用 SDK 调试日志 |
| `showCloseButton` | `Boolean` | `true` | 在验证步骤页面显示关闭（X）按钮 |
| `showExitConfirmation` | `Boolean` | `true` | 用户尝试退出时显示确认对话框 |
| `closeOnComplete` | `Boolean` | `false` | 完成后自动关闭验证界面（Web SDK 对应项：`closeModalOnComplete`） |
| `defaultDocumentCamera` | `CameraLens` | `CameraLens.BACK` | 打开证件拍摄页面时使用的镜头 |
| `defaultLivenessCamera` | `CameraLens` | `CameraLens.FRONT` | 打开人脸（活体）拍摄页面时使用的镜头 |
| `showDocumentCameraSwitchButton` | `Boolean` | `true` | 在证件拍摄页面显示拍摄界面中的相机切换按钮 |
| `showLivenessCameraSwitchButton` | `Boolean` | `true` | 在人脸拍摄页面显示拍摄界面中的相机切换按钮 |### 相机选项相机选项与 [Web SDK 相机选项](https://docs.didit.me/zh/integration/web-sdks/javascript-sdk.md#摄像头选项)一致。
每个镜头为 `CameraLens.FRONT` 或 `CameraLens.BACK`。
如果设备没有所请求的镜头，SDK 将使用设备上的第一个相机。
在只有一个相机的设备上，也会隐藏相机切换按钮。
将切换按钮选项设为 `false`，即可将用户锁定在该步骤的默认镜头上。```kotlin theme={null}
val configuration = Configuration(
    defaultLivenessCamera = CameraLens.BACK,
    showLivenessCameraSwitchButton = false
)
```> [!NOTE]
>
> 选项 `showLanguageSelector`、`showCloseButton`、`showExitConfirmation` 和 `closeOnComplete` 与 Web SDK 的 `DiditSdkConfiguration` 一致。移动端专属选项 `languageLocale` 和 `fontFamily` 的存在，是因为移动端 SDK 以原生方式渲染完整的验证界面（而 Web SDK 则将其委托给 iframe 内的托管前端）。> [!NOTE]
>
> **主题与颜色**：颜色、背景和引导页设置通过 Didit Console 中的 [白标设置](https://business.didit.me) 配置，而不是在 SDK 配置中配置。这样可确保所有平台的品牌形象保持一致。***## 语言支持SDK 支持 **54 种语言**。如果未指定语言，SDK 将使用设备语言区域，并以英语作为备选。```kotlin theme={null}
// Use device locale (default)
val config = Configuration()

// Force specific language
val config = Configuration(languageLocale = SupportedLanguage.FRENCH)

// Let the user change language on the welcome screen
val config = Configuration(showLanguageSelector = true)
```**[查看所有支持的语言 →](https://docs.didit.me/zh/integration/supported-languages.md)*****## 高级选项如需使用高级会话参数（`contact_details`、`expected_details`、`metadata`、`callback`），请使用 **后端会话** 方式。您的后端使用完整参数调用 [创建验证会话 API](https://docs.didit.me/zh/sessions-api/create-session.md)，然后将 `session_token` 传递给 SDK。***## Handling Results`VerificationResult` 密封类提供验证的结果：### 结果类型| 类型 | 说明 |
| - | - |
| `Completed(session)` | 验证流程已完成（请检查 `session.status` 以获取结果） |
| `Cancelled(session)` | 用户取消了验证流程 |
| `Failed(error, session)` | 验证过程中发生错误 |### SessionData 属性| 属性 | 类型 | 说明 |
| - | - | - |
| `sessionId` | `String` | 会话的唯一标识符 |
| `status` | `VerificationStatus` | `APPROVED`、`PENDING` 或 `DECLINED` |### 错误类型所有错误均为 `me.didit.sdk.VerificationError` 的密封子类：| 错误 | 说明 |
| - | - |
| `SessionExpired` | 会话已过期 |
| `NetworkError` | 网络连接问题 |
| `CameraAccessDenied` | 未授予相机权限 |
| `NotInitialized` | SDK 未初始化，请先调用 `DiditSdk.initialize(context)` |
| `ApiError` | 后端返回了错误响应 |
| `RetryBlocked` | 验证无法重试（例如已达到最大尝试次数） |
| `Unknown(message)` | 其他错误，附带错误信息 |### 网络连接中断如果设备在验证过程中断网，SDK 会显示一个"无网络连接"屏幕，而不是让某个步骤一直处于加载状态。SDK 会在后台持续检测连接，一旦可以连接到 Didit，该屏幕会自动关闭，用户将从中断前所在的步骤继续操作。该屏幕提供 **重试** 按钮；当 `showCloseButton` 为 `true`（默认值）时，还会提供 **退出验证** 按钮。退出后，验证将以 `VerificationResult.Failed(error = VerificationError.NetworkError, session = ...)` 结束，因此您的应用能够得知用户是因网络原因而退出的。### 完整结果处理示例```kotlin theme={null}
DiditSdk.startVerification(token = "your-token") { result ->
    when (result) {
        is VerificationResult.Completed -> {
            when (result.session.status) {
                VerificationStatus.APPROVED -> {
                    Log.d("Didit", "Approved! Session: ${result.session.sessionId}")
                    // User is verified - grant access
                }
                VerificationStatus.PENDING -> {
                    Log.d("Didit", "Under review. Session: ${result.session.sessionId}")
                    // Show "verification in progress" UI
                }
                VerificationStatus.DECLINED -> {
                    Log.d("Didit", "Declined. Session: ${result.session.sessionId}")
                    // Handle declined verification
                }
            }
        }
        is VerificationResult.Cancelled -> {
            Log.d("Didit", "Cancelled: ${result.session?.sessionId ?: "unknown"}")
            // User chose to cancel - maybe show retry option
        }
        is VerificationResult.Failed -> {
            Log.e("Didit", "Error: ${result.error.message}")
            // Handle error - show retry or contact support
        }
    }
}
```***## 观察 SDK 状态您可以观察 SDK 状态，以实现自定义的加载界面：```kotlin theme={null}
lifecycleScope.launch {
    DiditSdk.state.collect { state ->
        when (state) {
            is DiditSdkState.Idle -> {
                // Ready to start verification
            }
            is DiditSdkState.CreatingSession -> {
                // Show "Creating session..." progress
            }
            is DiditSdkState.Loading -> {
                // Show loading indicator
            }
            is DiditSdkState.Ready -> {
                // Verification UI is ready - launch it
                DiditSdk.launchVerificationUI(this@MainActivity)
            }
            is DiditSdkState.Error -> {
                // Show error message
                Log.e("Didit", "Error: ${state.message}")
            }
        }
    }
}
```***## ProGuard / R8SDK 内置了自己的使用方 ProGuard 规则，无需额外配置。如果您使用 R8 完整模式，可能需要在 `gradle.properties` 中添加以下内容：```properties theme={null}
android.enableR8.fullMode=false
```***## 端到端示例 (后端会话 → Android SDK → 结果)此模式即为可用于生产环境的集成方式。您的后端创建会话，您的 Android 应用接收 `session_token`，然后由 SDK 运行验证流程。### 1. 后端：创建会话```ts theme={null}
// Node.js / Express example. Run on your server. NEVER ship DIDIT_API_KEY to the device.
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/verification/start", async (req, res) => {
  const response = await fetch("https://verification.didit.me/v3/session/", {
    method: "POST",
    headers: {
      "x-api-key": process.env.DIDIT_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: req.body.userId, // your stable user id
    }),
  });

  if (!response.ok) {
    return res.status(500).json({ error: `Didit ${response.status}` });
  }

  const { session_id, session_token } = await response.json();
  // Persist session_id <-> userId so you can match the webhook later.
  res.json({ session_id, session_token });
});
```### 2. Android：交换令牌并启动 SDK```kotlin theme={null}
import android.app.Activity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.didit.sdk.DiditSdk
import me.didit.sdk.VerificationResult
import me.didit.sdk.VerificationStatus
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.MediaType.Companion.toMediaType
import org.json.JSONObject

class VerificationFlow(private val activity: ComponentActivity) {
    private val http = OkHttpClient()
    private val json = "application/json".toMediaType()

    fun start(userId: String) {
        activity.lifecycleScope.launch {
            val token = withContext(Dispatchers.IO) {
                val body = JSONObject().put("userId", userId).toString().toRequestBody(json)
                val req = Request.Builder()
                    .url("https://your-backend.example.com/api/verification/start")
                    .post(body)
                    .build()
                http.newCall(req).execute().use { resp ->
                    JSONObject(resp.body!!.string()).getString("session_token")
                }
            }

            DiditSdk.startVerification(token = token) { result ->
                when (result) {
                    is VerificationResult.Completed -> when (result.session.status) {
                        VerificationStatus.APPROVED -> { /* optimistic UI; rely on webhook for source of truth */ }
                        VerificationStatus.PENDING  -> { /* show "under review" */ }
                        VerificationStatus.DECLINED -> { /* show retry/contact support */ }
                    }
                    is VerificationResult.Cancelled -> { /* user cancelled */ }
                    is VerificationResult.Failed    -> { /* show error: result.error.message */ }
                }
            }
        }
    }
}
```### 3. 后端：通过 Webhook 接收最终决策SDK 的结果便于提供界面反馈，但权威结果通过 Webhook 送达。有关 HMAC 验证，请参阅 [Webhook 指南](https://docs.didit.me/zh/integration/webhooks.md)。```ts theme={null}
app.post("/api/webhooks/didit", express.raw({ type: "application/json" }), (req, res) => {
  // 1. Verify the X-Signature header (see Webhooks docs)
  // 2. Parse the body
  const event = JSON.parse(req.body.toString());
  if (event.webhook_type === "status.updated") {
    // event.session_id, event.status ("Approved" | "Declined" | "In Review" | ...)
    // Look up the user by session_id and update their verification state.
  }
  res.sendStatus(200);
});
```***## 完整示例```kotlin theme={null}
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import me.didit.sdk.Configuration
import me.didit.sdk.DiditSdk
import me.didit.sdk.DiditSdkState
import me.didit.sdk.VerificationResult
import me.didit.sdk.VerificationStatus
import me.didit.sdk.core.localization.SupportedLanguage

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Observe SDK state
        lifecycleScope.launch {
            DiditSdk.state.collect { state ->
                when (state) {
                    is DiditSdkState.Ready -> DiditSdk.launchVerificationUI(this@MainActivity)
                    is DiditSdkState.Error -> Log.e("Didit", "Error: ${state.message}")
                    else -> { /* Idle, Loading, CreatingSession */ }
                }
            }
        }
    }

    fun startVerification() {
        val config = Configuration(
            languageLocale = SupportedLanguage.ENGLISH,
            loggingEnabled = true
        )

        // Method 1: UniLink (no backend required)
        DiditSdk.startVerification(
            workflowId = "your-workflow-id",
            vendorData = "user-123",
            configuration = config
        ) { result ->
            handleResult(result)
        }

        // Method 2: Backend Session (recommended for production)
        // DiditSdk.startVerification(
        //     token = "your-session-token",
        //     configuration = config
        // ) { result ->
        //     handleResult(result)
        // }
    }

    private fun handleResult(result: VerificationResult) {
        when (result) {
            is VerificationResult.Completed -> {
                when (result.session.status) {
                    VerificationStatus.APPROVED -> {
                        Log.d("Didit", "Approved! Session: ${result.session.sessionId}")
                    }
                    VerificationStatus.PENDING -> {
                        Log.d("Didit", "Under review")
                    }
                    VerificationStatus.DECLINED -> {
                        Log.d("Didit", "Declined")
                    }
                }
            }
            is VerificationResult.Cancelled -> {
                Log.d("Didit", "Cancelled: ${result.session?.sessionId ?: "unknown"}")
            }
            is VerificationResult.Failed -> {
                Log.e("Didit", "Error: ${result.error.message}")
            }
        }
    }
}
```***


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