サブスクリプションイベントトラッキング実装ガイド
SingularのSDK、サーバー間統合、またはサードパーティパートナーを使用して、すべてのプラットフォームでサブスクリプション収益、ユーザー維持率、キャンペーンパフォーマンスを測定するための包括的なサブスクリプショントラッキングを実装します。
Singularは、新規サブスクリプション、更新、トライアル、キャンセル、払い戻しなどのサブスクリプションイベントを、ユーザーを生み出したマーケティングキャンペーンに正確に帰属させ、サブスクリプションライフサイクルと収益生成を完全に可視化します。
実装オプション
アプリのアーキテクチャとビジネス要件に最適な実装方法を選択します。
| 考慮事項 | SDKインテグレーション | サーバー間(S2S) | サードパーティとの統合 |
|---|---|---|---|
| 実装 | Singular SDKのカスタム収益メソッドでサブスクリプションイベントを送信 | お客様のバックエンドからSingularのREST APIに必要なモバイルデバイスのプロパティを指定してイベントを送信します。 | RevenueCat、Adapty、またはその他のサブスクリプションプラットフォームとの統合を設定します。 |
| アプリのアクティビティ依存性 | SDKがイベントを送信するにはアプリが起動している必要があります。 | アプリの状態とは無関係にリアルタイムでイベントが送信される | パートナープラットフォームからリアルタイムで送信されるイベント |
| イベントのタイミング | イベント時刻は、アプリの起動に基づく実際のサブスクリプション時刻と異なる場合があります。 | バックエンドで処理されるリアルタイムのイベントトラッキング | パートナー処理後のほぼリアルタイムのトラッキング |
| 複雑さ | シンプルなクライアントサイドの実装 | 安全なバックエンドインフラが必要 | パートナーアカウントと設定が必要 |
重要:サブスクリプションイベントのためにS2Sやサードパーティとの統合を使用しているほとんどのお客様は、完全なアトリビューショントラッキングのために、セッションとサブスクリプション以外のイベントを管理するためにSingular SDKを統合する必要があります。
アンドロイドGoogle Play課金の統合
Google Play Billing Library 8.0.0を統合して、Singular SDKと統合するために、デバイス上で直接サブスクリプション情報を照会し、サブスクリプションの状態を管理します。
前提条件
Google Play コンソールの設定
アプリに課金を実装する前に、Google Play コンソールでアプリ内商品と購読アイテムを設定します。
- サブスクリプション商品を作成します:Play Console でサブスクリプションの階層、課金期間、価格を定義します。
- オファーの設定:基本プラン、オファートークン、キャンペーン価格を設定します。
- テストセットアップ:テストアカウントを作成し、製品の可用性を検証します。
課金ライブラリの依存関係の追加
build.gradleを更新する
Google Play Billing Library 8.0.0をアプリの依存関係に追加し、Kotlin拡張でコルーチンをサポートします。
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の初期化
課金マネージャの作成
すべてのサブスクリプション操作を処理し、イベントトラッキングのためにSingular SDKと統合する課金マネージャークラスを設定します。
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日以内に購入を確認しないと、自動的に返金されます。
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 Revenue Tracking
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 Revenue Tracking
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 Revenue Tracking
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 Revenue Tracking
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 Revenue Tracking
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 Revenue Tracking
クリティカルサブスクリプショントラッキングのために、IAP(アプリ内課金)メソッドを使用したり、レシートの値をSingularに送信したりしないでください。レシートのないcustomRevenue() メソッドのみを使用してください。
サーバー間の統合
バックエンドからSingularのREST APIにリアルタイムでサブスクリプションイベントを送信し、アプリの状態に依存しない即時トラッキングを実現します。
実装要件
Singularのイベントエンドポイントを使用して、セキュアなバックエンドから収益パラメータ付きのサブスクリプションイベントを送信します。
イベントエンドポイント
サブスクリプションイベントデータと必要なモバイルデバイスプロパティをSingularのEvent APIにPOSTリクエストを送信します。
エンドポイント
POST https://api.singular.net/api/v1/evt
必須パラメータ
-
SDKキー:お客様のSingular SDKキー (
aパラメータ) -
イベント名サブスクリプションイベント名 (
nパラメータ) -
収益:サブスクリプション金額 (
amtパラメータ) -
通貨ISO 4217 通貨コード (
curパラメータ) - デバイス識別子:IDFA(iOS)またはGAID(Android)。
-
プラットフォームオペレーティングシステム (
pパラメータ)
ドキュメント:イベント・エンドポイント・リファレンスと 収益パラメータ
リクエスト例
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インテグレーション
注:サードパーティの統合はパートナーのプラットフォームによって管理されます。セッションと非サブスクリプションイベントのトラッキングには、Singular SDKを統合する必要があります。
サブスクリプションイベントの種類
様々なサブスクリプションライフサイクルイベントをトラッキングし、サブスクリプションジャーニー全体におけるユーザーエンゲージメント、リテンション、収益を測定します。
標準イベント名
プラットフォーム間で一貫したレポートを作成するためにSingularの標準イベント名を使用するか、トラッキング要件に基づいてカスタムイベント名を定義します。
| サブスクリプションの状態 | イベント名 | SDKメソッド | S2S統合 |
|---|---|---|---|
| 新しいサブスクリプション |
sng_subscribe
|
customRevenue()で収益額(レシートなし) | 収益パラメータ付きイベントエンドポイント |
| トライアル開始 |
sng_start_trial
|
収益なしのevent()メソッド | 収益なしの標準イベントエンドポイント |
| トライアル終了 |
sng_end_trial
|
収益なしのイベント()メソッド | 収益なしの標準イベント・エンドポイント |
| サブスクリプションの更新 |
subscription_renewed
|
customRevenue()メソッド、収益額あり(レシートなし) | 収益パラメータ付きのイベント・エンドポイント |
| キャンセル |
subscription_cancelled
|
収益なしのevent()メソッド | 収益なしの標準イベントエンドポイント |
| 払い戻し |
subscription_refunded
|
負の金額を持つcustomRevenue() (オプション) OR 収益のないevent() | マイナスの収益を持つイベント・エンドポイント (オプション) または標準イベント |
重要: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のポストバックタイミングウィンドウによってイベント計測が制限される場合があります。
ハイブリッドSKANの実装
ライフサイクルイベントにSingular SDKを使用し、サブスクリプションにS2S/サードパーティ統合を使用するアプリの場合は、ハイブリッドSKANを有効にして両方のデータソースを組み合わせてください。
サポートにお問い合わせください:カスタマーサクセスマネージャー(CSM)に連絡して、アプリのHybrid SKANを有効にしてください。これにより、バックエンドソースからの購読イベントがSKANの変換値で適切に帰属するようになります。
SKANポストバックウィンドウ
- SKAN 4.0:3つのポストバックウィンドウ(0~2日、3~7日、8~35日)により、初期の購読イベントと短期更新の測定が可能です。
- SKAN 3.0:単一の24時間ポストバックウィンドウにより、即時購読のみの測定に制限されます。
- 換算値:SKANの変換値スキーマでサブスクリプションイベントを設定し、価値の高いイベントを優先します。
ベストプラクティスと検証
実装のベストプラクティスに従い、本番展開前にサブスクリプション追跡が正しく動作することを確認してください。
実装のベストプラクティス
主なガイドライン
- レシートの検証を行わない:サブスクリプションをトラッキングする際、プラットフォームのレシート(Google Play、App Store)をSingularに送信しない-レシートなしでcustomRevenue()を使用する。
- リストアされた購入をフィルタリングする:収益の重複報告を避けるために、isRestoredがfalseのイベントのみを送信する。
- バックエンド検証:Singularにイベントを送信する前に、常に安全なバックエンドで購入を確認する。
- タイムリーな承認:Google Playでの購入を3日以内に確認し、自動返金を防ぐ。
- クエリーの再開:onResume()内でqueryPurchasesAsync()を呼び出し、アプリが終了している間に発生した更新をキャッチします。
- 一貫したイベント名:標準のSingularイベント名(sng_subscribe, subscription_renewed)を使用し、統一されたレポートを作成します。
- メタデータを含める:詳細な分析のために、subscription_tier、billing_period、product_idのような属性を追加する。
テストと検証
テストチェックリスト
- テスト環境のセットアップ:Google PlayとApp Storeの両方でサンドボックス/テストアカウントを使用する。
- 新しいサブスクリプション:sng_subscribeイベントが適切な金額と通貨で正しく送信されることを確認する。
- トライアルフロー:収益のないトライアル開始と終了イベントをテストする。
- 更新処理:更新が正しい収益でsubscription_renewedイベントをトリガーすることを確認する。
- キャンセルトラッキング:ユーザがサブスクリプションをキャンセルしたときにキャンセルイベントが発生することを確認する。
- リストアフィルタリング:リストアされた購入が重複したイベントを送信しないようにします。
- Singularダッシュボード:イベントが正しい属性でSingularに表示されることを確認します。
- クロスデバイス:複数のデバイスでサブスクリプションの同期をテストする
よくある問題
- イベントの欠落:課金クライアント接続がアクティブで、アプリのレジューム時にqueryPurchasesAsync()が呼び出されていることを確認する。
- イベントの重複:復元された購入がisRestoredチェックで適切にフィルタリングされていることを確認する。
- 不正な収益:ProductDetailsからの価格抽出がマイクロを1,000,000で正しく割っていることを確認する
- アトリビューションの問題デバイス識別子(IDFA/GAID)がS2Sリクエストに適切に含まれていることを確認する
- 確認応答の失敗:承認前にバックエンドのレシート検証が完了することを確認する
サポートリソーストラブルシューティングについては、SDKバージョン、プラットフォームの詳細、サンプルイベントペイロード、問題を示すSingularダッシュボードのスクリーンショットをSingularサポートまでご連絡ください。