Skip to content

Class Name: ZeroDollarCheckout

Last Updated: 2025-10-23 Source Code: https://bitbucket.org/i2cinc/i2c.salesforce.metadata/src/STAGING/force-app/main/default/classes/ZeroDollarCheckout.cls

API Name: ZeroDollarCheckout Type: Controller Test Coverage: Unknown

Business Purpose

The ZeroDollarCheckout class handles checkout processing for carts with $0 total amount. This supports free product orders (promotional items, free trials), orders with 100% discount coupons, and complimentary memberships. This streamlines the checkout experience by bypassing payment collection when the cart total is exactly $0.

Class Overview

Scope and Sharing

  • Sharing Model: with sharing
  • Access Modifier: public
  • Interfaces Implemented: None

Key Responsibilities

  • Validate that the cart has a $0 total amount
  • Close the cart without payment processing
  • Invoke the Zero_Dollar_Checkout Flow to create the order
  • Return the order summary number for display
  • Handle test context appropriately

Public Methods

closeCart

@AuraEnabled
public static String closeCart(String userId)

Purpose: Processes zero-dollar checkout and returns order number. This method is called from Lightning components when a user attempts to complete a checkout with a $0 cart total.

Parameters: - userId (String): The User Id (cart owner) whose active cart should be processed

Returns: - String: Order Summary Number (e.g., "#12345")

Throws: - AuraHandledException: Thrown when requirements are not met (cart not found, cart total is not $0, or general errors)

Usage Example:

String orderNumber = ZeroDollarCheckout.closeCart(UserInfo.getUserId());

Business Logic:

  1. Query Active Cart: Finds the active WebCart for the specified user
    WebCart cart = [
        SELECT Id, GrandTotalAmount
        FROM WebCart
        WHERE OwnerId = :userId
        AND Status != 'PendingDelete'
        AND Status != 'Closed'
        AND Status != 'PendingClosed'
        LIMIT 1
    ];
    
  2. Excludes carts that are being deleted or already closed
  3. Retrieves the GrandTotalAmount for validation

  4. Validate Zero Total: Ensures the cart total is exactly $0

    if (grandTotalAmount != 0){
        throw new AuraHandledException('Requirements are not met');
    }
    

  5. If cart total is not zero, throws an error

  6. Invoke Flow or Return Test Value: In production, invokes the Zero_Dollar_Checkout Flow; in tests, returns a mock value

    if (Test.isRunningTest()){
        orderNumber = '#orderZeroSummaryNumber';
    } else {
        Flow.Interview.Zero_Dollar_Checkout myFlow = new Flow.Interview.Zero_Dollar_Checkout(inputs);
        myFlow.start();
        orderNumber = (String) myFlow.getVariablevalue('orderSummaryNumber');
    }
    

  7. Flow creates OrderSummary and returns the order number
  8. Test context returns a mock order number

  9. Error Handling: Catches exceptions and wraps them in AuraHandledException

    catch (Exception e) {
        System.debug((e.getMessage()));
        System.debug((e.getStackTraceString()));
        if (e.getTypeName() == 'System.QueryException'){
            throw new AuraHandledException('Requirements are not met');
        }
        throw new AuraHandledException('Error');
    }
    

  10. Logs errors to debug logs
  11. Returns generic "Requirements are not met" for QueryException (no cart found)
  12. Returns generic "Error" for other exceptions

Private/Helper Methods

This class does not contain private or helper methods. All logic is contained within the single public method.


Dependencies

Apex Classes

  • None directly called

Salesforce Objects

  • WebCart: [Fields accessed: Id, OwnerId, GrandTotalAmount, Status]
  • Used to query and validate the active cart for the user
  • Status field used to exclude deleted or closed carts

Custom Settings/Metadata

  • None

External Services

  • Flow: Zero_Dollar_Checkout: Invoked to process the zero-dollar order
  • Input Variable: cartId (String) - WebCart Id
  • Output Variable: orderSummaryNumber (String) - Order number for display
  • Purpose: Creates OrderSummary without payment processing, marks cart as closed, fulfills order

LWC/Aura Components

  • Called from Lightning component via @AuraEnabled annotation
  • Likely used in checkout page component

Design Patterns

  • LWC Controller Pattern: Uses @AuraEnabled for Lightning component integration
  • Flow Integration Pattern: Delegates complex order creation logic to Flow
  • Test Bypass Pattern: Different behavior in tests using Test.isRunningTest() (anti-pattern)
  • Error Wrapping Pattern: Catches exceptions and wraps in AuraHandledException for Lightning components

Governor Limits Considerations

SOQL Queries: 1 query per invocation (WebCart query) DML Operations: None (handled by Flow) CPU Time: Minimal - simple validation logic Heap Size: Small - single cart record

Bulkification: Not applicable - @AuraEnabled methods process single records Async Processing: None - synchronous processing only

Error Handling

Strategy: try-catch block with AuraHandledException wrapping Logging: Uses System.debug() for error messages and stack traces User Notifications: Generic error messages returned to Lightning component

Exception Handling: - QueryException: Caught when no active cart is found for the user - returns "Requirements are not met" - Generic Exception: Catches all other exceptions - returns "Error"

Error Messages: 1. "Requirements are not met" - Cart total is not $0 OR no active cart found for user 2. "Error" - Flow execution failure or unexpected errors

Security Considerations

