preload and early hints. Read it before you edit head.html or split CSS.
## Run Lighthouse audits
PageSpeed Insights runs Lighthouse on Google's hardware instead of on your computer, so you can compare one test run to the next more fairly than if you only tested locally. It reports Web Vitals, Google's standard scores for how fast and stable the page feels. The fix list follows Google's performance guidance. When you change load order or assets, compare those suggestions with https://www.aem.live/developer/keeping-it-100 so resource hints, fonts, and load phases still match what Edge Delivery Services expects.
- [PageSpeed Insights](https://pagespeed.web.dev/) — Enter your storefront production URL (typically *.aem.live) for accurate results. That hostname sits on CDNs close to your customers, on the edge.
### Step-by-step
The following steps run a PageSpeed Insights audit on a URL you choose.
1. Go to the https://pagespeed.web.dev/.
1. Paste your storefront URL into the PageSpeed Insights input field.
1. If you use the default `main` branch pattern on Edge Delivery, typical preview and production hosts look like this (replace `{repo}` and `{owner}` with your GitHub repository name and owner):
- Preview: `https://main--{repo}--{owner}.aem.page/`
- Production: `https://main--{repo}--{owner}.aem.live/`
1. Click the **Analyze** button to run the audit.
1. You'll see full Web Vitals reports for mobile and desktop. Scores are often high on tuned Edge Delivery storefronts, but they vary with page content, third-party scripts, and test conditions, so treat the report as a snapshot, not a guarantee of 100 on every run.
## Performance in the Commerce boilerplate
### Delayed phase
https://www.aem.live/developer/keeping-it-100 describes a delayed phase for third-party tags, marketing tooling, extended analytics, consent, chat, and similar scripts. Load them through `delayed.js` so they do not compete with LCP or the rest of the experience.
In the Commerce boilerplate, implement that path in https://github.com/hlxsites/aem-boilerplate-commerce/blob/main/scripts/delayed.js. Keep it off the eager path. For Adobe Experience Platform and related patterns, see [Adobe Experience Platform analytics](https://experienceleague.adobe.com/developer/commerce/storefront/setup/analytics/adobe-experience-platform/).
### Eager vs lazy styles
Keeping it 100 treats styles in two phases so LCP stays predictable.
#### Eager phase
The eager phase covers the markup, CSS, and JavaScript that must load first so the main content can appear quickly and your LCP score can settle. Stay within the network and payload limits the Keeping it 100 page describes.
> **Preload and early hints** Too many preload tags, early hints, and preconnect entries can steal bandwidth from what the visitor needs first and hurt mobile LCP. Keeping it 100 explains how to stay inside safe limits instead of adding every hint you can think of.
#### Lazy phase
The lazy phase is for styles (and related assets) that can load after the main content and LCP finish so they do not slow the first screen.
In the Commerce boilerplate, eager, site-wide tokens and styles usually start in https://github.com/hlxsites/aem-boilerplate-commerce/blob/main/styles/styles.css. For deferred styles (for example, `lazy-styles.css`), use the folder-and-import patterns in [Branding and styles](https://experienceleague.adobe.com/developer/commerce/storefront/dropins/all/branding/) instead of merging them into the eager file.
After your split matches that guidance, align `head.html` and load order with Keeping it 100 so you are not maintaining a second rule set. Keeping it 100 also explains why preloading every web font often backfires. Keep the boilerplate font fallbacks unless you have a clear measurement that says to change them.
> **Related storefront docs** For tips on catalog pages (images, APIs, loading order), see the [FAQ](https://experienceleague.adobe.com/developer/commerce/storefront/troubleshooting/faq/#how-can-i-improve-the-performance-of-my-catalog-pages).
---
# Adobe Experience Platform
## Overview
Adobe Experience Platform (AEP) is a comprehensive suite of services that enables you to collect, unify, and analyze customer data from multiple touchpoints. By integrating your Adobe Commerce storefront with AEP, you can gain deeper insights into customer behavior and create more personalized experiences.

*Adobe Experience Platform architecture showing data flow from various sources to unified customer profiles.*
This integration allows your storefront to send commerce events (product views, purchases, cart actions) directly to the Experience Platform Edge Network, where they can be processed, stored, and used for real-time personalization and analytics.
For more information about Adobe Experience Platform capabilities, see the https://experienceleague.adobe.com/docs/experience-platform/landing/home.html.
## Prerequisites
Before configuring your integration with Adobe Experience Platform, ensure you have the following:
### Required Identifiers
* **IMS Organization ID**: Your Adobe organization identifier (format: `1234567890ABCDEF7F000101@AdobeOrg`)
* **Datastream ID**: A configured datastream for routing data (format: `12345678-1234-1234-1234-123456789012`)
### How to Find Your Identifiers
**IMS Organization ID:**
To locate your IMS Organization ID, refer to the https://experienceleague.adobe.com/docs/core-services/interface/administration/organizations.html. You can typically find this in:
- Adobe Admin Console
- Developer Console
- Any Adobe Experience Cloud application under Account Settings
**Datastream ID:**
Your datastream must be configured to route data to Adobe Experience Platform. For detailed instructions on creating and configuring a datastream, see the https://experienceleague.adobe.com/docs/experience-platform/datastreams/overview.html.
## Configuration
To enable data flow from your storefront to the Experience Platform Edge Network, you need to add your AEP credentials to your storefront configuration.
### Method 1: Configuration File (Recommended)
Add your AEP credentials to the `analytics` section of your /setup/configuration/commerce-configuration/:
```json title="config.json"
{
"public": {
"default": {
"analytics": {
"aep-ims-org-id": "1234567890ABCDEF7F000101@AdobeOrg",
"aep-datastream-id": "12345678-1234-1234-1234-123456789012",
"base-currency-code": "USD",
"environment": "Testing",
...
}
}
}
}
```
When both `aep-ims-org-id` and `aep-datastream-id` are configured, the storefront automatically:
- Enables event forwarding to Adobe Experience Platform
- Configures the AEP context with your credentials
- Begins sending commerce events to the Experience Platform Edge Network
### Method 2: Direct Script Configuration (Alternative)
Alternatively, you can configure AEP directly in your `scripts/delayed.js` file:
```js
window.adobeDataLayer.push(
{
aepContext: {
imsOrgId: '1234567890ABCDEF7F000101@AdobeOrg',
datastreamId: '12345678-1234-1234-1234-123456789012'
}
},
{
eventForwardingContext: {
aep: true
}
}
);
```
> **Recommended approach** Using the configuration file (Method 1) is recommended because it:
- Centralizes all configuration in one place
- Allows environment-specific settings without code changes
- Simplifies deployment and configuration management
### Configuration Parameters
| Parameter | Description | Required | Example |
|-----------|-------------|----------|---------|
| `aep-ims-org-id` | Your Adobe IMS Organization ID | Yes | `"1234567890ABCDEF7F000101@AdobeOrg"` |
| `aep-datastream-id` | Your configured datastream ID for routing data to AEP | Yes | `"12345678-1234-1234-1234-123456789012"` |
### What This Configuration Does
- **`aep-ims-org-id`**: Identifies your Adobe organization for routing events to the correct Experience Platform environment
- **`aep-datastream-id`**: Specifies the datastream configuration that determines how events are processed and where they are sent
- **Automatic event forwarding**: When both values are present, the storefront automatically enables `eventForwardingContext.aep` and configures the `aepContext`
## Storefront events
Once configured, your storefront will automatically send the following types of events to Adobe Experience Platform:
- **Shopping events**: Cart updates and views (`addToCart`, `removeFromCart`, `shoppingCartView`), page views (`pageView`, `productPageView`), checkout (`startCheckout`, `completeCheckout`) and more.
- **Customer profile events**: Customer login (`signIn`), customer logout (`signOut`), create account (`createAccount`), edit account (`editAccount`).
- **Search events**: Search query (`searchRequestSent`) and search results (`searchResponseReceived`).
> **Search events** If `LiveSearch` is not installed and configured, these search events are not sent.
For a complete list of storefront events, see the https://developer.adobe.com/commerce/services/shared-services/storefront-events/.
> **Debugging events** To debug events in your storefront, use the AEP Debugger Events view. See the https://experienceleague.adobe.com/docs/experience-platform/debugger/home.html for instructions.
These events are processed in real-time and can be used for:
- Customer journey analysis
- Real-time personalization
- Audience segmentation
- Attribution modeling
## Validation
### Testing Your Integration
### 1. Check browser console
After implementing the configuration, open your browser's developer tools and verify that:
- No JavaScript errors appear
- Adobe Data Layer events are being fired
- Network requests to Adobe Experience Platform Edge Network are successful
### 2. Monitor data ingestion
Use Adobe Experience Platform's monitoring tools to confirm data is being received:
- Navigate to your AEP workspace
- Check the **Monitoring** section for incoming data
- Verify events appear in your configured datasets
### Validate event structure
Ensure events contain the expected commerce data fields and customer identifiers.
### Troubleshooting
If data is not flowing as expected:
- **Verify credentials**: Double-check your IMS Organization ID and Datastream ID
- **Check datastream configuration**: Ensure your datastream is properly configured to route to Adobe Experience Platform
- **Review browser network tab**: Look for failed requests to Adobe Experience Platform endpoints
- **Validate Adobe Data Layer**: Confirm the Adobe Data Layer is properly initialized before your AEP configuration
For detailed validation procedures, refer to the https://experienceleague.adobe.com/docs/platform-learn/getting-started-for-data-architects-and-data-engineers/ingest-batch-data.html.
## Next Steps
After successful integration:
1. **Configure schemas**: Set up XDM schemas in Adobe Experience Platform to structure your commerce data
1. **Create audiences**: Build customer segments based on commerce behavior
1. **Set up Real-Time CDP**: Use collected data for personalization and marketing activation
1. **Monitor performance**: Regularly review data quality and ingestion metrics
For a complete implementation example, see the https://experienceleague.adobe.com/docs/core-services/interface/administration/organizations.html and https://github.com/hlxsites/aem-boilerplate-commerce.
---
# Analytics instrumentation
## Overview
Analytics instrumentation is the process of wiring your storefront so user interaction data reaches Adobe Commerce services (for example, Live Search and Product Recommendations) in the shape those services expect. This topic covers the Adobe Client Data Layer (ACDL), `config.json` analytics settings, and validation—not Adobe Analytics reporting alone.
### Why event collection matters
User interaction events collected through your storefront implementation enable:
- **Adobe Sensei features**: Intelligent merchandising and search result optimization in Live Search
- **Product recommendations**: Personalized product suggestions based on user behavior
- **Performance analytics**: Detailed dashboards showing search performance, conversion rates, and user engagement
- **Business intelligence**: Data-driven insights for inventory management and marketing strategies
> **Required for Core Features** Live Search and Product Recommendations events are not sent to AEP. For these features to function correctly, you must collect and send user interaction events to Adobe Commerce. Without proper instrumentation, these features will not work as expected.
## Adobe Client Data Layer (ACDL)
The https://github.com/adobe/adobe-client-data-layer is a standardized JavaScript framework that simplifies data collection on your storefront. It provides a unified approach to capturing, storing, and transmitting user interaction data.
### Key Capabilities
The ACDL enables your storefront to:
- **Collect interaction data**: Track user behaviors like product views, searches, cart actions, and purchases
- **Standardize data format**: Ensure consistent data structure across all events
- **Manage event timing**: Control when and how data is sent to analytics services
- **Support multiple integrations**: Work seamlessly with Adobe Experience Platform, Analytics, and other tools
### Core API Functions
| Function | Description | Use Case |
|----------|-------------|----------|
| `push()` | Add data or trigger events | Send product view, cart addition events |
| `getState()` | Retrieve current data layer state | Access user session or cart information |
| `addEventListener()` | Register event listeners | React to specific user actions |
| `getHistory()` | View event history | Debug or audit data collection |
> **Built-in Support** The Adobe Commerce boilerplate includes ACDL by default, so you don't need to install it separately. Drop-in components automatically send events to the data layer.
## Configuration
### Store Configuration
To enable proper event collection, you need to configure your store's analytics settings. This configuration tells the instrumentation system about your store's identity and structure. The specific values depend on your Commerce environment type.
Refer to the https://experienceleague.adobe.com/developer/commerce/storefront/setup/configuration/commerce-configuration/ for complete details on how to configure your store.
### Required Configuration Parameters
The analytics configuration structure varies based on your Commerce backend type:
### Adobe Commerce (PaaS) / ACCS
For Adobe Commerce PaaS and Adobe Commerce as a Cloud Service environments, use standard Commerce store and website identifiers from your Commerce environment. You can obtain these values using a `storeConfig` query.
```json title="config.json"
{
"analytics": {
"aep-ims-org-id": "{{IMS_ORG_ID}}",
"aep-datastream-id": "{{DATASTREAM_ID}}",
"base-currency-code": "{{CURRENCY_CODE}}",
"environment": "{{ENVIRONMENT_TYPE}}",
"environment-id": "{{ENVIRONMENT_ID}}",
"store-code": "{{STORE_CODE}}",
"store-id": {{STORE_ID}},
"store-name": "{{STORE_NAME}}",
"store-url": "{{STORE_URL}}",
"store-view-code": "{{STORE_VIEW_CODE}}",
"store-view-id": {{STORE_VIEW_ID}},
"store-view-name": "{{STORE_VIEW_NAME}}",
"website-code": "{{WEBSITE_CODE}}",
"website-id": {{WEBSITE_ID}},
"website-name": "{{WEBSITE_NAME}}"
}
}
```
**Configuration Properties:**
| Parameter | Description | Example | Required |
|-----------|-------------|---------|----------|
| `aep-ims-org-id` | Adobe IMS Organization ID for Experience Platform integration | `"1234567890ABCDEF7F000101@AdobeOrg"` | No (required for AEP) |
| `aep-datastream-id` | Datastream ID for routing data to Adobe Experience Platform | `"12345678-1234-1234-1234-123456789012"` | No (required for AEP) |
| `base-currency-code` | The base currency code for the store | `"USD"`, `"EUR"` | Yes |
| `environment` | Environment type | `"Testing"`, `"Production"` | Yes |
| `environment-id` | Unique identifier for the Commerce environment | `"f38a0de0-764b-41fa-bd2c-5bc2f3c7b39a"` | Yes |
| `store-code` | Code identifier for the store from your Commerce environment | `"main_website_store"` | Yes |
| `store-id` | Numeric ID for the store | `1`, `2` | Yes |
| `store-name` | Display name for the store | `"Main Website Store"` | Yes |
| `store-url` | Base URL for the store | `"https://example.com"` | Yes |
| `store-view-code` | Code identifier for the store view | `"default"` | Yes |
| `store-view-id` | Numeric ID for the store view | `1`, `2` | Yes |
| `store-view-name` | Display name for the store view | `"Default Store View"` | Yes |
| `website-code` | Code identifier for the website from your Commerce environment | `"base"` | Yes |
| `website-id` | Numeric ID for the website | `1`, `2` | Yes |
| `website-name` | Display name for the website | `"Main Website"` | Yes |
> **Adobe Experience Platform integration** To enable automatic event forwarding to Adobe Experience Platform, include both `aep-ims-org-id` and `aep-datastream-id` in your analytics configuration. When both values are present, events will automatically be sent to AEP. See the [Adobe Experience Platform integration guide](https://experienceleague.adobe.com/developer/commerce/storefront/setup/analytics/adobe-experience-platform/) for detailed setup instructions.
### Adobe Commerce Optimizer (ACO)
For Adobe Commerce Optimizer environments, use a simplified analytics configuration structure:
```json title="config.json"
{
"analytics": {
"aep-ims-org-id": "{{IMS_ORG_ID}}",
"aep-datastream-id": "{{DATASTREAM_ID}}",
"base-currency-code": "{{CURRENCY_CODE}}",
"environment": "{{ENVIRONMENT_TYPE}}",
"environment-id": "{{TENANT_ID}}",
"locale": "{{LOCALE}}",
"store-url": "{{STORE_URL}}",
"store-view-currency-code": "{{CURRENCY_CODE}}",
"storefront-template": "{{TEMPLATE_TYPE}}",
"view-id": "{{CATALOG_VIEW_ID}}"
}
}
```
**Configuration Properties:**
| Parameter | Description | Example | Required |
|-----------|-------------|---------|----------|
| `aep-ims-org-id` | Adobe IMS Organization ID for Experience Platform integration | `"1234567890ABCDEF7F000101@AdobeOrg"` | No (required for AEP) |
| `aep-datastream-id` | Datastream ID for routing data to Adobe Experience Platform | `"12345678-1234-1234-1234-123456789012"` | No (required for AEP) |
| `base-currency-code` | The base currency code for the store | `"USD"`, `"EUR"` | Yes |
| `environment` | Environment type | `"Testing"`, `"Production"` | Yes |
| `environment-id` | The tenant ID for the Adobe Commerce Optimizer instance | `"8idEEDDiVwjCEJAyB5kjfi"` | Yes |
| `locale` | Catalog source locale (language or geography) | `"en-US"` | Yes |
| `store-url` | Base URL for the store | `"https://example.com"` | Yes |
| `store-view-currency-code` | Currency code for the store view | `"USD"`, `"EUR"` | Yes |
| `storefront-template` | Storefront template type | `"Other"` | No |
| `view-id` | The unique ID assigned to the catalog view | `"0d3eebf7-b5fb-4904-9ccf-f35fcc61862b"` | Yes |
> **environment** The `environment` parameter is used to determine the type of environment your store is running in. This is important because it affects how data is collected and processed. In your storefront configuration, set the value to the JSON string `"Testing"` while you develop and `"Production"` when you deploy.
### Data Services Configuration
For the instrumentation to work with Adobe Commerce's data services, you'll need additional configuration parameters. The easiest way to obtain these is through the `magento/module-data-services-graphql` module, which exposes the necessary GraphQL endpoints.
#### Required for Data Services
- **Catalog Service credentials**: For product data synchronization
- **SaaS environment ID**: Links your storefront to Adobe Commerce SaaS services
- **API keys**: Authenticate with Adobe Commerce backend services
## Event Collection and Validation
### Automatic Event Collection
The Commerce boilerplate includes the https://github.com/adobe/commerce-events/tree/main/packages/storefront-events-collector, which automatically:
1. **Listens for ACDL events**: Monitors the data layer for new events
1. **Validates event structure**: Ensures events conform to required schemas
1. **Batches and sends data**: Efficiently transmits events to Adobe Commerce
1. **Handles errors**: Manages network issues and retry logic
### Event Types Collected
Your instrumentation will automatically track:
- **Shopping events**: Cart updates and views (`addToCart`, `removeFromCart`, `shoppingCartView`), page views (`pageView`, `productPageView`), checkout (`startCheckout`, `completeCheckout`) and more.
- **Customer profile events**: Customer login (`signIn`), customer logout (`signOut`), create account (`createAccount`), edit account (`editAccount`).
- **Search events**: Search query (`searchRequestSent`) and search results (`searchResponseReceived`).
> **Search events** If `LiveSearch` is not installed and configured, these search events are not sent.
> **Debugging events** To debug events in your storefront, use the AEP Debugger Events view. See the https://experienceleague.adobe.com/docs/experience-platform/debugger/home.html for instructions.
### Event Schema Compliance
All events must comply with the schema defined by the https://github.com/adobe/commerce-events/tree/main/packages/storefront-events-sdk. This ensures compatibility with Adobe Commerce services and analytics tools.
## Validation and Testing
The following sections describe how to validate and test your event implementation.
### Automated Validation
You can validate your event implementation using the https://github.com/adobe/adobe-client-data-layer/pull/156. This tool checks:
- **Event structure**: Verifies required fields are present
- **Data types**: Ensures values match expected formats
- **Schema compliance**: Confirms events follow Storefront Event SDK specifications
> **Performance Recommendation** For optimal performance, Adobe recommends writing events directly to ACDL rather than using the Storefront Events SDK wrapper. Drop-in components handle this automatically, but custom implementations should follow this practice.
### Manual Testing Steps
1. **Open browser developer tools** and navigate to the Console tab
1. **Check for ACDL**: Verify `window.adobeDataLayer` exists and contains events
1. **Monitor network requests**: Look for successful data transmission to Adobe services
1. **Validate event data**: Inspect event payloads for completeness and accuracy
1. **Confirm that event data is collected**: To confirm that data is being collected from your Commerce store, use the Adobe Experience Platform debugger to examine your Commerce site.
> **Debugging events** The AEP Debugger provides an Events view that you can use to examine the events being sent from your Commerce site. See the https://experienceleague.adobe.com/docs/experience-platform/debugger/home.html for instructions.
### Common Validation Issues
- **Missing configuration**: Ensure all required analytics parameters are set
- **Incorrect store IDs**: Verify store and website IDs match your Adobe Commerce setup
- **Network connectivity**: Check that your storefront can reach Adobe Commerce endpoints
- **Event timing**: Confirm events fire at the correct moments in the user journey
## Troubleshooting Configuration Issues
**Problem**: Events not being sent
**Solution**:
1. Verify your `config.json` contains all required analytics parameters
1. Check that store IDs match your Adobe Commerce backend configuration
1. Ensure the Storefront Events Collector is loading properly
**Problem**: Invalid event data
**Solution**:
1. Use the ACDL validator to check event structure
1. Verify custom events follow the Storefront Event SDK schema
1. Check for JavaScript errors that might corrupt event data
## Troubleshooting Integration Issues
**Problem**: Live Search not receiving data
**Solution**:
1. Confirm your SaaS environment ID is correctly configured
2. Verify API credentials are valid and have necessary permissions
3. Check that product catalog is properly synchronized
For additional troubleshooting, refer to the https://experienceleague.adobe.com/en/docs/commerce/product-recommendations/admin/workspace#data-collection.
## Best Practices
### Implementation Guidelines
- **Test thoroughly**: Validate events in development before deploying to production
- **Monitor regularly**: Set up alerts for data collection failures
- **Follow schemas**: Always comply with Storefront Event SDK specifications
- **Optimize performance**: Batch events when possible to reduce network overhead
### Data Quality
- **Validate user inputs**: Sanitize data before adding to events
- **Handle edge cases**: Account for scenarios like network failures or missing data
- **Maintain consistency**: Use standardized naming and formatting across all events
- **Respect privacy**: Ensure compliance with data protection regulations
---
# AEM Assets integration
The AEM Assets integration displays product images managed in AEM Assets instead of traditional Commerce-hosted images. The integration delivers enhanced image management capabilities: advanced optimization, cropping, and delivery through Adobe's Content Delivery Network (CDN). Learn more in the https://experienceleague.adobe.com/en/docs/commerce/aem-assets-integration/overview.
## Boilerplate update required
Only one update is required: set `"commerce-assets-enabled": true` in your [Storefront configuration](https://experienceleague.adobe.com/developer/commerce/storefront/setup/configuration/commerce-configuration/).
```json title="config.json" ins={'+':4}
{
"public": {
"default": {
"commerce-assets-enabled": true
}
}
}
```
The Commerce drop-ins automatically detect the `commerce-assets-enabled` configuration and adjust image handling accordingly. See the https://www.aemshop.net/config.json.
## Expected behaviors
The following table describes what happens in different configuration scenarios. The **Image source** refers to where you store your images: either "Commerce" for traditional Commerce-hosted images or "AEM Assets" for images managed in AEM Assets. The **`commerce-assets-enabled setting`** column indicates whether this configuration is set to `true` or `false`.
```text
[
['Image source', 'commerce-assets-enabled setting', 'Expected behavior'],
['Commerce-hosted images', 'true', 'Images display correctly. The AEM Assets integration code passes through Commerce-hosted images without modification.'],
['AEM Assets images', 'true', 'Images display correctly with proper AEM Assets CDN optimization parameters applied. This is the intended configuration for AEM Assets integration.'],
['Commerce-hosted images', 'false', 'Images display correctly using standard Commerce image handling without AEM Assets optimization.'],
['AEM Assets images', 'false', 'Images may not display correctly. AEM Assets images require specific optimization parameters that may conflict with standard Commerce image handling, potentially resulting in 400 errors or broken images.'],
]
```
## How it works in the boilerplate
The Commerce drop-ins automatically detect the `commerce-assets-enabled` configuration and adjust image handling accordingly. Here's how the boilerplate integrates this configuration:
### Import the AEM Assets utility
The Commerce blocks in the boilerplate import the `tryRenderAemAssetsImage` helper from the drop-ins tools package.
```javascript showLineNumbers=false
```
### Render images via drop-in slots
The Commerce blocks use the `tryRenderAemAssetsImage` function inside its drop-in image slots, as shown below.
```javascript showLineNumbers=false {"Container:":4-5} {"Container Image Slot:":6-8} {"AEM Assets integration:":10-17}
export default async function decorate(block) {
await dropinRenderer.render(DropinComponent, {
slots: {
DropinImageSlot: (ctx) => {
const { data, defaultImageProps } = ctx;
tryRenderAemAssetsImage(ctx, {
imageProps: defaultImageProps,
params: {
width: defaultImageProps.width,
height: defaultImageProps.height,
},
});
},
},
})(block);
}
```
## Real-world examples from the boilerplate
Based on the Commerce blocks in the boilerplate, AEM Assets integration uses the `tryRenderAemAssetsImage` function from `@dropins/tools/lib/aem/assets.js` as follows.
### Product List SearchResults container
```javascript showLineNumbers=false {"Container:":2-3} {"Container Image Slot:":4-6} {"AEM Assets integration:":11-20}
provider.render(SearchResults, {
slots: {
ProductImage: (ctx) => {
const { product, defaultImageProps } = ctx;
const anchorWrapper = document.createElement('a');
anchorWrapper.href = rootLink(`/products/${product.urlKey}/${product.sku}`);
tryRenderAemAssetsImage(ctx, {
alias: product.sku,
imageProps: defaultImageProps,
wrapper: anchorWrapper,
params: {
width: defaultImageProps.width,
height: defaultImageProps.height,
},
});
},
},
});
```
### Checkout OrderProductList container
```javascript showLineNumbers=false {"Container:":2-3} {"Container Image Slot:":4-6} {"AEM Assets integration:":8-16}
OrderProvider.render(OrderProductList, {
slots: {
CartSummaryItemImage: (ctx) => {
const { data, defaultImageProps } = ctx;
tryRenderAemAssetsImage(ctx, {
alias: data.product.sku,
imageProps: defaultImageProps,
params: {
width: defaultImageProps.width,
height: defaultImageProps.height,
},
});
},
...
},
})($orderProductList);
```
## Using drop-ins outside the boilerplate (optional)
If you use the boilerplate, AEM Assets works out of the box. Without the boilerplate, you need to implement the minimal config and slot usage below. See the [Commerce drop-ins overview](https://experienceleague.adobe.com/developer/commerce/storefront/dropins/all/introduction/) for how slots and initializers work.
To enable AEM Assets with standalone drop-ins, you'll need to implement the configuration system that drop-ins expect. See [Storefront configuration](https://experienceleague.adobe.com/developer/commerce/storefront/setup/configuration/commerce-configuration/) for full configuration details.
### Minimal configuration
Set `commerce-assets-enabled: true` in a `public.default` config (JSON or config service). Align endpoints/headers to your environment as needed.
> **Reference implementation** See the https://github.com/hlxsites/aem-boilerplate-commerce for complete examples.
### Minimal integration steps
1. Install `@dropins/tools`.
1. Add the `assets.js` file from the boilerplate to your project.
1. Import `tryRenderAemAssetsImage` into your drop-in components.
1. In each image slot, call `tryRenderAemAssetsImage(ctx, { alias:
⏳ Loading...
```
Example usage:
```javascript
// Display event data
const $data = document.getElementById('data');
events.on(' = (
callback: (next: unknown, state: State) => P
) => void;
export enum AgreementMode {
MANUAL = 'manual',
AUTO = 'auto',
}
. . .
Agreements?: SlotProps<{
appendAgreement: SlotMethod<{
name: string;
mode: AgreementMode;
text?: string;
translationId?: string;
}>;
}>;
. . .
```
- The `appendAgreement` configuration is a callback function which accepts the following attributes to configure an agreement:
- **`name`**
The agreement identifier
- **`mode`**
Specifies the mode how the checkbox should appear:
- 'manual': the user is required to manually check and accept the conditions to place an order
- 'auto': the checkbox will appear checked by default, conditions are automatically accepted upon checkout
- **`text`**
Optional attribute that contains directly the text to show, and it accepts HTML with links to a specific page in EDS. In case this attribute is not provided, the `translationId` must to. Finally, if both `text` and `translationId` are provided, the `text` has more preference and its content will be shown
- **`translationId`**
- This attribute references the translation label that contains the checkbox text. It first looks in the placeholders/checkout.json file for this label identifier, otherwise it looks up the entry in the dictionary. This attribute must be provided if it is not. As a reminder, if both `text` and `translationId` are provided, the `text` has more preference and its content will be shown.
## Example 1: Render a custom agreement
The following example renders the `TermsAndConditions` container on the checkout page, displaying a custom agreement that directly includes the label to show along with the link to the EDS page, within the element having the class `.checkout__terms-and-conditions`:
```ts
// Checkout Dropin
const $termsAndConditions = checkoutFragment.querySelector(
'.checkout__terms-and-conditions',
);
CheckoutProvider.render(TermsAndConditions, {
slots: {
Agreements: (ctx) => {
ctx.appendAgreement(() => ({
name: 'custom',
mode: 'auto',
text: 'Custom terms and conditions [Terms & Conditions](/en/terms-and-conditions).',
}));
},
},
})($termsAndConditions),
```
## Example 2: Render three different agreements using the translations configured in EDS
The following example renders the `TermsAndConditions` container on the checkout page. The container displays three different agreements using the labels from the translations in the **`placeholders`** sheet, within the element with the class `.checkout__terms-and-conditions`:
```ts
// Checkout Dropin
const $termsAndConditions = checkoutFragment.querySelector(
'.checkout__terms-and-conditions',
);
CheckoutProvider.render(TermsAndConditions, {
slots: {
Agreements: (ctx) => {
ctx.appendAgreement(() => ({
name: 'default',
mode: 'auto',
translationId: 'Checkout.TermsAndConditions.label',
}));
ctx.appendAgreement(() => ({
name: 'terms',
mode: 'manual',
translationId: 'Checkout.TermsAndConditions.terms_label',
}));
ctx.appendAgreement(() => ({
name: 'privacy',
mode: 'auto',
translationId: 'Checkout.TermsAndConditions.privacy_label',
}));
},
},
})($termsAndConditions),
```
## Example 3: Render the available agreements configured in the Admin Panel
The following example renders the `TermsAndConditions` container on a checkout page, displaying the available agreements configured in the Admin Panel retrieved using the `getCheckoutAgreements()` API function, in the element with the class `.checkout__terms-and-conditions`:
```ts
// Checkout Dropin
const $termsAndConditions = checkoutFragment.querySelector(
'.checkout__terms-and-conditions',
);
CheckoutProvider.render(TermsAndConditions, {
slots: {
Agreements: async (ctx) => {
const agreements = await checkoutApi.getCheckoutAgreements();
agreements.forEach((agreement) => {
ctx.appendAgreement(() => ({
name: agreement.name,
mode: agreement.mode,
text: agreement.text,
}));
});
},
},
})($termsAndConditions),
```
---
# Checkout Dictionary
The **Checkout dictionary** contains all user-facing text, labels, and messages displayed by this drop-in. Customize the dictionary to:
- **Localize** the drop-in for different languages and regions
- **Customize** labels and messages to match your brand voice
- **Override** default text without modifying source code for the drop-in
Dictionaries use the **i18n (internationalization)** pattern, where each text string is identified by a unique key path.
Version: 3.3.0
## How to customize
Override dictionary values during drop-in initialization. The drop-in deep-merges your custom values with the defaults.
```javascript
await initialize({
langDefinitions: {
en_US: {
"Checkout": {
"AddressValidation": {
"title": "My Custom Title",
"subtitle": "My Custom Title"
}
}
}
}
});
```
You only need to include the keys you want to change. For multi-language support and advanced patterns, see the [Dictionary customization guide](https://experienceleague.adobe.com/developer/commerce/storefront/dropins/all/dictionaries/).
## Default keys and values
Below are the default English (`en_US`) strings provided by the **Checkout** drop-in:
```json title="en_US.json"
{
"Checkout": {
"AddressValidation": {
"title": "Verify your address",
"subtitle": "To ensure accurate delivery, we suggest the changes highlighted below. Please choose which address you would like to use. If neither option is correct, edit your address.",
"suggestedAddress": "Suggested Address",
"originalAddress": "Original Address"
},
"BillToShippingAddress": {
"cartSyncError": "We were unable to save your changes. Please try again later.",
"title": "Bill to shipping address"
},
"EmptyCart": {
"button": "Start shopping",
"title": "Your cart is empty"
},
"EstimateShipping": {
"estimated": "Estimated Shipping",
"freeShipping": "Free",
"label": "Shipping",
"taxToBeDetermined": "TBD",
"withoutTaxes": "Excluding taxes",
"withTaxes": "Including taxes"
},
"LoginForm": {
"account": "Already have an account?",
"ariaLabel": "Email",
"emailExists": {
"alreadyHaveAccount": "It looks like you already have an account.",
"forFasterCheckout": "for a faster checkout.",
"signInButton": "Sign in"
},
"floatingLabel": "Email *",
"invalidEmailError": "Please enter a valid email address.",
"missingEmailError": "Enter an email address.",
"cartSyncError": "We were unable to save your changes. Please try again later.",
"placeholder": "Enter your email address",
"signIn": "Sign In",
"signOut": "Sign Out",
"switch": "Do you want to switch account?",
"title": "Contact details"
},
"MergedCartBanner": {
"items": {
"many": "{{count}} items from a previous session were added to your cart. Please review your new subtotal.",
"one": "1 item from a previous session was added to your cart. Please review your new subtotal."
}
},
"OutOfStock": {
"actions": {
"removeOutOfStock": "Remove out of stock items",
"reviewCart": "Review cart"
},
"alert": "Out of stock!",
"lowInventory": {
"many": "Only {{count}} left!",
"one": "Last item!"
},
"message": "The following items are out of stock:",
"title": "Your cart contains items that are out of stock"
},
"PaymentMethods": {
"cartSyncError": "We were unable to save your changes. Please try again later.",
"emptyState": "No payment methods available",
"title": "Payment"
},
"PaymentOnAccount": {
"referenceNumberLabel": "Custom Reference Number",
"referenceNumberPlaceholder": "Enter custom reference number",
"referenceNumberHint": "",
"availableCreditLabel": "Available Credit",
"exceedLimitWarning": "The credit limit is {{creditLimit}}. It will be exceeded by {{exceededAmount}} with this order.",
"exceedLimitWarningPrefix": "The credit limit is",
"exceedLimitWarningMiddle": ". It will be exceeded by",
"exceedLimitWarningSuffix": "with this order.",
"exceedLimitError": "Payment On Account cannot be used for this order because your order amount exceeds your credit amount."
},
"PurchaseOrder": {
"missingReferenceNumberError": "Reference number is required",
"referenceNumberHint": "",
"referenceNumberLabel": "Custom Reference Number",
"referenceNumberPlaceholder": "Enter custom reference number"
},
"PlaceOrder": {
"button": "Place Order"
},
"ServerError": {
"button": "Try again",
"contactSupport": "If you continue to have issues, please contact support.",
"title": "We were unable to process your order",
"unexpected": "An unexpected error occurred while processing your order. Please try again later.",
"permissionDenied": "You do not have permission to complete checkout. Please contact your administrator for assistance."
},
"Quote": {
"permissionDenied": "You do not have permission to checkout with this quote.",
"dataError": "We were unable to retrieve the quote data. Please try again later."
},
"ShippingMethods": {
"cartSyncError": "We were unable to save your changes. Please try again later.",
"emptyState": "This order can't be shipped to the address provided. Please review the address details you entered and make sure they're correct.",
"title": "Shipping options",
"accessibleOptionLabel": "Shipping option"
},
"Summary": {
"Edit": "Edit",
"heading": "Your Cart ({count})"
},
"Addresses": {
"billToNewAddress": "Bill to new address",
"shippingAddressTitle": "Shipping address",
"billingAddressTitle": "Billing address"
},
"TermsAndConditions": {
"error": "Please accept the Terms and Conditions to continue.",
"label": "I have read, understand, and accept our [Terms of Use, Terms of Sales, Privacy Policy, and Return Policy](https://www.adobe.com/legal/terms.html)."
},
"title": "Checkout"
}
}
```
---
# Error handling
Errors that occur during the checkout process must be caught and logged with clear context for quick resolution. This prevents unnecessary error propagation and provides better user experience and debugging capabilities. The checkout drop-in component must implement an error handling mechanism to improve observability and debugging capabilities.
It is critical to resolve errors promptly to avoid inconsistent states and clearly inform users about what occurred. This prevents data inconsistencies between the local application and the backend, which could result in incorrect orders.
## Generic strategy
Most issues arise from API call errors. The system must focus on how these errors propagate from API calls to the user interface and how they are presented to users in a friendly manner across different scenarios. Each container requires a centralized error handling system that captures errors as they occur, enabling control over error management and decision-making about subsequent actions.
## "Optimistic" UI updates with rollback pattern
The system implements optimistic UI updates with a rollback mechanism. This technique improves user experience by making the application feel more responsive to user interactions.
In an optimistic update, the UI behaves as though a change was successfully completed before receiving confirmation from the backend that it actually occurred. The system optimistically assumes it will eventually receive confirmation rather than an error. This approach allows for a more responsive user experience.
When a user performs an action that changes the state, the system immediately sends the information to the backend and optimistically updates the user interface (UI) to reflect the change. This process is called "optimistic" because the system updates the UI with the expectation that the backend will accept the state change. If the system waited for backend confirmation before updating the UI, the delay would negatively impact the user experience.
If the backend returns an error, the system performs a rollback to revert to the previous state (when possible) and displays an error message such as an inline alert. Additionally, the containers provide callback functions that merchants can use in the integration layer to display custom error messages.
---
# Event handling
The checkout drop-in component implements an event-driven architecture that uses the `@adobe-commerce/event-bus` package to facilitate communication between components. This event system enables containers to respond to application state changes, maintain loose coupling between components, and keep their state synchronized with the cart.
## Event system architecture
The system uses a publish-subscribe pattern where containers can:
1. Subscribe to specific events using `events.on()`
2. Emit events using `events.emit()`
3. Unsubscribe using `subscription.off()`
## Events declaration
The following code snippet shows the contracts that define the relationship between each event and its payload:
```js title='event-bus.d.ts'
declare module '@adobe-commerce/event-bus' {
interface Events {
'cart/initialized': CartModel | null;
'cart/updated': CartModel | null;
'cart/reset': void;
'cart/merged': { oldCartItems: any[] };
'checkout/initialized': CheckoutData | null;
'checkout/updated': CheckoutData | null;
'checkout/values': ValuesModel;
'shipping/estimate': ShippingEstimate;
authenticated: boolean;
error: { source: string; type: string; error: Error };
}
interface Cart extends CartModel {}
}
```
## Event subscription
If a component wants to listen for an event fired in another component, the component must subscribe to that event.
### Subscription configuration
To subscribe to an event, you must provide the following information:
1. The name of the event.
2. The event handler, which is a callback function to be executed when a new event is fired (the payload is passed as a parameter).
3. Event subscriptions can include an additional configuration parameter:
- `eager: true`: The handler executes immediately if the event has been emitted previously.
- `eager: false`: The handler only responds to future emissions of the event.
```js
const subscription = events.on('event-name', handler, { eager: true/false });
```
### Events subscribed by containers
The following list shows the events subscribed by the checkout drop-in component containers:
#### (i) External
When the event is fired by external components:
- `authenticated`: Indicates that a user has authenticated.
- `cart/initialized`: Indicates that a new cart has been created and initialized.
- `cart/reset`: Indicates that the order has been placed and the cart is not active any more.
- `cart/updated`: Indicates that the cart data has been added or updated.
- `cart/merged`: Indicates that a guest cart (created during the anonymous checkout) has been merged with a customer cart (recovered from a previous checkout process).
- `cart/data`: Provides cart data.
- `locale`: Indicates that the locale has been changed.
#### (ii) Internal
When the event is fired by internal checkout drop-in components:
- `checkout/initialized`: Indicates that the checkout drop-in has been initialized with cart data.
- `checkout/updated`: Indicates that the checkout data has been added or updated.
- `shipping/estimate`: Provides shipping estimate based on shipping method selected within a shipping address.
### Example
Listen to the checkout initialization event:
```js
events.on('checkout/initialized', (data) => {
// Handle checkout data
});
```
## Event emission
Each component can emit an event if it wants to share information with other components or drop-ins.
### Emission configuration
To emit an event, you must provide the following information:
1. The name of the event
2. The payload containing the data to be shared
```js
events.emit('event-name', payload);
```
### Events emitted by containers
The following list shows the events emitted by the checkout drop-in component containers:
- `checkout/initialized`: Indicates that the checkout drop-in has been initialized with cart data.
- `checkout/updated`: Indicates that the checkout data has been added or updated.
- `checkout/values`: Provides the local state values.
- `shipping/estimate`: Provides shipping estimate based on shipping method selected within a shipping address.
- `error`: Indicates that the system has received a network error type.
### Example
Emit the checkout values event:
```js
events.emit('checkout/values', data);
```
---
# Checkout Data & Events
The **Checkout** drop-in uses the [event bus](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/events/) to emit and listen to events for communication between drop-ins and external integrations.
Version: 3.3.0
## Events reference
{/* EVENTS_TABLE_START */}
| Event | Direction | Description |
|-------|-----------|-------------|
| [checkout/values](#checkoutvalues-emits) | Emits | Emitted when form or configuration values change. |
| [cart/data](#cartdata-listens) | Listens | Fired by Cart (`cart`) when data is available or changes. |
| [cart/initialized](#cartinitialized-listens) | Listens | Fired by Cart (`cart`) when the component completes initialization. |
| [cart/merged](#cartmerged-listens) | Listens | Fired by Cart (`cart`) when data is merged. |
| [cart/reset](#cartreset-listens) | Listens | Fired by Cart (`cart`) when the component state is reset. |
| [quote-management/quote-data](#quote-managementquote-data-listens) | Listens | Fired by Quote-management (`quote-management`) when a specific condition or state change occurs. |
| [checkout/error](#checkouterror-emits-and-listens) | Emits and listens | Triggered when an error occurs. |
| [checkout/initialized](#checkoutinitialized-emits-and-listens) | Emits and listens | Triggered when the component completes initialization. |
| [checkout/updated](#checkoutupdated-emits-and-listens) | Emits and listens | Triggered when the component state is updated. |
| [shipping/estimate](#shippingestimate-emits-and-listens) | Emits and listens | Triggered when an estimate is calculated. |
| [authenticated](#authenticated-listens) | Listens | Fired by Auth (`auth`) when the user authentication state changes. |
{/* EVENTS_TABLE_END */}
## Event details
The following sections provide detailed information about each event, including its direction, event payload, and usage examples.
### `cart/data` (listens)
Triggered when cart data is available or changes. This event provides the current cart state including items, totals, and addresses.
#### Event payload
```typescript
Cart | null
```
See [`Cart`](#cart) for full type definition.
#### Example
```js
events.on('cart/data', (payload) => {
console.log('cart/data event received:', payload);
// Add your custom logic here
});
```
### `cart/initialized` (listens)
Fired by Cart (`cart`) when the component completes initialization.
#### Event payload
```typescript
CartModel | null
```
See [`CartModel`](#cartmodel) for full type definition.
#### Example
```js
events.on('cart/initialized', (payload) => {
console.log('cart/initialized event received:', payload);
// Add your custom logic here
});
```
### `cart/merged` (listens)
Fired by Cart (`cart`) when data is merged.
#### Event payload
```typescript
{ oldCartItems: any[] }
```
#### Example
```js
events.on('cart/merged', (payload) => {
console.log('cart/merged event received:', payload);
// Add your custom logic here
});
```
### `cart/reset` (listens)
Fired by Cart (`cart`) when the component state is reset.
#### Event payload
#### Example
```js
events.on('cart/reset', (payload) => {
console.log('cart/reset event received:', payload);
// Add your custom logic here
});
```
### `checkout/error` (emits and listens)
Triggered when an error occurs during checkout operations such as address validation, payment processing, or order placement.
#### Event payload
```typescript
CheckoutError
```
See [`CheckoutError`](#checkouterror) for full type definition.
#### Example
```js
events.on('checkout/error', (payload) => {
console.log('checkout/error event received:', payload);
// Add your custom logic here
});
```
### `checkout/initialized` (emits and listens)
Triggered when the checkout component completes initialization with either cart or negotiable quote data. This indicates the checkout is ready for user interaction.
#### Event payload
```typescript
Cart | NegotiableQuote | null
```
See [`Cart`](#cart), [`NegotiableQuote`](#negotiablequote) for full type definitions.
#### Example
```js
events.on('checkout/initialized', (payload) => {
console.log('checkout/initialized event received:', payload);
// Add your custom logic here
});
```
### `checkout/updated` (emits and listens)
Triggered when the checkout state is updated, such as when shipping methods are selected, addresses are entered, or payment methods are chosen.
#### Event payload
```typescript
Cart | NegotiableQuote | null
```
See [`Cart`](#cart), [`NegotiableQuote`](#negotiablequote) for full type definitions.
#### Example
```js
events.on('checkout/updated', (payload) => {
console.log('checkout/updated event received:', payload);
// Add your custom logic here
});
```
### `checkout/values` (emits)
Emitted when form or configuration values change in the checkout. This event is useful for tracking user input, validating form fields, or synchronizing state across components.
#### Event payload
```typescript
ValuesModel
```
See [`ValuesModel`](#valuesmodel) for full type definition.
#### Example
```js
events.on('checkout/values', (payload) => {
console.log('checkout/values event received:', payload);
// Add your custom logic here
});
```
### `quote-management/quote-data` (listens)
Fired by Quote-management (`quote-management`) when a specific condition or state change occurs.
#### Event payload
```typescript
{
quote: NegotiableQuoteModel;
permissions: {
requestQuote: boolean;
editQuote: boolean;
deleteQuote: boolean;
checkoutQuote: boolean;
}
}
```
See [`NegotiableQuoteModel`](#negotiablequotemodel) for full type definition.
#### Example
```js
events.on('quote-management/quote-data', (payload) => {
console.log('quote-management/quote-data event received:', payload);
// Add your custom logic here
});
```
### `shipping/estimate` (emits and listens)
Triggered when shipping cost estimates are calculated for a given address. This event provides both the address used for estimation and the resulting shipping method with its cost.
#### Event payload
```typescript
ShippingEstimate
```
See [`ShippingEstimate`](#shippingestimate) for full type definition.
#### Example
```js
events.on('shipping/estimate', (payload) => {
console.log('shipping/estimate event received:', payload);
// Add your custom logic here
});
```
### `authenticated` (listens)
Fired by Auth (`auth`) when the user authentication state changes. Checkout listens to this event to update the `LoginForm` display — hiding the sign-in prompt when a user is authenticated and restoring it when they sign out.
#### Event payload
```typescript
boolean
```
The payload is `true` if the user is authenticated, `false` otherwise.
#### Example
```js
events.on('authenticated', (isAuthenticated) => {
console.log('authenticated event received:', isAuthenticated);
// Add your custom logic here
});
```
## Data Models
The following data models are used in event payloads for this drop-in.
### Cart
The `Cart` interface represents a shopping cart including items, pricing, addresses, and shipping/payment methods.
Used in: [`cart/data`](#cartdata-listens), [`checkout/initialized`](#checkoutinitialized-emits-and-listens), [`checkout/updated`](#checkoutupdated-emits-and-listens).
```ts
interface Cart {
type: 'cart';
availablePaymentMethods?: PaymentMethod[];
billingAddress?: CartAddress;
email?: string;
id: string;
isEmpty: boolean;
isGuest: boolean;
isVirtual: boolean;
selectedPaymentMethod?: PaymentMethod;
shippingAddresses: CartShippingAddress[];
}
```
### CartModel
Used in: [`cart/initialized`](#cartinitialized-listens).
```ts
interface CartModel {
id: string;
totalQuantity: number;
errors?: ItemError[];
items: Item[];
miniCartMaxItems: Item[];
total: {
includingTax: Price;
excludingTax: Price;
};
discount?: Price;
subtotal: {
excludingTax: Price;
includingTax: Price;
includingDiscountOnly: Price;
};
appliedTaxes: TotalPriceModifier[];
totalTax?: Price;
appliedDiscounts: TotalPriceModifier[];
shipping?: Price;
isVirtual?: boolean;
addresses: {
shipping?: {
countryCode: string;
zipCode?: string;
regionCode?: string;
}[];
};
isGuestCart?: boolean;
}
```
### CheckoutError
Used in: [`checkout/error`](#checkouterror-emits-and-listens).
```ts
interface CheckoutError {
/**
* The primary, user-friendly error message. This should be safe to display
* directly in the UI.
* @example "Your card was declined."
*/
message: string;
/**
* An optional, unique error code for programmatic handling. This allows the
* ServerError component to show specific icons, links, or actions.
* @example "payment_intent_declined"
*/
code?: string;
}
```
### NegotiableQuote
The `NegotiableQuote` interface represents a B2B negotiable quote, which functions similarly to a cart but includes additional negotiation features like price adjustments and approval workflows.
Used in: [`checkout/initialized`](#checkoutinitialized-emits-and-listens), [`checkout/updated`](#checkoutupdated-emits-and-listens).
```ts
interface NegotiableQuote {
type: 'quote';
availablePaymentMethods?: PaymentMethod[];
billingAddress?: Address;
email?: string;
isEmpty: boolean;
isVirtual: boolean;
name: string;
selectedPaymentMethod?: PaymentMethod;
shippingAddresses: ShippingAddress[];
status: NegotiableQuoteStatus;
uid: string;
}
```
### NegotiableQuoteModel
Used in: [`quote-management/quote-data`](#quote-managementquote-data-listens).
```ts
interface NegotiableQuoteModel {
uid: string;
name: string;
createdAt: string;
salesRepName: string;
expirationDate: string;
updatedAt: string;
status: NegotiableQuoteStatus;
buyer: {
firstname: string;
lastname: string;
};
templateName?: string;
comments?: {
uid: string;
createdAt: string;
author: {
firstname: string;
lastname: string;
};
text: string;
attachments?: {
name: string;
url: string;
}[];
}[];
history?: NegotiableQuoteHistoryEntry[];
prices: {
appliedDiscounts?: Discount[];
appliedTaxes?: Tax[];
discount?: Currency;
grandTotal?: Currency;
grandTotalExcludingTax?: Currency;
shippingExcludingTax?: Currency;
shippingIncludingTax?: Currency;
subtotalExcludingTax?: Currency;
subtotalIncludingTax?: Currency;
subtotalWithDiscountExcludingTax?: Currency;
totalTax?: Currency;
};
items: NegotiableQuoteCartItem[];
shippingAddresses?: ShippingAddress[];
canCheckout: boolean;
canSendForReview: boolean;
}
```
### ShippingEstimate
Used in: [`shipping/estimate`](#shippingestimate-emits-and-listens).
```ts
interface ShippingEstimate {
address: PartialShippingAddress;
availableShippingMethods?: ShippingMethod[];
shippingMethod: ShippingEstimateShippingMethod | null;
success?: boolean;
}
```
### ValuesModel
Used in: [`checkout/values`](#checkoutvalues-emits).
```ts
interface ValuesModel {
email: string;
isBillToShipping: boolean | undefined;
selectedPaymentMethod: PaymentMethod | null;
selectedShippingMethod: ShippingMethod | null;
}
```
---
# Extending the checkout drop-in component
The checkout drop-in component follows the Adobe Commerce out-of-process extensibility (OOPE) pattern, which requires components to be flexible and extensible. When the checkout drop-in component lacks a specific feature, it provides mechanisms that allow developers to easily expand and customize its functionality.
## GraphQL API
To extend the data payload of the drop-in, developers must use the GraphQL Extensibility API. This API allows developers to extend existing GraphQL operations to meet additional data requirements without increasing code complexity or negatively impacting performance. The API provides a flexible and efficient way to customize GraphQL fragments by integrating build-time modifications into the storefront's development pipeline.
GraphQL fragments are reusable pieces of GraphQL that developers can use to extend or customize the API for a drop-in component. Drop-in components expose the list of fragments that can be extended in the `fragments.ts` file. If the drop-in component does not expose these fragments, the build process fails when you install the application because it cannot locate the fragment you want to extend.
The checkout drop-in component exposes the following fragments:
```js title='fragments.ts'
export {
BILLING_CART_ADDRESS_FRAGMENT,
SHIPPING_CART_ADDRESS_FRAGMENT,
} from '@/checkout/api/graphql/CartAddressFragment.graphql';
export { CHECKOUT_DATA_FRAGMENT } from '@/checkout/api/graphql/CheckoutDataFragment.graphql';
export { CUSTOMER_FRAGMENT } from '@/checkout/api/graphql/CustomerFragment.graphql';
export {
NEGOTIABLE_QUOTE_BILLING_ADDRESS_FRAGMENT,
NEGOTIABLE_QUOTE_SHIPPING_ADDRESS_FRAGMENT,
} from '@/checkout/api/graphql/NegotiableQuoteAddressFragment.graphql';
export { NEGOTIABLE_QUOTE_FRAGMENT } from '@/checkout/api/graphql/NegotiableQuoteFragment.graphql';
export {
AVAILABLE_PAYMENT_METHOD_FRAGMENT,
SELECTED_PAYMENT_METHOD_FRAGMENT,
} from '@/checkout/api/graphql/PaymentMethodFragment.graphql';
export {
AVAILABLE_SHIPPING_METHOD_FRAGMENT,
ESTIMATE_SHIPPING_METHOD_FRAGMENT,
SELECTED_SHIPPING_METHOD_FRAGMENT,
} from '@/checkout/api/graphql/ShippingMethodFragment.graphql';
```
The fragment names above match the symbols exported from `@dropins/storefront-checkout` (for example, the package `fragments` entry). The `@/checkout/...` import paths reflect the checkout drop-in source layout; in your storefront, point `build.mjs` at the same fragment names using whatever `fragments.ts` path and re-exports your scaffold provides.
The `ESTIMATE_SHIPPING_METHOD_FRAGMENT` applies to the `estimateShippingMethods` mutation. Pair it with the `EstimateShippingModel` initializer model when you need to transform extended fields from the shipping estimate response. `AVAILABLE_SHIPPING_METHOD_FRAGMENT` and `SELECTED_SHIPPING_METHOD_FRAGMENT` cover cart shipping methods on the main checkout flow.
### Extend or customize a fragment
To make GraphQL fragments extensible in the drop-in component, you must first update the GraphQL fragment that the drop-in uses to request the additional field. You accomplish this by modifying the `build.mjs` script located at the root of your storefront project.
The `build.mjs` script automatically generates a new GraphQL query for the checkout drop-in component when you run the install command. This generated query includes the additional data that you specified in your fragment extensions.
#### Example 1: Adding new information
The merchant wants to extend the customer information by adding the gender and date of birth data.
```js title='build.mjs'
/* eslint-disable import/no-extraneous-dependencies */
overrideGQLOperations([
{
npm: '@dropins/storefront-checkout',
operations: [
`
fragment CUSTOMER_FRAGMENT on Customer {
gender
date_of_birth
}
`,
],
},
]);
```
After extending the API, you must extend the models and transformers during the initialization phase if data transformation is required. You accomplish this by modifying the `/scripts/initializers/checkout.js` script.
```js title='/scripts/initializers/checkout.js'
// Initialize checkout
await initializeDropin(async () => {
// Register the checkout component with models extensibility
const models = {
CustomerModel: {
transformer: (data) => ({
gender: ((gender) => {
switch (gender) {
case 1:
return "Male";
case 2:
return "Female";
case 3:
return "Not Specified";
default:
return "";
}
})(data?.gender),
dateOfBirth: data?.date_of_birth,
}),
},
};
// Register initializers
return initializers.mountImmediately(initialize, {
models
});
})();
```
#### Example 2: Removing information
The merchant wants to remove the selected payment method data.
```js title='build.mjs'
/* eslint-disable import/no-extraneous-dependencies */
overrideGQLOperations([
{
npm: '@dropins/storefront-checkout',
skipFragments: ['SELECTED_PAYMENT_METHOD_FRAGMENT'],
operations: [],
},
]);
```
> **Extending fragments** If the `build.mjs` script references a fragment that the drop-in component does not expose, the application build process fails.
> **Extending drop-in components** See the [GraphQL Extensibility API](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/graphql/) and [Extending drop-in components](https://experienceleague.adobe.com/developer/commerce/storefront/dropins/all/extending/) documentation to learn more about how to extend the API for a drop-in component.
---
# Checkout Functions
The Checkout drop-in provides API functions that enable you to programmatically control behavior, fetch data, and integrate with Adobe Commerce backend services.
Version: 3.3.0
| Function | Description |
| --- | --- |
| [`authenticateCustomer`](#authenticatecustomer) | API function for the drop-in. |
| [`estimateShippingMethods`](#estimateshippingmethods) | Calls the `estimateShippingMethods` mutation. |
| [`getCart`](#getcart) | Retrieves the current cart's checkout data from Adobe Commerce. |
| [`getCheckoutAgreements`](#getcheckoutagreements) | Returns a list with the available checkout agreements. |
| [`getCompanyCredit`](#getcompanycredit) | API function for the drop-in. |
| [`getCustomer`](#getcustomer) | API function for the drop-in. |
| [`getNegotiableQuote`](#getnegotiablequote) | Retrieves a negotiable quote for B2B customers. |
| [`getStoreConfig`](#getstoreconfig) | The `storeConfig` query defines information about a store's configuration. |
| [`getStoreConfigCache`](#getstoreconfigcache) | API function for the drop-in. |
| [`initializeCheckout`](#initializecheckout) | API function for the drop-in. |
| [`isEmailAvailable`](#isemailavailable) | Calls the `isEmailAvailable` query. |
| [`resetCheckout`](#resetcheckout) | API function for the drop-in. |
| [`setBillingAddress`](#setbillingaddress) | Calls the `setBillingAddressOnCart` mutation. |
| [`setGuestEmailOnCart`](#setguestemailoncart) | Calls the `setGuestEmailOnCart` mutation. |
| [`setPaymentMethod`](#setpaymentmethod) | Calls the `setPaymentMethodOnCart` mutation. |
| [`setShippingAddress`](#setshippingaddress) | Calls the `setShippingAddressesOnCart` mutation. |
| [`setShippingMethods`](#setshippingmethods) | Sets one or more shipping methods on the cart. Also exported as `setShippingMethodsOnCart`. |
| [`synchronizeCheckout`](#synchronizecheckout) | API function for the drop-in. |
## authenticateCustomer
### Signature
```typescript
function authenticateCustomer(authenticated = false): Promise