Skip to content

Troubleshooting

Learn how to diagnose and fix common issues with Checkout Drop-in.

Quick diagnostics

If you're experiencing issues with Drop-in, start with these quick diagnostic checks:

import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.checkoutdropin.types.CheckoutDropInConfig
import android.util.Log

// Drop-in diagnostic helper
fun diagnoseDropIn(
    context: Context,
    session: SessionConfig?,
    config: CheckoutDropInConfig
) {
    Log.d("DropInDiagnostics", "=== Checkout Drop-in diagnostics ===")
    
    // Check SDK import
    Log.d("DropInDiagnostics", "SDK imported: ${CheckoutDropIn::class.java.name}")
    
    // Check session data
    Log.d("DropInDiagnostics", "Session ID: ${if (session?.sessionId != null) "Present" else "Missing"}")
    Log.d("DropInDiagnostics", "HMAC key: ${if (session?.hmacKey != null) "Present" else "Missing"}")
    Log.d("DropInDiagnostics", "Allowed funding types: ${session?.allowedFundingTypes}")
    
    // Check environment
    Log.d("DropInDiagnostics", "Environment: ${config.environment}")
    Log.d("DropInDiagnostics", "Owner ID: ${config.ownerId}")
    Log.d("DropInDiagnostics", "Owner type: ${config.ownerType}")
    
    // Check device info
    Log.d("DropInDiagnostics", "Android version: ${Build.VERSION.SDK_INT}")
    Log.d("DropInDiagnostics", "Device: ${Build.MANUFACTURER} ${Build.MODEL}")
    Log.d("DropInDiagnostics", "Screen density: ${context.resources.displayMetrics.density}")
    
    // Check network connectivity
    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val network = connectivityManager.activeNetwork
    val capabilities = connectivityManager.getNetworkCapabilities(network)
    Log.d("DropInDiagnostics", "Network available: ${capabilities != null}")
    
    Log.d("DropInDiagnostics", "===================================")
}

Common issues

Drop-in not rendering

Symptoms:

  • The Drop-in component doesn't appear in your Compose UI.
  • No payment methods are visible.
  • There are no errors in Logcat.
CauseSolution
Compose recomposition issue.Ensure session data is loaded before rendering CheckoutDropIn. Use LaunchedEffect to fetch session data and conditional rendering.
Session data invalid.Verify session data includes sessionId, hmacKey, and allowedFundingTypes. Check backend session creation logs.
No payment methods enabled.Check allowedFundingTypes in your session. At least one payment method must be enabled in the Unity Portal.
Incorrect configuration.Verify environment, ownerId, and ownerType are set correctly.
ProGuard/R8 stripping SDK classes.Add ProGuard rules to keep SDK classes (see ProGuard section below).

Diagnostic steps:

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import android.util.Log
import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.checkoutdropin.types.CheckoutDropInConfig
import com.pxp.checkout.components.checkoutdropincomponent.CheckoutDropInComponent

