Advanced Retail Fees

Tiered fee configuration

Eclipse allows any fee to configured based on the transaction amount (e.g. tiered models) through a property that defines complex logic defined in a Javassist template.

Javassist is a flexible mechanism whereby complex business logic can be defined and compiled at a runtime – i.e. no deployment required for changes. Here is an example of logic implemented to define a tiered model for fee determination for retailer cash out:

public static java.math.BigDecimal calculateFee(java.math.BigDecimal amount, Object context) {
  	    //define tiers  
  		java.math.BigDecimal t1 = new java.math.BigDecimal("5000");
        java.math.BigDecimal t2 = new java.math.BigDecimal("10000");
        java.math.BigDecimal feeBase;
  			//if amount less than 5000 then fee is10
        if (amount.compareTo(t1) <= 0) {
            feeBase = new java.math.BigDecimal("10");
        }
  		//if amount more than 5000 and less than 10000 then fee is 30
  		else if (amount.compareTo(t2) <= 0) {
            feeBase = new java.math.BigDecimal("30");
        }
  		//if amount more than 10000 then fee is 40
  		else {
            feeBase = new java.math.BigDecimal("40");  
        }
  		//add 20% to base fee for tax to get total with tax
        java.math.BigDecimal feeTotalWithTax = feeBase.multiply(new java.math.BigDecimal("1.2"));
        return feeTotalWithTax;
}

Wallet Logic Sets

Eclipse also supports Javassist configurations for complex fee configuration covering additional use cases like:

  • Inter tenant and cross tenant transfers dependent on wallet types – e.g. a tenant want to charge to transfer from digital to card wallets, but not from digital to digital wallets.
  • Monthly wallet fees and activation or KYC fees.
  • Complex fee augmentation – e.g. once a fee is determined a tenant want to split it across different wallets for transaction fees and tax fees.
  • These can be viewed and configured through the admin portal under advanced fee configuration:

Javassist means the operator has a wide range of flexibility in terms of what fees can be configured. Here are some examples:

Charging a fee for KYC

This postTransferLogic definition checks if this is the first transaction on a wallet and if so charges a KYC fee as part of the transfer:

 /* This function charges an additional KYC as part of the first transaction on specific wallet */
 public void kycPostWalletTransfer(jakarta.persistence.EntityManager em,
			com.ukheshe.services.wallet.model.Transfer transfer, List hist,
			com.ukheshe.services.wallet.IWallet fromWallet, com.ukheshe.services.wallet.IWallet toWallet, boolean bulk,
			boolean scheduled, com.ukheshe.services.wallet.listener.logic.LogicHelper helper) {
   
    //the description to be used in the transaction
		String description = "walletID " + toWallet.getWalletId() + " KYC fee";
    
    //number of transactions on the wallet, used to determine if this is the first transaction
		int count = helper.getWalletHistoryCount(fromWallet.getTenantId(), Long.valueOf(toWallet.getWalletId()),
				description, "findWalletHistory");
   
    //KYC fee to be charged on first transaction
		BigDecimal kycFee = new BigDecimal(20);  
   
    //wallet for the fees to be transferred to
		long kycFeesWallet = 1234;
   
    //if this is the first transaction on a wallet and the destination and source wallet is not the same then we charge a KYC fee
		if (count == 0 && fromWallet.getWalletId() != toWallet.getWalletId()) {
			try {
				helper.doTransfer(em, toWallet.getWalletId(), kycFeesWallet, kycFee,
						"transfer.kyc.fee", description,
						"tfr-" + transfer.getUniqueId().substring(3, transfer.getUniqueId().length()), true, false);
			} catch (java.lang.Exception e) {
			}
		}
	}

Charging a monthly fee

This monthlyFeesLogic definition applies a monthly fee if the customer has one of the wallet types specified in the logic:

