Skip to content

Analytics

Get actionable, trackable data instantly to drive better decisions and performance.

Overview

Analytics events are structured data objects that get automatically triggered when significant actions or states occur within components. These allow you to monitor every aspect of the payment journey.

Analytics events allow you to:

  • Gain transparency with native transaction tracking in PXP reports.
  • Optimise conversion rates and reduce drop-offs, thanks to actionable insights.
  • Feed real-time data into your analytics and CRM systems.

Consume an event

Analytics events should be consumed in the PxpSdkConfig during SDK initialisation. For example:

val sdkConfig = PxpSdkConfig(
    environment = Environment.TEST,
    session = SessionConfig(
        sessionId = "your_session_id",
        sessionData = "your_session_data"
    ),
    merchantShopperId = "123",
    ownerId = "UnityGroup",
    ownerType = "MerchantGroup",
    transactionData = TransactionData(
        currency = CurrencyType.USD,
        amount = payAmount,
        entryType = EntryType.Ecom,
        intent = IntentType.Authorisation,
        merchantTransactionId = "9af8af33-59d5-432d-bd35-96124930ec9f",
        merchantTransactionDate = { Instant.now().toString() }
    ),
    analyticsEvent = { analyticsEvent ->
        when (analyticsEvent) {
            is ComponentLifecycleAnalyticsEvent -> {
                // Track component lifecycle (mount/unmount)
                Log.d("Analytics", "Component ${analyticsEvent.eventType}: ${analyticsEvent.componentId}")
                analytics.track(analyticsEvent.eventName, analyticsEvent.toMap())
            }
            is ErrorTracker.ComponentErrorEvent -> {
                // Log component errors for debugging
                Log.e("Analytics", "Component error: ${analyticsEvent.message}")
                crashlytics.recordException(Exception(analyticsEvent.message))
            }
            is ErrorTracker.ComponentValidationEvent -> {
                // Track validation events
                analytics.track("component_validation", mapOf(
                    "component_id" to analyticsEvent.componentId,
                    "is_valid" to analyticsEvent.isValid,
                    "validation_message" to analyticsEvent.validationMessage
                ))
            }
            else -> {
                // Handle other analytics events
                Log.d("Analytics", "Event: ${analyticsEvent.eventName}")
                analytics.track(analyticsEvent.eventName, analyticsEvent.toMap())
            }
        }
    }
)

val pxpCheckout = PxpCheckout.builder()
    .withConfig(sdkConfig)
    .withContext(this)
    .withDebugMode(true)
    .build()

Available analytics events

The Android SDK provides several types of analytics events:

Component lifecycle events

Track when components are mounted and unmounted in your application:

class ComponentLifecycleAnalyticsEvent(
    val eventType: LifecycleEventType, // MOUNT or UNMOUNT
    val componentId: String,
    sessionId: String,
    val componentType: String? = null
) : BaseAnalyticsEvent("ComponentLifecycleEvent", sessionId)

Component error events

Track errors that occur within components:

class ComponentErrorEvent(
    val code: String,
    val message: String,
    val componentId: String,
    sessionId: String
) : BaseAnalyticsEvent("ComponentErrorEvent", sessionId)

Component validation events

Track validation results for components:

class ComponentValidationEvent(
    val componentId: String,
    val isValid: Boolean,
    val validationMessage: String?,
    sessionId: String
) : BaseAnalyticsEvent("ComponentValidationEvent", sessionId)

Comprehensive analytics implementation

Here's a comprehensive example showing how to handle the available analytics events:

class AnalyticsManager {
    
    fun setupAnalyticsEventHandler(): (BaseAnalyticsEvent) -> Unit {
        return { analyticsEvent ->
            // Log all events for debugging (remove in production)
            Log.d("Analytics", "Event: ${analyticsEvent.eventName} at ${analyticsEvent.timestamp}")
            
            when (analyticsEvent) {
                // Component lifecycle events
                is ComponentLifecycleAnalyticsEvent -> {
                    when (analyticsEvent.eventType) {
                        LifecycleEventType.MOUNT -> {
                            trackComponentMount(analyticsEvent)
                        }
                        LifecycleEventType.UNMOUNT -> {
                            trackComponentUnmount(analyticsEvent)
                        }
                    }
                }
                
                // Error tracking
                is ErrorTracker.ComponentErrorEvent -> {
                    trackComponentError(analyticsEvent)
                    
                    // Send to crash reporting
                    crashlytics.recordException(Exception(analyticsEvent.message))
                }
                
                // Validation tracking
                is ErrorTracker.ComponentValidationEvent -> {
                    trackValidationEvent(analyticsEvent)
                }
                
                else -> {
                    // Send all events to your analytics service
                    sendToAnalyticsService(analyticsEvent)
                }
            }
        }
    }
    