@Composable
fun DiagnoseRenderingIssue() {
    val context = LocalContext.current
    var session by remember { mutableStateOf<SessionConfig?>(null) }
    var checkoutDropInComponent by remember { mutableStateOf<CheckoutDropInComponent?>(null) }
    var diagnostics by remember { mutableStateOf("") }
    
    LaunchedEffect(Unit) {
        diagnostics += "Step 1: Fetching session...\n"
        
        try {
            val response = apiClient.post("/api/create-session") {
                contentType(ContentType.Application.Json)
            }
            
            if (response.status.value == 200) {
                val result = response.body<SessionResponse>()
                if (result.success && result.data != null) {
                    session = result.data
                    diagnostics += "Step 2: Session fetched successfully\n"
                    diagnostics += "Session ID: ${result.data.sessionId}\n"
                } else {
                    diagnostics += "ERROR: Session fetch failed: ${result.error}\n"
                }
            } else {
                diagnostics += "ERROR: HTTP ${response.status.value}\n"
            }
        } catch (e: Exception) {
            diagnostics += "ERROR: ${e.message}\n"
            Log.e("DropIn", "Session fetch error", e)
        }
        
        // Step 3: Check session data
        session?.let { sessionConfig ->
            diagnostics += "Step 3: Validating session data...\n"
            
            if (sessionConfig.sessionId.isNullOrEmpty()) {
                diagnostics += "ERROR: Session ID missing\n"
            } else {
                diagnostics += "Session ID present\n"
            }
            
            if (sessionConfig.allowedFundingTypes == null) {
                diagnostics += "ERROR: No allowed funding types\n"
            } else {
                var paymentMethodCount = 0
                if (sessionConfig.allowedFundingTypes.cards != null) paymentMethodCount++
                if (sessionConfig.allowedFundingTypes.wallets?.paypal != null) paymentMethodCount++
                if (sessionConfig.allowedFundingTypes.wallets?.googlePay != null) paymentMethodCount++
                
                if (paymentMethodCount == 0) {
                    diagnostics += "ERROR: No payment methods enabled\n"
                } else {
                    diagnostics += "$paymentMethodCount payment method(s) available\n"
                }
            }
        }
    }
    
    // Initialize Drop-in once session is loaded
    LaunchedEffect(session) {
        session?.let { sessionConfig ->
            val checkoutDropIn = CheckoutDropIn.initialize(
                context = context,
                config = CheckoutDropInConfig(
                    environment = Environment.TEST,
                    session = sessionConfig,
                    ownerType = "MerchantGroup",
                    ownerId = "MERCHANT-1",
                    transactionData = DropInTransactionData(
                        currency = "GBP",
                        amount = 1.00,
                        entryType = EntryType.Ecom,
                    intent = DropInTransactionIntentData(
                        card = IntentType.Authorisation
                    ),
                    merchant = "Demo Store",
                    merchantTransactionId = "test-${System.currentTimeMillis()}",
                        merchantTransactionDate = { Instant.now().toString() }
                    ),
                    onSuccess = { result ->
                        Log.d("DropIn", "Success: ${result.systemTransactionId}")
                    },
                    onError = { error ->
                        Log.e("DropIn", "Error: ${error.message}", error)
                    }
                )
            )
            checkoutDropInComponent = checkoutDropIn.create()
        }
    }
    
    Column {
        Text(diagnostics)
        checkoutDropInComponent?.Content(modifier = Modifier.fillMaxWidth())
    }
}

Session expired errors

Symptoms:

  • A "Session expired" error message is displayed.
  • The drop-in loads but payment fails immediately.
  • Logcat shows session timeout errors.
CauseSolution
Session timeout exceeded.Sessions expire based on the sessionTimeout value (default = 120 minutes). Create a new session if expired.
Clock skewEnsure that the server and device clocks are synchronised.
Session reusedSessions are single-use. Create a new session for each checkout attempt.
Invalid HMAC signatureVerify that the HMAC key matches between session creation and SDK initialisation.

Solution: Implement session refresh

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.launch
import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.checkoutdropin.types.CheckoutDropInConfig
import com.pxp.checkout.components.checkoutdropincomponent.CheckoutDropInComponent