public void monthlyFee(jakarta.persistence.EntityManager em,
            com.ukheshe.services.wallet.IWallet wallet,
            com.ukheshe.services.wallet.listener.logic.LogicHelper helper) {
        BigDecimal fee = null;
        
		//Determine which wallets require a fee
		if (wallet.getWalletTypeId().intValue() == 123) {
            // Not dealing with a MPOS digital wallet
            fee = new BigDecimal("1.00");
        }
        if (wallet.getWalletTypeId().intValue() == 456) {
		    fee = new BigDecimal("2.00");
        }
        if (fee == null) {
            return;
        }

        String month = UK.DateTime.getCurrentWallClockStringInTimeZone(UK.DateTime.getUtcTimeZone(),
                DateTimeFormatter.ofPattern("yyyy-MM"));
        long monthlyFeesWallet = 1234L;
        helper.doTransfer(em, wallet.getWalletId(), monthlyFeesWallet, fee, "monthly.fee", "Monthly Fee - " + month, wallet.getWalletId() + "-monthly-fee-" + month, true, true);
    }

Applying wallet logic sets to wallet types or wallets

Wallet logic sets need to be applied to particular wallet types or wallets by setting following wallet type attributes:

  • sendTransferLogicSetId
  • receiveTransferLogicSetId
  • receiveTransferAugmentationLogicSetId
  • sendTransferAugmentationLogicSetId
  • monthlyFeeLogicSetId

📘

Note

By default if a monthlyFeeLogicSetId is set on a wallet type or wallet then the fee logic will be applied on the 1st day of the month. This can be customised by setting the wallet type attribute as follows: monthlyFeeLogicSetId.{dayOfMonth} e.g to apply monthly fee logic set 12 to a particular wallet type to run the fee logic on the 15th day of the month you would set wallet type attribute:

monthlyFeeLogicSetId.15=12

Event Triggered Retail Fees

The Eclipse retail billing engine support charging based on event triggers for ad hoc events - including charges like SMS, card pin reset, card replacement, card issuance etc.

Whenever an event is published to the retail billing service from Eclipse, events like SMS notifications, card pin reset, card replacement, card issuance etc, and these events contain walletId and tenantId identifiers, the retail billing service examines the event's configuration. Depending on the configuration, it can trigger the applicable fee charges.

The following event_types are currently supported: SMS, REPLACE_CARD, ISSUANCE_CARD, RESET_CARD_PIN

Here is an example of the retail billing configuration Javassist to charge for SMS events:

public com.ukheshe.services.retailbilling.model.RetailBillingResult calculateRetailFees(jakarta.persistence.EntityManager em, long tenantId,String eventType,java.util.Map additionalInfo,com.ukheshe.services.retailbilling.listener.logic.LogicHelper helper) {  
        long walletId=3185565l;  
        java.math.BigDecimal totalFee = java.math.BigDecimal.ZERO;  
        if ("SMS".equals(eventType) && additionalInfo != null && additionalInfo.get("data") != null && additionalInfo.get("data").toString().contains("sent")) {  
            totalFee = new BigDecimal(1);  
        }  
        return helper.createRetailBillingResult(walletId, totalFee);  
    }

##

These can be viewed and configured through the admin portal under advanced fee configuration:

Fee withdrawal

Fees are collected in real time and stored in a system wallet (essentially a suspense account) – this money can be periodically withdrawn by the tenant using the admin portal or a regular transfer from the fee wallet(s) into a designated bank account can be scheduled. This automated scheduling is configured through property: eclipse.system.wallet.eftout.config

#globally enable or disable wallet clearing
enabled=true

#tenants with wallet clearing enabled
tenantIds=12345

#wallets to be cleared for this tenant
tenant12345.sourceWalletIds=9876
  
#Sweep tenant fees for Test Tenant for tenant designated bank account
wallet9876.bankDetail=[{"att":"bankName", "val":"Acme"}, {"att":"accountHolderName", "val":"Acme"}, {"att":"accountNumber", "val":"12345"}, {"att":"branchCode", "val":"002"}]
wallet9876.clearance.dayOfWeek=0
wallet9876.clearance.hourOfDay=18
wallet9876.paymentReference=Eclipse tenant fees
wallet9876.description=Clear tenant fees