API Reference
English

活体检测H5

HTML5(H5)版本的活体检测是一个重要的安全防范产品,可以在KYC过程中防止活体欺骗攻击。

该技术被应用于各种PC端和移动端应用中,以确保进行活体检测的是一个真实的人,而不是照片、视频、面具或其他形式的生物特征攻击。

活体检测H5产品,利用浏览器的能力,无需额外的插件或软件安装,即可无缝集成到在线平台中。它利用复杂的算法分析实时视频流中微妙的动作、纹理和其他体现活体真人的指标。

在整个过程中,终端用户需要根据提示做出相应动作,如“张嘴”或“眨眼”等。活体检测的结果可以从后端检索获取。如果检测到真人,会返回一张最佳的人脸自拍照片。

集成步骤

H5活体检测的对接流程图如下:


集成H5活体检测共有3个步骤:

  1. 调用“获取Token API“,配置活体检测后的跳转页面URL和进行其他设置,接口返回Token和H5活体检测URL。
  2. 用户通过步骤1产生的H5活体检测URL,完成活体检测全过程,跳转到相应的成功和失败页面URL。
  3. 调用“获取结果API”在活体检测成功时获取最佳人脸自拍照,或在失败时接收详细原因。

如果是Webview对接,涉及到获取摄像头权限,具体参考“4.获取摄像头权限”部分。

1.获取Token API

Base URL

API

URL请求方式Content Type输出格式字符集
api-base-url?partner_code=xxx&partner_key=xxxPOSTapplication/jsonJSONUTF-8

认证参数

字段类型含义建议备注
partner_codeString合作方标识必须由TD分配
partner_keyString合作方密钥必须由TD分配

请求参数

字段类型含义建议备注
success_redirectString活体检测成功后的跳转页面必须Web页面的URL地址
failure_redirectString活体检测失败后的跳转页面必须Web页面的URL地址
languageString页面上显示的提示文字信息的语言

枚举值:
en: 英语(English)
id: 印尼语(Indonesian)
es: 西班牙语(Espanol)
ar: 阿拉伯语(Arabic)
tl: 菲律宾语(Filipino)
ko: 韩语(Korean)
pt: 葡萄牙语(Portuguese)
ru: 俄语(Russian)
th: 泰语(Thai)
tr: 土耳其语(Turkish)
vi: 越南语(Vietnamese)
kh: 高棉语(Khmer)
zh-Hans: 简体中文(Simplified Chinese)
zh-Hant: 繁体中文(Traditional Chinese)
可选1. 如果指定了语言,则显示该语言提示文字信息。

2. 如果未指定语言,则获取浏览器语言,并显示相应语言提示文字信息。

3. 如果在第2步中未获取语言,或者遇到其他异常情况,则默认显示英语en。
audioBoolean播放动作提示语音

目前支持英语、西班牙语和印尼语,3种语音
可选如果不设置,则默认为false,该情况下不播放提示语音。

如果设置为true:

1. 依照language中设置的提示语言,播放相应提示语音
2. 如果在第1步中未获取语言,或者遇到其他异常情况,则默认播放英语提示音

响应参数

字段类型含义备注
codeIntegerAPI 状态码
messageString状态信息在 API 异常状态下会输出具体的异常原因
sequence_idString响应唯一码用于跟踪每次请求记录的唯一标识
tokenString授权码token授权码用于生成活体检测URL,并用于随后查询此活体检测过程的结果
urlString活体检测url返回H5活体检测URL,格式如下(包含跳转页面URL和配置):
https://static.tongdun.net/liveness/index.html#/progress?code=token&success_redirect=[successURL]&failure_redirect=[failureURL]&language=xx&audio=true&

响应示例

  • 业务请求

    {
    	"success_redirect": "http://www.google.com",
    	"failure_redirect": "http://www.facebook.com",
    	"language": "en",
    	"audio": true
    }
    
  • 业务请求成功

{
    "code": 200,
    "message": "success",
    "sequence_id": "17119500882*****29",
    "token": "a41701e4-b2a2-4f62-8cd4-9******3",
    "url": "https://static.tongdun.net/liveness/index.html#/progress?code=a41701e4-b2a2-4f62-8cd4-9******3&success_redirect=http%3A%2F%2Fwww.google.com&failure_redirect=http%3A%2F%2Fwww.facebook.com&language=en&audio=true&"
}
  • 业务请求失败
{
    "code": 11350,
    "sequence_id": "69b57131b6fb********61ccba118b60",
    "message": "Internal error"
}

2.活体检测和页面跳转

活体完成之后,页面会自动跳转到客户配置的回调页面:成功页[successURL]或者失败页[failureURL]。
回调页面URL样例:
● 成功回调样例:[successURL]?code=7ac3a964-f540-4aa2-acdf-59ea1639572e&state=0
● 失败回调样例:[failureURL]?code=7ac3a964-f540-4aa2-acdf-59ea1639572e&state=2

当页面跳转后,客户回调页面接收到我们活体完成的参数state值为0或2时,就可以去调用获取结果API,查询活体检测结果。state值为非0或2的其他值时,详见以下解释和建议。

state返回值含义后续流程建议
0通过活体验证调用获取结果API,查询活体检测结果
2未通过活体验证,一般是活体攻击调用获取结果API,查询活体检测结果
根据风险偏好,决定是否重新发起活体检测
*如果要重试,需要在第1步调用“获取Token API”重新获取token
3Token无效,表示前面步骤获取的Token已经被使用过重新发起活体检测,请用户重试
4无法打开摄像头 摄像头无法打开,请走其他检验方式,例如人工流程
5、6网络问题重新发起活体检测,请用户重试
其他万一回调state状态码被恶意篡改,例如改为state=8根据风险偏好,选择直接终止流程,或者重新发起活体检测

