React Native SDK - 广告收入跟踪

广告收入归因

将广告收入与为您的应用带来用户的具体营销活动关联起来,全面掌握营销活动成本、应用内收入和广告收入,从而精确衡量 ROI。

概述

什么是广告收入归因

广告收入归因将移动应用的广告收入与带来用户的营销活动关联起来,把用户获取成本与包含广告变现在内的生命周期收入连接起来,从而衡量营销活动的真实表现。

  • 营销活动 ROI: 在统一报告中查看营销活动成本、应用内购买和广告收入,计算真实的广告支出回报率
  • 渠道优化: 将广告收入数据回传给广告渠道,以改进出价算法和营销活动表现
  • 数据来源: 支持来自 AdMob、AppLovin MAX、Unity LevelPlay(IronSource)和 TradPlus 等聚合平台的用户级和展示级数据

完整说明请参阅 广告收入归因常见问题

重要注意事项:

  1. 货币代码: 请使用 ISO 4217 三字母货币代码(USD、EUR、INR)。大多数聚合平台以 USD 报告,请在实施前确认您所用平台的货币
  2. 数据准确性: 发送到 Singular 之前请校验收入和货币数据。错误数据无法事后更正

实施要求

广告收入跟踪需要集成聚合平台 SDK 并配置收入回调。

  1. SDK 版本: 请更新到最新版本的 Singular React Native SDK
  2. 聚合平台: 集成您所用聚合平台(AdMob、AppLovin MAX、IronSource 或 TradPlus)的 React Native SDK
  3. 收入回调: 实现各平台专用的付费事件处理程序,以采集展示级收入数据
  4. 校验逻辑: 在向 Singular 发送数据前添加收入和货币校验

SDK 方法

Singular.adRevenue

将广告收入数据连同平台、货币和收入金额一并报告给 Singular,以归因到该用户的获客营销活动。

方法签名:

static adRevenue(adData: SingularAdData | {
    ad_platform: string;
    ad_currency: string;
    ad_revenue: number;
    [key: string]: any;
}): void

键名会被精确校验。 SDK 会查找 ad_platformad_currencyad_revenue。这三项中只要缺少任意一项, adRevenue() 就会直接返回、不发送任何数据,也不会抛出错误,因此键名拼写错误表现为毫无反应而不是失败。使用 SingularAdData 构建载荷即可避免这一风险。

必填参数:

  • ad_platform: 聚合平台名称(例如 "AdMob"、"AppLovin"、"IronSource"、"TradPlus")
  • ad_currency: ISO 4217 三字母货币代码(例如 "USD"、"EUR")
  • ad_revenue: 以指定货币计的收入金额(必须大于 0)

使用 SingularAdData 构建载荷

SingularAdData 在构造函数中接收三个必填值,并为每个可选字段提供可链式调用的方法。请从软件包中将其与 Singular一起导入。

import { Singular, SingularAdData } from 'singular-react-native';

const adData = new SingularAdData('AdMob', 'USD', 0.05)
  .withAdUnitId('ca-app-pub-1234567890123456/9876543210')
  .withAdType('Rewarded')
  .withPrecision('precise');

Singular.adRevenue(adData);

可选的广告数据字段

以下字段均为可选。请尽量发送聚合平台提供的所有数据:载荷越完整,Singular 中的广告收入报告就越精细。如果您构建的是普通对象而非使用构建器,请使用第二列中的载荷键。

构建器方法 载荷键 详情
withNetworkName() ad_mediation_platform 报告该次展示的聚合平台。默认取构造函数中传入的 ad_platform 值。
withAdType() ad_type 广告形式,例如 RewardedInterstitialBanner
withGroupType() ad_group_type 聚合平台报告的广告组类型。
withImpressionId() ad_impression_id 唯一展示标识符。使 Singular 能够对展示去重。
withAdPlacementName() ad_placement_name 可读的广告位名称。
withPlacementId() ad_placement_id 广告位标识符。
withAdUnitId() ad_unit_id 广告单元标识符。
withAdUnitName() ad_unit_name 可读的广告单元名称。
withAdGroupId() ad_group_id 广告组标识符。
withAdGroupName() ad_group_name 可读的广告组名称。
withAdGroupPriority() ad_group_priority 聚合平台报告的广告组优先级。
withPrecision() ad_precision 聚合平台报告的收入精度,例如 preciseestimatedpublisher_defined
withLimitDataSharing() sng_attr_limit_data_sharing 仅将此广告收入事件标记为限制数据共享,与整个 SDK 的 limitDataSharing 设置无关。Boolean。需要 React Native SDK 4.3.0 或更高版本。

