广告收入归属
将广告收入与为您的应用程序带来用户的特定营销活动联系起来,全面了解营销活动成本、应用程序内收入和广告收入,从而准确衡量投资回报率。
概述
什么是广告收入归因
广告收入归属将移动应用程序的广告收入与产生用户的营销活动联系起来,通过将用户获取成本与包括广告货币化在内的终生收入联系起来,使您能够衡量真实的营销活动绩效。
- 营销活动投资回报率:在统一的报告中查看营销活动成本、应用内购买和广告收入,计算广告支出的真实回报。
- 网络优化:将广告收入数据发送回广告网络,以改进竞价算法和广告系列性能
- 数据源:支持来自 AdMob、AppLovin MAX、Unity LevelPlay (IronSource) 和 TradPlus 等中介平台的用户级和印象级数据。
有关完整的详细信息,请参阅广告收入归因常见问题解答。
重要注意事项:
- 货币代码:使用 ISO 4217 三字母货币代码(美元、欧元、印度卢比)。大多数调解平台以美元为单位进行报告,请在实施前核实平台的货币。
- 数据准确性:在发送给 Singular 之前,请验证收入和货币数据。错误数据无法追溯更正
实施要求
广告收入跟踪需要与您的调解平台 SDK 集成,并配置收入回调。
- SDK 版本:更新至最新的 Singular React Native SDK 版本
- 调解平台:为您的中介平台(AdMob、AppLovin MAX、IronSource 或 TradPlus)集成 React Native SDK。
- 收入回调:实施特定平台的付费事件处理程序,以获取印象级收入数据
- 验证逻辑:在向 Singular 发送数据前添加收入和货币验证
SDK 方法
Singular.adRevenue
向 Singular 报告广告收入数据,包括平台、货币和收入金额,以便归属到用户的获取活动。
方法签名:
static adRevenue(data: {
adPlatform: string;
currency: string;
revenue: number;
}): void
参数:
- adPlatform:中介平台名称(例如,"AdMob"、"AppLovin"、"IronSource"、"TradPlus
- currency:货币:ISO 4217 三字母货币代码(如 "USD"、"EUR
- 收入:以指定货币计算的收入金额(必须大于 0
有关完整的方法文档,请参阅adRevenue 参考资料。
平台集成
AdMob 集成
使用 Google Mobile Ads SDK 付费事件回调实施 AdMob 广告收入跟踪,以获得印象级收入报告。
前提条件
实施概述
在加载广告格式(应用打开、横幅、插播、本地、奖励)时,配置一个付费事件处理程序,在广告产生收入时触发。 从事件数据中提取收入值和货币,验证这两个值并发送给 Singular。
平台差异:AdMob 按平台报告收入的方式不同。Android 以微米为单位报告收入(例如,0.005 美元显示为 5000),需要除以 1,000,000;而 iOS 直接以美元为单位报告收入(0.005)。根据平台检测调整转换逻辑。
AdMob 奖励广告示例
使用 AdMob 加载有奖广告并捕获付费事件以跟踪收入。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
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 = {
adPlatform: 'AdMob',
currency: currencyCode,
revenue: revenue
};
// Send to Singular
NativeSingular.adRevenue(adRevenueData);
console.log('Ad Revenue reported to Singular:', adRevenueData);
};
return null;
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
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 = {
adPlatform: 'AdMob',
currency: currencyCode,
revenue: revenue
};
// Send to Singular
Singular.adRevenue(adRevenueData);
console.log('Ad Revenue reported to Singular:', adRevenueData);
};
return null;
}
实施注意事项:
-
平台检测:使用
Platform.OS确定转换逻辑(Android 除以 1,000,000,iOS 直接使用值 - 收入验证:发送前确保收入大于 0
- 货币验证:验证货币代码是否为空
- 错误日志:记录无效数据以进行调试,无需发送至 Singular
AppLovin MAX 集成
使用印象级用户收入 API 实施 AppLovin MAX 广告收入跟踪,以实时报告所有广告格式的收入情况。
前提条件
- 集成 AppLovin MAX React Native SDK。请参阅入门指南
- 在您的 AppLovin 面板中启用印象级用户收入 API
实施概述
为每种广告格式(Interstitial、Rewarded、Banner、MRec、App Open)配置广告收入监听器,以捕获收入事件。从adInfo.revenue 提取收入,并以特定平台货币(通常为美元)发送至 Singular。
AppLovin MAX 收入跟踪
为所有 AppLovin MAX 广告格式设置全局收入监听器。
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
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 = {
adPlatform: 'AppLovin',
currency: CURRENCY,
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');
};
import { Singular } from 'singular-react-native';
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 = {
adPlatform: 'AppLovin',
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
Singular.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');
};
实施注意事项:
- 所有广告格式:为您的应用程序使用的所有广告类型(插播广告、奖励广告、横幅广告、MRec、应用程序打开)注册监听器。
- 货币:AppLovin 通常以美元报告收入--请在仪表板中核实
- 共享处理程序:对所有广告格式使用单一收入处理函数,以确保验证的一致性
Unity LevelPlay(IronSource)集成
使用印象级收入 (ILR) SDK API 实施 IronSource 广告收入跟踪,以获取来自 IronSource Ads 和中介网络的印象级数据。
前提条件
- 集成 IronSource React Native SDK。请参阅入门指南
- 在 IronSource 仪表板中启用 ARM SDK 回传标志
- 查看IronSource AdReact Native SDK 文档
实施概述
订阅onImpressionDataSuccess 事件发射器以接收印象数据。从impressionData.revenue 提取收入并以美元货币发送到 Singular。
IronSource 收入跟踪
为 IronSource 印象数据回调配置事件监听器。
// TurboModule direct API (React Native 0.76+ New Architecture)
import { NativeModules, NativeEventEmitter } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
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 = {
adPlatform: AD_PLATFORM,
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
NativeSingular.adRevenue(adRevenueData);
console.log('IronSource ad revenue reported:', adRevenueData);
});
console.log('IronSource revenue tracking initialized');
};
import { NativeModules, NativeEventEmitter } from 'react-native';
import { Singular } from 'singular-react-native';
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 = {
adPlatform: AD_PLATFORM,
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
Singular.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 单位是毫单位,将 eCPM 值除以 1000 转换为美元后再发送至 Singular。
TradPlus 收入跟踪
为 TradPlus 印象成功回调配置事件监听器。
// TurboModule direct API (React Native 0.76+ New Architecture)
import { NativeModules, NativeEventEmitter } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
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 = {
adPlatform: AD_PLATFORM,
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
NativeSingular.adRevenue(adRevenueData);
console.log('TradPlus ad revenue reported:', adRevenueData);
});
console.log('TradPlus revenue tracking initialized');
};
import { NativeModules, NativeEventEmitter } from 'react-native';
import { Singular } from 'singular-react-native';
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 = {
adPlatform: AD_PLATFORM,
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
Singular.adRevenue(adRevenueData);
console.log('TradPlus ad revenue reported:', adRevenueData);
});
console.log('TradPlus revenue tracking initialized');
};
实施注意事项:
- eCPM 格式:TradPlus 报告的 eCPM 单位是毫单位--在发送到 Singular 之前,请务必除以 1000。
- 类型检查:转换前验证 eCPM 是一个数字
- 转换后验证:除法后验证收入大于 0
通用集成
使用通用adRevenue() 方法为自定义调解平台或直接集成实施广告收入跟踪。
何时使用通用集成
- 标准集成未涵盖的自定义调解平台
- 无中介的直接广告网络整合
- 服务器端广告收入计算转发到应用程序
- 使用模拟数据测试广告收入跟踪
通用实施示例
创建一个可重复使用的函数,通过适当的验证从任何来源报告广告收入。
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
/**
* 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 = {
adPlatform: adPlatform.trim(),
currency: currency.toUpperCase().trim(),
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);
};
import { Singular } from 'singular-react-native';
/**
* 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 = {
adPlatform: adPlatform.trim(),
currency: currency.toUpperCase().trim(),
revenue: revenue
};
// Send to Singular
Singular.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 和无穷大
- 类型安全TypeScript 接口确保编译时类型检查
最佳实践
数据验证
实施强大的验证,防止不正确的数据进入 Singular 分析。
- 正收入:发送前始终验证收入大于零
- 有效货币:使用 ISO 4217 代码并验证非空字符串
- 平台一致性:在整个应用程序中使用一致的平台名称(例如,始终使用 "AdMob",而不是 "Admob "或 "ADMOB")。
- 类型检查:验证数据类型是否与预期值相匹配(数字表示收入,字符串表示货币/平台
- 空值检查:适当处理空值、未定义值和空值
关键:在 Singular 中无法追溯纠正不正确的广告收入数据。请务必在调用Singular.adRevenue() 之前验证数据。
货币处理
确保多地区应用程序和不同广告网络的货币报告准确无误。
- 验证平台货币:检查您的调解平台文档中的默认货币(大多数使用美元
- 统一格式:始终使用大写三个字母的 ISO 4217 代码
- 无转换:以广告网络提供的货币报告收入,不要转换货币
- 每个网络使用的货币:不同的广告网络可能使用不同的货币报告,请分别核实
平台特定考虑因素
处理收入报告格式和单位的平台差异。
- AdMob Android:以微米为单位报告收入--除以 1,000,000 换算成美元
- AdMob iOS:以美元为单位报告收入--直接使用数值,无需转换
- TradPlus:以毫单位报告的 eCPM--除以 1,000 换算成美元
- AppLovin:以美元为单位报告收入--直接使用价值
- IronSource:以美元为单位报告收入--直接使用价值
错误处理和日志记录
实施全面的日志记录,用于调试和监控广告收入跟踪。
- 验证失败:在验证失败时记录详细的错误信息,包括收到的实际值
- 成功日志:记录开发中成功的收入报告,包括平台、货币和金额
- 生产监控:使用错误跟踪服务(Sentry、Bugsnag)监控生产中的验证故障
- 收入异常:对可能表明集成问题的异常高或异常低的收入值发出警报
- 平台覆盖范围:监控哪些广告平台在报告收入,确保所有平台都能正确集成
测试策略
在生产部署前验证广告收入跟踪实施情况。
- 测试广告:在开发过程中使用调解平台的测试广告单元
- 验证事件:测试广告展示后,检查 Singular 面板上的广告收入事件
- 验证货币:确认货币代码正确显示在 Singular 报告中
- 平台准确性:确保报告中的平台名称一致且可识别
- 收入金额:验证收入金额是否符合测试广告单元的预期范围
- 多平台:在 iOS 和 Android 上进行测试,以验证特定平台的转换逻辑
性能优化
将广告收入跟踪对应用程序性能的影响降至最低。
- 异步处理:收入回调以异步方式执行,不会阻塞主线程
- 最小化验证:保持验证逻辑简单快速(类型检查、范围检查
- 批量考虑:对于大容量应用程序,如果分析后端支持批处理,可考虑批处理
- 错误处理:使用 try-catch 块防止收入跟踪错误导致应用程序崩溃
验证和故障排除
验证实施
确认广告收入数据正确流向 Singular。
- 启用测试广告:在调解平台中配置测试模式
- 触发印象:显示测试广告并触发付费事件
- 检查日志:验证收入跟踪日志是否以正确的值显示在控制台中
- 仪表板验证:检查 Singular 仪表板上的广告收入事件(可能需要 15-30 分钟
- 事件详细信息:验证 Singular 事件详细信息中的货币、平台和收入金额
常见问题
- 没有收入事件:加载广告前验证已注册付费事件处理程序,并正确初始化中介 SDK
- 零收入:检查特定平台的转换逻辑(Android AdMob 将微米转换为美元,TradPlus 将毫秒转换为美元
- 货币错误:验证货币代码是否与您的调解平台报告相符--检查平台文档
- 平台名称不匹配:使用与 Singular 认可平台一致的平台名称
- 缺少事件:确保在广告收入事件发生前初始化 Singular SDK
- 重复事件:验证付费事件处理程序只注册一次,而不是在每次广告加载时注册
其他资源:有关详细信息,请参阅广告收入归属常见问题解答和React Native SDK 方法参考。