Broadleaf Microservices
  • v1.0.0-latest-prod

Cart Operation Release Notes for 2.1.1-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

New Features & Notable Changes

  • Introduced improved support for externally-executed checkout payment transactions

    • Includes updates to the PaymentTransactionExecutionService (used by the PaymentTransactionExecutionActivity checkout workflow activity) to continue processing payments in the case that a payment transaction requiring 3DS verification or HPP (hosted payment page) interaction is required.

    • Introduced the AWAITING_PAYMENT_FINALIZATION used to block customer modification of the cart while the customer completes the required 3DS verification or HPP interaction.

    • Introduced an additional payment modification lock to block customers from modifying their payments while they’re completing the required 3DS verification or HPP interaction.

    • Introduced the ExternalPaymentTransactionCallbackEndpoint to handle callback redirects via the customer’s browser once they’ve completed the 3DS or HPP interaction. This endpoint validates the request, looks up & records the transaction, determines if the cart can be finalized, & returns a 302 response with details about the result of the interaction, causing the browser to redirect to the frontend application.

    • Introduced the ExternalPaymentTransactionResultListener to listen for webhook-recorded payment transaction results coming from PaymentTransactionServices. Based on the result of this transaction & the cart’s other payment transactions, this listener determines if the cart should be finalized.

    • Introduced the ExternalPaymentTransactionCartFinalizationListener which is responsible for finalizing carts in response to finalized payment interactions.

    • Introduced a cart history endpoint for gathering a registered-customer cart by id. This is used for rendering the order confirmation page.

    • Introduced a Cart history endpoint for gathering an anonymous-customer cart by id & email address. This is used for rendering the order confirmation page.

    • Enhanced the DefaultCheckoutWorkflow to only execute payment-related activities if the original cart status is AWAITING_PAYMENT_FINALIZATION. This streamlines the work that needs to be done to re-process payments in the case of a payment failure & re-declaration of payment methods by the customer.

    • Introduced a payment finalization TTL concept that helps to define the window in which a customer can complete their payment finalization interaction (i.e. 3DS verification or HPP interaction). Once the TTL has expired, the cart will automatically "break out" of the AWAITING_PAYMENT_FINALIZATION cart status the next time that the customer resolves the cart. This is also used to clean up the state of the cart payments prior to the transaction reversal candidate job reversing any successful checkout payment transactions.

    • Updated cart resolution & customer-facing endpoints to throw a CartAwaitingPaymentFinalizationException if a cart update interaction is being attempted while the cart’s status is AWAITING_PAYMENT_FINALIZATION

    • Introduced the release payment finalization lock endpoint to support the ability to "break out" of the AWAITING_PAYMENT_FINALIZATION cart status & allow the customer to continue modifying the cart.

    • Updated stale catalog repricing logic to be skipped if the cart is in the AWAITING_PAYMENT_FINALIZATION status

    • Updated guest checkout token logic to effectively break out of the AWAITING_PAYMENT_FINALIZATION cart status when the guest token expires

    • Introduced the ability to resend the ExternalPaymentTransactionCartFinalizationEvent via an API call

  • Introduced the GuestTokenExpirationException (an extension of GuestTokenValidationException) to better communicate when a guest token has expired.

  • Updated the cart status update endpoint to require that the API-calling customer is registered

  • Guard the ManageCartEndpoint.updateStatus endpoint with better validation

    • Use the CartStatusManager to update the Cart status that provides better validation logic

    • Introduced the new abstract class to change the statuses of the account carts - com.broadleafcommerce.cartoperation.service.status.accountcart.AbstractAccountCartStatusHandler

    • By default, the new approach allows only the following transitions:

      • REJECTED or REQUIRES_APPROVAL to IN_PROCESS

      • IN_PROCESS to REQUIRES_APPROVAL

      • REQUIRES_APPROVAL to REJECTED.

        • To support the other status transitions the new CartStatusHandler should be implemented.

        • If the new implementation causes any problems that cannot be fixed immediately, you can enable the previous version of this method. To do this, set the broadleaf.cartoperation.service.legacy-update-cart-status-enabled property to true.

Bugs Fixed

  • Archive a single-use payment when the cart price is changed and if the payment already has a successful authorized transaction or transactions pending a 3DS verification or external interaction

Upgrade Guide

API Changes

New Endpoints

Path Description

PATCH /cart/{cartId}/release-payment-finalization-lock

For carts in the AWAITING_PAYMENT_FINALIZATION status, this method updates the cart status to IN_PROCESS allowing the customer to once again edit the cart. Additionally, it makes the cart’s related entities, like the cart’s payments, editable once again.

