Skip to main content
Business & GrowthPayment Services286 lines

Klarna

Accept payments with Klarna. Use this skill when the project needs to integrate Klarna for buy-now-pay-later, installment plans, pay-in-30-days, or direct payments. Covers Klarna Payments API, Klarna Checkout, hosted payment page, order management, webhooks, and settlement.

Quick Summary27 lines
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: 286 lines
Paste into your CLAUDE.md or agent config

Klarna 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

EventAction
order.approvedOrder authorized, ready to capture
order.capturedFunds captured, confirm fulfillment
order.cancelledOrder cancelled
order.refundedRefund processed
fraud_risk.stoppedKlarna rejected the order
dispute.createdChargeback 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_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

Anti-Patterns

  • Not capturing orders — Klarna will auto-release the authorization and you lose the sale
  • Omitting order_lines or 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

Get CLI access →

Related Skills

Lemonsqueezy

Accept payments with Lemon Squeezy as merchant of record. Use this skill when the project needs to integrate Lemon Squeezy for SaaS subscriptions, digital product sales, license key management, checkout overlay, tax compliance, or affiliate programs. Covers the Lemon Squeezy API, checkout, subscriptions, license keys, webhooks, and Lemon.js.

Payment Services201L

Mollie

Accept payments with Mollie. Use this skill when the project needs to integrate Mollie for European payments, iDEAL, Bancontact, SEPA, Klarna, subscriptions, payment links, or multi-currency checkout. Covers the Mollie API, payments, subscriptions, mandates, refunds, and webhooks. Popular in Netherlands, Belgium, Germany, and across Europe.

Payment Services170L

Paddle

Accept payments with Paddle as merchant of record. Use this skill when the project needs to integrate Paddle for subscription billing, one-time payments, checkout overlay, tax compliance, invoicing, or global payment processing where Paddle handles tax, compliance, and payouts. Covers Paddle Billing API, checkout, subscriptions, transactions, webhooks, and Paddle.js.

Payment Services213L

Paypal

Accept payments with PayPal. Use this skill when the project needs to integrate PayPal for checkout, subscriptions, one-time payments, PayPal buttons, venmo, invoicing, payouts, or PayPal REST API. Covers the PayPal JavaScript SDK, REST API v2, Orders, Subscriptions, Webhooks, and Smart Payment Buttons.

Payment Services224L

Razorpay

Accept payments with Razorpay. Use this skill when the project needs to integrate Razorpay for payments in India and international markets, UPI, subscriptions, payment links, invoicing, route (split payments), or Razorpay Checkout. Covers the Razorpay API, Checkout.js, orders, subscriptions, webhooks, and UPI.

Payment Services223L

Square

Accept payments with Square. Use this skill when the project needs to integrate Square for online payments, in-person POS, invoicing, subscriptions, catalog management, or omnichannel commerce. Covers the Square API, Web Payments SDK, Orders, Subscriptions, Invoices, Catalog, and webhooks.

Payment Services218L