@myazahq/kyc-sdk-react-native 2.0.2 → 2.2.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 +152 -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 +70 -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 +162 -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 +322 -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 +207 -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/KeyPersonCard.tsx +119 -0
- package/src/screens/KeyPersonForm.tsx +148 -0
- package/src/screens/KeyPersonSheet.tsx +184 -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
package/src/types/config.ts
CHANGED
|
@@ -1,57 +1,34 @@
|
|
|
1
|
-
import type { KYCSubmission, KYCError } from './verification';
|
|
2
|
-
|
|
3
1
|
// ---------------------------------------------------------------------------
|
|
4
|
-
//
|
|
2
|
+
// The SDK's public configuration.
|
|
3
|
+
//
|
|
4
|
+
// The country/ID matrix, the presentation types and the workflow-driven blocks
|
|
5
|
+
// live in their own modules (200-line rule) and are re-exported here, so
|
|
6
|
+
// `types/config` remains the single import for consumers.
|
|
5
7
|
// ---------------------------------------------------------------------------
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
export interface IdTypeDefinition {
|
|
32
|
-
key: IdType;
|
|
33
|
-
label: string;
|
|
34
|
-
/**
|
|
35
|
-
* What the user actually types when it differs from the ID's name — e.g.
|
|
36
|
-
* Tax ID lookups are keyed off the person's NIN, so the input asks for a NIN.
|
|
37
|
-
*/
|
|
38
|
-
inputLabel?: string;
|
|
39
|
-
digits?: number;
|
|
40
|
-
pattern?: RegExp;
|
|
41
|
-
/** Whether this ID type requires photographing/uploading a physical document. */
|
|
42
|
-
requiresDocumentCapture: boolean;
|
|
43
|
-
/**
|
|
44
|
-
* How many sides of the document need to be scanned. Only present when
|
|
45
|
-
* `requiresDocumentCapture` is true.
|
|
46
|
-
* - `front_only` — single scan (passports, data-page only)
|
|
47
|
-
* - `front_and_back` — both sides required
|
|
48
|
-
*/
|
|
49
|
-
scanSides?: 'front_only' | 'front_and_back';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type IdTypesByCountry = {
|
|
53
|
-
[K in SupportedCountry]: readonly IdTypeDefinition[];
|
|
54
|
-
};
|
|
9
|
+
import type { KYCSubmission, KYCError } from './verification';
|
|
10
|
+
import type { IdType, IdTypeForCountry, SupportedCountry } from './id-types';
|
|
11
|
+
import type {
|
|
12
|
+
KYCAppearance,
|
|
13
|
+
KYCConsentContent,
|
|
14
|
+
KYCSuccessContent,
|
|
15
|
+
VoiceGuidanceOption,
|
|
16
|
+
} from './appearance';
|
|
17
|
+
import type { SubjectType, WorkflowBusinessConfig } from './business';
|
|
18
|
+
import type {
|
|
19
|
+
EmailVerificationConfig,
|
|
20
|
+
LivenessMode,
|
|
21
|
+
NfcConfig,
|
|
22
|
+
PhoneVerificationConfig,
|
|
23
|
+
ProofOfAddressConfig,
|
|
24
|
+
QuestionnaireConfig,
|
|
25
|
+
WorkflowCountry,
|
|
26
|
+
} from './workflow';
|
|
27
|
+
|
|
28
|
+
export type * from './workflow';
|
|
29
|
+
export type * from './appearance';
|
|
30
|
+
export type * from './id-types';
|
|
31
|
+
export type * from './business';
|
|
55
32
|
|
|
56
33
|
// ---------------------------------------------------------------------------
|
|
57
34
|
// KYC flow steps
|
|
@@ -59,82 +36,27 @@ export type IdTypesByCountry = {
|
|
|
59
36
|
|
|
60
37
|
export type KYCStep =
|
|
61
38
|
| 'consent'
|
|
39
|
+
// Contact-verification OTP steps — right after consent (a cheap pre-filter
|
|
40
|
+
// before capture/registry spend). Present when the workflow enables them.
|
|
41
|
+
| 'email-verification'
|
|
42
|
+
| 'phone-verification'
|
|
43
|
+
| 'country-select'
|
|
62
44
|
| 'id-type'
|
|
63
45
|
| 'id-input'
|
|
64
46
|
| 'document-capture'
|
|
47
|
+
// eMRTD chip read — a real step here, unlike the web SDK where it exists only
|
|
48
|
+
// as a builder-preview screen (Web NFC can't do ISO-DEP).
|
|
49
|
+
| 'nfc'
|
|
50
|
+
// KYB application steps — present only when the workflow configures them.
|
|
51
|
+
| 'business-details'
|
|
52
|
+
| 'business-key-people'
|
|
53
|
+
| 'business-documents'
|
|
54
|
+
| 'applicant-role'
|
|
65
55
|
| 'liveness'
|
|
56
|
+
| 'proof-of-address'
|
|
57
|
+
| 'questionnaire'
|
|
66
58
|
| 'submitted';
|
|
67
59
|
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
// Appearance / theming
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
|
|
72
|
-
export interface KYCAppearance {
|
|
73
|
-
/** Brand color — drives buttons, selected states, focus rings. */
|
|
74
|
-
primaryColor?: string;
|
|
75
|
-
/** Text/icon color rendered on top of `primaryColor` (e.g. button labels). */
|
|
76
|
-
primaryTextColor?: string;
|
|
77
|
-
/** Accent color for subtle hover/active surfaces. */
|
|
78
|
-
accentColor?: string;
|
|
79
|
-
/** Modal/sheet background color. */
|
|
80
|
-
backgroundColor?: string;
|
|
81
|
-
/** Elevated surface color for cards/panels. */
|
|
82
|
-
surfaceColor?: string;
|
|
83
|
-
/** Border + input outline color. */
|
|
84
|
-
borderColor?: string;
|
|
85
|
-
/** Primary text color. */
|
|
86
|
-
textColor?: string;
|
|
87
|
-
companyName?: string;
|
|
88
|
-
/**
|
|
89
|
-
* Logo to show in the flow.
|
|
90
|
-
* - An image URL renders that logo.
|
|
91
|
-
* - The literal `'default'` renders the org's own logo from the server config
|
|
92
|
-
* response (falls back to the built-in shield if the org has none set).
|
|
93
|
-
* - Omitted renders the built-in shield badge.
|
|
94
|
-
*/
|
|
95
|
-
logo?: string;
|
|
96
|
-
/** Initial light/dark mode. Applied on mount; the theme toggle can flip it. */
|
|
97
|
-
theme?: 'light' | 'dark';
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
// Consent / success screen content
|
|
102
|
-
// ---------------------------------------------------------------------------
|
|
103
|
-
|
|
104
|
-
export interface KYCConsentContent {
|
|
105
|
-
/** Consent heading. Supports `{firstName}` / `{lastName}` tokens. */
|
|
106
|
-
title?: string;
|
|
107
|
-
/** Sub-text under the heading. Same tokens. */
|
|
108
|
-
description?: string;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export interface KYCSuccessContent {
|
|
112
|
-
/** Success heading. Supports `{firstName}` / `{lastName}` tokens. */
|
|
113
|
-
title?: string;
|
|
114
|
-
/** Sub-text under the heading. Same tokens. */
|
|
115
|
-
description?: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// Voice guidance (spoken liveness instructions — TTS output only, no mic)
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Configuration for the spoken liveness instructions. TTS **output** for
|
|
124
|
-
* accessibility — it never records audio, so no microphone permission is
|
|
125
|
-
* involved. An object (not a bare boolean) so a `language` can be added later
|
|
126
|
-
* without a breaking change.
|
|
127
|
-
*/
|
|
128
|
-
export interface VoiceGuidanceConfig {
|
|
129
|
-
/** Whether spoken guidance plays. Default `true`. */
|
|
130
|
-
enabled?: boolean;
|
|
131
|
-
/** BCP-47 voice tag (e.g. `'en-US'`, `'fr-FR'`). Default `'en-US'`. */
|
|
132
|
-
language?: string;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** Accepts a bare boolean for ergonomics or the full {@link VoiceGuidanceConfig}. */
|
|
136
|
-
export type VoiceGuidanceOption = boolean | VoiceGuidanceConfig;
|
|
137
|
-
|
|
138
60
|
// ---------------------------------------------------------------------------
|
|
139
61
|
// Client-side SDK config (MyazaKYC.show() / useMyazaKYC options)
|
|
140
62
|
// ---------------------------------------------------------------------------
|
|
@@ -156,17 +78,57 @@ export interface MyazaKYCConfig<C extends SupportedCountry = SupportedCountry> {
|
|
|
156
78
|
*/
|
|
157
79
|
devUrl?: string;
|
|
158
80
|
|
|
159
|
-
/**
|
|
160
|
-
|
|
81
|
+
/**
|
|
82
|
+
* A published workflow to run (`wf_…`). The flow is resolved BEFORE the SDK
|
|
83
|
+
* mounts and its config wins over the props below — so an org can change the
|
|
84
|
+
* flow's shape in the dashboard builder without a redeploy. Runtime data
|
|
85
|
+
* (`userId`, `userData`, `metadata`, callbacks) always stays yours.
|
|
86
|
+
*
|
|
87
|
+
* A workflow supplies `country`, so it may be omitted alongside this.
|
|
88
|
+
*/
|
|
89
|
+
workflowId?: string;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* KYB only: the mapped applicant workflow's id (business.applicant.workflowId,
|
|
93
|
+
* resolved server-side). Set by the workflow gate after overlaying its
|
|
94
|
+
* capture template; stamped on the applicant's own submission so the server
|
|
95
|
+
* applies that workflow's gates, pricing and decision graph. Internal —
|
|
96
|
+
* never set this yourself.
|
|
97
|
+
*/
|
|
98
|
+
applicantWorkflowId?: string;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Two-letter (ISO-2) country code. Required unless {@link workflowId} is set
|
|
102
|
+
* (the flow carries the country) or {@link subjectType} is `'business'` (the
|
|
103
|
+
* business block carries its own registry country). When both are present the
|
|
104
|
+
* flow's country wins.
|
|
105
|
+
*/
|
|
106
|
+
country?: C;
|
|
161
107
|
|
|
162
108
|
/** Subset of ID types to offer. Only types valid for the country are accepted. */
|
|
163
109
|
idTypes?: IdTypeForCountry<C>[];
|
|
164
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The org's own reference for the person being verified (e.g. your internal
|
|
113
|
+
* user id). It is **not** matched during verification — it becomes
|
|
114
|
+
* `Entity.externalUserId` at the KYC seam, so repeat checks of the same user
|
|
115
|
+
* collapse onto one entity and you can correlate results back to your record.
|
|
116
|
+
* Optional; when omitted the server falls back to the provider record id.
|
|
117
|
+
*/
|
|
118
|
+
userId?: string;
|
|
119
|
+
|
|
165
120
|
/** Pre-populated user data. Fields provided here won't be collected again. */
|
|
166
121
|
userData?: {
|
|
167
122
|
firstName?: string;
|
|
168
123
|
lastName?: string;
|
|
169
124
|
dateOfBirth?: string;
|
|
125
|
+
/**
|
|
126
|
+
* The business's display name, for `{businessName}` tokens in consent
|
|
127
|
+
* copy on KYB flows. Registration details aren't collected until after
|
|
128
|
+
* consent, so this resolves only when the integrator passes it in —
|
|
129
|
+
* mirroring the web SDK.
|
|
130
|
+
*/
|
|
131
|
+
businessName?: string;
|
|
170
132
|
};
|
|
171
133
|
|
|
172
134
|
/** Enable the live-selfie capture step. */
|
|
@@ -185,6 +147,52 @@ export interface MyazaKYCConfig<C extends SupportedCountry = SupportedCountry> {
|
|
|
185
147
|
/** Enable liveness detection during selfie capture. Default `true`. */
|
|
186
148
|
enableLiveness?: boolean;
|
|
187
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Which liveness method runs: randomized gestures (default), the screen-
|
|
152
|
+
* reflection flash, or both. Workflow-driven.
|
|
153
|
+
*/
|
|
154
|
+
livenessMode?: LivenessMode;
|
|
155
|
+
|
|
156
|
+
/** Colours in the flash sequence (2–5, default 4). Flash modes only. */
|
|
157
|
+
flashSequenceLength?: number;
|
|
158
|
+
|
|
159
|
+
// ── Workflow-driven blocks ────────────────────────────────────────────────
|
|
160
|
+
// Authored in the dashboard's workflow builder, not usually in consumer code.
|
|
161
|
+
// A resolved flow always wins over a prop of the same name.
|
|
162
|
+
|
|
163
|
+
/** What this flow verifies. Absent = 'individual' (classic KYC). */
|
|
164
|
+
subjectType?: SubjectType;
|
|
165
|
+
|
|
166
|
+
/** KYB registry configuration. Required when `subjectType` is 'business'. */
|
|
167
|
+
business?: WorkflowBusinessConfig;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Multi-region: every country this flow serves. More than one inserts a
|
|
171
|
+
* country-select step between consent and ID type.
|
|
172
|
+
*/
|
|
173
|
+
countries?: WorkflowCountry[];
|
|
174
|
+
|
|
175
|
+
/** Email OTP possession check, right after consent. */
|
|
176
|
+
emailVerification?: EmailVerificationConfig;
|
|
177
|
+
|
|
178
|
+
/** Phone OTP possession check, right after consent / email. */
|
|
179
|
+
phoneVerification?: PhoneVerificationConfig;
|
|
180
|
+
|
|
181
|
+
/** Proof-of-address document check, after capture. */
|
|
182
|
+
proofOfAddress?: ProofOfAddressConfig;
|
|
183
|
+
|
|
184
|
+
/** Compliance declarations asked just before submission. */
|
|
185
|
+
questionnaire?: QuestionnaireConfig;
|
|
186
|
+
|
|
187
|
+
/** eMRTD chip read for chip-capable documents. */
|
|
188
|
+
nfc?: NfcConfig;
|
|
189
|
+
|
|
190
|
+
/** Collect device + IP fraud signals. Default `true`. */
|
|
191
|
+
deviceIntelligence?: boolean;
|
|
192
|
+
|
|
193
|
+
/** Refuse to run on a desktop/laptop. Default `false`. */
|
|
194
|
+
requireMobileDevice?: boolean;
|
|
195
|
+
|
|
188
196
|
/**
|
|
189
197
|
* Spoken liveness instructions (accessibility). `true`/omitted = on,
|
|
190
198
|
* `false` = off, or a {@link VoiceGuidanceConfig}. TTS output only — no
|
|
@@ -213,7 +221,11 @@ export interface MyazaKYCConfig<C extends SupportedCountry = SupportedCountry> {
|
|
|
213
221
|
/** Override the success (submitted) screen copy. */
|
|
214
222
|
success?: KYCSuccessContent;
|
|
215
223
|
|
|
216
|
-
/**
|
|
224
|
+
/**
|
|
225
|
+
* Arbitrary, free-form metadata forwarded verbatim with every verification
|
|
226
|
+
* request. Nothing here is required or interpreted by the SDK/server — use
|
|
227
|
+
* {@link userId} for the user reference, not a `userId` key in here.
|
|
228
|
+
*/
|
|
217
229
|
metadata?: Record<string, string>;
|
|
218
230
|
|
|
219
231
|
// Callbacks
|
|
@@ -232,6 +244,20 @@ export interface MyazaKYCConfig<C extends SupportedCountry = SupportedCountry> {
|
|
|
232
244
|
onError?: (error: KYCError) => void;
|
|
233
245
|
}
|
|
234
246
|
|
|
247
|
+
/**
|
|
248
|
+
* The config the FLOW runs on, as opposed to the config a consumer writes.
|
|
249
|
+
*
|
|
250
|
+
* `country` is optional on the public surface because a `workflowId` supplies
|
|
251
|
+
* it — but by the time anything mounts, the workflow gate has resolved one, and
|
|
252
|
+
* every screen from ID-type onward needs it. Making that guarantee a type keeps
|
|
253
|
+
* the "is it there yet?" question at the single place that answers it (the
|
|
254
|
+
* gate) instead of in every screen.
|
|
255
|
+
*/
|
|
256
|
+
export type ResolvedKYCConfig<C extends SupportedCountry = SupportedCountry> = Omit<
|
|
257
|
+
MyazaKYCConfig<C>,
|
|
258
|
+
'country'
|
|
259
|
+
> & { country: C };
|
|
260
|
+
|
|
235
261
|
// ---------------------------------------------------------------------------
|
|
236
262
|
// useMyazaKYC() hook return type
|
|
237
263
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// The country and ID-type matrix.
|
|
3
|
+
//
|
|
4
|
+
// Split from config.ts (200-line rule). These exist for compile-time ergonomics
|
|
5
|
+
// — autocomplete and per-country narrowing — not enforcement: the org's actual
|
|
6
|
+
// grants live server-side, and Global Documents means any ISO country can be
|
|
7
|
+
// offered. Adding a country here does not grant it.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export type SupportedCountry = 'NG' | 'GH' | 'KE' | 'ZA' | 'CI';
|
|
11
|
+
|
|
12
|
+
export type NigeriaIdType = 'bvn' | 'bvn-premium' | 'nin' | 'vnin' | 'tax-id' | 'passport' | 'drivers-license' | 'pvc';
|
|
13
|
+
export type GhanaIdType = 'ghana-card' | 'voters' | 'drivers-license' | 'ssnit' | 'passport';
|
|
14
|
+
export type KenyaIdType = 'national-id' | 'passport';
|
|
15
|
+
export type SouthAfricaIdType = 'national-id';
|
|
16
|
+
export type IvoryCoastIdType = 'cni' | 'residence-card';
|
|
17
|
+
|
|
18
|
+
export type IdType =
|
|
19
|
+
| NigeriaIdType
|
|
20
|
+
| GhanaIdType
|
|
21
|
+
| KenyaIdType
|
|
22
|
+
| SouthAfricaIdType
|
|
23
|
+
| IvoryCoastIdType;
|
|
24
|
+
|
|
25
|
+
/** Maps a country code to the ID types available in that country. */
|
|
26
|
+
export type IdTypeForCountry<C extends SupportedCountry> =
|
|
27
|
+
C extends 'NG' ? NigeriaIdType :
|
|
28
|
+
C extends 'GH' ? GhanaIdType :
|
|
29
|
+
C extends 'KE' ? KenyaIdType :
|
|
30
|
+
C extends 'ZA' ? SouthAfricaIdType :
|
|
31
|
+
C extends 'CI' ? IvoryCoastIdType :
|
|
32
|
+
never;
|
|
33
|
+
|
|
34
|
+
export interface IdTypeDefinition {
|
|
35
|
+
key: IdType;
|
|
36
|
+
label: string;
|
|
37
|
+
/**
|
|
38
|
+
* What the user actually types when it differs from the ID's name — e.g.
|
|
39
|
+
* Tax ID lookups are keyed off the person's NIN, so the input asks for a NIN.
|
|
40
|
+
*/
|
|
41
|
+
inputLabel?: string;
|
|
42
|
+
digits?: number;
|
|
43
|
+
pattern?: RegExp;
|
|
44
|
+
/** Whether this ID type requires photographing/uploading a physical document. */
|
|
45
|
+
requiresDocumentCapture: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* How many sides of the document need to be scanned. Only present when
|
|
48
|
+
* `requiresDocumentCapture` is true.
|
|
49
|
+
* - `front_only` — single scan (passports, data-page only)
|
|
50
|
+
* - `front_and_back` — both sides required
|
|
51
|
+
*/
|
|
52
|
+
scanSides?: 'front_only' | 'front_and_back';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type IdTypesByCountry = {
|
|
56
|
+
[K in SupportedCountry]: readonly IdTypeDefinition[];
|
|
57
|
+
};
|
|
@@ -34,6 +34,11 @@ export interface KYCSubmission {
|
|
|
34
34
|
export type KYCErrorCode =
|
|
35
35
|
| 'network_error'
|
|
36
36
|
| 'invalid_api_key'
|
|
37
|
+
// A `workflowId` that is unknown, unpublished, archived, or belongs to
|
|
38
|
+
// another org/environment. Blocking — the flow never mounts.
|
|
39
|
+
| 'invalid_workflow'
|
|
40
|
+
// The props are unusable as given (e.g. no `country` and no `workflowId`).
|
|
41
|
+
| 'invalid_config'
|
|
37
42
|
| 'insufficient_credits'
|
|
38
43
|
| 'upload_failed'
|
|
39
44
|
| 'camera_permission_denied'
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Workflow-driven configuration blocks.
|
|
3
|
+
//
|
|
4
|
+
// These are the parts of the flow an org authors in the dashboard's workflow
|
|
5
|
+
// builder rather than in consumer code: extra steps (contact OTP, proof of
|
|
6
|
+
// address, questionnaire, NFC), the KYB subject, and multi-region. They arrive
|
|
7
|
+
// either as props or from `GET /api/kyc/workflows/:id`, and the flow always
|
|
8
|
+
// wins over props (see `mergeWorkflowConfig`).
|
|
9
|
+
//
|
|
10
|
+
// Shapes mirror the web SDK's `types/config.ts` + `types/business.ts` exactly —
|
|
11
|
+
// they are the server's published contract, so the three SDKs must not drift.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
// ── Proof of Address ────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export type PoaDocumentType = 'utility_bill' | 'bank_statement' | 'tenancy_agreement' | 'other';
|
|
17
|
+
|
|
18
|
+
export interface ProofOfAddressConfig {
|
|
19
|
+
/** Adds the Proof of Address step (after capture, before the questionnaire). */
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
/** Accepted document kinds (absent = all). */
|
|
22
|
+
documentTypes?: PoaDocumentType[];
|
|
23
|
+
/** Custom label for the 'other' kind (absent = "Other document"). */
|
|
24
|
+
otherLabel?: string;
|
|
25
|
+
/** Recency window the server checks the document date against (default 90). */
|
|
26
|
+
maxAgeDays?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── NFC chip read (eMRTD) ───────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
export interface NfcConfig {
|
|
32
|
+
/** Adds the chip-read step. */
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
/** Which chip-capable IDs run it, as "CC/idType" keys (absent = all). */
|
|
35
|
+
idTypes?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Show a manual skip so someone who can't complete the read can proceed.
|
|
38
|
+
* Devices with no NFC radio auto-skip regardless — this is the escape hatch
|
|
39
|
+
* on NFC-capable phones.
|
|
40
|
+
*/
|
|
41
|
+
allowSkip?: boolean;
|
|
42
|
+
/** Match the selfie against the authenticated chip portrait (default on). */
|
|
43
|
+
facialMatch?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Contact verification (email / phone OTP) ────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/** Which code field the SDK renders — the org picks this in the builder. */
|
|
49
|
+
export type OtpInputStyle = 'segmented' | 'text';
|
|
50
|
+
|
|
51
|
+
export interface EmailVerificationConfig {
|
|
52
|
+
/** Adds the email OTP step (right after consent). */
|
|
53
|
+
enabled?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a verified email is required to proceed (default true when
|
|
56
|
+
* enabled). `false` shows a "skip for now" affordance and the server accepts
|
|
57
|
+
* a submission without the proof.
|
|
58
|
+
*/
|
|
59
|
+
required?: boolean;
|
|
60
|
+
/** Digits in the code (4–8; default 6). Drives the OTP-input slots. */
|
|
61
|
+
codeLength?: number;
|
|
62
|
+
/** Wrong entries allowed per code before it's dead (1–5; default 3). */
|
|
63
|
+
maxAttempts?: number;
|
|
64
|
+
inputStyle?: OtpInputStyle;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface PhoneVerificationConfig {
|
|
68
|
+
/** Adds the phone OTP step (after consent / email verification). */
|
|
69
|
+
enabled?: boolean;
|
|
70
|
+
required?: boolean;
|
|
71
|
+
codeLength?: number;
|
|
72
|
+
maxAttempts?: number;
|
|
73
|
+
inputStyle?: OtpInputStyle;
|
|
74
|
+
/** Offered delivery channels (default ['sms']). */
|
|
75
|
+
channels?: Array<'sms' | 'whatsapp'>;
|
|
76
|
+
/** Default dial-code country (falls back to the flow's country). */
|
|
77
|
+
defaultCountry?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── Questionnaire (compliance declarations) ─────────────────────────────────
|
|
81
|
+
|
|
82
|
+
export interface QuestionnaireFieldOption {
|
|
83
|
+
value: string;
|
|
84
|
+
label: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface QuestionnaireField {
|
|
88
|
+
/** Stable snake_case key — also the webhook/decisioning field name. */
|
|
89
|
+
key: string;
|
|
90
|
+
label: string;
|
|
91
|
+
/**
|
|
92
|
+
* 'money' = amount + currency. The answer stores `<key>` (number, 2dp) and a
|
|
93
|
+
* `<key>_currency` companion (ISO code).
|
|
94
|
+
*/
|
|
95
|
+
type: 'text' | 'number' | 'money' | 'select' | 'multiselect' | 'boolean' | 'date';
|
|
96
|
+
required?: boolean;
|
|
97
|
+
placeholder?: string;
|
|
98
|
+
helpText?: string;
|
|
99
|
+
options?: QuestionnaireFieldOption[];
|
|
100
|
+
min?: number;
|
|
101
|
+
max?: number;
|
|
102
|
+
/** money only: allowed ISO currency codes; the first is the default. */
|
|
103
|
+
currencies?: string[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface QuestionnaireConfig {
|
|
107
|
+
/**
|
|
108
|
+
* Off switch for the step. Omitted/true = shown; false = skipped even though
|
|
109
|
+
* the questions remain configured (the builder's Questionnaire toggle).
|
|
110
|
+
*/
|
|
111
|
+
enabled?: boolean;
|
|
112
|
+
title?: string;
|
|
113
|
+
description?: string;
|
|
114
|
+
fields: QuestionnaireField[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export type QuestionnaireAnswerValue = string | number | boolean | string[];
|
|
118
|
+
|
|
119
|
+
// ── Multi-region ────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
export interface WorkflowIdOption {
|
|
122
|
+
govDbCheck?: boolean;
|
|
123
|
+
documentIntelligence?: boolean;
|
|
124
|
+
facialMatch?: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface WorkflowCountry {
|
|
128
|
+
/** ISO-3166 alpha-2. */
|
|
129
|
+
country: string;
|
|
130
|
+
/** Offered IDs for this country (absent/empty = every granted ID). */
|
|
131
|
+
idTypes?: string[];
|
|
132
|
+
/** Per-ID validation toggles — restrict-only; they never widen a grant. */
|
|
133
|
+
idOptions?: Record<string, WorkflowIdOption>;
|
|
134
|
+
govDbCheck?: boolean;
|
|
135
|
+
documentIntelligence?: boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── Liveness ────────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
/** Gestures (default), screen-reflection flash, or both. */
|
|
141
|
+
export type LivenessMode = 'gestures' | 'flash' | 'both';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Amount formatting
|
|
3
|
+
//
|
|
4
|
+
// Groups thousands as the user types (250000 → 250,000) and caps the decimals,
|
|
5
|
+
// matching the Flutter SDK's AmountInputFormatter and the web SDK's money
|
|
6
|
+
// input. Long figures are the norm for the expected-volume question in
|
|
7
|
+
// low-denomination currencies, and an ungrouped "250000" is genuinely hard to
|
|
8
|
+
// read back.
|
|
9
|
+
//
|
|
10
|
+
// The DISPLAY is grouped; the ANSWER stays a number. Storing the grouped string
|
|
11
|
+
// would fail validation — `Number("250,000")` is NaN — and would reach the
|
|
12
|
+
// server as text where the decision graph expects something comparable.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
/** Keeps digits plus at most one '.', truncating past `decimalDigits`. */
|
|
16
|
+
function clean(input: string, decimalDigits: number): string {
|
|
17
|
+
let out = '';
|
|
18
|
+
let seenDot = false;
|
|
19
|
+
let decimals = 0;
|
|
20
|
+
|
|
21
|
+
for (const ch of input) {
|
|
22
|
+
if (ch === '.') {
|
|
23
|
+
// Integer-only: everything past the point is fractional, so DROP the
|
|
24
|
+
// rest. Merely skipping the '.' would splice "1234.99" into "123499".
|
|
25
|
+
if (decimalDigits === 0) break;
|
|
26
|
+
if (seenDot) continue;
|
|
27
|
+
seenDot = true;
|
|
28
|
+
out += ch;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (ch < '0' || ch > '9') continue;
|
|
32
|
+
if (seenDot) {
|
|
33
|
+
if (decimals >= decimalDigits) continue;
|
|
34
|
+
decimals += 1;
|
|
35
|
+
}
|
|
36
|
+
out += ch;
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function group(digits: string): string {
|
|
42
|
+
if (digits.length <= 3) return digits;
|
|
43
|
+
const lead = digits.length % 3;
|
|
44
|
+
const parts: string[] = [];
|
|
45
|
+
let i = 0;
|
|
46
|
+
if (lead > 0) {
|
|
47
|
+
parts.push(digits.slice(0, lead));
|
|
48
|
+
i = lead;
|
|
49
|
+
}
|
|
50
|
+
while (i < digits.length) {
|
|
51
|
+
parts.push(digits.slice(i, i + 3));
|
|
52
|
+
i += 3;
|
|
53
|
+
}
|
|
54
|
+
return parts.join(',');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** "250000.5" → "250,000.5". Non-numeric characters are dropped. */
|
|
58
|
+
export function formatGroupedAmount(input: string, decimalDigits = 2): string {
|
|
59
|
+
const cleaned = clean(input, decimalDigits);
|
|
60
|
+
if (cleaned === '') return '';
|
|
61
|
+
const dot = cleaned.indexOf('.');
|
|
62
|
+
const whole = dot === -1 ? cleaned : cleaned.slice(0, dot);
|
|
63
|
+
const fraction = dot === -1 ? '' : cleaned.slice(dot);
|
|
64
|
+
return group(whole) + fraction;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Numeric value of a grouped amount string ("250,000.50" → 250000.5). */
|
|
68
|
+
export function parseGroupedAmount(text: string): number | null {
|
|
69
|
+
const raw = text.replace(/,/g, '').trim();
|
|
70
|
+
if (raw === '' || raw === '.') return null;
|
|
71
|
+
const n = Number(raw);
|
|
72
|
+
return Number.isFinite(n) ? n : null;
|
|
73
|
+
}
|
package/src/utils/tokens.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { MyazaKYCConfig } from '../types/config';
|
|
2
2
|
|
|
3
|
-
// Replaces {firstName} / {lastName} tokens with values from
|
|
4
|
-
// mirroring the Flutter SDK's `_fillTokens` and the web
|
|
3
|
+
// Replaces {firstName} / {lastName} / {businessName} tokens with values from
|
|
4
|
+
// userData (or ''), mirroring the Flutter SDK's `_fillTokens` and the web
|
|
5
|
+
// SDK's token handling ({businessName} is the KYB consent-copy token).
|
|
5
6
|
export function fillTokens(template: string, userData: MyazaKYCConfig['userData']): string {
|
|
6
7
|
return template
|
|
7
8
|
.replace(/\{firstName\}/g, userData?.firstName ?? '')
|
|
8
9
|
.replace(/\{lastName\}/g, userData?.lastName ?? '')
|
|
10
|
+
.replace(/\{businessName\}/g, userData?.businessName ?? '')
|
|
9
11
|
.trim();
|
|
10
12
|
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { cardCropRect } from '../services/cardCrop';
|
|
2
|
-
import { CARD_GUIDE_ASPECT, PASSPORT_GUIDE_ASPECT } from '../config/idTypes';
|
|
3
|
-
|
|
4
|
-
describe('cardCropRect', () => {
|
|
5
|
-
it('produces a crop whose aspect equals the guide aspect', () => {
|
|
6
|
-
const r = cardCropRect(4000, 3000, CARD_GUIDE_ASPECT);
|
|
7
|
-
expect(r.width / r.height).toBeCloseTo(CARD_GUIDE_ASPECT, 3);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('stays within the image bounds', () => {
|
|
11
|
-
for (const [w, h] of [
|
|
12
|
-
[4000, 3000], // landscape sensor
|
|
13
|
-
[3000, 4000], // portrait sensor
|
|
14
|
-
[1920, 1080], // 16:9
|
|
15
|
-
] as const) {
|
|
16
|
-
const r = cardCropRect(w, h, CARD_GUIDE_ASPECT);
|
|
17
|
-
expect(r.originX).toBeGreaterThanOrEqual(0);
|
|
18
|
-
expect(r.originY).toBeGreaterThanOrEqual(0);
|
|
19
|
-
expect(r.originX + r.width).toBeLessThanOrEqual(w + 0.01);
|
|
20
|
-
expect(r.originY + r.height).toBeLessThanOrEqual(h + 0.01);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('centres the crop within the cover-fit 3:4 slice (landscape sensor)', () => {
|
|
25
|
-
// 4000×3000 → visible slice is 2250×3000 centred at x=875.
|
|
26
|
-
const r = cardCropRect(4000, 3000, CARD_GUIDE_ASPECT);
|
|
27
|
-
const sliceCentreX = 875 + 2250 / 2;
|
|
28
|
-
expect(r.originX + r.width / 2).toBeCloseTo(sliceCentreX, 3);
|
|
29
|
-
expect(r.originY + r.height / 2).toBeCloseTo(3000 / 2, 3); // vertically centred (no shift)
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('gives passports a taller crop (smaller aspect) than ID cards', () => {
|
|
33
|
-
const card = cardCropRect(4000, 3000, CARD_GUIDE_ASPECT);
|
|
34
|
-
const passport = cardCropRect(4000, 3000, PASSPORT_GUIDE_ASPECT);
|
|
35
|
-
expect(passport.height).toBeGreaterThan(card.height);
|
|
36
|
-
expect(passport.width).toBeCloseTo(card.width, 3); // same 88% width
|
|
37
|
-
expect(passport.width / passport.height).toBeCloseTo(PASSPORT_GUIDE_ASPECT, 3);
|
|
38
|
-
});
|
|
39
|
-
});
|