Klarna

Charge an on-demand payment via HPP

Charge customer-initiated on-demand purchases with the customer token using Klarna's Hosted Payment Page, 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 Hosted Payment Page (HPP) 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; you create an HPP session and redirect the customer to a Klarna-hosted page to complete step-up before returning to your success URL.
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 the JavaScript SDK instead? See Charge an on-demand payment via SDK.

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 — able to be redirected to Klarna's HPP and back within the session lifetime.
  • A working HPP integration with merchant_urls (back, cancel, error, failure, success, status_update) configured.
  • Klarna Payments API and HPP API basic-auth credentials.

Integration overview

sequenceDiagram autonumber participant Customer participant FE as Merchant Frontend participant BE as Merchant Backend participant HPP as Klarna HPP 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: session_id BE->>HPP: createHppSession (payment_session_url, merchant_urls, place_order_mode) HPP->>BE: redirect_url BE->>FE: redirect_url FE->>Customer: Redirect to Klarna HPP Customer->>HPP: Completes Klarna Purchase Journey (step-up) alt place_order_mode = PLACE_ORDER / CAPTURE_ORDER HPP->>Klarna: HPP places the order on behalf of merchant HPP->>Customer: Redirect to merchant_urls.success with order_id else place_order_mode = NONE par Authoritative path (mandatory) Klarna-->>BE: POST authorization_token to merchant_urls.authorization and Complementary path HPP->>Customer: Redirect to merchant_urls.success?token=… in URL Customer->>FE: Lands on success URL (UX signal) end BE->>Klarna: createOrder (authorization_token from server-side callback) Klarna->>BE: 200 + order_id end 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.

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.

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–7 to create a step-up session, redirect the customer to HPP, and complete the charge.
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>"
Store session_id from the response — you'll pass it to HPP in the next step.

Step 5: Create an HPP session

Call create HPP sessionAPI with payment_session_url pointing to the Klarna Payments session created in Step 4, your merchant_urls, and the place_order_mode that matches your integration.
place_order_modeWhat HPP does after the customer completes step-up
PLACE_ORDERHPP places the order on your behalf and redirects the customer to merchant_urls.success with order_id in the query string. No further API call from you.
CAPTURE_ORDERSame as PLACE_ORDER, plus HPP triggers capture immediately. Use when you do not need a separate capture step.
NONEHPP redirects the customer to merchant_urls.success with authorization_token in the query string. You then call createOrder with the authorization_token in Step 7.
Pick PLACE_ORDER (or CAPTURE_ORDER) when you want HPP to finalize the order and you do not need to run additional checks between authorization and order creation. Pick NONE when you need to validate the customer or cart server-side before placing the order.
Sample request
JSON
1 2 3 4 5 6 7 8 9 10
POST /hpp/v1/sessions Authorization: Basic <credentials> Content-Type: application/json { "payment_session_url": "https://api.klarna.com/payments/v1/sessions/<session_id>", "merchant_urls": { "back": "https://example.com/back", "cancel": "https://example.com/cancel", "error": "https://example.com/error",
HPP returns session_id and redirect_url. The redirect_url is what you send the customer to in the next step.

Step 6: Redirect the customer and track status

Send the customer to the HPP redirect_url. The customer authenticates on Klarna's hosted page, completes the step-up, and is redirected back to your merchant_urls.success URL when they are done. If they stop the flow, they go to merchant_urls.cancel instead.
The Klarna Purchase Journey is the surface where the customer completes step-up — sign-in, OTP entry, or picking an alternative Klarna payment method. With HPP, this happens on a Klarna-hosted page that the customer reaches via your redirect_url.
Track the HPP session status using HPP webhooks on your status_update URL or by polling, so you do not depend solely on the customer landing on your success URL.
If the customer is declined inside the Purchase Journey, HPP redirects them to merchant_urls.failure. Return them to the checkout surface to pick another payment method — do not retry the same charge silently.
Klarna Purchase Journey — Confirm and pay (step-up)

Step 7: Finalize the order

What you do here depends on the place_order_mode you set in Step 5.
place_order_modeWhat to do
PLACE_ORDER or CAPTURE_ORDERThe order is already created. The customer lands at merchant_urls.success with order_id in the query string. Fulfill the order. No further API call from you.
NONEDrive order creation from the merchant_urls.authorization server-side callback — Klarna POSTs authorization_token to that URL once step-up completes. From the callback handler, call create orderAPI on /payments/v1/authorizations/{authorizationToken}/order with the same cart. The ?token=… parameter on the customer's success redirect is a complementary UX signal; do not rely on it as your only path.
The merchant_urls.authorization callback is mandatory and authoritative in place_order_mode = NONE. Klarna POSTs the authorization_token to your registered server-side callback URL once step-up completes — and that is the source you must use to drive createOrder. The token included in the merchant_urls.success redirect is a low-latency UX signal you can use to update the page state, but customers may close the tab, drop network on the redirect hop, or be blocked by browser sandboxing. The server-side callback is delivered regardless.
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 HPP success redirect). 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.
On success, the 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 createOrder request (NONE mode)
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.
Configure merchant_urls.authorization on the Klarna Payments session (Step 4) and drive createOrder from that server-side callback in place_order_mode = NONE.Read authorization_token from the merchant_urls.success query string and drive createOrder from the browser — closed tabs and dropped redirects will produce missed orders.
Configure HPP merchant_urls (back, cancel, error, failure, success, status_update) and subscribe to HPP webhooks on status_update.Rely solely on the customer landing on merchant_urls.success — also use webhooks.
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.
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.
Pick place_order_mode based on whether you need server-side validation before order creation.Mix place_order_mode = PLACE_ORDER with a custom createOrder call — HPP already placed the order.
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.