@myazahq/kyc-sdk-react-native 2.4.0 → 2.6.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 +151 -5
- package/android/build.gradle +35 -3
- package/android/src/main/java/com/margelo/nitro/myazakyc/HybridMyazaFaceDetector.kt +77 -0
- package/app.plugin.js +89 -8
- package/ios/HybridMyazaFaceDetector.swift +16 -3
- package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.cpp +23 -0
- package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.hpp +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaFaceDetectorSpec.kt +9 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.hpp +43 -0
- package/nitrogen/generated/ios/c++/HybridMyazaFaceDetectorSpecSwift.hpp +17 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec_cxx.swift +31 -0
- package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.hpp +3 -0
- package/package.json +14 -2
- package/src/MyazaBiometricAuth.tsx +119 -0
- package/src/MyazaKYC.tsx +12 -0
- package/src/components/CountryField.tsx +8 -0
- package/src/components/CountryRegionPicker.tsx +184 -0
- package/src/components/DashedBorder.tsx +55 -0
- package/src/components/DialCodePicker.tsx +53 -67
- package/src/components/DialCodeRow.tsx +102 -0
- package/src/components/FramedMapPicker.tsx +122 -0
- package/src/components/GeoBadge.tsx +34 -0
- package/src/components/Icon.tsx +2 -176
- package/src/components/KycFlow.tsx +60 -32
- package/src/components/LineSkeleton.tsx +100 -0
- package/src/components/MapChrome.tsx +67 -0
- package/src/components/MapPinMarker.tsx +54 -0
- package/src/components/MapPinPicker.tsx +179 -0
- package/src/components/MilestoneTrack.tsx +155 -0
- package/src/components/MultiIdProgress.tsx +107 -0
- package/src/components/MyazaInput.tsx +39 -4
- package/src/components/MyazaSelect.tsx +2 -1
- package/src/components/OptionRow.tsx +22 -3
- package/src/components/OwnershipSlider.tsx +102 -0
- package/src/components/PhoneNumberInput.tsx +45 -4
- package/src/components/PoweredBy.tsx +5 -5
- package/src/components/PresenceBlocks.tsx +153 -0
- package/src/components/StaggerIn.tsx +63 -0
- package/src/components/StepView.tsx +43 -0
- package/src/components/StickyActions.tsx +56 -0
- package/src/components/dialCodeRows.ts +64 -0
- package/src/components/glass/FloatingSheet.tsx +73 -21
- package/src/components/glass/GlassSheet.tsx +14 -2
- package/src/components/icon-map.ts +176 -0
- package/src/components/stepHeaderMeta.tsx +45 -5
- package/src/config/addressCollection.ts +125 -0
- package/src/config/biometricOptions.ts +100 -0
- package/src/config/business.ts +26 -7
- package/src/config/businessDetailsValidity.ts +52 -0
- package/src/config/businessPrefill.ts +72 -0
- package/src/config/businessSteps.ts +20 -3
- package/src/config/consentStep.ts +19 -0
- package/src/config/keyPeople.ts +111 -7
- package/src/config/keyPeopleCorporate.ts +19 -0
- package/src/config/keyPeopleOwnerTypes.ts +12 -0
- package/src/config/keyPeopleOwners.ts +26 -0
- package/src/config/keyPeoplePrefill.ts +138 -0
- package/src/config/keyPeopleSectionDefs.ts +65 -0
- package/src/config/keyPeopleSections.ts +167 -0
- package/src/config/proofOfAddress.ts +54 -11
- package/src/config/regions.ts +24 -0
- package/src/config/registrationHint.ts +1 -1
- package/src/config/stepOrder.ts +100 -6
- package/src/config/theme.ts +8 -2
- package/src/config/uploadLimits.ts +35 -0
- package/src/config/website.ts +36 -0
- package/src/config/workflowMerge.ts +16 -5
- package/src/emrtd/activeAuth.ts +100 -0
- package/src/emrtd/extras.ts +76 -0
- package/src/emrtd/files.ts +5 -0
- package/src/emrtd/index.ts +1 -0
- package/src/emrtd/read.ts +8 -1
- package/src/emrtd/session.ts +41 -0
- package/src/emrtd/stages.ts +11 -0
- package/src/index.ts +54 -1
- package/src/lib/address-current-location.ts +171 -0
- package/src/lib/address-field-modes.ts +119 -0
- package/src/lib/address-flow.ts +172 -0
- package/src/lib/address-helpers.ts +48 -0
- package/src/lib/address-line.ts +76 -0
- package/src/lib/address-pin-move.ts +97 -0
- package/src/lib/address-step-recovery.ts +63 -0
- package/src/lib/authed-image.ts +78 -0
- package/src/lib/biometric-auth.ts +52 -0
- package/src/lib/biometric-copy.ts +51 -0
- package/src/lib/captureRing.ts +83 -0
- package/src/lib/contact-recovery.ts +44 -0
- package/src/lib/country-adoption.ts +89 -0
- package/src/lib/inferred-country.ts +65 -0
- package/src/lib/livenessLayout.ts +49 -0
- package/src/lib/map-frame.ts +159 -0
- package/src/lib/map-tiles.ts +143 -0
- package/src/lib/multi-id.ts +210 -0
- package/src/lib/poa-country-gate.ts +27 -0
- package/src/lib/resubmit.ts +130 -0
- package/src/lib/result-copy.ts +116 -0
- package/src/lib/result-wait.ts +53 -0
- package/src/lib/review-map-surface.ts +26 -0
- package/src/lib/scope.ts +31 -0
- package/src/lib/screen-corners.ts +50 -0
- package/src/lib/selfie-upload-wait.ts +79 -0
- package/src/lib/step-log.ts +24 -3
- package/src/lib/street-view-fov.ts +42 -0
- package/src/lib/use-multi-id-plan.ts +29 -0
- package/src/lib/webview-available.ts +47 -0
- package/src/liveness/useLiveness.ts +6 -1
- package/src/liveness/useModelReady.ts +65 -0
- package/src/liveness/visionCameraFaceDetector.ts +35 -0
- package/src/presence/background-math.ts +100 -0
- package/src/presence/background-store.ts +82 -0
- package/src/presence/background.ts +165 -0
- package/src/presence/foreground-service.ts +193 -0
- package/src/presence/fs.ts +51 -0
- package/src/presence/geofence.ts +41 -0
- package/src/presence/math.ts +44 -0
- package/src/presence/post.ts +40 -0
- package/src/presence/report.ts +87 -0
- package/src/presence/sampler.ts +110 -0
- package/src/presence/status.ts +92 -0
- package/src/presence/store.ts +94 -0
- package/src/presence/tier.ts +36 -0
- package/src/presence/watch-wait.ts +81 -0
- package/src/screens/AddressCountryControl.tsx +115 -0
- package/src/screens/ApplicantRoleStep.tsx +9 -5
- package/src/screens/BusinessCheckPanel.tsx +89 -0
- package/src/screens/BusinessDetailsFields.tsx +112 -0
- package/src/screens/BusinessDetailsStep.tsx +133 -145
- package/src/screens/BusinessDocumentSlot.tsx +5 -5
- package/src/screens/BusinessDocumentsStep.tsx +5 -56
- package/src/screens/BusinessKeyPeopleStep.tsx +103 -71
- package/src/screens/BusinessPickedCard.tsx +86 -0
- package/src/screens/BusinessPickedSection.tsx +51 -0
- package/src/screens/BusinessRegistryPickers.tsx +67 -0
- package/src/screens/BusinessSandboxToggle.tsx +114 -0
- package/src/screens/BusinessSearch.tsx +199 -0
- package/src/screens/BusinessSearchResults.tsx +115 -0
- package/src/screens/CompanyInfoFields.tsx +91 -51
- package/src/screens/ConsentStep.tsx +37 -17
- package/src/screens/ContactActions.tsx +54 -0
- package/src/screens/ContactDestinationField.tsx +5 -0
- package/src/screens/ContactEntryPanel.tsx +75 -0
- package/src/screens/ContactVerificationStep.tsx +43 -42
- package/src/screens/CountrySelectStep.tsx +13 -147
- package/src/screens/IdTypeStep.tsx +12 -5
- package/src/screens/KeepLinksSheet.tsx +88 -0
- package/src/screens/KeyPeopleAwaitCard.tsx +204 -0
- package/src/screens/KeyPeopleAwaitList.tsx +126 -0
- package/src/screens/KeyPeoplePending.tsx +154 -0
- package/src/screens/KeyPeopleSection.tsx +162 -0
- package/src/screens/KeyPeopleSectionsList.tsx +103 -0
- package/src/screens/KeyPeopleUboExemption.tsx +72 -0
- package/src/screens/KeyPersonCard.tsx +36 -4
- package/src/screens/KeyPersonForm.tsx +195 -36
- package/src/screens/KeyPersonKindToggle.tsx +68 -0
- package/src/screens/KeyPersonOwners.tsx +109 -0
- package/src/screens/KeyPersonRoleChips.tsx +68 -0
- package/src/screens/KeyPersonSheet.tsx +92 -108
- package/src/screens/LivenessAvatar.tsx +15 -3
- package/src/screens/LivenessStep.tsx +172 -52
- package/src/screens/NfcStep.tsx +14 -0
- package/src/screens/PoaDocumentTypeList.tsx +66 -0
- package/src/screens/ProofOfAddressParts.tsx +151 -0
- package/src/screens/ProofOfAddressStep.tsx +58 -96
- package/src/screens/SubmittedBadge.tsx +25 -0
- package/src/screens/SubmittedError.tsx +64 -0
- package/src/screens/SubmittedResult.tsx +117 -0
- package/src/screens/SubmittedStep.tsx +82 -119
- package/src/screens/SubmittedSuccess.tsx +127 -0
- package/src/screens/SubmittedWaiting.tsx +45 -0
- package/src/screens/address/AddressEntranceStep.tsx +161 -0
- package/src/screens/address/AddressIntroGate.tsx +148 -0
- package/src/screens/address/AddressMapStub.tsx +50 -0
- package/src/screens/address/AddressPinStep.tsx +178 -0
- package/src/screens/address/AddressReviewStep.tsx +162 -0
- package/src/screens/address/AddressSandboxTabs.tsx +158 -0
- package/src/screens/address/AddressSearchField.tsx +115 -0
- package/src/screens/address/AddressSearchStep.tsx +94 -0
- package/src/screens/address/CurrentLocationRow.tsx +135 -0
- package/src/screens/address/DetailsSheet.tsx +175 -0
- package/src/screens/address/DetailsSheetFields.tsx +184 -0
- package/src/screens/address/EntranceDropzone.tsx +178 -0
- package/src/screens/address/EntranceFraming.tsx +66 -0
- package/src/screens/address/EntrancePills.tsx +60 -0
- package/src/screens/address/FramedStreetView.tsx +155 -0
- package/src/screens/address/IntroDisclosures.tsx +210 -0
- package/src/screens/address/LabelDecisionRow.tsx +113 -0
- package/src/screens/address/PinSummaryRow.tsx +105 -0
- package/src/screens/address/ReviewAddressBand.tsx +111 -0
- package/src/screens/address/ReviewEntranceThumbs.tsx +64 -0
- package/src/screens/address/ReviewMapPicture.tsx +106 -0
- package/src/screens/address/SearchResults.tsx +105 -0
- package/src/screens/address/SearchScreen.tsx +176 -0
- package/src/screens/address/SkipForNow.tsx +43 -0
- package/src/screens/address/StreetViewChrome.tsx +81 -0
- package/src/screens/address/detail-values.ts +22 -0
- package/src/screens/address/fix-source.ts +27 -0
- package/src/screens/address/index.ts +8 -0
- package/src/screens/address/meta.ts +44 -0
- package/src/screens/address/use-address-flow.ts +200 -0
- package/src/screens/address/use-label-pin.ts +80 -0
- package/src/screens/address/use-pin-actions.ts +192 -0
- package/src/screens/biometric/BiometricAuthFlow.tsx +170 -0
- package/src/screens/consent/model.ts +79 -10
- package/src/screens/liveness/CaptureRing.tsx +91 -0
- package/src/screens/liveness/LivenessCamera.tsx +63 -0
- package/src/screens/liveness/LivenessHandover.tsx +43 -0
- package/src/screens/liveness/LivenessOutcome.tsx +4 -3
- package/src/screens/liveness/SelfiePreview.tsx +13 -4
- package/src/screens/liveness/ShutterFlash.tsx +25 -0
- package/src/screens/liveness/index.ts +4 -0
- package/src/screens/liveness/useSelfieUpload.ts +23 -3
- package/src/screens/nfc/NfcReadProgress.tsx +1 -0
- package/src/screens/nfc/NfcScanIllustration.tsx +7 -18
- package/src/screens/useAddressPhotoAttach.ts +77 -0
- package/src/screens/useAwaitingPeople.ts +90 -0
- package/src/screens/useBusinessDocumentAttach.ts +93 -0
- package/src/screens/usePoaAttach.ts +12 -15
- package/src/services/api-biometric.ts +37 -0
- package/src/services/api-types-biometric.ts +41 -0
- package/src/services/api-types.ts +251 -3
- package/src/services/api-verify-types.ts +90 -1
- package/src/services/api.ts +237 -3
- package/src/services/deviceMetadata.ts +1 -1
- package/src/services/errors.ts +10 -2
- package/src/services/location.ts +234 -0
- package/src/services/uploadErrors.ts +30 -0
- package/src/services/workflowGate.ts +8 -0
- package/src/specs/MyazaFaceDetector.nitro.ts +29 -0
- package/src/store/address.ts +97 -0
- package/src/store/businessCheck.ts +113 -0
- package/src/store/derive.ts +44 -4
- package/src/store/kycStore.ts +352 -16
- package/src/store/serverConfig.ts +20 -0
- package/src/store/session.ts +227 -0
- package/src/store/state.ts +289 -2
- package/src/store/submit.ts +80 -13
- package/src/store/submitApplicant.ts +5 -11
- package/src/types/business.ts +23 -2
- package/src/types/config.ts +70 -2
- package/src/types/verification.ts +13 -1
- package/src/types/workflow.ts +112 -1
- package/src/screens/KeyPeopleInviteLinks.tsx +0 -239
package/src/store/submit.ts
CHANGED
|
@@ -12,15 +12,44 @@
|
|
|
12
12
|
// unconfigured contact proof must not claim a check that never ran.
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
|
|
15
|
+
import { configScope, SCOPE_ID_TYPES } from '../lib/scope';
|
|
15
16
|
import { collectDeviceMetadata } from '../services/deviceMetadata';
|
|
16
17
|
import { generateRequestId } from '../utils/uuid';
|
|
17
18
|
import { hasActiveQuestionnaire, questionnairePayload } from '../config/questionnaire';
|
|
19
|
+
import { multiIdWireSlots } from '../lib/multi-id';
|
|
18
20
|
import { isBusinessFlow } from '../config/business';
|
|
21
|
+
import { addressPayload } from '../config/addressCollection';
|
|
19
22
|
import { businessSubmission, effectiveCountry } from './derive';
|
|
20
23
|
import type { ClientFingerprint } from '../services/fingerprint';
|
|
21
24
|
import type { VerifyRequest } from '../services/api';
|
|
22
25
|
import type { KycState } from './state';
|
|
23
26
|
|
|
27
|
+
/** The chip payload as the wire wants it. One builder, so a slot's chip and a
|
|
28
|
+
* single-ID run's are byte-identical to the server. */
|
|
29
|
+
export function nfcPayload(
|
|
30
|
+
chip: NonNullable<KycState['chipData']>,
|
|
31
|
+
): NonNullable<VerifyRequest['nfc']> {
|
|
32
|
+
return {
|
|
33
|
+
dg1: chip.dg1,
|
|
34
|
+
...(chip.sod ? { sod: chip.sod } : {}),
|
|
35
|
+
...(chip.dg2 ? { dg2: chip.dg2 } : {}),
|
|
36
|
+
...(chip.dg7 ? { dg7: chip.dg7 } : {}),
|
|
37
|
+
...(chip.dg11 ? { dg11: chip.dg11 } : {}),
|
|
38
|
+
...(chip.dg12 ? { dg12: chip.dg12 } : {}),
|
|
39
|
+
...(chip.dg15 ? { dg15: chip.dg15 } : {}),
|
|
40
|
+
...(chip.aaSignature ? { aaSignature: chip.aaSignature } : {}),
|
|
41
|
+
...(chip.aaChallengeId ? { aaChallengeId: chip.aaChallengeId } : {}),
|
|
42
|
+
chipAuth: chip.chipAuth,
|
|
43
|
+
// The PACE diagnostic. Read on every session and, until now, dropped here:
|
|
44
|
+
// every RN chip read reached the server with paceOutcome null, so the one
|
|
45
|
+
// question the field exists to answer — did this chip decline PACE, or did
|
|
46
|
+
// ours fail? — was unanswerable for the whole install base. Flutter has
|
|
47
|
+
// always sent it.
|
|
48
|
+
...(chip.paceOutcome ? { paceOutcome: chip.paceOutcome } : {}),
|
|
49
|
+
...(chip.paceDetail ? { paceDetail: chip.paceDetail } : {}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
24
53
|
export function buildVerifyRequest(
|
|
25
54
|
state: KycState,
|
|
26
55
|
fingerprint: ClientFingerprint | undefined,
|
|
@@ -30,17 +59,50 @@ export function buildVerifyRequest(
|
|
|
30
59
|
// capture media at all.
|
|
31
60
|
const business = isBusinessFlow(state.config) ? businessSubmission(state) : null;
|
|
32
61
|
|
|
62
|
+
// Multi-ID: every check was committed as a slot, and the whole run submits as
|
|
63
|
+
// ONE verification the server judges by the workflow's pass policy. The FIRST
|
|
64
|
+
// slot fills the single-ID fields, so anything reading a verification's own
|
|
65
|
+
// idType/idNumber keeps one meaning.
|
|
66
|
+
const multiSlots = !business && state.multiIdSlots.length >= 2 ? state.multiIdSlots : null;
|
|
67
|
+
const primary = multiSlots?.[0];
|
|
68
|
+
|
|
33
69
|
return {
|
|
34
70
|
country: business ? business.country : effectiveCountry(state),
|
|
35
|
-
|
|
36
|
-
|
|
71
|
+
// Scoped flows carry the scope's transport marker instead of a picked ID —
|
|
72
|
+
// the server requires a published workflow of the matching scope for it.
|
|
73
|
+
idType: business
|
|
74
|
+
? business.product
|
|
75
|
+
: configScope(state.config)
|
|
76
|
+
? SCOPE_ID_TYPES[configScope(state.config)!]
|
|
77
|
+
: (primary?.idType ?? state.selectedIdType ?? ''),
|
|
78
|
+
idNumber: business ? undefined : (primary?.idNumber ?? state.idNumber ?? undefined),
|
|
79
|
+
...(multiSlots
|
|
80
|
+
? {
|
|
81
|
+
idChecks: multiIdWireSlots(multiSlots).map((wire, i) => {
|
|
82
|
+
const chip = multiSlots[i]?.chipData;
|
|
83
|
+
return chip ? { ...wire, nfc: nfcPayload(chip) } : wire;
|
|
84
|
+
}),
|
|
85
|
+
}
|
|
86
|
+
: {}),
|
|
37
87
|
...(business ? { business: business.payload } : {}),
|
|
38
88
|
// The org's user reference (becomes Entity.externalUserId at the KYC seam).
|
|
39
89
|
...(state.config.userId ? { userId: state.config.userId } : {}),
|
|
40
90
|
userData: business ? undefined : state.config.userData,
|
|
41
|
-
|
|
91
|
+
// Multi-ID: the slot documents ride idChecks; only the RUN-level media (the
|
|
92
|
+
// one selfie and its video) sit at the top level. Sending a slot's document
|
|
93
|
+
// here too would file the last ID's capture as though it were the
|
|
94
|
+
// verification's own.
|
|
95
|
+
mediaIds: business
|
|
96
|
+
? {}
|
|
97
|
+
: multiSlots
|
|
98
|
+
? { ...state.mediaIds, documentFront: undefined, documentBack: undefined }
|
|
99
|
+
: state.mediaIds,
|
|
42
100
|
...(state.config.workflowId ? { workflowId: state.config.workflowId } : {}),
|
|
43
101
|
...(state.poaDocumentType ? { proofOfAddressType: state.poaDocumentType } : {}),
|
|
102
|
+
// The smart address, when the step gathered one. Sent on individual AND
|
|
103
|
+
// business submissions (a KYB flow's pin is the business premises) — the
|
|
104
|
+
// server validates it against the workflow either way.
|
|
105
|
+
...(state.address ? { address: addressPayload(state.address, state.config.addressCollection) } : {}),
|
|
44
106
|
...(state.contact.emailToken || state.contact.phoneToken
|
|
45
107
|
? {
|
|
46
108
|
contact: {
|
|
@@ -60,16 +122,9 @@ export function buildVerifyRequest(
|
|
|
60
122
|
// Omitted entirely when absent rather than sent empty: the server treats a
|
|
61
123
|
// present-but-hollow block as a failed read, which is not the same as a
|
|
62
124
|
// document whose chip was never scanned.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
dg1: state.chipData.dg1,
|
|
67
|
-
...(state.chipData.sod ? { sod: state.chipData.sod } : {}),
|
|
68
|
-
...(state.chipData.dg2 ? { dg2: state.chipData.dg2 } : {}),
|
|
69
|
-
chipAuth: state.chipData.chipAuth,
|
|
70
|
-
},
|
|
71
|
-
}
|
|
72
|
-
: {}),
|
|
125
|
+
// Multi-ID: the chip rides its OWN check (above) — a top-level payload could
|
|
126
|
+
// only ever be attributed to the primary one.
|
|
127
|
+
...(!multiSlots && state.chipData ? { nfc: nfcPayload(state.chipData) } : {}),
|
|
73
128
|
// Only sent when the flow actually asked — an inactive questionnaire
|
|
74
129
|
// must not put an empty object on the verification record.
|
|
75
130
|
...(hasActiveQuestionnaire(state.config.questionnaire)
|
|
@@ -80,12 +135,24 @@ export function buildVerifyRequest(
|
|
|
80
135
|
),
|
|
81
136
|
}
|
|
82
137
|
: {}),
|
|
138
|
+
// The attempt session this run happened under: the verification adopts its
|
|
139
|
+
// id, and any registry check paid at selection is not paid again at submit.
|
|
140
|
+
...(state.sessionId ? { sessionId: state.sessionId } : {}),
|
|
83
141
|
// `metadata` is free-form passthrough. The SDK-owned keys (`requestId` —
|
|
84
142
|
// the server's idempotency key — and `device`) are written AFTER the
|
|
85
143
|
// caller's metadata so consumer keys can never clobber them. The user
|
|
86
144
|
// reference is the top-level `userId` above. Identical on web + Flutter.
|
|
87
145
|
metadata: {
|
|
88
146
|
...(state.config.metadata ?? {}),
|
|
147
|
+
// Ignored by production, so it is safe to send whenever it is set.
|
|
148
|
+
...(business && state.business.sandboxOutcome
|
|
149
|
+
? { sandboxOutcome: state.business.sandboxOutcome }
|
|
150
|
+
: {}),
|
|
151
|
+
// The address flow's Test-result pick (dev/sandbox tabs on the review
|
|
152
|
+
// step) — same contract, same key, no clash: business flows never set it.
|
|
153
|
+
...(!business && state.addressSandboxOutcome
|
|
154
|
+
? { sandboxOutcome: state.addressSandboxOutcome }
|
|
155
|
+
: {}),
|
|
89
156
|
requestId: generateRequestId(),
|
|
90
157
|
device: {
|
|
91
158
|
...(collectDeviceMetadata() as unknown as Record<string, unknown>),
|
|
@@ -14,6 +14,7 @@ import { collectDeviceMetadata } from '../services/deviceMetadata';
|
|
|
14
14
|
import { generateRequestId } from '../utils/uuid';
|
|
15
15
|
import { splitFullName } from '../config/keyPeople';
|
|
16
16
|
import { effectiveCountry } from './derive';
|
|
17
|
+
import { nfcPayload } from './submit';
|
|
17
18
|
import type { ClientFingerprint } from '../services/fingerprint';
|
|
18
19
|
import type { VerifyRequest } from '../services/api';
|
|
19
20
|
import type { KycState } from './state';
|
|
@@ -53,17 +54,10 @@ export function buildApplicantVerifyRequest(
|
|
|
53
54
|
...(userData ? { userData } : {}),
|
|
54
55
|
mediaIds: state.mediaIds,
|
|
55
56
|
// The chip read, when the leg ran the NFC step (an overlaid applicant
|
|
56
|
-
// workflow can enable it).
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
dg1: state.chipData.dg1,
|
|
61
|
-
...(state.chipData.sod ? { sod: state.chipData.sod } : {}),
|
|
62
|
-
...(state.chipData.dg2 ? { dg2: state.chipData.dg2 } : {}),
|
|
63
|
-
chipAuth: state.chipData.chipAuth,
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
: {}),
|
|
57
|
+
// workflow can enable it). Built by the SHARED builder, not a second copy
|
|
58
|
+
// of the block: a hand-rolled twin drifts silently, and the applicant's
|
|
59
|
+
// chip must reach the server in the same shape as everyone else's.
|
|
60
|
+
...(state.chipData ? { nfc: nfcPayload(state.chipData) } : {}),
|
|
67
61
|
// NO top-level `userId` here on purpose: the server prefers it over
|
|
68
62
|
// metadata.userId, so sending the org's own user ref would sever the
|
|
69
63
|
// KeyPerson link this submission exists to make.
|
package/src/types/business.ts
CHANGED
|
@@ -17,8 +17,21 @@ export type KeyPersonRole = 'director' | 'beneficial_owner' | 'signatory' | 'sha
|
|
|
17
17
|
* plus 'authorized_representative' (someone filing on the company's behalf). */
|
|
18
18
|
export type ApplicantRole = KeyPersonRole | 'authorized_representative';
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/** Company-profile fields a KYB workflow can ask for. The last five are
|
|
21
|
+
* registry facts the applicant STATES (asked as their own answer rather than
|
|
22
|
+
* filled from the register, because where the two differ that is the
|
|
23
|
+
* finding). Mirrors the web SDK's CompanyInfoField exactly. */
|
|
24
|
+
export const COMPANY_INFO_FIELDS = [
|
|
25
|
+
'address',
|
|
26
|
+
'email',
|
|
27
|
+
'phone',
|
|
28
|
+
'website',
|
|
29
|
+
'dateOfIncorporation',
|
|
30
|
+
'taxId',
|
|
31
|
+
'vatNumber',
|
|
32
|
+
'companyType',
|
|
33
|
+
'natureOfBusiness',
|
|
34
|
+
] as const;
|
|
22
35
|
export type CompanyInfoField = (typeof COMPANY_INFO_FIELDS)[number];
|
|
23
36
|
export type CompanyInfoMode = 'off' | 'optional' | 'required';
|
|
24
37
|
|
|
@@ -55,12 +68,20 @@ export interface WorkflowKeyPeopleConfig {
|
|
|
55
68
|
requirement?: 'all_in_scope' | 'ubos_only' | 'advisory';
|
|
56
69
|
/** Per-role verification depth overrides (win over `level`). */
|
|
57
70
|
perRole?: Partial<Record<KeyPersonRole, KeyPeopleLevel>>;
|
|
71
|
+
/** Emails are mandatory for the roles that are sent a verification link. */
|
|
72
|
+
requireEmail?: boolean;
|
|
73
|
+
/** Explicit override of WHICH roles must carry an email. */
|
|
74
|
+
requireEmailRoles?: KeyPersonRole[];
|
|
58
75
|
/** Invite distribution for full-KYC people. */
|
|
59
76
|
invite?: {
|
|
60
77
|
channel?: string;
|
|
61
78
|
/** The KYC workflow the per-person invite links run through. */
|
|
62
79
|
workflowId?: string;
|
|
63
80
|
};
|
|
81
|
+
/** Nested KYB: a corporate shareholder is invited into its OWN business
|
|
82
|
+
* application (the server mints the link; the SDK only tells the applicant
|
|
83
|
+
* the truth about what happens to a company they list). */
|
|
84
|
+
corporateKyb?: { enabled?: boolean; workflowId?: string };
|
|
64
85
|
}
|
|
65
86
|
|
|
66
87
|
/**
|
package/src/types/config.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ResubmitConfig } from '../lib/resubmit';
|
|
1
2
|
// ---------------------------------------------------------------------------
|
|
2
3
|
// The SDK's public configuration.
|
|
3
4
|
//
|
|
@@ -6,7 +7,7 @@
|
|
|
6
7
|
// `types/config` remains the single import for consumers.
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
|
|
9
|
-
import type { KYCSubmission, KYCError } from './verification';
|
|
10
|
+
import type { KYCSubmission, KYCError, KYCResult } from './verification';
|
|
10
11
|
import type { IdType, IdTypeForCountry, SupportedCountry } from './id-types';
|
|
11
12
|
import type {
|
|
12
13
|
KYCAppearance,
|
|
@@ -23,6 +24,8 @@ import type {
|
|
|
23
24
|
ProofOfAddressConfig,
|
|
24
25
|
QuestionnaireConfig,
|
|
25
26
|
WorkflowCountry,
|
|
27
|
+
MultiIdConfig,
|
|
28
|
+
AddressCollectionConfig,
|
|
26
29
|
} from './workflow';
|
|
27
30
|
|
|
28
31
|
export type * from './workflow';
|
|
@@ -54,6 +57,13 @@ export type KYCStep =
|
|
|
54
57
|
| 'applicant-role'
|
|
55
58
|
| 'liveness'
|
|
56
59
|
| 'proof-of-address'
|
|
60
|
+
// The address flow, in order: find it (search) → confirm it (the PIN step,
|
|
61
|
+
// which keeps the original 'address-collection' wire name so older session
|
|
62
|
+
// progress restores cleanly) → show it (entrance photo) → commit it.
|
|
63
|
+
| 'address-search'
|
|
64
|
+
| 'address-collection'
|
|
65
|
+
| 'address-entrance'
|
|
66
|
+
| 'address-review'
|
|
57
67
|
| 'questionnaire'
|
|
58
68
|
| 'submitted';
|
|
59
69
|
|
|
@@ -155,16 +165,49 @@ export interface MyazaKYCConfig<C extends SupportedCountry = SupportedCountry> {
|
|
|
155
165
|
* reflection flash, or both. Workflow-driven.
|
|
156
166
|
*/
|
|
157
167
|
livenessMode?: LivenessMode;
|
|
168
|
+
/**
|
|
169
|
+
* KYB success screen: after submitting with key people still to verify,
|
|
170
|
+
* tapping Done offers the session's own web page so the applicant can reach
|
|
171
|
+
* the invite links again after the app closes. Default true.
|
|
172
|
+
*/
|
|
173
|
+
keyPeopleLinkRecovery?: boolean;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Show the consent (welcome) screen as the flow's first step. Default true.
|
|
177
|
+
* `false` is for a host app that has already collected the person's
|
|
178
|
+
* consent: the flow then opens straight on its first real step (the contact
|
|
179
|
+
* codes, the country picker, the ID list, the business form, or a scoped
|
|
180
|
+
* flow's own check). Normally set by a workflow. It does not change what
|
|
181
|
+
* the organisation attests to the verification provider.
|
|
182
|
+
*/
|
|
183
|
+
consentStep?: boolean;
|
|
158
184
|
|
|
159
185
|
/** Colours in the flash sequence (2–5, default 4). Flash modes only. */
|
|
160
186
|
flashSequenceLength?: number;
|
|
161
187
|
|
|
188
|
+
/**
|
|
189
|
+
* The biometric scopes' flow options (workflow-driven, or passed here on a
|
|
190
|
+
* prop-configured mount): `selfieReview` shows the captured selfie with
|
|
191
|
+
* Retake and Continue before submitting (off by default on both biometric
|
|
192
|
+
* scopes); `resultDelivery` says where a re-authentication's verdict lands,
|
|
193
|
+
* 'both' (the default: the SDK holds the person on one loading screen until
|
|
194
|
+
* the check settles, and the org's webhook receives it too), 'app' (the
|
|
195
|
+
* same wait, but the server sends no webhook for the check) or 'webhook'
|
|
196
|
+
* (fire-and-forget); `doneButton` (default true) hides the final screen's Done
|
|
197
|
+
* when the host app closes the flow itself from `onResult`. A flow key wins
|
|
198
|
+
* per field. See config/biometricOptions.
|
|
199
|
+
*/
|
|
200
|
+
biometric?: import('../config/biometricOptions').BiometricFlowConfig;
|
|
201
|
+
|
|
162
202
|
// ── Workflow-driven blocks ────────────────────────────────────────────────
|
|
163
203
|
// Authored in the dashboard's workflow builder, not usually in consumer code.
|
|
164
204
|
// A resolved flow always wins over a prop of the same name.
|
|
165
205
|
|
|
166
206
|
/** What this flow verifies. Absent = 'individual' (classic KYC). */
|
|
167
207
|
subjectType?: SubjectType;
|
|
208
|
+
/** Workflow scope — what this flow verifies (absent = full verification).
|
|
209
|
+
* Only ever set by a resolved workflow config / hosted session snapshot. */
|
|
210
|
+
scope?: import('../lib/scope').WorkflowScope;
|
|
168
211
|
|
|
169
212
|
/** KYB registry configuration. Required when `subjectType` is 'business'. */
|
|
170
213
|
business?: WorkflowBusinessConfig;
|
|
@@ -175,6 +218,10 @@ export interface MyazaKYCConfig<C extends SupportedCountry = SupportedCountry> {
|
|
|
175
218
|
*/
|
|
176
219
|
countries?: WorkflowCountry[];
|
|
177
220
|
|
|
221
|
+
/** Multi-ID policy: several ID checks in one run, judged by a pass policy.
|
|
222
|
+
* KYC only — publish rejects it on a KYB workflow. */
|
|
223
|
+
multiId?: MultiIdConfig;
|
|
224
|
+
|
|
178
225
|
/** Email OTP possession check, right after consent. */
|
|
179
226
|
emailVerification?: EmailVerificationConfig;
|
|
180
227
|
|
|
@@ -184,9 +231,22 @@ export interface MyazaKYCConfig<C extends SupportedCountry = SupportedCountry> {
|
|
|
184
231
|
/** Proof-of-address document check, after capture. */
|
|
185
232
|
proofOfAddress?: ProofOfAddressConfig;
|
|
186
233
|
|
|
234
|
+
/** Address Intelligence: a map-pin smart address (+ optional door photo and
|
|
235
|
+
* directions), corroborated server-side. KYC AND KYB (premises pin). */
|
|
236
|
+
addressCollection?: AddressCollectionConfig;
|
|
237
|
+
|
|
187
238
|
/** Compliance declarations asked just before submission. */
|
|
188
239
|
questionnaire?: QuestionnaireConfig;
|
|
189
240
|
|
|
241
|
+
/**
|
|
242
|
+
* A reviewer sent this attempt back to redo specific steps.
|
|
243
|
+
*
|
|
244
|
+
* Never set by a consumer and never part of a published workflow — it is
|
|
245
|
+
* stamped onto ONE session's config snapshot when somebody clicks "Send back".
|
|
246
|
+
* Absent means the ordinary full flow.
|
|
247
|
+
*/
|
|
248
|
+
resubmit?: ResubmitConfig;
|
|
249
|
+
|
|
190
250
|
/** eMRTD chip read for chip-capable documents. */
|
|
191
251
|
nfc?: NfcConfig;
|
|
192
252
|
|
|
@@ -255,9 +315,17 @@ export interface MyazaKYCConfig<C extends SupportedCountry = SupportedCountry> {
|
|
|
255
315
|
onStepChange?: (step: KYCStep) => void;
|
|
256
316
|
/**
|
|
257
317
|
* Fires immediately after the user submits. The submission is always
|
|
258
|
-
* status: '
|
|
318
|
+
* status: 'processing' — results arrive async via webhook.
|
|
259
319
|
*/
|
|
260
320
|
onSubmit?: (submission: KYCSubmission) => void;
|
|
321
|
+
/**
|
|
322
|
+
* Fires once with the verdict when the flow WAITS for it in-app (a
|
|
323
|
+
* biometric re-authentication on `resultDelivery: 'both'`, the default, or 'app').
|
|
324
|
+
* Never fires on a fire-and-forget flow, and never on a wait that timed out:
|
|
325
|
+
* the webhook stays the record either way. Carries state and reason only,
|
|
326
|
+
* never result data.
|
|
327
|
+
*/
|
|
328
|
+
onResult?: (result: KYCResult) => void;
|
|
261
329
|
onClose?: () => void;
|
|
262
330
|
/**
|
|
263
331
|
* Fires for technical errors only. Receives a {@link KYCError} carrying a
|
|
@@ -2,9 +2,21 @@
|
|
|
2
2
|
// Submission callback payload (returned to onSubmit)
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The verdict a flow that waited for it hands to `onResult`: the same state
|
|
7
|
+
* vocabulary `GET /api/kyc/status/:id` serves, plus the reason pair. No result
|
|
8
|
+
* data (scores, biodata) ever rides here; that stays behind the secret key.
|
|
9
|
+
*/
|
|
10
|
+
export interface KYCResult {
|
|
11
|
+
verificationId: string;
|
|
12
|
+
status: import('../services/api-types').SessionStatus;
|
|
13
|
+
reason: string | null;
|
|
14
|
+
reasonCode: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
export interface KYCSubmission {
|
|
6
18
|
verificationId: string;
|
|
7
|
-
status: '
|
|
19
|
+
status: 'processing';
|
|
8
20
|
metadata: Record<string, string>;
|
|
9
21
|
submittedAt: string;
|
|
10
22
|
}
|
package/src/types/workflow.ts
CHANGED
|
@@ -13,7 +13,16 @@
|
|
|
13
13
|
|
|
14
14
|
// ── Proof of Address ────────────────────────────────────────────────────────
|
|
15
15
|
|
|
16
|
-
export type PoaDocumentType =
|
|
16
|
+
export type PoaDocumentType =
|
|
17
|
+
| 'utility_bill'
|
|
18
|
+
| 'bank_statement'
|
|
19
|
+
| 'tenancy_agreement'
|
|
20
|
+
| 'government_document'
|
|
21
|
+
| 'other';
|
|
22
|
+
|
|
23
|
+
/** Whether the applicant's name must appear on the document — judged by the
|
|
24
|
+
* server; read here only to word the step. */
|
|
25
|
+
export type PoaNameRule = 'required' | 'optional' | 'off';
|
|
17
26
|
|
|
18
27
|
export interface ProofOfAddressConfig {
|
|
19
28
|
/** Adds the Proof of Address step (after capture, before the questionnaire). */
|
|
@@ -24,6 +33,93 @@ export interface ProofOfAddressConfig {
|
|
|
24
33
|
otherLabel?: string;
|
|
25
34
|
/** Recency window the server checks the document date against (default 90). */
|
|
26
35
|
maxAgeDays?: number;
|
|
36
|
+
/**
|
|
37
|
+
* The org's accepted countries (absent/empty = all). On the ADDRESS SCOPE
|
|
38
|
+
* this is exactly what the declared-country picker offers; on a full flow it
|
|
39
|
+
* gates the step client-side (the server stays soft).
|
|
40
|
+
*/
|
|
41
|
+
countries?: string[];
|
|
42
|
+
/**
|
|
43
|
+
* Per-country document-kind overrides (ISO-2 → kinds). A present entry
|
|
44
|
+
* REPLACES `documentTypes` for that country.
|
|
45
|
+
*/
|
|
46
|
+
countryDocuments?: Record<string, PoaDocumentType[]>;
|
|
47
|
+
/** The default name rule for every country and kind (absent = required). */
|
|
48
|
+
nameMatch?: PoaNameRule;
|
|
49
|
+
/** Per-country, per-kind exceptions to `nameMatch` (ISO-2 → kind → rule). */
|
|
50
|
+
countryNameMatch?: Record<string, Partial<Record<PoaDocumentType, PoaNameRule>>>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── Address Intelligence (smart-address capture) ────────────────────────────
|
|
54
|
+
|
|
55
|
+
export interface AddressCollectionConfig {
|
|
56
|
+
/** Adds the address-collection step (after Proof of Address; on KYB flows it
|
|
57
|
+
* collects the business premises pin). */
|
|
58
|
+
enabled?: boolean;
|
|
59
|
+
/** Block Continue without a confirmed pin (the server 422s without one too). */
|
|
60
|
+
requirePin?: boolean;
|
|
61
|
+
/** Whether the door-photo input is offered/required (default optional). */
|
|
62
|
+
photo?: 'off' | 'optional' | 'required';
|
|
63
|
+
/** Whether the directions field is offered/required (default optional). */
|
|
64
|
+
directions?: 'off' | 'optional' | 'required';
|
|
65
|
+
/**
|
|
66
|
+
* The GROUP default for the typed details-sheet fields: `'off'` hides them
|
|
67
|
+
* all, `'optional'` (the default) offers them, `'required'` requires the
|
|
68
|
+
* house or flat NUMBER (the building name stays optional). Resolved per
|
|
69
|
+
* field by lib/address-field-modes.ts, the mirror of the server's rule; the
|
|
70
|
+
* pin step holds Continue and the review holds Confirm until every required
|
|
71
|
+
* field shows a value, because the server 422s a submission that arrives
|
|
72
|
+
* without one.
|
|
73
|
+
*/
|
|
74
|
+
propertyFields?: 'off' | 'optional' | 'required';
|
|
75
|
+
/**
|
|
76
|
+
* Per-field overrides on the group default, keyed by the typed field
|
|
77
|
+
* (`propertyName`, `propertyNumber`, `street`, `unit`, `neighbourhood`,
|
|
78
|
+
* `city`, `state`, `postcode`). A `'required'` field the applicant leaves on
|
|
79
|
+
* its map prefill is submitted as displayed: they saw it and confirmed by
|
|
80
|
+
* continuing.
|
|
81
|
+
*/
|
|
82
|
+
fields?: Partial<
|
|
83
|
+
Record<
|
|
84
|
+
'propertyName' | 'propertyNumber' | 'street' | 'unit' | 'neighbourhood' | 'city' | 'state' | 'postcode',
|
|
85
|
+
'off' | 'optional' | 'required'
|
|
86
|
+
>
|
|
87
|
+
>;
|
|
88
|
+
/**
|
|
89
|
+
* Street View entrance framing. Default 'optional' (on wherever coverage
|
|
90
|
+
* exists, with the photo as the fallback); 'required' is a client-UX gate
|
|
91
|
+
* that removes the Skip affordance while coverage exists — no-coverage
|
|
92
|
+
* still falls back to the photo, and the server never refuses over it.
|
|
93
|
+
*
|
|
94
|
+
* Offered on mobile through the hosted /embed/street-view page in a
|
|
95
|
+
* WebView on the app grant, the way the framed map is (needs the server's
|
|
96
|
+
* maps frame URL and the optional `react-native-webview` peer); without
|
|
97
|
+
* either the entrance step is photo-only. See addressFlowOptions.
|
|
98
|
+
*/
|
|
99
|
+
streetView?: 'off' | 'optional' | 'required';
|
|
100
|
+
/**
|
|
101
|
+
* Take a one-shot device GPS fix when the pin is confirmed, so the server
|
|
102
|
+
* can judge "captured at the claimed address" (the `attested` tier). The fix
|
|
103
|
+
* is a CLAIM the server evaluates, never a verdict.
|
|
104
|
+
*/
|
|
105
|
+
attestPresence?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Phase 2: multi-day presence verification. When enabled, the SDK stores the
|
|
108
|
+
* confirmed pin ON-DEVICE so later `reportAddressPresence()` calls can
|
|
109
|
+
* evaluate the fence locally — coordinates never leave the phone after
|
|
110
|
+
* capture. `background` is reserved for the org-opt-in OS-geofence tier.
|
|
111
|
+
*/
|
|
112
|
+
presence?: {
|
|
113
|
+
enabled?: boolean;
|
|
114
|
+
windowDays?: number;
|
|
115
|
+
minNights?: number;
|
|
116
|
+
minDays?: number;
|
|
117
|
+
dwellFloorMinutes?: number;
|
|
118
|
+
background?: boolean;
|
|
119
|
+
/** OkHi-style always-on monitoring: the server renews each
|
|
120
|
+
* resolved cycle, and the on-device pin never self-expires. */
|
|
121
|
+
alwaysOn?: boolean;
|
|
122
|
+
};
|
|
27
123
|
}
|
|
28
124
|
|
|
29
125
|
// ── NFC chip read (eMRTD) ───────────────────────────────────────────────────
|
|
@@ -141,11 +237,26 @@ export interface WorkflowCountry {
|
|
|
141
237
|
idTypes?: string[];
|
|
142
238
|
/** Per-ID validation toggles — restrict-only; they never widen a grant. */
|
|
143
239
|
idOptions?: Record<string, WorkflowIdOption>;
|
|
240
|
+
/** Multi-ID: which IDs THIS country offers for each verification in the run.
|
|
241
|
+
* A pinned slot keeps its list; an absent entry offers everything. */
|
|
242
|
+
multiIdSlots?: Array<{ idTypes?: string[] }>;
|
|
144
243
|
govDbCheck?: boolean;
|
|
145
244
|
documentIntelligence?: boolean;
|
|
146
245
|
}
|
|
147
246
|
|
|
148
247
|
// ── Liveness ────────────────────────────────────────────────────────────────
|
|
149
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Multi-ID: several ID checks in ONE run, one selfie, one verification. The
|
|
251
|
+
* POLICY is workflow-level; WHICH IDs each verification offers is per country
|
|
252
|
+
* (`WorkflowCountry.multiIdSlots`), so multi-region flows work.
|
|
253
|
+
*/
|
|
254
|
+
export interface MultiIdConfig {
|
|
255
|
+
/** How many IDs the applicant completes (2–3). */
|
|
256
|
+
count: number;
|
|
257
|
+
/** How many must pass for the verification to be VERIFIED. */
|
|
258
|
+
minPassed: number;
|
|
259
|
+
}
|
|
260
|
+
|
|
150
261
|
/** Gestures (default), screen-reflection flash, or both. */
|
|
151
262
|
export type LivenessMode = 'gestures' | 'flash' | 'both';
|