What is payment fraud at checkout?
Payment fraud at checkout is the use of stolen cards, stolen accounts, or coordinated fake identities to push a charge through the payment step before it can be caught. The tell is concealment: the buyer hides behind a VPN, proxy, Tor, a datacenter IP, or an anti-detect browser so the session cannot be traced back to a single person or device.How ShieldLabs surfaces it
ShieldLabs resolves the buyer to a set of identifiers and returns a Risk Score with named anonymity signals. A naive checkout trusts the cookie, the session, or the buyer’s IP — all three are trivial to reset, so a fraudster clears cookies, opens incognito, or rotates to a fresh proxy IP, and a cookie- or IP-keyed rule treats each try as a brand-new buyer. The durable DeviceID is derived server-side, so it survives cleared cookies, incognito, and IP rotation and recognizes the same device behind a string of “new” checkout attempts. ShieldLabs surfaces the Score and the reasons; your checkout code owns the verdict.Prevent payment fraud at checkout
Read the fresh Risk Score and the durableDeviceID the moment the buyer reaches the payment step. The rule your code applies: gate the charge on the Score and its band, drawn tighter at payment than on a low-stakes page, and step up or hold when masking signals like Tor, Anti-detect Browser, or Abuser Flag fire, or when the DeviceID matches a device tied to past abuse. The outcome is that a buyer hiding behind a fresh proxy IP or cleared cookies still resolves to the same device, so your code can allow the clean charge, ask for 3DS or OTP on the borderline one, and route the high-band one to review.
Build it
Create a ShieldLabs account and get your keys
Sign up for free and get 5,000 identifications, or log in if you already have an account. Register the domain you want to identify visitors on, then open the Keys page. Use the Public Key to initialize the snippet in the browser, and keep your server-side credentials on your backend: the Private API Key authenticates the History API, and each webhook endpoint has its own
whsec_… signing secret. See Keys.Force a fresh check at the payment step
On page load you may already run The snippet POSTs the signals to
checkAuthenticatedUser for analytics. At the payment step you want a current read, so use the forceCheck* variant: it clears the session and runs a new identify call immediately, scoring the session as it is at payment time, not a stale score from page load. Pass a hashed or pseudonymous user id, never a raw email or account id.checkout.html
rest.shieldlabs.ai automatically; installing the snippet covers the React, Vue, Angular, Preact, and Svelte versions of the same dynamic-import pattern.Receive the webhook and gate the charge on Score plus band
ShieldLabs POSTs one signed webhook per scored identification. Verify The block, step-up, and review actions run in your application. ShieldLabs returns the Score and its
X-Shield-Signature on the raw body, then cache the result keyed by request_id so the checkout request can look it up — the shared waitForScore helper (defined once for every tutorial) does this read, polling the cache and falling back to the History API by request_id. Delivery is at-most-once with no retries, so the History fallback covers a dropped webhook; each returned row bills 1 request while the webhook is free.Branch on the Score and its band, which already fold in the anonymity signals. At the payment step, draw the band lines tighter than elsewhere.checkout.js
signals; your checkout code owns the verdict.Read the real network behind a VPN
For a stolen-card buyer who hides their location, the webhook carries two IPs.
public_ip.country comes from the public IP, which a VPN can put anywhere; local_ip.country is the real network IP, which can expose the network behind the VPN. When the two disagree, detection_flags.ip_mismatch is set to true — a buyer pretending to shop from one country while their real connection sits in another.local_ip.ip is empty when the follow-up network check could not complete; the Stun not checked signal may fire instead (the stun_not_checked flag), so you do not silently lose the comparison.For a hard rule that does not depend on the band, branch on the detection_flags object: its keys (vpn, tor, proxy, datacenter_ip, abuser, os_mismatch, ip_mismatch, anti_detect_browser, …) are stable booleans, safer than the human-readable signals labels.Reading the Risk Score at checkout
The four bands and their ranges are defined in Risk Scoring; the full action playbook is in Acting on the Risk Score. The payment step is a good place to draw the same band tighter than you would elsewhere:| Band | At a low-stakes page | At checkout |
|---|---|---|
| Clean (0–9) | Pass through | Allow, charge |
| Low (10–29) | Allow, log | Allow, log the signals |
| Medium (30–59) | Second look | Step up to 3DS or OTP before the charge |
| High (60–100) | Review or challenge | Hold for review or require verification, decided by your code |
signals for visibility and logging; treat them as illustrative. The Signals reference lists the full set with weights.
Signal in signals | Why it matters at payment |
|---|---|
| Tor | Connection exits through the Tor network. Rare for legitimate buyers. Usually a hard challenge or block decided by you. |
| Anti-detect Browser | Fingerprint-spoofing indicators. Common in coordinated payment abuse. |
| Proxy | IP flagged as a proxy. One signal among several; weigh with the rest. |
| Datacenter IP | IP is in a hosting range. Unusual for a real shopper on a personal device. |
| Abuser Flag | IP or device appears on an abuse reputation list. Corroborating, not conclusive on its own. |
| OS Mismatch | The OS the browser claims does not match other evidence. A spoofing indicator. |
Test it
Confirm the durable DeviceID holds before you wire thresholds to live charges. Run a checkout, note thedevice_id from the webhook, then repeat the visit in an incognito window, after clearing cookies, and from a second browser on the same machine: the cookie_id and visitor_id change each time, but the device_id stays the same — that is the identifier that links one buyer across “fresh” sessions. To see the Score react, repeat the checkout through a VPN or proxy and watch the signals array gain a VPN or Proxy entry with a higher Score.
Patterns like Many Accounts on One Device live on the dashboard only. They grade an entity (Suspicious or Dangerous over a rolling window, typically 30 days) and are not part of the checkout webhook. Use the per-request score, signals, and detection_flags for the in-the-moment charge decision; use Patterns (exported as CSV or JSON) for the offline review of repeat offenders. The two join on device_id.
Next steps
When a disputed charge lands weeks later, the sameDeviceID you scored here becomes chargeback-dispute evidence. Upstream, the same fresh-check pattern guards a suspicious login with step-up authentication and a new account at signup, and the durable device link surfaces one buyer running many accounts.
Acting on the Risk Score
Turn the Score and
signals into allow, challenge, review, and block logic in your app.Signals
Every signal that can appear in
signals, in plain language, with its weight.The Risk Score
How the 0 to 100 score is built, what
signals carries, and the band definitions.Chargeback evidence
The after-the-sale half: reconstruct the buyer’s device history into a dispute package.