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