dependencies {
    implementation(
        "com.remax:risk-control:0.1.4"
    )
}

Android SDK 0.1.4

接入文档

SDK 提供登录、设备风险采集和事件上报,负责会话缓存与上报重试。广告、提现和页面行为由宿主处理。

能力与入参

左侧查看开放能力,右侧看接入时的数据流向。点击条目查看具体用法。

  • 登录与会话

    ensureSession · 缓存复用、并发合并、加密持久化

    返回 userId / token / riskTolerance;可刷新、读取或观察会话

  • 设备风控

    自动采集 Root、ADB、模拟器、Hook、无障碍、VPN 等

    业务无需实现检测;独立检测 / 扫描 API 可按需使用

  • 事件上报

    ReportQueue.report · 业务在真实事件发生时调用

    SDK 负责签名、持久化队列、断网 / 限流重试与会话恢复

  • 初始化必填

    context · baseUrl · bundleId · appSecret

    loginParameters 回调提供 deviceId、country

  • 调用时必填

    登录:scope 登录态分组;上报:identityScope 同一分组 + EventType

    创建队列:RiskClient + 应用级 CoroutineScope

  • 可选覆盖

    设备字段 · gaid / adid / network 归因字段

    riskStatExtras 追加业务字段;日志 / tag、会话通知等按需配置

scope / identityScope 是业务必传的登录态分组名,没有默认值;同一身份的登录与上报传同一个值。 CoroutineScope 则是队列运行的协程作用域。查看分组示例。 广告屏蔽、提现限制和页面提示由业务根据返回结果执行。 UNKNOWN 的处理见 版本说明。

接入示意

业务传入

初始化
Context · 域名
bundleId · 应用密钥
登录
deviceId · country
scope 登录态分组
上报
同一分组 · EventType

Risk Control

登录与会话 · 设备检测 · 事件上报

签名 · 持久化 · 队列重试

业务获得

登录态
userId / token
风控结果
riskTolerance
通知
会话回调 / 状态流
上报回执:确认或失败

广告、提现和页面提示由业务处理。

Play Integrity · 按需独立调用
Context + Cloud 项目编号 → 加密 Token
业务提交后端验证,不自动接入登录。

添加依赖

在 App 模块添加依赖,并配置下方的 GitHub Packages 仓库。

app/build.gradle.kts
dependencies {
    implementation("com.remax:risk-control:0.1.4")
}
Android 6.0 / API 23+JVM target 17发布构建 compileSdk 36 · Kotlin 2.2.21

仓库凭据

在用户目录的 ~/.gradle/gradle.properties 配置拥有该仓库 / 包读取权限、包含 read:packages 的 GitHub 令牌;CI 可用同名用途的环境变量。以下均为占位符。

~/.gradle/gradle.properties
gpr.user=YOUR_GITHUB_USERNAME
gpr.key=YOUR_GITHUB_PACKAGES_TOKEN

Maven 仓库

合并到宿主现有的 dependencyResolutionManagement。仓库凭据只参与 Gradle 下载;SDK 的 appSecret 是另一项业务配置。

settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        exclusiveContent {
            forRepository {
                maven {
                    url = uri("https://maven.pkg.github.com/toukaremax/risk-control")
                    credentials {
                        username = providers.gradleProperty("gpr.user")
                            .orElse(providers.environmentVariable("GITHUB_ACTOR")).orNull
                        password = providers.gradleProperty("gpr.key")
                            .orElse(providers.environmentVariable("GITHUB_TOKEN")).orNull
                    }
                }
            }
            filter { includeModule("com.remax", "risk-control") }
        }
    }
}
传递依赖、权限与混淆
依赖 声明版本 用途
kotlinx-coroutines-core 1.10.2 挂起函数、StateFlow 和队列
okhttp 4.12.0 HTTP 请求
play:integrity 1.6.0 可选 Play Integrity API
play-services-tasks 18.2.0 Play Integrity 的 Task 返回类型

