set('admin_access.secrets_key', 'base64:'.base64_encode(random_bytes(32))); }); it('prefers the stored signing secret over the server file', function () { config()->set('services.stripe.webhook_secret_test', 'whsec_from_env'); OperatingMode::set(OperatingMode::Test); app(SecretVault::class)->put('stripe.webhook_secret', 'whsec_from_console', Operator::factory()->create()); expect(StripeWebhookSecret::current())->toBe('whsec_from_console'); }); it('keeps the two modes apart, stored just as in the server file', function () { // Der Grund, warum es überhaupt zwei Werte gibt: Stripe stellt je // Betriebsmodus ein eigenes Signaturgeheimnis aus. Ein Platz, der auf den // anderen zurückfällt, würde ein Testereignis mit dem Live-Schlüssel // prüfen — es scheitert, aber die Konsole sagt „gesetzt". $by = Operator::factory()->create(); $vault = app(SecretVault::class); $vault->put('stripe.webhook_secret', 'whsec_test_slot', $by, OperatingMode::Test); $vault->put('stripe.webhook_secret', 'whsec_live_slot', $by, OperatingMode::Live); OperatingMode::set(OperatingMode::Test); expect(StripeWebhookSecret::current())->toBe('whsec_test_slot'); OperatingMode::set(OperatingMode::Live); expect(StripeWebhookSecret::current())->toBe('whsec_live_slot'); }); it('still reads the server file when nothing is stored, per mode', function () { // Eine bestehende Installation hat beide Zeilen in der .env und darf sich // durch diesen Umzug nicht ändern — sonst weist sie ab morgen jedes // eingehende Zahlungsereignis ab. config()->set('services.stripe.webhook_secret', 'whsec_live_env'); config()->set('services.stripe.webhook_secret_test', 'whsec_test_env'); OperatingMode::set(OperatingMode::Test); expect(StripeWebhookSecret::current())->toBe('whsec_test_env'); OperatingMode::set(OperatingMode::Live); expect(StripeWebhookSecret::current())->toBe('whsec_live_env'); }); it('does not call an unset slot "from the server file" when the server file is empty for that mode', function () { // Die Herkunftsplakette auf der Karte muss zum Wert gehören, der WIRKLICH // gilt — genau der Fehler, den source() für dns.token schon einmal hatte. config()->set('services.stripe.webhook_secret', 'whsec_live_env'); config()->set('services.stripe.webhook_secret_test', ''); OperatingMode::set(OperatingMode::Test); expect(app(SecretVault::class)->source('stripe.webhook_secret'))->toBe('none') ->and(StripeWebhookSecret::current())->toBe(''); }); it('reports the source as the server file when that mode does have a line', function () { config()->set('services.stripe.webhook_secret_test', 'whsec_test_env'); OperatingMode::set(OperatingMode::Test); expect(app(SecretVault::class)->source('stripe.webhook_secret'))->toBe('environment'); }); it('offers the signing secret as a field on the payments card', function () { Livewire::actingAs(operator('Owner'), 'operator') ->test(Integrations::class) ->set('confirmablePassword', 'password') ->call('confirmPassword') ->assertSee('STRIPE_WEBHOOK_SECRET'); }); it('sends the readiness check to that card instead of the raw .env editor', function () { foreach (BillingChecks::all() as $check) { if ($check->key === 'billing.stripe_webhook_secret') { expect($check->tab)->toBe('services'); return; } } throw new RuntimeException('billing.stripe_webhook_secret check is gone'); });