Skip to main content

Features

Transaction Simulation

Auto-Simulation

Service secara otomatis menggenerate transaksi saat startup:

// Transaction types and distribution
@Component
public class TransactionSimulator {

@Scheduled(fixedRate = 5000)
public void generateTransaction() {
double rand = Math.random();
Transaction txn;

if (rand < 0.60) {
txn = createNormalTransfer();
} else if (rand < 0.75) {
txn = createHighValueTransfer();
} else if (rand < 0.85) {
txn = createRapidTransaction();
} else if (rand < 0.92) {
txn = createCrossBorderPayment();
} else {
txn = createFailedTransaction();
}

processTransaction(txn);
}
}

Transaction Types

TypeAmount RangeFrequencyRisk Level
Normal Transfer$100 - $10,00060%LOW
High-Value Transfer$10,000 - $100,00015%MEDIUM
Rapid Transaction$100 - $5,00010%HIGH
Cross-border Payment$1,000 - $50,0007%MEDIUM
Failed TransactionVarious8%HIGH

Velocity Checks

Threshold Configuration

@Configuration
public class VelocityConfig {

@Value("${velocity.threshold:5}")
private int threshold;

@Value("${velocity.window:60}")
private int windowSeconds;

public boolean isVelocityExceeded(String accountId) {
int recentCount = transactionRepository
.countByAccountIdAndTimestampAfter(
accountId,
Instant.now().minusSeconds(windowSeconds)
);
return recentCount >= threshold;
}
}

Detection Rules

RuleConditionAction
Rapid Succession>5 txns in 60sBlock + Alert
High Amount>$10,000Flag for review
Cross-borderInternational transferAdditional verification
Failed Attempts>3 failures in 5minTemporary lock

Risk Scoring

Risk Levels

LevelScoreDescriptionAction
LOW0-30Normal transactionAuto-approve
MEDIUM31-60Unusual patternFlag for review
HIGH61-80Suspicious activityBlock + Alert
CRITICAL81-100Known fraud patternBlock + Escalate

Scoring Algorithm

public int calculateRiskScore(Transaction txn) {
int score = 0;

// Amount factor
if (txn.getAmount() > 10000) score += 20;
if (txn.getAmount() > 50000) score += 30;

// Velocity factor
if (velocityService.isVelocityExceeded(txn.getAccountId())) {
score += 40;
}

// Time factor (unusual hours)
if (isUnusualHour(txn.getTimestamp())) {
score += 15;
}

// Cross-border factor
if (txn.isCrossBorder()) {
score += 25;
}

return Math.min(score, 100);
}

WebSocket Alerts

Topics

TopicDescription
/topic/transactionsTransaction events
/topic/alertsSecurity alerts
/topic/auditAudit logs
/topic/complianceCompliance checks

Alert Message Format

{
"type": "VELOCITY_ALERT",
"severity": "HIGH",
"timestamp": "2026-07-06T10:30:00Z",
"accountId": "ACC-001",
"transactionId": "TXN-123",
"message": "Velocity threshold exceeded: 6 transactions in 60 seconds",
"riskScore": 75
}

Audit Trail

Immutable Logging

@Entity
@Table(name = "audit_logs")
public class AuditLog {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String actor;
private String action;
private String target;
private String result;
private String ipAddress;

@Column(columnDefinition = "TEXT")
private String details;

private String previousHash;
private String currentHash;

@CreationTimestamp
private Instant timestamp;
}

Hash Chain

@Service
public class AuditService {

public void log(String actor, String action, String target, String result) {
String previousHash = getLastHash();
String currentHash = calculateHash(previousHash + actor + action + target + result);

AuditLog log = new AuditLog();
log.setActor(actor);
log.setAction(action);
log.setTarget(target);
log.setResult(result);
log.setPreviousHash(previousHash);
log.setCurrentHash(currentHash);

auditRepository.save(log);
}

private String calculateHash(String data) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
return Hex.encodeHexString(hash);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

Next Steps