anonymousId
. This ID is typically stored in either a client-side cookie or local storage. Although Carts are the most common resource created anonymously, other resources such as Custom Objects and Shopping Lists can also be linked to an anonymousId
.customerId
). Failing to do so results in a poor user experience because the user's Cart or other data appears to be lost upon login.Automatic Cart migration
For Carts, Composable Commerce provides a built-in mechanism to automate migration during sign-in or sign-up, which simplifies development for this common use case.
During sign-in and sign-up
anonymousCartId
in the request body of a POST /{projectKey}/login
or POST /{projectKey}/customers
request. The anonymousCartSignInMode
field (for login) and the presence of anonymousCartId
(for sign-up) control how the anonymous Cart is handled.anonymousCartSignInMode for login
POST /{projectKey}/login
request dictates how to integrate the anonymous Cart with the Customer's account.-
MergeWithExistingCustomerCart
(default): This mode preserves all items from both the anonymous and any existing Customer Carts.-
Scenario: Anonymous user has Cart A; Customer has an existing active Cart B.
- Result: Line Items from Cart A are merged into Cart B. Cart B remains the active Cart for the logged-in Customer. Cart A is typically deleted or marked as Merged.
- Conflict resolution: by default, if a Product Variant with identical Custom Fields exists in both Carts, then the higher quantity is retained. Otherwise, the item is added as a new Line Item.
-
Scenario: Anonymous user has Cart A; Customer has no active Cart.
- Result: Cart A is assigned to the Customer and becomes their active Cart.
-
-
UseAsNewActiveCustomerCart
: This mode replaces an existing Customer Cart with an anonymous Cart.-
Scenario: Anonymous user has Cart A; Customer has an existing active Cart B.
- Result: Cart A becomes the Customer's active Cart. Cart B remains in an
active
state, but is hidden in a single Cart scenario. To avoid confusion and prevent Cart B from reappearing after Cart A becomes an Order, you should consider deleting Cart B. - Use case: this is useful if the anonymous Cart is considered more current or if you want to override an older, stale customer Cart.
- Result: Cart A becomes the Customer's active Cart. Cart B remains in an
-
Scenario: Anonymous user has Cart A; Customer has no active Cart.
- Result: Cart A is assigned to the Customer, which is the same outcome as
MergeWithExistingCustomerCart
in this case.
- Result: Cart A is assigned to the Customer, which is the same outcome as
-
Sign-up
anonymousCartId
is provided in a POST /{projectKey}/customers
request, the Cart is automatically assigned to the new Customer. No anonymousCartSignInMode
is needed because there's no existing Customer Cart to merge with.Example: Merge Carts on login
The following example shows how to log in a Customer and merge their anonymous Cart with an existing Customer Cart:
import { apiRoot } from '../../client'; // Assuming apiRoot is initialized and exported here
import { CustomerSignInResult } from '@commercetools/platform-sdk'; // Import the type for the response body
// Function to handle login with anonymous cart merge
async function customerLoginWithCartMergeInStore(
storeKey: string,
email: string,
password: string,
anonymousCartId: string
): Promise<CustomerSignInResult> {
try {
const loginResponse = await apiRoot
.inStoreKeyWithStoreKeyValue({ storeKey: storeKey })
.login()
.post({
body: {
email: email,
password: password,
anonymousCartId: anonymousCartId,
anonymousCartSignInMode: 'MergeWithExistingCustomerCart', // Explicitly setting, though it's default
},
})
.execute();
console.log('Login successful with cart merge for store "${storeKey}"!');
console.log('Customer details:', loginResponse.body.customer.email);
console.log('Merged cart details:', loginResponse.body.cart);
return loginResponse.body;
} catch (error: any) {
// Type 'any' for caught error for now, as stricter typing for HTTP errors can be complex
console.error(
'Login failed with cart merge for store "${storeKey}":',
error
);
// You might add more specific error handling here based on Composable Commerce error codes
// For example, if (error.statusCode === 400 && error.body && error.body.errors.some(e => e.code === 'InvalidCredentials')) { ... }
throw error;
}
}
// Example login for David with anonymous cart, in a store
(async () => {
const davidEmail = 'david.shopper@example.com'; // Assuming David's email
const davidPassword = 'DavidSecurePass!'; // Assuming David's password
const davidAnonymousCartId = 'anonymous-cart-id-789'; // This ID would come from the client-side
const storeKey = 'electronics-high-tech-store';
console.log(
`\n--- Attempting store login for David with anonymous cart (${davidAnonymousCartId}) ---`
);
try {
const loginResult = await customerLoginWithCartMergeInStore(
storeKey,
davidEmail,
davidPassword,
davidAnonymousCartId
);
console.log(
`David's carts successfully merged upon store login. New active cart ID: ${loginResult.cart?.id}`
);
} catch (err) {
console.error('Failed to log in David and merge cart in the store.');
}
})();
Key takeaways
- Automatic for Carts: Composable Commerce automates Cart migration during login and sign-up.
- Trigger with
anonymousCartId
: pass theanonymousCartId
in the login or sign-up request to initiate migration. - Control with
anonymousCartSignInMode
: useMergeWithExistingCustomerCart
(default) to combine Carts orUseAsNewActiveCustomerCart
to replace the Customer's Cart with an anonymous Cart. - Sign-up is simpler: during sign-up, an anonymous Cart is directly assigned to the new Customer without needing a merge mode.
Manual resource migration
anonymousId
must be migrated manually. This is a critical responsibility of your application's backend or backend for frontend (BFF).Resources that require manual migration
- Custom Objects and Custom Fields: any custom data linked to an
anonymousId
, such as user preferences or browsing history, requires manual migration. - Shopping Lists: Shopping Lists associated with an
anonymousId
aren't automatically transferred. - Specific Line Items: under certain conditions, such as a missing
shippingAddress
on the Customer's Cart, Line Items from the anonymous Cart might not be merged automatically. For more information, see the Cart merge during sign-in documentation.
Manual migration process
Your application must implement the following logic to transfer ownership of these resources:
-
Identify anonymous resources: before authentication, query for any resources associated with the
anonymousId
from the client-side session. For example, you can query Shopping Lists or Custom Objects that store theanonymousId
. -
Authenticate user: after a successful sign-in or sign-up, obtain the
customerId
details. -
Update resources: use the appropriate update actions to re-associate the anonymous resources with the
customerId
.- Shopping Lists: use the
setCustomer
update action. - Custom Objects: update a field in the object to reference the
customerId
or recreate the object with a new key or container that includes thecustomerId
.
- Shopping Lists: use the
-
Clean up: after migration, remove the
anonymousId
from the client-side session and consider deleting the original anonymous resources to maintain data hygiene.
Example - Assign a Shopping List to a Customer
import { apiRoot } from '../../client'; // Assuming apiRoot is initialized and exported here
import { ShoppingList, ShoppingListUpdate } from '@commercetools/platform-sdk'; // Import necessary types
// Function to assign a Shopping List to a customer in a store
async function assignShoppingListToCustomerInStore(
storeKey: string,
shoppingListId: string,
shoppingListVersion: number,
customerId: string
): Promise<ShoppingList> {
try {
const shoppingListUpdate: ShoppingListUpdate = {
version: shoppingListVersion,
actions: [
{
action: 'setCustomer',
customer: {
typeId: 'customer',
id: customerId,
},
},
],
};
const updatedShoppingListResponse = await apiRoot
.inStoreKeyWithStoreKeyValue({ storeKey: storeKey })
.shoppingLists()
.withId({ ID: shoppingListId })
.post({ body: shoppingListUpdate })
.execute();
const updatedShoppingList: ShoppingList = updatedShoppingListResponse.body;
console.log(
`Shopping List ${shoppingListId} successfully assigned to customer ${customerId} within store "${storeKey}".`
);
return updatedShoppingList;
} catch (error: any) {
console.error(
`Error assigning Shopping List ${shoppingListId} to customer ${customerId} in store "${storeKey}":`,
error
);
throw error;
}
}
// Example usage after David logs in
(async () => {
const ELECTRONICS_HIGH_TECH_STORE_KEY = 'electronics-high-tech-store';
const davidCustomerId = 'david-customer-id'; // Obtained from successful login
// Assume you have an anonymous Shopping List ID and its current version,
// which was associated with 'electronics-high-tech-store'
const anonymousShoppingListId = 'anon-shoppingList-id-123'; // Replace with a real ID
const anonymousShoppingListVersion = 1; // Replace with a real version
console.log(
`\n--- Assigning anonymous Shopping List to customer ${davidCustomerId} for store "${ELECTRONICS_HIGH_TECH_STORE_KEY}" ---`
);
try {
await assignShoppingListToCustomerInStore(
ELECTRONICS_HIGH_TECH_STORE_KEY,
anonymousShoppingListId,
anonymousShoppingListVersion,
davidCustomerId
);
console.log(
`Anonymous Shopping List successfully migrated to David's account within ${ELECTRONICS_HIGH_TECH_STORE_KEY}.`
);
} catch (err) {
console.error('Failed to migrate anonymous Shopping List in-store.');
}
})();
Key takeaways
- Manual migration is required: resources such as Shopping Lists and Custom Objects aren't migrated automatically.
- Application responsibility: your application's backend or BFF must implement the logic for manual migration.
- Process: the process involves identifying anonymous resources, authenticating the user, updating the resources with the
customerId
, and cleaning up old anonymous data. - Flexibility: manual migration enables you to implement custom business logic for merging or transferring data beyond what the automatic process provides.
You now have a solid understanding of how to manage the transition from anonymous browsing to authenticated Customer sessions, both automatically for carts and manually for other vital resources. This ensures a smooth and continuous shopping journey for your Customers.
Entire process workflow
Here's a flowchart diagram that illustrates the complete process of migrating anonymous resources to authenticated Customer accounts: