@myazahq/kyc-sdk-react-native 2.0.0 → 2.0.1
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 +305 -51
- package/package.json +1 -1
- package/src/services/deviceMetadata.ts +1 -1
package/README.md
CHANGED
|
@@ -1,39 +1,67 @@
|
|
|
1
1
|
# @myazahq/kyc-sdk-react-native
|
|
2
2
|
|
|
3
3
|
Myaza KYC SDK for **React Native (Expo)** — ID verification, document capture, and
|
|
4
|
-
active liveness
|
|
4
|
+
active **on-device** liveness. Mirrors the [web](https://www.npmjs.com/package/@myazahq/kyc-sdk-react)
|
|
5
5
|
and Flutter SDKs feature-for-feature and calls the same Myaza KYC API server.
|
|
6
6
|
|
|
7
7
|
The SDK is a **thin UI layer**: it captures the user's data (ID number, document
|
|
8
8
|
photos, a live selfie), uploads the media, and submits a verification request.
|
|
9
9
|
All verification (OCR, facial comparison, gov-DB checks) happens server-side and
|
|
10
|
-
is delivered asynchronously via webhook — the SDK is fire-and-forget
|
|
10
|
+
is delivered asynchronously via webhook — the SDK is **fire-and-forget**.
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
This library ships **native code** (an Apple Vision + Google ML Kit face detector,
|
|
15
|
+
built as a [react-native-vision-camera](https://react-native-vision-camera.com) v5 /
|
|
16
|
+
Nitro module), so it needs a **custom native build** and **does not run in Expo Go**.
|
|
17
|
+
|
|
18
|
+
| Requirement | Minimum |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| **iOS** deployment target | **15.1** |
|
|
21
|
+
| **Android** `minSdkVersion` | **24** (Android 7.0) · `compileSdk` 34 · NDK 27.1 |
|
|
22
|
+
| **Expo SDK** | **56** (React 19, React Native 0.85) |
|
|
23
|
+
| **React Native** | **0.83+**, with the **New Architecture enabled** (VisionCamera v5 / Nitro requires it; Expo SDK 56 enables it by default) |
|
|
24
|
+
| **Build toolchain** | Xcode + CocoaPods (iOS) · **JDK 17** for Android Gradle builds |
|
|
25
|
+
| **Runtime** | A **dev client** or bare build — **not Expo Go** |
|
|
26
|
+
|
|
27
|
+
**Peer dependencies** to install in your app:
|
|
28
|
+
|
|
29
|
+
| Package | Range | Purpose |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `expo` | `>=56` | Expo module runtime (the SDK uses several `expo-*` modules) |
|
|
32
|
+
| `react` / `react-native` | `>=19` / `>=0.83` | — |
|
|
33
|
+
| `react-native-vision-camera` | `^5` | Camera preview + capture |
|
|
34
|
+
| `react-native-vision-camera-worklets` | `>=5` | Frame-processor worklet runtime |
|
|
35
|
+
| `react-native-worklets` | `>=0.8` | Worklet `runOnJS` bridge for liveness |
|
|
36
|
+
| `react-native-nitro-modules` | `>=0.35` | Native module runtime for the face detector |
|
|
37
|
+
| `react-native-nitro-image` | `>=0.15` | Frame → image interop used by the detector |
|
|
38
|
+
| `react-native-safe-area-context` | `>=4` | Safe-area insets for the modal |
|
|
39
|
+
| `react-native-svg` | `>=15` | Liveness/overlay graphics |
|
|
11
40
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
This library ships **native code** (an Apple Vision + Google ML Kit face-detector
|
|
15
|
-
VisionCamera frame-processor plugin), so it needs a custom native build. It does
|
|
16
|
-
**not** run in Expo Go. Pick your setup below.
|
|
41
|
+
> Voice guidance is **text-to-speech output only** — the SDK never records audio,
|
|
42
|
+
> so **no microphone permission** is requested or required.
|
|
17
43
|
|
|
18
|
-
|
|
19
|
-
`react-native-worklets-core`, `react-native-svg`, and `expo` (the SDK uses several
|
|
20
|
-
`expo-*` modules — see *Bare React Native* if you're not on a managed Expo app).
|
|
44
|
+
## Installation
|
|
21
45
|
|
|
22
46
|
### Expo app (managed / prebuild — recommended)
|
|
23
47
|
|
|
24
48
|
```sh
|
|
25
49
|
npx expo install @myazahq/kyc-sdk-react-native \
|
|
26
|
-
react-native-vision-camera react-native-worklets
|
|
50
|
+
react-native-vision-camera react-native-vision-camera-worklets \
|
|
51
|
+
react-native-worklets react-native-nitro-modules react-native-nitro-image \
|
|
52
|
+
react-native-safe-area-context react-native-svg
|
|
27
53
|
```
|
|
28
54
|
|
|
29
|
-
Add the config plugins to `app.json
|
|
55
|
+
Add the config plugins to `app.json`. The SDK's plugin adds the iOS camera usage
|
|
30
56
|
string + Android `CAMERA`/`INTERNET` permissions; the VisionCamera plugin wires up
|
|
31
|
-
the camera + frame processors
|
|
57
|
+
the camera + frame processors. Make sure the **New Architecture** is on (it is by
|
|
58
|
+
default on Expo SDK 56):
|
|
32
59
|
|
|
33
60
|
```jsonc
|
|
34
61
|
// app.json
|
|
35
62
|
{
|
|
36
63
|
"expo": {
|
|
64
|
+
"newArchEnabled": true,
|
|
37
65
|
"plugins": [
|
|
38
66
|
["react-native-vision-camera", { "enableMicrophonePermission": false }],
|
|
39
67
|
"@myazahq/kyc-sdk-react-native"
|
|
@@ -46,7 +74,8 @@ Then build a dev client (regenerates the native projects):
|
|
|
46
74
|
|
|
47
75
|
```sh
|
|
48
76
|
npx expo prebuild
|
|
49
|
-
npx expo run:ios
|
|
77
|
+
npx expo run:ios # iOS
|
|
78
|
+
JAVA_HOME=/path/to/jdk-17 npx expo run:android # Android — needs JDK 17
|
|
50
79
|
```
|
|
51
80
|
|
|
52
81
|
> The SDK plugin accepts an optional custom camera prompt:
|
|
@@ -63,7 +92,9 @@ npx install-expo-modules@latest
|
|
|
63
92
|
|
|
64
93
|
# 2. Install the SDK + peers:
|
|
65
94
|
npm install @myazahq/kyc-sdk-react-native \
|
|
66
|
-
react-native-vision-camera react-native-
|
|
95
|
+
react-native-vision-camera react-native-vision-camera-worklets \
|
|
96
|
+
react-native-worklets react-native-nitro-modules react-native-nitro-image \
|
|
97
|
+
react-native-safe-area-context react-native-svg \
|
|
67
98
|
expo expo-image-manipulator expo-image-picker expo-speech expo-font \
|
|
68
99
|
expo-glass-effect expo-application expo-crypto expo-device expo-localization
|
|
69
100
|
|
|
@@ -84,58 +115,285 @@ prebuild):
|
|
|
84
115
|
<uses-permission android:name="android.permission.CAMERA" />
|
|
85
116
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
86
117
|
```
|
|
87
|
-
-
|
|
88
|
-
|
|
118
|
+
- Ensure the **New Architecture** is enabled (`newArchEnabled=true` in
|
|
119
|
+
`android/gradle.properties`; `RCT_NEW_ARCH_ENABLED=1` for the iOS Podfile install),
|
|
120
|
+
and enable VisionCamera frame processors / the worklets babel plugin — see the
|
|
89
121
|
[VisionCamera setup docs](https://react-native-vision-camera.com/docs/guides).
|
|
90
122
|
|
|
91
|
-
The face-detector native module (
|
|
92
|
-
|
|
93
|
-
autolinking — no manual linking required.
|
|
94
|
-
|
|
95
|
-
> Voice guidance is **text-to-speech output only** — the SDK never records audio,
|
|
96
|
-
> so **no microphone permission** is requested or required
|
|
97
|
-
> (`enableMicrophonePermission: false`).
|
|
123
|
+
The face-detector native module (Nitro HybridObject + Android lib loader) is
|
|
124
|
+
autolinked via React Native / Expo autolinking — no manual linking required.
|
|
98
125
|
|
|
99
126
|
## Usage
|
|
100
127
|
|
|
128
|
+
`<MyazaKYC />` renders a "Verify Identity" trigger plus the full modal flow. Pass
|
|
129
|
+
`children` (a string) to relabel it. For a fully custom trigger, use the
|
|
130
|
+
[`useMyazaKYC()` hook](#trigger-component--hook).
|
|
131
|
+
|
|
101
132
|
```tsx
|
|
102
133
|
import { MyazaKYC } from '@myazahq/kyc-sdk-react-native';
|
|
103
134
|
|
|
104
|
-
export default function
|
|
135
|
+
export default function VerifyScreen() {
|
|
105
136
|
return (
|
|
106
137
|
<MyazaKYC
|
|
107
|
-
apiKey="
|
|
138
|
+
apiKey="pk_live_xxx" // prefix selects the env: pk_test_ → sandbox
|
|
108
139
|
country="NG"
|
|
109
|
-
idTypes={['bvn', 'nin', '
|
|
140
|
+
idTypes={['passport', 'drivers-license', 'bvn', 'nin', 'pvc']}
|
|
141
|
+
userData={{ firstName: 'Jane', lastName: 'Doe' }}
|
|
142
|
+
enableSelfie
|
|
110
143
|
enableDocumentCapture
|
|
111
144
|
enableLiveness
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
145
|
+
showThemeToggle
|
|
146
|
+
appearance={{
|
|
147
|
+
primaryColor: '#5645F5',
|
|
148
|
+
companyName: 'Myaza',
|
|
149
|
+
logo: 'default',
|
|
150
|
+
theme: 'dark',
|
|
151
|
+
}}
|
|
152
|
+
consent={{
|
|
153
|
+
title: 'Welcome, {firstName}',
|
|
154
|
+
description: "A quick check to confirm it's really you.",
|
|
155
|
+
}}
|
|
156
|
+
success={{
|
|
157
|
+
title: "You're all set, {firstName}!",
|
|
158
|
+
description: "We'll email you once your verification is reviewed.",
|
|
159
|
+
}}
|
|
160
|
+
metadata={{ userId: 'test_user_123' }}
|
|
161
|
+
onStart={() => console.log('KYC started')}
|
|
162
|
+
onStepChange={(step) => console.log('Step:', step)}
|
|
163
|
+
onSubmit={(submission) => {
|
|
164
|
+
// Fires as soon as the server accepts the request.
|
|
165
|
+
// submission.status is always 'pending' — the result arrives later via
|
|
166
|
+
// webhook to your backend (or poll GET /api/kyc/status/:id).
|
|
167
|
+
console.log('Submitted!', submission.verificationId);
|
|
168
|
+
}}
|
|
169
|
+
onClose={() => console.log('Modal closed')}
|
|
170
|
+
onError={(err) => console.warn('SDK error:', err.code, err.message)}
|
|
171
|
+
>
|
|
172
|
+
Verify my identity
|
|
173
|
+
</MyazaKYC>
|
|
116
174
|
);
|
|
117
175
|
}
|
|
118
176
|
```
|
|
119
177
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
178
|
+
## Props
|
|
179
|
+
|
|
180
|
+
| Prop | Type | Default | Description |
|
|
181
|
+
| ----------------------- | ----------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
182
|
+
| `apiKey` | `string` | — | **Required.** Sent as `Authorization: Bearer`. The **environment is derived from the key prefix** (`pk_test_…` → sandbox, `pk_live_…` → production); an unrecognized prefix throws. |
|
|
183
|
+
| `country` | `'NG' \| 'GH' \| 'KE' \| 'ZA' \| 'CI'` | — | **Required.** Country whose ID types are offered. |
|
|
184
|
+
| `idTypes` | `IdType[]` | all allowed for org | Subset of ID types to offer; must be valid for `country`. |
|
|
185
|
+
| `userData` | `{ firstName?, lastName?, dateOfBirth? }` | — | Pre-fills the user's details. |
|
|
186
|
+
| `enableSelfie` | `boolean` | `true` | Capture a selfie during liveness. |
|
|
187
|
+
| `enableDocumentCapture` | `boolean` | `true` | Enable the document-scan step for document IDs. |
|
|
188
|
+
| `allowDocumentUpload` | `boolean` | `true` | Allow picking a document photo from the gallery as an alternative to the camera. `false` hides every "upload instead" affordance (it's still offered on the camera-permission-denied screen as an escape hatch). |
|
|
189
|
+
| `enableLiveness` | `boolean` | `true` | Run the liveness challenge step. The server can still disable it per ID type. |
|
|
190
|
+
| `voiceGuidance` | `boolean \| { enabled?, language? }` | `true` | Spoken liveness instructions (accessibility, TTS **output** — no microphone). `false` mutes it; pass `{ language: 'fr-FR' }` to set the voice. See [Robustness & error handling](#robustness--error-handling). |
|
|
191
|
+
| `showThemeToggle` | `boolean` | `true` | Show a light/dark toggle inside the modal header. When `false`, the flow stays on `appearance.theme`. |
|
|
192
|
+
| `disableClose` | `boolean` | `false` | Hide the close (X) and block **all** user dismissal (X, Android back, iOS swipe-down). The flow can then only be closed programmatically via `useMyazaKYC().close()`. |
|
|
193
|
+
| `appearance` | `KYCAppearance` | brand defaults | Brand & theme the modal — colors, logo, light/dark. See [Appearance & theming](#appearance--theming). |
|
|
194
|
+
| `consent` | `KYCConsentContent` | built-in copy | Override the consent/welcome screen `title` and `description`. See [Consent screen copy](#consent-screen-copy). |
|
|
195
|
+
| `success` | `KYCSuccessContent` | built-in copy | Override the success/submitted screen `title` and `description`. See [Success screen copy](#success-screen-copy). |
|
|
196
|
+
| `metadata` | `Record<string, string>` | — | Forwarded with every verify request. |
|
|
197
|
+
| `onStart` | `() => void` | — | Called when the flow opens. |
|
|
198
|
+
| `onStepChange` | `(step: KYCStep) => void` | — | Called on each step transition. |
|
|
199
|
+
| `onSubmit` | `(submission: KYCSubmission) => void` | — | Called when the server accepts the verification. `status` is always `'pending'`. |
|
|
200
|
+
| `onError` | `(error: KYCError) => void` | — | Called for **technical** errors only. Receives a typed [`KYCError`](#robustness--error-handling). Verification outcomes never come through here. |
|
|
201
|
+
| `onClose` | `() => void` | — | Called when the user closes the flow. |
|
|
202
|
+
| `children` | `string` | `Verify Identity` | Trigger label. Defaults to `Verify with {companyName}` when `companyName` is set, else `Verify Identity`. |
|
|
203
|
+
| `disabled` | `boolean` | `false` | Disable the trigger. |
|
|
127
204
|
|
|
128
205
|
## Environment
|
|
129
206
|
|
|
130
|
-
|
|
131
|
-
|
|
207
|
+
There is **no `environment` prop** — the SDK derives the environment (and the
|
|
208
|
+
base URL) from the API key prefix, the single source of truth:
|
|
132
209
|
|
|
133
210
|
| Prefix | Environment | Base URL |
|
|
134
211
|
| ---------- | ----------- | --------------------------------- |
|
|
135
|
-
| `pk_dev_` | development | `http://localhost:3001` (override with `devUrl`) |
|
|
136
212
|
| `pk_test_` | sandbox | `https://sandbox.identity.myaza.app` |
|
|
137
213
|
| `pk_live_` | production | `https://identity.myaza.app` |
|
|
138
214
|
|
|
215
|
+
An unrecognized or malformed key throws at setup (it never silently defaults).
|
|
216
|
+
|
|
217
|
+
## Trigger component & hook
|
|
218
|
+
|
|
219
|
+
`<MyazaKYC />` renders a styled trigger that opens the modal. Beyond the config
|
|
220
|
+
props it accepts `children` (a string label) and `disabled`:
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
<MyazaKYC {...config} disabled={!ready}>
|
|
224
|
+
Start verification
|
|
225
|
+
</MyazaKYC>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
For a **fully custom** trigger (your own `Pressable`, an icon, a list row, or to
|
|
229
|
+
open the flow programmatically), use the `useMyazaKYC()` hook:
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import { Pressable, Text } from 'react-native';
|
|
233
|
+
import { useMyazaKYC } from '@myazahq/kyc-sdk-react-native';
|
|
234
|
+
|
|
235
|
+
function CustomTrigger() {
|
|
236
|
+
const { open, close, isOpen, currentStep } = useMyazaKYC({
|
|
237
|
+
apiKey: 'pk_live_xxx',
|
|
238
|
+
country: 'NG',
|
|
239
|
+
onSubmit: (s) => console.log('submitted', s.verificationId),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<Pressable onPress={open}>
|
|
244
|
+
<Text>Verify your identity</Text>
|
|
245
|
+
</Pressable>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`useMyazaKYC(config)` returns `{ open, close, isOpen, currentStep }`. `close()` is
|
|
251
|
+
the only way to dismiss the flow when `disableClose` is set.
|
|
252
|
+
|
|
253
|
+
## Appearance & theming
|
|
254
|
+
|
|
255
|
+
Pass an `appearance` object to brand the flow. Because the UI is token-driven,
|
|
256
|
+
setting one color cascades to all of its shades (hover/selected/focus states
|
|
257
|
+
included). Unset colors keep the built-in light/dark defaults.
|
|
258
|
+
|
|
259
|
+
| Field | Type | Description |
|
|
260
|
+
| ------------------ | ------------------- | -------------------------------------------------------------------------------- |
|
|
261
|
+
| `primaryColor` | `string` | Brand color — buttons, selected states, progress, the shield hero. Defaults to `#5645F5`. |
|
|
262
|
+
| `primaryTextColor` | `string` | Text/icons rendered on top of `primaryColor` (e.g. button labels). |
|
|
263
|
+
| `accentColor` | `string` | Subtle hover/active surfaces. |
|
|
264
|
+
| `backgroundColor` | `string` | Modal background. |
|
|
265
|
+
| `surfaceColor` | `string` | Cards & panels. |
|
|
266
|
+
| `borderColor` | `string` | Borders and input outlines. |
|
|
267
|
+
| `textColor` | `string` | Primary text color. |
|
|
268
|
+
| `companyName` | `string` | Used on the verify trigger ("Verify with …") and the persistent header. |
|
|
269
|
+
| `logo` | `string` | Image URL, or `'default'` to use your org's logo. See below. |
|
|
270
|
+
| `theme` | `'light' \| 'dark'` | Initial mode (defaults to `'light'`). With `showThemeToggle`, users can flip it. |
|
|
271
|
+
|
|
272
|
+
### Logo
|
|
273
|
+
|
|
274
|
+
The org logo renders as a small circular avatar in the modal header (top-left),
|
|
275
|
+
persistent on every step, alongside `companyName`.
|
|
276
|
+
|
|
277
|
+
- `logo: 'https://…/logo.png'` — uses that image directly.
|
|
278
|
+
- `logo: 'default'` — pulls your organization's logo configured in the **Myaza dashboard**
|
|
279
|
+
(returned by the server on mount). If your org has no logo set, or the image fails
|
|
280
|
+
to load, the avatar is hidden.
|
|
281
|
+
- omitted — no header logo.
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
appearance={{
|
|
285
|
+
primaryColor: '#0F7B6C',
|
|
286
|
+
primaryTextColor: '#FFFFFF',
|
|
287
|
+
surfaceColor: '#F4F7F6',
|
|
288
|
+
borderColor: '#D7E3E0',
|
|
289
|
+
logo: 'default',
|
|
290
|
+
theme: 'light',
|
|
291
|
+
}}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Consent screen copy
|
|
295
|
+
|
|
296
|
+
The welcome/consent step shows a heading and a short description. Override either
|
|
297
|
+
through the `consent` prop:
|
|
298
|
+
|
|
299
|
+
| Field | Type | Description |
|
|
300
|
+
| ------------- | -------- | ----------------------------------------------------------------------------------------------------- |
|
|
301
|
+
| `title` | `string` | Heading. Defaults to `Welcome, {firstName}` when a first name is known, else `Identity Verification`. |
|
|
302
|
+
| `description` | `string` | Sub-text under the heading. Defaults to the built-in regulatory copy. |
|
|
303
|
+
|
|
304
|
+
Both fields support `{firstName}` and `{lastName}` tokens, replaced with the values
|
|
305
|
+
from `userData` (empty string when absent), so a custom title can still greet the
|
|
306
|
+
user by name.
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
consent={{
|
|
310
|
+
title: 'Welcome, {firstName}',
|
|
311
|
+
description: "We just need to confirm it's really you. This takes about a minute.",
|
|
312
|
+
}}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Success screen copy
|
|
316
|
+
|
|
317
|
+
After the user submits, the final screen shows a confirmation heading and
|
|
318
|
+
description. Override either through the `success` prop:
|
|
319
|
+
|
|
320
|
+
| Field | Type | Description |
|
|
321
|
+
| ------------- | -------- | -------------------------------------------------------------------------- |
|
|
322
|
+
| `title` | `string` | Heading. Defaults to `Verification Submitted!`. |
|
|
323
|
+
| `description` | `string` | Sub-text under the heading. Defaults to the built-in "submitted for review" copy. |
|
|
324
|
+
|
|
325
|
+
Both fields support the same `{firstName}` / `{lastName}` tokens as `consent`.
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
success={{
|
|
329
|
+
title: "You're all set, {firstName}!",
|
|
330
|
+
description: "We'll email you once your verification is reviewed.",
|
|
331
|
+
}}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Robustness & error handling
|
|
335
|
+
|
|
336
|
+
The SDK is resilient to flaky networks, denied permissions, and poor capture
|
|
337
|
+
conditions, and reports technical failures through `onError` with a typed code.
|
|
338
|
+
|
|
339
|
+
### Typed errors (`onError`)
|
|
340
|
+
|
|
341
|
+
`onError` receives a `KYCError` with a typed `code`, a human-readable `message`,
|
|
342
|
+
and optional `details`. The codes are **identical to the web and Flutter SDKs**:
|
|
343
|
+
|
|
344
|
+
```tsx
|
|
345
|
+
import { MyazaKYC, type KYCError } from '@myazahq/kyc-sdk-react-native';
|
|
346
|
+
|
|
347
|
+
<MyazaKYC
|
|
348
|
+
{...config}
|
|
349
|
+
onError={(error: KYCError) => {
|
|
350
|
+
switch (error.code) {
|
|
351
|
+
case 'camera_permission_denied': /* ask the user to allow the camera */ break;
|
|
352
|
+
case 'insufficient_credits': /* error.details = { required, balance, currency } */ break;
|
|
353
|
+
case 'network_error':
|
|
354
|
+
case 'upload_failed': /* shown only after automatic retries */ break;
|
|
355
|
+
}
|
|
356
|
+
}}
|
|
357
|
+
/>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
| `code` | When it fires |
|
|
361
|
+
| -------------------------- | ------------------------------------------------------------------- |
|
|
362
|
+
| `network_error` | Connection failure / timeout, **after retries are exhausted**. |
|
|
363
|
+
| `invalid_api_key` | Server returned `401`. |
|
|
364
|
+
| `insufficient_credits` | Server returned `402`. `details = { required, balance, currency }`. |
|
|
365
|
+
| `upload_failed` | A media upload failed, **after retries are exhausted**. |
|
|
366
|
+
| `camera_permission_denied` | The user denied (or the OS blocks) camera access. |
|
|
367
|
+
| `feature_disabled` | Server returned `403` (ID type / feature not enabled for the org). |
|
|
368
|
+
| `unknown` | Anything else. |
|
|
369
|
+
|
|
370
|
+
> Verification *outcomes* (identity not found, document mismatch, …) never come
|
|
371
|
+
> through `onError` — they arrive asynchronously via webhook / `GET /api/kyc/status/:id`.
|
|
372
|
+
|
|
373
|
+
### Network resilience
|
|
374
|
+
|
|
375
|
+
Media uploads and the verify submission are wrapped in exponential-backoff retry
|
|
376
|
+
(with jitter), retrying only *transient* failures (network / timeout / `5xx`);
|
|
377
|
+
terminal `4xx` surface immediately. The UI shows a top toast while retrying, and
|
|
378
|
+
`onError` fires **only after retries are exhausted** (`upload_failed` for uploads,
|
|
379
|
+
`network_error` for connectivity).
|
|
380
|
+
|
|
381
|
+
### Camera permission
|
|
382
|
+
|
|
383
|
+
If the user denies camera access, the SDK shows a clear "camera access needed"
|
|
384
|
+
screen (with an **Open Settings** action) instead of hanging, and reports
|
|
385
|
+
`camera_permission_denied` to `onError`. Document capture additionally offers a
|
|
386
|
+
gallery-upload fallback unless `allowDocumentUpload` is `false`.
|
|
387
|
+
|
|
388
|
+
### Liveness quality guards
|
|
389
|
+
|
|
390
|
+
- **Multiple faces** — if more than one face is in frame, the challenge pauses
|
|
391
|
+
("Make sure only your face is visible") and resumes automatically when only one
|
|
392
|
+
face remains. This guards capture quality and a class of spoofing.
|
|
393
|
+
- **Lighting** — too-dark *and* too-bright (glare) conditions are detected live
|
|
394
|
+
during liveness; the SDK shows guidance ("Move to a brighter area" / "Too bright
|
|
395
|
+
— reduce glare") and blocks auto-capture until lighting is acceptable.
|
|
396
|
+
|
|
139
397
|
## Liveness
|
|
140
398
|
|
|
141
399
|
Active, challenge-based liveness (nod / turn / blink / smile — 2 randomly chosen
|
|
@@ -147,18 +405,14 @@ per session). Face detection runs **on-device, natively**:
|
|
|
147
405
|
simulators).
|
|
148
406
|
|
|
149
407
|
Both run as a [react-native-vision-camera](https://github.com/mrousavy/react-native-vision-camera)
|
|
150
|
-
frame
|
|
151
|
-
is **auto-captured** once challenges pass (anti-spoofing
|
|
152
|
-
video is recorded and uploaded best-effort.
|
|
408
|
+
v5 Nitro frame processor: the camera frame never crosses the JS bridge. The selfie
|
|
409
|
+
is **auto-captured** once challenges pass (anti-spoofing — never user-triggered),
|
|
410
|
+
and a short liveness video is recorded and uploaded best-effort.
|
|
153
411
|
|
|
154
|
-
##
|
|
412
|
+
## Documentation
|
|
155
413
|
|
|
156
|
-
|
|
157
|
-
(`status: 'pending'`). `onError(KYCError)` fires for **technical** errors only
|
|
158
|
-
(`network_error`, `invalid_api_key`, `insufficient_credits`, `upload_failed`,
|
|
159
|
-
`camera_permission_denied`, `feature_disabled`). Verification *outcomes* arrive
|
|
160
|
-
asynchronously via webhook / `GET /api/kyc/status/:id` — never through `onError`.
|
|
414
|
+
Full documentation, configuration options, and webhook setup: **[identity.myaza.co/documentation/sdks](https://identity.myaza.co/documentation/sdks)**.
|
|
161
415
|
|
|
162
416
|
## License
|
|
163
417
|
|
|
164
|
-
|
|
418
|
+
MIT © Flitstack Technologies Inc.
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ export type DeviceType = 'mobile' | 'tablet' | 'desktop' | 'unknown';
|
|
|
14
14
|
* Single source of truth for the SDK version — also used by `services/api.ts`
|
|
15
15
|
* for the `X-SDK-Version` header. Keep in sync with `package.json`.
|
|
16
16
|
*/
|
|
17
|
-
export const SDK_VERSION = '2.0.
|
|
17
|
+
export const SDK_VERSION = '2.0.1';
|
|
18
18
|
|
|
19
19
|
export interface ReactNativeDeviceMetadata {
|
|
20
20
|
sdkType: 'react-native';
|