Before you begin
This guide assumes that you know how to:Accept payments with Klarna
Prepare the client session
Klarna requires the following data to process a payment successfully. Pass the following data in the client session, or in the payment request (for manual payment creation).| Parameter Name | Required | Description |
|---|---|---|
| currencyCode | β | 3-letter currency code in ISO 4217 format, e.g. USD for US dollars |
| order β³ lineItems | β | Details of the line items of the order |
| order β³ countryCode | β | The country code of the user.Learn more about the supported countries and currencies by visiting Klarna Documentation. |
| customer β³ emailAddress | Customerβs email address. Pass this value to pre-fill the Klarna payment form. | |
| customer β³ mobileNumber | Customerβs mobile number (make sure it is the correct format for the country code). Pass this value to pre-fill the Klarna payment form. | |
| customer β³ shippingAddress | Customerβs shipping address. Pass this value to pre-fill the Klarna payment form. | |
| customer β³ billingAddress | Customerβs billing address. Pass this value to pre-fill the Klarna payment form. When passing customer.billingAddress in the client session, ensure that Klarna has payment method categories configured for the given address. If Klarna is not supported for the given billing address, no Klarna button will be rendered in the checkout | |
| paymentMethod β³ options β³ KLARNA β³ extraMerchantData | Extra Merchant Data (EMD) package supported by Klarna can be passed via the client session. Any JSON object that will be accepted by Klarna can be sent. Available schemas are documented here |
Prepare the SDK for payments
- Web
- iOS
- Android
- React Native
Handle payment method
Integration
The KlarnaPaymentMethodMethodManager is an SDK manager designed to streamline the headless integrations of the Klarna payment method that is comprised of an initial component/form for selecting a payment category and a redirect flow.To integrateKlarnaPaymentMethodMethodManager, follow these steps:- The PrimerHeadlessCheckout will help you configure the headless payment method and then start the payment process
-
When calling configure provide a callback function for
onAvailablePaymentMethodsLoadedas described here -
For the Klarna payment method,configure a handler for the
KLARNApayment method manager type and create a payment method manager as described here ` -
When the payment categories for the Klarna payment method have been retrieved,
onPaymentMethodCategoriesChangecallback will be called with a payload that will have a format as seen here -
You can use the payment categories retrieved through the
onPaymentMethodCategoriesChangecallback to create the custom component/form -
When the user has selected a payment category you should call the
renderCategoryfunction available in the manager. TherenderCategoryfunction requires, as parameters, the selected category id, a container id where to render the details about the payment category and anonHeightChangecallback that will be called when the payment category height has changed. You can use the height returned by theonHeightChangecallback to update the height of the container in which the payment category details will be displayed. -
After the user has selected a category and the details about this category have been displayed you can call the
startfunction provided by the payment method manager created at step 3
Create the PrimerHeadlessCheckout in order to interact with the Primer API
You can follow the steps described here in order to configure the PrimerHeadlessCheckout.Provide a custom handler for the KLARNA payment method manager
As described here the onAvailablePaymentMethodsLoad callback will be called with all available payment methods. Use this function and create a custom configuration for the KLARNA payment method manager type and afterwards start the payment process.JS
await headless.configure({
onAvailablePaymentMethodsLoad(paymentMethods) {
// Called when the available payment methods are retrieved
for (const paymentMethod of paymentMethods) {
// `type` is a unique ID representing the payment method
const { type, managerType } = paymentMethod
if (managerType === 'KLARNA') {
configureKlarnaPaymentMethodManager()
}
}
},
onCheckoutComplete({ payment }) {
console.log('onCheckoutComplete', payment)
},
onCheckoutFail(error, { payment }, handler) {
return handler.showErrorMessage()
},
})
await headless.start()
Call the start function in order to start the flow of the component:
JS
function configureKlarnaPaymentMethodManager(){
paymentMethodManager = await headless.createPaymentMethodManager(
paymentMethod,
{
onPaymentMethodCategoriesChange:(categories: KlarnaPaymentMethodCategory[]) =>{
renderCustomComponent(categories)
},
},
);
}
onPaymentMethodCategoriesChange callback received a payload as described here The options array can be used to configure a custom component/form.Handle data collectionAs mentioned previously, the component allows you to collect the payment categories, and to request payment finalization by calling the
start function from the payment method manager. You can use the result of the start function to handle any errorsJS
async function onPaymentCategorySelected( categoryId: string ) {
const response = await paymentMethodManager.start({
paymentMethodCategoryId: categoryId,
});
if (response?.outcome === 'DENIED') {
console.log("Something went wrong.", result.validationErrors)
return;
}
console.log("The payment finalized")
}
Pre-requisites
Some payment methods like Klarna Direct Debit (part of Klarnaβs Pay Now), require manual enablement for the available markets (e.g.: DE, SE, NL). This is done by Klarna upon merchant request.Install Primer Klarna SDK We support the Klarna In-App SDK (from version 2.7.0).In order to be able to use Klarna you will have to install thePrimerKlarnaSDK, currently available on CocoaPods.CocoaPods
Add the line below in youPodfileRUBY
use_frameworks!
target 'PrimerSDK_Example' do
pod 'PrimerSDK'
# Add the line below π
pod 'PrimerKlarnaSDK'
end
urlScheme of the paymentMethodOptions of your settings.SWIFT
let settings = PrimerSettings(
paymentHandling: .auto,
// π Add this line
paymentMethodOptions: PrimerPaymentMethodOptions(urlScheme: "merchant://primer.io")
)
Handle payment method
Integration[PrimerHeadlessUniversalCheckout.KlarnaManager](/sdk/ios/v2.x.x/primer-headless-checkout/klarna-headless-manager/init) is an SDK manager designed to streamline the headless integration with Klarna. To integrate PrimerHeadlessUniversalCheckout.KlarnaManager, follow these steps:Create a new instance of KlarnaComponent
For the primer session intent, pass either PrimerSessionIntent.CHECKOUT or PrimerSessionIntent.VAULT, depending on the session type youβd like to create:SWIFT
do {
manager = PrimerHeadlessUniversalCheckout.KlarnaManager()
klarnaComponent = try manager.provideKlarnaComponent(with: sessionIntent)
} catch {
// Catch errors here
}
Listen for the emitted data
Conform to the stepDelegate in order to be notified about the state of the payment process:SWIFT
func didReceiveStep(step: PrimerSDK.PrimerHeadlessStep) {
if let step = step as? KlarnaStep {
switch step {
// Emitted once the component is started via `start()` method call.
case .paymentSessionCreated(let clientToken, let paymentCategories):
// Render categories.
// Emitted once the payment options are collected
// via `updateCollectedData(collectableData: KlarnaCollectableData)` method call.
case .viewLoaded(let view):
// Render the payment view corresponding to the selected payment category
// Emitted once the payment is authorized via `submit()` method call
// after the user confirms the payment via the Klarna payment screen
case .paymentSessionAuthorized( _, let checkoutData):
// Payment was authorized, now you can perform your finalization flow.
// Payment requires finalization.
case .paymentSessionFinalizationRequired:
// Perform klarnaComponent.updateCollectedData(collectableData: KlarnaCollectableData.finalizePayment)
// to perform the finalization
// Emitted once the payment finalization is requested
// via `updateCollectedData(collectableData: KlarnaCollectableData.finalizePayment)`.
case .paymentSessionFinalized( _, let checkoutData):
// Payment was finalized, now you can perform your finalization flow.
default:
break
}
}
}
Listen to the data validation status
Itβs worth mentioning that the data validation is designed to operate in real-time. This means that the accuracy and completeness of the collected data can be confirmed even while users are actively inputting their information. This real-time validation feature ensures that your data remains accurate and complete throughout the data entry process, enhancing the user experience and the reliability of the collected data. Conform to the validationDelegate to handle any validation statuses that may occur.SWIFT
func didUpdate(validationStatus: PrimerSDK.PrimerValidationStatus, for data: PrimerSDK.PrimerCollectableData?) {
switch validationStatus {
case .validating:
// Handle validation being in progress (e.g. show a progress view)
case .valid:
// Handle valid (e.g. hide a progress view)
case .invalid(errors: let errors):
// Handle errors that might occour, display them to the user
case .error(error: let error):
// Display the error message to the user
}
}
Call the start method in order to start the flow of the component:
klarnaComponent.start()
paymentSessionCreated step will be emitted. As mentioned previously, the paymentSessionCreated step contains information about the available Klarna categories. Use these categories to build a category selection user interface. Clicking on any such category should update the payment options via updateCollectedData, as described below.Handle data collectionThe component allows you to collect the payment options (category), and to request payment finalization.Payment options should be set before submitting via the submit method:
SWIFT
func updatePaymentOptions(paymentCategory: KlarnaPaymentCategory) {
let klarnaCollectableData = KlarnaCollectableData.paymentCategory(paymentCategory, clientToken: clientToken)
klarnaComponent.updateCollectedData(collectableData: klarnaCollectableData)
}
paymentSessionFinalizationRequired is emitted as a step.SWIFT
klarnaComponent.updateCollectedData(collectableData: KlarnaCollectableData.finalizePayment)
KlarnaStep.paymentSessionAuthorized step.SWIFT
klarnaComponent?.submit()
You can conform to the errorDelegate to handle any errors that may occur during the payment flow:
SWIFT
func didReceiveError(error: PrimerSDK.PrimerError) {
// Handle errors (e.g., display error messages to the user)
}
errorDelegate is a protocol that emits PrimerError objects, allowing you to identify and respond to any errors that occur during the payment process.Troubleshooting
Flaky BankId
The Klarna direct debit flow for Sweden will try to redirect the user to the Mobile BankID app. This action is flaky when in sandbox mode & can sometimes failPre-requisites
Some payment methods like Klarna Direct Debit (part of Klarnaβs Pay Now), require manual enablement for the available markets (eg: DE, SE, NL). This is done by Klarna upon merchant request.ConfigurationWe support the Klarna In-App SDK (from version 2.7.0).Create a
PrimerKlarnaOptions object, then initialise the SDK as usual.βΉοΈ Starting from version
1.0.3onward, we are transitioning our SDK artifact distribution to Maven Central. This means you no longer need to reference the private Artifactory URL (PRIMER_ANDROID_ARTIFACTORY_URL) for future updates.- Add the URL to Klarnaβs CDN to your
gradle.properties:
JSX
KLARNA_DISTRIBUTION_URL=https://x.klarnacdn.net/mobile-sdk/
JSX
Amend the repositories section of your app's build.gradle to include our given repositories
repositories {
/* Other repositories... */
maven {
url "${KLARNA_DISTRIBUTION_URL}"
}
}
build.gradle to include the Primerβs klarna-android library:JSX
dependencies {
/* Other dependencies... */
implementation "io.primer:klarna-android:1.2.0"
}
Handle payment method
IntegrationPrimerHeadlessUniversalCheckoutKlarnaManager is an SDK manager designed to streamline the headless integration with Klarna. To integrate
PrimerHeadlessUniversalCheckoutKlarnaManager, follow these steps:Create a new instance of KlarnaComponent
For the primer session intent, pass either PrimerSessionIntent.CHECKOUT or PrimerSessionIntent.VAULT, depending on the session type youβd like to create:KOTLIN
val klarnaComponent = PrimerHeadlessUniversalCheckoutKlarnaManager(viewModelStoreOwner).provideKlarnaComponent(PrimerSessionIntent.CHECKOUT)
KOTLIN
klarnaComponent.componentStep.collectLatest { klarnaPaymentStep ->
when (klarnaPaymentStep) {
// Emitted once the component is started via a call to the `start()` function.
is KlarnaPaymentStep.PaymentSessionCreated -> {
klarnaStep.paymentCategories.forEach { paymentCategory ->
// Render category
}
}
// Emitted once the payment options are collected via a call to the
// `updateCollectedData(KlarnaPaymentCollectableData.PaymentOptions(...))` function.
is KlarnaPaymentStep.PaymentViewLoaded -> {
// Render the payment view corresponding to the selected payment category
}
// Emitted once the component is submitted via a call to the `submit()` function,
// after the user confirms the payment via the Klarna payment screen
is KlarnaPaymentStep.PaymentSessionAuthorized -> {
if (klarnaStep.isFinalized) {
// no-op
} else {
// Payment requires finalization. From this point on, you may call
// klarnaComponent.updateCollectedData(KlarnaPaymentCollectableData.FinalizePayment)
// to perform the finalization.
}
}
// Emitted once the component is submitted and payment finalization is requested
// via `updateCollectedData(KlarnaPaymentCollectableData.FinalizePayment)`.
is KlarnaPaymentStep.PaymentSessionFinalized -> {
// Do something when the payment is finalized
}
}
}
Listen to the data validation statuses
This component allows you to collect the payment options (category and return URL), and to request payment finalization. Itβs worth mentioning that the data validation is designed to operate in real-time. This means that the accuracy and completeness of the collected data can be confirmed even while users are actively inputting their information. This real-time validation feature ensures that your data remains accurate and complete throughout the data entry process, enhancing the user experience and the reliability of the collected data. Listen to the componentValidationStatus to handle any validation statuses that may occur.KOTLIN
klarnaComponent.componentValidationStatus.collectLatest { validationStatus ->
// Handle validation being in progress (e.g. show progress view),
// valid (e.g. enable payment button, submit data automatically..),
// invalid (e.g. disable submit button, show validation error messages in input components..)
// and error status (e.g. display error messages to the user)
}
KOTLIN
klarnaComponent.start()
PaymentSessionCreated step will be emitted. As mentioned previously, the PaymentSessionCreated step contains information about the available Klarna categories. Use these categories to build a category selection user interface. Clicking on any such category should update the payment options via updateCollectedData, as described below.Handle data collectionAs mentioned previously, the component allows you to collect the payment options (category and return URL), and to request payment finalization.Create a deeplink activity and define it in your
AndroidManifest.xml with the appropriate intent filter:XML
<activity
android:name=".DeeplinkReturnActivity"
android:exported="true"
android:launchMode="singleTop">
<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="app" />
<data android:host="deeplink.return.activity" />
</intent-filter>
</activity>
KOTLIN
fun updatePaymentOptions() {
klarnaComponent.updateCollectedData(
KlarnaPaymentCollectableData.PaymentOptions(
context = requireContext(),
paymentCategory = getSelectedPaymentCategory(),
// Built from the deeplink activity's intent filter scheme and host.
// This will be used by third-party apps to link back to your app.
returnIntentData = "app://deeplink.return.activity"
)
)
)
}
fun getSelectedPaymentCategory() : KlarnaPaymentCategory {
// Return the payment category selected by the user
}
PaymentSessionAuthorized is emitted as step, and PaymentSessionAuthorized.isFinalized returns false:KOTLIN
klarnaComponent.updateCollectedData(KlarnaPaymentCollectableData.FinalizePayment)
KlarnaPaymentStep.PaymentSessionAuthorized step.KOTLIN
klarnaComponent.submit()
Handle errors
You can listen to the componentError to handle any errors that may occur during the payment flow:KOTLIN
klarnaComponent.componentError.collectLatest { error ->
// Handle errors (e.g., display error messages to the user)
}
componentError is a Flow that emits PrimerError objects, allowing you to identify and respond to any errors that occur during the payment process.Troubleshooting
Flaky BankId
The Klarna direct debit flow for Sweden will try to redirect the user to the Mobile BankID app. This action is flaky when in sandbox mode & can sometimes failPre-requisites
Some payment methods like Klarna Direct Debit (part of Klarnaβs Pay Now), require manual enablement for the available markets (eg: DE, SE, NL). This is done by Klarna upon merchant request.Handle payment method
IntegrationKlarnaManager is an SDK manager designed to streamline the headless integration with Klarna. To integrate
KlarnaManager, follow these steps:Create a new instance of KlarnaManagerPropsTypescript
const klarnaManagerProps: KlarnaManagerProps = {
// Pass either PrimerSessionIntent.CHECKOUT or PrimerSessionIntent.VAULT, depending on the session type you'd like to create.
primerSessionIntent: PrimerSessionIntent.CHECKOUT,
onStep: (data: KlarnaPaymentStep) => {
// Show loading indicator, render categories or finalize payment
},
onError: (error: PrimerError) => {
// An error that occurred during the component's operation.
},
onInvalid: (data: PrimerInvalidComponentData<KlarnaPaymentValidatableData>) => {
// Data was considered invalid during validation.
},
onValid: (data: PrimerValidComponentData<KlarnaPaymentValidatableData>) => {
// Data was successfully validated.
},
onValidating: (data: PrimerValidatingComponentData<KlarnaPaymentValidatableData>) => {
// Data is in the process of being validated.
},
onValidationError: (data: PrimerComponentDataValidationError<KlarnaPaymentValidatableData>) => {
// Error occurred during data validation
},
};
KlarnaComponentTypescript
const klarnaManager = new KlarnaManager();
const klarnaComponent: KlarnaComponent = await klarnaManager.provide(klarnaManagerProps);
Typescript
const klarnaManagerProps: KlarnaManagerProps = {
...
onStep: (data: KlarnaPaymentStep) => {
switch(data.stepName) {
case "paymentSessionCreated":
// Emitted once the component is started via a call to the `start()` function.
// Render the payment categories, found in `data.paymentCategories` from here.
break;
case "paymentViewLoaded":
// Emitted once the payment options are collected via a call to the `onSetPaymentOptions()` function.
// Render the PrimerKlarnaPaymentView from here.
break;
case "paymentSessionAuthorized":
// Emitted once the component is submitted via a call to the `submit()` function,
// after the user confirms the payment via the Klarna payment screen.
if (data.isFinalized) {
// no-op
} else {
// Payment requires finalization. From this point on, you may call
// klarnaComponent.onFinalizePayment() to perform the finalization.
}
break;
case "paymentSessionFinalized":
// Emitted once the component is submitted and payment finalization is requested
// via `onFinalizePayment()`.
break;
}
},
...
};
Rendering the Klarna payment view
As mentioned before, the Klarna payment view must be redender when receiving thepaymentViewLoaded step. This payment view contains information about the selected payment category, and it must be rendered before submit() is called on the component.Typescript
// Import PrimerKlarnaPaymentView
import { PrimerKlarnaPaymentView } from '@primer-io/react-native';
...
// Define a hook that controls the payment view's visibility
const [isKlarnaPaymentViewVisible, setKlarnaPaymentViewVisible] = useState<boolean>(false)
...
const klarnaManagerProps: KlarnaManagerProps = {
...
onStep: (data: KlarnaPaymentStep) => {
switch(data.stepName) {
...
case "paymentViewLoaded":
// Update the hook's state
setKlarnaPaymentViewVisible(true)
break;
...
}
},
...
};
...
// JSX
// Render the payment view whenever the visible state is true.
// Please note that setting a 'minHeight' is mandatory for the payment view to show.
{isKlarnaPaymentViewVisible && <PrimerKlarnaPaymentView style={{ minHeight: 250 }} />}
...
Typescript
const klarnaManagerProps: KlarnaManagerProps = {
...
onInvalid: (data: PrimerInvalidComponentData<KlarnaPaymentValidatableData>) => {
// Data was considered invalid during validation, perhaps display an error message to the user.
switch(data.data.validatableDataName) {
case 'klarnaPaymentOptions':
// ...
break;
case 'klarnaPaymentFinalization':
// ...
break;
}
},
onValid: (data: PrimerValidComponentData<KlarnaPaymentValidatableData>) => {
// Data was successfully validated, perhaps enable a submission button.
switch(data.data.validatableDataName) {
case 'klarnaPaymentOptions':
// ...
break;
case 'klarnaPaymentFinalization':
// ...
break;
}
},
onValidating: (data: PrimerValidatingComponentData<KlarnaPaymentValidatableData>) => {
// Data is in the process of being validated, perhaps show a progress view.
switch(data.data.validatableDataName) {
case 'klarnaPaymentOptions':
// ...
break;
case 'klarnaPaymentFinalization':
// ...
break;
}
},
onValidationError: (data: PrimerComponentDataValidationError<KlarnaPaymentValidatableData>) => {
// Error occurred during data validation, perhaps display an error message to the user.
switch(data.data.validatableDataName) {
case 'klarnaPaymentOptions':
// ...
break;
case 'klarnaPaymentFinalization':
// ...
break;
}
},
};
Typescript
klarnaComponent.start()
PaymentSessionCreated step will be emitted. As mentioned previously, the PaymentSessionCreated step contains information about the available Klarna categories. Use these categories to build a category selection user interface. Clicking on any such category should update the payment options via onSetPaymentOptions, as described below.Handle data collectionAs mentioned previously, the component allows you to collect the payment options (category and return URL (Android only)), and to request payment finalization.Update your Android module by creating a deeplink activity and defining it in your
AndroidManifest.xml with the appropriate intent filter:XML
<activity
android:name=".DeeplinkReturnActivity"
android:exported="true"
android:launchMode="singleTop">
<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="app" />
<data android:host="deeplink.return.activity" />
</intent-filter>
</activity>
KOTLIN
class DeeplinkReturnActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
}
}
Typescript
function updatePaymentOptions() {
try {
const klarnaPaymentCategory = getSelectedPaymentCategory();
await klarnaComponent.onSetPaymentOptions(
{
/*
* Built from the deeplink activity's intent filter scheme and host.
* This will be used by third-party apps to link back to your app on Android devices.
*/
returnIntentUrl: "app://deeplink.return.activity.rn",
paymentCategory: klarnaPaymentCategory
}
);
} catch (err) {
console.error(err);
}
}
function getSelectedPaymentCategory() : KlarnaPaymentCategory {
// Return the payment category selected by the user
}
PaymentSessionAuthorized is emitted as step, and PaymentSessionAuthorized.isFinalized returns false:Typescript
klarnaComponent.onFinalizePayment();
PaymentSessionAuthorized step.Typescript
klarnaComponent.submit()
Handle errors
Make use of KlarnaManagerProps.onError to handle any errors that may occur during the payment flow:Typescript
const klarnaManagerProps: KlarnaManagerProps = {
...
onError: (error: PrimerError) => {
// An error that occurred during the component's operation, perhaps display an error message to the user.
},
...
};
componentError is a Flow that emits PrimerError objects, allowing you to identify and respond to any errors that occur during the payment process.Troubleshooting
Flaky BankId
The Klarna direct debit flow for Sweden will try to redirect the user to the Mobile BankID app. This action is flaky when in sandbox mode & can sometimes failVaulting
In order to vault Klarna accounts, pass the following data in the Client Session:| Parameter Name | Required | Description |
|---|---|---|
| customerId | β | A unique identifier for your customer |
Prepare the SDK for vaulting
- Web
- iOS
- Android
JS
await createHeadless(clientToken, {
klarna: {
paymentFlow: PaymentFlow.PREFER_VAULT,
recurringPaymentDescription: "<description>"
}
})
Klarna requires a Native UI Manager in order to be presented to the user.Pass
PrimerSessionItent.VAULT for Klarna to be vaulted.SWIFT
// π Create the payment method manager
let nativeUIPaymentMethodManager = try PrimerHeadlessUniversalCheckout.NativeUIManager(paymentMethodType: "KLARNA")
// π Show the payment method
try nativeUIPaymentMethodManager.showPaymentMethod(intent: .vault)
Klarna requires a Native UI Manager in order to be presented to the user.Pass
PrimerSessionItent.VAULT for Klarna to be vaulted.KOTLIN
// π Create the payment method manager
val nativeUiManager = PrimerHeadlessUniversalCheckoutNativeUiManager.newInstance("KLARNA")
// π Show the payment method
nativeUiManager.showPaymentMethod(this, PrimerSessionIntent.VAULT)