@sanctum-key/react-native-sdk 1.0.53 → 1.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/android/build/.transforms/3db9b15041d4285a820fc84462995ea1/results.bin +1 -0
- package/android/build/.transforms/3db9b15041d4285a820fc84462995ea1/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$BarcodeRegion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$1$5$1.class → SanctumKeySdkModule$definition$1$6$1.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$1$5$2.class → SanctumKeySdkModule$definition$1$6$2.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$1.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$2.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$3.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$4.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$5.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$6.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$1.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$2.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$3.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$4.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$5.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$6.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$7.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$View$1.class → SanctumKeySdkModule$definition$lambda$16$$inlined$View$1.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class → SanctumKeySdkModule$definition$lambda$16$lambda$15$$inlined$Prop$1.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class → SanctumKeySdkModule$definition$lambda$16$lambda$15$$inlined$Prop$2.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule.class +0 -0
- package/android/build.gradle +7 -0
- package/android/src/main/java/kyc/sanctumkey/com/SanctumKeySdkModule.kt +428 -1
- package/build/package.json +1 -1
- package/build/src/SanctumKeySdkModule.d.ts +1 -0
- package/build/src/SanctumKeySdkModule.d.ts.map +1 -1
- package/build/src/SanctumKeySdkModule.js.map +1 -1
- package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.js +50 -41
- package/build/src/components/EnhancedCameraView.js.map +1 -1
- package/build/src/components/EnhancedCameraView.web.d.ts +2 -0
- package/build/src/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.web.js +11 -6
- package/build/src/components/EnhancedCameraView.web.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +499 -78
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +3 -9
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.js +0 -2
- package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/src/components/WebBarcodeScanner.d.ts +2 -0
- package/build/src/components/WebBarcodeScanner.d.ts.map +1 -1
- package/build/src/components/WebBarcodeScanner.js +99 -85
- package/build/src/components/WebBarcodeScanner.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts +1 -0
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +1 -1
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/build/src/modules/api/types.d.ts +1 -0
- package/build/src/modules/api/types.d.ts.map +1 -1
- package/build/src/modules/api/types.js.map +1 -1
- package/package.json +1 -1
- package/plugin/build/withVisionCamera.d.ts +1 -0
- package/plugin/build/withVisionCamera.js +13 -1
- package/plugin/src/withVisionCamera.js +18 -2
- package/plugin/src/withVisionCamera.ts +19 -2
- package/src/SanctumKeySdkModule.ts +1 -0
- package/src/components/EnhancedCameraView.tsx +52 -41
- package/src/components/EnhancedCameraView.web.tsx +15 -5
- package/src/components/KYCElements/IDCardCapture.tsx +552 -75
- package/src/components/TemplateKYCExample.tsx +6 -14
- package/src/components/TemplateKYCFlowRefactored.tsx +0 -2
- package/src/components/WebBarcodeScanner.tsx +147 -198
- package/src/modules/api/KYCService.ts +3 -3
- package/src/modules/api/types.ts +1 -0
|
@@ -87,40 +87,32 @@ export function TemplateKYCExample({
|
|
|
87
87
|
|
|
88
88
|
if ((data.status === 'success' || data.status === 'in_progress') && data.result) {
|
|
89
89
|
|
|
90
|
-
// 1. Grab the backend data directly from the result (no docKey needed)
|
|
91
90
|
const backendData = data.result.data;
|
|
92
91
|
|
|
93
92
|
if (backendData) {
|
|
94
93
|
const personalInfo = backendData.user_data?.personal_information || {};
|
|
95
|
-
|
|
96
|
-
// Ensure we capture the session ID from whichever prop was provided
|
|
97
94
|
const activeSessionId = kyc_session_id || existingSessionId || backendData.session_id || "";
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
const docType = (backendData as any).document_type || (backendData as any).documentType;
|
|
98
98
|
|
|
99
|
-
// 2. Strict mapping to your expected format with improved fallbacks
|
|
100
99
|
finalPayload = {
|
|
101
100
|
data: {
|
|
101
|
+
document_type: docType,
|
|
102
102
|
verification_id: backendData.verification_id,
|
|
103
103
|
verification_status: backendData.verification_status,
|
|
104
104
|
session_id: activeSessionId,
|
|
105
105
|
user_data: {
|
|
106
106
|
personal_information: {
|
|
107
|
-
// Safely fallback to given_name and surname if first/last are empty strings
|
|
108
107
|
first_name: personalInfo.first_name || personalInfo.given_name || undefined,
|
|
109
108
|
last_name: personalInfo.last_name || personalInfo.surname || undefined,
|
|
110
109
|
given_name: personalInfo.given_name || undefined,
|
|
111
110
|
surname: personalInfo.surname || undefined,
|
|
112
|
-
|
|
113
111
|
address: personalInfo.address || undefined,
|
|
114
|
-
city: personalInfo.city || undefined,
|
|
115
|
-
postal_code: personalInfo.postal_code || undefined,
|
|
116
112
|
expiry_date: personalInfo.expiry_date || undefined,
|
|
117
113
|
issue_date: personalInfo.issue_date || undefined,
|
|
118
|
-
|
|
119
|
-
// Fallback to birth_date if date_of_birth is missing
|
|
120
114
|
date_of_birth: personalInfo.date_of_birth || personalInfo.birth_date || undefined,
|
|
121
115
|
birth_date: personalInfo.birth_date || undefined,
|
|
122
|
-
|
|
123
|
-
country: personalInfo.country || undefined,
|
|
124
116
|
document_number: personalInfo.document_number || undefined,
|
|
125
117
|
nationality: personalInfo.nationality || undefined,
|
|
126
118
|
sex: personalInfo.sex || undefined,
|
|
@@ -129,7 +121,7 @@ export function TemplateKYCExample({
|
|
|
129
121
|
}
|
|
130
122
|
};
|
|
131
123
|
|
|
132
|
-
sdkLogger.info("Wrapper
|
|
124
|
+
sdkLogger.info("Wrapper formatteddddd completion payload successfully", {
|
|
133
125
|
data: finalPayload
|
|
134
126
|
});
|
|
135
127
|
}
|
|
@@ -137,7 +129,7 @@ export function TemplateKYCExample({
|
|
|
137
129
|
sdkLogger.warn("Wrapper received completion without a success result object", { status: data.status });
|
|
138
130
|
}
|
|
139
131
|
|
|
140
|
-
onComplete(finalPayload
|
|
132
|
+
onComplete(finalPayload);
|
|
141
133
|
};
|
|
142
134
|
|
|
143
135
|
const handleCancel = () => {
|
|
@@ -169,8 +169,6 @@ export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
|
|
|
169
169
|
onComplete={(data) => {
|
|
170
170
|
sdkLogger.info("KYC Flow Completed", { referenceId: data?.result?.referenceId });
|
|
171
171
|
sdkLogger.flush();
|
|
172
|
-
logger.log('this is the data going to the TG side',data)
|
|
173
|
-
console.log('this is the data going to the TG side',data)
|
|
174
172
|
if (onComplete) onComplete(data);
|
|
175
173
|
}}
|
|
176
174
|
onError={(err) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
import type { CodeType } from 'react-native-vision-camera';
|
|
2
3
|
import {
|
|
3
4
|
MultiFormatReader,
|
|
4
5
|
BarcodeFormat,
|
|
@@ -13,261 +14,209 @@ interface WebBarcodeScannerProps {
|
|
|
13
14
|
videoRef: React.RefObject<HTMLVideoElement | null>;
|
|
14
15
|
active: boolean;
|
|
15
16
|
onBarcodeScanned: (value: string) => void;
|
|
17
|
+
formats?: CodeType[];
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
interface RoiProfile {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
contrast: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ROI_PROFILES: RoiProfile[] = [
|
|
27
|
+
// Full frame catches barcodes before the user has centered them.
|
|
28
|
+
{ width: 1, height: 1, contrast: 1 },
|
|
29
|
+
// Most ID barcodes sit in the wide center strip shown by the overlay.
|
|
30
|
+
{ width: 0.98, height: 0.64, contrast: 1 },
|
|
31
|
+
// A tighter, contrast-enhanced pass helps very dense or low-contrast PDF417.
|
|
32
|
+
{ width: 0.94, height: 0.46, contrast: 1.55 },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const ROTATIONS = [180, 90, -90];
|
|
36
|
+
const DEFAULT_FORMATS: CodeType[] = ['pdf-417'];
|
|
37
|
+
|
|
38
|
+
const ZXING_FORMATS: Partial<Record<CodeType, BarcodeFormat>> = {
|
|
39
|
+
'pdf-417': BarcodeFormat.PDF_417,
|
|
40
|
+
'code-128': BarcodeFormat.CODE_128,
|
|
41
|
+
'code-39': BarcodeFormat.CODE_39,
|
|
42
|
+
'code-93': BarcodeFormat.CODE_93,
|
|
43
|
+
codabar: BarcodeFormat.CODABAR,
|
|
44
|
+
'ean-13': BarcodeFormat.EAN_13,
|
|
45
|
+
'ean-8': BarcodeFormat.EAN_8,
|
|
46
|
+
itf: BarcodeFormat.ITF,
|
|
47
|
+
'itf-14': BarcodeFormat.ITF,
|
|
48
|
+
'upc-a': BarcodeFormat.UPC_A,
|
|
49
|
+
'upc-e': BarcodeFormat.UPC_E,
|
|
50
|
+
qr: BarcodeFormat.QR_CODE,
|
|
51
|
+
aztec: BarcodeFormat.AZTEC,
|
|
52
|
+
'data-matrix': BarcodeFormat.DATA_MATRIX,
|
|
53
|
+
'gs1-data-bar': BarcodeFormat.RSS_14,
|
|
54
|
+
'gs1-data-bar-limited': BarcodeFormat.RSS_14,
|
|
55
|
+
'gs1-data-bar-expanded': BarcodeFormat.RSS_EXPANDED,
|
|
56
|
+
};
|
|
57
|
+
|
|
18
58
|
export const WebBarcodeScanner: React.FC<WebBarcodeScannerProps> = ({
|
|
19
59
|
videoRef,
|
|
20
60
|
active,
|
|
21
61
|
onBarcodeScanned,
|
|
62
|
+
formats = DEFAULT_FORMATS,
|
|
22
63
|
}) => {
|
|
23
64
|
const readerRef = useRef<MultiFormatReader | null>(null);
|
|
24
|
-
|
|
25
65
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
26
|
-
|
|
27
|
-
const lastValueRef = useRef<string | null>(null);
|
|
28
|
-
|
|
66
|
+
const rotationCanvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
67
|
+
const lastValueRef = useRef<{ value: string | null; emittedAt: number }>({ value: null, emittedAt: 0 });
|
|
68
|
+
const attemptRef = useRef(0);
|
|
69
|
+
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
29
70
|
const stoppedRef = useRef(false);
|
|
30
|
-
|
|
31
71
|
const callbackRef = useRef(onBarcodeScanned);
|
|
32
72
|
|
|
33
73
|
useEffect(() => {
|
|
34
74
|
callbackRef.current = onBarcodeScanned;
|
|
35
75
|
}, [onBarcodeScanned]);
|
|
36
76
|
|
|
37
|
-
const decodeBitmap = useCallback(
|
|
38
|
-
(canvas
|
|
39
|
-
|
|
40
|
-
|
|
77
|
+
const decodeBitmap = useCallback((canvas: HTMLCanvasElement) => {
|
|
78
|
+
if (!readerRef.current || canvas.width === 0 || canvas.height === 0) return null;
|
|
79
|
+
|
|
80
|
+
const source = new HTMLCanvasElementLuminanceSource(canvas);
|
|
81
|
+
const attempts = [
|
|
82
|
+
new BinaryBitmap(new HybridBinarizer(source)),
|
|
83
|
+
new BinaryBitmap(new GlobalHistogramBinarizer(source)),
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
for (const bitmap of attempts) {
|
|
87
|
+
try {
|
|
88
|
+
const result = readerRef.current.decodeWithState(bitmap);
|
|
89
|
+
const text = result?.getText();
|
|
90
|
+
if (text) return text;
|
|
91
|
+
} catch {
|
|
92
|
+
// Try the next binarization strategy/rotation.
|
|
41
93
|
}
|
|
94
|
+
}
|
|
42
95
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return null;
|
|
68
|
-
},
|
|
69
|
-
[]
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const drawROI = useCallback(
|
|
73
|
-
(
|
|
74
|
-
video: HTMLVideoElement,
|
|
75
|
-
canvas: HTMLCanvasElement
|
|
76
|
-
) => {
|
|
77
|
-
const ctx = canvas.getContext('2d', {
|
|
78
|
-
willReadFrequently: true,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
if (!ctx) {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const roi = 0.8;
|
|
86
|
-
|
|
87
|
-
const sw = Math.floor(
|
|
88
|
-
video.videoWidth * roi
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const sh = Math.floor(
|
|
92
|
-
video.videoHeight * roi
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
const sx = Math.floor(
|
|
96
|
-
(video.videoWidth - sw) / 2
|
|
97
|
-
);
|
|
96
|
+
return null;
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
const drawROI = useCallback((video: HTMLVideoElement, canvas: HTMLCanvasElement, profile: RoiProfile) => {
|
|
100
|
+
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
101
|
+
if (!ctx) return false;
|
|
102
|
+
|
|
103
|
+
const sourceWidth = Math.max(1, Math.floor(video.videoWidth * profile.width));
|
|
104
|
+
const sourceHeight = Math.max(1, Math.floor(video.videoHeight * profile.height));
|
|
105
|
+
const sourceX = Math.floor((video.videoWidth - sourceWidth) / 2);
|
|
106
|
+
const sourceY = Math.floor((video.videoHeight - sourceHeight) / 2);
|
|
107
|
+
|
|
108
|
+
// Nearest-neighbour enlargement gives ZXing more pixels per narrow module
|
|
109
|
+
// on sub-1080p browser streams without allocating an unbounded canvas.
|
|
110
|
+
const desiredScale = sourceWidth < 1600 ? Math.min(1.75, 1600 / sourceWidth) : 1;
|
|
111
|
+
const boundedScale = Math.min(desiredScale, 2800 / Math.max(sourceWidth, sourceHeight));
|
|
112
|
+
const outputWidth = Math.max(1, Math.floor(sourceWidth * boundedScale));
|
|
113
|
+
const outputHeight = Math.max(1, Math.floor(sourceHeight * boundedScale));
|
|
114
|
+
|
|
115
|
+
if (canvas.width !== outputWidth || canvas.height !== outputHeight) {
|
|
116
|
+
canvas.width = outputWidth;
|
|
117
|
+
canvas.height = outputHeight;
|
|
118
|
+
}
|
|
98
119
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
121
|
+
ctx.clearRect(0, 0, outputWidth, outputHeight);
|
|
122
|
+
ctx.imageSmoothingEnabled = false;
|
|
123
|
+
ctx.filter = profile.contrast === 1 ? 'none' : `grayscale(1) contrast(${profile.contrast})`;
|
|
124
|
+
ctx.drawImage(
|
|
125
|
+
video,
|
|
126
|
+
sourceX,
|
|
127
|
+
sourceY,
|
|
128
|
+
sourceWidth,
|
|
129
|
+
sourceHeight,
|
|
130
|
+
0,
|
|
131
|
+
0,
|
|
132
|
+
outputWidth,
|
|
133
|
+
outputHeight,
|
|
134
|
+
);
|
|
135
|
+
ctx.filter = 'none';
|
|
136
|
+
return true;
|
|
137
|
+
}, []);
|
|
102
138
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
canvas.height !== sh
|
|
106
|
-
) {
|
|
107
|
-
canvas.width = sw;
|
|
108
|
-
canvas.height = sh;
|
|
109
|
-
}
|
|
139
|
+
const rotateCanvas = useCallback((sourceCanvas: HTMLCanvasElement, degrees: number) => {
|
|
140
|
+
if (!rotationCanvasRef.current) rotationCanvasRef.current = document.createElement('canvas');
|
|
110
141
|
|
|
111
|
-
ctx.drawImage(
|
|
112
|
-
video,
|
|
113
|
-
sx,
|
|
114
|
-
sy,
|
|
115
|
-
sw,
|
|
116
|
-
sh,
|
|
117
|
-
0,
|
|
118
|
-
0,
|
|
119
|
-
sw,
|
|
120
|
-
sh
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
return true;
|
|
124
|
-
},
|
|
125
|
-
[]
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
// Add a persistent rotation canvas ref at the top
|
|
129
|
-
const rotationCanvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
130
|
-
|
|
131
|
-
// Update rotateCanvas to re-use the persistent canvas
|
|
132
|
-
const rotateCanvas = useCallback(
|
|
133
|
-
(sourceCanvas: HTMLCanvasElement, degrees: number) => {
|
|
134
|
-
if (!rotationCanvasRef.current) {
|
|
135
|
-
rotationCanvasRef.current = document.createElement('canvas');
|
|
136
|
-
}
|
|
137
142
|
const rotated = rotationCanvasRef.current;
|
|
138
|
-
const
|
|
139
|
-
|
|
143
|
+
const swapDimensions = Math.abs(degrees) === 90;
|
|
144
|
+
rotated.width = swapDimensions ? sourceCanvas.height : sourceCanvas.width;
|
|
145
|
+
rotated.height = swapDimensions ? sourceCanvas.width : sourceCanvas.height;
|
|
140
146
|
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
143
|
-
rotated.width = sourceCanvas.height;
|
|
144
|
-
rotated.height = sourceCanvas.width;
|
|
145
|
-
} else {
|
|
146
|
-
rotated.width = sourceCanvas.width;
|
|
147
|
-
rotated.height = sourceCanvas.height;
|
|
148
|
-
}
|
|
147
|
+
const ctx = rotated.getContext('2d', { willReadFrequently: true });
|
|
148
|
+
if (!ctx) return null;
|
|
149
149
|
|
|
150
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
150
151
|
ctx.clearRect(0, 0, rotated.width, rotated.height);
|
|
151
152
|
ctx.translate(rotated.width / 2, rotated.height / 2);
|
|
152
|
-
ctx.rotate(
|
|
153
|
+
ctx.rotate((degrees * Math.PI) / 180);
|
|
153
154
|
ctx.drawImage(sourceCanvas, -sourceCanvas.width / 2, -sourceCanvas.height / 2);
|
|
154
155
|
return rotated;
|
|
155
|
-
},
|
|
156
|
-
[]
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
}, []);
|
|
160
157
|
|
|
161
158
|
const tryDecode = useCallback(() => {
|
|
162
159
|
const video = videoRef.current;
|
|
163
|
-
|
|
164
160
|
const canvas = canvasRef.current;
|
|
161
|
+
if (!video || !canvas || video.readyState < 2 || video.videoWidth === 0 || video.videoHeight === 0) return null;
|
|
165
162
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
video.readyState < 2
|
|
170
|
-
) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
163
|
+
const attempt = attemptRef.current++;
|
|
164
|
+
const profile = ROI_PROFILES[attempt % ROI_PROFILES.length];
|
|
165
|
+
if (!drawROI(video, canvas, profile)) return null;
|
|
173
166
|
|
|
174
|
-
const
|
|
167
|
+
const directValue = decodeBitmap(canvas);
|
|
168
|
+
if (directValue) return directValue;
|
|
175
169
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
170
|
+
// Spread rotation work over successive frames to keep the UI responsive.
|
|
171
|
+
const rotation = ROTATIONS[attempt % ROTATIONS.length];
|
|
172
|
+
const rotated = rotateCanvas(canvas, rotation);
|
|
173
|
+
return rotated ? decodeBitmap(rotated) : null;
|
|
174
|
+
}, [decodeBitmap, drawROI, rotateCanvas, videoRef]);
|
|
179
175
|
|
|
180
|
-
|
|
176
|
+
const tick = useCallback(() => {
|
|
177
|
+
if (stoppedRef.current) return;
|
|
181
178
|
|
|
179
|
+
const value = tryDecode();
|
|
182
180
|
if (value) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
for (const rotation of rotations) {
|
|
189
|
-
const rotated = rotateCanvas(
|
|
190
|
-
canvas,
|
|
191
|
-
rotation
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
if (!rotated) {
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
value = decodeBitmap(rotated);
|
|
199
|
-
|
|
200
|
-
if (value) {
|
|
201
|
-
return value;
|
|
181
|
+
const now = Date.now();
|
|
182
|
+
const previous = lastValueRef.current;
|
|
183
|
+
if (value !== previous.value || now - previous.emittedAt > 1500) {
|
|
184
|
+
lastValueRef.current = { value, emittedAt: now };
|
|
185
|
+
callbackRef.current(value);
|
|
202
186
|
}
|
|
203
187
|
}
|
|
204
188
|
|
|
205
|
-
|
|
206
|
-
}, [
|
|
207
|
-
videoRef,
|
|
208
|
-
drawROI,
|
|
209
|
-
decodeBitmap,
|
|
210
|
-
rotateCanvas,
|
|
211
|
-
]);
|
|
212
|
-
|
|
213
|
-
const tick = useCallback(() => {
|
|
214
|
-
if (stoppedRef.current) return;
|
|
215
|
-
|
|
216
|
-
const value = tryDecode();
|
|
217
|
-
if (value && value !== lastValueRef.current) {
|
|
218
|
-
lastValueRef.current = value;
|
|
219
|
-
callbackRef.current(value);
|
|
220
|
-
// If you want it to stop scanning on success, leave 'return;'
|
|
221
|
-
// but ensure your parent component handles unmounting the scanner!
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
setTimeout(tick, 100);
|
|
189
|
+
timerRef.current = setTimeout(tick, 180);
|
|
226
190
|
}, [tryDecode]);
|
|
227
191
|
|
|
228
192
|
useEffect(() => {
|
|
229
|
-
if (!active)
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
193
|
+
if (!active) return;
|
|
232
194
|
|
|
233
195
|
stoppedRef.current = false;
|
|
196
|
+
attemptRef.current = 0;
|
|
197
|
+
lastValueRef.current = { value: null, emittedAt: 0 };
|
|
198
|
+
if (!canvasRef.current) canvasRef.current = document.createElement('canvas');
|
|
234
199
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
canvasRef.current =
|
|
239
|
-
document.createElement('canvas');
|
|
240
|
-
}
|
|
241
|
-
|
|
200
|
+
const possibleFormats = Array.from(new Set(
|
|
201
|
+
formats.map(format => ZXING_FORMATS[format]).filter((format): format is BarcodeFormat => format !== undefined),
|
|
202
|
+
));
|
|
242
203
|
const hints = new Map();
|
|
204
|
+
hints.set(DecodeHintType.POSSIBLE_FORMATS, possibleFormats.length > 0 ? possibleFormats : [BarcodeFormat.PDF_417]);
|
|
205
|
+
hints.set(DecodeHintType.TRY_HARDER, true);
|
|
243
206
|
|
|
244
|
-
|
|
245
|
-
DecodeHintType.POSSIBLE_FORMATS,
|
|
246
|
-
[BarcodeFormat.PDF_417]
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
hints.set(
|
|
250
|
-
DecodeHintType.TRY_HARDER,
|
|
251
|
-
true
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
const reader =
|
|
255
|
-
new MultiFormatReader();
|
|
256
|
-
|
|
207
|
+
const reader = new MultiFormatReader();
|
|
257
208
|
reader.setHints(hints);
|
|
258
|
-
|
|
259
209
|
readerRef.current = reader;
|
|
260
|
-
|
|
261
210
|
tick();
|
|
262
211
|
|
|
263
212
|
return () => {
|
|
264
213
|
stoppedRef.current = true;
|
|
265
|
-
|
|
214
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
215
|
+
timerRef.current = null;
|
|
266
216
|
readerRef.current?.reset();
|
|
267
|
-
|
|
268
217
|
readerRef.current = null;
|
|
269
218
|
};
|
|
270
|
-
}, [active, tick]);
|
|
219
|
+
}, [active, formats, tick]);
|
|
271
220
|
|
|
272
221
|
return null;
|
|
273
|
-
};
|
|
222
|
+
};
|
|
@@ -279,14 +279,14 @@ export class KYCService {
|
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
async extractBarcode(params: { fileUri: string; token: string; docType: string; postfix?: string }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
|
|
282
|
+
async extractBarcode(params: { fileUri: string; token: string; docType: string; postfix?: string; timeoutMs?: number }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
|
|
283
283
|
const formData = new FormData();
|
|
284
284
|
await appendFileToFormData(formData, 'file', params.fileUri, `id_${params.postfix || 'back'}_photo.jpg`, 'image/jpeg');
|
|
285
285
|
|
|
286
286
|
const url = `${this.barcodeServiceURL}/decode_barcode/?doc_type=${encodeURIComponent(params.docType)}`;
|
|
287
287
|
|
|
288
288
|
try {
|
|
289
|
-
return await this.crossPlatformMultipartUpload<any>(url, formData, params.token, 60000);
|
|
289
|
+
return await this.crossPlatformMultipartUpload<any>(url, formData, params.token, params.timeoutMs ?? 60000);
|
|
290
290
|
} catch (e: any) {
|
|
291
291
|
throw e;
|
|
292
292
|
}
|
|
@@ -651,4 +651,4 @@ export const clearAuthToken = () => {
|
|
|
651
651
|
|
|
652
652
|
export const errorMessage = (error: any) => {
|
|
653
653
|
return error.response?.data?.message || error.response?.data?.detail || error.response?.data?.error || error.response?.data || error.message;
|
|
654
|
-
}
|
|
654
|
+
}
|
package/src/modules/api/types.ts
CHANGED