以上依赖通过 Maven 元数据自动传递,最终版本由宿主 Gradle 冲突解析确定。AAR 合并 INTERNET、ACCESS_NETWORK_STATE 普通权限,无运行时授权弹窗。登录和事件上报无需接入内容签名插件或 Play Integrity。

SDK 发布 AAR 不预混淆,随宿主 Release 的 R8 一起处理;目前无需额外添加 SDK keep 规则。HTTP 明文地址是否允许由宿主网络安全配置决定。

最简接入

只需提供 Application Context、接口域名、BundleId、应用密钥、稳定设备 ID 和国家。 在 Application 或业务单例中创建一次,后续复用。

Kotlin · 创建客户端与事件队列
import com.remax.riskcontrol.sdk.client.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob

val riskClient = RiskClient(
    context = applicationContext,
    config = RiskClientConfig(
        baseUrl = apiBaseUrl,
        bundleId = backendBundleId,
        appSecret = backendAppSecret,
    ),
    loginParameters = { scope ->
        // 本例用国家代码作为登录态分组,因此 country 取 scope。
        LoginParameters(deviceId = stableDeviceId, country = scope)
    },
)
val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
val reports = ReportQueue(riskClient, applicationScope)

型号、系统版本、运营商、UA、应用版本和 RiskStat 由 SDK 默认采集;登录态和事件队列默认持久化。 gaid / adid / network 由业务按需提供,不传则省略。

0.1.2 已默认将 UNKNOWN 按未发现上报,无需额外兼容回调。 见 RiskStat 的版本说明。

Kotlin · 登录与上报
// 登录态分组名,由业务必传;本例按国家分组,设备身份固定。
val identityScope = "USA"

// 在业务协程中获取登录态,有缓存时直接复用。
val session = riskClient.ensureSession(identityScope)
// session.userId / session.token 供业务接口使用;使用前检查 canLogin 和凭据。

// 在真实事件发生时调用;SDK 自动登录、持久化和重试,无需等待回执。
reports.report(identityScope, ReportEventType.CONTENT_START)
reports.report(identityScope, ReportEventType.OPEN_PAYOUT_PAGE)

按需覆盖:业务已有的设备或归因值可直接传入 LoginParameters; 日志、会话通知、风险策略和网络配置均为可选项,见 配置参考。

登录态分组:登录参数叫 scope,上报参数叫 identityScope,含义相同。 同组复用登录态,不同组隔离;分组名是 SDK 内部标识,不作为接口字段发送。 单一身份可固定传 "default",此时 loginParameters 应独立提供真实的国家代码,不能把 default 当作 country。 按国家区分时可用 "USA" / "BRA" / "IDN";多账号还需包含账号标识。 分组名必须稳定、非空,切换身份时换组,并让 loginParameters(scope) 能根据分组返回对应的设备 ID 和国家,包括积压事件的旧分组。

创建 RiskClient 本身不登录;队列恢复积压事件时可能触发登录。队列使用 Application 生命周期。

登录态与风控

ensureSession 表示确保有可复用结果;有缓存时不会重新请求登录。

先查找内存会话;未命中时尝试恢复磁盘缓存,仍未命中才请求登录。恢复缓存或取得新登录结果后,触发 onSession。

公开函数 返回值 行为
state(scope: String) StateFlow<SessionState> 观察 LoggedOut / LoggingIn / Authenticated(session) / Restricted(riskTolerance) / Failed(reason)。
currentSession(scope: String) RiskSession? 仅读内存;不登录、不恢复磁盘。
suspend ensureSession(scope: String) RiskSession 同一登录态分组的并发调用合并;优先内存,再恢复磁盘,最后登录。
suspend invalidateSession(scope: String, expected: RiskSession) Unit 仅清除与 expected 相同实例的当前会话,同时清磁盘;旧请求不会误删较新的会话。
suspend refreshSession(scope: String) RiskSession 主动清除缓存,再获取新登录结果。

RiskSession 返回值

