# Kustom Checkout for Magento / Adobe Commerce - Comprehensive Developer Guide
## Table of Contents
1. [Introduction](#introduction)
2. [Installation & Setup](#installation--setup)
3. [Configuration Reference](#configuration-reference)
4. [Architecture Overview](#architecture-overview)
5. [Module Reference](#module-reference)
6. [Database Schema](#database-schema)
7. [Data Flow & Integration](#data-flow--integration)
8. [Extension Points](#extension-points)
9. [API Reference](#api-reference)
10. [Troubleshooting](#troubleshooting)
11. [Support](#support)
12. [Quick Reference](#quick-reference)
# Introduction
Kustom Checkout is a modular, embedded checkout solution for Adobe Commerce and Magento Open Source 2.4 that supports multiple payment methods, B2B purchases, and visual customization to match the store theme.
With a single integration, merchants can enable all Kustom payment methods in supported markets and keep shoppers on-site in a fast checkout flow without redirects.
## Important Note: Naming Convention
While the composer vendor name is `kustom`, the internal PHP namespaces use `Klarna` due to the module's origin. This guide uses both names as appropriate:
- **Composer packages**: `kustom/module-*`
- **PHP namespaces**: `Klarna\*`
- **Database tables**: `klarna_*`
- **Configuration paths**: `klarna/*`
This naming reflects the transition from Klarna to Kustom branding while maintaining backward compatibility.
## Key Highlights
- Broad payment method coverage in all Kustom-supported countries
- Pre-filled customer addresses for a near one-click checkout
- Custom marketing and account-creation checkboxes
- Business-to-business (B2B) purchase support
- Optional shipping assistant integration
- Extra Merchant Data to improve risk decisions
- Merchant reference fields for settlements and reconciliation
- Flexible UI options to align with the store's design
## Requirements
- Adobe Commerce (cloud or on-premise) 2.4 or Magento Open Source 2.4
- PHP 8.1, 8.2, or 8.4 aligned with Adobe's certified versions
- HTTPS with a valid SSL certificate
- An active Kustom merchant account with API credentials
## Compatibility
- Adobe Commerce Cloud 2.4
- Adobe Commerce on-premise 2.4
- Magento Open Source 2.4
## Supported Markets
Kustom Checkout is offered in many countries, with a core set including Nordic, major European, and selected other markets such as:
- **Nordic**: Sweden, Norway, Finland, Denmark
- **Europe**: United Kingdom, Germany, the Netherlands, Austria, Switzerland, Spain, France, Belgium, Poland
- **Americas**: United States
- **Other**: Additional markets available (contact Kustom for full list)
# Installation & Setup
Installing Kustom Checkout is straightforward using Composer, which automatically manages all module dependencies. The process involves installing the package, running Magento's setup commands, and configuring your API credentials. For local development, you'll need to set up a tunnel service to make callbacks publicly accessible. This section guides you through the complete installation and initial setup process.
## Installation via Composer
Kustom Checkout is installed using Composer. The main package automatically pulls in all required dependencies.
**Install the Kustom Checkout module:**
```bash
# Install the main checkout module (includes all dependencies)
composer require kustom/module-checkout
# Run Magento setup commands
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy
php bin/magento cache:flush
```
**Verify installation:**
```bash
# Check that all Kustom modules are enabled
php bin/magento module:status | grep Klarna
# You should see modules like:
# Klarna_Base
# Klarna_Kco
# Klarna_Orderlines
# Klarna_Kss
# ... etc.
```
## Initial Configuration
After installation, you need to configure your Kustom API credentials and enable the checkout. The configuration is region-specific (EU, US, AU, etc.), so make sure to select the region that matches your merchant account and store country. The Terms & Conditions URL is mandatory - checkout will not work without it. Remember to flush caches after any configuration changes for settings to take effect.
1. Navigate to **Stores → Configuration → Sales → Payment Methods → Kustom**
2. **API Credentials** (per region):
- Select your region (EU, US, AU, etc.)
- Enter API username (production)
- Enter API password (production)
- Set API mode (Test/Live)
3. **Enable Kustom Checkout**:
- Set "Enabled" to Yes
- Configure allowed countries
- Set order status for new orders
4. **Configure Terms URL** (Required):
- Navigate to **Stores → Configuration → Sales → Checkout → Kustom Checkout**
- Set "Terms & Conditions URL" - **checkout will fail without this**
5. **Flush Caches**:
**Option A - Via Admin Panel** (easiest for non-developers):
- Navigate to **System → Cache Management**
- Click **Flush Magento Cache** button
**Option B - Via Command Line**:
```bash
php bin/magento cache:flush
```
## QA Verification Steps
Once installation and configuration are complete, test the integration to ensure everything works correctly. Use your test/playground API credentials for these verification steps to avoid creating real transactions. This checklist confirms that the module is properly installed, configured, and communicating with Kustom's API.
1. **Verify module is active**:
```bash
php bin/magento module:status Klarna_Kco
# Should show: Module is enabled
```
2. **Check configuration is saved**:
- Navigate to **Stores → Configuration → Sales → Payment Methods → Kustom**
- Verify your API credentials are present
- Confirm "Enabled" is set to Yes
- Ensure API mode is set to "Playground" or "Test"
3. **Test the checkout flow**:
- Add a product to your cart
- Proceed to checkout
- **Expected result**: Kustom checkout iframe loads successfully
- Verify payment methods are displayed in the iframe
- Enter a test address to trigger the address update callback
- Select a shipping method to trigger the shipping callback
4. **Check for errors**:
- Open browser developer console (F12)
- Look for JavaScript errors (there should be none)
- Check Network tab for failed API calls
5. **Complete a test order**:
- Use Kustom test credentials to complete the payment
- **Expected result**: Order is created in Magento
- You should be redirected to the confirmation page
6. **Verify order in Magento Admin**:
- Navigate to **Sales → Orders**
- Find your test order
- Verify order status is correct (typically "Pending Payment" or "Processing")
- Check that order totals match the checkout
7. **Check logs for issues** (optional but recommended):
```sql
SELECT * FROM klarna_logs
ORDER BY created_at DESC
LIMIT 10;
```
- Verify API calls show status 200/201
- Check that callbacks (address_update, shipping_update, push) completed successfully
## Local Development Setup
When developing locally (e.g., on localhost or a private network), Kustom's servers cannot reach your callbacks since they're not publicly accessible. To test the full checkout flow including callbacks, you need to expose your local environment to the internet using a tunneling service. This allows Kustom to send address updates, shipping requests, and the final push callback to your development machine.
**Option 1: ngrok (Recommended)**
ngrok creates a secure tunnel to your local server and provides a public HTTPS URL:
```bash
# Start ngrok tunnel to your local Magento
ngrok http 80
# ngrok will display a URL like: https://abc123-456.ngrok-free.app
```
After starting ngrok:
1. Copy the HTTPS URL provided by ngrok
2. Update your Magento base URL:
- **Admin Panel**: Stores → Configuration → General → Web → Base URLs
- Set "Base URL" and "Base URL (Secure)" to your ngrok URL
3. Clear Magento cache
4. Test checkout - Kustom callbacks will now reach your local environment
**Option 2: localtunnel**
An alternative open-source tunneling solution:
```bash
# Install localtunnel (one-time)
npm install -g localtunnel
# Create tunnel
lt --port 80
# Use the provided URL (e.g., https://random-word-123.loca.lt)
```
Follow the same steps as ngrok to update your Magento base URL.
**Important Notes:**
- **No whitelisting required**: Kustom accepts callbacks from any URL, so your tunnel URL will work immediately
- **HTTPS is required**: Both ngrok and localtunnel provide HTTPS by default
- **Remember to revert base URL**: After development, change your base URLs back to your actual domain
- **Tunnel URLs change**: Free ngrok/localtunnel URLs change each restart, so you'll need to update Magento's base URL each time
# Configuration Reference
Kustom Checkout is configured through Magento's admin panel, with settings organized by region and functionality. Configuration includes API credentials for different regions (EU, US, AU, etc.), payment method settings, checkout behavior, and visual design options. Most settings are store-scoped, allowing different configurations per store view. Sensitive values like API passwords are automatically encrypted by Magento. This section provides a complete reference of all configuration paths and their purposes.
## API Configuration Paths
### Core API Settings
```
klarna/api/
- debug (boolean, default: 0)
Enable debug logging
⚠️ WARNING: Only enable debug mode when actively troubleshooting issues.
Debug mode generates extensive logs that can quickly consume disk space
and fill the klarna_logs database table. Always disable debug mode after
completing your investigation to prevent performance issues.
- region (checkbox: au, ca, eu, mx, nz, us)
Active regions for API connections
```
### Region-Specific API Credentials
Replace `{region}` with: au, ca, eu, mx, nz, or us
```
klarna/api_{region}/
- api_mode (select: 0=production, 1=playground)
Environment selection
- username_production (string, required)
Production API username
- password_production (encrypted, required)
Production API password (stored encrypted)
- client_identifier_production (string)
Optional client identifier
- username_playground (string)
Playground/test username
- password_playground (encrypted)
Playground/test password (stored encrypted)
- client_identifier_playground (string)
Optional test client identifier
```
### Payment Method Configuration
**Klarna Payments (KP)**:
```
payment/klarna_kp/
- active (boolean)
Enable/disable Klarna Payments
- enable_b2b (boolean)
Enable B2B purchase support
- sort_order (integer)
Display order in payment methods list
- title (string)
Payment method title shown to customers
```
**Kustom Checkout (KCO)**:
```
payment/klarna_kco/
- active (boolean)
Enable/disable Kustom Checkout
- order_status (select)
Initial order status after placement
- shipping_countries (multiselect)
Allowed shipping countries
- billing_countries (multiselect)
Allowed billing countries
- disable_customer_group (multiselect)
Customer groups excluded from KCO
```
### Checkout Configuration
```
checkout/klarna_kco/
- guest_checkout (boolean)
Allow guest checkout in KCO
- enable_b2b (boolean)
Enable business purchases
- auto_focus (boolean)
Auto-focus first field in iframe
- merchant_prefill (boolean)
Pre-fill customer data
- terms_url (text, REQUIRED)
Terms & conditions URL (must be set)
- shipping_in_iframe (boolean)
Show shipping methods in iframe
- phone_mandatory (boolean)
Make phone number required
- dob_mandatory (boolean)
Make date of birth required
```
### Design Configuration
```
checkout/klarna_kco_design/
- color_button (color)
Button color (hex value)
- color_button_text (color)
Button text color
- color_checkbox (color)
Checkbox color
- color_header (color)
Header color
- color_link (color)
Link color
- radius_border (text)
Border radius in pixels
```
### Shipping Configuration
```
klarna/shipping/
- product_unit (select: cm/inch)
Unit for package dimensions
- length (attribute)
Product attribute for length
- width (attribute)
Product attribute for width
- height (attribute)
Product attribute for height
```
### Kustom Shipping Service
```
payment/klarna_kss/
- enabled (boolean)
Enable dynamic shipping in KCO
```
## Programmatic Configuration Access
```php
scopeConfig = $scopeConfig;
}
public function getApiUsername($storeId = null)
{
return $this->scopeConfig->getValue(
'klarna/api_eu/username_production',
ScopeInterface::SCOPE_STORE,
$storeId
);
}
public function isDebugEnabled($storeId = null)
{
return $this->scopeConfig->isSetFlag(
'klarna/api/debug',
ScopeInterface::SCOPE_STORE,
$storeId
);
}
}
```
## Sensitive Configuration
The following configuration values are encrypted:
- All `password_production` fields
- All `password_playground` fields
- `klarna/api/merchant_id`
- `klarna/api/shared_secret`
## Environment-Specific Configuration
These settings typically differ between environments:
- `klarna/api_{region}/api_mode` - Test mode in dev/staging
- `klarna/api/debug` - Enabled in dev, disabled in production
- `checkout/klarna_kco/terms_url` - Environment-specific URLs
# Architecture Overview
The Kustom integration is built on a modular architecture where each module has a specific responsibility. At the core is the Base module, which handles all API communication, while specialized modules manage payments (KP), checkout (KCO), shipping (KSS), and order management. All modules share common infrastructure for logging, configuration, and data transformation. Understanding this architecture helps developers know where to find functionality and how to extend the system safely.
## System Components
The Kustom integration consists of several interconnected modules, each with specific responsibilities:
```
┌─────────────────────────────────────────────────────────────┐
│ Magento Storefront │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ KP Payment │ │ KCO Iframe │ │ PWA/GraphQL │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Kustom API Layer (Base Module) │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌───────────┐ ┌──────┐ │
│ │ KP │ │ KCO │ │ KSS │ │Orderlines │ │ OM │ │
│ └────────┘ └────────┘ └────────┘ └───────────┘ └──────┘ │
│ ┌──────────────────────┐ ┌──────────────────────────┐ │
│ │ Logger / Settings │ │ Supporting Modules │ │
│ └──────────────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Core Architectural Principles
1. **Unified API Client**: All Kustom communication flows through the Base module
2. **Orderlines as Foundation**: Required by both KP and KCO, recalculated whenever totals change
3. **Public Callbacks**: KCO requires publicly accessible callback endpoints
4. **Centralized Logging**: All requests, responses, and callbacks captured by Logger module
5. **Order State Synchronization**: Order Management ensures Magento state matches Kustom
## Important Deprecation Notice
The following Klarna capabilities are being phased out and should not be relied upon for new development:
- **Klarna Payments (KP)** - legacy payment method support
- **Klarna Sign-In (SIWK)** - customer identity/prefill
- **Klarna On-Site Messaging (OSM)** - promotional widgets
These features continue to work but are scheduled for removal as Kustom transitions to a fully vendor-independent checkout architecture.
# Module Reference
This section provides detailed information about each Kustom module, including its purpose, key files, capabilities, and developer notes. The modules are organized into core foundation modules (Base, Logger, Orderlines), payment and checkout modules (KP, KCO, KSS), backend operations (Order Management), and optional enhancement modules (Express Checkout, Sign-In-With-Klarna, On-Site Messaging). Understanding each module's role helps developers navigate the codebase and troubleshoot issues effectively.
## Module Dependencies
The modules have specific dependencies that are **automatically handled by Composer**. When you install `kustom/module-checkout`, Composer will pull in all required dependencies in the correct order.
**Dependency Chain** (for reference):
```
### Base Module (foundation)
├─ Logger
├─ AdminSettings
└─ Orderlines
├─ KCO (Kustom Checkout)
│ └─ KSS (Shipping Service)
├─ KP (Klarna Payments)
│ └─ KpGraphQl
└─ Backend (Order Management)
Optional Modules:
├─ KEC (Express Checkout)
├─ SIWK (Sign-In-With-Klarna)
├─ OSM (On-Site Messaging)
└─ Support, Interoperability, etc.
```
## Core Foundation Modules
### Base Module (`kustom/module-base`)
The Base module is the foundation of the entire Kustom integration, providing shared infrastructure that all other modules depend on. It handles all HTTP communication with Kustom's API, manages authentication and environment selection (test vs production), and provides utilities for configuration access. Think of it as the core engine that powers everything else.
**Key Capabilities**:
- HTTP client wrapper for all API communication
- Authentication and environment selection (test/live)
- Store-scoped configuration retrieval
- Shared utilities and constants
- Error wrapping and transport handling
**Key Files**:
- `Api/ServiceInterface.php` - HTTP client interface
- `Api/OrderRepositoryInterface.php` - Order data access
- `Model/Api/Service.php` - Main API client implementation
- `Helper/ConfigHelper.php` - Configuration access
**Developer Notes**:
- Used by every other Kustom module
- Provides a connection factory for different Kustom services (KCO, KP, OM)
- Handles region-specific API endpoints (EU, US, AU, etc.)
### Admin Settings Module (`kustom/module-admin-settings`)
This module manages all configuration storage and retrieval for Kustom. It defines the admin panel structure where merchants configure API credentials, enable/disable modules, set merchant references, and control debug mode. All configuration values are store-scoped and cached by Magento's config system for performance.
**Key Capabilities**:
- API credentials management (test/live environments)
- Activation toggles per module
- Merchant references for ERP integration
- Debug mode configuration
**Key Files**:
- `etc/adminhtml/system.xml` - Admin configuration structure
- `etc/config.xml` - Default configuration values
**Developer Notes**:
- Configuration values are store-scoped
- Cached by Magento's config cache
- Sensitive data (passwords, API keys) is encrypted
### Logger Module (`kustom/module-logger`)
The Logger module captures all API interactions and callback activity for debugging and auditing purposes. It stores logs both in the database (`klarna_logs` table) and files (`var/log/klarna.log`), making it easy to track down issues during development and production. This is your first stop when troubleshooting integration problems.
**Key Capabilities**:
- Logs all API requests and responses
- Captures callback payloads (address, shipping, validate, push)
- Optional database logging for debugging
- File-based logging via Monolog
**Storage Locations**:
- Database: `klarna_logs` table
- File: `var/log/klarna.log`
**Cron Jobs**:
- `klarna_core_clean_logs` - Daily at midnight (cleanup old entries)
- `klarna_core_update_api_log` - Every minute (update log entries)
**Developer Notes**:
- **Enable debug mode in Admin Settings to see detailed logs**
- Essential for troubleshooting API issues
- Check `klarna_logs` table for order-specific debugging
### Orderlines Module (`kustom/module-orderlines`)
This critical module translates Magento's cart totals into the orderline format required by Kustom's API. It handles all the complexity of converting products, shipping, discounts, taxes, gift cards, and other totals into properly formatted line items with correct amounts in minor units (cents). Most integration issues stem from orderlines mismatches, making this module essential to understand.
**Key Capabilities**:
- Translates Magento totals into Kustom line items
- Handles tax, discounts, bundles, shipping
- Regenerates orderlines when cart totals change
- Ensures numeric values follow Kustom formatting rules
**Orderline Types Handled**:
- Product items
- Shipping costs
- Discounts (cart/coupon)
- Tax line items
- Gift cards
- Store credit
- Reward points
- Gift wrapping
- Custom surcharges
**Key Files**:
- `Model/Container/Parameter.php` - Orderline container
- `Model/Container/AbstractParameter.php` - Base for custom handlers
- `Helper/DataHelper.php` - Amount formatting utilities
**Developer Notes**:
- **CRITICAL**: Most Kustom integration issues stem from incorrect orderlines
- Always verify amounts are in minor units (cents)
- Ensure rounding precision matches Kustom expectations
- Line item totals must match grand total
- VAT/tax alignment is crucial
## Payment & Checkout Modules
### Kustom Payments (KP) (`kustom/module-payments`)
Klarna Payments integrates as a standard Magento payment method, allowing customers to select Klarna as their payment option during checkout while staying within Magento's native checkout flow. It manages payment sessions, handles authorization callbacks, and supports B2B purchases with multiple payment categories (pay now, pay later, pay over time). Note that this module is being phased out in favor of Kustom Checkout (KCO) for new implementations.
**Payment Method Code**: `klarna_kp`
**Key Capabilities**:
- Session management
- Authorization callback support
- B2B support
- Multiple payment categories (pay_now, pay_later, pay_over_time)
**Key Files**:
- `Model/Payment/Kp.php` - Payment method implementation
- `Model/Api/Builder/Kasper.php` - Session builder
- `Gateway/Command/` - Payment commands
**Database Tables**:
- `klarna_payments_quote` - Stores KP sessions per quote
**Cron Jobs**:
- `klarna_payments_quote_clean_old_entries` - Weekly on Saturday (cleanup expired sessions)
### Payments GraphQL (`kustom/module-payments-graph-ql`)
This module extends Klarna Payments with GraphQL support for headless and PWA storefronts. It provides mutations to create payment sessions and set payment methods, allowing frontend applications to integrate Klarna Payments without relying on traditional Magento checkout templates. Essential for modern, decoupled commerce architectures.
**Key Capabilities**:
- Create KP session via GraphQL mutation
- Return client token for frontend widget
- Handle authorization in headless architecture
**Key Files**:
- `Model/Resolver/CreateSession.php` - GraphQL resolver
- `etc/schema.graphqls` - GraphQL schema definition
**Developer Notes**:
- Required for PWA storefronts
- Frontend must load Klarna widget with client token
- See [API Reference](#api-reference) for GraphQL schema
### Kustom Checkout (KCO) (`kustom/module-kco`)
Kustom Checkout replaces Magento's entire checkout experience with an embedded iframe that handles address collection, shipping selection, and payment in one unified flow. Kustom manages the customer experience while Magento responds to callbacks to update orderlines and ultimately create the order. This is the recommended integration method for new implementations, offering the smoothest customer experience.
**Payment Method Code**: `klarna_kco`
**Key Capabilities**:
- Loads Kustom iframe on checkout page
- Kustom handles address collection, shipping selection, and payment
- Magento responds to Kustom callbacks
- Push callback creates/updates Magento order
**Callback Endpoints**:
| Callback | Route | Trigger | Purpose |
| --- | --- | --- | --- |
| address_update | `/kco/api/addressupdate` | Address change | Update orderlines with new address/tax |
| shipping_option_update | `/kco/api/shippingmethodupdate` | Shipping selection | Update orderlines with shipping cost |
| validate | `/kco/api/validate` | Before purchase | Final validation before Kustom authorizes |
| push | `/kco/api/push` | Post-authorization | Create/update Magento order |
| confirmation | `/kco/klarna/confirmation` | Success redirect | Display confirmation page |
**Key Files**:
- `Controller/Api/` - Callback controllers
- `Model/Api/Builder/` - Session builders
- `Model/Checkout/` - Order creation logic
**Database Tables**:
- `klarna_kco_quote` - Links quotes to KCO orders
**Required Configuration**:
- `checkout/klarna_kco/terms_url` - **REQUIRED** (checkout will fail without this)
- `checkout/klarna_kco_design/*` - Iframe styling options
**Developer Requirements**:
- ⚠️ Callbacks must be publicly accessible
- Use ngrok/localtunnel for local development
- Maintenance mode breaks KCO (callbacks return 503)
### Kustom Shipping Service (KSS) (`kustom/module-kss`)
The Shipping Service acts as a bridge between Magento's shipping methods and the Kustom Checkout iframe. When a customer enters their address in KCO, Kustom requests available shipping options from Magento via KSS, which returns the appropriate carriers and costs. This ensures shipping calculations stay synchronized between Magento and the checkout experience.
**Key Capabilities**:
- Supplies Magento shipping methods to KCO
- Supports multiple carriers and delivery modes
- Recalculates totals when shipping method changes
**REST API Endpoint**:
```
GET /V1/getDeliveryDataByKlarnaSessionId/:sessionId
Authentication: Required (Magento token)
Returns: JSON delivery data
```
**Key Files**:
- `Api/DeliveryDetailsInterface.php` - REST service interface
- `Model/ShippingProvider.php` - Shipping method provider
**Database Tables**:
- `klarna_shipping_method_gateway` - Stores shipping methods for KSS
**Developer Notes**:
- Used **exclusively by KCO callbacks**, not by external systems
- Session ID must be a valid Klarna session
- Called during `shipping_option_update` callback
## Backend Operations (Order Management)
### Backend Module (`kustom/module-backend`)
Order Management keeps Magento and Kustom synchronized throughout the order lifecycle. When you create an invoice in Magento, it triggers a capture in Kustom. Credit memos trigger refunds, and cancellations are synchronized automatically. This ensures that order states remain consistent across both systems without manual intervention.
**Key Capabilities**:
- Invoice → Capture in Kustom
- Credit Memo → Refund in Kustom
- Cancel Order → Cancel in Kustom
- Transaction info retrieval
**Gateway Commands**:
- `CaptureCommand` - Triggered on invoice creation
- `RefundCommand` - Triggered on credit memo
- `CancelCommand` - Triggered on order cancellation
- `FetchTransactionInfoCommand` - Retrieve transaction details
**Order Lifecycle & State Mapping**:
The order flow differs between KCO and KP, but both follow similar state transitions:
1. **Order Placement** (Push Callback for KCO, Authorization for KP):
- **Kustom State**: `AUTHORIZED`
- **Magento State**: `new` or `payment_review` (initially)
- **What happens**: Order is created in Magento, payment is authorized in Kustom
- **Note**: Order is NOT yet captured/invoiced at this stage
2. **Acknowledgement** (automatic after order placement):
- **Action**: `acknowledgeOrder()` API call
- **Purpose**: Notifies Kustom that Magento received the order
- **When**: Called automatically during push callback (KCO) or after authorization (KP)
- **Magento State**: Transitions from `payment_review` to configured status (typically `processing`)
- **Database**: Sets `is_acknowledged = 1` in `klarna_core_order` table
3. **Manual Invoice Creation** (merchant action):
- **Magento Action**: Create invoice in admin
- **Triggers**: `CaptureCommand`
- **Kustom API Call**: `POST /ordermanagement/v1/orders/{id}/captures`
- **Kustom State**: `AUTHORIZED` → `CAPTURED` (full capture) or `PART_CAPTURED` (partial)
- **Magento State**: `processing` (remains in processing)
4. **Refund** (merchant action):
- **Magento Action**: Create a credit memo
- **Triggers**: `RefundCommand`
- **Kustom API Call**: `POST /ordermanagement/v1/orders/{id}/refunds`
- **Kustom State**: `CAPTURED` → `REFUNDED` or `PART_REFUNDED`
- **Magento State**: `closed` (if fully refunded)
5. **Cancellation** (merchant action):
- **Magento Action**: Cancel order
- **Triggers**: `CancelCommand`
- **Kustom API Call**: `POST /ordermanagement/v1/orders/{id}/cancel`
- **Kustom State**: `AUTHORIZED` → `CANCELLED`
- **Magento State**: `canceled`
**Important Notes**:
- **No automatic capture**: Creating the order does NOT automatically capture payment. Capture only happens when you manually create an invoice.
- **Acknowledgement is automatic**: The order is acknowledged to Kustom immediately after placement, transitioning from `payment_review` to your configured order status.
- **No cron for capture**: There is no cron job that automatically captures orders. Merchants must manually invoice orders to trigger capture.
**Key Files**:
- `Gateway/Command/Capture.php` - Capture command (invoice creation)
- `Gateway/Command/Refund.php` - Refund command (credit memo)
- `Gateway/Command/Cancel.php` - Cancel command
- `Model/Api/OrderManagement.php` - Order Management API client
**Developer Notes**:
- Uses Magento's Payment Gateway pattern
- Commands extend both `KlarnaKpCommandPool` and `KcoPaymentCommandPool`
- Capture can include shipping information if "Create Shipment" is selected during invoice creation
- Extensible via dependency injection
## Optional / Enhancement Modules
### Express Checkout (KEC) (`kustom/module-kec`)
Express Checkout adds a one-click checkout button to product and cart pages, allowing returning customers to skip multiple checkout steps by using their pre-filled Kustom data. The button can be placed on product pages, cart, or during checkout, and its appearance is fully configurable to match your store's design.
**Key Capabilities**:
- Pre-populated customer data
- Skips Magento checkout steps
- Configurable button placement and styling
**Configuration**:
- `payment/kec/enabled`
- `payment/kec/position` - Placement (product, cart, checkout)
- `payment/kec/theme` + `shape` - Button styling
### Sign-In-With-Klarna (SIWK) (`kustom/module-siwk`)
This module implements OAuth-based authentication allowing customers to sign in with their Klarna account and automatically populate their profile, email, phone, and address information. The integration uses JWT tokens validated with the Firebase PHP-JWT library. Note that this module is being phased out as part of Kustom's transition away from Klarna-specific features.
**OAuth Scopes**:
- `openid` - Required
- `profile` - Name, DOB
- `email`, `phone`, `address` - Optional
**Database Tables**:
- `klarna_siwk_token` - OAuth tokens
- `klarna_siwk_customer` - Maps Magento customers to Klarna customers
**Security**:
- Uses Firebase PHP-JWT library for token validation
- Access tokens, refresh tokens, and ID tokens stored securely
### On-Site Messaging (OSM) (`kustom/module-osm`)
On-Site Messaging displays promotional payment messaging widgets throughout your store (product pages, cart, category pages, checkout) to inform customers about available payment options like "Pay in 3 installments" or "Buy now, pay later". The widgets are customizable and designed to increase conversion by highlighting flexible payment options. This module is being phased out.
**Placement Options**:
- Product pages
- Cart page
- Category pages
- Checkout
**Configuration**:
- `klarna/osm/enabled`
- `klarna/osm/theme` - Styling options
- `klarna/osm/position` - Widget placement
# Database Schema
Kustom modules use several database tables to store session data, order mappings, shipping information, and logs. The primary tables link Magento orders to Kustom orders, track payment sessions, and log all API interactions for debugging. Understanding the database schema is essential for troubleshooting, reporting, and building custom integrations. All tables use the `klarna_` prefix for easy identification, and most are linked to Magento's core tables via foreign keys with cascade delete behavior.
## Core Tables
### `klarna_core_order`
The central table that links Magento orders to Kustom orders, storing the order ID mappings, acknowledgement status, and business purchase flags. This table is created by the **Base Module** and is used by all other modules to look up Kustom order information.
**Key Columns**:
- `klarna_order_id` - Kustom's order identifier
- `session_id` - Checkout session ID
- `reservation_id` - Payment reservation ID (used for Order Management API calls)
- `order_id` - Foreign key to Magento `sales_order.entity_id` (CASCADE DELETE)
- `is_acknowledged` - Whether order has been acknowledged to Kustom (0 or 1)
- `is_b2b` - Business purchase flag
**Indexes**: `klarna_order_id`, `order_id`, `is_acknowledged`
**Schema Location**: `vendor/kustom/module-base/etc/db_schema.xml`
### `klarna_logs`
Stores all API requests and responses for debugging and auditing, including callback payloads. This table is your primary debugging resource when troubleshooting integration issues. Created by the **Logger Module**, it can grow large if debug mode is left enabled, so regular cleanup via cron is essential.
**Key Columns**:
- `status` - HTTP response status (200, 201, 400, etc.)
- `action` - Action type (`create_order`, `update_order`, `push`, `capture`, etc.)
- `klarna_id` - Kustom order ID
- `increment_id` - Magento order increment ID
- `url` - Full API endpoint URL
- `method` - HTTP method (GET, POST, etc.)
- `service` - Service type (KCO, KP, OM)
- `request` - Full request payload (JSON)
- `response` - Full response payload (JSON)
**Indexes**: `action`, `klarna_id`, `increment_id`, `status`, composite index
**Schema Location**: `vendor/kustom/module-logger/etc/db_schema.xml`
**Common Queries**:
```sql
-- Find all failed API calls
SELECT * FROM klarna_logs
WHERE status NOT IN ('200', '201')
ORDER BY created_at DESC;
-- Find logs for specific order
SELECT * FROM klarna_logs
WHERE increment_id = '000000123'
ORDER BY created_at ASC;
-- Find KCO callback logs
SELECT * FROM klarna_logs
WHERE url LIKE '%/api/%'
AND method = 'POST'
ORDER BY created_at DESC;
-- Find logs by action type
SELECT * FROM klarna_logs
WHERE action IN ('create_order', 'update_order', 'push')
ORDER BY created_at DESC
LIMIT 100;
```
### `klarna_payments_quote`
Manages Klarna Payments (KP) session data per quote, storing the client token for frontend widgets and authorization tokens for payment processing. Created by the **Kp Module**, entries are automatically cleaned up weekly via cron for expired sessions.
**Key Columns**:
- `session_id` - KP session identifier from Kustom API
- `client_token` - Token for loading Klarna widget in frontend
- `authorization_token` - Token received after customer authorizes payment
- `quote_id` - Foreign key to Magento `quote.entity_id` (CASCADE DELETE)
- `is_active` - Whether session is still valid
- `is_kec_session` - Express checkout flag
- `payment_methods` - Available payment categories (JSON)
**Indexes**: `session_id`, `authorization_token`, `quote_id`, `order_id`
**Schema Location**: `vendor/kustom/module-payments/etc/db_schema.xml`
### `klarna_kco_quote`
Links Magento quotes to Kustom Checkout order IDs, tracking whether the checkout session is active and whether cart contents have changed. Created by the **Kco Module**, this table is essential for maintaining the connection between Magento's cart and Kustom's checkout iframe.
**Key Columns**:
- `klarna_checkout_id` - Kustom Checkout order ID
- `quote_id` - Foreign key to Magento `quote.entity_id` (CASCADE DELETE)
- `is_active` - Whether checkout session is still active
- `is_changed` - Flag indicating cart contents changed since last KCO update
**Indexes**: `klarna_checkout_id`, `quote_id`
**Schema Location**: `vendor/kustom/module-kco/etc/db_schema.xml`
### `klarna_shipping_method_gateway`
Stores shipping method information for Kustom Shipping Service (KSS) integration, associating shipping options with Kustom checkout sessions. Created by the **Kss Module**, this table enables dynamic shipping method delivery to the KCO iframe based on customer address.
**Key Columns**:
- `klarna_session_id` - Kustom Checkout session ID
- `shipping_method_id` - Magento shipping method code (e.g., `flatrate_flatrate`)
- `is_pick_up_point` - Whether this is a pickup point delivery
- `shipping_amount` - Shipping cost
- `tax_amount` - Tax amount for shipping
- `delivery_details` - Additional delivery information (JSON)
**Indexes**: `klarna_session_id`
**Schema Location**: `vendor/kustom/module-kss/etc/db_schema.xml`
## Database Relationships
```
sales_order (Magento Core)
↓ (one-to-one)
klarna_core_order
↓ (references)
klarna_logs (many-to-one via klarna_order_id)
```
```
quote (Magento Core)
↓ (one-to-one)
klarna_kco_quote
↓ (references)
klarna_shipping_method_gateway (via klarna_checkout_id)
```
```
quote (Magento Core)
↓ (one-to-one)
klarna_payments_quote
```
```
customer_entity (Magento Core)
↓ (one-to-one)
klarna_siwk_token
↓
klarna_siwk_customer
```
## Maintenance & Cleanup
**Automated Cleanup**:
- `klarna_logs` - Cleaned daily by `klarna_core_clean_logs` cron
- `klarna_payments_quote` - Cleaned weekly by `klarna_payments_quote_clean_old_entries` cron
**Manual Cleanup**:
```sql
-- Clean old logs (older than 30 days)
DELETE FROM klarna_logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- Clean expired KP sessions (older than 7 days)
DELETE FROM klarna_payments_quote
WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY)
AND is_active = 0;
```
# Data Flow & Integration
Understanding how data flows through the Kustom integration is crucial for debugging and customization. This section maps out the complete journey from customer checkout through order creation, including all API calls, callbacks, and state transitions. For Kustom Payments (KP), the flow is relatively simple with session creation and authorization. For Kustom Checkout (KCO), the flow involves multiple callbacks as the customer enters their address, selects shipping, and completes payment. The Orderlines data flow shows how Magento cart totals are transformed into the format required by Kustom's API.
## Kustom Payments (KP) Flow
Klarna Payments integrates into Magento's native checkout flow as a payment method option. The process involves session creation, widget rendering, authorization, and optional capture.
```
1. Customer Reaches Payment Step
└─> Magento creates KP session
├─> API: POST /payments/v1/sessions
├─> Payload: Cart totals, customer data, orderlines
├─> Response: client_token + available payment_categories
└─> Database: Insert into klarna_payments_quote
2. Frontend Widget Loading
└─> Inject Klarna JavaScript SDK
└─> Initialize widget with client_token
└─> Widget displays payment method options (pay now/later/over time)
└─> Customer selects payment method and authorizes
└─> Widget returns authorization_token to Magento
3. Order Placement (Customer clicks "Place Order")
└─> Magento receives authorization_token
├─> Stores token in klarna_payments_quote
├─> Creates Magento order
│ ├─> Initial state: new or payment_review
│ └─> Database: Insert into sales_order
├─> Links order to Kustom
│ └─> Database: Insert into klarna_core_order
├─> Acknowledges order to Kustom (automatic)
│ ├─> API: POST /ordermanagement/v1/orders/{id}/acknowledge
│ ├─> Database: Set is_acknowledged = 1
│ └─> Magento state: payment_review → configured status (typically processing)
└─> Redirect to success page
4. Post-Order Operations (Merchant Actions)
└─> Create Invoice (manual action)
├─> Triggers: CaptureCommand
├─> API: POST /ordermanagement/v1/orders/{id}/captures
├─> Kustom state: AUTHORIZED → CAPTURED
└─> Payment is now settled
└─> Create Credit Memo (if needed)
├─> Triggers: RefundCommand
├─> API: POST /ordermanagement/v1/orders/{id}/refunds
└─> Kustom state: CAPTURED → REFUNDED
```
**Key Points**:
- Authorization happens during checkout, NOT at order placement
- Order is created with payment authorized but NOT captured
- Capture requires manual invoice creation (no auto-capture)
- Session expires after a period of inactivity
## Kustom Checkout (KCO) Flow
Kustom Checkout replaces Magento's entire checkout experience with an iframe. The flow involves multiple callbacks as Kustom requests updated information based on customer actions.
```
1. Checkout Page Load
└─> Magento creates KCO session
├─> API: POST /checkout/v3/orders
├─> Payload: Initial cart totals, merchant URLs, orderlines
├─> Response: order_id (Kustom session ID) + html_snippet (iframe code)
└─> Database: Insert into klarna_kco_quote
└─> Render html_snippet in page
└─> Kustom iframe loads and displays checkout form
2. Address Update Callback (⚠️ Publicly Accessible Required)
└─> Customer enters/changes address in iframe
└─> Kustom → POST /kco/api/addressupdate
├─> Payload: Customer address, Kustom order_id
├─> Magento loads quote by klarna_kco_quote.klarna_checkout_id
├─> Updates shipping address on quote
├─> Recalculates tax based on new address
├─> Regenerates orderlines with updated tax
├─> Response: Updated orderlines JSON
└─> Database: Updates quote shipping address
└─> Iframe updates totals display
3. Shipping Method Selection Callback (⚠️ Publicly Accessible Required)
└─> Iframe requests available shipping methods
└─> Kustom → POST /kco/api/shippingmethodupdate
├─> Payload: Customer address, Kustom order_id
├─> Magento calls KSS to get shipping options
├─> Returns: Available carriers and costs
└─> Database: Insert into klarna_shipping_method_gateway
└─> Customer selects shipping method in iframe
└─> Kustom → POST /kco/api/shippingmethodupdate (again)
├─> Payload: Selected shipping_method_id
├─> Magento applies shipping to quote
├─> Regenerates orderlines with shipping cost
├─> Response: Updated orderlines JSON
└─> Database: Updates quote with shipping method
└─> Iframe updates totals with shipping
4. Validation Callback (⚠️ Publicly Accessible Required)
└─> Customer clicks "Complete Purchase" button
└─> Kustom → POST /kco/api/validate
├─> Payload: Final order details
├─> Magento performs validation:
│ ├─ Inventory check
│ ├─ Quote still valid
│ ├─ Totals match
│ └─ Custom business rules
├─> Response: Success (200) or Failure (400 with error message)
└─> If validation fails, iframe shows error to customer
5. Authorization & Push Callback (⚠️ Publicly Accessible Required)
└─> Kustom authorizes payment with payment provider
└─> Kustom → POST /kco/api/push
├─> Payload: order_id, Kustom order details
├─> Magento creates order from quote:
│ ├─ Database: Insert into sales_order
│ ├─ Initial state: new or payment_review
│ ├─ Database: Insert into klarna_core_order
│ └─ Link: order_id → klarna_order_id
├─> Acknowledges order (automatic):
│ ├─ API: POST /ordermanagement/v1/orders/{id}/acknowledge
│ ├─ Database: Set is_acknowledged = 1
│ └─ State: payment_review → configured status (typically processing)
├─> Updates merchant references:
│ └─ API: POST /ordermanagement/v1/orders/{id}/merchant-references
├─> Response: Success (200)
└─> Sends order confirmation email
6. Confirmation Page
└─> Customer redirected to confirmation page
└─> GET /kco/klarna/confirmation?sid={session_id}
├─> Magento loads order by klarna_kco_quote.klarna_checkout_id
├─> Displays order details and success message
└─> Quote is converted, session expires
```
**Key Points**:
- **All callbacks must be publicly accessible** (use ngrok for local dev)
- Callbacks can be called multiple times (address changes, shipping changes)
- Order is only created during push callback (step 5)
- Magento must respond quickly (<2s) to keep checkout smooth
- If push callback fails, customer sees error but payment IS authorized (must handle manually)
## Orderlines Data Flow
```
Quote/Order Totals (Magento)
↓
Orderlines Container (collects all line items)
├─> Product Items Handler
├─> Shipping Handler
├─> Discount Handler
├─> Tax Handler
├─> Gift Card Handler
├─> Custom Handlers (extensible)
↓
Format to Kustom API Structure
├─> Convert to minor units (cents)
├─> Calculate tax rates
├─> Validate totals match
↓
Send to Kustom API
├─> Create Order API
├─> Update Order API
├─> Callbacks (address/shipping updates)
```
## State Synchronization
### Magento → Kustom (Merchant Actions)
These operations are triggered by merchant actions in Magento Admin and synchronized to Kustom via Order Management API:
| Magento Action | Gateway Command | Kustom API Call | Kustom State Change | When to Use |
| --- | --- | --- | --- | --- |
| Create Invoice | `CaptureCommand` | `POST /captures` | AUTHORIZED → CAPTURED | After fulfilling order, to settle payment |
| Create Credit Memo | `RefundCommand` | `POST /refunds` | CAPTURED → REFUNDED | To refund customer after capture |
| Cancel Order | `CancelCommand` | `POST /cancel` | AUTHORIZED → CANCELLED | To cancel before fulfillment/capture |
**Important**: These are **manual merchant actions**, not automatic. There are no cron jobs that auto-capture orders.
### Kustom → Magento (Payment Events)
These events originate from Kustom and trigger actions in Magento:
| Kustom Event | Callback Endpoint | Magento Action | Magento State | Notes |
| --- | --- | --- | --- | --- |
| Authorization Complete | `POST /kco/api/push` | Create order | new → payment_review → configured status | Automatic during KCO checkout |
| Authorization Complete | (none for KP) | Create order | new → payment_review → configured status | Automatic during KP checkout |
| Manual Capture (Portal) | (none) | **No action** | No change | ⚠️ Manual sync required - update order in Magento manually |
**Warning**: If a merchant captures an order in the Kustom merchant portal (outside Magento), Magento is NOT notified. You must manually create the invoice in Magento to keep systems synchronized.
# Extension Points
Kustom modules are designed to be extended without modifying core code. This section shows how to customize the integration using Magento's standard extension patterns: plugins (interceptors), events and observers, and dependency injection. Common use cases include adding custom data to payment sessions, inserting additional orderlines (fees, surcharges), integrating with ERP systems during order placement, and adding custom validation logic to callbacks. All examples follow Magento best practices and are safe for production use.
## Session Builder Plugins
Use Magento's plugin system (interceptors) to modify session data before it's sent to Kustom's API. This is useful for adding merchant references, custom data fields, or ERP integration identifiers that you want Kustom to store with the order.
**Use Cases**:
- Add ERP order references for reconciliation
- Include internal tracking IDs
- Pass custom metadata for analytics
- Add customer segment information
**Target Classes**:
- `Klarna\Kco\Model\Api\Builder\Kasper` - KCO session builder
- `Klarna\Kp\Model\Api\Builder\Kasper` - KP session builder
**Example: Add ERP Reference to KCO Session**
```xml
```
```php
getErpReference($quote);
// Add custom data
$result['merchant_data'] = json_encode([
'internal_id' => $quote->getId(),
'customer_segment' => $this->getCustomerSegment($quote)
]);
return $result;
}
private function getErpReference(CartInterface $quote): string
{
// Custom logic to generate ERP reference
return 'ERP-' . $quote->getId();
}
private function getCustomerSegment(CartInterface $quote): string
{
// Example: Determine customer segment
$customer = $quote->getCustomer();
if ($customer && $customer->getId()) {
return $customer->getGroupId() == 1 ? 'VIP' : 'Standard';
}
return 'Guest';
}
}
```
**Important Notes**:
- Use `afterGenerateRequest` to add data without breaking existing logic
- Use `aroundGenerateRequest` if you need to modify existing fields
- Always return the modified `$result` array
- Test thoroughly - incorrect data can cause Kustom API errors
## Orderlines Customization
Add custom line items to the orderlines sent to Kustom. This is essential when you have fees, surcharges, or discounts that aren't part of Magento's standard totals but need to be included in the payment amount.
**Use Cases**:
- Payment processing fees
- Handling fees or packaging charges
- Custom discounts not in Magento's discount system
- Environmental fees or taxes
- Membership discounts
**Important**: Custom orderlines must be added to the total correctly, otherwise Kustom will reject the order due to amount mismatch.
**Example: Add Payment Processing Fee**
```xml
- Vendor\Module\Model\Orderlines\ProcessingFee
```
```php
calculateProcessingFee($quote);
if ($fee > 0) {
$this->addItem([
'type' => 'surcharge',
'reference' => 'processing_fee',
'name' => 'Processing Fee',
'quantity' => 1,
'unit_price' => $this->helper->toApiFloat($fee), // Convert to cents
'tax_rate' => 0, // Tax rate in basis points (2500 = 25%)
'total_amount' => $this->helper->toApiFloat($fee),
'total_tax_amount' => 0,
]);
}
}
private function calculateProcessingFee(Quote $quote): float
{
// Custom logic - e.g., percentage of subtotal
$subtotal = $quote->getSubtotal();
return $subtotal * 0.02; // 2% processing fee
}
}
```
**Critical Points**:
- **Always convert to cents** using `toApiFloat()` - Kustom API expects minor units
- **Tax rates in basis points**: 2500 = 25%, 1000 = 10%, 0 = no tax
- **Total must match**: `total_amount = (unit_price × quantity) + total_tax_amount`
- Test with various cart totals to ensure rounding is correct
- Use `type: 'surcharge'` for fees, `type: 'discount'` for reductions
## Event Observers
Kustom modules dispatch events at key points in the checkout and order management flow. Use observers to react to these events for integrations, logging, or custom business logic.
**Common Use Cases**:
- ERP integration (send order data when placed)
- Custom analytics tracking
- Fraud prevention checks
- Inventory synchronization
- Email notifications to internal teams
**Available Events**:
```php
// Before creating KCO order
Event: klarna_kco_create_order_before
Payload: ['api' => $api, 'request' => &$createData]
// After creating KCO order
Event: klarna_kco_create_order_after
Payload: ['api' => $api, 'response' => $response, 'request' => $createData]
// Before updating KCO order
Event: klarna_kco_update_order_before
Payload: ['api' => $api, 'request' => &$updateData, 'order_id' => $orderId]
// After updating KCO order
Event: klarna_kco_update_order_after
Payload: ['api' => $api, 'response' => $response, 'request' => $updateData]
// Before placing Magento order (during push callback)
Event: klarna_kco_place_order_before
Payload: ['quote' => $quote, 'klarna_order' => $klarnaOrder]
// After placing Magento order (during push callback)
Event: klarna_kco_place_order_after
Payload: ['order' => $order, 'klarna_order' => $klarnaOrder]
```
**Example: ERP Integration on Order Placement**
```xml
```
```php
erpClient = $erpClient;
}
/**
* Send order to ERP system after KCO order placement
*/
public function execute(Observer $observer): void
{
/** @var OrderInterface $order */
$order = $observer->getData('order');
$klarnaOrder = $observer->getData('klarna_order');
try {
// Send to ERP
$erpOrderId = $this->erpClient->createOrder([
'magento_order_id' => $order->getIncrementId(),
'klarna_order_id' => $klarnaOrder->getKlarnaOrderId(),
'customer_data' => $this->extractCustomerData($order),
'items' => $this->extractItems($order),
]);
// Store ERP reference on order
$order->setData('erp_order_id', $erpOrderId);
$order->addCommentToStatusHistory('Sent to ERP: ' . $erpOrderId);
$order->save();
} catch (\Exception $e) {
// CRITICAL: Always catch exceptions in observers
// Don't let external system failures break customer checkout
$this->logger->error('ERP integration failed: ' . $e->getMessage());
}
}
private function extractCustomerData(OrderInterface $order): array
{
return [
'email' => $order->getCustomerEmail(),
'name' => $order->getCustomerName(),
'phone' => $order->getBillingAddress()->getTelephone(),
];
}
private function extractItems(OrderInterface $order): array
{
$items = [];
foreach ($order->getAllVisibleItems() as $item) {
$items[] = [
'sku' => $item->getSku(),
'name' => $item->getName(),
'qty' => $item->getQtyOrdered(),
'price' => $item->getPrice(),
];
}
return $items;
}
}
```
**Best Practices for Observers**:
- **Always wrap in try/catch** - External system failures shouldn't break checkout
- **Log failures** - But don't throw exceptions in `place_order_after` events
- **Keep execution fast** - Long-running observers slow down checkout
- **Consider async processing** - Use message queues for time-consuming tasks
- **Test edge cases** - What if ERP is down? Network timeout? Invalid response?
## Payment Gateway Command Extension
Extend Magento's payment gateway pattern to add custom commands to the Kustom payment flow. This is an advanced extension point for custom payment operations.
**Use Cases**:
- Custom pre-authorization checks
- Fraud detection integration
- Split payment handling
- Custom settlement logic
**Available Command Pools**:
- `KlarnaKpCommandPool` - For Klarna Payments (KP)
- `KcoPaymentCommandPool` - For Kustom Checkout (KCO)
**Example: Add Custom Pre-Authorization Validation**
```xml
- Vendor\Module\Gateway\Command\ValidateCommand
```
```php
getPayment();
$order = $payment->getOrder();
// Custom validation logic
if (!$this->fraudCheck($order)) {
throw new \Magento\Framework\Exception\LocalizedException(
__('Order failed fraud validation')
);
}
return null;
}
private function fraudCheck($order): bool
{
// Your fraud detection logic
return true;
}
}
```
**Note**: This is an advanced extension point. Most customizations can be done with simpler observers.
## Callback Customization
Hook into KCO callbacks to add custom validation, modify behavior, or integrate with external systems during the checkout flow.
**Use Cases**:
- Custom inventory validation during checkout
- Credit limit checks before authorization
- Fraud scoring during validation callback
- Custom shipping restrictions
- Business hours validation
**Available Callback Events**:
- `klarna_kco_validate` - Before final validation (can reject checkout)
- `klarna_kco_push` - During order creation (can modify order)
- `klarna_kco_address_update` - When address changes
- `klarna_kco_shipping_update` - When shipping method changes
**Example: Custom Inventory Validation**
```xml
```
```php
inventoryCheck = $inventoryCheck;
$this->customerCreditValidator = $customerCreditValidator;
$this->logger = $logger;
}
/**
* Perform custom validation before order authorization
*
* IMPORTANT: Exceptions thrown here will be shown to the customer
* in the Kustom iframe, so messages must be customer-friendly.
*/
public function execute(Observer $observer): void
{
$quote = $observer->getData('quote');
// Log validation attempt for debugging
$this->logger->info('Custom validation for quote: ' . $quote->getId());
// Check real-time inventory from warehouse system
if (!$this->inventoryCheck->hasStock($quote)) {
throw new \Magento\Framework\Exception\LocalizedException(
__('Sorry, some items are no longer in stock. Please update your cart.')
);
}
// Validate customer credit limit for B2B
if ($quote->getCustomerId() && !$this->customerCreditValidator->validate($quote)) {
throw new \Magento\Framework\Exception\LocalizedException(
__('This order exceeds your available credit limit. Please contact customer service.')
);
}
// Example: Block orders during maintenance window
if ($this->isMaintenanceWindow()) {
throw new \Magento\Framework\Exception\LocalizedException(
__('Orders are temporarily unavailable during system maintenance. Please try again later.')
);
}
}
private function isMaintenanceWindow(): bool
{
// Example: Block orders between 2 AM and 3 AM
$hour = (int)date('G');
return $hour >= 2 && $hour < 3;
}
}
```
**Critical Considerations**:
- **Exception messages are shown to customers** - Keep them friendly and actionable
- **Keep validation fast** (<1 second) - Customers are waiting in the checkout iframe
- **Don't fail on external system errors** - If your inventory API is down, allow the order or have a fallback
- **Log all validation attempts** - Essential for debugging failed checkouts
- **Test thoroughly** - Failed validation prevents order completion
# API Reference
Kustom provides both REST and GraphQL APIs for different integration scenarios. The REST API is primarily used internally by the modules for server-to-server communication, with the Shipping Service (KSS) endpoint being the main developer-facing API for customizing shipping options during checkout. The GraphQL API is essential for headless, PWA, and mobile implementations, enabling complete checkout flows without relying on Magento's default frontend. This section provides complete authentication examples, detailed field descriptions, error handling patterns, and full workflow demonstrations for common integration scenarios.
## REST API
The REST API uses Magento's standard authentication mechanism and follows REST conventions. All endpoints require proper authentication tokens and return standard HTTP status codes.
### Authentication
REST API calls require a valid Magento integration or admin token. Create an integration in **System → Extensions → Integrations** to obtain an access token.
**Example Token Request**:
```bash
# Get admin token (for testing only - use integrations in production)
curl -X POST "https://example.com/rest/V1/integration/admin/token" \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "password123"}'
# Response
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
**Using Token in Requests**:
```bash
curl -X GET "https://example.com/rest/V1/endpoint" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json"
```
**Important Notes**:
- Admin tokens expire based on Magento configuration (default: 4 hours)
- Integration tokens do not expire but can be revoked
- Always use HTTPS in production
- Store tokens securely, never expose in frontend code
### Get Shipping Options (KSS)
This endpoint retrieves available shipping methods for a Kustom Checkout session, typically used when implementing the Kustom Shipping Service integration or building custom shipping selection interfaces.
**Endpoint**: `GET /rest/V1/getDeliveryDataByKlarnaSessionId/:sessionId`
**Authentication**: Required (Magento integration token)
**When to Use**:
- Building custom checkout flows that need to display shipping options outside the standard KCO iframe
- Implementing third-party shipping calculators that integrate with Kustom
- Creating mobile apps that need to fetch shipping data programmatically
**Parameters**:
- `sessionId` (path, required) - The Klarna session ID from the checkout session
**Success Response (200 OK)**:
```json
{
"shipping_options": [
{
"id": "flatrate_flatrate",
"name": "Flat Rate",
"description": "Fixed shipping cost",
"price": 500,
"tax_amount": 125,
"tax_rate": 2500,
"preselected": true
},
{
"id": "tablerate_bestway",
"name": "Best Way",
"description": "Table Rate shipping method",
"price": 350,
"tax_amount": 87,
"tax_rate": 2500,
"preselected": false
}
]
}
```
**Response Fields**:
- `id` - Unique identifier combining carrier and method codes (e.g., `flatrate_flatrate`)
- `name` - Display name shown to customers
- `description` - Additional details about the shipping method
- `price` - Shipping cost in minor units (500 = 5.00 in currency)
- `tax_amount` - Tax on shipping in minor units
- `tax_rate` - Tax rate in basis points (2500 = 25%)
- `preselected` - Whether this option is selected by default
**Error Responses**:
```json
// 401 Unauthorized - Invalid or expired token
{
"message": "The consumer isn't authorized to access %resources.",
"parameters": {
"resources": "Magento_Backend::all"
}
}
// 404 Not Found - Invalid session ID
{
"message": "No such entity with sessionId = invalid_id"
}
// 500 Internal Server Error - Quote or session issue
{
"message": "Unable to retrieve shipping options for session"
}
```
**Complete Example**:
```bash
# 1. Obtain integration token (one-time setup in Admin)
# System → Extensions → Integrations → Create new integration
# Save and activate, copy the access token
# 2. Make API request
curl -X GET \
"https://example.com/rest/V1/getDeliveryDataByKlarnaSessionId/abc123def456" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json"
# 3. Handle response
# Success: Parse shipping_options array and display to user
# Error: Check message field and handle appropriately
```
**Common Issues**:
- **401 error**: Token expired or lacks permissions - regenerate integration token
- **404 error**: Session ID is invalid or expired - create new checkout session
- **Empty shipping_options array**: No shipping methods configured for customer's address or all methods filtered out
## GraphQL API
The GraphQL API enables headless implementations by providing flexible queries and mutations for the complete checkout flow. Unlike the REST API, GraphQL allows you to request exactly the data you need in a single request, making it ideal for mobile apps, PWA storefronts, and custom frontend frameworks.
### Authentication for GraphQL
GraphQL endpoints support both guest cart tokens and customer tokens depending on the checkout scenario.
**Guest Checkout Flow**:
```graphql
# 1. Create empty cart (no authentication needed)
mutation {
createEmptyCart
}
# Response
{
"data": {
"createEmptyCart": "xyz123abc456"
}
}
# 2. Use cart_id in all subsequent mutations
# No additional authentication required for guest checkout
```
**Customer Checkout Flow**:
```bash
# 1. Generate customer token via REST
curl -X POST "https://example.com/rest/V1/integration/customer/token" \
-H "Content-Type: application/json" \
-d '{"username": "customer@example.com", "password": "password"}'
# Response
"customer_token_here"
# 2. Include token in GraphQL headers
curl -X POST "https://example.com/graphql" \
-H "Authorization: Bearer customer_token_here" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { createCustomerCart }"}'
```
**Important Notes**:
- Guest cart tokens are embedded in the `cart_id` itself
- Customer tokens are required for accessing saved addresses and order history
- Cart IDs expire based on Magento quote lifetime configuration (default: 30 days for guests, persistent for customers)
- Always validate token expiration and handle re-authentication gracefully
### Create Klarna Payments Session
This mutation initializes a Klarna Payments session and returns the client token needed to render the Klarna payment widget on your frontend. Call this after setting shipping address and method, as Klarna needs the complete order total to create a valid session.
**When to Use**:
- Building PWA or headless storefronts with Klarna Payments
- Creating mobile checkout experiences
- Implementing custom payment flows that need Klarna as an option
**Prerequisites**:
- Cart must have items
- Shipping address must be set
- Shipping method must be selected
- Cart totals must be calculated
**Mutation**:
```graphql
mutation CreateKlarnaSession($cartId: String!) {
createKlarnaPaymentsSession(input: {
cart_id: $cartId
}) {
client_token
payment_method_categories {
identifier
name
asset_urls {
descriptive
standard
}
}
}
}
```
**Variables**:
```json
{
"cartId": "xyz123abc456"
}
```
**Success Response**:
```json
{
"data": {
"createKlarnaPaymentsSession": {
"client_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiYWJjMTIzIiwiaWF0IjoxNjMwMDAwMDAwfQ...",
"payment_method_categories": [
{
"identifier": "pay_later",
"name": "Pay later in 30 days",
"asset_urls": {
"descriptive": "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg",
"standard": "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.png"
}
},
{
"identifier": "pay_over_time",
"name": "Pay over time",
"asset_urls": {
"descriptive": "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg",
"standard": "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.png"
}
},
{
"identifier": "pay_now",
"name": "Pay now",
"asset_urls": {
"descriptive": "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg",
"standard": "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.png"
}
}
]
}
}
}
```
**Response Fields**:
- `client_token` - JWT token required to initialize Klarna's payment widget (expires in ~1 hour)
- `payment_method_categories` - Available payment options for the customer's region and order total
- `identifier` - Unique ID for the payment category (`pay_later`, `pay_over_time`, `pay_now`)
- `name` - Customer-facing display name
- `asset_urls` - Logo images for rendering payment options
- `descriptive` - SVG version (recommended for web)
- `standard` - PNG version (fallback)
**Error Responses**:
```json
// Missing shipping address
{
"errors": [
{
"message": "The shipping address is missing. Set the address and try again.",
"extensions": {
"category": "graphql-input"
}
}
],
"data": {
"createKlarnaPaymentsSession": null
}
}
// Invalid cart ID
{
"errors": [
{
"message": "Could not find a cart with ID \"invalid_cart\"",
"extensions": {
"category": "graphql-no-such-entity"
}
}
],
"data": {
"createKlarnaPaymentsSession": null
}
}
// Cart total too low
{
"errors": [
{
"message": "The order total is below Klarna's minimum amount",
"extensions": {
"category": "graphql-input"
}
}
],
"data": {
"createKlarnaPaymentsSession": null
}
}
```
**Frontend Implementation Example**:
```javascript
// 1. Call GraphQL mutation to get client_token
const response = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `mutation { createKlarnaPaymentsSession(input: { cart_id: "${cartId}" }) { client_token } }`
})
});
const { data } = await response.json();
const clientToken = data.createKlarnaPaymentsSession.client_token;
// 2. Load Klarna SDK
const script = document.createElement('script');
script.src = 'https://x.klarna.com/kp/lib/v1/api.js';
script.onload = () => initKlarna(clientToken);
document.head.appendChild(script);
// 3. Initialize Klarna widget
function initKlarna(clientToken) {
Klarna.Payments.init({ client_token: clientToken });
Klarna.Payments.load({
container: '#klarna-payments-container',
payment_method_category: 'pay_later'
}, (res) => {
if (res.show_form) {
// Widget loaded successfully
}
});
}
// 4. Authorize payment when customer clicks "Place Order"
Klarna.Payments.authorize({
payment_method_category: 'pay_later'
}, (res) => {
if (res.approved) {
// Use res.authorization_token in setPaymentMethodOnCart mutation
setPaymentMethod(res.authorization_token);
}
});
```
**Common Issues**:
- **Token expires before customer completes payment**: Refresh session by calling mutation again
- **No payment categories returned**: Check customer's country and cart total against Klarna's requirements
- **Widget fails to load**: Verify CSP headers allow Klarna domains (`x.klarna.com`, `x.klarnacdn.net`)
### Set Klarna Payment Method
This mutation sets Klarna Payments as the selected payment method for the cart and attaches the authorization token obtained from the Klarna widget. This must be called after the customer authorizes payment through the widget and before placing the order.
**When to Use**:
- After customer successfully authorizes payment in Klarna widget
- Before calling `placeOrder` mutation
- As part of the final checkout step in headless flows
**Prerequisites**:
- Klarna session created (`createKlarnaPaymentsSession` called)
- Customer authorized payment through Klarna widget
- Valid `authorization_token` received from widget callback
**Mutation**:
```graphql
mutation SetKlarnaPayment($cartId: String!, $authToken: String!) {
setPaymentMethodOnCart(input: {
cart_id: $cartId
payment_method: {
code: "klarna_kp"
klarna: {
authorization_token: $authToken
}
}
}) {
cart {
selected_payment_method {
code
title
}
}
}
}
```
**Variables**:
```json
{
"cartId": "xyz123abc456",
"authToken": "b4c5d6e7-f8g9-h0i1-j2k3-l4m5n6o7p8q9"
}
```
**Success Response**:
```json
{
"data": {
"setPaymentMethodOnCart": {
"cart": {
"selected_payment_method": {
"code": "klarna_kp",
"title": "Klarna Payments"
}
}
}
}
}
```
**Error Responses**:
```json
// Invalid authorization token
{
"errors": [
{
"message": "Klarna authorization token is invalid or expired",
"extensions": {
"category": "graphql-input"
}
}
],
"data": {
"setPaymentMethodOnCart": null
}
}
// Payment method not available
{
"errors": [
{
"message": "The payment method klarna_kp is not available",
"extensions": {
"category": "graphql-input"
}
}
],
"data": {
"setPaymentMethodOnCart": null
}
}
// Cart modified after authorization
{
"errors": [
{
"message": "Cart totals have changed since authorization. Please authorize payment again.",
"extensions": {
"category": "graphql-input"
}
}
],
"data": {
"setPaymentMethodOnCart": null
}
}
```
**Important Notes**:
- Authorization tokens are single-use and expire quickly (typically 5-10 minutes)
- If cart totals change after authorization, you must create a new session and re-authorize
- Always call this mutation immediately before `placeOrder` to minimize token expiration risk
- For KCO (Kustom Checkout), use the embedded iframe instead - this mutation is only for KP
**Common Issues**:
- **Token expired**: Customer took too long to complete checkout - reinitialize Klarna widget and re-authorize
- **Cart changed**: Items added/removed or prices updated - refresh session and re-authorize
- **Payment method unavailable**: Check configuration in Admin and verify customer's region is supported
### Complete GraphQL Checkout Flow
This example demonstrates a complete headless checkout flow from empty cart to placed order, including all required steps, error handling, and best practices for production implementations.
**Flow Overview**:
1. Create cart
2. Add products
3. Set shipping address
4. Set shipping method
5. Create Klarna session
6. Load Klarna widget and authorize (frontend)
7. Set payment method with auth token
8. Place order
**Step 1: Create Empty Cart**
```graphql
mutation CreateCart {
createEmptyCart
}
```
**Response**:
```json
{
"data": {
"createEmptyCart": "xyz123abc456"
}
}
```
**Step 2: Add Products to Cart**
```graphql
mutation AddProducts($cartId: String!) {
addSimpleProductsToCart(input: {
cart_id: $cartId
cart_items: [
{
data: {
sku: "PRODUCT-SKU-001"
quantity: 2
}
},
{
data: {
sku: "PRODUCT-SKU-002"
quantity: 1
}
}
]
}) {
cart {
items {
id
quantity
product {
name
sku
}
prices {
row_total {
value
currency
}
}
}
prices {
grand_total {
value
currency
}
}
}
}
}
```
**Variables**:
```json
{
"cartId": "xyz123abc456"
}
```
**Response**:
```json
{
"data": {
"addSimpleProductsToCart": {
"cart": {
"items": [
{
"id": "123",
"quantity": 2,
"product": {
"name": "Example Product 1",
"sku": "PRODUCT-SKU-001"
},
"prices": {
"row_total": {
"value": 39.98,
"currency": "USD"
}
}
},
{
"id": "124",
"quantity": 1,
"product": {
"name": "Example Product 2",
"sku": "PRODUCT-SKU-002"
},
"prices": {
"row_total": {
"value": 24.99,
"currency": "USD"
}
}
}
],
"prices": {
"grand_total": {
"value": 64.97,
"currency": "USD"
}
}
}
}
}
}
```
**Step 3: Set Shipping Address**
```graphql
mutation SetShippingAddress($cartId: String!) {
setShippingAddressesOnCart(input: {
cart_id: $cartId
shipping_addresses: [{
address: {
firstname: "John"
lastname: "Doe"
company: "Example Corp"
street: ["123 Main Street", "Apartment 4B"]
city: "New York"
region: "NY"
postcode: "10001"
country_code: "US"
telephone: "+1-555-123-4567"
save_in_address_book: false
}
}]
}) {
cart {
shipping_addresses {
firstname
lastname
street
city
region { code label }
postcode
country { code label }
telephone
available_shipping_methods {
carrier_code
method_code
carrier_title
method_title
amount {
value
currency
}
}
}
}
}
}
```
**Response**:
```json
{
"data": {
"setShippingAddressesOnCart": {
"cart": {
"shipping_addresses": [
{
"firstname": "John",
"lastname": "Doe",
"street": ["123 Main Street", "Apartment 4B"],
"city": "New York",
"region": {
"code": "NY",
"label": "New York"
},
"postcode": "10001",
"country": {
"code": "US",
"label": "United States"
},
"telephone": "+1-555-123-4567",
"available_shipping_methods": [
{
"carrier_code": "flatrate",
"method_code": "flatrate",
"carrier_title": "Flat Rate",
"method_title": "Fixed",
"amount": {
"value": 5.00,
"currency": "USD"
}
},
{
"carrier_code": "tablerate",
"method_code": "bestway",
"carrier_title": "Best Way",
"method_title": "Table Rate",
"amount": {
"value": 3.50,
"currency": "USD"
}
}
]
}
]
}
}
}
}
```
**Step 4: Set Shipping Method**
```graphql
mutation SetShippingMethod($cartId: String!) {
setShippingMethodsOnCart(input: {
cart_id: $cartId
shipping_methods: [{
carrier_code: "flatrate"
method_code: "flatrate"
}]
}) {
cart {
shipping_addresses {
selected_shipping_method {
carrier_code
method_code
carrier_title
method_title
amount {
value
currency
}
}
}
prices {
grand_total {
value
currency
}
}
}
}
}
```
**Response**:
```json
{
"data": {
"setShippingMethodsOnCart": {
"cart": {
"shipping_addresses": [
{
"selected_shipping_method": {
"carrier_code": "flatrate",
"method_code": "flatrate",
"carrier_title": "Flat Rate",
"method_title": "Fixed",
"amount": {
"value": 5.00,
"currency": "USD"
}
}
}
],
"prices": {
"grand_total": {
"value": 69.97,
"currency": "USD"
}
}
}
}
}
}
```
**Step 5: Create Klarna Session**
```graphql
mutation CreateKlarnaSession($cartId: String!) {
createKlarnaPaymentsSession(input: {
cart_id: $cartId
}) {
client_token
payment_method_categories {
identifier
name
}
}
}
```
**Response**:
```json
{
"data": {
"createKlarnaPaymentsSession": {
"client_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"payment_method_categories": [
{
"identifier": "pay_later",
"name": "Pay later in 30 days"
},
{
"identifier": "pay_over_time",
"name": "Pay over time"
}
]
}
}
}
```
**Step 6: Load Klarna Widget (Frontend JavaScript)**
```javascript
// Initialize Klarna SDK with client_token from Step 5
Klarna.Payments.init({
client_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
});
// Load payment widget
Klarna.Payments.load({
container: '#klarna-payments-container',
payment_method_category: 'pay_later'
}, (res) => {
console.log('Widget loaded:', res.show_form);
});
// Authorize when customer clicks "Place Order"
document.getElementById('place-order-btn').addEventListener('click', () => {
Klarna.Payments.authorize({
payment_method_category: 'pay_later'
}, (res) => {
if (res.approved) {
// Proceed to Step 7 with res.authorization_token
setPaymentMethodAndPlaceOrder(res.authorization_token);
} else {
alert('Payment not approved. Please try again.');
}
});
});
```
**Step 7: Set Payment Method**
```graphql
mutation SetPayment($cartId: String!, $authToken: String!) {
setPaymentMethodOnCart(input: {
cart_id: $cartId
payment_method: {
code: "klarna_kp"
klarna: {
authorization_token: $authToken
}
}
}) {
cart {
selected_payment_method {
code
title
}
}
}
}
```
**Variables**:
```json
{
"cartId": "xyz123abc456",
"authToken": "b4c5d6e7-f8g9-h0i1-j2k3-l4m5n6o7p8q9"
}
```
**Response**:
```json
{
"data": {
"setPaymentMethodOnCart": {
"cart": {
"selected_payment_method": {
"code": "klarna_kp",
"title": "Klarna Payments"
}
}
}
}
}
```
**Step 8: Place Order**
```graphql
mutation PlaceOrder($cartId: String!) {
placeOrder(input: {
cart_id: $cartId
}) {
order {
order_number
}
}
}
```
**Response**:
```json
{
"data": {
"placeOrder": {
"order": {
"order_number": "000000123"
}
}
}
}
```
**Error Handling Best Practices**:
```javascript
async function executeGraphQLMutation(query, variables) {
try {
const response = await fetch('/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Store': 'default' // Include store code for multi-store setups
},
body: JSON.stringify({ query, variables })
});
const result = await response.json();
// Check for GraphQL errors
if (result.errors) {
console.error('GraphQL errors:', result.errors);
// Handle specific error types
const error = result.errors[0];
if (error.extensions?.category === 'graphql-no-such-entity') {
// Cart expired or invalid
alert('Your cart has expired. Please start over.');
createNewCart();
} else if (error.extensions?.category === 'graphql-input') {
// Validation error
alert(`Validation error: ${error.message}`);
} else {
// Generic error
alert('An error occurred. Please try again.');
}
return null;
}
return result.data;
} catch (error) {
console.error('Network error:', error);
alert('Network error. Please check your connection.');
return null;
}
}
```
**Complete Integration Example**:
```javascript
// Complete checkout flow with error handling
async function completeCheckout() {
try {
// 1. Create cart
const cartData = await executeGraphQLMutation(
'mutation { createEmptyCart }',
{}
);
const cartId = cartData.createEmptyCart;
// 2. Add products
await executeGraphQLMutation(addProductsMutation, {
cartId,
items: [{ sku: 'PROD-001', quantity: 1 }]
});
// 3. Set shipping address
await executeGraphQLMutation(setShippingAddressMutation, {
cartId,
address: customerAddress
});
// 4. Set shipping method
await executeGraphQLMutation(setShippingMethodMutation, {
cartId,
carrier: 'flatrate',
method: 'flatrate'
});
// 5. Create Klarna session
const klarnaData = await executeGraphQLMutation(
createKlarnaSessionMutation,
{ cartId }
);
const clientToken = klarnaData.createKlarnaPaymentsSession.client_token;
// 6. Initialize Klarna widget
await initializeKlarnaWidget(clientToken);
// 7. Authorize payment (triggered by user clicking button)
const authToken = await authorizeKlarnaPayment();
// 8. Set payment method
await executeGraphQLMutation(setPaymentMethodMutation, {
cartId,
authToken
});
// 9. Place order
const orderData = await executeGraphQLMutation(placeOrderMutation, {
cartId
});
// Success!
window.location.href = `/checkout/success?order=${orderData.placeOrder.order.order_number}`;
} catch (error) {
console.error('Checkout failed:', error);
alert('Checkout failed. Please try again or contact support.');
}
}
```
**Performance Optimization Tips**:
- Batch independent mutations when possible (e.g., set email and billing address together)
- Cache cart ID in localStorage to resume abandoned carts
- Implement retry logic for network failures
- Show loading states between each step
- Validate input client-side before sending mutations to reduce round trips
# Troubleshooting
Most Kustom integration issues fall into a few common categories: checkout not loading, region mismatches, callback failures, orderlines mismatches, and payment authorization problems. This section provides step-by-step solutions for each, along with diagnostic commands and SQL queries to identify root causes. The debug checklist walks through systematic troubleshooting, and the performance section addresses checkout speed and database growth issues. Always enable debug mode and check the `klarna_logs` table when investigating problems.
## Common Issues
### 1. Checkout Fails to Load
**Symptoms**:
- Kustom iframe doesn't appear
- Blank checkout page
- JavaScript console errors
**Solutions**:
Check module is enabled:
```bash
php bin/magento module:status | grep Klarna
```
Verify configuration:
- Admin → Stores → Configuration → Sales → Payment Methods → Kustom Checkout
- Ensure "Enabled" is set to "Yes"
- Verify API credentials are entered
- Check "Terms URL" is configured
Clear caches and redeploy:
```bash
php bin/magento cache:flush
php bin/magento cache:clean config
# If issue persists, redeploy static content
php bin/magento setup:static-content:deploy
```
Check browser console for CSP errors:
- Add Kustom domains to Content Security Policy if using CSP headers
- Common CSP issue: iframe not loading due to frame-src restrictions
Check logs:
```sql
SELECT * FROM klarna_logs
WHERE action = 'create_order'
AND status != '200'
ORDER BY created_at DESC
LIMIT 10;
```
### 2. Region Not Supported
**Symptoms**:
- Error: "Region not supported"
- Checkout fails to initialize
**Solutions**:
Verify region alignment:
- **Store country**: Admin → Stores → Configuration → General → Country
- **Base currency** matches region (EUR for EU, USD for US, etc.)
- **Merchant region** matches store region
- **API credentials** match region (EU credentials for EU stores, US credentials for US stores)
Ensure alignment across all three:
- Base currency + Store country + Merchant account region must match
- Example: EUR + Germany + EU merchant account ✓
- Example: USD + Germany + EU merchant account ✗ (currency mismatch)
Check API region configuration:
- Admin → Stores → Configuration → Sales → Payment Methods → Kustom API
- Ensure correct region is selected
- Verify region-specific credentials
### 3. Callbacks Not Working (Webhook Issues)
**Symptoms**:
- Checkout gets stuck at address entry
- Shipping options don't load
- Order doesn't complete after payment
- Push callback doesn't trigger
**Solutions**:
Verify webhook/callback endpoint is reachable:
- Callbacks must be **publicly accessible** (not behind firewall)
- Must return HTTP 200 response
- Must not be blocked by maintenance mode or IP restrictions
Verify callbacks are publicly accessible:
```bash
# Test from external server (not localhost)
curl -X POST https://yourdomain.com/kco/api/addressupdate \
-H "Content-Type: application/json" \
-d '{"test": true}'
# Should return HTTP 200, not 403/404/503
```
Check maintenance mode:
```bash
# Maintenance mode blocks callbacks
php bin/magento maintenance:status
# If enabled: php bin/magento maintenance:disable
```
For local development, use ngrok:
```bash
ngrok http 80
# Update Magento base URL to ngrok URL
```
Check callback logs:
```sql
SELECT * FROM klarna_logs
WHERE url LIKE '%/api/%'
ORDER BY created_at DESC
LIMIT 20;
```
### 4. Orderlines Mismatch
**Symptoms**:
- Error: "Order total mismatch"
- API returns 400 error
- Logs show "orderlines validation failed"
**Solutions**:
Enable debug mode:
- Admin → Stores → Configuration → Sales → Payment Methods → Kustom API
- Set "Debug Mode" to "Yes"
- Flush caches
Check orderlines in logs:
```sql
SELECT request, response FROM klarna_logs
WHERE action IN ('create_order', 'update_order')
AND status = '400'
ORDER BY created_at DESC
LIMIT 1;
```
Common orderlines issues:
- **Rounding errors**: Ensure amounts are in minor units (cents) with proper rounding
- **Tax calculation**: Verify tax rates match (2500 = 25%)
- **Missing line items**: Check all totals are represented (shipping, discounts, etc.)
- **Negative amounts**: Discounts should have positive `unit_price` but reduce total
Validate orderlines total:
```php
// In custom observer/plugin
$orderlines = $klarnaRequest['order_lines'];
$calculatedTotal = array_sum(array_column($orderlines, 'total_amount'));
$quoteTotal = $quote->getGrandTotal() * 100; // Convert to cents
if ($calculatedTotal !== $quoteTotal) {
$this->logger->error('Orderlines mismatch', [
'calculated' => $calculatedTotal,
'quote' => $quoteTotal,
'difference' => $calculatedTotal - $quoteTotal
]);
}
```
### 5. Payment Authorization Fails
**Symptoms**:
- Customer completes checkout but order doesn't create
- Error: "Authorization failed"
- Push callback doesn't trigger
**Solutions**:
Check Kustom merchant portal:
- Log in to merchant portal
- Check if order appears as "AUTHORIZED"
- If yes: push callback failed (see callback troubleshooting above)
- If no: payment was declined by Kustom
Verify push callback endpoint:
```bash
curl -X POST https://yourdomain.com/kco/api/push \
-H "Content-Type: application/json" \
-d '{"order_id": "test"}'
```
Check for duplicate orders:
```sql
SELECT * FROM klarna_core_order
WHERE klarna_order_id = 'KLARNA_ORDER_ID';
-- If multiple rows exist, indicates duplicate push callbacks
```
### 6. Orders Stuck in Pending Payment
**Symptoms**:
- Orders created but remain in "pending_payment" status
- Invoice not automatically created
**Solutions**:
Check capture configuration:
- Admin → Stores → Configuration → Sales → Payment Methods → Kustom
- Verify "Payment Action" setting
Manual capture:
```bash
# Check if order is authorized in Kustom
SELECT * FROM klarna_core_order WHERE order_id = {magento_order_id};
# If authorized, manually create invoice in Admin
# This will trigger capture via Order Management module
```
Check Order Management logs:
```sql
SELECT * FROM klarna_logs
WHERE action = 'capture'
AND klarna_id = 'KLARNA_ORDER_ID'
ORDER BY created_at DESC;
```
## Debug Checklist
When troubleshooting any issue:
### 1. **Enable debug mode**
- Admin → Sales → Payment Methods → Kustom API → Debug Mode: Yes
### 2. **Check module status**
```bash
php bin/magento module:status | grep Klarna
```
### 3. **Check logs**
```sql
-- Last 50 log entries
SELECT * FROM klarna_logs
ORDER BY created_at DESC
LIMIT 50;
-- Failed requests only
SELECT * FROM klarna_logs
WHERE status NOT IN ('200', '201')
ORDER BY created_at DESC;
```
### 4. **Check file logs**
```bash
tail -f var/log/klarna.log
tail -f var/log/system.log
tail -f var/log/exception.log
```
### 5. **Verify configuration**
```sql
SELECT path, value FROM core_config_data
WHERE path LIKE 'klarna/%' OR path LIKE 'payment/klarna_%'
ORDER BY path;
```
### 6. **Test API connectivity**
```php
// In custom script or controller
$service = $this->serviceFactory->create();
$service->connect(\Klarna\Base\Api\ServiceInterface::SERVICE_KCO, $storeId);
try {
$response = $service->makeRequest('/checkout/v3/orders', [], 'OPTIONS');
echo "API connection successful";
} catch (\Exception $e) {
echo "API connection failed: " . $e->getMessage();
}
```
### 7. **Check quote/order data**
```sql
-- Check if quote has KCO session
SELECT * FROM klarna_kco_quote WHERE quote_id = {quote_id};
-- Check if order is linked
SELECT * FROM klarna_core_order WHERE order_id = {order_id};
```
## Performance Issues
### Slow Checkout
**Symptoms**:
- Callbacks take > 2 seconds
- Checkout feels sluggish
**Solutions**:
Enable caching:
- Ensure Redis/Varnish is configured
- Check cache hit rate
Optimize orderlines collection:
- Profile `Klarna\Orderlines\Model\Container\Parameter::collect()`
- Remove unnecessary custom orderline handlers
Check database indexes:
```sql
SHOW INDEX FROM klarna_logs;
SHOW INDEX FROM klarna_core_order;
-- Ensure indexes exist on frequently queried columns
```
### Database Table Growth
**Symptoms**:
- `klarna_logs` table grows large (hundreds of MB or GB)
- Slow queries on logs
- Disk space warnings
**Common Causes**:
- Debug mode left enabled in production
- High transaction volume with long retention period
- Cron job not running to clean old logs
**Solutions**:
**1. Disable debug mode if enabled:**
```bash
# Check current debug mode status
php bin/magento config:show klarna/api/debug
# Disable debug mode
php bin/magento config:set klarna/api/debug 0
php bin/magento cache:flush
```
> **⚠️ IMPORTANT**: Debug mode should ONLY be enabled temporarily when troubleshooting specific issues. It generates extensive logs that can quickly fill disk space and impact performance. Always disable it after completing your investigation.
**2. Verify cron is running:**
```bash
php bin/magento cron:run
# The klarna_core_clean_logs job runs daily at midnight
```
**3. Manually clean old logs:**
```sql
-- Clean logs older than 30 days
DELETE FROM klarna_logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- For immediate space recovery, clean logs older than 7 days
DELETE FROM klarna_logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY);
```
**4. Check table size:**
```sql
SELECT
table_name AS 'Table',
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)'
FROM information_schema.TABLES
WHERE table_schema = DATABASE()
AND table_name = 'klarna_logs';
```
# Support
When you need help with the Kustom integration, providing detailed information upfront speeds up resolution time significantly. Effective support requests include environment details (Magento version, PHP version, module versions), reproduction steps, relevant log entries from the `klarna_logs` table, and screenshots of errors. Never share sensitive information like full API credentials or customer payment details. This section guides you through creating comprehensive support requests and gathering the right diagnostic information.
## Technical Support
Technical support is available via the Kustom support email address.
## Creating Effective Support Requests
A well-structured support request accelerates resolution time. Include the following information in your support ticket to help the team diagnose and fix issues quickly.
### Required Information
**1. Issue Summary**
- Brief, clear description of the problem (1-2 sentences)
- When the issue started occurring (date and time)
- Frequency (always, intermittent, specific conditions)
Example:
> "KCO checkout iframe fails to load for all customers since Dec 10 at 3pm EST. Error appears consistently on all product checkouts."
**2. Environment Details**
Gather technical environment information:
```bash
# Magento version
php bin/magento --version
# PHP version
php -v
# Module versions
composer show kustom/* --format=json | jq '.installed[] | {name, version}'
# Or simpler output
composer show kustom/*
# Server information (if relevant)
uname -a
```
Include:
- Magento version (e.g., Adobe Commerce 2.4.6-p3)
- PHP version (e.g., PHP 8.2.12)
- All Kustom module versions
- Operating system (e.g., Ubuntu 22.04, CentOS 8)
- Web server (Nginx/Apache version)
- Database (MySQL/MariaDB version)
**3. Store Details**
- Store URL (e.g., https://example.com)
- Merchant ID (first 4 characters only, e.g., "AB12...")
- Affected store view(s) (if multi-store)
- Region/Market (EU, US, UK, etc.)
- Test or Production environment
**4. Detailed Reproduction Steps**
Provide step-by-step instructions:
1. Exact actions taken (e.g., "Add product SKU ABC123 to cart")
2. Expected result (e.g., "Checkout page should load with KCO iframe")
3. Actual result (e.g., "Blank page, console error: 'kco-v1.js failed to load'")
4. Frequency (happens every time, 50% of attempts, etc.)
**5. Timestamps**
- When did the issue occur? (Include multiple occurrences if intermittent)
- Timezone (critical for log correlation)
- Duration (if applicable)
Example:
> - First occurrence: 2025-12-10 15:23:45 EST
- Last occurrence: 2025-12-10 16:45:12 EST
- Approximately 15 failed checkout attempts during this window
**6. Order/Transaction IDs**
Provide specific identifiers for investigation:
- Magento order number (e.g., "000000123")
- Klarna order ID (if available, find in `klarna_core_order` table)
- Quote ID (for checkout issues)
- Session ID (for KCO issues, find in `klarna_kco_quote` table)
Query to find IDs:
```sql
-- Get all relevant IDs for an order
SELECT
so.increment_id AS order_number,
so.entity_id AS order_id,
kco.klarna_order_id,
kco.reservation_id,
so.quote_id
FROM sales_order so
LEFT JOIN klarna_core_order kco ON so.entity_id = kco.order_id
WHERE so.increment_id = 'ORDER_NUMBER';
```
**7. Logs & Screenshots**
Include relevant diagnostic data:
- **Database logs**: Export from `klarna_logs` table (see below)
- **File logs**: Excerpts from `var/log/klarna.log`
- **Browser console**: Full console output for frontend issues (F12 → Console)
- **Network tab**: Failed requests (F12 → Network)
- **Screenshots**: Visual evidence of the issue
- **Stack traces**: Full exception traces if available
### How to Export Logs for Support
**Method 1: Direct SQL Query (Recommended)**
```sql
-- Export logs for specific order (readable format)
SELECT
log_id,
DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') AS timestamp,
action,
method,
url,
status,
increment_id,
klarna_id,
SUBSTRING(request, 1, 500) AS request_preview,
SUBSTRING(response, 1, 500) AS response_preview
FROM klarna_logs
WHERE increment_id = 'ORDER_NUMBER'
ORDER BY created_at ASC;
-- Export to CSV file (if you have file write permissions)
SELECT * FROM klarna_logs
WHERE increment_id = 'ORDER_NUMBER'
ORDER BY created_at ASC
INTO OUTFILE '/tmp/klarna_logs_ORDER_NUMBER.csv'
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';
```
**Method 2: Using mysqldump**
```bash
# Export specific order logs
mysqldump -u USER -p DATABASE klarna_logs \
--where="increment_id='ORDER_NUMBER'" \
--no-create-info \
> klarna_logs_ORDER_NUMBER.sql
# Export logs from last 24 hours
mysqldump -u USER -p DATABASE klarna_logs \
--where="created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)" \
--no-create-info \
> klarna_logs_last_24h.sql
```
**Method 3: Export from Magento Admin**
If you have database access restrictions, use the Klarna Logger module's export feature (if available), or run this PHP script:
```php
getObjectManager();
$resource = $objectManager->get(\Magento\Framework\App\ResourceConnection::class);
$connection = $resource->getConnection();
$incrementId = 'ORDER_NUMBER'; // Change this
$select = $connection->select()
->from('klarna_logs')
->where('increment_id = ?', $incrementId)
->order('created_at ASC');
$logs = $connection->fetchAll($select);
file_put_contents(
"klarna_logs_{$incrementId}.json",
json_encode($logs, JSON_PRETTY_PRINT)
);
echo "Exported " . count($logs) . " log entries to klarna_logs_{$incrementId}.json\n";
```
**Method 4: Export Recent Errors Only**
```sql
-- Get last 50 failed API calls with details
SELECT
log_id,
created_at,
action,
status,
increment_id,
klarna_id,
method,
url,
request,
response
FROM klarna_logs
WHERE status NOT IN ('200', '201')
ORDER BY created_at DESC
LIMIT 50;
```
### Security & Privacy Guidelines
When sharing logs with support, protect sensitive information by following these guidelines:
**Never Share:**
- Full API credentials (username/password/shared secret)
- Customer personal data (unless absolutely necessary and with explicit consent)
- Full credit card numbers (Kustom doesn't store these, but check request/response fields)
- Database credentials or connection strings
- Production API keys in clear text
**Always Mask Sensitive Data:**
```sql
-- Create a sanitized copy of logs before exporting
CREATE TEMPORARY TABLE klarna_logs_sanitized AS
SELECT
log_id,
created_at,
action,
method,
url,
status,
increment_id,
klarna_id,
-- Mask email addresses
REGEXP_REPLACE(request, '"email":"[^"]*"', '"email":"***@***.com"') AS request,
-- Mask phone numbers
REGEXP_REPLACE(
REGEXP_REPLACE(request, '"phone":"[^"]*"', '"phone":"***"'),
'"telephone":"[^"]*"', '"telephone":"***"'
) AS response
FROM klarna_logs
WHERE increment_id = 'ORDER_NUMBER';
-- Export sanitized data
SELECT * FROM klarna_logs_sanitized;
```
**Best Practices:**
- Share only the minimum necessary log entries (specific order or time range)
- Use secure file transfer methods (encrypted email, secure file sharing)
- Delete exported log files after support issue is resolved
- Inform customers if sharing their order data with support
- Redact customer names if not essential for troubleshooting
### Preparing Diagnostic Information Package
Create a comprehensive support package with all relevant information:
```bash
#!/bin/bash
# save as generate_support_package.sh
ORDER_NUMBER="000000123"
PACKAGE_DIR="kustom_support_${ORDER_NUMBER}_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$PACKAGE_DIR"
# 1. System information
echo "=== Magento Version ===" > "$PACKAGE_DIR/system_info.txt"
php bin/magento --version >> "$PACKAGE_DIR/system_info.txt"
echo -e "\n=== PHP Version ===" >> "$PACKAGE_DIR/system_info.txt"
php -v >> "$PACKAGE_DIR/system_info.txt"
echo -e "\n=== Kustom Modules ===" >> "$PACKAGE_DIR/system_info.txt"
composer show kustom/* >> "$PACKAGE_DIR/system_info.txt"
# 2. Module status
php bin/magento module:status | grep Klarna > "$PACKAGE_DIR/module_status.txt"
# 3. Recent Klarna log file entries
tail -n 500 var/log/klarna.log > "$PACKAGE_DIR/klarna_file_log.txt" 2>/dev/null
# 4. System log errors (Klarna-related only)
grep -i klarna var/log/system.log | tail -n 100 > "$PACKAGE_DIR/system_log_klarna.txt" 2>/dev/null
# 5. Exception log errors (Klarna-related only)
grep -i klarna var/log/exception.log | tail -n 100 > "$PACKAGE_DIR/exception_log_klarna.txt" 2>/dev/null
# 6. Configuration (non-sensitive)
echo "Klarna configuration paths (values hidden for security):" > "$PACKAGE_DIR/config_paths.txt"
mysql -u USER -p -e "SELECT path FROM core_config_data WHERE path LIKE 'klarna/%' OR path LIKE 'payment/klarna_%' ORDER BY path;" DATABASE >> "$PACKAGE_DIR/config_paths.txt"
# 7. Create README
cat > "$PACKAGE_DIR/README.txt" << EOF
Kustom Support Package
Generated: $(date)
Order Number: $ORDER_NUMBER
Contents:
- system_info.txt: Magento, PHP, and module versions
- module_status.txt: Klarna module status
- klarna_file_log.txt: Recent Klarna log file entries
- system_log_klarna.txt: Klarna-related system log entries
- exception_log_klarna.txt: Klarna-related exception log entries
- config_paths.txt: Klarna configuration paths (values hidden)
- database_logs.sql: Export from klarna_logs table (run separately)
Next steps:
1. Export database logs using the SQL queries in the documentation
2. Add screenshots if applicable
3. Add browser console output if frontend issue
4. Compress this folder and send to support
IMPORTANT: Review all files and remove any sensitive information
before sharing with support.
EOF
echo "Support package created in: $PACKAGE_DIR"
echo "Review files and export database logs separately"
# Create archive
tar -czf "${PACKAGE_DIR}.tar.gz" "$PACKAGE_DIR"
echo "Archive created: ${PACKAGE_DIR}.tar.gz"
```
### What Support Needs Most
To prioritize your diagnostic information, here's what support finds most valuable:
**Critical (Always Include):**
1. Exact error message or symptom description
2. Magento and PHP versions
3. Kustom module versions
4. Logs from `klarna_logs` table for the affected order/timeframe
5. Reproduction steps
**Very Helpful:**
6. Browser console errors (for frontend issues)
7. Network tab showing failed requests
8. Screenshots showing the issue
9. Recent configuration changes
**Nice to Have:**
10. Full environment details
11. Server logs
12. Notes on what you've already tried
### Common Support Scenarios
**Scenario 1: Checkout Not Loading**
```bash
# Provide:
- Browser console errors (screenshot)
- Network tab showing blocked requests
- Last 20 entries from klarna_logs for that session
- CSP headers from browser DevTools
```
**Scenario 2: Order Not Creating After Payment**
```bash
# Provide:
- Klarna order ID from merchant portal
- Quote ID from session
- All klarna_logs entries for that quote_id
- Push callback logs from web server access logs
```
**Scenario 3: Orderlines Mismatch**
```bash
# Provide:
- Full request/response from klarna_logs showing the error
- Cart contents and totals
- Tax configuration
- Any custom totals or fees applied
```
**Scenario 4: Performance Issues**
```bash
# Provide:
- Callback response times from klarna_logs
- Database table sizes (especially klarna_logs)
- Debug mode status
- Number of orders per day
```
# Quick Reference
Quick lookup reference for common development tasks, frequently used commands, file locations, and database queries. Bookmark this section for fast access during development and troubleshooting.
## Module Directory Structure
```
vendor/kustom/
├── module-base/ # Core API client, service factory, configuration helpers
├── module-admin-settings/ # Admin panel configuration UI
├── module-logger/ # Database and file logging infrastructure
├── module-orderlines/ # Cart → Klarna orderlines conversion engine
├── module-kp/ # Klarna Payments (native checkout integration)
├── module-payments-graph-ql/ # GraphQL API for headless KP implementation
├── module-kco/ # Kustom Checkout (iframe-based checkout)
├── module-kss/ # Kustom Shipping Service integration
├── module-backend/ # Order Management (capture, refund, cancel)
├── module-kec/ # Express Checkout buttons
├── module-siwk/ # Sign-In-With-Klarna authentication
├── module-osm/ # On-Site Messaging promotional widgets
├── module-support/ # Support tools and diagnostics
├── module-interoperability/ # Compatibility with external payment methods
├── module-klarna-api/ # Low-level API client (internal)
└── module-plugins-api/ # Installation and version tracking
```
## Critical File Paths
### Configuration Files
```
vendor/kustom/module-*/etc/adminhtml/system.xml # Admin UI configuration
vendor/kustom/module-*/etc/config.xml # Default configuration values
vendor/kustom/module-*/etc/di.xml # Dependency injection setup
```
### API Clients
```
vendor/kustom/module-base/Model/Api/Service.php # Base API service factory
vendor/kustom/module-kco/Model/Api/Rest/Service.php # KCO API client
vendor/kustom/module-kp/Model/Api/Rest/Service.php # KP API client
vendor/kustom/module-backend/Model/Api/Rest/Service.php # Order Management API
```
### Controllers (Callback Endpoints)
```
vendor/kustom/module-kco/Controller/Api/Push.php # Order creation callback
vendor/kustom/module-kco/Controller/Api/AddressUpdate.php # Address validation
vendor/kustom/module-kco/Controller/Api/ShippingOptionUpdate.php # Shipping updates
vendor/kustom/module-kco/Controller/Api/OrderValidation.php # Pre-order validation
vendor/kustom/module-kco/Controller/Klarna/Success.php # Confirmation page
```
### Orderlines Engine
```
vendor/kustom/module-orderlines/Model/Container/Parameter.php # Main container
vendor/kustom/module-orderlines/Model/Container/Products.php # Product line items
vendor/kustom/module-orderlines/Model/Container/Shipping.php # Shipping costs
vendor/kustom/module-orderlines/Model/Container/Discount.php # Discounts
vendor/kustom/module-orderlines/Model/Container/Tax.php # Tax handling
```
### Database Setup
```
vendor/kustom/module-*/etc/db_schema.xml # Table definitions
vendor/kustom/module-*/Setup/Patch/Data/*.php # Data patches
```
### Key Models
```
vendor/kustom/module-kco/Model/Checkout/Order.php # KCO order placement
vendor/kustom/module-backend/Gateway/Command/Capture.php # Invoice capture
vendor/kustom/module-base/Model/OrderRepository.php # Order data access
```
## Essential Commands
### Installation & Upgrades
```bash
# Initial installation
composer require kustom/module-checkout
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento cache:flush
# Update to latest version
composer update kustom/*
php bin/magento setup:upgrade
php bin/magento cache:flush
# Production mode deployment
php bin/magento setup:static-content:deploy -f
php bin/magento setup:di:compile
```
### Module Management
```bash
# Check module status
php bin/magento module:status | grep Klarna
# Enable/disable modules
php bin/magento module:enable Klarna_Kco Klarna_Base
php bin/magento module:disable Klarna_Kp
# List all Kustom modules with versions
composer show kustom/* | grep -E '^name|^versions'
```
### Cache Management
```bash
# Flush all caches
php bin/magento cache:flush
# Clean specific cache types
php bin/magento cache:clean config
php bin/magento cache:clean layout
php bin/magento cache:clean full_page
# Disable cache for development
php bin/magento cache:disable
# Enable cache for production
php bin/magento cache:enable
```
### Debugging
```bash
# Monitor Klarna logs in real-time
tail -f var/log/klarna.log
# Monitor system logs
tail -f var/log/system.log | grep -i klarna
# Monitor exception logs
tail -f var/log/exception.log
# Check last 100 Klarna log entries
tail -n 100 var/log/klarna.log
# Search for specific error
grep "orderlines" var/log/klarna.log
```
### Configuration Management
```bash
# Export configuration to file
php bin/magento app:config:dump
# Show specific config value
php bin/magento config:show klarna/api/debug
php bin/magento config:show payment/klarna_kco/active
# Set config value
php bin/magento config:set klarna/api/debug 0
php bin/magento config:set payment/klarna_kco/active 1
# Clear config cache after changes
php bin/magento cache:clean config
```
### Cron Management
```bash
# Run cron manually (includes log cleanup)
php bin/magento cron:run
# List cron jobs
php bin/magento cron:list
# Install crontab entries
php bin/magento cron:install
```
### Troubleshooting Commands
```bash
# Reindex if orders not appearing
php bin/magento indexer:reindex
# Check for compilation errors
php bin/magento setup:di:compile
# Verify database schema
php bin/magento setup:db:status
# Check for module conflicts
php bin/magento info:dependencies:show-modules-circular
```
## Admin Panel Configuration Paths
Quick navigation to common configuration areas:
```
Stores → Configuration → Sales → Payment Methods → Kustom API
- API credentials (username, password)
- Test/Live mode
- Debug logging toggle
- Region selection
Stores → Configuration → Sales → Payment Methods → Kustom Checkout
- Enable/disable KCO
- Available payment methods
- Terms & conditions URL
- Shipping options
Stores → Configuration → Sales → Checkout → Kustom Checkout → Design
- Color scheme customization
- Button styles
- Widget placement
Stores → Configuration → Sales → Payment Methods → Klarna Payments
- KP-specific settings (legacy)
- Payment method categories
System → Cache Management
- Clear Kustom-related caches
System → Tools → Index Management
- Reindex after database changes
```
## Database Query Reference
### Order Lookups
```sql
-- Find order by Klarna order ID
SELECT
so.increment_id AS order_number,
so.entity_id AS order_id,
so.status,
so.state,
kco.klarna_order_id,
kco.reservation_id,
kco.is_acknowledged
FROM sales_order so
JOIN klarna_core_order kco ON so.entity_id = kco.order_id
WHERE kco.klarna_order_id = 'KLARNA_ORDER_ID';
-- Find order by increment ID with all Klarna data
SELECT
so.increment_id,
so.customer_email,
so.grand_total,
so.status,
kco.*
FROM sales_order so
LEFT JOIN klarna_core_order kco ON so.entity_id = kco.order_id
WHERE so.increment_id = 'ORDER_NUMBER';
-- Find orders in specific state
SELECT
so.increment_id,
so.created_at,
so.status,
kco.klarna_order_id
FROM sales_order so
JOIN klarna_core_order kco ON so.entity_id = kco.order_id
WHERE so.status = 'pending_payment'
ORDER BY so.created_at DESC
LIMIT 20;
```
### Log Queries
```sql
-- Get all logs for specific order
SELECT
log_id,
created_at,
action,
method,
status,
url,
SUBSTRING(request, 1, 200) AS request_preview,
SUBSTRING(response, 1, 200) AS response_preview
FROM klarna_logs
WHERE increment_id = 'ORDER_NUMBER'
ORDER BY created_at ASC;
-- Find failed API calls today
SELECT
created_at,
action,
status,
increment_id,
klarna_id,
response
FROM klarna_logs
WHERE DATE(created_at) = CURDATE()
AND status NOT IN ('200', '201')
ORDER BY created_at DESC;
-- Find logs by action type (last 24 hours)
SELECT * FROM klarna_logs
WHERE action = 'capture'
AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY created_at DESC;
-- Count API calls by action (performance analysis)
SELECT
action,
COUNT(*) AS call_count,
AVG(CASE WHEN status IN ('200', '201') THEN 1 ELSE 0 END) * 100 AS success_rate
FROM klarna_logs
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY action
ORDER BY call_count DESC;
```
### Quote/Session Queries
```sql
-- Find active KCO sessions
SELECT
q.entity_id AS quote_id,
q.customer_email,
q.created_at,
q.updated_at,
kq.klarna_checkout_id,
kq.is_active
FROM quote q
JOIN klarna_kco_quote kq ON q.entity_id = kq.quote_id
WHERE kq.is_active = 1
ORDER BY q.updated_at DESC;
-- Find abandoned KCO checkouts (sessions created but no order)
SELECT
q.entity_id AS quote_id,
q.customer_email,
q.grand_total,
q.created_at,
kq.klarna_checkout_id
FROM quote q
JOIN klarna_kco_quote kq ON q.entity_id = kq.quote_id
LEFT JOIN sales_order so ON q.entity_id = so.quote_id
WHERE kq.is_active = 1
AND so.entity_id IS NULL
AND q.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY q.created_at DESC;
```
### Configuration Queries
```sql
-- Show all Klarna configuration (paths only for security)
SELECT path, scope, scope_id
FROM core_config_data
WHERE path LIKE 'klarna/%' OR path LIKE 'payment/klarna_%'
ORDER BY path;
-- Check if debug mode is enabled
SELECT path, value, scope
FROM core_config_data
WHERE path = 'klarna/api/debug';
-- Find stores with KCO enabled
SELECT
ccd.scope_id,
ccd.value,
s.name AS store_name
FROM core_config_data ccd
JOIN store s ON ccd.scope_id = s.store_id
WHERE ccd.path = 'payment/klarna_kco/active'
AND ccd.scope = 'stores';
```
### Maintenance Queries
```sql
-- Check klarna_logs table size
SELECT
table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb,
table_rows
FROM information_schema.TABLES
WHERE table_schema = DATABASE()
AND table_name LIKE 'klarna%'
ORDER BY (data_length + index_length) DESC;
-- Clean old logs (older than 30 days)
DELETE FROM klarna_logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- Find oldest and newest log entries
SELECT
MIN(created_at) AS oldest_log,
MAX(created_at) AS newest_log,
COUNT(*) AS total_logs,
COUNT(DISTINCT increment_id) AS unique_orders
FROM klarna_logs;
```
## Callback URLs Reference
Production URLs that Klarna will call:
```
https://yourdomain.com/kco/api/push # Order creation (POST)
https://yourdomain.com/kco/api/addressupdate # Address validation (POST)
https://yourdomain.com/kco/api/shippingoptionupdate # Shipping options (POST)
https://yourdomain.com/kco/api/ordervalidation # Pre-order validation (POST)
https://yourdomain.com/kco/klarna/success # Customer redirect (GET)
```
**Important**: All callback URLs must be publicly accessible (no firewall, no maintenance mode, no IP restrictions).
## Common Error Codes
Quick reference for troubleshooting:
| HTTP Status | Meaning | Common Cause |
| --- | --- | --- |
| 200 | Success | Request completed successfully |
| 400 | Bad Request | Orderlines mismatch, validation error |
| 401 | Unauthorized | Invalid API credentials |
| 403 | Forbidden | API credentials lack permissions |
| 404 | Not Found | Order/resource doesn't exist |
| 409 | Conflict | Order already captured/refunded |
| 500 | Server Error | Magento exception, check logs |
| 503 | Service Unavailable | Klarna API down (rare) |
## Quick Diagnostic Checklist
When something isn't working, check these in order:
1. ✓ Modules enabled: `php bin/magento module:status | grep Klarna`
2. ✓ Configuration saved: Check Admin → Kustom API settings
3. ✓ Cache cleared: `php bin/magento cache:flush`
4. ✓ API credentials correct: Test vs Live mode matches credentials
5. ✓ Logs show error: `tail -f var/log/klarna.log`
6. ✓ Database logs: `SELECT * FROM klarna_logs ORDER BY created_at DESC LIMIT 10`
7. ✓ Callbacks accessible: Test URLs from external source
8. ✓ Browser console: Check for JavaScript errors (F12)
**Document Version**: 1.0
**Last Updated**: December 2025
**Maintained by**: Kustom Development Team