Migrating to v3000
This guide describes how to upgrade to BlinkCard v3000.
For specific platforms (web, iOS, Android...), additional details may be available in their own API references.
BlinkCard v3000 brings new capabilities, other than those listed in this migration guide. The migration guide is focused on getting you to successfully migrate to the new version, and so it doesn't talk about the new features. Read the release notes for your SDK platform to find out more about what else is new.
Versioning change
Starting with v3000, BlinkCard will adopt the epoch versioning scheme.
{EPOCH * 1000 + MAJOR}.MINOR.PATCHEPOCH: Increment when you make significant or groundbreaking changes.
MAJOR: Increment when you make minor incompatible API changes.
MINOR: Increment when you add functionality in a backwards-compatible manner.
PATCH: Increment when you make backwards-compatible bug fixes.
So, instead of going from v2.x.x to v3.x.x, BlinkCard will go from v2.x.x to v3000.x.x.
Subsequent versions will follow the same scheme.
Architecture changes
BlinkCard v3000, similarly to BlinkID v7 and above, will adopt a session-based architecture.
This means that you no longer need to configure and string together individual recognizers.
Instead, the whole concept of a recognizer is replaced by the concept of a session.
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TypeScript)
v2 (Recognizer-based):
import { createBlinkCardRecognizer } from "@microblink/blinkcard-in-browser-sdk";
const recognizer = await createBlinkCardRecognizer(wasmSDK);
await recognizer.updateSettings({
extractOwner: false,
returnFullDocumentImage: true
});
const result = await recognizer.getResult();
v3000 (Session-based):
import { createBlinkCard } from "@microblink/blinkcard";
// Complete solution with UI components
const blinkCard = await createBlinkCard({
licenseKey: "your-license-key",
scanningSettings: {
extractionSettings: { extractCardholderName: false },
croppedImageSettings: { returnCardImage: true }
}
});
v2 (Recognizer-based):
val recognizer = BlinkCardRecognizer()
recognizer.setExtractOwner(false)
recognizer.setReturnFullDocumentImage(true)
v3000 (Session-based):
val sdkSettings = BlinkCardSdkSettings(
licenseKey = "your-license-key"
)
val sdk = BlinkCardSdk.initializeSdk(context, sdkSettings).getOrThrow()
val sessionSettings = BlinkCardSessionSettings(
inputImageSource = InputImageSource.Video,
scanningSettings = ScanningSettings(
extractionSettings = ExtractionSettings(extractCardholderName = false),
croppedImageSettings = CroppedImageSettings(returnCardImage = true)
)
)
val session = sdk.createScanningSession(sessionSettings)
v2 (Recognizer-based):
let recognizer = MBCBlinkCardRecognizer()
recognizer.extractOwner = false
recognizer.returnFullDocumentImage = true
v3000 (Session-based):
let sdkSettings = BlinkCardSdkSettings(
licenseKey: "your-license-key",
downloadResources: true
)
let sdk = try await BlinkCardSdk.createBlinkCardSdk(withSettings: sdkSettings)
let sessionSettings = BlinkCardSessionSettings(
inputImageSource: .video,
scanningMode: .automatic,
scanningSettings: ScanningSettings(
extractionSettings: ExtractionSettings(extractCardholderName: false),
croppedImageSettings: CroppedImageSettings(returnCardImage: true)
)
)
let session = await sdk.createScanningSession(settings: sessionSettings)
v2 (Recognizer-based):
var recognizer = BlinkCardRecognizer();
recognizer.extractOwner = false
recognizer.returnFullDocumentImage = true
v3000 (Session-based):
final sdkSettings = BlinkCardSdkSettings(licenseKey: blinkCardLicenseKey);
sdkSettings.downloadResources = true;
final sessionSettings = BlinkCardSessionSettings();
final scanningSettings = ScanningSettings();
scanningSettings.skipImagesWithBlur = true;
scanningSettings.tiltDetectionLevel = DetectionLevel.mid;
sessionSettings.scanningSettings = scanningSettings;
final scanningUxSettings = ScanningUxSettings();
scanningUxSettings.showHelpButton = true;
scanningUxSettings.showIntroductionAlert = false;
scanningUxSettings.preferredCameraPosition = CameraPosition.back;
scanningUxSettings.allowHapticFeedback = true;
final blinkCardResult = await blinkCardPlugin.performScan(
blinkCardSdkSettings: sdkSettings,
blinkCardSessionSettings: sessionSettings,
scanningUxSettings: scanningUxSettings,
);
v2 (Recognizer-based):
var recognizer = new BlinkCardReactNative.BlinkCardRecognizer();
recognizer.extractOwner = false
recognizer.returnFullDocumentImage = true;
v3000 (Session-based):
const sdkSettings = new BlinkCardSdkSettings({
licenseKey: blinkCardLicenseKey
});
sdkSettings.downloadResources = true;
const sessionSettings = new BlinkCardSessionSettings();
const scanningSettings = new ScanningSettings();
scanningSettings.skipImagesWithBlur = true;
scanningSettings.tiltDetectionLevel = DetectionLevel.Mid;
sessionSettings.scanningSettings = scanningSettings;
const scanningUxSettings = new ScanningUxSettings();
scanningUxSettings.showHelpButton = true;
scanningUxSettings.showIntroductionAlert = false;
scanningUxSettings.preferredCameraPosition = CameraPosition.Back;
scanningUxSettings.allowHapticFeedback = true;
const blinkCardResult = await performScan(
sdkSettings,
sessionSettings,
scanningUxSettings,
);
License considerations
Old v2 licenses are compatible with v3000. You don't need to issue new licenses.
Installation and dependencies
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TypeScript)
v2:
npm install @microblink/blinkcard-in-browser-sdk
v3000:
npm install @microblink/blinkcard
The @microblink/blinkcard package (v3000.0.0) is the main user-facing package that provides a complete solution for card scanning in web applications.
It combines core scanning functionality, camera management, and UI components.
For advanced usage scenarios where you need more control over individual components, you can also use the constituent packages directly:
@microblink/blinkcard-core: core scanning functionality and WASM management@microblink/blinkcard-ux-manager: UI feedback and user experience management@microblink/camera-manager: camera management (shared library)
v2:
repositories {
maven { url 'https://maven.microblink.com' }
}
dependencies {
implementation('com.microblink:blinkcard:2.12.0@aar') {
transitive = true
}
}
v3000:
repositories {
mavenCentral() // Changed from maven.microblink.com
}
dependencies {
// Core SDK (required)
implementation('com.microblink:blinkcard-core:3000.0.0')
// UI components (optional, Jetpack Compose-based)
implementation('com.microblink:blinkcard-ux:3000.0.0')
}
BlinkCard v3000 introduces significant changes to the Android SDK:
- Minimum SDK raised: Android 7.0 (API 24) - drops Android 5.x and 6.x support
- Kotlin upgrade: 1.9.x → 2.1.20
- Android Gradle Plugin: Requires AGP 8.0+
- UI framework: Migrated from Views to Jetpack Compose
- Modular architecture: Split into 2 AARs (core + UX)
- Distribution: Now available on Maven Central (instead of Microblink Maven)
v2:
BlinkCard v2 was available via CocoaPods, Carthage, or Swift Package Manager:
# CocoaPods
pod 'PPBlinkCard'
# Carthage
binary "https://github.com/blinkcard/blinkcard-ios/blob/master/blinkcard-ios.json"
v3000:
BlinkCard v3000 is available via Swift Package Manager (recommended) or manual integration:
// Swift Package Manager
dependencies: [
.package(url: "https://github.com/blinkcard/blinkcard-ios.git",
from: "3000.0.0")
]
v2:
dependencies:
...
blinkcard_flutter:
v3000:
BlinkCard v3000 is available via pub.dev, and the only significant change for the iOS native packages is that the distribution was switched from CocoaPods to Swift Package Manager.
Before running the flutter pub get command, run the following command to enable Swift Package Manager in your Flutter application:
flutter config --enable-swift-package-manager
dependencies:
...
blinkcard_flutter:
// Swift Package Manager
let package = Package(
name: "blinkcard_flutter",
platforms: [
.iOS("16.0")
],
products: [
.library(name: "blinkcard-flutter", targets: ["blinkcard_flutter"])
],
dependencies: [
.package(url: "https://github.com/BlinkCard/blinkcard-ios.git", exact: .init(3000, 0, 0))
],
targets: [
.target(
name: "blinkcard_flutter",
dependencies: [
.product(name: "BlinkCardUX", package: "blinkcard-ios")
],
resources: []
)
]
)
BlinkCard v3000 introduces significant changes to both Android and iOS platforms:
Android
- Minimum SDK raised: Android 7.0 (API 24) - drops Android 5.x and 6.x support
- Kotlin upgrade: 1.9.x → 2.1.20
- Android Gradle Plugin: Requires AGP 8.0+
iOS
- Minimum iOS version: 16.0
- Swift Package Manager: must be enabled in Flutter setup as the SDK is no longer distributed via CocoaPods.
v2:
npm install @microblink/blinkcard-react-native
v3000:
BlinkCard v3000 is available via NPM, and the only significant change for the iOS native packages is that the distribution was switched from CocoaPods to directly packing native Xcframeworks in the project structure.
npm install @microblink/blinkcard-react-native
BlinkCard v3000 introduces significant changes to the React Native SDK, but also to both Android and iOS platforms:
React Native
- BlinkID React Native was built and tested with React Native v0.82.1 and was built with the new (Turbo module) architecture.
- It is also compatible with React Native applications running on the old architecture as it contains backward compatibility with Native Module implementation.
- v3000 is fully written in TypeScript for improved type safety and developer experience.
Android
- Minimum SDK raised: Android 7.0 (API 24) - drops Android 5.x and 6.x support
- Kotlin upgrade: 1.9.x → 2.1.20
- Android Gradle Plugin: Requires AGP 8.0+
iOS
- Minimum iOS version: 16.0
- Swift Package Manager: must be enabled in Flutter setup as the SDK is no longer distributed via CocoaPods.
Import statement changes
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TypeScript)
v2:
import { createBlinkCardRecognizer } from "@microblink/blinkcard-in-browser-sdk";
v3000:
// Main package (recommended for most use cases)
import { createBlinkCard } from "@microblink/blinkcard";
// Or for advanced usage with more control
import { loadBlinkCardCore } from "@microblink/blinkcard-core";
v2:
import com.microblink.blinkcard.entities.recognizers.Recognizer
import com.microblink.blinkcard.entities.recognizers.RecognizerBundle
import com.microblink.blinkcard.entities.recognizers.blinkcard.BlinkCardRecognizer
import com.microblink.blinkcard.uisettings.BlinkCardUISettings
import com.microblink.blinkcard.uisettings.UISettings
v3000:
import com.microblink.blinkcard.core.BlinkCardSdk
import com.microblink.blinkcard.core.session.BlinkCardScanningSession
import com.microblink.blinkcard.core.session.BlinkCardSessionSettings
v2:
import BlinkCard
v3000:
import BlinkCard
import BlinkCardUX // If using the UX components
v2:
import 'package:blinkcard_flutter/microblink_scanner.dart';
v3000:
import 'package:blinkcard_flutter/blinkcard_flutter.dart';
v2:
import * as BlinkCardReactNative from '@microblink/blinkcard-react-native';
v3000:
import { performScan, perfromDirectApiScan } from "@microblink/blinkcard-react-native";
Changes in scanning settings
Changes in structure
The settings body used to be a flat object, with all items in the root level.
The settings structure is now hierarchical, with sub-structures that group related items:
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TypeScript)
v2:
await recognizer.updateSettings({
allowBlurFilter: true,
tiltDetectionLevel: DetectionLevel.Mid,
paddingEdge: 0.02,
extractCvv: true,
extractIban: true,
extractOwner: true,
extractExpiryDate: true,
allowInvalidCardNumber: false
});
v3000:
{
skipImagesWithBlur: false,
tiltDetectionLevel: "mid",
inputImageMargin: 0.02,
extractionSettings: {
extractCvv: true,
extractIban: true,
extractCardholderName: true,
extractExpiryDate: true,
extractInvalidCardNumber: false
}
}
v2:
recognizer.allowBlurFilter(true)
recognizer.setTiltDetectionLevel(DetectionLevel.Mid)
recognizer.setPaddingEdge(0.02f)
recognizer.setExtractCvv(true)
recognizer.setExtractIban(true)
recognizer.setExtractOwner(true)
recognizer.setExtractExpiryDate(true)
recognizer.setAllowInvalidCardNumber(false)
v3000:
ScanningSettings(
skipImagesWithBlur = false,
tiltDetectionLevel = DetectionLevel.Mid,
inputImageMargin = 0.02f,
extractionSettings = ExtractionSettings(
extractCvv = true,
extractIban = true,
extractCardholderName = true,
extractExpiryDate = true,
extractInvalidCardNumber = false
)
)
v2:
recognizer.allowBlurFilter = true
recognizer.tiltDetectionLevel = MBCDetectionLevel.mid
recognizer.paddingEdge = 0.02
recognizer.extractCvv = true
recognizer.extractIban = true
recognizer.extractOwner = true
recognizer.extractExpiryDate = true
recognizer.allowInvalidCardNumber = false
v3000:
ScanningSettings(
skipImagesWithBlur: false,
tiltDetectionLevel: .mid,
inputImageMargin: 0.02,
extractionSettings: ExtractionSettings(
extractCvv: true,
extractIban: true,
extractCardholderName: true,
extractExpiryDate: true,
extractInvalidCardNumber: false
)
)
v2:
recognizer.allowBlurFilter = true
recognizer.tiltDetectionLevel = DetectionLevel.mid
recognizer.paddingEdge = 0.02
recognizer.extractCvv = true
recognizer.extractIban = true
recognizer.extractOwner = true
recognizer.extractExpiryDate = true
recognizer.allowInvalidCardNumber = false
v3000:
final scanningSettings = ScanningSettings(
skipImagesWithBlur: true,
tiltDetectionLevel: DetectionLevel.mid,
inputImageMargin: 0.02,
livenessSettings: LivenessSettings(),
anonymizationSettings: AnonymizationSettings(),
croppedImageSettings: CroppedImageSettings(),
extractionSettings: ExtractionSettings(
extractCvv: true,
extractIban: true,
extractCardholderName: true,
extractExpiryDate: true,
extractInvalidCardNumber: false,
),
);
v2:
recognizer.allowBlurFilter = true
recognizer.tiltDetectionLevel = DetectionLevel.mid
recognizer.paddingEdge = 0.02
recognizer.extractCvv = true
recognizer.extractIban = true
recognizer.extractOwner = true
recognizer.extractExpiryDate = true
recognizer.allowInvalidCardNumber = false
v3000:
const scanningSettings = new ScanningSettings({
skipImagesWithBlur: true,
tiltDetectionLevel: DetectionLevel.Mid,
inputImageMargin: 0.02,
livenessSettings: new LivenessSettings(),
extractionSettings: new ExtractionSettings({
extractIban: true,
extractExpiryDate: true,
extractCardholderName: true,
extractCvv: true,
extractInvalidCardNumber: false,
}),
croppedImageSettings: new CroppedImageSettings(),
anonymizationSettings: new AnonymizationSettings(),
});
Renamed settings
| v2 | v3000 | Notes |
|---|---|---|
extractOwner | extractCardholderName | Moved to extractionSettings |
allowBlurFilter | skipImagesWithBlur | Logic inverted: true now means skip blurred frames |
paddingEdge | inputImageMargin | Same meaning, default changed from 0.0 to 0.02 |
allowInvalidCardNumber | extractInvalidCardNumber | Moved to extractionSettings |
handScaleThreshold | handToCardSizeRatio | Moved to livenessSettings |
handDocumentOverlapThreshold | handCardOverlapThreshold | Moved to livenessSettings |
photocopyAnalysisMatchLevel | photocopyCheckStrictnessLevel | Moved to livenessSettings |
screenAnalysisMatchLevel | screenCheckStrictnessLevel | Moved to livenessSettings |
returnFullDocumentImage | returnCardImage | Moved to croppedImageSettings |
ownerAnonymizationMode | cardholderNameAnonymizationMode | Moved to anonymizationSettings |
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TypeScript)
| v2 | v3000 |
|---|---|
fullDocumentImageDpi | croppedImageSettings.dotsPerInch |
fullDocumentImageExtensionFactors | croppedImageSettings.extensionFactor |
| v2 | v3000 |
|---|---|
getFullDocumentImageDpi() / setFullDocumentImageDpi() | croppedImageSettings.dotsPerInch |
getFullDocumentImageExtensionFactors() | croppedImageSettings.extensionFactor |
| v2 | v3000 |
|---|---|
fullDocumentImageDpi | croppedImageSettings.dotsPerInch |
fullDocumentImageExtensionFactors | croppedImageSettings.extensionFactor |
| v2 | v3000 |
|---|---|
fullDocumentImageDpi | croppedImageSettings.dotsPerInch |
fullDocumentImageExtensionFactors | croppedImageSettings.extensionFactor |
| v2 | v3000 |
|---|---|
fullDocumentImageDpi | croppedImageSettings.dotsPerInch |
fullDocumentImageExtensionFactors | croppedImageSettings.extensionFactor |
Removed settings
- Setting JPEG encoding is no longer a separate option:
- Web: removed
returnEncodedFullDocumentImage - Android, iOS: removed
encodeFullDocumentImage
- Web: removed
- Liveness status no longer in settings:
- Web: removed
documentLivenessCallback - Android: removed
setLivenessStatusCallback() - iOS: removed
livenessStatusCallback(the delegate method)
- Web: removed
Added settings
| Setting | Location | Description |
|---|---|---|
enableCardHeldInHandCheck | livenessSettings | Enables/disables the hand presence check |
Nested settings details
In v3000, settings are organized into logical groups. Here's what each group contains:
ExtractionSettings
Controls which fields should be extracted from the card:
| Field | Type | Default | Description |
|---|---|---|---|
extractIban | boolean | true | Extract IBAN number |
extractExpiryDate | boolean | true | Extract card expiry date |
extractCardholderName | boolean | true | Extract cardholder name |
extractCvv | boolean | true | Extract CVV security code |
extractInvalidCardNumber | boolean | false | Accept card numbers that fail Luhn checksum validation |
CroppedImageSettings
Controls image cropping and return options:
| Field | Type | Default | Description |
|---|---|---|---|
dotsPerInch | number | 250 | DPI value for cropped images |
extensionFactor | number | 0.0 | Extension factor for cropped card image |
returnCardImage | boolean | false | Whether to return the cropped card image |
LivenessSettings
Controls liveness detection parameters:
| Field | Type | Default | Description |
|---|---|---|---|
handToCardSizeRatio | number | 0.15 | Minimum hand-to-card size ratio for valid hand detection |
handCardOverlapThreshold | number | 0.05 | Minimum overlap threshold between hand and card |
enableCardHeldInHandCheck | boolean | true | Enable/disable hand presence check |
screenCheckStrictnessLevel | StrictnessLevel | Level5 | Sensitivity for screen detection |
photocopyCheckStrictnessLevel | StrictnessLevel | Level5 | Sensitivity for photocopy detection |
AnonymizationSettings
Controls data anonymization:
| Field | Type | Default | Description |
|---|---|---|---|
cardNumberAnonymizationSettings | object | See below | Card number anonymization configuration |
cardNumberPrefixAnonymizationMode | AnonymizationMode | None | Card number prefix anonymization |
cvvAnonymizationMode | AnonymizationMode | None | CVV anonymization |
ibanAnonymizationMode | AnonymizationMode | None | IBAN anonymization |
cardholderNameAnonymizationMode | AnonymizationMode | None | Cardholder name anonymization |
CardNumberAnonymizationSettings:
| Field | Type | Default | Description |
|---|---|---|---|
mode | AnonymizationMode | None | Anonymization mode |
prefixDigitsVisible | number | 0 | Number of prefix digits to keep visible |
suffixDigitsVisible | number | 0 | Number of suffix digits to keep visible |
AnonymizationMode values:
| iOS, Android | Web | Description |
|---|---|---|
None | none | No anonymization |
ImageOnly | image-only | Anonymize in images only |
ResultFieldsOnly | result-fields-only | Anonymize in result fields only |
FullResult | full-result | Anonymize in both images and result fields |
StrictnessLevel changes
The MatchLevel enum has been renamed to StrictnessLevel:
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React-Native (TypeScript)
// v2
enum MatchLevel {
Disabled = 0,
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
Level5 = 5,
Level6 = 6,
Level7 = 7,
Level8 = 8,
Level9 = 9,
Level10 = 10
}
// v3000
type StrictnessLevel =
| "disabled"
| "level-1"
| "level-2"
| "level-3"
| "level-4"
| "level-5"
| "level-6"
| "level-7"
| "level-8"
| "level-9"
| "level-10";
Key changes:
- Changed from
enumto type union of string literals - Values changed from PascalCase (
Level1) to kebab-case ("level-1") Disabledrenamed to"disabled"(lowercase)
// v2
enum class MatchLevel { Disabled, Level1, Level2, ..., Level10 }
// v3000
enum class StrictnessLevel { Disabled, Level1, Level2, ..., Level10 }
// v2
enum MBCMatchLevel { disabled, level1, level2, ..., level10 }
// v3000
enum StrictnessLevel { disabled, level1, level2, ..., level10 }
// v2
enum BlinkCardMatchLevel {
(0)
Disabled,
(1)
Level1,
(2)
Level2,
(3)
Level3,
(4)
Level4,
(5)
Level5,
(6)
Level6,
(7)
Level7,
(8)
Level8,
(9)
Level9,
(10)
Level10,
}
// v3000
enum StrictnessLevel {
("disabled")
disabled,
("level1")
level1,
("level2")
level2,
("level3")
level3,
("level4")
level4,
("level5")
level5,
("level6")
level6,
("level7")
level7,
("level8")
level8,
("level9")
level9,
("level10")
level10,
}
Key changes:
- Changed from
inttostringenums - All cases renamed from uppercase to lowercase to follow Dart's coding practices.
// v2
export const BlinkCardMatchLevel = Object.freeze(
{
Disabled: 0,
Level1: 1,
Level2: 2,
Level3: 3,
Level4: 4,
Level5: 5,
Level6: 6,
Level7: 7,
Level8: 8,
Level9: 9,
Level10: 10
}
);
// v3000
export enum StrictnessLevel {
Disabled = 'disabled',
Level1 = 'level1',
Level2 = 'level2',
Level3 = 'level3',
Level4 = 'level4',
Level5 = 'level5',
Level6 = 'level6',
Level7 = 'level7',
Level8 = 'level8',
Level9 = 'level9',
Level10 = 'level10',
}
Key changes:
- Changed from
numbertostringenums.
Changes in the result object
Structural changes
The result structure in v3000 is organized differently:
- Per-side results: Card data and images are now organized by side (
firstSideResult,secondSideResult) - Card accounts array: The
cardAccountsfield now supports multiple payment accounts on a single card - Liveness results: Liveness checks are now per-side and include a new overall result
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TypeScript)
v2 structure:
{
cardNumber: "1234567890123456",
cvv: "123",
expiryDate: { /* ... */ },
owner: "John Doe",
issuer: CardIssuer.Visa,
processingStatus: ProcessingStatus.Success,
firstSideFullDocumentImage: { /* ... */ },
firstSideBlurred: false,
documentLivenessCheck: {
front: {
screenCheck: { result: CheckResult.Pass, matchLevel: MatchLevel.Level5 },
photocopyCheck: { result: CheckResult.Pass, matchLevel: MatchLevel.Level5 },
handPresenceCheck: CheckResult.Pass
},
back: { /* ... */ }
}
// ... other fields
}
v3000 structure:
{
issuingNetwork: "Visa", // String (was enum)
cardholderName: "John Doe",
// Card account fields now in array (cardNumber, cvv, expiryDate moved here)
cardAccounts: [
{
cardNumber: "1234567890123456",
cvv: "123",
expiryDate: { /* ... */ },
// ...
}
],
overallCardLivenessResult: "pass", // New field
firstSideResult: {
cardImage: { image: { /* ... */ } },
cardLivenessCheckResult: {
screenCheckResult: "pass", // Simplified (no matchLevel)
photocopyCheckResult: "pass",
cardHeldInHandCheckResult: "pass"
}
},
secondSideResult: { /* ... */ }
// ... other fields
}
v2 structure:
result.cardNumber // "1234567890123456"
result.cvv // "123"
result.expiryDate // Date
result.owner // "John Doe"
result.issuer // CardIssuer.Visa (enum)
result.processingStatus // ProcessingStatus
result.firstSideFullDocumentImage // Image
result.isFirstSideBlurred() // Boolean
result.documentLivenessCheck.front.screenCheck // TieredCheck
result.documentLivenessCheck.front.photocopyCheck // TieredCheck
result.documentLivenessCheck.front.handPresenceCheck // CheckResult
result.documentLivenessCheck.back // ...
// ... other fields
v3000 structure:
result.issuingNetwork // String (was enum)
result.cardholderName // "John Doe"
// Card account fields now in list
result.cardAccounts[0].cardNumber // "1234567890123456"
result.cardAccounts[0].cvv // "123"
result.cardAccounts[0].expiryDate // DateResult
result.overallCardLivenessResult // CheckResult (new)
result.firstSideResult?.cardImage // CroppedImageResult
result.firstSideResult?.cardLivenessCheckResult.screenCheckResult // CheckResult
result.firstSideResult?.cardLivenessCheckResult.photocopyCheckResult // CheckResult
result.firstSideResult?.cardLivenessCheckResult.cardHeldInHandCheckResult // CheckResult
result.secondSideResult // ...
// ... other fields
v2 structure:
result.cardNumber // "1234567890123456"
result.cvv // "123"
result.expiryDate // MBCDate
result.owner // "John Doe"
result.issuer // MBCIssuer (enum)
result.processingStatus // MBCBlinkCardProcessingStatus
result.firstSideFullDocumentImage // MBCImage
result.firstSideBlurred // Bool
result.documentLivenessCheck.front.screenCheck // MBCTieredCheck
result.documentLivenessCheck.front.photocopyCheck // MBCTieredCheck
result.documentLivenessCheck.front.handPresenceCheck // MBCCheckResult
result.documentLivenessCheck.back // ...
// ... other fields
v3000 structure:
result.issuingNetwork // String (was enum)
result.cardholderName // "John Doe"
// Card account fields now in array
result.cardAccounts[0].cardNumber // "1234567890123456"
result.cardAccounts[0].cvv // "123"
result.cardAccounts[0].expiryDate // DateResult
result.overallCardLivenessResult // CheckResult (new)
result.firstSideResult?.cardImage // CroppedImageResult
result.firstSideResult?.cardLivenessCheckResult.screenCheckResult // CheckResult
result.firstSideResult?.cardLivenessCheckResult.photocopyCheckResult // CheckResult
result.firstSideResult?.cardLivenessCheckResult.cardHeldInHandCheckResult // CheckResult
result.secondSideResult // ...
// ... other fields
v2 structure:
result.cardNumber // "1234567890123456"
result.cvv // "123"
result.expiryDate // Date
result.owner // "John Doe"
result.issuer // Issuer (enum)
result.processingStatus // BlinkCardProcessingStatus
result.firstSideFullDocumentImage // Base64 String
result.firstSideBlurred // Bool
result.documentLivenessCheck.front.screenCheck // BlinkCardCheckResult
result.documentLivenessCheck.front.photocopyCheck // BlinkCardCheckResult
result.documentLivenessCheck.front.handPresenceCheck // BlinkCardCheckResult
result.documentLivenessCheck.back // ...
// ... other fields
v3000 structure:
result.issuingNetwork // String (was enum)
result.cardholderName // "John Doe"
// Card account fields now in array
result.cardAccounts[0].cardNumber // "1234567890123456"
result.cardAccounts[0].cvv // "123"
result.cardAccounts[0].expiryDate // DateResult<String>
result.overallCardLivenessResult // CheckResult (new)
result.firstSideResult?.cardImage // CroppedImageResult
result.firstSideResult?.cardLivenessCheckResult.screenCheckResult // CheckResult
result.firstSideResult?.cardLivenessCheckResult.photocopyCheckResult // CheckResult
result.firstSideResult?.cardLivenessCheckResult.cardHeldInHandCheckResult // CheckResult
result.secondSideResult // BlinkCardSingleSideScanningResult
// ... other fields
v2 structure:
result.cardNumber // "1234567890123456"
result.cvv // "123"
result.expiryDate // Date
result.owner // "John Doe"
result.issuer // Issuer (enum)
result.processingStatus // BlinkCardProcessingStatus
result.firstSideFullDocumentImage // Base64 String
result.firstSideBlurred // Bool
result.documentLivenessCheck.front.screenCheck // BlinkCardCheckResult
result.documentLivenessCheck.front.photocopyCheck // BlinkCardCheckResult
result.documentLivenessCheck.front.handPresenceCheck // BlinkCardCheckResult
result.documentLivenessCheck.back // ...
// ... other fields
v3000 structure:
result.issuingNetwork // String (was enum)
result.cardholderName // "John Doe"
// Card account fields now in array
result.cardAccounts[0].cardNumber // "1234567890123456"
result.cardAccounts[0].cvv // "123"
result.cardAccounts[0].expiryDate // DateResult<string>
result.overallCardLivenessResult // CheckResult (new)
result.firstSideResult?.cardImage // CroppedImageResult
result.firstSideResult?.cardLivenessCheckResult.screenCheckResult // CheckResult
result.firstSideResult?.cardLivenessCheckResult.photocopyCheckResult // CheckResult
result.firstSideResult?.cardLivenessCheckResult.cardHeldInHandCheckResult // CheckResult
result.secondSideResult // BlinkCardSingleSideScanningResult
// ... other fields
Moved fields
Fields that haven't been renamed, but just moved, now live in the objects inside the cardAccounts array.
These are:
cardNumbercardNumberPrefixcardNumberValidcvvexpiryDate
Renamed (and moved) fields
owner->cardholderNameissuer->issuingNetwork- Changed from enum to string
firstSideFullDocumentImage->firstSideResult.cardImage- Now nested in side result
secondSideFullDocumentImage->secondSideResult.cardImage- Now nested in side result
firstSideBlurred->inputImageAnalysisResult.blurDetectionStatus- Now in ProcessResult
secondSideBlurred->inputImageAnalysisResult.blurDetectionStatus- Now in ProcessResult
documentLivenessCheck->cardLivenessCheckResult- Split per-side
documentLivenessCheck.front.screenCheck.result->firstSideResult.cardLivenessCheckResult.screenCheckResult- Simplified (no matchLevel)
documentLivenessCheck.front.photocopyCheck.result->firstSideResult.cardLivenessCheckResult.photocopyCheckResult- Simplified (no matchLevel)
documentLivenessCheck.front.handPresenceCheck->firstSideResult.cardLivenessCheckResult.cardHeldInHandCheckResult
Removed fields
- Encoded image has been removed from the result:
- Web:
firstSideFullDocumentImage.encodedImagesecondSideFullDocumentImage.encodedImage
- iOS, Android, Flutter, React Native:
encodedFirstSideFullDocumentImageencodedSecondSideFullDocumentImage
- Web:
scanningFirstSideDonefirstSideAnonymizedsecondSideAnonymizedprocessingStatus- Match levels no longer exposed in results:
- Web, Android, Flutter, React Native:
documentLivenessCheck.front.screenCheck.matchLeveldocumentLivenessCheck.front.photocopyCheck.matchLevel
- iOS:
documentLivenessCheck.front.screenCheck.leveldocumentLivenessCheck.front.photocopyCheck.level
- Web, Android, Flutter, React Native:
New fields
overallCardLivenessResult- Aggregated liveness check across all sides.
- Possible values (web):
"not-available","pass","fail" - Possible values (iOS, Android):
"NotPerformed","Pass","Fail"
cardHeldInHandCheckResult- Result of the hand presence check (per-side).
- Possible values (web):
"not-available","pass","fail" - Possible values (iOS, Android):
"NotPerformed","Pass","Fail"
cardAccounts- Supports multiple payment accounts on a card
cardAccounts[].fundingType- Optional: Card funding type (e.g., "DEBIT", "CREDIT", "CHARGE CARD")
cardAccounts[].cardCategory- Optional: Card category (e.g., "PERSONAL", "BUSINESS", "PREPAID")
cardAccounts[].issuerName- Optional: Name of the issuing financial institution
cardAccounts[].issuerCountryCode- Optional: ISO 3166-1 alpha-3 country code (e.g., "USA", "GBR", "HRV")
cardAccounts[].issuerCountry- Optional: Name of the issuer's country
CardAccountResult structure
The cardAccounts field is now an array that can contain multiple payment accounts.
Each CardAccountResult contains:
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TypeScript)
{
cardNumber: string;
cardNumberValid: boolean;
cardNumberPrefix: string | undefined;
cvv: string | undefined;
expiryDate: DateResult;
fundingType: string | undefined; // New in v3000
cardCategory: string | undefined; // New in v3000
issuerName: string | undefined; // New in v3000
issuerCountryCode: string | undefined; // New in v3000
issuerCountry: string | undefined; // New in v3000
}
data class CardAccountResult(
val cardNumber: String,
val cardNumberValid: Boolean,
val cardNumberPrefix: String?,
val cvv: String?,
val expiryDate: DateResult<String>?,
val fundingType: String?, // New in v3000
val cardCategory: String?, // New in v3000
val issuerName: String?, // New in v3000
val issuerCountryCode: String?, // New in v3000
val issuerCountry: String? // New in v3000
)
struct CardAccountResult {
var cardNumber: String
var cardNumberValid: Bool
var cardNumberPrefix: String?
var cvv: String?
var expiryDate: DateResult?
var fundingType: String? // New in v3000
var cardCategory: String? // New in v3000
var issuerName: String? // New in v3000
var issuerCountryCode: String? // New in v3000
var issuerCountry: String? // New in v3000
}
final class CardAccountResult {
final String cardNumber;
final bool cardNumberValid;
final String? cardNumberPrefix;
final String? cvv;
final DateResult<String>? expiryDate;
final String? fundingType; // New in v3000
final String? cardCategory; // New in v3000
final String? issuerName; // New in v3000
final String? issuerCountryCode; // New in v3000
final String? issuerCountry; // New in v3000
}
export class CardAccountResult {
cardNumber: string;
cardNumberValid: boolean;
cardNumberPrefix?: string;
cvv?: string;
expiryDate: DateResult<string>;
fundingType?: string; // New in v3000
cardCategory?: string; // New in v3000
issuerName?: string; // New in v3000
issuerCountryCode?: string; // New in v3000
issuerCountry?: string; // New in v3000
Processing status changes
Processing status and other frame-level information is now available in the ProcessResult object returned during scanning.
This provides real-time feedback about the scanning process on a per-frame basis.
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
Example of what you'll see when logging the ProcessResult:
{
inputImageAnalysisResult: {
processingStatus: "success", // or "detection-failed", "image-preprocessing-failed", etc.
scanningSide: "first", // or "second"
detectionStatus: "success", // or "failed", "camera-too-far", "camera-too-close", etc.
cardLocation: {
upperLeft: 123,
upperRight: 456,
lowerRight: 789,
lowerLeft: 012
},
blurDetectionStatus: "not-detected", // or "detected", "not-available"
cardRotation: "zero" // or "clockwise-90", "counter-clockwise-90", "upside-down", "not-available"
},
resultCompleteness: {
scanningStatus: "scanning-side-in-progress", // or "side-scanned", "card-scanned"
cardNumberExtractionStatus: "extracted", // or "not-available", "not-requested", "not-present", "not-extracted"
cardNumberPrefixExtractionStatus: "extracted",
expiryDateExtractionStatus: "not-present",
cardholderNameExtractionStatus: "extracted",
cvvExtractionStatus: "not-requested",
ibanExtractionStatus: "not-present",
cardImageExtractionStatus: "extracted"
}
}
Example of what you'll see when logging the ProcessResult:
BlinkCardProcessResult(
inputImageAnalysisResult = InputImageAnalysisResult(
processingStatus = ProcessingStatus.Success, // or DetectionFailed, ImagePreprocessingFailed, etc.
scanningSide = ScanningSide.First, // or Second
detectionStatus = DetectionStatus.Success, // or Failed, CameraTooFar, CameraTooClose, etc.
cardLocation = Quadrilateral(
upperLeft = Point(123, 456),
upperRight = Point(789, 456),
lowerRight = Point(789, 012),
lowerLeft = Point(123, 012)
),
blurDetectionStatus = ImageAnalysisDetectionStatus.NotDetected, // or Detected, NotAvailable
cardRotation = CardRotation.Zero // or Clockwise90, CounterClockwise90, UpsideDown, NotAvailable
),
resultCompleteness = ResultCompleteness(
scanningStatus = ScanningStatus.ScanningSideInProgress, // or SideScanned, CardScanned
cardNumberExtractionStatus = FieldExtractionStatus.Extracted, // or NotAvailable, NotRequested, NotPresent, NotExtracted
cardNumberPrefixExtractionStatus = FieldExtractionStatus.Extracted,
expiryDateExtractionStatus = FieldExtractionStatus.NotPresent,
cardholderNameExtractionStatus = FieldExtractionStatus.Extracted,
cvvExtractionStatus = FieldExtractionStatus.NotRequested,
ibanExtractionStatus = FieldExtractionStatus.NotPresent,
cardImageExtractionStatus = ImageExtractionStatus.Extracted
)
)
Example of what you'll see when logging the ProcessResult:
BlinkCardSDK.ProcessResult(
inputImageAnalysisResult: InputImageAnalysisResult(
processingStatus: .success, // or .detectionFailed, .imagePreprocessingFailed, etc.
scanningSide: .first, // or .second
detectionStatus: .success, // or .failed, .cameraTooFar, .cameraTooClose, etc.
cardLocation: Quadrilateral(
upperLeft: Point(x: 123, y: 456),
upperRight: Point(x: 789, y: 456),
lowerRight: Point(x: 789, y: 012),
lowerLeft: Point(x: 123, y: 012)
),
blurDetectionStatus: .notDetected, // or .detected, .notAvailable
cardRotation: .zero // or .clockwise90, .counterClockwise90, .upsideDown, .notAvailable
),
resultCompleteness: ResultCompleteness(
scanningStatus: .scanningSideInProgress, // or .sideScanned, .cardScanned
cardNumberExtractionStatus: .extracted, // or .notAvailable, .notRequested, .notPresent, .notExtracted
cardNumberPrefixExtractionStatus: .extracted,
expiryDateExtractionStatus: .notPresent,
cardholderNameExtractionStatus: .extracted,
cvvExtractionStatus: .notRequested,
ibanExtractionStatus: .notPresent,
cardImageExtractionStatus: .extracted
)
)
Key differences from v2:
processingStatusmoved from the final result to per-frameProcessResult- New
resultCompletenessprovides detailed extraction status for each field scanningStatusindicates overall progress (scanning-side-in-progress,side-scanned,card-scanned)- Blur detection status is now in
inputImageAnalysisResult.blurDetectionStatus
Resource management
BlinkCard v3000 requires recognition resources (models and configuration files) to perform card scanning. The SDK supports different approaches for managing these resources depending on the platform.
Web
Resources are loaded from a specified path (typically hosted on your server or CDN):
const blinkCard = await createBlinkCard({
licenseKey: "your-license-key",
resourcesLocation: "https://example.com", // URL for WASM and model files; we append "/resources" to this value
// ... other settings
});
Best practices:
- Host resources on the same domain to avoid CORS issues
- Use a CDN for better performance and caching
- Ensure resources are served with appropriate cache headers
iOS
BlinkCard SDK supports both downloaded and bundled resources:
let settings = BlinkCardSdkSettings(
licenseKey: "your-license-key",
downloadResources: true, // true = download, false = use bundled
resourceLocalFolder: "BlinkCard" // Optional custom storage location
)
Downloaded resources:
- Smaller app bundle size
- Resources downloaded on first use
- Requires network connection on first launch
Bundled resources:
- Larger app bundle size
- No network call for resources
- Immediate availability
Android
Similar to iOS, Android supports both downloaded and bundled resources:
val settings = BlinkCardSdkSettings(
licenseKey = "your-license-key",
downloadResources = true, // true = download, false = use bundled
resourceLocalFolder = "BlinkCard" // Optional custom storage location
)
Flutter
For the Flutter SDK, in BlinkCardSdkSettings object, both native platforms need to be configured:
final sdkSettings = BlinkCardSdkSettings(
licenseKey: "your-blinkcard-license-key",
downloadResources: false, // true = download, false = use bundled
resourceLocalFolder: "BlinkCard", // Optional custom storage location - match the folder names in Android and iOS applications
bundleIdentifier: "com.myiOSapplication", // iOS-specific - need to provide the applications Bundle Identifier
);
React Native
Similar to Flutter, both native platforms need to be configured in BlinkCardSdkSettings:
const sdkSettings = new BlinkCardSdkSettings({
licenseKey: 'your-blinkcard-license-key',
downloadResources: false, // true = download, false = use bundled
resourceLocalFolder: 'BlinkCard', // Optional custom storage location - match the folder names in Android and iOS applications
bundleIdentifier: 'com.myiOSapplication', // iOS-specific - need to provide the applications Bundle Identifier
});
V2 maintenance
With the v3000 release, v2 goes into maintenance mode.
This means that v2 will stop receiving feature updates, but in case of security issues, it will be patched.
For product improvements, better scanning performance, etc., please use v3000.