> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gr4vy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Klarna

> Set up Klarna and charge recurring subscriptions using merchant-initiated transactions (MIT) with a saved payment method.

export const connector = {
  displayName: "Klarna",
  method: "klarna",
  features: "create_session create_transaction delayed_capture delete_token direct_integration_create multi_capture partial_capture partial_refunds payment_method_tokenization redirect_requires_popup refunds requires_webhook_setup verify_credentials void",
  supportedCountries: "AT AU BE CA CH DE DK ES FI FR GB IE IT NL NO PL PT SE US",
  supportedCurrencies: "AUD CAD CHF DKK EUR GBP NOK PLN SEK USD"
};

export const ConnectorRegions = ({data, kind, name: nameOverride}) => {
  const [query, setQuery] = useState("");
  const [open, setOpen] = useState(false);
  const isCountries = kind === "countries";
  const raw = data && (isCountries ? data.supportedCountries : data.supportedCurrencies);
  const codes = typeof raw === "string" ? raw.split(/\s+/).filter(Boolean) : Array.isArray(raw) ? raw : [];
  const DISPLAY_NAME_OVERRIDES = {
    authorizenet: "Authorize.net",
    cardpointe: "Fiserv CardPointe",
    dlocal: "dLocal",
    shift4i4go: "Shift4 i4go",
    tokenex: "TokenEx"
  };
  const rawName = data && data.displayName || "";
  const name = nameOverride || DISPLAY_NAME_OVERRIDES[rawName.toLowerCase()] || rawName || "This connector";
  const verb = isCountries ? "supports transactions from buyers in" : "supports processing payments in";
  const noun = isCountries ? "countries" : "currencies";
  if (codes.length === 0) return null;
  let displayNames = null;
  try {
    displayNames = new Intl.DisplayNames(["en"], {
      type: isCountries ? "region" : "currency"
    });
  } catch (e) {
    displayNames = null;
  }
  const resolve = code => {
    if (!displayNames) return null;
    try {
      const resolved = displayNames.of(code);
      return resolved && resolved !== code ? resolved : null;
    } catch (e) {
      return null;
    }
  };
  const MAJOR_CURRENCIES = ["USD", "EUR", "GBP", "CAD", "AUD", "JPY", "CHF", "CNY", "SGD", "HKD", "NZD", "SEK", "NOK", "DKK", "MXN", "BRL", "INR"];
  const items = codes.map(code => ({
    code,
    label: resolve(code)
  }));
  if (isCountries) {
    items.sort((a, b) => (a.label || a.code).localeCompare(b.label || b.code));
  } else {
    const rank = code => {
      const i = MAJOR_CURRENCIES.indexOf(code);
      return i === -1 ? MAJOR_CURRENCIES.length : i;
    };
    items.sort((a, b) => rank(a.code) - rank(b.code) || a.code.localeCompare(b.code));
  }
  if (codes.length <= 3) {
    const parts = items.map(it => isCountries || !it.label ? it.label || it.code : `${it.label} (${it.code})`);
    const joined = parts.length === 1 ? parts[0] : parts.length === 2 ? `${parts[0]} and ${parts[1]}` : `${parts.slice(0, -1).join(", ")}, and ${parts[parts.length - 1]}`;
    return <p>
        {name} {verb} {joined}.
      </p>;
  }
  const chipStyle = {
    display: "inline-flex",
    alignItems: "baseline",
    gap: "0.4rem",
    padding: "0.15rem 0.55rem",
    borderRadius: "0.375rem",
    border: "1px solid rgba(128, 128, 128, 0.25)",
    fontSize: "0.875rem",
    lineHeight: 1.5
  };
  const codeStyle = {
    fontFamily: "var(--font-mono, ui-monospace, monospace)",
    fontWeight: 600,
    fontSize: "0.8125rem"
  };
  const controlStyle = {
    color: "inherit",
    background: "transparent",
    border: "1px solid rgba(128, 128, 128, 0.3)",
    borderRadius: "0.5rem",
    fontSize: "0.875rem"
  };
  const renderChip = it => <span key={it.code} style={chipStyle} title={isCountries ? it.code : it.label || it.code}>
      {isCountries ? it.label || it.code : <span style={codeStyle}>{it.code}</span>}
      {!isCountries && it.label ? <span style={{
    opacity: 0.7
  }}>{it.label}</span> : null}
    </span>;
  const PREVIEW = 5;
  const collapsible = items.length > PREVIEW;
  const q = query.trim().toLowerCase();
  const filtered = q ? items.filter(it => it.code.toLowerCase().includes(q) || it.label && it.label.toLowerCase().includes(q)) : items;
  const expanded = open || q !== "";
  const visible = !collapsible ? items : expanded ? filtered : items.slice(0, PREVIEW);
  const toggle = () => {
    const next = !open;
    setOpen(next);
    if (!next) setQuery("");
  };
  return <div>
      <p>
        {name} {verb} the following {codes.length} {noun}:
      </p>

      {collapsible ? <input type="text" value={query} onChange={e => setQuery(e.target.value)} placeholder={`Filter ${noun}…`} aria-label={`Filter ${noun}`} style={{
    ...controlStyle,
    display: "block",
    width: "100%",
    maxWidth: "22rem",
    padding: "0.4rem 0.7rem",
    margin: "0 0 0.75rem"
  }} /> : null}

      <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.4rem"
  }}>
        {visible.map(renderChip)}
      </div>

      {q && filtered.length === 0 ? <p style={{
    opacity: 0.7,
    marginTop: "0.6rem"
  }}>
          No {noun} match “{query.trim()}”.
        </p> : null}
      {q && filtered.length > 0 ? <p style={{
    opacity: 0.6,
    fontSize: "0.8125rem",
    marginTop: "0.6rem"
  }}>
          Showing {filtered.length} of {items.length}.
        </p> : null}

      {collapsible && !q ? <button type="button" aria-expanded={open} onClick={toggle} style={{
    ...controlStyle,
    display: "inline-flex",
    alignItems: "center",
    gap: "0.4rem",
    padding: "0.35rem 0.75rem",
    marginTop: "0.75rem",
    cursor: "pointer"
  }}>
          <span aria-hidden="true" style={{
    display: "inline-block",
    transform: open ? "rotate(90deg)" : "none",
    transition: "transform 0.15s ease"
  }}>
            ›
          </span>
          {open ? "Show fewer" : `and ${items.length - PREVIEW} more`}
        </button> : null}
    </div>;
};

