# FLOW STRIPE - DOCUMENTATION TECHNIQUE COMPLÈTE

## 🎯 Vue d'ensemble

Ce document détaille le flow complet des paiements Stripe dans l'application GEOSECTOR, incluant la création des comptes Stripe Connect pour les amicales, les paiements web et Tap to Pay via l'application Flutter.

---

## 🏛️ FLOW STRIPE CONNECT - CRÉATION COMPTE AMICALE

### 🔄 Processus de création et configuration

Le système utilise **Stripe Connect** pour permettre à chaque amicale de recevoir directement ses paiements sur son propre compte bancaire.

### 📋 Prérequis et conditions

#### Configuration requise
- **Plateforme** : Web uniquement (pas disponible sur mobile)
- **Rôle utilisateur** : Admin amicale (rôle ≥ 2) minimum
- **Statut amicale** : Amicale existante avec données complètes

#### Vérifications automatiques
```dart
// Contrôles avant activation Stripe
if (!kIsWeb) {
  // Afficher dialog "Configuration Web requise"
  return;
}

if (userRole < 2) {
  // Seuls les admins d'amicale peuvent configurer Stripe
  return;
}

if (amicale == null || amicale.id == 0) {
  // L'amicale doit exister en base
  return;
}
```

### 🔄 Diagramme de séquence - Onboarding Stripe Connect

```
┌─────────────────┐    ┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│   Admin Web     │    │   App Web   │    │   API PHP    │    │   Stripe    │
└─────────┬───────┘    └──────┬──────┘    └──────┬───────┘    └──────┬──────┘
          │                   │                  │                   │
      [1] │ Coche "CB accepté"│                  │                   │
          │──────────────────>│                  │                   │
          │                   │                  │                   │
      [2] │ Clic "Configurer" │                  │                   │
          │──────────────────>│                  │                   │
          │                   │                  │                   │
      [3] │                   │ POST /stripe/create-account         │
          │                   │─────────────────>│                   │
          │                   │ (amicale_data)   │                   │
          │                   │                  │                   │
      [4] │                   │                  │ Create Account    │
          │                   │                  │──────────────────>│
          │                   │                  │                   │
      [5] │                   │                  │<──────────────────│
          │                   │                  │ account_id        │
          │                   │                  │                   │
      [6] │                   │                  │ Create Onboarding │
          │                   │                  │──────────────────>│
          │                   │                  │                   │
      [7] │                   │                  │<──────────────────│
          │                   │                  │ onboarding_url    │
          │                   │                  │                   │
      [8] │                   │<─────────────────│                   │
          │                   │ onboarding_url   │                   │
          │                   │                  │                   │
      [9] │<──────────────────│                   │                   │
          │ Redirection Stripe│                   │                   │
          │                   │                  │                   │
     [10] │ STRIPE ONBOARDING │                   │                   │
          │ ================== │                   │                   │
          │ • Infos entreprise │                   │                   │
          │ • Infos bancaires  │                   │                   │
          │ • Vérifications    │                   │                   │
          │ ================== │                   │                   │
          │                   │                  │                   │
     [11] │ Retour application │                  │                   │
          │──────────────────>│                  │                   │
          │                   │                  │                   │
     [12] │                   │ GET /stripe/status│                   │
          │                   │─────────────────>│                   │
          │                   │                  │                   │
     [13] │                   │                  │ Retrieve Account  │
          │                   │                  │──────────────────>│
          │                   │                  │                   │
     [14] │                   │                  │<──────────────────│
          │                   │                  │ account_status    │
          │                   │                  │                   │
     [15] │                   │<─────────────────│                   │
          │                   │ status_response  │                   │
          │                   │                  │                   │
     [16] │<──────────────────│                   │                   │
          │ Affichage statut  │                   │                   │
```

### 📋 Détail des étapes

#### Étape 1-2 : ACTIVATION INTERFACE
**Acteur:** Admin amicale sur interface web
**Actions:**
- Activation de la checkbox "Accepte les règlements en CB"
- Clic sur le bouton "Configurer Stripe"
- Affichage dialog de confirmation avec informations sur le processus

#### Étape 3 : CRÉATION DU COMPTE STRIPE
**Requête:** `POST /api/stripe/create-account`
**Payload:**
```json
{
  "amicale_id": 45,
  "business_name": "Amicale des Pompiers de Paris",
  "business_type": "non_profit",
  "email": "contact@pompiers-paris.fr",
  "phone": "0145123456",
  "address": {
    "line1": "123 Rue de la Caserne",
    "postal_code": "75001",
    "city": "Paris",
    "country": "FR"
  },
  "url": "https://app3.geosector.fr/stripe/return",
  "refresh_url": "https://app3.geosector.fr/stripe/refresh"
}
```

#### Étape 4-7 : ONBOARDING STRIPE
**Processus côté API:**
```php
// 1. Création du compte Stripe Connect
$account = \Stripe\Account::create([
    'type' => 'express',
    'country' => 'FR',
    'business_type' => 'non_profit',
    'company' => [
        'name' => $amicale->name,
        'phone' => $amicale->phone,
        'address' => [...],
    ],
    'email' => $amicale->email
]);

// 2. Création du lien d'onboarding
$onboardingLink = \Stripe\AccountLink::create([
    'account' => $account->id,
    'refresh_url' => 'https://app3.geosector.fr/stripe/refresh',
    'return_url' => 'https://app3.geosector.fr/stripe/return',
    'type' => 'account_onboarding'
]);

// 3. Sauvegarde en base
$amicale->stripe_id = $account->id;
$amicale->save();

return ['onboarding_url' => $onboardingLink->url];
```

#### Étape 8-11 : ONBOARDING UTILISATEUR
**Processus côté Stripe:**
1. **Redirection** vers l'interface Stripe dédiée
2. **Collecte informations** :
   - Informations légales de l'amicale
   - Coordonnées bancaires (IBAN français)
   - Documents justificatifs si nécessaire
   - Vérification d'identité du représentant légal
3. **Validation** automatique ou manuelle par Stripe
4. **Retour** vers l'application GEOSECTOR

#### Étape 12-16 : VÉRIFICATION STATUT
**Requête:** `GET /api/stripe/status/{amicale_id}`
**Réponse:**
```json
{
  "account_id": "acct_1234567890",
  "onboarding_completed": true,
  "can_accept_payments": true,
  "capabilities": {
    "card_payments": "active",
    "transfers": "active"
  },
  "requirements": {
    "currently_due": [],
    "pending_verification": []
  },
  "status_message": "Compte actif - Prêt pour les paiements",
  "status_color": "#4CAF50"
}
```