字段 含义
scope: String 对应的登录态分组名,由业务传入;与上报的 identityScope 使用同一个值。
userId: Long / token: String 业务接口所用登录凭据。等级 3 可能缺少凭据,不能拿空凭据调用业务接口。
riskTolerance: Int 原样保留服务端等级,见下表。
canLogin: Boolean riskTolerance 在 0..2 时为 true。
canWithdraw: Boolean riskTolerance 为 0 时为 true。
isRestored: Boolean true 表示从磁盘恢复,是旧决策,不是刚取得的服务端结果。
等级 SDK 语义 短剧目前采用的业务策略
0 正常 允许广告和提现,清除旧限制。
1 限制提现 提现入口提示,不限制广告。
2 限制广告和提现 调用广告屏蔽 API,提现入口提示。
3 本次不可登录;canLogin=false 短剧同样屏蔽广告和提现,SDK 仍保留等级 3。

SDK 不调用广告库、不弹 Toast、不关闭应用,也不代替业务判断提现。是否上报以是否有完整凭据为准;等级 1 / 2 仍可上报。等级 3 无 Token 时失败结束,不循环登录。

业务接口如何无感刷新

SDK 自动处理自己的 report 会话失效。提现等宿主接口继续由现有拦截逻辑识别失效,并调用以下公共函数:

Kotlin · 业务请求已确认会话失效后
// failedSession 必须是这次失败请求实际使用的会话对象。
riskClient.invalidateSession(identityScope, failedSession)
val refreshed = riskClient.ensureSession(identityScope)
// 由业务决定是否使用 refreshed 重发该业务请求,并限制恢复次数。
// 不要将所有业务错误都当成 Token 失效。

SDK 的会话失效兼容条件为 Result=201 / 406 或 HTTP 401。单独 HTTP 406 不算;这是当前客户端兼容规则。

缓存隔离与持久化细节

默认以 Android Keystore AES-GCM 加密 Token、UserId、RiskTolerance 等会话数据,存入私有 SharedPreferences。按宿主包名、规范化 baseUrl(含路径)、bundleId、appSecret 和 scope 隔离,恢复时校验 DeviceId / Country。加密不可用时只用内存,不降级明文。

SDK 不猜测 Token 有效期;久未使用的缓存可先复用,服务端返回失效后再刷新。等级 3 不保存成可恢复会话,但进程内保留决策,防止循环重登。新登录时才能拿到新风控结果。

业务广告 / 提现策略的持久化由宿主维护。短剧会单独保存限制;磁盘恢复的旧会话不会覆盖更新的业务限制。新登录结果为 0 时清除限制,失败 / 离线时不自动放行。

事件上报

普通业务全部使用 ReportQueue。SDK 负责登录、签名、持久化、串行发送和失败重试。

Kotlin · 公开上报入口
// scope 是应用级 CoroutineScope,store 通常省略。
val reports = ReportQueue(client = riskClient, scope = applicationScope)

val receipt: ReportReceipt = reports.report(
    identityScope = identityScope, // 必填:登录态分组,与 ensureSession 的 scope 一致
    type = ReportEventType.AD_COMPLETE,
    value = revenue, // Double;默认 0.0,必须非负且有限
)
// receipt.id 是本地事件 ID;不会作为幂等键发送给后端。
// 在协程中 receipt.await() 等待确认或失败;取消等待不取消事件。
枚举 EventType 业务调用位置 / Value
NOTHING 0 空事件,按服务端用途使用。
CONTENT_COMPLETE 1 内容完成的既有业务回调,通常 Value=0。
AD_COMPLETE 2 短剧沿用广告收益回调,Value=收益,单位沿用服务端约定;不是广告关闭回调。
CONTENT_START 4 播放器实际开始播放的回调;宿主避免缓冲 / 暂停恢复重复计数。
OPEN_PAYOUT_PAGE 5 进入提现页面后,Value=0;入口被拦截未进页面不报。

当前不提供广告开始 AD_START / 3。开发工具可以直接调用相同公共队列,模拟内容开始、提现页打开等事件。

JSON · report 实际请求正文示例
{
  "EventType": 5,
  "Value": 0,
  "Timestamp": 1793318400,
  "Retry": 0
}

