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 в личном кабинете |
Примеры запроса:
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.
Пример ответа:
Время жизни сессии
Сессия действительна 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 |
Примеры запроса:
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.
Пример ответа:
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, есть возможность изменить ИИН и номер телефона.
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.
Пример ответа:
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 |