Files
geo/api/docs/STRIPE-BACKEND-MIGRATION.md
pierre 2f5946a184 feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles
- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD)
  * DEV: Clés TEST Pierre (mode test)
  * REC: Clés TEST Client (mode test)
  * PROD: Clés LIVE Client (mode live)
- Ajout de la gestion des bases de données immeubles/bâtiments
  * Configuration buildings_database pour DEV/REC/PROD
  * Service BuildingService pour enrichissement des adresses
- Optimisations pages et améliorations ergonomie
- Mises à jour des dépendances Composer
- Nettoyage des fichiers obsolètes

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-09 18:26:27 +01:00

465 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔧 Migration Backend Stripe - Option A (Tout en 1)
## 📋 Objectif
Optimiser la création de compte Stripe Connect en **1 seule requête** côté Flutter qui crée :
1. Le compte Stripe Connect
2. La Location Terminal
3. Le lien d'onboarding
---
## 🗄️ 1. Modification de la base de données
### **Ajouter la colonne `stripe_location_id`**
```sql
ALTER TABLE amicales
ADD COLUMN stripe_location_id VARCHAR(255) NULL
AFTER stripe_id;
```
**Vérification** :
```sql
DESCRIBE amicales;
```
Doit afficher :
```
+-------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+-------+
| stripe_id | varchar(255) | YES | | NULL | |
| stripe_location_id| varchar(255) | YES | | NULL | |
+-------------------+--------------+------+-----+---------+-------+
```
---
## 🔧 2. Modification de l'endpoint `POST /stripe/accounts`
### **Fichier** : `app/Http/Controllers/StripeController.php` (ou similaire)
### **Méthode** : `createAccount()` ou `store()`
### **Code proposé** :
```php
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Models\Amicale;
/**
* Créer un compte Stripe Connect avec Location Terminal et lien d'onboarding
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function createStripeAccount(Request $request)
{
$request->validate([
'fk_entite' => 'required|integer|exists:amicales,id',
'return_url' => 'required|string|url',
'refresh_url' => 'required|string|url',
]);
$fkEntite = $request->fk_entite;
$amicale = Amicale::findOrFail($fkEntite);
// Vérifier si un compte existe déjà
if (!empty($amicale->stripe_id)) {
return $this->handleExistingAccount($amicale, $request);
}
DB::beginTransaction();
try {
// Configurer la clé Stripe (selon environnement)
\Stripe\Stripe::setApiKey(config('services.stripe.secret'));
// 1⃣ Créer le compte Stripe Connect Express
$account = \Stripe\Account::create([
'type' => 'express',
'country' => 'FR',
'email' => $amicale->email,
'business_type' => 'non_profit', // ou 'company' selon le cas
'business_profile' => [
'name' => $amicale->name,
'url' => config('app.url'),
],
'capabilities' => [
'card_payments' => ['requested' => true],
'transfers' => ['requested' => true],
],
]);
\Log::info('Stripe account created', [
'amicale_id' => $amicale->id,
'account_id' => $account->id,
]);
// 2⃣ Créer la Location Terminal pour Tap to Pay
$location = \Stripe\Terminal\Location::create([
'display_name' => $amicale->name,
'address' => [
'line1' => $amicale->adresse1 ?: 'Non renseigné',
'line2' => $amicale->adresse2,
'city' => $amicale->ville ?: 'Non renseigné',
'postal_code' => $amicale->code_postal ?: '00000',
'country' => 'FR',
],
], [
'stripe_account' => $account->id, // ← Important : Connect account
]);
\Log::info('Stripe Terminal Location created', [
'amicale_id' => $amicale->id,
'location_id' => $location->id,
]);
// 3⃣ Créer le lien d'onboarding
$accountLink = \Stripe\AccountLink::create([
'account' => $account->id,
'refresh_url' => $request->refresh_url,
'return_url' => $request->return_url,
'type' => 'account_onboarding',
]);
\Log::info('Stripe onboarding link created', [
'amicale_id' => $amicale->id,
'account_id' => $account->id,
]);
// 4⃣ Sauvegarder TOUT en base de données
$amicale->stripe_id = $account->id;
$amicale->stripe_location_id = $location->id;
$amicale->chk_stripe = true;
$amicale->save();
DB::commit();
// 5⃣ Retourner TOUTES les informations
return response()->json([
'success' => true,
'account_id' => $account->id,
'location_id' => $location->id,
'onboarding_url' => $accountLink->url,
'charges_enabled' => $account->charges_enabled,
'payouts_enabled' => $account->payouts_enabled,
'existing' => false,
'message' => 'Compte Stripe Connect créé avec succès',
], 201);
} catch (\Stripe\Exception\ApiErrorException $e) {
DB::rollBack();
\Log::error('Stripe API error', [
'amicale_id' => $amicale->id,
'error' => $e->getMessage(),
'type' => get_class($e),
]);
return response()->json([
'success' => false,
'message' => 'Erreur Stripe : ' . $e->getMessage(),
], 500);
} catch (\Exception $e) {
DB::rollBack();
\Log::error('Stripe account creation failed', [
'amicale_id' => $amicale->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
return response()->json([
'success' => false,
'message' => 'Erreur lors de la création du compte Stripe',
], 500);
}
}
/**
* Gérer le cas d'un compte Stripe existant
*/
private function handleExistingAccount(Amicale $amicale, Request $request)
{
try {
\Stripe\Stripe::setApiKey(config('services.stripe.secret'));
// Récupérer les infos du compte existant
$account = \Stripe\Account::retrieve($amicale->stripe_id);
// Si pas de location_id, la créer maintenant
if (empty($amicale->stripe_location_id)) {
$location = \Stripe\Terminal\Location::create([
'display_name' => $amicale->name,
'address' => [
'line1' => $amicale->adresse1 ?: 'Non renseigné',
'city' => $amicale->ville ?: 'Non renseigné',
'postal_code' => $amicale->code_postal ?: '00000',
'country' => 'FR',
],
], [
'stripe_account' => $amicale->stripe_id,
]);
$amicale->stripe_location_id = $location->id;
$amicale->save();
\Log::info('Location created for existing account', [
'amicale_id' => $amicale->id,
'location_id' => $location->id,
]);
}
// Si le compte est déjà complètement configuré
if ($account->charges_enabled && $account->payouts_enabled) {
return response()->json([
'success' => true,
'account_id' => $amicale->stripe_id,
'location_id' => $amicale->stripe_location_id,
'onboarding_url' => null,
'charges_enabled' => true,
'payouts_enabled' => true,
'existing' => true,
'message' => 'Compte Stripe déjà configuré et actif',
]);
}
// Compte existant mais configuration incomplète : générer un nouveau lien
$accountLink = \Stripe\AccountLink::create([
'account' => $amicale->stripe_id,
'refresh_url' => $request->refresh_url,
'return_url' => $request->return_url,
'type' => 'account_onboarding',
]);
return response()->json([
'success' => true,
'account_id' => $amicale->stripe_id,
'location_id' => $amicale->stripe_location_id,
'onboarding_url' => $accountLink->url,
'charges_enabled' => $account->charges_enabled,
'payouts_enabled' => $account->payouts_enabled,
'existing' => true,
'message' => 'Compte existant, configuration à finaliser',
]);
} catch (\Exception $e) {
\Log::error('Error handling existing account', [
'amicale_id' => $amicale->id,
'error' => $e->getMessage(),
]);
return response()->json([
'success' => false,
'message' => 'Erreur lors de la vérification du compte existant',
], 500);
}
}
```
---
## 📡 3. Modification de l'endpoint `GET /stripe/accounts/{id}/status`
Ajouter `location_id` dans la réponse :
```php
public function checkAccountStatus($amicaleId)
{
$amicale = Amicale::findOrFail($amicaleId);
if (empty($amicale->stripe_id)) {
return response()->json([
'has_account' => false,
'account_id' => null,
'location_id' => null,
'charges_enabled' => false,
'payouts_enabled' => false,
'onboarding_completed' => false,
]);
}
try {
\Stripe\Stripe::setApiKey(config('services.stripe.secret'));
$account = \Stripe\Account::retrieve($amicale->stripe_id);
return response()->json([
'has_account' => true,
'account_id' => $amicale->stripe_id,
'location_id' => $amicale->stripe_location_id, // ← Ajouté
'charges_enabled' => $account->charges_enabled,
'payouts_enabled' => $account->payouts_enabled,
'onboarding_completed' => $account->details_submitted,
]);
} catch (\Exception $e) {
return response()->json([
'has_account' => false,
'error' => $e->getMessage(),
], 500);
}
}
```
---
## 🗑️ 4. Endpoint à SUPPRIMER (devenu inutile)
### **❌ `POST /stripe/locations`**
Cet endpoint n'est plus nécessaire car la Location est créée automatiquement dans `POST /stripe/accounts`.
**Option 1** : Supprimer complètement
**Option 2** : Le garder pour compatibilité temporaire (si utilisé ailleurs)
---
## 📝 5. Modification du modèle Eloquent
### **Fichier** : `app/Models/Amicale.php`
Ajouter le champ `stripe_location_id` :
```php
protected $fillable = [
// ... autres champs
'stripe_id',
'stripe_location_id', // ← Ajouté
'chk_stripe',
];
protected $casts = [
'chk_stripe' => 'boolean',
];
```
---
## ✅ 6. Tests à effectuer
### **Test 1 : Nouvelle amicale**
```bash
curl -X POST http://localhost/api/stripe/accounts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {token}" \
-d '{
"fk_entite": 123,
"return_url": "https://app.geosector.fr/stripe/success",
"refresh_url": "https://app.geosector.fr/stripe/refresh"
}'
```
**Réponse attendue** :
```json
{
"success": true,
"account_id": "acct_xxxxxxxxxxxxx",
"location_id": "tml_xxxxxxxxxxxxx",
"onboarding_url": "https://connect.stripe.com/setup/...",
"charges_enabled": false,
"payouts_enabled": false,
"existing": false,
"message": "Compte Stripe Connect créé avec succès"
}
```
### **Test 2 : Amicale avec compte existant**
```bash
curl -X POST http://localhost/api/stripe/accounts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {token}" \
-d '{
"fk_entite": 456,
"return_url": "https://app.geosector.fr/stripe/success",
"refresh_url": "https://app.geosector.fr/stripe/refresh"
}'
```
**Réponse attendue** :
```json
{
"success": true,
"account_id": "acct_xxxxxxxxxxxxx",
"location_id": "tml_xxxxxxxxxxxxx",
"onboarding_url": null,
"charges_enabled": true,
"payouts_enabled": true,
"existing": true,
"message": "Compte Stripe déjà configuré et actif"
}
```
### **Test 3 : Vérifier la BDD**
```sql
SELECT id, name, stripe_id, stripe_location_id, chk_stripe
FROM amicales
WHERE id = 123;
```
**Résultat attendu** :
```
+-----+------------------+-------------------+-------------------+------------+
| id | name | stripe_id | stripe_location_id| chk_stripe |
+-----+------------------+-------------------+-------------------+------------+
| 123 | Pompiers Paris15 | acct_xxxxxxxxxxxxx| tml_xxxxxxxxxxxxx | 1 |
+-----+------------------+-------------------+-------------------+------------+
```
---
## 🚀 7. Déploiement
### **Étapes** :
1. ✅ Appliquer la migration SQL
2. ✅ Déployer le code Backend modifié
3. ✅ Tester avec Postman/curl
4. ✅ Déployer le code Flutter modifié
5. ✅ Tester le flow complet depuis l'app
---
## 📊 Comparaison Avant/Après
| Aspect | Avant | Après |
|--------|-------|-------|
| **Appels API Flutter → Backend** | 3 | 1 |
| **Appels Backend → Stripe** | 3 | 3 (mais atomiques) |
| **Latence totale** | ~3-5s | ~1-2s |
| **Gestion erreurs** | Complexe | Simplifié avec transaction |
| **Atomicité** | ❌ Non | ✅ Oui (DB transaction) |
| **Location ID sauvegardé** | ❌ Non | ✅ Oui |
---
## 🎯 Bénéfices
1.**Performance** : Latence divisée par 2-3
2.**Fiabilité** : Transaction BDD garantit la cohérence
3.**Simplicité** : Code Flutter plus simple
4.**Maintenance** : Moins de code à maintenir
5.**Traçabilité** : Logs centralisés côté Backend
6.**Tap to Pay prêt** : `location_id` disponible immédiatement
---
## ⚠️ Points d'attention
1. **Rollback** : Si la transaction échoue, rien n'est sauvegardé (bon comportement)
2. **Logs** : Bien logger chaque étape pour le debug
3. **Stripe Connect limitations** : Respecter les rate limits Stripe
4. **Tests** : Tester avec des comptes Stripe de test d'abord
---
## 📚 Ressources
- [Stripe Connect Express Accounts](https://stripe.com/docs/connect/express-accounts)
- [Stripe Terminal Locations](https://stripe.com/docs/terminal/fleet/locations)
- [Stripe Account Links](https://stripe.com/docs/connect/account-links)