'dash-admin-'.uniqid('', true).'@test.com', 'password' => Hash::make('secret'), 'admin' => 2, 'lang' => 'de', ]); } function dashboardPayment(array $overrides = []): ShoppingPayment { return ShoppingPayment::forceCreate(array_merge([ 'shopping_order_id' => 1, 'clearingtype' => 'cc', 'reference' => 'dash-'.uniqid('', true), 'amount' => 5000, 'currency' => 'EUR', 'mode' => 'test', ], $overrides)); } function dashboardAbo(array $overrides = []): UserAbo { $consultant = User::forceCreate([ 'email' => 'dash-abo-'.uniqid('', true).'@test.com', 'password' => Hash::make('secret'), 'lang' => 'de', ]); return UserAbo::create(array_merge([ 'user_id' => $consultant->id, 'shopping_user_id' => 1, 'is_for' => 'me', 'email' => $consultant->email, 'payone_userid' => 0, 'clearingtype' => 'cc', 'active' => true, 'status' => 2, 'abo_interval' => 5, 'next_date' => now()->addDays(3), ], $overrides)); } // ─── Provider-Filter Zahlungen ──────────────────────────────────────────────── it('filtert Zahlungen nach Provider', function () { $this->actingAs(dashboardAdmin()); $stripe = dashboardPayment(['payment_provider' => 'stripe', 'txaction' => 'paid']); $payone = dashboardPayment(['payment_provider' => 'payone']); request()->merge(['provider' => 'stripe', 'days' => 7]); $view = (new PaymentDashboardController)->payments(); $ids = $view->getData()['payments']->pluck('id'); expect($ids->all())->toBe([$stripe->id]); request()->merge(['provider' => 'payone']); $view = (new PaymentDashboardController)->payments(); $ids = $view->getData()['payments']->pluck('id'); expect($ids->all())->toBe([$payone->id]); }); it('liefert Umsatz und Zahlungen je Provider in den Payment-Stats', function () { $this->actingAs(dashboardAdmin()); dashboardPayment(['payment_provider' => 'stripe', 'txaction' => 'paid', 'amount' => 10000]); dashboardPayment(['payment_provider' => 'stripe', 'txaction' => 'failed', 'amount' => 2000]); dashboardPayment(['payment_provider' => 'payone', 'txaction' => 'paid', 'amount' => 5000]); $view = (new PaymentDashboardController)->payments(); $providers = $view->getData()['paymentStats']['providers']; expect($providers['stripe']['total'])->toBe(2) ->and($providers['stripe']['paid'])->toBe(1) ->and($providers['stripe']['paid_amount'])->toBe(100.0) ->and($providers['payone']['paid'])->toBe(1) ->and($providers['payone']['paid_amount'])->toBe(50.0); }); // ─── Provider-Filter Transaktionen ──────────────────────────────────────────── it('filtert Transaktionen nach Provider und liefert Erfolgsraten je Provider', function () { $this->actingAs(dashboardAdmin()); $payment = dashboardPayment(['payment_provider' => 'stripe']); $stripeTx = PaymentTransaction::create([ 'shopping_payment_id' => $payment->id, 'provider' => 'stripe', 'request' => 'paid', 'txaction' => 'paid', 'txid' => 'pi_123', 'userid' => 0, ]); PaymentTransaction::create([ 'shopping_payment_id' => $payment->id, 'provider' => 'payone', 'request' => 'failed', 'txaction' => 'failed', 'txid' => '99001', 'userid' => 0, ]); request()->merge(['provider' => 'stripe', 'days' => 7]); $view = (new PaymentDashboardController)->transactions(); $data = $view->getData(); expect($data['transactions']->pluck('id')->all())->toBe([$stripeTx->id]) ->and($data['transactionStats']['providers']['stripe']['success_rate'])->toBe(100.0) ->and($data['transactionStats']['providers']['payone']['failed'])->toBe(1); }); // ─── Abos je Provider (Auslauf-Fortschritt) ─────────────────────────────────── it('zeigt aktive Abos je Provider inkl. Stripe-Anteil und Fehlerzählern', function () { $this->actingAs(dashboardAdmin()); dashboardAbo(['payment_provider' => 'stripe']); dashboardAbo(['payment_provider' => 'payone']); dashboardAbo(['payment_provider' => 'payone', 'status' => 3]); dashboardAbo(['payment_provider' => 'stripe', 'status' => 5]); // beendet → zählt nicht dashboardAbo(['payment_provider' => 'payone', 'active' => false]); // inaktiv → zählt nicht $view = (new PaymentDashboardController)->index(); $stats = $view->getData()['aboProviderStats']; expect($stats['total'])->toBe(3) ->and($stats['providers']['stripe']['total'])->toBe(1) ->and($stats['providers']['payone']['total'])->toBe(2) ->and($stats['providers']['payone']['failed'])->toBe(1) ->and($stats['stripe_share'])->toBe(33.3); }); it('führt Stripe in den Provider-Status-Karten', function () { $this->actingAs(dashboardAdmin()); $view = (new PaymentDashboardController)->index(); $data = $view->getData(); expect($data['providerStats'])->toHaveKey('stripe') ->and($data['uptimeStats'])->toHaveKey('stripe') ->and($data['stats'])->toHaveKey('stripe_incidents_30d'); }); // ─── Stripe-Deep-Links ──────────────────────────────────────────────────────── it('baut Stripe-Dashboard-Links nur für Stripe-Zahlungen mit PaymentIntent', function () { $testMode = dashboardPayment(['payment_provider' => 'stripe', 'external_reference' => 'pi_abc', 'mode' => 'test']); $liveMode = dashboardPayment(['payment_provider' => 'stripe', 'external_reference' => 'pi_def', 'mode' => 'live']); $sessionOnly = dashboardPayment(['payment_provider' => 'stripe', 'external_reference' => 'cs_xyz']); $payone = dashboardPayment(['payment_provider' => 'payone', 'external_reference' => 'pi_fake']); expect($testMode->stripeDashboardUrl())->toBe('https://dashboard.stripe.com/test/payments/pi_abc') ->and($liveMode->stripeDashboardUrl())->toBe('https://dashboard.stripe.com/payments/pi_def') ->and($sessionOnly->stripeDashboardUrl())->toBeNull() ->and($payone->stripeDashboardUrl())->toBeNull(); }); it('baut Stripe-Dashboard-Links für Stripe-Transaktionen über die txid', function () { $payment = dashboardPayment(['payment_provider' => 'stripe']); $stripeTx = PaymentTransaction::create([ 'shopping_payment_id' => $payment->id, 'provider' => 'stripe', 'request' => 'paid', 'txaction' => 'paid', 'txid' => 'pi_tx1', 'userid' => 0, 'mode' => 'live', ]); $sessionTx = PaymentTransaction::create([ 'shopping_payment_id' => $payment->id, 'provider' => 'stripe', 'request' => 'appointed', 'txaction' => 'appointed', 'txid' => 'cs_tx1', 'userid' => 0, ]); $payoneTx = PaymentTransaction::create([ 'shopping_payment_id' => $payment->id, 'provider' => 'payone', 'request' => 'paid', 'txaction' => 'paid', 'txid' => '123456', 'userid' => 0, ]); expect($stripeTx->stripeDashboardUrl())->toBe('https://dashboard.stripe.com/payments/pi_tx1') ->and($sessionTx->stripeDashboardUrl())->toBeNull() ->and($payoneTx->stripeDashboardUrl())->toBeNull(); }); // ─── Log-Viewer ─────────────────────────────────────────────────────────────── it('unterstützt im Log-Viewer stripe- und payone-Kanäle mit stripe als Default', function () { $this->actingAs(dashboardAdmin()); $view = (new PaymentDashboardController)->logs(); expect($view->getData()['selectedChannel'])->toBe('stripe') ->and($view->getData()['availableChannels'])->toBe(['stripe', 'payone']); request()->merge(['channel' => 'payone']); $view = (new PaymentDashboardController)->logs(); expect($view->getData()['selectedChannel'])->toBe('payone'); request()->merge(['channel' => '../etc/passwd']); $view = (new PaymentDashboardController)->logs(); expect($view->getData()['selectedChannel'])->toBe('stripe'); }); // ─── Uptime-Check ───────────────────────────────────────────────────────────── it('prüft die Erreichbarkeit von Stripe und PAYONE im Uptime-Check', function () { Http::fake([ 'api.stripe.com/*' => Http::response('', 401), 'api.pay1.de/*' => Http::response('', 200), ]); $this->artisan('payment:check-uptime')->assertSuccessful(); expect(ProviderUptimeLog::where('provider', 'stripe')->where('is_up', true)->count())->toBe(1) ->and(ProviderUptimeLog::where('provider', 'payone')->where('is_up', true)->count())->toBe(1); });