Every fiat-adjacent cashier we've audited has the same weak link: a screenshot upload. The player pays via Zelle, Cash App, or a wire, and uploads proof. An agent — or a bot — approves the screenshot, and the balance credits.
The moment fraudsters find the pattern, the screenshots start looking too good. Correct fonts, correct layout, plausible transaction ID. The naive approach — trust the agent's eye — falls apart at any real volume.
Layer 1: metadata
EXIF data on the uploaded image tells you more than most operators check. Was it generated by a screenshotting tool on a phone (expected) or a desktop image editor (suspicious)? Was it re-encoded through a Telegram compression pass (fine) or through a photo-editing app's export pipeline (not fine)?
We reject any image whose creation timestamp is more than 24 hours before submission, or whose editor tag matches a known image-manipulation app.
Layer 2: perceptual hashing
A player who submits the same screenshot twice — same pixels, same crop — is either confused or malicious. Either way, the second submission needs a human. We hash every accepted screenshot with a perceptual hash (pHash) and reject exact matches on sight. Near-matches (Hamming distance under a threshold) get routed to review.
phash(image) = 91d3f4a2
phash(prior_accepted) = 91d3f4a2
> reject: duplicate proof submissionLayer 3: structural OCR
Real payment screenshots have predictable structure. Cash App confirmation pages have the amount in a specific font at a specific vertical position, above the payment ID. Zelle has a green check and a recipient line. Deviations from the template — amount in the wrong place, missing timestamp, wrong currency glyph — are cheap to detect and expensive to fake.
We OCR every screenshot into a structured record, then validate it against the expected template for that rail. Structural mismatches don't reject the payment outright — they route it to review with the specific field highlighted.
Layer 4: cross-rail verification
This is the one that catches sophisticated fraud. The screenshot claims a $500 Cash App payment at 14:22 UTC. We check the operator's Cash App account for a matching credit within a 15-minute window. No match, no balance credit. Fraudsters can fake the screenshot; they cannot fake the money actually arriving.
Why the layers matter together
Any one of these layers can be defeated by a determined attacker. Layered, they aren't. A fraudster who scrubs EXIF still trips the perceptual hash on their second attempt. A fraudster who generates a fresh screenshot still fails cross-rail verification. A fraudster who somehow does actually pay the operator has stopped being a fraudster — they're a customer.
At operator scale, the objective isn't zero false positives. It's driving the cost-per-fraud-attempt above what the attacker can profitably absorb. The layered stack does that without adding agent time.