Wholesale Fees
Institutions that white label Eclipse as a fintech enablement platform need to be able to configure wholesale fees – i.e. the fees to be recovered from tenants.
Eclipse has a highly flexible wholesale billing engine:
- Chargeable transaction events are published to a wholesale event queue – this ensures that wholesale logic is decoupled from the primary transactions.
- Complex logic can be configured in Javassist specific to transaction types to determine wholesale fee logic. Javassist is a flexible mechanism whereby complex business logic can be defined and compiled at a runtime – i.e. no deployment required for changes.
- This logic includes the business rules to determine the fee amounts as well as splitting between system wallets if required (suspense accounts)
- Wholesale fees are collected in near realtime and withdrawals of these fees to an external account can be automated on a schedule.
In order to configure wholesale fee logic the following information is required:
- The wallet from where tenant fees should be paid. This operational wallet can be configured to allow negative balances or minimum balance can be set to zero, mandating prefunding.
- The event type of this fee - currently supported types are Wallet-Transfer, PAYMENT and WITHDRAWAL
- The transaction type to be charged – e.g. DTB_KE_PESALINK
- The Javassist logic that determines the fee to be charged as well as the manner in which the fee should be split and the system wallets (suspense accounts) where those fees should be collected.
Here is an example Javassist template that allows admins to define:
- The destination fee wallets and fee split per wallet.
- The fee type: fixedAmount, fixedPercentage, TieredAmount, TieredPercentage
- The tier logic for tieredAmount and tieredPercentage billing
- An administrator can complete the sections marked complete for a particular tenant and then load as a wholesale configuration using the admin portal.
public com.ukheshe.services.wholesalebilling.model.WholesaleBillingResult calculateWholesaleFees(jakarta.persistence.EntityManager em, long tenantId,
java.math.BigDecimal amount,
long debitFeeWalletId, String uniqueId,
long transactedWalletId,
com.ukheshe.services.wholesalebilling.listener.logic.LogicHelper helper) {
long revShareWallet1 = 1234; //TO COMPLETE
long revShareWallet2 = 1234; //TO COMPLETE
long transactionCostWallet = 1234; //TO COMPLETE
long taxWallet = 1234; //TO COMPLETE
String feeType = "FixedAmount"; //TO COMPLETE - possibly values: FixedAmount, FixedPercentage, TieredAmount, TieredPercentage
String revShareWallet1FeeConfig = "40P";
String revShareWallet2FeeConfig = "40P";
String transactionCostWalletFeeConfig = "20P";
String taxWalletFeeConfig = "15P"; //15% OF ACTUAL FEE
com.ukheshe.services.wholesalebilling.model.WholesaleBillingResult result = new com.ukheshe.services.wholesalebilling.model.WholesaleBillingResult();
java.math.BigDecimal wholesaleFee = java.math.BigDecimal.ZERO;
java.math.BigDecimal totalFee = java.math.BigDecimal.ZERO;
//LOGIC to exclude system wallets from wholesale fees
if(transactedWalletId == revShareWallet1 || transactedWalletId == revShareWallet2 || transactedWalletId == transactionCostWallet || transactedWalletId == taxWallet) {
return result;
}
//LOGIC FOR FixedAmount
if ("FixedAmount".equals(feeType)) {
String fixedAmountConfig = "10A"; //TO COMPLETE
wholesaleFee = helper.calculateFee(fixedAmountConfig, amount);
//LOGIC FOR FixedPercentage
} else if ("FixedPercentage".equals(feeType)) {
String fixedPercentageConfig = "5P"; //TO COMPLETE
wholesaleFee = helper.calculateFee(fixedPercentageConfig, amount);
//LOGIC FOR TieredAmount
} else if ("TieredAmount".equals(feeType)) {
if (amount.intValue() >= 0 && amount.intValue() <= 1000) {
String tieredAmountConfig = "10A"; //TO COMPLETE
wholesaleFee = helper.calculateFee(tieredAmountConfig, amount);
} else if (amount.intValue() > 1000 && amount.intValue() <= 2000) {
String tieredAmountConfig = "20A"; //TO COMPLETE
wholesaleFee = helper.calculateFee(tieredAmountConfig, amount);
} else if (amount.intValue() > 2000) {
String tieredAmountConfig = "30A"; //TO COMPLETE
wholesaleFee = helper.calculateFee(tieredAmountConfig, amount);
}
//LOGIC FOR TieredPercentage
} else if ("TieredPercentage".equals(feeType)) {
if (amount.intValue() >= 0 && amount.intValue() <= 100) {
String tieredPercentageConfig = "0P"; //TO COMPLETE
wholesaleFee = helper.calculateFee(tieredPercentageConfig, amount);
} else if (amount.intValue() > 100 && amount.intValue() <= 1500) {
String tieredPercentageConfig = "1P"; //TO COMPLETE
wholesaleFee = helper.calculateFee(tieredPercentageConfig, amount);
} else if (amount.intValue() > 1500 && amount.intValue() <= 2500) {
String tieredPercentageConfig = "3P"; //TO COMPLETE
wholesaleFee = helper.calculateFee(tieredPercentageConfig, amount);
} else if (amount.intValue() > 2500 && amount.intValue() <= 3500) {
String tieredPercentageConfig = "5P"; //TO COMPLETE
wholesaleFee = helper.calculateFee(tieredPercentageConfig, amount);
} else if (amount.intValue() > 3500) {
String tieredPercentageConfig = "6P"; //TO COMPLETE
wholesaleFee = helper.calculateFee(tieredPercentageConfig, amount);
}
}
java.math.BigDecimal revShare1 = helper.calculateFee(revShareWallet1FeeConfig, wholesaleFee);
java.math.BigDecimal revShare2 = helper.calculateFee(revShareWallet2FeeConfig, wholesaleFee);
java.math.BigDecimal transactionCost = helper.calculateFee(transactionCostWalletFeeConfig, wholesaleFee);
java.math.BigDecimal taxFee = helper.calculateFee(taxWalletFeeConfig, wholesaleFee);
return helper.createWholesaleBillingResult(tenantId, debitFeeWalletId, revShareWallet1,
revShare1, revShareWallet2, revShare2,
transactionCostWallet, transactionCost, taxWallet, taxFee);
}
- Load the final wholesale billing configuration in the admin portal. Note this can also be loaded using the wholesale configuration endpoint.
Wholesale Fee withdrawal
Fees are collected in near real time and stored in system wallet(s) that represent suspense accounts – these wallets can be automatically cleared on a schedule to an external account. This automated scheduling is configured through property: eclipse.system.wallet.eftout.config
Scheduling withdrawals
We support the following parameters for scheduling basic sweeps based on time and the day of the week:
- wallet{walletId}.clearance.dayOfWeek - refers to the day when a wallet withdrawal should happen. 1-7 maps to Monday to Sunday. 0 means everyday.
- wallet{walletId}.clearance.hourOfDay - refers to the hour in 24 hour notation for when a wallet withdrawal should happen on a day. e.g. 18 means a sweep scheduled for that day will happen at 18:00.
For more advanced schedule configuration we support cron expressions. A cron expressions is an expression language that allow you to concisely define complex schedules - for more details, including examples, please refer here:
- wallet{walletId}.clearance.cron - cron expression to determine the schedule of the withdrawal.
#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,5432
#Sweep wholesale fees for Test Tenant for institution 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 wholesale fees
wallet9876.description=Clear wholesale fees
#Sweep wholesale fees for Test Tenant for institution designated bank account
wallet5432.bankDetail=[{"att":"bankName", "val":"Acme"}, {"att":"accountHolderName", "val":"Acme"}, {"att":"accountNumber", "val":"12345"}, {"att":"branchCode", "val":"002"}]
wallet5432.clearance.cron=0 * * * *
wallet5432.paymentReference=Eclipse wholesale fees
wallet5432.description=Clear wholesale fees
Updated 6 months ago