> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Guide d'intégration mobile

> Guide unifié pour intégrer Dodo Payments dans des applications mobiles Android, iOS, React Native et Flutter.

<CardGroup cols={2}>
  <Card title="Quick Start" icon="rocket" href="#integration-workflow">
    Lancez votre intégration de paiement mobile en 4 étapes simples
  </Card>

  <Card title="Platform Examples" icon="code" href="#platform-specific-integration">
    Exemples de code complets pour Android, iOS, React Native et Flutter
  </Card>
</CardGroup>

## Prérequis

Avant d'intégrer Dodo Payments dans votre application mobile, assurez-vous d'avoir :

* **Compte Dodo Payments** : Compte marchand actif avec accès API
* **Identifiants API** : Clé API et clé secrète de webhook depuis votre tableau de bord
* **Projet d'application mobile** : Application Android, iOS, React Native ou Flutter
* **Serveur backend** : Pour gérer en toute sécurité la création de sessions de paiement

## Flux d'intégration

L'intégration mobile suit un processus sécurisé en 4 étapes où votre backend gère les appels API et votre application mobile gère l'expérience utilisateur.

<Steps>
  <Step title="Backend: Create Checkout Session">
    <Card title="Checkout Session API Docs" icon="book" href="/developer-resources/checkout-session">
      Découvrez comment créer une session de paiement dans votre backend en utilisant Node.js, Python et plus encore. Consultez des exemples complets et les références des paramètres dans la documentation dédiée <strong>Checkout Sessions API</strong>.
    </Card>

    <Note>
      **Sécurité** : les sessions de paiement doivent être créées sur votre serveur backend, jamais dans l’application mobile. Cela protège vos clés API et assure une validation correcte.
    </Note>
  </Step>

  <Step title="Mobile: Get Checkout URL">
    Votre application mobile appelle votre backend pour obtenir l'URL de paiement :

    <Tabs>
      <Tab title="iOS (Swift)">
        ```swift theme={null}
        func getCheckoutURL(productId: String, customerEmail: String, customerName: String) async throws -> String {
            let url = URL(string: "https://your-backend.com/api/create-checkout-session")!
            var request = URLRequest(url: url)
            request.httpMethod = "POST"
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
            
            let requestData: [String: Any] = [
                "productId": productId,
                "customerEmail": customerEmail,
                "customerName": customerName
            ]
            request.httpBody = try JSONSerialization.data(withJSONObject: requestData)
            
            let (data, _) = try await URLSession.shared.data(for: request)
            let response = try JSONDecoder().decode(CheckoutResponse.self, from: data)
            return response.checkout_url
        }
        ```
      </Tab>

      <Tab title="Android (Kotlin)">
        ```kotlin theme={null}
        suspend fun getCheckoutURL(productId: String, customerEmail: String, customerName: String): String {
            val client = OkHttpClient()
            val requestBody = JSONObject().apply {
                put("productId", productId)
                put("customerEmail", customerEmail)
                put("customerName", customerName)
            }.toString().toRequestBody("application/json".toMediaType())
            
            val request = Request.Builder()
                .url("https://your-backend.com/api/create-checkout-session")
                .post(requestBody)
                .build()
            
            val response = client.newCall(request).execute()
            val responseBody = response.body?.string()
            val jsonResponse = JSONObject(responseBody ?: "")
            return jsonResponse.getString("checkout_url")
        }
        ```
      </Tab>

      <Tab title="React Native (JavaScript)">
        ```javascript theme={null}
        const getCheckoutURL = async (productId, customerEmail, customerName) => {
          try {
            const response = await fetch('https://your-backend.com/api/create-checkout-session', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                productId,
                customerEmail,
                customerName
              })
            });
            
            const data = await response.json();
            return data.checkout_url;
          } catch (error) {
            console.error('Failed to get checkout URL:', error);
            throw error;
          }
        };
        ```
      </Tab>
    </Tabs>

    <Note>
      **Sécurité** : les applications mobiles ne communiquent qu'avec votre backend, jamais directement avec l'API Dodo Payments.
    </Note>
  </Step>

  <Step title="Mobile: Open Checkout in Browser">
    Ouvrez l'URL de paiement dans un navigateur intégré sécurisé pour le traitement.

    <Card title="See platform-specific integration examples" icon="box" href="#platform-specific-integration">
      Consultez le code complet et les instructions d'installation pour les paiements mobiles Android, iOS et Flutter.
    </Card>
  </Step>

  <Step title="Backend: Handle Payment Completion">
    Traitez la finalisation du paiement via des webhooks et des URL de redirection pour confirmer le statut du paiement.
  </Step>
