Broadleaf Microservices

Checkout Components

Overview

This article details the components used to process checkout requests.

Sequence Diagram for Checkout
Figure 1. Checkout Sequence Diagram

CheckoutService

Overview

Service responsible for handling checkout requests and initiating the checkout workflow.

Default Implementation Details

DefaultCheckoutService initiates the checkout workflow by first validating that the cart’s status is valid for checkout. Valid statuses are IN_PROCESS and CSR_OWNDED. Any other statuses will result in the checkout submission being rejected. If the status is valid, then CheckoutService will update the cart’s status to SUBMISSION_IN_PROGRESS to prevent further user modification.

Tip
The Cart domain also has a version field to ensure that stale cart states do not overwrite the latest one. This applies to all cart-modifying requests.

Whether success or failure, the result is a CheckoutResponse.

Example CheckoutResponse payload
{
  "cart": {},
  "success": false,
  "failureType": "INVALID_CART_ITEM_CONFIG",
  "failuresMessage": "Cart item(s) have incomplete or invalid configuration. This configuration must be corrected before checkout can be completed.",
  "itemFailureMessages": {
    "cart-item-1-id": "Cannot add an item to the cart with a quantity less than 1."
  }
}

Error Handling

Any unrecoverable error during the checkout workflow should result in a CheckoutWorkflowException. These will be caught by the CheckoutService and included in a CheckoutResponse. By this point, any rollbacks of state necessary should already have taken place aside from resetting the cart’s status, which CheckoutService will handle. See CheckoutWorkflow for rollback handling.

Completion Messaging

Once the checkout workflow has finished successfully, a message with the submitted cart as the payload is transmitted on the checkoutCompletionOutput channel and can be consumed using a checkoutCompletionInput channel. An example listener is provided below showing how to consume the message. The following are consumers provided by other services out-of-box for typical post-checkout operations:

  • Offer Services has RecordOfferUsageEventListener to record the offers and offer codes used

  • Campaign Services has RecordCampaignCodeUsageEventListener to record the campaign codes used

  • Order Services has CheckoutCompletionListener that creates an OMS order based on the cart

  • Inventory Services has OrderSubmittedInventoryAdjustmentMessageListener that converts "soft" inventory reservations for cart items into "hard" reservations, essentially to decrement inventory.

Example 1. Example Checkout Completion Listener
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.lang.Nullable;

import com.broadleafcommerce.cart.client.domain.Cart;
import com.broadleafcommerce.common.messaging.service.IdempotentMessageConsumptionService;
import com.broadleafcommerce.data.tracking.core.context.ContextInfo;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class CheckoutCompletionListener {

    private final IdempotentMessageConsumptionService idempotentConsumptionService;

    /**
     * Event listener entry point. Checks if the message has already been received and, if not,
     * begins processing it.
     *
     * @param message the message payload event
     */
    @StreamListener("checkoutCompletionInputMyServiceName")
    public void listen(Message<CheckoutCompletionEvent> message) {
        idempotentConsumptionService.consumeMessage(message,
                CheckoutCompletionListener.class.getSimpleName(), this::processMessage);
    }

    private void processMessage(Message<CheckoutCompletionEvent> message) {
        CheckoutCompletionEvent event = message.getPayload();

        if (shouldProcessEvent(event)) {
            // process event
        }
    }

    protected boolean shouldProcessEvent(CheckoutCompletionEvent event) {
        return event.getCart() != null && "SUBMITTED".equalsIgnoreCase(event.getCart().getStatus());
    }


    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class CheckoutCompletionEvent {

        /**
         * The cart object that completed the checkout workflow
         */
        @Nullable
        private Cart cart;

        /**
         * The id representing this request to checkout
         *
         * This value can be used to identify entities that were produced due to this request and
         * therefore need to be rolled back
         *
         * @return The id representing this request to checkout
         */
        @Nullable
        private String requestId;

        /**
         * The {@link ContextInfo} derived from the original request containing tenant and sandbox info.
         *
         * @return The {@link ContextInfo} derived from the original request
         */
        @Nullable
        private ContextInfo contextInfo;

    }
}

Guest Checkout Security

This service supports a layer of security for guest carts to help protect against attackers for guest customers. Learn more about Guest Checkout Tokens.

CheckoutWorkflow

Overview

Service responsible for executing all checkout activities and handling rollbacks due to failures.

Checkout should be considered the process whereby the submitted cart’s state is finalized from the originating user’s perspective. Thus, any information that requires their input should be validated so that errors or missing information can be presented while we still have their attention. The workflow, then, should included steps such as:

Tip
Broadleaf recommends an activity for authorizing payment (ensuring funds can be captured) during checkout but postponing capturing funds until time of fulfillment, that is, post-checkout. This makes partial fulfillment and cancellation, handling unexpected inventory mixups, and other scenarios easier for an OMS.

Each of these can be represented as isolated CheckoutWorkflowActivities. This allows efficient rollback handling, insertion of new activities, and reordering of existing activities.

Tip

If an activity can wait to be handled until after checkout, then it should not be included in the checkout workflow. This obviates the need for excessive failure and rollback scenarios. For instance, if you were to include a SendConfirmationEmailActivity, then the workflow could potentially rollback if your email service is unavailable. Such a rollback is entirely unnecessary since the submitted cart’s state is unaffected. Thus, it is naturally a post-checkout activity since it does not affect the cart state.

Error Handling

When an individual CheckoutWorkflowActivity encounters a unrecoverable error, it should produce a CheckoutWorkflowActivityException. The WorkflowHandler will catch this and initiate the rollback process before throwing a CheckoutWorkflowException for CheckoutService to handle. The activity exception will be recorded on the cart in its internalAttributes for audit purposes.

To initiate rollbacks, a message is produced on the checkoutRollbackOutput channel and consumed by listeners using the checkoutRollbackInput channel. Therefore, the default rollback process is asynchronous. Also by default, the only activity that has any work to rollback is the PaymentConfirmationActivity, which is the last activity run.

CheckoutWorkflowActivity

Overview

Within the execution of the CheckoutWorkflow, CheckoutWorkflowActivities are responsible for contributing an independent and isolated portion of the work required for a cart to complete checkout.

Error Handling

If an unrecoverable error is encountered during the completion of an activity, then the entire workflow must be rolled back. Each activity is thus responsible for providing a rollback method to undo any work or stored state from its execution. To initiate a rollback, it should produce a CheckoutWorkflowActivityException.

Coordinating the execution of the rollback for the workflow as a whole is the responsibility of the CheckoutWorkflow. A CheckoutWorkflowActivity should only be concerned about its own contributions to the overall workflow and how to roll them back.

Default Activities

Below is the list of activities configured out-of-the-box in the order they are executed:

Also, there is CartOfferValidationActivity, but it’s not used by default—offers/discounts are only validated when initially added to the cart. The thought behind this is that it provides a better user experience to allow the discounts if they were valid when initially added.