PAYATHON 2026

为什么不同游戏应用的支付SDK调用权限不同?

支付老李

结论

即使接入同一家支付 SDK,不同游戏的实际采集行为也可能不一样。常见原因有:SDK 版本和依赖组件不同、支付渠道不同、初始化参数或远程配置不同、宿主应用已有权限不同,以及检测环境和触发时机不同。

还需要区分“读取数据”和“申请权限”:

  • 读取 Android ID 通常不需要申请 Android 运行时权限。
  • 获取网络连接状态可能需要 ACCESS_NETWORK_STATE,但这是普通权限,不会弹出授权框。
  • 发起支付网络请求需要 INTERNET,支付服务器也会从请求中获得来源 IP。客户端通常无法在正常联网支付的同时,让服务器看不到 IP。
  • 加速度计、陀螺仪等部分传感器在许多 Android 版本上不需要运行时权限,但隐私检测工具仍可能记录相关调用。

所以,只删除危险权限未必能阻止 SDK 调用相关 API。应先确认真正的调用来源,再关闭 SDK 的可选采集功能,并把初始化放在用户同意隐私政策之后,最后用真实设备重新验证。如果 SDK 没有提供关闭接口,应要求供应商提供合规版本,不要自行修改 SDK。

为什么另一款游戏没有出现相同调用

SDK 及其依赖并不完全相同

所谓“最新版支付 SDK”,可能同时包含设备识别、反欺诈、风控、统计、广告归因或崩溃分析模块。另一款游戏使用的可能是:

  • 不同版本的支付 SDK;
  • 精简版或合规版 SDK;
  • 不同的渠道包;
  • 不同版本的间接依赖;
  • 已经移除风控、统计等可选组件的构建产物。

比较时不能只看主 SDK 的名称,还要核对最终 APK 或 AAB 实际打包了哪些依赖。

可以通过 Gradle 查看依赖关系:

./gradlew app:dependencies

也可以检查运行时依赖:

./gradlew app:dependencyInsight \
  --dependency SDK依赖名称 \
  --configuration releaseRuntimeClasspath

初始化参数或后台配置不同

部分 SDK 会根据商户号、应用标识、支付渠道、地区、风险等级或服务端开关,决定是否启用某些功能。即使客户端使用相同版本,只要服务端下发的配置不同,数据采集行为也可能不同。

这类差异无法单靠反编译或清单文件完全确认,需要向 SDK 提供方核实:

  • 哪些字段是支付必需数据;
  • 哪些字段用于可选的风控或统计功能;
  • 是否提供隐私合规开关;
  • 开关由客户端配置还是服务端控制;
  • 关闭后是否会影响某些支付渠道。

调用可能发生在不同阶段

敏感 API 不一定要等到用户点击支付按钮后才会调用。有些 SDK 会在以下阶段提前采集:

  • Application.onCreate();
  • ContentProvider 自动初始化;
  • 首次进入游戏;
  • SDK 预加载;
  • 登录或实名认证;
  • 创建支付订单;
  • 打开收银台后的异步线程。

另一款应用可能在监测开始前就完成了采集,也可能直到支付结束后才调用。如果只监测“点击支付到收银台出现”这一小段时间,得到的结论可能并不完整。

系统、设备和检测条件不同

Android 版本、厂商系统、目标 SDK 版本、设备是否安装 SIM 卡、网络类型及传感器配置,都可能影响 SDK 走哪条代码分支。权限监测工具本身也存在差异,有些工具可能:

  • 只记录运行时权限弹窗;
  • 同时记录敏感 API 调用;
  • 不区分宿主应用和第三方 SDK 的调用;
  • 无法覆盖子进程、WebView 或支付应用中的调用;
  • 不能完整识别反射、Native 代码或持续时间很短的异步调用。

比较时,应尽量使用相同的设备、系统版本、网络环境、支付渠道和监测区间。

建议的处理步骤

1. 定位真正的调用方

先确认调用是否确实来自支付 SDK,而不是宿主应用或其他第三方组件。检查时应重点关注调用栈、线程、进程名,以及对应的类或 .so 文件。

如果监测工具只能显示“应用读取了 Android ID”,却不能提供调用栈,就不能直接认定是支付 SDK 所为。可以暂时移除该 SDK,或者制作一个只包含支付功能的最小测试应用进行对照。

建议至少覆盖以下场景:

  1. 未初始化 SDK;
  2. 初始化 SDK,但不发起支付;
  3. 创建订单;
  4. 打开支付页面;
  5. 支付成功、取消和失败;
  6. 用户同意隐私政策前后。

2. 检查最终合并后的权限

第三方依赖可以通过自己的 AndroidManifest.xml 向应用添加权限,因此,只看项目主清单并不够。应检查构建后的合并清单,并确认每项权限来自哪个依赖。

如果确定某项权限不是支付必需项,可以在宿主清单中移除依赖添加的声明:

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission
        android:name="android.permission.BODY_SENSORS"
        tools:node="remove" />

    <uses-permission
        android:name="android.permission.READ_PHONE_STATE"
        tools:node="remove" />

    <application>
        <!-- application components -->
    </application>
</manifest>

不要机械地删除 INTERNET,否则支付 SDK 通常无法访问支付服务。移除 ACCESS_NETWORK_STATE 等权限之前,也要确认 SDK 在缺少权限时可以正常降级,不会崩溃或导致支付失败。

