Guía de implementación del seguimiento de eventos de suscripción

Guía de implementación del seguimiento de eventos de suscripción

Implementa un seguimiento integral de suscripciones para medir los ingresos por suscripción, la retención de usuarios y el rendimiento de las campañas en todas las plataformas mediante el SDK de Singular, la integración server-to-server o partners de terceros.

Singular permite atribuir con precisión los eventos de suscripción—incluidas las nuevas suscripciones, renovaciones, pruebas, cancelaciones y reembolsos—a las campañas de marketing que generaron los usuarios, brindando visibilidad completa del ciclo de vida de la suscripción y la generación de ingresos.


Opciones de implementación

Elige el método de implementación que mejor se adapte a la arquitectura de tu app y a tus requisitos de negocio.

Consideración Integración mediante SDK Server-to-Server (S2S) Integración con terceros
Implementación Envía eventos de suscripción mediante los métodos de ingresos personalizados en el SDK de Singular Envía eventos desde tu backend a la REST API de Singular con las propiedades de dispositivo móvil requeridas Configura integraciones con RevenueCat, Adapty, Superwall, Xsolla u otras plataformas de suscripción
Dependencia de la actividad de la app La app debe iniciarse para que el SDK envíe eventos Los eventos se envían en tiempo real, independientemente del estado de la app Los eventos se envían en tiempo real desde la plataforma del partner
Momento del evento El momento del evento puede diferir del momento real de la suscripción según el inicio de la app Seguimiento de eventos en tiempo real conforme se procesan en el backend Seguimiento casi en tiempo real tras el procesamiento del partner
Complejidad Implementación sencilla del lado del cliente Requiere infraestructura de backend segura Requiere una cuenta y configuración del partner

Importante: La mayoría de los clientes que usan integraciones S2S o de terceros para eventos de suscripción deberían integrar igualmente el SDK de Singular para gestionar las sesiones y los eventos que no son de suscripción, y así lograr un seguimiento de atribución completo.


Android: integración con Google Play Billing

Integra Google Play Billing Library 8.0.0 para consultar la información de suscripción y gestionar el estado de la suscripción directamente en el dispositivo para la integración con el SDK de Singular.

Requisitos previos

Configurar Google Play Console

Configura los productos in-app y los elementos de suscripción en la Google Play Console antes de implementar la facturación en tu app.

  1. Crear productos de suscripción: Define los niveles de suscripción, los períodos de facturación y los precios en Play Console
  2. Configurar ofertas: Configura planes base, tokens de oferta y precios promocionales
  3. Configuración de pruebas: Crea cuentas de prueba y verifica la disponibilidad de los productos

Agregar la dependencia de la Billing Library

Actualizar build.gradle

Agrega Google Play Billing Library 8.0.0 a las dependencias de tu app con las extensiones de Kotlin para admitir corrutinas.

Kotlin DSL
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")
}

Funciones de la versión 8.0.0: Esta versión introduce la reconexión automática del servicio con enableAutoServiceReconnection() , APIs de consulta de compras mejoradas y una gestión optimizada de las transacciones pendientes.


Inicializar BillingClient

Crear el administrador de facturación

Configura una clase de administrador de facturación para gestionar todas las operaciones de suscripción e integrarla con el SDK de Singular para el seguimiento de eventos.

Kotlin
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"
    }
}

Funciones clave:

  • Reconexión automática: El enableAutoServiceReconnection() de la versión 8.0.0 restablece automáticamente las conexiones cuando es necesario
  • Listener de compras: Recibe callbacks de todas las actualizaciones de compra, incluidas las renovaciones y las transacciones pendientes
  • Gestión de errores: Gestión integral de las cancelaciones de usuarios y de los errores de facturación

Consultar la información de suscripción

Recuperar las suscripciones activas

Consulta las suscripciones existentes mediante queryPurchasesAsync() para gestionar las renovaciones y las suscripciones compradas fuera de tu app.