GET /checkout/{cartId}/payment-callback/{gatewayType}

Endpoint used as a callback URL for payments that require external interaction (e.g. 3DS verification or HPP interaction)

POST /checkout/{cartId}/payment-callback/{gatewayType}

Endpoint used as a callback URL for payments that require external interaction (e.g. 3DS verification or HPP interaction)

GET /cart-history?cartId={cartId}

Retrieves a historical cart by ID for the authenticated customer.

GET /cart-history?cartId={cartId}&emailAddress={emailAddress}

Retrieves a historical cart by ID for an anonymous customer.

POST /carts/{cartId}/resend-external-payment-transaction-cart-finalization-event

Resends an external payment transaction cart finalization event to finalize the cart with the AWAITING_PAYMENT_FINALIZATION status. This is useful to manually recover from scenarios where the cart has to be finalized, but failed to send the cart finalization event.

Configuration Properties

Added Properties

  • broadleaf.cartoperation.service.legacy-update-cart-status-enabled

    • Determines whether to use the previous version of the ManageCartEndpoint.updateStatus method

    • Disabled by default.

  • broadleaf.payment.legacy-external-payment-pattern-enabled

    • Declares if the legacy pattern should be used for externally-executed checkout payment transactions.

    • Default value: false

  • broadleaf.cartoperation.service.checkout.external-payment-transaction.cart-payment-finalization.enter-awaiting-payment-finalization-on-payment-failure

    • If true, then the cart will enter the AWAITING_PAYMENT_FINALIZATION status when any non-3DS & non-HPP payment failure is encountered. Otherwise, this status is only entered if the cart’s payments have failures requiring 3DS verification or a HPP interaction, & if any other payment failure is encountered, then the cart status is returned to IN_PROCESS.

    • Default value: false

  • broadleaf.cartoperation.service.checkout.external-payment-transaction.cart-payment-finalization.cart-lock-ttl

    • Sets the timeframe in which the customer can work to finalize their payments. This is relevant for both anonymous & registered customer carts.

    • Default value: 1 hour

  • broadleaf.cartoperation.service.checkout.external-payment-transaction.cart-payment-finalization.archive-payments-when-cart-is-unlocked

    • If true, then the cart’s payments will be archived when the cart is unlocked via CartOperationService#releasePaymentFinalizationLock(…​). If false, then the CUSTOMER_MUTABILITY_BLOCKED_FOR_PAYMENT_FINALIZATION access restriction is removed from the cart’s relevant payments.

    • Default value: true

  • broadleaf.cartoperation.service.checkout.external-payment-transaction.callback-redirection.storefront-base-url

    • The default base url of the frontend storefront app used by the ExternalPaymentTransactionCallbackEndpoint to redirect the customer’s browser

  • broadleaf.cartoperation.service.checkout.external-payment-transaction.callback-redirection.default-redirect-uri

    • The default redirect uri used if a payment finalized uri, payment modification uri, or external payment interaction uri are not defined

  • broadleaf.cartoperation.service.checkout.external-payment-transaction.callback-redirection.payment-finalized-uri

    • The redirect uri used if all of the carts payments are finalized & the cart is being finalized. This typically leads to showing the customer the order confirmation page.

  • broadleaf.cartoperation.service.checkout.external-payment-transaction.callback-redirection.payment-modification-uri

    • The redirect uri used if one or more of the external payment interactions failed, & the customer must provide a different form of payment. This typically points to a payment modification page.

  • broadleaf.cartoperation.service.checkout.external-payment-transaction.callback-redirection.external-payment-interaction-uri

    • The redirect uri used if one or more of the cart’s payments still requires an external payment interaction to execute the transaction & finalize the payment. This typically leads to the customer’s browser being redirected to a 3DS verification page or HPP.

New Auth Configuration

To support new service-to-service calls while handling externally-executed checkout payment transactions, the following configuration must be added to the cartops authorize client:

-- SEND_EXTERNAL_PAYMENT_TRANSACTION_CART_FINALIZATION_EVENT
INSERT INTO auth.blc_security_scope (id, "name", open) values ('-860', 'SEND_EXTERNAL_PAYMENT_TRANSACTION_CART_FINALIZATION_EVENT', 'N');
INSERT INTO auth.blc_permission_scope (id, permission, is_permission_root, scope_id) values ('-860', 'SEND_EXTERNAL_PAYMENT_TRANSACTION_CART_FINALIZATION_EVENT', 'Y', '-860');
INSERT INTO auth.blc_client_permissions (id, permission) values ('cartopsclient', 'ALL_SEND_EXTERNAL_PAYMENT_TRANSACTION_CART_FINALIZATION_EVENT');
INSERT INTO auth.blc_client_scopes (id, scope) values ('cartopsclient', 'SEND_EXTERNAL_PAYMENT_TRANSACTION_CART_FINALIZATION_EVENT');

