From 69ec45eea65a00a6a5d05b1b0ce2703d2ee6525a Mon Sep 17 00:00:00 2001 From: isaacclad Date: Wed, 15 Jul 2026 13:11:02 +0000 Subject: [PATCH] Fix 500 when saving payment gateway credentials. Implement the missing SettingsController::persistGateway method so merchant Paystack/Flutterwave/Hubtel keys can be saved, and cover create + keep-blank-secrets paths with tests. --- .../Controllers/Pos/SettingsController.php | 42 +++++++++++ tests/Feature/PosHardwareTest.php | 74 +++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/app/Http/Controllers/Pos/SettingsController.php b/app/Http/Controllers/Pos/SettingsController.php index abdbf3b..1c197da 100644 --- a/app/Http/Controllers/Pos/SettingsController.php +++ b/app/Http/Controllers/Pos/SettingsController.php @@ -8,6 +8,7 @@ use App\Models\PosLocation; use App\Models\PosMember; use App\Models\PaymentGatewaySetting; use App\Models\PosTable; +use App\Models\User; use App\Services\Import\CrmProductImportService; use App\Services\Import\MerchantCatalogImportService; use App\Services\Payments\MerchantGatewayService; @@ -118,6 +119,47 @@ class SettingsController extends Controller return back()->with('success', 'Settings saved.'); } + /** + * Upsert merchant gateway credentials for the acting account. + * Blank secret fields keep the previously stored encrypted values. + */ + protected function persistGateway(Request $request, User $user): void + { + $ownerRef = (string) $user->public_id; + $existing = PaymentGatewaySetting::query()->where('owner_ref', $ownerRef)->first(); + + $provider = trim((string) $request->input('gateway_provider', '')); + $publicKey = trim((string) $request->input('gateway_public_key', '')); + $secretKey = trim((string) $request->input('gateway_secret_key', '')); + $webhookSecret = trim((string) $request->input('gateway_webhook_secret', '')); + + // No provider chosen and no prior config — nothing to persist. + if ($provider === '' && $existing === null) { + return; + } + + $attributes = [ + 'provider' => $provider !== '' ? $provider : $existing->provider, + 'is_active' => $request->boolean('gateway_is_active'), + ]; + + // Leave blank on the form means "keep the saved credential". + if ($publicKey !== '') { + $attributes['public_key'] = $publicKey; + } + if ($secretKey !== '') { + $attributes['secret_key'] = $secretKey; + } + if ($webhookSecret !== '') { + $attributes['webhook_secret'] = $webhookSecret; + } + + PaymentGatewaySetting::query()->updateOrCreate( + ['owner_ref' => $ownerRef], + $attributes, + ); + } + public function storeTable(Request $request): RedirectResponse { $data = $request->validate([ diff --git a/tests/Feature/PosHardwareTest.php b/tests/Feature/PosHardwareTest.php index 994acca..d6a2202 100644 --- a/tests/Feature/PosHardwareTest.php +++ b/tests/Feature/PosHardwareTest.php @@ -3,6 +3,7 @@ namespace Tests\Feature; use App\Http\Middleware\EnsurePlatformSession; +use App\Models\PaymentGatewaySetting; use App\Models\PosLocation; use App\Models\PosProduct; use App\Models\PosSale; @@ -164,6 +165,79 @@ class PosHardwareTest extends TestCase Storage::disk('public')->assertExists($location->receipt_logo_path); } + public function test_settings_saves_payment_gateway_credentials(): void + { + $user = $this->user(); + PosLocation::create([ + 'owner_ref' => $user->public_id, + 'name' => 'Main register', + 'currency' => 'GHS', + ]); + + $this->actingAs($user) + ->put(route('pos.settings.update'), [ + 'name' => 'Main register', + 'currency' => 'GHS', + 'service_style' => 'retail', + 'printer_paper_mm' => 80, + 'gateway_provider' => PaymentGatewaySetting::PROVIDER_PAYSTACK, + 'gateway_public_key' => 'pk_test_abc', + 'gateway_secret_key' => 'sk_test_xyz', + 'gateway_webhook_secret' => 'whsec_test', + 'gateway_is_active' => '1', + ]) + ->assertRedirect() + ->assertSessionHas('success'); + + $setting = PaymentGatewaySetting::query()->where('owner_ref', $user->public_id)->first(); + $this->assertNotNull($setting); + $this->assertSame(PaymentGatewaySetting::PROVIDER_PAYSTACK, $setting->provider); + $this->assertSame('pk_test_abc', $setting->public_key); + $this->assertSame('sk_test_xyz', $setting->secret_key); + $this->assertSame('whsec_test', $setting->webhook_secret); + $this->assertTrue($setting->is_active); + $this->assertTrue($setting->isConfigured()); + } + + public function test_settings_keeps_existing_gateway_secrets_when_left_blank(): void + { + $user = $this->user(); + PosLocation::create([ + 'owner_ref' => $user->public_id, + 'name' => 'Main register', + 'currency' => 'GHS', + ]); + PaymentGatewaySetting::create([ + 'owner_ref' => $user->public_id, + 'provider' => PaymentGatewaySetting::PROVIDER_PAYSTACK, + 'public_key' => 'pk_keep', + 'secret_key' => 'sk_keep', + 'webhook_secret' => 'whsec_keep', + 'is_active' => true, + ]); + + $this->actingAs($user) + ->put(route('pos.settings.update'), [ + 'name' => 'Main register', + 'currency' => 'GHS', + 'service_style' => 'retail', + 'printer_paper_mm' => 80, + 'gateway_provider' => PaymentGatewaySetting::PROVIDER_FLUTTERWAVE, + 'gateway_public_key' => '', + 'gateway_secret_key' => '', + 'gateway_webhook_secret' => '', + 'gateway_is_active' => '1', + ]) + ->assertRedirect() + ->assertSessionHas('success'); + + $setting = PaymentGatewaySetting::query()->where('owner_ref', $user->public_id)->firstOrFail(); + $this->assertSame(PaymentGatewaySetting::PROVIDER_FLUTTERWAVE, $setting->provider); + $this->assertSame('pk_keep', $setting->public_key); + $this->assertSame('sk_keep', $setting->secret_key); + $this->assertSame('whsec_keep', $setting->webhook_secret); + } + public function test_receipt_page_shows_uploaded_logo(): void { Storage::fake('public');