API Reference
English

iOS

集成要求

合规说明

请注意,在贵司的App中集成同盾提供的SDK产品时:

1.1 根据《网络安全法》《电信条例》《电信和互联网用户个人信息保护规定》等相关法律法规要求及监管实践中的标准,在贵司的最终用户首次启动App并在贵司开始采集信息之前,贵司应以交互界面或设计(如隐私政策弹窗等)向最终用户完整告知收集、使用、与第三方共享最终用户个人信息的目的、方式和范围,并征得最终用户的明示同意。

1.2 为向贵司提供业务安全和风控服务,同盾SDK将采集、处理、使用用户的手机终端唯一标志信息(IMEI/IDFA)、Android ID、OAID、IMSI、MEID、MAC 地址、SIM 卡序列号、设备序列号、设备类型、设备型号、系统类型、地理位置、登录 IP 地址等设备信息。为确保贵司使用相关服务的合规性,前述隐私政策应涵盖对同盾SDK提供服务并采集、处理、使用相关信息的授权,以下条款内容供贵司参考,具体表述可由贵司根据贵司隐私协议的整体框架和内容自行确定:

同盾SDK:为了业务安全和风控,我司使用了同盾 SDK,该 SDK 需要获取您的手机终端唯一标志信息(IMEI/IDFA)、Android ID、OAID、IMSI、MEID、MAC 地址、SIM卡序列号、设备序列号、设备类型、设备型号、系统类型、地理位置、登录 IP 地址、应用程序列表、运行中进程信息、传感器(光传感器、重力传感器、磁场传感器、加速度传感器、陀螺仪传感器、心率传感器)相关设备信息,用于设备欺诈风险识别。

同盾隐私协议:https://www.tongdun.cn/other/privacy/id=1

环境要求

条目说明
兼容版本iOS11.0+
支持架构arm64

集成步骤

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

  1. 调用 获取License API,得到一个在时间有效期范围内的License。
  2. 集成安卓和iOS平台SDK,用第1步中的License初始化活体检测流程,并从 TDLivenessResultStruct 结构体中获取 livenessId 。
  3. 调用 获取结果 API,传入第2步中获得的“livenessId”,在活体检测成功时获取最佳人脸自拍照,或在失败时接收详细原因。

1.获取License API

请按 https://cn-doc.trustdecision.com/reference/liveness-api#获取license-api 步骤操作

2.集成SDK

CocoaPods集成

  • 在 Podfile 文件中对应 target 中新增 pod 'TrustDecisionPro', '4.3.1.3'
  • 在 Podfile 文件中对应 target 中使用,若完整版则新增 pod 'TrustDecisionLiveness', '2.4.3' ,若使用精简版则新增 pod 'TrustDecisionLiveness-Lite', '2.4.0'
  • 在 Podfile 所在文件夹中执行 pod install --repo-update 命令 (M1系列mac电脑需要执行 arch -x86_64 pod install --repo-update 命令)

SDK目录

Pods/TrustDecisionPro 文件夹下存在如下文件:

  • TDMobRisk.framework(终端SDK主文件,静态库类型)
  • TDCorePlugin.framework (终端SDK依赖核心插件,Embed动态库类型)

Pods/TrustDecisionLiveness 文件夹下存在如下文件:

  • libTDLiveness.a(终端SDK 活体组件,静态库类型)
  • CWResource.bundle (终端SDK 活体组件需要用到的资源包)

引入头文件

在调用的位置引入头文件

#import <TDMobRisk/TDMobRisk.h>
import TDMobRisk

SDK初始化

注意事项

安装后首次启动时,在用户同意隐私协议后,再进行SDK初始化。

避免出现用户未同意隐私协议就进行SDK初始化采集,引发合规风险事故。

接口定义

void (*initWithOptions)(NSDictionary *options);

initWithOptions方法必传配置

配置 key 定义 说明 场景 示例代码
partner 合作方编码 同盾的合作方编码,请联系同盾运营获取 所有场景 Objective C
[options setValue:@"请输入您的合作方编码" forKey:@"partner"];
Swift
options.updateValue("请输入您的合作方编码" , forKey: "partner")
appKey 应用标识 同盾生成的应用标识,和app绑定,用于校验app的有效性,请联系同盾运营获取。
appkey创建需要用户提供应用包名
所有场景 Objective C
[options setValue:@"请输入您的appKey" forKey:@"appKey"];
Swift
options.updateValue("请输入您的appKey" , forKey: "appKey")
country 国家地区 国家地区参数,如cn sg us fra idna 根据国家地区填写对应参数。
cn代表中国,
sg代表新加坡,
us代表北美,
fra代表欧洲,
idna代表印尼
Objective C
[options setValue:@"请输入您所在的国家地区" forKey:@"country"];
Swift
options.updateValue("请输入您所在的国家地区" , forKey: "country")

