Files
geo/api/docs/STRIPE-TAP-TO-PAY-FLOW.md
pierre 570a1fa1f0 feat: Version 3.3.4 - Nouvelle architecture pages, optimisations widgets Flutter et API
- Mise à jour VERSION vers 3.3.4
- Optimisations et révisions architecture API (deploy-api.sh, scripts de migration)
- Ajout documentation Stripe Tap to Pay complète
- Migration vers polices Inter Variable pour Flutter
- Optimisations build Android et nettoyage fichiers temporaires
- Amélioration système de déploiement avec gestion backups
- Ajout scripts CRON et migrations base de données

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 20:11:15 +02:00

12 KiB

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 :

{
  "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 :

{
  "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 :

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 :

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 :

{
  "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
{
  "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

{
  "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 :

// 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 :

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 :

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