    // Helper functions for tracking specific events
    private fun trackComponentMount(event: ComponentLifecycleAnalyticsEvent) {
        analytics.track("component_mounted", mapOf(
            "component_id" to event.componentId,
            "component_type" to event.componentType,
            "session_id" to event.sessionId,
            "timestamp" to event.timestamp
        ))
    }
    
    private fun trackComponentUnmount(event: ComponentLifecycleAnalyticsEvent) {
        analytics.track("component_unmounted", mapOf(
            "component_id" to event.componentId,
            "component_type" to event.componentType,
            "session_id" to event.sessionId,
            "timestamp" to event.timestamp
        ))
    }
    
    private fun trackComponentError(event: ErrorTracker.ComponentErrorEvent) {
        analytics.track("component_error", mapOf(
            "component_id" to event.componentId,
            "error_code" to event.code,
            "error_message" to event.message,
            "session_id" to event.sessionId
        ))
    }
    
    private fun trackValidationEvent(event: ErrorTracker.ComponentValidationEvent) {
        analytics.track("component_validation", mapOf(
            "component_id" to event.componentId,
            "is_valid" to event.isValid,
            "validation_message" to event.validationMessage,
            "session_id" to event.sessionId
        ))
    }
    
    private fun sendToAnalyticsService(event: BaseAnalyticsEvent) {
        // Send to your analytics service (Firebase, Mixpanel, etc.)
        firebaseAnalytics.logEvent(event.eventName, Bundle().apply {
            event.toMap().forEach { (key, value) ->
                when (value) {
                    is String -> putString(key, value)
                    is Int -> putInt(key, value)
                    is Long -> putLong(key, value)
                    is Boolean -> putBoolean(key, value)
                    is Double -> putDouble(key, value)
                    else -> putString(key, value.toString())
                }
            }
        })
    }
}

Integration with analytics platforms

Firebase Analytics

private fun logToFirebase(event: BaseAnalyticsEvent) {
    val bundle = Bundle().apply {
        event.toMap().forEach { (key, value) ->
            when (value) {
                is String -> putString(key, value)
                is Int -> putInt(key, value)
                is Long -> putLong(key, value)
                is Boolean -> putBoolean(key, value)
                is Double -> putDouble(key, value)
                else -> putString(key, value.toString())
            }
        }
    }
    
    firebaseAnalytics.logEvent(event.eventName, bundle)
}

Custom analytics service

private fun logToCustomService(event: BaseAnalyticsEvent) {
    val payload = JSONObject().apply {
        put("event_name", event.eventName)
        put("session_id", event.sessionId)
        put("timestamp", event.timestamp.time)
        
        event.toMap().forEach { (key, value) ->
            put(key, value)
        }
    }
    
    // Send to your custom analytics endpoint
    analyticsApiClient.logEvent(payload)
}

Best practices

Event handling

  • Filter events: Only track events relevant to your business needs
  • Error handling: Always wrap analytics calls in try-catch blocks
  • Performance: Avoid blocking the main thread with analytics calls
  • Privacy: Ensure compliance with data protection regulations

Implementation tips

// Good: Non-blocking analytics
analyticsEvent = { event ->
    GlobalScope.launch(Dispatchers.IO) {
        try {
            handleAnalyticsEvent(event)
        } catch (e: Exception) {
            Log.e("Analytics", "Failed to track event", e)
        }
    }
}

// Good: Selective event tracking
analyticsEvent = { event ->
    when (event) {
        is ComponentLifecycleAnalyticsEvent -> {
            // Only track lifecycle for specific components
            if (event.componentType in criticalComponents) {
                trackEvent(event)
            }
        }
        is ErrorTracker.ComponentErrorEvent -> {
            // Always track errors
            trackEvent(event)
        }
        // Ignore other events
    }
}

Privacy considerations

  • Always obtain user consent before collecting analytics data
  • Anonymize sensitive information in event payloads
  • Provide opt-out mechanisms for analytics tracking
  • Comply with GDPR, CCPA, and other relevant regulations
// Example: Privacy-aware analytics
analyticsEvent = { event ->
    if (userHasConsentedToAnalytics()) {
        val sanitizedEvent = sanitizeEventData(event)
        trackEvent(sanitizedEvent)
    }
}

private fun sanitizeEventData(event: BaseAnalyticsEvent): BaseAnalyticsEvent {
    // Remove or hash sensitive data
    val sanitizedMap = event.toMap().filterKeys { key ->
        key !in sensitiveDataKeys
    }
    
    return event // Return sanitized version
}