Flutter SDK - 広告収入のトラッキング

ドキュメント

広告収益の帰属

メディエーションプラットフォームからの広告収入を追跡し、アプリにユーザーをもたらした特定のマーケティングキャンペーンに帰属させることで、包括的なROI分析を可能にします。

広告収益アトリビューションは、モバイルアプリの広告収益をユーザーを生み出したマーケティングキャンペーンに結び付け、キャンペーンコスト、アプリ内収益、広告収益を統合レポートにまとめます。このデータは広告ネットワークにもフィードバックされ、キャンペーンのパフォーマンスを最適化します。

詳細はこちら:アトリビューション方法と対応するメディエーションプラットフォームの詳細については、Singular広告収益アトリビューションFAQをご覧ください。


広告収益アトリビューションの仕組み

アトリビューションの流れ

お客様のメディエーションプラットフォームは、コールバックを通してインプレッションレベルまたはユーザーレベルの収益データを報告します。

  • キャンペーンアトリビューション:広告収益を各ユーザーを獲得した特定のキャンペーンに関連付け、キャンペーンごとの真のROIを表示します。
  • データソース収益データは、ユーザーレベルまたはインプレッションレベルのいずれかの粒度で、貴社のメディエーション・プラットフォームから得られます。
  • ネットワークの最適化:収益データを広告ネットワークに戻し、ターゲティングと入札戦略を改善します。

実装要件

誤った収益データは送信後に修正できないため、広告収益トラッキングを実施する前にデータの正確性を確認すること。

重要な要件

  • 通貨コード:3文字のISO 4217コード(USD、EUR、INR)を使用すること。ほとんどの媒介プラットフォームはデフォルトでUSDを使用する。
  • データの検証:Singularに送信する前に、収益の値が正であり、通貨コードが空でないことを常に確認すること。
  • 単位変換:プラットフォームによっては、マイクロ単位(1,000,000=1.00ドル)で収益を報告するものもあります。Singularに送信する前にドルに変換してください。

設定ステップ

メディエーションプラットフォームで広告収益アトリビューションを実装するには、以下の手順に従ってください。

  1. SDKを更新する:Singular Flutter SDKの最新バージョンを実行していることを確認します。
  2. プラットフォームを設定します:メディエーションプラットフォームのダッシュボードで広告収益レポートを有効にする(AdMob、AppLovinなど)
  3. コールバックを実装します:メディエーションSDKから収益イベントリスナーを追加し、インプレッションデータを取得します。
  4. データを検証する:Singularに転送する前に、収益>0、通貨が有効であることを確認します。
  5. 統合のテスト:収益データが24時間以内にSingularのレポートに表示されることを確認します。

AdMobインテグレーション

前提条件

Singularと統合する前に、AdMobアカウントで広告収益レポートを有効にし、Google Mobile Ads SDK for Flutterを実装してください。

  • AdMobのセットアップ:AdMobダッシュボードでインプレッションレベルの広告収益を有効にする。AdMobサポートを参照
  • Flutterパッケージ:google_mobile_adsパッケージをインストールします。スタートアップガイドをご覧ください。

プラットフォームの違い:AdMobはプラットフォームによって収益の報告が異なります。Androidはマイクロ単位(5000 = $0.005)で収益を報告しますが、iOSは10進数(0.005 = $0.005)で報告します。Androidの値は、Singularに送信する前に1,000,000で割って変換してください。


実装ステップ

成功した広告インプレッションから収益データを取得するために、広告読み込み時にonPaidEvent コールバックを設定します。

  1. 広告をロードする:広告ユニット(リワード、インタースティシャル、バナーなど)を作成し、ロードする。
  2. コールバックを設定します:広告で収益が発生した際にAdValue をキャプチャするためにonPaidEvent コールバックを割り当てます。
  3. 単位を変換する: valueMicros を1,000,000で割ってドルに変換する。
  4. Validate:収益が0以上で、通貨が空でないことを確認する。
  5. Singularに送信:検証済みのデータをSingular.adRevenue() にコールする。

リワード広告の例

広告のロード完了後にonPaidEvent コールバックを設定することで、リワード動画広告から広告収益をキャプチャします。

