Balance Transactions

The Balance Transaction feature provides a generic balance mutation API that can be used to build product-specific transaction workflows. The API offers fine-granular balance operations with default (but customizable) accounting behavior.

👋

It is crucial that the workflows that use BalanceTransactions apply proper access control! The BalanceTransaction methods that allow balance mutations require system context, and do not provide any more specific access control, because this is always specific for the business process using it.

It is a key feature to provide proper decoupling of custom transaction workflows and consistent balance mutations. Each BalanceTransaction has a Transaction Owner, which refers to a custom transaction using a TypedID (weak reference).

The following operations are available in the service BalanceTransactionService:

  • Reserve - reserves an amount on a WalletAccount, and creates a WalletAccountItem if needed
  • AdjustReservation - adjusts the amount of an existing Reserve (higher or lower), including the associated WalletAccountItem . The Reserve BalanceTransaction is updated (no new BalanceTransaction is created)
  • Authorize - directly transfers an amount from a WalletAccount to another account (either WalletAccount or system-account), and creates a WalletAccountItem if needed
  • Capture - releases a Reserved amount and transfers it to another account (either WalletAccount or system-account), and creates a WalletAccountItem if needed. A new BalanceTransaction is created for each Capture, associated with the Reserve BalanceTransaction. Either the full amount or a partial amount can be captured. The following modes determine how to deal with the Reserve:
    • ReleaseFullReservation - the full reservation is released, even if only a partial amount is captured
    • KeepRemainingReservation - only the captured amount is released, any remaining amount remains reserved
  • Cancel - cancels an existing BalanceTransaction, and reverts all emoney transfers associated with it. A BalanceTransaction cannot be cancelled, if there are any non-cancelled Refunds associated with it.
  • Refund - refunds a Captured or Authorized amount, and transfers the emoney back to the original WalletAccount, and creates a WalletAccountItem if needed. A new BalanceTransaction is created for each Refund, associated with the Capture or Authorize BalanceTransaction.
  • Credit - transfers an amount from another account (either WalletAccount or system-account) to a WalletAccount, and creates a WalletAccountItem if needed.

The following table shows which operations are allowed for which BalanceTransactions:

Allowed?

Operation

Balance Transaction

Adjust Reservation

Capture

Refund

Cancel

Reserve

Yes

Yes

No

Yes

Capture

No

No

Yes

Yes*

Authorize

No

No

Yes

Yes*

Refund

No

No

No

Yes

Credit

No

No

No

Yes

* = A BalanceTransaction cannot be cancelled, if there are any non-cancelled Refunds associated with it.

The type parameter for the BalanceTransaction determines which accounting-handler will be used for the emoney-transfers. The subtype can be used to identify multiple variations, e.g. for the wallet-account-items.

The wallet-accounting-configuration must be setup properly to support each BalanceTransaction based on the type/subtype. The corresponding transaction-type is composed as follows: BalanceTransaction/{type}/{subtype}

The following diagram shows how the various balance-transactions are related.

Several optional parameters allow to fine-tune the actual behavior:

  • minimumRemainingBalance - determines the expected remaining balance on the WalletAccount after the emoney is transferred. If not specified, there is no balance restriction (thus the balance can also become negative). If specified, the accounting-handler will use an exclusive balance check to ensure the balance-restriction is enforced.
  • minimumRemainingReservedAmount: - specifically used for capture requests, allows to capture more than the reserved amount.
    • Default is 0 which allows us to capture a maximum of the reserved amount.
    • When value is null allows us to capture more than the reserved amount.
    • If the value is positive then the remaining reservation amount must not exceed the specified value.
    • If the value is negative then allows over-capture up to maximum of the specified amount, negative amount determines the maximum allowed over-capture amount.
  • categoryCode - determines the type of emoney that can be used for this particular transfer. If not specified, it defaults to Unrestricted, so that all emoney-types can be used.
  • externalId - if given, enforces a uniqueness of this externalId in the scope of the transaction-owner. This can be used to prevent accidental double captures/refunds, e.g. due to technical errors, retries, etc.

There is a default implementation for the AccountingHandler (DefaultBalanceTransactionAccountingHandler), which is configurable to some extent:

  • transferModeDebit - determines the default transfer-mode for debit-transactions, which defaults to ErrorOnOverdraw
  • transferModeCredit - determines the default transfer-mode for credit-transactions, which defaults to ErrorOnOverdraw
  • transactionType - determines for which BalanceTransaction-types this AccountingHandler will be used, which defaults to 'BalanceTransaction.Default'

It can also be subclassed to customize behavior, e.g. provide additional logic to determine the transfer-mode, have additional logic during accounting-transfers, customize WalletAccountItem creation, etc.

The default implementation can be registered for a particular BalanceTransaction-type (e.g. MyType) to the dispatcher using the following XML snippet in the Spring application context:

<bean id="product.impl.BalanceTransactionAccountingHandlerRegistry" class="com.trimplement.wallet.server.wallet.impl.transaction.balance.accounting.handler.BalanceTransactionAccountingHandlerRegistry" lazy-init="false">
    <property name="dispatchingBalanceTransactionAccountingHandler" ref="wallet.impl.DispatchingBalanceTransactionAccountingHandler" />
    <property name="balanceTransactionAccountingHandlers">
        <list>
            <bean class="com.trimplement.wallet.server.wallet.impl.transaction.balance.accounting.handler.DefaultBalanceTransactionAccountingHandler" parent="wallet.impl.DefaultBalanceTransactionAccountingHandler">
                <property name="transactionType" value="MyType" />
            </bean>
        </list>
    </property>
</bean>