export const ConnectorCapabilities = ({data}) => {
  const CAPABILITIES = [{
    keys: ["three_d_secure_hosted"],
    label: "3-D Secure (hosted)",
    description: "Gr4vy-hosted 3DS authentication flow.",
    cardOnly: true
  }, {
    keys: ["three_d_secure_pass_through"],
    label: "3-D Secure (pass-through)",
    description: "Pass through 3DS data authenticated by a third party.",
    cardOnly: true
  }, {
    keys: ["partial_authorization"],
    label: "Partial authorization",
    description: "Support partial approval responses."
  }, {
    keys: ["zero_auth"],
    label: "Zero auth",
    description: "Verify a card without charging it."
  }, {
    keys: ["void"],
    label: "Void",
    description: "Cancel an authorized transaction before capture."
  }, {
    keys: ["direct_capture"],
    label: "Direct capture",
    description: "Capture a payment immediately at authorization.",
    hideWhenUnsupported: true
  }, {
    keys: ["delayed_capture"],
    label: "Delayed capture",
    description: "Authorize a payment and capture it at a later time."
  }, {
    keys: ["partial_capture"],
    label: "Partial capture",
    description: "Capture a portion of the authorized amount."
  }, {
    keys: ["over_capture"],
    label: "Over capture",
    description: "Capture more than the originally authorized amount."
  }, {
    keys: ["refunds"],
    label: "Refunds",
    description: "Refund a captured payment."
  }, {
    keys: ["partial_refunds"],
    label: "Partial refunds",
    description: "Refund a portion of the captured amount."
  }, {
    keys: ["settlement_reporting"],
    label: "Settlement reporting",
    description: "Automatic settlement and reconciliation reporting."
  }, {
    keys: ["create_session"],
    label: "Create session",
    description: "Create a connector session for client-side flows."
  }, {
    keys: ["network_tokens_default", "network_tokens_toggle"],
    label: "Network tokens",
    description: "Network-level tokenization for improved approval rates.",
    cardOnly: true
  }, {
    keys: ["digital_wallets"],
    label: "Digital wallets",
    description: "Apple Pay, Google Pay, and other wallet integrations."
  }, {
    keys: ["payment_method_tokenization", "payment_method_tokenization_toggle"],
    label: "Payment method tokenization",
    description: "Store payment methods outside of transactions."
  }, {
    keys: ["transaction_sync"],
    label: "Transaction sync",
    description: "Synchronize transaction state from the connector."
  }, {
    keys: ["create_token"],
    label: "Tokenization",
    description: "Create a token from card details collected via Secure Fields.",
    hideWhenUnsupported: true
  }, {
    keys: ["delete_token"],
    label: "Delete token",
    description: "Delete a stored token.",
    hideWhenUnsupported: true
  }, {
    keys: ["verify_credentials"],
    label: "Verify credentials",
    description: "Validate the configured credentials against the connector.",
    hideWhenUnsupported: true
  }];
  const raw = data && data.features;
  const enabled = typeof raw === "string" ? new Set(raw.split(/\s+/).filter(Boolean)) : Array.isArray(raw) ? new Set(raw) : new Set(Object.keys(raw || ({})).filter(key => raw[key]));
  const isOn = entry => entry.keys.some(key => enabled.has(key));
  const isNonCard = data && data.method && data.method !== "card";
  const renderGroup = (title, entries, supported) => {
    if (entries.length === 0) return null;
    const mark = supported ? "✓" : "✕";
    const markColor = supported ? "#16a34a" : "#9ca3af";
    return <div style={{
      marginTop: "1rem"
    }}>
        <div style={{
      fontSize: "0.75rem",
      fontWeight: 600,
      letterSpacing: "0.05em",
      textTransform: "uppercase",
      opacity: 0.6,
      marginBottom: "0.25rem"
    }}>
          {title}
        </div>
        {}
        <div role="list">
          {entries.map(entry => <div role="listitem" key={entry.label} style={{
      display: "flex",
      gap: "0.5rem",
      alignItems: "baseline",
      padding: "0.3rem 0",
      opacity: supported ? 1 : 0.7
    }}>
              <span aria-hidden="true" style={{
      color: markColor,
      fontWeight: 700,
      flexShrink: 0
    }}>
                {mark}
              </span>
              <span>
                <strong>{entry.label}</strong>
                {entry.description ? <span style={{
      opacity: 0.85
    }}> — {entry.description}</span> : null}
              </span>
            </div>)}
        </div>
      </div>;
  };
  const visible = isNonCard ? CAPABILITIES.filter(entry => !entry.cardOnly) : CAPABILITIES;
  const supported = visible.filter(isOn);
  const unsupported = visible.filter(entry => !isOn(entry) && !entry.hideWhenUnsupported);
  return <div>
      {renderGroup("Supported", supported, true)}
      {renderGroup("Not supported", unsupported, false)}
    </div>;
};

