实名认证和防沉迷开发指南
使用 TDS 实名认证和防沉迷服务之前,需要在 开发者中心后台 > 游戏服务 > 生态服务 > 合规认证 处开通服务,可选择「已有版号」或「暂无版号」方案。
推荐阅读博客:实名认证和防沉迷功能接入,加深对实名认证和防沉迷功能的理解。
SDK 配置
可以在 下载页 获得 TapSDK,引入防沉迷模块。
- Unity
- Android
- iOS
- UE4
Unity 模块,移动端通过引入 iOS 和 Android 模块后增加桥接文件打包出的 .unitypackage
,方便以 Unity 开发的游戏直接引入。其他引擎/平台的游戏可以通过 iOS/Android 原生的方式接入,详见 iOS/Android 接入文档。PC(Windows、Mac、Unity Editor)下已经直接通过 Unity Native 的方式实现不需要增加桥接文件。
Unity 开发环境要求:Unity 2019.4 或更高版本
支持版本:
- Android:最低支持 5.0
- iOS:最低支持 iOS 10.0
- Windows 以及 Mac 均已支持
SDK 可以通过 Unity Package Manager 导入或手动导入,二者任选其一。请根据项目需要选择。
方法一:使用 Unity Package Manager
在项目的 Packages/manifest.json
文件中添加以下依赖:
"dependencies":{
"com.tapsdk.antiaddiction":"https://github.com/taptap/TapAntiAddiction-Unity.git#3.18.3",
"com.leancloud.storage": "https://github.com/leancloud/csharp-sdk-upm.git#storage-1.0.2",
"com.taptap.tds.common":"https://github.com/TapTap/TapCommon-Unity.git#3.18.3",
}
在 Unity 顶部菜单中选择 Window > Package Manager 可查看已经安装在项目中的包。
方法二:手动导入
在 下载页 找到 TapSDK Unity 和 LeanCloud C# SDK 下载地址,分别下载 TapSDK-UnityPackage.zip 和 LeanCloud-SDK-Storage-Unity.zip。
- TapSDK-UnityPackage.zip 解压后,导入其中的
TapTap_Common_3.18.3.unitypackage
和TapTap_AntiAddiction_3.18.3.unitypackage
。
- LeanCloud-SDK-Storage-Unity.zip,解压后为 Plugins 文件夹,拖拽至 Unity 即可。
iOS 平台配置:
使用 Xcode 13.0 beta 5 编译,检查 Unity 输出的 Xcode 工程:
- 请确保设置
Xcode
-General
-Frameworks, Libraries, and Embedded Content
中的AntiAddictionService.framework
和AntiAddictionUI.framework
为Do Not Embed
。 - 如果编译报错找不到头文件或者模块,请确保
Xcode
-Build Settings
-Framework Search Paths
中的路径以保证 Xcode 正常编译。 - 确保 Xcode 工程的
Build Settings
的Swift Compile Language
/Swift Language Version
为Swift5
。 - 添加依赖库
libz.tbd
、libc++.tbd
。 - 开始代码接入。
- 将
AntiAddiction-Unity/Assets/Plugins/iOS/Resource/AntiAdictionResources.bundle
拷贝到游戏项目下(如果 Unity 项目没有正确导入AntiAddictionResources.bundle
)。
最低支持 Android 5.0(API level 21),编译环境为 Android Studio。
- 将 防沉迷 SDK
AntiAddiction_3.18.3.aar
拷贝到游戏目录下的src/main/libs
目录中 - 将 防沉迷 SDK
AntiAddictionUI_3.18.3.aar
拷贝到游戏目录下的src/main/libs
目录中 - 将
TapCommon_3.18.3.aar
拷贝到游戏目录下的src/main/libs
目录中
在游戏目录下 build.gradle
文件中添加代码
repositories{
flatDir{
dirs 'src/main/libs'
}
}
dependencies {
// ...
implementation(name: "AntiAddiction_3.18.3", ext: "aar") // 防沉迷 SDK
implementation(name: "AntiAddictionUI_3.18.3", ext: "aar") // 防沉迷 SDK
implementation(name: "TapCommon_3.18.3", ext: "aar")
// ...
}
支持的 iOS 版本为最低为 iOS 10,SDK 编译环境为 Xcode 12。
iOS 防沉迷 SDK 结构:
AntiAddictionService
防沉迷基础库,源码由 Swift 编写。AntiAddictionUI
带 UI 的防沉迷库,依赖AntiAddictionService
,源码由 Objective-C 编写。AntiAddictionResources.bundle
资源文件
其他依赖:
TapCommonSDK.framework
基础库
添加防沉迷库文件:
添加
AntiAddictionService.framework
、AntiAddictionUI.framework
和TapCommonSDK.framework
静态库。注意添加时选择 Embed 方式为 Do Not Embed。引用代码:
// AntiAddictionUI
#import <AntiAddictionUI/AntiAddictionUI.h>
添加系统依赖库:
请检查项目中是否已自动添加以下依赖项:
libc++.tdb
libz.tdb
若运行时遇到相关依赖库加载报错,可改为 Optional 尝试。
配置编译选项:
在 Build Setting 中的 Other Link Flag 中添加
-ObjC
。在 Build Setting 中的 Always Embed Swift Standard Libraries 设置为 YES,即始终引入 Swift 标准库,避免 App 启动时报错「无法找到 Swift 标准库之类」。如果项目中找不到,可以建立一个空 Swift 文件,Xcode 会自动建立桥接关系。
在 Build Setting 中的 Swift Compiler - Language/Swift Language Version 选择 Swift 5。
环境要求
- 安装 UE 4.26 及以上版本
- iOS 12 或更高版本
- Android 5.0(API level 21)或更高版本
- macOS 10.14.0 或更高版本
- Windows 7 或更高版本
支持平台:iOS / Android / Windows / macOS
安装插件
- 下载 TapSDK UE4,TapSDK-UE4-xxx.zip 解压后将
AntiAddiction
、TapCommon
文件夹 Copy 到项目的Plugins
目录中 - 重启 Unreal Editor
- 打开 编辑 > 插件 > 项目 > TapTap,开启
AntiAddiction
模块
添加依赖
在 Project.Build.cs 中添加所需模块:
PublicDependencyModuleNames.AddRange(new string[] { "Core",
"CoreUObject",
"Engine",
"Json",
"InputCore",
"JsonUtilities",
"SlateCore",
"TapCommon",
"AntiAddiction"
});
导入头文件
#include "TapUEBootstrap.h"
iOS 打包 Objective-C 和 Swift 的混编解决方案
目前有两种解决方案
一、防沉迷库替换成动态库
优缺点:
- 优点:可以不用修改引擎的代码
- 缺点:
- 包体积略微增大
- 最低支持 iOS 13,低于该系统版本会造成闪退
操作步骤:
下载 TapSDK-iOS 相同版本的库文件
把
Plugins/AntiAddiction/Source/AntiAddiction/ios/framework/AntiAddictionService.zip
中的AntiAddictionService.framework
替换成刚下载到的Dylib/AntiAddictionService.framework
(解压缩 -> 替换 -> 压缩)把
AntiAddiction.Build.cs
文件中new Framework(
"AntiAddictionService",
"../AntiAddiction/ios/framework/AntiAddictionService.zip"
)替换成:
new Framework(
"AntiAddictionService",
"../AntiAddiction/ios/framework/AntiAddictionService.zip",
null,
true
)重新编译即可
二、修改 UnrealBuildTool
修改
XcodeProject.cs
文件路径:
Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProject.cs
在函数:
private void AppendProjectBuildConfiguration(StringBuilder Content, string ConfigName, string ConfigGuid)
中添加如下代码:
// Enable Swift
Content.Append("\t\t\t\tCLANG_ENABLE_MODULES = YES;" + ProjectFileGenerator.NewLine);
Content.Append("\t\t\t\tSWIFT_VERSION = 5.0;" + ProjectFileGenerator.NewLine);
Content.Append("\t\t\t\tLIBRARY_SEARCH_PATHS = \"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\";" + ProjectFileGenerator.NewLine);
if (ConfigName == "Debug")
{
Content.Append("\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";" + ProjectFileGenerator.NewLine);
}
Content.Append("\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;" + ProjectFileGenerator.NewLine);
Content.Append("\t\t\t\tEMBEDDED_CONTENT_CONTAINS_SWIFT = YES;" + ProjectFileGenerator.NewLine);参考如下:
修改
IOSToolChain.cs
文件路径:
Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSToolChain.cs
在函数:
string GetLinkArguments_Global(LinkEnvironment LinkEnvironment)
中添加如下代码:
// 该行代码需要前置(前置的代码位置见下面示例图片)
// Added by uwellpeng: enable swift support, make sure '/usr/lib/swift' goes before '@executable_path/Frameworks'
Result += " -rpath \"/usr/lib/swift\"";
// enable swift support
Result += " -rpath \"@executable_path/Frameworks\"";
// /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/swift/
String swiftLibPath = String.Format(" -L {0}Platforms/{1}.platform/Developer/SDKs/{1}{2}.sdk/usr/lib/swift",
Settings.Value.XcodeDeveloperDir, bIsDevice? Settings.Value.DevicePlatformName : Settings.Value.SimulatorPlatformName, Settings.Value.IOSSDKVersion);
Result += swiftLibPath;
Log.TraceInformation("Add swift lib path : {0}", swiftLibPath);
///Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos
swiftLibPath = String.Format(" -L {0}Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/{1}",
Settings.Value.XcodeDeveloperDir, bIsDevice? Settings.Value.DevicePlatformName.ToLower() : Settings.Value.SimulatorPlatformName.ToLower());
Result += swiftLibPath;
Log.TraceInformation("Add swift lib path : {0}", swiftLibPath);
///Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/iphoneos
swiftLibPath = String.Format(" -L {0}Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/{1}",
Settings.Value.XcodeDeveloperDir, bIsDevice? Settings.Value.DevicePlatformName.ToLower() : Settings.Value.SimulatorPlatformName.ToLower());
Result += swiftLibPath;
// Xcode 12 多了 swiftcompatabiliy51 的库,需要新增以下代码
if (Settings.Value.IOSSDKVersionFloat >= 14.0f)
{
Result += String.Format(" -lswiftCompatibility51");
}需要注意的是
Result += " -rpath \"/usr/lib/swift\"";
这段代码需要加在@executable_path/Frameworks
前面参考:
重新编译 UBT
使用
msbuild
工具重新编译UnrealBuildTool
,即在Engine/Source/Programs/UnrealBuildTool
目录运行Terminal
指令msbuild
来重新编译(如果引擎目录在一些不可编辑的目录下,可以加上sudo
命令,即sudo msbuild
)。完成上述三个步骤即可在解决 UnrealEngine 上 Swift 的混编问题
防沉迷 SDK 需要联网和发送请求数据的权限,请开发者注意在项目中声明相应权限。
初始化
初始化防沉迷 UI 模块,设置启动防沉迷功能的配置,注册防沉迷的消息监听。请注意,触发回调需要调用防沉迷认证接口。
- Unity
- Android
- iOS
- UE4
using TapTap.AntiAddiction;
using TapTap.AntiAddiction.Model;
AntiAddictionConfig config = new AntiAddictionConfig()
{
gameId = "your_client_id", // TapTap 开发者中心对应 Client ID
showSwitchAccount = false, // 是否显示切换账号按钮
};
Action<int, string> callback = (code, errorMsg) => {
// code == 500; // 登录成功
// code == 1000; // 用户登出
// code == 1001; // 切换账号
// code == 1030; // 用户当前无法进行游戏
// code == 1050; // 时长限制
// code == 9002; // 实名过程中点击了关闭实名窗
UnityEngine.Debug.LogFormat($"code: {code} error Message: {errorMsg}");
};
AntiAddictionUIKit.Init(config, callback);
// 如果是 PC 平台还需要额外设置一下 gameId
TapTap.AntiAddiction.TapTapAntiAddictionManager.AntiAddictionConfig.gameId = "your_client_id"
// Android SDK 的各接口第一个参数是当前 Activity,以下不再说明
Config config = new Config.Builder()
.withClientId("your_client_id") // TapTap 开发者中心对应 Client ID
.showSwitchAccount(false) // 是否显示切换账号按钮
.build();
AntiAddictionUIKit.init(activity, config, new AntiAddictionUICallback() {
@Override
public void onCallback(int code, Map<String, Object> extras) {
if (code == Constants.ANTI_ADDICTION_CALLBACK_CODE.LOGIN_SUCCESS){
Log.d("TapTap-AntiAddiction", "玩家登录后判断当前玩家可以进行游戏");
}
}
});
AntiAddictionConfig *config = [[AntiAddictionConfig alloc] init];
config.clientID = @"your_client_id"; // TapTap 开发者中心对应 Client ID
config.showSwitchAccount = YES;
// self 需要实现 AntiAddictionDelegate 协议,self 用其他遵守 AntiAddictionDelegate 协议的对象替换
[AntiAddiction initWithConfig:config delegate:self];
+[AntiAddiction initWithConfig:delegate:]
中的 delegate
对象需要实现以下协议方法来接收回调:
- (void)antiAddictionCallbackWithCode:(AntiAddictionResultHandlerCode)code extra:(NSString * _Nullable)extra {
// 防沉迷回调
}
FAAUConfig Config;
Config.ClientID = TEXT("your_client_id"); // TapTap 开发者中心对应 Client ID
Config.ShowSwitchAccount = false;
AntiAddictionUE::Init(Config);
防沉迷启动后,会有各种事件的回调,所以需要监听回调 AntiAddictionUE::OnCallBack
,具体的事件可以参考 AntiAddictionUE::ResultHandlerCode
值。
// 绑定 AntiAddictionUE::OnCallBack 回调
AntiAddictionUE::OnCallBack.BindUObject(this, &UAntiAddictionWidget::OnCallBack);
void UAntiAddictionWidget::OnCallBack(AntiAddictionUE::ResultHandlerCode Code, const FString& Message) {
TUDebuger::DisplayShow(FString::Printf(TEXT("Code: %d, Message: %s"), Code, *Message));
switch (Code) {
case AntiAddictionUE::LoginSuccess:
TUDebuger::DisplayShow(TEXT("登录成功"));
break;
case AntiAddictionUE::Exited:
TUDebuger::DisplayShow(TEXT("用户登出"));
break;
case AntiAddictionUE::SwitchAccount:
TUDebuger::DisplayShow(TEXT("切换账号"));
break;
case AntiAddictionUE::DurationLimit:
TUDebuger::DisplayShow(TEXT("时长耗尽,用户当前无法进行游戏"));
break;
case AntiAddictionUE::PeriodRestrict:
TUDebuger::DisplayShow(TEXT("宵禁时间,用户当前无法进行游戏"));
break;
case AntiAddictionUE::RealNameStop:
TUDebuger::DisplayShow(TEXT("取消实名"));
break;
default:
TUDebuger::WarningLog(TEXT("未知回调的 Code: ") + FString::FromInt(Code));
break;
}
}
参数说明
- Unity
- Android
- iOS
- UE4
config
是防沉迷功能的配置,包括如下参数:gameId
游戏的Client ID
,可以在控制台查看(开发者中心 > 你的游戏 > 游戏服务 > 应用配置)。showSwitchAccount
是否显示切换账号按钮。如果游戏没有切换账号功能,可以在初始化阶段配置隐藏切换账号按钮;如果游戏选择显示切换账号按钮(如下图所示),玩家点击之后会触发1001
回调,游戏可根据这个回调 code 做相应处理。
callback
是防沉迷的在各种情况下,通知用户的回调接口,包含参数如下:code
不同情况下的回调类型,详情可以参考下面的回调类型。errorMsg
在执行不同业务时,发生错误时的错误消息。
防沉迷初始化参数包括:
- 游戏的
Client ID
,可以在控制台查看(开发者中心 > 你的游戏 > 游戏服务 > 应用配置)。 - 是否显示切换账号按钮。如果游戏没有切换账号功能,可以在初始化阶段配置隐藏切换账号按钮;如果游戏选择显示切换账号按钮(如下图所示),玩家点击之后会触发
1001
回调,游戏可根据这个回调 code 做相应处理。
config
是防沉迷功能的配置,包括如下参数:clientID
游戏的Client ID
,可以在控制台查看(开发者中心 > 你的游戏 > 游戏服务 > 应用配置)。showSwitchAccount
是否显示切换账号按钮。如果游戏没有切换账号功能,可以在初始化阶段配置隐藏切换账号按钮;如果游戏选择显示切换账号按钮(如下图所示),玩家点击之后会触发1001
回调,游戏可根据这个回调 code 做相应处理。
delegate
对象需要实现-[AntiAddictionDelegate antiAddictionCallbackWithCode:extra:]
协议方法来接收回调code
不同情况下的回调类型,详情可以参考下面的回调类型。extra
在执行不同业务时,会带上一些详细信息。
config
是防沉迷功能的配置,包括如下参数:ClientID
游戏的Client ID
,可以在控制台查看(开发者中心 > 你的游戏 > 游戏服务 > 应用配置)。ShowSwitchAccount
是否显示切换账号按钮。如果游戏没有切换账号功能,可以在初始化阶段配置隐藏切换账号按钮;如果游戏选择显示切换账号按钮(如下图所示),玩家点击之后会触发1001
回调,游戏可根据这个回调 code 做相应处理。
回调类型
回调 code | 回调类型 | 触发逻辑 |
---|---|---|
500 | LOGIN_SUCCESS | 玩家登录后判断当前玩家可以进行游戏 |
1000 | EXITED | 退出账号 |
1001 | SWITCH_ACCOUNT | 点击切换账号按钮 |
1030 | PERIOD_RESTRICT | 未成年玩家当前无法进行游戏 |
1050 | DURATION_LIMIT | 时长限制 |
9002 | REAL_NAME_STOP | 实名过程中点击了关闭实名窗 |
防沉迷认证
SDK 支持两种防沉迷认证方式:
- 使用 TapTap 快速认证,可令玩家授权游戏使用 TapTap 上的年龄段信息完成实名。
- 不使用 TapTap 快速认证,玩家在 SDK 提供的界面中手动输入身份证号等实名信息。
TDS 云端会根据实名信息判断玩家是否可以进行游戏,并将相关信息上报至中宣部防沉迷实名认证系统。
我们推荐使用第一种方式,对于玩家而言,不需要手动输入信息,可以快速完成认证、进入游戏,体验更佳。
TapTap 快速认证
防沉迷 v3.7.1 开始,使用快速认证不再依赖 TapTap 登录 SDK,但快速认证需要玩家在已登录状态下授权游戏使用其在 TapTap 上的年龄段信息完成实名,请确保完成了以下工作:
传入玩家唯一标识 userIdentifier
及 是否是 Tap 用户参数时,即可开始 TapTap 快速认证。SDK 会拉起 TapTap 开始快速认证,如果 SDK 检测到玩家设备中未安装 TapTap 客户端,则会打开 WebView,玩家可授权游戏使用其在 TapTap 上的年龄段信息来完成游戏内的实名流程。
其中的玩家唯一标识 userIdentifier
,如果接入 TDS 内建账户系统,可以用玩家的 objectId
;如果使用单纯 TapTap 用户认证则可以用 openid
或 unionid
。
对于是否是 Tap 用户,游戏可根据当前玩家选择的登录方式进行设置,即选择 Tap 登录方式的设置为 true
, 其他类型设置为 false
。
- Unity
- Android
- iOS
- UE4
// 注意唯一标识参数值长度不能超过 64 字符
string userIdentifier = "玩家的唯一标识";
AntiAddictionUIKit.Startup(userIdentifier, true);
注意:Unity 项目如果没有接入登录模块,则项目导出的 Xcode 工程打包 iOS 时需要配置 URLSchema
。
查看 Xcode 工程如何配置 URLSchema
打开 info.plist
,添加如下配置(请替换 clientID
为你在控制台获取的 Client ID):
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>taptap</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- 这里注意下,中括号需要去掉,最终是 Client ID 前拼接上 `tt` 的形式,例如:ttFwFdCxxxxxxxQDQwQN -->
<string>tt[clientID]</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>tapiosdk</string>
<string>tapsdk</string>
</array>
// 注意唯一标识参数值长度不能超过 64 字符
String userIdentifier = "玩家的唯一标识";
AntiAddictionUIKit.startup(activity, userIdentifier, true);
// 注意唯一标识参数值长度不能超过 64 字符
NSString *userIdentifier = @"玩家的唯一标识";
[AntiAddiction startupWithUserID:userIdentifier isTapUser: YES];
使用 TapTap 快速认证 需要在 AppDelegate
里嵌入回调。
注意:如果已经在登录模块添加过,可以跳过此步。
#import <TapCommonSDK/TapCommonSDK.h>
// 新的回调
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [TDSHandleUrl handleOpenURL:url];
}
此外,如果没有接入登录模块,还需要配置 URLSchema
。
打开 info.plist
,添加如下配置(请替换 clientID
为你在控制台获取的 Client ID):
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>taptap</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- 这里注意下,中括号需要去掉,最终是 Client ID 前拼接上 `tt` 的形式,例如:ttFwFdCxxxxxxxQDQwQN -->
<string>tt[clientID]</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>tapiosdk</string>
<string>tapsdk</string>
</array>
AntiAddictionUE::Startup(TEXT("your_userIdentifier"), true);
登出
玩家在游戏内退出账号时调用,重置防沉迷状态。
- Unity
- Android
- iOS
- UE4
AntiAddictionUIKit.Exit();
AntiAddictionUIKit.exit();
[AntiAddiction exit];
AntiAddictionUE::Exit();
获取玩家年龄段
调用该接口可获取玩家所处年龄段:
- Unity
- Android
- iOS
- UE4
int ageRange = AntiAddictionUIKit.AgeRange;
int ageRange = AntiAddictionUIKit.getAgeRange();
NSInteger ageRange = [AntiAddiction getAgeRange];
EAAUAgeLimit AgeLimit = AntiAddictionUE::GetAgeRange();
上例中的 ageRange
是一个整数,表示玩家所处年龄段的下限(最低年龄)。
特别地,-1
表示「未知」,说明该用户还未实名。
类型数值 | 含义 |
---|---|
-1 | 未知 |
0 | 0 到 7 岁 |
8 | 8 到 15 岁 |
16 | 16 到 17 岁 |
18 | 成年玩家 |
获取剩余时长(单位:秒)
获取玩家当前剩余时长:
- Unity
- Android
- iOS
- UE4
int remainingTimeInSeconds = AntiAddictionUIKit.RemainingTime;
int remainingTimeInSeconds = AntiAddictionUIKit.getRemainingTime();
NSInteger remainingTimeInSeconds = [AntiAddiction getRemainingTime];
int RemainingTime = AntiAddictionUE::GetRemainingTime();
获取剩余时长(单位:分钟)
获取玩家当前剩余时长:
- Unity
- Android
- iOS
- UE4
int remainingTimeInMinutes = AntiAddictionUIKit.RemainingTimeInMinutes;
int remainingTimeInMinutes = AntiAddictionUIKit.getRemainingTimeInMinutes();
NSInteger remainingTimeInMinutes = [AntiAddiction getRemainingTimeInMinutes];
int RemainingTimeInMinutes = AntiAddictionUE::GetRemainingTimeInMinutes();
检查消费上限
根据年龄段的不同,未成年玩家的消费金额有不同的上限。 如果启用消费限制功能,开发者需要在未成年玩家消费前检查是否受限,并在成功消费后上报消费金额。
游戏在收到玩家的付费请求后,调用以下接口当前玩家的付费行为是否被限制:
- Unity
- Android
- iOS
- UE4
long amount = 100;
AntiAddictionUIKit.CheckPayLimit(amount,
(result) => {
// status 为 1 时可以支付
int status = result.status;
if (status == 1) {
// 可以进行支付
}
},
(exception) => {
// 处理异常
}
);
long amount = 100;
AntiAddictionUIKit.checkPayLimit(activity, amount,
new Callback<CheckPayResult>() {
@Override
public void onSuccess(CheckPayResult result) {
// status 为 true 时可以支付,false 则限制消费
if (result.status) {
}
}
@Override
public void onError(Throwable throwable) {
// 处理异常
}
}
);
NSInteger amount = 100;
[AntiAddiction checkPayLimit:amount resultBlock:^(BOOL status) {
if (status) {
// 无限制
}
} failureHandler:^(NSString * _Nonnull error) {
// 处理异常
}];
AntiAddictionUE::CheckPayLimit(FCString::Atoi(*AmountTF->Text.ToString()), [](bool Status) {
TUDebuger::DisplayShow(FString::Printf(TEXT("Status: %d"), Status));
}, [](const FString& Msg) {
TUDebuger::ErrorShow(Msg);
});
消费金额的单位为分。
检查消费上限需要游戏事先上报未成年玩家的消费金额。 建议开发者在服务端上报,服务端上报方式参见 相关 REST API 用法说明。 开发者也可以调用 SDK 提供的接口,当未成年玩家消费成功后,在客户端上报消费金额,在客户端上报的可靠性低于在服务端上报,主要适用于无服务端的单机游戏。
- Unity
- Android
- iOS
- UE4
long amount = 100;
AntiAddictionUIKit.SubmitPayResult(amount,
() => {
// 成功
}, (exception) => {
// 处理异常
}
);
long amount = 100;
AntiAddictionUIKit.submitPayResult(amount,
new Callback<SubmitPayResult>() {
@Override
public void onSuccess(SubmitPayResult result) {
// 提交成功
}
@Override
public void onError(Throwable throwable) {
// 处理异常
}
}
);
NSInteger amount = 100;
[AntiAddiction submitPayResult:amount callBack:^(BOOL success) {
if (success) {
// 提交成功
}
} failureHandler:^(NSString * _Nonnull error) {
// 处理异常
}];
AntiAddictionUE::SubmitPayResult(FCString::Atoi(*AmountTF->Text.ToString()), [](bool Success) {
TUDebuger::DisplayShow(FString::Printf(TEXT("Success: %d"), Success));
}, [](const FString& Msg) {
TUDebuger::ErrorShow(Msg);
});
上报消费金额时,传入的消费金额的单位同样为分。
上报游戏时长
已登录的玩家,开始游戏时调用此接口,之后 SDK 会自动轮询上报游戏时长。
- Unity
- Android
- iOS
- UE4
AntiAddictionUIKit.EnterGame();
AntiAddictionUIKit.enterGame();
[AntiAddiction enterGame];
AntiAddictionUE::EnterGame();
相应地,已登录的玩家,停止游戏时调用此接口,之后 SDK 停止轮询上报时长。
- Unity
- Android
- iOS
- UE4
AntiAddictionUIKit.LeaveGame();
AntiAddictionUIKit.leaveGame();
[AntiAddiction leaveGame];
AntiAddictionUE::LeaveGame();
REST API
请求格式
REST API 请求的主体为 JSON 格式,HTTP header 的 Content-Type
需要设置为 application/json
。
请求通过 Authorization TOKEN
HTTP 头进行鉴权。
开发者需要在客户端通过 SDK 接口获取 TOKEN
后传给服务端,然后在服务端凭借此 TOKEN
调用防沉迷服务的 REST API。
API Base 为 https://tds-tapsdk.cn.tapapis.com
。
获取鉴权 Token
- Unity
- Android
- iOS
- UE4
string token = AntiAddictionUIKit.CurrentToken;
String token = AntiAddictionUIKit.currentToken();
NSString *token = [AntiAddiction currentToken];
FString Token = AntiAddictionUE::CurrentToken();
鉴权 Token 永久有效。
检查玩家当前能否游戏
curl -X POST \
-H "Content-Type: application/json" \
-H 'Authorization: {{token}}' \
https://tds-tapsdk.cn.tapapis.com/anti-addiction/v1/clients/{{clientId}}/users/{{userIdentifier}}/playable
其中:
{{token}}
需替换为客户端获取的鉴权 token。{{clientId}}
需替换为 开发者中心后台游戏服务 > 应用配置 中的 Client ID。{{userIdentifier}}
需替换为游戏在调用 防沉迷认证 时使用的玩家唯一标识。
以下情况的响应状态码均为 200:
// 实名认证失败
{
"success": true,
"data": {
"status": 2,
"anti_addiction_token": "",
"age_limit": 18,
"has_auth_record": false
}
}
// 成年玩家
{
"success":true,
"data":{
"code":200,
"can_play":true,
"message":"游戏时间不受限制",
"remain_time":60,
"cost_time":0,
"restrict_type":0,
"title":"健康游戏提示",
"description":"当前为成年人账号"
}
}
// 未成年玩家,当前可以游戏
{
"success":true,
"data":{
"code":200,
"can_play":true,
"message":"游戏允许时间",
"remain_time": {{remainTime}},
"cost_time": {{costtime}},
"restrict_type":1,
"title":"健康游戏提示","description":"你当前为未成年账号,已被纳入防沉迷系统。根据国家相关规定,周五、周六、周日及法定节假日 20 点 - 21 点之外为健康保护时段。你今日游戏时间还剩余${remainTime}分钟,请注意适当休息。"
}
}
// 未成年玩家,当前不可游戏
{
"success":true,
"data":{
"code":200,
"can_play":false,
"message":"游戏时间耗尽",
"remain_time": 0,
"cost_time": 60,
"restrict_type":1,
"title":"健康游戏提示",
"description":"你当前为未成年账号,已被纳入防沉迷系统。根据国家相关规定,周五、周六、周日及法定节假日 20 点 - 21 点之外为健康保护时段。当前时间段无法游玩,请合理安排时间。"
}
}
Token 解析错误会返回 401 错误:
{
"success":false,
"data":{
"code":16,
"error":"实名认证失败",
"error_description":"未实名用户不能进入游戏",
"msg":"该账号没有通过实名认证"
}
}
检查玩家消费是否受限
充值金额以分为单位,比如检查玩家是否可以消费 1 元(100 分):
curl -X POST \
-H "Content-Type: application/json" \
-H 'Authorization: {{token}}' \
-d '{"amount": 100}' \
https://tds-tapsdk.cn.tapapis.com/anti-addiction/v1/clients/{{clientId}}/users/{{userIdentifier}}/payable
消费受限和不受限时响应的状态码都是 200:
// 受限
{
"success":true,
"data":{
"code":200,
"status":false,
"message":"限额提示",
"title":"健康消费提示",
"description":"允许充值根据国家相关规定,未满8周岁:不提供付费服务;8-16周岁以下:单笔付费不超过50元,每月累计不超过200元;16-18周岁以下:单笔付费不超过100元,每月累计不超过400元。"
}
}
// 允许
{
"success":true,
"data":{
"code":200,
"status":true,
"message":"限额提示",
"title":"健康消费提示",
"description":"允许充值根据国家相关规定,未满8周岁:不提供付费服务;8-16周岁以下:单笔付费不超过50元,每月累计不超过200元;16-18周岁以下:单笔付费不超过100元,每月累计不超过400元。"
}
}
金额格式异常时返回 400 错误:
{
"success":false,
"data":{
"code":3,
"error":"上传金额不正确",
"error_description":"金额大于等于0并小于100_000_000_000","msg":"请输入正确的金额格式"
}
}
实名认证失败(包括 Token 解析错误)时返回 401 错误:
{
"success":false,
"data":{
"code":16,
"error":"实名认证失败",
"error_description":"未实名用户不能进入游戏",
"msg":"该账号没有通过实名认证"
}
}
上报消费金额
玩家充值 1 元(100 分),提交消费金额:
curl -X POST \
-H "Content-Type: application/json" \
-H 'Authorization: {{token}}' \
-d '{"amount": 100}' \
https://tds-tapsdk.cn.tapapis.com/anti-addiction/v1/clients/{{clientId}}/users/{userIdentifier}/payments
上报成功时响应的状态码为 200,返回结果:
{
"success":true,
"data":{"message":"上传金额成功"}
}
金额格式异常时返回 400 错误:
{
"success":false,
"data":{
"code":3,
"error":"上传金额不正确",
"error_description":"金额大于等于0并小于100_000_000_000","msg":"请输入正确的金额格式"
}
}
常见问题
如何开通实名认证与防沉迷服务
可以在 开发者中心后台 > 游戏服务 > 生态服务 > 合规认证 处自助开通服务。目前提供两种方案,游戏开通时选择其中一种:
- 有版号。完成控制台提示的前置条件,点击开通,然后配置好中宣部参数:
- 将中宣部系统后台的游戏备案识别码、应用标识、应用密钥填写到 TapTap 开发者中心后台对应处。
- 将 TapTap 开发者中心后台显示的 IP 白名单地址复制、填写到中宣部系统后台。
- 暂无版号。无版号游戏无法配置中宣部参数,可直接选择开通,等游戏有了版号,可以配置中宣部参数并切换到有版号方案。切换后客户端代码不受影响,不需要修改。
SDK 自带哪些用户界面(UI)
实名认证和防沉迷 SDK 提供的用户界面主要在防沉迷授权阶段,可参考功能介绍文档中的界面预览。
授权失败
在实名认证时使用 TapTap 快速认证服务提示「授权失败」,请至后台配置签名证书。
未查询到实名认证配置
实名认证时使用手动输入实名信息服务提示「未查询到实名认证配置」,原因是未开启实名认证服务,需要在 开发者中心后台 > 游戏服务 > 生态服务 > 合规认证 处自助开通服务。
userIdentifier is empty
调用认证接口时需要传入的玩家唯一标识 userIdentifier 参数值为空,建议开发者对此做非空判断。
未弹出实名认证窗口/未收到回调
这种情况一般是仅调用了初始化防沉迷 UI 模块代码,也就是说只完成了 SDK 的初始化,同时注册防沉迷的消息监听。
触发实名认证弹窗必须调用 认证接口,之后才会收到回调。
重复认证
我们预期同一个玩家认证过一次之后不再触发弹窗,防沉迷服务直接使用第一次认证的结果,这样用户体验更好。
如果出现重复认证,可以按照以下思路排查:
- 首先确认游戏使用的 玩家唯一标识 userIdentifier 符合要求。如果同一个玩家用的 userIdentifier 会发生改变,在防沉迷服务中会被视为不同用户,导致重复认证。这个时候需要游戏传入合适的 userIdentifier,建议接入 TDS 内建账户系统,用
objectId
作为玩家唯一标识;或者使用单纯 TapTap 用户认证,传入openid
或unionid
作为玩家唯一标识。 - 如果是有版号游戏,请确认在 开发者中心后台游戏服务 > 生态服务 > 合规认证 处填写的参数无误。如果参数有问题,请求中宣部接口会失败,导致重复认证。请检查以下参数配置:
- IP 白名单地址全部填入中宣部系统后台。
- 游戏备案识别码、应用标识和中宣部后台保持一致。
- 应用密钥在有效期内(有效期为半年,注意在失效前更新)。
注意事项
玩家唯一标识 userIdentifier 参数说明
第一次认证会触发实名认证弹窗,让玩家授权游戏获取 TapTap 实名信息或输入身份信息,认证通过后,后面每一次以同样的 userIdentifier 调用认证接口都会直接拿到第一次认证的结果,不再触发弹窗。
因此,同一个用户的唯一标识应该要保证唯一性。
测试实名认证环境
无论是 Android 还是 iOS 项目,不支持在 Unity Editor 环境里调试,请对应打包到真实设备或者移动端的模拟器中进行测试实名认证防沉迷的相关功能。
更新日志
3.18.3
内容变更:
- 修复 Unity 接入时 iOS 无回调问题
3.18.2
内容变更:
- 修复提示窗口 UI 错位问题
- 修复 iOS 部分情况下进入宵禁未弹出提示异常
接口变更
新增接口
防沉迷开始认证接口添加 isTapUser 参数,当登录方式为 Tap 登录时设置为 true
, 其他方式设置为 false
。
- Unity
- Android
- iOS
- UE4
// 注意唯一标识参数值长度不能超过 64 字符
string userIdentifier = "玩家的唯一标识";
// Deprecated in 3.18.2 Added in 3.15.0
// AntiAddictionUIKit.Startup(userIdentifier);
AntiAddictionUIKit.Startup(userIdentifier, true);
// 注意唯一标识参数值长度不能超过 64 字符
String userIdentifier = "玩家的唯一标识";
// Deprecated in 3.18.2 Added in 3.15.0
// AntiAddictionUIKit.startup(activity, userIdentifier);
AntiAddictionUIKit.startup(activity, userIdentifier, true);
// 注意唯一标识参数值长度不能超过 64 字符
NSString *userIdentifier = @"玩家的唯一标识";
// Deprecated in 3.18.2 Added in 3.15.0
// [AntiAddiction startupWithUserID:userIdentifier];
[AntiAddiction startupWithUserID:userIdentifier isTapUser: YES];
// Deprecated in 3.18.2
// AntiAddictionUE::Startup(TEXT("your_userIdentifier"));
AntiAddictionUE::Startup(TEXT("your_userIdentifier"), true);