Timestamp 是入队时记录的事件 UTC Unix 秒,重试保持原值。Retry 首次即传 0;同一事件重发递增。每次请求头 Timestamp / Nonce / Sign 会重新生成。仅登录阶段失败时,尚未发出 report,不增加该事件的 Retry。

提现页打开上报不阻塞提现。调用 report 后业务正常显示页面 / 请求提现,不等待 receipt.await()。等待回执主要用于开发工具或确实需要确认的场景。
单次调用:reportOnce
Kotlin · 不进入队列的适配入口
riskClient.reportOnce(
    scope = identityScope,
    type = ReportEventType.CONTENT_START,
    value = 0.0,
    timestampSeconds = occurredAtUtcSeconds, // 可省略,默认当前 UTC 秒
)

挂起函数,返回 Unit;不持久化,不做网络 / 429 定时重试,仅支持一次失效会话恢复重发。常规业务和短剧开发工具均应使用 ReportQueue。

队列与重试

同一队列按入队顺序发送。网络恢复由定时尝试发现,没有主动注册网络恢复监听。

  1. 第一次重试:等待 30 秒。
  2. 第二次重试:等待 2 分钟。
  3. 第三次重试:等待 5 分钟。
  4. 之后每 30 分钟重试一次。

前三档最多追加 20% 随机延迟;之后每 30 分钟一次。与服务端 Retry-After 取较长时间。网络失败与 HTTP 429 共用退避次数。

结果 / 情况 队列处理
断网、连接失败、读写超时、HTTP 429 保留事件、持久化下次尝试时间;队首等待期间,后续事件排队。
report 会话过期 清除失效会话、重新登录,原事件最多立即恢复重试一次;网络 / 限流重试不重置该次数。
服务端成功 移除持久化记录,完成回执并记录 ack;不另外保存成功历史。
业务错误、其他 HTTP 错误(含普通 5xx)、无效 JSON、明确证书 / 主机名校验失败 完成失败并移出队列,继续后续事件。
风控拒绝且缺少凭据 失败结束,不自动循环登录。
App 进程退出 下次初始化队列恢复;上次仍标记发送中的事件按不确定尝试保留,增加 Retry 并退避。
持久化读写失败 停止本轮发送并通知当前等待方,不盲目继续发请求。
成功口径、重复发送和生命周期边界
  • 当前成功判断是 HTTP 2xx + 可解析 JSON + 解析后的 Result=0。兼容解析中缺失 / 非数字 Result 会按 0 处理,并非只接受显式 HTTP 200 / Result=0。
  • 本地删除发生在成功响应后;终止类错误也会删除,但回执失败、日志为 failed。调用 report 返回回执不等于已经成功,也不等于已写盘。
  • 超时可能发生在服务端已接收之后;目前后端没有事件幂等键,因此重发有重复计数可能。receipt.id 只用于本地跟踪。
  • 重试由循环和挂起 delay 实现,不递归增加调用栈。没有队列容量上限或自动过期清理,长期离线持续入队会增加存储占用。
  • 进程存活时按计时器重试,进程结束后等待下次启动;不创建系统后台任务。重启 / 新事件不会重置已经持久化的退避时间。
  • 默认 SharedPreferences 存储按宿主包名、baseUrl(含路径)、bundleId 隔离;密钥轮换不会切换事件队列。每份存储使用一个队列实例。

RiskStat

SDK 默认自动采集并拼接 RiskStat,业务无需实现检测或手动调用采集函数。 原始检测结果区分 DETECTED、NOT_DETECTED 和 UNKNOWN。

0.1.2 默认行为:UNKNOWN 按未发现编码为 1,不再因此中断登录,原始检测结果不变。 旧版 0.1.1 遇到 UNKNOWN 会拒绝序列化,仍使用旧版时可参考下方兼容方式。

自定义策略可通过 deviceRisk 返回调整后的结果;明确状态会按业务值上报。 显式传 deviceRisk = null 或回调返回 null,则省略整个 RiskStat。

追加业务字段:riskStatExtras(0.1.3 起)

