集成要求
合规说明
请注意,在贵司的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个步骤:
- 调用 获取License API,得到一个在时间有效期范围内的License。
- 集成安卓和iOS平台SDK,用第1步中的License初始化活体检测流程,并从 TDLivenessResultStruct 结构体中获取 livenessId 。
- 调用 获取结果 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
注意事项
- 请在
initWithOptions
后getBlackBoxAsync
或getBlackBox
,否则会引起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)")
}
}
}
错误码
代码 | 提示 | 是否计费 |
---|---|---|
200 | success 成功(真人) | 是 |
20700 | No face detected 没有检测到人脸 | 否 |
20702 | Person change detected 检测到换人 | 否 |
20703 | Detection timeout 检测超时 | 否 |
20705 | Screen lock or background exit during detection 检测过程中锁屏或退出后台 | 否 |
20710 | No camera permission 没有相机权限 | 否 |
20711 | User actively cancels detection on the preparation page 准备页面用户主动取消检测 | 否 |
20712 | User actively cancels detection on the detection page 检测页面用户主动取消检测 | 否 |
20723 | Face too small 人脸偏小 | 否 |
20731 | Face not centered 人脸未居中 | 否 |
20732 | Face not facing the screen 人脸未正对屏幕 | 否 |
20749 | Inconsistent action, tilt head down 动作不一致做出低头动作 | 否 |
60001 | Network issue, failed to retrieve session 网络问题,无法获取session | 否 |
60002 | Network issue, failed to call anti-hack 网络问题,无法调用anti-hack | 否 |
11350 | Internal 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")