Dart
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:singular_flutter_sdk/singular.dart';

const String adUnitId = 'YOUR_AD_UNIT_ID';

class AdManager {
  RewardedAd? _rewardedAd;

  void loadRewardedAd() {
    RewardedAd.load(
      adUnitId: adUnitId,
      request: AdRequest(),
      rewardedAdLoadCallback: RewardedAdLoadCallback(
        onAdLoaded: (RewardedAd ad) {
          _rewardedAd = ad;
          print('Rewarded ad loaded successfully');

          // Set up full screen content callback
          _rewardedAd?.fullScreenContentCallback = FullScreenContentCallback(
            onAdShowedFullScreenContent: () {
              print('Rewarded ad displayed');
            },
            onAdFailedToShowFullScreenContent: (AdError adError) {
              print('Rewarded ad failed to show: ${adError.message}');
            },
            onAdDismissedFullScreenContent: () {
              print('Rewarded ad dismissed');
              _rewardedAd = null;
            },
          );

          // Set up paid event callback for revenue tracking
          _rewardedAd?.onPaidEvent = (AdValue adValue) {
            // Convert revenue from micros to dollars
            double revenue = adValue.valueMicros / 1_000_000.0;
            String? currency = adValue.currencyCode;

            // Validate revenue and currency before sending
            if (revenue > 0 && currency != null && currency.isNotEmpty) {
              final adData = {
                'adPlatform': 'AdMob',
                'currency': currency,
                'revenue': revenue,
              };

              // Send ad revenue data to Singular
              Singular.adRevenue(adData);

              // Log for debugging
              print('Ad Revenue reported: $revenue $currency');
            } else {
              print('Invalid ad revenue: revenue=$revenue, currency=$currency');
            }
          };
        },
        onAdFailedToLoad: (LoadAdError loadAdError) {
          print('Rewarded ad failed to load: ${loadAdError.message}');
        },
      ),
    );
  }

  void showRewardedAd() {
    if (_rewardedAd != null) {
      _rewardedAd!.show(
        onUserEarnedReward: (AdWithoutView ad, RewardItem reward) {
          print('User earned reward: ${reward.amount} ${reward.type}');
        },
      );
    } else {
      print('Rewarded ad not ready');
    }
  }
}

インタースティシャル広告の例

同じonPaidEvent パターンを使用して、インタースティシャル広告からの収益をトラッキングします。

Dart
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:singular_flutter_sdk/singular.dart';

const String interstitialAdUnitId = 'YOUR_INTERSTITIAL_AD_UNIT_ID';

class InterstitialAdManager {
  InterstitialAd? _interstitialAd;

  void loadInterstitialAd() {
    InterstitialAd.load(
      adUnitId: interstitialAdUnitId,
      request: AdRequest(),
      adLoadCallback: InterstitialAdLoadCallback(
        onAdLoaded: (InterstitialAd ad) {
          _interstitialAd = ad;
          print('Interstitial ad loaded');

          // Set paid event callback
          _interstitialAd?.onPaidEvent = (AdValue adValue) {
            double revenue = adValue.valueMicros / 1_000_000.0;
            String? currency = adValue.currencyCode;

            if (revenue > 0 && currency != null && currency.isNotEmpty) {
              Singular.adRevenue({
                'adPlatform': 'AdMob',
                'currency': currency,
                'revenue': revenue,
              });
              
              print('Interstitial revenue: $revenue $currency');
            }
          };

          // Set full screen callback
          _interstitialAd?.fullScreenContentCallback = FullScreenContentCallback(
            onAdDismissedFullScreenContent: () {
              _interstitialAd = null;
              loadInterstitialAd(); // Load next ad
            },
          );
        },
        onAdFailedToLoad: (LoadAdError error) {
          print('Interstitial ad failed to load: ${error.message}');
        },
      ),
    );
  }

  void showInterstitialAd() {
    _interstitialAd?.show();
  }
}

バナー広告の例

Flutter UIに表示されるバナー広告からの収益をトラッキングします。

Dart
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:singular_flutter_sdk/singular.dart';

const String bannerAdUnitId = 'YOUR_BANNER_AD_UNIT_ID';