Kotlin
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}")
        }
    }
}

Práctica recomendada: Llama a queryPurchasesAsync() en onResume() para detectar las renovaciones de suscripción ocurridas mientras tu app estaba en segundo plano o cerrada.


Consultar los detalles del producto

Recupera los detalles del producto de suscripción, incluidos los precios, los períodos de facturación y los tokens de oferta, antes de mostrarlos a los usuarios.

Kotlin
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}")
            }
        }
    }
}

Procesar las actualizaciones de compra

Gestionar los eventos de suscripción

Procesa los cambios de estado de la compra y envía los eventos correspondientes a Singular según el ciclo de vida de la suscripción.

Kotlin
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}")
        }
    }
}

Crítico: NO envíes el recibo de Google Play a Singular al hacer seguimiento de eventos de suscripción. Usa el método customRevenue() sin validación de recibos. Confirma las compras dentro de 3 días o se reembolsarán automáticamente.


iOS: integración con StoreKit

Integra el framework StoreKit de Apple para gestionar las suscripciones y enviar eventos a Singular en apps de iOS.

Requisitos previos

Configurar App Store Connect

Configura los productos de suscripción y los grupos de suscripción en App Store Connect antes de implementar StoreKit en tu app.

  1. Crear grupos de suscripción: Organiza las suscripciones en grupos según los niveles de acceso
  2. Definir productos de suscripción: Configura los niveles de suscripción, las duraciones y los precios
  3. Configurar ofertas: Crea ofertas introductorias, ofertas promocionales y códigos de suscripción
  4. Entorno de pruebas: Configura cuentas de prueba de sandbox para el desarrollo

Implementar StoreKit

Configurar el observador de transacciones

Implementa SKPaymentTransactionObserver para recibir las notificaciones de compra de suscripciones y las renovaciones.

Swift
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
                ]
            )
        }
    }
}

Consultar el estado de la suscripción

Usa la validación de recibos para comprobar el estado actual de la suscripción y gestionar las renovaciones.

Swift
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)")
    }
}

Método de integración mediante SDK

Envía eventos de suscripción desde tu app usando los métodos de ingresos personalizados del SDK de Singular sin validación de recibos.

Métodos del SDK específicos por plataforma

Usa el método del SDK adecuado para tu plataforma para hacer seguimiento de los eventos de suscripción.

SDK de Android

Kotlin
// 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"
    )
)

Documentación: Seguimiento de ingresos del SDK de Android


SDK de iOS

Swift
// 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"
    ]
)

Documentación: Seguimiento de ingresos del SDK de iOS


SDK de React Native

JavaScript
// Track subscription without receipt
Singular.customRevenueWithArgs(
    "sng_subscribe",  // Event name
    "USD",            // Currency
    9.99,             // Amount
    {                 // Additional attributes
        subscription_id: "premium_monthly",
        billing_period: "monthly"
    }
);

Documentación: Seguimiento de ingresos del SDK de React Native


SDK de Unity

C#
// 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" }
    }
);

Documentación: Seguimiento de ingresos del SDK de Unity


SDK de Flutter

Dart
// Track subscription without receipt
Singular.customRevenueWithAttributes(
    "sng_subscribe",  // Event name
    "USD",            // Currency
    9.99,             // Amount
    {                 // Additional attributes
        "subscription_id": "premium_monthly",
        "billing_period": "monthly"
    }
);

Documentación: Seguimiento de ingresos del SDK de Flutter


SDK de Cordova

JavaScript
// 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"
    }
);

Documentación: Seguimiento de ingresos del SDK de Cordova

Crítico: NO uses métodos de IAP (In-App Purchase) ni envíes valores de recibo a Singular para el seguimiento de suscripciones. Usa únicamente los métodos customRevenue() sin recibos.


Integración server-to-server

