Integrate Klarna's Payment Authorize API for hosted checkout and embedded elements. This guide covers backend implementation for handling authorization results, step-up flows, and finalizing payments.
This guide is part of the Payment Authorization flow for hosted checkout and embedded elements integration path. It covers the backend implementation — what the Acquiring Partner's server does after the customer interacts with the Klarna payment form on the frontend.
Where this fits in the integration flow:
This guide is a continuation of the Payment Presentation guide and focuses on the server-side authorization flow.
What you'll implement:
APPROVED, STEP_UP_REQUIRED, DECLINED)Before implementing the backend endpoint, ensure you have:
amount, currency, supplementary_purchase_data, klarna_network_session_token, klarna_network_data)Your backend's role: Your backend receives the authorization request from the frontend, calls Klarna's authorizePayment API, and returns the result so the frontend can proceed accordingly.
The backend authorization flow starts after the customer selects a Klarna payment method in your checkout. Your frontend triggers the initiate callback, which calls your backend. Your backend then forwards the request to Klarna and returns the result.
The integration process consists of the following key steps:
initiate callback: The Klarna web SDK calls your configured callback when the customer initiates a payment authorization.klarna_network_session_token and payment_option_id from your frontendSTEP_UP_REQUIRED, APPROVED, or DECLINED outcomes and return the result to your frontendinitiationMode: Control how the Klarna Web SDK launches the Klarna Purchase Journey for step-upSTEP_UP_REQUIRED)initiate callbackWhen the customer selects a Klarna payment method and clicks the Klarna payment button in your checkout, the Klarna web SDK triggers the initiate callback function configured in your Payment Presentation. This callback calls your backend authorization endpoint.
Implement the initiate callback to call your backend endpoint and handle authorization results. The frontend is responsible for:
klarnaNetworkSessionToken and paymentOptionIdresult fieldpaymentRequestId to the Klarna Web SDK if step-up is required (the SDK handles launching the Klarna Purchase Journey)async function initiateKlarnaPayment(klarnaNetworkSessionToken, paymentOptionId) {
try {
// 1) Ask your backend to authorize the payment
const response = await fetch('/api/authorize-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ klarnaNetworkSessionToken, paymentOptionId })
});
if (!response.ok) throw new Error('Network or server error');
// 2) Handle backend responseThe following steps in this guide explain how to implement the backend endpoint that this initiate callback calls.
Your backend endpoint receives the authorization request from the frontend after the customer clicks the Klarna payment button.
Endpoint: POST /api/authorize-payment (or your chosen path)
Input from frontend (provided by the Klarna Web SDK via the initiate callback):
| Parameter | Type | Required | Description |
|---|---|---|---|
klarnaNetworkSessionToken | string | Yes | The session token provided by the Klarna Web SDK through the initiate callback |
paymentOptionId | string | Yes | The payment option identifier selected by the customer, provided by the Klarna Web SDK through the initiate callback |
Example frontend request:
{
"klarnaNetworkSessionToken": "krn:network:us1:test:session-token:eyJhbGciOiJIU[...]",
"paymentOptionId": "S0xBUk5BXzE3NzI3MjQ5MTQzMjk="
}Forward the complete payment context to Klarna using the Payment Authorize API. Include all payment data collected from the Partner (same as used during Payment Presentation).
The authorizePayment API is used to start the payment process on Klarna whether they are with or without customer presence.
curl https://api-global.test.klarna.com/v2/accounts/{partner_account_id}/payment/authorize \
-H 'Authorization: Basic <API key>' \
-H 'Content-Type: application/json' \
-H 'Klarna-Customer-Token: krn:partner:us1:test:identity:customer-token:ACZawkw7-2[...]' \
-H 'Klarna-Network-Session-Token: krn:network:us1:test:session-token:eyJhbGciOiJIU[...]' \
-d '{
"currency": "USD",
"supplementary_purchase_data": {
"purchase_reference": "ORDER-12345",
"line_items": [
{
"name": "Running Shoes",The customer_token can be obtained from the Payment Tokenization flow.
Important considerations:
amount, currency, and supplementary_purchase_data values collected from the Partner (same as used during Payment Presentation)step_up_config with HANDOVER method to allow the customer to complete authentication when neededThe authorizePayment API returns one of three possible results in the
payment_transaction_response.result field. Your backend reads this field and returns the appropriate data to your frontend so it can proceed.
The result field indicates the outcome and determines your next action. It has the following possible values:
| Result | Description | Next steps |
|---|---|---|
STEP_UP_REQUIRED | Additional customer interaction is needed to complete the authorization (step-up scenario). A payment_request is created. | Handle this result when Klarna requires additional customer interaction to complete authorization:
|
APPROVED | The authorization succeeded and a payment_transaction was created. | The Payment Authorization is complete. Handle this result when Klarna authorizes the payment without requiring customer interaction:
|
DECLINED | The authorization was not approved and no transaction was created. | Handle the rejection in your frontend (for example, offer the customer alternative payment methods):
|
Here's the high-level diagram of the Payment Authorization results and resources it produces:
Payment authorization results
The response schema is as follows:
{
"payment_transaction_response": {
"result": "STEP_UP_REQUIRED"
},
"payment_request": {
"payment_request_id": "krn:payment:us1:request:552603c0-fe8b-4ab1-[...]",
"payment_request_reference": "backend-request-ref-12345",
"amount": 11800,
"currency": "USD",
"state": "SUBMITTED",
"expires_at": "2025-01-02T13:00:00Z",
"created_at": "2025-01-01T12:00:00Z",Your backend endpoint should handle the different authorization outcomes and communicate the results to your frontend.
Here is an example showing how your frontend can retrieve the results from your backend endpoint
// Example Node.js/Express endpoint
app.post('/api/authorize-payment', async (req, res) => {
try {
const { klarnaNetworkSessionToken, paymentOptionId } = req.body;
// Call Klarna Payment Authorize API
const klarnaResponse = await callKlarnaAuthorizeAPI({
klarnaNetworkSessionToken,
paymentOptionId,
amount: 11800,
currency: 'USD',
// ... other payment contextWhat happens next:
When the frontend receives STEP_UP_REQUIRED with the paymentRequestId, the Klarna Web SDK automatically launches the Klarna Purchase Journey (see Step 5).
Acquiring Partners must include return URLs in the step_up_config when calling authorizePayment to enable the step-up scenario.
These URLs, set inside the customer_interaction_config object, tell Klarna where to redirect the customer after they complete or stop the purchase journey.
If the Klarna Purchase Journey is launched in a web environment, Klarna redirects the customer to the return_url after they finish — whether they complete or stop the flow. This can be a URL managed by the Acquiring Partner (which handles redirection logic) or one collected directly from the Partner.
On mobile, the customer may be redirected to a third-party app (such as a bank app) or the Klarna app during the purchase journey. The app_return_url brings the customer back to the Partner's mobile app when this happens.
Partners must register a URL scheme (e.g., yourapp://klarna) or a universal link that resumes the payment flow. Klarna invokes this URL after the customer completes a native app-based step, such as biometric authentication or Klarna app login. Partners are expected to resume the mobile app in its last state without applying state changes or deep link navigations.
The return_url and app_return_url are not mutually exclusive. Depending on the device and environment, either or both may be triggered:
| Scenario | Description |
|---|---|
| Pure web flow | The customer starts the purchase journey in a desktop browser. After completing the flow, Klarna redirects them to return_url. |
| App-to-app flow | The Partner's native app opens the purchase journey using a universal link. If the Klarna app is installed, the customer goes directly into it. After completion, Klarna redirects them to app_return_url. |
| WebView flow with app handover | The Partner's native app starts the purchase journey in a System WebView. If the customer must authenticate via an external banking app, app_return_url returns them to the Partner's app mid-flow. They then resume in the WebView and, on completion, are redirected to return_url. |
If the Acquiring Partner already collects a suitable return_url or app_return_url from the Partner, do not request a second one — this would increase the minimum integration requirements for Klarna to work.
A step-up occurs when Klarna requires additional customer interaction to complete a Payment Authorization — for example, when the customer needs to log in, authenticate, or select a payment method.
When step-up is triggered, authorizePayment returns a
STEP_UP_REQUIRED result along with a Payment Request containing the payment_request_id which is required for the Klarna Web SDK to launch the Klarna Purchase Journey.
initiationModeThe Klarna web SDK launches the purchase journey through the initiate function on the Klarna Payment button. The function returns a promise that resolves to an object containing the paymentRequestId:
const buttonConfig = {
initiate: () => {
// Call to Payment Authorize API returns STEP_UP_REQUIRED
return { paymentRequestId: "krn:payment:us1:request:552603c0-fe8b-4ab1-[...]" };
},
initiationMode: "DEVICE_BEST", // "DEVICE_BEST" (default) | "ON_PAGE" | "REDIRECT"
};
// Render and mount the button with the assigned configuration
paymentPresentation.paymentButton
.component(buttonConfig)
.mount("#klarna-button-container");The initiationMode parameter controls how the Klarna Purchase Journey is launched when your backend returns STEP_UP_REQUIRED. Configure this in your frontend payment button.
| initiationMode | Description |
|---|---|
DEVICE_BEST | Automatically selects the best way to launch the Klarna Purchase Journey depending on the device - this is the default and recommended value: *Mobile: Always redirect *Desktop: pop-up if possible, fallback to redirect
|
ON_PAGE | The Klarna Purchase Journey is triggered on the same page. The customer never leaves the page. The Klarna Purchase Journey opens in a pop-up if possible and fallback to fullscreen iframe if necessary. |
REDIRECT | The customer will be redirected to the Klarna Purchase Journey. Note: for this initiationMode, a return_url is required in the customer_interaction_config object when calling the Payment Authorize API. |
When does this matter? The initiationMode parameter only affects the customer experience when your backend returns STEP_UP_REQUIRED. If your backend returns APPROVED, the customer proceeds directly to the confirmation page without launching the Klarna Purchase Journey.
For complete frontend implementation details, see the Payment Presentation guide.
The purchase journey allows the customer to authenticate with Klarna (via login, one-time passwords, etc.), choose a payment method, and accept the payment.

Phone collection in the Klarna purchase journey
Klarna redirects the customer to the return_url in customer_interaction_config, except when a pop-up experience is used and the customer stops the flow.
On completion, Klarna issues a klarna_network_session_token to the Acquiring Partner to finalize the payment.
Acquiring Partners can register event handlers on the KlarnaPayment SDK interface to track purchase journey stages:
| Event handler | Purpose |
|---|---|
on("complete", callback) | Triggered when the customer accepts the purchase. During a redirect flow, the handler fires once the success page loads. Pending updates are triggered even if the handler is registered after the page loads, removing the need for polling. |
on("abort", callback) | Triggered when the customer stops the Klarna purchase journey. |
on("error", callback) | Triggered when an error occurs during the Payment Request lifecycle. When defined, all errors are emitted to this handler instead of being thrown. |
Event handlers provide access to the following paymentRequest properties:
| Property | Definition |
|---|---|
paymentRequestId | Unique identifier of the Payment Request. |
state | Current state of the Payment Request. |
previousState | Previous state of the Payment Request. |
stateReason | The reason the Payment Request is in its current state. |
stateContext | State-specific context. The klarna_network_session_token is stored here when the Payment Request reaches the COMPLETED state. |
klarna.Payment.on("complete", (paymentRequest) => {
console.log("Payment request completed");
// By default, Klarna will redirect to the submitted return_url upon completion.
// Return false if you want to prevent the redirection to the success page.
return true
});Use Web SDK events to enhance the customer experience — for example, displaying clear error messages when a purchase is stopped, or disabling page interactions while payment authorization is in progress.
Never use SDK events to trigger Payment Authorization. Always rely on Klarna webhooks to receive the klarna_network_session_token and finalize the payment after the customer approves the purchase.
When the authorizePayment API returns
STEP_UP_REQUIRED, the customer needs to complete the Klarna Purchase Journey before you can finalize the authorization. Your backend monitors for step-up completion by the customer to know when to proceed.
Once the customer completes the step-up, Klarna issues a new klarna_network_session_token that you'll need for the finalization call in Step 7.
The klarna_network_session_token is valid for only 1 hour, so be sure to use it promptly.
Subscribe to the payment.request.state-change.completed webhook event using the guidelines provided here. Klarna sends this event when the Payment Request reaches the COMPLETED state.
This event indicates that the customer has finished the step-up interaction and that the Acquiring Partner can proceed with finalizing the authorization.
Sample payload
{
"metadata": {
"event_type": "payment.request.state-change.completed",
"event_id": "d9f9b1a0-5b1a-4b0e-9b0a-9e9b1a0d5b1a",
"event_version": "v2",
"occurred_at": "2024-01-01T12:00:00Z",
"correlation_id": "2d1557e8-17c3-466c-924a-bbc3e91c2a02",
"subject_account_id": "krn:partner:global:account:test:HGBY07TR",
"recipient_account_id": "krn:partner:global:account:test:LWT2XJSE",
"product_instance_id": "krn:partner:product:payment:ad71bc48-8a07-4919-[...]",
"webhook_id": "krn:partner:global:notification:webhook:120e5b7e-abcd-4def-[...]",
"live": falseAs an alternative method, retrieve the Klarna token(s) by calling the readPaymentRequest API.
Once the Payment Request reaches the COMPLETED state, the token(s) are available in the state_context object of the Payment Request.
Sample payload
{
"payment_request_id": "krn:payment:us1:request:552603c0-fe8b-4ab1-[...]",
"state": "COMPLETED",
"previous_state": "IN_PROGRESS",
"state_context": {
"klarna_network_session_token": "krn:network:us1:test:session-token:eyJhbGciOiJIU..."
},
"expires_at": "2025-02-26T17:25:34.534721775Z",
"created_at": "2025-02-24T17:25:34.534721775Z",
"updated_at": "2025-02-24T17:25:34.534721775Z",
"payment_request_reference": "backend-request-ref-12345",
"amount": 11800,After receiving the klarna_network_session_token from webhook or polling, finalize the authorization by calling the Payment Authorize API again with the new token.
Call authorizePayment using:
klarna_network_session_token in the Klarna-Network-Session-Token request headeramount, currency, supplementary_purchase_data, klarna_network_data) used in the initial authorizationpayment_transaction_reference to link the requestsExample finalization request:
curl https://api-global.test.klarna.com/v2/accounts/{partner_account_id}/payment/authorize \
-H 'Authorization: Basic <API key>' \
-H 'Content-Type: application/json' \
-H 'Klarna-Network-Session-Token: krn:network:us1:test:session-token:eyJhbGciOiJIU...' \
-d '{
"currency": "USD",
"supplementary_purchase_data": { ... },
"klarna_network_data": "{\"content_type\":\"vnd.klarna.network-data.v1+json\",\"content\":{\"operation\":\"payment_request\",\"request\":{\"supplementary_purchase_data\":{...}}}",
"request_payment_transaction": {
"amount": 11800,
"payment_option_id": "S0xBUk5BXzE3NzI3MjQ5MTQzMjk=",
"payment_transaction_reference": "backend-transaction-ref-12345"If the klarna_network_session_token has expired (1-hour validity) or the payment context doesn't match the initial authorization, the finalization call returns DECLINED.
When finalization succeeds, Klarna returns APPROVED with the payment transaction:
{
"payment_transaction_response": {
"result": "APPROVED",
"payment_transaction": {
"payment_transaction_id": "krn:payment:us1:transaction:791bbb7b-813a-[...]",
"amount": 11800,
"currency": "USD",
"payment_pricing": {...},
"payment_funding": {
"type": "GUARANTEED",
"state": "FUNDED"
}Map the Klarna authorization result to the Partner-facing API response:
klarna_network_response_dataRequirements
klarna_network_response_data in the Partner-facing API to ensure Partners can easily identify and use the parameter{
"checkout_id": "cs_27234RBQD9NAKD032BN",
"amount": 17800,
"status": "completed",
"currency": "USD",
"additional_data": {
"klarna_network_response_data": "{\"content_type\":\"vnd.klarna.network-data.v1+json\",\"content\":{\"operation\":\"payment_request\",\"response\":{\"result\":\"APPROVED\",\"payment_transaction\":{\"payment_transaction_id\":\"krn:payment:us1:transaction:791bbb7b-813a-[...]\"}}}}"
}
}After implementing the backend authorization endpoint, complete your integration: