@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.
Files changed (246) hide show
  1. package/README.md +151 -5
  2. package/android/build.gradle +35 -3
  3. package/android/src/main/java/com/margelo/nitro/myazakyc/HybridMyazaFaceDetector.kt +77 -0
  4. package/app.plugin.js +89 -8
  5. package/ios/HybridMyazaFaceDetector.swift +16 -3
  6. package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.cpp +23 -0
  7. package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.hpp +2 -0
  8. package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaFaceDetectorSpec.kt +9 -0
  9. package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.cpp +8 -0
  10. package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.hpp +43 -0
  11. package/nitrogen/generated/ios/c++/HybridMyazaFaceDetectorSpecSwift.hpp +17 -0
  12. package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  13. package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec.swift +2 -0
  14. package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec_cxx.swift +31 -0
  15. package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.cpp +2 -0
  16. package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.hpp +3 -0
  17. package/package.json +14 -2
  18. package/src/MyazaBiometricAuth.tsx +119 -0
  19. package/src/MyazaKYC.tsx +12 -0
  20. package/src/components/CountryField.tsx +8 -0
  21. package/src/components/CountryRegionPicker.tsx +184 -0
  22. package/src/components/DashedBorder.tsx +55 -0
  23. package/src/components/DialCodePicker.tsx +53 -67
  24. package/src/components/DialCodeRow.tsx +102 -0
  25. package/src/components/FramedMapPicker.tsx +122 -0
  26. package/src/components/GeoBadge.tsx +34 -0
  27. package/src/components/Icon.tsx +2 -176
  28. package/src/components/KycFlow.tsx +60 -32
  29. package/src/components/LineSkeleton.tsx +100 -0
  30. package/src/components/MapChrome.tsx +67 -0
  31. package/src/components/MapPinMarker.tsx +54 -0
  32. package/src/components/MapPinPicker.tsx +179 -0
  33. package/src/components/MilestoneTrack.tsx +155 -0
  34. package/src/components/MultiIdProgress.tsx +107 -0
  35. package/src/components/MyazaInput.tsx +39 -4
  36. package/src/components/MyazaSelect.tsx +2 -1
  37. package/src/components/OptionRow.tsx +22 -3
  38. package/src/components/OwnershipSlider.tsx +102 -0
  39. package/src/components/PhoneNumberInput.tsx +45 -4
  40. package/src/components/PoweredBy.tsx +5 -5
  41. package/src/components/PresenceBlocks.tsx +153 -0
  42. package/src/components/StaggerIn.tsx +63 -0
  43. package/src/components/StepView.tsx +43 -0
  44. package/src/components/StickyActions.tsx +56 -0
  45. package/src/components/dialCodeRows.ts +64 -0
  46. package/src/components/glass/FloatingSheet.tsx +73 -21
  47. package/src/components/glass/GlassSheet.tsx +14 -2
  48. package/src/components/icon-map.ts +176 -0
  49. package/src/components/stepHeaderMeta.tsx +45 -5
  50. package/src/config/addressCollection.ts +125 -0
  51. package/src/config/biometricOptions.ts +100 -0
  52. package/src/config/business.ts +26 -7
  53. package/src/config/businessDetailsValidity.ts +52 -0
  54. package/src/config/businessPrefill.ts +72 -0
  55. package/src/config/businessSteps.ts +20 -3
  56. package/src/config/consentStep.ts +19 -0
  57. package/src/config/keyPeople.ts +111 -7
  58. package/src/config/keyPeopleCorporate.ts +19 -0
  59. package/src/config/keyPeopleOwnerTypes.ts +12 -0
  60. package/src/config/keyPeopleOwners.ts +26 -0
  61. package/src/config/keyPeoplePrefill.ts +138 -0
  62. package/src/config/keyPeopleSectionDefs.ts +65 -0
  63. package/src/config/keyPeopleSections.ts +167 -0
  64. package/src/config/proofOfAddress.ts +54 -11
  65. package/src/config/regions.ts +24 -0
  66. package/src/config/registrationHint.ts +1 -1
  67. package/src/config/stepOrder.ts +100 -6
  68. package/src/config/theme.ts +8 -2
  69. package/src/config/uploadLimits.ts +35 -0
  70. package/src/config/website.ts +36 -0
  71. package/src/config/workflowMerge.ts +16 -5
  72. package/src/emrtd/activeAuth.ts +100 -0
  73. package/src/emrtd/extras.ts +76 -0
  74. package/src/emrtd/files.ts +5 -0
  75. package/src/emrtd/index.ts +1 -0
  76. package/src/emrtd/read.ts +8 -1
  77. package/src/emrtd/session.ts +41 -0
  78. package/src/emrtd/stages.ts +11 -0
  79. package/src/index.ts +54 -1
  80. package/src/lib/address-current-location.ts +171 -0
  81. package/src/lib/address-field-modes.ts +119 -0
  82. package/src/lib/address-flow.ts +172 -0
  83. package/src/lib/address-helpers.ts +48 -0
  84. package/src/lib/address-line.ts +76 -0
  85. package/src/lib/address-pin-move.ts +97 -0
  86. package/src/lib/address-step-recovery.ts +63 -0
  87. package/src/lib/authed-image.ts +78 -0
  88. package/src/lib/biometric-auth.ts +52 -0
  89. package/src/lib/biometric-copy.ts +51 -0
  90. package/src/lib/captureRing.ts +83 -0
  91. package/src/lib/contact-recovery.ts +44 -0
  92. package/src/lib/country-adoption.ts +89 -0
  93. package/src/lib/inferred-country.ts +65 -0
  94. package/src/lib/livenessLayout.ts +49 -0
  95. package/src/lib/map-frame.ts +159 -0
  96. package/src/lib/map-tiles.ts +143 -0
  97. package/src/lib/multi-id.ts +210 -0
  98. package/src/lib/poa-country-gate.ts +27 -0
  99. package/src/lib/resubmit.ts +130 -0
  100. package/src/lib/result-copy.ts +116 -0
  101. package/src/lib/result-wait.ts +53 -0
  102. package/src/lib/review-map-surface.ts +26 -0
  103. package/src/lib/scope.ts +31 -0
  104. package/src/lib/screen-corners.ts +50 -0
  105. package/src/lib/selfie-upload-wait.ts +79 -0
  106. package/src/lib/step-log.ts +24 -3
  107. package/src/lib/street-view-fov.ts +42 -0
  108. package/src/lib/use-multi-id-plan.ts +29 -0
  109. package/src/lib/webview-available.ts +47 -0
  110. package/src/liveness/useLiveness.ts +6 -1
  111. package/src/liveness/useModelReady.ts +65 -0
  112. package/src/liveness/visionCameraFaceDetector.ts +35 -0
  113. package/src/presence/background-math.ts +100 -0
  114. package/src/presence/background-store.ts +82 -0
  115. package/src/presence/background.ts +165 -0
  116. package/src/presence/foreground-service.ts +193 -0
  117. package/src/presence/fs.ts +51 -0
  118. package/src/presence/geofence.ts +41 -0
  119. package/src/presence/math.ts +44 -0
  120. package/src/presence/post.ts +40 -0
  121. package/src/presence/report.ts +87 -0
  122. package/src/presence/sampler.ts +110 -0
  123. package/src/presence/status.ts +92 -0
  124. package/src/presence/store.ts +94 -0
  125. package/src/presence/tier.ts +36 -0
  126. package/src/presence/watch-wait.ts +81 -0
  127. package/src/screens/AddressCountryControl.tsx +115 -0
  128. package/src/screens/ApplicantRoleStep.tsx +9 -5
  129. package/src/screens/BusinessCheckPanel.tsx +89 -0
  130. package/src/screens/BusinessDetailsFields.tsx +112 -0
  131. package/src/screens/BusinessDetailsStep.tsx +133 -145
  132. package/src/screens/BusinessDocumentSlot.tsx +5 -5
  133. package/src/screens/BusinessDocumentsStep.tsx +5 -56
  134. package/src/screens/BusinessKeyPeopleStep.tsx +103 -71
  135. package/src/screens/BusinessPickedCard.tsx +86 -0
  136. package/src/screens/BusinessPickedSection.tsx +51 -0
  137. package/src/screens/BusinessRegistryPickers.tsx +67 -0
  138. package/src/screens/BusinessSandboxToggle.tsx +114 -0
  139. package/src/screens/BusinessSearch.tsx +199 -0
  140. package/src/screens/BusinessSearchResults.tsx +115 -0
  141. package/src/screens/CompanyInfoFields.tsx +91 -51
  142. package/src/screens/ConsentStep.tsx +37 -17
  143. package/src/screens/ContactActions.tsx +54 -0
  144. package/src/screens/ContactDestinationField.tsx +5 -0
  145. package/src/screens/ContactEntryPanel.tsx +75 -0
  146. package/src/screens/ContactVerificationStep.tsx +43 -42
  147. package/src/screens/CountrySelectStep.tsx +13 -147
  148. package/src/screens/IdTypeStep.tsx +12 -5
  149. package/src/screens/KeepLinksSheet.tsx +88 -0
  150. package/src/screens/KeyPeopleAwaitCard.tsx +204 -0
  151. package/src/screens/KeyPeopleAwaitList.tsx +126 -0
  152. package/src/screens/KeyPeoplePending.tsx +154 -0
  153. package/src/screens/KeyPeopleSection.tsx +162 -0
  154. package/src/screens/KeyPeopleSectionsList.tsx +103 -0
  155. package/src/screens/KeyPeopleUboExemption.tsx +72 -0
  156. package/src/screens/KeyPersonCard.tsx +36 -4
  157. package/src/screens/KeyPersonForm.tsx +195 -36
  158. package/src/screens/KeyPersonKindToggle.tsx +68 -0
  159. package/src/screens/KeyPersonOwners.tsx +109 -0
  160. package/src/screens/KeyPersonRoleChips.tsx +68 -0
  161. package/src/screens/KeyPersonSheet.tsx +92 -108
  162. package/src/screens/LivenessAvatar.tsx +15 -3
  163. package/src/screens/LivenessStep.tsx +172 -52
  164. package/src/screens/NfcStep.tsx +14 -0
  165. package/src/screens/PoaDocumentTypeList.tsx +66 -0
  166. package/src/screens/ProofOfAddressParts.tsx +151 -0
  167. package/src/screens/ProofOfAddressStep.tsx +58 -96
  168. package/src/screens/SubmittedBadge.tsx +25 -0
  169. package/src/screens/SubmittedError.tsx +64 -0
  170. package/src/screens/SubmittedResult.tsx +117 -0
  171. package/src/screens/SubmittedStep.tsx +82 -119
  172. package/src/screens/SubmittedSuccess.tsx +127 -0
  173. package/src/screens/SubmittedWaiting.tsx +45 -0
  174. package/src/screens/address/AddressEntranceStep.tsx +161 -0
  175. package/src/screens/address/AddressIntroGate.tsx +148 -0
  176. package/src/screens/address/AddressMapStub.tsx +50 -0
  177. package/src/screens/address/AddressPinStep.tsx +178 -0
  178. package/src/screens/address/AddressReviewStep.tsx +162 -0
  179. package/src/screens/address/AddressSandboxTabs.tsx +158 -0
  180. package/src/screens/address/AddressSearchField.tsx +115 -0
  181. package/src/screens/address/AddressSearchStep.tsx +94 -0
  182. package/src/screens/address/CurrentLocationRow.tsx +135 -0
  183. package/src/screens/address/DetailsSheet.tsx +175 -0
  184. package/src/screens/address/DetailsSheetFields.tsx +184 -0
  185. package/src/screens/address/EntranceDropzone.tsx +178 -0
  186. package/src/screens/address/EntranceFraming.tsx +66 -0
  187. package/src/screens/address/EntrancePills.tsx +60 -0
  188. package/src/screens/address/FramedStreetView.tsx +155 -0
  189. package/src/screens/address/IntroDisclosures.tsx +210 -0
  190. package/src/screens/address/LabelDecisionRow.tsx +113 -0
  191. package/src/screens/address/PinSummaryRow.tsx +105 -0
  192. package/src/screens/address/ReviewAddressBand.tsx +111 -0
  193. package/src/screens/address/ReviewEntranceThumbs.tsx +64 -0
  194. package/src/screens/address/ReviewMapPicture.tsx +106 -0
  195. package/src/screens/address/SearchResults.tsx +105 -0
  196. package/src/screens/address/SearchScreen.tsx +176 -0
  197. package/src/screens/address/SkipForNow.tsx +43 -0
  198. package/src/screens/address/StreetViewChrome.tsx +81 -0
  199. package/src/screens/address/detail-values.ts +22 -0
  200. package/src/screens/address/fix-source.ts +27 -0
  201. package/src/screens/address/index.ts +8 -0
  202. package/src/screens/address/meta.ts +44 -0
  203. package/src/screens/address/use-address-flow.ts +200 -0
  204. package/src/screens/address/use-label-pin.ts +80 -0
  205. package/src/screens/address/use-pin-actions.ts +192 -0
  206. package/src/screens/biometric/BiometricAuthFlow.tsx +170 -0
  207. package/src/screens/consent/model.ts +79 -10
  208. package/src/screens/liveness/CaptureRing.tsx +91 -0
  209. package/src/screens/liveness/LivenessCamera.tsx +63 -0
  210. package/src/screens/liveness/LivenessHandover.tsx +43 -0
  211. package/src/screens/liveness/LivenessOutcome.tsx +4 -3
  212. package/src/screens/liveness/SelfiePreview.tsx +13 -4
  213. package/src/screens/liveness/ShutterFlash.tsx +25 -0
  214. package/src/screens/liveness/index.ts +4 -0
  215. package/src/screens/liveness/useSelfieUpload.ts +23 -3
  216. package/src/screens/nfc/NfcReadProgress.tsx +1 -0
  217. package/src/screens/nfc/NfcScanIllustration.tsx +7 -18
  218. package/src/screens/useAddressPhotoAttach.ts +77 -0
  219. package/src/screens/useAwaitingPeople.ts +90 -0
  220. package/src/screens/useBusinessDocumentAttach.ts +93 -0
  221. package/src/screens/usePoaAttach.ts +12 -15
  222. package/src/services/api-biometric.ts +37 -0
  223. package/src/services/api-types-biometric.ts +41 -0
  224. package/src/services/api-types.ts +251 -3
  225. package/src/services/api-verify-types.ts +90 -1
  226. package/src/services/api.ts +237 -3
  227. package/src/services/deviceMetadata.ts +1 -1
  228. package/src/services/errors.ts +10 -2
  229. package/src/services/location.ts +234 -0
  230. package/src/services/uploadErrors.ts +30 -0
  231. package/src/services/workflowGate.ts +8 -0
  232. package/src/specs/MyazaFaceDetector.nitro.ts +29 -0
  233. package/src/store/address.ts +97 -0
  234. package/src/store/businessCheck.ts +113 -0
  235. package/src/store/derive.ts +44 -4
  236. package/src/store/kycStore.ts +352 -16
  237. package/src/store/serverConfig.ts +20 -0
  238. package/src/store/session.ts +227 -0
  239. package/src/store/state.ts +289 -2
  240. package/src/store/submit.ts +80 -13
  241. package/src/store/submitApplicant.ts +5 -11
  242. package/src/types/business.ts +23 -2
  243. package/src/types/config.ts +70 -2
  244. package/src/types/verification.ts +13 -1
  245. package/src/types/workflow.ts +112 -1
  246. package/src/screens/KeyPeopleInviteLinks.tsx +0 -239
@@ -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
- idType: business ? business.product : (state.selectedIdType ?? ''),
36
- idNumber: business ? undefined : (state.idNumber ?? undefined),
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
- mediaIds: business ? {} : state.mediaIds,
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
- ...(state.chipData
64
- ? {
65
- nfc: {
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). Same block as the individual flow's builder.
57
- ...(state.chipData
58
- ? {
59
- nfc: {
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.
@@ -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
- /** Supporting-document kinds a KYB workflow can request from the applicant. */
21
- export const COMPANY_INFO_FIELDS = ['address', 'email', 'phone', 'website'] as const;
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
  /**
@@ -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: 'pending' — results arrive async via webhook.
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: 'pending';
19
+ status: 'processing';
8
20
  metadata: Record<string, string>;
9
21
  submittedAt: string;
10
22
  }
@@ -13,7 +13,16 @@
13
13
 
14
14
  // ── Proof of Address ────────────────────────────────────────────────────────
15
15
 
16
- export type PoaDocumentType = 'utility_bill' | 'bank_statement' | 'tenancy_agreement' | 'other';
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';