Envía eventos de suscripción en tiempo real desde tu backend a la REST API de Singular para un seguimiento inmediato, independiente del estado de la app.

Requisitos de implementación

Usa el Event Endpoint de Singular para enviar eventos de suscripción con parámetros de ingresos desde tu backend seguro.

Event Endpoint

Envía solicitudes POST a la Event API de Singular con los datos del evento de suscripción y las propiedades de dispositivo móvil requeridas.

Endpoint:

POST https://api.singular.net/api/v1/evt

Parámetros requeridos:

  • SDK Key: Tu SDK key de Singular (parámetro a )
  • Nombre del evento: Nombre del evento de suscripción (parámetro n )
  • Ingresos: Importe de la suscripción (parámetro amt )
  • Moneda: Código de moneda ISO 4217 (parámetro cur )
  • Identificadores de dispositivo: IDFA (iOS) o GAID (Android) para la atribución
  • Plataforma: Sistema operativo (parámetro p )

Documentación: Referencia del Event Endpoint y Parámetros de ingresos


Solicitud de ejemplo

cURL
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 no requerido: Si usas S2S para los eventos de suscripción, no necesitas enviar estos eventos desde el SDK. Sin embargo, la mayoría de las apps deberían integrar igualmente el SDK para el seguimiento de sesiones y los eventos que no son de suscripción.


Integraciones con terceros

Configura el seguimiento de suscripciones mediante plataformas de terceros que gestionan la infraestructura de suscripción y envían eventos directamente a Singular.

Partners compatibles

Integra con plataformas de gestión de suscripciones que tienen compatibilidad integrada con Singular.

Integración con RevenueCat

RevenueCat gestiona la infraestructura de suscripción y envía automáticamente los eventos de suscripción a la REST API de Singular.

Pasos de configuración:

  1. Crear una cuenta de RevenueCat: Configura tu app en el dashboard de RevenueCat
  2. Configurar la integración con Singular: Agrega el SDK key de Singular en la configuración de integraciones de RevenueCat
  3. Mapear eventos: Configura qué eventos de RevenueCat deben enviarse a Singular
  4. Probar la integración: Verifica que los eventos aparezcan en el dashboard de Singular

Documentación: Integración de RevenueCat con Singular


Integración con Adapty

Adapty ofrece analíticas de suscripción y envía eventos a Singular a través de su integración.

Pasos de configuración:

  1. Crear una cuenta de Adapty: Regístrate y configura tu app en Adapty
  2. Habilitar la integración con Singular: Ve a las integraciones y agrega las credenciales de Singular
  3. Configurar el mapeo de eventos: Selecciona qué eventos de suscripción reenviar
  4. Verificar el flujo de datos: Confirma que los eventos llegan a Singular correctamente

Documentación: Integración de Adapty con Singular


Integración con Superwall

Conecta Singular para recibir los eventos de ciclo de vida de suscripción e ingresos de Superwall, con compatibilidad con SDID para una atribución más sólida.

Pasos de configuración:

  1. Integrar el SDK de Singular: Agrega el SDK de Singular a tu app para el seguimiento de sesiones y eventos
  2. Configurar el SDID de Singular (opcional): Configura el SDID de Singular y actualiza los atributos de usuario de Superwall con el SDID en tu app para una atribución más sólida
  3. Agregar la integración de Singular en Superwall: Habilita y configura la integración de Singular en tu dashboard de Superwall

Documentación: Integración de Superwall con Singular


Integración con Xsolla

Xsolla envía a Singular información sobre las compras en Web Shop como eventos in-app móviles, que Singular luego atribuye a instalaciones de aplicaciones móviles, adquisición de usuarios y campañas de re-engagement.

Pasos de configuración:

  1. Integrar Singular en tus apps móviles: Agrega el SDK de Singular a tus apps móviles
  2. Configurar un evento in-app: Envía un evento in-app a Singular, por ejemplo login, o cualquier otro evento que incluya el CUID
  3. Configurar la Singular Partner Configuration: Configura la Singular partner configuration para Xsolla
  4. Agregar Singular en tu cuenta de Xsolla: Habilita Singular en tu cuenta de Xsolla

