omnipay-reactnative-sdk 1.2.2-beta.3 → 1.2.2-beta.6
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 +93 -43
- package/android/build.gradle +16 -15
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/com/omniretail/omnipay/OmnipayActivityPackage.java +2 -2
- package/android/src/main/java/com/omniretail/omnipay/OmnipayLivenessCameraView.java +153 -0
- package/android/src/main/java/com/omniretail/omnipay/OmnipayLivenessCameraViewManager.java +49 -0
- package/android/src/main/java/com/omniretail/omnipay/OmnipayLivenessModule.java +524 -0
- package/ios/OmnipayLivenessCameraView.h +15 -0
- package/ios/OmnipayLivenessCameraView.m +80 -0
- package/ios/OmnipayLivenessCameraViewManager.m +19 -0
- package/ios/OmnipayLivenessModule.h +38 -0
- package/ios/OmnipayLivenessModule.m +554 -0
- package/lib/commonjs/components/OmnipayProvider.js +2 -66
- package/lib/commonjs/components/OmnipayProvider.js.map +1 -1
- package/lib/commonjs/components/OmnipayView.js.map +1 -1
- package/lib/commonjs/components/biometrics/FaceVerification.js +252 -345
- package/lib/commonjs/components/biometrics/FaceVerification.js.map +1 -1
- package/lib/commonjs/components/biometrics/LivenessDetection.js +90 -198
- package/lib/commonjs/components/biometrics/LivenessDetection.js.map +1 -1
- package/lib/commonjs/components/biometrics/OmnipayLivenessCameraView.js +15 -0
- package/lib/commonjs/components/biometrics/OmnipayLivenessCameraView.js.map +1 -0
- package/lib/commonjs/components/biometrics/PermissionManager.js +279 -0
- package/lib/commonjs/components/biometrics/PermissionManager.js.map +1 -0
- package/lib/commonjs/components/biometrics/index.js +45 -0
- package/lib/commonjs/components/biometrics/index.js.map +1 -0
- package/lib/commonjs/components/biometrics/types.js +17 -0
- package/lib/commonjs/components/biometrics/types.js.map +1 -0
- package/lib/commonjs/components/views/BvnVerification.js.map +1 -1
- package/lib/commonjs/components/views/PaylaterAgreement.js.map +1 -1
- package/lib/commonjs/components/views/Registration.js.map +1 -1
- package/lib/commonjs/index.js +23 -18
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/OmnipayProvider.js +3 -67
- package/lib/module/components/OmnipayProvider.js.map +1 -1
- package/lib/module/components/OmnipayView.js.map +1 -1
- package/lib/module/components/biometrics/FaceVerification.js +254 -346
- package/lib/module/components/biometrics/FaceVerification.js.map +1 -1
- package/lib/module/components/biometrics/LivenessDetection.js +75 -197
- package/lib/module/components/biometrics/LivenessDetection.js.map +1 -1
- package/lib/module/components/biometrics/OmnipayLivenessCameraView.js +7 -0
- package/lib/module/components/biometrics/OmnipayLivenessCameraView.js.map +1 -0
- package/lib/module/components/biometrics/PermissionManager.js +272 -0
- package/lib/module/components/biometrics/PermissionManager.js.map +1 -0
- package/lib/module/components/biometrics/index.js +12 -0
- package/lib/module/components/biometrics/index.js.map +1 -0
- package/lib/module/components/biometrics/types.js +16 -0
- package/lib/module/components/biometrics/types.js.map +1 -0
- package/lib/module/components/views/BvnVerification.js.map +1 -1
- package/lib/module/components/views/PaylaterAgreement.js.map +1 -1
- package/lib/module/components/views/Registration.js.map +1 -1
- package/lib/module/index.js +5 -4
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/{src/components → components}/OmnipayProvider.d.ts +1 -1
- package/lib/typescript/components/OmnipayProvider.d.ts.map +1 -0
- package/lib/typescript/{src/components → components}/OmnipayView.d.ts +21 -20
- package/lib/typescript/components/OmnipayView.d.ts.map +1 -0
- package/lib/typescript/components/biometrics/FaceVerification.d.ts +11 -0
- package/lib/typescript/components/biometrics/FaceVerification.d.ts.map +1 -0
- package/lib/typescript/components/biometrics/LivenessDetection.d.ts +33 -0
- package/lib/typescript/components/biometrics/LivenessDetection.d.ts.map +1 -0
- package/lib/typescript/components/biometrics/OmnipayLivenessCameraView.d.ts +18 -0
- package/lib/typescript/components/biometrics/OmnipayLivenessCameraView.d.ts.map +1 -0
- package/lib/typescript/components/biometrics/PermissionManager.d.ts +58 -0
- package/lib/typescript/components/biometrics/PermissionManager.d.ts.map +1 -0
- package/lib/typescript/components/biometrics/index.d.ts +5 -0
- package/lib/typescript/components/biometrics/index.d.ts.map +1 -0
- package/lib/typescript/components/biometrics/types.d.ts +73 -0
- package/lib/typescript/components/biometrics/types.d.ts.map +1 -0
- package/lib/typescript/{src/components → components}/views/BvnVerification.d.ts +2 -1
- package/lib/typescript/components/views/BvnVerification.d.ts.map +1 -0
- package/lib/typescript/{src/components → components}/views/PaylaterAgreement.d.ts +2 -1
- package/lib/typescript/components/views/PaylaterAgreement.d.ts.map +1 -0
- package/lib/typescript/{src/components → components}/views/Registration.d.ts +2 -1
- package/lib/typescript/components/views/Registration.d.ts.map +1 -0
- package/lib/typescript/functions.d.ts.map +1 -0
- package/lib/typescript/hooks/useOmnipay.d.ts +28 -0
- package/lib/typescript/hooks/useOmnipay.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +7 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/lib/colors.d.ts.map +1 -0
- package/lib/typescript/lib/config.d.ts.map +1 -0
- package/omnipay-reactnative-sdk.podspec +32 -29
- package/package.json +16 -11
- package/src/components/OmnipayProvider.tsx +3 -106
- package/src/components/OmnipayView.tsx +1 -1
- package/src/components/biometrics/FaceVerification.tsx +291 -368
- package/src/components/biometrics/LivenessDetection.ts +113 -250
- package/src/components/biometrics/OmnipayLivenessCameraView.tsx +19 -0
- package/src/components/biometrics/PermissionManager.ts +317 -0
- package/src/components/biometrics/index.ts +11 -0
- package/src/components/biometrics/types.ts +86 -0
- package/src/components/views/BvnVerification.tsx +1 -1
- package/src/components/views/PaylaterAgreement.tsx +1 -1
- package/src/components/views/Registration.tsx +1 -1
- package/src/index.tsx +4 -15
- package/android/src/main/java/com/omniretail/omnipay/LivenessCameraViewManager.java +0 -116
- package/android/src/main/java/com/omniretail/omnipay/LivenessDetectionModule.java +0 -588
- package/ios/LivenessCameraView.h +0 -22
- package/ios/LivenessCameraView.m +0 -135
- package/ios/LivenessCameraViewManager.h +0 -12
- package/ios/LivenessCameraViewManager.m +0 -24
- package/ios/LivenessDetectionModule.h +0 -46
- package/ios/LivenessDetectionModule.m +0 -603
- package/lib/commonjs/components/biometrics/LivenessCameraView.js +0 -45
- package/lib/commonjs/components/biometrics/LivenessCameraView.js.map +0 -1
- package/lib/module/components/biometrics/LivenessCameraView.js +0 -39
- package/lib/module/components/biometrics/LivenessCameraView.js.map +0 -1
- package/lib/typescript/demo/src/App.d.ts +0 -3
- package/lib/typescript/demo/src/App.d.ts.map +0 -1
- package/lib/typescript/demo/src/Body.d.ts +0 -3
- package/lib/typescript/demo/src/Body.d.ts.map +0 -1
- package/lib/typescript/demo/src/NotificationsExample.d.ts +0 -4
- package/lib/typescript/demo/src/NotificationsExample.d.ts.map +0 -1
- package/lib/typescript/src/components/OmnipayProvider.d.ts.map +0 -1
- package/lib/typescript/src/components/OmnipayView.d.ts.map +0 -1
- package/lib/typescript/src/components/biometrics/FaceVerification.d.ts +0 -12
- package/lib/typescript/src/components/biometrics/FaceVerification.d.ts.map +0 -1
- package/lib/typescript/src/components/biometrics/LivenessCameraView.d.ts +0 -22
- package/lib/typescript/src/components/biometrics/LivenessCameraView.d.ts.map +0 -1
- package/lib/typescript/src/components/biometrics/LivenessDetection.d.ts +0 -73
- package/lib/typescript/src/components/biometrics/LivenessDetection.d.ts.map +0 -1
- package/lib/typescript/src/components/views/BvnVerification.d.ts.map +0 -1
- package/lib/typescript/src/components/views/PaylaterAgreement.d.ts.map +0 -1
- package/lib/typescript/src/components/views/Registration.d.ts.map +0 -1
- package/lib/typescript/src/functions.d.ts.map +0 -1
- package/lib/typescript/src/hooks/useOmnipay.d.ts +0 -28
- package/lib/typescript/src/hooks/useOmnipay.d.ts.map +0 -1
- package/lib/typescript/src/index.d.ts +0 -8
- package/lib/typescript/src/index.d.ts.map +0 -1
- package/lib/typescript/src/lib/colors.d.ts.map +0 -1
- package/lib/typescript/src/lib/config.d.ts.map +0 -1
- package/src/components/biometrics/LivenessCameraView.tsx +0 -61
- /package/lib/typescript/{src/functions.d.ts → functions.d.ts} +0 -0
- /package/lib/typescript/{src/lib → lib}/colors.d.ts +0 -0
- /package/lib/typescript/{src/lib → lib}/config.d.ts +0 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { Platform, Alert, Linking, PermissionsAndroid } from 'react-native';
|
|
2
|
+
import { LivenessDetection } from './LivenessDetection';
|
|
3
|
+
|
|
4
|
+
export enum PermissionResult {
|
|
5
|
+
GRANTED = 'granted',
|
|
6
|
+
DENIED = 'denied',
|
|
7
|
+
BLOCKED = 'blocked',
|
|
8
|
+
UNAVAILABLE = 'unavailable',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PermissionResponse {
|
|
12
|
+
result: PermissionResult;
|
|
13
|
+
canRequest: boolean;
|
|
14
|
+
message?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class CameraPermissionManager {
|
|
18
|
+
/**
|
|
19
|
+
* Check current camera permission status
|
|
20
|
+
*/
|
|
21
|
+
static async checkCameraPermission(): Promise<PermissionResponse> {
|
|
22
|
+
try {
|
|
23
|
+
if (Platform.OS === 'android') {
|
|
24
|
+
return await this.checkAndroidCameraPermission();
|
|
25
|
+
} else {
|
|
26
|
+
// For iOS, we'll check through the native module
|
|
27
|
+
return await this.checkIOSCameraPermission();
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Error checking camera permission:', error);
|
|
31
|
+
return {
|
|
32
|
+
result: PermissionResult.UNAVAILABLE,
|
|
33
|
+
canRequest: false,
|
|
34
|
+
message: 'Unable to check camera permission',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Request camera permission with user-friendly flow
|
|
41
|
+
*/
|
|
42
|
+
static async requestCameraPermission(
|
|
43
|
+
showRationale: boolean = true
|
|
44
|
+
): Promise<PermissionResponse> {
|
|
45
|
+
try {
|
|
46
|
+
// First check current status
|
|
47
|
+
const currentStatus = await this.checkCameraPermission();
|
|
48
|
+
|
|
49
|
+
if (currentStatus.result === PermissionResult.GRANTED) {
|
|
50
|
+
return currentStatus;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (currentStatus.result === PermissionResult.BLOCKED) {
|
|
54
|
+
if (showRationale) {
|
|
55
|
+
this.showPermissionBlockedAlert();
|
|
56
|
+
}
|
|
57
|
+
return currentStatus;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Show rationale if needed
|
|
61
|
+
if (showRationale && currentStatus.result === PermissionResult.DENIED) {
|
|
62
|
+
const shouldProceed = await this.showPermissionRationale();
|
|
63
|
+
if (!shouldProceed) {
|
|
64
|
+
return {
|
|
65
|
+
result: PermissionResult.DENIED,
|
|
66
|
+
canRequest: true,
|
|
67
|
+
message: 'User declined to grant permission',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Request permission based on platform
|
|
73
|
+
if (Platform.OS === 'android') {
|
|
74
|
+
return await this.requestAndroidCameraPermission();
|
|
75
|
+
} else {
|
|
76
|
+
return await this.requestIOSCameraPermission();
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error('Error requesting camera permission:', error);
|
|
80
|
+
return {
|
|
81
|
+
result: PermissionResult.UNAVAILABLE,
|
|
82
|
+
canRequest: false,
|
|
83
|
+
message: 'Unable to request camera permission',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check Android camera permission using PermissionsAndroid
|
|
90
|
+
*/
|
|
91
|
+
private static async checkAndroidCameraPermission(): Promise<PermissionResponse> {
|
|
92
|
+
const hasPermission = await PermissionsAndroid.check(
|
|
93
|
+
PermissionsAndroid.PERMISSIONS.CAMERA
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (hasPermission) {
|
|
97
|
+
return {
|
|
98
|
+
result: PermissionResult.GRANTED,
|
|
99
|
+
canRequest: false,
|
|
100
|
+
message: 'Camera permission granted',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
result: PermissionResult.DENIED,
|
|
106
|
+
canRequest: true,
|
|
107
|
+
message: 'Camera permission not granted',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Request Android camera permission
|
|
113
|
+
*/
|
|
114
|
+
private static async requestAndroidCameraPermission(): Promise<PermissionResponse> {
|
|
115
|
+
try {
|
|
116
|
+
const granted = await PermissionsAndroid.request(
|
|
117
|
+
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
118
|
+
{
|
|
119
|
+
title: 'Camera Permission Required',
|
|
120
|
+
message:
|
|
121
|
+
'Face verification requires camera access to capture your image and perform liveness detection.',
|
|
122
|
+
buttonNeutral: 'Ask Me Later',
|
|
123
|
+
buttonNegative: 'Cancel',
|
|
124
|
+
buttonPositive: 'OK',
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
switch (granted) {
|
|
129
|
+
case PermissionsAndroid.RESULTS.GRANTED:
|
|
130
|
+
return {
|
|
131
|
+
result: PermissionResult.GRANTED,
|
|
132
|
+
canRequest: false,
|
|
133
|
+
message: 'Camera permission granted',
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
case PermissionsAndroid.RESULTS.DENIED:
|
|
137
|
+
return {
|
|
138
|
+
result: PermissionResult.DENIED,
|
|
139
|
+
canRequest: true,
|
|
140
|
+
message: 'Camera permission denied',
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
case PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN:
|
|
144
|
+
return {
|
|
145
|
+
result: PermissionResult.BLOCKED,
|
|
146
|
+
canRequest: false,
|
|
147
|
+
message: 'Camera permission permanently blocked',
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
default:
|
|
151
|
+
return {
|
|
152
|
+
result: PermissionResult.DENIED,
|
|
153
|
+
canRequest: true,
|
|
154
|
+
message: 'Camera permission denied',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error('Error requesting Android camera permission:', error);
|
|
159
|
+
return {
|
|
160
|
+
result: PermissionResult.UNAVAILABLE,
|
|
161
|
+
canRequest: false,
|
|
162
|
+
message: 'Unable to request camera permission',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Check iOS camera permission through native module
|
|
169
|
+
*/
|
|
170
|
+
private static async checkIOSCameraPermission(): Promise<PermissionResponse> {
|
|
171
|
+
try {
|
|
172
|
+
// iOS permissions are handled automatically by AVCaptureDevice
|
|
173
|
+
// We'll use the liveness detection module to check support
|
|
174
|
+
const isSupported = await LivenessDetection.isSupported();
|
|
175
|
+
|
|
176
|
+
if (isSupported) {
|
|
177
|
+
return {
|
|
178
|
+
result: PermissionResult.GRANTED,
|
|
179
|
+
canRequest: false,
|
|
180
|
+
message: 'Camera access available',
|
|
181
|
+
};
|
|
182
|
+
} else {
|
|
183
|
+
return {
|
|
184
|
+
result: PermissionResult.UNAVAILABLE,
|
|
185
|
+
canRequest: false,
|
|
186
|
+
message: 'Camera not available',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
return {
|
|
191
|
+
result: PermissionResult.UNAVAILABLE,
|
|
192
|
+
canRequest: false,
|
|
193
|
+
message: 'Unable to check camera availability',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Request iOS camera permission through native module
|
|
200
|
+
*/
|
|
201
|
+
private static async requestIOSCameraPermission(): Promise<PermissionResponse> {
|
|
202
|
+
// iOS camera permissions are handled automatically by the native module
|
|
203
|
+
// when startLivenessDetection is called. The system will show the permission
|
|
204
|
+
// dialog automatically if needed.
|
|
205
|
+
return {
|
|
206
|
+
result: PermissionResult.GRANTED,
|
|
207
|
+
canRequest: false,
|
|
208
|
+
message: 'iOS camera permission will be requested automatically',
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Request permission with retry logic
|
|
214
|
+
*/
|
|
215
|
+
static async requestWithRetry(
|
|
216
|
+
maxRetries: number = 2
|
|
217
|
+
): Promise<PermissionResponse> {
|
|
218
|
+
let lastResponse: PermissionResponse;
|
|
219
|
+
|
|
220
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
221
|
+
const isFirstAttempt = attempt === 0;
|
|
222
|
+
lastResponse = await this.requestCameraPermission(isFirstAttempt);
|
|
223
|
+
|
|
224
|
+
if (lastResponse.result === PermissionResult.GRANTED) {
|
|
225
|
+
return lastResponse;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (
|
|
229
|
+
lastResponse.result === PermissionResult.BLOCKED ||
|
|
230
|
+
lastResponse.result === PermissionResult.UNAVAILABLE
|
|
231
|
+
) {
|
|
232
|
+
break; // No point in retrying
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Wait a bit before retry
|
|
236
|
+
if (attempt < maxRetries) {
|
|
237
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return lastResponse!;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Show rationale dialog explaining why camera permission is needed
|
|
246
|
+
*/
|
|
247
|
+
private static showPermissionRationale(): Promise<boolean> {
|
|
248
|
+
return new Promise((resolve) => {
|
|
249
|
+
Alert.alert(
|
|
250
|
+
'Camera Permission Required',
|
|
251
|
+
'Face verification requires camera access to capture your image and perform liveness detection. This ensures secure identity verification.',
|
|
252
|
+
[
|
|
253
|
+
{
|
|
254
|
+
text: 'Not Now',
|
|
255
|
+
style: 'cancel',
|
|
256
|
+
onPress: () => resolve(false),
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
text: 'Allow Camera',
|
|
260
|
+
onPress: () => resolve(true),
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
{ cancelable: false }
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Show alert when permission is permanently blocked
|
|
270
|
+
*/
|
|
271
|
+
private static showPermissionBlockedAlert(): void {
|
|
272
|
+
Alert.alert(
|
|
273
|
+
'Camera Permission Blocked',
|
|
274
|
+
'Camera access has been blocked. To use face verification, please enable camera permission in your device settings.',
|
|
275
|
+
[
|
|
276
|
+
{
|
|
277
|
+
text: 'Cancel',
|
|
278
|
+
style: 'cancel',
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
text: 'Open Settings',
|
|
282
|
+
onPress: () => Linking.openSettings(),
|
|
283
|
+
},
|
|
284
|
+
]
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get user-friendly permission status message
|
|
290
|
+
*/
|
|
291
|
+
static getPermissionStatusMessage(result: PermissionResult): string {
|
|
292
|
+
switch (result) {
|
|
293
|
+
case PermissionResult.GRANTED:
|
|
294
|
+
return 'Camera access granted';
|
|
295
|
+
case PermissionResult.DENIED:
|
|
296
|
+
return 'Camera access denied. Tap to allow camera permission.';
|
|
297
|
+
case PermissionResult.BLOCKED:
|
|
298
|
+
return 'Camera access blocked. Please enable in device settings.';
|
|
299
|
+
case PermissionResult.UNAVAILABLE:
|
|
300
|
+
return 'Camera not available on this device';
|
|
301
|
+
default:
|
|
302
|
+
return 'Unknown permission status';
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Check if the device has camera capability
|
|
308
|
+
*/
|
|
309
|
+
static async hasCamera(): Promise<boolean> {
|
|
310
|
+
try {
|
|
311
|
+
const status = await this.checkCameraPermission();
|
|
312
|
+
return status.result !== PermissionResult.UNAVAILABLE;
|
|
313
|
+
} catch {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Export the main face verification component with liveness detection
|
|
2
|
+
export { default as FaceVerification } from './FaceVerification';
|
|
3
|
+
|
|
4
|
+
// Export the liveness detection manager
|
|
5
|
+
export { LivenessDetection } from './LivenessDetection';
|
|
6
|
+
|
|
7
|
+
// Export the native camera view
|
|
8
|
+
export { OmnipayLivenessCameraView } from './OmnipayLivenessCameraView';
|
|
9
|
+
|
|
10
|
+
// Export all types
|
|
11
|
+
export * from './types';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Liveness Detection Types
|
|
2
|
+
|
|
3
|
+
export enum LivenessChallenge {
|
|
4
|
+
SMILE = 'smile',
|
|
5
|
+
BLINK = 'blink',
|
|
6
|
+
TURN_LEFT = 'turnLeft',
|
|
7
|
+
TURN_RIGHT = 'turnRight',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface LivenessConfig {
|
|
11
|
+
/** Which challenges to include in the detection flow */
|
|
12
|
+
challenges?: LivenessChallenge[];
|
|
13
|
+
/** Timeout in seconds for each challenge (default: 10) */
|
|
14
|
+
challengeTimeout?: number;
|
|
15
|
+
/** Total timeout in seconds for entire flow (default: 60) */
|
|
16
|
+
totalTimeout?: number;
|
|
17
|
+
/** Enable debug mode with visual feedback */
|
|
18
|
+
debugMode?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface LivenessResult {
|
|
22
|
+
/** Whether all challenges were completed successfully */
|
|
23
|
+
success: boolean;
|
|
24
|
+
/** Base64 encoded final screenshot after completion */
|
|
25
|
+
screenshot?: string;
|
|
26
|
+
/** Results for each individual challenge */
|
|
27
|
+
challengeResults: ChallengeResult[];
|
|
28
|
+
/** Total time taken in milliseconds */
|
|
29
|
+
totalDuration: number;
|
|
30
|
+
/** Reason for failure if unsuccessful */
|
|
31
|
+
failureReason?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ChallengeResult {
|
|
35
|
+
/** The challenge that was performed */
|
|
36
|
+
challenge: LivenessChallenge;
|
|
37
|
+
/** Whether this specific challenge was successful */
|
|
38
|
+
success: boolean;
|
|
39
|
+
/** Time taken for this challenge in milliseconds */
|
|
40
|
+
duration: number;
|
|
41
|
+
/** Confidence score (0-1) if available */
|
|
42
|
+
confidence?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Event callback types
|
|
46
|
+
export interface LivenessEventCallbacks {
|
|
47
|
+
/** Called when a challenge starts */
|
|
48
|
+
onChallengeStart?: (challenge: LivenessChallenge) => void;
|
|
49
|
+
/** Called when a challenge is completed successfully */
|
|
50
|
+
onChallengeSuccess?: (
|
|
51
|
+
challenge: LivenessChallenge,
|
|
52
|
+
result: ChallengeResult
|
|
53
|
+
) => void;
|
|
54
|
+
/** Called when a challenge fails */
|
|
55
|
+
onChallengeFailure?: (challenge: LivenessChallenge, reason: string) => void;
|
|
56
|
+
/** Called when all challenges are completed */
|
|
57
|
+
onAllChallengesComplete?: () => void;
|
|
58
|
+
/** Called when the final screenshot is captured */
|
|
59
|
+
onScreenshotCaptured?: (screenshot: string) => void;
|
|
60
|
+
/** Called when detection is cancelled or fails */
|
|
61
|
+
onDetectionFailed?: (reason: string) => void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Native module interface
|
|
65
|
+
export interface LivenessNativeModule {
|
|
66
|
+
/** Start the liveness detection flow */
|
|
67
|
+
startLivenessDetection(config: LivenessConfig): Promise<LivenessResult>;
|
|
68
|
+
/** Stop the current detection */
|
|
69
|
+
stopDetection(): Promise<void>;
|
|
70
|
+
/** Check if the device supports liveness detection */
|
|
71
|
+
isSupported(): Promise<boolean>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Component props
|
|
75
|
+
export interface LivenessDetectionProps extends LivenessEventCallbacks {
|
|
76
|
+
/** Whether the liveness detection modal is visible */
|
|
77
|
+
visible: boolean;
|
|
78
|
+
/** Called when user closes the modal */
|
|
79
|
+
onClose: () => void;
|
|
80
|
+
/** Called when liveness detection completes successfully */
|
|
81
|
+
onSuccess: (result: LivenessResult) => void;
|
|
82
|
+
/** Configuration for the liveness detection */
|
|
83
|
+
config?: LivenessConfig;
|
|
84
|
+
/** Primary color for UI elements */
|
|
85
|
+
primaryColor?: string;
|
|
86
|
+
}
|
|
@@ -29,7 +29,7 @@ export const BvnVerification = ({
|
|
|
29
29
|
onVerificationSuccessful,
|
|
30
30
|
onClose,
|
|
31
31
|
customerRef,
|
|
32
|
-
}: OmnipayProps): JSX.Element => {
|
|
32
|
+
}: OmnipayProps): React.JSX.Element => {
|
|
33
33
|
const webviewRef = useRef<WebView>(null);
|
|
34
34
|
const [webviewStatus, setWebviewStatus] = useState<Status>('loading');
|
|
35
35
|
const webHost = getWebHost();
|
|
@@ -41,7 +41,7 @@ export const PaylaterAgreement = ({
|
|
|
41
41
|
feesId,
|
|
42
42
|
sublimitId,
|
|
43
43
|
onClose,
|
|
44
|
-
}: OmnipayProps): JSX.Element => {
|
|
44
|
+
}: OmnipayProps): React.JSX.Element => {
|
|
45
45
|
const webviewRef = useRef<WebView>(null);
|
|
46
46
|
const [webviewStatus, setWebviewStatus] = useState<Status>('loading');
|
|
47
47
|
const webHost = getWebHost();
|
|
@@ -39,7 +39,7 @@ export const Registration = ({
|
|
|
39
39
|
phoneNumber,
|
|
40
40
|
onRegistrationSuccessful,
|
|
41
41
|
onClose,
|
|
42
|
-
}: OmnipayProps): JSX.Element => {
|
|
42
|
+
}: OmnipayProps): React.JSX.Element => {
|
|
43
43
|
const webviewRef = useRef<WebView>(null);
|
|
44
44
|
const [webviewStatus, setWebviewStatus] = useState<Status>('loading');
|
|
45
45
|
const webHost = getWebHost();
|
package/src/index.tsx
CHANGED
|
@@ -3,21 +3,10 @@ import Omnipay from './components/OmnipayView';
|
|
|
3
3
|
export { OmnipayProvider } from './components/OmnipayProvider';
|
|
4
4
|
export { useOmnipay } from './hooks/useOmnipay';
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
export {
|
|
8
|
-
LivenessDetection,
|
|
9
|
-
LivenessConstants,
|
|
10
|
-
type LivenessDetectionConfig,
|
|
11
|
-
type ChallengeResult,
|
|
12
|
-
type LivenessResult,
|
|
13
|
-
type LivenessCallbacks,
|
|
14
|
-
} from './components/biometrics/LivenessDetection';
|
|
6
|
+
// Biometric components and utilities
|
|
7
|
+
export { FaceVerification, LivenessDetection } from './components/biometrics';
|
|
15
8
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type LivenessCameraViewProps,
|
|
19
|
-
} from './components/biometrics/LivenessCameraView';
|
|
20
|
-
|
|
21
|
-
export { default as FaceVerification } from './components/biometrics/FaceVerification';
|
|
9
|
+
// Biometric types
|
|
10
|
+
export * from './components/biometrics/types';
|
|
22
11
|
|
|
23
12
|
export default Omnipay;
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
package com.omniretail.omnipay;
|
|
2
|
-
|
|
3
|
-
import android.content.Context;
|
|
4
|
-
import android.view.View;
|
|
5
|
-
import android.widget.FrameLayout;
|
|
6
|
-
|
|
7
|
-
import androidx.annotation.NonNull;
|
|
8
|
-
import androidx.annotation.Nullable;
|
|
9
|
-
import androidx.camera.view.PreviewView;
|
|
10
|
-
|
|
11
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
12
|
-
import com.facebook.react.bridge.ReactContext;
|
|
13
|
-
import com.facebook.react.common.MapBuilder;
|
|
14
|
-
import com.facebook.react.uimanager.SimpleViewManager;
|
|
15
|
-
import com.facebook.react.uimanager.ThemedReactContext;
|
|
16
|
-
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
17
|
-
|
|
18
|
-
import java.util.Map;
|
|
19
|
-
|
|
20
|
-
public class LivenessCameraViewManager extends SimpleViewManager<FrameLayout> {
|
|
21
|
-
public static final String REACT_CLASS = "LivenessCameraView";
|
|
22
|
-
private ReactApplicationContext reactContext;
|
|
23
|
-
|
|
24
|
-
public LivenessCameraViewManager(ReactApplicationContext reactContext) {
|
|
25
|
-
this.reactContext = reactContext;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
@Override
|
|
29
|
-
@NonNull
|
|
30
|
-
public String getName() {
|
|
31
|
-
return REACT_CLASS;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
@Override
|
|
35
|
-
@NonNull
|
|
36
|
-
public FrameLayout createViewInstance(@NonNull ThemedReactContext context) {
|
|
37
|
-
FrameLayout frameLayout = new FrameLayout(context);
|
|
38
|
-
frameLayout.setLayoutParams(new FrameLayout.LayoutParams(
|
|
39
|
-
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
40
|
-
FrameLayout.LayoutParams.MATCH_PARENT
|
|
41
|
-
));
|
|
42
|
-
|
|
43
|
-
// Get the camera preview from the LivenessDetectionModule
|
|
44
|
-
try {
|
|
45
|
-
LivenessDetectionModule livenessModule = reactContext.getNativeModule(LivenessDetectionModule.class);
|
|
46
|
-
if (livenessModule != null) {
|
|
47
|
-
PreviewView previewView = livenessModule.getPreviewView();
|
|
48
|
-
if (previewView != null) {
|
|
49
|
-
// Remove from any existing parent
|
|
50
|
-
if (previewView.getParent() != null) {
|
|
51
|
-
((FrameLayout) previewView.getParent()).removeView(previewView);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
frameLayout.addView(previewView, new FrameLayout.LayoutParams(
|
|
55
|
-
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
56
|
-
FrameLayout.LayoutParams.MATCH_PARENT
|
|
57
|
-
));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
} catch (Exception e) {
|
|
61
|
-
// Handle gracefully - the camera view will be set up later
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return frameLayout;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
@ReactProp(name = "scaleType")
|
|
68
|
-
public void setScaleType(FrameLayout view, @Nullable String scaleType) {
|
|
69
|
-
// Find the PreviewView child and set scale type
|
|
70
|
-
for (int i = 0; i < view.getChildCount(); i++) {
|
|
71
|
-
View child = view.getChildAt(i);
|
|
72
|
-
if (child instanceof PreviewView) {
|
|
73
|
-
PreviewView previewView = (PreviewView) child;
|
|
74
|
-
if ("fillStart".equals(scaleType)) {
|
|
75
|
-
previewView.setScaleType(PreviewView.ScaleType.FILL_START);
|
|
76
|
-
} else if ("fillCenter".equals(scaleType)) {
|
|
77
|
-
previewView.setScaleType(PreviewView.ScaleType.FILL_CENTER);
|
|
78
|
-
} else if ("fillEnd".equals(scaleType)) {
|
|
79
|
-
previewView.setScaleType(PreviewView.ScaleType.FILL_END);
|
|
80
|
-
} else if ("fitStart".equals(scaleType)) {
|
|
81
|
-
previewView.setScaleType(PreviewView.ScaleType.FIT_START);
|
|
82
|
-
} else if ("fitCenter".equals(scaleType)) {
|
|
83
|
-
previewView.setScaleType(PreviewView.ScaleType.FIT_CENTER);
|
|
84
|
-
} else if ("fitEnd".equals(scaleType)) {
|
|
85
|
-
previewView.setScaleType(PreviewView.ScaleType.FIT_END);
|
|
86
|
-
} else {
|
|
87
|
-
// Default to fill center
|
|
88
|
-
previewView.setScaleType(PreviewView.ScaleType.FILL_CENTER);
|
|
89
|
-
}
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
@Override
|
|
96
|
-
public void onDropViewInstance(@NonNull FrameLayout view) {
|
|
97
|
-
super.onDropViewInstance(view);
|
|
98
|
-
// Clean up camera resources if needed
|
|
99
|
-
try {
|
|
100
|
-
LivenessDetectionModule livenessModule = reactContext.getNativeModule(LivenessDetectionModule.class);
|
|
101
|
-
if (livenessModule != null) {
|
|
102
|
-
// The module will handle cleanup when detection stops
|
|
103
|
-
}
|
|
104
|
-
} catch (Exception e) {
|
|
105
|
-
// Handle gracefully
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
@Override
|
|
110
|
-
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
|
|
111
|
-
return MapBuilder.<String, Object>builder()
|
|
112
|
-
.put("onCameraReady", MapBuilder.of("registrationName", "onCameraReady"))
|
|
113
|
-
.put("onCameraError", MapBuilder.of("registrationName", "onCameraError"))
|
|
114
|
-
.build();
|
|
115
|
-
}
|
|
116
|
-
}
|