Sharing Rules: Respects sharing rules (with sharing) Field-Level Security: Not explicitly enforced with WITH SECURITY_ENFORCED CRUD Permissions: Not explicitly checked Input Validation: Validates cart total is $0; no validation that userId matches current user

Security Concerns: - OwnerId Filter: Uses userId parameter to find cart - assumes userId is current user. If LWC passes different userId, could potentially access other users' carts. Should validate userId matches current user using UserInfo.getUserId() - No User Authorization Check: Should validate that the passed userId matches the current user before processing

Test Class

Test Class: Needs identification Coverage: Unknown Test Scenarios Covered: - Test class requirements identified in documentation: - Success case: Cart with $0 total - Error case: Cart with non-zero total - Error case: No active cart found - Mock return value: '#orderZeroSummaryNumber'

Changes & History

  • Unknown: Initial implementation for zero-dollar checkout by original developer

⚠️ Pre-Go-Live Concerns

CRITICAL - Fix Before Go-Live

  • Security: User Validation Missing: No check that userId matches current user
  • Could allow accessing other users' carts if userId parameter is manipulated
  • Fix: Add validation: if(userId != UserInfo.getUserId()) throw new AuraHandledException('Unauthorized access');
  • Generic Error Messages: "Requirements are not met" and "Error" messages are not helpful to users
  • Users cannot understand what went wrong
  • Fix: Provide specific error messages: "No active cart found", "Cart total must be $0 for zero-dollar checkout", "Failed to create order"

HIGH - Address Soon After Go-Live

  • Parameter Type Mismatch: userId parameter is String but should be Id type
  • Fix: Change method signature to public static String closeCart(Id userId)
  • Test.isRunningTest() Anti-Pattern: Production code behaves differently in tests
  • Makes testing less reliable
  • Fix: Consider dependency injection for Flow execution or accept that Flow runs in tests
  • Unused Variables: customerId and paymentId declared but never used (lines 17-18)
  • Fix: Remove unused variables or implement intended functionality

MEDIUM - Future Enhancement

  • Improve Error Logging: Errors only logged to debug logs
  • Consider persistent error logging for troubleshooting
  • Enhancement: Implement custom logging framework or use platform event logging
  • Empty Order Number Check: Doesn't validate that orderNumber returned from Flow is not blank
  • Enhancement: Add validation: if(String.isBlank(orderNumber)) throw new AuraHandledException('Failed to create order');
  • Cart Query Enhancement: Uses QueryException handling instead of checking if cart exists first
  • Enhancement: Use List query and check isEmpty() instead of catching QueryException

LOW - Monitor

  • Flow Dependency: Requires Zero_Dollar_Checkout Flow to exist and function correctly
  • Flow must maintain the expected input/output contract
  • Monitor for Flow API version changes
  • Cart Status Handling: Properly excludes closed/deleted carts but relies on specific Status values
  • Monitor if new cart statuses are added to Commerce Cloud

Maintenance Notes

Complexity: Low - Single method with straightforward logic Recommended Review Schedule: Annually or when Commerce Cloud is upgraded Key Maintainer Notes: - Flow Dependency: This class is tightly coupled to the Zero_Dollar_Checkout Flow. Any changes to the Flow's input/output variables will break this integration. - Test Behavior: In test context, the Flow is not executed and a mock order number is returned. This means Flow logic is not tested through this class. - User Validation Critical: The biggest security concern is the lack of user validation. This should be addressed immediately to prevent potential security issues. - Error Message Improvement: Current error messages are too generic. When troubleshooting production issues, developers will need to check debug logs to understand actual failures. - Commerce Cloud Updates: Monitor Salesforce Commerce Cloud release notes for changes to WebCart object or checkout process that might affect this class. - Non-Obvious Behavior: The class uses Test.isRunningTest() which means the actual Flow execution path is never tested when running Apex tests.

@AuraEnabled
public static String closeCart(Id userId) {
    // Validate userId matches current user
    if(userId != UserInfo.getUserId()) {
        throw new AuraHandledException('Unauthorized access');
    }

    try {
        List<WebCart> carts = [
            SELECT Id, GrandTotalAmount
            FROM WebCart
            WHERE OwnerId = :userId
            AND Status NOT IN ('PendingDelete', 'Closed', 'PendingClosed')
            LIMIT 1
        ];

        if(carts.isEmpty()) {
            throw new AuraHandledException('No active cart found');
        }

        WebCart cart = carts[0];

        if(cart.GrandTotalAmount != 0) {
            throw new AuraHandledException('Cart total must be $0 for zero-dollar checkout. Current total: $' + cart.GrandTotalAmount);
        }

        // Invoke Flow
        Map<String, Object> inputs = new Map<String, Object>{'cartId' => cart.Id};
        Flow.Interview.Zero_Dollar_Checkout myFlow = new Flow.Interview.Zero_Dollar_Checkout(inputs);
        myFlow.start();

        String orderNumber = (String) myFlow.getVariablevalue('orderSummaryNumber');

        if(String.isBlank(orderNumber)) {
            throw new AuraHandledException('Failed to create order');
        }

        return orderNumber;

    } catch(AuraHandledException e) {
        throw e; // Re-throw AuraHandledExceptions
    } catch(Exception e) {
        System.debug(LoggingLevel.ERROR, 'Zero-dollar checkout failed: ' + e.getMessage());
        System.debug(LoggingLevel.ERROR, 'Stack trace: ' + e.getStackTraceString());
        throw new AuraHandledException('Checkout failed: ' + e.getMessage());
    }
}