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

环境要求

条目说明
兼容版本iOS 9.0及以上
支持架构armv7, arm64, x86_64

集成步骤

CocoaPods集成

  • 在 Podfile 文件中对应 target 中新增 pod 'TrustDecisionPro', '4.2.6.3'
  • 在 Podfile 文件中对应 target 中新增 pod 'TrustDecisionCaptcha', '2.1.8.4'
  • 在 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/TrustDecisionCaptcha 文件夹下存在如下文件:

  • libTDCaptcha.a(终端SDK 验证码组件,静态库类型)
  • TDCaptchaResource.bundle (终端SDK 验证码组件需要用到的资源包)

引入头文件

在调用的位置引入头文件

#import <TDMobRisk/TDMobRisk.h>
import TDMobRisk

隐私文件配置

根据苹果公司公布的最新 App Store 隐私政策,自2024年春季开始,上架 App Store 的应用需要携带一份 App 的隐私清单文件。
从 2024 年 5 月 1 日开始,App Store Connect 不接受未在隐私清单文件中描述其使用所需原因 API 的应用程序。

适配措施

请根据您的实际情况选择解决方案进行处理:

  1. 如果工程目录中不存在 PrivacyInfo.xcprivacy 文件
  • 在工程目录下通过 Xcode 新建 App Privacy 类型的文件,命名为 PrivacyInfo,同时勾选需要的 Targets
  • 在工程目录中选中 PrivacyInfo.xcprivacy 文件并右击打开菜单,通过菜单中的 Open As -> Source Code 的方式打开文件
  • 将下方 SDK 的 PrivacyInfo.xcprivacy 粘贴到工程目录的 PrivacyInfo.xcprivacy 文件中
  1. 如果工程目录中已存在 PrivacyInfo.xcprivacy 文件
  • 在工程目录中选中 PrivacyInfo.xcprivacy 文件并右击打开菜单,通过菜单中的 Open As -> Source Code 的方式打开文件
  • 在工程目录 PrivacyInfo.xcprivacy 文件中补充SDK 的 PrivacyInfo.xcprivacy 里有提及但工程目录 PrivacyInfo.xcprivacy 文件中仍缺失的内容

SDK 的 PrivacyInfo.xcprivacy

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>NSPrivacyCollectedDataTypes</key>
	<array>
		<dict>
			<key>NSPrivacyCollectedDataType</key>
			<string>NSPrivacyCollectedDataTypeDeviceID</string>
			<key>NSPrivacyCollectedDataTypeLinked</key>
			<true/>
			<key>NSPrivacyCollectedDataTypeTracking</key>
			<false/>
			<key>NSPrivacyCollectedDataTypePurposes</key>
			<array>
				<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
				<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
			</array>
		</dict>
	</array>
	<key>NSPrivacyAccessedAPITypes</key>
	<array>
		<dict>
			<key>NSPrivacyAccessedAPIType</key>
			<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
			<key>NSPrivacyAccessedAPITypeReasons</key>
			<array>
				<string>E174.1</string>
			</array>
		</dict>
		<dict>
			<key>NSPrivacyAccessedAPIType</key>
			<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
			<key>NSPrivacyAccessedAPITypeReasons</key>
			<array>
				<string>CA92.1</string>
			</array>
		</dict>
		<dict>
			<key>NSPrivacyAccessedAPIType</key>
			<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
			<key>NSPrivacyAccessedAPITypeReasons</key>
			<array>
				<string>35F9.1</string>
			</array>
		</dict>
	</array>
</dict>
</plist>

SDK初始化

注意事项

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

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

接口定义

void (*initWithOptions)(NSDictionary *options);

initWithOptions方法必传配置

配置 key 定义 说明 场景 示例代码
partner 合作方编码 同盾的合作方编码,请联系同盾运营获取 所有场景 Objective C
[options setValue:@"请输入您的合作方编码" forKey:@"partner"];
Swift
options.updateValue("请输入您的合作方编码" as NSObject, forKey: "partner")
appKey 应用标识 同盾生成的应用标识,和app绑定,用于校验app的有效性,请联系同盾运营获取。
appkey创建需要用户提供应用包名
所有场景 Objective C
[options setValue:@"请输入您的appKey" forKey:@"appKey"];
Swift
options.updateValue("请输入您的appKey" as NSObject, forKey: "appKey")
appName 应用名称 同盾平台注册的应用名称,请联系同盾运营获取 所有场景 Objective C
[options setValue:@"请输入您的appName" forKey:@"appName"];
Swift
options.updateValue("请输入您的appName" as NSObject, forKey: "appName")
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:@"请输入您的appName" forKey:@"appName"];
  // 国家地区参数,参考下面`必传配置`
  [options setValue:@"请输入您所在的国家地区" forKey:@"country"];