@Composable
fun CheckoutScreenWithSessionRefresh() {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()
    var session by remember { mutableStateOf<SessionConfig?>(null) }
    var checkoutDropInComponent by remember { mutableStateOf<CheckoutDropInComponent?>(null) }
    var refreshKey by remember { mutableStateOf(0) }
    
    // Fetch session
    LaunchedEffect(refreshKey) {
        try {
            session = fetchSessionFromBackend()
        } catch (e: Exception) {
            Log.e("Checkout", "Failed to fetch session", e)
            Toast.makeText(
                context,
                "Failed to load checkout. Please try again.",
                Toast.LENGTH_LONG
            ).show()
        }
    }
    
    // Initialize Drop-in once session is loaded
    LaunchedEffect(session) {
        session?.let { sessionConfig ->
            val checkoutDropIn = CheckoutDropIn.initialize(
                context = context,
                config = CheckoutDropInConfig(
                    environment = Environment.LIVE,
                    session = sessionConfig,
                    ownerType = "MerchantGroup",
                    ownerId = "MERCHANT-1",
                    transactionData = DropInTransactionData(
                        currency = "GBP",
                        amount = 99.99,
                        entryType = EntryType.Ecom,
                            intent = DropInTransactionIntentData(
                                card = IntentType.Authorisation
                            ),
                            merchant = "Demo Store",
                            merchantTransactionId = UUID.randomUUID().toString(),
                        merchantTransactionDate = { Instant.now().toString() }
                    ),
                    onSuccess = { result ->
                        coroutineScope.launch {
                            verifyPaymentOnBackend(result)
                        }
                    },
                    onError = { error ->
                        // Handle session expiry
                        if (error.message?.contains("expired", ignoreCase = true) == true ||
                            error.message?.contains("session", ignoreCase = true) == true) {
                            Log.w("Checkout", "Session expired, refreshing...")
                            
                            Toast.makeText(
                                context,
                                "Session expired. Refreshing checkout...",
                                Toast.LENGTH_SHORT
                            ).show()
                            
                            // Trigger session refresh
                            refreshKey++
                        } else {
                            Log.e("Checkout", "Payment error", error)
                            Toast.makeText(
                                context,
                                "Payment failed: ${error.message}",
                                Toast.LENGTH_LONG
                            ).show()
                        }
                    }
                )
            )
            checkoutDropInComponent = checkoutDropIn.create()
        }
    }
    
    // Render Drop-in
    checkoutDropInComponent?.Content(modifier = Modifier.fillMaxWidth())
}

suspend fun fetchSessionFromBackend(): SessionConfig {
    val response = apiClient.post("/api/create-session") {
        contentType(ContentType.Application.Json)
    }
    
    if (response.status.value != 200) {
        throw Exception("Failed to create session: HTTP ${response.status.value}")
    }
    
    val result = response.body<SessionResponse>()
    if (!result.success || result.data == null) {
        throw Exception("Session creation failed: ${result.error}")
    }
    
    return result.data
}

Payment method not appearing

Symptoms:

  • An expected payment method isn't shown.
  • Only the card payment method is visible.
  • Google Pay doesn't appear even though it's enabled.
Payment methodCommon causesSolutions
CardsCards aren't enabled in the Unity Portal or are missing from the session configuration.Enable the Card service in the Unity Portal and verify that the session includes allowedFundingTypes.cards.
PayPalPayPal onboarding wasn't completed or is missing from session configuration.Complete PayPal onboarding in Unity Portal and verify that the session includes allowedFundingTypes.wallets.paypal.
Google PayGoogle Play Services not available, no cards in Google Wallet, or unsupported device.Verify Google Play Services is installed, add test cards to Google Wallet, and test on a physical device or emulator with Play Services.

Diagnostic steps:

import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability

fun diagnosePaymentMethodVisibility(
    context: Context,
    session: SessionConfig?
) {
    Log.d("PaymentMethods", "=== Payment method diagnostics ===")
    
    val fundingTypes = session?.allowedFundingTypes
    Log.d("PaymentMethods", "Session funding types: $fundingTypes")
    
    // Check cards
    if (fundingTypes?.cards != null) {
        Log.d("PaymentMethods", "Cards enabled: ${fundingTypes.cards}")
    } else {
        Log.w("PaymentMethods", "Cards not enabled in session")
    }
    
    // Check PayPal
    if (fundingTypes?.wallets?.paypal != null) {
        Log.d("PaymentMethods", "PayPal enabled: ${fundingTypes.wallets.paypal}")
    } else {
        Log.w("PaymentMethods", "PayPal not enabled in session")
    }
    
    // Check Google Pay
    if (fundingTypes?.wallets?.googlePay != null) {
        Log.d("PaymentMethods", "Google Pay configuration present")
        
        // Check Google Play Services availability
        val apiAvailability = GoogleApiAvailability.getInstance()
        val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
        
        if (resultCode == ConnectionResult.SUCCESS) {
            Log.d("PaymentMethods", "Google Play Services available")
        } else {
            Log.w("PaymentMethods", "Google Play Services not available: $resultCode")
        }
    } else {
        Log.w("PaymentMethods", "Google Pay not configured in session")
    }
    
    // Check device info
    Log.d("PaymentMethods", "Android API level: ${Build.VERSION.SDK_INT}")
    Log.d("PaymentMethods", "Device: ${Build.MANUFACTURER} ${Build.MODEL}")
    
    Log.d("PaymentMethods", "===================================")
}

Google Pay not working

Symptoms:

  • Google Pay button doesn't appear.
  • Google Pay sheet doesn't open when tapped.
  • Payment fails with Google Pay errors.
CauseSolution
Google Play Services not installed.Install Google Play Services on the device or use an emulator with Play Services. Complete Google Pay onboarding in Unity Portal and verify that the session includes allowedFundingTypes.wallets.googlePay.
No cards in Google Wallet.Add test cards to Google Wallet manually.
Minimum API level not met.Google Pay requires Android 7.0 (API level 24) or higher.
Merchant ID missing or invalid.Add a valid Google Pay merchant ID in production (optional for testing).
Device not supported.Test on a physical device or emulator with Google Play.

Solution:

import com.google.android.gms.wallet.PaymentsClient
import com.google.android.gms.wallet.Wallet
import com.google.android.gms.wallet.IsReadyToPayRequest
import org.json.JSONObject
import org.json.JSONArray

fun checkGooglePayAvailability(context: Context) {
    val paymentsClient: PaymentsClient = Wallet.getPaymentsClient(
        context,
        Wallet.WalletOptions.Builder()
            .setEnvironment(WalletConstants.ENVIRONMENT_TEST)
            .build()
    )
    
    val request = IsReadyToPayRequest.fromJson(
        JSONObject().apply {
            put("apiVersion", 2)
            put("apiVersionMinor", 0)
            put("allowedPaymentMethods", JSONArray().apply {
                put(JSONObject().apply {
                    put("type", "CARD")
                    put("parameters", JSONObject().apply {
                        put("allowedAuthMethods", JSONArray().apply {
                            put("PAN_ONLY")
                            put("CRYPTOGRAM_3DS")
                        })
                        put("allowedCardNetworks", JSONArray().apply {
                            put("VISA")
                            put("MASTERCARD")
                        })
                    })
                })
            })
        }.toString()
    )
    
    paymentsClient.isReadyToPay(request).addOnCompleteListener { task ->
        if (task.isSuccessful) {
            Log.d("GooglePay", "Google Pay is available: ${task.result}")
        } else {
            Log.w("GooglePay", "Google Pay check failed", task.exception)
        }
    }
}

Backend verification failing

Symptoms:

  • Frontend onSuccess fires but backend verification fails.
  • Orders aren't being fulfilled.
  • "Payment verification failed" errors.
CauseSolution
Webhook not configured.Set up a webhook URL in the Unity Portal and implement a webhook handler on your backend.
Webhook authentication failing.Verify your webhook signature/authentication. Check your HMAC implementation.
Race condition (GET before webhook).Implement a fallback to the Get transaction details API if the webhook hasn't arrived yet.
Transaction ID mismatchVerify that the systemTransactionId and merchantTransactionId match database records.

Solution: Robust backend verification