新增可选 riskStatExtras: suspend (scope: String) -> Map<String, Any?>。 业务可直接返回缓存,也可挂起等待结果;每次真实登录在 IO 调用,并等返回后再合并、签名和发送。 等待时长及失败时是否继续登录由业务决定。同组并发登录共用本次等待,内存或磁盘会话命中时不调用。

Kotlin · RiskStat 扩展字段
// 添加到 RiskClient(...) 参数中;awaitPlayResult 是业务函数。
// PlayInstall 仅为示例字段名,须与后端约定。
riskStatExtras = { scope ->
    val result = kotlinx.coroutines.withTimeoutOrNull(1_000) {
        awaitPlayResult(scope)
    }
    mapOf("PlayInstall" to result)
},

不需要等待时直接读取业务缓存即可。支持字符串、布尔及有限的基本数值类型;null 字段省略,空 Map 保留默认字段。 内置字段仍通过 deviceRisk 调整;冲突键、空白键及不支持的值会使本次登录失败。 回调异常和取消向外传播,需要继续登录时由业务处理并返回空 Map。 显式省略整个 RiskStat 时也跳过扩展回调。SDK 不因此自动执行 Play Integrity,字段值由业务提供。

仅 0.1.1 需要:UNKNOWN 按未发现处理

在 RiskClient 构造参数中补充以下回调。升级到 0.1.2 后可省略。

Kotlin · 0.1.1 兼容参数
// Status 来自 com.remax.riskcontrol.sdk.Status。
// 添加到 RiskClient(...) 的参数中:
deviceRisk = {
    val observed = collectDeviceRisk(applicationContext)
    fun resolve(value: Status) =
        if (value == Status.UNKNOWN) Status.NOT_DETECTED else value
    observed.copy(
        root = resolve(observed.root), adb = resolve(observed.adb),
        emulator = resolve(observed.emulator), hook = resolve(observed.hook),
        accessibility = resolve(observed.accessibility), vpn = resolve(observed.vpn),
    )
}
DeviceRisk 字段 RiskStat 字段 含义
networkType / timezone / locale NetworkType / Timezone / Locale 当前网络类型、时区、系统语言标记。
root Milan Root 检测。
adb Como ADB 开关;与调试器检测分开。
emulator Lazio 模拟器检测。
hook Roma Hook 检测。
accessibility Genoa 无障碍服务检测。
vpn Verona 默认网络 VPN;不把单纯代理当 VPN。

collectDeviceRisk(context: Context): DeviceRisk 是挂起函数,内部切换 IO。DeviceRisk.toJson(): JSONObject 将 DETECTED 编码为 0、NOT_DETECTED 编码为 1;UNKNOWN 按上方版本说明处理。 采集只包含登录需要的设备项,不进行完整 APK 扫描,也不请求 Play Integrity Token。

需要独立检测时可用 RiskScanner.scan() 或直接检测函数,详见 设备检测与可选插件指南 ↗。这部分是 SDK 的独立能力。

调试日志

所有 login / report 请求及响应、RiskStat 明细和队列状态,统一使用 RiskClient 的 debug 配置。

从 0.1.4 起,RiskStat 和 EventType 的释义直接拼在请求日志的 JSON 值中, 不再另外输出释义段。字段名不变,原始 UNKNOWN 会注明“未知,按未发现上报”。 业务追加的 RiskStat 字段保留原样。

带释义的字符串仅用于 debug 日志;实际 HTTP 仍发送原协议值和类型,签名不变。例如日志中的 EventType 为“4(CONTENT_START,内容开始)”,实际发送的仍是数字 4。

