From 9a1e36337dc1682ea85372024c47296522608fba Mon Sep 17 00:00:00 2001 From: ncoronel Date: Mon, 6 Jul 2026 08:40:32 -0300 Subject: [PATCH] feat: implement BaseIntegrationService and TelepagosIntegrationService with integration handling and header generation --- .../Services/BaseIntegrationService.php | 141 ++++++++++++++ .../Services/TelepagosIntegrationService.php | 44 +++++ .../Integration/IntegrationServiceTest.php | 181 ++++++++++++++++++ 3 files changed, 366 insertions(+) create mode 100644 app/Domains/Integration/Services/BaseIntegrationService.php create mode 100644 app/Domains/Integration/Services/TelepagosIntegrationService.php create mode 100644 tests/Feature/Integration/IntegrationServiceTest.php diff --git a/app/Domains/Integration/Services/BaseIntegrationService.php b/app/Domains/Integration/Services/BaseIntegrationService.php new file mode 100644 index 0000000..7175602 --- /dev/null +++ b/app/Domains/Integration/Services/BaseIntegrationService.php @@ -0,0 +1,141 @@ +integrationCode = $integrationCode; + return $this; + } + + /** + * Get the integration code. + * + * @return string + */ + public function getIntegrationCode(): string + { + return $this->integrationCode; + } + + /** + * Set the tenant code and load the integration models. + * + * @param string $tenantCode + * @return $this + * @throws Exception + */ + public function forTenant(string $tenantCode): self + { + $this->tenantCode = $tenantCode; + $this->loadIntegration(); + return $this; + } + + /** + * Load the Integration and TenantIntegration models. + * + * @throws Exception + */ + protected function loadIntegration(): void + { + if (empty($this->integrationCode)) { + throw new Exception("Integration code is not set."); + } + + $this->integration = Integration::where('integration_code', $this->integrationCode)->first(); + if (!$this->integration) { + throw new Exception("Integration with code '{$this->integrationCode}' not found."); + } + + $this->tenantIntegration = TenantIntegration::where('tenant_code', $this->tenantCode) + ->where('integration_code', $this->integrationCode) + ->first(); + + if (!$this->tenantIntegration) { + throw new Exception("Tenant '{$this->tenantCode}' does not have integration '{$this->integrationCode}' configured."); + } + } + + /** + * Build the request URL. + * + * @param string $path + * @return string + * @throws Exception + */ + public function getUrl(string $path = ''): string + { + if (!$this->integration) { + throw new Exception("Integration is not loaded. Call forTenant() first."); + } + + $baseUrl = rtrim($this->integration->url, '/'); + $path = ltrim($path, '/'); + + return $path !== '' ? "{$baseUrl}/{$path}" : $baseUrl; + } + + /** + * Get integration setting by key from tenant's integration data. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + protected function getIntegrationSetting(string $key, mixed $default = null): mixed + { + if (!$this->tenantIntegration || !$this->tenantIntegration->integration_data) { + return $default; + } + + return $this->tenantIntegration->integration_data[$key] ?? $default; + } + + /** + * Get the headers for the integration. + * + * @return array + */ + abstract public function getHeaders(): array; +} diff --git a/app/Domains/Integration/Services/TelepagosIntegrationService.php b/app/Domains/Integration/Services/TelepagosIntegrationService.php new file mode 100644 index 0000000..7eaba78 --- /dev/null +++ b/app/Domains/Integration/Services/TelepagosIntegrationService.php @@ -0,0 +1,44 @@ +integrationCode = $integrationCode; + } + + /** + * Get the headers for Telepagos integration. + * + * @return array + * @throws Exception + */ + public function getHeaders(): array + { + if (!$this->tenantIntegration) { + throw new Exception("Tenant integration is not loaded. Call forTenant() first."); + } + + $username = $this->getIntegrationSetting('username'); + $password = $this->getIntegrationSetting('password'); + + if (empty($username) || empty($password)) { + throw new Exception("Missing username or password in Telepagos integration settings."); + } + + return [ + 'Authorization' => 'Basic ' . base64_encode($username . ':' . $password), + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + ]; + } +} diff --git a/tests/Feature/Integration/IntegrationServiceTest.php b/tests/Feature/Integration/IntegrationServiceTest.php new file mode 100644 index 0000000..c95d831 --- /dev/null +++ b/tests/Feature/Integration/IntegrationServiceTest.php @@ -0,0 +1,181 @@ + 'base64:' . base64_encode(random_bytes(32))]); + + $hdrKey = (string) \Illuminate\Support\Str::uuid(); + $ftrKey = (string) \Illuminate\Support\Str::uuid(); + + $headerAttachment = \App\Domains\Attachable\Models\Attachment::create([ + 'key' => $hdrKey, + 'path' => 'tenants/' . $hdrKey . '.png', + 'filename' => 'logo_header.png', + 'type' => \App\Domains\Attachable\Enums\AttachmentType::Image, + 'mime_type' => 'image/png', + ]); + $footerAttachment = \App\Domains\Attachable\Models\Attachment::create([ + 'key' => $ftrKey, + 'path' => 'tenants/' . $ftrKey . '.png', + 'filename' => 'logo_footer.png', + 'type' => \App\Domains\Attachable\Enums\AttachmentType::Image, + 'mime_type' => 'image/png', + ]); + + // Create a test tenant + $this->tenant = Tenant::create([ + 'codigo' => 'test-tenant', + 'nombre' => 'Test Tenant', + 'dominio' => 'test.com', + 'primary_color' => '#ffffff', + 'secondary_color' => '#ffffff', + 'danger_color' => '#ffffff', + 'success_color' => '#ffffff', + 'header_bg_color' => '#ffffff', + 'footer_bg_color' => '#ffffff', + 'header_logo_id' => $headerAttachment->id, + 'footer_logo_id' => $footerAttachment->id, + ]); + } + + public function test_it_throws_exception_if_integration_code_is_invalid(): void + { + $service = new TelepagosIntegrationService('invalid_code'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage("Integration with code 'invalid_code' not found."); + + $service->forTenant($this->tenant->codigo); + } + + public function test_it_throws_exception_if_tenant_integration_is_not_configured(): void + { + // Seed integration but don't configure for tenant + Integration::create([ + 'integration_code' => 'telepagos', + 'name' => 'Telepagos', + 'url' => 'https://api.telepagos.com.ar', + 'integration_data_schema' => [ + 'username' => 'required|string', + 'password' => 'required|string', + ] + ]); + + $service = new TelepagosIntegrationService(); + + $this->expectException(Exception::class); + $this->expectExceptionMessage("Tenant 'test-tenant' does not have integration 'telepagos' configured."); + + $service->forTenant($this->tenant->codigo); + } + + public function test_it_resolves_base_url_and_paths(): void + { + Integration::create([ + 'integration_code' => 'telepagos', + 'name' => 'Telepagos', + 'url' => 'https://api.telepagos.com.ar/', // Trailing slash to test trimming + 'integration_data_schema' => [ + 'username' => 'required|string', + 'password' => 'required|string', + ] + ]); + + TenantIntegration::create([ + 'tenant_code' => $this->tenant->codigo, + 'integration_code' => 'telepagos', + 'integration_data' => [ + 'username' => 'user123', + 'password' => 'pass123', + ] + ]); + + $service = new TelepagosIntegrationService(); + $service->forTenant($this->tenant->codigo); + + $this->assertEquals('https://api.telepagos.com.ar', $service->getUrl()); + $this->assertEquals('https://api.telepagos.com.ar/v1/payments', $service->getUrl('v1/payments')); + $this->assertEquals('https://api.telepagos.com.ar/v1/payments', $service->getUrl('/v1/payments')); + } + + public function test_it_generates_correct_headers_for_telepagos(): void + { + Integration::create([ + 'integration_code' => 'telepagos', + 'name' => 'Telepagos', + 'url' => 'https://api.telepagos.com.ar', + 'integration_data_schema' => [ + 'username' => 'required|string', + 'password' => 'required|string', + ] + ]); + + TenantIntegration::create([ + 'tenant_code' => $this->tenant->codigo, + 'integration_code' => 'telepagos', + 'integration_data' => [ + 'username' => 'tele_user', + 'password' => 'tele_pass', + ] + ]); + + $service = new TelepagosIntegrationService(); + $service->forTenant($this->tenant->codigo); + + $headers = $service->getHeaders(); + + $expectedAuth = 'Basic ' . base64_encode('tele_user:tele_pass'); + $this->assertEquals($expectedAuth, $headers['Authorization']); + $this->assertEquals('application/json', $headers['Content-Type']); + $this->assertEquals('application/json', $headers['Accept']); + } + + public function test_it_throws_exception_if_credentials_are_missing(): void + { + Integration::create([ + 'integration_code' => 'telepagos', + 'name' => 'Telepagos', + 'url' => 'https://api.telepagos.com.ar', + 'integration_data_schema' => [ + 'username' => 'required|string', + 'password' => 'required|string', + ] + ]); + + TenantIntegration::create([ + 'tenant_code' => $this->tenant->codigo, + 'integration_code' => 'telepagos', + 'integration_data' => [ + 'username' => '', + 'password' => 'tele_pass', + ] + ]); + + $service = new TelepagosIntegrationService(); + $service->forTenant($this->tenant->codigo); + + $this->expectException(Exception::class); + $this->expectExceptionMessage("Missing username or password in Telepagos integration settings."); + + $service->getHeaders(); + } +}