### 🎮 Interface utilisateur et états

#### États possibles du compte Stripe

| État | Description | Interface | Actions |
|------|-------------|-----------|---------|
| **Non configuré** | Checkbox décochée | Gris | Cocher la case |
| **En cours de config** | Onboarding incomplet | Orange + ⏳ | Compléter sur Stripe |
| **Actif** | Prêt pour paiements | Vert + ✅ | Aucune action requise |
| **En attente** | Vérifications Stripe | Orange + ⚠️ | Attendre validation |
| **Rejeté** | Compte refusé | Rouge + ❌ | Contacter support |

#### Affichage dynamique

**1. CONFIGURATION NON DÉMARRÉE**
```
☐ Accepte les règlements en CB
   [Configurer Stripe]
   💳 Activez les paiements par carte bancaire pour vos membres
```

**2. CONFIGURATION EN COURS**
```
☑ Accepte les règlements en CB
   [⏳ Configuration en cours]  [⚠️ Tooltip: "Veuillez compléter..."]
   ⏳ Configuration Stripe en cours. Veuillez compléter le processus d'onboarding.
```

**3. COMPTE ACTIF**
```
☑ Accepte les règlements en CB
   [✅ Compte actif]  [✅ Tooltip: "Compte configuré"]
   ✅ Compte Stripe configuré - 100% des paiements pour votre amicale
```

### 🔐 Sécurité et conformité

#### Conformité Stripe Connect
- **PCI DSS** : Stripe gère la conformité PCI
- **KYC/AML** : Vérifications d'identité automatiques
- **Comptes séparés** : Chaque amicale a son propre compte
- **Fonds isolés** : Pas de commingling des fonds

#### Validation côté serveur
```php
// Vérifications obligatoires
if (!$user->canManageAmicale($amicaleId)) {
    throw new UnauthorizedException();
}

if (!$amicale->isComplete()) {
    throw new ValidationException('Amicale incomplète');
}

if ($amicale->stripe_id && $this->stripeService->accountExists($amicale->stripe_id)) {
    throw new ConflictException('Compte déjà existant');
}
```

### 📊 Suivi et monitoring

#### Métriques importantes
- **Taux de completion** de l'onboarding (objectif > 85%)
- **Temps moyen** de configuration (< 10 minutes)
- **Taux d'approbation** Stripe (> 95%)
- **Délai d'activation** des comptes

#### Logs et audit
```php
Log::info('Stripe onboarding started', [
    'amicale_id' => $amicaleId,
    'user_id' => $userId,
    'account_id' => $accountId
]);

Log::info('Stripe account activated', [
    'amicale_id' => $amicaleId,
    'account_id' => $accountId,
    'capabilities' => $capabilities
]);
```

---

## 📱 FLOW TAP TO PAY (Application Flutter)

### 🎯 Architecture technique

Le flow Tap to Pay repose sur trois composants principaux :

1. **DeviceInfoService** - Vérification de compatibilité hardware (Android SDK ≥ 28, NFC disponible)
2. **StripeTapToPayService** - Gestion du SDK Stripe Terminal et des paiements
3. **Backend API** - Endpoints PHP pour les tokens de connexion et PaymentIntents

### 🔄 Diagramme de séquence complet

```
┌─────────────┐     ┌─────────────────┐     ┌──────────┐     ┌─────────┐     ┌──────────────┐
│ App Flutter │     │  DeviceInfo     │     │   API    │     │  Stripe  │     │ SDK Terminal │
│             │     │  Service        │     │   PHP    │     │          │     │              │
└──────┬──────┘     └────────┬────────┘     └────┬─────┘     └────┬─────┘     └──────┬───────┘
       │                     │                    │                │                  │
   [1] │ Login utilisateur   │                    │                │                  │
       │────────────────────>│                    │                │                  │
       │                     │                    │                │                  │
   [2] │                     │ checkStripeCertification()          │                  │
       │                     │ • Android SDK ≥ 28                  │                  │
       │                     │ • NFC disponible                    │                  │
       │                     │                    │                │                  │
   [3] │<────────────────────│                    │                │                  │
       │ ✅ Compatible       │                    │                │                  │
       │                     │                    │                │                  │
   [4] │ Validation form     │                    │                │                  │
       │ + montant CB        │                    │                │                  │
       │                     │                    │                │                  │
   [5] │ POST/PUT passage    │                    │                │                  │
       │────────────────────────────────────────>│                │                  │
       │                     │                    │                │                  │
   [6] │<────────────────────────────────────────│                │                  │
       │ Passage ID: 456     │                    │                │                  │
       │                     │                    │                │                  │
   [7] │ initialize()        │                    │                │                  │
       │────────────────────────────────────────────────────────────────────────────>│
       │                     │                    │                │                  │
   [8] │                     │                    │                │  Terminal.initTerminal()
       │                     │                    │                │                  │ (fetchToken callback)
       │                     │                    │                │                  │
   [9] │                     │                    │ POST /terminal/connection-token  │
       │────────────────────────────────────────>│                │                  │
       │  {amicale_id, stripe_account, location_id}              │                  │
       │                     │                    │                │                  │
  [10] │                     │                    │ CreateConnectionToken            │
       │                     │                    │───────────────>│                  │
       │                     │                    │                │                  │
  [11] │                     │                    │<───────────────│                  │
       │                     │                    │ {secret: "..."}│                  │
       │                     │                    │                │                  │
  [12] │<────────────────────────────────────────│                │                  │
       │ Connection Token    │                    │                │                  │
       │                     │                    │                │                  │
  [13] │────────────────────────────────────────────────────────────────────────────>│
       │ Token delivered to SDK                   │                │  ✅ SDK Ready   │
       │                     │                    │                │                  │
  [14] │ createPaymentIntent()                    │                │                  │
       │────────────────────────────────────────>│                │                  │
       │  {amount, passage_id, amicale_id}        │                │                  │
       │                     │                    │                │                  │
  [15] │                     │                    │ Create PaymentIntent             │
       │                     │                    │───────────────>│                  │
       │                     │                    │                │                  │
  [16] │                     │                    │<───────────────│                  │
       │                     │                    │ pi_xxx + secret│                  │
       │                     │                    │                │                  │
  [17] │<────────────────────────────────────────│                │                  │
       │ PaymentIntent ID    │                    │                │                  │
       │                     │                    │                │                  │
  [18] │ collectPayment()    │                    │                │                  │
       │────────────────────────────────────────────────────────────────────────────>│
       │                     │                    │                │                  │
  [19] │                     │                    │                │  discoverReaders()
       │                     │                    │                │  + connectReader()
       │                     │                    │                │                  │
  [20] │                     │                    │                │  collectPaymentMethod()
       │ 💳 "Présentez la carte"                  │                │  ⏳ Attente NFC │
       │                     │                    │                │                  │
  [21] │<──────────────────────────────────────────────────────────── 📱 TAP CARTE  │
       │                     │                    │                │                  │
  [22] │ confirmPayment()    │                    │                │                  │
       │────────────────────────────────────────────────────────────────────────────>│
       │                     │                    │                │                  │
  [23] │                     │                    │                │  confirmPaymentIntent()
       │                     │                    │                │                  │
  [24] │                     │                    │                │  ✅ Succeeded   │
       │<────────────────────────────────────────────────────────────────────────────│
       │ Payment Success     │                    │                │                  │
       │                     │                    │                │                  │
  [25] │ PUT passage/456     │                    │                │                  │
       │────────────────────────────────────────>│                │                  │
       │  {stripe_payment_id: "pi_xxx"}           │                │                  │
       │                     │                    │                │                  │
  [26] │<────────────────────────────────────────│                │                  │
       │ ✅ Passage updated  │                    │                │                  │
```

