Перейти к содержанию

Flow WebView

Описание#

Flow Webview - это способ интеграции сервиса биометрии в приложение клиентской стороны с использованием готового решения в виде WebView библиотеки.

Использование данного способа интеграции предполагает получение сессии по созданному flow и последующее перенаправление конечного пользователя на наш сервер.

Примечание

На данный момент интеграция происходит полностью со стороны клиента. Ниже приведены примеры кода для Flutter, iOS (Swift) и Android (Kotlin).

Этапы:#

1. Создание Flow#

Первым делом необходимо создать Flow в личном кабинете. Для этого перейдите по данной ссылке.

После того, как flow будет создан необходимо воспользоваться его API KEY на втором этапе. Найти API KEY можно на странице со списком созданных Flow.

Упрощенный пример таблицы flows личного кабинета:
Наименование Flow API KEY Технологии
Flow для всех TTpme201NiUMBA... LD, F2F
Flow для физ. лиц Efy202XKbVAWRu... LD, ED, F2F
Flow для юр. лиц UuuH4V1XC-M9je... LD, DR, F2F

Примечание

В последующих примерах будет использоваться первый Flow из таблицы выше: "Flow для всех".

Также, для наглядности используется укороченная длина API KEY. Его фактическая длина состовляет 47 и более символов.

2. Создание Сессии#

Для работы с сервисом нужно создать сессию. Для этого необходимо отправить POST-запрос на создание сессии используя API KEY созданного flow. Подробнее про сессии можно прочитать здесь.

В запросах к нашему сервису используются только данные в формате JSON. Ответные данные от сервиса будут представлены в том же формате.

URL запроса:

https://kyc.biometric.kz/api/v1/flows/session/create/

Формат запроса Метод запроса
JSON POST

API KEY необходимо передать в теле запроса:

Наименование поля Тип Обязательно Описание
api_key String Да API KEY созданного flow в личном кабинете

Примеры запроса:

curl --location --request POST 'https://kyc.biometric.kz/api/v1/flows/session/create/' \
--header 'Content-Type: application/json' \
--data-raw '{
"api_key": "YOUR_FLOW_API_KEY"
}'
import requests
import json

url = "https://kyc.biometric.kz/api/v1/flows/session/create/"

payload = json.dumps({
  "api_key": "YOUR_FLOW_API_KEY",
})
headers = {
  'Content-Type': 'application/json',
}

response = requests.request(
  "POST",
  url=url,
  headers=headers,
  data=payload,
)

print(response.json())
const apiKey = 'YOUR_FLOW_API_KEY' // flow api key

fetch('https://kyc.biometric.kz/api/v1/flows/session/create/', {
    method: 'POST',
    body: JSON.stringify({
        api_key: apiKey
    })
})
.then(response => response.json())
.then(data => {
    const { session_id, technologies } = data
})
const apiKey: string = 'YOUR_FLOW_API_KEY' // flow api key

interface SessionResponseData {
    session_id: string,
    technologies: string[]
}

fetch('https://kyc.biometric.kz/api/v1/flows/session/create/', {
    method: 'POST',
    body: JSON.stringify({
        api_key: apiKey
    })
})
.then(response => response.json())
.then(data => {
    const { session_id, technologies }: SessionResponseData = data
})

В качестве ответа придет JSON со следующими полями:

  • session_id - одноразовый идентификатор сессии для прохождения flow;
  • technologies - упорядоченный набор технологий flow.

Пример ответа:

{
  "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "technologies": ["LDHP", "F2F"]
}

Время жизни сессии

Сессия действительна 30 минут с момента создания. Если пользователь не успел пройти верификацию — необходимо создать новую сессию.

Создавайте сессию на вашем сервере

Сессию необходимо создавать на стороне вашего сервераAPI KEY не должен попадать в клиентский код мобильного приложения.

3. Метаданные Сессии#

При создании Flow можно включить определенные метаданные, которые способствуют созданию более гибкого и динамичного процесса, а также упрощают процедуру верификации.

API KEY и metadata необходимо передать в теле запроса. Значением для metadata является объект который включает в себя объект c названием технологии (face2face, edocument) состоящий из метаданных для технологии.

Наименование поля Тип Обязательно Описание
api_key String Да API KEY созданного flow в личном кабинете
metadata Нет Метаданные сесии