示例代码

- (void)initTrustDeviceSDK {
  TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
  NSMutableDictionary *options = [NSMutableDictionary dictionary];

  /*************************** 必传 ***************************/
  // 合作方编码,如tongdun,参考`必传配置`
  [options setValue:@"请输入您的合作方编码" forKey:@"partner"];
    // 同盾平台注册的应用标识,参考`必传配置`
  [options setValue:@"请输入您的appKey" forKey:@"appKey"];
  // 国家地区参数,参考`必传配置`
  [options setValue:@"请输入您所在的国家地区" forKey:@"country"];

  /*************************** 选传 ***************************/
#ifdef DEBUG
  // !!! DEBUG模式下若不设置此参数,app运行会闪退
  [options setValue:@(YES) forKey:@"allowed"];
#endif
  // !!!callback方式已移除,现新增getBlackBoxAsync的方式去异步获取blackbox
  riskManager->initWithOptions(options);
}
func initTrustDecisionSDK() {
  let riskManager = TDMobRiskManager.sharedManager()
  var options = Dictionary<String, Any>()
  /*************************** 必传 ***************************/
  // 合作方编码,如tongdun,参考`必传配置`
  options.updateValue("请输入您的合作方编码", forKey: "partner")
    // 同盾平台注册的应用标识,参考`必传配置`
  options.updateValue("请输入您的appKey", forKey: "appKey")
  // 国家地区参数,参考`必传配置`
  options.updateValue("请输入您所在的国家地区", forKey: "country")

  /*************************** 选传 ***************************/
#if DEBUG
  // !!! DEBUG模式下若不设置此参数,app运行会闪退
  options.updateValue(true, forKey: "allowed");
#endif
  // !!!callback方式已移除,现新增getBlackBoxAsync的方式去异步获取blackbox
  riskManager?.pointee.initWithOptions(options)
}

获取blackBox

注意事项

  • 请在 initWithOptionsgetBlackBoxAsyncgetBlackBox,否则会引起SDK异常
  • 建议开发者不要在App内对返回的blackBox进行缓存,获取blackBox请依赖以下函数

异步方法 getBlackBoxAsync (推荐)

  /* 异步获取blackBox
   */
  void (*getBlackBoxAsync)(TDGetBlackBoxAsyncBlock block);

使用场景说明

优点: 网络正常情况下返回非降级blackBox,会降低后续查询接口上传的数据量,数据量大小为26字节左右;
缺点: 不是立即返回,根据网络情况进行等待,一般耗时300ms左右返回;
适用场景: 需要获取最新且为非降级blackBox的场景;

示例代码

TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
riskManager->getBlackBoxAsync(^(NSString* blackBox){
    NSLog(@"blackBox:%@",blackBox);
});
let riskManager = TDMobRiskManager.sharedManager()
riskManager?.pointee.getBlackBoxAsync(){(blackBoxString : String!)->Void in
   print("blackBox:" + blackBoxString)                               
}

同步方法 getBlackBox

  /* 同步获取blackBox
   * @return 初始化SDK后,同步获取的blackBox
   */
  NSString (*getBlackBox)(void);

使用场景说明

优点: 会立即返回blackBox,不受网络状态的影响;
缺点: 在集成设备指纹SDK后,在之前没有获取到非降级blackBox的情况下,会返回降级blackBox,会增大后续查询接口上传的数据量,数据量大小为5000字节左右;
适用场景: 需要立即获取blackBox的场景;

示例代码

// 同步获取blackBox,未进行SDK初始化禁止调用,否则会引起SDK异常。
TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
NSString *blackBox = riskManager->getBlackBox();
// 同步获取blackBox,未进行SDK初始化禁止调用,否则会引起SDK异常。
let riskManager = TDMobRiskManager.sharedManager()
let blackBox = riskManager?.pointee.getBlackBox()

弹出活体弹窗

initWithOptions方法选传参数