完整的方法文档请参阅 adRevenue 参考


平台集成

AdMob 集成

使用 Google Mobile Ads SDK 的付费事件回调实现 AdMob 广告收入跟踪,以报告展示级收入。

前提条件

  • 在您的 AdMob 账户中启用广告收入报告。请参阅 AdMob 支持
  • 集成 React Native Google Mobile Ads 软件包。请参阅 入门指南

实施概述

加载广告形式(App Open、Banner、Interstitial、Native、Rewarded)时,配置在广告产生收入时触发的付费事件处理程序。从事件数据中提取收入值和货币,校验这两个值后发送给 Singular。

平台差异: AdMob 在不同平台上报告收入的方式不同。Android 以微单位报告(例如 $0.005 显示为 5000),需要除以 1,000,000。iOS 直接以美元报告(0.005)。请根据平台检测结果调整换算逻辑。


AdMob 激励广告示例

使用 AdMob 加载激励广告,并采集付费事件用于收入跟踪。

New ArchitectureOld Architecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/js/NativeSingular';
import { RewardedAd, AdEventType } from 'react-native-google-mobile-ads';
import { Platform } from 'react-native';

const AD_UNIT_ID = 'ca-app-pub-xxxxxxxxxxxxx/yyyyyyyyyy';

export default function AdMobRevenueTracker() {
  useEffect(() => {
    loadRewardedAd();
  }, []);

  const loadRewardedAd = () => {
    // Create RewardedAd instance
    const rewardedAd = RewardedAd.createForAdRequest(AD_UNIT_ID);

    // Set up event listener for ad events
    rewardedAd.addAdEventListener((type, error, data) => {
      if (type === AdEventType.LOADED) {
        console.log('Rewarded ad loaded');
      } else if (type === AdEventType.ERROR) {
        console.error('Rewarded ad failed to load:', error);
      } else if (type === AdEventType.PAID_EVENT) {
        // Handle paid event with revenue data
        handleAdRevenue(data);
      }
    });

    // Load the ad
    rewardedAd.load();
  };

  const handleAdRevenue = (data) => {
    const { value, currencyCode } = data;

    // Validate revenue and currency
    if (!value || value <= 0) {
      console.error('Invalid ad revenue value:', value);
      return;
    }

    if (!currencyCode || currencyCode.trim() === '') {
      console.error('Invalid currency code:', currencyCode);
      return;
    }

    // Convert revenue based on platform
    let revenue;
    if (Platform.OS === 'android') {
      // Android reports in micros - convert to dollars
      revenue = value / 1_000_000.0;
    } else {
      // iOS reports in dollars directly
      revenue = value;
    }

    const adRevenueData = {
      ad_platform: 'AdMob',
      ad_currency: currencyCode,
      ad_revenue: revenue
    };

    // Send to Singular
    NativeSingular.adRevenue(adRevenueData);

    console.log('Ad Revenue reported to Singular:', adRevenueData);
  };

  return null;
}

实施说明:

  • 平台检测: 使用 Platform.OS 确定换算逻辑(Android 除以 1,000,000,iOS 直接使用该值)
  • 收入校验: 发送前确保收入大于 0
  • 货币校验: 确认货币代码非空
  • 错误日志: 记录无效数据以便调试,但不要发送给 Singular

AppLovin MAX 集成

使用 Impression-Level User Revenue API 实现 AppLovin MAX 广告收入跟踪,在所有广告形式上实时报告收入。

前提条件

  • 集成 AppLovin MAX React Native SDK。请参阅 入门指南
  • 在 AppLovin 控制台中启用 Impression-Level User Revenue API

实施概述

为每种广告形式(Interstitial、Rewarded、Banner、MRec、App Open)配置广告收入监听器以采集收入事件。从 adInfo.revenue 中提取收入,并连同平台专用货币(通常为 USD)一并发送给 Singular。


AppLovin MAX 收入跟踪

为所有 AppLovin MAX 广告形式设置全局收入监听器。

