Comprehensive Guide: How to Make a Java Program to Calculate Charges
Creating a Java program to calculate charges is a fundamental skill for developers working on financial applications, e-commerce platforms, or any system that processes transactions. This guide will walk you through the complete process of building a robust charge calculator in Java, from basic implementations to advanced features.
1. Understanding Charge Calculation Fundamentals
Before writing code, it’s essential to understand the different types of charge calculations:
- Percentage-based charges: Common in credit card processing (e.g., 2.9% + $0.30)
- Flat fees: Fixed amounts regardless of transaction size (e.g., $5 service fee)
- Tiered pricing: Different rates for different amount ranges (e.g., 1% for first $1000, 0.5% above)
- Conditional charges: Fees that apply based on specific criteria (e.g., rush processing fee)
- Compound charges: Multiple charge types combined (e.g., percentage + flat fee)
2. Basic Java Implementation
Let’s start with a simple percentage-based charge calculator:
public class BasicChargeCalculator {
public static double calculatePercentageCharge(double amount, double percentage) {
// Validate inputs
if (amount < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
if (percentage < 0 || percentage > 100) {
throw new IllegalArgumentException(“Percentage must be between 0 and 100”);
}
// Calculate and return the charge
return amount * (percentage / 100);
}
public static void main(String[] args) {
double amount = 150.00;
double percentage = 5.5; // 5.5%
double charge = calculatePercentageCharge(amount, percentage);
double total = amount + charge;
System.out.printf(“Base Amount: $%.2f%n”, amount);
System.out.printf(“Charge (%.2f%%): $%.2f%n”, percentage, charge);
System.out.printf(“Total Amount: $%.2f%n”, total);
}
}
This basic implementation:
- Takes an amount and percentage as input
- Validates the inputs to prevent negative values
- Calculates the charge by multiplying amount by percentage
- Returns the calculated charge
- Includes a main method for demonstration
3. Advanced Charge Calculator with Multiple Types
For a more comprehensive solution, we can create a calculator that handles multiple charge types:
public enum ChargeType {
PERCENTAGE,
FLAT,
TIERED,
COMPOUND
}
public class AdvancedChargeCalculator {
public static double calculateCharge(double amount, ChargeType type, Object… params) {
switch (type) {
case PERCENTAGE:
double percentage = (double) params[0];
return amount * (percentage / 100);
case FLAT:
double flatAmount = (double) params[0];
return flatAmount;
case TIERED:
// Expecting an array of tier thresholds and rates
double[][] tiers = (double[][]) params[0];
double charge = 0;
double remaining = amount;
for (double[] tier : tiers) {
double threshold = tier[0];
double rate = tier[1];
if (remaining <= 0) break;
double amountInTier = Math.min(remaining, threshold);
charge += amountInTier * (rate / 100);
remaining -= amountInTier;
}
return charge;
case COMPOUND:
// First param is percentage, second is flat fee
double pct = (double) params[0];
double flat = (double) params[1];
return (amount * (pct / 100)) + flat;
default:
throw new IllegalArgumentException("Unknown charge type");
}
}
public static void main(String[] args) {
double amount = 2500.00;
// Percentage example (3%)
double pctCharge = calculateCharge(amount, ChargeType.PERCENTAGE, 3.0);
System.out.printf("Percentage Charge: $%.2f%n", pctCharge);
// Flat fee example ($15)
double flatCharge = calculateCharge(amount, ChargeType.FLAT, 15.0);
System.out.printf("Flat Charge: $%.2f%n", flatCharge);
// Tiered example: 2% on first $1000, 1% on next $1000, 0.5% above
double[][] tiers = {{1000, 2.0}, {1000, 1.0}, {Double.MAX_VALUE, 0.5}};
double tieredCharge = calculateCharge(amount, ChargeType.TIERED, tiers);
System.out.printf("Tiered Charge: $%.2f%n", tieredCharge);
// Compound example: 2.5% + $5
double compoundCharge = calculateCharge(amount, ChargeType.COMPOUND, 2.5, 5.0);
System.out.printf("Compound Charge: $%.2f%n", compoundCharge);
}
}
4. Handling Edge Cases and Validation
Robust charge calculators must handle various edge cases:
| Edge Case |
Potential Issue |
Solution |
| Negative amounts |
Could result in negative charges |
Validate and throw IllegalArgumentException |
| Zero amount |
Division by zero risks in some formulas |
Return zero charge for zero amount |
| Extremely large amounts |
Potential integer overflow |
Use double or BigDecimal for monetary values |
| Invalid percentages |
Percentages > 100 or < 0 |
Validate range (0-100) |
| Missing parameters |
NullPointerException risks |
Null checks and default values |
| Currency conversion |
Precision loss in conversions |
Use BigDecimal with proper rounding |
Here’s an enhanced version with comprehensive validation:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class RobustChargeCalculator {
private static final int DEFAULT_SCALE = 2;
private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_UP;
public static BigDecimal calculateCharge(BigDecimal amount, ChargeType type, Object… params)
throws IllegalArgumentException {
// Validate base amount
if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Amount cannot be null or negative");
}
switch (type) {
case PERCENTAGE:
if (params.length < 1 || !(params[0] instanceof Number)) {
throw new IllegalArgumentException("Percentage value required");
}
BigDecimal percentage = BigDecimal.valueOf(((Number) params[0]).doubleValue());
if (percentage.compareTo(BigDecimal.ZERO) < 0 ||
percentage.compareTo(BigDecimal.valueOf(100)) > 0) {
throw new IllegalArgumentException(“Percentage must be between 0 and 100”);
}
return amount.multiply(percentage)
.divide(BigDecimal.valueOf(100), DEFAULT_SCALE, DEFAULT_ROUNDING);
case FLAT:
if (params.length < 1 || !(params[0] instanceof Number)) {
throw new IllegalArgumentException("Flat amount required");
}
BigDecimal flatAmount = BigDecimal.valueOf(((Number) params[0]).doubleValue());
if (flatAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Flat amount cannot be negative");
}
return flatAmount;
// Additional cases would go here...
default:
throw new IllegalArgumentException("Unsupported charge type");
}
}
public static void main(String[] args) {
try {
BigDecimal amount = new BigDecimal("1250.65");
BigDecimal charge = calculateCharge(
amount,
ChargeType.PERCENTAGE,
3.75
);
System.out.println("Base Amount: $" + amount);
System.out.println("Charge: $" + charge);
System.out.println("Total: $" + amount.add(charge));
} catch (IllegalArgumentException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
5. Implementing Tiered Pricing Logic
Tiered pricing is common in many financial systems. Here’s a detailed implementation:
public class TieredChargeCalculator {
public static class Tier {
private final BigDecimal threshold;
private final BigDecimal rate;
public Tier(BigDecimal threshold, BigDecimal rate) {
this.threshold = threshold;
this.rate = rate;
}
// Getters omitted for brevity
}
public static BigDecimal calculateTieredCharge(BigDecimal amount, List tiers) {
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
if (tiers == null || tiers.isEmpty()) {
throw new IllegalArgumentException("At least one tier required");
}
// Sort tiers by threshold (ascending)
tiers.sort(Comparator.comparing(t -> t.threshold));
BigDecimal remaining = amount;
BigDecimal totalCharge = BigDecimal.ZERO;
for (Tier tier : tiers) {
if (remaining.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
BigDecimal amountInTier = remaining.min(tier.threshold);
BigDecimal tierCharge = amountInTier.multiply(tier.rate)
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
totalCharge = totalCharge.add(tierCharge);
remaining = remaining.subtract(amountInTier);
}
return totalCharge;
}
public static void main(String[] args) {
List tiers = Arrays.asList(
new Tier(new BigDecimal(“1000.00”), new BigDecimal(“2.5”)), // 2.5% on first $1000
new Tier(new BigDecimal(“1500.00”), new BigDecimal(“1.8”)), // 1.8% on next $1500
new Tier(new BigDecimal(“5000.00”), new BigDecimal(“1.2”)), // 1.2% on next $5000
new Tier(BigDecimal.valueOf(Double.MAX_VALUE), new BigDecimal(“0.8”)) // 0.8% above $7500
);
BigDecimal amount = new BigDecimal(“12500.00”);
BigDecimal charge = calculateTieredCharge(amount, tiers);
System.out.printf(“Amount: $%,.2f%n”, amount);
System.out.printf(“Charge: $%,.2f%n”, charge);
System.out.printf(“Effective Rate: %.2f%%%n”,
charge.divide(amount, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100)));
}
}
6. Creating a User Interface for the Calculator
While our interactive calculator above uses HTML/JavaScript, you can also create a Java GUI using Swing or JavaFX. Here’s a Swing example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class ChargeCalculatorGUI {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame(“Java Charge Calculator”);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 400);
frame.setLayout(new BorderLayout(10, 10));
// Input panel
JPanel inputPanel = new JPanel(new GridLayout(0, 2, 10, 10));
inputPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
JTextField amountField = new JTextField();
JComboBox typeCombo = new JComboBox<>(ChargeType.values());
JTextField param1Field = new JTextField();
JTextField param2Field = new JTextField();
JTextArea resultArea = new JTextArea(5, 20);
resultArea.setEditable(false);
inputPanel.add(new JLabel(“Amount:”));
inputPanel.add(amountField);
inputPanel.add(new JLabel(“Charge Type:”));
inputPanel.add(typeCombo);
inputPanel.add(new JLabel(“Parameter 1:”));
inputPanel.add(param1Field);
inputPanel.add(new JLabel(“Parameter 2:”));
inputPanel.add(param2Field);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
JButton calculateButton = new JButton(“Calculate”);
buttonPanel.add(calculateButton);
// Result panel
JPanel resultPanel = new JPanel(new BorderLayout());
resultPanel.setBorder(BorderFactory.createTitledBorder(“Results”));
resultPanel.add(new JScrollPane(resultArea), BorderLayout.CENTER);
// Add components to frame
frame.add(inputPanel, BorderLayout.NORTH);
frame.add(buttonPanel, BorderLayout.CENTER);
frame.add(resultPanel, BorderLayout.SOUTH);
// Calculate button action
calculateButton.addActionListener((ActionEvent e) -> {
try {
BigDecimal amount = new BigDecimal(amountField.getText());
ChargeType type = (ChargeType) typeCombo.getSelectedItem();
Object[] params = new Object[2];
try {
params[0] = param1Field.getText().isEmpty() ?
0 : new BigDecimal(param1Field.getText());
} catch (NumberFormatException ex) {
params[0] = 0;
}
try {
params[1] = param2Field.getText().isEmpty() ?
0 : new BigDecimal(param2Field.getText());
} catch (NumberFormatException ex) {
params[1] = 0;
}
BigDecimal charge = AdvancedChargeCalculator.calculateCharge(
amount, type, params
);
BigDecimal total = amount.add(charge);
BigDecimal rate = charge.divide(amount, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
resultArea.setText(String.format(
“Base Amount: $%,.2f\n” +
“Charge Type: %s\n” +
“Calculated Charge: $%,.2f\n” +
“Total Amount: $%,.2f\n” +
“Effective Rate: %,.2f%%”,
amount, type, charge, total, rate
));
} catch (Exception ex) {
resultArea.setText(“Error: ” + ex.getMessage());
}
});
frame.setVisible(true);
});
}
}
7. Performance Considerations
When building charge calculators for high-volume systems, consider these performance optimizations:
| Consideration |
Impact |
Solution |
| Floating-point precision |
Monetary calculations can lose precision with float/double |
Use BigDecimal for all monetary values |
| Object creation |
Frequent BigDecimal creation can impact performance |
Reuse common values (e.g., BigDecimal.ZERO) and pool objects |
| Rounding operations |
Custom rounding can be expensive |
Pre-define RoundingMode constants |
| Tiered calculations |
Complex tier logic can be slow for many tiers |
Pre-sort tiers and optimize loop conditions |
| Concurrent access |
Race conditions in multi-threaded environments |
Make calculator methods stateless or thread-safe |
| Caching |
Repeated calculations with same inputs |
Implement memoization for common calculations |
Here’s an optimized version with some of these considerations:
public class OptimizedChargeCalculator {
private static final BigDecimal ZERO = BigDecimal.ZERO;
private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);
private static final RoundingMode ROUNDING = RoundingMode.HALF_UP;
private static final int SCALE = 2;
// Cache for common percentage values
private static final Map PERCENTAGE_CACHE = new ConcurrentHashMap<>();
public static BigDecimal calculateOptimizedCharge(
BigDecimal amount,
ChargeType type,
Object… params) {
if (amount.compareTo(ZERO) < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
switch (type) {
case PERCENTAGE:
BigDecimal percentage = ((Number) params[0]).doubleValue() <= 0 ?
ZERO : BigDecimal.valueOf(((Number) params[0]).doubleValue());
// Check cache first
BigDecimal cached = PERCENTAGE_CACHE.get(percentage);
if (cached != null) {
return amount.multiply(cached).divide(HUNDRED, SCALE, ROUNDING);
}
// Calculate and cache
BigDecimal multiplier = percentage.divide(HUNDRED, 6, ROUNDING);
PERCENTAGE_CACHE.put(percentage, multiplier);
return amount.multiply(multiplier).setScale(SCALE, ROUNDING);
// Other cases would be similarly optimized...
default:
throw new IllegalArgumentException("Unsupported charge type");
}
}
}
8. Testing Your Charge Calculator
Comprehensive testing is crucial for financial calculations. Use JUnit to test various scenarios:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.math.BigDecimal;
class ChargeCalculatorTest {
@Test
void testPercentageCharge() {
BigDecimal amount = new BigDecimal(“1000.00”);
BigDecimal expected = new BigDecimal(“50.00”);
BigDecimal result = BasicChargeCalculator.calculatePercentageCharge(
amount, new BigDecimal(“5.0”)
);
assertEquals(0, expected.compareTo(result),
“Percentage calculation should match expected value”);
}
@Test
void testNegativeAmount() {
assertThrows(IllegalArgumentException.class, () -> {
BasicChargeCalculator.calculatePercentageCharge(
new BigDecimal(“-100.00”), new BigDecimal(“5.0”)
);
}, “Negative amount should throw exception”);
}
@Test
void testTieredCalculation() {
BigDecimal amount = new BigDecimal(“2500.00”);
List tiers = Arrays.asList(
new TieredChargeCalculator.Tier(new BigDecimal(“1000.00”), new BigDecimal(“2.0”)),
new TieredChargeCalculator.Tier(new BigDecimal(“1500.00”), new BigDecimal(“1.0”))
);
BigDecimal result = TieredChargeCalculator.calculateTieredCharge(amount, tiers);
BigDecimal expected = new BigDecimal(“40.00”); // (1000*0.02) + (1500*0.01)
assertEquals(0, expected.compareTo(result),
“Tiered calculation should match expected value”);
}
@Test
void testEdgeCases() {
// Test zero amount
assertEquals(BigDecimal.ZERO,
BasicChargeCalculator.calculatePercentageCharge(BigDecimal.ZERO, new BigDecimal(“5.0”)));
// Test very large amount
BigDecimal largeAmount = new BigDecimal(“1000000000.00”);
BigDecimal result = BasicChargeCalculator.calculatePercentageCharge(
largeAmount, new BigDecimal(“0.1”)
);
assertEquals(new BigDecimal(“1000000.00”), result);
}
}
9. Integrating with Payment Systems
When integrating your charge calculator with real payment systems, consider these best practices:
- Idempotency: Ensure repeated calculations with the same inputs produce identical results
- Auditing: Log all calculation parameters and results for compliance
- Round-trip testing: Verify that calculated amounts match what payment processors expect
- Currency handling: Support multiple currencies with proper conversion rates
- Tax calculations: Separate tax calculations from service charges when required
- Regulatory compliance: Ensure calculations meet financial regulations for your industry
Example integration with a payment processor:
public class PaymentProcessorIntegration {
private final ChargeCalculator calculator;
private final TaxCalculator taxCalculator;
private final AuditLogger auditLogger;
public PaymentProcessorIntegration() {
this.calculator = new AdvancedChargeCalculator();
this.taxCalculator = new StandardTaxCalculator();
this.auditLogger = new DatabaseAuditLogger();
}
public PaymentResult processPayment(
BigDecimal amount,
ChargeType chargeType,
Object[] chargeParams,
String currency,
String customerId) {
// 1. Calculate service charge
BigDecimal serviceCharge = calculator.calculateCharge(amount, chargeType, chargeParams);
// 2. Calculate taxes
BigDecimal taxAmount = taxCalculator.calculateTax(
amount.add(serviceCharge), currency, customerId
);
// 3. Calculate total
BigDecimal total = amount.add(serviceCharge).add(taxAmount);
// 4. Create payment request
PaymentRequest request = new PaymentRequest()
.setAmount(amount)
.setServiceCharge(serviceCharge)
.setTaxAmount(taxAmount)
.setTotalAmount(total)
.setCurrency(currency)
.setCustomerId(customerId);
// 5. Log audit trail
auditLogger.logCalculation(
customerId,
amount,
chargeType,
chargeParams,
serviceCharge,
taxAmount,
total
);
// 6. Submit to payment processor
return PaymentGateway.submit(request);
}
}
10. Real-World Applications and Case Studies
Charge calculators are used in various industries:
| Industry |
Application |
Typical Charge Structure |
Example Companies |
| Payment Processing |
Credit card transaction fees |
2.9% + $0.30 per transaction |
Stripe, PayPal, Square |
| E-commerce |
Shipping and handling fees |
Tiered by order value or weight |
Amazon, Shopify, eBay |
| Banking |
Loan interest calculations |
Amortized interest over term |
Chase, Bank of America |
| Telecommunications |
Usage-based billing |
Per-minute/data charges with tiers |
AT&T, Verizon, Vodafone |
| SaaS |
Subscription pricing |
Monthly/annual fees with usage overages |
Salesforce, Slack, Zoom |
| Healthcare |
Insurance copays and deductibles |
Percentage of service cost after deductible |
UnitedHealthcare, Aetna |
For example, Stripe’s pricing structure (as of 2023) demonstrates a compound charge model:
- 2.9% of the transaction amount
- Plus $0.30 per successful transaction
- Additional 1% for international cards
- Extra 1% if currency conversion is required
Implementing this in Java:
public class StripeFeeCalculator {
private static final BigDecimal DOMESTIC_RATE = new BigDecimal(“2.9”);
private static final BigDecimal DOMESTIC_FEE = new BigDecimal(“0.30”);
private static final BigDecimal INTERNATIONAL_RATE = new BigDecimal(“1.0”);
private static final BigDecimal CONVERSION_RATE = new BigDecimal(“1.0”);
public static BigDecimal calculateStripeFee(
BigDecimal amount,
boolean isInternational,
boolean requiresConversion) {
// Base fee
BigDecimal fee = amount.multiply(DOMESTIC_RATE)
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)
.add(DOMESTIC_FEE);
// Additional fees
if (isInternational) {
fee = fee.add(amount.multiply(INTERNATIONAL_RATE)
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
}
if (requiresConversion) {
fee = fee.add(amount.multiply(CONVERSION_RATE)
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
}
return fee;
}
public static void main(String[] args) {
BigDecimal amount = new BigDecimal(“100.00”);
// Domestic transaction
System.out.printf(“Domestic fee: $%.2f%n”,
calculateStripeFee(amount, false, false));
// International transaction
System.out.printf(“International fee: $%.2f%n”,
calculateStripeFee(amount, true, false));
// Transaction with currency conversion
System.out.printf(“With conversion fee: $%.2f%n”,
calculateStripeFee(amount, false, true));
}
}
11. Security Considerations
Financial calculations require special security considerations:
- Input validation: Prevent injection attacks by validating all inputs
- Precision attacks: Protect against floating-point manipulation
- Audit trails: Maintain immutable records of all calculations
- Access control: Restrict who can modify charge parameters
- Encryption: Encrypt sensitive calculation parameters at rest
- Rate limiting: Prevent brute-force attacks on calculation endpoints
Example of secure parameter handling:
public class SecureChargeCalculator {
private static final Set ALLOWED_CURRENCIES = Set.of(
“USD”, “EUR”, “GBP”, “JPY”, “CAD”
);
private static final Map CURRENCY_PRECISION = Map.of(
“USD”, BigDecimal.valueOf(0.01),
“EUR”, BigDecimal.valueOf(0.01),
“GBP”, BigDecimal.valueOf(0.01),
“JPY”, BigDecimal.ONE, // Yen has no decimal places
“CAD”, BigDecimal.valueOf(0.01)
);
public static BigDecimal calculateSecureCharge(
String amountStr,
String chargeTypeStr,
String[] paramStrs,
String currency)
throws IllegalArgumentException {
// 1. Validate currency
if (!ALLOWED_CURRENCIES.contains(currency)) {
throw new IllegalArgumentException(“Unsupported currency”);
}
// 2. Parse amount with currency-specific precision
BigDecimal amount;
try {
amount = new BigDecimal(amountStr);
amount = amount.setScale(
CURRENCY_PRECISION.get(currency).scale(),
RoundingMode.HALF_UP
);
} catch (Exception e) {
throw new IllegalArgumentException(“Invalid amount format”);
}
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
// 3. Parse charge type
ChargeType type;
try {
type = ChargeType.valueOf(chargeTypeStr);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid charge type");
}
// 4. Parse and validate parameters
Object[] params = new Object[paramStrs.length];
for (int i = 0; i < paramStrs.length; i++) {
try {
params[i] = new BigDecimal(paramStrs[i]);
} catch (Exception e) {
throw new IllegalArgumentException(
String.format("Invalid parameter %d format", i+1)
);
}
}
// 5. Perform calculation in secure context
return AdvancedChargeCalculator.calculateCharge(amount, type, params);
}
}
12. Future Trends in Charge Calculation
Emerging trends that may affect charge calculation systems:
- Dynamic pricing: Real-time adjustment based on demand, time, or other factors
- AI-driven fees: Machine learning models determining optimal charge structures
- Blockchain-based fees: Smart contracts automating charge calculations
- Regulatory tech: Automated compliance with evolving financial regulations
- Quantum computing: Potential for ultra-precise financial calculations
- Personalized pricing: Individualized charge structures based on customer history
Example of a dynamic pricing calculator:
public class DynamicPricingCalculator {
private final MarketDataService marketData;
private final CustomerProfileService customerProfile;
public DynamicPricingCalculator(
MarketDataService marketData,
CustomerProfileService customerProfile) {
this.marketData = marketData;
this.customerProfile = customerProfile;
}
public BigDecimal calculateDynamicCharge(
BigDecimal baseAmount,
String customerId,
String productId) {
// 1. Get current market conditions
MarketConditions conditions = marketData.getCurrentConditions(productId);
// 2. Get customer profile
CustomerProfile profile = customerProfile.getProfile(customerId);
// 3. Calculate base charge
BigDecimal baseCharge = baseAmount.multiply(conditions.getBaseRate())
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
// 4. Apply dynamic adjustments
BigDecimal demandAdjustment = baseCharge.multiply(conditions.getDemandFactor());
BigDecimal loyaltyAdjustment = baseCharge.multiply(profile.getLoyaltyDiscount());
BigDecimal timeAdjustment = baseCharge.multiply(conditions.getTimeFactor());
// 5. Calculate final charge
BigDecimal totalCharge = baseCharge
.add(demandAdjustment)
.subtract(loyaltyAdjustment)
.add(timeAdjustment);
// 6. Apply minimum/maximum limits
return totalCharge.max(conditions.getMinimumCharge())
.min(conditions.getMaximumCharge());
}
}
Additional Resources and Authority References
For further study on Java financial calculations and best practices:
For academic perspectives on financial calculations in computer science: