Payment Phase R: PAYONE-Integration entflechten (R1–R7 + Review)
Strangler-Refactoring der PAYONE-Zahlungslogik in eine testbare
Service-Schicht, ohne Verhalten in Checkout/Cron/IPN zu ändern.
Neue Services (app/Services/Payment/Payone):
- PayonePaymentService (+ PayonePaymentResult-DTO) für CC-/SEPA-Check
und Abo-Abbuchung
- PayoneRequestBuilder, PaymentMethodResolver, PayoneMethodConfig
- PayoneTransactionRecorder: zentrale PaymentTransaction-Erstellung
(API-Response, Invoice, Webhook) inkl. PCI-Redaction (cardpan/cardcvc2)
- PayoneWebhookHandler (+ PayoneWebhookResult): IPN/TSOK-Logik aus
Api\PayoneController; response('TSOK') statt echo/exit
- PayoneTransportException + Handler-Mapping (403) statt abort() im Service
Hotfixes (R1): var_dump/exit & bare exit() entfernt, Log-Redaction.
Security (R-Review):
- PAYONE_KEY_HASH (MD5-Hash, kein Klartext) mit Boot-Hard-Fail; schließt
zugleich die ''-Webhook-Bypass-Lücke. Artisan-Command payone:hash-key
(verdeckte Eingabe, optional --write) zur Hash-Erzeugung.
- Webhook: txaction Pflichtfeld, mode-Fallback auf payment->mode.
Pay\PayoneController ist @deprecated-Fassade (614 → 328 Zeilen),
delegiert an Services via Container-Singleton.
Migration: fehlende Spalte shopping_payments.carddata nachgezogen.
Tests: 47 grün (PayonePaymentService, Recorder, Webhook, Resolver,
TransportException, hash-key). End-to-End im Test-System verifiziert
(siehe dev/2026-06-09-payments).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1668e5a879
commit
ccc6f85809
31 changed files with 3420 additions and 717 deletions
86
app/Console/Commands/PayoneHashKey.php
Normal file
86
app/Console/Commands/PayoneHashKey.php
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
use function Laravel\Prompts\confirm;
|
||||
use function Laravel\Prompts\password;
|
||||
|
||||
class PayoneHashKey extends Command
|
||||
{
|
||||
protected $signature = 'payone:hash-key
|
||||
{--write : Schreibt den erzeugten Hash als PAYONE_KEY_HASH in die .env}';
|
||||
|
||||
protected $description = 'Erzeugt aus dem PAYONE-Portal-Key (Klartext) den benötigten MD5-Hash für PAYONE_KEY_HASH';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$this->components->info('Der Klartext-Key wird nur flüchtig zur Hash-Erzeugung verwendet und nirgends gespeichert oder geloggt.');
|
||||
|
||||
$plainKey = trim((string) password(
|
||||
label: 'PAYONE-Portal-Key (Klartext, verdeckte Eingabe)',
|
||||
required: true,
|
||||
));
|
||||
|
||||
if ($plainKey === '') {
|
||||
$this->components->error('Es wurde kein Key eingegeben.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$hash = hash('md5', $plainKey);
|
||||
|
||||
$this->newLine();
|
||||
$this->components->twoColumnDetail('PAYONE_KEY_HASH', $hash);
|
||||
$this->newLine();
|
||||
|
||||
if (! $this->option('write')) {
|
||||
$this->line(' Ohne <comment>--write</comment>: Wert manuell als <comment>PAYONE_KEY_HASH</comment> in die .env übernehmen.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if (! confirm("PAYONE_KEY_HASH in {$this->envPath()} schreiben/aktualisieren?", default: true)) {
|
||||
$this->components->warn('Abgebrochen – .env wurde nicht verändert.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if (! $this->writeHashToEnv($hash)) {
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->components->info('PAYONE_KEY_HASH in die .env geschrieben.');
|
||||
$this->line(' Danach <comment>php artisan config:clear</comment> ausführen (bzw. config neu cachen).');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function envPath(): string
|
||||
{
|
||||
return base_path('.env');
|
||||
}
|
||||
|
||||
private function writeHashToEnv(string $hash): bool
|
||||
{
|
||||
$path = $this->envPath();
|
||||
|
||||
if (! is_file($path) || ! is_writable($path)) {
|
||||
$this->components->error(".env nicht gefunden oder nicht beschreibbar: {$path}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$contents = (string) file_get_contents($path);
|
||||
$line = 'PAYONE_KEY_HASH='.$hash;
|
||||
|
||||
if (preg_match('/^PAYONE_KEY_HASH=.*$/m', $contents)) {
|
||||
$contents = preg_replace('/^PAYONE_KEY_HASH=.*$/m', $line, $contents);
|
||||
} else {
|
||||
$contents = rtrim($contents, "\n")."\n".$line."\n";
|
||||
}
|
||||
|
||||
return file_put_contents($path, $contents) !== false;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue