"Creates a Klarna payment button that handles the customer flow.
You provide an initiate callback that returns either a paymentRequestUrl
(created server-side) or PaymentRequestData (created client-side),
and Klarna takes care of launching the purchase flow.
Register event handlers (complete, error, abort) before mounting
the button so that all events are captured from the first interaction."
// 1. Register event handlers first
klarna.Payment.on("complete", async (paymentRequest) => {
console.log("Payment completed:", paymentRequest.state);
});
klarna.Payment.on("error", async (error) => {
console.error("Payment error:", error);
});
// 2. Create and mount the button
klarna.Payment.button({
initiate: async ({ klarnaNetworkSessionToken }) => {
// Create the payment request on your server
const response = await fetch("/api/create-payment-request", {
method: "POST",
body: JSON.stringify({ klarnaNetworkSessionToken }),
});
const { paymentRequestUrl } = await response.json();
return { paymentRequestUrl };
},
}).mount("#klarna-button-container");
"Cancels the payment request with the given ID. It's only possible to cancel payment requests created by the same client/credentials.
Pass the payment request URL received from the payment request creation response.
Use this when the customer clears their cart, starts a new checkout session, or you otherwise need to invalidate an in-progress payment request."
// Cancel an existing payment request when the customer empties their cart
const cancelledRequest = await klarna.Payment.cancel("kr_payment_req_123");
console.log("Payment request cancelled, state:", cancelledRequest.state);
"Fetches the current state of a payment request. It's only possible to fetch payment requests created by the same client/credentials.
Pass the payment request URL received from the payment request creation response.
This is useful in redirect flows where the customer returns to your site and you need to check the payment request state to determine the outcome."
// Fetch by payment request Url
const paymentRequest = await klarna.Payment.fetch("https://pay.klarna.com/l/...");
if (paymentRequest.state === "COMPLETED") {
const token = paymentRequest.stateContext?.klarnaNetworkSessionToken;
// Send the token to your backend for authorization
await authorizePayment(token);
} else if (paymentRequest.state === "DECLINED") {
console.error("Payment declined:", paymentRequest.stateReason);
}
Optionaloptions: PaymentRequestOptions"Initiates a payment with Klarna. Use this method only if you absolutely need to own your
button. Otherwise, use the {@docs-portal-link Payment.button} method. If you use this method
along with initiationMode of DEVICE_BEST or ON_PAGE, it has to be called right after
the button is clicked. If there are long running operations such as network requests, you
can do that within the callback."
// Prefer Payment.button() — it handles click timing and popup blockers automatically.
// Use initiate() only if you need full control over your own button.
// Call initiate immediately after a click event.
// Move network requests inside the callback to avoid browser popup blockers.
let isInitiating = false;
// Suppose "payWithKlarnaButton" is your checkout button
payWithKlarnaButton.addEventListener('click', async () => {
if (isInitiating) {
return;
}
try {
isInitiating = true;
payWithKlarnaButton.disabled = true;
await klarna.Payment.initiate(async ({ klarnaNetworkSessionToken }) => {
// Do network requests inside the callback.
// The klarnaNetworkSessionToken can optionally be sent to your backend
// for server-side payment request creation.
const paymentData = await createPaymentSession(klarnaNetworkSessionToken);
return {
currency: "USD",
amount: paymentData.amount,
customerInteractionConfig: {
returnUrl: "https://example.com/success",
},
};
}, {
initiationMode: "DEVICE_BEST",
});
} catch (error) {
console.error('Payment initiation failed:', error);
} finally {
isInitiating = false;
payWithKlarnaButton.disabled = false;
}
});
"Unregister an event handler for the given event. Pass the same function
reference that was used when registering with on(). This is useful for
cleanup when your component unmounts or when you need to swap handlers."
// Keep a reference to the handler so it can be removed later
const onComplete = async (paymentRequest) => {
await handleCompletion(paymentRequest);
return true;
};
// Register
klarna.Payment.on("complete", onComplete);
// Later, unregister (e.g. on component unmount)
klarna.Payment.off("complete", onComplete);
"Register an event handler for the complete event. A payment request is
completed when the customer has approved the purchase.
During a redirection flow the complete handler will trigger once your
page has loaded on the success page. Pending updates are triggered even if
the event handler is registered after the page has loaded to avoid the
need for polling.
Return false from the callback to prevent automatic redirection to the
returnUrl (e.g. if you want to show a custom confirmation screen)."
klarna.Payment.on("complete", async (paymentRequest) => {
const token = paymentRequest.stateContext?.klarnaNetworkSessionToken;
// Send the token to your backend for authorization
await fetch("https://example.com/api/authorize", {
method: "POST",
body: JSON.stringify({ klarnaNetworkSessionToken: token }),
});
// Return false to prevent automatic redirection to returnUrl
return false;
});
"Register an event handler for the error event. Anytime an error occurs
during the payment request lifecycle, the error event is triggered. If the
error event handler is defined, all errors will be emitted to it instead
of thrown upwards.
The error parameter may be a PaymentError (with structured
errorCode and errorMessage fields) or a generic Error."
klarna.Payment.on("error", async (error, paymentRequest) => {
if ("errorCode" in error) {
// Structured PaymentError
console.error(`Payment error [${error.errorCode}]: ${error.errorMessage}`);
} else {
// Generic Error
console.error("Unexpected error:", error.message);
}
if (paymentRequest) {
console.log("Payment request state:", paymentRequest.state);
}
});
"Register an event handler for the abort event. This fires when the
customer cancels the payment request by closing the popup or otherwise
stopping the process. This is distinct from {@docs-portal-link Payment.cancel}, which is
initiated programmatically by the integrator."
"The shipping address changed event handler is triggered when the customer changes their shipping address during the payment flow. If the shipping address is present in the customer account, the event will be triggered immediately after the customer has logged in. Here, you are given the opportunity to accept or reject the shipping address.
If you did not return anything during the 10 seconds after receiving this event, the customer will see the error screen and payment request will be aborted."
klarna.Payment.on("shippingaddresschange", (paymentRequest, shippingAddress) => {
// 1. Accept — return a list of shipping options.
// The customer will be prompted to select one.
if (canShipTo(shippingAddress)) {
return {
shippingOptions: [
{
shippingOptionReference: "standard",
amount: 500,
displayName: "Standard Shipping",
description: "3-5 business days",
},
{
shippingOptionReference: "express",
amount: 1500,
displayName: "Express Shipping",
description: "1-2 business days",
shippingType: "EXPRESS",
},
],
// 2. Accept with preselection — return selectedShippingOptionReference along
// with updated amount and lineItems to skip the selection screen.
amount: totalWithShipping,
lineItems: [
...lineItems,
{ name: "Standard Shipping", quantity: 1, totalAmount: 500 },
],
selectedShippingOptionReference: "standard",
};
}
// 3. Reject — return a rejectionReason to prompt the customer for a new address.
// Available reasons: POSTAL_CODE_NOT_SUPPORTED, CITY_NOT_SUPPORTED,
// REGION_NOT_SUPPORTED, COUNTRY_NOT_SUPPORTED, ADDRESS_NOT_SUPPORTED.
return {
rejectionReason: "COUNTRY_NOT_SUPPORTED",
};
});
Shipping option selected event handler callback
"The shipping option selected event handler is triggered when the customer selects a shipping option during the payment flow. Here, you are given the opportunity to accept or reject the shipping option.
If you did not return anything during the 10 seconds after receiving this event, the customer will see the error screen and payment request will be aborted."
klarna.Payment.on("shippingoptionselect", async (paymentRequest, shippingOption) => {
// 1. Accept — return the updated order payload with the selected shipping
// cost added to amount and lineItems.
if (isValidOption(shippingOption)) {
const { amount } = await fetch("/cart", {
method: "POST",
body: JSON.stringify({ shipping_option: shippingOption }),
});
return {
amount,
lineItems: [
...lineItems,
{ name: "Shipping", quantity: 1, totalAmount: 500 },
],
};
}
// 2. Reject — return a rejectionReason to prompt the customer to select
// a different option. Available reasons: INVALID_OPTION.
return {
rejectionReason: "INVALID_OPTION",
};
});
"Returns instructions on how Klarna should be presented in the payment selector
and UI components to render.
Use the instruction field to determine whether Klarna should be shown, preselected,
shown exclusively, or hidden. Then mount the returned UI components (icon, header,
subheader, message, and paymentButton) into your checkout page.
The paymentButton is the component the customer clicks to start the Klarna purchase flow."
// Fetch the presentation, handle the instruction, and mount Klarna UI components.
const paymentPresentation = await klarna.Payment.presentation({
amount: 11800,
currency: "USD",
locale: "en-US",
intent: "PAY",
});
if (paymentPresentation) {
switch (paymentPresentation.instruction) {
case "SHOW_KLARNA":
renderKlarnaOption(paymentPresentation);
break;
case "PRESELECT_KLARNA":
renderKlarnaOption(paymentPresentation);
break;
case "SHOW_ONLY_KLARNA":
document.getElementById("other-payment-methods")?.remove();
renderKlarnaOption(paymentPresentation);
break;
case "HIDE_KLARNA":
document.getElementById("klarna-option")?.remove();
break;
default:
renderKlarnaOption(paymentPresentation);
break;
}
}
function renderKlarnaOption(paymentPresentation) {
const klarnaContainer = document.getElementById("klarna-option");
if (!klarnaContainer) {
return;
}
const { paymentOption } = paymentPresentation;
if (!paymentOption) {
return;
}
klarnaContainer.innerHTML = `
<div id="klarna-icon-container"></div>
<div id="klarna-header-container"></div>
<div id="klarna-subheader-container"></div>
<div id="klarna-message-container"></div>
<div id="klarna-button-container"></div>
`;
// Mount Klarna UI components
paymentOption.icon
.component({ shape: "badge" })
.mount("#klarna-icon-container");
paymentOption.header
.component()
.mount("#klarna-header-container");
paymentOption.subheader
.component()
.mount("#klarna-subheader-container");
if (paymentOption.message) {
paymentOption.message
.component()
.mount("#klarna-message-container");
}
// Mount the payment button — the customer clicks this to start the Klarna flow
paymentOption.paymentButton
.component({
initiate: async ({ klarnaNetworkSessionToken, paymentOptionId }) => {
const response = await fetch("/api/create-payment-request", {
method: "POST",
body: JSON.stringify({ klarnaNetworkSessionToken, paymentOptionId }),
});
const { paymentRequestUrl } = await response.json();
return { paymentRequestUrl };
},
})
.mount("#klarna-button-container");
}
The Klarna Payment interface provides access to the payment request API.
Accepting a payment using the Klarna Payment button:
Example Group
label="Example"
Example: Typescript
Example: HTML
Package Interface