#ifdef DEBUG
  // !!! DEBUG模式下若不设置此参数,app运行会闪退
  [options setValue:@(YES) forKey:@"allowed"];
  // 监听错误码
  riskManager->setOnErrorCodeListener(^(int errorCode,const char* errorMsg){
      NSLog(@":errorCode:%d,errorMsg:%s",errorCode,errorMsg);
  });
#endif
  riskManager->initWithOptions(options);
  riskManager->getBlackBoxAsync(^(NSString* blackBox){
    NSLog(@"blackBox:%@",blackBox);
});
}
func initTrustDecisionSDK() {
  let riskManager = TDMobRiskManager.sharedManager()
  var options = Dictionary<String, Any>()
  /*************************** 必传 ***************************/
  // 合作方编码,如tongdun,参考下面`必传配置`
  options.updateValue("请输入您的合作方编码", forKey: "partner")
  // 同盾平台注册的应用标识,参考下面`必传配置`
  options.updateValue("请输入您的appKey", forKey: "appKey")
  // 同盾平台注册的应用名称,参考下面`必传配置`
  options.updateValue("请输入您的appName", forKey: "appName")
  // 国家地区参数,参考下面`必传配置`
  options.updateValue("请输入您所在的国家地区", forKey: "country")

#if DEBUG
  // !!! DEBUG模式下若不设置此参数,app运行会闪退
  options.updateValue(true, forKey: "allowed")
  riskManager?.pointee.setOnErrorCodeListener(){(errorCode : Int32!,errorMsg : UnsafePointer<CChar>!)->Void in
      print("errorCode: \(errorCode), errorMsg: \(String(validatingUTF8: errorMsg))")
  };
#endif
  riskManager?.pointee.initWithOptions(options)
	riskManager?.pointee.getBlackBoxAsync(){(blackBoxString : String!)->Void in
   print("blackBox:" + blackBoxString)                               
}
}

initWithOptions方法选传参数

配置 key 定义 说明 场景 示例代码
language 语言类型 可选项:
1-简体中文、2-繁体中文、3-英 文、4-日文、5-韩文、6-⻢来语、7-泰语、8-印尼语、9-俄 语
默认:
1-简体中文
客户根据需要设置语言类型。
国内支持1-5
海外支持1-9
Objective C
[options setValue:@"1" forKey:@"language"];
Swift
options.updateValue("1", forKey: "language")
tapToClose 点击空白处是否关闭验证码 可选项: true、false
默认: false
开启后,点击界面空白处,会关闭验证码弹窗,关闭弹窗更加便捷Objective C
[options setValue:@(true) forKey:@"tapToClose"];
Swift
options.updateValue(true, forKey: "tapToClose")
needSeqid 失败回调信息中是否携带seqid 可选项: true、false
默认:true
开启后,失败信息中会携带seqid序列号,将seqid提供给同盾,方便排查失败原因 Objective C
[options setValue:@(YES) forKey:@"needSeqid"];
Swift
options.updateValue(true, forKey: "needSeqid")
hideLoadHud 是否跳过加载动画 可选项: true、false
默认:false
开启后,弹出验证码弹窗时不会再显示加载动画,缩短验证时间 Objective C
[options setValue:@(YES) forKey:@"hideLoadHud"];
Swift
options.updateValue(true, forKey: "hideLoadHud")
hideWebCloseButton 是否隐藏webview的关闭按钮 可选项: true、false
默认:false
需要强制完成验证码验证的场景 Objective C
[options setValue:@(YES) forKey:@"hideWebCloseButton"];
Swift
options.updateValue(true, forKey: "hideWebCloseButton")
openLog 是否打开log 可选项: true、false
默认:false
开启后,调试时控制台会输出更多的log信息,方便排查问题 Objective C
[options setValue:@(YES) forKey:@"openLog"];
Swift
options.updateValue(true, forKey: "openLog")
skipCaptcha 是否跳过同盾验证码验证 可选项: true、false
默认:false
开启将不会进行验证码的验证,同时返回4000错误码,用于动态设置是否使用同盾验证码SDK验证的场景 Objective C
[options setValue:@(YES) forKey:@"skipCaptcha"];
Swift
options.updateValue(true, forKey: "skipCaptcha")
mfaId MFA产品 可选项: string
默认:nil
如果您接入了MFA产品(未对接MFA,可忽略该说明),请将MFA流程中获取的 `mfaid`传递给验证码配置参数 Objective C
[options setValue:@"mfaId string" forKey:@"mfaId"];
Swift
options.updateValue("mfaId string", forKey: "mfaId")

弹出验证码弹窗

showCaptcha函数

showCaptcha 函数 用于显示验证码弹窗,其接口定义如下

// 显示验证码弹窗
// @param superView 弹窗显示在的父视图,UIView 类型
// @param block 结果回调的block
void (*showCaptcha)(id superView,TDShowCaptchaBlock block);

