Klarna

Charge an on-demand payment via SDK

Charge customer-initiated on-demand purchases with the customer token using the Klarna Payments JavaScript SDK, including support for step-up when customer interaction is required to authorize the purchase.

Overview

Charge a customer-initiated on-demand purchase with an existing Klarna customer token, using the Klarna Payments JavaScript SDK to authorize the charge. Klarna aims to authorize immediately so the customer can continue without friction. Your integration must handle two outcomes:
  1. 1.
    Klarna authorizes immediately — you fulfil the order.
  2. 2.
    Klarna requires a step-up — to protect the customer or satisfy risk / compliance requirements, Klarna returns a step_up_id; the SDK hands off to the Klarna Purchase Journey to collect the customer's authorization (login, OTP, alternative funding source).
Partner checkout
Klarna Purchase Journey
Partner confirmation
1. Partner checkout — customer confirms cart and selects Klarna2. Klarna Purchase Journey — shown only if Klarna requires step-up3. Partner confirmation — order placed; customer token updated
Using Hosted Payment Page instead? See Charge an on-demand payment via HPP.

Prerequisites

  • An active customer token (check status) issued from on-demand signup (intent: tokenize or buy_and_tokenize, no subscription object).
  • The customer is present at the moment of charge.
  • The Klarna Payments JavaScript SDK loaded on every page where step-up may run — not only the original signup checkout page.
  • Klarna Payments API basic-auth credentials.

Integration overview

sequenceDiagram autonumber participant Customer participant FE as Merchant Frontend participant SDK as Klarna Payments SDK participant BE as Merchant Backend participant Klarna as Klarna Payments API Customer->>FE: Trigger on-demand purchase FE->>BE: Submit confirmed cart BE->>Klarna: createOrder on customer token (step_up: SUPPORTED) alt No step-up required Klarna->>BE: 200 + order_id BE->>FE: Order confirmed FE->>Customer: Confirmation else Step-up required Klarna->>BE: Response with step_up_id BE->>Klarna: createCreditSession (step_up_id, customer_token, same cart) Klarna->>BE: client_token BE->>FE: client_token FE->>SDK: Klarna.Payments.init(client_token) FE->>SDK: Klarna.Payments.authorize SDK->>Customer: Klarna Purchase Journey (login / OTP / pick funding source) Customer->>SDK: Completes step-up par Authoritative path (mandatory) Klarna-->>BE: POST authorization_token to merchant_urls.authorization and Complementary path SDK-->>FE: authorization_token in SDK callback FE->>BE: Notify completion (optional UX signal) end BE->>Klarna: createOrder (authorization_token from server-side callback) Klarna->>BE: 200 + order_id Note over Klarna: Customer token updated with confirmed funding source BE->>FE: Order confirmed FE->>Customer: Confirmation end

Integration details

Step 1: Present the on-demand purchase to the customer

Before charging, your frontend shows the customer the standard purchase-confirmation surface they expect — a one-tap checkout sheet, a wallet drawer, a "Buy now" page, or whatever your flow uses. The cart, total, currency, and the saved Klarna payment method must be visible so the customer can confirm before you call Klarna.
The Klarna Payments JavaScript SDK must already be loaded on this surface. Do not lazy-load it after the charge response — by the time Klarna returns step_up_id, the SDK must be ready to mount the Klarna Purchase Journey.
Include the SDK script tag with the rest of your initial page assets:
HTML
1
<script src="https://x.klarnacdn.net/kp/lib/v1/api.js" async></script>
The SDK exposes window.Klarna.Payments once loaded. Defer the createOrder call (Step 2) until the script is on the page on initial render — or, if you really must inject it dynamically, until you've awaited its onload.

Step 2: Call create order on the customer token

When the customer confirms the purchase, your backend calls createOrder on the customer tokenAPI with step_up: SUPPORTED. This signals to Klarna that you are ready to handle a step-up if Klarna decides to run one.
Send a unique Klarna-Idempotency-Key per charge attempt. Reuse the same key when retrying the same logical charge after a network failure — never reuse it across distinct charge attempts.
Sample request
JSON
1 2 3 4 5 6 7 8 9 10
POST /customer-token/v1/tokens/{customerToken}/order Authorization: Basic <credentials> Klarna-Idempotency-Key: a29f321c-8f38-11ec-b909-0242ac120003 Content-Type: application/json { "purchase_country": "US", "purchase_currency": "USD", "locale": "en-US", "auto_capture": true,
You must set step_up: SUPPORTED on every on-demand charge — i.e. every time the customer is present at the moment of charge. Without it, Klarna cannot trigger step-up even when it would otherwise approve the charge with one, and you will see avoidable declines. Klarna still decides whether step-up actually runs based on the current request — your job is to be ready to handle either outcome on the same flow.

Step 3: Handle the response

Klarna evaluates the request and returns one of the outcomes below. Branch on whether the response contains step_up_id.
ResultWhat you receiveNext steps
Successorder_id, fraud_status: ACCEPTED, authorized_payment_method, redirect_urlFulfill the order. Store order_id. No further calls required — the flow ends here.
Step-up requiredstep_up_id, error_code: UNAVAILABLE_PAYMENT_METHOD, fraud_status: REJECTED, authorized_payment_methodKlarna needs additional customer interaction. Continue with Steps 4–6 to create a step-up session and complete the charge through the SDK.
Other 4xxerror_code, error_messages, correlation_idReturn the customer to the checkout surface and surface a clear failure message. See validations in Klarna Payments for code-by-code handling.
5xxTransient errorRetry with the same Klarna-Idempotency-Key. Do not generate a new key on retry.
JSON
1 2 3 4 5 6 7 8
{ "order_id": "3eaeb557-5e30-47f8-b840-b8d987f5945d", "redirect_url": "https://payments.klarna.com/redirect/...", "fraud_status": "ACCEPTED", "authorized_payment_method": { "type": "invoice" } }
The step-up response carries fraud_status: REJECTED and an error_code — this is the API contract for step-up signaling, not a final decline. The presence of step_up_id is the only signal that matters: when it is present, continue to Step 4. When it is absent on a non-200 response, treat the response as a final failure.

Step 4: Create a step-up Klarna Payments session

When the response contains step_up_id, your backend creates a fresh Klarna Payments session referencing that step_up_id and the customer token. Call create sessionAPI with the same cart that you used in Step 2 and the same intent you used when the token was created (typically buy_and_tokenize).
Sample request
JSON
1 2 3 4 5 6 7 8 9 10
POST /payments/v1/sessions Authorization: Basic <credentials> Content-Type: application/json { "acquiring_channel": "ECOMMERCE", "intent": "buy_and_tokenize", "step_up_id": "06826d76-c2f4-4378-b586-2751e6f84adf", "customer": { "customer_token": "<customer_token>"
Klarna returns a client_token you pass to the SDK. Store the session_id so you can correlate the eventual order with the original createOrder attempt.

Step 5: Initialize the SDK and authorize

On the same frontend surface where the customer was already confirming the purchase, initialize the SDK with the client_token and call Klarna.Payments.authorize. The SDK takes over: it presents the Klarna Purchase Journey and lets the customer authenticate or pick an alternative Klarna payment method.
The Klarna Purchase Journey is the surface where the customer completes step-up — sign-in, OTP entry, or picking an alternative Klarna payment method. The Klarna Payments SDK launches it inline (or as a redirect on devices where inline is not possible).
If the customer is declined inside the Purchase Journey, the SDK callback returns approved: false. Return the customer to the checkout surface so they can choose another payment method — do not retry the same charge silently.
Klarna Purchase Journey — Confirm and pay (step-up)
Sample SDK call
JAVASCRIPT
1 2 3 4 5 6 7 8 9 10
Klarna.Payments.init({ client_token: "<client_token>" }); Klarna.Payments.authorize( {}, { shipping_address: { given_name: "Jane", family_name: "Doe",
The merchant_urls.authorization callback is mandatory and authoritative. When the customer completes step-up, Klarna POSTs the authorization_token to your registered server-side callback URL — and that is the source you must use to drive createOrder. The SDK callback (res.authorization_token) is a complementary low-latency signal you can use to update UI immediately, but it is not reliable on its own: closed tabs, network interruptions, ad blockers, and browser sandboxing can all drop it.
Callback request from Klarna
HTTP
1 2 3 4 5 6 7
POST https://example.com/authorization_callbacks Content-Type: application/json { "authorization_token": "1eddf502-f3a0-45bf-b1fd-f2e3a2758200", "session_id": "e4b81ca2-0aae-4c16-bcb2-29a0a088a35b" }
Callback contract you must implement
AspectRequirement
TransportHTTPS endpoint registered on merchant_urls.authorization.
Timeout2 s connection + 2 s read — return a 2xx response within this window.
AcknowledgementAny 2xx response (e.g. 204 No Content) means "received".
Retries on non-2xxKlarna retries up to 3 times with exponential backoff starting at 1 s.
Delivery semanticsAt-least-once. The same authorization_token may arrive multiple times (retries, or in parallel with the SDK callback). Your handler must be idempotent — drive createOrder exactly once per token.
Token lifetimeauthorization_token is valid for 60 minutes — call createOrder from the handler promptly.
AuthenticationDo not require auth on the URL. To verify origin, embed a one-time secret in the URL query string (e.g. ?secretToken=…) generated per session.
Multiple authorizationsIf the first createOrder attempt does not succeed and the customer re-authorizes, Klarna may send a new callback with a different authorization_token. Always use the latest token.

Step 6: Create the order with the authorization token

Your merchant_urls.authorization endpoint receives a POST from Klarna with the authorization_token once the customer completes step-up. From that handler, call create orderAPI on /payments/v1/authorizations/{authorizationToken}/order with the same cart you used in Step 2. Klarna creates the order, and the existing customer token is updated with the funding source the customer just confirmed in step-up — future on-demand charges on this token use the updated funding source.
Sample request
JSON
1 2 3 4 5 6 7 8 9 10
POST /payments/v1/authorizations/{authorization_token}/order Authorization: Basic <credentials> Content-Type: application/json { "purchase_country": "US", "purchase_currency": "USD", "locale": "en-US", "auto_capture": true, "order_amount": 9500,
Klarna returns one of the outcomes below.
JSON
1 2 3 4 5 6 7 8
{ "order_id": "3eaeb557-5e30-47f8-b840-b8d987f5945d", "redirect_url": "https://payments.klarna.com/redirect/...", "fraud_status": "ACCEPTED", "authorized_payment_method": { "type": "card" } }
Store order_id, fulfill the order, and surface confirmation to the customer. The customer token is updated with the funding source confirmed during step-up — future on-demand charges use this funding source.

Do's and don'ts

DoDon't
Set step_up: SUPPORTED on every on-demand charge — Klarna decides whether to run step-up.Treat the step-up response as a final failure — step_up_id means continue, not stop.
Load the Klarna Payments SDK wherever step-up may run, before you call createOrder.Lazy-load the SDK after the charge response — it must be ready when step_up_id arrives.
Pass step_up_id, customer_token, and the same cart and intent into createCreditSession.Change the cart between Step 2 and Step 4 — the customer confirmed Step 2's cart.
Drive createOrder from the merchant_urls.authorization server-side callback — that is the authoritative source of authorization_token.Rely on the SDK callback (res.authorization_token) as your only path — closed tabs, network drops, and ad blockers can kill it.
Use the SDK callback as a complementary signal to update UI immediately (e.g. show a spinner).Place the order from the frontend by forwarding res.authorization_token from the SDK to the backend — race conditions and dropped callbacks will produce missed orders.
Generate a unique Klarna-Idempotency-Key per charge attempt and reuse it only on retry of the same attempt.Reuse an idempotency key across distinct charge attempts.
Return the customer to the checkout surface when step-up is declined so they can pick another payment method.Silently retry the same charge after a step-up decline.