### 🎮 Gestion du Terminal de Paiement

#### États du Terminal
Le terminal de paiement reste affiché jusqu'à la réponse définitive de Stripe. Il gère plusieurs états :

| État | Description | Actions disponibles |
|------|-------------|-------------------|
| `confirming` | Demande confirmation utilisateur | Annuler / Lancer paiement |
| `initializing` | Initialisation du SDK | Aucune (attente) |
| `awaiting_tap` | Attente carte NFC | Annuler uniquement |
| `processing` | Traitement paiement | Aucune (bloqué) |
| `success` | Paiement réussi | Fermeture auto (2s) |
| `error` | Échec paiement | Annuler / Réessayer |

#### Interface utilisateur

**1. ATTENTE CARTE**
```
┌──────────────────────┐
│   Présentez la carte  │
│      📱               │
│   [===========]       │ ← Barre de progression
│   Montant: 20.00€     │
│                       │
│   [Annuler]          │ ← Seul bouton disponible
└──────────────────────┘
```

**2. TRAITEMENT**
```
┌──────────────────────┐
│  Traitement...        │
│      ⟳                │ ← Spinner
│  Ne pas retirer       │
│  la carte             │
│                       │ ← Pas de bouton
└──────────────────────┘
```

**3. RÉSULTAT**
- **Succès** : Message de confirmation + fermeture automatique après 2 secondes
- **Erreur** : Message d'erreur + options Annuler/Réessayer

#### Points importants
- **Dialog non-dismissible** : `barrierDismissible: false` empêche la fermeture accidentelle
- **Timeout** : 60 secondes pour présenter la carte, 30 secondes pour le traitement
- **Persistence** : Le terminal reste ouvert jusqu'à réponse définitive de Stripe
- **Gestion d'erreur** : Possibilité de réessayer sans perdre le contexte

### 🔑 Connection Token - Flow détaillé

Le Connection Token est crucial pour le SDK Stripe Terminal. Il est récupéré via un callback au moment de l'initialisation.

**Code côté App (stripe_tap_to_pay_service.dart:87-89) :**
```dart
await Terminal.initTerminal(
  fetchToken: _fetchConnectionToken,  // Callback appelé automatiquement
);
```

**Callback de récupération (lignes 137-161) :**
```dart
Future<String> _fetchConnectionToken() async {
  debugPrint('🔑 Récupération du token de connexion Stripe...');

  final response = await ApiService.instance.post(
    '/stripe/terminal/connection-token',
    data: {
      'amicale_id': CurrentAmicaleService.instance.amicaleId,
      'stripe_account': _stripeAccountId,
      'location_id': _locationId,
    }
  );

  final token = response.data['secret'];
  if (token == null || token.isEmpty) {
    throw Exception('Token de connexion invalide');
  }

  debugPrint('✅ Token de connexion récupéré');
  return token;
}
```

**Backend PHP :**
```php
// POST /stripe/terminal/connection-token
$token = \Stripe\Terminal\ConnectionToken::create([], [
    'stripe_account' => $amicale->stripe_id,
]);

return response()->json([
    'secret' => $token->secret,
]);
```

**Points importants :**
- ✅ Le token est **temporaire** (valide quelques minutes)
- ✅ Un nouveau token est créé à **chaque initialisation** du SDK
- ✅ Le token est spécifique au **compte Stripe Connect** de l'amicale
- ✅ Utilisé pour **authentifier** le Terminal SDK auprès de Stripe

### 📋 Détail des étapes

#### Étape 1 : VALIDATION DU FORMULAIRE
**Acteur:** Application Flutter
**Actions:**
- L'utilisateur remplit le formulaire de passage complet
- Saisie du montant du don
- Sélection du mode de paiement "Carte Bancaire"
- Validation de tous les champs obligatoires

#### Étape 2 : SAUVEGARDE DU PASSAGE
**Requête:** `POST /api/passages` (nouveau) ou `PUT /api/passages/{id}` (modification)
**Payload:**
```json
{
  "numero": "10",
  "rue": "Rue de la Paix",
  "ville": "Paris",
  "montant": "20.00",
  "fk_type_reglement": 3,  // CB
  "fk_type": 1,            // Effectué
  // ... autres champs sans stripe_payment_id
}
```
**Réponse:**
```json
{
  "id": 456,  // ID réel du passage créé/modifié
  "status": "created"
}
```
**Note:** Le passage est TOUJOURS sauvegardé en premier pour obtenir un ID réel.

#### Étape 3 : DEMANDE DE PAYMENT INTENT
**Requête:** `POST /api/stripe/payments/create-intent`
**Payload envoyé par l'app:**
```json
{
  "amount": 2000,                    // Montant en centimes (20€)
  "currency": "eur",
  "payment_method_types": ["card_present"],  // Pour Tap to Pay
  "passage_id": 456,                 // ID RÉEL du passage sauvegardé
  "amicale_id": 45,                  // ID de l'amicale
  "member_id": 67,                   // ID du membre pompier
  "stripe_account": "acct_1234",     // Compte Stripe Connect
  "location_id": "loc_xyz",          // Location Terminal (optionnel)
  "metadata": {
    "passage_id": "456",              // ID réel, jamais 0
    "amicale_name": "Pompiers de Paris",
    "member_name": "Jean Dupont",
    "type": "tap_to_pay"
  }
}
```

