# Flow de paiement Stripe Tap to Pay ## Vue d'ensemble Ce document décrit le flow complet pour les paiements Stripe Tap to Pay dans l'application GeoSector, depuis la création du compte Stripe Connect jusqu'au paiement final. --- ## 🏢 PRÉALABLE : Création d'un compte Amicale Stripe Connect Avant de pouvoir utiliser les paiements Stripe, chaque amicale doit créer son compte Stripe Connect. ### 📋 Flow de création du compte #### 1. Initiation depuis l'application web admin **Endpoint :** `POST /api/stripe/accounts/create` **Requête :** ```json { "amicale_id": 45, "type": "express", // Type de compte Stripe Connect "country": "FR", "email": "contact@amicale-pompiers-paris.fr", "business_profile": { "name": "Amicale des Pompiers de Paris", "product_description": "Vente de calendriers des pompiers", "mcc": "8398", // Code activité : organisations civiques "url": "https://www.amicale-pompiers-paris.fr" } } ``` #### 2. Création du compte Stripe **Actions API :** 1. Appel Stripe API pour créer un compte Express 2. Génération d'un lien d'onboarding personnalisé 3. Sauvegarde en base de données **Réponse :** ```json { "success": true, "stripe_account_id": "acct_1O3ABC456DEF789", "onboarding_url": "https://connect.stripe.com/express/oauth/authorize?...", "status": "pending" } ``` #### 3. Processus d'onboarding Stripe **Actions utilisateur (dirigeant amicale) :** 1. Clic sur le lien d'onboarding 2. Connexion/création compte Stripe 3. Saisie des informations légales : - **Entité** : Association loi 1901 - **SIRET** de l'amicale - **RIB** pour les virements - **Pièce d'identité** du représentant légal 4. Validation des conditions d'utilisation #### 4. Vérification et activation **Webhook Stripe → API :** ```json POST /api/stripe/webhooks { "type": "account.updated", "data": { "object": { "id": "acct_1O3ABC456DEF789", "charges_enabled": true, "payouts_enabled": true, "details_submitted": true } } } ``` **Actions API :** 1. Mise à jour du statut en base 2. Notification email à l'amicale 3. Activation des fonctionnalités de paiement #### 5. Structure en base de données **Table `stripe_accounts` :** ```sql CREATE TABLE `stripe_accounts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `fk_entite` int(10) unsigned NOT NULL, `stripe_account_id` varchar(50) NOT NULL, `account_type` enum('express','standard','custom') DEFAULT 'express', `charges_enabled` tinyint(1) DEFAULT 0, `payouts_enabled` tinyint(1) DEFAULT 0, `details_submitted` tinyint(1) DEFAULT 0, `country` varchar(2) DEFAULT 'FR', `default_currency` varchar(3) DEFAULT 'eur', `business_name` varchar(255) DEFAULT NULL, `support_email` varchar(255) DEFAULT NULL, `onboarding_completed_at` timestamp NULL DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NULL DEFAULT NULL ON UPDATE current_timestamp(), PRIMARY KEY (`id`), UNIQUE KEY `stripe_account_id` (`stripe_account_id`), KEY `fk_entite` (`fk_entite`), CONSTRAINT `stripe_accounts_ibfk_1` FOREIGN KEY (`fk_entite`) REFERENCES `entites` (`id`) ); ``` ### 🔐 Sécurité et validation #### Prérequis pour créer un compte : - ✅ Utilisateur administrateur de l'amicale - ✅ Amicale active avec statut validé - ✅ Email de contact vérifié - ✅ Informations légales complètes (SIRET, adresse) #### Validation avant paiements : - ✅ `charges_enabled = 1` (peut recevoir des paiements) - ✅ `payouts_enabled = 1` (peut recevoir des virements) - ✅ `details_submitted = 1` (onboarding terminé) ### 📊 États du compte Stripe | État | Description | Actions possibles | |------|-------------|-------------------| | `pending` | Compte créé, onboarding en cours | Compléter l'onboarding | | `restricted` | Informations manquantes | Fournir documents manquants | | `restricted_soon` | Vérification en cours | Attendre validation Stripe | | `active` | Compte opérationnel | Recevoir des paiements ✅ | | `rejected` | Compte refusé par Stripe | Contacter support | ### 🚨 Gestion des erreurs #### Erreurs courantes lors de la création : - **400** : Données manquantes ou invalides - **409** : Compte Stripe déjà existant pour cette amicale - **403** : Utilisateur non autorisé #### Erreurs durant l'onboarding : - Documents manquants ou invalides - Informations bancaires incorrectes - Activité non autorisée par Stripe ### 📞 Support et résolution #### Pour les amicales : 1. **Email support** : support@geosector.fr 2. **Documentation** : Guides d'onboarding disponibles 3. **Assistance téléphonique** : Disponible aux heures ouvrables #### Pour les développeurs : 1. **Stripe Dashboard** : Accès aux comptes et statuts 2. **Logs API** : Traçabilité complète des opérations 3. **Webhook monitoring** : Suivi des événements Stripe --- ## 🚨 IMPORTANT : Nouveau Flow (v2) **Le passage est TOUJOURS créé/modifié EN PREMIER** pour obtenir un ID réel, PUIS le PaymentIntent est créé avec cet ID. ## Flow détaillé ### 1. Sauvegarde du passage EN PREMIER L'application crée ou modifie d'abord le passage pour obtenir un ID réel : ``` POST /api/passages/create // Nouveau passage PUT /api/passages/456 // Mise à jour passage existant ``` **Réponse avec l'ID réel :** ```json { "status": "success", "passage_id": 456 // ID RÉEL du passage créé/modifié } ``` ### 2. Création du PaymentIntent AVEC l'ID réel Ensuite seulement, création du PaymentIntent avec le `passage_id` réel : ``` POST /api/stripe/payments/create-intent ``` ```json { "amount": 2500, // En centimes (25€) "passage_id": 456, // ID RÉEL du passage (JAMAIS 0) "payment_method_types": ["card_present"], // Tap to Pay "location_id": "tml_xxx", // Terminal reader location "amicale_id": 45, "member_id": 67, "stripe_account": "acct_xxx" } ``` #### Réponse ```json { "status": "success", "data": { "client_secret": "pi_3QaXYZ_secret_xyz", "payment_intent_id": "pi_3QaXYZ123ABC456", "amount": 2500, "currency": "eur", "passage_id": 789, // 0 pour nouveau passage "type": "tap_to_pay" } } ``` ### 2. Traitement du paiement côté client L'application utilise le SDK Stripe pour traiter le paiement via NFC : ```dart // Flutter - Utilisation du client_secret final paymentResult = await stripe.collectPaymentMethod( clientSecret: response['client_secret'], // ... configuration Tap to Pay ); ``` ### 3. Traitement du paiement Tap to Pay L'application utilise le SDK Stripe Terminal avec le `client_secret` pour collecter le paiement via NFC. ### 4. Mise à jour du passage avec stripe_payment_id Après succès du paiement, l'app met à jour le passage avec le `stripe_payment_id` : ```json PUT /api/passages/456 { "stripe_payment_id": "pi_3QaXYZ123ABC456", // ← LIEN AVEC STRIPE // ... autres champs si nécessaire } ``` ## Points clés du nouveau flow ### ✅ Avantages 1. **Passage toujours existant** : Le passage existe toujours avec un ID réel avant le paiement 2. **Traçabilité garantie** : Le `passage_id` dans Stripe est toujours valide 3. **Gestion d'erreur robuste** : Si le paiement échoue, le passage existe déjà 4. **Cohérence des données** : Pas de passage "orphelin" ou de paiement sans passage ### ❌ Ce qui n'est plus supporté 1. **passage_id=0** : Plus jamais utilisé dans `/create-intent` 2. **operation_id** : Plus nécessaire car le passage existe déjà 3. **Création conditionnelle** : Le passage est toujours créé avant ## Schéma de séquence (Nouveau Flow v2) ``` ┌─────────┐ ┌─────────┐ ┌────────┐ ┌────────────┐ │ App │ │ API │ │ Stripe │ │ ope_pass │ └────┬────┘ └────┬────┘ └────┬───┘ └─────┬──────┘ │ │ │ │ │ 1. CREATE/UPDATE passage │ │ ├──────────────>│ │ │ │ ├────────────────┼───────────────>│ │ │ │ INSERT/UPDATE │ │ │ │ │ │ 2. passage_id: 456 (réel) │ │ │<──────────────│ │ │ │ │ │ │ │ 3. create-intent (id=456) │ │ ├──────────────>│ │ │ │ │ │ │ │ │ 4. Create PI │ │ │ ├───────────────>│ │ │ │ │ │ │ │ 5. PI created │ │ │ │<───────────────│ │ │ │ │ │ │ │ 6. UPDATE │ │ │ ├────────────────┼───────────────>│ │ │ stripe_payment_id = pi_xxx │ │ │ │ │ │ 7. client_secret + pi_id │ │ │<──────────────│ │ │ │ │ │ │ │ 8. Tap to Pay │ │ │ ├───────────────┼───────────────>│ │ │ avec SDK │ │ │ │ │ │ │ │ 9. Payment OK │ │ │ │<──────────────┼────────────────│ │ │ │ │ │ │ 10. UPDATE passage (optionnel) │ │ ├──────────────>│ │ │ │ ├────────────────┼───────────────>│ │ │ Confirmer stripe_payment_id │ │ │ │ │ │ 11. Success │ │ │ │<──────────────│ │ │ │ │ │ │ ``` ## Points importants (Nouveau Flow v2) 1. **Passage créé en premier** : Le passage est TOUJOURS créé/modifié AVANT le PaymentIntent 2. **ID réel obligatoire** : Le `passage_id` ne peut jamais être 0 dans `/create-intent` 3. **Lien Stripe automatique** : Le `stripe_payment_id` est ajouté automatiquement lors de la création du PaymentIntent 4. **Idempotence** : Un passage ne peut avoir qu'un seul `stripe_payment_id` 5. **Validation stricte** : Vérification du montant, propriété et existence du passage ## Erreurs possibles - **400** : - `passage_id` manquant ou ≤ 0 - Montant invalide (< 1€ ou > 999€) - Passage déjà payé par Stripe - Montant ne correspond pas au passage - **401** : Non authentifié - **403** : Passage non autorisé (pas le bon utilisateur) - **404** : Passage non trouvé ## Migration base de données La colonne `stripe_payment_id VARCHAR(50)` a été ajoutée via : ```sql ALTER TABLE `ope_pass` ADD COLUMN `stripe_payment_id` VARCHAR(50) DEFAULT NULL; ALTER TABLE `ope_pass` ADD INDEX `idx_stripe_payment` (`stripe_payment_id`); ``` ## Environnements - **DEV** : dva-geo sur IN3 - Base mise à jour ✅ - **REC** : rca-geo sur IN3 - Base mise à jour ✅ - **PROD** : pra-geo sur IN4 - À mettre à jour