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.
- Crear productos de suscripción: Define los niveles de suscripción, los períodos de facturación y los precios en Play Console
- Configurar ofertas: Configura planes base, tokens de oferta y precios promocionales
- 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.
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.
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.
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.
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.
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.
- Crear grupos de suscripción: Organiza las suscripciones en grupos según los niveles de acceso
- Definir productos de suscripción: Configura los niveles de suscripción, las duraciones y los precios
- Configurar ofertas: Crea ofertas introductorias, ofertas promocionales y códigos de suscripción
- 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.
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.
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
// 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
// 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
// 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
// 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
// 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
// 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 -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:
- Crear una cuenta de RevenueCat: Configura tu app en el dashboard de RevenueCat
- Configurar la integración con Singular: Agrega el SDK key de Singular en la configuración de integraciones de RevenueCat
- Mapear eventos: Configura qué eventos de RevenueCat deben enviarse a Singular
- 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:
- Crear una cuenta de Adapty: Regístrate y configura tu app en Adapty
- Habilitar la integración con Singular: Ve a las integraciones y agrega las credenciales de Singular
- Configurar el mapeo de eventos: Selecciona qué eventos de suscripción reenviar
- 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:
- Integrar el SDK de Singular: Agrega el SDK de Singular a tu app para el seguimiento de sesiones y eventos
- 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
- 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:
- Integrar Singular en tus apps móviles: Agrega el SDK de Singular a tus apps móviles
- Configurar un evento in-app: Envía un evento in-app a Singular, por ejemplo login, o cualquier otro evento que incluya el CUID
- Configurar la Singular Partner Configuration: Configura la Singular partner configuration para Xsolla
- 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.
// 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.
// 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.
// 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).
// 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
- Configuración del entorno de pruebas: Usa cuentas de sandbox/prueba tanto para Google Play como para App Store
- Nueva suscripción: Verifica que el evento sng_subscribe se envíe correctamente con el importe y la moneda adecuados
- Flujo de prueba: Prueba los eventos de inicio y fin de prueba sin ingresos
- Gestión de renovaciones: Confirma que las renovaciones activan los eventos subscription_renewed con los ingresos correctos
- Seguimiento de cancelaciones: Verifica que los eventos de cancelación se disparen cuando los usuarios cancelen las suscripciones
- Filtrado de restauraciones: Asegúrate de que las compras restauradas no envíen eventos duplicados
- Dashboard de Singular: Comprueba que los eventos aparezcan en Singular con la atribución correcta
- 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.