Class Name: CartController¶
Last Updated: 2025-10-22 Source Code: https://github.com/AANP-IT/I2C.Salesforce.Metadata/blob/STAGING/force-app/main/default/classes/CartController.cls
API Name: CartController Type: Lightning Web Component Controller (Commerce Cloud) Test Coverage: CartControllerTest.cls Created: 08/17/2024 Author: Ecaterina Popa Last Modified: 09/03/2024 by Ecaterina Popa
Business Purpose¶
This controller provides Lightning Web Components with Commerce Cloud cart management functionality for AANP's B2B/B2C storefront. It wraps ConnectApi.CommerceCart methods to add products to cart, apply coupon codes, retrieve cart information, and manage cart lifecycle. The class resolves community IDs to webstore IDs for multi-site commerce implementations and provides a simplified API layer for LWC components to interact with Salesforce Commerce Cloud's cart system.
Class Overview¶
Scope and Sharing¶
- Sharing Model: with sharing (respects record-level security)
- Access Modifier: public
- Interfaces Implemented: None
Key Responsibilities¶
- Resolve community ID to webstore ID for Commerce Cloud operations
- Add single product to cart via ConnectApi
- Add multiple products to cart in batch via ConnectApi
- Apply coupon codes to cart
- Retrieve current cart for user
- Delete cart
- Cache community-to-webstore mappings for performance
- Handle Commerce Cloud exceptions gracefully
Public Properties¶
communityIdToWebStoreIdCache¶
@TestVisible
private static Map<String, String> communityIdToWebStoreIdCache = new Map<String, String>();
Purpose: Static cache mapping community IDs to webstore IDs to avoid repeated SOQL queries.
Visibility: private (exposed to tests via @TestVisible)
Usage: Populated by resolveCommunityIdToWebstoreId(), checked before querying
Public Methods¶
resolveCommunityIdToWebstoreId¶
Purpose: Retrieves the webstore ID associated with a given community ID, using cache to optimize performance.
Parameters:
- communityId (String): Network/Community ID
Returns: String - WebStore ID or error message
Logic:
if (communityIdToWebStoreIdCache.containsKey(communityId)) {
return communityIdToWebStoreIdCache.get(communityId);
} else {
String webStoreId = [
SELECT WebStoreId
FROM WebStoreNetwork
WHERE NetworkId = :communityId
LIMIT 1
].WebStoreId;
communityIdToWebStoreIdCache.put(communityId, webStoreId);
return webStoreId;
}
Error Handling: - Try-catch returns exception message as string (POOR PRACTICE) - Calling methods don't know if return value is valid webstore ID or error
Issues: - Returns error message as String (should throw exception) - No validation that webstore ID is valid - Query could return no results (NullPointerException)
addProductToCart¶
@AuraEnabled
public static ConnectApi.CartItem addProductToCart(
String communityId,
String productId,
String quantity,
String effectiveAccountId
)
Purpose: Adds single product to user's cart using Commerce Cloud ConnectApi.
Parameters:
- communityId (String): Community ID for webstore resolution
- productId (String): Product2 ID to add
- quantity (String): Quantity (passed as string)
- effectiveAccountId (String): Account ID (buyer account)
Returns: ConnectApi.CartItem - Newly created cart item, or null on error
Business Logic:
-
Resolve Webstore:
-
Build Cart Input:
-
Add to Cart:
Error Handling: - Catch-all returns null (LWC can't distinguish failure types) - No logging of errors
Issues: - Returns null on error (no error details to LWC) - Hardcoded 'current' cart identifier - Quantity passed as String (correct for ConnectApi)
addMultipleProductsToCart¶
@AuraEnabled
public static ConnectApi.BatchResult[] addMultipleProductsToCart(
String communityId,
List<String> products,
String effectiveAccountId
)
Purpose: Adds multiple products to cart in single batch operation using Commerce Cloud ConnectApi.
Parameters:
- communityId (String): Community ID
- products (ListeffectiveAccountId (String): Account ID
Returns: ConnectApi.BatchResult[] - Array of results (one per product), or null on error
Business Logic:
-
Resolve Webstore:
-
Build Batch Input:
List<ConnectApi.BatchInput> cartItems = new List<ConnectApi.BatchInput>(); for (String prod : products) { ConnectApi.CartItemInput cartInput = new ConnectApi.CartItemInput(); cartInput.productId = prod; cartInput.quantity = '1'; // Hardcoded quantity cartInput.type = ConnectApi.CartItemType.PRODUCT; ConnectApi.BatchInput batchInput = new ConnectApi.BatchInput(cartInput); cartItems.add(batchInput); } -
All products get quantity = 1 (hardcoded)
-
Add Items:
Issues: - Quantity hardcoded to '1' for all products - No way to specify different quantities per product - Returns null on error (no error details)
applyCouponToTheCart¶
@AuraEnabled
public static ConnectApi.CartCouponCollection applyCouponToTheCart(
String communityId,
String effectiveAccountId,
String couponCode
)
Purpose: Applies coupon/promotion code to current cart.
Parameters:
- communityId (String): Community ID
- effectiveAccountId (String): Account ID
- couponCode (String): Coupon code to apply
Returns: ConnectApi.CartCouponCollection - Collection of applied coupons, or null on error
Business Logic:
-
Resolve Webstore:
-
Build Coupon Input:
-
Apply Coupon:
Error Handling: - Returns null on error (coupon invalid, expired, etc.) - LWC must interpret null as failure
getCurrentCart¶
Purpose: Retrieves or creates current cart for user.
Parameters:
- effectiveAccountId (String): Account ID
Returns: WebCart - Current cart record
Business Logic:
-
Check Object Access:
-
Query Existing Cart:
- Finds cart in Active, Checkout, or Processing status
-
Uses WITH SECURITY_ENFORCED
-
Return Existing or Create New:
if(!currentCarts.isEmpty()){ return currentCarts[0]; }else { Id webStoreId = resolveCommunityIdToWebstoreId(Network.getNetworkId()); ConnectApi.CartSummary cartSummary = ConnectApi.CommerceCart.createCart(webStoreId,new ConnectApi.CartInput()); currentCarts = [ SELECT Id, AccountId, Status FROM WebCart WHERE Id =: cartSummary.cartId ]; return currentCarts[0]; } - Creates new cart via ConnectApi if none exists
- Queries created cart to return WebCart record
Issues: - Creates cart even if user just wants to check for existence - Uses Network.getNetworkId() (requires Experience Cloud context) - Could return null if cart creation fails
deleteCart¶
@AuraEnabled
public static void deleteCart(String communityId, String effectiveAccountId, String cartId)
Purpose: Deletes specified cart via Commerce Cloud ConnectApi.
Parameters:
- communityId (String): Community ID
- effectiveAccountId (String): Account ID
- cartId (String): WebCart ID to delete
Returns: void
Business Logic:
String webstoreId = CartController.resolveCommunityIdToWebstoreId(communityId);
ConnectApi.CommerceCart.deleteCart(webstoreId, effectiveAccountId, cartId);
Error Handling: - Throws AuraHandledException on failure - Generic error message
Private/Helper Methods¶
None - All logic in public methods.
Could Be Enhanced With:
- validateCartAccess(accountId) - Verify user owns cart
- handleCommerceException(e) - Centralized error handling
- logCartOperation(operation, result) - Audit logging
Dependencies¶
Apex Classes¶
- None - Standalone controller
Salesforce Objects¶
WebStoreNetwork (Commerce Cloud) - Fields: NetworkId, WebStoreId - Purpose: Map communities to webstores
WebCart (Commerce Cloud) - Fields: Id, AccountId, Status - Purpose: Shopping cart records
ConnectApi Classes¶
ConnectApi.CommerceCart - Methods: - addItemToCart() - addItemsToCart() - applyCartCoupon() - createCart() - deleteCart() - Purpose: Commerce Cloud cart operations
ConnectApi Types: - CartItemInput - CartCouponInput - BatchInput - CartItem - CartCouponCollection - BatchResult[]
Custom Settings/Metadata¶
- None
Design Patterns¶
- Controller Pattern: LWC backend layer
- Facade Pattern: Wraps ConnectApi for simplified access
- Cache Pattern: Caches webstore lookups
- Null Object Pattern: Returns null on errors (anti-pattern)
Why These Patterns: - Controller pattern standard for LWC - Facade simplifies complex ConnectApi - Cache optimizes repeated lookups - Null returns avoid exception handling (but lose error context)
Governor Limits Considerations¶
SOQL Queries: 1-3 per method call (webstore lookup, cart query) DML Operations: 0 (uses ConnectApi) CPU Time: Low Heap Size: Low
Bulkification: N/A (single cart operations)
Async Processing: No
Governor Limit Risks: - LOW: 1-3 queries per call well within limits - LOW: Cache reduces repeated webstore queries
Performance Considerations: - Cache significantly improves webstore resolution - ConnectApi calls are relatively fast - Cart queries simple and indexed
Error Handling¶
Strategy: Inconsistent - some methods return null, others throw
Return Patterns:
- resolveCommunityIdToWebstoreId() - Returns error message as String
- addProductToCart() - Returns null
- addMultipleProductsToCart() - Returns null
- applyCouponToTheCart() - Returns null
- getCurrentCart() - Throws AuraHandledException
- deleteCart() - Throws AuraHandledException
Logging: - NONE - No error logging anywhere - LWC must handle null returns without knowing why
Recommended Improvements: 1. HIGH: Standardize error handling (always throw AuraHandledException) 2. HIGH: Add error logging for troubleshooting 3. MEDIUM: Return structured error objects instead of null 4. MEDIUM: Validate inputs before calling ConnectApi 5. LOW: Add debug logs for successful operations
Security Considerations¶
Sharing Rules: RESPECTED - Uses 'with sharing' - Appropriate for user-facing cart operations
Field-Level Security: PARTIALLY ENFORCED - getCurrentCart uses WITH SECURITY_ENFORCED - Other queries don't enforce FLS
CRUD Permissions: PARTIALLY ENFORCED - getCurrentCart checks WebCart.isAccessible() - Other methods don't check CRUD
Input Validation: MINIMAL - No validation of productId, quantity, accountId - Trusts LWC input
Security Risks: - MEDIUM: resolveCommunityIdToWebstoreId returns error as string (could expose sensitive data) - LOW: No validation that accountId belongs to current user - LOW: Commerce Cloud enforces cart ownership
Commerce Cloud Security: - ConnectApi methods enforce cart ownership - User can only modify own carts - Appropriate security for cart operations
Test Class¶
Test Class: CartControllerTest.cls
Test Scenarios That Should Be Covered:
✓ resolveCommunityIdToWebstoreId: - Valid community ID (returns webstore ID) - Invalid community ID (returns error) - Cached lookup (no query) - Multiple lookups (cache effectiveness)
✓ addProductToCart: - Add valid product - Add with quantity - Add to new cart - Add to existing cart - Invalid product ID - Invalid account ID
✓ addMultipleProductsToCart: - Add 2-5 products - Add with some invalid IDs - Verify all quantities = 1 - Batch result validation
✓ applyCouponToTheCart: - Apply valid coupon - Apply invalid coupon - Apply expired coupon - Apply to cart without items
✓ getCurrentCart: - Existing active cart - No cart (create new) - Multiple carts (returns first) - Access check failure
✓ deleteCart: - Delete existing cart - Delete non-existent cart - Delete other user's cart (should fail)
Testing Challenges: - Mocking ConnectApi difficult - Commerce Cloud test data setup complex - WebStoreNetwork requires setup - Experience Cloud context for Network.getNetworkId()
Changes & History¶
- Created: 2024-08-17 by Ecaterina Popa
- Modified: 2024-09-03 by Ecaterina Popa
- Purpose: LWC controller for Commerce Cloud cart operations
⚠️ Pre-Go-Live Concerns¶
CRITICAL - Fix Before Go-Live¶
- INCONSISTENT ERROR HANDLING: Some methods return null, others throw exceptions. Standardize to always throw AuraHandledException with specific error messages.
- ERROR MESSAGE AS STRING: resolveCommunityIdToWebstoreId() returns exception message as string. Calling methods can't distinguish error from valid ID. Throw exception instead.
- NO ERROR LOGGING: Zero error logging. Production failures invisible. Add logging to all catch blocks.
- HARDCODED CART IDENTIFIER: All methods use 'current' for cart identifier. Use constant or make configurable.
HIGH - Address Soon After Go-Live¶
- HARDCODED QUANTITY: addMultipleProductsToCart() hardcodes quantity='1'. Add parameter to specify quantities per product.
- NO INPUT VALIDATION: No validation of productId, accountId, quantity. Add validation before ConnectApi calls.
- INCONSISTENT SECURITY CHECKS: Only getCurrentCart() checks object access and uses WITH SECURITY_ENFORCED. Add to all methods.
- NULL POINTER RISK: resolveCommunityIdToWebstoreId() query could return no results. Add null check.
MEDIUM - Future Enhancement¶
- CACHE MANAGEMENT: Static cache never cleared. Add cache invalidation or time-based expiration.
- BATCH OPERATION: addMultipleProductsToCart() could accept Map
for product ID → quantity mapping. - CART VALIDATION: No validation that cart belongs to specified account. Rely on ConnectApi enforcement.
- NO AUDIT TRAIL: No logging of cart operations for business analytics.
LOW - Monitor¶
- CODE ORGANIZATION: Could extract error handling to helper method.
- MAGIC STRINGS: 'current', 'Active', 'Checkout', 'Processing' hardcoded. Use constants.
- TEST VISIBILITY: communityIdToWebStoreIdCache is @TestVisible. Consider making it package-visible class.
Maintenance Notes¶
Complexity: Low (wrapper around ConnectApi) Recommended Review Schedule: Quarterly, before Commerce Cloud upgrades
Key Maintainer Notes:
🛒 COMMERCE CLOUD WRAPPER: - This class wraps ConnectApi.CommerceCart methods - Simplifies LWC integration with Commerce Cloud - Must stay synchronized with Commerce Cloud API changes
📋 Usage Patterns: - Called from LWCs on product pages, cart page, checkout - Typical flow: resolve webstore → add products → apply coupon → checkout - Cache reduces query overhead for webstore resolution
🧪 Testing Requirements: - Mock ConnectApi calls (difficult) - Test all error scenarios - Verify cache behavior - Test with multiple webstores
🔧 Configuration Dependencies: - WebStore: Must be configured in Commerce Cloud - WebStoreNetwork: Links community to webstore - Experience Cloud: Required for cart operations - Products: Must be available in webstore
⚠️ Gotchas and Warnings: - Returns null on errors (LWC must handle) - Hardcoded 'current' cart identifier - addMultipleProductsToCart() always quantity 1 - Cache never expires - resolveCommunityIdToWebstoreId() returns error as String - getCurrentCart() creates cart if none exists
📅 When to Review This Class: - Before Commerce Cloud upgrades - When ConnectApi methods change - If cart operations fail - When adding new cart features - During LWC cart component updates
🛑 Emergency Deactivation:
Cannot deactivate - critical for storefront. If issues:
// Add metadata check at start of methods:
Commerce_Settings__mdt settings = Commerce_Settings__mdt.getInstance('Cart_Operations');
if (settings != null && !settings.Enabled__c) {
throw new AuraHandledException('Cart operations temporarily disabled');
}
🔍 Debugging Tips: - Enable debug logs for user - Check WebStoreNetwork configuration - Verify productId valid and in webstore - Check cart Status values - Query WebCart directly to see state - Test ConnectApi calls in Execute Anonymous
📊 Monitoring Checklist: - Daily: Cart operation success/failure rates - Weekly: ConnectApi errors - Monthly: Cache hit rate - Alert: Spike in null returns - Alert: ConnectApi exceptions
🔗 Related Components: - LWC Cart Components: Primary consumers - Product Pages: Call addProductToCart() - Checkout Flow: Uses getCurrentCart() - ConnectApi: External dependency - Commerce Cloud: Platform dependency
Business Owner¶
Primary: E-Commerce / Digital Experience Secondary: IT Operations Stakeholders: Product, Marketing, Member Services, Development