Documentación: Integración de Xsolla con Singular

Nota: Las integraciones con terceros son gestionadas por las plataformas de los partners. La configuración debe completarse en sus respectivos dashboards. El SDK de Singular debe integrarse igualmente para el seguimiento de sesiones y de los eventos que no son de suscripción.


Tipos de eventos de suscripción

Haz seguimiento de diversos eventos del ciclo de vida de la suscripción para medir la interacción de los usuarios, la retención y los ingresos a lo largo de todo el recorrido de la suscripción.

Nombres de eventos estándar

Usa los nombres de eventos estándar de Singular para lograr informes coherentes en todas las plataformas, o define nombres de eventos personalizados según tus requisitos de seguimiento.

Estado de la suscripción Nombre del evento Método del SDK Integración S2S
Nueva suscripción sng_subscribe customRevenue() con importe de ingresos (sin recibo) Event endpoint con parámetros de ingresos
Inicio de prueba sng_start_trial método event() sin ingresos Event endpoint estándar sin ingresos
Fin de prueba sng_end_trial método event() sin ingresos Event endpoint estándar sin ingresos
Renovación de suscripción subscription_renewed customRevenue() con importe de ingresos (sin recibo) Event endpoint con parámetros de ingresos
Cancelación subscription_cancelled método event() sin ingresos Event endpoint estándar sin ingresos
Reembolso subscription_refunded customRevenue() con importe negativo (opcional) O event() sin ingresos Event endpoint con ingresos negativos (opcional) O evento estándar

Importante: Al enviar eventos de suscripción mediante customRevenue(), envía únicamente los eventos donde isRestored sea false . Las compras restauradas representan suscripciones existentes, no nuevos ingresos.


Ejemplos de implementación de eventos

Nueva suscripción

Haz seguimiento de cuándo los usuarios compran una nueva suscripción por primera vez.

Kotlin
// New subscription purchase
Singular.customRevenue(
    "sng_subscribe",
    "USD",
    9.99,
    mapOf(
        "subscription_tier" to "premium",
        "billing_period" to "monthly",
        "product_id" to "premium_monthly"
    )
)

Inicio de prueba

Haz seguimiento de cuándo los usuarios inician un período de prueba gratuita sin ingresos.

Kotlin
// Trial start (no revenue)
Singular.event(
    "sng_start_trial",
    mapOf(
        "trial_duration" to "7_days",
        "subscription_tier" to "premium"
    )
)

Renovación de suscripción

Haz seguimiento de las renovaciones automáticas de suscripción con importe de ingresos.

Kotlin
// Subscription renewal
Singular.customRevenue(
    "subscription_renewed",
    "USD",
    9.99,
    mapOf(
        "subscription_tier" to "premium",
        "renewal_count" to 3,
        "product_id" to "premium_monthly"
    )
)

Cancelación

Haz seguimiento de cuándo los usuarios cancelan su suscripción (evento sin ingresos).

Kotlin
// Subscription cancelled (no revenue)
Singular.event(
    "subscription_cancelled",
    mapOf(
        "cancellation_reason" to "too_expensive",
        "days_active" to 45,
        "subscription_tier" to "premium"
    )
)

Medición con SKAdNetwork

Mide los eventos de suscripción mediante el SKAdNetwork de Apple para una atribución compatible con la privacidad en iOS.

Compatibilidad de SKAN con suscripciones

Singular puede medir los datos de eventos de suscripción a través de todos los métodos de integración (SDK, S2S y terceros). La medición de eventos puede estar limitada por las ventanas de tiempo de los postbacks de SKAN según la versión de SKAN.

Implementación híbrida de SKAN