New ArchitectureOld Architecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';
import {
  InterstitialAd,
  RewardedAd,
  BannerAd,
  MRecAd,
  AppOpenAd
} from 'react-native-applovin-max';

// Currency constant - AppLovin typically reports in USD
const CURRENCY = 'USD';

// Generic handler for ad revenue
const handleAdRevenue = (adInfo) => {
  if (!adInfo) {
    console.error('AdInfo is null or undefined');
    return;
  }

  const revenue = adInfo.revenue;

  // Validate revenue
  if (!revenue || revenue <= 0) {
    console.error('Invalid revenue value:', revenue);
    return;
  }

  const adRevenueData = {
    ad_platform: 'AppLovin',
    ad_currency: CURRENCY,
    ad_revenue: revenue
  };

  // Send to Singular
  NativeSingular.adRevenue(adRevenueData);

  console.log('AppLovin ad revenue reported:', adRevenueData);
};

// Set up listeners for each ad type
export const setupAppLovinRevenueTracking = () => {
  InterstitialAd.addAdRevenuePaidListener(handleAdRevenue);
  RewardedAd.addAdRevenuePaidListener(handleAdRevenue);
  BannerAd.addAdRevenuePaidListener(handleAdRevenue);
  MRecAd.addAdRevenuePaidListener(handleAdRevenue);
  AppOpenAd.addAdRevenuePaidListener(handleAdRevenue);

  console.log('AppLovin MAX revenue tracking initialized');
};

实施说明:

  • 所有广告形式: 为应用使用的所有广告类型(Interstitial、Rewarded、Banner、MRec、App Open)注册监听器
  • 货币: AppLovin 通常以 USD 报告收入,请在控制台中确认
  • 共用处理程序: 对所有广告形式使用同一个收入处理函数,以确保校验逻辑一致

Unity LevelPlay(IronSource)集成

使用 Impression Level Revenue(ILR)SDK API 实现 IronSource 广告收入跟踪,以获取来自 IronSource Ads 及其聚合渠道的展示级数据。

前提条件


实施概述

订阅 onImpressionDataSuccess 事件发射器以接收展示数据。从 impressionData.revenue 中提取收入,并以 USD 货币发送给 Singular。


IronSource 收入跟踪

为 IronSource 展示数据回调配置事件监听器。

New ArchitectureOld Architecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import { NativeModules, NativeEventEmitter } from 'react-native';
import NativeSingular from 'singular-react-native/js/NativeSingular';

const { IronSourceModule } = NativeModules;
const ironSourceEventEmitter = new NativeEventEmitter(IronSourceModule);

const AD_PLATFORM = 'IronSource';
const CURRENCY = 'USD'; // IronSource typically reports in USD

export const setupIronSourceRevenueTracking = () => {
  ironSourceEventEmitter.addListener('onImpressionDataSuccess', (impressionData) => {
    // Validate impression data
    if (!impressionData) {
      console.error('No impression data available');
      return;
    }

    const revenue = impressionData.revenue;

    // Validate revenue value
    if (!revenue || revenue <= 0) {
      console.error('Invalid revenue value:', revenue);
      return;
    }

    const adRevenueData = {
      ad_platform: AD_PLATFORM,
      ad_currency: CURRENCY,
      ad_revenue: revenue
    };

    // Send to Singular
    NativeSingular.adRevenue(adRevenueData);

    console.log('IronSource ad revenue reported:', adRevenueData);
  });

  console.log('IronSource revenue tracking initialized');
};

实施说明:

  • 原生事件发射器: IronSource 使用 React Native 的原生事件系统进行展示回调
  • ARM 回传: 确认 IronSource 控制台中已启用 ARM SDK Postbacks 标志
  • 事件订阅: 在初始化 IronSource SDK 之前设置监听器

TradPlus 集成

使用全局展示监听器实现 TradPlus 广告收入跟踪,从广告展示中采集 eCPM 数据。

前提条件

  • 在您的应用中集成 TradPlus React Native SDK
  • 在控制台中配置 TradPlus 广告单元

实施概述

订阅 onImpressionSuccess 事件以接收广告展示数据。从 tpAdInfo.ecpm中提取 eCPM 值,从毫单位换算为美元(除以 1000),然后发送给 Singular。

