diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 434d2f0..b2c7761 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -52,11 +52,13 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.2', '8.3', '8.4'] - laravel: ['11.*', '12.*'] + php: ['8.2', '8.3', '8.4', '8.5'] + laravel: ['11.*', '12.*', '13.*'] exclude: - php: '8.2' laravel: '12.*' + - php: '8.2' + laravel: '13.*' name: Laravel ${{ matrix.laravel }} / PHP ${{ matrix.php }} @@ -90,7 +92,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.2', '8.3', '8.4'] + php: ['8.2', '8.3', '8.4', '8.5'] symfony: ['7.0', '7.1', '7.2', '8.0'] exclude: - php: '8.2' diff --git a/composer.json b/composer.json index 93d107a..cb7d502 100644 --- a/composer.json +++ b/composer.json @@ -23,10 +23,10 @@ "friendsofphp/php-cs-fixer": "^2.0||^3.0", "guzzlehttp/guzzle": "^6.3||^7.0", "guzzlehttp/psr7": "^1.4||^2.0", - "illuminate/http": "^11.0||^12.0", - "illuminate/support": "^11.0||^12.0", - "illuminate/contracts": "^11.0||^12.0", - "orchestra/testbench": "^9.0||^10.0", + "illuminate/http": "^11.0||^12.0||^13.0", + "illuminate/support": "^11.0||^12.0||^13.0", + "illuminate/contracts": "^11.0||^12.0||^13.0", + "orchestra/testbench": "^9.0||^10.0||^11.0", "symfony/http-kernel": "^7.0||^8.0", "symfony/config": "^7.0||^8.0", "symfony/dependency-injection": "^7.0||^8.0", diff --git a/src/Environment.php b/src/Environment.php index 5e1ce37..28fffa5 100644 --- a/src/Environment.php +++ b/src/Environment.php @@ -6,11 +6,6 @@ enum Environment: string { - case PRODUCTION = 'https://api.paymentic.com/v1_2'; - case SANDBOX = 'https://api.sandbox.paymentic.com/v1_2'; - - public function getBaseUrl(): string - { - return $this->value; - } + case PRODUCTION = 'PRODUCTION'; + case SANDBOX = 'SANDBOX'; } diff --git a/src/PaymenticClient.php b/src/PaymenticClient.php index cc764b5..8f40979 100644 --- a/src/PaymenticClient.php +++ b/src/PaymenticClient.php @@ -20,13 +20,17 @@ public function __construct( RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, Environment $environment = Environment::PRODUCTION, + ?string $baseUrl = null, ) { $internalClient = new HttpClient( $httpClient, $requestFactory, $streamFactory, $apiKey, - $environment->getBaseUrl(), + $baseUrl ?? match ($environment) { + Environment::PRODUCTION => 'https://api.paymentic.com/v1_2', + Environment::SANDBOX => 'https://api.sandbox.paymentic.com/v1_2', + }, ); $this->paymentClient = new PaymentClient($internalClient); diff --git a/src/PaymenticClientFactory.php b/src/PaymenticClientFactory.php index 99ad1a2..aeba3f1 100644 --- a/src/PaymenticClientFactory.php +++ b/src/PaymenticClientFactory.php @@ -15,6 +15,7 @@ class PaymenticClientFactory { private string $apiKey; private Environment $environment = Environment::PRODUCTION; + private ?string $baseUrl = null; private ?ClientInterface $httpClient = null; private ?RequestFactoryInterface $requestFactory = null; private ?StreamFactoryInterface $streamFactory = null; @@ -50,6 +51,13 @@ public function withProduction(): self return $this; } + public function withBaseUrl(string $baseUrl): self + { + $this->baseUrl = $baseUrl; + + return $this; + } + public function withHttpClient(ClientInterface $client): self { $this->httpClient = $client; @@ -79,6 +87,7 @@ public function build(): PaymenticClient requestFactory: $this->requestFactory ?? $this->discoverRequestFactory(), streamFactory: $this->streamFactory ?? $this->discoverStreamFactory(), environment: $this->environment, + baseUrl: $this->baseUrl, ); } diff --git a/tests/Unit/EnvironmentTest.php b/tests/Unit/EnvironmentTest.php index 487cc5e..6c01804 100644 --- a/tests/Unit/EnvironmentTest.php +++ b/tests/Unit/EnvironmentTest.php @@ -10,29 +10,11 @@ final class EnvironmentTest extends TestCase { - #[Test] - public function productionReturnsCorrectBaseUrl(): void - { - $this->assertSame( - 'https://api.paymentic.com/v1_2', - Environment::PRODUCTION->getBaseUrl(), - ); - } - - #[Test] - public function sandboxReturnsCorrectBaseUrl(): void - { - $this->assertSame( - 'https://api.sandbox.paymentic.com/v1_2', - Environment::SANDBOX->getBaseUrl(), - ); - } - #[Test] public function productionHasCorrectValue(): void { $this->assertSame( - 'https://api.paymentic.com/v1_2', + 'PRODUCTION', Environment::PRODUCTION->value, ); } @@ -41,7 +23,7 @@ public function productionHasCorrectValue(): void public function sandboxHasCorrectValue(): void { $this->assertSame( - 'https://api.sandbox.paymentic.com/v1_2', + 'SANDBOX', Environment::SANDBOX->value, ); } diff --git a/tests/Unit/PaymenticClientFactoryTest.php b/tests/Unit/PaymenticClientFactoryTest.php index 1329be9..9494493 100644 --- a/tests/Unit/PaymenticClientFactoryTest.php +++ b/tests/Unit/PaymenticClientFactoryTest.php @@ -92,6 +92,23 @@ public function buildsClientWithCustomEnvironment(): void $this->assertInstanceOf(PaymenticClient::class, $client); } + #[Test] + public function buildsClientWithCustomBaseUrl(): void + { + $psrClient = $this->createStub(ClientInterface::class); + $requestFactory = $this->createStub(RequestFactoryInterface::class); + $streamFactory = $this->createStub(StreamFactoryInterface::class); + + $client = PaymenticClientFactory::create('test-api-key') + ->withBaseUrl('https://api.staging.paymentic.com/v1_2') + ->withHttpClient($psrClient) + ->withRequestFactory($requestFactory) + ->withStreamFactory($streamFactory) + ->build(); + + $this->assertInstanceOf(PaymenticClient::class, $client); + } + #[Test] public function autoDiscoversGuzzleWhenNotProvided(): void { @@ -124,6 +141,7 @@ public function supportsFluentInterface(): void $this->assertSame($factory, $factory->withEnvironment(Environment::SANDBOX)); $this->assertSame($factory, $factory->withSandbox()); $this->assertSame($factory, $factory->withProduction()); + $this->assertSame($factory, $factory->withBaseUrl('https://api.staging.paymentic.com/v1_2')); $this->assertSame($factory, $factory->withHttpClient($psrClient)); $this->assertSame($factory, $factory->withRequestFactory($requestFactory)); $this->assertSame($factory, $factory->withStreamFactory($streamFactory)); diff --git a/tests/Unit/PaymenticClientTest.php b/tests/Unit/PaymenticClientTest.php index 840813f..997457b 100644 --- a/tests/Unit/PaymenticClientTest.php +++ b/tests/Unit/PaymenticClientTest.php @@ -66,6 +66,84 @@ public function usesSandboxEnvironment(): void $this->assertInstanceOf(PaymentClient::class, $client->payment()); } + #[Test] + public function productionEnvironmentResolvesToProductionBaseUrl(): void + { + $mockClient = new MockPsrClient($this->validPingResponse()); + $client = new PaymenticClient( + apiKey: 'test-api-key', + httpClient: $mockClient, + requestFactory: new MockRequestFactory(), + streamFactory: new MockStreamFactory(), + environment: Environment::PRODUCTION, + ); + + $client->payment()->system()->ping(); + + $this->assertSame( + 'https://api.paymentic.com/v1_2/payment/ping', + (string) $mockClient->getLastRequest()?->getUri(), + ); + } + + #[Test] + public function sandboxEnvironmentResolvesToSandboxBaseUrl(): void + { + $mockClient = new MockPsrClient($this->validPingResponse()); + $client = new PaymenticClient( + apiKey: 'test-api-key', + httpClient: $mockClient, + requestFactory: new MockRequestFactory(), + streamFactory: new MockStreamFactory(), + environment: Environment::SANDBOX, + ); + + $client->payment()->system()->ping(); + + $this->assertSame( + 'https://api.sandbox.paymentic.com/v1_2/payment/ping', + (string) $mockClient->getLastRequest()?->getUri(), + ); + } + + #[Test] + public function customBaseUrlOverridesEnvironment(): void + { + $mockClient = new MockPsrClient($this->validPingResponse()); + $client = new PaymenticClient( + apiKey: 'test-api-key', + httpClient: $mockClient, + requestFactory: new MockRequestFactory(), + streamFactory: new MockStreamFactory(), + environment: Environment::PRODUCTION, + baseUrl: 'https://api.staging.paymentic.com/v1_2', + ); + + $client->payment()->system()->ping(); + + $this->assertSame( + 'https://api.staging.paymentic.com/v1_2/payment/ping', + (string) $mockClient->getLastRequest()?->getUri(), + ); + } + + /** + * @return array + */ + private function validPingResponse(): array + { + return [ + 'data' => [ + 'message' => 'pong', + 'environment' => 'sandbox', + 'tokenId' => 'token-id', + 'clientId' => 'client-id', + 'version' => '1.0', + 'scopes' => [], + ], + ]; + } + private function createClient(): PaymenticClient { return new PaymenticClient(