# Payment Race Condition Tests Diese Test-Suite überprüft die Behebung von Race Conditions bei der Zahlungsabwicklung und Rechnungsnummernvergabe. ## Übersicht ### Problem Wenn mehrere Zahlungen fast gleichzeitig über die Payone API eingehen, konnte es zu Race Conditions kommen: - Doppelte Rechnungsnummern - Mehrfache Verarbeitung derselben Zahlung - Inkonsistente Datenbank-Zustände ### Lösung 3-Stufen-Absicherung implementiert: 1. **Database Locking** im PayoneController mit `lockForUpdate()` 2. **Atomic Invoice Number** Vergabe mit Transaction + Lock 3. **Double-Check Pattern** zur zusätzlichen Absicherung ## Test-Dateien ### 1. ConcurrentPaymentTest.php Tests für gleichzeitige Rechnungsnummernvergabe: - ✅ `it_generates_unique_invoice_numbers_under_concurrent_load()` - Eindeutigkeit bei hoher Last - ✅ `it_atomically_increments_invoice_numbers()` - Atomare Inkrementierung - ✅ `it_does_not_skip_invoice_numbers_on_transaction_rollback()` - Keine Lücken - ✅ `it_uses_database_locking_for_invoice_numbers()` - Lock-Mechanismus - ✅ `it_prevents_double_processing_of_same_payment()` - Doppelverarbeitung verhindern - ✅ `it_can_lock_settings_for_update()` - Setting-Lock funktioniert - ✅ `it_creates_invoice_numbers_with_correct_format()` - Korrekte Formatierung - ✅ `it_handles_rapid_sequential_invoice_creation()` - Schnelle Sequenzen - ✅ `it_initializes_invoice_number_if_not_exists()` - Initialisierung - ✅ `it_handles_concurrent_transaction_commits()` - Gleichzeitige Commits ### 2. PayoneRaceConditionTest.php Tests für Payone-spezifische Race Conditions: - ✅ `it_locks_shopping_order_during_payment_processing()` - Order wird gelockt - ✅ `it_prevents_double_payment_processing()` - Doppelte Zahlung verhindern - ✅ `it_serializes_concurrent_payment_requests()` - Serialisierung - ✅ `it_rolls_back_payment_on_error()` - Rollback bei Fehler - ✅ `it_tracks_payment_status_transitions()` - Status-Übergänge - ✅ `it_handles_concurrent_payments_for_different_orders()` - Verschiedene Orders - ✅ `it_isolates_payment_transactions()` - Transaction Isolation - ✅ `it_prevents_concurrent_modifications()` - Gleichzeitige Änderungen verhindern - ✅ `it_enforces_unique_payment_references()` - Eindeutige Referenzen ### 3. InvoiceServiceTest.php (Unit Tests) Unit-Tests für Invoice Service: - ✅ `it_gets_current_invoice_number()` - Nummer abrufen - ✅ `it_increments_invoice_number()` - Inkrementierung - ✅ `it_increments_sequentially()` - Sequenzielle Inkrementierung - ✅ `it_formats_invoice_number_with_year_prefix()` - Formatierung - ✅ `it_generates_correct_storage_paths()` - Storage-Pfade - ✅ `it_generates_correct_filenames()` - Dateinamen - ✅ `it_initializes_invoice_number_when_not_exists()` - Initialisierung - ✅ `it_uses_transaction_for_invoice_number_increment()` - Transaction Usage - ✅ `it_locks_setting_during_increment()` - Lock während Increment - ✅ `it_pads_invoice_numbers_correctly()` - Korrekte Padding - ✅ `it_returns_zero_when_invoice_number_not_set()` - Default-Wert - ✅ `it_handles_rapid_increments_without_gaps()` - Keine Lücken - ✅ `it_returns_integer_invoice_number()` - Typ-Sicherheit ## Tests ausführen ### Alle Payment-Tests ```bash ./vendor/bin/phpunit tests/Feature/Payment/ ``` ### Einzelne Test-Datei ```bash ./vendor/bin/phpunit tests/Feature/Payment/ConcurrentPaymentTest.php ./vendor/bin/phpunit tests/Feature/Payment/PayoneRaceConditionTest.php ./vendor/bin/phpunit tests/Unit/Services/InvoiceServiceTest.php ``` ### Einzelner Test ```bash ./vendor/bin/phpunit --filter it_generates_unique_invoice_numbers_under_concurrent_load ``` ### Mit Coverage ```bash ./vendor/bin/phpunit --coverage-html coverage/ tests/Feature/Payment/ ``` ### Mit Pest (falls verwendet) ```bash ./vendor/bin/pest tests/Feature/Payment/ ``` ## Wichtige Hinweise ### Database Transactions Alle Tests verwenden `DatabaseTransactions` Trait: - Änderungen werden nach jedem Test automatisch zurückgerollt - Tests sind isoliert und beeinflussen sich nicht gegenseitig - Keine manuelle Datenbank-Bereinigung nötig ### Test-Daten Tests erstellen eigene Test-Daten: - ShoppingOrder, ShoppingPayment, ShoppingUser - Setting für invoice-number - Alle Daten werden nach Test automatisch entfernt ### Performance Tests simulieren Concurrent Scenarios: - Verwenden separate DB-Transactions - Testen Lock-Mechanismen - Prüfen auf Race Conditions ## Continuous Integration Diese Tests sollten Teil der CI/CD Pipeline sein: ```yaml # .github/workflows/tests.yml - name: Run Payment Tests run: ./vendor/bin/phpunit tests/Feature/Payment/ ``` ## Monitoring in Production Nach Deployment überwachen: 1. **Log-Einträge prüfen:** ```bash grep "Error:2008" storage/logs/laravel.log ``` 2. **Rechnungsnummern auf Lücken prüfen:** ```sql SELECT * FROM user_invoices ORDER BY number -- Prüfe auf Lücken in der Sequenz ``` 3. **Doppelte Rechnungsnummern prüfen:** ```sql SELECT full_number, COUNT(*) FROM user_invoices GROUP BY full_number HAVING COUNT(*) > 1; ``` ## Troubleshooting ### Test schlägt fehl: "Database not configured" ```bash cp .env.example .env.testing php artisan key:generate --env=testing ``` ### Test schlägt fehl: "Setting not found" Tests initialisieren automatisch - prüfe Migration: ```bash php artisan migrate --env=testing ``` ### Test schlägt fehl: "Foreign key constraint" Prüfe, ob alle Referenzen korrekt erstellt werden: ```bash php artisan migrate:fresh --env=testing ``` ## Weiterführende Informationen - **Code-Änderungen:** Siehe Git Commit mit Tag `race-condition-fix` - **Dokumentation:** `/dev/[DATUM]/payment-race-condition-fix.md` - **Issue:** Siehe entsprechendes GitHub Issue --- **Erstellt:** Januar 2026 **Autor:** Claude AI Assistant **Status:** ✅ Production Ready