删除权限只能阻止依赖该权限的操作。对于不需要运行时权限的 Android ID、部分传感器和基础设备信息读取,修改清单可能没有作用。

3. 关闭可选的数据采集功能

优先使用 SDK 官方提供的隐私配置、合规模式或模块开关。不同厂商使用的 API 名称并不相同,应以当前版本的官方接入文档为准,不要照搬实际并不存在的参数。

下面的代码只用于表示推荐的调用顺序,并不对应某个真实 SDK 的 API:

class PaymentInitializer {

    fun initializeAfterConsent(context: Context) {
        val privacyOptions = PrivacyOptions(
            enableDeviceIdentifier = false,
            enableSensorCollection = false,
            enableOptionalNetworkInfo = false,
            enableAnalytics = false
        )

        PaymentSdk.initialize(
            context = context.applicationContext,
            privacyOptions = privacyOptions
        )
    }
}

如果官方 SDK 没有类似配置,应直接向供应商索取明确说明或合规版本。不要通过反射修改 SDK 的私有字段,也不要直接删除 AAR 中的类或 Native 库,否则可能破坏签名校验、风控流程或支付稳定性。

4. 将初始化放到用户同意之后

不要在用户作出隐私选择前初始化会采集数据的 SDK:

fun onPrivacyConsentAccepted(context: Context) {
    PaymentInitializer().initializeAfterConsent(context)
}

还要检查 SDK 是否通过 ContentProvider 自动初始化。即使应用没有主动调用 initialize(),自动注册的组件仍可能在 Application.onCreate() 之前运行。

如果官方文档允许关闭自动初始化,可以通过清单移除相应组件,再改为手动初始化:

<application
    xmlns:tools="http://schemas.android.com/tools">

    <provider
        android:name="com.vendor.payment.PaymentInitProvider"
        android:authorities="${applicationId}.payment-init"
        tools:node="remove" />

</application>

这里的类名和 authorities 只是结构示例。实际使用时,必须替换为合并清单中的真实声明,并先确认 SDK 官方支持手动初始化。随意删除必要的 provider 可能导致 SDK 无法工作。

5. 不要提前申请非必要权限

如果支付 SDK 只在某个可选功能中需要传感器或设备权限,应等用户主动使用该功能时再申请,不要在应用启动或打开支付页时统一申请。

宿主应用也不应为了“防止 SDK 报错”而提前授予权限。合规的 SDK 应当能在权限未授予时正常降级:

if (ContextCompat.checkSelfPermission(
        context,
        Manifest.permission.BODY_SENSORS
    ) == PackageManager.PERMISSION_GRANTED
) {
    // 仅在业务确实需要时使用相关功能
}

这段判断只能控制宿主应用自己的逻辑,无法拦截第三方 SDK 在内部直接调用敏感 API。

6. 重新进行全链路验证

完成配置后,应使用正式发布包测试。Debug 和 Release 构建所包含的依赖、混淆规则及渠道配置可能不同。测试范围应包括:

  • 首次安装和非首次启动;
  • 用户同意前、拒绝后和同意后;
  • Wi-Fi、移动网络及无网络环境;
  • 不同支付渠道;
  • 支付成功、失败和取消;
  • 主进程、支付子进程及 WebView;
  • SDK 冷启动与已初始化状态。

测试时还要记录调用时间、调用栈、所属 SDK、数据类型和对应的业务操作。检测到某个 API 被调用,并不能证明数据已经上传。要确认上传行为,还需结合网络请求、SDK 数据清单和供应商说明进行判断。

关于几类数据的具体处理

Android ID

Settings.Secure.ANDROID_ID 通常可以直接读取,不依赖危险权限。因此,仅拒绝权限无法阻止 SDK 获取它。可以采取的处理方式主要有:

  • 使用 SDK 提供的关闭开关;
  • 延迟初始化;
  • 移除不必要的设备识别或风控模块;
  • 更换不采集该字段的 SDK 版本;
  • 要求供应商提供合规实现。

IP 地址

只要客户端连接支付服务器,服务器通常就能从网络连接中获得来源 IP。这与 SDK 主动枚举本机网卡、读取局域网地址,或者将 IP 用于额外画像并不是一回事。

如果支付必须联网,就不能承诺“完全不处理 IP”。更实际的做法是确认 IP 的处理目的、保存期限、共享对象和安全措施,同时避免将其用于支付之外的统计、广告或画像。

传感器

SDK 访问传感器,可能是为了判断设备姿态、识别自动化操作或进行支付风控。部分传感器不需要危险权限,因此不会出现系统授权弹窗,但相关访问仍可能属于需要披露或控制的数据处理行为。

如果支付功能不依赖这些数据,应关闭对应模块。如果供应商认为传感器数据是风控必需项,应要求其说明具体的数据范围、采样频率、处理目的、是否上传,以及关闭后会产生什么影响。

注意事项

  • 不要把“没有弹出权限框”等同于“没有读取数据”。
  • 不要只比较 APK 声明的权限,还要比较依赖、初始化流程和网络行为。
  • tools:node="remove" 只影响清单合并,不能阻止不需要权限的 API 调用。
  • SDK 的远程开关可能改变实际行为。升级版本或调整商户配置后,需要重新测试。
  • 隐私政策中的披露不能代替最小化采集和用户同意。
  • 如果敏感数据并非支付必需,而 SDK 又无法关闭相关采集,最稳妥的处理方式是要求供应商整改、改用精简版本或更换 SDK。

备注:内容仅供参考。