Quick diagnosis
Symptom Likely Cause Solution Compose version conflict Compiler version mismatch Align kotlinCompilerExtensionVersion Checkout stays in Loading Invalid/expired client token Check Logcat for errors, regenerate token Payment methods empty Dashboard misconfiguration Verify Dashboard settings and client session fields Recomposition causes re-init Controller created outside remember Use rememberPrimerCheckoutController() State not updating in UI Not using lifecycle-aware collect Use collectAsStateWithLifecycle()
Installation issues
Compose version conflict
Cause: Build fails with Compose compiler version mismatch.
Solution: Ensure your Compose compiler version is compatible:
android {
composeOptions {
kotlinCompilerExtensionVersion = "1.5.8"
}
}
Runtime issues
Checkout stays in Loading state
Cause: PrimerCheckoutState.Loading never transitions to Ready.
Possible causes:
Invalid or expired client token
Network connectivity issues
Incorrect API key in client session creation
Solution: Check Logcat for error messages from the SDK. Ensure your client token is fresh and generated correctly.
Payment methods not showing
Cause: PrimerPaymentMethods shows an empty list.
Possible causes:
No payment methods configured in Primer Dashboard
Client session missing required fields
Payment methods not enabled for the currency/country
Solution: Verify your Primer Dashboard configuration and ensure your client session includes the correct currencyCode and countryCode.
3DS challenge not completing
Cause: Payment gets stuck during 3DS authentication.
Solution: Ensure your Activity has android:configChanges="orientation|screenSize" to prevent the WebView from being destroyed during configuration changes.
Compose-specific issues
Recomposition causes re-initialization
Cause: Checkout reinitializes on every recomposition.
Solution: Use rememberPrimerCheckoutController() which survives recomposition:
// Correct
val checkout = rememberPrimerCheckoutController (clientToken)
// Incorrect - creates new controller on recomposition
val checkout = PrimerCheckoutController (clientToken)
State not updating in UI
Cause: UI doesn’t reflect state changes from the SDK.
Solution: Use collectAsStateWithLifecycle():
val state by checkout.state. collectAsStateWithLifecycle ()
Validation vs payment errors
Understanding the difference helps with proper error handling:
Error Type When It Occurs How It’s Handled Validation errors During input (invalid format, missing fields) Handled automatically by input components; prevents form submission Payment failures After form submission (declined card, network issues) Requires explicit handling with error container or custom code
Don’t confuse these two error types. Validation errors prevent form submission and are shown inline. Payment failures occur after the form is submitted and require explicit handling.
Debugging tips
Log checkout state changes
LaunchedEffect (checkout) {
checkout.state. collect { state ->
Log. d ( "PrimerCheckout" , "State: $state " )
}
}
Log all checkout events
PrimerCheckoutSheet (
checkout = checkout,
onEvent = { event ->
Log. d ( "PrimerCheckout" , "Event: $event " )
when (event) {
is PrimerCheckoutEvent.Success -> { }
is PrimerCheckoutEvent.Failure -> {
Log. e ( "PrimerCheckout" , "diagnosticsId: ${ event.error.diagnosticsId } " )
}
}
},
)
Getting help
When contacting Primer support, include:
The diagnosticsId from any error callbacks
Your Android API level, Compose version, and SDK version
Steps to reproduce the issue
onEvent = { event ->
when (event) {
is PrimerCheckoutEvent.Failure -> {
Log. e ( "Checkout" , "diagnosticsId: ${ event.error.diagnosticsId } " )
}
}
}
See also
Events guide Event handling patterns