The distribution of the link to the consumer may be achieved by integrating our API directly to send SMS, email or display QR codes, but this can also be achieved by simply integrating our interface directly into your own user interface.
This interface implements all the best practices to get the link delivered to the end Consumer the fastest possible. It supports an interface for a seller (staff, sale associate, clerk) as well as one that is directed to the consumer themselves in a self-checkout context.
After the integration, you will be able to update the features of the interface without touching to your code using our profiles mechanism.
Video of a Self Checkout integration
Sequence diagram of a Self Checkout integration
<!DOCTYPE html>
<html>
<head>
<title>Sample POS Integration</title>
<script src="https://x.klarnacdn.net/hpp/distribution/sdk/v1/api.js" async></script>
</head>
<body>
<button id="distribute-button">Distribute</button>
<script>
window.klarnaHppDistributionAsyncCallback = function() {
You need to load the SDK asynchronously from our CDN https://x.klarnacdn.net/hpp/distribution/sdk/v1/api.js
. Make sure to have a function on the window object called klarnaHppDistributionAsyncCallback
, it will be called when the SDK is loaded.
You will then be able to make the init
call which will pre-fetch some of our resources so that the user experience is better. This should be done event if Klarna hasn’t been chosen as the payment method, so that bootstraping everything will be seen as seamless.
The SDK needs to be initialized once and lets you handle multiple sessions, which means that you don’t need to do these steps again in the context of a Single Application Page.
<script>
window.klarnaHppDistributionAsyncCallback = function() {
// Klarna.HPP.Distribution is available now, you should initialize the SDK
}
</script>
<script src="https://x.klarnacdn.net/hpp/distribution/sdk/v1/api.js" async>
</script>
try {
Klarna.HPP.Distribution.init()
} catch (err) {
// Handle error.
}
When the Consumer has decided to use Klarna has their payment method for the purchase, you will need to create an HPP Session. See our documentation on how to create an HPP Session.
In the response, you get back a token in the field distribution_module.token
that you will be able to use with the SDK. The service that creates this session has to give this token to the frontend layer.
{
"session_id": "<hpp_session_id>",
"redirect_url": "https://payment.klarna.com/<random_access_id>",
"session_url": "https://api.klarna.com/hpp/v1/sessions/<hpp_session_id>",
"qr_code_url": "https://payment.klarna.com/<hpp_session_id>/qr",
"distribution_url": "https://api.klarna.com/hpp/v1/sessions/<hpp_session_id>/distribution",
"distribution_module:": {
"token": "eyJhbGciOiJSUzI1NiJ9.ewogICJzZXNzaW9uX2lkIiA6ICIxM2E5NDA5NC0wM2RiLTY0NzgtYTFmNi0xMzYzMTQ3MTczYjIiLAogICJiYXNlX3VybCIgOiAiaHR0cHM6Ly9rbGFybmEtcGF5bWVudHMtZXUuc3RhZ2luZy5ldTEueWFjby5rbGFybmEubmV0IiwKICAiZGVzaWduIiA6ICJrbGFybmEiLAogICJsYW5ndWFnZSIgOiAiZW4iLAogICJwdXJjaGFzZV9jb3VudHJ5IiA6ICJTRSIsCiAgImFuYWx5dGljc19wcm9wZXJ0eV9pZCIgOiAiVUEtMzYwNTMxMzctMTEiLAogICJ0cmFjZV9mbG93IiA6IGZhbHNlLAogICJlbnZpcm9ubWVudCIgOiAic3RhZ2luZyI",
"standalone_url": "https://payment-eu.playground.klarna.com/distributions/<random_distribution_id>?param1={{profile_id}}¶m2={{view_id}}",
"generation_url": "https://api.klarna.com/hpp/v1/sessions/<hpp_session_id>/distribution-module"
distribution_url
and qr_code_url
present in this response let you handle distribution by yourself by using our APIs directly.distribution_module.standalone_url
is to be used when you want to use our Distribution Module, yet you want to open it by yourself in a webview or without using our Javascript SDK.distribution_module.generate_url
lets you retrieve the Distribution Module’s parameter at a later time if you want to open it again on the same transaction.distribution_module.token
is a JWT that you will use in this guide to open the Distribution Module using the SDK.Now that the frontend is in possession of the token
, you need to use the SDK to create a DistributionView
. You will need to create this object for every session that needs to be distributed.
This object will allow you to start the User Interface that can be used by a person to interact and distribute your HPP Session. When making this call you need to define what constrains the User Interface needs to respect, especially who interacts with the interface, a store associate or the consumer themselves. If both of them can see the screen, it may be wiser to target consumers.
After the object creation, you will need to register callbacks to handle the different outputs.
Looking for a way to try it out really quick? Look at our predefined Profiles for tests at the end of the guide !
Name | Accepted Values | Description |
---|---|---|
token | Valid token | The distribution token received when creating the HPP session |
target | Klarna.HPP.Distribution.Type.NEW_WINDOW, Klarna.HPP.Distribution.Type.FULLSCREEN | Whether the page should be open in a fullscreen iframe or in a new window |
profile_id | Profile identifier | Identifier of the profile to use on the user interface, this will define what distribution methods are available, if the user is a seller or the consumers themselves. Profiles can be updated server side. |
InvalidParameter
: One or more parameters were incorrect, message will contain which one(s).try {
const distributionView = Klarna.HPP.Distribution.create(
{
token: 'eyJhbGciOiJSUzI1NiJ9.ewogICJzZXNzaW9uX2lkIiA6ICIxM2E5NDA5NC0wM2RiLTY0NzgtYTFmNi0xMzYzMTQ3MTczYjIiLAogICJiYXNlX3VybCIgOiAiaHR0cHM6Ly9rbGFybmEtcGF5bWVudHMtZXUuc3RhZ2luZy5ldTEueWFjby5rbGFybmEubmV0IiwKICAiZGVzaWduIiA6ICJrbGFybmEiLAogICJsYW5ndWFnZSIgOiAiZW4iLAogICJwdXJjaGFzZV9jb3VudHJ5IiA6ICJTRSIsCiAgImFuYWx5dGljc19wcm9wZXJ0eV9pZCIgOiAiVUEtMzYwNTMxMzctMTEiLAogICJ0cmFjZV9mbG93IiA6IGZhbHNlLAogICJlbnZpcm9ubWVudCIgOiAic3RhZ2luZyI',
target: Klarna.HPP.Distribution.Type.FULLSCREEN,
profile_id: "362fa3e5-c439-7851-b885-4648ea469e7a"
}
)
} catch (err) {
// Handle error.
eventName
.eventName:string
The name of the event to which you want to subscribe.eventHandler:on~eventHandler
The function that should be called when the event is emitted.eventName
.eventName:string
The name of the event from which you want to unsubscribe.eventHandler:on~eventHandler
The function that was previously registered for the eventName
. Omit if you want to unregister all handlers for the eventName
.The close
event will be emitted when the Distribution Module is closed. This usually happens in one of these cases:
close
method was called on the distribution viewThe event can happen when the payment session has not changed to a state that is considered final, which is defined by the boolean final
. The success of the payment depends on the value of the boolean approved
.
Parameter | Description |
---|---|
payment.final | The session reached a status considered to be a final decision. See our state machine. |
payment.approved | The session was completed and payment accepted. |
distributionView.on(Klarna.HPP.Distribution.Event.CLOSE, function({ payment: { final, approved } }) {
if(final) {
if(approved) {
// handle case where payment was successful
}
else {
// handle case where payment can't be made with Klarna...
}
}
else {
The client-error
event is emitted when HPP is displaying the “Distribution Payment Error” page and an unexpected JavaScript error occurs on the user’s browser. If a handler is defined for this event, it will be then called with an error
object as an argument.
The Distribution Module closes automatically before emitting the client-error
event.
distributionView.on(Klarna.HPP.Distribution.Event.CLIENT_ERROR, function(error) {
// here your integration should handle what happens when an error happens on the browser
console.error(error)
})
distributionView.off(Klarna.HPP.Distribution.Event.CLOSE, onClose)
distributionView.off(Klarna.HPP.Distribution.Event.CLIENT_ERROR)
Opens the distribution view on the specified target set when the instance was created (fullscreen_iframe
or new_window
). The iframe or the window will be automatically closed when the HPP Session moves to a final state, or if some unexpected javascript error happens.
This method will call the function given as argument as soon as the distribution view has actually started and the user can interact with the distribution.
distributionView.open(function(error) {
if(error) console.log('view could not be opened')
else console.log('view opened')
})
You can call the keyboardInputRequest
method with in-store-token data provided via keyboard input as the first parameter and an event id as an optional second parameter to send to HPP.
Name | Values | Description |
---|---|---|
data | String | Keyboard input string |
eventId? | String | Event ID that will be send back in responce, to help connect requests with responces (Optiional parameter) |
HPP will emit a KEYBOARD_INPUT_RESPONSE
event on every keyboardInputRequest
call. That response (KEYBOARD_INPUT_RESPONSE
) contains a data object with the following properties:
Name | Values | Description |
---|---|---|
success | Boolean | Indicates whether the request was successfully accepted. (Note: if an error occurred during the processing of the request, that error will appear in the Distribution Module window) |
message | String | Human readable message of the results of accepting and processing a request |
eventId | String | Event ID that was sent in request |
Example of usage
distributionView.on(Distribution.Event.KEYBOARD_INPUT_RESPONSE, function(data) {
console.log('keyboard-input-response', data)
})
distributionView.keyboardInputRequest("some keyboard input", "some-event-id-###")
You can integrate the Distribution Module in any web application or application capable of displaying web pages. To do this, you will receive from HPP a standalone_url
when you create the HPP Session. This URL is the link that you should open in the browser (say a new window or a webview).
After creating an HPP Session, you get back a Distribution Module URL in the field distribution_module.standalone_url
. Any browser or webview accessing this URL will be able to distribute the HPP Session. You just need to open it, and then, from your native application, choose one of the different ways available to track the flow.
{
"session_id": "<hpp_session_id>",
"redirect_url": "https://payment.klarna.com/<random_access_id>",
"session_url": "https://api.klarna.com/hpp/v1/sessions/<hpp_session_id>",
"qr_code_url": "https://payment.klarna.com/<hpp_session_id>/qr",
"distribution_url": "https://api.klarna.com/hpp/v1/sessions/<hpp_session_id>/distribution",
"distribution_module:": {
"token": "eyJhbGciOiJSUzI1NiJ9.ewogICJzZXNzaW9uX2lkIiA6ICIxM2E5NDA5NC0wM2RiLTY0NzgtYTFmNi0xMzYzMTQ3MTczYjIiLAogICJiYXNlX3VybCIgOiAiaHR0cHM6Ly9rbGFybmEtcGF5bWVudHMtZXUuc3RhZ2luZy5ldTEueWFjby5rbGFybmEubmV0IiwKICAiZGVzaWduIiA6ICJrbGFybmEiLAogICJsYW5ndWFnZSIgOiAiZW4iLAogICJwdXJjaGFzZV9jb3VudHJ5IiA6ICJTRSIsCiAgImFuYWx5dGljc19wcm9wZXJ0eV9pZCIgOiAiVUEtMzYwNTMxMzctMTEiLAogICJ0cmFjZV9mbG93IiA6IGZhbHNlLAogICJlbnZpcm9ubWVudCIgOiAic3RhZ2luZyI",
"standalone_url": "https://payment-eu.playground.klarna.com/distributions/<random_distribution_id>?param1={{profile_id}}¶m2={{view_id}}",
"generation_url": "https://api.klarna.com/hpp/v1/sessions/<hpp_session_id>/distribution-module"
distribution_url
and qr_code_url
present in this response let you handle distribution by yourself by using our APIs directly.distribution_module.generate_url
lets you retrieve the Distribution Module’s parameters at a later time if you want to open it again on the same transaction.distribution_module.token
is a JWT that you would use if you use our JavaScript SDK.The standalone_url
you got back from our servers contains two placeholders that you needs to replace with the correct data:
{{profile_id}}
should be replace by the ID of the Profile you want to use{{view_id}}
is a way for your application to give an identifier you will be able to get back from all events happening in the page. It could be a UUIDv4. The value will be validated against this regular expression: `[A-Z-a-z0-9\-_]{8,64}`It is important that your service doesn’t tamper with anything else than the placeholders.
https://payment-eu.playground.klarna.com/distributions/82631291-62d4-4f3e-8906-517f7addb178?param1={{profile_id}}¶m2={{view_id}}
https://payment-eu.playground.klarna.com/distributions/82631291-62d4-4f3e-8906-517f7addb178?param1=ac0ef585-b1b1-4951-ab0f-f250f44f6b08¶m2=b5485de0-11e0-4f36-8e50-c8ec68bfc806
Where:
ac0ef585-b1b1-4951-ab0f-f250f44f6b08
is a pre-configured profile identifierb5485de0-11e0-4f36-8e50-c8ec68bfc806
is a UUID your service generated for this precise renderingA Distribution Module message is a string containing a JSON serialized event, which has a name and some data relevant to it. The events are the same ones supported by our SDK and follow the same structure and can be seem here[[#registering-for-events-happening-during-distribution]].After you get your distribution_module.standalone_url
and load it in the webview you can inject JavaScript code telling the Distribution Module how to comunicate back with your native code. This can be done by defining a function on the window object called klarnaWebViewPostMessage
, this function will be called with a Distribution Module message that should be sent back to your native app.
Whenever the object window.klarnaWebViewPostMessage
exists, the Distribution Module will consider this as the way for you to track the events. It will send events using the object as a function.The event serialized in the message has the following fields:
Property | Description |
---|---|
name | The event name |
data | Extra data relevant to that event, which will be different for all types of events. Serialized in JSON. |
view_id | The value you initially used as an identifier to correlate all events happening on the Distribution Module UI. |
{
"name": "close",
"data": {
"payment": {
"final": true,
"approved": true
}
},
"view_id": "c988375c-fb76-4fc7-959b-de4e11ce8c74"
}
In React Native, the message is contained in the data
field of the nativeEvent
, we need to parse the JSON and act on it. React Native has a handy way to inject JavaScript in the webview. Use attribute onMessage
of the WebView to register method for handling events, that are happening during distribution.
function onDistributionModuleMessage({ nativeEvent }) {
const {name, data, view_id} = JSON.parse(nativeEvent.data)
if (name === 'close') {
const { final, approved} = data.payment
if(final) {
if(approved) {
// handle case where payment was successful
}
When the Distribution Module is open using the standalone_url
and the object window.klarnaWebViewPostMessage
doesn’t exist, you can listen for url changes on the webview.
In this case, when the flow reaches a close status, the url will change to about:blank?klarna-distribution-module-close
. You can react to this change and call our backend to get the latest payment status.
For integrations where you can’t interact with the webview, like opening an external browser window to display the Distribution Module, you can poll the payment status and proceed when it reaches a final state.
The webservice to manage profiles hasn’t reached general availability. Reach out to your Klarna technical contact to help you roll this out. If you want to try it out know, you can already use one of the test profiles we have set up for you.
The user interface can be configured to better match your flow using profiles. Indeed, the distribution user interface may be shown either to a store associate or a consumer, and this depends on the context. There is no reason to show a QR code if your consumer can’t read it anyway. Also, there is no reason to show an email field input in a store context.
The configuration is defined in a Profile, stored on our servers so that you can benefit from any new functionality by changing your Profile without any other need.
Name | Accepted Values | Default | Description |
---|---|---|---|
name | String | Friendly name for the Profile | |
user | SELLER, BUYER | Whether the page will be used by the seller or the customer | |
methods | SMA, SMS_TOUCHPAD, QR, EMAIL, SMS_AND_EMAIL | [SMS_AND_EMAIL] | Which distribution methods will be displayed. Order will be used to define which one is shown first. |
touchscreen | boolean | false | Is the device equipped with a touchscreen or not |
synchronous | boolean | true | Is the sale synchronous or not. When synchronous, the user needs to cancel the transaction to be able to exit the distribution interface, which will always lead to an end event callback. When asynchronous, the close event callback may happen with a non final state for the session. |
timeout | positive number | 300 | Duration that the interface will wait after the last user interaction before triggering a specific screen that requires the user to give a sign of activity (in seconds) |
The Distribution Module has a close button on its top, which supports two different flows:
In this example, the user interface is used in a Point of Sale terminal. The store associate scans the goods for the customer and then ask them how to pay. When they say that they want to pay with Klarna, the POS terminal triggers the HPP Distribution Module.
SELLER
.{
"name": "store associate distribution",
"user": "SELLER",
"methods": ["SMS_TOUCHPAD"],
"touchscreen": true,
"synchronous": true,
"timeout": 300
}
{
"profile_id": "1b9d742b-a234-7a5e-b9bd-4e2cdc36bb57"
}
try {
const distributionView = Klarna.HPP.Distribution.create(
{
token: 'eyJhbGciOiJSUzI1NiJ9.ewogICJzZXNzaW9uX2lkIiA6ICIxM2E5NDA5NC0wM2RiLTY0NzgtYTFmNi0xMzYzMTQ3MTczYjIiLAogICJiYXNlX3VybCIgOiAiaHR0cHM6Ly9rbGFybmEtcGF5bWVudHMtZXUuc3RhZ2luZy5ldTEueWFjby5rbGFybmEubmV0IiwKICAiZGVzaWduIiA6ICJrbGFybmEiLAogICJsYW5ndWFnZSIgOiAiZW4iLAogICJwdXJjaGFzZV9jb3VudHJ5IiA6ICJTRSIsCiAgImFuYWx5dGljc19wcm9wZXJ0eV9pZCIgOiAiVUEtMzYwNTMxMzctMTEiLAogICJ0cmFjZV9mbG93IiA6IGZhbHNlLAogICJlbnZpcm9ubWVudCIgOiAic3RhZ2luZyI',
target: Klarna.HPP.Distribution.Type.NEW_WINDOW,
profile_id: "1b9d742b-a234-7a5e-b9bd-4e2cdc36bb57"
}
)
} catch (err) {
// Handle error.
In a “webview” component you are not able to call distributionView.keyboardInputRequest("some keyboard input", "some-event-id-###")
. You can call the `window.ReactNativeWebView.postMessage` method instead, like in the code example below:
window.ReactNativeWebView.postMessage(JSON.stringify({
"type": "keyboard-input-request",
"id": "some-event-id-###",
"data": "some keyboard input"
}))
The keyboard-input-responce
will come to onDistributionModuleMessage
method, if it was defined as shown in the beginning of “React Native example” section.
In this example, the user interface will be embedded in your self checkout screen that your customers use to pay by themselves. They start by scanning their items and then pay, they can choose Klarna as a payment method. When they choose Klarna, the system triggers the HPP Distribution Module.
BUYER
.{
"name": "Selfcheckout distribution",
"user": "BUYER",
"methods": ["SMS_TOUCHPAD", "QR"],
"touchscreen": true,
"synchronous": true,
"timeout": 300
}
{
"id": "362fa3e5-c439-7851-b885-4648ea469e7a"
}
try {
const distributionView = Klarna.HPP.Distribution.create(
{
token: 'eyJhbGciOiJSUzI1NiJ9.ewogICJzZXNzaW9uX2lkIiA6ICIxM2E5NDA5NC0wM2RiLTY0NzgtYTFmNi0xMzYzMTQ3MTczYjIiLAogICJiYXNlX3VybCIgOiAiaHR0cHM6Ly9rbGFybmEtcGF5bWVudHMtZXUuc3RhZ2luZy5ldTEueWFjby5rbGFybmEubmV0IiwKICAiZGVzaWduIiA6ICJrbGFybmEiLAogICJsYW5ndWFnZSIgOiAiZW4iLAogICJwdXJjaGFzZV9jb3VudHJ5IiA6ICJTRSIsCiAgImFuYWx5dGljc19wcm9wZXJ0eV9pZCIgOiAiVUEtMzYwNTMxMzctMTEiLAogICJ0cmFjZV9mbG93IiA6IGZhbHNlLAogICJlbnZpcm9ubWVudCIgOiAic3RhZ2luZyI',
target: Klarna.HPP.Distribution.Type.FULLSCREEN,
profile_id: "362fa3e5-c439-7851-b885-4648ea469e7a"
}
)
} catch (err) {
// Handle error.
In this example, the user interface is used in a telesales context where the telesales person is trying to close a deal with a customer on the phone.
SELLER
.{
"name": "Telesales distribution",
"user": "SELLER",
"methods": ["SMS_AND_EMAIL"],
"touchscreen": false,
"synchronous": false,
"timeout": 0
}
{
"profile_id": "d3c63b29-51a8-7d02-928f-abab925e1e50"
}
try {
const distributionView = Klarna.HPP.Distribution.create(
{
token: 'eyJhbGciOiJSUzI1NiJ9.ewogICJzZXNzaW9uX2lkIiA6ICIxM2E5NDA5NC0wM2RiLTY0NzgtYTFmNi0xMzYzMTQ3MTczYjIiLAogICJiYXNlX3VybCIgOiAiaHR0cHM6Ly9rbGFybmEtcGF5bWVudHMtZXUuc3RhZ2luZy5ldTEueWFjby5rbGFybmEubmV0IiwKICAiZGVzaWduIiA6ICJrbGFybmEiLAogICJsYW5ndWFnZSIgOiAiZW4iLAogICJwdXJjaGFzZV9jb3VudHJ5IiA6ICJTRSIsCiAgImFuYWx5dGljc19wcm9wZXJ0eV9pZCIgOiAiVUEtMzYwNTMxMzctMTEiLAogICJ0cmFjZV9mbG93IiA6IGZhbHNlLAogICJlbnZpcm9ubWVudCIgOiAic3RhZ2luZyI',
target: Klarna.HPP.Distribution.Type.FULLSCREEN,
profile_id: "d3c63b29-51a8-7d02-928f-abab925e1e50"
}
)
} catch (err) {
// Handle error.
This a list of predefined Profiles that you can use to quickly try out our Distribution Module.
Profile Id | Features |
---|---|
ac0ef585-b1b1-4951-ab0f-f250f44f6b08 | Profile targets BUYER with QR and SMS_TOUCHPAD, closing will cancel session |
4baf6592-2675-4ed7-93cc-783b8f73de5e | Profile targets BUYER with SMS_TOUCHPAD and QR, closing will cancel session |
78417431-3bf2-456e-9f18-95c3313db874 | Profile targets SELLER with SMS_AND_EMAIL, closing will cancel session |
e205530c-37b5-4843-a228-871b5fd80c97 | Profile targets SELLER with SMS_TOUCHPAD, closing will cancel session |
2696202f-c5f2-40d5-bbc5-50a9ff039949 | Profile targets SELLER with SMS_AND_EMAIL, closing will leave the session ongoing |
9a627b00-1ea8-40e7-9e92-3eca00809e85 | Profile targets SELLER with SMS_TOUCHPAD and QR, closing will leave the session ongoing |