<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Spryker Documentation</title>
        <description>Spryker documentation center.</description>
        <link>https://docs.spryker.com/</link>
        <atom:link href="https://docs.spryker.com/feed.xml" rel="self" type="application/rss+xml"/>
        <lastBuildDate>Mon, 27 Apr 2026 14:49:40 +0000</lastBuildDate>
        <generator>Jekyll v4.2.2</generator>
        
        
        <item>
            <title>Release notes for spryker-rabbitmq 20260427</title>
            <description>This document describes the changes that have been recently released.
For additional support with this content, contact our support.
If you found a new security vulnerability, contact us at **security@spryker.com**.

---

## Release notes for spryker-rabbitmq 20260427

**Improvements**:

Adjusted migration to check if rabbit settings already exist. 

### Security Fixes


### Official RabbitMQ release notes page

https://www.rabbitmq.com/release-information

!!! PLEASE INCLUDE RELEASE NOTES OF THE SPECIFIC VERSION FROM THE OFFICIAL SOURCE !!!

### Version release notes


</description>
            <pubDate>Mon, 27 Apr 2026 14:49:01 +0000</pubDate>
            <link>https://docs.spryker.com/docs/about/all/releases/image-releases/rabbitmq/release-notes-spryker-rabbitmq-20260427.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/about/all/releases/image-releases/rabbitmq/release-notes-spryker-rabbitmq-20260427.html</guid>
            
            
        </item>
        
        <item>
            <title>PunchOut Gateway</title>
            <description>PunchOut Gateway module provides basic implementation 

## Support Use Cases

Current implementation supports any number of simultaneously active OCI and cXML connections inn one Spryker shop.