class BannerAdWidget extends StatefulWidget {
  @override
  _BannerAdWidgetState createState() => _BannerAdWidgetState();
}

class _BannerAdWidgetState extends State<BannerAdWidget> {
  BannerAd? _bannerAd;
  bool _isBannerAdReady = false;

  @override
  void initState() {
    super.initState();
    _loadBannerAd();
  }

  void _loadBannerAd() {
    _bannerAd = BannerAd(
      adUnitId: bannerAdUnitId,
      size: AdSize.banner,
      request: AdRequest(),
      listener: BannerAdListener(
        onAdLoaded: (Ad ad) {
          setState(() {
            _isBannerAdReady = true;
          });
          print('Banner ad loaded');

          // Set paid event callback
          (ad as BannerAd).onPaidEvent = (AdValue adValue) {
            double revenue = adValue.valueMicros / 1_000_000.0;
            String? currency = adValue.currencyCode;

            if (revenue > 0 && currency != null && currency.isNotEmpty) {
              Singular.adRevenue({
                'adPlatform': 'AdMob',
                'currency': currency,
                'revenue': revenue,
              });
              
              print('Banner revenue: $revenue $currency');
            }
          };
        },
        onAdFailedToLoad: (Ad ad, LoadAdError error) {
          ad.dispose();
          print('Banner ad failed to load: ${error.message}');
        },
      ),
    );

    _bannerAd?.load();
  }

  @override
  void dispose() {
    _bannerAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_isBannerAdReady && _bannerAd != null) {
      return Container(
        alignment: Alignment.center,
        width: _bannerAd!.size.width.toDouble(),
        height: _bannerAd!.size.height.toDouble(),
        child: AdWidget(ad: _bannerAd!),
      );
    }
    return SizedBox.shrink();
  }
}

AppLovin MAXインテグレーション

前提条件

Singular と統合する前に、AppLovin MAX Flutter プラグインを実装し、インプレッションレベルの収益トラッキングを設定してください。

  • MAXのセットアップ:AppLovin MAXダッシュボードでアプリを設定します。
  • Flutterプラグイン:applovin_maxパッケージをインストールします。スタートアップガイドをご覧ください。
  • 収益API:MAXダッシュボードの設定でImpression-Level User Revenue APIを有効にする。

実装の概要

onAdRevenuePaidCallback リスナーを各広告フォーマットに設定し、インプレッションが収益を発生させた時に収益をキャプチャします。

  1. MAXの初期化:SDKキーでAppLovin MAX SDKを設定します。
  2. リスナーを設定します:各広告フォーマットにonAdRevenuePaidCallback
  3. 収益を抽出します: ad.revenue プロパティから収益を取得
  4. 通貨の取り扱い:AppLovinは通常USDで報告するが、ad.currency
  5. Singularに転送:検証されたデータをSingular.adRevenue()

完全なMAXの実装

統一されたハンドラーを使用して、すべてのMAX広告フォーマット(リワード、インタースティシャル、バナー、MREC)から収益をキャプチャします。

Dart
import 'package:flutter/material.dart';
import 'package:applovin_max/applovin_max.dart';
import 'package:singular_flutter_sdk/singular.dart';

class AppLovinMaxManager extends StatefulWidget {
  @override
  _AppLovinMaxManagerState createState() => _AppLovinMaxManagerState();
}

class _AppLovinMaxManagerState extends State<AppLovinMaxManager> {
  @override
  void initState() {
    super.initState();
    _initializeAppLovinMax();
  }

  Future<void> _initializeAppLovinMax() async {
    // Initialize AppLovin MAX SDK
    await AppLovinMAX.initialize('YOUR_SDK_KEY');

    // Set up revenue listeners for all ad formats
    _setupRewardedAdListeners();
    _setupInterstitialAdListeners();
    _setupBannerAdListeners();
    _setupMRecAdListeners();

    print('AppLovin MAX initialized with revenue tracking');
  }

  void _setupRewardedAdListeners() {
    AppLovinMAX.setRewardedAdListener(RewardedAdListener(
      onAdRevenuePaidCallback: (ad) {
        _handleAdRevenuePaid(ad, 'Rewarded');
      },
      onAdLoadedCallback: (ad) {
        print('Rewarded ad loaded');
      },
      onAdLoadFailedCallback: (adUnitId, error) {
        print('Rewarded ad failed to load: $error');
      },
    ));
  }

