# 🔧 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)
