anonymousCartSignInMode for automatic merging and implementing custom merge logic for more complex scenarios.Use anonymousCartSignInMode
CustomerSignIn and MyCustomerSignIn types provide the anonymousCartSignInMode field to control how an anonymous Cart is handled when its owner signs in. This is the most direct method to manage Cart transitions.The available modes are as follows:
MergeWithExistingCustomerCart: merges the Line Items of an anonymous Cart with the Customer's existing active Cart. If the Customer has an active Cart with a shipping address, tax rates are recalculated based on that address before merging. If the Customer has no active Cart, then the anonymous Cart becomes their new active Cart.UseAsNewActiveCustomerCart: the anonymous Cart becomes the Customer's new active Cart. Tax recalculation depends on the Customer's previous active Cart:- No previous active Cart: taxes are recalculated.
- Previous active Cart without shipping address: taxes are not recalculated.
- Previous active Cart with shipping address: taxes are recalculated only if the anonymous Cart lacks a shipping address.
How merge modes work
The shipping address is critical because it determines whether tax rates need to be recalculated during the merge process. This is important because these changes can directly impact the prices your Customers see. The presence or absence of a shipping address doesn't prevent items from being merged—it only affects tax recalculation.
MergeWithExistingCustomerCart mode will recalculate prices using the New York tax rate before merging the items. This ensures the Customer sees accurate pricing based on their delivery location immediately after sign-in.Line Item matching logic
When merging Carts, the system uses a two-step process to determine which Line Items to merge:
Choosing the right merge mode
Use the following table to determine which merge mode fits your business requirements:
| Aspect | MergeWithExistingCustomerCart | UseAsNewActiveCustomerCart |
|---|---|---|
| Result | Combines Line Items from both Carts into one | Replaces the Customer's Cart with the anonymous Cart |
| Use when | Customers should keep items from both sessions | Anonymous session represents the Customer's current intent |
| Tax recalculation | Yes, if active Cart has a shipping address | Conditional: Yes if no active Cart; No if active Cart lacks shipping address; Yes if active Cart has shipping address but only if anonymous Cart lacks one |
| Previous active Cart | Updated with merged items | Disassociated but not deleted |
Example - Comparison of Cart merging strategies
The following example demonstrates both merge modes in action, showing how each mode affects the final Cart contents when a Customer signs in.
anon-cart-1) contains one Zen Speaker X item and the Customer's existing active cart (cust-cart-A) contains one Zen Tablet Y item.import { apiRoot } from '../../client'; // Assuming apiRoot is initialized and exported here
import {
CustomerSignInResult,
AnonymousCartSignInMode,
} from '@commercetools/platform-sdk'; // Import necessary types
async function simulateLoginAndCartHandlingInStore(
storeKey: string,
email: string,
password: string,
anonymousCartId: string,
mode: AnonymousCartSignInMode
): Promise<CustomerSignInResult> {
try {
const requestBody = {
email: email,
password: password,
anonymousCartId: anonymousCartId, // This is always a string as per function signature
anonymousCartSignInMode: mode,
};
console.log(
`\n--- Attempting in-store login for ${email} with anonymous cart ${anonymousCartId} using mode: ${mode} for store "${storeKey}" ---`
);
const loginResponse = await apiRoot
.inStoreKeyWithStoreKeyValue({ storeKey: storeKey })
.login()
.post({
body: requestBody,
})
.execute();
const signInResult: CustomerSignInResult = loginResponse.body; // Explicitly type the response body
console.log(
`In-Store Login successful. Customer: ${signInResult.customer.email}`
);
console.log(`Active Cart ID: ${signInResult.cart?.id}`);
if (signInResult.cart?.lineItems) {
console.log('Cart Line Items:');
signInResult.cart.lineItems.forEach((item) => {
// Ensure name is accessed safely, as it's a LocalizedString
console.log(` - ${item.name['en-US']} (Quantity: ${item.quantity})`);
});
}
return signInResult;
} catch (error: any) {
// Type 'any' for caught error for now because stricter typing for HTTP errors can be complex
console.error(
`In-Store Login failed with mode ${mode} for store "${storeKey}":`,
error
);
throw error;
}
}
(async () => {
const davidEmail = 'david.shopper@example.com';
const davidPassword = 'DavidSecurePass!';
const anonCartId = 'anon-cart-1'; // Anonymous Cart: 1x Zen Speaker X
const ELECTRONICS_HIGH_TECH_STORE_KEY = 'electronics-high-tech-store';
// David's existing active Cart: 1x Zen Tablet Y
// Scenario 1: MergeWithExistingCustomerCart
// Result: Active Cart contains 1x Zen Speaker X AND 1x Zen Tablet Y
try {
await simulateLoginAndCartHandlingInStore(
ELECTRONICS_HIGH_TECH_STORE_KEY,
davidEmail,
davidPassword,
anonCartId,
'MergeWithExistingCustomerCart'
);
} catch (e) {
console.error(
`Failed to simulate MergeWithExistingCustomerCart scenario for store ${ELECTRONICS_HIGH_TECH_STORE_KEY}.`
);
/* handle error */
}
// Scenario 2: UseAsNewActiveCustomerCart
// Result: Active Cart contains ONLY 1x Zen Speaker X (previous Cart disassociated)
try {
await simulateLoginAndCartHandlingInStore(
ELECTRONICS_HIGH_TECH_STORE_KEY,
davidEmail,
davidPassword,
anonCartId,
'UseAsNewActiveCustomerCart'
);
} catch (e) {
console.error(
`Failed to simulate UseAsNewActiveCustomerCart scenario for store ${ELECTRONICS_HIGH_TECH_STORE_KEY}.`
);
/* handle error */
}
})();
Implement custom Cart merging logic
While the built-in merge modes handle most scenarios and use cases, some business requirements need more control. In these cases, you can implement custom Cart merging logic for specific needs.
When to use custom Cart merging logic
Consider implementing custom Cart merging logic if you need to:
- Apply complex business rules: merge only items from specific categories or add duplicate products as separate Line Items instead of increasing quantity.
- Allow Customer selection: present both Carts to the user and let them choose which items to keep.
- Merge custom data: handle complex merging of custom fields on Carts or Line Items.
- Custom conflict resolution: go beyond the default behavior for handling Cart conflicts.
How to implement custom Cart merging logic
A custom Cart merging process involves these steps:
-
Sign in without
anonymousCartId: call the login endpoint with only credentials to prevent automatic merging and retrieve thecustomerIdandactiveCart. -
Fetch both Carts: retrieve the anonymous Cart ID from the client (for example, a cookie) and fetch both Carts to compare their contents.
-
Apply your merge logic: use Cart update actions (
addLineItem,changeLineItemQuantity,removeLineItem,setCustomField) to build the final merged Cart according to your business rules. -
Clean up: optionally delete the anonymous Cart after successful merging.
Custom Cart merging offers maximum flexibility but introduces complexity in development, testing, and maintenance. Use it only when the built-in modes don't meet your requirements.
Key takeaways
- Use
anonymousCartSignInModeon sign-in to control how anonymous Carts are handled automatically. - Choose
MergeWithExistingCustomerCartwhen Customers should keep items from both their anonymous and logged-in sessions. - Choose
UseAsNewActiveCustomerCartwhen the anonymous session represents the Customer's current intent and should replace their existing Cart. - Tax recalculation during merge depends on whether the Customer's active Cart has a shipping address—this impacts the final prices Customers see.
- Line Items are matched by properties (product, variant, channels) not keys, so identical items with different keys will merge.
- For complex business rules like conditional merging or Customer-driven selection, implement custom Cart merging logic in your backend.
- See the Cart merge API reference for complete technical specifications.