CrossBorderTransactionRule.java
package com.hsbc.fraud.rules.impl;
import com.hsbc.fraud.model.AlertType;
import com.hsbc.fraud.model.Transaction;
import com.hsbc.fraud.model.TransactionType;
import com.hsbc.fraud.rules.FraudRule;
import com.hsbc.fraud.rules.RuleResult;
import java.math.BigDecimal;
import java.util.Optional;
/**
* Rule to detect suspicious cross-border wire transfers.
* Applies stricter thresholds for international transactions.
*/
public class CrossBorderTransactionRule implements FraudRule {
private static final long serialVersionUID = 1L;
private final String homeCountryCode;
private final BigDecimal crossBorderThreshold;
public CrossBorderTransactionRule() {
this("US", new BigDecimal("5000"));
}
public CrossBorderTransactionRule(String homeCountryCode, BigDecimal crossBorderThreshold) {
this.homeCountryCode = homeCountryCode;
this.crossBorderThreshold = crossBorderThreshold;
}
@Override
public String getRuleName() {
return "CROSS_BORDER_TRANSACTION";
}
@Override
public String getDescription() {
return "Detects suspicious cross-border transactions exceeding threshold";
}
@Override
public Optional<RuleResult> evaluate(Transaction transaction) {
if (transaction == null) {
return Optional.empty();
}
// Only apply to wire transfers
if (transaction.getTransactionType() != TransactionType.WIRE_TRANSFER &&
transaction.getTransactionType() != TransactionType.TRANSFER) {
return Optional.empty();
}
String countryCode = transaction.getCountryCode();
if (countryCode == null || countryCode.equalsIgnoreCase(homeCountryCode)) {
return Optional.empty();
}
BigDecimal amount = transaction.getAmount();
if (amount == null) {
return Optional.empty();
}
if (amount.compareTo(crossBorderThreshold) >= 0) {
double riskScore = 0.5 + (amount.divide(crossBorderThreshold.multiply(new BigDecimal("4")),
2, java.math.RoundingMode.HALF_UP)
.min(new BigDecimal("0.45"))
.doubleValue());
return Optional.of(new RuleResult(
getRuleName(),
AlertType.CROSS_BORDER_SUSPICIOUS,
riskScore,
String.format("Cross-border %s to %s for %s %s exceeds threshold of %s",
transaction.getTransactionType(),
countryCode,
amount,
transaction.getCurrency(),
crossBorderThreshold)
));
}
return Optional.empty();
}
@Override
public int getPriority() {
return 70;
}
public String getHomeCountryCode() {
return homeCountryCode;
}
public BigDecimal getCrossBorderThreshold() {
return crossBorderThreshold;
}
}