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

343 lines
12 KiB
Markdown

# 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