Para las apps que usan el SDK de Singular para los eventos del ciclo de vida e integraciones S2S/de terceros para las suscripciones, habilita Hybrid SKAN para combinar ambas fuentes de datos.

Contactar al soporte: Ponte en contacto con tu Customer Success Manager (CSM) para habilitar Hybrid SKAN en tu app. Esto garantiza que los eventos de suscripción procedentes de fuentes de backend se atribuyan correctamente en los conversion values de SKAN.

Ventanas de postback de SKAN:

  • SKAN 4.0: Tres ventanas de postback (0-2 días, 3-7 días, 8-35 días) permiten medir los eventos de suscripción tempranos y las renovaciones a corto plazo
  • SKAN 3.0: Una única ventana de postback de 24 horas limita la medición únicamente a las suscripciones inmediatas
  • Conversion values: Configura los eventos de suscripción en tu esquema de conversion value de SKAN para priorizar los eventos de alto valor

Prácticas recomendadas y validación

Sigue las prácticas recomendadas de implementación y verifica que el seguimiento de suscripciones funcione correctamente antes de la implementación en producción.

Prácticas recomendadas de implementación

Pautas clave

  • Sin validación de recibos: Nunca envíes los recibos de la plataforma (Google Play, App Store) a Singular al hacer seguimiento de suscripciones—usa customRevenue() sin recibos
  • Filtrar las compras restauradas: Envía únicamente los eventos donde isRestored sea false para evitar informes de ingresos duplicados
  • Verificación en el backend: Verifica siempre las compras en tu backend seguro antes de enviar los eventos a Singular
  • Confirmación oportuna: Confirma las compras de Google Play dentro de 3 días para evitar reembolsos automáticos
  • Consultar en resume: Llama a queryPurchasesAsync() en onResume() para detectar las renovaciones ocurridas mientras la app estaba cerrada
  • Nombres de eventos coherentes: Usa los nombres de eventos estándar de Singular (sng_subscribe, subscription_renewed) para informes unificados
  • Incluir metadatos: Agrega atributos como subscription_tier, billing_period y product_id para un análisis detallado

Pruebas y validación

Lista de verificación de pruebas

  1. Configuración del entorno de pruebas: Usa cuentas de sandbox/prueba tanto para Google Play como para App Store
  2. Nueva suscripción: Verifica que el evento sng_subscribe se envíe correctamente con el importe y la moneda adecuados
  3. Flujo de prueba: Prueba los eventos de inicio y fin de prueba sin ingresos
  4. Gestión de renovaciones: Confirma que las renovaciones activan los eventos subscription_renewed con los ingresos correctos
  5. Seguimiento de cancelaciones: Verifica que los eventos de cancelación se disparen cuando los usuarios cancelen las suscripciones
  6. Filtrado de restauraciones: Asegúrate de que las compras restauradas no envíen eventos duplicados
  7. Dashboard de Singular: Comprueba que los eventos aparezcan en Singular con la atribución correcta
  8. Entre dispositivos: Prueba la sincronización de suscripciones en varios dispositivos

Problemas comunes

  • Eventos faltantes: Verifica que la conexión del billing client esté activa y que queryPurchasesAsync() se llame al reanudar la app
  • Eventos duplicados: Comprueba que las compras restauradas se filtren correctamente con la verificación de isRestored
  • Ingresos incorrectos: Asegúrate de que la extracción del precio desde ProductDetails divida correctamente los micros entre 1,000,000
  • Problemas de atribución: Confirma que los identificadores de dispositivo (IDFA/GAID) se incluyan correctamente en las solicitudes S2S
  • Fallos de confirmación: Verifica que la validación del recibo en el backend se complete antes de la confirmación

Recursos de soporte: Para obtener ayuda con la resolución de problemas, contacta al soporte de Singular con la versión del SDK, los detalles de la plataforma, ejemplos de payloads de eventos y capturas de pantalla del dashboard de Singular que muestren el problema.