@myazahq/kyc-sdk-react-native 2.0.2 → 2.1.0
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 +111 -8
- package/android/build.gradle +6 -0
- package/android/src/main/AndroidManifest.xml +14 -4
- package/android/src/main/java/com/margelo/nitro/myazakyc/HybridMyazaEmrtd.kt +358 -0
- package/android/src/main/java/com/margelo/nitro/myazakyc/HybridMyazaFaceDetector.kt +88 -1
- package/android/src/main/java/com/margelo/nitro/myazakyc/HybridMyazaRectDetector.kt +34 -0
- package/android/src/main/java/com/margelo/nitro/myazakyc/HybridMyazaTextRecognizer.kt +118 -0
- package/android/src/main/java/com/margelo/nitro/myazakyc/Jp2Decoder.kt +117 -0
- package/app.plugin.js +102 -13
- package/ios/HybridMyazaEmrtd.swift +447 -0
- package/ios/HybridMyazaFaceDetector.swift +104 -2
- package/ios/HybridMyazaRectDetector.swift +103 -0
- package/ios/HybridMyazaTextRecognizer.swift +111 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/KycSdkReactNative+autolinking.cmake +87 -0
- package/nitrogen/generated/android/KycSdkReactNative+autolinking.gradle +27 -0
- package/nitrogen/generated/android/KycSdkReactNativeOnLoad.cpp +102 -0
- package/nitrogen/generated/android/KycSdkReactNativeOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JDetectedRect.hpp +81 -0
- package/nitrogen/generated/android/c++/JEmrtdApduResponse.hpp +61 -0
- package/nitrogen/generated/android/c++/JEmrtdTagInfo.hpp +61 -0
- package/nitrogen/generated/android/c++/JFaceResult.hpp +113 -0
- package/nitrogen/generated/android/c++/JHybridMyazaEmrtdSpec.cpp +176 -0
- package/nitrogen/generated/android/c++/JHybridMyazaEmrtdSpec.hpp +75 -0
- package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.cpp +60 -0
- package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.hpp +63 -0
- package/nitrogen/generated/android/c++/JHybridMyazaRectDetectorSpec.cpp +60 -0
- package/nitrogen/generated/android/c++/JHybridMyazaRectDetectorSpec.hpp +63 -0
- package/nitrogen/generated/android/c++/JHybridMyazaTextRecognizerSpec.cpp +80 -0
- package/nitrogen/generated/android/c++/JHybridMyazaTextRecognizerSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JTextResult.hpp +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/DetectedRect.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/EmrtdApduResponse.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/EmrtdTagInfo.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/FaceResult.kt +121 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaEmrtdSpec.kt +103 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaFaceDetectorSpec.kt +55 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaRectDetectorSpec.kt +55 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaTextRecognizerSpec.kt +60 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/KycSdkReactNativeOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/TextResult.kt +51 -0
- package/nitrogen/generated/ios/KycSdkReactNative+autolinking.rb +62 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.cpp +143 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.hpp +423 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Umbrella.hpp +81 -0
- package/nitrogen/generated/ios/KycSdkReactNativeAutolinking.mm +57 -0
- package/nitrogen/generated/ios/KycSdkReactNativeAutolinking.swift +62 -0
- package/nitrogen/generated/ios/c++/HybridMyazaEmrtdSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridMyazaEmrtdSpecSwift.hpp +184 -0
- package/nitrogen/generated/ios/c++/HybridMyazaFaceDetectorSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridMyazaFaceDetectorSpecSwift.hpp +87 -0
- package/nitrogen/generated/ios/c++/HybridMyazaRectDetectorSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridMyazaRectDetectorSpecSwift.hpp +87 -0
- package/nitrogen/generated/ios/c++/HybridMyazaTextRecognizerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridMyazaTextRecognizerSpecSwift.hpp +98 -0
- package/nitrogen/generated/ios/swift/DetectedRect.swift +59 -0
- package/nitrogen/generated/ios/swift/EmrtdApduResponse.swift +34 -0
- package/nitrogen/generated/ios/swift/EmrtdTagInfo.swift +34 -0
- package/nitrogen/generated/ios/swift/FaceResult.swift +99 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_EmrtdApduResponse.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_EmrtdTagInfo.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_TextResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridMyazaEmrtdSpec.swift +67 -0
- package/nitrogen/generated/ios/swift/HybridMyazaEmrtdSpec_cxx.swift +317 -0
- package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec_cxx.swift +143 -0
- package/nitrogen/generated/ios/swift/HybridMyazaRectDetectorSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridMyazaRectDetectorSpec_cxx.swift +143 -0
- package/nitrogen/generated/ios/swift/HybridMyazaTextRecognizerSpec.swift +57 -0
- package/nitrogen/generated/ios/swift/HybridMyazaTextRecognizerSpec_cxx.swift +162 -0
- package/nitrogen/generated/ios/swift/TextResult.swift +35 -0
- package/nitrogen/generated/shared/c++/DetectedRect.hpp +107 -0
- package/nitrogen/generated/shared/c++/EmrtdApduResponse.hpp +87 -0
- package/nitrogen/generated/shared/c++/EmrtdTagInfo.hpp +87 -0
- package/nitrogen/generated/shared/c++/FaceResult.hpp +139 -0
- package/nitrogen/generated/shared/c++/HybridMyazaEmrtdSpec.cpp +33 -0
- package/nitrogen/generated/shared/c++/HybridMyazaEmrtdSpec.hpp +80 -0
- package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.hpp +67 -0
- package/nitrogen/generated/shared/c++/HybridMyazaRectDetectorSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridMyazaRectDetectorSpec.hpp +67 -0
- package/nitrogen/generated/shared/c++/HybridMyazaTextRecognizerSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridMyazaTextRecognizerSpec.hpp +70 -0
- package/nitrogen/generated/shared/c++/TextResult.hpp +84 -0
- package/package.json +16 -4
- package/src/MyazaKYC.tsx +115 -22
- package/src/capture/documentIdentity.ts +153 -0
- package/src/capture/documentSignals.ts +177 -0
- package/src/capture/documentTextGate.ts +171 -0
- package/src/capture/hints.ts +57 -0
- package/src/capture/index.ts +24 -0
- package/src/capture/rectDetector.ts +84 -0
- package/src/capture/rectGate.ts +91 -0
- package/src/capture/useAutoCapture.ts +198 -0
- package/src/components/CameraViewfinder.tsx +226 -70
- package/src/components/ContactCodeEntry.tsx +139 -0
- package/src/components/CountryField.tsx +87 -0
- package/src/components/CountryFlag.tsx +15 -15
- package/src/components/DialCodePicker.tsx +186 -0
- package/src/components/DocumentCropper.tsx +1 -1
- package/src/components/DocumentReview.tsx +142 -0
- package/src/components/DocumentReviewSide.tsx +135 -0
- package/src/components/DocumentReviewZoom.tsx +119 -0
- package/src/components/ExpiryCountdown.tsx +52 -0
- package/src/components/FlashOverlay.tsx +156 -0
- package/src/components/Icon.tsx +67 -1
- package/src/components/KycFlow.tsx +66 -94
- package/src/components/KycSheet.tsx +148 -18
- package/src/components/MediaSourceSheet.tsx +163 -0
- package/src/components/MyazaButton.tsx +14 -2
- package/src/components/MyazaDateField.tsx +240 -0
- package/src/components/MyazaInput.tsx +73 -26
- package/src/components/MyazaSelect.tsx +283 -0
- package/src/components/MyazaWordmark.tsx +33 -0
- package/src/components/OptionRow.tsx +132 -0
- package/src/components/PhoneNumberInput.tsx +149 -0
- package/src/components/PoweredBy.tsx +89 -0
- package/src/components/ReadyPrimer.tsx +149 -0
- package/src/components/StepView.tsx +91 -0
- package/src/components/Typography.tsx +18 -3
- package/src/components/VerifiedNotice.tsx +38 -0
- package/src/components/WhatsAppIcon.tsx +29 -0
- package/src/components/WorkflowGate.tsx +128 -0
- package/src/components/brand-font.ts +113 -0
- package/src/components/flashHoleGeometry.ts +35 -0
- package/src/components/flow/FatalConfigError.tsx +56 -0
- package/src/components/fonts.ts +2 -19
- package/src/components/readyPrimerContent.ts +52 -0
- package/src/components/runtime.tsx +38 -43
- package/src/components/stepHeaderMeta.tsx +87 -0
- package/src/components/theme-provider.tsx +89 -0
- package/src/components/useWorkflowMount.ts +116 -0
- package/src/components/viewfinder/CardGuide.tsx +60 -0
- package/src/components/viewfinder/DocumentGhost.tsx +149 -0
- package/src/components/viewfinder/ImmersiveBottomBar.tsx +172 -0
- package/src/components/viewfinder/ImmersiveControls.tsx +127 -0
- package/src/components/viewfinder/ImmersiveGuide.tsx +174 -0
- package/src/components/viewfinder/ImmersiveOverlay.tsx +79 -0
- package/src/components/viewfinder/ViewfinderControls.tsx +154 -0
- package/src/config/brand.ts +97 -0
- package/src/config/business.ts +179 -0
- package/src/config/businessSteps.ts +105 -0
- package/src/config/contact.ts +159 -0
- package/src/config/countryNames.g.ts +254 -0
- package/src/config/currencyFlags.ts +144 -0
- package/src/config/font-resolve.ts +78 -0
- package/src/config/idTypes.ts +96 -1
- package/src/config/keyPeople.ts +152 -0
- package/src/config/phone.ts +39 -0
- package/src/config/proofOfAddress.ts +75 -0
- package/src/config/questionnaire.ts +153 -0
- package/src/config/regions.ts +118 -0
- package/src/config/registrationHint.ts +87 -0
- package/src/config/stepOrder.ts +118 -0
- package/src/config/theme.ts +38 -2
- package/src/config/workflowMerge.ts +124 -0
- package/src/emrtd/bac.ts +128 -0
- package/src/emrtd/bytes.ts +127 -0
- package/src/emrtd/crypto.ts +163 -0
- package/src/emrtd/der.ts +192 -0
- package/src/emrtd/dg2.ts +111 -0
- package/src/emrtd/files.ts +215 -0
- package/src/emrtd/index.ts +30 -0
- package/src/emrtd/mrzKey.ts +54 -0
- package/src/emrtd/native.ts +79 -0
- package/src/emrtd/optionalRead.ts +42 -0
- package/src/emrtd/read.ts +224 -0
- package/src/emrtd/retry.ts +98 -0
- package/src/emrtd/secureMessaging.ts +178 -0
- package/src/emrtd/session.ts +200 -0
- package/src/emrtd/stages.ts +122 -0
- package/src/index.ts +110 -0
- package/src/lib/calendar.ts +71 -0
- package/src/lib/use-keyboard-inset.ts +34 -0
- package/src/liveness/challengeManager.ts +27 -1
- package/src/liveness/faceContinuity.ts +153 -0
- package/src/liveness/flashDetector.ts +145 -0
- package/src/liveness/flashReadyGate.ts +106 -0
- package/src/liveness/flashRunner.ts +118 -0
- package/src/liveness/integritySignals.ts +60 -0
- package/src/liveness/livenessState.ts +139 -0
- package/src/liveness/speech.ts +19 -1
- package/src/liveness/types.ts +48 -1
- package/src/liveness/useFlashPhase.ts +78 -0
- package/src/liveness/useLiveness.ts +260 -75
- package/src/liveness/visionCameraFaceDetector.ts +14 -0
- package/src/mrz/extract.ts +101 -0
- package/src/mrz/parse.ts +178 -0
- package/src/mrz/textRecognizer.ts +149 -0
- package/src/screens/ApplicantRoleStep.tsx +331 -0
- package/src/screens/BusinessDetailsStep.tsx +212 -0
- package/src/screens/BusinessDocumentSlot.tsx +167 -0
- package/src/screens/BusinessDocumentsStep.tsx +213 -0
- package/src/screens/BusinessKeyPeopleStep.tsx +151 -0
- package/src/screens/BusinessKeyPersonRow.tsx +170 -0
- package/src/screens/CompanyInfoFields.tsx +83 -0
- package/src/screens/ConsentStep.tsx +68 -68
- package/src/screens/ContactChannelChoice.tsx +133 -0
- package/src/screens/ContactCodeStep.tsx +130 -0
- package/src/screens/ContactDestinationField.tsx +70 -0
- package/src/screens/ContactFooterNote.tsx +34 -0
- package/src/screens/ContactVerificationStep.tsx +199 -0
- package/src/screens/ContactVerifiedPanel.tsx +36 -0
- package/src/screens/CountrySelectStep.tsx +210 -0
- package/src/screens/DocumentCaptureStep.tsx +156 -132
- package/src/screens/IdInputStep.tsx +13 -5
- package/src/screens/IdTypeStep.tsx +74 -33
- package/src/screens/KeyPeopleInviteLinks.tsx +239 -0
- package/src/screens/LivenessStep.tsx +155 -247
- package/src/screens/MrzScanView.tsx +173 -0
- package/src/screens/NfcStep.tsx +290 -0
- package/src/screens/ProofOfAddressStep.tsx +238 -0
- package/src/screens/QuestionnaireField.tsx +219 -0
- package/src/screens/QuestionnaireMoneyField.tsx +134 -0
- package/src/screens/QuestionnaireStep.tsx +76 -0
- package/src/screens/SubmittedStep.tsx +5 -0
- package/src/screens/consent/model.ts +109 -0
- package/src/screens/document/CameraPhase.tsx +150 -0
- package/src/screens/document/RequiredPill.tsx +60 -0
- package/src/screens/liveness/LightingBanner.tsx +50 -0
- package/src/screens/liveness/LivenessOutcome.tsx +109 -0
- package/src/screens/liveness/ProgressDots.tsx +67 -0
- package/src/screens/liveness/SelfiePreview.tsx +39 -0
- package/src/screens/liveness/constants.ts +31 -0
- package/src/screens/liveness/index.ts +8 -0
- package/src/screens/liveness/resolveGuidance.ts +27 -0
- package/src/screens/liveness/useSelfieUpload.ts +107 -0
- package/src/screens/nfc/NfcMrzPrompt.tsx +40 -0
- package/src/screens/nfc/NfcReadBody.tsx +63 -0
- package/src/screens/nfc/NfcReadProgress.tsx +96 -0
- package/src/screens/nfc/NfcScanIllustration.tsx +244 -0
- package/src/screens/nfc/NfcScannedSummary.tsx +74 -0
- package/src/screens/nfc/NfcSuccessPanel.tsx +162 -0
- package/src/screens/nfc/availability.ts +73 -0
- package/src/screens/nfc/readErrorMessage.ts +93 -0
- package/src/screens/nfc/useChipPortrait.ts +58 -0
- package/src/screens/nfc/useNfcAvailability.ts +82 -0
- package/src/screens/useFlashHole.ts +105 -0
- package/src/screens/useFlashSequence.ts +96 -0
- package/src/screens/usePoaAttach.ts +121 -0
- package/src/services/api-types.ts +164 -0
- package/src/services/api-verify-types.ts +99 -0
- package/src/services/api.ts +72 -102
- package/src/services/cardCrop.ts +25 -10
- package/src/services/contactErrors.ts +43 -0
- package/src/services/deviceMetadata.ts +1 -1
- package/src/services/documentPicker.ts +20 -0
- package/src/services/fingerprint-sources.ts +129 -0
- package/src/services/fingerprint.ts +101 -0
- package/src/services/haptics.ts +38 -0
- package/src/services/mediaCompress.ts +7 -2
- package/src/services/resolveUrl.ts +2 -2
- package/src/services/workflowGate.ts +138 -0
- package/src/specs/MyazaEmrtd.nitro.ts +114 -0
- package/src/specs/MyazaFaceDetector.nitro.ts +10 -0
- package/src/specs/MyazaRectDetector.nitro.ts +50 -0
- package/src/specs/MyazaTextRecognizer.nitro.ts +48 -0
- package/src/store/derive.ts +225 -0
- package/src/store/kycStore.ts +250 -149
- package/src/store/state.ts +253 -0
- package/src/store/submit.ts +101 -0
- package/src/store/submitApplicant.ts +81 -0
- package/src/types/appearance.ts +113 -0
- package/src/types/business.ts +131 -0
- package/src/types/config.ts +150 -124
- package/src/types/id-types.ts +57 -0
- package/src/types/verification.ts +5 -0
- package/src/types/workflow.ts +141 -0
- package/src/utils/amountFormat.ts +73 -0
- package/src/utils/tokens.ts +4 -2
- package/src/__tests__/cardCrop.test.ts +0 -39
- package/src/__tests__/deviceMetadata.test.ts +0 -34
- package/src/__tests__/errors.test.ts +0 -34
- package/src/__tests__/flow.test.ts +0 -61
- package/src/__tests__/gestureDetector.test.ts +0 -37
- package/src/__tests__/liveness.test.ts +0 -112
- package/src/__tests__/resolveUrl.test.ts +0 -64
- package/src/__tests__/validators.test.ts +0 -38
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
+
import type { Frame } from 'react-native-vision-camera';
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Nitro spec for document EDGE detection (VisionCamera v5).
|
|
6
|
+
//
|
|
7
|
+
// iOS only, on purpose. Apple Vision ships a rectangle detector
|
|
8
|
+
// (`VNDetectRectanglesRequest`); ML Kit has no frame-by-frame equivalent, so
|
|
9
|
+
// Android's auto-capture rides text recognition alone — which is why the text
|
|
10
|
+
// gate had to answer the framing question by itself and does so on both
|
|
11
|
+
// platforms. This is an ADDITIONAL signal on iOS, never a replacement:
|
|
12
|
+
//
|
|
13
|
+
// • geometry answers "is it framed like a document"
|
|
14
|
+
// • text answers "is it the RIGHT document"
|
|
15
|
+
//
|
|
16
|
+
// Aspect ratio cannot tell a passport from a driver's licence, so shooting on
|
|
17
|
+
// geometry alone would happily capture the wrong document. Both must agree.
|
|
18
|
+
//
|
|
19
|
+
// A build without this module simply reports nothing found, and auto-capture
|
|
20
|
+
// falls back to the text gate — the same null-degrade posture as the rest of
|
|
21
|
+
// the native surface.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
export interface DetectedRect {
|
|
25
|
+
/** Whether a rectangle was found at all. */
|
|
26
|
+
found: boolean;
|
|
27
|
+
/** Normalised 0..1 bounding box of the detected rectangle. */
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
/** Width ÷ height of the detected shape — what identifies a card vs a page. */
|
|
33
|
+
aspect: number;
|
|
34
|
+
/** Detector confidence, 0..1. */
|
|
35
|
+
confidence: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MyazaRectDetector
|
|
39
|
+
extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
|
|
40
|
+
/**
|
|
41
|
+
* Find the most prominent document-like rectangle in a camera {@link Frame}.
|
|
42
|
+
*
|
|
43
|
+
* Called per-frame from the VisionCamera worklet, so the frame never crosses
|
|
44
|
+
* the JS bridge — only the measured box comes back.
|
|
45
|
+
*
|
|
46
|
+
* Returns `found: false` when nothing qualifies, which is the ordinary case
|
|
47
|
+
* while the user is still aiming.
|
|
48
|
+
*/
|
|
49
|
+
detectRect(frame: Frame): DetectedRect;
|
|
50
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
+
import type { Frame } from 'react-native-vision-camera';
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Nitro spec for on-device text recognition (VisionCamera v5).
|
|
6
|
+
//
|
|
7
|
+
// Same shape as the face detector: nitrogen generates the Swift and Kotlin base
|
|
8
|
+
// classes from this file, and `nitro.json` autolinks the concrete impls —
|
|
9
|
+
// iOS = Apple Vision (`VNRecognizeTextRequest`), Android = Google ML Kit.
|
|
10
|
+
//
|
|
11
|
+
// It runs inside the camera-thread worklet, so the frame never crosses the JS
|
|
12
|
+
// bridge; only the recognised lines come back.
|
|
13
|
+
//
|
|
14
|
+
// Deliberately returns LINES rather than a parsed MRZ. Parsing belongs in TS
|
|
15
|
+
// where it is testable against the ICAO specimen without a device — and the
|
|
16
|
+
// check digits, not the recogniser, are what decide whether a read is good.
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
export interface TextResult {
|
|
20
|
+
/**
|
|
21
|
+
* Recognised lines, top to bottom. Empty when nothing was found — which is
|
|
22
|
+
* the ordinary case for most frames while the user is still aiming.
|
|
23
|
+
*/
|
|
24
|
+
lines: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MyazaTextRecognizer extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
|
|
28
|
+
/**
|
|
29
|
+
* Recognise text in a camera {@link Frame}. Called per-frame from the
|
|
30
|
+
* VisionCamera worklet.
|
|
31
|
+
*
|
|
32
|
+
* `bottomFraction` restricts recognition to the bottom slice of the frame,
|
|
33
|
+
* where the MRZ lives. Passing 1 scans the whole frame. Cropping is what
|
|
34
|
+
* makes this fast enough to run per-frame AND what stops the printed fields
|
|
35
|
+
* above competing with the MRZ for the recogniser's attention.
|
|
36
|
+
*/
|
|
37
|
+
recognizeText(frame: Frame, bottomFraction: number): TextResult;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Recognise text in a STILL image at `uri` (a local `file://` path).
|
|
41
|
+
*
|
|
42
|
+
* This is what the document step uses: it captures a photo rather than
|
|
43
|
+
* streaming frames, and reading the MRZ off the picture the user just took is
|
|
44
|
+
* what spares them scanning the same passport twice. Same `bottomFraction`
|
|
45
|
+
* crop as the frame version.
|
|
46
|
+
*/
|
|
47
|
+
recognizeTextInImage(uri: string, bottomFraction: number): Promise<TextResult>;
|
|
48
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Pure derivations over the flow state.
|
|
3
|
+
//
|
|
4
|
+
// Split from kycStore.ts (200-line rule). Everything here is a function of
|
|
5
|
+
// state alone — no `set`, no side effects — which is what makes the flow's
|
|
6
|
+
// shape (which steps run, in what order, against which country) testable and
|
|
7
|
+
// keeps a single answer to each of those questions.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
import { requiresDocumentCapture, supportsNfcChip } from '../config/idTypes';
|
|
11
|
+
import {
|
|
12
|
+
getStepProgress,
|
|
13
|
+
nextStepInOrder,
|
|
14
|
+
previousStepInOrder,
|
|
15
|
+
type StepOrderOptions,
|
|
16
|
+
} from '../config/stepOrder';
|
|
17
|
+
import { hasProofOfAddressStep } from '../config/proofOfAddress';
|
|
18
|
+
import { businessProductsForCountry, isBusinessFlow } from '../config/business';
|
|
19
|
+
import {
|
|
20
|
+
hasApplicantVerification,
|
|
21
|
+
hasBusinessDocumentsStep,
|
|
22
|
+
hasKeyPeopleCollection,
|
|
23
|
+
} from '../config/businessSteps';
|
|
24
|
+
import { hasActiveQuestionnaire } from '../config/questionnaire';
|
|
25
|
+
import { applicantSelfCountry, keyPeoplePayload } from '../config/keyPeople';
|
|
26
|
+
import { featuresFor } from './serverConfig';
|
|
27
|
+
import type { VerifyRequest } from '../services/api';
|
|
28
|
+
import type { KYCStep, SupportedCountry } from '../types/config';
|
|
29
|
+
import type { KycState } from './state';
|
|
30
|
+
|
|
31
|
+
export function livenessEnabled(state: KycState): boolean {
|
|
32
|
+
// Consumer baseline: liveness is on unless explicitly disabled.
|
|
33
|
+
if (state.config.enableLiveness === false) return false;
|
|
34
|
+
const idType = state.selectedIdType;
|
|
35
|
+
if (!idType) return true;
|
|
36
|
+
// Server flag wins when present; otherwise keep the consumer baseline (on).
|
|
37
|
+
const features = featuresFor(state.serverConfig, effectiveCountry(state), idType);
|
|
38
|
+
return features ? features.livenessCheck : true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Derive the step-order inputs from live state.
|
|
43
|
+
*
|
|
44
|
+
* `hasDocCapture` defaults TRUE before an ID is picked so the order stays a
|
|
45
|
+
* plausible shape while the user is still on consent — nothing navigates by it
|
|
46
|
+
* until an ID is selected, and the same default is what the web SDK's progress
|
|
47
|
+
* bar uses.
|
|
48
|
+
*/
|
|
49
|
+
export function stepOrderOptions(state: KycState): StepOrderOptions {
|
|
50
|
+
const { config } = state;
|
|
51
|
+
const idType = state.selectedIdType;
|
|
52
|
+
return {
|
|
53
|
+
isBusiness: isBusinessFlow(config),
|
|
54
|
+
business: config.business,
|
|
55
|
+
hasDocCapture: idType ? requiresDocumentCapture(idType) : true,
|
|
56
|
+
hasNfc: nfcEnabled(state),
|
|
57
|
+
hasLiveness: livenessEnabled(state),
|
|
58
|
+
// Individual flows offer the workflow's `countries`; the KYB applicant's
|
|
59
|
+
// own capture leg has no such list (business configs carry no individual
|
|
60
|
+
// fields), so it offers the org's GRANTED countries — the applicant may
|
|
61
|
+
// hold an ID issued anywhere the org can verify. Mirrors the web SDK.
|
|
62
|
+
// EXCEPT when the applicant picked THEMSELVES from the key people and
|
|
63
|
+
// that entry carries a country: "where was your ID issued?" was already
|
|
64
|
+
// answered there, so the leg skips the country-select step.
|
|
65
|
+
hasCountrySelect:
|
|
66
|
+
countrySelectOptions(state).length > 1 &&
|
|
67
|
+
applicantSelfCountry(state.businessApplication) === null,
|
|
68
|
+
hasEmailVerification: config.emailVerification?.enabled === true,
|
|
69
|
+
hasPhoneVerification: config.phoneVerification?.enabled === true,
|
|
70
|
+
hasPoa: hasProofOfAddressStep(config.proofOfAddress),
|
|
71
|
+
hasQuestionnaire: hasActiveQuestionnaire(config.questionnaire),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The countries the country-select step offers: the workflow's `countries`
|
|
77
|
+
* when configured, else the org's granted countries from the server config
|
|
78
|
+
* (the KYB applicant leg's case — business configs carry no individual
|
|
79
|
+
* country list). Empty while the server config is still loading.
|
|
80
|
+
*/
|
|
81
|
+
export function countrySelectOptions(state: Pick<KycState, 'config' | 'serverConfig'>): string[] {
|
|
82
|
+
const configured = (state.config.countries ?? []).map((entry) => entry.country.toUpperCase());
|
|
83
|
+
if (configured.length > 0) return configured;
|
|
84
|
+
if (state.serverConfig.status !== 'ready') return [];
|
|
85
|
+
const seen = new Set<string>();
|
|
86
|
+
for (const row of state.serverConfig.idTypes) seen.add(row.country.toUpperCase());
|
|
87
|
+
return [...seen];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The country this session is actually verifying against.
|
|
92
|
+
*
|
|
93
|
+
* A multi-region flow lets the user pick one, and that choice — not the
|
|
94
|
+
* workflow's primary country — decides the ID types, the validators, the
|
|
95
|
+
* endpoints and the chip capability. Every read must go through here; a stray
|
|
96
|
+
* `config.country` in a later step is a flow that offers Ghanaian IDs and then
|
|
97
|
+
* validates them as Nigerian.
|
|
98
|
+
*
|
|
99
|
+
* KYB configs carry no individual `country`, so the applicant's capture leg
|
|
100
|
+
* falls back to the registry country until (or unless) a country is picked.
|
|
101
|
+
*/
|
|
102
|
+
export function effectiveCountry(state: KycState): SupportedCountry {
|
|
103
|
+
return (state.selectedCountry ??
|
|
104
|
+
state.config.country ??
|
|
105
|
+
state.business.country ??
|
|
106
|
+
state.config.business?.country) as SupportedCountry;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The registry details a KYB submission carries.
|
|
111
|
+
*
|
|
112
|
+
* The country is the one PICKED on business-details (multi-registry workflows
|
|
113
|
+
* offer several) rather than the workflow's primary, and the product is
|
|
114
|
+
* narrowed to that country — offering Nigeria's TIN after the visitor picks
|
|
115
|
+
* Ghana produces a submission the server refuses.
|
|
116
|
+
*/
|
|
117
|
+
export function businessSubmission(state: KycState): {
|
|
118
|
+
country: string;
|
|
119
|
+
product: string;
|
|
120
|
+
payload: NonNullable<VerifyRequest['business']>;
|
|
121
|
+
} {
|
|
122
|
+
const config = state.config.business!;
|
|
123
|
+
const b = state.business;
|
|
124
|
+
const app = state.businessApplication;
|
|
125
|
+
const people = keyPeoplePayload(app.keyPeople, app.applicantKeyPersonIndex);
|
|
126
|
+
const country = b.country ?? config.country;
|
|
127
|
+
const product = b.product ?? businessProductsForCountry(config, country)[0]!;
|
|
128
|
+
const text = (value: string): string | undefined => {
|
|
129
|
+
const trimmed = value.trim();
|
|
130
|
+
return trimmed === '' ? undefined : trimmed;
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
country,
|
|
134
|
+
product,
|
|
135
|
+
payload: {
|
|
136
|
+
registrationNumber: b.registrationNumber.trim(),
|
|
137
|
+
product,
|
|
138
|
+
...(text(b.registrationName) ? { registrationName: text(b.registrationName) } : {}),
|
|
139
|
+
...(text(b.contactEmail) ? { contactEmail: text(b.contactEmail) } : {}),
|
|
140
|
+
...(text(b.address) ? { address: text(b.address) } : {}),
|
|
141
|
+
...(text(b.email) ? { email: text(b.email) } : {}),
|
|
142
|
+
...(text(b.phone) ? { phone: text(b.phone) } : {}),
|
|
143
|
+
...(text(b.website) ? { website: text(b.website) } : {}),
|
|
144
|
+
// Extras ride along ONLY when the workflow configures them — the server
|
|
145
|
+
// ignores an unconfigured block, but sending one is a claim we did not
|
|
146
|
+
// collect properly.
|
|
147
|
+
...(hasBusinessDocumentsStep(config) && app.documents.length > 0
|
|
148
|
+
? { documents: app.documents.map((d) => ({ type: d.type, mediaId: d.mediaId })) }
|
|
149
|
+
: {}),
|
|
150
|
+
...(hasKeyPeopleCollection(config) && people.length > 0 ? { keyPeople: people } : {}),
|
|
151
|
+
...(hasApplicantVerification(config) && app.applicantRole
|
|
152
|
+
? {
|
|
153
|
+
applicant: {
|
|
154
|
+
role: app.applicantRole,
|
|
155
|
+
...(app.applicantName.trim() ? { name: app.applicantName.trim() } : {}),
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
: {}),
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Whether the chip-read step runs for the selected ID.
|
|
165
|
+
*
|
|
166
|
+
* Off unless the workflow turns it on — a chip read needs a physical document
|
|
167
|
+
* with a chip AND an NFC radio, so it is never a silent default. `idTypes` is
|
|
168
|
+
* a list of "CC/idType" keys; absent/empty means every chip-capable ID.
|
|
169
|
+
*/
|
|
170
|
+
export function nfcEnabled(state: KycState): boolean {
|
|
171
|
+
return nfcDecision(state).enabled;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Whether the chip step runs — and, when it does not, WHICH condition removed it.
|
|
176
|
+
*
|
|
177
|
+
* Four independent gates can each delete this step, and a missing step looks
|
|
178
|
+
* identical from the outside however it went missing: the flow simply walks past
|
|
179
|
+
* the chip read with nothing logged. That has already cost real debugging time
|
|
180
|
+
* once, when a transient native-module miss was indistinguishable from a phone
|
|
181
|
+
* with no NFC radio.
|
|
182
|
+
*
|
|
183
|
+
* The reason is therefore returned rather than collapsed into a boolean, and
|
|
184
|
+
* logged in dev. It costs nothing in production and turns "the NFC screen did
|
|
185
|
+
* not show up" into a one-line answer.
|
|
186
|
+
*/
|
|
187
|
+
export function nfcDecision(state: KycState): { enabled: boolean; reason?: string } {
|
|
188
|
+
const nfc = state.config.nfc;
|
|
189
|
+
if (nfc?.enabled !== true) return { enabled: false, reason: 'workflow_nfc_disabled' };
|
|
190
|
+
const idType = state.selectedIdType;
|
|
191
|
+
if (!idType) return { enabled: false, reason: 'no_id_type_selected' };
|
|
192
|
+
// The server's per-row answer wins when config has loaded — it covers
|
|
193
|
+
// documents the local catalogue has never heard of.
|
|
194
|
+
const country = effectiveCountry(state);
|
|
195
|
+
const row = state.serverConfig.idTypes.find(
|
|
196
|
+
(t) => t.country === country && t.idType === idType,
|
|
197
|
+
);
|
|
198
|
+
if (!supportsNfcChip(country, idType, row?.supportsNfc)) {
|
|
199
|
+
return { enabled: false, reason: `id_not_chip_capable:${country}/${idType}` };
|
|
200
|
+
}
|
|
201
|
+
const keys = nfc.idTypes;
|
|
202
|
+
if (!keys || keys.length === 0) return { enabled: true };
|
|
203
|
+
return keys.includes(`${country}/${idType}`)
|
|
204
|
+
? { enabled: true }
|
|
205
|
+
: { enabled: false, reason: `id_not_selected_in_workflow:${country}/${idType}` };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The step that follows `step`, given the current selection + flags. */
|
|
209
|
+
export function nextStepAfter(step: KYCStep, state: KycState): KYCStep {
|
|
210
|
+
return nextStepInOrder(step, stepOrderOptions(state));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** The step before `step` (for the back button). */
|
|
214
|
+
export function previousStepBefore(step: KYCStep, state: KycState): KYCStep {
|
|
215
|
+
return previousStepInOrder(step, stepOrderOptions(state));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Percentage complete, for the sheet's progress indicator. */
|
|
219
|
+
export function stepProgress(step: KYCStep, state: KycState): number {
|
|
220
|
+
return getStepProgress(step, stepOrderOptions(state));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
// Store factory
|
|
225
|
+
// ---------------------------------------------------------------------------
|