</Steps>

## Intégration spécifique à la plateforme

Choisissez votre plateforme mobile ci-dessous pour des exemples d'implémentation complets :

<Tabs>
  <Tab title="Android">
    ### Intégration Android

    #### Mise en œuvre des onglets personnalisés Chrome

    ```kotlin theme={null}
    // Add Chrome Custom Tabs dependency to build.gradle
    implementation 'androidx.browser:browser:1.5.0'

    // In your Activity
    class PaymentActivity : AppCompatActivity() {
        private var customTabsSession: CustomTabsSession? = null
        private var customTabsClient: CustomTabsClient? = null
        private var customTabsServiceConnection: CustomTabsServiceConnection? = null

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            // Initialize Custom Tabs
            customTabsServiceConnection = object : CustomTabsServiceConnection() {
                override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) {
                    customTabsClient = client
                    customTabsClient?.warmup(0L)
                    customTabsSession = customTabsClient?.newSession(object : CustomTabsCallback() {
                        override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {
                            // Handle navigation events
                        }
                    })
                }
                override fun onServiceDisconnected(name: ComponentName) {
                    customTabsClient = null
                }
            }
            CustomTabsClient.bindCustomTabsService(
                this,
                "com.android.chrome",
                customTabsServiceConnection!!
            )
            // Get checkout URL from backend and launch
            lifecycleScope.launch {
                try {
                    val checkoutURL = getCheckoutURL("prod_123", "customer@example.com", "Customer Name")
                    val customTabsIntent = CustomTabsIntent.Builder(customTabsSession)
                        .build()
                    customTabsIntent.launchUrl(this@PaymentActivity, Uri.parse(checkoutURL))
                } catch (e: Exception) {
                    // Handle error
                    Log.e("PaymentActivity", "Failed to get checkout URL", e)
                }
            }
        }
    }
    ```
  </Tab>

  <Tab title="iOS">
    ### Intégration iOS

    #### Mise en œuvre de SFSafariViewController

    ```swift theme={null}
    import SafariServices
    class PaymentViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            Task {
                do {
                    let checkoutURL = try await getCheckoutURL(
                        productId: "prod_123",
                        customerEmail: "customer@example.com",
                        customerName: "Customer Name"
                    )
                    if let url = URL(string: checkoutURL) {
                        let safariVC = SFSafariViewController(url: url)
                        present(safariVC, animated: true, completion: nil)
                    }
                } catch {
                    // Handle error
                    print("Failed to get checkout URL: \(error)")
                }
            }
        }
    }
    ```

    #### Gestion des liens profonds

    Configurez les schémas d'URL pour gérer les redirections de finalisation de paiement :

    **1. Configurez les schémas d'URL dans Info.plist :**

    ```xml theme={null}
    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleURLName</key>
        <string>myapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
          <string>myapp</string>
        </array>
      </dict>
    </array>
    ```

    **2. Gérez les redirections dans AppDelegate :**

    ```swift theme={null}
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        if url.scheme == "myapp" && url.host == "payment-status" {
            NotificationCenter.default.post(name: .paymentCompleted, object: url)
            return true
        }
        return false
    }
    ```

    **3. Écoutez la finalisation du paiement :**

    ```swift theme={null}
    NotificationCenter.default.addObserver(self, selector: #selector(handlePaymentCompletion(_:)), name: .paymentCompleted, object: nil)

    @objc func handlePaymentCompletion(_ notification: Notification) {
        if let url = notification.object as? URL {
            safariVC?.dismiss(animated: true)
            // Parse payment status from URL parameters
            let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
            let paymentId = components?.queryItems?.first(where: { $0.name == "payment_id" })?.value
            let status = components?.queryItems?.first(where: { $0.name == "status" })?.value
            // Handle payment completion
        }
    }
    ```
  </Tab>

  <Tab title="React Native">
    ### Intégration React Native

    #### Utilisation des liens de paiement (InAppBrowser)

    Ouvrez la page de paiement hébergée par Dodo dans un navigateur intégré.

    ##### Mise en œuvre de InAppBrowser

    ```javascript theme={null}
    import React from 'react';
    import { View, TouchableOpacity, Text, Alert } from 'react-native';
    import { InAppBrowser } from 'react-native-inappbrowser-reborn';

    const PaymentButton = ({ productId, onPaymentComplete }) => {
      const openPaymentLink = async () => {
        try {
          // Get checkout URL from backend
          const checkoutURL = await getCheckoutURL(
            productId,
            'customer@example.com',
            'Customer Name'
          );
          
          const result = await InAppBrowser.open(checkoutURL, {
            // iOS options
            dismissButtonStyle: 'cancel',
            preferredBarTintColor: '#453AA4',
            preferredControlTintColor: 'white',
            readerMode: false,
            animated: true,
            modalPresentationStyle: 'fullScreen',
            modalTransitionStyle: 'coverVertical',
            modalEnabled: true,
            enableBarCollapsing: false,
            // Android options
            showTitle: true,
            toolbarColor: '#453AA4',
            secondaryToolbarColor: 'black',
            navigationBarColor: 'black',
            navigationBarDividerColor: 'white',
            enableUrlBarHiding: true,
            enableDefaultShare: true,
            forceCloseOnRedirection: false,
            animations: {
              startEnter: 'slide_in_right',
              startExit: 'slide_out_left',
              endEnter: 'slide_in_left',
              endExit: 'slide_out_right'
            },
            headers: {
              'my-custom-header': 'my custom header value'
            }
          });

          // Handle payment completion based on result
          if (result.type === 'dismiss') {
            // User dismissed the browser
            console.log('Payment browser dismissed');
          }
        } catch (error) {
          Alert.alert('Error', 'Failed to open payment page');
          console.error('InAppBrowser error:', error);
        }
      };

      return (
        <View style={{ padding: 20 }}>
          <TouchableOpacity
            style={{
              backgroundColor: '#007AFF',
              padding: 15,
              borderRadius: 8,
              alignItems: 'center'
            }}
            onPress={openPaymentLink}
          >
            <Text style={{ color: 'white', fontSize: 16, fontWeight: 'bold' }}>
              Pay Now
            </Text>
          </TouchableOpacity>
        </View>
      );
    };

    export default PaymentButton;
    ```

    #### Gestion des liens profonds pour les rappels de paiement

    ```javascript theme={null}
    // App.js or your main component
    import React, { useEffect } from 'react';
    import { Linking } from 'react-native';

    const App = () => {
      useEffect(() => {
        // Handle deep links when app is already running
        const handleDeepLink = (url) => {
          if (url.includes('payment-status')) {
            const urlParams = new URLSearchParams(url.split('?')[1]);
            const paymentId = urlParams.get('payment_id');
            const status = urlParams.get('status');
            
            // Handle payment completion
            handlePaymentCallback(paymentId, status);
          }
        };

        // Listen for deep links
        const subscription = Linking.addEventListener('url', ({ url }) => {
          handleDeepLink(url);
        });

        // Handle deep link if app was opened from a link
        Linking.getInitialURL().then((url) => {
          if (url) {
            handleDeepLink(url);
          }
        });

        return () => subscription?.remove();
      }, []);

      const handlePaymentCallback = (paymentId, status) => {
        if (status === 'succeeded') {
          // Navigate to success screen or show success message
          console.log('Payment successful:', paymentId);
        } else {
          // Handle payment failure
          console.log('Payment failed:', paymentId);
        }
      };

      // Your app components...
    };

    export default App;
    ```

    #### Dépendances de package

    Add this dependency to your `package.json` :

    ```json theme={null}
    {
      "dependencies": {
        "react-native-inappbrowser-reborn": "^6.4.0"
      }
    }
    ```

    #### Commandes d'installation

    ```bash theme={null}
    # Install InAppBrowser
    npm install react-native-inappbrowser-reborn
    cd ios && pod install
    ```

    #### Configuration Android

    Add to `android/app/src/main/AndroidManifest.xml` :

    ```xml theme={null}
    <activity
      android:name="com.reactnativeinappbrowserreborn.InAppBrowserActivity"
      android:theme="@style/Theme.AppCompat.Dialog"
      android:exported="false" />
    ```

    #### Configuration iOS

    Add to `ios/YourApp/Info.plist` :

    ```xml theme={null}
    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleURLName</key>
        <string>myapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
          <string>myapp</string>
        </array>
      </dict>
    </array>
    ```

    <Tip>
      Utilisez InAppBrowser pour une expérience de navigateur native. Gérez les liens profonds pour les retours de paiement.
    </Tip>

    ***

    #### Alternative : SDK de paiement natif

    Pour une expérience de paiement plus intégrée avec une interface utilisateur native et une personnalisation complète, Dodo Payments propose un SDK React Native.

    <Warning>
      **Conformité régionale** : les SDK de paiement intégrés sont soumis à des restrictions régionales. Sur **iOS**, les SDK de paiement natifs ne sont légaux que dans l'**UE** (conformément aux conditions DMA). Sur **Android**, ils sont légaux dans les marchés User Choice Billing (UCB). Pour les autres régions, utilisez l'approche des liens de paiement ci-dessus afin de garantir la conformité avec l'App Store et le Play Store.
    </Warning>

    <Card title="React Native SDK Integration Guide" icon="mobile-screen" href="/developer-resources/react-native-integration">
      Guide complet pour intégrer le SDK React Native de Dodo avec des feuilles de paiement natives, des options de personnalisation complètes et la gestion directe des paiements. Comprend les instructions d'installation, de mise en œuvre et de test.
    </Card>

    <Info>
      Review the [App Store Fee Bypass Guide](/features/bypassing-app-store-fees) to understand regional compliance requirements for in-app payment integrations.
    </Info>
  </Tab>

  <Tab title="Flutter">
    ### Intégration Flutter

    ```dart theme={null}
    import 'package:flutter/material.dart';
    import 'package:webview_flutter/webview_flutter.dart';
    import 'package:http/http.dart' as http;
    import 'dart:convert';

    Future<String> getCheckoutURL(String productId, String customerEmail, String customerName) async {
      final response = await http.post(
        Uri.parse('https://your-backend.com/api/create-checkout-session'),
        headers: {'Content-Type': 'application/json'},
        body: json.encode({
          'productId': productId,
          'customerEmail': customerEmail,
          'customerName': customerName,
        }),
      );
      
      if (response.statusCode == 200) {
        final data = json.decode(response.body);
        return data['checkout_url'];
      } else {
        throw Exception('Failed to get checkout URL: ${response.statusCode}');
      }
    }
    class PaymentScreen extends StatefulWidget {
      @override
      _PaymentScreenState createState() => _PaymentScreenState();
    }
    class _PaymentScreenState extends State<PaymentScreen> {
      late WebViewController _controller;
      String? checkoutURL;
      
      @override
      void initState() {
        super.initState();
        _getCheckoutURL();
      }
      
      Future<void> _getCheckoutURL() async {
        try {
          final url = await getCheckoutURL('prod_123', 'customer@example.com', 'Customer Name');
          setState(() {
            checkoutURL = url;
          });
        } catch (e) {
          // Handle error
          print('Failed to get checkout URL: $e');
        }
      }
      
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text('Payment')),
          body: checkoutURL == null 
            ? Center(child: CircularProgressIndicator())
            : WebView(
            initialUrl: checkoutURL,
            javascriptMode: JavascriptMode.unrestricted,
            onWebViewCreated: (WebViewController webViewController) {
              _controller = webViewController;
            },
            navigationDelegate: (NavigationRequest request) {
              if (request.url.contains('payment_id') && request.url.contains('status')) {
                // Handle payment completion
                final uri = Uri.parse(request.url);
                final paymentId = uri.queryParameters['payment_id'];
                final status = uri.queryParameters['status'];
                handlePaymentCompletion(paymentId, status);
                return NavigationDecision.prevent;
              }
              return NavigationDecision.navigate;
            },
          ),
        );
      }
      void handlePaymentCompletion(String? paymentId, String? status) {
        if (status == 'succeeded') {
          // Payment successful
        } else {
          // Payment failed
        }
      }
    }
    ```

    <Tip>
      Utilisez WebView pour les liens de paiement. Gérez l'URL de redirection pour détecter la finalisation du paiement.
    </Tip>
  </Tab>
