veza/docs/archive/V0_603_RELEASE_SCOPE.md
senke dcf5aab783 docs: add RETROSPECTIVE_V0603.md
chore(release): archive v0.603 scope, create v0.604 placeholder
2026-02-23 22:59:59 +01:00

238 lines
11 KiB
Markdown

# V0.603 Release Scope — Transfer automatique, Commission & Stabilisation
**Statut** : En cours
**Phase** : 6+
**Prérequis** : v0.602 (taguée)
**Date cible** : TBD
**Estimation** : ~4 sprints (20 jours ouvrés)
**Précédente** : [v0.602](archive/V0_602_RELEASE_SCOPE.md)
---
## 1. Objectif
Boucler la chaîne de paiement vendeur en implémentant le **transfert automatique Stripe Connect** après paiement réussi (reporté depuis v0.602), ajouter la **commission plateforme configurable**, nettoyer la **dette technique** (triage des 210+ TODOs, archivage docs obsolètes), et renforcer les **tests du flux paiement**.
---
## 2. État actuel (post-v0.602)
| Composant | État | Détail |
|-----------|------|--------|
| **Stripe Connect onboarding** | ✅ Livré v0.602 | POST /sell/connect/onboard, GET /sell/balance |
| **seller_stripe_accounts** | ✅ Livré v0.602 | Migration 114, modèle GORM |
| **StripeConnectService.CreateTransfer** | ✅ Code existant | Méthode implémentée mais **jamais appelée** |
| **ProcessPaymentWebhook** | ⚠️ Incomplet | Crée les licences mais **ne déclenche pas les transferts** |
| **Commission plateforme** | ❌ Absente | Aucune config ni calcul de commission sur ventes marketplace |
| **Suivi des transferts** | ❌ Absent | Pas de table ni log des transferts effectués |
| **TODOs backend** | ⚠️ 210+ | Triage nécessaire (supprimer obsolètes, transformer restants en tickets) |
| **Docs obsolètes** | ⚠️ 82 fichiers | Archiver les docs pre-v0.501 dans docs/archive/ |
---
## 3. Lots
### Lot T1 — Transfer automatique après vente (Stripe Connect)
**Objectif** : Lorsqu'un paiement réussit (webhook Hyperswitch `succeeded`), transférer automatiquement la part vendeur vers son compte Stripe Connect.
| # | Tâche | Fichiers impactés | Effort |
|---|-------|--------------------|--------|
| T1-01 | Config commission plateforme — `PLATFORM_FEE_RATE` (default 0.10 = 10%) | `internal/config/config.go`, `.env.example` | S |
| T1-02 | Migration `115_seller_transfers.sql` — table de suivi des transferts | `migrations/115_seller_transfers.sql` | S |
| T1-03 | Modèle `SellerTransfer` — GORM model + BeforeCreate UUID hook | `internal/core/marketplace/models.go` | S |
| T1-04 | Interface `TransferService` — abstraction pour le service de transfert | `internal/core/marketplace/service.go` | S |
| T1-05 | Option `WithTransferService` — injection optionnelle dans marketplace.Service | `internal/core/marketplace/service.go` | S |
| T1-06 | Logique transfer dans `ProcessPaymentWebhook` — après création licences, regrouper items par vendeur, calculer montant net, appeler CreateTransfer, enregistrer SellerTransfer | `internal/core/marketplace/service.go` | L |
| T1-07 | Endpoint GET /sell/transfers — historique des transferts pour un vendeur | `internal/handlers/sell_handler.go`, routes | M |
| T1-08 | Frontend : SellerTransfersCard — liste des transferts dans SellerDashboard | `apps/web/src/features/seller/` | M |
| T1-09 | MSW handlers + Story — mock GET /sell/transfers | `apps/web/src/mocks/handlers.ts`, stories | S |
| T1-10 | Tests unitaires — ProcessPaymentWebhook avec transfert, multi-vendeur, vendeur sans Connect | `internal/core/marketplace/process_webhook_test.go` | M |
### Lot DT1 — Dette technique & Nettoyage
**Objectif** : Réduire la dette technique accumulée, archiver les docs obsolètes.
| # | Tâche | Fichiers impactés | Effort |
|---|-------|--------------------|--------|
| DT1-01 | Triage TODOs backend — supprimer obsolètes, convertir pertinents en issues GitHub | `veza-backend-api/internal/**` | M |
| DT1-02 | Archiver docs pre-v0.501 — déplacer dans docs/archive/ les scopes/plans obsolètes | `docs/*.md``docs/archive/` | S |
| DT1-03 | Mettre à jour PAYOUT_MANUAL.md — remplacer « procédure manuelle » par « transfert automatique v0.603 » | `docs/PAYOUT_MANUAL.md` | S |
| DT1-04 | Nettoyer code mort marketplace — imports inutilisés, variables non référencées | `internal/core/marketplace/` | S |
### Lot QA3 — Tests & Documentation de release
**Objectif** : Valider le flux transfer E2E, documenter, tagger.
| # | Tâche | Fichiers impactés | Effort |
|---|-------|--------------------|--------|
| QA3-01 | Test E2E transfer — flow complet : product → order → webhook succeeded → transfer créé → SellerTransfer enregistré | `internal/core/marketplace/` ou integration tests | M |
| QA3-02 | Test multi-vendeur — commande avec 2 produits de vendeurs différents → 2 transfers distincts | `internal/core/marketplace/process_webhook_test.go` | M |
| QA3-03 | Test vendeur sans Connect — paiement réussit, licences créées, transfer ignoré (log warning) | `internal/core/marketplace/process_webhook_test.go` | S |
| QA3-04 | Smoke test v0.603 — checklist complète | `docs/SMOKE_TEST_V0603.md` | S |
| QA3-05 | Mise à jour PROJECT_STATE, FEATURE_STATUS, CHANGELOG | `docs/` | S |
| QA3-06 | Rétrospective v0.603, archivage scope, placeholder v0.604, tag | `docs/`, Git | S |
---
## 4. Hors scope v0.603
| Élément | Version cible |
|---------|---------------|
| Go Live (streaming vidéo) | v0.703 |
| 2FA SMS / Passkeys | v0.104 |
| IaC (Terraform/Pulumi) | v0.801 |
| Migration React 19 | v0.604 |
| Payout scheduling (cron récurrent) | v0.604 |
| Dashboard transferts admin (vue admin des transferts plateforme) | v0.604 |
---
## 5. Détail technique — Lot T1
### 5.1 Commission plateforme
```go
// config.go
PlatformFeeRate float64 // PLATFORM_FEE_RATE, default 0.10 (10%)
```
Le calcul pour chaque item :
- `sellerAmount = item.Price * (1 - platformFeeRate)`
- Arrondi à l'unité inférieure (centimes) pour éviter les fractions
- Les montants sont en centimes (int64) pour Stripe
### 5.2 Migration 115_seller_transfers.sql
```sql
CREATE TABLE IF NOT EXISTS seller_transfers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
seller_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
stripe_transfer_id VARCHAR(255),
amount_cents BIGINT NOT NULL,
platform_fee_cents BIGINT NOT NULL,
currency VARCHAR(3) NOT NULL DEFAULT 'EUR',
status VARCHAR(50) NOT NULL DEFAULT 'pending',
error_message TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_seller_transfers_seller ON seller_transfers(seller_id);
CREATE INDEX idx_seller_transfers_order ON seller_transfers(order_id);
CREATE UNIQUE INDEX idx_seller_transfers_seller_order ON seller_transfers(seller_id, order_id);
```
### 5.3 Injection dans marketplace.Service
Le `StripeConnectService` sera injecté via le pattern `ServiceOption` existant :
```go
// TransferService abstraction pour découpler marketplace du service Stripe
type TransferService interface {
CreateTransfer(ctx context.Context, sellerUserID uuid.UUID, amount int64, currency, orderID string) error
}
// WithTransferService injecte le service de transfert
func WithTransferService(ts TransferService, feeRate float64) ServiceOption {
return func(s *Service) {
s.transferService = ts
s.platformFeeRate = feeRate
}
}
```
### 5.4 Logique transfer dans ProcessPaymentWebhook
Après la boucle de création des licences (dans le cas `succeeded`), ajouter :
1. Regrouper les items par `product.SellerID`
2. Pour chaque vendeur unique :
a. Calculer le montant total des items de ce vendeur
b. Soustraire la commission plateforme
c. Vérifier si le vendeur a un compte Connect actif (via DB)
d. Si oui : appeler `CreateTransfer`, enregistrer un `SellerTransfer` avec status `completed`
e. Si non : logger un warning, enregistrer un `SellerTransfer` avec status `skipped`
f. En cas d'erreur Stripe : enregistrer avec status `failed` + `error_message`, ne pas bloquer la commande
### 5.5 Gestion des erreurs
- Un échec de transfert **ne doit pas** annuler la commande ni les licences
- Le transfert est enregistré en DB avec son statut pour retry ultérieur
- Log `zap.Error` pour alerter l'équipe
---
## 6. Fichiers impactés (récapitulatif)
### Backend Go (nouveau)
| Fichier | Action |
|---------|--------|
| `migrations/115_seller_transfers.sql` | Nouveau — table suivi transferts |
### Backend Go (modifier)
| Fichier | Action |
|---------|--------|
| `internal/config/config.go` | Ajout `PlatformFeeRate` |
| `internal/core/marketplace/models.go` | Ajout modèle `SellerTransfer` |
| `internal/core/marketplace/service.go` | Interface `TransferService`, option `WithTransferService`, logique transfer dans `ProcessPaymentWebhook` |
| `internal/core/marketplace/process_webhook_test.go` | Tests transfer, multi-vendeur, vendeur sans Connect |
| `internal/handlers/sell_handler.go` | GET /sell/transfers |
| `.env.example` | Documenter `PLATFORM_FEE_RATE` |
### Frontend (modifier)
| Fichier | Action |
|---------|--------|
| `apps/web/src/features/seller/SellerDashboardView.tsx` | Carte transferts |
| `apps/web/src/mocks/handlers.ts` | Handler GET /sell/transfers |
### Documentation
| Fichier | Action |
|---------|--------|
| `docs/PAYOUT_MANUAL.md` | Mise à jour (procédure auto) |
| `docs/PROJECT_STATE.md` | Section v0.603 |
| `docs/FEATURE_STATUS.md` | Marketplace → transfert auto |
| `CHANGELOG.md` | Section v0.603 |
| `docs/SMOKE_TEST_V0603.md` | Nouveau |
| `docs/RETROSPECTIVE_V0603.md` | Nouveau (en fin de release) |
---
## 7. Critères d'acceptation globaux
- [ ] Transfer auto : paiement réussi (webhook) → licences créées + transfer Stripe exécuté
- [ ] Multi-vendeur : commande multi-produit → 1 transfer par vendeur distinct
- [ ] Commission : montant vendeur = prix - commission plateforme (configurable)
- [ ] Résilience : échec transfer ≠ échec commande — enregistré avec status `failed`
- [ ] Vendeur sans Connect : transfer ignoré (status `skipped`), log warning
- [ ] GET /sell/transfers : vendeur voit historique de ses transferts
- [ ] Frontend : carte transferts dans SellerDashboard
- [ ] Tests : unitaires ProcessWebhook + transfer, multi-vendeur, sans Connect
- [ ] TODOs : 50%+ des TODOs obsolètes supprimés, restants en issues
- [ ] Tag v0.603 créé
---
## 8. Risques
| Risque | Mitigation |
|--------|------------|
| Stripe Connect rate limits (250 req/s) | Batch par commande, pas par item. En prod, volume faible au démarrage |
| Erreur transfer après licences créées | Transfer enregistré `failed` en DB — retry manuel ou cron v0.604 |
| Float precision sur montants | Calcul en centimes (int64) uniquement, conversion float → int64 dès l'entrée |
| Commission non configurable par vendeur | MVP = taux unique global. Taux par vendeur/catégorie → v0.604 |
---
## 9. Références
- [RETROSPECTIVE_V0602.md](RETROSPECTIVE_V0602.md)
- [PAYOUT_MANUAL.md](PAYOUT_MANUAL.md)
- [SCOPE_CONTROL.md](SCOPE_CONTROL.md)
- [Stripe Connect Transfers](https://stripe.com/docs/connect/charges#transfer-availability)
- `veza-backend-api/internal/services/stripe_connect_service.go`
- `veza-backend-api/internal/core/marketplace/service.go`