回调参数结构体TDShowCaptchaResultStruct

我们使用TDShowCaptchaResultStruct 结构体存储回调的结果;

typedef enum {
    // 验证码校验成功,此时可以获取到有效的 validateToken;
    TDShowCaptchaResultTypeSuccess,
    // 验证码校验失败,可以获取错误码 errorCode和errorMsg,根据文档中的`错误码`排查原因;
    TDShowCaptchaResultTypeFailed,
    // 验证码弹窗成功,等待被验证;
    TDShowCaptchaResultTypeReady,
} TDShowCaptchaResultType;


typedef struct _TDShowCaptchaResultStruct{
    // 返回的结果类型
    TDShowCaptchaResultType resultType;
    // 返回成功后,返回的验证码的token
    const char*validateToken;
    // 返回失败后,返回的错误码,可以根据文档排查原因
    NSInteger errorCode;
    // 返回失败后,返回的错误信息
    const char*errorMsg;
}TDShowCaptchaResultStruct;

示例代码

- (void)showCaptcha {
    UIWindow * keyWindow = [UIApplication sharedApplication].keyWindow;
    TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
    riskManager->showCaptcha(keyWindow,^(TDShowCaptchaResultStruct resultStruct) {
        switch (resultStruct.resultType) {
            case TDShowCaptchaResultTypeSuccess:
            {
                NSString * validateToken = @(resultStruct.validateToken);
                NSLog(@"获取同盾验证码成功!!!,validateToken:%@",validateToken);
            }
                break;
            case TDShowCaptchaResultTypeFailed:
            {
                NSString * errorMsg = @(resultStruct.errorMsg);
                NSLog(@"获取同盾验证码失败!!!,errorCode:%ld,errorMsg:%@",resultStruct.errorCode,errorMsg);
            }
                break;
            case TDShowCaptchaResultTypeReady:
                NSLog(@"验证码弹窗成功,等待被验证!!!");
                break;
            default:
                break;
        }
    });
}
func showCaptcha() {
    let keyWindow = UIApplication.shared.keyWindow
    let riskManager = TDMobRiskManager.sharedManager()
    riskManager?.pointee.showCaptcha(keyWindow) { (resultStruct : TDShowCaptchaResultStruct!)->Void  in
        switch resultStruct.resultType {
        case TDShowCaptchaResultTypeSuccess:
            let validateToken = String(cString:resultStruct.validateToken)
            print("获取同盾验证码成功!!!,validateToken:\(validateToken)")
        case TDShowCaptchaResultTypeFailed:
            let errorMsg = String(cString:resultStruct.errorMsg)
            print("获取同盾验证码失败!!!,errorCode:\(resultStruct.errorCode),errorMsg:\(errorMsg)")
        case TDShowCaptchaResultTypeReady:
            print("验证码弹窗成功,等待被验证!!!")
        default:
            break
        }
    }
}

错误码

验证码功能模块的错误码会通过 showCaptcha 函数输出

错误码错误信息处理方式
1001关闭了验证码窗口弹出验证码后,用户手动取消了验证码,不需要处理
2001请求参数异常,请检查参数请检查appName和partnerCode参数
2100请求参数异常,请检查参数请检查传递参数
2101请求参数异常,请检查参数请求过程出错,请联系运营
2102请求参数异常,请检查参数参数缺失,请检查参数
2111验证⻚面网络错误稍后再试,或者请联系运营
2112验证⻚面操作太频繁稍后再试
2113未知错误未知错误,请联系运营
2114关闭了验证码窗口点击了验证码关闭按钮,不需要处理
2115验证⻚面网络错误网络资源加载失败
2116验证⻚面网络错误网络资源加载失败
2202验证成功验证结果成功,不需要处理
2301未购买此服务请联系运营
2302流量已被禁用请联系运营
2303流量不足请联系运营
2304服务已过期请联系运营
2305日流量已封顶请联系运营
2600系统繁忙,请稍后再试系统繁忙,请稍后再试
2601验证失败,稍后重试验证失败,请稍后重试
2602验证失败,稍后重试验证失败,请稍后重试
2603验证失败,稍后重试验证失败,请稍后重试
2604验证失败,稍后重试刷新频繁,请稍后重试
2605验证失败,稍后重试获取验证码信息失败
2702验证失败,稍后重试解析错误,请稍后重试
3001SSL证书校验失败请关闭网络代理工具
3002验证页面加载出错刷新网络后重试
3003验证⻚面加载超时检查网络后重试
4000验证逻辑跳过开发者手动处理验证跳过逻辑
9000设备指纹没有挂载集成验证码需要先集成设备指纹
9001没有网络请检查网络连接
9002请求超时检查网络,稍后重试
9003返回结果异常服务端错误,返回结果异常,联系技术支持
9004全局加载超时检查网络,稍后重试

获取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")