From 04b2bb0b2a516c55ffe6fd1adc6e2c7d002179b4 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Mon, 6 Jul 2026 08:58:14 -0300 Subject: [PATCH] feat: add QR code generation and response handling to TelepagosIntegrationService with logging for errors --- .../Services/TelepagosIntegrationService.php | 59 ++++++++- .../Integration/IntegrationServiceTest.php | 112 ++++++++++++++++++ 2 files changed, 166 insertions(+), 5 deletions(-) diff --git a/app/Domains/Integration/Services/TelepagosIntegrationService.php b/app/Domains/Integration/Services/TelepagosIntegrationService.php index 4460f59..d0c7317 100644 --- a/app/Domains/Integration/Services/TelepagosIntegrationService.php +++ b/app/Domains/Integration/Services/TelepagosIntegrationService.php @@ -5,6 +5,7 @@ namespace App\Domains\Integration\Services; use Exception; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Log; use Carbon\Carbon; class TelepagosIntegrationService extends BaseIntegrationService @@ -84,12 +85,12 @@ class TelepagosIntegrationService extends BaseIntegrationService 'password' => $password, ]); - if ($response->failed() || $response->json('status') !== 'ok') { - throw new Exception("Telepagos authentication failed: " . ($response->json('message') ?? $response->body())); - } + $data = $this->handleResponse($response, 'authentication', [ + 'username' => $username, + ]); - $token = $response->json('token'); - $expiresAtStr = $response->json('expires_at'); + $token = $data['token'] ?? null; + $expiresAtStr = $data['expires_at'] ?? null; if (!$token || !$expiresAtStr) { throw new Exception("Telepagos authentication response is missing token or expires_at."); @@ -105,6 +106,54 @@ class TelepagosIntegrationService extends BaseIntegrationService return $token; } + /** + * Generate a QR code for cash-in. + * + * @param float $amount + * @param string $concept + * @param string $description + * @return array + * @throws Exception + */ + public function generateQr(float $amount, string $concept, string $description): array + { + $response = $this->client()->post('/v2/payment/cashin/qr/generate', [ + 'amount' => $amount, + 'concept' => $concept, + 'description' => $description, + ]); + + return $this->handleResponse($response, 'QR generation', [ + 'amount' => $amount, + 'concept' => $concept, + 'description' => $description, + ]); + } + + /** + * Handle the Telepagos API response, logging any failures and throwing Exceptions. + * + * @param \Illuminate\Http\Client\Response $response + * @param string $actionDescription + * @param array $context + * @return array + * @throws Exception + */ + protected function handleResponse(\Illuminate\Http\Client\Response $response, string $actionDescription, array $context = []): array + { + if ($response->failed() || $response->json('status') !== 'ok') { + $errorMessage = $response->json('message') ?? $response->body(); + Log::error("Telepagos {$actionDescription} failed: {$errorMessage}", array_merge([ + 'response_status' => $response->status(), + 'response_body' => $response->json() ?? $response->body(), + ], $context)); + + throw new Exception("Telepagos {$actionDescription} failed: {$errorMessage}"); + } + + return $response->json() ?? []; + } + /** * Clear the cached token. * diff --git a/tests/Feature/Integration/IntegrationServiceTest.php b/tests/Feature/Integration/IntegrationServiceTest.php index 534e3d8..60527ba 100644 --- a/tests/Feature/Integration/IntegrationServiceTest.php +++ b/tests/Feature/Integration/IntegrationServiceTest.php @@ -239,6 +239,14 @@ class IntegrationServiceTest extends TestCase ], 401) ]); + \Illuminate\Support\Facades\Log::shouldReceive('error') + ->once() + ->with('Telepagos authentication failed: Invalid credentials', \Mockery::on(function ($context) { + return $context['username'] === 'tele_user' + && $context['response_status'] === 401 + && $context['response_body'] === ['status' => 'error', 'message' => 'Invalid credentials']; + })); + $service = new TelepagosIntegrationService('telepagos'); $service->forTenant($this->tenant->codigo); @@ -299,4 +307,108 @@ class IntegrationServiceTest extends TestCase && $request->url() === 'https://api.telepagos.com.ar/v1/payments'; }); } + + public function test_it_generates_qr_code(): void + { + Integration::create([ + 'integration_code' => 'telepagos_homo', + '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_homo', + 'integration_data' => [ + 'username' => 'tele_user', + 'password' => 'tele_pass', + ] + ]); + + Http::fake([ + 'https://api.telepagos.com.ar/v2/auth/token' => Http::response([ + 'status' => 'ok', + 'token' => 'mock-jwt-token-123', + 'expires_at' => now()->addHour()->toDateTimeString() + ], 200), + 'https://api.telepagos.com.ar/v2/payment/cashin/qr/generate' => Http::response([ + 'status' => 'ok', + 'qr_code' => 'mock-qr-code-data', + 'qr_order_id' => 6353 + ], 200) + ]); + + $service = new TelepagosIntegrationService('telepagos'); + $service->forTenant($this->tenant->codigo); + + $result = $service->generateQr(1200.00, 'Test Concept', 'Test Description'); + + $this->assertEquals('ok', $result['status']); + $this->assertEquals('mock-qr-code-data', $result['qr_code']); + $this->assertEquals(6353, $result['qr_order_id']); + + Http::assertSent(function ($request) { + return $request->hasHeader('Authorization', 'Bearer mock-jwt-token-123') + && $request->url() === 'https://api.telepagos.com.ar/v2/payment/cashin/qr/generate' + && $request['amount'] === 1200.00 + && $request['concept'] === 'Test Concept' + && $request['description'] === 'Test Description'; + }); + } + + public function test_it_logs_and_throws_exception_on_generate_qr_error(): void + { + Integration::create([ + 'integration_code' => 'telepagos_homo', + '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_homo', + 'integration_data' => [ + 'username' => 'tele_user', + 'password' => 'tele_pass', + ] + ]); + + Http::fake([ + 'https://api.telepagos.com.ar/v2/auth/token' => Http::response([ + 'status' => 'ok', + 'token' => 'mock-jwt-token-123', + 'expires_at' => now()->addHour()->toDateTimeString() + ], 200), + 'https://api.telepagos.com.ar/v2/payment/cashin/qr/generate' => Http::response([ + 'status' => 'error', + 'message' => 'Importe inválido' + ], 422) + ]); + + \Illuminate\Support\Facades\Log::shouldReceive('error') + ->once() + ->with('Telepagos QR generation failed: Importe inválido', \Mockery::on(function ($context) { + return $context['amount'] === 1200.00 + && $context['concept'] === 'Test Concept' + && $context['description'] === 'Test Description' + && $context['response_status'] === 422 + && $context['response_body'] === ['status' => 'error', 'message' => 'Importe inválido']; + })); + + $service = new TelepagosIntegrationService('telepagos'); + $service->forTenant($this->tenant->codigo); + + $this->expectException(Exception::class); + $this->expectExceptionMessage("Telepagos QR generation failed: Importe inválido"); + + $service->generateQr(1200.00, 'Test Concept', 'Test Description'); + } }