配置 key 定义 完整版 精简版 示例代码
language 语言类型
Default: en 英语 Options:
en 英语
zh-Hans 简体中文
zh-Hant 繁體中文
es 西班牙语
id 印尼语
ar 阿拉伯语
fil 菲律宾语
ko 韩语
pt 葡萄牙语
ru 俄语
th 泰语
tr 土耳其语
vi 越南语
Objective C
[options setValue:@"en" forKey:@"language"];
Swift
options.updateValue("en", forKey: "language")
playAudio 是否播报语音(默认为 NO) Objective C
[options setValue:@NO forKey:@"playAudio"];
Swift
options.updateValue(false, forKey: "playAudio")
livenessDetectionThreshold 活体检测难易度阈值,分为high、medium、low三个等级 默认为medium,中等难度 Objective C
[options setValue:@"medium" forKey:@"livenessDetectionThreshold"];
Swift
options.updateValue("medium", forKey: "livenessDetectionThreshold")
livenessHttpTimeOut SDK网络超时时间配置(单位:秒), 默认为15s Objective C
[options setValue:@8 forKey:@"livenessHttpTimeOut"];
Swift
options.updateValue(8, forKey: "livenessHttpTimeOut")
showReadyPage 启动人脸时,会弹出检测准备页面,默认为 YES, 即显示 Objective C
[options setValue:@YES forKey:@"showReadyPage"];
Swift
options.updateValue(true, forKey: "showReadyPage")
faceMissingInterval 没有检测到人脸时的超时时间 (单位:毫秒),默认为 1000ms Objective C
[options setValue:@(1000) forKey:@"faceMissingInterval"];
Swift
options.updateValue(1000, forKey: "faceMissingInterval")
prepareStageTimeout 准备检测动作时候的起始时间 (单位:秒),默认为 0S, 即永远不超时 Objective C
[options setValue:@(0) forKey:@"prepareStageTimeout"];
Swift
options.updateValue(0, forKey: "prepareStageTimeout")
actionStageTimeout 动作阶段中, 最长验证时间 (单位:秒),默认为 8S Objective C
[options setValue:@(8) forKey:@"actionStageTimeout"];
Swift
options.updateValue(8, forKey: "actionStageTimeout")

showLiveness

showLiveness 函数 用于显示活体界面,其接口定义如下

/**
 * 显示活体窗口的方法
 * @usage: manager->showLiveness(self,^(TDLivenessResultStruct resultStruct) {
 * });
 * @param targetVC 显示活体窗口的VC, UIViewController 类型
 * TrustDecisionLiveness 版本库 将会使用  [targetVC.navigationController pushViewController:livenessVC animated:YES]; 方法显示
 * TrustDecisionLiveness-Lite 版本库 将会使用 [targetVC.view addSubview:[TDLiveness sharedManager].webView]; 方法显示
 * @param block 结果回调
*/
void (*showLiveness)(id targetVC,TDShowLivenessBlock block);

/**
* 显示活体窗口的方法
* @usage: manager->showLivenessWithShowStyle(self,@"Input_your_license",TDLivenessShowStylePresent,^(TDLivenessResultStruct resultStruct) {
* });
* @param targetVC 显示活体窗口的VC, UIViewController 类型,和 @param showStyle 搭配使用
* TrustDecisionLiveness 版本库 将会使用 
* if(showStyle == TDLivenessShowStylePush){[targetVC.navigationController pushViewController:livenessVC animated:YES];}
* else{[targetVC presentModalViewController:livenessVC animated:YES];} 方法显示
*
* TrustDecisionLiveness-Lite 版本库,@param showStyle 不再生效,将会使用 [targetVC.view addSubview:[TDLiveness sharedManager].webView]; 方法显示
* @param license 许可,字符串类型
* @param showStyle 显示风格,TDLivenessShowStylePush 或 TDLivenessShowStylePresent
* @param block 结果回调
*/
void (*showLivenessWithShowStyle)(id targetVC,NSString* _Nullable license,TDLivenessShowStyle showStyle,TDShowLivenessBlock block);

TDLivenessResultStruct

我们使用TDLivenessResultStruct 结构体存储showLiveness方法回调的结果;

typedef enum {
    
    // The Liveness is verified successfully;
    TDLivenessResultTypeSuccess,
    // If the Liveness fails, you can get the error code errorCode and errorMsg, and check the cause according to the `Error code` in the document;
    TDLivenessResultTypeFailed,
    // The Liveness window pop-up is successful, waiting to be verified;
    TDLivenessResultTypeReady,
    
} TDLivenessResultType;