-- OpenAPI
INSERT INTO auth.blc_client_permissions (id, permission) values ('openapi', 'ALL_SEND_EXTERNAL_PAYMENT_TRANSACTION_CART_FINALIZATION_EVENT');
INSERT INTO auth.blc_client_scopes (id, scope) values ('openapi', 'SEND_EXTERNAL_PAYMENT_TRANSACTION_CART_FINALIZATION_EVENT');

-- SEND_EXTERNAL_PAYMENT_TRANSACTION_CART_FINALIZATION_EVENT
INSERT INTO auth.blc_security_scope (id, "name", open) values ('-870', 'MUTABILITY_BLOCK_FOR_PAYMENT_FINALIZATION', 'N');
INSERT INTO auth.blc_permission_scope (id, permission, is_permission_root, scope_id) values ('-870', 'MUTABILITY_BLOCK_FOR_PAYMENT_FINALIZATION', 'Y', '-870');
INSERT INTO auth.blc_client_permissions (id, permission) values ('cartopsclient', 'ALL_MUTABILITY_BLOCK_FOR_PAYMENT_FINALIZATION');
INSERT INTO auth.blc_client_scopes (id, scope) values ('cartopsclient', 'MUTABILITY_BLOCK_FOR_PAYMENT_FINALIZATION');

-- OpenAPI
INSERT INTO auth.blc_client_permissions (id, permission) values ('openapi', 'ALL_MUTABILITY_BLOCK_FOR_PAYMENT_FINALIZATION');
INSERT INTO auth.blc_client_scopes (id, scope) values ('openapi', 'MUTABILITY_BLOCK_FOR_PAYMENT_FINALIZATION');

-- VALIDATION_FOR_PAYMENT_CALLBACK_TOKEN
INSERT INTO auth.blc_security_scope (id, "name", open) values ('-880', 'VALIDATION_FOR_PAYMENT_CALLBACK_TOKEN', 'N');
INSERT INTO auth.blc_permission_scope (id, permission, is_permission_root, scope_id) values ('-880', 'VALIDATION_FOR_PAYMENT_CALLBACK_TOKEN', 'Y', '-880');
INSERT INTO auth.blc_client_permissions (id, permission) values ('cartopsclient', 'ALL_VALIDATION_FOR_PAYMENT_CALLBACK_TOKEN');
INSERT INTO auth.blc_client_scopes (id, scope) values ('cartopsclient', 'VALIDATION_FOR_PAYMENT_CALLBACK_TOKEN');

-- OpenAPI
INSERT INTO auth.blc_client_permissions (id, permission) values ('openapi', 'ALL_VALIDATION_FOR_PAYMENT_CALLBACK_TOKEN');
INSERT INTO auth.blc_client_scopes (id, scope) values ('openapi', 'VALIDATION_FOR_PAYMENT_CALLBACK_TOKEN');

-- HYDRATED_EXTERNAL_TRANSACTION_RESULT
INSERT INTO auth.blc_security_scope (id, "name", open) values ('-890', 'HYDRATED_EXTERNAL_TRANSACTION_RESULT', 'N');
INSERT INTO auth.blc_permission_scope (id, permission, is_permission_root, scope_id) values ('-890', 'HYDRATED_EXTERNAL_TRANSACTION_RESULT', 'Y', '-890');
INSERT INTO auth.blc_client_permissions (id, permission) values ('cartopsclient', 'ALL_HYDRATED_EXTERNAL_TRANSACTION_RESULT');
INSERT INTO auth.blc_client_scopes (id, scope) values ('cartopsclient', 'HYDRATED_EXTERNAL_TRANSACTION_RESULT');

-- OpenAPI
INSERT INTO auth.blc_client_permissions (id, permission) values ('openapi', 'ALL_HYDRATED_EXTERNAL_TRANSACTION_RESULT');
INSERT INTO auth.blc_client_scopes (id, scope) values ('openapi', 'HYDRATED_EXTERNAL_TRANSACTION_RESULT');

Spring Cloud Stream Message Binding Updates

In your project, make sure to define the following properties to complete the configuration:

spring:
  cloud:
    stream:
      bindings:
        externalPaymentTransactionResultInput:
          group: cart-operation-external-payment-transaction-result
          destination: paymentTransactionWebhook
        externalPaymentTransactionCartFinalizationInput:
          group: cart-operation-external-payment-transaction-cart-finalization
          destination: externalPaymentTransactionCartFinalization