Kotlin · 与业务统一 tag
RiskClientConfig(
    baseUrl = baseUrl,
    bundleId = bundleId,
    appSecret = appSecret,
    debug = RiskDebugConfig(enabled = BuildConfig.DEBUG, tag = "CoreModule"),
)
// 可选:传给 RiskClient 的 diagnostics 参数。
val diagnostics = RiskDiagnostics { tag, message ->
    android.util.Log.d(tag, "MultiCountryPayout: " + message)
}
Logcat · 结构示例(正文为示例值)
request /user/login headers=
{ ... } body=
{
  "DeviceId": "example-device",
  "Country": "USA",
  "RiskStat": {
    "NetworkType": "wifi(网络类型)",
    "Timezone": "Asia/Shanghai(时区)",
    "Locale": "en-US(系统语言)",
    "Milan": "1(Root,未发现)",
    "Como": "0(ADB 调试,已检测到)",
    "Lazio": "1(模拟器,未发现)",
    "Roma": "1(Hook,未知,按未发现上报)",
    "Genoa": "1(无障碍服务,未发现)",
    "Verona": "0(VPN,已检测到)"
  }
}
request /user/report headers=
{ ... } body=
{
  "EventType": "4(CONTENT_START,内容开始)",
  "Value": 0,
  "Timestamp": 1793318400,
  "Retry": 0
}
response /user/report http=200 result=0 body=
{
  "Result": 0
}
report_queue ack type=CONTENT_START retry=0
report_queue retry type=CONTENT_START reason=network login_only=false delay_ms=32000

默认 enabled=false,不传 tag 时为 RiskControl。开启后完整打印请求头 / 正文 / 响应,包括 Token、DeviceId、UserId、Sign 等,不脱敏;RiskStat 和 EventType 的日志值附带释义。JSON 两空格缩进,长消息分段编号;不改变实际请求正文和签名。appSecret 本身不是请求字段,不单独输出。

操作 线程行为
HTTP、默认采集、会话与队列磁盘 IO SDK 内部在 IO 执行,report 可由主线程触发。
参数校验、读内存、事件时间记录 在调用线程执行。
loginParameters / deviceRisk IO 上执行。
onSession 调用协程的上下文,不保证 Main;UI 更新自行切主线程或观察 StateFlow。
RiskDiagnostics.log 可能后台调用;日志回调异常不会破坏业务。
独立 checkRoot 等同步检测函数 调用方负责放后台;RiskScanner 使用内部工作线程。

配置参考

可选参数省略时使用默认实现;只覆盖业务已经维护的字段。

RiskClientConfig

参数 默认值 / 要求 作用
baseUrl: String 必填 后端根地址,拼接 /user/login 和 /user/report;支持根地址包含路径。
bundleId: String 必填 服务端应用标识并参与签名,可与开发 APK 的包名不同。
appSecret: String 必填 login 签名密钥,不是 GitHub Packages 令牌。
debug: RiskDebugConfig enabled=false
tag="RiskControl"
统一控制 login、report、队列日志,可覆盖 tag。
persistSession: Boolean true 加密保存登录态;false 时只使用内存,不影响事件持久化。

RiskClient 构造参数

参数 默认 / 类型 调用时机与用途
config 必填 · RiskClientConfig 服务配置。
context 必填 · Context 内部仅保留 applicationContext,用于设备默认值、采集、持久化。
loginParameters 必填 · suspend (String) → LoginParameters 接收登录态分组名 scope,向业务获取该组的设备 ID、国家和其他登录字段。IO 上执行;首次校验磁盘缓存及每次真实登录调用,内存命中不调用。
deviceRisk 可选 · suspend () → DeviceRisk? 默认 collectDeviceRisk(context),真实登录时在 IO 采集。可覆盖采集或映射策略;UNKNOWN 处理见 RiskStat 的版本说明。 显式传 null 或返回 null 省略 RiskStat。
riskStatExtras 可选 · 0.1.3 起 挂起回调,根据 scope 追加 RiskStat 字段;真实登录在 IO 等待返回,超时与降级由业务决定。见 RiskStat 章节。
client 可选 · OkHttpClient 默认 connectTimeout=15 秒、readTimeout=30 秒。report 使用派生 client,关闭自动连接重试和重定向;login 保留传入配置。
nowEpochSeconds 可选 · () → Long 默认系统 Unix 秒。请求签名、Retry-After 计算、reportOnce 默认时间使用它。队列事件时间独立在入队时采集。
nonceSource 可选 · () → String 默认 UUID;每次带会话的请求产生新 Nonce。login 不使用 Nonce。
diagnostics 可选 · RiskDiagnostics 默认 Android Log.d;debug 开启时收到 tag 和文本,可接宿主日志系统。
onSession 可选 · (RiskSession) → Unit 默认空函数。恢复磁盘或新登录结果写入内存 / 状态后通知业务,包括等级 3;内存命中、登录失败不通知。