// Backend webhook handler (Node.js/Express example)
app.post('/webhooks/pxp', async (req, res) => {
  try {
    const events = req.body;
    
    // Verify webhook authenticity using HMAC
    if (!verifyWebhookSignature(req)) {
      console.error('Invalid webhook signature');
      return res.status(401).json({ error: 'Unauthorised' });
    }
    
    for (const event of events) {
      if (event.eventCategory === 'Transaction') {
        const txn = event.eventData;
        
        console.log('Processing transaction:', txn.systemTransactionId);
        
        // Idempotency check
        const existing = await db.transactions.findOne({
          systemTransactionId: txn.systemTransactionId
        });
        
        if (existing) {
          console.log('Transaction already processed, skipping');
          continue;
        }
        
        // Verify transaction state
        if (txn.state === 'Authorised' || txn.state === 'Captured') {
          // Find order by merchant transaction ID
          const order = await db.orders.findOne({
            transactionId: txn.merchantTransactionId
          });
          
          if (!order) {
            console.error('Order not found:', txn.merchantTransactionId);
            continue;
          }
          
          // Verify amount matches
          const transactionAmount = txn.amounts?.transactionValue || txn.amount;
          if (Math.abs(transactionAmount - order.amount) > 0.01) {
            console.error('Amount mismatch:', {
              expected: order.amount,
              actual: transactionAmount
            });
            continue;
          }
          
          // Mark transaction as processed
          await db.transactions.create({
            systemTransactionId: txn.systemTransactionId,
            merchantTransactionId: txn.merchantTransactionId,
            amount: transactionAmount,
            state: txn.state,
            processedAt: new Date()
          });
          
          // Fulfill order
          await fulfillOrder(order.id, txn.systemTransactionId);
          
          console.log('Order fulfilled:', order.id);
        }
      }
    }
    
    // Always return success
    res.json({ state: 'Success' });
  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

Android-specific issues

ProGuard/R8 stripping SDK classes

Symptoms:

  • Drop-in works in debug builds but crashes in release builds.
  • ClassNotFoundException or NoSuchMethodException in production.

Solution: Add ProGuard rules

Create or update proguard-rules.pro:

# Keep PXP SDK classes
-keep class com.pxp.checkout.** { *; }
-keepclassmembers class com.pxp.checkout.** { *; }

# Keep data classes used for serialization
-keep @kotlinx.serialization.Serializable class ** {
    *;
}

# Keep Kotlin metadata
-keepattributes *Annotation*
-keepattributes Signature
-keepattributes InnerClasses
-keepattributes EnclosingMethod

# Keep Compose
-keep class androidx.compose.** { *; }
-keepclassmembers class androidx.compose.** { *; }

Memory leaks

Symptoms:

  • App performance degrades over time.
  • Android Studio Profiler shows growing memory usage.
  • Out of memory crashes after multiple checkout attempts.

Solution: Proper lifecycle management

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.checkoutdropin.types.CheckoutDropInConfig
import com.pxp.checkout.components.checkoutdropincomponent.CheckoutDropInComponent

@Composable
fun CheckoutScreen() {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    var sessionData by remember { mutableStateOf<SessionConfig?>(null) }
    var isActive by remember { mutableStateOf(true) }
    
    // Monitor lifecycle
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_PAUSE -> {
                    isActive = false
                    Log.d("Checkout", "Paused")
                }
                Lifecycle.Event.ON_RESUME -> {
                    isActive = true
                    Log.d("Checkout", "Resumed")
                }
                Lifecycle.Event.ON_DESTROY -> {
                    isActive = false
                    sessionData = null
                    Log.d("Checkout", "Destroyed")
                }
                else -> {}
            }
        }
        
        lifecycleOwner.lifecycle.addObserver(observer)
        
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
            // Clean up resources
            sessionData = null
        }
    }
    
    // Only render if active
    var checkoutDropInComponent by remember { mutableStateOf<CheckoutDropInComponent?>(null) }
    
    LaunchedEffect(isActive, sessionData) {
        if (isActive && sessionData != null) {
            val checkoutDropIn = CheckoutDropIn.initialize(
                context = context,
                config = CheckoutDropInConfig(
                    // ... config
                )
            )
            checkoutDropInComponent = checkoutDropIn.create()
        } else {
            checkoutDropInComponent = null
        }
    }
    
    // Render component if available
    checkoutDropInComponent?.Content(modifier = Modifier.fillMaxWidth())
}