Метаданные для технологии Face2Face:

Наименование поля Тип Обязательно Описание
face2face Нет Метаданные для технологии Face2Face
photo1 String Нет Фотография, используемая для сличения в формате base64

Метаданные для технологии E-Document:

Наименование поля Тип Обязательно Описание
edocument Нет Метаданные для технологии E-Document
iin Нет Поле ИИН
phone Нет Поле Телефона субъекта, формат: 77*********
value String Нет Значение полей
changeable Boolean Нет Возможность изменить данные пользователем
skip_input Boolean Нет Пропустить ввод данных пользователем (Перейти на шаг ввода OTP-кода)

Взаимозаменяемые технологии

Список взаимозаменяемых технологий (interchangeable_techs) настраивается на уровне конфигурации Flow в личном кабинете, а не в метаданных сессии.

Примечание

Если метаданные отсутствуют, то Flow будет протекать по умолчанию.

3.1. Метаданные для Face2Face#

При наличии фотографии лица пользователя, есть возможность передавать фотографию в метаданных сесии. Это позволяет упростить и сократить процесс верификации и предотвратить ошибки, которые могли бы возникнуть при прохождении верификации пользователем. Необходимо передавать в теле запроса поле metadata.

Необходимо учесть

Метаданные технологии Face2Face будут работать только для следующих Flow:

  • Liveness + Face2Face
  • E-Docuemnt + Face2Face
  • Document Recognition + Face2Face
Наименование поля Тип Обязательно Описание
face2face Нет Метаданные для технологии Face2Face
photo1 String Нет Фотография, используемая для сличения в формате base64

URL запроса:

https://kyc.biometric.kz/api/v1/flows/session/create/

Формат запроса Метод запроса
JSON POST

Примеры запроса:

curl --location --request POST 'https://kyc.biometric.kz/api/v1/flows/session/create/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "api_key": "YOUR_FLOW_API_KEY",
    "metadata": {
        "face2face": {
            "photo1": "base64"
        }
    }
}'
import requests
import json
import base64

url = "https://kyc.biometric.kz/api/v1/flows/session/create/"

with open('path/to/your/image.jpg', 'rb') as image_file:
    encoded_image = base64.b64encode(image_file.read()).decode('utf-8')

payload = json.dumps({
    "api_key": "YOUR_FLOW_API_KEY",
    "metadata": {
        "face2face": {
            "photo1": encoded_image,
        },
    },
})

headers = {
    'Content-Type': 'application/json',
}

response = requests.post(url=url, headers=headers, data=payload)

print(response.json())
const apiKey = 'YOUR_FLOW_API_KEY';
const imageFilePath = 'path/to/your/image.jpg'; // Путь к вашему изображению

const fs = require('fs');
const path = require('path');

const imageBase64 = fs.readFileSync(path.resolve(imageFilePath), { encoding: 'base64' });

const payload = {
    api_key: apiKey,
    metadata: {
        face2face: {
            photo1: imageBase64
        }
    }
};

fetch('https://kyc.biometric.kz/api/v1/flows/session/create/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
    const { session_id, technologies } = data;
})

В качестве ответа придет JSON со следующими полями:

  • session_id - одноразовый идентификатор сессии для прохождения flow;
  • technologies - упорядоченный набор технологий flow.

Пример ответа:

{
  "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "technologies": ["LDHP", "F2F"]
}

3.2. Метаданные для E-Document#

При наличии таких данных о пользователе, как ИИН, номер телефона, наличие в Базе Мобильных Граждан. Есть возможность передать их на форму ввода при прохождении технологии EDocument. Это позволяет упростить и сократить процесс верификации и предотвратить ошибки, которые могли бы возникнуть при вводе данных пользователем. Необходимо передавать в теле запроса поле metadata.

Примечание

Поле skip_input по умолчанию имеет значение false. (Шаг ввода данных не пропускается) Поле changeable по умолчанию имеет значение true. (Пользователь может изменить значения поля ввода данных)

Наименование поля Тип Обязательно Описание
edocument Нет Метаданные для технологии E-Document
iin Нет Поле ИИН
phone Нет Поле Телефона субъекта, формат: 77*********
value String Нет Значение полей
changeable Boolean Нет Возможность изменить данные пользователем
skip_input Boolean Нет Пропустить ввод данных пользователем