#### Étape 4 : CRÉATION CÔTÉ STRIPE
**Acteur:** API PHP → Stripe
**Actions de l'API:**
1. Validation des données reçues
2. Vérification des permissions utilisateur
3. Appel Stripe API :
```php
$paymentIntent = \Stripe\PaymentIntent::create([
    'amount' => 2000,
    'currency' => 'eur',
    'payment_method_types' => ['card_present'],
    'capture_method' => 'automatic',
    'metadata' => [
        'passage_id' => '123',
        'amicale_id' => '45',
        'member_id' => '67'
    ]
], ['stripe_account' => 'acct_1234']);
```

#### Étape 5 : RETOUR DU PAYMENT INTENT
**Réponse API → App:**
```json
{
  "success": true,
  "payment_intent_id": "pi_3O123abc",
  "client_secret": "pi_3O123abc_secret_xyz",
  "amount": 2000,
  "status": "requires_payment_method"
}
```

#### Étape 6 : COLLECTE NFC
**Acteur:** Application Flutter (SDK Stripe Terminal)
**Actions:**
1. Initialisation du Terminal SDK
2. Activation du NFC
3. Affichage interface "Approchez la carte"
4. Lecture des données de la carte
5. Animation visuelle pendant la lecture

#### Étape 7 : TRAITEMENT STRIPE
**Acteur:** SDK → Stripe
**Actions automatiques:**
- Envoi sécurisé des données carte
- Vérification 3D Secure si nécessaire
- Autorisation bancaire
- Capture automatique du paiement
- Retour du statut à l'application

#### Étape 8 : CONFIRMATION
**Requête:** `POST /api/stripe/payments/confirm`
**Payload:**
```json
{
  "payment_intent_id": "pi_3O123abc",
  "status": "succeeded",
  "amount": 2000,
  "amicale_id": 45,
  "member_id": 67
}
```

**Note importante:** Cette confirmation est envoyée AVANT la sauvegarde du passage. Elle permet à l'API de :
- Tracker la tentative de paiement
- Vérifier la cohérence avec Stripe
- Enregistrer le succès/échec indépendamment du passage

#### Étape 9 : MISE À JOUR DU PASSAGE
**Requête:** `PUT /api/passages/456`
**Payload:**
```json
{
  "id": 456,
  "stripe_payment_id": "pi_3O123abc",  // Ajout du payment ID
  // ... autres champs inchangés
}
```

**Note:** Seul le `stripe_payment_id` est ajouté au passage déjà existant.

#### Étape 10 : CONFIRMATION FINALE
**Réponse API → App:**
```json
{
  "success": true,
  "passage": {
    "id": 123,
    "stripe_payment_id": "pi_3O123abc",
    "status": "completed"
  }
}
```

---

## 📱 FLOW PAIEMENT QR CODE (Web + Mobile)

### 🎯 Vue d'ensemble

Le paiement par QR Code permet aux clients de payer directement avec leur téléphone en scannant un code QR généré par l'application. Cette méthode fonctionne sur **Web et Mobile** et ne nécessite pas de matériel spécifique.

### 🔄 Diagramme de séquence complet

```
┌─────────────┐     ┌─────────────┐     ┌──────────┐     ┌─────────┐     ┌─────────┐
│ App Flutter │     │   API PHP   │     │  Stripe  │     │ Client  │     │ Passage │
└──────┬──────┘     └──────┬──────┘     └────┬─────┘     └────┬────┘     └────┬────┘
       │                   │                  │                │               │
   [1] │ Validation form   │                  │                │               │
       │ + montant CB      │                  │                │               │
       │                   │                  │                │               │
   [2] │ POST/PUT passage  │                  │                │               │
       │──────────────────>│                  │                │               │
       │                   │                  │                │               │
   [3] │<──────────────────│                  │                │               │
       │ Passage ID: 456   │                  │                │               │
       │                   │                  │                │               │
   [4] │ Vérif Stripe      │                  │                │               │
       │ chkStripe=true    │                  │                │               │
       │ + stripeId rempli │                  │                │               │
       │                   │                  │                │               │
   [5] │ Dialog Sélection  │                  │                │               │
       │ "Règlement CB"    │                  │                │               │
       │ [QRCode|TapToPay] │                  │                │               │
       │                   │                  │                │               │
   [6] │ Clic "QR Code"    │                  │                │               │
       │                   │                  │                │               │
   [7] │ POST payment-links│                  │                │               │
       │──────────────────>│ (passage_id: 456)│                │               │
       │                   │                  │                │               │
   [8] │                   │ Create PaymentLink                │               │
       │                   │─────────────────>│                │               │
       │                   │                  │                │               │
   [9] │                   │<─────────────────│                │               │
       │                   │ link_id + url    │                │               │
       │                   │                  │                │               │
  [10] │<──────────────────│                  │                │               │
       │ PaymentLink data  │                  │                │               │
       │                   │                  │                │               │
  [11] │ Génération QR     │                  │                │               │
       │ avec URL Stripe   │                  │                │               │
       │                   │                  │                │               │
  [12] │ Affichage dialog  │                  │                │               │
       │ QR Code           │                  │                │               │
       │ ┌──────────────┐  │                  │                │               │
       │ │   QR Code    │  │                  │                │               │
       │ │   ▓▓▓▓▓▓▓▓   │  │                  │                │               │
       │ │   20.00 €    │  │                  │                │               │
       │ └──────────────┘  │                  │                │               │
       │                   │                  │                │               │
  [13] │                   │                  │   Scan QR Code │               │
       │                   │                  │<───────────────│               │
       │                   │                  │                │               │
  [14] │                   │                  │ Page paiement  │               │
       │                   │                  │───────────────>│               │
       │                   │                  │ Stripe hosted  │               │
       │                   │                  │                │               │
  [15] │                   │                  │   Saisie CB    │               │
       │                   │                  │<───────────────│               │
       │                   │                  │                │               │
  [16] │                   │                  │  Validation    │               │
       │                   │                  │───────────────>│               │
       │                   │                  │                │               │
  [17] │                   │  Webhook         │                │               │
       │                   │<─────────────────│                │               │
       │                   │ payment_succeeded│                │               │
       │                   │                  │                │               │
  [18] │                   │ Update passage   │                │               │
       │                   │────────────────────────────────────────────────>│
       │                   │ stripe_payment_id│                │               │
       │                   │                  │                │               │
  [19] │                   │                  │ Confirmation   │               │
       │                   │                  │───────────────>│               │
       │                   │                  │ "Merci!"       │               │
```

