FinTech

Real-time fraud detection inside the transaction flow

A payments platform scores every transaction for fraud while it is being processed. The Deep Netts model runs inside the Java payment service itself – no Python scoring service, no network hop.

30%

reduction in fraud losses

ms

decision latency, in-process

0

extra Python infrastructure

How it works – the animation

0
Scored
0
Approved
0
Blocked
0
Last score
Step 1 / 5
Payments

Every payment passes through the platform's Java service. Fraud is a small fraction of them, and its patterns keep changing – rules alone are easy to bypass.

30%
lower fraud losses (the case)
ms
decisions, in the same JVM
0
extra Python infrastructure

Case: payments platform, not named – 30%, ms and 0 from the case. Features, model, 0.70 threshold, benchmark and test results: Deep Netts talk “AI in Java for the Financial Industry”, Amsterdam JUG at ING, Oct 2026 (an example model, not the customer's). Payments drawn are illustrative.

1.In short

  • A payments platform has to decide for every payment, while it is being processed, whether it looks like fraud.
  • Sending each payment to a separate Python scoring service added a network hop, more infrastructure and a second stack to run.
  • With Deep Netts the fraud model runs inside the Java payment service itself: it is loaded once and scores each payment in the same JVM.
  • The customer reports 30% lower fraud losses, decisions in milliseconds and no extra Python infrastructure.

2.The problem

Fraud is a tiny fraction of all payments, and its patterns keep changing. The talk quotes a projection of $33 billion lost to payment-card fraud worldwide every year (The Nilson Report).

Fixed rules ("block payments over X from country Y") are simple, but they age fast and fraudsters learn to step around them. A machine learning model learns the patterns from the transaction history instead, and can be retrained when they change.

The goal, in the talk's words: catch more fraud, block fewer good transactions – and do it in real time, at payment scale.

3.What the system must do

RequirementWhy it matters
Low latencyA payment cannot wait. High transaction rates call for several small, fast models.
High scalabilityVolume peaks (holidays, sales) must not slow scoring down.
Infrastructure and costEvery extra service is more machines, more monitoring and more people to run it.
IntegrationThe model has to fit into the infrastructure that already processes the payments.
From the talk. Its conclusion: mixing different technology stacks adds latency, scalability and complexity problems to the whole pipeline.

4.What the model sees

A model does not see a payment – it sees numbers built from it. The talk groups the usual inputs for fraud detection into four families:

FamilyExamples
Core attributesAmount, time of day, merchant category
Spatial contextCross-border flag, distance from the last transaction, distance from home
Velocity and frequencyNumber of transactions in the last 5 / 10 / 30 minutes, 1 hour, 24 hours; number of merchants in the last 5 / 10 / 30 minutes
Derived featuresRatio to the customer's median purchase, combinations of the indicators above

The 8 inputs of the example service

The Spring Boot service shown in the talk builds 8 numbers for every payment – the same 8 the animation shows next to the network:

#InputMeaning
1amountHow much the payment is for
2crossBorder1 if the card and the merchant are in different countries, else 0
3attemptsLast30MinPayment attempts with this card in the last 30 minutes
4minutesSincePreviousTransactionTime since the card's previous payment
5distanceFromPreviousTransactionKmHow far this payment is from the previous one
6differentMerchantsLast30MinDifferent merchants in the last 30 minutes
7customerCategoryThe customer's segment, as a number
8productCategoryWhat is being bought, as a number

5.Where Deep Netts comes in

Everything happens in one process – the payment never leaves the JVM to be scored:

  1. Payment arrives

    The Java payment service receives the transaction.

  2. Features

    The service turns it into the 8 numbers above.

  3. Deep Netts

    Fraud model

    A feed-forward network, loaded once at start-up, returns a probability 0–1.

  4. Decision

    0.70 or more: block. Below: approve and continue.

Before, the third box was a separate Python service: one more network call per payment, one more deployment, one more runtime to monitor. Now it is a method call.

6.Three models to choose from

Deep Netts has the three models the talk recommends for fraud out of the box. All three are built with the same builder and answer the same question: how likely is it that this payment is fraud.

Logistic regression – the baseline

The simplest classifier: one weight per input, a probability out. Easy to explain (each weight says how much an input matters) and a good first baseline.

CreditCardFraudLogisticRegression

FeedForwardNetwork logReg = FeedForwardNetwork.builder()
      .addInputLayer(numInputs)
      .addOutputLayer(1, SIGMOID)
      .lossFunction(CROSS_ENTROPY)
      .build();

Feed-forward neural network – the one in the animation

Logistic regression with hidden layers in between. The hidden layers let the model combine inputs (a small amount, but abroad, the fifth attempt in ten minutes …) and catch patterns a single weight per input cannot – higher precision and fewer missed frauds.

FraudDetectionNN.java

FeedForwardNetwork neuralNet = FeedForwardNetwork.builder()
      .addInputLayer(numInputs)
      .addFullyConnectedLayer(32, ActivationType.RELU)
      .addFullyConnectedLayer(16, ActivationType.RELU)
      .addOutputLayer(numOutputs, ActivationType.SIGMOID)
      .lossFunction(LossType.CROSS_ENTROPY)
      .build();
8 inputs → 32 → 16 → 1 probability. The animation draws fewer neurons so the picture stays readable.

Auto-encoder – anomaly detection

Learns only from normal payments what "normal" looks like, and flags what it cannot reproduce. Useful when there are too few labelled frauds to train a classifier.

CreditCardFraudAutoencoder

FeedForwardNetwork autoencoder = FeedForwardNetwork.builder()
      .addInputLayer(numInputs)
      .addFullyConnectedLayer(5)
      .addFullyConnectedLayer(3)
      .addFullyConnectedLayer(5)
      .addOutputLayer(numInputs, SIGMOID)
      .lossFunction(MEAN_SQUARED_ERROR)
      .build();

7.Training

Training shows the model past payments with the answer (fraud or not) and lets it reduce its error step by step. Three settings decide when it stops and how big each step is:

Training the network

neuralNet.getTrainer()
      .setStopEpochs(10000)    // at most this many passes over the data
      .setStopError(0.15f)     // or stop when the error is this low
      .setLearningRate(0.01f); // how big each correction is
neuralNet.train(trainingSet);
From the talk. In its log, training accuracy rises from 91.3% after the first pass to 91.8% after the fifth, at about 0.2–0.45 s per pass.

8.Inside the payment service

The trained model is a file. The service loads it once when it starts, and from then on every payment is one predict call in memory. The threshold is configuration, so it can change without a new release.

FraudDetectionService.java (Spring Boot)

@Service
public class FraudDetectionService implements AutoCloseable {

    private final String modelPath;
    private final double threshold;
    private FeedForwardNetwork network;

    public FraudDetectionService(@Value("${fraud.model-path}") String modelPath,
                                 @Value("${fraud.threshold:0.70}") double threshold) {
        this.modelPath = modelPath;
        this.threshold = threshold;
    }

    @PostConstruct
    public void loadModel() throws IOException {
        network = NeuralNetwork.load(modelPath, FeedForwardNetwork.class);
    }

    public FraudResponse predict(FraudRequest request) {
        float[] features = new float[] {
            request.amount().floatValue(),
            request.crossBorder() ? 1.0f : 0.0f,
            request.attemptsLast30Min().floatValue(),
            request.minutesSincePreviousTransaction().floatValue(),
            request.distanceFromPreviousTransactionKm().floatValue(),
            request.differentMerchantsLast30Min().floatValue(),
            request.customerCategory().floatValue(),
            request.productCategory().floatValue()
        };
        float[] output = network.predict(features);
        double prob = output[0];
        return new FraudResponse(prob, prob >= threshold, threshold);
    }
}
From the talk (one stray bracket fixed).

9.How good is it

The talk tests the example network on 200,000 payments it did not see in training. Every payment ends in one of four boxes:

Really fraudReally good
Model said fraud11,447 caught (true positive)1,445 good payments blocked (false positive)
Model said good6,034 frauds missed (false negative)181,075 correctly approved (true negative)
Confusion matrix of the talk's example model on 200,000 test payments.
MeasureValueIn plain words
Accuracy96.26%How often the model is right overall
Precision88.79%Of the payments it blocks, how many really are fraud
Recall65.48%Of all frauds, how many it catches
F1 score75.38%One number that balances precision and recall
These are the numbers of the talk's example model on its test data – not the customer's model. Accuracy alone says little when fraud is rare: a model that approves everything is also "mostly right". Precision and recall are the numbers to watch.

10.The 0.70 threshold

The network does not say "fraud" or "not fraud" – it gives a probability. The service turns it into a decision with a threshold, 0.70 by default in the example.

  • A lower threshold blocks more: more frauds caught (higher recall), but more good customers stopped (lower precision).
  • A higher threshold blocks less: fewer false alarms, but more frauds slip through.
  • Because the threshold is a configuration value, the fraud team can move it without retraining or redeploying the model.

11.Speed and hardware

Deep Netts can use the Java Vector API (CPU) or an Nvidia GPU through CUDA – switched on with a JVM option and one line of code:

Hardware acceleration

// Vector API – JVM option: --add-modules jdk.incubator.vector
DeepNetts.getInstance().setUseVectorAPI(true);

// Nvidia GPU / CUDA – JVM option: --enable-native-access=ALL-UNNAMED
DeepNetts.getInstance().setUseCuda(true);
DatasetCPU, single threadVector APIGPU
1M rows1,238 ms554 ms (2.2×)595 ms (2.1×)
10M rows11,763 ms5,847 ms (2.0×)1,285 ms (9.2×)
20M rows25,587 ms11,884 ms (2.2×)2,684 ms (9.5×)
50M rows62,957 ms31,716 ms (2.0×)6,680 ms (9.4×)
The talk's benchmark: a small feed-forward network (hidden layers 32, 16, 8) on an Intel i9 CPU and an RTX 4070 GPU.

12.Results for the customer

Result
Fraud losses30% lower
Decision timeMilliseconds, in-process – inside the JVM that processes the payment
Extra infrastructureNone – no separate Python scoring service
Way of workingThe fraud team iterates on models with the tools the platform team already runs

13.Glossary

In-process
Running inside the same program (here: the same JVM), so no network call is needed.
Feature
A number the model gets as input, built from the payment and its history.
Feed-forward network
The classic neural network: numbers in, layers of neurons, a score out.
ReLU, sigmoid
Activation functions. ReLU in the hidden layers; sigmoid squeezes the output into 0–1.
Cross-entropy
The error measure used to train a yes/no classifier.
Threshold
The probability above which a payment is blocked (0.70 in the example).
Precision
Of the blocked payments, the share that really were fraud.
Recall
Of all frauds, the share the model caught.
Auto-encoder
A network that learns to reproduce normal data; what it cannot reproduce is suspicious.
Vector API
A Java API that uses the CPU's vector instructions to do many calculations at once.

14.Sources

  • Deep Netts, "AI in Java for the Financial Industry", talk at ING, Amsterdam – models, code, test results and benchmark on this page.
  • The Nilson Report, as quoted in the talk – the $33B annual payment-card fraud projection.
  • The customer's case (the customer is not named) – 30% lower fraud losses, in-process decisions, no Python service.