URL запроса:

https://kyc.biometric.kz/api/v1/flows/session/create/

Формат запроса Метод запроса
JSON POST

Case №1:

Примечание

Пропустить шаг ввода данных для пользователя, возможно только если:
1) Был передан value для iin и phone
2) Был передан changeable для iin и phone со значением false
3) Был передан skip_input со значением true

У пользователя будет пропущен шаг ввода данных. Пользователь будет перенаправлен на шаг ввода OTP-кода от 1414.

curl --location --request POST 'https://kyc.biometric.kz/api/v1/flows/session/create/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "api_key": "YOUR_FLOW_API_KEY",
    "metadata": {
        "edocument": {
            "iin": {
                "value": "<subjects_iin>",
                "changeable": false,
            },
            "phone": {
                "value": "<subjects_phone>",
                "changeable": false,
            },
            "skip_input": true
        }
    }
}'
import requests
import json

url = "https://kyc.biometric.kz/api/v1/flows/session/create/"

payload = json.dumps({
    "api_key": "YOUR_FLOW_API_KEY",
    "metadata": {
        "edocument": {
            "iin": {
                "value": "<subjects_iin>",
                "changeable": False,
            },
            "phone": {
                "value": "<subjects_phone>",
                "changeable": False,
            },
            "skip_input": True
        },
})

headers = {
    'Content-Type': 'application/json',
}

response = requests.post(url=url, headers=headers, data=payload)

print(response.json())
const apiKey = 'YOUR_FLOW_API_KEY'; // Ключ API вашего процесса
const subjectsIIN = '123456789012'; // Пример ИИН пользователя
const subjectsPhone = '77*********'; // Пример номера телефона пользователя

fetch('https://kyc.biometric.kz/api/v1/flows/session/create/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        api_key: apiKey,
        metadata: {
            edocument: {
                iin: {
                    value: subjectsIIN,
                    changeable: false,
                },
                phone: {
                    value: subjectsPhone,
                    changeable: false,
                },
                skip_input: true
            }
        }
    })
})
    .then(response => response.json())
    .then(data => {
        const { session_id, technologies } = data;
    })

Case №2:

У пользователя на шаге ввода данных в полях ввода будут значения переданные в metadata, без возможности изменить ИИН.

Примечание

У changeable по умолчанию значение true.
У skip_input по умолчанию значение false.

curl --location --request POST 'https://kyc.biometric.kz/api/v1/flows/session/create/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "api_key": "YOUR_FLOW_API_KEY",
    "metadata": {
        "edocument": {
            "iin": {
                "value": "<subjects_iin>",
                "changeable": false,
            },
            "phone": {
                "value": "<subjects_phone>",
            }
        }
    }
}'
import requests
import json

url = "https://kyc.biometric.kz/api/v1/flows/session/create/"

payload = json.dumps({
    "api_key": "YOUR_FLOW_API_KEY",
    "metadata": {
        "edocument": {
            "iin": {
                "value": "<subjects_iin>",
                "changeable": False,
            },
            "phone": {
                "value": "<subjects_phone>",
            },
        },
})

headers = {
    'Content-Type': 'application/json',
}

response = requests.post(url=url, headers=headers, data=payload)

print(response.json())
const apiKey = 'YOUR_FLOW_API_KEY'; // Ключ API вашего процесса
const subjectsIIN = '123456789012'; // Пример ИИН пользователя
const subjectsPhone = '77*********'; // Пример номера телефона пользователя

fetch('https://kyc.biometric.kz/api/v1/flows/session/create/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        api_key: apiKey,
        metadata: {
            edocument: {
                iin: {
                    value: subjectsIIN,
                    changeable: false,
                },
                phone: {
                    value: subjectsPhone,
                }
            }
        }
    })
})
    .then(response => response.json())
    .then(data => {
        const { session_id, technologies } = data;
    })

Case №3:

У пользователя на шаге ввода данных в полях ввода будут значения переданные в metadata, есть возможность изменить ИИН и номер телефона.

curl --location --request POST 'https://kyc.biometric.kz/api/v1/flows/session/create/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "api_key": "YOUR_FLOW_API_KEY",
    "metadata": {
        "edocument": {
            "iin": {
                "value": "<subjects_iin>",
            },
            "phone": {
                "value": "<subjects_phone>",
            }
        }
    }
}'
import requests
import json

url = "https://kyc.biometric.kz/api/v1/flows/session/create/"

payload = json.dumps({
    "api_key": "YOUR_FLOW_API_KEY",
    "metadata": {
        "edocument": {
            "iin": {
                "value": "<subjects_iin>",
            },
            "phone": {
                "value": "<subjects_phone>",
            },
        },
})

headers = {
    'Content-Type': 'application/json',
}

response = requests.post(url=url, headers=headers, data=payload)

print(response.json())
const apiKey = 'YOUR_FLOW_API_KEY'; // Ключ API вашего процесса
const subjectsIIN = '123456789012'; // Пример ИИН пользователя
const subjectsPhone = '77*********'; // Пример номера телефона пользователя

fetch('https://kyc.biometric.kz/api/v1/flows/session/create/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        api_key: apiKey,
        metadata: {
            edocument: {
                iin: {
                    value: subjectsIIN,
                },
                phone: {
                    value: subjectsPhone,
                }
            }
        }
    })
})
    .then(response => response.json())
    .then(data => {
        const { session_id, technologies } = data;
    })

В качестве ответа придет JSON со следующими полями:

  • session_id - одноразовый идентификатор сессии для прохождения flow;
  • technologies - упорядоченный набор технологий flow.

Пример ответа:

{
  "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "technologies": ["LDHP", "ED", "F2F"]
}

4. Интеграция в приложение#

После получения session_id откройте в WebView следующий URL:

https://remote.biometric.kz/flow/<session_id>?web_view=true

Параметр web_view=true обязателен — он сообщает приложению, что оно запущено внутри WebView. В этом режиме:

  • отключаются браузерные редиректы;
  • по завершении сессии происходит переход на https://remote.biometric.kz/finished;
  • не показывается кнопка «Закрыть» с переходом во внешний браузер.

4.1 Обязательные настройки WebView#

Для корректной работы камеры и видео внутри WebView необходимы следующие настройки:

Настройка Значение Почему важно
allowsInlineMediaPlayback true Видео должно воспроизводиться внутри WebView, а не в полноэкранном плеере
mediaPlaybackRequiresUserGesture false Разрешает автоматический запуск потока с камеры
JavaScript включён Приложение построено на Vue.js
Доступ к камере разрешён Биометрическая верификация требует камеру

4.2 Примеры кода#

import 'package:webview_flutter/webview_flutter.dart';

class BiometricWebView extends StatefulWidget {
  final String sessionId;
  const BiometricWebView({required this.sessionId, super.key});

  @override
  State<BiometricWebView> createState() => _BiometricWebViewState();
}

class _BiometricWebViewState extends State<BiometricWebView> {
  late final WebViewController _controller;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(
        NavigationDelegate(
          onNavigationRequest: (request) {
            if (request.url.startsWith('https://remote.biometric.kz/finished')) {
              Navigator.of(context).pop(); // закрываем WebView
              _fetchResult();              // запрашиваем результат
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      )
      ..loadRequest(
        Uri.parse(
          'https://remote.biometric.kz/flow/${widget.sessionId}?web_view=true&locale=ru',
        ),
      );
  }

  void _fetchResult() {
    // Запросить результат с вашего сервера по session_id
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: WebViewWidget(controller: _controller),
    );
  }
}
import WebKit

class BiometricViewController: UIViewController, WKNavigationDelegate {
    var sessionId: String = ""
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = WKWebViewConfiguration()
        // Обязательно: воспроизведение видео без жеста пользователя
        config.allowsInlineMediaPlayback = true
        config.mediaTypesRequiringUserActionForPlayback = []

        webView = WKWebView(frame: view.bounds, configuration: config)
        webView.navigationDelegate = self
        view.addSubview(webView)

        let url = URL(string: "https://remote.biometric.kz/flow/\(sessionId)?web_view=true&locale=ru")!
        webView.load(URLRequest(url: url))
    }