### 📋 Détail des étapes

#### Étape 1-3 : SAUVEGARDE DU PASSAGE
**Identique au flow Tap to Pay** - Le passage est toujours créé en premier pour obtenir un ID réel.

#### Étape 4 : VÉRIFICATION STRIPE
**Acteur:** Application Flutter
**Conditions vérifiées:**
```dart
final amicale = CurrentAmicaleService.instance.currentAmicale;
final stripeEnabled = amicale?.chkStripe == true &&
                     amicale?.stripeId != null &&
                     amicale!.stripeId.isNotEmpty;

if (stripeEnabled && fkTypeReglement == 3 && montant > 0) {
  // Afficher dialog de sélection de méthode
}
```

#### Étape 5 : DIALOG DE SÉLECTION DE MÉTHODE
**Widget:** `PaymentMethodSelectionDialog`
**Interface affichée:**
```
┌────────────────────────────────┐
│     Règlement CB               │
│                                │
│  👤 Jean Dupont                │
│  💰 20.00 €                    │
│                                │
│  Sélectionnez une méthode :    │
│                                │
│  ┌──────────────────────────┐  │
│  │ 📱 Paiement par QR Code  │  │
│  │ Le client scanne le code │  │
│  └──────────────────────────┘  │
│                                │
│  ┌──────────────────────────┐  │ (Si compatible)
│  │ 💳 Tap to Pay            │  │
│  │ Paiement sans contact    │  │
│  └──────────────────────────┘  │
│                                │
│  🔒 Sécurisé par Stripe        │
└────────────────────────────────┘
```

#### Étape 6-10 : CRÉATION DU PAYMENT LINK
**Requête:** `POST /api/stripe/payment-links`
**Payload:**
```json
{
  "amount": 2000,
  "currency": "eur",
  "description": "Calendrier pompiers - Jean Dupont",
  "passage_id": 456,
  "metadata": {
    "passage_id": "456",
    "habitant_name": "Jean Dupont",
    "adresse": "10 Rue de la Paix, Paris"
  }
}
```

**Réponse:**
```json
{
  "success": true,
  "payment_link_id": "plink_1234567890",
  "url": "https://buy.stripe.com/test_xxxxxxxxxxxxx",
  "amount": 2000,
  "passage_id": 456
}
```

**Code PHP Backend:**
```php
$paymentLink = \Stripe\PaymentLink::create([
    'line_items' => [[
        'price_data' => [
            'currency' => 'eur',
            'product_data' => [
                'name' => 'Calendrier pompiers',
            ],
            'unit_amount' => 2000,
        ],
        'quantity' => 1,
    ]],
    'metadata' => [
        'passage_id' => '456',
        'type' => 'qr_code_payment',
    ],
    'after_completion' => [
        'type' => 'hosted_confirmation',
        'hosted_confirmation' => [
            'custom_message' => 'Merci pour votre paiement !',
        ],
    ],
], [
    'stripe_account' => $amicale->stripe_id,
]);
```

#### Étape 11-12 : AFFICHAGE DU QR CODE
**Widget:** `QRCodePaymentDialog`
**Interface:**
```
┌──────────────────────────────┐
│  Paiement par QR Code        │
│                              │
│  💰 20.00 €                  │
│                              │
│  ┌────────────────────────┐  │
│  │                        │  │
│  │    ▓▓▓▓▓  ▓▓▓▓▓▓▓     │  │
│  │    ▓▓▓▓▓  ▓▓   ▓▓     │  │
│  │    ▓▓▓▓▓  ▓▓▓▓▓▓▓     │  │
│  │    QR CODE HERE        │  │
│  │                        │  │
│  └────────────────────────┘  │
│                              │
│  Scannez ce QR code avec     │
│  votre téléphone             │
│                              │
│  Vous serez redirigé vers    │
│  une page sécurisée Stripe   │
│                              │
│  🔒 Paiement sécurisé        │
│                              │
│         [Fermer]             │
└──────────────────────────────┘
```

#### Étape 13-16 : PAIEMENT CLIENT
**Acteur:** Client final
**Actions:**
1. Scan du QR Code avec son smartphone
2. Ouverture automatique de l'URL Stripe dans le navigateur
3. Affichage de la page de paiement Stripe hébergée
4. Saisie des informations de carte bancaire
5. Validation 3D Secure si nécessaire
6. Confirmation du paiement

#### Étape 17-18 : WEBHOOK ET MISE À JOUR
**Requête Webhook:** `POST /api/stripe/webhook`
**Event:** `checkout.session.completed` ou `payment_intent.succeeded`
**Payload Stripe:**
```json
{
  "type": "payment_intent.succeeded",
  "data": {
    "object": {
      "id": "pi_1234567890",
      "amount": 2000,
      "metadata": {
        "passage_id": "456"
      }
    }
  }
}
```

**Action Backend:**
```php
// Récupérer le passage
$passage = Passage::find($paymentIntent->metadata->passage_id);

// Mettre à jour avec le payment ID
$passage->stripe_payment_id = $paymentIntent->id;
$passage->save();

Log::info('Payment confirmed via QR Code', [
    'passage_id' => $passage->id,
    'payment_id' => $paymentIntent->id,
]);
```

### 🔄 Comparaison QR Code vs Tap to Pay

| Aspect | QR Code | Tap to Pay |
|--------|---------|------------|
| **Plateformes** | Web + Mobile | Mobile uniquement |
| **Matériel requis** | Aucun | NFC (iPhone XS+ / Android certifié) |
| **Client utilise** | Son propre téléphone | Le téléphone du pompier |
| **payment_method_types** | `["card"]` | `["card_present"]` |
| **Interface paiement** | Page Stripe hébergée | NFC sur l'app |
| **Délai confirmation** | Immédiat (webhook) | Immédiat (SDK) |
| **Expérience client** | Scan + saisie CB | Approche carte |
| **Sans contact physique** | ✅ Oui (COVID-safe) | ❌ Non (proximité requise) |
| **Montant minimum** | 0.50€ | 1.00€ |
| **Cas d'usage idéal** | Client à distance | Face à face |

