CORRECTIONS_PAIEMENT_ROBUSTESSE
Corrections de robustesse du système de paiement
✅ Corrections appliquées
1. Validation du montant débité vs montant calculé ✅
Fichiers modifiés :
app/Services/PaymentVerificationService.phpapp/Http/Controllers/CheckoutController.php
Corrections :
- Vérification que le montant débité par Stripe correspond au montant calculé (tolérance de 0.01€ pour les arrondis)
- Dans
verifyAndMarkPaid(): comparaison$session->amount_totalvs$echeance->montant_final - Dans
markEcheancePaidFromPaymentIntent(): comparaison$pi->amountvs$echeance->montant_final - Dans
CheckoutController::charge(): vérification après création du PaymentIntent
Code ajouté :
// Tolérance de 0.01€ pour les arrondis
if (abs($amountPaid - $expectedAmount) > 0.01) {
// Rejet du paiement
}
2. Verrous transactionnels pour éviter les doubles paiements ✅
Fichiers modifiés :
app/Services/PaymentVerificationService.phpapp/Http/Controllers/CheckoutController.php
Corrections :
- Utilisation de
DB::transaction()aveclockForUpdate()pour verrouiller l'échéance pendant le traitement - Rechargement de l'échéance avec verrou avant mise à jour
- Vérification que l'échéance n'est pas déjà payée après le verrou
Code ajouté :
DB::transaction(function () use ($echeance) {
$echeanceLocked = Echeance::where('id', $echeance->id)
->lockForUpdate()
->first();
if (!$echeanceLocked || $echeanceLocked->estPayee()) {
return; // Déjà payée
}
$echeanceLocked->update([...]);
});
3. Exclusion des statuts annulé/arrêté ✅
Fichiers modifiés :
app/Services/PaymentVerificationService.phpapp/Http/Controllers/CheckoutController.php
Corrections :
- Vérification explicite que l'échéance n'est pas
STATUT_ANNULEouSTATUT_ARRETE - Dans
charge():whereNotIn('statut', [Echeance::STATUT_ANNULE, Echeance::STATUT_ARRETE]) - Dans
verifyAndMarkPaid()etmarkEcheancePaidFromPaymentIntent(): vérification avant traitement
Code ajouté :
if (in_array($echeance->statut, [Echeance::STATUT_ANNULE, Echeance::STATUT_ARRETE], true)) {
return ['ok' => false, 'message' => 'Échéance annulée ou arrêtée, impossible de payer.'];
}
4. Validation du montant dans les webhooks ✅
Fichiers modifiés :
app/Services/PaymentVerificationService.php
Corrections :
- Vérification du montant dans
verifyAndMarkPaid()avant de marquer l'échéance payée - Comparaison
$session->amount_totalavec$echeance->montant_final - Log d'avertissement si le montant ne correspond pas
5. Amélioration de la validation des codes promo ✅
Fichiers modifiés :
app/Http/Controllers/CheckoutController.php
Corrections :
- Validation du code promo avant le calcul du montant
- Vérification que le code promo n'a pas déjà été utilisé pour cette échéance
- Rejet immédiat si le code est invalide
Code ajouté :
if ($codePromo) {
$promo = PromoCode::validateCode($codePromo, $user);
if (!$promo) {
return response()->json(['success' => false, 'error' => 'Code promo invalide ou expiré.'], 422);
}
}
6. Validation stricte du montant final ✅
Fichiers modifiés :
app/Http/Controllers/CheckoutController.php
Corrections :
- Vérification que
$montantFinal > 0 - Vérification que
$montantFinal <= $montantDu(le montant final ne peut pas dépasser le montant dû) - Rejet avec message d'erreur clair si validation échoue
Code ajouté :
if ($montantFinal > $montantDu) {
return response()->json(['success' => false, 'error' => 'Erreur de calcul du montant. Contactez le support.'], 422);
}
🧪 Tests de robustesse créés
1. PaymentVerificationServiceTest.php
Tests unitaires pour PaymentVerificationService :
- ✅ Vérification que le montant débité correspond au montant attendu
- ✅ Rejet si le montant débité ne correspond pas
- ✅ Rejet si l'échéance est annulée
- ✅ Idempotence (ne pas payer deux fois)
- ✅ Tolérance d'arrondi de 0.01€
- ✅ Rejet si différence > 0.01€
Note : Ces tests nécessitent une base de données configurée et des mocks Stripe.
2. CalculMontantDuServiceTest.php
Tests unitaires pour CalculMontantDuService :
- ✅ Calcul correct pour une échéance simple
- ✅ Application d'un code promo pourcentage
- ✅ Application d'un code promo montant fixe
- ✅ Réduction manuelle appliquée
- ✅ Montant final ne peut pas être négatif
- ✅ Code promo expiré n'est pas appliqué
- ✅ Code promo inactif n'est pas appliqué
- ✅ Arrondi correct à 2 décimales
3. PaymentRaceConditionTest.php
Tests de race conditions :
- ✅ Vérifier que le verrou transactionnel empêche les doubles paiements
- ✅ Vérifier que les échéances annulées sont rejetées
- ✅ Vérifier que les échéances déjà payées sont rejetées
- ✅ Vérifier la validation du montant final
📋 Checklist de déploiement
Avant de déployer en production :
- [ ] Exécuter les tests :
php artisan test - [ ] Vérifier que les migrations sont à jour
- [ ] Tester manuellement un paiement avec montant exact
- [ ] Tester un paiement avec code promo
- [ ] Tester un paiement d'échéance annulée (doit être rejeté)
- [ ] Tester un double paiement simultané (doit être bloqué)
- [ ] Vérifier les logs d'audit pour les erreurs de montant
- [ ] Monitorer les webhooks Stripe pour détecter les incohérences
🔍 Points de vigilance
-
Tolérance d'arrondi : La tolérance de 0.01€ peut masquer de petites différences. À surveiller dans les logs.
-
Performance : Les verrous
lockForUpdate()peuvent ralentir les paiements simultanés. Monitorer les temps de réponse. -
Logs : Tous les rejets de paiement sont loggés dans
PaymentAuditLog. Vérifier régulièrement. -
Webhooks : Si un webhook échoue, le CRON de réconciliation devrait rattraper. Vérifier que le CRON tourne correctement.
📝 Notes techniques
- Les verrous transactionnels utilisent
lockForUpdate()qui bloque la ligne jusqu'à la fin de la transaction - La tolérance de 0.01€ est nécessaire car Stripe peut arrondir différemment selon les devises
- Les tests utilisent
RefreshDatabasepour isoler chaque test - Les mocks Stripe ne sont pas implémentés dans les tests actuels (nécessite configuration)
🚀 Prochaines améliorations possibles
- Alertes automatiques : Système d'alerte si un montant ne correspond pas
- Réconciliation automatique : CRON plus fréquent pour réconcilier les échéances en attente
- Tests d'intégration : Tests avec Stripe Test Mode pour valider le flux complet
- Monitoring : Dashboard pour surveiller les incohérences de montant
Document généré le 2026-01-25