  void _setupInterstitialAdListeners() {
    AppLovinMAX.setInterstitialListener(InterstitialListener(
      onAdRevenuePaidCallback: (ad) {
        _handleAdRevenuePaid(ad, 'Interstitial');
      },
      onAdLoadedCallback: (ad) {
        print('Interstitial ad loaded');
      },
      onAdLoadFailedCallback: (adUnitId, error) {
        print('Interstitial ad failed to load: $error');
      },
    ));
  }

  void _setupBannerAdListeners() {
    AppLovinMAX.setBannerListener(AdViewAdListener(
      onAdRevenuePaidCallback: (ad) {
        _handleAdRevenuePaid(ad, 'Banner');
      },
      onAdLoadedCallback: (ad) {
        print('Banner ad loaded');
      },
      onAdLoadFailedCallback: (adUnitId, error) {
        print('Banner ad failed to load: $error');
      },
    ));
  }

  void _setupMRecAdListeners() {
    AppLovinMAX.setMRecListener(AdViewAdListener(
      onAdRevenuePaidCallback: (ad) {
        _handleAdRevenuePaid(ad, 'MREC');
      },
      onAdLoadedCallback: (ad) {
        print('MREC ad loaded');
      },
      onAdLoadFailedCallback: (adUnitId, error) {
        print('MREC ad failed to load: $error');
      },
    ));
  }

  void _handleAdRevenuePaid(Ad ad, String adType) {
    // Extract revenue and currency from ad object
    final double revenueValue = ad.revenue ?? 0.0;
    final String currency = ad.revenueCurrency ?? 'USD';

    // Validate revenue before sending
    if (revenueValue > 0) {
      final adData = {
        'adPlatform': 'AppLovin',
        'currency': currency,
        'revenue': revenueValue,
      };

      // Send ad revenue to Singular
      Singular.adRevenue(adData);

      // Log for debugging
      print('[$adType] Revenue: $revenueValue $currency');
    } else {
      print('[$adType] Invalid revenue: $revenueValue');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AppLovin MAX Revenue Tracking'),
      ),
      body: Center(
        child: Text('MAX revenue tracking active'),
      ),
    );
  }
}

ベストプラクティス広告をロードする前に、アプリの初期化中に広告収益リスナーをセットアップし、インプレッションを見逃さないようにします。


IronSource(Unityレベルプレイ)の統合

前提条件

Singularと統合する前に、IronSourceダッシュボードでImpression Level Revenue (ILR)を有効にし、IronSource Flutterプラグインを実装してください。

  • IronSourceのセットアップ:IronSourceダッシュボードでARM SDK Postbacksフラグを有効にする。
  • Flutterプラグイン:ironsource_mediationパッケージをインストールします。スタートアップガイドを参照
  • ILR API:アプリのインプレッションレベルの収益トラッキングを設定する

実装例

onImpressionDataSuccess コールバックを使用して、IronSource メディエーションからインプレッションレベルの収益データを取得します。

Dart
import 'package:ironsource_mediation/ironsource_mediation.dart';
import 'package:singular_flutter_sdk/singular.dart';

void setupIronSourceRevenueTracking() {
  // Set up impression data listener
  IronSource.setImpressionDataListener((ISImpressionData? impressionData) {
    onImpressionDataSuccess(impressionData);
  });
}

void onImpressionDataSuccess(ISImpressionData? impressionData) {
  // Ensure impression data is not null
  if (impressionData == null) {
    print('No impression data available');
    return;
  }

  // Extract and validate revenue
  final revenue = impressionData.revenue?.toDouble() ?? 0.0;
  
  if (revenue <= 0) {
    print('Invalid revenue value: $revenue');
    return;
  }

  // Create ad revenue data for Singular
  final adData = {
    'adPlatform': 'IronSource',
    'currency': 'USD',
    'revenue': revenue,
  };

  // Send to Singular
  Singular.adRevenue(adData);

  // Log for debugging
  print('IronSource Revenue: $revenue USD');
}