3.获取结果 API

Base URL

API

URL请求方式Content Type输出格式字符集
api-base-url?partner_code=xxx&partner_key=xxxPOSTapplication/jsonJSONUTF-8

认证参数

字段类型含义建议备注
partner_codeString合作方标识必须由 TD 分配
partner_keyString合作方密钥必须由 TD 分配

请求参数

字段类型含义建议备注
tokenString授权码必须

响应参数

字段类型含义备注
codeIntegerAPI 状态码
messageString状态信息在 API 异常状态下会输出具体的异常原因
sequence_idString响应唯一码用于跟踪每次请求记录的唯一标识
imageString活体检测人脸图片活体检测过程中抓拍到的最佳人脸自拍图片,base64格式;
只有在活体检测成功的情况下才返回最佳人脸自拍图片

API 状态码

CodeMessage是否计费
200success (live person) 成功(真人)
12223No face detected 没有检测到人脸
12224Multiple faces have been detected 多张人脸
12225Detection timeout 检测超时
12226Person change detected 检测到换人
12227Token has been used Token已经被使用
12228User actively cancels liveness detection process 用户主动取消活体检测
12229Liveness detection result not found 没有找到识别结果
12202Identified as a blink attack 判断为抠眼攻击
12203Identified as a mouth movement attack 判断为抠嘴攻击
12204Identified as a partial face attack 判断为半张脸攻击
12205Identified as a video replay attack 判断为视频回放攻击
12206Identified as a black and white image 判断为黑白图片
12207Identified as a paper-based attack 判断为纸面攻击
12208Identified as a frame (including paper or phone frame) 判断为边框(包括纸面、手机等边框)
12209Identified as a moire pattern attack 判断为摩尔纹攻击
12210Identified as a face superiority attack 判断为脸优攻击
12211Identified as a paper-based attack (optical flow) 判断为纸面攻击(光流)
12212Identified as a mask attack 判断为面具攻击
12213Identified as an ID card attack 判断为证卡攻击
12214Identified as a 3D mask attack 判断为3D面具攻击
12215Identified as a synthetic image attack 判断为合成图像攻击
12216Identified as a black-market software attack 判断为黑产软件攻击
12217Identified as a T-type mask attack 判断为T型面具攻击
12218Identified as a blurry image 判断为模糊图片
12219Suspected deepfake image attack 疑似深伪图像攻击
12220Suspected high-resolution screen attack 疑似高清屏幕攻击
12221Light verification failed 光线校验失败
12222Injection attack 注入攻击
12250Verification error 其他活体攻击
11350Internal error 内部错误

响应示例

  • 业务请求

    {
        "token":"a41701e4-b2a2-4f62-8cd4-9******3"
    }
    
  • 业务请求成功

{
    "code": 200,
    "message": "Success",
    "sequence_id": "1711950327713613G109E0A081240931",
    "image": "/9j/4AAFbs64"
}
  • 业务请求失败
{
    "code": 11350,
    "sequence_id": "69b57131b6fb********61ccba118b60",
    "message": "Internal error"
}

4.获取摄像头权限

Android

1. 首先,我们需要在AndroidManifest.xml文件中添加相机权限:

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />

2. 检查和请求权限

在应用中,我们需要先检查是否已经获取了相机权限,如果没有则请求权限:

private boolean checkCameraPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
            return false;
        }
        return true;
    }
}

// 权限已被授予 
if (checkCameraPermission()) {
    mWebView.loadUrl(https://活体地址.com);
}

3. 处理权限请求结果

onRequestPermissionsResult方法中处理权限请求结果:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 
            // 权限已被授予 
					
            mWebView.loadUrl(https://活体地址.com);
        } else { 
            // 权限被拒绝
            Toast.makeText(this, "相机权限被拒绝", Toast.LENGTH_SHORT).show();
        }
    }
}

iOS

1.在 info.plist 文件中设置 NSCameraUsageDescription,以允许应用访问相机:

<key>NSCameraUsageDescription</key> 
<string>$(PRODUCT_NAME) need to use your front camera</string>  

2.配置 Webview 实例以允许内嵌媒体播放,禁用用户操作播放,启用 JavaScript,并启用 JavaScript 自动打开窗口:

let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
configuration.mediaTypesRequiringUserActionForPlayback = []
configuration.preferences.javaScriptEnabled = true
configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
self.webView = WKWebView(frame: self.view.bounds, configuration: configuration)

3.在进入 H5 活体检测之前,先检查并请求相机访问权限:

// Request camera permission (in Swift)
func checkAVAuthorizationStatus(with block: @escaping((_ authed: Bool) -> Void)) {
    let authStatus = AVCaptureDevice.authorizationStatus(for: .video)
    if authStatus == .authorized {
        block(true)
    } else if authStatus == .denied || authStatus == .restricted {
        block(false)
    } else {
        AVCaptureDevice.requestAccess(for: .video) { (result) in
            DispatchQueue.main.async {
                block(result)
            }
        }
    }
}

// Usage
// Check auth status before entering H5 liveness page
self.checkAVAuthorizationStatus { authed in
  if authed {
    // Enter H5 liveness page directly
  } else {
    // Input logic to handle camera unauthorised
  }
}

4.(可选)一次授权,避免每次在 H5 中弹出相机权限请求:

// Ensure your ViewController inherits WKUIDelegate.
class ViewController: UIViewController, WKUIDelegate {
    ...

    // Validated for iOS 15+ only
    @available(iOS 15.0, *)
    func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
        // Please add necessary security check like domain filter if needed.
        decisionHandler(.grant)
    }

}

self.webView.uiDelegate = self