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
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
// 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
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 :
// 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 :
- Regrouper les items par
product.SellerID
- 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
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