Klarna
Accept payments with Klarna. Use this skill when the project needs to integrate
You are a payments specialist who integrates Klarna into projects. Klarna is a
buy-now-pay-later (BNPL) provider that also offers direct payments, installment
plans, and a full hosted checkout. Klarna takes on the credit risk — merchants
get paid upfront regardless of the customer's payment schedule.
## Key Points
- Portal: `portal.playground.klarna.com`
- API: `api.playground.klarna.com`
- Test persona for US: use any name/address, Klarna auto-approves in playground
- Test with different countries to see available payment methods change
- Capture orders promptly — Klarna authorizations expire (typically 28 days)
- For digital goods, capture immediately after order placement
- Always include `order_lines` with accurate item details — Klarna uses them for risk assessment
- Use `merchant_reference1` to link Klarna orders to your internal order IDs
- Provide accurate `purchase_country` — it determines which payment methods appear
- Handle `fraud_risk.stopped` — Klarna may reject orders after initial approval
- Test with multiple countries in playground to verify localized payment methods
- Not capturing orders — Klarna will auto-release the authorization and you lose the sale
## Quick Example
```typescript
await klarnaRequest('POST', `/ordermanagement/v1/orders/${klarnaOrderId}/cancel`);
```skilldb get payment-services-skills/KlarnaFull skill: 287 linesKlarna Payment Integration
You are a payments specialist who integrates Klarna into projects. Klarna is a buy-now-pay-later (BNPL) provider that also offers direct payments, installment plans, and a full hosted checkout. Klarna takes on the credit risk — merchants get paid upfront regardless of the customer's payment schedule.
Core Philosophy
Klarna pays you upfront
When a customer chooses "Pay in 3" or "Pay later", Klarna pays the merchant immediately. Klarna collects from the customer over time. This eliminates credit risk for the merchant.
Two integration paths
Klarna Payments embeds Klarna as a payment option alongside other methods. Klarna Checkout is a full hosted checkout that handles the entire purchase flow. Choose Payments for flexibility, Checkout for speed.
Orders must be acknowledged and captured
Klarna separates authorization from capture. You must capture (acknowledge the order) when you ship goods. For digital goods, capture immediately.
Setup
API credentials
Klarna uses HTTP Basic Auth with your API credentials (username/password) from the Klarna Merchant Portal. There is no official Node.js SDK — use HTTP directly or a wrapper library.
const KLARNA_API_BASE = process.env.KLARNA_ENV === 'production'
? 'https://api.klarna.com'
: 'https://api.playground.klarna.com';
const KLARNA_AUTH = Buffer.from(
`${process.env.KLARNA_USERNAME}:${process.env.KLARNA_PASSWORD}`
).toString('base64');
async function klarnaRequest(method: string, path: string, body?: object) {
const res = await fetch(`${KLARNA_API_BASE}${path}`, {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${KLARNA_AUTH}`,
},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) throw new Error(`Klarna ${res.status}: ${await res.text()}`);
return res.status === 204 ? null : res.json();
}
Key Techniques
Klarna Payments — Create a session
const session = await klarnaRequest('POST', '/payments/v1/sessions', {
purchase_country: 'US',
purchase_currency: 'USD',
locale: 'en-US',
order_amount: 5000, // $50.00 in minor units
order_tax_amount: 0,
order_lines: [
{
name: 'Pro Plan — Monthly',
quantity: 1,
unit_price: 5000,
total_amount: 5000,
tax_rate: 0,
total_tax_amount: 0,
},
],
});
// Return session.client_token and session.payment_method_categories to frontend
Frontend — Klarna Payments widget
<script src="https://x.klarnacdn.net/kp/lib/v1/api.js"></script>
<div id="klarna-container"></div>
<script>
Klarna.Payments.init({ client_token: clientToken });
// Load the widget for a specific payment method
Klarna.Payments.load(
{
container: '#klarna-container',
payment_method_category: 'pay_later', // or 'pay_over_time', 'direct_debit'
},
(res) => {
if (res.show_form) {
// Widget loaded successfully
}
}
);
// When user clicks "Place Order"
async function authorize() {
Klarna.Payments.authorize(
{ payment_method_category: 'pay_later' },
{
purchase_country: 'US',
purchase_currency: 'USD',
locale: 'en-US',
order_amount: 5000,
order_tax_amount: 0,
order_lines: [
{
name: 'Pro Plan — Monthly',
quantity: 1,
unit_price: 5000,
total_amount: 5000,
tax_rate: 0,
total_tax_amount: 0,
},
],
},
async (res) => {
if (res.approved) {
// Send res.authorization_token to your server to place the order
await fetch('/api/klarna/place-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ authorizationToken: res.authorization_token }),
});
}
}
);
}
</script>
Place an order (server-side)
const order = await klarnaRequest(
'POST',
`/payments/v1/authorizations/${authorizationToken}/order`,
{
purchase_country: 'US',
purchase_currency: 'USD',
locale: 'en-US',
order_amount: 5000,
order_tax_amount: 0,
order_lines: [
{
name: 'Pro Plan — Monthly',
quantity: 1,
unit_price: 5000,
total_amount: 5000,
tax_rate: 0,
total_tax_amount: 0,
},
],
merchant_reference1: orderId,
merchant_urls: {
confirmation: 'https://yourdomain.com/confirmation',
notification: 'https://yourdomain.com/api/klarna/webhook',
},
}
);
// Store order.order_id for capture/refund
Capture an order (on fulfillment)
await klarnaRequest('POST', `/ordermanagement/v1/orders/${klarnaOrderId}/captures`, {
captured_amount: 5000,
description: 'Shipped order',
order_lines: [
{
name: 'Pro Plan — Monthly',
quantity: 1,
unit_price: 5000,
total_amount: 5000,
tax_rate: 0,
total_tax_amount: 0,
},
],
});
Refund a capture
await klarnaRequest('POST', `/ordermanagement/v1/orders/${klarnaOrderId}/refunds`, {
refunded_amount: 5000,
description: 'Customer requested refund',
order_lines: [
{
name: 'Pro Plan — Monthly',
quantity: 1,
unit_price: 5000,
total_amount: 5000,
tax_rate: 0,
total_tax_amount: 0,
},
],
});
Cancel an uncaptured order
await klarnaRequest('POST', `/ordermanagement/v1/orders/${klarnaOrderId}/cancel`);
Webhook Processing
| Event | Action |
|---|---|
order.approved | Order authorized, ready to capture |
order.captured | Funds captured, confirm fulfillment |
order.cancelled | Order cancelled |
order.refunded | Refund processed |
fraud_risk.stopped | Klarna rejected the order |
dispute.created | Chargeback initiated |
export async function POST(req: Request) {
const body = await req.json();
// Klarna webhooks are verified by registering a webhook URL in the portal
// and optionally checking the Klarna-Idempotency-Key header
switch (body.event_type) {
case 'order.approved':
// For digital goods, capture immediately
await captureOrder(body.event_data.order_id);
break;
case 'order.captured':
await markOrderFulfilled(body.event_data.order_id);
break;
case 'fraud_risk.stopped':
await cancelInternalOrder(body.event_data.order_id);
break;
case 'dispute.created':
await handleDispute(body.event_data.order_id);
break;
}
return new Response('OK', { status: 200 });
}
Testing
Klarna playground environment:
- Portal:
portal.playground.klarna.com - API:
api.playground.klarna.com - Test persona for US: use any name/address, Klarna auto-approves in playground
- Test with different countries to see available payment methods change
Best Practices
- Capture orders promptly — Klarna authorizations expire (typically 28 days)
- For digital goods, capture immediately after order placement
- Always include
order_lineswith accurate item details — Klarna uses them for risk assessment - Use
merchant_reference1to link Klarna orders to your internal order IDs - Provide accurate
purchase_country— it determines which payment methods appear - Handle
fraud_risk.stopped— Klarna may reject orders after initial approval - Test with multiple countries in playground to verify localized payment methods
Anti-Patterns
- Not capturing orders — Klarna will auto-release the authorization and you lose the sale
- Omitting
order_linesor using placeholder data — increases decline rates - Assuming all payment methods are available everywhere — availability varies by country
- Ignoring Klarna's fraud rejection webhooks — you ship goods that Klarna won't pay for
- Using Klarna for subscription billing without separate authorization per cycle
- Capturing more than the authorized amount — this will fail
- Not testing with the playground environment — live credentials have real financial impact
Install this skill directly: skilldb add payment-services-skills
Related Skills
Adyen
Accept payments with Adyen. Use this skill when the project needs to integrate
Braintree
Accept payments with Braintree (PayPal). Use this skill when the project needs
Checkout Com
Accept payments with Checkout.com. Use this skill when the project needs to
Coinbase Commerce
Accept cryptocurrency payments with Coinbase Commerce. Use this skill when the
Creem
Accept payments with Creem as merchant of record. Use this skill when the project
Lemonsqueezy
Accept payments with Lemon Squeezy as merchant of record. Use this skill when