Broadleaf Microservices
  • v1.0.0-latest-prod

Payment Management

Payment Management Endpoints

Payment management endpoints can be split into two categories: customer payment management interactions vs system payment management interactions.

Customer Payment Management (CustomerPaymentManagementEndpoint.java)

  • Meant for customer-driven interactions. For example: adding/updating payments within your checkout flow.

  • Note: the update endpoint includes additional logic to simplify client-side actions. More on this in the Updating a Payment section below.

  • Endpoints

    • POST /customer-payments

    • GET /customer-payments

    • PATCH /customer-payments/{id}

    • DELETE /customer-payments/{id}

System Payment Management (PaymentManagementEndpoint.java)

  • Meant for service-to-service CRUD interactions. Examples:

    • CartOperationServices uses the GET /payments in the checkout workflow when validating the cart’s payments

    • OrderOperationServices uses the GET /payments when identifying the payments to charge for a fulfillment

  • Endpoints

    • POST /payments

    • GET /payments

    • GET /payments/{id}

    • PATCH /payments/{id}

    • DELETE /payments/{id}

For more details on these endpoints, please review the PaymentTransactionServices OpenAPI Docs.

Getting Payments

In most use cases, payments are gathered by owner type & id. For example, gathering all payments related to a cart by providing owner type = BLC_CART & owner id = {cart.id}.

Optionally, the transactions within the payments can be narrowed down further by providing the source entity type & id. For example, let’s say we have a payment that has two Capture transactions, one for OrderFulfillment1 and the other for OrderFulfillment2. To gather payments with transaction data only relevant to OrderFulfillment2, we can provide source entity type = ORDER_FULFILLMENT & source entity id = {orderFulfillment.id}.

Gathering payments as a customer (i.e. via the CustomerPaymentManagementEndpoint.java) includes additional validation to ensure that the customer has access to the payment(s). For more detail on these restrictions, see the Customer Access Restrictions section below.

Adding a Payment

The CustomerPaymentManagementEndpoint and PaymentManagementEndpoint both make use of CreatePaymentRequest.java

  • Notable fields:

    • ownerType - Describes the owner of the payment. For example: BLC_CART, if the payment is related to a cart.

    • ownerId - The id of the entity that owns this payment. For example: a cart id.

    • gatewayType - String identifier for the related payment gateway.

    • amount - The amount for which the payment is allotted.

    • paymentMethodProperties - Map to capture any information about the payment method needed to perform gateway transactions.

Note
The only difference between the CustomerPaymentManagementEndpoint and the PaymentManagementEndpoint, is that the CustomerPaymentManagementEndpoint populates Payment#customerRef using the authenticated customer.
Note
See the Using a Saved Payment Method section for detail on creating a payment based on a saved payment method.

Updating a Payment

Updating a payment is significantly different for customer vs system-to-system interactions, so we’ll discuss them independently.

Using CustomerPaymentManagementEndpoint to Update a Payment

Customer-driven updates use the UpdatePaymentRequest which closely parallels the CreatePaymentRequest, except that you aren’t allowed to redefine the ownerType & ownerId.

This endpoint makes use of additional logic meant to simplify client-side actions, including:

  • If the payment does not include transaction history, then simply update the payment.

  • If the payment includes transaction history, then clone the payment (excluding transaction data), apply the updates to the new payment, & archive the old payment.

    • In the end, we’ll be left with an archived (and otherwise unmodified) payment with transaction history, and an updated payment that the customer can continue to use.

    • This allows customers to execute simple update requests, while enabling us to maintain accurate transaction history.

Using PaymentManagementEndpoint to Update a Payment

The SystemUpdatePaymentRequest is a simple extension of the UpdatePaymentRequest, including the following fields that can only be provided by system-to-system/service-to-service interactions:

  • markTransactionsIneligibleForAutomaticReversal - Declares whether the payment’s transactions should be marked ineligible for automatic reversal via the Transaction Reversal Jobs.

    • Default value: false

  • accessRestrictions - The list of restrictions that apply to payment access. For more detail on these restrictions, see the Customer Access Restrictions section below.

Note
markTransactionsIneligibleForAutomaticReversal and accessRestrictions are primarily used by CartOperationServices to restrict the customer’s access and the automatic reversal of checkout-related transactions, once a successful checkout has been completed.

