Scoring every card payment for fraud in under 40 ms
Every time a Pocket card is used, we decide whether the payment looks like you before the terminal finishes beeping. How the fraud model, its features and its fallbacks fit in 40 milliseconds.


Jun Seo and Clara Novak
Staff Engineer, Payments
Share

Photo:
When you tap a Pocket card in a café, the terminal sends a request through the card network to us, and we have to answer yes or no. The network allows issuers a couple of seconds before it gives up, but people standing at a till notice anything much over half a second. So we hold ourselves to a much tighter budget: 150 milliseconds inside Pocket, from the request arriving to the answer leaving.
Within that, fraud scoring gets 40 milliseconds at the 99th percentile. Every card payment goes through it, around 30 million a week, and this post explains how it fits.
Where the time goes
An authorisation is a short chain of steps, all on the critical path. These are our p99 timings for last month:
Step | p99 | Median |
|---|---|---|
Receive and parse the request from the network | 4 ms | 1 ms |
Load the card, account and available balance | 7 ms | 2 ms |
Fraud scoring | 38 ms | 11 ms |
Place a hold on the ledger and reply | 12 ms | 4 ms |
Fraud scoring is the largest slice by design. It is where a few extra milliseconds buy the most, because a better decision means fewer stolen cards getting through and fewer genuine payments declined.
What the model looks at
The model sees about 300 features for each payment. They fall into three families:
The payment itself. Amount, currency, merchant category, country, and how the card was used: chip, contactless, online, or through a phone wallet.
You. The merchants you usually pay, your typical amounts at this time of day, the countries you have spent in recently, and how many payments you have made in the last minute, hour and day.
The merchant. How long it has been taking card payments, its recent fraud rate across the network, and whether other Pocket cards have been tested there with small amounts.
Almost none of this can be computed in 40 milliseconds from scratch. So we compute it ahead of time. Every transaction, login and card setting change is published to a stream, and a set of consumers keep each customer's features up to date in an in-memory store within a second or so of anything happening. At authorisation time, the scorer only reads.
Travel is the classic case. If you have just topped up your yen balance and opened the app in Osaka, the "new country" feature is already warm by the time you tap your card at a convenience store, and the payment looks normal rather than alarming.
The model, and why it is boring
We use gradient-boosted decision trees, compiled to native code and loaded into the authorisation service itself. There is no network call to a separate model server, which removes the single largest source of latency variation we had in the first version.
We have tried deep learning models several times. They were slightly better offline and noticeably worse in production, mostly because of their latency tail. Trees also give us something we value a lot: for each decision, we can list the features that pushed the score up. Those reasons go to our fraud analysts, and a simplified version goes to you.
A score response looks like this:
The model is retrained every week on payments whose outcome we now know: chargebacks, payments customers confirmed as fraud, and the far larger set that turned out to be fine. A new version runs in shadow for two weeks, scoring every payment without acting on it, before it can make real decisions.
What a score does
Scores land in one of three bands:
Approve. About 99.5% of payments. You get your notification and nothing else happens.
Approve and check. The payment goes through, and the app asks "Was this you?". One tap says yes. Saying no freezes the card and opens a dispute.
Challenge. The payment is declined and you get a notification straight away asking you to confirm. If you do, we approve the same merchant for the next ten minutes, so trying again at the till works.
A small set of hard rules sits alongside the model: blocked merchant categories you have chosen in the app, spending limits, and frozen cards. Those never depend on a score.
When things go wrong
If the scorer is slow or fails, the payment must still get an answer. A cheaper rules-only decision is computed in parallel with every score, and if the model has not replied by 40 milliseconds, the rules decide. Last quarter that happened on 0.002% of payments, mostly during deploys.
The worst outcome is not a fraudulent payment. It is a genuine one declined while someone is standing at a till with their shopping.
That is why the number we watch most closely is not fraud caught but genuine payments declined. It is currently 1 in 2,900, down from 1 in 1,100 a year ago. Fraud losses over the same period fell by a third. We share the wider picture of how we protect accounts on our security page, and if this kind of work interests you, we are hiring engineers in Amsterdam, London and Lisbon.
#fraud
#machine-learning
#cards

Written by
Jun Seo
Staff Engineer, Payments

Co-author
Clara Novak
Head of Product