Klarna is a buy now, pay later (BNPL) payment method that offers flexible payment options and supports recurring payments through merchant-initiated transactions (MIT).

## Setup

A Klarna account can be requested from the
[Klarna Business Portal](https://portal.klarna.com/products/signup/basic-info).

## Credentials

To connect a Klarna account, obtain the following credentials from the Klarna Business Portal.

* **Username:** An API username which can be generated by navigating to **Preferences** → **Settings** → **Manage Klarna API Credentials**
* **Password:** An API password which can be generated by navigating to **Preferences** → **Settings** → **Manage Klarna API Credentials**
* **Region:** The region of the Klarna account. Enter `EU` for Europe, `NA` for North America, or `OC` for Oceania
* **Client ID** *(optional)*: Required only for [Express Checkout](#express-checkout). Obtain from your Klarna Business Portal and enter it here so Gr4vy can return it via the Create Session API to initialize the Klarna Payments Buttons SDK.

## Capabilities

<ConnectorCapabilities data={connector} />

## Supported countries

<ConnectorRegions data={connector} kind="countries" />

## Supported currencies

<ConnectorRegions data={connector} kind="currencies" />

## Integration

For Klarna, the default integration uses a redirect to the Klarna hosted payments page. A direct integration using the Klarna JavaScript SDK or mobile SDKs is also supported.

### Redirect integration

Start by creating a new transaction with the following required fields.

<CodeGroup>
  ```csharp C# theme={"system"}
  var transaction = await client.Transactions.CreateAsync(
    transactionCreate: new TransactionCreate()
    {
      Amount = 1299,
      Currency = "USD",
      Country = "US",
      PaymentMethod = new PaymentMethodRequest()
      {
        Method = "klarna",
        RedirectUrl = "https://example.com/callback",
        Country = "US",
        Currency = "USD",
      },
    }
  );
  ```

  ```go Go theme={"system"}
  amount := int64(1299)
  currency := "USD"
  country := "US"

  transactionCreate := components.TransactionCreate{
    Amount:   &amount,
    Currency: currency,
    Country:  &country,
    PaymentMethod: &components.PaymentMethodRequest{
      Method:      "klarna",
      RedirectUrl: "https://example.com/callback",
      Country:     "US",
      Currency:    "USD",
    },
  }

  transaction, err := client.Transactions.Create(ctx, transactionCreate, nil, nil)
  ```

  ```java Java theme={"system"}
  CreateTransactionResponse transactionResponse = gr4vyClient.transactions().create()
    .transactionCreate(TransactionCreate.builder()
      .amount(1299L)
      .currency("USD")
      .country("US")
      .paymentMethod(new PaymentMethodRequest.Builder()
        .method("klarna")
        .redirectUrl("https://example.com/callback")
        .country("US")
        .currency("USD")
        .build())
      .build())
    .call();

  Transaction transaction = transactionResponse.transaction().orElse(null);
  ```

  ```php PHP theme={"system"}
  $transactionCreate = new TransactionCreate(
    amount: 1299,
    currency: 'USD',
    country: 'US',
    paymentMethod: new PaymentMethodRequest(
      method: 'klarna',
      redirectUrl: 'https://example.com/callback',
      country: 'US',
      currency: 'USD',
    )
  );
  $response = $client->transactions->create($transactionCreate);
  $transaction = $response->transaction;
  ```

  ```python Python theme={"system"}
  transaction: models.Transaction = client.transactions.create(
    amount=1299,
    currency="USD",
    country="US",
    payment_method={
      "method": "klarna",
      "redirect_url": "https://example.com/callback",
      "country": "US",
      "currency": "USD",
    }
  )
  ```

  ```ts TypeScript theme={"system"}
  const transaction = await gr4vy.transactions.create({
    amount: 1299,
    currency: "USD",
    country: "US",
    paymentMethod: {
      method: "klarna",
      redirectUrl: "https://example.com/callback",
      country: "US",
      currency: "USD",
    },
  });
  ```
</CodeGroup>

After the transaction is created, the API response includes `payment_method.approval_url` and the `buyer_approval_pending` status.

```json theme={"system"}
{
  "type": "transaction",
  "id": "ea1efdd0-20f9-44d9-9b0b-0a3d71e9b625",
  "payment_method": {
    "type": "payment-method",
    "approval_url": "https://cdn.sandbox.spider.gr4vy.app/connectors/klarna/apm.html?token=..."
  },
  "method": "klarna"
}
```

Redirect the buyer to the `approval_url` to complete the Klarna approval flow. Once approved, the transaction moves to `authorization_succeeded`.

### Direct integration

Gr4vy supports direct integration with Klarna using the [Klarna Payments SDK](https://docs.klarna.com/klarna-payments/sdk-reference/) or mobile SDKs, allowing you to embed the payment UI directly in your app while using Gr4vy to manage the transaction lifecycle. Two modes are supported: **Standard Checkout** (session-based) and **Express Checkout** (button-based, no session required).

#### Standard Checkout

To initiate Standard Checkout, create a transaction with `integration_client` set to `web`, `ios`, or `android`. For mobile, use your app deep link for `redirect_url` (for example, `yourapp://`).

After the transaction is created, the API response includes a `session_token` which can be used to get the [session data](/reference/transactions/get-transaction-session) for that transaction.

```sh theme={"system"}
POST /transactions/:transaction_id/session?token=:session_token
```

<CodeGroup>
  ```json Web theme={"system"}
  {
    "session_data": {
      "client_token": "...",
      "purchase_country": "US",
      "purchase_currency": "USD",
      "amount": 1000
    },
    "default_completion_url": "https://api.sandbox.spider.gr4vy.app/transactions/approve/some-token",
    "integration_client": "web"
  }
  ```

  ```json iOS theme={"system"}
  {
    "session_data": {
      "client_token": "...",
      "purchase_country": "US",
      "purchase_currency": "USD",
      "amount": 1000
    },
    "default_completion_url": "https://api.sandbox.spider.gr4vy.app/transactions/approve/some-token",
    "integration_client": "ios"
  }
  ```

  ```json Android theme={"system"}
  {
    "session_data": {
      "client_token": "...",
      "purchase_country": "US",
      "purchase_currency": "USD",
      "amount": 1000
    },
    "default_completion_url": "https://api.sandbox.spider.gr4vy.app/transactions/approve/some-token",
    "integration_client": "android"
  }
  ```
</CodeGroup>

Use the `client_token` from the session data to initialize the Klarna SDK. Refer to the [Klarna SDK documentation](https://docs.klarna.com/klarna-payments/sdk-reference/) for web integration.

**Web**

```html theme={"system"}
<script src="https://js.klarna.com/web-sdk/v1/klarna.js"></script>
```

**iOS (Swift)** — See the [iOS SDK guide](https://docs.klarna.com/klarna-payments/ios-sdk/).

```swift theme={"system"}
let paymentView = KlarnaPaymentView(category: "pay_over_time", eventHandler: self)
paymentView.initialize(clientToken: sessionData.client_token, returnUrl: URL(string: "yourapp://")!)
```

**Android (Kotlin)** — See the [Android SDK guide](https://docs.klarna.com/klarna-payments/android-sdk/).

```kotlin theme={"system"}
val paymentView = KlarnaPaymentView(context, "pay_over_time")
paymentView.initialize(clientToken, "yourapp://")
```

**Complete the transaction**

After the customer authorizes the payment through the Klarna SDK, Klarna returns an `authorization_token`. To finalize the transaction, send this token to Gr4vy as a query parameter on the `default_completion_url` returned in the session response.

```bash Terminal theme={"system"}
curl -X GET \
    "<default_completion_url>&authorization_token=<AUTHORIZATION_TOKEN>"
```

Once the request completes successfully, the transaction moves to `authorization_succeeded`. It can then be captured, voided, or refunded through Gr4vy.

#### Express Checkout

Klarna Express Checkout embeds the Klarna payment button directly in your page using the [Klarna Payments Buttons SDK](https://docs.klarna.com/acquirer/klarna/express-checkout/). The buyer authorizes the payment inside the SDK widget — no redirect to a hosted page is required. After authorization, your backend creates the Gr4vy transaction in a single call using the `authorization_token` returned by the SDK.

**Get the client ID**

The Klarna Payments Buttons SDK requires a `client_id` to initialize. You can obtain this in one of two ways:

* **Via the Gr4vy API** — Call the Create Payment Service Session endpoint for your Klarna connection. Gr4vy returns a `clientId` from the connector credentials without making any Klarna API call.

  ```bash Terminal theme={"system"}
  POST /payment-services/{payment_service_id}/sessions
  ```

  ```json Response theme={"system"}
  {
    "client_id": "klarna_live_client_..."
  }
  ```

* **Hardcoded** — Copy the `client_id` from your Klarna Business Portal and embed it directly in your frontend. This is suitable when the client ID does not change per session.

**Embed the Express Checkout button**

Load the Klarna SDK and render the Express Checkout button. The `klarnaAsyncCallback` function must be defined before the SDK script tag so it is available when the SDK initializes.

```html theme={"system"}
<div id="klarna-express-button"></div>

<script>
  window.klarnaAsyncCallback = function () {
    var orderPayload = {
      purchase_country: "US",
      purchase_currency: "USD",
      locale: "en-US",
      order_amount: 2999,
      order_tax_amount: 0,
      order_lines: [
        {
          type: "physical",
          reference: "item-001",
          name: "Example Product",
          quantity: 1,
          unit_price: 2999,
          tax_rate: 0,
          total_amount: 2999,
          total_tax_amount: 0,
        },
      ],
    };

    var klarnaButton = window.Klarna.Payments.Buttons.init({
      client_id: "<YOUR_CLIENT_ID>",
    });

    klarnaButton.load({
      container: document.getElementById("klarna-express-button"),
      theme: "default",
      shape: "default",
      locale: "en-US",
      on_click: function (authorize) {
        authorize(
          {
            collect_shipping_address: true,
            auto_finalize: true,
          },
          orderPayload,
          function (response) {
            if (!response.approved) {
              // buyer cancelled or was declined
              return;
            }

            // Send authorization_token to your server
            fetch("/create-transaction", {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({
                authorization_token: response.authorization_token,
              }),
            });
          }
        );
      },
    });
  };
</script>

<script defer src="https://x.klarnacdn.net/kp/lib/v1/api.js"></script>
```

**Mobile**

For mobile applications, use the [Klarna Mobile SDK](https://docs.klarna.com/acquirer/klarna/express-checkout/integrate-express-checkout/mobile-sdk-integration/). The SDK is initialized with a `client_id` and renders a `KlarnaExpressCheckoutButton`.

**iOS (Swift)** — See the [iOS integration guide](https://docs.klarna.com/acquirer/klarna/express-checkout/integrate-express-checkout/mobile-sdk-integration/ios/).

```swift theme={"system"}
let options = KlarnaExpressCheckoutButtonOptions(
    sessionOptions: .ClientSideSession(clientId: "<CLIENT_ID>", sessionData: orderData),
    returnUrl: "yourapp://",
    delegate: self
)
```

**Android (Kotlin)** — See the [Android integration guide](https://docs.klarna.com/acquirer/klarna/express-checkout/integrate-express-checkout/mobile-sdk-integration/android/).

```kotlin theme={"system"}
val sessionOptions = KlarnaExpressCheckoutSessionOptions.ClientSideSession(
    clientId = "<CLIENT_ID>",
    sessionData = orderData,
)
```

**React Native** — See the [React Native integration guide](https://docs.klarna.com/acquirer/klarna/express-checkout/integrate-express-checkout/mobile-sdk-integration/react-native/).

```tsx theme={"system"}
<KlarnaExpressCheckoutView
    sessionOptions={{ clientId: "<CLIENT_ID>" }}
    returnUrl="yourapp://klarna-redirect"
    onAuthorized={handleAuthorization}
/>
```

**Create the transaction**

Once your server receives the `authorization_token`, create a Gr4vy transaction and pass the token in `connection_options` under the `klarna-klarna` key. Gr4vy forwards the token to Klarna to place the order directly — the transaction moves to `authorization_succeeded` without any redirect.

<CodeGroup>
  ```csharp C# theme={"system"}
  var res = await sdk.Transactions.CreateAsync(transactionCreate: new TransactionCreate() {
      Amount = 2999,
      Currency = "USD",
      Country = "US",
      PaymentMethod = new PaymentMethodRequest() {
          Method = "klarna",
      },
      CartItems = new List<CartItem> {
          new CartItem() {
              Name = "Example Product",
              Quantity = 1,
              UnitAmount = 2999,
              Sku = "item-001",
              Type = "physical",
          },
      },
      ConnectionOptions = new Dictionary<string, object> {
          ["klarna-klarna"] = new Dictionary<string, object> {
              ["authorization_token"] = "<AUTHORIZATION_TOKEN>",
          },
      },
  });
  ```

  ```go Go theme={"system"}
  res, err := s.Transactions.Create(context.Background(), &components.TransactionCreate{
      Amount:   gr4vy.Int64(2999),
      Currency: "USD",
      Country:  gr4vy.String("US"),
      PaymentMethod: &components.PaymentMethodRequest{
          Method: "klarna",
      },
      CartItems: []components.CartItem{
          {
              Name:       "Example Product",
              Quantity:   1,
              UnitAmount: gr4vy.Int64(2999),
              Sku:        gr4vy.String("item-001"),
              Type:       gr4vy.String("physical"),
          },
      },
      ConnectionOptions: map[string]interface{}{
          "klarna-klarna": map[string]interface{}{
              "authorization_token": "<AUTHORIZATION_TOKEN>",
          },
      },
  })
  ```

  ```php PHP theme={"system"}
  $res = $sdk->transactions->create(new Components\TransactionCreate(
      amount: 2999,
      currency: 'USD',
      country: 'US',
      paymentMethod: new Components\PaymentMethodRequest(
          method: 'klarna',
      ),
      cartItems: [
          new Components\CartItem(
              name: 'Example Product',
              quantity: 1,
              unitAmount: 2999,
              sku: 'item-001',
              type: 'physical',
          ),
      ],
      connectionOptions: [
          'klarna-klarna' => [
              'authorization_token' => '<AUTHORIZATION_TOKEN>',
          ],
      ],
  ));
  ```

  ```python Python theme={"system"}
  res = s.transactions.create(
      components.TransactionCreate(
          amount=2999,
          currency="USD",
          country="US",
          payment_method=components.PaymentMethodRequest(
              method="klarna",
          ),
          cart_items=[
              components.CartItem(
                  name="Example Product",
                  quantity=1,
                  unit_amount=2999,
                  sku="item-001",
                  type="physical",
              ),
          ],
          connection_options={
              "klarna-klarna": {
                  "authorization_token": "<AUTHORIZATION_TOKEN>",
              }
          },
      ),
  )
  ```

  ```ts TypeScript theme={"system"}
  const res = await gr4vy.transactions.create({
    amount: 2999,
    currency: "USD",
    country: "US",
    paymentMethod: {
      method: "klarna",
    },
    cartItems: [
      {
        name: "Example Product",
        quantity: 1,
        unitAmount: 2999,
        sku: "item-001",
        type: "physical",
      },
    ],
    connectionOptions: {
      "klarna-klarna": {
        authorizationToken: "<AUTHORIZATION_TOKEN>",
      },
    },
  });
  ```
</CodeGroup>

<Note>
  The `authorization_token` returned by the Klarna SDK expires within approximately 60 minutes. Create the Gr4vy transaction immediately after receiving it.
</Note>

## Subscriptions (MIT)

Klarna supports storing the buyer's payment method during the first (customer-present) payment and charging future renewals as merchant-initiated transactions (MIT) using the saved payment method, typically with no redirect.

<Note>
  Webhooks must be configured for the Klarna payment service. After the buyer approves the payment, Klarna sends an `authorization_token` via webhook which Gr4vy uses to create the reusable customer token. Without this webhook, `store: true` will not work.
</Note>

### First payment

Set `store` to `true` to save the Klarna payment method for the buyer.

<CodeGroup>
  ```csharp C# theme={"system"}
  using Gr4vy;
  using Gr4vy.Models.Components;

  // Load your private key from disk, env, or your secret manager
  var privateKey = "...";

  var sdk = new Gr4vySDK(
      id: "example",
      server: SDKConfig.Server.Sandbox,
      bearerAuthSource: Auth.WithToken(privateKey),
      merchantAccountId: "default"
  );

  var res = await sdk.Transactions.CreateAsync(transactionCreate: new TransactionCreate() {
      Amount = 5000,
      Currency = "USD",
      Country = "US",
      PaymentMethod = new PaymentMethodRequest() {
          Method = "klarna",
          RedirectUrl = "https://yourapp.com/callback",
          Country = "US",
          Currency = "USD"
      },
      Store = true,
      PaymentSource = "recurring",
      MerchantInitiated = false,
      IsSubsequentPayment = false,
  });
  // handle response
  ```

  ```go Go theme={"system"}
  package main

  import(
      "context"
      "log"
      "github.com/gr4vy/gr4vy-go"
      "github.com/gr4vy/gr4vy-go/models/components"
  )

  func main() {
      // Load your private key from disk, env, or your secret manager
      privateKey := "..."

      s := gr4vy.New(
          gr4vy.WithID("example"),
          gr4vy.WithServer(gr4vy.ServerSandbox),
          gr4vy.WithBearerAuth(gr4vy.WithToken(privateKey)),
          gr4vy.WithMerchantAccountID("default"),
      )

      amount := int64(5000)
      store := true
      merchantInitiated := false
      isSubsequentPayment := false
      res, err := s.Transactions.Create(context.Background(), &components.TransactionCreate{
          Amount: &amount,
          Currency: "USD",
          Country: "US",
          PaymentMethod: &components.PaymentMethodRequest{
              Method: "klarna",
              RedirectUrl: "https://yourapp.com/callback",
              Country: "US",
              Currency: "USD",
          },
          Store: &store,
          PaymentSource: "recurring",
          MerchantInitiated: &merchantInitiated,
          IsSubsequentPayment: &isSubsequentPayment,
      })
      if err != nil {
          log.Fatal(err)
      }

      if res.StatusCode == 201 {
          // handle response
      }
  }
  ```

  ```java Java theme={"system"}
  import com.gr4vy.Gr4vy;
  import com.gr4vy.models.components.*;
  import com.gr4vy.models.operations.*;

  public class Main {
      public static void main(String[] args) {
          // Load your private key from disk, env, or your secret manager
          String privateKey = "...";

          Gr4vy sdk = new Gr4vy.Builder()
              .id("example")
              .server(Server.SANDBOX)
              .bearerAuth(Auth.withToken(privateKey))
              .merchantAccountId("default")
              .build();

          TransactionCreate transactionCreate = new TransactionCreate.Builder()
              .amount(5000)
              .currency("USD")
              .country("US")
              .paymentMethod(new PaymentMethodRequest.Builder()
                  .method("klarna")
                  .redirectUrl("https://yourapp.com/callback")
                  .country("US")
                  .currency("USD")
                  .build())
              .store(true)
              .paymentSource("recurring")
              .merchantInitiated(false)
              .isSubsequentPayment(false)
              .build();

          try {
              CreateTransactionResponse res = sdk.transactions().create(transactionCreate);

              if (res.statusCode == 201) {
                  // handle response
              }
          } catch (Exception e) {
              // handle exception
          }
      }
  }
  ```

  ```php PHP theme={"system"}
  <?php

  require_once 'vendor/autoload.php';

  use Gr4vy;
  use Gr4vy\Auth;
  use Gr4vy\Models\Components;

  // Load your private key from disk, env, or your secret manager
  $privateKey = '...';

  $sdk = Gr4vy\SDK::builder()
      ->setId('example')
      ->setServer('sandbox')
      ->setSecuritySource(Auth::withToken($privateKey))
      ->setMerchantAccountId('default')
      ->build();

  $res = $sdk->transactions->create(new Components\TransactionCreate(
      amount: 5000,
      currency: 'USD',
      country: 'US',
      paymentMethod: new Components\PaymentMethodRequest(
          method: 'klarna',
          redirectUrl: 'https://yourapp.com/callback',
          country: 'US',
          currency: 'USD',
      ),
      store: true,
      paymentSource: 'recurring',
      merchantInitiated: false,
      isSubsequentPayment: false,
  ));

  if ($res->statusCode === 201) {
      // handle response
  }
  ```

  ```python Python theme={"system"}
  import gr4vy
  from gr4vy import auth
  from gr4vy.models import components

  # Load your private key from disk, env, or your secret manager
  private_key = "..."

  s = gr4vy.Gr4vy(
      id="example",
      server=gr4vy.SERVER_SANDBOX,
      bearer_auth=auth.with_token(private_key),
      merchant_account_id="default",
  )

  res = s.transactions.create(
      components.TransactionCreate(
          amount=5000,
          currency="USD",
          country="US",
          payment_method=components.PaymentMethodRequest(
              method="klarna",
              redirect_url="https://yourapp.com/callback",
              country="US",
              currency="USD",
          ),
          store=True,
          payment_source="recurring",
          merchant_initiated=False,
          is_subsequent_payment=False,
      ),
  )

  if res.status_code == 201:
      # handle response
      pass
  ```

  ```ts TypeScript theme={"system"}
  import { Gr4vy, withToken } from "@gr4vy/sdk";

  // Load your private key from disk, env, or your secret manager
  const privateKey = process.env.GR4VY_PRIVATE_KEY ?? "...";

  const gr4vy = new Gr4vy({
    id: "example",
    server: "sandbox",
    bearerAuth: withToken({ privateKey }),
    merchantAccountId: "default",
  });

  const res = await gr4vy.transactions.create({
    amount: 5000,
    currency: "USD",
    country: "US",
    paymentMethod: {
      method: "klarna",
      redirectUrl: "https://yourapp.com/callback",
      country: "US",
      currency: "USD",
    },
    store: true,
    paymentSource: "recurring",
    merchantInitiated: false,
    isSubsequentPayment: false,
  });

  if (res.statusCode === 201) {
    // handle response
  }
  ```
</CodeGroup>

### Subsequent payment

After the payment method is saved, use the payment method ID to charge future renewals.

* Set `payment_method.method` to `id` and pass the saved payment method ID.
* Set `payment_source` to `recurring`.
* Set `merchant_initiated` and `is_subsequent_payment` to `true`.

<CodeGroup>
  ```csharp C# theme={"system"}
  using Gr4vy;
  using Gr4vy.Models.Components;

  // Load your private key from disk, env, or your secret manager
  var privateKey = "...";

  var sdk = new Gr4vySDK(
      id: "example",
      server: SDKConfig.Server.Sandbox,
      bearerAuthSource: Auth.WithToken(privateKey),
      merchantAccountId: "default"
  );

  var res = await sdk.Transactions.CreateAsync(transactionCreate: new TransactionCreate() {
      Amount = 5000,
      Currency = "USD",
      Country = "US",
      PaymentMethod = new PaymentMethodRequest() {
          Method = "id",
          Id = "c2495b14-ca95-4199-87c3-27cbfefcbe9e"
      },
      PaymentSource = "recurring",
      MerchantInitiated = true,
      IsSubsequentPayment = true,
  });
  // handle response
  ```

  ```go Go theme={"system"}
  package main

  import(
      "context"
      "log"
      "github.com/gr4vy/gr4vy-go"
      "github.com/gr4vy/gr4vy-go/models/components"
  )

  func main() {
      // Load your private key from disk, env, or your secret manager
      privateKey := "..."

      s := gr4vy.New(
          gr4vy.WithID("example"),
          gr4vy.WithServer(gr4vy.ServerSandbox),
          gr4vy.WithBearerAuth(gr4vy.WithToken(privateKey)),
          gr4vy.WithMerchantAccountID("default"),
      )

      amount := int64(5000)
      merchantInitiated := true
      isSubsequentPayment := true
      res, err := s.Transactions.Create(context.Background(), &components.TransactionCreate{
          Amount: &amount,
          Currency: "USD",
          Country: "US",
          PaymentMethod: &components.PaymentMethodRequest{
              Method: "id",
              Id: "c2495b14-ca95-4199-87c3-27cbfefcbe9e",
          },
          PaymentSource: "recurring",
          MerchantInitiated: &merchantInitiated,
          IsSubsequentPayment: &isSubsequentPayment,
      })
      if err != nil {
          log.Fatal(err)
      }

      if res.StatusCode == 201 {
          // handle response
      }
  }
  ```

  ```java Java theme={"system"}
  import com.gr4vy.Gr4vy;
  import com.gr4vy.models.components.*;
  import com.gr4vy.models.operations.*;

  public class Main {
      public static void main(String[] args) {
          // Load your private key from disk, env, or your secret manager
          String privateKey = "...";

          Gr4vy sdk = new Gr4vy.Builder()
              .id("example")
              .server(Server.SANDBOX)
              .bearerAuth(Auth.withToken(privateKey))
              .merchantAccountId("default")
              .build();

          TransactionCreate transactionCreate = new TransactionCreate.Builder()
              .amount(5000)
              .currency("USD")
              .country("US")
              .paymentMethod(new PaymentMethodRequest.Builder()
                  .method("id")
                  .id("c2495b14-ca95-4199-87c3-27cbfefcbe9e")
                  .build())
              .paymentSource("recurring")
              .merchantInitiated(true)
              .isSubsequentPayment(true)
              .build();

          try {
              CreateTransactionResponse res = sdk.transactions().create(transactionCreate);

              if (res.statusCode == 201) {
                  // handle response
              }
          } catch (Exception e) {
              // handle exception
          }
      }
  }
  ```

  ```php PHP theme={"system"}
  <?php

  require_once 'vendor/autoload.php';

  use Gr4vy;
  use Gr4vy\Auth;
  use Gr4vy\Models\Components;

  // Load your private key from disk, env, or your secret manager
  $privateKey = '...';

  $sdk = Gr4vy\SDK::builder()
      ->setId('example')
      ->setServer('sandbox')
      ->setSecuritySource(Auth::withToken($privateKey))
      ->setMerchantAccountId('default')
      ->build();

  $res = $sdk->transactions->create(new Components\TransactionCreate(
      amount: 5000,
      currency: 'USD',
      country: 'US',
      paymentMethod: new Components\PaymentMethodRequest(
          method: 'id',
          id: 'c2495b14-ca95-4199-87c3-27cbfefcbe9e',
      ),
      paymentSource: 'recurring',
      merchantInitiated: true,
      isSubsequentPayment: true,
  ));

  if ($res->statusCode === 201) {
      // handle response
  }
  ```

  ```python Python theme={"system"}
  import gr4vy
  from gr4vy import auth
  from gr4vy.models import components

  # Load your private key from disk, env, or your secret manager
  private_key = "..."

  s = gr4vy.Gr4vy(
      id="example",
      server=gr4vy.SERVER_SANDBOX,
      bearer_auth=auth.with_token(private_key),
      merchant_account_id="default",
  )

  res = s.transactions.create(
      components.TransactionCreate(
          amount=5000,
          currency="USD",
          country="US",
          payment_method=components.PaymentMethodRequest(
              method="id",
              id="c2495b14-ca95-4199-87c3-27cbfefcbe9e",
          ),
          payment_source="recurring",
          merchant_initiated=True,
          is_subsequent_payment=True,
      ),
  )

  if res.status_code == 201:
      # handle response
      pass
  ```

  ```ts TypeScript theme={"system"}
  import { Gr4vy, withToken } from "@gr4vy/sdk";

  // Load your private key from disk, env, or your secret manager
  const privateKey = process.env.GR4VY_PRIVATE_KEY ?? "...";

  const gr4vy = new Gr4vy({
    id: "example",
    server: "sandbox",
    bearerAuth: withToken({ privateKey }),
    merchantAccountId: "default",
  });

  const res = await gr4vy.transactions.create({
    amount: 5000,
    currency: "USD",
    country: "US",
    paymentMethod: {
      method: "id",
      id: "c2495b14-ca95-4199-87c3-27cbfefcbe9e",
    },
    paymentSource: "recurring",
    merchantInitiated: true,
    isSubsequentPayment: true,
  });

  if (res.statusCode === 201) {
    // handle response
  }
  ```
</CodeGroup>
