From c6c7c8b20f26647e4f48aa626b8486e2e5b1d8ba Mon Sep 17 00:00:00 2001 From: senke Date: Mon, 23 Feb 2026 23:21:06 +0100 Subject: [PATCH] docs: add v0.701 release scope, smoke test, and update references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 7 kickoff — Retry Transfers, Admin Dashboard & Deep Health. Absorbs v0.604 backlog. Updates SCOPE_CONTROL, PROJECT_STATE, .cursorrules. --- .cursorrules | 8 +- docs/PROJECT_STATE.md | 15 +- docs/SCOPE_CONTROL.md | 47 ++--- docs/SMOKE_TEST_V0701.md | 131 ++++++++++++ docs/V0_701_RELEASE_SCOPE.md | 375 +++++++++++++++++++++++++++++++++++ 5 files changed, 544 insertions(+), 32 deletions(-) create mode 100644 docs/SMOKE_TEST_V0701.md create mode 100644 docs/V0_701_RELEASE_SCOPE.md diff --git a/.cursorrules b/.cursorrules index 92e9df885..ffba7783a 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1,10 +1,10 @@ # Règles de Développement UI - Projet SaaS -## 0. Scope v0.603 (priorité absolue) +## 0. Scope v0.701 (priorité absolue) -- **Référence** : `docs/V0_603_RELEASE_SCOPE.md` et `docs/SCOPE_CONTROL.md` -- Avant toute modification : vérifier si le changement est **dans le scope v0.603** -- **Autorisé v0.603** : à définir (voir V0_603_RELEASE_SCOPE.md) +- **Référence** : `docs/V0_701_RELEASE_SCOPE.md` et `docs/SCOPE_CONTROL.md` +- Avant toute modification : vérifier si le changement est **dans le scope v0.701** +- **Autorisé v0.701** : Retry transfers, Admin transfers dashboard, Deep health checks, API documentation (voir V0_701_RELEASE_SCOPE.md) - **Interdit** : nouvelles routes/pages hors scope, nouvelles dépendances (sauf correctif sécurité) - En cas de doute : ne pas ajouter. Créer une issue pour une version ultérieure. diff --git a/docs/PROJECT_STATE.md b/docs/PROJECT_STATE.md index daf0a0557..7bf2e09ff 100644 --- a/docs/PROJECT_STATE.md +++ b/docs/PROJECT_STATE.md @@ -8,10 +8,10 @@ | Élément | Valeur | |---------|--------| -| **Dernier tag** | v0.602 | +| **Dernier tag** | v0.603 | | **Branche courante** | `main` | -| **Phase** | Phase 6+ Payout, Dette Technique & Tests E2E — v0.602 livrée | -| **Prochaine version** | v0.603 | +| **Phase** | Phase 7 — Production Readiness | +| **Prochaine version** | v0.701 | --- @@ -122,8 +122,13 @@ - QA1 : Tests, documentation - Référence : [V0_503_RELEASE_SCOPE.md](V0_503_RELEASE_SCOPE.md) -### Prochaine version (v0.604) -- À définir — Référence : [V0_604_RELEASE_SCOPE.md](V0_604_RELEASE_SCOPE.md) (placeholder) +### Prochaine version (v0.701) +- Retry automatique des transferts échoués (cron + backoff exponentiel) +- Dashboard admin des transferts (GET /admin/transfers, retry manuel) +- Deep health checks (GET /health/deep — DB, Redis, S3, disk) +- Startup config validation +- Documentation API Reference +- Référence : [V0_701_RELEASE_SCOPE.md](V0_701_RELEASE_SCOPE.md) --- diff --git a/docs/SCOPE_CONTROL.md b/docs/SCOPE_CONTROL.md index a1059ab89..0009af1f5 100644 --- a/docs/SCOPE_CONTROL.md +++ b/docs/SCOPE_CONTROL.md @@ -1,23 +1,23 @@ # Contrôle du scope — Anti-scope-creep **Objectif** : Éviter toute dérive de scope. Chaque modification doit être intentionnelle et traçable. -**Référence active** : [V0_604_RELEASE_SCOPE.md](V0_604_RELEASE_SCOPE.md) +**Référence active** : [V0_701_RELEASE_SCOPE.md](V0_701_RELEASE_SCOPE.md) **Version précédente** : [V0_603_RELEASE_SCOPE.md](archive/V0_603_RELEASE_SCOPE.md) --- ## 1. Règle d'or -> **Avant d'ajouter quoi que ce soit : vérifier si c'est dans le scope v0.603.** +> **Avant d'ajouter quoi que ce soit : vérifier si c'est dans le scope v0.701.** > Si non → ne pas ajouter. Créer un ticket pour une version ultérieure. --- -## 2. Pendant la phase v0.603 (jusqu'au tag) +## 2. Pendant la phase v0.701 (jusqu'au tag) ### 2.1 Autorisé -- **Corrections de bugs** sur les features IN SCOPE v0.603 +- **Corrections de bugs** sur les features IN SCOPE v0.701 - **Stabilisation** : tests, refactoring sans changement de comportement - **Nettoyage** : suppression de code mort, consolidation - **Documentation** : mise à jour des docs existantes @@ -26,20 +26,20 @@ ### 2.2 Interdit -- **Nouvelles features** hors scope v0.603 +- **Nouvelles features** hors scope v0.701 - **Nouvelles routes** ou pages hors scope - **Nouvelles dépendances** (sauf correctif sécurité) - **Changements de comportement** sur les features HORS SCOPE -- **"Améliorations"** non liées à un bug identifié ou une feature IN SCOPE v0.603 +- **"Améliorations"** non liées à un bug identifié ou une feature IN SCOPE v0.701 ### 2.3 Cas limite | Situation | Action | |-----------|--------| -| Bug dans une feature HORS SCOPE | Corriger si blocant pour une feature IN SCOPE v0.603. Sinon : ticket pour plus tard. | +| Bug dans une feature HORS SCOPE | Corriger si blocant pour une feature IN SCOPE v0.701. Sinon : ticket pour plus tard. | | Dépendance obsolète/vulnérable | Mettre à jour. Documenter dans la PR. | | Refactoring qui change une API interne | Autorisé si 0 impact sur le contrat public et tests passent. | -| "Petite amélioration UX" | **Non.** Créer un ticket pour v0.502+. | +| "Petite amélioration UX" | **Non.** Créer un ticket pour v0.702+. | --- @@ -47,13 +47,13 @@ ### 3.1 Checklist pré-commit (dans la tête) -1. **Mon changement modifie-t-il une feature IN SCOPE v0.603 ?** +1. **Mon changement modifie-t-il une feature IN SCOPE v0.701 ?** - Oui → Continuer. S'assurer qu'il n'y a pas de régression. - Non → **STOP.** Est-ce une correction de bug ? Si oui, la feature est-elle IN SCOPE ? 2. **Mon changement ajoute-t-il du code ?** - - Nouvelle route, nouveau composant, nouveau service → Vérifier V0_603_RELEASE_SCOPE. Si hors scope → **STOP.** - - Correction, refactoring, test → OK si lié à une feature IN SCOPE v0.603. + - Nouvelle route, nouveau composant, nouveau service → Vérifier V0_701_RELEASE_SCOPE. Si hors scope → **STOP.** + - Correction, refactoring, test → OK si lié à une feature IN SCOPE v0.701. 3. **Mes tests passent-ils ?** - `npm test -- --run` (frontend) @@ -81,7 +81,7 @@ Format : `type(scope): description` Dans chaque PR, le relecteur doit valider : -- [ ] Le changement est dans le scope v0.603 (voir [V0_603_RELEASE_SCOPE.md](V0_603_RELEASE_SCOPE.md)) +- [ ] Le changement est dans le scope v0.701 (voir [V0_701_RELEASE_SCOPE.md](V0_701_RELEASE_SCOPE.md)) - [ ] Aucune nouvelle feature ajoutée - [ ] Aucune régression sur les flows critiques - [ ] Les tests passent @@ -92,7 +92,7 @@ Dans chaque PR, le relecteur doit valider : Une PR sera rejetée si : - Elle ajoute une nouvelle route, page ou feature -- Elle modifie le comportement d'une feature HORS SCOPE v0.603 (sauf correctif bug critique) +- Elle modifie le comportement d'une feature HORS SCOPE v0.701 (sauf correctif bug critique) - Les tests échouent - Elle introduit une dépendance non justifiée @@ -104,16 +104,16 @@ Une PR sera rejetée si : Utiliser le template [Feature request](.github/ISSUE_TEMPLATE/feature_request.md) avec : -- **Alignement scope** : cocher "Hors scope v0.603 — pour v0.7+" +- **Alignement scope** : cocher "Hors scope v0.701 — pour v0.702+" - **Justification** : pourquoi cette feature est nécessaire - **Effort estimé** : S / M / L / XL -- **Dépendances** : quelles features v0.601 doivent être stables avant +- **Dépendances** : quelles features v0.701 doivent être stables avant ### 5.2 Workflow 1. Créer une issue avec le template -2. **Ne pas implémenter** tant que v0.603 n'est pas taguée -3. Une fois v0.603 stable, prioriser les issues post-v0.603 dans le scope suivant +2. **Ne pas implémenter** tant que v0.701 n'est pas taguée +3. Une fois v0.701 stable, prioriser les issues post-v0.701 dans le scope suivant --- @@ -125,14 +125,14 @@ Si une vulnérabilité critique est identifiée : - Correctif autorisé **immédiatement** - Documenter dans la PR -- Pas besoin d'être dans le scope v0.502 +- Pas besoin d'être dans le scope v0.701 ### 6.2 Blocage production Si un bug bloque un déploiement ou un flow critique : - Correctif autorisé -- La feature concernée doit être IN SCOPE v0.603 ou dépendance directe d'une feature IN SCOPE +- La feature concernée doit être IN SCOPE v0.701 ou dépendance directe d'une feature IN SCOPE ### 6.3 Décision collégiale @@ -140,13 +140,13 @@ Pour tout cas ambigu : - Ouvrir une issue "Scope clarification" - Décision documentée dans l'issue -- Mise à jour de V0_502_RELEASE_SCOPE.md si le scope est étendu (exception rare) +- Mise à jour de V0_701_RELEASE_SCOPE.md si le scope est étendu (exception rare) --- ## 7. Après le tag d'une version -1. **Créer** le document de scope de la version suivante (ex: `V0_502_RELEASE_SCOPE.md`) +1. **Créer** le document de scope de la version suivante (ex: `V0_702_RELEASE_SCOPE.md`) 2. **Définir** explicitement les nouvelles features autorisées 3. **Mettre à jour** la référence active dans ce document (section header) 4. **Reprendre** ce processus avec le nouveau document de scope @@ -172,12 +172,13 @@ Pour tout cas ambigu : - v0.601 : Phase 6 — Production Readiness & Commerce — taguée - v0.602 : Phase 6+ — Payout, Dette Technique & Tests E2E — taguée - v0.603 : Phase 6+ — Transfer automatique Stripe Connect, Commission plateforme, Dette technique — taguée -- v0.604 : Phase 6+ — en planification +- v0.604 : Phase 6+ — absorbé par v0.701 +- v0.701 : Phase 7 — Retry Transfers, Admin Dashboard, Deep Health — en cours --- ## 8. Rappel pour les contributeurs - **Cursor / IA** : Les règles dans `.cursorrules` rappellent de vérifier le scope avant toute modification. -- **Humains** : Lire [V0_603_RELEASE_SCOPE.md](V0_603_RELEASE_SCOPE.md) avant de coder. +- **Humains** : Lire [V0_701_RELEASE_SCOPE.md](V0_701_RELEASE_SCOPE.md) avant de coder. - **En doute ?** Ouvrir une issue "Scope clarification" plutôt que de coder. diff --git a/docs/SMOKE_TEST_V0701.md b/docs/SMOKE_TEST_V0701.md new file mode 100644 index 000000000..41ba4ccc3 --- /dev/null +++ b/docs/SMOKE_TEST_V0701.md @@ -0,0 +1,131 @@ +# Smoke Test v0.701 — Retry Transfers, Admin Dashboard & Deep Health + +**Objectif** : Valider toutes les fonctionnalités v0.701 et vérifier l'absence de régressions. + +--- + +## Prérequis + +- [ ] Backend Go compile sans erreur (`go build ./...`) +- [ ] Frontend build OK (`npm run build` dans `apps/web`) +- [ ] Base de données migrée (migration 116 appliquée) +- [ ] Variables d'environnement configurées : `TRANSFER_RETRY_ENABLED`, `TRANSFER_RETRY_MAX`, `TRANSFER_RETRY_INTERVAL_MIN` + +--- + +## 1. Transfer Retry Worker (Lot R1) + +### 1.1 Retry automatique +- [ ] Créer un SellerTransfer avec status `failed` en DB +- [ ] Démarrer le backend avec `TRANSFER_RETRY_ENABLED=true` +- [ ] Le worker retente le transfert dans le délai configuré +- [ ] En cas de succès : status passe à `completed`, retry_count incrémenté +- [ ] En cas d'échec : retry_count incrémenté, next_retry_at calculé (backoff exponentiel) + +### 1.2 Max retries +- [ ] Un transfert avec retry_count >= max_retries n'est plus retenté +- [ ] Status passe à `permanently_failed` + +### 1.3 Backoff exponentiel +- [ ] Délai entre retries augmente : base * 2^retry_count +- [ ] next_retry_at est correctement calculé et stocké + +### 1.4 Prometheus metrics +- [ ] `veza_transfer_retry_total` est incrémenté à chaque tentative +- [ ] `veza_transfer_retry_success` est incrémenté en cas de succès +- [ ] `veza_transfer_retry_failures` est incrémenté en cas d'échec + +--- + +## 2. Admin Transfers Dashboard (Lot A1) + +### 2.1 Backend +- [ ] `GET /admin/transfers` retourne la liste paginée (200) +- [ ] Filtre par status fonctionne (`?status=failed`) +- [ ] Filtre par seller_id fonctionne (`?seller_id=uuid`) +- [ ] Filtre par date range fonctionne (`?from=...&to=...`) +- [ ] Pagination fonctionne (`?limit=10&offset=0`) +- [ ] Non-admin reçoit 403 + +### 2.2 Retry manuel +- [ ] `POST /admin/transfers/:id/retry` retry un transfert failed (200) +- [ ] Transfer non-failed retourne erreur appropriée (400) +- [ ] Transfer inexistant retourne 404 +- [ ] Non-admin reçoit 403 + +### 2.3 Frontend +- [ ] Route `/admin/transfers` est accessible pour un admin +- [ ] Table affiche : seller, order, montant net, fee, status, retry_count, date +- [ ] Badge de status coloré (completed=vert, failed=rouge, permanently_failed=gris) +- [ ] Bouton Retry visible uniquement sur les transferts `failed` +- [ ] Clic Retry → appel API → table rafraîchie +- [ ] État Loading (skeleton) visible au chargement +- [ ] État Empty visible si aucun transfert + +--- + +## 3. Deep Health Checks (Lot H1) + +### 3.1 Endpoint +- [ ] `GET /health/deep` retourne JSON avec status global et par service +- [ ] DB healthy → ping réussi, latency_ms renseigné +- [ ] Redis healthy → ping réussi, latency_ms renseigné +- [ ] S3 indisponible → status `degraded` (pas unhealthy), error renseigné +- [ ] DB indisponible → status `unhealthy` + +### 3.2 Startup validation +- [ ] JWT_SECRET vide → log fatal + exit +- [ ] DATABASE_URL invalide → log fatal + exit +- [ ] PLATFORM_FEE_RATE > 1 → log fatal + exit +- [ ] STRIPE_CONNECT_ENABLED=true sans STRIPE_SECRET_KEY → log warning +- [ ] En mode dev : warnings au lieu de fatals pour configs non-critiques + +### 3.3 Prometheus +- [ ] `veza_health_check_status` gauge exposé par service (1=healthy, 0=unhealthy) + +--- + +## 4. Documentation API (Lot D1) + +- [ ] `docs/API_REFERENCE.md` existe et couvre les endpoints clés +- [ ] Exemples curl pour auth (login, register) +- [ ] Exemples curl pour marketplace (products, orders) +- [ ] Exemples curl pour admin (transfers) +- [ ] Exemples curl pour health (deep) +- [ ] `docs/MIGRATIONS.md` inclut migration 116 + +--- + +## 5. Flux existants (non-régressions) + +### 5.1 Commerce +- [ ] Création de produit marketplace fonctionne +- [ ] Création de commande fonctionne +- [ ] Webhook payment succeeded → licences créées + transfer déclenché + +### 5.2 Seller +- [ ] GET /sell/transfers retourne l'historique du vendeur (inchangé) +- [ ] SellerDashboard affiche les transferts (inchangé) + +### 5.3 Auth +- [ ] Login/register fonctionnent +- [ ] JWT refresh fonctionne + +### 5.4 Backend +- [ ] `go test ./...` — tous les tests passent +- [ ] GET /health retourne 200 (endpoint basique inchangé) + +### 5.5 Frontend +- [ ] `npm run build` — build OK +- [ ] Storybook : AdminTransfersView stories sans erreur + +--- + +## 6. Documentation & Release + +- [ ] CHANGELOG.md mis à jour avec section v0.701 +- [ ] PROJECT_STATE.md mis à jour +- [ ] FEATURE_STATUS.md mis à jour +- [ ] SCOPE_CONTROL.md pointe vers v0.702 (prochaine) +- [ ] RETROSPECTIVE_V0701.md créé +- [ ] Tag `v0.701` créé diff --git a/docs/V0_701_RELEASE_SCOPE.md b/docs/V0_701_RELEASE_SCOPE.md new file mode 100644 index 000000000..2e7892d3b --- /dev/null +++ b/docs/V0_701_RELEASE_SCOPE.md @@ -0,0 +1,375 @@ +# V0.701 Release Scope — Retry Transfers, Admin Dashboard & Deep Health + +**Statut** : En cours +**Phase** : 7 (Production Readiness) +**Prérequis** : v0.603 (taguée) +**Date cible** : TBD +**Estimation** : ~3 sprints (15 jours ouvrés) +**Précédente** : [v0.603](archive/V0_603_RELEASE_SCOPE.md) + +--- + +## 1. Objectif + +Première version de la Phase 7 (Production Readiness). Comble les lacunes identifiées en v0.603 : **retry automatique des transferts échoués** (cron + backoff exponentiel), **dashboard admin des transferts** (visibilité plateforme), **deep health checks** (validation infrastructure), et **documentation API**. Absorbe le backlog v0.604 pertinent. + +--- + +## 2. État actuel (post-v0.603) + +| Composant | État | Détail | +|-----------|------|--------| +| **Transfer automatique Stripe Connect** | ✅ Livré v0.603 | processSellerTransfers dans ProcessPaymentWebhook | +| **seller_transfers** | ✅ Livré v0.603 | Migration 115, modèle SellerTransfer | +| **GET /sell/transfers** | ✅ Livré v0.603 | Historique vendeur de ses transferts | +| **Retry transferts échoués** | ❌ Absent | Transferts `failed` restent en DB sans retry | +| **Admin transfers dashboard** | ❌ Absent | Aucune vue admin des transferts plateforme | +| **Deep health checks** | ❌ Absent | GET /health basique (DB uniquement), pas de Redis/S3/config | +| **Documentation API** | ❌ Absent | Aucune spec OpenAPI, endpoints documentés uniquement dans le code | +| **Validation config startup** | ❌ Absent | Pas de vérification des configs critiques au démarrage | + +--- + +## 3. Lots + +### Lot R1 — Retry automatique des transferts échoués + +**Objectif** : Retenter automatiquement les transferts Stripe Connect qui ont échoué, avec backoff exponentiel et limite de tentatives. + +| # | Tâche | Fichiers impactés | Effort | +|---|-------|--------------------|--------| +| R1-01 | Migration `116_seller_transfers_retry.sql` — ajout colonnes retry_count, max_retries, next_retry_at | `migrations/116_seller_transfers_retry.sql` | S | +| R1-02 | Mise à jour modèle `SellerTransfer` — ajout champs retry | `internal/core/marketplace/models.go` | S | +| R1-03 | Config `TRANSFER_RETRY_MAX` et `TRANSFER_RETRY_INTERVAL_MIN` | `internal/config/config.go`, `.env.example` | S | +| R1-04 | `TransferRetryWorker` — goroutine background avec ticker, query transfers `failed` et `retry_count < max`, calcul backoff exponentiel, appel CreateTransfer, update status | `internal/core/marketplace/transfer_retry.go` (nouveau) | L | +| R1-05 | Démarrage du worker dans `cmd/server/main.go` — optionnel via config `TRANSFER_RETRY_ENABLED` | `cmd/server/main.go` | S | +| R1-06 | Prometheus metrics — `veza_transfer_retry_total`, `veza_transfer_retry_success`, `veza_transfer_retry_failures` | `internal/monitoring/metrics.go` | S | +| R1-07 | Tests unitaires retry — retry success, max retries atteint, backoff calculation | `internal/core/marketplace/transfer_retry_test.go` (nouveau) | M | + +### Lot A1 — Admin Transfers Dashboard + +**Objectif** : Offrir aux administrateurs une vue complète des transferts plateforme (tous vendeurs), avec filtres et action de retry manuel. + +| # | Tâche | Fichiers impactés | Effort | +|---|-------|--------------------|--------| +| A1-01 | `GET /admin/transfers` — liste paginée de tous les SellerTransfer (filtres: status, seller_id, date range) | `internal/handlers/admin_handler.go` (nouveau ou existant), routes | M | +| A1-02 | `POST /admin/transfers/:id/retry` — retry manuel d'un transfert failed | `internal/handlers/admin_handler.go`, routes | S | +| A1-03 | Route admin dans `routes_core.go` — ajout dans le groupe `/admin` protégé | `internal/api/routes_core.go` | S | +| A1-04 | Frontend `AdminTransfersView` — table avec colonnes (seller, order, montant, fee, status, retry_count, date), badge status, bouton Retry | `apps/web/src/components/admin/AdminTransfersView.tsx` (nouveau) | L | +| A1-05 | Frontend route `/admin/transfers` — ajout dans routeConfig | `apps/web/src/router/routeConfig.tsx` | S | +| A1-06 | Service frontend `getAdminTransfers`, `retryAdminTransfer` | `apps/web/src/services/commerceService.ts` | S | +| A1-07 | MSW handlers — mock GET /admin/transfers, POST /admin/transfers/:id/retry | `apps/web/src/mocks/handlers-marketplace.ts` | S | +| A1-08 | Story `AdminTransfersView.stories.tsx` — états Loading, Default, Empty, WithFailedTransfers | `apps/web/src/components/admin/AdminTransfersView.stories.tsx` (nouveau) | M | +| A1-09 | Tests unitaires — admin handler GET + POST retry, authorization check | `internal/handlers/admin_handler_test.go` (nouveau) | M | + +### Lot H1 — Deep Health Checks & Startup Validation + +**Objectif** : Fournir un endpoint de santé détaillé pour les load balancers et le monitoring, et valider la configuration critique au démarrage. + +| # | Tâche | Fichiers impactés | Effort | +|---|-------|--------------------|--------| +| H1-01 | `GET /health/deep` — check DB (ping), Redis (ping), S3 (HEAD bucket), disk space, config validity | `internal/api/routes_health.go` (nouveau) | M | +| H1-02 | Réponse structurée JSON — status global (healthy/degraded/unhealthy) + status individuel par service | `internal/api/routes_health.go` | S | +| H1-03 | Startup config validation — vérifier au boot : JWT_SECRET non vide, DATABASE_URL valide, PLATFORM_FEE_RATE dans [0,1], ports non conflictuels | `internal/config/validate.go` (nouveau) | M | +| H1-04 | Prometheus metrics — `veza_health_check_status` gauge (1=healthy, 0=unhealthy) par service | `internal/monitoring/metrics.go` | S | +| H1-05 | Tests unitaires — deep health check avec services mockés (all healthy, partial degradation, all down) | `internal/api/routes_health_test.go` (nouveau) | M | + +### Lot D1 — Documentation API & Nettoyage + +**Objectif** : Documenter les endpoints API pour faciliter l'intégration et réduire la dette documentaire. + +| # | Tâche | Fichiers impactés | Effort | +|---|-------|--------------------|--------| +| D1-01 | `docs/API_REFERENCE.md` — documentation des endpoints clés (auth, marketplace, seller, admin, health) avec exemples curl | `docs/API_REFERENCE.md` (nouveau) | L | +| D1-02 | Mise à jour `docs/MIGRATIONS.md` — ajouter migration 116 | `docs/MIGRATIONS.md` | S | +| D1-03 | Archiver `V0_604_RELEASE_SCOPE.md` — placeholder absorbé par v0.701 | `docs/V0_604_RELEASE_SCOPE.md` → `docs/archive/` | S | + +### Lot QA1 — Tests & Release + +**Objectif** : Valider les nouveaux composants, documenter, tagger. + +| # | Tâche | Fichiers impactés | Effort | +|---|-------|--------------------|--------| +| QA1-01 | Test E2E retry — transfer failed → worker retry → status completed | Tests | M | +| QA1-02 | Test admin endpoint — authorization check (non-admin = 403) | Tests | S | +| QA1-03 | Smoke test v0.701 — checklist complète | `docs/SMOKE_TEST_V0701.md` | S | +| QA1-04 | Mise à jour PROJECT_STATE, FEATURE_STATUS, CHANGELOG | `docs/` | S | +| QA1-05 | Rétrospective v0.701, archivage scope, placeholder v0.702, tag | `docs/`, Git | S | + +--- + +## 4. Hors scope v0.701 + +| Élément | Version cible | +|---------|---------------| +| Go Live (streaming vidéo) | v0.703 | +| 2FA SMS / Passkeys | v0.104 | +| IaC (Terraform/Pulumi) | v0.801 | +| Payout scheduling (cron récurrent) | v0.702 | +| Commission par vendeur/catégorie | v0.702 | +| OpenAPI/Swagger auto-génération (swaggo) | v0.702 | +| Load testing service (k6/vegeta intégré) | v0.703 | +| Frontend E2E test fixes (Playwright) | v0.702 | + +--- + +## 5. Détail technique — Lot R1 + +### 5.1 Migration 116_seller_transfers_retry.sql + +```sql +ALTER TABLE seller_transfers + ADD COLUMN IF NOT EXISTS retry_count INTEGER NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS max_retries INTEGER NOT NULL DEFAULT 3, + ADD COLUMN IF NOT EXISTS next_retry_at TIMESTAMPTZ; + +CREATE INDEX IF NOT EXISTS idx_seller_transfers_retry + ON seller_transfers(status, next_retry_at) + WHERE status = 'failed' AND next_retry_at IS NOT NULL; +``` + +### 5.2 Modèle SellerTransfer mis à jour + +```go +type SellerTransfer struct { + // ... champs existants v0.603 ... + RetryCount int `gorm:"default:0" json:"retry_count"` + MaxRetries int `gorm:"default:3" json:"max_retries"` + NextRetryAt *time.Time `json:"next_retry_at,omitempty"` +} +``` + +### 5.3 TransferRetryWorker + +```go +type TransferRetryWorker struct { + db *gorm.DB + transferService TransferService + logger *zap.Logger + interval time.Duration // ticker interval (e.g. 5 min) + maxRetries int + baseDelay time.Duration // base delay for exponential backoff (e.g. 5 min) +} + +func (w *TransferRetryWorker) Start(ctx context.Context) { + ticker := time.NewTicker(w.interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + w.processRetries(ctx) + } + } +} + +func (w *TransferRetryWorker) processRetries(ctx context.Context) { + var transfers []SellerTransfer + w.db.Where("status = ? AND retry_count < max_retries AND (next_retry_at IS NULL OR next_retry_at <= ?)", + "failed", time.Now()). + Find(&transfers) + + for _, t := range transfers { + err := w.transferService.CreateTransfer(ctx, t.SellerID, t.AmountCents, t.Currency, t.OrderID.String()) + if err != nil { + t.RetryCount++ + delay := w.baseDelay * time.Duration(1<= t.MaxRetries { + t.Status = "permanently_failed" + } + t.ErrorMessage = err.Error() + } else { + t.Status = "completed" + t.NextRetryAt = nil + } + w.db.Save(&t) + } +} +``` + +### 5.4 Backoff exponentiel + +| Retry | Délai (base = 5 min) | +|-------|---------------------| +| 1 | 10 min | +| 2 | 20 min | +| 3 | 40 min (max atteint → `permanently_failed`) | + +### 5.5 Statuts des transferts + +| Status | Description | +|--------|-------------| +| `pending` | Transfert en cours de traitement | +| `completed` | Transfert Stripe réussi | +| `failed` | Transfert échoué, retry possible | +| `permanently_failed` | Max retries atteint, intervention manuelle requise | +| `skipped` | Vendeur sans compte Connect | + +--- + +## 6. Détail technique — Lot H1 + +### 6.1 GET /health/deep — Réponse + +```json +{ + "status": "healthy", + "timestamp": "2026-02-23T12:00:00Z", + "services": { + "database": {"status": "healthy", "latency_ms": 2}, + "redis": {"status": "healthy", "latency_ms": 1}, + "s3": {"status": "degraded", "error": "bucket unreachable"}, + "disk": {"status": "healthy", "free_gb": 42.5} + }, + "config": { + "jwt_secret_set": true, + "stripe_connect_enabled": true, + "platform_fee_rate": 0.10, + "transfer_retry_enabled": true + } +} +``` + +### 6.2 Logique de status global + +- `healthy` : tous les services OK +- `degraded` : un ou plusieurs services non-critiques en erreur (S3, Redis) +- `unhealthy` : service critique en erreur (DB) + +### 6.3 Startup validation + +Au démarrage du serveur (`cmd/server/main.go`), avant d'ouvrir les ports : + +1. `JWT_SECRET` non vide et longueur >= 32 caractères +2. `DATABASE_URL` parse OK et ping réussit +3. `PLATFORM_FEE_RATE` dans [0, 1] +4. Si `STRIPE_CONNECT_ENABLED=true` : `STRIPE_SECRET_KEY` et `STRIPE_CONNECT_CLIENT_ID` non vides +5. Si `TRANSFER_RETRY_ENABLED=true` : `TRANSFER_RETRY_MAX` >= 1 + +En cas d'échec critique (DB, JWT_SECRET) : log fatal + exit. En cas d'échec non-critique : log warning, continuer. + +--- + +## 7. Fichiers impactés (récapitulatif) + +### Backend Go (nouveau) + +| Fichier | Action | +|---------|--------| +| `migrations/116_seller_transfers_retry.sql` | Nouveau — colonnes retry pour seller_transfers | +| `internal/core/marketplace/transfer_retry.go` | Nouveau — TransferRetryWorker goroutine | +| `internal/core/marketplace/transfer_retry_test.go` | Nouveau — tests retry | +| `internal/api/routes_health.go` | Nouveau — GET /health/deep | +| `internal/api/routes_health_test.go` | Nouveau — tests deep health | +| `internal/config/validate.go` | Nouveau — startup config validation | +| `internal/handlers/admin_handler.go` | Nouveau — GET /admin/transfers, POST retry | +| `internal/handlers/admin_handler_test.go` | Nouveau — tests admin handler | + +### Backend Go (modifier) + +| Fichier | Action | +|---------|--------| +| `internal/config/config.go` | Ajout `TransferRetryEnabled`, `TransferRetryMax`, `TransferRetryIntervalMin` | +| `internal/core/marketplace/models.go` | Ajout champs retry à SellerTransfer | +| `internal/api/routes_core.go` | Ajout routes admin transfers | +| `internal/monitoring/metrics.go` | Ajout métriques retry + health | +| `cmd/server/main.go` | Démarrage TransferRetryWorker, startup validation | +| `.env.example` | Documenter variables retry | + +### Frontend (nouveau) + +| Fichier | Action | +|---------|--------| +| `apps/web/src/components/admin/AdminTransfersView.tsx` | Nouveau — dashboard admin transferts | +| `apps/web/src/components/admin/AdminTransfersView.stories.tsx` | Nouveau — stories | + +### Frontend (modifier) + +| Fichier | Action | +|---------|--------| +| `apps/web/src/services/commerceService.ts` | Ajout getAdminTransfers, retryAdminTransfer | +| `apps/web/src/router/routeConfig.tsx` | Ajout route /admin/transfers | +| `apps/web/src/mocks/handlers-marketplace.ts` | Handlers admin transfers | + +### Documentation + +| Fichier | Action | +|---------|--------| +| `docs/API_REFERENCE.md` | Nouveau — référence API | +| `docs/SMOKE_TEST_V0701.md` | Nouveau — checklist validation | +| `docs/MIGRATIONS.md` | Ajout migration 116 | +| `docs/PROJECT_STATE.md` | Section v0.701 | +| `docs/FEATURE_STATUS.md` | Mise à jour | +| `CHANGELOG.md` | Section v0.701 | +| `docs/RETROSPECTIVE_V0701.md` | Nouveau (fin de release) | + +--- + +## 8. Critères d'acceptation globaux + +- [ ] Retry auto : transfer `failed` → worker retry → status `completed` après succès +- [ ] Backoff exponentiel : délai entre retries augmente (5min, 10min, 20min) +- [ ] Max retries : après 3 échecs → `permanently_failed`, plus de retry +- [ ] Admin GET /admin/transfers : liste paginée de tous les transferts, filtres status/seller/date +- [ ] Admin POST /admin/transfers/:id/retry : retry manuel, 403 si non-admin +- [ ] Frontend AdminTransfersView : table avec badge status, bouton Retry, états Loading/Empty +- [ ] Deep health : GET /health/deep retourne status de DB, Redis, S3, disk +- [ ] Health degraded : S3 down → status `degraded` (pas `unhealthy`) +- [ ] Startup validation : JWT_SECRET vide → exit fatal au boot +- [ ] Prometheus : métriques retry (total, success, failures) et health exposées +- [ ] API Reference : documentation des endpoints clés avec exemples curl +- [ ] Tag v0.701 créé + +--- + +## 9. Risques + +| Risque | Mitigation | +|--------|------------| +| Worker retry en conflit avec webhook simultané | Vérifier status avant retry (skip si `completed`) + row locking | +| Backoff trop agressif (40 min max) | Configurable via env ; admin retry manuel en fallback | +| Deep health check lourd (S3 HEAD) | Cache résultat 30s, ne pas appeler à chaque request | +| Admin endpoint sans pagination | Pagination obligatoire (limit/offset, default 50) | +| Startup validation trop stricte bloque le dev | Env-dependent : strict en prod, warning en dev | + +--- + +## 10. Architecture + +```mermaid +graph TD + A[ProcessPaymentWebhook] --> B{Transfer Stripe} + B -->|Success| C[SellerTransfer status=completed] + B -->|Failure| D[SellerTransfer status=failed] + D --> E[TransferRetryWorker] + E -->|Ticker 5min| F{Query failed transfers} + F --> G{retry_count < max?} + G -->|Yes| H[CreateTransfer] + H -->|Success| C + H -->|Failure| I[retry_count++, backoff, next_retry_at] + G -->|No| J[status=permanently_failed] + + K[Admin] --> L[GET /admin/transfers] + K --> M[POST /admin/transfers/:id/retry] + M --> H + + N[Load Balancer] --> O[GET /health/deep] + O --> P{DB ping} + O --> Q{Redis ping} + O --> R{S3 HEAD} + O --> S{Disk space} +``` + +--- + +## 11. Références + +- [RETROSPECTIVE_V0603.md](RETROSPECTIVE_V0603.md) +- [V0_603_RELEASE_SCOPE.md](archive/V0_603_RELEASE_SCOPE.md) +- [SCOPE_CONTROL.md](SCOPE_CONTROL.md) +- [Stripe Connect Transfers](https://stripe.com/docs/connect/charges#transfer-availability) +- `veza-backend-api/internal/core/marketplace/service.go` (processSellerTransfers) +- `veza-backend-api/internal/core/marketplace/models.go` (SellerTransfer)