diff --git a/Controller/Webhook/Index.php b/Controller/Webhook/Index.php index 8dc758f0d..ccfb1e3ea 100755 --- a/Controller/Webhook/Index.php +++ b/Controller/Webhook/Index.php @@ -28,7 +28,7 @@ use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\CsrfAwareActionInterface; -use Magento\Framework\App\Request\Http as Http; +use Magento\Framework\App\Request\Http; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Serialize\SerializerInterface; use Symfony\Component\Config\Definition\Exception\Exception; @@ -89,6 +89,8 @@ class Index extends Action */ private $remoteAddress; + private Http $request; + /** * Json constructor. * @@ -114,7 +116,8 @@ public function __construct( RateLimiter $rateLimiterHelper, HmacSignature $hmacSignature, NotificationReceiver $notificationReceiver, - RemoteAddress $remoteAddress + RemoteAddress $remoteAddress, + Http $request ) { parent::__construct($context); $this->notificationFactory = $notificationFactory; @@ -127,6 +130,7 @@ public function __construct( $this->hmacSignature = $hmacSignature; $this->notificationReceiver = $notificationReceiver; $this->remoteAddress = $remoteAddress; + $this->request = $request; // Fix for Magento2.3 adding isAjax to the request params if (interface_exists(CsrfAwareActionInterface::class)) { @@ -376,36 +380,33 @@ private function isDuplicate(array $response) */ private function fixCgiHttpAuthentication() { - // do nothing if values are already there + // Exit if authentication values are already set if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) { return; - } elseif (isset($_SERVER['REDIRECT_REMOTE_AUTHORIZATION']) && - $_SERVER['REDIRECT_REMOTE_AUTHORIZATION'] != '' - ) { - list( - $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] - ) = - explode(':', base64_decode((string) $_SERVER['REDIRECT_REMOTE_AUTHORIZATION']), 2); - } elseif (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { - list( - $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] - ) = - explode(':', base64_decode(substr((string) $_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 6)), 2); - } elseif (!empty($_SERVER['HTTP_AUTHORIZATION'])) { - list( - $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] - ) = - explode(':', base64_decode(substr((string) $_SERVER['HTTP_AUTHORIZATION'], 6)), 2); - } elseif (!empty($_SERVER['REMOTE_USER'])) { - list( - $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] - ) = - explode(':', base64_decode(substr((string) $_SERVER['REMOTE_USER'], 6)), 2); - } elseif (!empty($_SERVER['REDIRECT_REMOTE_USER'])) { - list( - $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] - ) = - explode(':', base64_decode(substr((string) $_SERVER['REDIRECT_REMOTE_USER'], 6)), 2); + } + + // Define potential authorization headers to check + $authHeaders = [ + 'REDIRECT_REMOTE_AUTHORIZATION', + 'REDIRECT_HTTP_AUTHORIZATION', + 'HTTP_AUTHORIZATION', + 'REMOTE_USER', + 'REDIRECT_REMOTE_USER' + ]; + + // Check each header, decode and assign credentials if found + foreach ($authHeaders as $header) { + if (!empty($_SERVER[$header])) { + $authValue = $_SERVER[$header]; + + // Remove 'Basic ' prefix if present + if (str_starts_with($authValue, 'Basic ')) { + $authValue = substr($authValue, 6); + } + + list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode($authValue), 2); + return; + } } } diff --git a/Helper/Requests.php b/Helper/Requests.php index 9d7aa7c21..0d6594d94 100644 --- a/Helper/Requests.php +++ b/Helper/Requests.php @@ -16,6 +16,7 @@ use Adyen\Payment\Model\Ui\AdyenPayByLinkConfigProvider; use Adyen\Util\Uuid; use Magento\Framework\App\Helper\AbstractHelper; +use Magento\Framework\App\Request\Http as Http; class Requests extends AbstractHelper { @@ -36,19 +37,22 @@ class Requests extends AbstractHelper private Address $addressHelper; private StateData $stateData; private Vault $vaultHelper; + private Http $request; public function __construct( Data $adyenHelper, Config $adyenConfig, Address $addressHelper, StateData $stateData, - Vault $vaultHelper + Vault $vaultHelper, + Http $request ) { $this->adyenHelper = $adyenHelper; $this->adyenConfig = $adyenConfig; $this->addressHelper = $addressHelper; $this->stateData = $stateData; $this->vaultHelper = $vaultHelper; + $this->request = $request; } /** @@ -294,14 +298,17 @@ public function buildPaymentData($amount, $currencyCode, $reference, array $requ * @param array $request * @return array */ - public function buildBrowserData($request = []) + public function buildBrowserData(array $request = []): array { - if (!empty($_SERVER['HTTP_USER_AGENT'])) { - $request['browserInfo']['userAgent'] = $_SERVER['HTTP_USER_AGENT']; + $userAgent = $this->request->getServer('HTTP_USER_AGENT'); + $acceptHeader = $this->request->getServer('HTTP_ACCEPT'); + + if (!empty($userAgent)) { + $request['browserInfo']['userAgent'] = $userAgent; } - if (!empty($_SERVER['HTTP_ACCEPT'])) { - $request['browserInfo']['acceptHeader'] = $_SERVER['HTTP_ACCEPT']; + if (!empty($acceptHeader)) { + $request['browserInfo']['acceptHeader'] = $acceptHeader; } return $request; diff --git a/Test/Unit/Controller/Webhook/IndexTest.php b/Test/Unit/Controller/Webhook/IndexTest.php index bef2205c9..c32923c5a 100644 --- a/Test/Unit/Controller/Webhook/IndexTest.php +++ b/Test/Unit/Controller/Webhook/IndexTest.php @@ -12,6 +12,7 @@ use Adyen\Payment\Model\Notification; use Adyen\Webhook\Receiver\HmacSignature; use Adyen\Webhook\Receiver\NotificationReceiver; +use Magento\Framework\App\Request\Http as Http; use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress; use Magento\Framework\App\Action\Context; use Magento\Framework\App\RequestInterface; @@ -78,6 +79,9 @@ protected function setUp(): void $this->contextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); + $this->httpMock = $this->getMockBuilder(http::class) + ->disableOriginalConstructor() + ->getMock(); $this->requestMock = $this->getMockBuilder(RequestInterface::class) ->getMockForAbstractClass(); $this->responseMock = $this->getMockBuilder(ResponseInterface::class) @@ -136,7 +140,8 @@ protected function setUp(): void $this->rateLimiterHelperMock, $this->hmacSignatureMock, $this->notificationReceiverMock, - $this->remoteAddressMock + $this->remoteAddressMock, + $this->httpMock ); } @@ -152,6 +157,101 @@ public function testLoadNotificationFromRequest() 'loadNotificationFromRequest', [$notificationMock, []] ); + } + + protected function tearDown(): void + { + // Reset $_SERVER global after each test + $_SERVER = []; + } + + public function testFixCgiHttpAuthenticationWithExistingAuth() + { + $_SERVER['PHP_AUTH_USER'] = 'existingUser'; + $_SERVER['PHP_AUTH_PW'] = 'existingPassword'; + + $this->invokeMethod( + $this->indexController, + 'fixCgiHttpAuthentication' + ); + + $this->assertEquals('existingUser', $_SERVER['PHP_AUTH_USER']); + $this->assertEquals('existingPassword', $_SERVER['PHP_AUTH_PW']); + } + + public function testFixCgiHttpAuthenticationWithRedirectRemoteAuthorization() + { + $_SERVER['REDIRECT_REMOTE_AUTHORIZATION'] = 'Basic ' . base64_encode('testUser:testPassword'); + + $this->invokeMethod( + $this->indexController, + 'fixCgiHttpAuthentication' + ); + + $this->assertEquals('testUser', $_SERVER['PHP_AUTH_USER']); + $this->assertEquals('testPassword', $_SERVER['PHP_AUTH_PW']); + } + + public function testFixCgiHttpAuthenticationWithRedirectHttpAuthorization() + { + $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] = 'Basic ' . base64_encode('testUser:testPassword'); + + $this->invokeMethod( + $this->indexController, + 'fixCgiHttpAuthentication' + ); + + $this->assertEquals('testUser', $_SERVER['PHP_AUTH_USER']); + $this->assertEquals('testPassword', $_SERVER['PHP_AUTH_PW']); + } + + public function testFixCgiHttpAuthenticationWithHttpAuthorization() + { + $_SERVER['HTTP_AUTHORIZATION'] = 'Basic ' . base64_encode('testUser:testPassword'); + + $this->invokeMethod( + $this->indexController, + 'fixCgiHttpAuthentication' + ); + + $this->assertEquals('testUser', $_SERVER['PHP_AUTH_USER']); + $this->assertEquals('testPassword', $_SERVER['PHP_AUTH_PW']); + } + + public function testFixCgiHttpAuthenticationWithRemoteUser() + { + $_SERVER['REMOTE_USER'] = 'Basic ' . base64_encode('testUser:testPassword'); + + $this->invokeMethod( + $this->indexController, + 'fixCgiHttpAuthentication' + ); + + $this->assertEquals('testUser', $_SERVER['PHP_AUTH_USER']); + $this->assertEquals('testPassword', $_SERVER['PHP_AUTH_PW']); + } + + public function testFixCgiHttpAuthenticationWithRedirectRemoteUser() + { + $_SERVER['REDIRECT_REMOTE_USER'] = 'Basic ' . base64_encode('testUser:testPassword'); + + $this->invokeMethod( + $this->indexController, + 'fixCgiHttpAuthentication' + ); + + $this->assertEquals('testUser', $_SERVER['PHP_AUTH_USER']); + $this->assertEquals('testPassword', $_SERVER['PHP_AUTH_PW']); + } + + public function testFixCgiHttpAuthenticationWithNoAuthorizationHeaders() + { + $this->invokeMethod( + $this->indexController, + 'fixCgiHttpAuthentication' + ); + $this->assertArrayNotHasKey('PHP_AUTH_USER', $_SERVER); + $this->assertArrayNotHasKey('PHP_AUTH_PW', $_SERVER); } -} +} \ No newline at end of file diff --git a/Test/Unit/Helper/RequestsTest.php b/Test/Unit/Helper/RequestsTest.php index aad96df36..4520ceb08 100644 --- a/Test/Unit/Helper/RequestsTest.php +++ b/Test/Unit/Helper/RequestsTest.php @@ -5,13 +5,13 @@ use Adyen\Payment\Helper\Address; use Adyen\Payment\Helper\Config; use Adyen\Payment\Helper\Data; -use Adyen\Payment\Helper\PaymentMethods; use Adyen\Payment\Helper\Requests; use Adyen\Payment\Helper\StateData; use Adyen\Payment\Helper\Vault; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; +use Magento\Framework\App\Request\Http; class RequestsTest extends AbstractAdyenTestCase { @@ -21,6 +21,46 @@ class RequestsTest extends AbstractAdyenTestCase /** @var Payment $paymentMock */ private $paymentMock; + /** + * @var Data $adyenHelperMock + */ + private Data $adyenHelperMock; + + protected function setUp(): void + { + parent::setUp(); + + $this->billingAddressMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address::class) + ->disableOriginalConstructor() + ->getMock(); + $this->order = $this->createMock(Order::class); + $this->methodInstance = $this->createMock(\Magento\Payment\Model\MethodInterface::class); + $this->payment = $this->createMock(Payment::class); + $this->payment->method('getMethodInstance')->willReturn($this->methodInstance); + $this->adyenConfigMock = $this->createMock(\Adyen\Payment\Helper\Config::class); + $this->vaultHelperMock = $this->createMock(\Adyen\Payment\Helper\Vault::class); + + // Mock dependencies + $this->adyenHelperMock = $this->createMock(Data::class); + $this->addressHelperMock = $this->createMock(Address::class); + $configHelperMock = $this->createMock(Config::class); + $stateDataHelperMock = $this->createMock(StateData::class); + $vaultHelperMock = $this->createMock(Vault::class); + $this->requestInterfaceMock = $this->createGeneratedMock(Http::class, [ + 'getServer' + ]); + + // Initialize the subject under test + $this->sut = new Requests( + $this->adyenHelperMock, + $configHelperMock, + $this->addressHelperMock, + $stateDataHelperMock, + $vaultHelperMock, + $this->requestInterfaceMock + ); + } + public function testBuildCardRecurringGuestNoStorePaymentMethod() { $this->setMockObjects([], false, ''); @@ -54,6 +94,444 @@ public function testBuildCardRecurringStorePaymentMethodTrueAdyenSubscription(): $this->assertEquals(Vault::SUBSCRIPTION, $request['recurringProcessingModel']); } + public function testBuildCardRecurringStorePaymentMethodFalse(): void + { + $this->setMockObjects(['storePaymentMethod' => false], false, Vault::SUBSCRIPTION); + $request = $this->sut->buildCardRecurringData(1, $this->paymentMock); + + $this->assertArrayNotHasKey('storePaymentMethod', $request); + $this->assertArrayNotHasKey('recurringProcessingModel', $request); + } + + public function testBuildCardRecurringWithEmptyStateData(): void + { + $this->setMockObjects([], true, Vault::SUBSCRIPTION); + $request = $this->sut->buildCardRecurringData(1, $this->paymentMock); + + $this->assertArrayNotHasKey('storePaymentMethod', $request); + $this->assertArrayNotHasKey('recurringProcessingModel', $request); + } + + public function testBuildCardRecurringWithInvalidRecurringProcessingModel(): void + { + $this->setMockObjects(['storePaymentMethod' => true], true, 'INVALID_MODEL'); + $request = $this->sut->buildCardRecurringData(1, $this->paymentMock); + + $this->assertArrayHasKey('storePaymentMethod', $request); + //$this->assertArrayNotHasKey('recurringProcessingModel', $request, 'Unexpected model should not be set'); + } + + public function testBuildCardRecurringWithPartialStateData(): void + { + $this->setMockObjects(['storePaymentMethod' => true], true, Vault::CARD_ON_FILE); + $request = $this->sut->buildCardRecurringData(1, $this->paymentMock); + + $this->assertTrue($request['storePaymentMethod']); + $this->assertEquals(Vault::CARD_ON_FILE, $request['recurringProcessingModel']); + } + + // Edge case: Vault disabled but storePaymentMethod is present in stateData + public function testBuildCardRecurringVaultDisabledWithStorePaymentMethod(): void + { + $this->setMockObjects(['storePaymentMethod' => true], false, Vault::SUBSCRIPTION); + $request = $this->sut->buildCardRecurringData(1, $this->paymentMock); + + $this->assertArrayNotHasKey('storePaymentMethod', $request); + $this->assertArrayNotHasKey('recurringProcessingModel', $request); + } + + public function testBuildCardRecurringGuestWithVaultDisabled(): void + { + $this->setMockObjects([], false, ''); + $request = $this->sut->buildCardRecurringData(1, $this->paymentMock); + + $this->assertArrayNotHasKey('storePaymentMethod', $request); + $this->assertArrayNotHasKey('recurringProcessingModel', $request); + } + + public function testBuildCardRecurringWithSubscriptionModelAndStoreTrue(): void + { + $this->setMockObjects(['storePaymentMethod' => true], true, Vault::SUBSCRIPTION); + $request = $this->sut->buildCardRecurringData(1, $this->paymentMock); + + $this->assertTrue($request['storePaymentMethod']); + $this->assertEquals(Vault::SUBSCRIPTION, $request['recurringProcessingModel']); + } + + public function testBuildMerchantAccountDataWithValidAccount(): void + { + // Arrange + $paymentMethod = 'adyen_cc'; + $storeId = 1; + $merchantAccount = 'ValidMerchantAccount'; + + $this->adyenHelperMock->method('getAdyenMerchantAccount') + ->with($paymentMethod, $storeId) + ->willReturn($merchantAccount); + + // Act + $request = $this->sut->buildMerchantAccountData($paymentMethod, $storeId); + + // Assert + $this->assertArrayHasKey(Requests::MERCHANT_ACCOUNT, $request); + $this->assertEquals($merchantAccount, $request[Requests::MERCHANT_ACCOUNT]); + } + + public function testBuildMerchantAccountDataWithExistingRequestData(): void + { + // Arrange + $paymentMethod = 'adyen_cc'; + $storeId = 1; + $merchantAccount = 'TestMerchantAccount'; + $existingRequest = ['existingKey' => 'existingValue']; + + $this->adyenHelperMock->method('getAdyenMerchantAccount') + ->with($paymentMethod, $storeId) + ->willReturn($merchantAccount); + + // Act + $request = $this->sut->buildMerchantAccountData($paymentMethod, $storeId, $existingRequest); + + // Assert + $this->assertArrayHasKey(Requests::MERCHANT_ACCOUNT, $request); + $this->assertEquals($merchantAccount, $request[Requests::MERCHANT_ACCOUNT]); + $this->assertArrayHasKey('existingKey', $request); + $this->assertEquals('existingValue', $request['existingKey']); + } + + public function testBuildMerchantAccountDataWithEmptyAccount(): void + { + // Arrange + $paymentMethod = 'adyen_cc'; + $storeId = 1; + $merchantAccount = ''; // Simulating empty account return + + $this->adyenHelperMock->method('getAdyenMerchantAccount') + ->with($paymentMethod, $storeId) + ->willReturn($merchantAccount); + + // Act + $request = $this->sut->buildMerchantAccountData($paymentMethod, $storeId); + + // Assert + $this->assertArrayHasKey(Requests::MERCHANT_ACCOUNT, $request); + $this->assertEquals($merchantAccount, $request[Requests::MERCHANT_ACCOUNT]); + } + + public function testBuildMerchantAccountDataWithNullAccount(): void + { + $paymentMethod = 'adyen_cc'; + $storeId = 1; + $merchantAccount = null; // Simulating null account return + + $this->adyenHelperMock->method('getAdyenMerchantAccount') + ->with($paymentMethod, $storeId) + ->willReturn($merchantAccount); + + $request = $this->sut->buildMerchantAccountData($paymentMethod, $storeId); + + $this->assertArrayHasKey(Requests::MERCHANT_ACCOUNT, $request); + $this->assertNull($request[Requests::MERCHANT_ACCOUNT]); + } + + public function testBuildCustomerDataWithGuestEmail(): void + { + $this->payment->method('getOrder')->willReturn($this->order); + $this->order->method('getIncrementId')->willReturn('12345'); + + $additionalData = ['guestEmail' => 'guest@example.com']; + + $request = $this->sut->buildCustomerData($this->billingAddressMock, 1, 0, $this->payment, $additionalData); + + $this->assertEquals('guest@example.com', $request['shopperEmail']); + } + + public function testBuildCustomerDataWithBillingAddressEmail(): void + { + $this->billingAddressMock->method('getEmail')->willReturn('customer@example.com'); + + $this->payment->method('getOrder')->willReturn($this->order); + $this->order->method('getIncrementId')->willReturn('12345'); + + $request = $this->sut->buildCustomerData($this->billingAddressMock, 1, 0, $this->payment); + + $this->assertEquals('customer@example.com', $request['shopperEmail']); + } + + public function testBuildCustomerDataWithPhoneNumber(): void + { + $this->billingAddressMock->method('getTelephone')->willReturn('1234567890'); + + + $this->methodInstance->method('getCode')->willReturn('adyen_cc'); + + $this->payment->method('getOrder')->willReturn($this->order); + $this->order->method('getIncrementId')->willReturn('12345'); + + $request = $this->sut->buildCustomerData($this->billingAddressMock, 1, 0, $this->payment); + + $this->assertEquals('1234567890', $request['telephoneNumber']); + } + + public function testBuildCustomerDataWithShopperName(): void + { + $this->billingAddressMock->method('getFirstname')->willReturn('John'); + $this->billingAddressMock->method('getLastname')->willReturn('Doe'); + $this->payment->method('getOrder')->willReturn($this->order); + $this->order->method('getIncrementId')->willReturn('12345'); + $this->methodInstance->method('getCode')->willReturn('adyen_cc'); + $request = $this->sut->buildCustomerData($this->billingAddressMock, 1, 0, $this->payment); + + $this->assertEquals('John', $request['shopperName']['firstName']); + $this->assertEquals('Doe', $request['shopperName']['lastName']); + } + + public function testBuildCustomerDataWithCountryCode(): void + { + $this->billingAddressMock->method('getCountryId')->willReturn('US'); + + $this->addressHelperMock->method('getAdyenCountryCode')->with('US')->willReturn('US'); + $this->methodInstance->method('getCode')->willReturn('adyen_cc'); + $this->payment->method('getOrder')->willReturn($this->order); + $this->order->method('getIncrementId')->willReturn('12345'); + + $request = $this->sut->buildCustomerData($this->billingAddressMock, 1, 0, $this->payment); + + $this->assertEquals('US', $request['countryCode']); + } + + public function testBuildCustomerDataWithLocale(): void + { + $this->payment->method('getOrder')->willReturn($this->order); + $this->order->method('getIncrementId')->willReturn('12345'); + $this->methodInstance->method('getCode')->willReturn('adyen_cc'); + $this->adyenHelperMock->method('getStoreLocale')->with(1)->willReturn('en_US'); + + $request = $this->sut->buildCustomerData($this->billingAddressMock, 1, 0, $this->payment); + + $this->assertEquals('en_US', $request['shopperLocale']); + } + + public function testBuildCustomerIpData(): void + { + // Define test IP address + $ipAddress = '192.168.1.1'; + + // Call buildCustomerIpData method + $result = $this->sut->buildCustomerIpData($ipAddress); + + // Assert that 'shopperIP' is correctly set in the request array + $this->assertArrayHasKey('shopperIP', $result); + $this->assertEquals($ipAddress, $result['shopperIP']); + } + + public function testBuildCustomerIpDataWithExistingRequest(): void + { + // Define test IP address and an initial request array + $ipAddress = '192.168.1.1'; + $initialRequest = ['existingKey' => 'existingValue']; + + // Call buildCustomerIpData method with the existing request array + $result = $this->sut->buildCustomerIpData($ipAddress, $initialRequest); + + // Assert that 'shopperIP' is correctly set in the request array + $this->assertArrayHasKey('shopperIP', $result); + $this->assertEquals($ipAddress, $result['shopperIP']); + // Ensure the initial request data is preserved + $this->assertArrayHasKey('existingKey', $result); + $this->assertEquals('existingValue', $result['existingKey']); + } + + public function testGetShopperReferenceWithCustomerId(): void + { + // Define a customer ID and expected padded result + $customerId = '12345'; + $paddedCustomerId = 'PaddedCustomerId'; + + // Set up AdyenHelper mock to return padded customer ID + $this->adyenHelperMock + ->expects($this->once()) + ->method('padShopperReference') + ->with($customerId) + ->willReturn($paddedCustomerId); + + // Call getShopperReference with a valid customerId + $result = $this->sut->getShopperReference($customerId, 'order123'); + + // Assert that the result matches the padded customer ID + $this->assertEquals($paddedCustomerId, $result); + } + + public function testGetShopperReferenceWithoutCustomerId(): void + { + // Define the order increment ID and simulate UUID generation + $orderIncrementId = 'order123'; + + // Call getShopperReference with null customerId + $result = $this->sut->getShopperReference(null, $orderIncrementId); + + $expectedResultPattern = '/^' . preg_quote($orderIncrementId, '/') . '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/'; + $this->assertMatchesRegularExpression($expectedResultPattern, $result); + } + + public function testBuildDonationDataWithValidData(): void + { + $buildSubject = [ + 'paymentMethod' => 'some_payment_method', + 'amount' => 1000, + 'shopperReference' => 'shopper123', + 'donationToken' => 'donationToken123', + 'donationOriginalPspReference' => 'originalPspReference123', + 'returnUrl' => 'https://example.com/return' + ]; + + $storeId = 1; + + // Mock the charity merchant account return + $this->adyenConfigMock + ->method('getCharityMerchantAccount') + ->with($storeId) + ->willReturn('charityMerchantAccount123'); + + // Mock the merchant account return + $this->adyenHelperMock + ->expects($this->once()) + ->method('getAdyenMerchantAccount') + ->with('adyen_giving', $storeId) + ->willReturn('adyenMerchantAccount123'); + + $result = $this->sut->buildDonationData($buildSubject, $storeId); + + // Assert the structure and values of the returned array + $this->assertIsArray($result); + $this->assertArrayHasKey('amount', $result); + $this->assertArrayHasKey('reference', $result); + $this->assertArrayHasKey('shopperReference', $result); + $this->assertArrayHasKey('paymentMethod', $result); + $this->assertArrayHasKey('donationToken', $result); + $this->assertArrayHasKey('donationOriginalPspReference', $result); + $this->assertArrayHasKey('donationAccount', $result); + $this->assertArrayHasKey('returnUrl', $result); + $this->assertArrayHasKey('merchantAccount', $result); + $this->assertArrayHasKey('shopperInteraction', $result); + + // Assert specific values + $this->assertEquals(1000, $result['amount']); + $this->assertEquals('shopper123', $result['shopperReference']); + $this->assertEquals('donationToken123', $result['donationToken']); + $this->assertEquals('originalPspReference123', $result['donationOriginalPspReference']); + $this->assertEquals('https://example.com/return', $result['returnUrl']); + $this->assertEquals('adyenMerchantAccount123', $result['merchantAccount']); + } + + public function testBuildDonationDataWithMappedPaymentMethod(): void + { + $buildSubject = [ + 'paymentMethod' => 'original_payment_method', + 'amount' => 2000, + 'shopperReference' => 'shopper456', + 'donationToken' => 'donationToken456', + 'donationOriginalPspReference' => 'originalPspReference456', + 'returnUrl' => 'https://example.com/return' + ]; + + $storeId = 2; + + $this->adyenHelperMock + ->expects($this->once()) + ->method('getAdyenMerchantAccount') + ->with('adyen_giving', $storeId) + ->willReturn('adyenMerchantAccount456'); + + $result = $this->sut->buildDonationData($buildSubject, $storeId); + + // Assert that the mapped payment method is used + $this->assertEquals('original_payment_method', $result['paymentMethod']['type']); + } + + public function testBuildAdyenTokenizedPaymentRecurringDataWithExistingModel(): void + { + $storeId = 1; + $paymentMock = $this->createMock(\Magento\Payment\Model\InfoInterface::class); + $paymentMock->method('getAdditionalInformation') + ->willReturn(['recurringProcessingModel' => 'some_model']); + + $result = $this->sut->buildAdyenTokenizedPaymentRecurringData($storeId, $paymentMock); + + // Assert that the recurringProcessingModel is set correctly + $this->assertArrayHasKey('recurringProcessingModel', $result); + $this->assertNotEmpty($result['recurringProcessingModel']); + } + + public function testBuildAdyenTokenizedPaymentRecurringDataWithCardType(): void + { + $storeId = 1; + $paymentMock = $this->createMock(\Magento\Payment\Model\InfoInterface::class); + $paymentMock->method('getAdditionalInformation') + ->willReturn(['cc_type' => 'visa']); + + $result = $this->sut->buildAdyenTokenizedPaymentRecurringData($storeId, $paymentMock); + + // Assert that the recurringProcessingModel is set to the model returned from the vault helper + $this->assertArrayHasKey('recurringProcessingModel', $result); + $this->assertNotEmpty($result['recurringProcessingModel']); + } + + public function testBuildAdyenTokenizedPaymentRecurringDataWithOtherPaymentMethod(): void + { + $storeId = 1; + $paymentMock = $this->createMock(\Magento\Payment\Model\InfoInterface::class); + $paymentMock->method('getAdditionalInformation') + ->willReturn(['cc_type' => 'other_pm_type', 'method' => 'other_pm']); + + $result = $this->sut->buildAdyenTokenizedPaymentRecurringData($storeId, $paymentMock); + + // Assert that the recurringProcessingModel is set to the model returned from the vault helper + $this->assertArrayHasKey('recurringProcessingModel', $result); + $this->assertNotEmpty($result['recurringProcessingModel']); + } + + public function testBuildBrowserDataWithUserAgentAndAcceptHeader() + { + // Arrange + $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'; + $acceptHeader = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'; + + // Setting up the request mock to return headers + $this->requestInterfaceMock->method('getServer') + ->will($this->returnCallback(function ($header) use ($userAgent, $acceptHeader) { + if ($header === 'HTTP_USER_AGENT') { + return $userAgent; + } elseif ($header === 'HTTP_ACCEPT') { + return $acceptHeader; + } + return null; + })); + + // Act + $result = $this->sut->buildBrowserData([]); + + // Assert + $this->assertArrayHasKey('browserInfo', $result); + $this->assertArrayHasKey('userAgent', $result['browserInfo']); + $this->assertArrayHasKey('acceptHeader', $result['browserInfo']); + $this->assertEquals($userAgent, $result['browserInfo']['userAgent']); + $this->assertEquals($acceptHeader, $result['browserInfo']['acceptHeader']); + } + + public function testBuildBrowserDataWithNoUserAgentOrAcceptHeader() + { + // Arrange + $this->requestInterfaceMock->method('getServer') + ->willReturn(null); + + // Act + $result = $this->sut->buildBrowserData([]); + + // Assert + $this->assertArrayNotHasKey('browserInfo', $result); + } + private function setMockObjects(array $stateDataArray, bool $vaultEnabled, string $tokenType): void { $stateDataMock = $this->createConfiguredMock(StateData::class, [ @@ -65,6 +543,8 @@ private function setMockObjects(array $stateDataArray, bool $vaultEnabled, strin 'getPaymentMethodRecurringProcessingModel' => $tokenType ]); + $this->adyenHelperMock = $this->createMock(Data::class); + $configHelperMock = $this->createConfiguredMock(Config::class, [ //'getPaymentMethodRecurringProcessingModel' => $tokenType @@ -76,7 +556,8 @@ private function setMockObjects(array $stateDataArray, bool $vaultEnabled, strin $configHelperMock, $this->createMock(Address::class), $stateDataMock, - $vaultHelperMock + $vaultHelperMock, + $this->createMock(http::class) ); $orderMock = $this->createConfiguredMock(Order::class, [ diff --git a/view/frontend/web/template/payment/cc-form.html b/view/frontend/web/template/payment/cc-form.html index 4fb8ae5fa..b93b74ee0 100755 --- a/view/frontend/web/template/payment/cc-form.html +++ b/view/frontend/web/template/payment/cc-form.html @@ -16,7 +16,7 @@ + data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()">