# Magento and Adobe Commerce

Install Kustom Checkout on Adobe Commerce or Magento 2.4 with Composer, set credentials and the terms URL, verify it and fix the failures behind most tickets.

Kustom Checkout for Adobe Commerce and Magento Open Source is a set of Composer modules that
replace the Magento checkout with an embedded Kustom iframe. One integration covers every
Kustom payment method in your markets, plus B2B purchases, the Shipping Assistant, Extra
Merchant Data and merchant references for settlement reconciliation. This page gets it
installed, configured and verified. For the module map, configuration paths, database tables
and extension points, see the [Magento module reference](/partners/magento-module-reference/).

## Before you start

- Adobe Commerce 2.4, cloud or on-premise, or Magento Open Source 2.4.
- PHP 8.1, 8.2 or 8.4, matching Adobe's certified versions for your Magento release. Both
  upstream sources omit 8.3 from that list. Check Adobe's certification matrix before you
  assume it's unsupported.
- HTTPS with a valid SSL certificate.
- Composer access to the store's codebase and a CLI you can run `bin/magento` from.
- An active Kustom merchant account with API credentials for the region your store sells in —
  see [Activate Kustom Checkout on your platform](/partners/activate-kustom-checkout/).

Your store must be reachable from the public internet. Kustom posts address updates, shipping
updates and the final push callback to it, and the checkout stalls without them. On a local
environment you'll need a tunnel — see [Variations](#variations).

## Step by step

### 1. Install the modules with Composer

`kustom/module-checkout` is the only package you request. Composer pulls in every dependency in
the right order.

```bash
composer require kustom/module-checkout
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy
php bin/magento cache:flush
```

Confirm the modules registered:

```bash
php bin/magento module:status | grep Klarna
```

You should see `Klarna_Base`, `Klarna_Kco`, `Klarna_Orderlines`, `Klarna_Kss` and others. The
Composer vendor is `kustom` but the PHP namespaces, database tables and configuration paths are
all `Klarna` — that's deliberate backward compatibility from the module's origin, not a wrong
package. The [module reference](/partners/magento-module-reference/) explains where each name
applies.

Don't look for the module in **System → Web Setup Wizard → Extension Manager**. Older upstream
instructions point there, but Adobe removed the Web Setup Wizard in Magento 2.4 — the only
version line this module supports. Composer is the install route.

### 2. Enter your API credentials

Go to **Stores → Configuration → Sales → Payment Methods → Kustom** and set:

1. Your **region** — EU, US, AU and others. Credentials are region-specific.
2. The **API username** and **API password** for that region.
3. **API mode** — Playground while you test, Live afterwards.
4. **Enabled** to Yes, and the **allowed countries** your Kustom contract covers.
5. The order status new orders should be created with.

### 3. Set the terms and conditions URL

Go to **Stores → Configuration → Sales → Checkout → Kustom Checkout** and set **Terms &
Conditions URL**. This is mandatory: the checkout fails to initialize without it, and the
failure doesn't name the missing setting.

### 4. Flush the cache

Configuration changes don't take effect until you do. Either **System → Cache Management →
Flush Magento Cache**, or:

```bash
php bin/magento cache:flush
```

### 5. Go live

Switch **API mode** to Live, enter your production region credentials, flush the cache again
and run one real purchase.

## Check that it works

Do this with playground credentials before you touch production.

1. Confirm the checkout module is on:

   ```bash
   php bin/magento module:status Klarna_Kco
   ```

2. Reopen **Stores → Configuration → Sales → Payment Methods → Kustom** and confirm the
   credentials saved, **Enabled** is Yes and API mode is Playground.
3. Add a product to the cart and go to checkout. The Kustom iframe should load with your
   payment methods visible.
4. Enter a test address — this fires the address update callback — and pick a shipping method,
   which fires the shipping callback. Both must return before the iframe lets you pay.
5. Open the browser console. There should be no JavaScript errors and no failed requests in the
   Network tab.
6. Complete the payment with [test data](/basics/test-data/). You should be redirected to the
   confirmation page.
7. Find the order under **Sales → Orders** and confirm the totals match what the checkout showed.
8. Check the log table:

   ```sql
   SELECT * FROM klarna_logs ORDER BY created_at DESC LIMIT 10;
   ```

   API calls should show 200 or 201, and the `address_update`, `shipping_update` and `push`
   callbacks should all have completed.

## Variations

### Developing against a local Magento

Kustom's servers can't reach `localhost`, so callbacks never arrive and the checkout stalls at
address entry. Expose the environment with a tunnel:

```bash
ngrok http 80
```

Then set both **Base URL** and **Base URL (Secure)** under **Stores → Configuration → General →
Web → Base URLs** to the tunnel's HTTPS URL, and flush the cache. `localtunnel` (`lt --port 80`)
works the same way.

No allow-listing is needed at Kustom's end. Callbacks go to whatever base URL the store
advertises. Free tunnel URLs change on every restart, so the base URL has to be updated each
time — and set back to the real domain when you're done.

### Markets

Kustom Checkout's core markets on this module are SE, NO, FI, DK, GB, DE, NL, AT, CH, US, ES,
FR, BE and PL, with further markets available on request. One upstream page claims availability
in 229 countries. Treat that as marketing rather than a configuration you can enable, and
confirm your actual coverage against your Kustom contract before activating a country.

## Troubleshooting