    func webView(_ webView: WKWebView, decidePolicyFor action: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if let url = action.request.url,
           url.absoluteString.hasPrefix("https://remote.biometric.kz/finished") {
            decisionHandler(.cancel)
            dismiss(animated: true)
            fetchResult()
            return
        }
        decisionHandler(.allow)
    }

    func fetchResult() {
        // Запросить результат с вашего сервера по session_id
    }
}
import android.webkit.WebView
import android.webkit.WebViewClient
import android.webkit.WebChromeClient
import android.webkit.PermissionRequest

class BiometricActivity : AppCompatActivity() {
    private lateinit var webView: WebView
    private val sessionId = "your_session_id"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_biometric)

        webView = findViewById(R.id.webView)
        webView.settings.apply {
            javaScriptEnabled = true
            mediaPlaybackRequiresUserGesture = false
            allowContentAccess = true
        }

        webView.webChromeClient = object : WebChromeClient() {
            override fun onPermissionRequest(request: PermissionRequest) {
                // Разрешаем доступ к камере
                request.grant(request.resources)
            }
        }

        webView.webViewClient = object : WebViewClient() {
            override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
                if (url?.startsWith("https://remote.biometric.kz/finished") == true) {
                    finish() // закрываем Activity
                    fetchResult()
                    return true
                }
                return false
            }
        }

        val url = "https://remote.biometric.kz/flow/$sessionId?web_view=true&locale=ru"
        webView.loadUrl(url)
    }

    private fun fetchResult() {
        // Запросить результат с вашего сервера по session_id
    }
}

Другие платформы

Если ваше приложение написано на другом фреймворке (React Native, .NET MAUI и др.), убедитесь, что в конфигурации WebView включён аналог allowsInlineMediaPlayback (чтобы избежать полноэкранного плеера), а запросы разрешения камеры обрабатываются.

5. Обработка завершения#

По завершении верификации веб-приложение выполняет редирект на страницу:

https://remote.biometric.kz/finished

Отловите этот переход внутри вашего приложения (см. навигационные обработчики в примерах выше), закройте WebView и продолжите свою логику.

6. Получение результата#

После закрытия WebView запросите общий результат по сессии с вашего сервера:

URL запроса:

https://kyc.biometric.kz/api/v1/flows/session/result/

Формат запроса Метод запроса
JSON GET
Параметр Тип Обязательно Описание
session_id String Да Идентификатор flow сессии
flow_api_key String Да API KEY созданного flow в личном кабинете который использовался при создании сессии
curl --location --request GET 'https://kyc.biometric.kz/api/v1/flows/session/result/?session_id=<session_id>&flow_api_key=<flow_api_key>'

Подробное описание полей ответа, LIGHT-эндпоинт (временные URL вместо base64), загрузка видео Liveness и PDF-отчёты описаны в документации Flow Remote.

7. Параметры URL#

Полный список параметров, поддерживаемых в URL WebView:

Параметр Тип Описание
web_view boolean Обязательный. true — режим WebView
locale string Язык интерфейса: 'kz' | 'en' | 'ru' | 'my' | 'de' | 'es' | 'fa' | 'fr' | 'it' | 'ja' | 'kg' | 'ko' | 'pt'
isMobile boolean Принудительно задать тип устройства (true/false)
documentType string Тип документа, например passport (доступные значения зависят от конфигурации Flow)
from_session_id string UUID предыдущей сессии (цепочки сессий)

Пример URL с параметрами:

https://remote.biometric.kz/flow/3fa85f64-5717-4562-b3fc-2c963f66afa6?web_view=true&locale=ru&isMobile=true

8. Возможные ошибки#

Проблема Причина Решение
Камера не запускается (iOS) allowsInlineMediaPlayback = false Установить allowsInlineMediaPlayback = true
Камера не запускается (Android) Разрешения не выданы Реализовать onPermissionRequest в WebChromeClient
Сессия не найдена (404) Неверный session_id Проверить session_id
Ошибка 400 по сессии Сессия истекла (TTL 30 минут) или API-ключ от другого flow Создать новую сессию; проверить соответствие ключа
Белый экран JavaScript отключён Включить javaScriptEnabled = true
Завершение не отловлено Не отслеживается URL finished Добавить проверку в навигационный делегат
Сессия INVALID Fingerprint-ошибка или Too many sessions by fingerprint Создать новую сессию; не открывать один session_id в нескольких WebView