Archiving a Payment

Archiving a payment is relatively simple. First, we validate that the provided payment version is up-to-date. Additionally, if the customer is attempting to archive a payment, then we validate that they are the owner of the payment. From there, we can simply archive the payment.

Note
If the payment has successful Authorize or AuthorizeAndCapture transactions, then we must schedule these transactions for reversal. See the Updating or Archiving Payments with Successful Transactions section below for more detail.

Payment Validation Logic

When adding or updating a payment, the following validation is executed to ensure that down-stream processes (like executing transactions) don’t have to concerned about the validity of the payment data:

  • Validate the provided payment version to ensure that we’re not acting against out-of-date payment data

  • For customer-driven update interactions only: Validate that the customer has access to update the payment. For more detail on these restrictions, see the Customer Access Restrictions section below.

  • Validate that a payment gateway is registered with the same gatewayType

  • Validate that the payment amount fields

    • amount must not be null and must be greater than 0

    • If provided, subtotal, adjustmentsTotal, fulfillmentTotal, & taxTotal must be greater than 0

  • Validate that if we’re using a saved payment method, then we aren’t also requesting that the payment be saved for future use

  • Validate the paymentMethodProperties

    • Each payment gateway requires different values in the paymentMethodProperties map. Therefore, a PaymentGatewayPaymentValidator implementation (identified by the gatewayType) is used to validate that sufficient data has been provided in the paymentMethodProperties map.

Updating or Archiving Payments with Successful Transactions

If a payment that has successful Authorize or AuthorizeAndCapture transactions is archived (i.e. we declare that it will no longer be used), then we must also reverse any outstanding Authorize or AuthorizeAndCapture amounts, to ensure that the customer’s funds are not unnecessarily reserved/collected.

If a payment that has successful Authorize or AuthorizeAndCapture transactions has its amount updated, then we also look to reverse any outstanding Authorize or AuthorizeAndCapture amounts. This is done to simplify the amount used for any subsequent Authorize or AuthorizeAndCapture transactions against the payment.

In both scenarios, PaymentTransaction#managementState is set to REQUIRES_REVERSAL for each of the successful Authorize or AuthorizeAndCapture transactions. From there, the transaction reversal job handles the Reverse-Authorize or Refund transaction.

Payment TTL

To protect the payment data of anonymous customers, we introduced a payment time-to-live concept. Each anonymous payment is given duration in which the payment is considered active. When the customer attempts to access this payment, we call DefaultPaymentTTLValidationService#validatePaymentTTL() to check if the duration has been exceeded. * If the duration has been exceeded, then the payment is archived and a PaymentTTLException is thrown. From this point on, the payment can no longer be gathered or used. * If the duration has not been exceeded, then the payment is still valid and the anonymous user can still access the payment.

Customer Access Restrictions

To restrict a customer’s ability to access/modify their payments, we added an access restriction concept (PaymentManagementAccessRestrictions) to the payment domain. These restrictions allow the payments to be modified or accessed only in certain contexts. By default, these values are:

  • CUSTOMER_MUTABILITY_BLOCKED

    • This restriction signifies that the customer no longer has access to modify the payment. This can be added to the collection of payment restrictions anytime a customer should be blocked from mutating the payment. For example, this restriction should be added after completing a successful checkout to ensure that the customer doesn’t modify their cart’s payments.

  • CSR_OWNED

    • This restriction signifies that the cart, and therefore also the payments, are currently owned by a CSR user, and the customer cannot modify these payments.

  • TEST_USER_OWNED

    • This restriction signifies that the cart, and therefore also the payments, were created in from the admin preview mode, and are not eligible for checkout.

In order for an external service to apply these restrictions, we’ve added endpoint methods to the PaymentManagementEndpoint.

  • To finalize the payments following a successful checkout, you would call PaymentManagementEndpoint#finalizePayments(). This marks the payments with CUSTOMER_MUTABILITY_BLOCKED and makes them unable to be reversed.

  • To transfer the payments to a CSR, you would call PaymentManagementEndpoint#transferPaymentsToCSR(). This assigns the CSR_OWNED access restriction to the payments, making them no longer accessible by the customer.

  • To transfer the payments to a customer, you would call PaymentManagementEndpoint#transferPaymentsFromCSR(). This removes the CSR_OWNED access restriction from the payments.