omnipay-reactnative-sdk 1.2.2-beta.0 → 1.2.2-beta.2
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 +45 -136
- package/android/build.gradle +13 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/omniretail/omnipay/LivenessCameraViewManager.java +116 -0
- package/android/src/main/java/com/omniretail/omnipay/LivenessDetectionModule.java +588 -0
- package/android/src/main/java/com/omniretail/omnipay/OmnipayActivityPackage.java +4 -1
- package/ios/LivenessCameraView.h +22 -0
- package/ios/LivenessCameraView.m +135 -0
- package/ios/LivenessCameraViewManager.h +12 -0
- package/ios/LivenessCameraViewManager.m +24 -0
- package/ios/LivenessDetectionModule.h +46 -0
- package/ios/LivenessDetectionModule.m +603 -0
- package/lib/commonjs/components/OmnipayProvider.js +6 -56
- package/lib/commonjs/components/OmnipayProvider.js.map +1 -1
- package/lib/commonjs/components/biometrics/FaceVerification.js +439 -0
- package/lib/commonjs/components/biometrics/FaceVerification.js.map +1 -0
- package/lib/commonjs/components/biometrics/LivenessCameraView.js +43 -0
- package/lib/commonjs/components/biometrics/LivenessCameraView.js.map +1 -0
- package/lib/commonjs/components/biometrics/LivenessDetection.js +252 -0
- package/lib/commonjs/components/biometrics/LivenessDetection.js.map +1 -0
- package/lib/commonjs/index.js +28 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/OmnipayProvider.js +6 -56
- package/lib/module/components/OmnipayProvider.js.map +1 -1
- package/lib/module/components/biometrics/FaceVerification.js +429 -0
- package/lib/module/components/biometrics/FaceVerification.js.map +1 -0
- package/lib/module/components/biometrics/LivenessCameraView.js +38 -0
- package/lib/module/components/biometrics/LivenessCameraView.js.map +1 -0
- package/lib/module/components/biometrics/LivenessDetection.js +244 -0
- package/lib/module/components/biometrics/LivenessDetection.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/components/OmnipayProvider.d.ts.map +1 -1
- package/lib/typescript/components/biometrics/FaceVerification.d.ts +12 -0
- package/lib/typescript/components/biometrics/FaceVerification.d.ts.map +1 -0
- package/lib/typescript/components/biometrics/LivenessCameraView.d.ts +22 -0
- package/lib/typescript/components/biometrics/LivenessCameraView.d.ts.map +1 -0
- package/lib/typescript/components/biometrics/LivenessDetection.d.ts +73 -0
- package/lib/typescript/components/biometrics/LivenessDetection.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +3 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/omnipay-reactnative-sdk.podspec +47 -0
- package/package.json +6 -11
- package/src/components/OmnipayProvider.tsx +8 -65
- package/src/components/biometrics/FaceVerification.tsx +484 -0
- package/src/components/biometrics/LivenessCameraView.tsx +61 -0
- package/src/components/biometrics/LivenessDetection.ts +305 -0
- package/src/index.tsx +18 -0
- package/lib/commonjs/components/FaceVerification.js +0 -755
- package/lib/commonjs/components/FaceVerification.js.map +0 -1
- package/lib/commonjs/types/faceVerification.js +0 -2
- package/lib/commonjs/types/faceVerification.js.map +0 -1
- package/lib/commonjs/types/index.js +0 -17
- package/lib/commonjs/types/index.js.map +0 -1
- package/lib/module/components/FaceVerification.js +0 -746
- package/lib/module/components/FaceVerification.js.map +0 -1
- package/lib/module/types/faceVerification.js +0 -2
- package/lib/module/types/faceVerification.js.map +0 -1
- package/lib/module/types/index.js +0 -2
- package/lib/module/types/index.js.map +0 -1
- package/lib/typescript/components/FaceVerification.d.ts +0 -10
- package/lib/typescript/components/FaceVerification.d.ts.map +0 -1
- package/lib/typescript/types/faceVerification.d.ts +0 -18
- package/lib/typescript/types/faceVerification.d.ts.map +0 -1
- package/lib/typescript/types/index.d.ts +0 -2
- package/lib/typescript/types/index.d.ts.map +0 -1
- package/src/components/FaceVerification.tsx +0 -884
- package/src/types/faceVerification.ts +0 -27
- package/src/types/index.ts +0 -1
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { NativeModules, NativeEventEmitter } from 'react-native';
|
|
2
|
+
|
|
3
|
+
const { LivenessDetection: LivenessDetectionNative } = NativeModules;
|
|
4
|
+
|
|
5
|
+
// TypeScript interfaces matching PRD specifications
|
|
6
|
+
export interface LivenessDetectionConfig {
|
|
7
|
+
challenges: ('smile' | 'blink' | 'turn_left' | 'turn_right')[];
|
|
8
|
+
timeout?: number; // Optional timeout in milliseconds
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ChallengeResult {
|
|
12
|
+
challenge: string;
|
|
13
|
+
success: boolean;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
duration?: number; // Time taken to complete challenge
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface LivenessResult {
|
|
19
|
+
success: boolean;
|
|
20
|
+
screenshot?: string; // Base64 encoded image
|
|
21
|
+
challengeResults: ChallengeResult[];
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Event callback types
|
|
26
|
+
export interface LivenessCallbacks {
|
|
27
|
+
onChallengeStart?: (challenge: string) => void;
|
|
28
|
+
onChallengeSuccess?: (challenge: string) => void;
|
|
29
|
+
onChallengeFailure?: (challenge: string, reason: string) => void;
|
|
30
|
+
onAllChallengesComplete?: (result: LivenessResult) => void;
|
|
31
|
+
onScreenshotCaptured?: (screenshot: string) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Main LivenessDetection class
|
|
35
|
+
class LivenessDetectionManager {
|
|
36
|
+
private eventEmitter: NativeEventEmitter | null = null;
|
|
37
|
+
private listeners: any[] = [];
|
|
38
|
+
private callbacks: LivenessCallbacks = {};
|
|
39
|
+
private isActive = false;
|
|
40
|
+
private challengeResults: ChallengeResult[] = [];
|
|
41
|
+
private startTime: number = 0;
|
|
42
|
+
|
|
43
|
+
constructor() {
|
|
44
|
+
if (LivenessDetectionNative) {
|
|
45
|
+
this.eventEmitter = new NativeEventEmitter(LivenessDetectionNative);
|
|
46
|
+
this.setupEventListeners();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private setupEventListeners() {
|
|
51
|
+
if (!this.eventEmitter) return;
|
|
52
|
+
|
|
53
|
+
// Challenge start event
|
|
54
|
+
this.listeners.push(
|
|
55
|
+
this.eventEmitter.addListener('onChallengeStart', (event) => {
|
|
56
|
+
console.log('Liveness: Challenge started -', event.challenge);
|
|
57
|
+
if (this.callbacks.onChallengeStart) {
|
|
58
|
+
this.callbacks.onChallengeStart(event.challenge);
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Challenge success event
|
|
64
|
+
this.listeners.push(
|
|
65
|
+
this.eventEmitter.addListener('onChallengeSuccess', (event) => {
|
|
66
|
+
console.log('Liveness: Challenge completed -', event.challenge);
|
|
67
|
+
|
|
68
|
+
// Record challenge result
|
|
69
|
+
const result: ChallengeResult = {
|
|
70
|
+
challenge: event.challenge,
|
|
71
|
+
success: true,
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
duration: Date.now() - this.startTime,
|
|
74
|
+
};
|
|
75
|
+
this.challengeResults.push(result);
|
|
76
|
+
|
|
77
|
+
if (this.callbacks.onChallengeSuccess) {
|
|
78
|
+
this.callbacks.onChallengeSuccess(event.challenge);
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Challenge failure event
|
|
84
|
+
this.listeners.push(
|
|
85
|
+
this.eventEmitter.addListener('onChallengeFailure', (event) => {
|
|
86
|
+
console.log(
|
|
87
|
+
'Liveness: Challenge failed -',
|
|
88
|
+
event.challenge,
|
|
89
|
+
event.reason
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Record failed challenge result
|
|
93
|
+
const result: ChallengeResult = {
|
|
94
|
+
challenge: event.challenge,
|
|
95
|
+
success: false,
|
|
96
|
+
timestamp: Date.now(),
|
|
97
|
+
duration: Date.now() - this.startTime,
|
|
98
|
+
};
|
|
99
|
+
this.challengeResults.push(result);
|
|
100
|
+
|
|
101
|
+
if (this.callbacks.onChallengeFailure) {
|
|
102
|
+
this.callbacks.onChallengeFailure(event.challenge, event.reason);
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// All challenges complete event
|
|
108
|
+
this.listeners.push(
|
|
109
|
+
this.eventEmitter.addListener('onAllChallengesComplete', (event) => {
|
|
110
|
+
console.log('Liveness: All challenges completed', event);
|
|
111
|
+
|
|
112
|
+
const result: LivenessResult = {
|
|
113
|
+
success: event.success,
|
|
114
|
+
screenshot: event.screenshot,
|
|
115
|
+
challengeResults: this.challengeResults,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
this.isActive = false;
|
|
119
|
+
|
|
120
|
+
if (this.callbacks.onAllChallengesComplete) {
|
|
121
|
+
this.callbacks.onAllChallengesComplete(result);
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Screenshot captured event
|
|
127
|
+
this.listeners.push(
|
|
128
|
+
this.eventEmitter.addListener('onScreenshotCaptured', (event) => {
|
|
129
|
+
console.log('Liveness: Screenshot captured');
|
|
130
|
+
|
|
131
|
+
if (this.callbacks.onScreenshotCaptured) {
|
|
132
|
+
this.callbacks.onScreenshotCaptured(event.screenshot);
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Start liveness detection with specified challenges
|
|
140
|
+
* @param config - Configuration including challenges array
|
|
141
|
+
* @param callbacks - Event callbacks
|
|
142
|
+
* @returns Promise resolving to LivenessResult
|
|
143
|
+
*/
|
|
144
|
+
async startLivenessDetection(
|
|
145
|
+
config: LivenessDetectionConfig,
|
|
146
|
+
callbacks?: LivenessCallbacks
|
|
147
|
+
): Promise<LivenessResult> {
|
|
148
|
+
if (this.isActive) {
|
|
149
|
+
throw new Error('Liveness detection is already active');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!LivenessDetectionNative) {
|
|
153
|
+
throw new Error('Liveness detection is not available on this platform');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Validate challenges
|
|
157
|
+
if (!config.challenges || config.challenges.length === 0) {
|
|
158
|
+
throw new Error('At least one challenge must be specified');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const validChallenges = ['smile', 'blink', 'turn_left', 'turn_right'];
|
|
162
|
+
for (const challenge of config.challenges) {
|
|
163
|
+
if (!validChallenges.includes(challenge)) {
|
|
164
|
+
throw new Error(`Invalid challenge: ${challenge}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
// Check camera permission first
|
|
170
|
+
const hasPermission = await this.checkCameraPermission();
|
|
171
|
+
if (!hasPermission) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
'Camera permission is required. Please call requestCameraPermission() first.'
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Set callbacks
|
|
178
|
+
this.callbacks = callbacks || {};
|
|
179
|
+
this.challengeResults = [];
|
|
180
|
+
this.startTime = Date.now();
|
|
181
|
+
this.isActive = true;
|
|
182
|
+
|
|
183
|
+
// Start detection on native side
|
|
184
|
+
const result = await LivenessDetectionNative.startLivenessDetection(
|
|
185
|
+
config.challenges
|
|
186
|
+
);
|
|
187
|
+
console.log('Liveness detection started:', result);
|
|
188
|
+
|
|
189
|
+
// Return a promise that resolves when all challenges are complete
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
const originalOnComplete = this.callbacks.onAllChallengesComplete;
|
|
192
|
+
|
|
193
|
+
this.callbacks.onAllChallengesComplete = (
|
|
194
|
+
livenessResult: LivenessResult
|
|
195
|
+
) => {
|
|
196
|
+
// Call original callback if provided
|
|
197
|
+
if (originalOnComplete) {
|
|
198
|
+
originalOnComplete(livenessResult);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Resolve the promise
|
|
202
|
+
resolve(livenessResult);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Set up timeout if specified
|
|
206
|
+
if (config.timeout) {
|
|
207
|
+
setTimeout(() => {
|
|
208
|
+
if (this.isActive) {
|
|
209
|
+
this.stopDetection();
|
|
210
|
+
reject(new Error('Liveness detection timed out'));
|
|
211
|
+
}
|
|
212
|
+
}, config.timeout);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
this.isActive = false;
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Stop liveness detection
|
|
223
|
+
*/
|
|
224
|
+
stopDetection(): void {
|
|
225
|
+
if (LivenessDetectionNative && this.isActive) {
|
|
226
|
+
LivenessDetectionNative.stopLivenessDetection();
|
|
227
|
+
this.isActive = false;
|
|
228
|
+
this.callbacks = {};
|
|
229
|
+
this.challengeResults = [];
|
|
230
|
+
console.log('Liveness detection stopped');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Check if liveness detection is currently active
|
|
236
|
+
*/
|
|
237
|
+
isDetectionActive(): boolean {
|
|
238
|
+
return this.isActive;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get available challenge types
|
|
243
|
+
*/
|
|
244
|
+
getAvailableChallenges(): string[] {
|
|
245
|
+
return ['smile', 'blink', 'turn_left', 'turn_right'];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Check if camera permission is granted
|
|
250
|
+
*/
|
|
251
|
+
async checkCameraPermission(): Promise<boolean> {
|
|
252
|
+
if (!LivenessDetectionNative) {
|
|
253
|
+
throw new Error('Liveness detection is not available on this platform');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
return await LivenessDetectionNative.checkCameraPermission();
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error('Failed to check camera permission:', error);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Request camera permission from the user
|
|
266
|
+
*/
|
|
267
|
+
async requestCameraPermission(): Promise<boolean> {
|
|
268
|
+
if (!LivenessDetectionNative) {
|
|
269
|
+
throw new Error('Liveness detection is not available on this platform');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
return await LivenessDetectionNative.requestCameraPermission();
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error('Failed to request camera permission:', error);
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Cleanup - remove all event listeners
|
|
282
|
+
*/
|
|
283
|
+
cleanup(): void {
|
|
284
|
+
this.listeners.forEach((listener) => {
|
|
285
|
+
if (listener && listener.remove) {
|
|
286
|
+
listener.remove();
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
this.listeners = [];
|
|
290
|
+
this.stopDetection();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Export singleton instance
|
|
295
|
+
export const LivenessDetection = new LivenessDetectionManager();
|
|
296
|
+
|
|
297
|
+
// Types are already exported above
|
|
298
|
+
|
|
299
|
+
// Constants from native module
|
|
300
|
+
export const LivenessConstants = {
|
|
301
|
+
CHALLENGE_SMILE: 'smile',
|
|
302
|
+
CHALLENGE_BLINK: 'blink',
|
|
303
|
+
CHALLENGE_TURN_LEFT: 'turn_left',
|
|
304
|
+
CHALLENGE_TURN_RIGHT: 'turn_right',
|
|
305
|
+
} as const;
|
package/src/index.tsx
CHANGED
|
@@ -2,4 +2,22 @@ import Omnipay from './components/OmnipayView';
|
|
|
2
2
|
|
|
3
3
|
export { OmnipayProvider } from './components/OmnipayProvider';
|
|
4
4
|
export { useOmnipay } from './hooks/useOmnipay';
|
|
5
|
+
|
|
6
|
+
// Liveness Detection exports
|
|
7
|
+
export {
|
|
8
|
+
LivenessDetection,
|
|
9
|
+
LivenessConstants,
|
|
10
|
+
type LivenessDetectionConfig,
|
|
11
|
+
type ChallengeResult,
|
|
12
|
+
type LivenessResult,
|
|
13
|
+
type LivenessCallbacks,
|
|
14
|
+
} from './components/biometrics/LivenessDetection';
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
default as LivenessCameraView,
|
|
18
|
+
type LivenessCameraViewProps,
|
|
19
|
+
} from './components/biometrics/LivenessCameraView';
|
|
20
|
+
|
|
21
|
+
export { default as FaceVerification } from './components/biometrics/FaceVerification';
|
|
22
|
+
|
|
5
23
|
export default Omnipay;
|