eCPM 换算: TradPlus 以毫单位报告 eCPM。发送给 Singular 之前,请将 eCPM 值除以 1000 换算为美元金额。


TradPlus 收入跟踪

为 TradPlus 展示成功回调配置事件监听器。

New ArchitectureOld Architecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import { NativeModules, NativeEventEmitter } from 'react-native';
import NativeSingular from 'singular-react-native/js/NativeSingular';

const { TradPlusModule } = NativeModules;
const tradPlusEventEmitter = new NativeEventEmitter(TradPlusModule);

const AD_PLATFORM = 'TradPlus';
const CURRENCY = 'USD'; // TradPlus typically reports in USD

export const setupTradPlusRevenueTracking = () => {
  tradPlusEventEmitter.addListener('onImpressionSuccess', (tpAdInfo) => {
    // Validate ad info
    if (!tpAdInfo) {
      console.error('AdInfo is null');
      return;
    }

    // eCPM is reported in milli-units - convert to dollars
    if (!tpAdInfo.ecpm || typeof tpAdInfo.ecpm !== 'number') {
      console.error('Invalid eCPM value:', tpAdInfo.ecpm);
      return;
    }

    const revenue = tpAdInfo.ecpm / 1000.0;

    // Validate revenue after conversion
    if (revenue <= 0) {
      console.error('Revenue out of expected range:', revenue);
      return;
    }

    const adRevenueData = {
      ad_platform: AD_PLATFORM,
      ad_currency: CURRENCY,
      ad_revenue: revenue
    };

    // Send to Singular
    NativeSingular.adRevenue(adRevenueData);

    console.log('TradPlus ad revenue reported:', adRevenueData);
  });

  console.log('TradPlus revenue tracking initialized');
};

实施说明:

  • eCPM 格式: TradPlus 以毫单位报告 eCPM,发送给 Singular 之前请务必除以 1000
  • 类型检查: 换算前确认 eCPM 是数字
  • 换算后校验: 相除后校验收入是否大于 0

通用集成

对于标准集成未覆盖的自定义聚合平台或直接集成,请使用通用的 adRevenue() 方法实现广告收入跟踪。

何时使用通用集成

  • 标准集成未覆盖的自定义聚合平台
  • 不经聚合的广告渠道直接集成
  • 由服务端计算并转发到应用的广告收入
  • 使用模拟数据测试广告收入跟踪

通用实施示例

创建一个可复用的函数,在完成适当校验后报告来自任意来源的广告收入。

New ArchitectureOld Architecture
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';

/**
 * Report ad revenue to Singular with validation
 * 
 * @param {string} adPlatform - Name of the ad platform or mediation provider
 * @param {string} currency - ISO 4217 three-letter currency code (e.g., 'USD', 'EUR')
 * @param {number} revenue - Revenue amount in the specified currency
 */
export const reportAdRevenue = (adPlatform, currency, revenue) => {
  // Validate platform
  if (!adPlatform || adPlatform.trim() === '') {
    console.error('Invalid ad platform:', adPlatform);
    return;
  }

  // Validate currency code
  if (!currency || currency.trim() === '' || currency.length !== 3) {
    console.error('Invalid currency code:', currency);
    return;
  }

  // Validate revenue
  if (typeof revenue !== 'number' || revenue <= 0 || !isFinite(revenue)) {
    console.error('Invalid revenue value:', revenue);
    return;
  }

  const adRevenueData = {
    ad_platform: adPlatform.trim(),
    ad_currency: currency.toUpperCase().trim(),
    ad_revenue: revenue
  };

  // Send to Singular
  NativeSingular.adRevenue(adRevenueData);

  console.log('Ad Revenue reported to Singular:', adRevenueData);
};

// Example usage
export const trackCustomAdRevenue = () => {
  // Example: Custom mediation platform
  reportAdRevenue('CustomPlatform', 'USD', 0.05);

  // Example: Direct network integration
  reportAdRevenue('FacebookAudienceNetwork', 'EUR', 0.03);
};

校验功能:

  • 平台校验: 确保平台名称非空并已去除首尾空格
  • 货币校验: 验证三字母 ISO 4217 代码格式并转换为大写
  • 收入校验: 检查是否为正数,并排除 NaN 和 Infinity
  • 类型安全: TypeScript 接口可保证编译期类型检查

最佳实践

数据校验