Turn on **Debug Mode** under **Stores → Configuration → Sales → Payment Methods → Kustom API**
first, flush the cache, then reproduce. Everything below reads out of `klarna_logs`. Turn debug
mode off again afterwards — see the warning at the end of this section.

### The checkout iframe does not appear

Confirm the module is enabled, credentials are entered and the terms URL is set. Then clear
caches and, if it persists, redeploy static content:

```bash
php bin/magento cache:flush
php bin/magento cache:clean config
php bin/magento setup:static-content:deploy
```

If the page is blank with console errors, check your Content Security Policy — a restrictive
`frame-src` blocks the iframe.

```sql
SELECT * FROM klarna_logs
WHERE action = 'create_order' AND status != '200'
ORDER BY created_at DESC LIMIT 10;
```

### "Region is not supported"

Store country, base currency and merchant account region must all agree, and the API credentials
must be the ones issued for that region. EUR plus Germany plus an EU merchant account works.
USD plus Germany plus an EU account does not.

### Callbacks are not arriving

Symptoms are a checkout stuck at address entry, shipping options that never load or an order
that never completes after payment. The endpoints must be publicly reachable and return 200 —
not 403, 404 or 503:

```bash
curl -X POST https://yourdomain.com/kco/api/addressupdate \
  -H "Content-Type: application/json" -d '{"test": true}'
```

Maintenance mode blocks callbacks entirely:

```bash
php bin/magento maintenance:status
```

### "Order total mismatch" or a 400 on create/update

The order lines Magento sent don't add up to the total it claimed. Read the failing request:

```sql
SELECT request, response FROM klarna_logs
WHERE action IN ('create_order', 'update_order') AND status = '400'
ORDER BY created_at DESC LIMIT 1;
```

The usual causes are rounding when converting to minor units, tax rates in the wrong scale
(2500 means 25%), a total with no matching order line — shipping and discounts each need one —
and a `discount` line whose amounts don't reconcile with the order total. A `discount` line is
negative by design: its `unit_price`, `total_amount` and `total_tax_amount` are all negative, and
none of those three has a lower bound for exactly that reason — only `total_discount_amount` must
be zero or more. The failure is a
discount that doesn't add up, not a discount that is negative.

### Payment authorized but no order in Magento

Check the Kustom Merchant Portal. If the order is `AUTHORIZED` there, the push callback failed —
work the callback section above. If it isn't there at all, Kustom declined the payment.
Duplicate rows in `klarna_core_order` for one `klarna_order_id` mean the push callback fired
more than once.

### Orders stuck in "pending payment"

Check the **Payment Action** setting. If the order is authorized at Kustom, creating the invoice
in the admin manually triggers the capture through the Order Management module. Tick **Create
Shipment** on that invoice and the tracking numbers go to Kustom with the capture.

### An order handled in the Kustom Portal still looks open in Magento

Nothing done in the Kustom Merchant Portal reaches Magento. The module only sends — an invoice
becomes a capture, a credit memo a refund, a cancellation a cancellation — so a capture or
refund made in the Portal leaves the Magento order exactly as it was, and you mirror it by hand.
How each mirror behaves:

- **Captured in full in the Portal.** Create the invoice in Magento as usual. Before it
  captures, the module reads the order's captures from Kustom, finds the whole amount already
  captured and doesn't capture again.
- **Captured in part in the Portal.** Avoid it. That check is all or nothing, so the Magento
  invoice for those items either captures them a second time or is refused for asking more than
  is left. Make partial captures from Magento instead.
- **Refunded in the Portal.** Create the credit memo with **Refund Offline**. An online credit
  memo sends the refund to Kustom again.

The simplest rule is to run order management from one place, and on Magento that place is the
Magento admin.

### The checkout is slow, or `klarna_logs` has grown to gigabytes

Debug mode left on in production is the usual cause of both. Check and disable it:

```bash
php bin/magento config:show klarna/api/debug
php bin/magento config:set klarna/api/debug 0
php bin/magento cache:flush
```

Then confirm cron is running — `klarna_core_clean_logs` prunes the table daily at midnight —
and, if you need space back immediately, delete rows older than 7 or 30 days by `created_at`.

### Contacting support

Send `support@kustom.co` a short summary, your merchant name and Merchant ID, store URL,
environment, Magento and PHP versions, the payment module version, the time of the issue in
ISO 8601 UTC, the affected order and transaction IDs and steps to reproduce. Attach screenshots,
`system.log`, `exception.log` and `klarna.log`, and an export of the `klarna_logs` rows for the
order. Mask card PANs and API keys before sending, and never attach a full card number or CVV.

## Related

- [Magento module reference](/partners/magento-module-reference/) — the module map, config paths, database schema and extension points
- [Capture an order](/order-management/capture/) — how invoicing in Magento reaches Kustom
- [Open source platform responsibility](/partners/open-source-responsibility/) — what you own when you run Kustom on Magento Open Source
- [Activate Kustom Checkout on your platform](/partners/activate-kustom-checkout/) — getting the merchant account and credentials first
- [Authentication and environments](/api/authentication/) — playground versus live, and where region credentials come from
- [Shipping Assistant](/checkout/shipping/#shipping-assistant) — what the KSS module plugs into
- [Test data](/basics/test-data/) — identities and cards that pass in playground
- [Platform catalogue](/partners/) — the other platforms with plugins
