Passer au contenu principal

Documentation Index

Fetch the complete documentation index at: https://developers.ligdicash.com/llms.txt

Use this file to discover all available pages before exploring further.

Sur mobile, ouvrir l’URL de paiement dans le navigateur système fait quitter l’application et rend le retour utilisateur difficile à gérer. La WebView native maintient l’utilisateur dans l’app, permet de détecter automatiquement la fin du flux de paiement et de déclencher la vérification sans action supplémentaire de l’utilisateur.

WebView native vs navigateur externe

ApprocheRetour automatiqueUXRecommandé
WebView nativeOui — détection par URLFluide, reste dans l’app
SFSafariViewController / Chrome Custom TabsPartiel — deep link requisSort brièvement de l’appAcceptable
Navigateur systèmeNonQuitte l’app
SFSafariViewController (iOS) et Chrome Custom Tabs (Android) ne permettent pas d’intercepter les redirections vers vos URLs de retour. Ils ne conviennent pas pour ce cas d’usage sans mettre en place un deep link.

React Native

Dépendance : react-native-webview
npm install react-native-webview
# iOS uniquement
npx pod-install
React Native
import { useState } from "react";
import { ActivityIndicator, StyleSheet, View } from "react-native";
import { WebView } from "react-native-webview";

export function PaiementWebView({ paymentUrl, onSuccess, onCancel, onError }) {
  const [loading, setLoading] = useState(true);

  const handleNavigationChange = (navState) => {
    const { url } = navState;

    if (url.startsWith("https://monapp.com/paiement/succes")) {
      onSuccess();
    } else if (url.startsWith("https://monapp.com/paiement/annule")) {
      onCancel();
    }
  };

  return (
    <View style={styles.container}>
      <WebView
        source={{ uri: paymentUrl }}
        onNavigationStateChange={handleNavigationChange}
        onLoadStart={() => setLoading(true)}
        onLoadEnd={() => setLoading(false)}
        onError={() => onError?.()}
        javaScriptEnabled
      />
      {loading && (
        <ActivityIndicator style={styles.loader} size="large" color="#0066cc" />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  loader: { position: "absolute", alignSelf: "center", top: "50%" },
});
Remplacez https://monapp.com/paiement/succes et https://monapp.com/paiement/annule par vos return_url et cancel_url exactes passées à la création de la facture.

Flutter

Dépendance : webview_flutter
pubspec.yaml
dependencies:
  webview_flutter: ^4.0.0
Flutter
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class PaiementWebView extends StatefulWidget {
  final String paymentUrl;
  final VoidCallback onSuccess;
  final VoidCallback onCancel;

  const PaiementWebView({
    required this.paymentUrl,
    required this.onSuccess,
    required this.onCancel,
    super.key,
  });

  @override
  State<PaiementWebView> createState() => _PaiementWebViewState();
}

class _PaiementWebViewState extends State<PaiementWebView> {
  late final WebViewController _controller;
  bool _loading = true;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(NavigationDelegate(
        onPageStarted: (_) => setState(() => _loading = true),
        onPageFinished: (_) => setState(() => _loading = false),
        onNavigationRequest: (request) {
          final url = request.url;

          if (url.startsWith("https://monapp.com/paiement/succes")) {
            widget.onSuccess();
            return NavigationDecision.prevent;
          } else if (url.startsWith("https://monapp.com/paiement/annule")) {
            widget.onCancel();
            return NavigationDecision.prevent;
          }
          return NavigationDecision.navigate;
        },
      ))
      ..loadRequest(Uri.parse(widget.paymentUrl));
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebViewWidget(controller: _controller),
        if (_loading)
          const Center(child: CircularProgressIndicator()),
      ],
    );
  }
}

iOS (Swift / WKWebView)

iOS (Swift)
import UIKit
import WebKit

class PaiementViewController: UIViewController, WKNavigationDelegate {

    private var webView: WKWebView!
    var paymentUrl: URL!
    var onSuccess: (() -> Void)?
    var onCancel: (() -> Void)?

    override func viewDidLoad() {
        super.viewDidLoad()

        webView = WKWebView(frame: view.bounds)
        webView.navigationDelegate = self
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(webView)

        webView.load(URLRequest(url: paymentUrl))
    }

    func webView(
        _ webView: WKWebView,
        decidePolicyFor navigationAction: WKNavigationAction,
        decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
    ) {
        let url = navigationAction.request.url?.absoluteString ?? ""

        if url.hasPrefix("https://monapp.com/paiement/succes") {
            decisionHandler(.cancel)
            dismiss(animated: true) { self.onSuccess?() }
        } else if url.hasPrefix("https://monapp.com/paiement/annule") {
            decisionHandler(.cancel)
            dismiss(animated: true) { self.onCancel?() }
        } else {
            decisionHandler(.allow)
        }
    }
}
Présenter le ViewController :
iOS (Swift)
let vc = PaiementViewController()
vc.paymentUrl = URL(string: data.response_text)!
vc.onSuccess = {
    // fermeture effectuée automatiquement, lancer la vérification
    self.verifierPaiement()
}
vc.onCancel = {
    self.afficherMessageAnnulation()
}
present(vc, animated: true)

Android (Kotlin / WebView)

Android (Kotlin)
import android.os.Bundle
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity

class PaiementActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val paymentUrl = intent.getStringExtra("payment_url") ?: return finish()

        val webView = WebView(this).apply {
            settings.javaScriptEnabled = true
            webViewClient = object : WebViewClient() {
                override fun shouldOverrideUrlLoading(
                    view: WebView,
                    request: WebResourceRequest
                ): Boolean {
                    val url = request.url.toString()

                    return when {
                        url.startsWith("https://monapp.com/paiement/succes") -> {
                            setResult(RESULT_OK)
                            finish()
                            true
                        }
                        url.startsWith("https://monapp.com/paiement/annule") -> {
                            setResult(RESULT_CANCELED)
                            finish()
                            true
                        }
                        else -> false
                    }
                }
            }
            loadUrl(paymentUrl)
        }

        setContentView(webView)
    }
}
Démarrer l’Activity et récupérer le résultat :
Android (Kotlin)
val launcher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    when (result.resultCode) {
        Activity.RESULT_OK -> verifierPaiement()
        Activity.RESULT_CANCELED -> afficherMessageAnnulation()
    }
}

val intent = Intent(this, PaiementActivity::class.java).apply {
    putExtra("payment_url", data.response_text)
}
launcher.launch(intent)
N’oubliez pas de déclarer PaiementActivity dans votre AndroidManifest.xml :
AndroidManifest.xml
<activity android:name=".PaiementActivity" />

Après la détection du retour

Le flux est identique quelle que soit la plateforme :
1

Fermer la WebView

Dépiler ou dismisser le composant WebView pour revenir à l’écran précédent.
2

Afficher un écran de vérification

Montrez un indicateur de chargement — « Vérification du paiement en cours… » — le temps d’interroger votre backend.
3

Appeler votre backend

Votre backend appelle l’endpoint confirm avec le token stocké à la création de la facture.
4

Afficher le résultat

Selon le status retourné : confirmez la commande (completed), informez l’utilisateur (notcompleted) ou invitez-le à patienter (pending).
La détection de return_url est un signal d’interface, pas une preuve de paiement. Un utilisateur peut naviguer manuellement vers cette URL. Confirmez toujours le paiement côté serveur avant d’honorer la commande.

Pages associées