Support for the shop integration to iFrame can only be enabled globally for the whole shop, following [this guideline](/docs/pbc/all/punchout-gateway/integrate-punchout-gateway#support-iframe-embedding).  

For OCI flow, the cart is created for every new request. The first one is named `Punchout`, the second `Punchout 1` and so forth.
Cart cleanup functionality will be implemented in the following releases.

For cXML flow, a single cart is created or reused for each `BuyerCookie` value. 



### Additional links

Check [integration guide](/docs/pbc/all/punchout-gateway/integrate-punchout-gateway) to integrate PunchOut Gateway module into your Spryker shop.

Check [project configuration](/docs/pbc/all/punchout-gateway/project-configuration-for-punchout-gateway) documentation for details about fine-tuning the integration on the project level.

</description>
            <pubDate>Mon, 27 Apr 2026 10:31:31 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/punchout-gateway/punchout-gateway.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/punchout-gateway/punchout-gateway.html</guid>
            
            
        </item>
        
        <item>
            <title>Project configuration for PunchOut Gateway</title>
            <description>This document describes the project configuration to enable eProcurement systems support via PunchOut flow.

## PunchOut connection configuration

Since the UI for connection setup is not yet ready, we provide these two console commands to create a demo configuration:

- OCI flow:

```bash
  vendor/bin/console punchout-gateway:oci:demo-connection:create
```

- cXML flow:

```bash
  vendor/bin/console punchout-gateway:cxml:demo-connection:create
```

To configure a connection, create a row in the `spy_punchout_connection` table:

| Column | Value                                    | Comments                                                                                                                                                 |
|--------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| `fk_store` | Store ID (for example, DE)               | ID of the store the customer must be logged in to.                                                                                                       |
| `name` | Human-readable label                     | Used only for readability                                                                                                                                |
| `is_active` | `true`                                   | Determines whether the connection can be used.                                                                                                           |
| `allow_iframe` | `true` / `false`                         | Enforces iframe-specific headers when the PunchOut session is active. If **~TARGET** is sent during the request, the headers are sent regardless of this value. |
| `protocol_type` | `&apos;oci&apos;` or `&apos;xml&apos;`                       | Flow type.                                                                                                                                               |
| `processor_plugin_class` | Full class name of the processor plugin. | Processor to be used.                                                                                                                                    |

### OCI connection configuration

OCI-specific configuration

| Column | Value                                    | Comments                                                                                                                                                                                                          |
|--------|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `request_url` | `&apos;/punchout-gateway/oci/my-company&apos;`     | Endpoint path the buyer posts the OCI login form to. This URL without a domain is the unique identifier of each connection, and can be anything that starts with ``https://&lt;shop-domain&gt;/punchout-gateway/oci/``. |
| `configuration` | JSON configuration                       | See the *OCI Login configuration* section below.                                                                                                                                                                   |
| `protocol_type` | `&apos;oci&apos;`                       | Flow type.                                                                                                                                               |
| `processor_plugin_class` | Full class name of the processor plugin. | `\SprykerEco\Zed\PunchoutGateway\Communication\Plugin\PunchoutGateway\DefaultOciProcessorPlugin` or a project&apos;s implementation.                                                                                   |

The triplet `protocol_type`, `fk_store` and `request_url` must be unique.

Column `configuration` contains JSON with the following optional keys. Override only when the value differs from the default.

| Key | Default | Purpose                                                     |
|-----|---------|-------------------------------------------------------------|
| `usernameField` | `USERNAME` | Form field name carrying the username during login request. |
| `passwordField` | `PASSWORD` | Form field name carrying the password during login request. |

Additionally, configure credentials for customers who will access the shop.
To do this, create rows in the `spy_punchout_credential` table:

| Column                 | Value           | Comment                                                                                                                                                       |
|------------------------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `fk_punchout_connection` | integer         | ID of the connection                                                                                                                                          |
| `fk_customer`            | int             | ID of the customer                                                                                                                                            |
| `username`               | string          | Username, that&apos;s sent in the `username` field                                                                                                                 |
| `password_hash`          | hashed password | Hashed password, that&apos;s sent in the `password` field. Validation happens using [password_verify](https://www.php.net/manual/en/function.password-verify.php). |
| `is_active`              | `true`/`false`  | Active flag of this customer                                                                                                                                  |

Customers used for an OCI connection are expected to be fully configured in the shop so that only permitted products and prices are accessible.

### cXML connection configuration

cXML-specific configuration columns:

| Column | Value                                    | Comments                                                                                                                                                                                                          |
|--------|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `request_url` | `&apos;/punchout-gateway/cxml/my-company&apos;`    | Endpoint path the cXML request is posted to. This URL without a domain is the unique identifier of each connection, and can be anything that starts with ``https://&lt;shop-domain&gt;/punchout-gateway/cxml/``.        |
| `configuration` | JSON configuration                       | See the *cXML configuration* section below.                                                                                                                                                                        |
| `protocol_type` | `&apos;xml&apos;`                       | Flow type.                                                                                                                                                                                                        |
| `processor_plugin_class` | Full class name of the processor plugin. | `\SprykerEco\Zed\PunchoutGateway\Communication\Plugin\PunchoutGateway\DefaultCxmlProcessorPlugin` or a project&apos;s implementation.                                                                                  |

The `sender_identity` value must be globally unique — each cXML system maps to exactly one connection.

Column `configuration` contains JSON with the following keys:

| Key | Required | Purpose                                          |
|-----|----------|--------------------------------------------------|
| `senderSharedSecret` | yes | Shared secret used to authenticate the request. Validation happens using [password_verify](https://www.php.net/manual/en/function.password-verify.php). |

For a cXML connection, no additional PunchOut-related configuration is required.
The logged-in customer is identified by the `UserEmail` extrinsic field.
Customers used for a cXML connection are expected to be fully configured in the shop so that only permitted products and prices are accessible.


## PunchOut flow processor plugin

Each PunchOut connection resolves its processor plugin at runtime using the fully qualified class name stored in `spy_punchout_connection.processor_plugin_class`.
The plugin must implement one of the following interfaces:
- for OCI flow - `\SprykerEco\Zed\PunchoutGateway\Dependency\Plugin\PunchoutProcessorPluginInterface`
- for cXML flow - `\SprykerEco\Zed\PunchoutGateway\Dependency\Plugin\PunchoutCxmlProcessorPluginInterface`.

This module provides default functionality:
- \SprykerEco\Zed\PunchoutGateway\Communication\Plugin\PunchoutGateway\DefaultCxmlProcessorPlugin - for cXML flow,
- \SprykerEco\Zed\PunchoutGateway\Communication\Plugin\PunchoutGateway\DefaultOciProcessorPlugin - for OCI flow.

No dependency injection registration is required. The plugin is loaded at runtime.

### Creation of a custom plugin

Place the plugin in your project&apos;s Zed communication layer, for example:

**src/Pyz/Zed/ProjectPunchoutGateway/Communication/Plugin/PunchoutGateway/CustomOciProcessorPlugin.php**

The simplest approach is to extend the default OCI plugin and override only the methods you need:

```php
namespace Pyz\Zed\ProjectPunchoutGateway\Communication\Plugin\PunchoutGateway;

use SprykerEco\Zed\PunchoutGateway\Communication\Plugin\PunchoutGateway\DefaultOciProcessorPlugin;

class CustomOciProcessorPlugin extends DefaultOciProcessorPlugin
{
    // Override only the methods you need to customise.
}
```

Set `spy_punchout_connection.processor_plugin_class` on the connection that uses this plugin to `\Pyz\Zed\ProjectPunchoutGateway\Communication\Plugin\PunchoutGateway\CustomOciProcessorPlugin`.

### Global plugin methods

| Method | Called when                                                  | Functionality                                                                                                     |
|--------|--------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
| `authenticate` | First step of the login flow                                 | Finds a connection based on the setup request. Returns `null` if no valid connection is found.                    |
| `resolveCustomer` | After a valid connection was found                           | Finds the customer to use for the PunchOut session. Returns `null` if no valid customer is found.                 |
| `resolveQuote` | After a valid customer is resolved                           | Creates a new quote or reuses an existing one. An empty `QuoteTransfer` can be returned.                          |
| `expandQuote` | After the quote is resolved                                  | Allows adjusting the quote after PunchOut-specific preparations are done.                                         |
| `resolveSession` | After the quote is expanded, before the session is persisted | Additional session validation logic can be added here.                                                            |

#### cXML-specific plugin methods

| Method | Called when                                      | Functionality                                                                                           |
|--------|--------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| `parseCxmlRequest` | XML was parsed and a valid connection was found. | Additional mapping of the cXML data onto setup request.                                                 |
| `expandResponse` | After successful session creation                | Response to the login request can be expanded, for example, with the default start URL for the customer. |


## Form handler plugin

Projects extend or replace the storefront &quot;Transfer Cart&quot; form via plugins implementing `\SprykerEco\Yves\PunchoutGateway\Plugin\Form\PunchoutFormHandlerPluginInterface`.
At render time, `PunchoutFormDataBuilder::build()` iterates the registered handlers in order and returns the result of the first whose `isApplicable()` returns `true`.

The module ships two defaults, both registered in `\SprykerEco\Yves\PunchoutGateway\PunchoutGatewayDependencyProvider::getPunchoutFormHandlerPlugins()`:

- `\SprykerEco\Yves\PunchoutGateway\Plugin\Form\DefaultCxmlPunchoutFormHandlerPlugin` — for cXML sessions.
- `\SprykerEco\Yves\PunchoutGateway\Plugin\Form\DefaultOciPunchoutFormHandlerPlugin` — for OCI sessions.

### Plugin methods

| Method | Called when                                                              | Functionality                                                                                                                                                                    |
|--------|--------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `isApplicable` | Before each render of the widget                                         | Returns `true` when the quote&apos;s PunchOut session matches this handler&apos;s protocol.                                                                                                |
| `handle` | After `isApplicable()` returned `true`                                   | Builds the `PunchoutFormDataTransfer` carrying the action URL and all hidden form fields, or returns `null` when the form cannot be built (for example, missing session data). |

### Register a custom handler

Place the plugin in your project&apos;s Yves layer, for example:

**src/Pyz/Yves/ProjectPunchoutGateway/Plugin/Form/CustomCxmlPunchoutFormHandlerPlugin.php**

Then register it before the defaults so it takes precedence for the same protocol:

**src/Pyz/Yves/PunchoutGateway/PunchoutGatewayDependencyProvider.php**

```php
namespace Pyz\Yves\PunchoutGateway;

use Pyz\Yves\ProjectPunchoutGateway\Plugin\Form\CustomCxmlPunchoutFormHandlerPlugin;
use SprykerEco\Yves\PunchoutGateway\PunchoutGatewayDependencyProvider as SprykerEcoPunchoutGatewayDependencyProvider;

class PunchoutGatewayDependencyProvider extends SprykerEcoPunchoutGatewayDependencyProvider
{
    protected function getPunchoutFormHandlerPlugins(): array
    {
        return [
            new CustomCxmlPunchoutFormHandlerPlugin(),
            ...parent::getPunchoutFormHandlerPlugins(),
        ];
    }
}
```


## Punchout-specific security header expander plugin

At PunchOut session start, Yves applies protocol-specific `Content-Security-Policy` directives to allow the Storefront to be embedded and to post back to the buyer&apos;s procurement system.

Directive generation is delegated to plugins implementing `\SprykerEco\Yves\PunchoutGateway\Dependency\Plugin\PunchoutSecurityHeaderExpanderPluginInterface`.
`PunchoutSecurityHeaderSessionWriter` walks the registered plugins and accumulates their directives for the active session once, persisting the value into the session.

By default, we provide an OCI-specific plugin only:

- `\SprykerEco\Yves\PunchoutGateway\Plugin\SecurityHeader\DefaultOciSecurityHeaderExpanderPlugin` — adds `frame-ancestors` to the CSP for OCI sessions when `spy_punchout_connection.allow_iframe` is `true` or the OCI login carries a `~TARGET` form field.

### Plugin methods

| Method | Called when                                      | Functionality                                                                                                                                        |
|--------|--------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| `isApplicable` | Before the directives are collected for a session | Returns `true` when this plugin handles the PunchOut session&apos;s protocol.                                                                              |
| `expand` | After `isApplicable()` returned `true`           | Appends protocol-specific CSP directive strings to the given list. Implementations must not add duplicate directives.                                 |

### Register a custom expander

In case your system requires additional Punchout-specific security headers, add your plugin to the Yves dependency provider. 

**src/Pyz/Yves/PunchoutGateway/PunchoutGatewayDependencyProvider.php**

```php
namespace Pyz\Yves\PunchoutGateway;

use Pyz\Yves\ProjectPunchoutGateway\Plugin\SecurityHeader\CustomCxmlSecurityHeaderExpanderPlugin;
use SprykerEco\Yves\PunchoutGateway\PunchoutGatewayDependencyProvider as SprykerEcoPunchoutGatewayDependencyProvider;

class PunchoutGatewayDependencyProvider extends SprykerEcoPunchoutGatewayDependencyProvider
{
    protected function getPunchoutSecurityHeaderExpanderPlugins(): array
    {
        return [
            ...parent::getPunchoutSecurityHeaderExpanderPlugins(),
            new CustomCxmlSecurityHeaderExpanderPlugin(),
        ];
    }
}
```


## Session-in-quote expander plugin

`PunchoutQuoteExpander` (Zed) loads the PunchOut session that belongs to the current quote and runs it through every registered `\SprykerEco\Zed\PunchoutGateway\Dependency\Plugin\PunchoutSessionInQuoteExpanderPluginInterface` before stamping it on the `QuoteTransfer`.
Use this extension point to enrich or override PunchOut session fields based on the quote (for example, to copy a project-specific field onto the session before it is persisted with the cart).

### Plugin methods

| Method | Called when                                               | Functionality                                                                                           |
|--------|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| `isApplicable` | For each plugin, before `expand()` runs                   | Returns `true` when the plugin should run for the given session/quote combination.                      |
| `expand` | After `isApplicable()` returned `true`                    | Expands the `PunchoutSessionTransfer` before it is assigned to the `QuoteTransfer`.                     |

## Default implementations

This section describes the behavior of the two shipped default processor plugins and the storefront &quot;Transfer Cart&quot; widget for each lifecycle step.
Use it to understand what you get out-of-the-box and to identify which plugin method to override when customising.

### Shared behavior

Both `DefaultOciProcessorPlugin` and `DefaultCxmlProcessorPlugin` rely on `QuoteCreator` to stamp the following fields on every new quote:

- **Store** — resolved from `spy_punchout_connection.fk_store`.
- **Currency** — set to the default currency of the resolved store.

### OCI

**Customer identification**

`OciCustomerResolver` looks up the customer by the `idCustomer` that is already on the connection transfer.
That value is stamped during authentication: `PunchoutOciAuthenticator` matches the username and password from the OCI form fields against `spy_punchout_connection_credential` and, on success, writes `connection.idCustomer`.
The buyer identity therefore comes entirely from the credential record — nothing from the buyer&apos;s login payload is used.
Override `resolveCustomer` on the processor plugin to source the customer differently (for example, from a custom form field).

**Quote identification**

`OciPunchoutQuoteFinder` always returns a fresh empty `QuoteTransfer` with `DEFAULT_QUOTE_NAME`.
There is no session-to-quote lookup on OCI login; every login starts a new cart.
Store and currency are set by `QuoteCreator` as described in the shared behavior section above.
Override `resolveQuote` to reuse a per-customer cart across sessions.

**Quote fill with items**

OCI login does not carry item data.
`DefaultOciProcessorPlugin::expandQuote` is a pass-through and leaves the quote empty.
Items are transferred back to the buyer&apos;s procurement system via the storefront form POST (see &quot;Transfer Cart button&quot; below), not populated during login.

**Transfer Cart button**

`DefaultOciPunchoutFormHandlerPlugin` (registered in `YvesPunchoutGatewayDependencyProvider`) makes the button visible when the current quote has a PunchOut session whose `punchoutData.ociLoginRequest` is not `null`.
When applicable, the action URL is taken from the `HOOK_URL` form field of the OCI login request (validated to start with `https://` at session creation time).
The hidden form fields are OCI-flat `NEW_ITEM-…` key/value pairs produced by `OciFormFieldBuilder`.
The button is not rendered when no punchout session exists on the quote, when the session was created by a cXML login, or when no handler plugin is registered for the Yves factory.

### cXML

**Customer identification**

`CxmlCustomerResolver` reads the `Extrinsic[name=&quot;UserEmail&quot;]` field from the parsed `PunchOutSetupRequest` and resolves the customer via `CustomerFacade::getCustomer` by email.
Connection authentication is a separate step that runs first: `PunchoutCxmlAuthenticator` verifies the `senderSharedSecret` from the cXML header against the connection record using `password_verify`.
If the `UserEmail` extrinsic is absent or empty, `resolveCustomer` returns `null` and the session is rejected.
Override `resolveCustomer` to read identity from a different extrinsic or from a custom header field.

**Quote identification**

`CxmlPunchoutQuoteFinder` uses `BuyerCookie` from the `PunchOutSetupRequest` to look up an existing PunchOut session via `PunchoutGatewayRepository::findPunchoutSessionByBuyerCookie`.
When a session is found, the linked quote is reused — allowing the buyer to resume an in-progress cart.
If the found quote belongs to a different store than the current connection&apos;s `idStore`, the old quote is deleted and a new empty one is created.
When `BuyerCookie` is missing or no session matches, a new `DEFAULT_QUOTE_NAME` quote is returned.
Store and currency are then set by `QuoteCreator`.
Override `resolveQuote` to change cookie-matching rules or to support cross-store quote reuse.

**Quote fill with items**

`CxmlPunchoutQuoteExpander::expand` runs during `expandQuote` and handles three operations:

- `operation=edit` — maps each `PunchoutItemTransfer` to an `ItemTransfer` (`sku` = `SupplierPartId`, `quantity` from the request, `unitGrossPrice = UnitPrice × 100` in minor currency units) and adds them to the quote via `CartFacade::addToCart`, which runs the full cart validation and expander pipeline.
  The `ShipTo` address from the setup request is also mapped to `QuoteTransfer.shippingAddress` and propagated to each item&apos;s shipment.
- `operation=create` — clears all existing items on the quote; `ShipTo` is still mapped if present.
- `operation=inspect` — items and address are left untouched.

Override `expandQuote` to change how SKUs are matched, how prices are converted, or to add custom item attributes.

**Transfer Cart button**

`DefaultCxmlPunchoutFormHandlerPlugin` makes the button visible when the current quote has a PunchOut session whose `punchoutData.cxmlSetupRequest` is not `null`.
The action URL is `punchoutSession.browserFormPostUrl`, captured from the original `PunchOutSetupRequest.BrowserFormPost.URL` at login time.
A single hidden field (`CXML_FORM_FIELD_NAME`) carries the `PunchOutOrderMessage` built by `PunchoutGatewayService::buildCxmlPunchoutOrderMessage`.
The button is not rendered when no punchout session exists, when the session was created by an OCI login, or when no handler plugin is registered.

### Widget visibility summary

`PunchoutCartWidget` is always instantiated on pages where it is embedded.
Actual form visibility is determined by `PunchoutFormDataBuilder::build()`, which calls plugins of interface `PunchoutFormHandlerPluginInterface` and returns the result of the first whose `isApplicable()` returns `true`.
The Twig template renders the `&lt;form&gt;` and submit button only when `formData` is not `null` and `formData.actionUrl` is not empty.
If you register a custom handler plugin, place it before the default plugins in the dependency provider so it takes precedence for the same protocol.

For OCI flow, the button is rendered for the empty cart as well to allow empty-order return to the eProcurement platform.

For cXML flow, no button is rendered for the empty cart. This behavior will be improved in the next version.</description>
            <pubDate>Mon, 27 Apr 2026 10:31:31 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/punchout-gateway/project-configuration-for-punchout-gateway.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/punchout-gateway/project-configuration-for-punchout-gateway.html</guid>
            
            
        </item>
        
        <item>
            <title>Integrate PunchOut Gateway</title>
            <description>This document describes how to integrate the PunchOut Gateway module into a Spryker shop.

## 1. Install the module

Install the PunchOut Gateway module using Composer:

```bash
composer require spryker-eco/punchout-gateway:^0.2.0
```

## 2. Configure the module

To control logging through the AWS Parameter Store, add the following optional configuration:

**config/Shared/config_default.php**

```php
use SprykerEco\Shared\PunchoutGateway\PunchoutGatewayConstants;

$config[PunchoutGatewayConstants::ENABLE_LOGGING] = getenv(&apos;PUNCHOUT_GATEWAY_ENABLE_LOGGING&apos;) ?: false;
```

### Configuration constants

| Constant | Description                                                                                                                                     | Default  |
|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|----------|
| `ENABLE_LOGGING` | Enables or disables logging for PunchOut Gateway. Check `\SprykerEco\Shared\PunchoutGateway\Logger\PunchoutLogger` to see what is logged. | `false` |

## 3. Additional module configuration

`src/Pyz/Zed/PunchoutGateway/PunchoutGatewayConfig.php` provides the following configuration methods:

| Method | Default | Description |
|--------|---------|-------------|
| `isLoggingEnabled()` | `true` | Enables or disables PunchOut Gateway logging. |
| `getCxmlSessionStartUrlValidityInSeconds()` | `600` | Validity period of the cXML session start URL in seconds. |
| `getOciDefaultStartUrl()` | `&apos;/&apos;` | Default redirect URL after OCI session start. |
| `getCxmlSessionTokenLength()` | `32` | Length of the generated cXML session token. |

## 4. Update Quote configuration

Update `QuoteConfig` to allow the PunchOut session field to be saved with the quote.

**src/Pyz/Zed/Quote/QuoteConfig.php**

```php
use Generated\Shared\Transfer\QuoteTransfer;

public function getQuoteFieldsAllowedForSaving(): array
{
    return array_merge(parent::getQuoteFieldsAllowedForSaving(), [
        // ...
        QuoteTransfer::PUNCHOUT_SESSION,
    ]);
}
```

## 5. Set up the database schema

Install the database schema:

```bash
vendor/bin/console propel:install
```

This creates the following tables:

| Table | Description |
|-------|-------------|
| `spy_punchout_connection` | Stores PunchOut connection configuration per store. |
| `spy_punchout_credential` | Stores credentials (username/password) linked to a connection and customer. |
| `spy_punchout_session` | Stores active PunchOut sessions linked to a quote. |

## 6. Generate transfer objects

Generate transfer objects for the module:

```bash
vendor/bin/console transfer:generate
```

## 7. Register plugins

### Register the Quote expander plugin

Add the PunchOut session expander plugin:

**src/Pyz/Zed/Quote/QuoteDependencyProvider.php**

```php
use SprykerEco\Zed\PunchoutGateway\Communication\Plugin\Quote\PunchoutSessionQuoteExpanderPlugin;

protected function getQuoteExpanderPlugins(): array
{
    return [
        // ...
        new PunchoutSessionQuoteExpanderPlugin(),
    ];
}
```

### Register the route provider plugin

Add the route provider plugin:

**src/Pyz/Yves/Router/RouterDependencyProvider.php**

```php
use SprykerEco\Yves\PunchoutGateway\Plugin\Router\PunchoutGatewayRouteProviderPlugin;

protected function getRouteProvider(): array
{
    return [
        // ...
        new PunchoutGatewayRouteProviderPlugin(),
    ];
}
```

### Register the security header expander plugin

Add the security header expander plugin:

**src/Pyz/Yves/Application/ApplicationDependencyProvider.php**

```php
use SprykerEco\Yves\PunchoutGateway\Plugin\Application\PunchoutSecurityHeaderExpanderPlugin;

protected function getSecurityHeaderExpanderPlugins(): array
{
    return [
        // ...
        new PunchoutSecurityHeaderExpanderPlugin(),
    ];
}
```

### Support iframe embedding

If your eProcurement system requires embedding into an iframe, you must also adjust this variable in the deploy file for your environments:

```yml
image:
  environment:
    SPRYKER_YVES_SESSION_COOKIE_SAMESITE: &apos;none&apos;
```


## 8. Register the cart widget

Add the PunchOut cart widget:

**src/Pyz/Yves/ShopApplication/ShopApplicationDependencyProvider.php**

```php
use SprykerEco\Yves\PunchoutGateway\Widget\PunchoutCartWidget;

protected function getGlobalWidgets(): array
{
    return [
        // ...
        PunchoutCartWidget::class,
    ];
}
```

If you have custom Yves templates or your own frontend, add `PunchoutCartWidget` to your cart template. The core template is located at `SprykerShop/Yves/CartPage/Theme/default/templates/page-layout-cart/page-layout-cart.twig`.

The following example shows `PunchoutCartWidget` usage:

```twig
{% raw %}
{% widget &apos;PunchoutCartWidget&apos; args [data.cart] only %}{% endwidget %}
{% endraw %}
```

## 9. Import glossary data

The module provides glossary translations used by the PunchOut flow.

**Option 1: Import using the module&apos;s configuration file**

```bash
vendor/bin/console data:import --config=vendor/spryker-eco/punchout-gateway/data/import/punchout-gateway.yml
```

**Option 2: Copy file content and import individually**

Copy content from `vendor/spryker-eco/punchout-gateway/data/import/*.csv` to the corresponding files in `data/import/common/common/`. Then run:

```bash
vendor/bin/console data:import glossary
```

## Additional links

For details about fine-tuning the integration on the project level, see [Project configuration for PunchOut Gateway](/docs/pbc/all/punchout-gateway/project-configuration-for-punchout-gateway.html).</description>
            <pubDate>Mon, 27 Apr 2026 10:31:31 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/punchout-gateway/integrate-punchout-gateway.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/punchout-gateway/integrate-punchout-gateway.html</guid>
            
            
        </item>
        
        <item>
            <title>Cart performance configuration</title>
            <description>&lt;h2 id=&quot;cart-page-performance-configuration&quot;&gt;Cart page performance configuration&lt;/h2&gt;
&lt;p&gt;Cart page performance is crucial for providing a smooth and efficient shopping experience for your customers. This guideline outlines best practices and configurations to help you optimize the performance of the cart, basket, and checkout pages in Spryker-based projects.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;1. Prerequisites&lt;/h2&gt;
&lt;p&gt;Before implementing performance configurations, ensure that you have installed the latest packages and modules in your Spryker project. These include:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;composer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^7.16.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merchant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;offer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^1.11.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;money&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^2.15.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^6.13.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^4.49.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bundle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^7.27.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^2.6.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^3.56.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merchant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^1.5.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alternative&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^1.6.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;detail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^3.27.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^1.4.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^1.6.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^1.16.0&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^1.100.0 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;configurations&quot;&gt;2. Configurations&lt;/h2&gt;
&lt;p&gt;To enhance cart page performance, implement the following configurations:&lt;/p&gt;
&lt;h3 id=&quot;enable-caching&quot;&gt;2.1 Enable Caching&lt;/h3&gt;
&lt;p&gt;By default, the cart page recalculates on every request. To improve performance, enable caching for the cart page by configuring the cache settings in the &lt;code&gt;CartPageConfig&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;To disable cart recalculation on a cart page request, use the following configuration:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isQuoteValidationEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To disable cart recalculation on every AJAX request, use the following configuration:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isQuoteValidationEnabledForAjaxCartItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In both cases, the cart recalculates only when items are added or removed. Ensure that your project requirements permit disabling cart recalculation.&lt;/p&gt;
&lt;p&gt;To enable cart recalculation while improving performance, set a time limit for recalculation:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getQuoteValidationCacheTtl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Time in seconds&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this case, the cart recalculates when items are added or removed and when the customer opens the cart after five minutes (300 seconds).&lt;/p&gt;
&lt;h3 id=&quot;enable-widget-caching-for-the-merchant-feature-only&quot;&gt;2.2 Enable widget caching (for the Merchant feature only)&lt;/h3&gt;
&lt;p&gt;If you use the Merchant feature, enable widget caching to reduce server load and improve response times. Configure this setting in the &lt;code&gt;ShopApplicationDependencyProvider&lt;/code&gt; class.&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getWidgetCacheKeyGeneratorStrategyPlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// other plugins&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SoldByMerchantWidgetCacheKeyGeneratorStrategyPlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;configure-product-relations-feature&quot;&gt;2.3 Configure Product Relations feature&lt;/h3&gt;
&lt;p&gt;If your cart page displays &lt;a href=&quot;/docs/pbc/all/product-relationship-management/latest/product-relationship-management.html&quot;&gt;product relations&lt;/a&gt; (for example, related products or upsells), ensure that the Product Relations feature is properly configured to optimize performance.&lt;/p&gt;
&lt;p&gt;Limit the number of product relations loaded on the cart page in the &lt;code&gt;ProductRelationStorageConfig&lt;/code&gt; class:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getUpsellingProductLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Update product relation widgets on the cart page to use carousel rendering to improve loading times and enhance user experience.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ProductAlternativeListWidget:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;language-twig highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;widget&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;ProductAlternativeListWidget&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data.product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;isNewCarouselRenderingEnabled&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;only&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;ProductReplacementForListWidget:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;language-twig highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;widget&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;ProductReplacementForListWidget&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data.product.sku&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;isNewCarouselRenderingEnabled&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;only&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;SimilarProductsWidget:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;language-twig highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;widget&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;SimilarProductsWidget&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data.product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;isNewCarouselRenderingEnabled&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;only&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Verify that these widgets are present in your project Twig templates, and update them as needed.&lt;/p&gt;
&lt;h2 id=&quot;cart-operations-performance-configuration&quot;&gt;Cart operations performance configuration&lt;/h2&gt;
&lt;p&gt;To optimize the performance of cart operations such as adding, removing, or updating items in the cart, consider the following configurations:&lt;/p&gt;
&lt;h2 id=&quot;prerequisites-1&quot;&gt;1. Prerequisites&lt;/h2&gt;
&lt;p&gt;Ensure that you have the latest versions of the following packages installed in your Spryker project:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;composer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merchant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^3.19.0&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^3.14.0&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;configurations-1&quot;&gt;2. Configurations&lt;/h2&gt;
&lt;p&gt;You can divide cart operations into two categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Inside-cart operations&lt;/strong&gt; (add to cart, remove from cart, change item quantity, and similar actions)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;General cart operations&lt;/strong&gt; (create cart, delete cart, share cart, and similar actions)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After each operation, the system executes expander plugins. Previously, the same plugin stack was used for both inside-cart and general cart operations.&lt;/p&gt;
&lt;p&gt;With the updated spryker/persistent-cart, these plugin stacks are now separated.&lt;/p&gt;
&lt;h3 id=&quot;enable-configuration-on-the-project-level&quot;&gt;2.1. Enable configuration on the project level&lt;/h3&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pyz\Shared\PersistentCart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Spryker\Shared\PersistentCart\PersistentCartConfig&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerPersistentCartConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersistentCartConfig&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerPersistentCartConfig&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IS_QUOTE_UPDATE_PLUGINS_INSIDE_CART_ENABLED&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;review-plugin-stacks-in-zed&quot;&gt;2.2. Review plugin stacks in ZED&lt;/h3&gt;
&lt;p&gt;By default, the following plugin stack is defined in
&lt;code&gt;\Pyz\Zed\PersistentCart\PersistentCartDependencyProvider::getQuoteResponseExpanderPlugins&lt;/code&gt;.
It is currently used for all operations. After the fix, it is used only for general cart operations.&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cd&quot;&gt;/**
 * @return array&amp;lt;\Spryker\Zed\PersistentCartExtension\Dependency\Plugin\QuoteResponseExpanderPluginInterface&amp;gt;
*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getQuoteResponseExpanderPlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerCartQuoteResponseExpanderPlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// MultiCartFeature&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SharedCartQuoteResponseExpanderPlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// SharedCartFeature&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A new plugin stack is introduced specifically for inside-cart operations. It is empty by default. If your project uses custom plugins for inside-cart updates, you must enable them in this method:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cd&quot;&gt;/**
 * @return array&amp;lt;\Spryker\Zed\PersistentCartExtension\Dependency\Plugin\QuoteResponseExpanderPluginInterface&amp;gt;
 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getQuoteResponseExpanderPluginsForInsideCartOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Both methods expect plugins that implement the same interface, so you can easily divide the plugins between the two stacks.&lt;/p&gt;
&lt;h3 id=&quot;review-plugin-stacks-in-client&quot;&gt;2.3. Review plugin stacks in Client&lt;/h3&gt;
&lt;p&gt;By default, the following plugin stack is defined in
&lt;code&gt;\Pyz\Client\PersistentCart\PersistentCartDependencyProvider::getQuoteUpdatePlugins&lt;/code&gt;.
It is currently used for all operations. After the fix, it is used only for general cart operations.&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cd&quot;&gt;/**
 * @return array&amp;lt;\Spryker\Client\PersistentCartExtension\Dependency\Plugin\QuoteUpdatePluginInterface&amp;gt;
 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getQuoteUpdatePlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SaveCustomerQuotesQuoteUpdatePlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// MultiCartFeature&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SharedCartsUpdateQuoteUpdatePlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;// SharedCartFeature&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DefaultQuoteUpdatePlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;// MultiCartFeature&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PermissionUpdateQuoteUpdatePlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// SharedCartFeature&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A new plugin stack is introduced specifically for inside-cart operations. It is empty by default. If your project requires inside-cart-specific plugins, enable them here:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cd&quot;&gt;/**
 * @return array&amp;lt;\Spryker\Client\PersistentCartExtension\Dependency\Plugin\QuoteUpdatePluginInterface&amp;gt;
 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getQuoteUpdatePluginsForInsideCartOperations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Both methods expect plugins that implement the same interface, which allows you to separate them without additional adjustments.&lt;/p&gt;
&lt;h2 id=&quot;shopping-list-to-cart-performance-configuration&quot;&gt;Shopping list to cart performance configuration&lt;/h2&gt;
&lt;p&gt;Adding all items from a shopping list with 100 or more items to the cart can be slow when the bulk add-to-cart optimization is disabled. This section describes how to enable it.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites-2&quot;&gt;1. Prerequisites&lt;/h2&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;composer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spryker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^7.17.0&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;configuration&quot;&gt;2. Configuration&lt;/h2&gt;
&lt;p&gt;To enable bulk add-to-cart from shopping lists, override the &lt;code&gt;isAddToCartBulkEnabled()&lt;/code&gt; method in &lt;code&gt;CartConfig&lt;/code&gt; at the project level:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pyz\Zed\Cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Spryker\Zed\Cart\CartConfig&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerCartConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CartConfig&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerCartConfig&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isAddToCartBulkEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When enabled, items from a shopping list are added to the cart in a single bulk operation instead of individual requests, which significantly reduces the time required for large shopping lists.&lt;/p&gt;
</description>
            <pubDate>Mon, 27 Apr 2026 08:54:30 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/cart-page-performance-configuration.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/cart-page-performance-configuration.html</guid>
            
            
        </item>
        
        <item>
            <title>Install AI Commerce</title>
            <description>This document describes how to install the AI Commerce base package. Individual AI Commerce features require additional installation steps — see their respective installation guides.

## Prerequisites

Install the required features:

| NAME | VERSION | INSTALLATION GUIDE |
|------|---------|-------------------|
| AiFoundation | {{page.release_tag}} | |

## 1) Install the required modules

Install the required module:

```bash
composer require spryker-feature/ai-commerce --update-with-dependencies
```

## 2) Configure AiFoundation

Add the AI provider configuration:

`config/Shared/config_ai.php`

```php
$openAiConfiguration = [
    &apos;provider_name&apos; =&gt; \Spryker\Shared\AiFoundation\AiFoundationConstants::PROVIDER_OPENAI,
    &apos;provider_config&apos; =&gt; [
        &apos;key&apos; =&gt; getenv(&apos;OPEN_AI_API_TOKEN&apos;) ?: &apos;&apos;, // provide your OpenAi api key
        &apos;model&apos; =&gt; &apos;gpt-4o-mini&apos;,
    ],
];
$config[\Spryker\Shared\AiFoundation\AiFoundationConstants::AI_CONFIGURATIONS] = [
    \Spryker\Shared\AiFoundation\AiFoundationConstants::AI_CONFIGURATION_DEFAULT =&gt; $openAiConfiguration,
];
```

`config/Shared/config_default.php`

```php
require &apos;config_ai.php&apos;;
```

## 3) Enable AI Commerce features at the project level

AI Commerce features are disabled by default in the module-level configuration. To enable individual features, create a project-level configuration file that overrides the default settings.

Create the following file:

**data/configuration/ai_commerce.configuration.yml**

```yaml
features:
    - key: ai_commerce
      tabs:
          - key: backoffice_assistant
            enabled: true
          - key: quick_order
            enabled: true
          - key: search_by_image
            enabled: true
```

Set `enabled: true` only for the features you want to activate. You can omit tabs you do not want to enable.

After creating the file, sync the configuration to the database:

```bash
console configuration:sync
```

{% info_block warningBox &quot;Verification&quot; %}

In the Back Office, go to **AI Commerce** and make sure the enabled features are visible in the navigation.

{% endinfo_block %}
</description>
            <pubDate>Sat, 25 Apr 2026 07:19:42 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/ai/ai-commerce/install-ai-commerce.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/ai/ai-commerce/install-ai-commerce.html</guid>
            
            
        </item>
        
        <item>
            <title>AI Dev SDK Overview</title>
            <description>&lt;section class=&apos;info-block info-block--warning&apos;&gt;&lt;i class=&apos;info-block__icon icon-warning&apos;&gt;&lt;/i&gt;&lt;div class=&apos;info-block__content&apos;&gt;&lt;div class=&quot;info-block__title&quot;&gt;Experimental module&lt;/div&gt;
&lt;p&gt;The AiDev module is experimental and not stable. There is no backward compatibility promise for this module. We welcome your feedback and contributions as we continue to develop and improve this module.&lt;/p&gt;
&lt;/div&gt;&lt;/section&gt;
&lt;section class=&apos;info-block info-block--warning&apos;&gt;&lt;i class=&apos;info-block__icon icon-warning&apos;&gt;&lt;/i&gt;&lt;div class=&apos;info-block__content&apos;&gt;&lt;div class=&quot;info-block__title&quot;&gt;Prerequisites&lt;/div&gt;
&lt;p&gt;This module requires &lt;code&gt;^1.71.0&lt;/code&gt; version of &lt;code&gt;docker/sdk&lt;/code&gt; for proper usage.
Make sure your development environment is up to date before installing the AiDev module.&lt;/p&gt;
&lt;/div&gt;&lt;/section&gt;
&lt;p&gt;This document describes how to integrate and use the AiDev module to connect your Spryker application to AI development tools through the Model Context Protocol (MCP).&lt;/p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;The AiDev module provides an MCP server that enables AI assistants to interact with your Spryker application.
It exposes Spryker-specific information through MCP tools and prompts, allowing AI assistants to better understand and work with your codebase.&lt;/p&gt;
&lt;p&gt;The module includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MCP Server&lt;/strong&gt;: A console command that runs an MCP server to communicate with AI assistants&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extension Points&lt;/strong&gt;: Plugin interfaces for adding custom MCP tools and prompts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in Tools&lt;/strong&gt;: Pre-configured tools for accessing Spryker transfers, interfaces, and OMS information&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prompt Generation&lt;/strong&gt;: Automatic generation of context-aware prompts from documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;console-commands&quot;&gt;Console commands&lt;/h2&gt;
&lt;p&gt;The AiDev module provides the following console commands:&lt;/p&gt;
&lt;h3 id=&quot;mcp-server-command&quot;&gt;MCP Server Command&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;ai-dev:mcp-server&lt;/code&gt; command starts an MCP server that allows AI assistants to interact with your Spryker application.&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk console ai-dev:mcp-server &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Starts an MCP server using stdio transport&lt;/li&gt;
&lt;li&gt;Registers all configured MCP tool and prompt plugins&lt;/li&gt;
&lt;li&gt;Automatically generates prompts if they don’t exist&lt;/li&gt;
&lt;li&gt;Listens for requests from AI assistants&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: This command is typically configured in AI assistant tools (like Claude Desktop) to enable them to access Spryker-specific information.&lt;/p&gt;
&lt;h3 id=&quot;setup-command&quot;&gt;Setup Command&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;ai-dev:setup&lt;/code&gt; command sets up AI tooling for your Spryker project. It generates rules, an agents/context file, and skills — all tailored to the selected AI tool.&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk console ai-dev:setup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command automatically detects the AI tool installed in your project and prompts you to confirm or select a different one. It then generates the following for the selected tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rules&lt;/strong&gt;: Coding conventions and architectural guidelines.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agents/context file&lt;/strong&gt;: Project-specific context for AI agents.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skills&lt;/strong&gt;: Reusable task-specific AI skill files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Output modes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The command supports two output modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ready to use&lt;/strong&gt;: Files are generated directly in the tool-specific directories listed in the table below.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Example&lt;/strong&gt;: Files are generated in example directories — for example, &lt;code&gt;.claude/rules-example/&lt;/code&gt; instead of &lt;code&gt;.claude/rules/&lt;/code&gt;. Rename the directories when ready to use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Output locations by AI tool&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AI tool&lt;/th&gt;
&lt;th&gt;Rules directory&lt;/th&gt;
&lt;th&gt;Agents/context file&lt;/th&gt;
&lt;th&gt;Skills directory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.claude/rules/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.claude/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windsurf&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.windsurf/rules/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.windsurfrules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.windsurf/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Copilot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.github/instructions/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.github/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cursor/rules/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cursor/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.opencode/rules/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.agents/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex CLI&lt;/td&gt;
&lt;td&gt;Not supported — see below&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.agents/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Codex CLI does not have a native rules format. When you select it, the command offers to generate rules in another tool’s format instead and places them in that tool’s rules directory.&lt;/p&gt;
&lt;section class=&apos;info-block &apos;&gt;&lt;i class=&apos;info-block__icon icon-info&apos;&gt;&lt;/i&gt;&lt;div class=&apos;info-block__content&apos;&gt;&lt;div class=&quot;info-block__title&quot;&gt;GitHub Copilot and Docker sync&lt;/div&gt;
&lt;p&gt;If you use Docker sync, the &lt;code&gt;/.git*&lt;/code&gt; entry in &lt;code&gt;.dockersyncignore&lt;/code&gt; also excludes the &lt;code&gt;.github&lt;/code&gt; folder, which prevents Copilot-generated files from being available inside the container. To fix this, add the following line to &lt;code&gt;.dockersyncignore&lt;/code&gt; after the &lt;code&gt;/.git*&lt;/code&gt; entry:&lt;/p&gt;
&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!/.github
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/section&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: Run this command once when setting up AI tooling for a project.&lt;/p&gt;
&lt;h3 id=&quot;generate-prompts-command&quot;&gt;Generate Prompts Command&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;ai-dev:generate-prompts&lt;/code&gt; command generates MCP prompts from a configured &lt;a href=&quot;https://github.com/spryker-dev/prompt-library&quot;&gt;Prompt Library&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk console ai-dev:generate-prompts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fetches prompts&lt;/li&gt;
&lt;li&gt;Generates PHP-based prompt classes&lt;/li&gt;
&lt;li&gt;Stores generated prompts in the configured directory&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: Use this command when you need to regenerate prompts from updated documentation or when initializing the module for the first time.&lt;/p&gt;
&lt;h2 id=&quot;extension-points&quot;&gt;Extension points&lt;/h2&gt;
&lt;p&gt;The AiDev module provides plugin interfaces for extending the MCP server with custom functionality:&lt;/p&gt;
&lt;h3 id=&quot;aidevmcptoolplugininterface&quot;&gt;AiDevMcpToolPluginInterface&lt;/h3&gt;
&lt;p&gt;Implement this interface to add custom MCP tools that AI assistants can use to query or interact with your application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interface location&lt;/strong&gt;: &lt;code&gt;SprykerSdk\Zed\AiDev\Dependency\AiDevMcpToolPluginInterface&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Integration&lt;/strong&gt;: Register your tool plugins in &lt;code&gt;AiDevDependencyProvider::getMcpToolPlugins()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pyz\Zed\AiDev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerSdk\Zed\AiDev\AiDevDependencyProvider&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerAiDevDependencyProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pyz\Zed\AiDev\Communication\Plugins\CustomAiDevMcpToolPlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AiDevDependencyProvider&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerAiDevDependencyProvider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cd&quot;&gt;/**
     * @return array&amp;lt;\SprykerSdk\Zed\AiDev\Dependency\AiDevMcpToolPluginInterface&amp;gt;
     */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getMcpToolPlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;array_merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getMcpToolPlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomAiDevMcpToolPlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;aidevmcppromptplugininterface&quot;&gt;AiDevMcpPromptPluginInterface&lt;/h3&gt;
&lt;p&gt;Implement this interface to add custom MCP prompts that provide context or instructions to AI assistants.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interface location&lt;/strong&gt;: &lt;code&gt;SprykerSdk\Zed\AiDev\Dependency\AiDevMcpPromptPluginInterface&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Integration&lt;/strong&gt;: Register your prompt plugins in &lt;code&gt;AiDevDependencyProvider::getMcpPromptPlugins()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pyz\Zed\AiDev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerSdk\Zed\AiDev\AiDevDependencyProvider&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerAiDevDependencyProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pyz\Zed\AiDev\Communication\Plugins\CustomAiDevMcpPromptPlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AiDevDependencyProvider&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerAiDevDependencyProvider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cd&quot;&gt;/**
     * @return array&amp;lt;\SprykerSdk\Zed\AiDev\Dependency\AiDevMcpPromptPluginInterface&amp;gt;
     */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getMcpPromptPlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomAiDevMcpPromptPlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;configuration&quot;&gt;Configuration&lt;/h2&gt;
&lt;p&gt;The AiDev module can be configured through the &lt;code&gt;AiDevConfig&lt;/code&gt; class. Key configuration options include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prompt Directory&lt;/strong&gt;: Set the directory where generated prompts are stored&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Refer to the module’s configuration class for available options and their default values.&lt;/p&gt;
&lt;h2 id=&quot;debugging-the-mcp-server&quot;&gt;Debugging the MCP server&lt;/h2&gt;
&lt;p&gt;Before connecting your MCP server to AI assistants, you can test and debug it using the &lt;a href=&quot;https://modelcontextprotocol.io/docs/tools/inspector&quot;&gt;MCP Inspector&lt;/a&gt; tool. The inspector provides a web interface to interact with your MCP server, test tools, and verify that everything works correctly.&lt;/p&gt;
&lt;h3 id=&quot;using-the-mcp-inspector&quot;&gt;Using the MCP Inspector&lt;/h3&gt;
&lt;p&gt;Navigate to your Spryker project directory and run:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx @modelcontextprotocol/inspector docker/sdk console ai-dev:mcp-server &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This command will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start the MCP Inspector in your browser&lt;/li&gt;
&lt;li&gt;Connect to your local MCP server&lt;/li&gt;
&lt;li&gt;Display all available tools and prompts&lt;/li&gt;
&lt;li&gt;Allow you to test tool calls interactively&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With Xdebug&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx @modelcontextprotocol/inspector docker/sdk cli &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; console ai-dev:mcp-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;section class=&apos;info-block &apos;&gt;&lt;i class=&apos;info-block__icon icon-info&apos;&gt;&lt;/i&gt;&lt;div class=&apos;info-block__content&apos;&gt;&lt;div class=&quot;info-block__title&quot;&gt;Node.js required&lt;/div&gt;
&lt;p&gt;The MCP Inspector requires Node.js to be installed on your system. The &lt;code&gt;npx&lt;/code&gt; command will automatically download and run the inspector tool without requiring a global installation.&lt;/p&gt;
&lt;/div&gt;&lt;/section&gt;
&lt;p&gt;&lt;img src=&quot;https://spryker.s3.eu-central-1.amazonaws.com/docs/dg/dev/ai-dev/mcp-inspector.png&quot; alt=&quot;MCP Inspector&quot; /&gt;&lt;/p&gt;
</description>
            <pubDate>Thu, 23 Apr 2026 07:22:30 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/ai/ai-dev/ai-dev-overview.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/ai/ai-dev/ai-dev-overview.html</guid>
            
            
        </item>
        
        <item>
            <title>AI Dev SDK</title>
            <description>&lt;section class=&apos;info-block info-block--warning&apos;&gt;&lt;i class=&apos;info-block__icon icon-warning&apos;&gt;&lt;/i&gt;&lt;div class=&apos;info-block__content&apos;&gt;&lt;div class=&quot;info-block__title&quot;&gt;Warning&lt;/div&gt;
&lt;p&gt;Before you use AI-related tools, consult your legal department.&lt;/p&gt;
&lt;/div&gt;&lt;/section&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;When you use AI tools to write code, they rely on general patterns, which leads to mistakes and requires you to repeatedly explain your project structure.&lt;/p&gt;
&lt;p&gt;The AI Dev SDK provides an MCP server, which is a console command that your AI tool runs in your project’s Docker container and helps it to increase Spryker-context awareness. MCP (Model Context Protocol) is a standard protocol that lets AI tools request specific information from external sources, similar to how a browser requests data from an API.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;
&lt;p&gt;After you install the SDK, &lt;a href=&quot;/docs/dg/dev/ai/ai-dev/ai-dev-mcp-server.html&quot;&gt;add a simple configuration&lt;/a&gt; to your AI tool. When your Spryker project is running, your AI tool can access Spryker-specific information and prompts through the MCP server.&lt;/p&gt;
&lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;
&lt;p&gt;AI generates better code faster and with fewer errors. You spend less time correcting mistakes and explaining Spryker concepts.&lt;/p&gt;
&lt;h2 id=&quot;key-capabilities&quot;&gt;Key capabilities&lt;/h2&gt;
&lt;h3 id=&quot;faster-spryker-specific-answers&quot;&gt;Faster Spryker-specific answers&lt;/h3&gt;
&lt;p&gt;AI can search Spryker documentation instead of requiring you to explain basic concepts or guessing how features work.&lt;/p&gt;
&lt;h3 id=&quot;smarter-code-generation&quot;&gt;Smarter code generation&lt;/h3&gt;
&lt;p&gt;AI can look up your actual transfer objects, module dependencies, and interface methods so that the generated code matches your project structure instead of relying on assumptions.&lt;/p&gt;
&lt;h3 id=&quot;oms-debugging-made-easy&quot;&gt;OMS debugging made easy&lt;/h3&gt;
&lt;p&gt;AI can analyze your OMS flows to find possible next states, transitions, conditions, and timeouts for any order or state. This capability is especially helpful when you work with complex OMS schemas. You no longer need to manually follow arrows in large diagrams.&lt;/p&gt;
&lt;h3 id=&quot;working-with-complex-data-imports&quot;&gt;Working with complex data imports&lt;/h3&gt;
&lt;p&gt;AI can analyze, modify, and transform multi-column CSV files correctly. This task normally requires significant manual effort.&lt;/p&gt;
&lt;h3 id=&quot;sharing-prompts-across-your-team&quot;&gt;Sharing prompts across your team&lt;/h3&gt;
&lt;p&gt;The SDK includes access to the Spryker prompt library, and you can add your own project-specific prompts. This means your team can reuse effective prompts instead of everyone writing their own.&lt;/p&gt;
&lt;h3 id=&quot;database-queries&quot;&gt;Database queries&lt;/h3&gt;
&lt;p&gt;AI can execute read-only database queries to inspect data when debugging issues.&lt;/p&gt;
&lt;h3 id=&quot;extensible-for-your-project&quot;&gt;Extensible for your project&lt;/h3&gt;
&lt;p&gt;You can extend the MCP server by creating custom plugins (to add new tools) and custom prompts.&lt;/p&gt;
&lt;h2 id=&quot;install-the-ai-dev-sdk&quot;&gt;Install the AI Dev SDK&lt;/h2&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Ensure that you have a Spryker project with Composer installed.&lt;/p&gt;
&lt;h3 id=&quot;installation-steps&quot;&gt;Installation steps&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Require the package as a development dependency:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;composer require spryker-sdk/ai-dev &lt;span class=&quot;nt&quot;&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generate the transfer objects:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;console transfer:generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Register the console commands in your &lt;code&gt;ConsoleDependencyProvider&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerSdk\Zed\AiDev\Communication\Console\AiToolSetupConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerSdk\Zed\AiDev\Communication\Console\GeneratePromptsConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SprykerSdk\Zed\AiDev\Communication\Console\McpServerConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getConsoleCommands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Container&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$container&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;mf&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;class_exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;McpServerConsole&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$commands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;McpServerConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;class_exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;GenerateSkillsConsole&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$commands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AiToolSetupConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;class_exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;GeneratePromptsConsole&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$commands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GeneratePromptsConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;mf&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Connect the AI Dev SDK to your AI agent. For detailed configuration instructions, see &lt;a href=&quot;/docs/dg/dev/ai/ai-dev/ai-dev-mcp-server.html&quot;&gt;Configure the AiDev MCP server&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/ai/ai-dev/ai-dev-mcp-server.html&quot;&gt;Configure the AiDev MCP server&lt;/a&gt; - Set up the connection to your AI tool&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/ai/ai-dev/ai-dev-overview.html&quot;&gt;AI Dev SDK Overview&lt;/a&gt; - Learn more about the AI Dev SDK features and capabilities&lt;/li&gt;
&lt;/ul&gt;
</description>
            <pubDate>Thu, 23 Apr 2026 07:15:42 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/ai/ai-dev/ai-dev.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/ai/ai-dev/ai-dev.html</guid>
            
            
        </item>
        
        <item>
            <title>Test console commands</title>
            <description>Spryker supports several [test helpers](/docs/dg/dev/guidelines/testing-guidelines/test-helpers/test-helpers.html) to assist you in testing your project. This article provides details of how to test console commands with ConsoleHelper.

To test console commands, do the following:

1. Add `\SprykerTest\Zed\Console\Helper\ConsoleHelper` to the `codeception.yml` file:

```yml
suites:
    Communication:
        path: Communication
        actor: {ModuleName}CommunnicationTester
        modules:
            enabled:
                - \SprykerTest\Zed\Console\Helper\ConsoleHelper
                - ...
```

For more information about the `codeception.yml` file, see [Test framework](/docs/dg/dev/guidelines/testing-guidelines/test-framework.html).

2. Create the test directory `tests/PyzTests/Zed/FooModule/Communication/Plugin/Console/`, if it&apos;s not available yet.
3. Add the test class:

```php
&lt;?php

namespace PyzTest\Zed\FooModule\Communication\Plugin\Console;

use Codeception\Test\Unit;
use PyzTest\Zed\FooModule\FooModuleCommunicationTester;

class MyConsoleCommandTest extends Unit
{
    /**
     * @var \{Organization}Test\Zed\FooModule\FooModuleCommunicationTester
     */
    protected FooModuleCommunicationTester $tester;
}
```

4. Add the test method:

```php
public function testMyConsoleCommand(): void
{
    // You can also use a mocked command or add a mocked facade etc.
    $command = new MyConsoleCommand();
    $commandTester = $this-&gt;tester-&gt;getConsoleTester($command);

    $input = [
        MyConsoleCommand::ARGUMENT_FOO =&gt; &apos;foo-argument&apos;,
        &apos;--&apos; . MyConsoleCommand::OPTION_BAR =&gt; &apos;bar-option&apos;,
    ];

    $commandTester-&gt;execute($input);

    $this-&gt;assertSame(MyConsoleCommand::CODE_SUCCESS, $commandTester-&gt;getStatusCode());

    // When you expect some output from the console command you can assert it with:
    $this-&gt;assertStringContainsString(&apos;My console command output.&apos;, $commandTester-&gt;getDisplay());
}
```

That&apos;s it. You are all set to test the console commands.
</description>
            <pubDate>Wed, 22 Apr 2026 12:15:12 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/guidelines/testing-guidelines/executing-tests/test-console-commands.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/guidelines/testing-guidelines/executing-tests/test-console-commands.html</guid>
            
            
        </item>
        
        <item>
            <title>New Relic transactions grouping by queue names</title>
            <description>By default, the `spryker/monitoring` module groups all New Relic transactions generated from a console command by the command&apos;s name. For example, `ooms:check-timeout`.

This strategy works in most cases, and you can still query the data from a needed queue using [NRQL](https://docs.newrelic.com/docs/query-your-data/nrql-new-relic-query-language/get-started/nrql-syntax-clauses-functions/). But you might want to override this behavior.

For example, you run  multiple commands like `queue:task:start publish.product_abstract` and `queue:task:start sync.storage.url`. With the default naming strategy, all the calls of the command will be grouped together by `queue:task:start`. Transaction data is aggregated under the same name for all types of queue processors.

In New Relic dashboard, this looks as follows:

![Transactions under the same name](https://spryker.s3.eu-central-1.amazonaws.com/docs/scos/dev/guidelines/performance-guidelines/elastic-computing/enable-queue-task-flow-in-new-relic.md/transactions-under-the-same-name.png)

## Group transactions by queue name

As queue name usually comes as the first argument of a command, to enable grouping by names, you need to need to enable grouping by the first argument as follows:

1. Integrate the `spryker/monitoring` module.
2. Provide an advanced extendable infrastructure for [New relic monitoring transaction](https://docs.newrelic.com/docs/apm/transactions/intro-transactions/transactions-new-relic-apm/) naming strategies. This lets you implement, configure, and override the default transaction naming behavior.
3. Implement the strategy that enables configured console commands to apply naming that groups transaction by command name and first argument.
4. Enable the new strategy for `queue:task:start` on the project level.

As a result, the transactions are displayed as follows:

![Aggregation of transactions](https://spryker.s3.eu-central-1.amazonaws.com/docs/scos/dev/guidelines/performance-guidelines/elastic-computing/enable-queue-task-flow-in-new-relic.md/aggregation-of-transactions.png)

## Integrate New Relic transactions grouping by queue names

For instructions, see [Integrate New Relic transactions grouping by queue names](/docs/dg/dev/integrate-and-configure/integrate-elastic-computing.html#integrate-new-relic-monitoring).

## Implementation details

![New Relic transaction grouping by name implementation details](https://confluence-connect.gliffy.net/embed/image/59eaf32b-df1e-4fb9-a5e7-64b77c8ab870.png?utm_medium=live&amp;utm_source=custom)
</description>
            <pubDate>Wed, 22 Apr 2026 12:15:12 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/guidelines/performance-guidelines/elastic-computing/new-relic-transaction-grouping-by-queue-names.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/guidelines/performance-guidelines/elastic-computing/new-relic-transaction-grouping-by-queue-names.html</guid>
            
            
        </item>
        
    </channel>
</rss>