### ✅ Conditions d'éligibilité

#### Affichage du bouton "QR Code"
```dart
// Conditions cumulatives :
final canShowQRCode =
    amicale.chkStripe == true &&           // Stripe activé
    amicale.stripeId.isNotEmpty &&         // Compte configuré
    fkTypeReglement == 3 &&                // CB sélectionnée
    montant > 0;                           // Montant valide
```

#### Différences avec Tap to Pay
- **Pas besoin de** `stripeLocationId` (spécifique Terminal)
- **Pas de vérification** device (fonctionne partout)
- **Pas de batterie** minimum requise

### 🎨 Widgets créés

#### 1. PaymentMethodSelectionDialog
**Fichier:** `lib/presentation/widgets/payment_method_selection_dialog.dart`
**Rôle:** Choisir entre QR Code et Tap to Pay
**Props:**
```dart
PaymentMethodSelectionDialog({
  required PassageModel passage,
  required double amount,
  required String habitantName,
  required StripeConnectService stripeConnectService,
  VoidCallback? onTapToPaySelected,
})
```

#### 2. QRCodePaymentDialog
**Fichier:** `lib/presentation/widgets/qr_code_payment_dialog.dart`
**Rôle:** Afficher le QR Code généré
**Props:**
```dart
QRCodePaymentDialog({
  required PaymentLinkResult paymentLink,
  VoidCallback? onClose,
})
```

### 🔧 Services modifiés

#### StripeConnectService
**Nouvelle méthode:**
```dart
Future<PaymentLinkResult?> createPaymentLink({
  required int amountInCents,
  required int passageId,
  String? description,
  Map<String, dynamic>? metadata,
})
```

### 📊 Modèles créés

#### PaymentLinkResult
**Fichier:** `lib/core/data/models/payment_link_result.dart`
```dart
class PaymentLinkResult {
  final String paymentLinkId;
  final String url;
  final int amount;
  final int? passageId;
}
```

### 🔐 Sécurité

#### Validation Backend
```php
// Vérifications obligatoires
$request->validate([
    'amount' => 'required|integer|min:50',
    'passage_id' => 'required|integer|exists:ope_pass,id',
]);

// Vérifier Stripe activé
if (!$amicale->chk_stripe || empty($amicale->stripe_id)) {
    return response()->json(['error' => 'Stripe non activé'], 403);
}

// Créer sur le compte Connect de l'amicale
\Stripe\PaymentLink::create([...], [
    'stripe_account' => $amicale->stripe_id,
]);
```

### 📱 Package ajouté

**pubspec.yaml:**
```yaml
dependencies:
  qr_flutter: ^4.1.0  # Génération de QR codes
```

---

## 💻 FLOW PAIEMENT WEB

### 🔄 Principales différences avec Tap to Pay

| Aspect | Web | Tap to Pay |
|--------|-----|------------|
| **payment_method_types** | `["card"]` | `["card_present"]` |
| **SDK** | Stripe.js dans navigateur | Stripe Terminal SDK natif |
| **Interface paiement** | Formulaire carte web | NFC téléphone |
| **capture_method** | `manual` ou `automatic` | Toujours `automatic` |
| **Metadata type** | `"web"` | `"tap_to_pay"` |
| **Client secret usage** | Pour Stripe Elements | Pour Terminal SDK |

### 📋 Flow Web simplifié

```
1. Utilisateur remplit formulaire web avec montant
2. POST /api/stripe/payments/create-intent
   - payment_method_types: ["card"]
   - metadata.type: "web"
3. API crée PaymentIntent et retourne client_secret
4. Frontend utilise Stripe.js pour afficher formulaire carte
5. Utilisateur saisit données carte
6. Stripe.js confirme le paiement
7. Webhook Stripe notifie l'API du succès
8. API met à jour le passage en base
```

---

## 📱 VALIDATION ET CONTRÔLES CÔTÉ APP

### Vérifications avant affichage du Terminal

L'application effectue une série de vérifications **avant** d'afficher le terminal de paiement :

#### 1. Dans le formulaire de passage
```dart
void _handleSubmit() {
  // ✅ Validation des champs du formulaire
  if (!_formKey.currentState!.validate()) return;

  // ✅ Vérification CB sélectionnée + montant > 0
  if (_fkTypeReglement == 3 && montant > 0) {
    await _attemptTapToPay();  // Lance le flow
  }
}
```

#### 2. Dans le service StripeTapToPayService
```dart
initialize() {
  // ✅ User connecté
  if (!CurrentUserService.instance.isLoggedIn) return false;

  // ✅ Amicale avec Stripe activé
  if (!amicale.chkStripe || amicale.stripeId.isEmpty) return false;

  // ✅ Appareil compatible (iPhone XS+, iOS 16.4+)
  if (!DeviceInfoService.instance.canUseTapToPay()) return false;

  // ✅ Configuration Stripe récupérée
  await _fetchConfiguration();
}
```

#### 3. Dans le Dialog Tap to Pay
```dart
_startPayment() {
  // ✅ Service initialisé ou initialisation réussie
  if (!initialized) throw Exception('Impossible d\'initialiser');

  // ✅ Prêt pour paiements (toutes conditions remplies)
  if (!isReadyForPayments()) throw Exception('Appareil non prêt');

  // Création PaymentIntent et collecte NFC...
}
```

### Flow de sauvegarde et paiement

Le nouveau flow garantit que le passage existe TOUJOURS avant le paiement :

```dart
// 1. SAUVEGARDE DU PASSAGE EN PREMIER
Future<void> _savePassage() {
  // Créer ou modifier le passage
  PassageModel? savedPassage;
  if (widget.passage == null) {
    // Création avec retour de l'ID
    savedPassage = await passageRepository.createPassageWithReturn(passageData);
  } else {
    // Modification
    savedPassage = passageData;
  }

  // 2. SI CB SÉLECTIONNÉE, LANCER TAP TO PAY
  if (typeReglement == CB && montant > 0) {
    await _attemptTapToPayWithPassage(savedPassage, montant);
  }
}

// 3. PAIEMENT AVEC ID RÉEL
_attemptTapToPayWithPassage(PassageModel passage, double montant) {
  _TapToPayFlowDialog(
    passageId: passage.id,  // ← ID réel, jamais 0
    onSuccess: (paymentIntentId) {
      // 4. MISE À JOUR DU PASSAGE
      final updated = passage.copyWith(
        stripePaymentId: paymentIntentId
      );
      passageRepository.updatePassage(updated);
    }
  );
}
```