</Tabs>

## Meilleures pratiques

* **Sécurité** : Ne stockez jamais les clés API dans le code de votre application. Utilisez un stockage sécurisé et le pinning SSL.
* **Expérience utilisateur** : Affichez des indicateurs de chargement, gérez les erreurs avec soin et fournissez des messages clairs.
* **Tests** : Utilisez des cartes de test, simulez des erreurs réseau et testez sur divers appareils.

## Dépannage

### Problèmes courants

* **WebView n'ouvre pas le lien de paiement** : Assurez-vous que le lien de paiement est valide et utilise HTTPS.
* **Rappel non reçu** : Vérifiez votre URL de retour et la configuration du webhook.
* **Erreurs de clé API** : Vérifiez que votre clé API est correcte et dispose des autorisations nécessaires.

## Ressources supplémentaires

* [Guide d'Intégration de Paiement](/developer-resources/integration-guide)
* [Documentation des Webhooks](/developer-resources/webhooks/intents/webhook-events-guide)
* [Processus de Test](/miscellaneous/testing-process)
* [FAQ Techniques](/miscellaneous/faq)

<Check>
  Pour toute question ou assistance, contactez <a href="mailto:support@dodopayments.com">[support@dodopayments.com](mailto:support@dodopayments.com)</a>.
</Check>