LoginParameters

参数 → 协议字段 默认行为 覆盖规则
deviceId → DeviceId 必填非空 沿用宿主稳定设备 / 用户身份。
country → Country 必填非空 服务端约定的地区代码。
deviceType → DeviceType 厂商 + 型号 null 使用默认值;非 null 使用传入值。
deviceVersion → DeviceVersion Android OS 版本 / API / Build 同上。
isp → Isp 系统运营商名(尽力获取) null 尝试默认值;空串省略字段。
userAgent → UserAgent WebSettings 默认 UA / http.agent null 尝试默认值;空串省略字段。
version → Version 宿主 versionName null 使用默认值;非 null 使用传入值。
gaid → Gaid
adid → Adid
network → Network
不自动采集归因信息 由业务提供,null / 空串省略。

Network 是业务归因渠道,RiskStat.NetworkType 是当前设备网络类型,两者含义不同。

辅助 API

标准接入省略 store 即可。仅兼容旧队列或有独立存储需求时覆盖。

ReportStore.preferences(...) 可指定持久化名称并迁移旧 scope; ReportStore(read, write, legacyScope) 可对接业务存储。 显式传入无参数的 ReportStore() 会使用纯内存,普通接入应省略 store。

辅助 API / 类型 用途
RiskProtocol.sign(content: String, key: String): String HMAC-SHA256,输出小写十六进制;供确需自行适配业务签名的宿主使用,login/report 已自动签名。
riskRetryAfterMillis(value: String?, nowMillis: Long): Long? 解析 Retry-After 秒数或 HTTP 日期,返回等待毫秒;无效 / 不支持返回 null。
RiskServiceException 提供 httpCode、result、serverMessage、retryAfterMillis、requestPath,以及 isExpiredSession()。
RiskRestrictedException 提供 riskTolerance,表示缺少可用凭据的风控结果。
ReportReceipt id: String;suspend await(): Unit。成功确认时完成,终止失败抛异常;只是服务确认,不代表业务收益到账。
RiskDiagnostics 函数接口 log(tag: String, message: String)。
ReportStore(read, write, legacyScope) read: () → String?;write: (String) → Unit;legacyScope: (String?) → String?。省略三者为纯内存存储。

普通 IOException、参数校验异常也可能返回给调用方;协程取消继续传播。SDK 没有对外提供手动 flush、成功历史查询、重试次数配置或队列清空函数。

更新记录

0.1.4 · 已发布

RiskStat 和 EventType 释义合并到请求日志 JSON 中,移除独立释义段。保留业务扩展字段、debug 开关和自定义 tag;实际 HTTP 请求、协议类型及签名不变。

0.1.3 · 已发布

新增 riskStatExtras 挂起回调,可在默认 RiskStat 中追加业务字段,由业务决定是否等待结果及超时策略;保留原构造函数。report 调试日志补充 EventType 中文释义。能力与入参增加接入示意图。

0.1.2 · 已发布

UNKNOWN 默认按未发现上报,保留原始检测状态和业务自定义能力;login 日志增加 RiskStat 字段释义。接入文档补充最简示例、能力导图和登录态分组说明。

0.1.1 · 已发布

  • 统一 HTTP 日志:login / report 的请求、响应和 RiskStat 明细;JSON 格式化,长日志分段。
  • 修复主线程发请求:开发工具由 Main 触发事件时,队列显式调度至 IO。
  • 持久化网络重试:断网 / 超时保留事件,使用 30 秒 → 2 分钟 → 5 分钟 → 30 分钟退避。
  • 恢复未确认事件:重启保留事件时间、Retry 和退避进度,继续尝试未确认的发送。