From e42a42b0d54ea9a56404afc59720f736567972f6 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 27 Apr 2021 16:45:22 +0200 Subject: [PATCH 1/2] [change] (UMCS-181) Refactor applepay adapter to set certificates/key separately. --- src/Adapter/ApplepayAdapter.php | 40 +++++++++++++----------- test/integration/ApplepayAdapterTest.php | 17 +++++----- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/Adapter/ApplepayAdapter.php b/src/Adapter/ApplepayAdapter.php index cd33937a..72e496ba 100644 --- a/src/Adapter/ApplepayAdapter.php +++ b/src/Adapter/ApplepayAdapter.php @@ -36,10 +36,8 @@ class ApplepayAdapter private $request; /** - * @param string $merchantValidationURL URL for merchant validation request - * @param ApplepaySession $applePaySession Containing applepay session data. - * @param string $merchantValidationCertificatePath Path to merchant identification certificate - * @param string|null $merchantValidationCertificateKeyChainPath + * @param string $merchantValidationURL URL for merchant validation request + * @param ApplepaySession $applePaySession Containing applepay session data. * * @return string|null * @@ -47,20 +45,18 @@ class ApplepayAdapter */ public function validateApplePayMerchant( string $merchantValidationURL, - ApplepaySession $applePaySession, - string $merchantValidationCertificatePath, - ?string $merchantValidationCertificateKeyChainPath = null + ApplepaySession $applePaySession ): ?string { if (!$this->validMerchantValidationDomain($merchantValidationURL)) { - throw new ApplepayMerchantValidationException('Invalid URL used for merchantValidation request.'); + throw new ApplepayMerchantValidationException("Invalid URL used for merchantValidation request."); + } + if ($this->request === null) { + throw new ApplepayMerchantValidationException('No curl adapter initiated yet. Make sure to cal init() function before.'); } $payload = $applePaySession->jsonSerialize(); - $this->init( - $merchantValidationURL, - $payload, - $merchantValidationCertificatePath, - $merchantValidationCertificateKeyChainPath - ); + $this->setOption(CURLOPT_URL, $merchantValidationURL); + $this->setOption(CURLOPT_POSTFIELDS, $payload); + $sessionResponse = $this->execute(); $this->close(); return $sessionResponse; @@ -71,6 +67,7 @@ public function validateApplePayMerchant( * * @param string $merchantValidationURL URL used for merchant validation request. * + * @return bool */ public function validMerchantValidationDomain(string $merchantValidationURL): bool { @@ -81,18 +78,20 @@ public function validMerchantValidationDomain(string $merchantValidationURL): bo } /** - * {@inheritDoc} + * @param string $sslCert Path to merchant identification certificate. + * @param string|null $sslKey Path to merchant identification key file. + * This is necessary if the ssl cert file doesn't contain key already. + * @param string|null $caCert Path to CA certificate. */ - public function init($url, $payload, $sslCert, $caCert = null): void + public function init(string $sslCert, string $sslKey = null, string $caCert = null): void { $timeout = EnvironmentService::getTimeout(); $curlVerbose = EnvironmentService::isCurlVerbose(); - $this->request = curl_init($url); + $this->request = curl_init(); $this->setOption(CURLOPT_HTTPHEADER, ['Content-Type: application/json']); $this->setOption(CURLOPT_POST, 1); $this->setOption(CURLOPT_DNS_USE_GLOBAL_CACHE, false); - $this->setOption(CURLOPT_POSTFIELDS, $payload); $this->setOption(CURLOPT_FAILONERROR, false); $this->setOption(CURLOPT_TIMEOUT, $timeout); $this->setOption(CURLOPT_CONNECTTIMEOUT, $timeout); @@ -104,7 +103,10 @@ public function init($url, $payload, $sslCert, $caCert = null): void $this->setOption(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); $this->setOption(CURLOPT_SSLCERT, $sslCert); - if (isset($caCert)) { + if (isset($sslKey) && !empty($sslKey)) { + $this->setOption(CURLOPT_SSLKEY, $sslKey); + } + if (isset($caCert) && !empty($caCert)) { $this->setOption(CURLOPT_CAINFO, $caCert); } } diff --git a/test/integration/ApplepayAdapterTest.php b/test/integration/ApplepayAdapterTest.php index 98f002b7..011db0d0 100644 --- a/test/integration/ApplepayAdapterTest.php +++ b/test/integration/ApplepayAdapterTest.php @@ -64,18 +64,17 @@ protected function setUp(): void * * @test * - * @throws \UnzerSDK\Exceptions\ApplepayMerchantValidationException + * @throws ApplepayMerchantValidationException */ public function verifyMerchantValidationRequest(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->merchantValidationCertificatePath, null, $this->appleCaCertificatePath); $validationResponse = $appleAdapter->validateApplePayMerchant( $this->merchantValidationUrl, - $applepaySession, - $this->merchantValidationCertificatePath, - $this->appleCaCertificatePath + $applepaySession ); $this->assertNotNull($validationResponse); @@ -86,17 +85,17 @@ public function verifyMerchantValidationRequest(): void * * @test * - * @throws \UnzerSDK\Exceptions\ApplepayMerchantValidationException + * @throws ApplepayMerchantValidationException */ public function merchantValidationWorksWithoutCaCert(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->merchantValidationCertificatePath); $validationResponse = $appleAdapter->validateApplePayMerchant( $this->merchantValidationUrl, - $applepaySession, - $this->merchantValidationCertificatePath + $applepaySession ); $this->assertNotNull($validationResponse); @@ -112,14 +111,14 @@ public function merchantValidationThrowsErrorForInvalidDomain(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->merchantValidationCertificatePath); $this->expectException(ApplepayMerchantValidationException::class); $this->expectExceptionMessage('Invalid URL used for merchantValidation request.'); $appleAdapter->validateApplePayMerchant( 'https://invalid.domain.com/some/path', - $applepaySession, - $this->merchantValidationCertificatePath + $applepaySession ); } From 74ede46ebb98d81c7d77cac23555cbc83a797e90 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Wed, 28 Apr 2021 10:34:13 +0200 Subject: [PATCH 2/2] [change] (UMCS-181) Refactor applepay adapter: Add test for merchant id key file. --- src/Services/EnvironmentService.php | 8 +- test/integration/ApplepayAdapterTest.php | 125 ++++++++++++++++++----- 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/src/Services/EnvironmentService.php b/src/Services/EnvironmentService.php index 4888a8d2..ac1ea74b 100755 --- a/src/Services/EnvironmentService.php +++ b/src/Services/EnvironmentService.php @@ -41,7 +41,7 @@ class EnvironmentService public const ENV_VAR_TEST_PRIVATE_KEY_NON_3DS = 'UNZER_PAPI_TEST_PRIVATE_KEY_NON_3DS'; public const ENV_VAR_TEST_PUBLIC_KEY_NON_3DS = 'UNZER_PAPI_TEST_PUBLIC_KEY_NON_3DS'; - public const ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE = 'UNZER_APPLE_MERCHANT_CERTIFICATE_PATH'; + public const ENV_VAR_TEST_APPLE_MERCHANT_ID_FOLDER = 'UNZER_APPLE_MERCHANT_ID_PATH'; public const ENV_VAR_TEST_APPLE_CA_CERTIFICATE = 'UNZER_APPLE_CA_CERTIFICATE_PATH'; private const ENV_VAR_NAME_TIMEOUT = 'UNZER_PAPI_TIMEOUT'; @@ -142,13 +142,13 @@ public static function getTestPublicKey($non3ds = false): string } /** - * Returns the apple merchant certificate path set via environment variable. + * Returns the path to apple merchant ID folder set via environment variable. * * @return string */ - public static function getAppleMerchantCertificatePath(): string + public static function getAppleMerchantIdPath(): string { - return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE] ?? ''); + return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_ID_FOLDER] ?? ''); } /** diff --git a/test/integration/ApplepayAdapterTest.php b/test/integration/ApplepayAdapterTest.php index 011db0d0..323f3bab 100644 --- a/test/integration/ApplepayAdapterTest.php +++ b/test/integration/ApplepayAdapterTest.php @@ -34,20 +34,16 @@ class ApplepayAdapterTest extends BaseIntegrationTest { + /** @var string $appleCaCertificatePath Path to ca Certificate file. */ + protected $appleCaCertificatePath; + /** @var string $merchantValidationUrl merchant validation url for testing. */ private $merchantValidationUrl; - private $merchantValidationCertificatePath; - private $appleCaCertificatePath; - - public function domainShouldBeValidatedCorrectlyDP() - { - return [ - 'invalid: example.domain.com' => ['https://example.domain.com', false], - 'valid: https://apple-pay-gateway.apple.com/some/path' => ['https://apple-pay-gateway.apple.com/some/path', true], - 'valid: https://cn-apple-pay-gateway.apple.com' => ['https://cn-apple-pay-gateway.apple.com', true], - 'invalid: apple-pay-gateway-nc-pod1.apple.com' => ['apple-pay-gateway-nc-pod1.apple.com', false], - 'invalid: (empty)' => ['', false], - ]; - } + /** @var string $applepayCombinedCertPath Path to combined certificate file. */ + private $applepayCombinedCertPath; + /** @var string $applepayCertPath Path to merchant ID certificate file. */ + private $applepayCertPath; + /** @var string $applepayKeyPath Path to merchant ID key file. */ + private $applepayKeyPath; /** * @inheritDoc @@ -55,12 +51,16 @@ public function domainShouldBeValidatedCorrectlyDP() protected function setUp(): void { $this->merchantValidationUrl = 'https://apple-pay-gateway-cert.apple.com/paymentservices/startSession'; - $this->merchantValidationCertificatePath = EnvironmentService::getAppleMerchantCertificatePath(); + + $appleMerchantIdPath = EnvironmentService::getAppleMerchantIdPath(); + $this->applepayCertPath = $appleMerchantIdPath . 'merchant_id.pem'; + $this->applepayKeyPath = $appleMerchantIdPath . 'merchant_id.key'; + $this->applepayCombinedCertPath = $appleMerchantIdPath . 'apple-pay-cert.pem'; $this->appleCaCertificatePath = EnvironmentService::getAppleCaCertificatePath(); } /** - * test merchant validation request. + * Test merchant validation request. * * @test * @@ -70,7 +70,7 @@ public function verifyMerchantValidationRequest(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); - $appleAdapter->init($this->merchantValidationCertificatePath, null, $this->appleCaCertificatePath); + $appleAdapter->init($this->applepayCertPath, $this->applepayKeyPath, $this->appleCaCertificatePath); $validationResponse = $appleAdapter->validateApplePayMerchant( $this->merchantValidationUrl, @@ -81,17 +81,25 @@ public function verifyMerchantValidationRequest(): void } /** - * test merchant validation request without ca certificate. + * @return ApplepaySession + */ + private function createApplepaySession(): ApplepaySession + { + return new ApplepaySession('merchantIdentifier', 'displayName', 'domainName'); + } + + /** + * Test merchant validation request without ca certificate. * * @test * * @throws ApplepayMerchantValidationException */ - public function merchantValidationWorksWithoutCaCert(): void + public function merchantValidationWorksWithApplepayCertOnly(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); - $appleAdapter->init($this->merchantValidationCertificatePath); + $appleAdapter->init($this->applepayCombinedCertPath); $validationResponse = $appleAdapter->validateApplePayMerchant( $this->merchantValidationUrl, @@ -101,6 +109,68 @@ public function merchantValidationWorksWithoutCaCert(): void $this->assertNotNull($validationResponse); } + /** + * Test merchant validation request without ca certificate. + * + * @test + * + * @throws ApplepayMerchantValidationException + */ + public function merchantValidationWorksWithCertAndKey(): void + { + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->applepayCertPath, $this->applepayKeyPath); + + $validationResponse = $appleAdapter->validateApplePayMerchant( + $this->merchantValidationUrl, + $applepaySession + ); + + $this->assertNotNull($validationResponse); + } + + /** + * Test merchant validation request without key and only the merchant id certificate should throw exception. + * + * @test + * + * @throws ApplepayMerchantValidationException + */ + public function missingKeyShouldThrowException(): void + { + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->applepayCertPath); + + $this->expectException(ApplepayMerchantValidationException::class); + $appleAdapter->validateApplePayMerchant( + $this->merchantValidationUrl, + $applepaySession + ); + } + + /** + * Test merchant validation request without init() call should throw exception. + * + * @test + * + * @throws ApplepayMerchantValidationException + */ + public function missingInitCallThrowsException(): void + { + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + + $this->expectException(ApplepayMerchantValidationException::class); + $this->expectExceptionMessage('No curl adapter initiated yet. Make sure to cal init() function before.'); + + $appleAdapter->validateApplePayMerchant( + $this->merchantValidationUrl, + $applepaySession + ); + } + /** * Merchant validation call should throw Exception if domain of Validation url is invalid. * @@ -111,7 +181,7 @@ public function merchantValidationThrowsErrorForInvalidDomain(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); - $appleAdapter->init($this->merchantValidationCertificatePath); + $appleAdapter->init($this->applepayCombinedCertPath); $this->expectException(ApplepayMerchantValidationException::class); $this->expectExceptionMessage('Invalid URL used for merchantValidation request.'); @@ -140,11 +210,18 @@ public function domainShouldBeValidatedCorrectly($validationUrl, $expectedResult $this->assertEquals($expectedResult, $domainValidation); } - /** - * @return ApplepaySession + /** Provides different urls to test domain validation. + * + * @return array[] */ - private function createApplepaySession(): ApplepaySession + public function domainShouldBeValidatedCorrectlyDP(): array { - return new ApplepaySession('merchantIdentifier', 'displayName', 'domainName'); + return [ + 'invalid: example.domain.com' => ['https://example.domain.com', false], + 'valid: https://apple-pay-gateway.apple.com/some/path' => ['https://apple-pay-gateway.apple.com/some/path', true], + 'valid: https://cn-apple-pay-gateway.apple.com' => ['https://cn-apple-pay-gateway.apple.com', true], + 'invalid: apple-pay-gateway-nc-pod1.apple.com' => ['apple-pay-gateway-nc-pod1.apple.com', false], + 'invalid: (empty)' => ['', false], + ]; } }