Klarna
Klarna renders its native payment widget inside your screen. Your Pay button submits: /validate runs first, then Klarna's own authorization inside the same call.
Setup
| Artifact | eu.purse:headless-checkout-klarna (brings the Klarna Mobile SDK) |
| Repository | Klarna's SDK is not on Maven Central: add https://x.klarnacdn.net/mobile-sdk/ |
| Dashboard | Klarna installments activated on the entity. The Klarna client token reaches the app through /init |
| Manifest | A deep link on your checkout Activity, with launchMode="singleTask" |
| Keys | None in the app |
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://x.klarnacdn.net/mobile-sdk/")
}
}
// app/build.gradle.kts
dependencies {
implementation("eu.purse:headless-checkout-klarna:${SDK_VERSION}")
}
The return deep link
Klarna's widget can open a banking or identity app and must return to the same Activity instance that hosts it. Declare the deep link on your checkout Activity; singleTask guarantees no second instance is created:
<activity android:name=".CheckoutActivity" android:launchMode="singleTask" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="${applicationId}.klarna" android:host="return" />
</intent-filter>
</activity>
rememberPaymentElement hands Klarna "{packageName}.klarna://return" by default, which matches this filter. If you declare another scheme, pass it as klarnaReturnUrl.
Render and pay
Create the element when the shopper selects Klarna. Once PaymentElementUi has attached the widget's container, the SDK runs POST /init to obtain the client token, then loads the widget.
val element = rememberPaymentElement(checkout, klarnaMethod, listener) // klarnaReturnUrl = "{packageName}.klarna://return"
LaunchedEffect(klarnaMethod.id) { checkout.setPrimary(klarnaMethod) }
PaymentElementUi(element, Modifier.fillMaxWidth()) // the native widget, sized by Klarna
Button(onClick = { element.submit() }, enabled = element.ready) { Text("Pay with Klarna") }
element.ready turns true once the widget loaded. submit() runs /validate, and only if it succeeds asks Klarna to authorize (and finalize when Klarna requires it). If /validate fails, Klarna is never asked.
Outcomes
| Outcome | When |
|---|---|
Completed | /validate succeeded and Klarna authorized |
ReturnedFromRedirect | Klarna sent the shopper to an external bank or identity step and they came back. Not an outcome. Show a processing state; your backend confirms the payment |
Canceled | Klarna declined the authorization, or the external step was dismissed. Not an error |
Failed | The widget failed to load, /init or /validate failed, or Klarna reported an outright error |
A Failed before any submit (the widget did not load) is not a failed payment: the method is disabled with PARTNER_UI_FATAL_ERROR, leave the shopper able to pick another method.
Instalment simulation
A Klarna method flagged simulable can show the schedule before the shopper selects it: Instalment simulation.
Troubleshooting
| Symptom | Cause |
|---|---|
| The widget shows a generic "see our terms" card, no instalment breakdown | Klarna offers no instalment product for this amount and market. A Klarna-side configuration, not an SDK issue |
Failed with API_ERROR, message mentions no klarna_client_token | /init did not create a Klarna session for this item. Check the method's configuration |
Failed with PARTNER_UI_ERROR mentioning ERROR_INVALID_CLIENT_TOKEN | A stale token: the session expired between selection and payment. Refresh the session |
Canceled right after tapping Pay | /validate succeeded but Klarna declined the authorization |
| After a bank app step, the widget is gone and a new screen instance opened | Missing launchMode="singleTask" or a scheme that does not match klarnaReturnUrl |
Gradle cannot resolve com.klarna.mobile:sdk | Klarna's Maven repository is missing from settings.gradle.kts |
The method is listed but has no UI, submit() fails with METHOD_NOT_IMPLEMENTED | The -klarna artifact is missing. See Payment methods |