TradPlusインテグレーション

前提条件

TradPlusインプレッションデリゲートをセットアップして、広告が収益を生成したときにeCPMデータを取得します。

  • TradPlusのセットアップ:TradPlusダッシュボードでアプリを設定します。
  • インプレッションデリゲート:TradPlusの設定でインプレッションレベルのトラッキングを有効にする

eCPMコンバージョン:TradPlusは通常、eCPMをミリ単位でレポートします。1000.0で割ってドルに変換してからSingularに送信してください。


実装例

グローバルインプレッションリスナーを使用して、TradPlusのすべての広告フォーマットから収益データを取得します。

Dart
import 'package:singular_flutter_sdk/singular.dart';

void setupTradPlusImpressionListener() {
  // Set up global impression listener
  TradPlusSdk.setGlobalImpressionListener((tpAdInfo) {
    if (tpAdInfo == null) {
      print('AdInfo is null');
      return;
    }

    // Ensure eCPM is available
    if (tpAdInfo.ecpm == null) {
      print('eCPM value is null');
      return;
    }

    // Convert eCPM from milli-units to dollars
    double revenue = tpAdInfo.ecpm! / 1000.0;

    // Validate revenue
    if (revenue <= 0) {
      print('Invalid revenue: $revenue');
      return;
    }

    // Create ad revenue data
    final adData = {
      'adPlatform': 'TradPlus',
      'currency': 'USD',
      'revenue': revenue,
    };

    // Send to Singular
    Singular.adRevenue(adData);

    // Log for debugging
    print('TradPlus Revenue: $revenue USD');
  });
}

void main() {
  // Initialize TradPlus SDK
  setupTradPlusImpressionListener();
  
  runApp(MyApp());
}

一般的な統合(その他のプラットフォーム)

カスタム実装

上記で明確にカバーされていないメディエーションプラットフォームの場合は、Singularの広告収益メソッドを使用してカスタム収益ハンドラを実装します。

Dart
import 'package:singular_flutter_sdk/singular.dart';

/// Generic function to report ad revenue to Singular
/// Use this for any mediation platform not explicitly supported above
void reportAdRevenue({
  required String adPlatform,
  required String currency,
  required double revenue,
}) {
  // Validate revenue value
  if (revenue <= 0) {
    print('Invalid revenue value: $revenue');
    return;
  }

  // Validate currency code
  if (currency.isEmpty) {
    print('Currency code is empty');
    return;
  }

  // Create ad revenue data
  final adData = {
    'adPlatform': adPlatform,
    'currency': currency,
    'revenue': revenue,
  };

  // Send to Singular
  Singular.adRevenue(adData);

  // Log for debugging
  print('Ad Revenue reported: $revenue $currency from $adPlatform');
}

// Example usage with a custom mediation platform
void onCustomAdImpression(Map<String, dynamic> impressionData) {
  reportAdRevenue(
    adPlatform: 'CustomNetwork',
    currency: impressionData['currency'] ?? 'USD',
    revenue: impressionData['revenue'] ?? 0.0,
  );
}

ベストプラクティスとトラブルシューティング

データ検証

  • 常に検証:Singularに送信する前に、収益>0、通貨が空でないことを確認する。
  • 単位変換:プラットフォームがマイクロでレポートするかドルでレポートするかを確認し、それに応じて変換する。
  • 通貨コード:ISO 4217の3文字コード(USD、EUR、JPY)を一貫して使用する。
  • 徹底的なテスト:本番リリース前に収益データがSingularレポートに表示されることを確認する

よくある問題

  • 収益の欠落:メディエーションプラットフォームのダッシュボードでインプレッションレベルの収益が有効になっていることを確認する。
  • 不正な値:特定のプラットフォームの単位変換(マイクロとドル)を確認する
  • レポートにデータがない:導入後、Singularのレポートにデータが表示されるまで24~48時間かかります。
  • 収益がゼロ:広告のロードが完了する前に、広告のコールバックが適切に設定されているか確認する。

重要:広告収益データは送信後に修正できません。Singular.adRevenue() を呼び出す前に、必ずデータの正確性を確認してください。