> For the complete documentation index, see [llms.txt](https://doc.ozforensics.com/oz-knowledge-ru/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://doc.ozforensics.com/oz-knowledge-ru/obshaya-informaciya/kratkie-rukovodstva-po-integracii/kak-integrirovat-oz-liveness-v-vashe-mobilnoe-prilozhenie.md).

# Как интегрировать Oz Liveness в ваше мобильное приложение

{% hint style="warning" icon="display" %}
Внимание: это машинный перевод.
{% endhint %}

Oz Liveness Mobile SDK предоставляет готовый пользовательский интерфейс захвата лица, необходимый для бесшовного клиентского опыта и точных результатов анализа Liveness.

Это руководство поможет вам выполнить первую проверку Liveness с помощью Mobile SDK (iOS или Android) и Oz API.

Описанный поток сочетает два подхода.

* **Режим съемки** – Mobile SDK снимает видео для Liveness в приложении, а бэкенд клиента передает его в Oz API. Результаты съемки не отправляются из SDK напрямую в Oz API: бэкенд клиента выступает посредником.
* **OzCapsula** – проприетарный зашифрованный бинарный контейнер. SDK упаковывает в него снятые медиаданные; расшифровать и обработать его может только Oz API. На стороне клиента это непрозрачный blob — его нельзя декодировать или просмотреть, можно только передать в исходном виде.

Каждый шаг помечен:

* **\[Oz SDK]** — вызов нашего SDK API.
* **\[Ваш код]** — вызов вашего бэкенда или вашего кода.

`{{host}}` в примерах ниже — базовый URL вашего развертывания Oz API, который предоставляется вам при настройке.

## Требования

| Компонент                 | Минимальная версия |
| ------------------------- | ------------------ |
| Oz API                    | **6.6.1**          |
| iOS, Android, Flutter SDK | **10.0.0**         |

Используйте `Content-Type: application/octet-stream` для любого контейнера, отправляемого в Oz API.

Прежде чем начать, убедитесь, что у вас есть учетные данные Oz API. При работе с SaaS API они предоставляются [нами](mailto:info@ozforensics.com):

{% code title="" expandable="true" %}

```
Login: j.doe@yourcompany.com
Password: …
API: https://<link>.com
Web Console: https://<link>.com
```

{% endcode %}

Для on-premise Oz API вам нужно создать пользователя самостоятельно или попросить команду, которая управляет API. См. [руководство по созданию пользователя через Web Console](https://doc.ozforensics.com/oz-knowledge/guides/user-guide/oz-webui/webui-users#adding-a-user). Учитывайте подходящую роль пользователя (`CLIENT` в большинстве случаев или `CLIENT ADMIN`, если SDK будет работать с папками, созданными другими пользователями API). В итоге вам нужно получить набор учетных данных, аналогичный тому, что вы получили бы в сценарии SaaS.

Также рекомендуем использовать наш сервис логирования под названием telemetry, поскольку он значительно помогает в исследовании деталей атак. Для пользователей Oz API сервис включен по умолчанию. Для on-premise установок мы предоставим вам учетные данные.

Oz Liveness Mobile SDK требует лицензию. Лицензия привязана к `bundle_id` вашего приложения, например, `com.yourcompany.yourapp`.

## Инструкции

{% stepper %}
{% step %}

### (ваш код) Добавьте SDK в ваш проект

<details>

<summary>Android</summary>

В build.gradle вашего проекта добавьте:

```kotlin
allprojects {
    repositories {
        maven { url "https://ozforensics.jfrog.io/artifactory/main" }
    }
}
```

В build.gradle модуля добавьте:

```kotlin
dependencies {
    implementation 'com.ozforensics.liveness:full:<version>'
    // You can find the version needed in the Android changelog
}
```

</details>

<details>

<summary>iOS</summary>

[**CocoaPods**](https://cocoapods.org/)

Чтобы интегрировать OZLivenessSDK в проект Xcode, добавьте в Podfile:

```swift
pod 'OZLivenessSDK', :git => 'https://gitlab.com/oz-forensics/oz-mobile-ios-sdk', :tag => '<version>' // You can find the version needed in  iOS changelog

```

**SPM**

Добавьте следующие зависимости пакета через SPM: <https://gitlab.com/oz-forensics/oz-mobile-ios-sdk> (если вам нужно руководство по добавлению зависимостей пакета, см. [документацию Apple](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app)). **OzLivenessSDK** обязателен. Пропустите файл **OzLivenessSDKOnDevice**.

</details>

<details>

<summary>Flutter</summary>

Добавьте в pubspec.yaml:

```dart
ozsdk: ^10.0.0
```

</details>
{% endstep %}

{% step %}

### (Oz SDK) Инициализируйте SDK с лицензией

Вызывается один раз при запуске приложения.

<details>

<summary>Android</summary>

```kotlin
OzLivenessSDK.init(
    context,
    listOf(LicenseSource.LicenseAssetId(R.raw.forensics)),
    object : StatusListener<LicensePayload> {
        override fun onSuccess(result: LicensePayload) {
            // SDK ready — proceed to Step 3
        }
        override fun onError(error: OzException) {
            // handle license error
        }
    }
)
```

#### Подключите telemetry receiver

После загрузки лицензии укажите SDK на Oz telemetry receiver, чтобы он мог отправлять телеметрию. Передайте хост событий вместе с сервисным токеном.

```kotlin
val eventsConnection = OzConnection.fromServiceToken(
    host = "<telemetry-receiver-host>",
    token = "<telemetry-service-token>"
)

OzLivenessSDK.setEventsConnection(eventsConnection, object : StatusListener<String?> {
    override fun onSuccess(accessToken: String?) {
        // telemetry receiver ready — proceed to Step 2
    }
    override fun onError(error: OzException) {
        // handle events connection error
    }
})
```

Хост событий и сервисный токен предоставляются Oz Forensics. Они отличаются от `session_token`, используемого для съемки на шаге 2.

</details>

<details>

<summary>iOS</summary>

```swift
OZSDK(licenseSources: [.licenseFilePath(path)]) { licenseData, error in
    if let error = error {
        // handle license error
    } else {
        // SDK ready — proceed to Step 3
    }
}
```

Опциональная настройка языка:

```swift
OZSDK.set(languageBundle: Bundle(for: OzSdk.self))
OZSDK.localizationCode = (lang == "en") ? .en : .custom(lang)
```

#### Подключите telemetry receiver

После загрузки лицензии укажите SDK на Oz telemetry receiver, чтобы он мог отправлять телеметрию. Передайте хост событий вместе с сервисным токеном.

```swift
let eventsConnection = Connection.fromServiceToken(
    host: "<telemetry-receiver-host>",
    token: "<telemetry-service-token>",
    sslPins: nil
)

OZSDK.setEventsConnection(eventsConnection) { accessToken, error in
    if let error = error {
        // handle events connection error
    } else {
        // telemetry receiver ready — proceed to Step 2
    }
}
```

Хост событий и сервисный токен предоставляются Oz Forensics. Они отличаются от `session_token`, используемого для съемки на шаге 2.

</details>

<details>

<summary>Flutter</summary>

```dart
await OZSDK.initSDK([<% license path and license file name %>]);
```

#### Connect the telemetry receiver

После загрузки лицензии укажите SDK на Oz telemetry receiver, чтобы он мог отправлять телеметрию. Передайте хост событий вместе с сервисным токеном.

```dart
await OZSDK.setEventConnectionWithToken(<telemetry-receiver-token>, <telemetry-receiver-host>);
```

Хост событий и сервисный токен предоставляются Oz Forensics. Они отличаются от `session_token`, используемого для съемки на шаге 2.

</details>

{% hint style="warning" %}
На стадии разработки анализ может быть отклонен из-за непрохождения проверки безопасности в SDK — например, если включен режим отладки. Если при разработке или тестировании вам необходимо запустить SDK в этом режиме, вы можете снизить строгость проверок: установите окружение `debug`, как показано ниже:

<details>

<summary>Android</summary>

```kotlin
OzLivenessSDK.setEnvironment(OzLivenessSDK.Environment.DEBUG)
```

</details>

<details>

<summary>iOS</summary>

```swift
OZSDK.setEnvironment(environment: .debug)
```

</details>

<details>

<summary>Flutter</summary>

```dart
OZSDK.setEnvironment(debug)
```

</details>

Окружение `debug` должно использоваться исключительно в целях разработки или тестирования. Все релизные сборки должны использовать значение по умолчанию, при котором все проверки безопасности работают в полной мере: `PRODUCTION` (Android), `.prod` (iOS), `production` (Flutter).
{% endhint %}
{% endstep %}

{% step %}

### (ваш код) Получите `session_token` из вашего бэкенда

OzCapsula использует session token. Токен обязателен для OzCapsula.

Ваш бэкенд запрашивает session token у Oz API. Запрашивайте его перед каждой сессией съемки — как можно ближе к моменту, когда пользователь начинает съемку.

**Эндпойнт:**

```
GET {{host}}/api/authorize/session_token
```

**Запрос:**

```shell
curl --location -g '{{host}}/api/authorize/session_token' \
--header 'X-Forensic-Access-Token: {{access_token}}' ## опционально, зависит от настроек установки
```

Механизм авторизации при вызовах Oz API (`access_token`) зависит от настроек вашей установки: при использовании Instant API вы можете интегрировать его в рамках собственной схемы авторизации. Если в вашем случае требуется токен доступа, см. раздел [Аутентификация](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/use-cases/authentication). Также обратите внимание, что [токен доступа и session token — это разные токены, выдаваемые для разных целей](https://doc.ozforensics.com/oz-knowledge/other/faq#what-is-the-difference-between-access_token-and-session_token).

**Ответ:**

```json
{
  "session_token": "<session_token>"
}
```

Токен краткосрочный (время жизни по умолчанию: 900 секунд для API Full, 120 секунд для Instant API). Запрашивайте один токен на сессию съемки и сразу же передавайте его в SDK — не кэшируйте и не используйте повторно в разных сессиях.
{% endstep %}

{% step %}

### (Oz SDK) Запустите экран съемки с `session_token`

Передайте `session_token` из **шага 3**.

<details>

<summary>Android</summary>

```kotlin
val captureRequest = CaptureRequest(
    listOf(
        AnalysisProfile(
            Analysis.Type.QUALITY,
            listOf(MediaRequest.ActionMedia(OzAction.Blank))
        )
    )
)
val intent = OzLivenessSDK.createMediaCaptureScreen(captureRequest, sessionToken)
startActivityForResult(intent, REQUEST_LIVENESS_CONTAINER)
```

</details>

<details>

<summary>iOS</summary>

```swift
let mediaRequest = MediaRequest.action(.selfie)

let profile = AnalysisProfile(
    mediaList: [mediaRequest],
    type: .quality,
    params: [:]
)

let request = CaptureRequest(
    analysisProfileList: [profile],
    cameraPosition: .front
)

let livenessVC = try OZSDK.createMediaCaptureScreen(
    delegate,
    request,
    sessionToken: sessionToken          // from Step 3
)

livenessVC.modalPresentationStyle = .fullScreen
parent.present(livenessVC, animated: true)
```

</details>

<details>

<summary>Flutter</summary>

```dart
final sessionToken = await _getSessionToken();
OZSDK.createMediaCaptureScreen(
  CaptureRequest(
    analysisProfileList: [
      AnalysisProfile(
        Type.quality,
        mediaList: [ActionMedia(VerificationAction.blank)]
      ),
    ],
  ),
  sessionToken,
);
```

</details>
{% endstep %}

{% step %}

### (Oz SDK) Получите контейнер OzCapsula

{% hint style="info" %}
Контейнер — это **непрозрачный зашифрованный blob**. Вы **не можете** декодировать его, просматривать его содержимое или извлекать из него отдельные изображения, кадры или метаданные на стороне клиента. Контейнер необходимо передать на бэкенд в исходном виде. Если вам нужен доступ к снятым изображениям (например, к [лучшему кадру](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/basic-scenarios/liveness/best-shot)), получайте их из ответа Oz API на стороне сервера.
{% endhint %}

<details>

<summary>Android</summary>

Извлеките `DataContainer` из result intent.

```kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_LIVENESS_CONTAINER) {
        when (resultCode) {
            OzLivenessResultCode.SUCCESS -> {
                val container = OzLivenessSDK.getContainerFromIntent(data)
                handleContainer(container)
            }
            OzLivenessResultCode.USER_CLOSED_LIVENESS -> { /* user closed the screen */ }
            else -> {
                val errorMessage = OzLivenessSDK.getErrorFromIntent(data)
                /* show error */
            }
        }
    }
}
```

</details>

<details>

<summary>iOS</summary>

Реализуйте delegate. При успехе вы получаете `DataContainer`.

```swift
extension YourViewController: LivenessDelegate {
    func onResult(container: DataContainer) {
        // Pass container to Step 6
        handleContainer(container)
    }

    func onError(status: OZVerificationStatus?) {
        // user cancelled, capture failed, etc.
    }
}
```

</details>

<details>

<summary>Flutter</summary>

Подпишитесь на `livenessResult`; контейнер вернется в формате `DataContainer`.

```dart
@override
void initState() {
  super.initState();
  _subscription = OZSDK.livenessResult.listen((result) {
    if (result is DataContainer) {
      // OzCapsula media
    }
  }, onError: (Object error) {
    // handle error, in most cases PlatformException
  });
}
```

</details>
{% endstep %}

{% step %}

### (ваш код) Отправьте контейнер в Oz API

Отправьте зашифрованный контейнер на ваш собственный бэкенд в виде необработанных байтов (`application/octet-stream`). Ваш бэкенд передает его в Oz API и возвращает ответ с результатом анализа.

{% hint style="warning" %}
**Не** запускайте анализ напрямую из мобильного SDK. При интеграции со встроенным SDK вызов анализа должен проходить через ваш собственный бэкенд.
{% endhint %}

<details>

<summary>Android</summary>

{% hint style="info" %}
Код ниже приведен для иллюстрации — `YourBackend.uploadOzContainer(...)` **не** является частью Oz SDK. Вы реализуете этот метод самостоятельно в клиенте вашего бэкенда; Oz SDK не предоставляет хелпер для загрузки для этого пути интеграции.
{% endhint %}

```kotlin
private fun handleContainer(container: DataContainer?) {
    if (container == null) return

    YourBackend.uploadOzContainer(
        container = container.getBytes(),
        contentType = "application/octet-stream"
    )
}
```

{% hint style="warning" %}
**Не** вызывайте `AnalysisRequest.Builder().addContainer(...).build().run(...)`.
{% endhint %}

</details>

<details>

<summary>iOS</summary>

{% hint style="info" %}
Код ниже приведен для иллюстрации — `YourBackend.uploadOzContainer` **не** является частью Oz SDK. Вы реализуете этот метод самостоятельно в клиенте вашего бэкенда; Oz SDK не предоставляет хелпер для загрузки для этого пути интеграции.
{% endhint %}

```swift
func handleContainer(_ container: DataContainer) {
    YourBackend.uploadOzContainer(
        container,
        contentType: "application/octet-stream"
    ) { result in
        DispatchQueue.main.async {
            self.handleAnalysisResult(result)
        }
    }
}
```

{% hint style="warning" %}
**Не** вызывайте `AnalysisRequestBuilder` / `AnalysisRequest.run(...)`.
{% endhint %}

</details>

<details>

<summary>Flutter</summary>

{% hint style="info" %}
Код ниже приведен для иллюстрации — `YourBackend.uploadOzContainer` **не** является частью Oz SDK. Вы реализуете этот метод самостоятельно в клиенте вашего бэкенда; Oz SDK не предоставляет хелпер для загрузки для этого пути интеграции.
{% endhint %}

```dart
final Uint8List? containerBytes = await OZSDK.getContainerBytes(dataContainer);
await YourBackend.uploadOzContainer(
        container: containerBytes,
        contentType: "application/octet-stream",
      );
```

{% hint style="warning" %}
**Не** вызывайте `OZSDK.analyze`.
{% endhint %}

</details>

Ваш бэкенд передает контейнер в Oz API в одном синхронном `POST`-запросе.

**Эндпойнт (Instant API):**

```
POST {{host}}/api/instant/folders/
```

**Эндпойнт (Full API):**

```
POST {{host}}/api/folders/
```

**Заголовки:**

* `Content-Type: application/octet-stream` (обязательно).
* `X-Forensic-Access-Token: {{access_token}}` (опционально, зависит от настроек установки).

**Тело запроса:** необработанные байты контейнера — без JSON-обертки, без multipart, без base64.

**Запрос (Instant API):**

```shell
curl -X POST '{{host}}/api/instant/folders/' \
  --header 'X-Forensic-Access-Token: {{access_token}}' ## опционально, зависит от настроек установки
  -H 'Content-Type: application/octet-stream' \
  --data-binary '@/path/to/container.dat'
```

**Запрос (Full API):**

```shell
curl -X POST '{{host}}/api/folders/' \
  --header 'X-Forensic-Access-Token: {{access_token}}' ## опционально, зависит от настроек установки
  -H 'Content-Type: application/octet-stream' \
  --data-binary '@/path/to/container.dat'
```

**При успехе:** HTTP `201` с JSON-объектом папки, описывающим результат. Перейдите к **шагу 7**.

**При ошибке на уровне контейнера:** HTTP `400` — см. раздел [Исключения](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/ozcapsula-data-container#exceptions).

Это руководство охватывает анализ Liveness. Чтобы выполнить анализ сопоставления лиц в рамках OzCapsula-потока, см. [Выполнение анализа сопоставления лиц в OzCapsula-потоке](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/ozcapsula-data-container/how-to-perform-a-face-matching-analysis-within-ozcapsula-flow).
{% endstep %}

{% step %}

### (ваш код) Обработайте ответ

Ваш бэкенд получает ответ — JSON-объект папки с описанием результата.

Принимайте решение о допуске или отклонении только на основе этих двух полей верхнего уровня:

```json
$.resolution_status   // "FINISHED" — анализы завершены; "FAILED" — ошибка при обработке анализа
$.system_resolution   // "SUCCESS", "DECLINED" или "FAILED"
```

Интерпретация значений `system_resolution`:

* `SUCCESS` — все проверки пройдены; пользователь допускается.
* `DECLINED` — одна или несколько проверок не пройдены; пользователь отклоняется.
* `FAILED` — системная ошибка не позволила завершить хотя бы один анализ; не следует трактовать как допуск или отклонение.

Для получения результатов по каждому анализу отдельно (например, чтобы узнать, прошла ли проверка Liveness, но не прошло сопоставление лиц) читайте `$.analyses[*].resolution_status` со значениями, описанными выше.
{% endstep %}
{% endstepper %}

## Чеклист

* [ ] iOS, Android, Flutter SDK **10.0.0+**, Oz API **6.6.1+**.
* [ ] Инициализация лицензии на шаге 2 выполняется один раз при запуске приложения.
* [ ] Шаг 2 также подключает telemetry receiver с хостом событий и сервисным токеном.
* [ ] Шаг 3 получает свежий `session_token` из вашего бэкенда перед каждой съемкой.
* [ ] Шаг 4 передает этот `session_token` в `createMediaCaptureScreen(...)`.
* [ ] Шаг 5 возвращает `DataContainer`; попыток декодировать или просмотреть изображения на устройстве нет.
* [ ] Шаг 6 загружает контейнер на ваш собственный бэкенд как `application/octet-stream`, и ваш бэкенд передает его в `POST {{host}}/api/instant/folders/` (Instant API) или `POST {{host}}/api/folders/` (Full API).
* [ ] Анализ никогда не запускается напрямую из мобильного SDK.
* [ ] Решение о допуске или отклонении принимается только на основе `resolution_status` и `system_resolution`.
* [ ] `session_token` никогда не кэшируется, не используется повторно и не передается между сессиями или пользователями.

Шаги выше помогут вам в базовой интеграции наших мобильных SDK в ваше приложение. Чтобы получить доступ к снятым видео и результатам анализов, воспользуйтесь [веб-консолью](/oz-knowledge-ru/rukovodstva/rukovodstvo-polzovatelya/oz-webui.md) или API-запросами (для API Full).

В руководстве разработчика вы также найдете инструкции по настройке внешнего вида SDK и список методов SDK:

| [Образец кода](https://gitlab.com/oz-forensics/oz-liveness-android) для Android                                         | [Образец кода](https://gitlab.com/oz-forensics/public/oz-liveness-ios-sample/-/tree/main) для iOS               | [Образец кода](https://gitlab.com/oz-forensics/oz-mobile-flutter-plugin/-/tree/develop/example?ref_type=heads) для Flutter |
| ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| [Руководство разработчика](/oz-knowledge-ru/rukovodstva/rukovodstvo-razrabotchika/sdk/oz-mobile-sdk/android.md) Android | [Руководство разработчика](/oz-knowledge-ru/rukovodstva/rukovodstvo-razrabotchika/sdk/oz-mobile-sdk/ios.md) iOS | [Руководство разработчика](/oz-knowledge-ru/rukovodstva/rukovodstvo-razrabotchika/sdk/oz-mobile-sdk/flutter.md) Flutter    |
| [Demo app](https://play.google.com/store/apps/details?id=com.ozforensics.liveness.demo\&hl=en) в PlayMarket             | [Demo app](https://testflight.apple.com/join/mBbPQqnM) в TestFlight                                             |                                                                                                                            |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://doc.ozforensics.com/oz-knowledge-ru/obshaya-informaciya/kratkie-rukovodstva-po-integracii/kak-integrirovat-oz-liveness-v-vashe-mobilnoe-prilozhenie.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
