Skip to main content
Version: v3000

Detect fraud and liveness

BlinkCard runs liveness checks while scanning to help you spot cards that aren't physically present. Three checks run on each side of the card:

  • screen check: detects a card displayed on a screen (a recapture)
  • photocopy check: detects a photocopy of a card
  • hand presence check: detects whether the card is being held in a human hand
note

The liveness checks, their result fields, and their settings are identical across the Web, Android, and iOS SDKs. The code samples below show each platform.

Interpret the liveness result​

The result exposes an overall verdict and the per-check, per-side detail.

overallCardLivenessResult passes only when every individual check passes, and fails if any check fails:

Each check result is one of "pass", "fail", or "not-available".

blinkCard.addOnResultCallback((result) => {
if (result.overallCardLivenessResult === "fail") {
// Treat the card as potentially fraudulent: reject or send to manual review
}
});

To see exactly which check failed, read the per-side cardLivenessCheckResult:

const checks = result.firstSideResult?.cardLivenessCheckResult;

if (checks?.screenCheckResult === "fail") {
// Card was likely displayed on a screen
}
if (checks?.photocopyCheckResult === "fail") {
// Card was likely a photocopy
}
if (checks?.cardHeldInHandCheckResult === "fail") {
// Card was not detected being held in a hand
}

A "not available" result means the check did not run or could not produce a verdict for that side, for example when the check is disabled. The same per-side detail is available on secondSideResult?.cardLivenessCheckResult.

Tune detection strictness​

Liveness sensitivity is configured through the livenessSettings inside the scanning settings. Pass these settings when you start scanning:

Pass scanningSettings.livenessSettings to createBlinkCard:

const blinkCard = await createBlinkCard({
licenseKey: "your-license-key",
scanningSettings: {
livenessSettings: {
screenCheckStrictnessLevel: "level-7",
photocopyCheckStrictnessLevel: "level-7",
enableCardHeldInHandCheck: true,
},
},
});

Screen and photocopy strictness​

screenCheckStrictnessLevel and photocopyCheckStrictnessLevel accept a "disabled" value or one of ten levels. On web the values are "disabled" or "level-1" through "level-10". On Android and iOS they are the StrictnessLevel enum: Disabled/.disabled or Level1/.level1 through Level10/.level10. Both default to level 5.

  • type: string
  • Higher levels are stricter: they catch more fraud (lower false accepts) but may reject more genuine cards (higher false rejects).
  • The disabled value turns the check off.

Hand presence​

Enable or disable the hand presence check with enableCardHeldInHandCheck. When enabled, you can tune how a hand is recognized:

  • handToCardSizeRatio: the minimum hand-to-card size ratio for a valid detection. Lower values accept smaller or more distant hands.
  • handCardOverlapThreshold: the minimum overlap between the detected hand and card regions, used to ignore hands that are present but not holding the card.
Test before changing defaults

The default strictness levels are tuned to balance user experience against risk. Adjust them based on your own risk tolerance, and test thoroughly before deploying changes.

What the BIN tells you​

The leading digits of a payment card number form its Bank Identification Number (BIN). BlinkCard matches the scanned number against its BIN database and uses the match to fill in the card's issuer details.

Fields are populated from the BIN in two tiers:

  • issuingNetwork (on the result) is filled whenever the BIN is matched.
  • fundingType, cardCategory, issuerName, and issuerCountryCode (on each entry in cardAccounts) are filled only when the card number is also valid, that is when cardNumberValid is true.

See the result reference for the full list of fields and their types.

Empty BIN fields as a fraud signal​

When BlinkCard cannot match the scanned number against its BIN database, these fields come back empty (undefined on web, null on Android, nil on iOS). An empty issuingNetwork means the scanned number could not be matched, which is a potential fraud signal worth routing to manual review.

The four issuer-detail fields are also empty when the card number fails validation, that is when cardNumberValid is false.