サブスクリプションイベントトラッキング実装ガイド
包括的なサブスクリプショントラッキングを実装して、Singular の SDK、 サーバー間 (server-to-server) 連携、またはサードパーティパートナーを使用し、 すべてのプラットフォームにわたってサブスクリプション収益、ユーザーのリテンション、 キャンペーンパフォーマンスを測定します。
Singular では、新規サブスクリプション、更新、トライアル、キャンセル、返金を含む サブスクリプションイベントを、ユーザーを獲得したマーケティングキャンペーンに正確に アトリビューションできます。これにより、サブスクリプションのライフサイクルと収益創出を 完全に可視化できます。
実装オプション
アプリのアーキテクチャとビジネス要件に最も適した実装方法を選択してください。
| 検討事項 | SDK 連携 | サーバー間連携 (S2S) | サードパーティ連携 |
|---|---|---|---|
| 実装 | Singular SDK のカスタム収益メソッドを介してサブスクリプションイベントを送信します | バックエンドから、必要なモバイルデバイスプロパティを付けて Singular の REST API にイベントを送信します | RevenueCat、Adapty、Superwall、Xsolla、その他のサブスクリプションプラットフォームとの 連携を構成します |
| アプリアクティビティへの依存 | SDK がイベントを送信するにはアプリを起動する必要があります | アプリの状態に依存せず、イベントはリアルタイムで送信されます | イベントはパートナープラットフォームからリアルタイムで送信されます |
| イベントのタイミング | アプリの起動に応じて、イベント時刻が実際のサブスクリプション時刻と 異なる場合があります | バックエンドで処理されるとおりにリアルタイムでイベントをトラッキングします | パートナーによる処理後にほぼリアルタイムでトラッキングします |
| 複雑さ | シンプルなクライアント側の実装 | 安全なバックエンドインフラが必要です | パートナーアカウントと構成が必要です |
重要: サブスクリプションイベントに S2S またはサードパーティ連携を使用する ほとんどのお客様は、完全なアトリビューショントラッキングのために、セッションおよび サブスクリプション以外のイベントを管理する目的で Singular SDK も引き続き連携させる 必要があります。
Android: Google Play Billing 連携
Google Play Billing Library 8.0.0 を連携させてサブスクリプション情報を照会し、 Singular SDK 連携のためにデバイス上で直接サブスクリプションの状態を管理します。
前提条件
Google Play Console を構成する
アプリで課金を実装する前に、 Google Play Console でアプリ内商品とサブスクリプションアイテムを設定します。
- サブスクリプション商品を作成する: Play Console でサブスクリプションの ティア、課金期間、価格を定義します
- オファーを構成する: ベースプラン、オファートークン、プロモーション価格を 設定します
- テスト設定: テストアカウントを作成し、商品の利用可能性を確認します
Billing Library の依存関係を追加する
build.gradle を更新する
コルーチンをサポートする Kotlin 拡張機能とともに、Google Play Billing Library 8.0.0 を アプリの依存関係に追加します。
dependencies {
val billingVersion = "8.0.0"
// Google Play Billing Library
implementation("com.android.billingclient:billing:$billingVersion")
// Kotlin extensions and coroutines support
implementation("com.android.billingclient:billing-ktx:$billingVersion")
}
バージョン 8.0.0 の機能:
このバージョンでは、
enableAutoServiceReconnection()
による自動サービス再接続、改善された購入照会 API、
強化された保留中トランザクション処理が導入されています。
BillingClient を初期化する
Billing Manager を作成する
すべてのサブスクリプション操作を処理し、イベントトラッキングのために Singular SDK と 連携させる billing manager クラスを設定します。
class SubscriptionManager(private val context: Context) {
private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->
when (billingResult.responseCode) {
BillingResponseCode.OK -> {
purchases?.forEach { purchase ->
handlePurchaseUpdate(purchase)
}
}
BillingResponseCode.USER_CANCELED -> {
Log.d(TAG, "User canceled the purchase")
}
else -> {
Log.e(TAG, "Purchase error: ${billingResult.debugMessage}")
}
}
}
private val billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases() // Required for all purchases
.enableAutoServiceReconnection() // NEW in 8.0.0 - handles reconnection automatically
.build()
fun initialize() {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingResponseCode.OK) {
Log.d(TAG, "Billing client ready")
// Query existing subscriptions
queryExistingSubscriptions()
} else {
Log.e(TAG, "Billing setup failed: ${billingResult.debugMessage}")
}
}
override fun onBillingServiceDisconnected() {
// With enableAutoServiceReconnection(), SDK handles reconnection
// Manual retry is no longer required
Log.d(TAG, "Billing service disconnected - auto-reconnection enabled")
}
})
}
companion object {
private const val TAG = "SubscriptionManager"
}
}
主な機能:
-
自動再接続:
バージョン 8.0.0 の
enableAutoServiceReconnection()は、必要に応じて接続を自動的に再確立します - 購入リスナー: 更新や保留中トランザクションを含む、すべての購入更新の コールバックを受信します
- エラー処理: ユーザーによるキャンセルや課金エラーに対する包括的な処理
サブスクリプション情報を照会する
有効なサブスクリプションを取得する
更新やアプリ外で購入されたサブスクリプションを処理するために、
queryPurchasesAsync()
を使用して既存のサブスクリプションを照会します。
private suspend fun queryExistingSubscriptions() {
val params = QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
.build()
withContext(Dispatchers.IO) {
val result = billingClient.queryPurchasesAsync(params)
if (result.billingResult.responseCode == BillingResponseCode.OK) {
result.purchasesList.forEach { purchase ->
when (purchase.purchaseState) {
Purchase.PurchaseState.PURCHASED -> {
// Active subscription - process it
handleSubscriptionPurchase(purchase)
}
Purchase.PurchaseState.PENDING -> {
// Pending payment - notify user
Log.d(TAG, "Subscription pending: ${purchase.products}")
}
}
}
} else {
Log.e(TAG, "Query failed: ${result.billingResult.debugMessage}")
}
}
}
ベストプラクティス:
アプリがバックグラウンドにある間、または閉じられている間に
発生したサブスクリプションの更新を捕捉するには、
onResume()
内で
queryPurchasesAsync()
を呼び出します。
商品詳細を照会する
ユーザーに表示する前に、価格、課金期間、オファートークンを含むサブスクリプション商品の 詳細を取得します。
private suspend fun querySubscriptionProducts() {
val productList = listOf(
QueryProductDetailsParams.Product.newBuilder()
.setProductId("premium_monthly")
.setProductType(BillingClient.ProductType.SUBS)
.build(),
QueryProductDetailsParams.Product.newBuilder()
.setProductId("premium_yearly")
.setProductType(BillingClient.ProductType.SUBS)
.build()
)
val params = QueryProductDetailsParams.newBuilder()
.setProductList(productList)
.build()
withContext(Dispatchers.IO) {
val productDetailsResult = billingClient.queryProductDetails(params)
if (productDetailsResult.billingResult.responseCode == BillingResponseCode.OK) {
// Process successfully retrieved product details
productDetailsResult.productDetailsList?.forEach { productDetails ->
// Extract subscription offers
productDetails.subscriptionOfferDetails?.forEach { offer ->
val price = offer.pricingPhases.pricingPhaseList.firstOrNull()
Log.d(TAG, "Product: ${productDetails.productId}, Price: ${price?.formattedPrice}")
}
}
// Handle unfetched products
productDetailsResult.unfetchedProductList?.forEach { unfetchedProduct ->
Log.w(TAG, "Unfetched: ${unfetchedProduct.productId}")
}
}
}
}
購入更新を処理する
サブスクリプションイベントを処理する
購入状態の変化を処理し、サブスクリプションのライフサイクルに基づいて適切なイベントを Singular に送信します。
private fun handlePurchaseUpdate(purchase: Purchase) {
when (purchase.purchaseState) {
Purchase.PurchaseState.PURCHASED -> {
if (!purchase.isAcknowledged) {
// Verify purchase on your backend first
verifyPurchaseOnBackend(purchase) { isValid ->
if (isValid) {
// Send subscription event to Singular
trackSubscriptionToSingular(purchase)
// Grant entitlement to user
grantSubscriptionAccess(purchase)
// Acknowledge purchase to Google
acknowledgePurchase(purchase)
}
}
}
}
Purchase.PurchaseState.PENDING -> {
// Notify user that payment is pending
Log.d(TAG, "Purchase pending approval: ${purchase.products}")
notifyUserPendingPayment(purchase)
}
}
}
private fun trackSubscriptionToSingular(purchase: Purchase) {
// Get cached product details for pricing information
val productDetails = getCachedProductDetails(purchase.products.first())
productDetails?.let { details ->
val subscriptionOffer = details.subscriptionOfferDetails?.firstOrNull()
val pricingPhase = subscriptionOffer?.pricingPhases?.pricingPhaseList?.firstOrNull()
val price = pricingPhase?.priceAmountMicros?.div(1_000_000.0) ?: 0.0
val currency = pricingPhase?.priceCurrencyCode ?: "USD"
// Determine event name based on purchase type
val eventName = if (isNewSubscription(purchase)) {
"sng_subscribe"
} else {
"subscription_renewed"
}
// Send to Singular WITHOUT receipt
Singular.customRevenue(
eventName,
currency,
price,
mapOf(
"subscription_id" to purchase.products.first(),
"order_id" to (purchase.orderId ?: ""),
"purchase_time" to purchase.purchaseTime.toString()
)
)
Log.d(TAG, "Tracked $eventName to Singular: $price $currency")
}
}
private fun acknowledgePurchase(purchase: Purchase) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
lifecycleScope.launch {
val ackResult = withContext(Dispatchers.IO) {
billingClient.acknowledgePurchase(acknowledgePurchaseParams)
}
if (ackResult.responseCode == BillingResponseCode.OK) {
Log.d(TAG, "Purchase acknowledged successfully")
} else {
Log.e(TAG, "Acknowledgement failed: ${ackResult.debugMessage}")
}
}
}
重大:
サブスクリプションイベントをトラッキングする際に、Google Play のレシートを
Singular に送信しないでください。レシート検証なしで
customRevenue()
メソッドを使用してください。購入は 3 日以内に承認 (acknowledge) してください。
そうしないと自動的に返金されます。
iOS: StoreKit 連携
Apple の StoreKit フレームワークを連携させてサブスクリプションを管理し、iOS アプリ向けに Singular へイベントを送信します。
前提条件
App Store Connect を構成する
アプリで StoreKit を実装する前に、 App Store Connect でサブスクリプション商品とサブスクリプショングループを設定します。
- サブスクリプショングループを作成する: アクセスレベルに基づいてサブスクリプションを グループに整理します
- サブスクリプション商品を定義する: サブスクリプションのティア、期間、価格を 設定します
- オファーを構成する: 導入オファー、プロモーションオファー、サブスクリプション コードを作成します
- テスト環境: 開発用にサンドボックスのテストアカウントを設定します
StoreKit を実装する
トランザクションオブザーバーを設定する
サブスクリプションの購入通知や更新を受信するために、
SKPaymentTransactionObserver
を実装します。
import StoreKit
class SubscriptionManager: NSObject, SKPaymentTransactionObserver {
static let shared = SubscriptionManager()
private override init() {
super.init()
// Add transaction observer
SKPaymentQueue.default().add(self)
}
deinit {
SKPaymentQueue.default().remove(self)
}
// MARK: - SKPaymentTransactionObserver
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
// New subscription or renewal
handlePurchasedTransaction(transaction)
case .restored:
// Subscription restored from another device
handleRestoredTransaction(transaction)
case .failed:
// Purchase failed
handleFailedTransaction(transaction)
case .deferred:
// Purchase awaiting approval (family sharing)
print("Transaction deferred: \(transaction.payment.productIdentifier)")
case .purchasing:
// Transaction in progress
break
@unknown default:
break
}
}
}
private func handlePurchasedTransaction(_ transaction: SKPaymentTransaction) {
// Verify receipt with your backend
verifyReceipt { isValid in
if isValid {
// Send to Singular
self.trackSubscriptionToSingular(transaction)
// Grant entitlement
self.grantSubscriptionAccess(transaction)
// Finish transaction
SKPaymentQueue.default().finishTransaction(transaction)
}
}
}
private func trackSubscriptionToSingular(_ transaction: SKPaymentTransaction) {
// Get product details for pricing
let productId = transaction.payment.productIdentifier
// You should cache product details from SKProductsRequest
if let product = getCachedProduct(productId) {
let price = product.price.doubleValue
let currency = product.priceLocale.currencyCode ?? "USD"
// Determine event name
let eventName = isNewSubscription(transaction) ? "sng_subscribe" : "subscription_renewed"
// Send to Singular WITHOUT receipt
Singular.customRevenue(
eventName,
currency: currency,
amount: price,
withAttributes: [
"subscription_id": productId,
"transaction_id": transaction.transactionIdentifier ?? "",
"transaction_date": transaction.transactionDate?.timeIntervalSince1970 ?? 0
]
)
}
}
}
サブスクリプションのステータスを照会する
レシート検証を使用して現在のサブスクリプションのステータスを確認し、更新を処理します。
func refreshSubscriptionStatus() {
let request = SKReceiptRefreshRequest()
request.delegate = self
request.start()
}
extension SubscriptionManager: SKRequestDelegate {
func requestDidFinish(_ request: SKRequest) {
// Receipt refreshed successfully
if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
// Send receipt to your backend for validation
validateReceiptOnServer()
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("Receipt refresh failed: \(error.localizedDescription)")
}
}
SDK 連携方式
Singular SDK のカスタム収益メソッドを使用して、レシート検証なしでアプリから サブスクリプションイベントを送信します。
プラットフォーム別の SDK メソッド
サブスクリプションイベントをトラッキングするには、お使いのプラットフォームに適した SDK メソッドを使用します。
Android SDK
// Track subscription without receipt
Singular.customRevenue(
"sng_subscribe", // Event name
"USD", // Currency
9.99, // Amount
mapOf( // Additional attributes
"subscription_id" to "premium_monthly",
"billing_period" to "monthly"
)
)
ドキュメント: Android SDK 収益トラッキング
iOS SDK
// Track subscription without receipt
Singular.customRevenue(
"sng_subscribe", // Event name
currency: "USD", // Currency
amount: 9.99, // Amount
withAttributes: [ // Additional attributes
"subscription_id": "premium_monthly",
"billing_period": "monthly"
]
)
ドキュメント: iOS SDK 収益トラッキング
React Native SDK
// Track subscription without receipt
Singular.customRevenueWithArgs(
"sng_subscribe", // Event name
"USD", // Currency
9.99, // Amount
{ // Additional attributes
subscription_id: "premium_monthly",
billing_period: "monthly"
}
);
ドキュメント: React Native SDK 収益トラッキング
Unity SDK
// Track subscription without receipt
SingularSDK.CustomRevenue(
"sng_subscribe", // Event name
"USD", // Currency
9.99, // Amount
new Dictionary<string, object> { // Additional attributes
{ "subscription_id", "premium_monthly" },
{ "billing_period", "monthly" }
}
);
ドキュメント: Unity SDK 収益トラッキング
Flutter SDK
// Track subscription without receipt
Singular.customRevenueWithAttributes(
"sng_subscribe", // Event name
"USD", // Currency
9.99, // Amount
{ // Additional attributes
"subscription_id": "premium_monthly",
"billing_period": "monthly"
}
);
ドキュメント: Flutter SDK 収益トラッキング
Cordova SDK
// Track subscription without receipt
cordova.plugins.SingularCordovaSdk.customRevenueWithArgs(
"sng_subscribe", // Event name
"USD", // Currency
9.99, // Amount
{ // Additional attributes
subscription_id: "premium_monthly",
billing_period: "monthly"
}
);
ドキュメント: Cordova SDK 収益トラッキング
重大:
サブスクリプショントラッキングにおいて、IAP (アプリ内購入) メソッドを
使用したり、レシート値を Singular に送信したりしないでください。レシートなしの
customRevenue()
メソッドのみを使用してください。
サーバー間連携
アプリの状態に依存せず即時にトラッキングできるよう、バックエンドから Singular の REST API にサブスクリプションイベントをリアルタイムで送信します。
実装要件
Singular の Event Endpoint を使用して、安全なバックエンドから収益パラメータ付きの サブスクリプションイベントを送信します。
Event Endpoint
サブスクリプションイベントデータと必要なモバイルデバイスプロパティを付けて、Singular の Event API に POST リクエストを送信します。
エンドポイント:
POST https://api.singular.net/api/v1/evt
必須パラメータ:
-
SDK Key:
Singular SDK キー (
aパラメータ) -
Event Name:
サブスクリプションイベント名 (
nパラメータ) -
Revenue:
サブスクリプション金額 (
amtパラメータ) -
Currency:
ISO 4217 通貨コード (
curパラメータ) - Device Identifiers: アトリビューション用の IDFA (iOS) または GAID (Android)
-
Platform:
オペレーティングシステム (
pパラメータ)
ドキュメント: Event Endpoint リファレンス および 収益パラメータ
リクエスト例
curl -X POST 'https://api.singular.net/api/v1/evt' \
-H 'Content-Type: application/json' \
-d '{
"a": "YOUR_SDK_KEY",
"n": "sng_subscribe",
"r": 9.99,
"pcc": "USD",
"idfa": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"p": "iOS",
"install_time": 1697000000000,
"extra": {
"subscription_id": "premium_monthly",
"order_id": "GPA.1234.5678.9012"
}
}'
SDK は不要: サブスクリプションイベントに S2S を使用する場合、これらのイベントを SDK から送信する必要はありません。ただし、ほとんどのアプリはセッショントラッキングおよび サブスクリプション以外のイベントのために SDK も引き続き連携させる必要があります。
サードパーティ連携
サブスクリプションインフラを管理し、イベントを Singular に直接送信するサードパーティ プラットフォームを通じてサブスクリプショントラッキングを構成します。
サポートされているパートナー
Singular サポートが組み込まれているサブスクリプション管理プラットフォームと連携します。
RevenueCat 連携
RevenueCat はサブスクリプションインフラを処理し、サブスクリプションイベントを Singular の REST API に自動的に送信します。
設定手順:
- RevenueCat アカウントを作成する: RevenueCat のダッシュボードでアプリを 設定します
- Singular 連携を構成する: RevenueCat の連携設定に Singular SDK キーを 追加します
- イベントをマッピングする: どの RevenueCat イベントを Singular に送信するかを 構成します
- 連携をテストする: イベントが Singular のダッシュボードに表示されることを 確認します
ドキュメント: RevenueCat Singular 連携
Adapty 連携
Adapty はサブスクリプション分析を提供し、その連携を通じてイベントを Singular に送信します。
設定手順:
- Adapty アカウントを作成する: Adapty に登録してアプリを構成します
- Singular 連携を有効にする: 連携画面に移動して Singular の認証情報を 追加します
- イベントマッピングを構成する: どのサブスクリプションイベントを転送するかを 選択します
- データフローを確認する: イベントが Singular に正常に届くことを確認します
ドキュメント: Adapty Singular 連携
Superwall 連携
Singular を接続して Superwall のサブスクリプションライフサイクルおよび収益イベントを 受信します。より強力なアトリビューションのために SDID がサポートされています。
設定手順:
- Singular SDK を連携させる: セッションおよびイベントトラッキングのために Singular SDK をアプリに追加します
- Singular SDID を設定する (任意): Singular SDID を設定し、より強力な アトリビューションのためにアプリ内で SDID を使用して Superwall のユーザー属性を 更新します
- Superwall で Singular 連携を追加する: Superwall のダッシュボードで Singular 連携を有効化して構成します
ドキュメント: Superwall Singular 連携
Xsolla 連携
Xsolla は Web Shop での購入に関する情報をモバイルアプリ内イベントとして Singular に 送信します。その後 Singular は、それをモバイルアプリケーションのインストール、 ユーザー獲得、リエンゲージメントキャンペーンにアトリビューションします。
設定手順:
- モバイルアプリに Singular を連携させる: Singular SDK をモバイルアプリに 追加します
- アプリ内イベントを構成する: Singular にアプリ内イベントを送信します。 たとえば login や、CUID を含むその他のイベントです
- Singular パートナー構成を設定する: Xsolla 向けの Singular パートナー 構成を設定します
- Xsolla アカウントに Singular を追加する: Xsolla アカウントで Singular を 有効にします
ドキュメント: Xsolla Singular 連携
注: サードパーティ連携はパートナープラットフォームによって管理されます。構成は それぞれのダッシュボードで完了する必要があります。セッションおよびサブスクリプション 以外のイベントのトラッキングのために、Singular SDK も引き続き連携させる必要があります。
サブスクリプションイベントの種類
さまざまなサブスクリプションライフサイクルイベントをトラッキングして、サブスクリプションの 全体的な過程にわたるユーザーエンゲージメント、リテンション、収益を測定します。
標準イベント名
プラットフォーム間で一貫したレポーティングを行うために Singular の標準イベント名を使用するか、 トラッキング要件に基づいてカスタムイベント名を定義します。
| サブスクリプションの状態 | イベント名 | SDK メソッド | S2S 連携 |
|---|---|---|---|
| 新規サブスクリプション |
sng_subscribe
|
収益金額付きの customRevenue() (レシートなし) | 収益パラメータ付きの Event Endpoint |
| トライアル開始 |
sng_start_trial
|
収益なしの event() メソッド | 収益なしの標準 Event Endpoint |
| トライアル終了 |
sng_end_trial
|
収益なしの event() メソッド | 収益なしの標準 Event Endpoint |
| サブスクリプション更新 |
subscription_renewed
|
収益金額付きの customRevenue() (レシートなし) | 収益パラメータ付きの Event Endpoint |
| キャンセル |
subscription_cancelled
|
収益なしの event() メソッド | 収益なしの標準 Event Endpoint |
| 返金 |
subscription_refunded
|
負の金額付きの customRevenue() (任意)、または収益なしの event() | 負の収益付きの Event Endpoint (任意)、または標準イベント |
重要:
customRevenue() を介してサブスクリプションイベントを送信する際は、
isRestored
が
false
のイベントのみを送信してください。復元された購入は既存のサブスクリプションを
表すものであり、新規収益ではありません。
イベント実装例
新規サブスクリプション
ユーザーが初めて新しいサブスクリプションを購入したときにトラッキングします。
// New subscription purchase
Singular.customRevenue(
"sng_subscribe",
"USD",
9.99,
mapOf(
"subscription_tier" to "premium",
"billing_period" to "monthly",
"product_id" to "premium_monthly"
)
)
トライアル開始
ユーザーが無料トライアル期間を開始したときに、収益なしでトラッキングします。
// Trial start (no revenue)
Singular.event(
"sng_start_trial",
mapOf(
"trial_duration" to "7_days",
"subscription_tier" to "premium"
)
)
サブスクリプション更新
自動サブスクリプション更新を収益金額付きでトラッキングします。
// Subscription renewal
Singular.customRevenue(
"subscription_renewed",
"USD",
9.99,
mapOf(
"subscription_tier" to "premium",
"renewal_count" to 3,
"product_id" to "premium_monthly"
)
)
キャンセル
ユーザーがサブスクリプションをキャンセルしたときにトラッキングします (収益イベントなし)。
// Subscription cancelled (no revenue)
Singular.event(
"subscription_cancelled",
mapOf(
"cancellation_reason" to "too_expensive",
"days_active" to 45,
"subscription_tier" to "premium"
)
)
SKAdNetwork 測定
iOS のプライバシーに準拠したアトリビューションのために、Apple の SKAdNetwork を通じて サブスクリプションイベントを測定します。
SKAN サブスクリプションサポート
Singular はすべての連携方式 (SDK、S2S、サードパーティ) を介してサブスクリプション イベントデータを測定できます。イベントの測定は、SKAN のバージョンに応じて、SKAN の postback タイミングウィンドウによって制限される場合があります。
ハイブリッド SKAN 実装
ライフサイクルイベントに Singular SDK を使用し、サブスクリプションに S2S/サードパーティ 連携を使用するアプリでは、両方のデータソースを組み合わせるためにハイブリッド SKAN を 有効にします。
サポートへのお問い合わせ: アプリでハイブリッド SKAN を有効にするには、 担当のカスタマーサクセスマネージャー (CSM) にお問い合わせください。これにより、 バックエンドソースからのサブスクリプションイベントが SKAN の conversion value に 適切にアトリビューションされます。
SKAN Postback ウィンドウ:
- SKAN 4.0: 3 つの postback ウィンドウ (0~2 日、3~7 日、8~35 日) により、 初期のサブスクリプションイベントや短期の更新を測定できます
- SKAN 3.0: 単一の 24 時間 postback ウィンドウにより、測定は即時の サブスクリプションのみに制限されます
- Conversion Value: 価値の高いイベントを優先するために、SKAN の conversion value スキーマにサブスクリプションイベントを構成します
ベストプラクティスと検証
実装のベストプラクティスに従い、本番環境へのデプロイ前にサブスクリプショントラッキングが 正しく機能していることを確認します。
実装のベストプラクティス
主なガイドライン
- レシート検証を行わない: サブスクリプションをトラッキングする際に、 プラットフォームのレシート (Google Play、App Store) を Singular に決して送信しないでください。 レシートなしで customRevenue() を使用します
- 復元された購入をフィルタリングする: 収益の重複レポーティングを避けるために、 isRestored が false のイベントのみを送信します
- バックエンドでの検証: Singular にイベントを送信する前に、必ず安全な バックエンドで購入を検証します
- 速やかな承認 (acknowledge): 自動返金を防ぐために、Google Play の購入は 3 日以内に承認します
- Resume 時に照会する: アプリが閉じている間に発生した更新を捕捉するために、 onResume() 内で queryPurchasesAsync() を呼び出します
- 一貫したイベント名: 統一されたレポーティングのために、標準の Singular イベント名 (sng_subscribe、subscription_renewed) を使用します
- メタデータを含める: 詳細な分析のために、subscription_tier、 billing_period、product_id などの属性を追加します
テストと検証
テストチェックリスト
- テスト環境の設定: Google Play と App Store の両方で、サンドボックス/ テストアカウントを使用します
- 新規サブスクリプション: sng_subscribe イベントが適切な金額と通貨で 正しく送信されることを確認します
- トライアルフロー: トライアル開始とトライアル終了のイベントを収益なしで テストします
- 更新処理: 更新によって subscription_renewed イベントが正しい収益で トリガーされることを確認します
- キャンセルトラッキング: ユーザーがサブスクリプションをキャンセルしたときに キャンセルイベントが発火することを確認します
- 復元のフィルタリング: 復元された購入が重複イベントを送信しないことを 確認します
- Singular ダッシュボード: イベントが正しいアトリビューションで Singular に 表示されることを確認します
- クロスデバイス: 複数のデバイス間でサブスクリプションが同期されることを テストします
よくある問題
- イベントの欠落: billing client の接続がアクティブであり、アプリの resume 時に queryPurchasesAsync() が呼び出されていることを確認します
- イベントの重複: 復元された購入が isRestored チェックで適切に フィルタリングされていることを確認します
- 誤った収益: ProductDetails からの価格抽出が、micros を 1,000,000 で 正しく除算していることを確認します
- アトリビューションの問題: デバイス識別子 (IDFA/GAID) が S2S リクエストに 適切に含まれていることを確認します
- 承認 (acknowledge) の失敗: 承認の前にバックエンドのレシート検証が 完了していることを確認します
サポートリソース: トラブルシューティングのサポートについては、SDK バージョン、 プラットフォームの詳細、サンプルイベントペイロード、問題を示す Singular ダッシュボードの スクリーンショットを添えて Singular サポートにお問い合わせください。