typedef struct _TDLivenessResultStruct{
    // return result type
    const TDLivenessResultType resultType;
    // the seqId of the Liveness returned
    char* const seqId;
    // After the return fails, the returned error code can be checked according to the document
    const NSInteger errorCode;
    // Return error message after failure
    char* const errorMsg;
    // liveness score when success
    double score;
    // best image string
    char* const bestImageString;
    // liveness detection ID
    char* const livenessId;
    
}TDLivenessResultStruct;

示例代码

- (void)showLiveness {
    [TDMobRiskManager sharedManager]->showLivenessWithShowStyle(self,@"Input_your_license",TDLivenessShowStylePush,^(TDLivenessResultStruct resultStruct) {
        // 成功
        if(resultStruct.resultType == TDLivenessResultTypeSuccess){
            // 如果存在最佳照片
            if(resultStruct.bestImageString.length > 0){
                NSData* data = [[NSData alloc]initWithBase64EncodedString:resultStruct.bestImageString options:NSDataBase64DecodingIgnoreUnknownCharacters];
                UIImage * image = [UIImage imageWithData:data];
                UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
            }
            
            NSString*successMsg = [NSString stringWithUTF8String:resultStruct.errorMsg];
            NSLog(@"successMsg-::%@,score:%f,livenessId:%s",successMsg,resultStruct.score,resultStruct.livenessId);
        }
        // 失败
        else{
            NSString*errorMsg = [NSString stringWithUTF8String:resultStruct.errorMsg];
            NSString*errorMsgInfo = [NSString stringWithFormat:@"人脸检测失败,错误码:%ld,错误信息:%@,livenessId:%s",resultStruct.errorCode,errorMsg,resultStruct.livenessId];
            NSLog(@"errorMsgInfo-::%@",errorMsgInfo);
        }
    });
}

    func showLiveness() {
        let targetVC = self
        let riskManager = TDMobRiskManager.sharedManager()
        riskManager?.pointee.showLivenessWithShowStyle(self,"Input_your_license",TDLivenessShowStyle(rawValue: TDLivenessShowStylePush.rawValue)){ (resultStruct : TDLivenessResultStruct!)->Void  in
            // 成功
            if(resultStruct.resultType == TDLivenessResultTypeSuccess){
                let bestImageString : String? = String(utf8String: resultStruct.bestImageString)
                if(bestImageString != nil && bestImageString!.count > 0){
                    let data : Data? = Data(base64Encoded: bestImageString!)
                    if(data != nil){
                        let image : UIImage? = UIImage(data: data!)
                        if(image != nil){
                            UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil);
                        }
                    }
                }
                let successMsg = String(cString:resultStruct.errorMsg)
                let seqId = String(cString:resultStruct.seqId)
                print("人脸识别成功!!!,seqId:\(seqId),successMsg:\(successMsg),score:\(resultStruct.score),livenessId:\(resultStruct.livenessId)")
            }
            // 失败
            else{
                let errorMsg = String(cString:resultStruct.errorMsg)
                print("人脸识别失败,errorCode:\(resultStruct.errorCode),errorMsg:\(errorMsg),livenessId:\(resultStruct.livenessId)")
            }
        }
    }

错误码

代码提示是否计费
200success 成功(真人)
20700No face detected 没有检测到人脸
20702Person change detected 检测到换人
20703Detection timeout 检测超时
20705Screen lock or background exit during detection 检测过程中锁屏或退出后台
20710No camera permission 没有相机权限
20711User actively cancels detection on the preparation page 准备页面用户主动取消检测
20712User actively cancels detection on the detection page 检测页面用户主动取消检测
20723Face too small 人脸偏小
20731Face not centered 人脸未居中
20732Face not facing the screen 人脸未正对屏幕
20749Inconsistent action, tilt head down 动作不一致做出低头动作
60001Network issue, failed to retrieve session 网络问题,无法获取session
60002Network issue, failed to call anti-hack 网络问题,无法调用anti-hack
11350Internal error 内部错误

3.获取结果 API

请按 https://cn-doc.trustdecision.com/reference/liveness-api#获取结果-api 步骤操作

获取SDK版本号

示例代码

// 获取SDK版本号
TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
NSString *sdkVersion = riskManager->getSDKVersion();
// 获取SDK版本号
let riskManager = TDMobRiskManager.sharedManager()
let sdkVersion = riskManager?.pointee.getSDKVersion()

FAQ

Q1:引入终端SDK后,工程无法再进行 Xcode 调试,如何解决?

A1:请参考 SDK初始化 在终端SDK初始化时,加入如下参数

[options setValue:@(YES) forKey:@"allowed"];
options.updateValue(true, forKey: "allowed")