## 🔐 SÉCURITÉ ET BONNES PRATIQUES

### 🛡️ Principes de sécurité

1. **Jamais de données carte en clair** - Toujours via SDK Stripe
2. **HTTPS obligatoire** - Toutes communications chiffrées
3. **Validation côté serveur** - Ne jamais faire confiance au client
4. **Tokens temporaires** - Connection tokens à durée limitée
5. **Logs sans données sensibles** - Pas de numéros carte dans les logs

### ✅ Validations requises

#### Côté App Flutter:
- Vérifier compatibilité appareil (iPhone XS+, iOS 16.4+)
- Valider montant (min 1€, max 999€)
- Vérifier connexion internet avant paiement
- Gérer timeouts réseau

#### Côté API:
- Authentification utilisateur obligatoire
- Vérification appartenance à l'amicale
- Validation montants et devises
- Vérification compte Stripe actif
- Rate limiting sur endpoints

---

## 📊 DOUBLE CONFIRMATION API

### Pourquoi deux appels distincts ?

Le système utilise **deux endpoints séparés** pour une meilleure traçabilité :

#### 1. Confirmation du paiement (`/api/stripe/payments/confirm`)
```json
POST /api/stripe/payments/confirm
{
  "payment_intent_id": "pi_xxx",
  "status": "succeeded",  // ou "failed"
  "amount": 2000
}
```
**Rôle :** Notifier l'API du résultat Stripe (succès/échec)

#### 2. Sauvegarde du passage (`/api/passages`)
```json
POST/PUT /api/passages
{
  "stripe_payment_id": "pi_xxx",
  "montant": "20.00",
  "fk_type_reglement": 3  // CB
}
```
**Rôle :** Sauvegarder le passage **uniquement si paiement réussi**

### Avantages du nouveau flow

| Aspect | Bénéfice |
|--------|----------|
| **Passage toujours créé** | Même si le paiement échoue, le passage existe |
| **ID réel dans Stripe** | Les metadata contiennent toujours le vrai `passage_id` |
| **Traçabilité complète** | Liaison bidirectionnelle garantie (passage → Stripe et Stripe → passage) |
| **Gestion d'erreur robuste** | Si paiement échoue, le passage reste sans `stripe_payment_id` |
| **Mode offline** | Le passage peut être créé localement avec ID temporaire |

## 🔄 GESTION DES ERREURS

### 📱 Erreurs Tap to Pay - Messages utilisateur clairs

L'application détecte automatiquement le type d'erreur et affiche un message adapté :

#### Gestion intelligente des erreurs (passage_form_dialog.dart)

```dart
catch (e) {
  final errorMsg = e.toString().toLowerCase();
  String userMessage;
  bool shouldCancelPayment = true;

  if (errorMsg.contains('canceled') || errorMsg.contains('cancelled')) {
    // Annulation volontaire
    userMessage = 'Paiement annulé';

  } else if (errorMsg.contains('cardreadtimedout') || errorMsg.contains('timed out')) {
    // Timeout NFC avec conseils
    userMessage = 'Lecture de la carte impossible.\n\n'
        'Conseils :\n'
        '• Maintenez la carte contre le dos du téléphone\n'
        '• Ne bougez pas jusqu\'à confirmation\n'
        '• Retirez la coque si nécessaire\n'
        '• Essayez différentes positions sur le téléphone';

  } else if (errorMsg.contains('already') && errorMsg.contains('payment')) {
    // PaymentIntent existe déjà
    userMessage = 'Un paiement est déjà en cours pour ce passage.\n'
        'Veuillez réessayer dans quelques instants.';
    shouldCancelPayment = false;
  }

  // Annulation automatique du PaymentIntent pour permettre nouvelle tentative
  if (shouldCancelPayment && _paymentIntentId != null) {
    await StripeTapToPayService.instance.cancelPayment(_paymentIntentId!);
  }
}
```

#### Table des erreurs et actions

| Type erreur | Message utilisateur | Action automatique |
|-------------|--------------------|--------------------|
| `canceled` / `cancelled` | "Paiement annulé" | Annulation PaymentIntent ✅ |
| `cardReadTimedOut` | Message avec 4 conseils NFC | Annulation PaymentIntent ✅ |
| `already payment` | "Paiement déjà en cours" | Pas d'annulation ⏳ |
| `device_not_compatible` | "Appareil non compatible" | Annulation PaymentIntent ✅ |
| `nfc_disabled` | "NFC désactivé" | Annulation PaymentIntent ✅ |
| Autre erreur | Message technique complet | Annulation PaymentIntent ✅ |

### ⚠️ Contraintes NFC - Tap to Pay vs Google Pay

**Différence fondamentale :**
- **Google Pay (émission)** : Le téléphone *émet* un signal NFC puissant → fonctionne avec coque
- **Tap to Pay (réception)** : Le téléphone *lit* le signal de la carte → très sensible aux interférences

#### Coques problématiques
- ❌ **Kevlar / Carbone** : Fibres conductrices perturbent la réception NFC
- ❌ **Métal** : Bloque complètement les ondes NFC
- ❌ **Coque épaisse** : Réduit la portée effective
- ✅ **TPU / Silicone** : Compatible

#### Bonnes pratiques pour réussite NFC

**Position optimale :**
```
┌─────────────────┐
│   📱 Téléphone  │
│                 │
│    [Capteur NFC]│ ← Généralement vers le haut du dos
│                 │
│                 │
│  💳 Carte ici   │ ← Maintenir immobile 3-5 secondes
└─────────────────┘
```

**Checklist utilisateur :**
1. ✅ Retirer la coque si échec
2. ✅ Carte à plat contre le dos du téléphone
3. ✅ Ne pas bouger pendant toute la lecture
4. ✅ Essayer différentes positions (haut/milieu du téléphone)
5. ✅ Carte sans contact activée (logo sans contact visible)

### 🔄 Flow de retry automatique

```
1. Erreur détectée → Analyse du type
2. Annulation automatique PaymentIntent (si applicable)
3. Message clair avec conseils contextuels
4. Bouton "Réessayer" disponible
5. Nouveau PaymentIntent créé automatiquement
6. Conservation du contexte (montant, passage)
```

**Avantages :**
- ✅ Pas de blocage "PaymentIntent déjà existant"
- ✅ Nombre illimité de tentatives
- ✅ Contexte préservé (pas besoin de tout ressaisir)
- ✅ Messages orientés solution plutôt qu'erreur technique

