Applications mobiles (iOS et Android)
tryVidimus n'a pas de SDK natif : la page de capture est une page web, et votre application ne fait que l'héberger. Ce guide montre comment l'ouvrir dans une webview, autoriser la caméra et revenir dans votre application.
Comment ça marche
Votre serveur crée la session de capture (POST /v1/capture-sessions) et envoie la capture_url à votre application. L'application ouvre cette adresse et la personne prend les photos sur la page, avec la caméra en direct : aucun import depuis la galerie. Deux façons de l'ouvrir. Dans une webview à l'intérieur de votre application (recommandé : la personne ne quitte jamais votre application et vous pouvez attraper la fin de la capture). Ou dans le navigateur du téléphone (Safari, Chrome, ou un onglet intégré comme SFSafariViewController ou Chrome Custom Tabs) : plus simple, mais la personne revient dans votre application par un lien.
Ce dont la page de capture a besoin
Une webview système à jour (iOS 14.3 ou plus pour la caméra dans WKWebView ; un Android System WebView récent). JavaScript activé. Le stockage du DOM activé : la page garde les photos sur le téléphone pour qu'une page fermée puisse être reprise. L'autorisation de la caméra, accordée par la personne et par votre application. L'accès réseau à l'hôte de capture et au stockage de fichiers vers lequel la page envoie les photos (ne bloquez pas les autres hôtes). Testez sur un vrai téléphone : les simulateurs n'ont pas de caméra.
iOS : l'autorisation de la caméra
Ajoutez la raison d'utiliser la caméra à votre Info.plist. Sans elle, le système arrête l'application dès que la caméra est demandée. Écrivez-la dans la langue de votre application.
<key>NSCameraUsageDescription</key>
<string>Take the photos requested for your booking.</string>
iOS : une WKWebView
Ouvrez la capture_url dans une WKWebView. Accordez la caméra à la seule page de capture (iOS 15 et plus : requestMediaCapturePermissionFor) et refusez tout autre site. N'utilisez pas UIWebView, qui est supprimée.
import WebKit
final class CaptureViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
let captureHost = "capture.tryvidimus.com"
let returnHost = "app.example.com" // the site you declared
var captureURL: URL! // capture_url, received from YOUR server
override func viewDidLoad() {
super.viewDidLoad()
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
let webView = WKWebView(frame: view.bounds, configuration: config)
webView.uiDelegate = self
webView.navigationDelegate = self
view.addSubview(webView)
webView.load(URLRequest(url: captureURL))
}
// iOS 15+: the capture page may use the camera, no other site may.
func webView(_ webView: WKWebView,
requestMediaCapturePermissionFor origin: WKSecurityOrigin,
initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType,
decisionHandler: @escaping (WKPermissionDecision) -> Void) {
decisionHandler(origin.host == captureHost ? .grant : .deny)
}
// The page ends by going to your return_url: catch it and close the screen.
func webView(_ webView: WKWebView, decidePolicyFor action: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = action.request.url, url.host == returnHost,
url.path == "/bookings/4471/capture-done" {
decisionHandler(.cancel)
dismiss(animated: true) // url carries ?proof_id=…&session_id=…
return
}
decisionHandler(.allow)
}
}
Android : l'autorisation de la caméra
Déclarez la caméra dans le manifeste. L'autorisation se demande aussi à l'exécution, comme dans l'exemple ci-dessous : la WebView ne la demande pas toute seule à la personne.
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
Android : une WebView
Activez JavaScript et le stockage du DOM. Dans onPermissionRequest, vérifiez que la demande vient de l'hôte de capture, demandez la permission CAMERA à la personne si besoin, puis accordez RESOURCE_VIDEO_CAPTURE. Refusez tout le reste.
class CaptureActivity : ComponentActivity() {
private val captureHost = "capture.tryvidimus.com"
private val returnHost = "app.example.com" // the site you declared
private var pending: PermissionRequest? = null
private val askCamera =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
pending?.let {
if (granted) it.grant(arrayOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) else it.deny()
}
pending = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val webView = WebView(this)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true // the page keeps the photos to resume
webView.settings.mediaPlaybackRequiresUserGesture = false
webView.webChromeClient = object : WebChromeClient() {
override fun onPermissionRequest(request: PermissionRequest) {
if (request.origin.host != captureHost) { request.deny(); return }
runOnUiThread { pending = request; askCamera.launch(Manifest.permission.CAMERA) }
}
}
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
val url = request.url
if (url.host == returnHost && url.path == "/bookings/4471/capture-done") {
finish() // url carries proof_id and session_id
return true
}
return false
}
}
setContentView(webView)
webView.loadUrl(intent.getStringExtra("capture_url")!!) // from YOUR server
}
}
Revenir dans votre application
À la fin de la capture, la page va à la return_url que vous avez passée (4 secondes, avec un bouton Retour). Dans une webview, attrapez vous-même cette navigation (decidePolicyFor sur iOS, shouldOverrideUrlLoading sur Android), fermez l'écran et continuez dans votre application : aucun lien universel n'est nécessaire. Si vous avez ouvert le navigateur du téléphone, la personne revient par un lien universel ou un lien d'application de votre site. Dans les deux cas, la return_url doit être une adresse https d'un site que vous avez déclaré (page Sites de votre compte ou API des réglages) ; elle porte proof_id et session_id. Les adresses de schéma d'application comme monapp:// sont refusées exprès : une autre application pourrait les réclamer.
Retour depuis le navigateur : lien universel iOS
Ajoutez la capacité Associated Domains (applinks:app.example.com) et servez ce fichier depuis votre site, sans redirection. Remplacez TEAMID et l'identifiant de l'application.
// https://app.example.com/.well-known/apple-app-site-association { "applinks": { "details": [ { "appIDs": ["TEAMID.com.example.app"], "components": [{ "/": "/bookings/*/capture-done" }] } ] } }
Retour depuis le navigateur : lien d'application Android
Ajoutez ce filtre d'intention à votre activité et servez /.well-known/assetlinks.json depuis votre site, avec le nom de paquet de votre application et l'empreinte de son certificat de signature.
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="app.example.com"
android:pathPrefix="/bookings" />
</intent-filter>
React Native, Flutter, Capacitor
Les mêmes règles s'appliquent, par le composant de webview du framework. React Native (react-native-webview) : activez allowsInlineMediaPlayback, accordez la caméra sur iOS avec mediaCapturePermissionGrantType, demandez la permission CAMERA d'Android avec PermissionsAndroid et attrapez la fin avec onShouldStartLoadWithRequest. Flutter (flutter_inappwebview ou webview_flutter) : demandez la caméra avec permission_handler et accordez-la au seul hôte de capture. Le nom des options change d'une version à l'autre : testez sur un vrai téléphone, sur iOS et sur Android.
Sécurité
Créez les sessions depuis votre serveur seulement, avec votre clé d'API, et remettez à l'application la seule capture_url : le lien est valable un temps limité et prend fin avec la capture. Laissez la webview ne charger que vos hôtes, l'hôte de capture et le stockage de fichiers ; n'injectez aucun script dans la page ; n'écrivez pas la capture_url dans vos journaux ni votre analytique.
Si quelque chose ne marche pas
La page dit que la caméra est refusée : l'application n'a pas l'autorisation (Info.plist ou manifeste), ou la personne l'a refusée ; les réglages du téléphone permettent de la changer. Une image noire ou une page qui n'ouvre pas la caméra : une webview ancienne, JavaScript ou le stockage du DOM désactivé, ou le délégué iOS / onPermissionRequest Android absent. La personne reste sur la page de fin au lieu de revenir : la return_url n'a pas été passée, son site n'est pas déclaré (422 return_url_not_allowed à la création), ou l'application n'attrape pas la navigation. L'application ne sait pas que la capture est terminée : utilisez le webhook capture_session.completed, pas la redirection.
Ne mettez jamais votre clé d'API dans l'application : votre serveur crée la session de capture et remet à l'application la capture_url. Et ne vous fiez jamais au seul retour : attendez le webhook, ou lisez la preuve depuis votre serveur.
Retour dans votre application après la captureVoir la référence de l'API