Ouvrez la capture depuis une application React Native avec un module natif.
Votre back-end crée le processus dans Human Plugin avec un flux Journey et un callbackUri qui utilise le scheme d'URL de votre application. Un module natif ouvre le link de la réponse dans ASWebAuthenticationSession sur iOS et dans Custom Tabs sur Android. Lorsque la capture se termine, le module résout sa promesse et l'application reprend la main.
Conservez l'id Human Plugin de la réponse de création. Votre back-end lit le résultat avec cet id.
Prérequis
- React Native 0.71 ou version ultérieure
- Xcode 14 ou version ultérieure pour iOS
- Android Studio pour Android
- Un scheme d'URL propre à votre application, tel que
myapp
1. Créer le processus avec votre scheme
Votre back-end envoie le scheme comme callbackUri :
{
"flow": "humantoken",
"clientId": "client-a",
"correlationId": "order-1001",
"credential": { "type": "DUI_TYPE_NG_BVN", "value": "<document number>" },
"callbackUri": "myapp://callback"
}
et renvoie id et link à l'application.
2. Créer le bridge TypeScript
// src/modules/CaptureModule.ts
import { NativeModules } from 'react-native';
const { CaptureModule } = NativeModules;
if (!CaptureModule) {
throw new Error('CaptureModule not found. Make sure the native module is linked correctly.');
}
export const Capture = {
openSession: (url: string): Promise<string> => CaptureModule.openSession(url),
};
3. Implémenter le module iOS
- Bridge Objective-C
- Implémentation Swift
// ios/CaptureModule.m
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(CaptureModule, NSObject)
RCT_EXTERN_METHOD(openSession:(NSString *)url
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
@end
// ios/CaptureModule.swift
import AuthenticationServices
@objc(CaptureModule)
class CaptureModule: NSObject, ASWebAuthenticationPresentationContextProviding {
private var session: ASWebAuthenticationSession?
@objc func openSession(_ url: String,
resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
guard let sessionURL = URL(string: url) else {
reject("INVALID_URL", "Invalid URL", nil)
return
}
DispatchQueue.main.async {
let newSession = ASWebAuthenticationSession(
url: sessionURL,
callbackURLScheme: "myapp"
) { callbackURL, error in
self.session = nil
if let callbackURL = callbackURL {
resolve(callbackURL.absoluteString)
} else {
reject("SESSION_ERROR", error?.localizedDescription ?? "Session cancelled", error)
}
}
newSession.presentationContextProvider = self
newSession.prefersEphemeralWebBrowserSession = true
newSession.start()
self.session = newSession
}
}
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first else {
return ASPresentationAnchor()
}
return window
}
@objc static func requiresMainQueueSetup() -> Bool { return true }
}
4. Implémenter le module Android
Ajoutez la dépendance à android/app/build.gradle :
implementation("androidx.browser:browser:1.5.0")
// android/app/src/main/java/<your.package>/CaptureModule.kt
package com.yourapp
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import com.facebook.react.bridge.*
class CaptureModule(private val reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
private var pendingPromise: Promise? = null
override fun getName() = "CaptureModule"
@ReactMethod
fun openSession(url: String, promise: Promise) {
pendingPromise = promise
currentActivity?.runOnUiThread {
CustomTabsIntent.Builder().build()
.launchUrl(currentActivity!!, Uri.parse(url))
} ?: promise.reject("NO_ACTIVITY", "No current activity")
}
fun handleRedirectUrl(uri: Uri) {
if (uri.scheme == "myapp") {
pendingPromise?.resolve(uri.toString())
pendingPromise = null
}
}
}
// CapturePackage.kt
package com.yourapp
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class CapturePackage : ReactPackage {
override fun createNativeModules(ctx: ReactApplicationContext): List<NativeModule> =
listOf(CaptureModule(ctx))
override fun createViewManagers(ctx: ReactApplicationContext): List<ViewManager<*, *>> =
emptyList()
}
Enregistrez-le dans MainApplication.kt :
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(CapturePackage())
}
Gérez le retour dans MainActivity.kt :
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
intent.data?.let { uri ->
if (uri.scheme == "myapp") {
val module = reactInstanceManager
.currentReactContext
?.getNativeModule(CaptureModule::class.java)
module?.handleRedirectUrl(uri)
}
}
}
5. Enregistrer votre scheme sur Android
Dans AndroidManifest.xml :
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
<activity android:name=".MainActivity" ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="callback" />
</intent-filter>
</activity>
6. Démarrer la capture
import { Capture } from './modules/CaptureModule';
try {
await Capture.openSession(link);
// La capture est terminée. Demandez à votre back-end de lire le résultat
// avec GET /v1/processes/{id}, en utilisant l'id Human Plugin.
} catch {
// La personne a fermé la session.
}
Récapitulatif
| Fichier | Rôle |
|---|---|
src/modules/CaptureModule.ts | Bridge TypeScript |
ios/CaptureModule.m | Déclaration Objective-C |
ios/CaptureModule.swift | iOS : ASWebAuthenticationSession |
android/.../CaptureModule.kt | Android : Custom Tabs |
android/.../CapturePackage.kt | Enregistrement du package |
android/.../MainApplication.kt | Ajoute le package |
android/.../MainActivity.kt | Gère le retour |
AndroidManifest.xml | Intent filter |