### 🏗️ Environnement et Build Release

#### Détection automatique de l'environnement

L'application détecte l'environnement via l'URL de l'API (plus fiable que `kDebugMode`) :

```dart
// stripe_tap_to_pay_service.dart (lignes 236-252)
Future<bool> _ensureReaderConnected() async {
  // Détection via URL API
  final apiUrl = ApiService.instance.baseUrl;
  final isProduction = apiUrl.contains('app3.geosector.fr');
  final isSimulated = !isProduction;

  final config = TapToPayDiscoveryConfiguration(
    isSimulated: isSimulated,
  );

  debugPrint('🔧 Environnement: ${isProduction ? "PRODUCTION (réel)" : "DEV/REC (simulé)"}');
  debugPrint('🔧 isSimulated: $isSimulated');
}
```

**Mapping environnement :**
| URL API | Environnement | Reader Stripe | Cartes acceptées |
|---------|---------------|---------------|------------------|
| `dapp.geosector.fr` | DEV | Simulé | Cartes test uniquement |
| `rapp.geosector.fr` | REC | Simulé | Cartes test uniquement |
| `app3.geosector.fr` | PROD | Réel | Cartes réelles uniquement |

#### ⚠️ Restriction Stripe - Build Release obligatoire en PROD

**Erreur si app debuggable en PROD :**
```
Debuggable applications are not supported when using the production
version of the Tap to Pay reader. Please use a simulated version of
the reader by setting TapToPayDiscoveryConfiguration.isSimulated to true.
```

**Solution - Build release :**
```bash
# Build APK optimisé pour production
flutter build apk --release

# Installation sur device
adb install build/app/outputs/flutter-apk/app-release.apk
```

**Différences debug vs release :**
| Aspect | Debug (`flutter run`) | Release (`flutter build`) |
|--------|-----------------------|--------------------|
| **Optimisation** | ❌ Code non optimisé | ✅ R8/ProGuard activé |
| **Taille APK** | ~200 MB | ~30-50 MB |
| **Performance** | Lente (dev mode) | Rapide (optimisée) |
| **Tap to Pay PROD** | ❌ Refusé par Stripe | ✅ Accepté |
| **Débogage** | ✅ Hot reload, logs | ❌ Pas de hot reload |

**Pourquoi Stripe refuse les apps debug :**
- **Sécurité renforcée** : Les apps debuggables peuvent être inspectées
- **Conformité PCI-DSS** : Exigences de sécurité pour paiements réels
- **Protection production** : Éviter utilisation accidentelle de readers réels en dev

---

## 📊 MONITORING ET LOGS

### 📈 Métriques à suivre

1. **Taux de succès** des paiements (objectif > 95%)
2. **Temps moyen** de transaction (< 15 secondes)
3. **Types d'erreurs** les plus fréquentes
4. **Appareils utilisés** (modèles iPhone)
5. **Montants moyens** des transactions

### 📝 Logs essentiels

#### App Flutter:
```dart
debugPrint('🚀 PaymentIntent créé: $paymentIntentId');
debugPrint('💳 Collecte NFC démarrée');
debugPrint('✅ Paiement confirmé: $amount €');
debugPrint('❌ Erreur paiement: $errorCode');
```

#### API PHP:
```php
Log::info('PaymentIntent created', [
    'id' => $paymentIntent->id,
    'amount' => $amount,
    'amicale_id' => $amicaleId
]);
```

---

## 🚀 OPTIMISATIONS ET PERFORMANCES

### ⚡ Optimisations implémentées

1. **Cache Box Hive** - Éviter accès répétés
2. **Batch API calls** - Grouper les requêtes
3. **Lazy loading** - Charger données à la demande
4. **Connection pooling** - Réutiliser connexions HTTP
5. **Queue offline** - File d'attente locale

### 🎯 Points d'amélioration

- [ ] Pré-création PaymentIntent pendant saisie montant
- [ ] Cache des configurations Stripe
- [ ] Compression des payloads API
- [ ] Optimisation animations NFC
- [ ] Réduction taille APK/IPA

---

## 📱 COMPATIBILITÉ APPAREILS

### 🍎 iOS - Tap to Pay

**Appareils compatibles:**
- iPhone XS, XS Max, XR
- iPhone 11, 11 Pro, 11 Pro Max
- iPhone 12, 12 mini, 12 Pro, 12 Pro Max
- iPhone 13, 13 mini, 13 Pro, 13 Pro Max
- iPhone 14, 14 Plus, 14 Pro, 14 Pro Max
- iPhone 15, 15 Plus, 15 Pro, 15 Pro Max
- iPhone 16 (tous modèles)

**Prérequis:**
- iOS 16.4 minimum
- NFC activé
- Bluetooth activé (pour certains cas)

### 🤖 Android - Tap to Pay (V2.2+)

**À venir - Liste dynamique via API**
- Appareils certifiés Google Pay
- Android 9.0+ (API 28+)
- NFC requis

---

## 🔗 RESSOURCES ET DOCUMENTATION

### 📚 Documentation officielle

- [Stripe Terminal Flutter](https://stripe.com/docs/terminal/payments/collect-payment?platform=flutter)
- [Stripe PaymentIntents API](https://stripe.com/docs/api/payment_intents)
- [Apple Tap to Pay](https://developer.apple.com/tap-to-pay/)
- [PCI DSS Compliance](https://stripe.com/docs/security/guide)

### 🛠️ Outils de test

- **Cartes de test Stripe**: 4242 4242 4242 4242
- **iPhone Simulator**: Ne supporte pas NFC
- **Stripe CLI**: Pour webhooks locaux
- **Postman**: Collection API fournie

### 📞 Support

- **Stripe Support**: support@stripe.com
- **Équipe Backend**: API PHP GEOSECTOR
- **Équipe Mobile**: Flutter GEOSECTOR

---

## 📅 HISTORIQUE DES VERSIONS

| Version | Date | Modifications |
|---------|------|--------------|
| 1.0 | 28/09/2025 | Création documentation initiale |
| 1.1 | 28/09/2025 | Ajout flow complet Tap to Pay |
| 1.2 | 28/09/2025 | Intégration passage_id et metadata |
| 1.3 | 05/11/2025 | Ajout flow paiement par QR Code |

---

*Document technique - Flow Stripe GEOSECTOR*
*Dernière mise à jour : 5 novembre 2025*