Keyboard covering input fields

Symptoms:

  • Keyboard covers the submit button.
  • User can't see what they're typing.
  • Layout doesn't adjust when keyboard appears.

Solution: Proper window insets handling

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.checkoutdropin.types.CheckoutDropInConfig
import com.pxp.checkout.components.checkoutdropincomponent.CheckoutDropInComponent

@Composable
fun CheckoutScreen() {
    val context = LocalContext.current
    val scrollState = rememberScrollState()
    
    Scaffold(
        modifier = Modifier
            .fillMaxSize()
            .imePadding() // Adjust for keyboard
    ) { paddingValues ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues)
                .verticalScroll(scrollState)
                .padding(16.dp)
        ) {
            // Your checkout content
            var checkoutDropInComponent by remember { mutableStateOf<CheckoutDropInComponent?>(null) }
            
            LaunchedEffect(Unit) {
                val checkoutDropIn = CheckoutDropIn.initialize(
                    context = context,
                    config = CheckoutDropInConfig(
                        // ... config
                    )
                )
                checkoutDropInComponent = checkoutDropIn.create()
            }
            
            checkoutDropInComponent?.Content(modifier = Modifier.fillMaxWidth())
        }
    }
}

In AndroidManifest.xml:

<activity
    android:name=".CheckoutActivity"
    android:windowSoftInputMode="adjustResize">
</activity>

Error codes reference

For a complete list of error codes and their meanings, see the Error handling guide.

Getting additional help

If you're still experiencing issues, try these troubleshooting steps.

Enable debug logging

Add detailed logging using Logcat:

CheckoutDropInConfig(
    // ... other config
    analyticsEvent = { event ->
        if (BuildConfig.DEBUG) {
            Log.d("DropInAnalytics", """
                Event: ${event.eventName}
                Session ID: ${event.sessionId}
                Timestamp: ${event.timestamp}
                Data: ${event.toMap()}
            """.trimIndent())
        }
    }
)

Collect diagnostic information

When contacting support, include:

import android.content.Context
import android.os.Build
import org.json.JSONObject

fun collectDiagnosticInfo(context: Context, session: SessionConfig?): String {
    val info = JSONObject().apply {
        // Device information
        put("manufacturer", Build.MANUFACTURER)
        put("model", Build.MODEL)
        put("androidVersion", Build.VERSION.SDK_INT)
        put("androidRelease", Build.VERSION.RELEASE)
        
        // Screen information
        val metrics = context.resources.displayMetrics
        put("screenWidth", metrics.widthPixels)
        put("screenHeight", metrics.heightPixels)
        put("screenDensity", metrics.density)
        
        // App information
        val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
        put("appVersion", packageInfo.versionName)
        put("appVersionCode", packageInfo.versionCode)
        
        // SDK information (update these placeholders with your actual values)
        put("sdkVersion", "1.0.0") // TODO: Replace with actual SDK version from your build
        put("environment", "test") // TODO: Replace with actual environment from your config
        
        // Session info (sanitised)
        put("sessionIdPresent", session?.sessionId != null)
        put("allowedFundingTypes", session?.allowedFundingTypes.toString())
        
        // Timestamp
        put("timestamp", Instant.now().toString())
    }
    
    val diagnostics = info.toString(2)
    Log.d("Diagnostics", diagnostics)
    
    // Copy to clipboard
    val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    val clip = ClipData.newPlainText("Diagnostic Info", diagnostics)
    clipboard.setPrimaryClip(clip)
    
    return diagnostics
}

When contacting support, always include your merchant ID, environment (test or production), Android version, device model, and any relevant error messages or Logcat logs.