实施稳健的校验,防止错误数据进入 Singular 分析。

  • 正数收入: 发送前务必确认收入大于零
  • 有效货币: 使用 ISO 4217 代码并确认字符串非空
  • 平台名称一致性: 在整个应用中使用一致的平台名称(例如始终使用 "AdMob",而不是 "Admob" 或 "ADMOB")
  • 类型检查: 确认数据类型与预期值一致(收入为数字,货币和平台为字符串)
  • 空值检查: 妥善处理 null、undefined 和空值

关键提示: 错误的广告收入数据在 Singular 中无法事后更正。调用以下方法前请务必校验数据: Singular.adRevenue()


货币处理

为多地区应用和多样化的广告渠道确保准确的货币报告。

  • 确认平台货币: 查阅聚合平台文档以了解默认货币(大多数使用 USD)
  • 格式一致: 始终使用大写的三字母 ISO 4217 代码
  • 不要换算: 按广告渠道提供的货币原样报告收入,不要进行货币换算
  • 各渠道货币: 不同广告渠道可能以不同货币报告,请分别确认

平台专项注意事项

处理各平台在收入报告格式和单位上的差异。

  • AdMob Android: 收入以微单位报告,除以 1,000,000 可换算为美元
  • AdMob iOS: 收入以美元报告,直接使用该值,无需换算
  • TradPlus: eCPM 以毫单位报告,除以 1,000 可换算为美元
  • AppLovin: 收入以美元报告,直接使用该值
  • IronSource: 收入以美元报告,直接使用该值

错误处理与日志

实施全面的日志记录,以便调试和监控广告收入跟踪。

  • 校验失败: 校验失败时记录详细的错误信息,包括实际接收到的值
  • 成功日志: 在开发环境中记录成功的收入报告,包含平台、货币和金额
  • 生产环境监控: 使用错误跟踪服务(Sentry、Bugsnag)监控生产环境中的校验失败
  • 收入异常: 对可能表明集成问题的异常高或异常低的收入值设置告警
  • 平台覆盖: 监控哪些广告平台正在报告收入,以确保所有平台都已正确集成

测试策略

在部署到生产环境之前验证广告收入跟踪的实施。

  1. 测试广告: 开发期间使用聚合平台提供的测试广告单元
  2. 校验事件: 在测试广告展示后,在 Singular 控制台中查看广告收入事件
  3. 确认货币: 确认货币代码在 Singular 报告中正确显示
  4. 平台名称准确性: 确保报告中的平台名称一致且可识别
  5. 收入金额: 确认收入金额与测试广告单元的预期区间一致
  6. 多平台: 在 iOS 和 Android 上分别测试,以验证平台专用的换算逻辑

性能优化

尽量降低广告收入跟踪对应用性能的影响。

  • 异步处理: 收入回调以异步方式执行,不会阻塞主线程
  • 精简校验: 保持校验逻辑简单快速(类型检查、范围检查)
  • 批处理考量: 对于高流量应用,如果您的分析后端支持,可考虑批量处理
  • 错误处理: 使用 try-catch 代码块,防止收入跟踪错误导致应用崩溃

验证与故障排查

验证实施

确认广告收入数据正确传输到 Singular。

  1. 启用测试广告: 在聚合平台中配置测试模式
  2. 触发展示: 展示测试广告并触发付费事件
  3. 检查日志: 确认收入跟踪日志以正确的值输出到控制台
  4. 控制台验证: 在 Singular 控制台中查看广告收入事件(可能需要 15–30 分钟)
  5. 事件详情: 在 Singular 事件详情中确认货币、平台和收入金额

常见问题

  • 没有收入事件: 确认付费事件处理程序在加载广告之前已注册,且聚合 SDK 已正确初始化
  • 收入为零: 检查平台专用的换算逻辑(Android AdMob 从微单位换算为美元,TradPlus 从毫单位换算为美元)
  • 货币错误: 确认货币代码与聚合平台报告的一致,请查阅平台文档
  • 平台名称不匹配: 使用与 Singular 可识别平台相匹配的一致平台名称
  • 事件缺失: 确保 Singular SDK 在广告收入事件发生之前完成初始化
  • 重复事件: 确认付费事件处理程序只注册一次,而不是每次加载广告时都注册

更多资源: 详细信息请参阅 广告收入归因常见问题React Native SDK 方法参考