- 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>
343 lines
12 KiB
Markdown
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 |