In some checkout scenarios, you can be left with a successful payment transaction and an overall failed checkout. For example, if you have 2 payments, the first payment’s transaction is successful, and the second payment’s transaction fails. In this case, the successful transaction should eventually be reversed, if it’s not used for a subsequent successful checkout.
To support this behavior, we use the CheckoutRollbackEventListener
to mark the successful transactions as reversal candidates (i.e. managementState = "REVERSAL_CANDIDATE"
). These transactions are identified by the payment’s related cart id & the checkout attempt’s requestId
.
From there, the PaymentTransactionReversalJobListener
is responsible for identifying reversal candidate transactions and executing reversal transactions. By default, this scheduled job is configured to execute every 5 minutes, and uses a 15 minutes buffer for identifying transactions. Therefore, reversal candidate transactions have a 15-minute window where they can still contribute to a successful checkout attempt.
Note
|
|
To avoid a race condition where the CheckoutRollbackEventListener
and the customer’s second checkout attempt don’t negatively effect each other, we take the following steps:
Lock the payment when taking action against its transaction(s)
When reusing a successful transaction for the second checkout attempt, we update the transaction’s requestId and clear its managementState
(previously blank or "REVERSAL_CANDIDATE"
). Therefore, if the CheckoutRollbackEvent
is processed after this checkout attempt, then it won’t find the successful transaction to mark it as a reversal candidate. NOTE: This is done via PaymentTransactionExecutionActivity
(CartOperationServices) calling PaymentTransactionManagementEndpoint#claimTransactionsForRequest(…)
(PaymentTransactionServices).
Note
|
When a checkout successfully completed, the cart’s payment transactions have their managementState set to "AUTOMATIC_REVERSAL_NOT_ALLOWED" to block the reversal of the transactions.
|