diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 8331abca6..968ede2ad 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -1,17 +1,16 @@ name: Magento 2 E2E Pipeline run-name: Adyen Magento 2 Payment Plugin E2E tests -on: +on: pull_request: - types: [opened, synchronize] + types: [opened, synchronize, ready_for_review] + pull_request_target: + types: [opened, synchronize, ready_for_review] jobs: build: - if: | - ${{ - github.event.pull_request.draft == false && - (github.actor != 'renovate[bot]' || github.actor != 'lgtm-com[bot]') - }} + if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) + environment: ${{ (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) && 'external' || 'internal' }} runs-on: group: larger-runners labels: ubuntu-latest-8-cores @@ -43,7 +42,7 @@ jobs: - name: Install plugin run: docker exec -u www-data magento2-container make plugin - + - name: Kill Cron Jobs run: docker exec magento2-container /etc/init.d/cron stop @@ -51,7 +50,7 @@ jobs: uses: actions/checkout@v3 with: repository: Adyen/adyen-magento2-dev - ref: 'main' + ref: "main" token: ${{ secrets.ADYEN_AUTOMATION_BOT_TEST_ACCESS_TOKEN }} path: Developer diff --git a/.github/workflows/graphql-test.yml b/.github/workflows/graphql-test.yml index 0486e3e4d..ef1627349 100644 --- a/.github/workflows/graphql-test.yml +++ b/.github/workflows/graphql-test.yml @@ -1,8 +1,10 @@ name: GraphQL Tests -on: [pull_request] +on: [pull_request, pull_request_target] jobs: build: + if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) + environment: ${{ (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) && 'external' || 'internal' }} strategy: matrix: php-version: ["8.1"] diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a844d5a96..272f8b25c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,47 +1,49 @@ name: Main CI workflow -on: [pull_request] +on: [pull_request, pull_request_target] jobs: build: + if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) + environment: ${{ (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) && 'external' || 'internal' }} runs-on: ubuntu-latest - + strategy: matrix: php-version: [8.1] - + steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Use PHP ${{ matrix.php-version }} - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - tools: composer:v1 - - - name: Test plugin installation - run: | - echo "{\"http-basic\":{\"repo.magento.com\":{\"username\":\"${MAGENTO_USERNAME}\",\"password\":\"${MAGENTO_PASSWORD}\"}}}" > auth.json - composer install --prefer-dist - env: - CI: true - MAGENTO_USERNAME: ${{ secrets.MAGENTO_USERNAME }} - MAGENTO_PASSWORD: ${{ secrets.MAGENTO_PASSWORD }} - - - name: Code Sniffer - run: vendor/bin/phpcs --extensions=php,phtml --error-severity=10 --ignore-annotations - - - name: Run PHPUnit - run: vendor/bin/phpunit --coverage-clover=build/clover.xml --log-junit=build/tests-log.xml -c Test/phpunit.xml Test/Unit - - - name: Fix code coverage paths - run: sed -i "s;`pwd`/;;g" build/*.xml - - - name: SonarCloud Scan - if: ${{ env.SONAR_TOKEN }} - uses: SonarSource/sonarcloud-github-action@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Use PHP ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + tools: composer:v1 + + - name: Test plugin installation + run: | + echo "{\"http-basic\":{\"repo.magento.com\":{\"username\":\"${MAGENTO_USERNAME}\",\"password\":\"${MAGENTO_PASSWORD}\"}}}" > auth.json + composer install --prefer-dist + env: + CI: true + MAGENTO_USERNAME: ${{ secrets.MAGENTO_USERNAME }} + MAGENTO_PASSWORD: ${{ secrets.MAGENTO_PASSWORD }} + + - name: Code Sniffer + run: vendor/bin/phpcs --extensions=php,phtml --error-severity=10 --ignore-annotations + + - name: Run PHPUnit + run: vendor/bin/phpunit --coverage-clover=build/clover.xml --log-junit=build/tests-log.xml -c Test/phpunit.xml Test/Unit + + - name: Fix code coverage paths + run: sed -i "s;`pwd`/;;g" build/*.xml + + - name: SonarCloud Scan + if: ${{ env.SONAR_TOKEN }} + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/restapi-test.yml b/.github/workflows/restapi-test.yml index d7227b9b9..ba4607099 100644 --- a/.github/workflows/restapi-test.yml +++ b/.github/workflows/restapi-test.yml @@ -1,8 +1,10 @@ name: REST API Tests -on: [pull_request] +on: [pull_request, pull_request_target] jobs: build: + if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) + environment: ${{ (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) && 'external' || 'internal' }} strategy: matrix: php-version: ["8.1"] diff --git a/Block/Checkout/Multishipping/Success.php b/Block/Checkout/Multishipping/Success.php index f718a7ff2..f647976a6 100644 --- a/Block/Checkout/Multishipping/Success.php +++ b/Block/Checkout/Multishipping/Success.php @@ -28,12 +28,12 @@ class Success extends \Magento\Multishipping\Block\Checkout\Success { - const FINAL_RESULT_CODES = array( - PaymentResponseHandler::AUTHORISED, - PaymentResponseHandler::PENDING, - PaymentResponseHandler::REFUSED, - PaymentResponseHandler::PRESENT_TO_SHOPPER - ); + const ACTION_REQUIRED_STATUSES = [ + PaymentResponseHandler::REDIRECT_SHOPPER, + PaymentResponseHandler::IDENTIFY_SHOPPER, + PaymentResponseHandler::CHALLENGE_SHOPPER, + PaymentResponseHandler::PENDING + ]; /** * @var bool @@ -122,7 +122,7 @@ public function __construct( public function renderAction() { foreach ($this->paymentResponseEntities as $paymentResponseEntity) { - if (!in_array($paymentResponseEntity['result_code'], self::FINAL_RESULT_CODES)) { + if (in_array($paymentResponseEntity['result_code'], self::ACTION_REQUIRED_STATUSES)) { return true; } } @@ -189,7 +189,7 @@ private function setOrderInfo($orderIds) public function getIsPaymentCompleted(int $orderId) { // TODO check for all completed responses, not only Authorised, Refused, Pending or PresentToShopper - return in_array($this->ordersInfo[$orderId]['resultCode'], self::FINAL_RESULT_CODES); + return !in_array($this->ordersInfo[$orderId]['resultCode'], self::ACTION_REQUIRED_STATUSES); } public function getPaymentButtonLabel(int $orderId) diff --git a/Controller/Return/Index.php b/Controller/Return/Index.php index e5cf3cb07..4a175fe6b 100755 --- a/Controller/Return/Index.php +++ b/Controller/Return/Index.php @@ -394,6 +394,7 @@ protected function validatePayloadAndReturnResponse(array $result): array $request["details"] = $details; $requestOptions['idempotencyKey'] = $this->idempotencyHelper->generateIdempotencyKey($request); + $requestOptions['headers'] = $this->adyenDataHelper->buildRequestHeaders(); try { $response = $service->paymentsDetails($request, $requestOptions); diff --git a/Gateway/Http/Client/TransactionCancel.php b/Gateway/Http/Client/TransactionCancel.php index d83b8fb7d..49ae63440 100644 --- a/Gateway/Http/Client/TransactionCancel.php +++ b/Gateway/Http/Client/TransactionCancel.php @@ -46,6 +46,7 @@ public function placeRequest(TransferInterface $transferObject): array $headers['idempotencyExtraData'] ?? null ); $requestOptions['idempotencyKey'] = $idempotencyKey; + $requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders(); $this->adyenHelper->logRequest($requests, Client::API_CHECKOUT_VERSION, '/cancels'); try { $responses = $service->cancels($requests, $requestOptions); diff --git a/Gateway/Http/Client/TransactionCapture.php b/Gateway/Http/Client/TransactionCapture.php index 9a80ca339..fcbea1a72 100644 --- a/Gateway/Http/Client/TransactionCapture.php +++ b/Gateway/Http/Client/TransactionCapture.php @@ -57,6 +57,7 @@ public function placeRequest(TransferInterface $transferObject): array ); $requestOptions['idempotencyKey'] = $idempotencyKey; + $requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders(); if (array_key_exists(self::MULTIPLE_AUTHORIZATIONS, $request)) { return $this->placeMultipleCaptureRequests($service, $request, $requestOptions); diff --git a/Gateway/Http/Client/TransactionDonate.php b/Gateway/Http/Client/TransactionDonate.php index d6c5d4313..7475993fc 100644 --- a/Gateway/Http/Client/TransactionDonate.php +++ b/Gateway/Http/Client/TransactionDonate.php @@ -53,6 +53,7 @@ public function placeRequest(TransferInterface $transferObject) ); $requestOptions['idempotencyKey'] = $idempotencyKey; + $requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders(); $this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, 'donations'); try { diff --git a/Gateway/Http/Client/TransactionPayment.php b/Gateway/Http/Client/TransactionPayment.php index 140d194c2..a490caceb 100644 --- a/Gateway/Http/Client/TransactionPayment.php +++ b/Gateway/Http/Client/TransactionPayment.php @@ -84,6 +84,7 @@ public function placeRequest(TransferInterface $transferObject): array $headers['idempotencyExtraData'] ?? null ); $requestOptions['idempotencyKey'] = $idempotencyKey; + $requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders(); $this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/payments'); $response = $service->payments($request, $requestOptions); diff --git a/Gateway/Http/Client/TransactionPaymentLinks.php b/Gateway/Http/Client/TransactionPaymentLinks.php index 8f03888e1..e3e71d13a 100644 --- a/Gateway/Http/Client/TransactionPaymentLinks.php +++ b/Gateway/Http/Client/TransactionPaymentLinks.php @@ -52,6 +52,7 @@ public function placeRequest(TransferInterface $transferObject): array ); $requestOptions['idempotencyKey'] = $idempotencyKey; + $requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders(); $this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/paymentLinks'); try { diff --git a/Gateway/Http/Client/TransactionRefund.php b/Gateway/Http/Client/TransactionRefund.php index 719aecb0a..c67b95503 100644 --- a/Gateway/Http/Client/TransactionRefund.php +++ b/Gateway/Http/Client/TransactionRefund.php @@ -47,8 +47,9 @@ public function placeRequest(TransferInterface $transferObject): array $headers['idempotencyExtraData'] ?? null ); $requestOptions['idempotencyKey'] = $idempotencyKey; - $this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/refunds'); + $requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders(); + $this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/refunds'); try { $response = $service->refunds($request, $requestOptions); // Add amount original reference and amount information to response diff --git a/Gateway/Request/CheckoutDataBuilder.php b/Gateway/Request/CheckoutDataBuilder.php index a2d324c7f..657a3b750 100644 --- a/Gateway/Request/CheckoutDataBuilder.php +++ b/Gateway/Request/CheckoutDataBuilder.php @@ -210,10 +210,6 @@ public function build(array $buildSubject): array $requestBody['additionalData']['allow3DS2'] = true; - if (isset($requestBodyPaymentMethod)) { - $requestBody['paymentMethod'] = $requestBodyPaymentMethod; - } - return [ 'body' => $requestBody ]; diff --git a/Gateway/Request/GiftcardDataBuilder.php b/Gateway/Request/GiftcardDataBuilder.php index d71c37eeb..ca4135fcd 100644 --- a/Gateway/Request/GiftcardDataBuilder.php +++ b/Gateway/Request/GiftcardDataBuilder.php @@ -31,7 +31,8 @@ public function build(array $buildSubject): array { /** @var PaymentDataObject $paymentDataObject */ $paymentDataObject = SubjectReader::readPayment($buildSubject); - $order = $paymentDataObject->getOrder(); + $payment = $paymentDataObject->getPayment(); + $order = $payment->getOrder(); $request = []; diff --git a/Gateway/Response/PaymentPosCloudHandler.php b/Gateway/Response/PaymentPosCloudHandler.php index b8a1ae215..b697b7b3c 100644 --- a/Gateway/Response/PaymentPosCloudHandler.php +++ b/Gateway/Response/PaymentPosCloudHandler.php @@ -57,6 +57,10 @@ public function handle(array $handlingSubject, array $response) ); $payment->setAdditionalInformation('additionalData', $paymentResponseDecoded['additionalData']); + if (isset($paymentResponseDecoded['additionalData']['pspReference'])) { + $payment->setAdditionalInformation('pspReference', $paymentResponseDecoded['additionalData']['pspReference']); + } + $this->vaultHelper->handlePaymentResponseRecurringDetails( $payment->getOrder()->getPayment(), $paymentResponseDecoded diff --git a/Helper/Data.php b/Helper/Data.php index 1e82490b6..5ee44447f 100755 --- a/Helper/Data.php +++ b/Helper/Data.php @@ -1146,9 +1146,9 @@ public function initializeAdyenClient( $client->setRegion($checkoutFrontendRegion); } - $moduleVersion = $this->getModuleVersion(); - $client->setMerchantApplication($this->getModuleName(), $moduleVersion); - $client->setExternalPlatform($this->productMetadata->getName(), $this->productMetadata->getVersion(), 'Adyen'); + $client->setMerchantApplication($this->getModuleName(), $this->getModuleVersion()); + $platformData = $this->getMagentoDetails(); + $client->setExternalPlatform($platformData['name'], $platformData['version'], 'Adyen'); if ($isDemo) { $client->setEnvironment(Environment::TEST); } else { @@ -1158,6 +1158,27 @@ public function initializeAdyenClient( return $client; } + public function getMagentoDetails() + { + return [ + 'name' => $this->productMetadata->getName(), + 'version' => $this->productMetadata->getVersion(), + 'edition' => $this->productMetadata->getEdition(), + ]; + } + + public function buildRequestHeaders() + { + $magentoDetails = $this->getMagentoDetails(); + return [ + 'external-platform-name' => $this->getModuleName(), + 'external-platform-version' => $this->getModuleVersion(), + 'merchant-application-name' => $magentoDetails['name'], + 'merchant-application-version' => $magentoDetails['version'], + 'merchant-application-edition' => $magentoDetails['edition'], + ]; + } + /** * @throws AdyenException * @throws NoSuchEntityException diff --git a/Helper/PaymentMethods.php b/Helper/PaymentMethods.php index b7ce299ea..a55fa2a6c 100644 --- a/Helper/PaymentMethods.php +++ b/Helper/PaymentMethods.php @@ -20,6 +20,7 @@ use Magento\Framework\App\Helper\AbstractHelper; use Magento\Framework\App\Helper\Context; use Magento\Framework\App\RequestInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\View\Asset\Repository; @@ -29,7 +30,6 @@ use Magento\Payment\Helper\Data as MagentoDataHelper; use Magento\Payment\Model\MethodInterface; use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\PaymentMethodInterface; use Magento\Sales\Model\Order; use Adyen\Payment\Helper\Data as AdyenDataHelper; use Magento\Store\Model\ScopeInterface; @@ -266,9 +266,10 @@ protected function setQuote(\Magento\Quote\Model\Quote $quote): void $this->quote = $quote; } - protected function getCurrentShopperReference(): ?int + protected function getCurrentShopperReference(): ?string { - return $this->getQuote()->getCustomerId(); + $customerId = $this->getQuote()->getCustomerId(); + return $customerId ? (string)$customerId : null; } protected function getPaymentMethodsRequest( @@ -380,21 +381,6 @@ protected function addExtraConfigurationToPaymentMethods( return $paymentMethodsExtraDetails; } - public function isWalletTxVariant(string $notificationPaymentMethod): bool - { - $walletPaymentMethods = [ - 'googlepay', - 'paywithgoogle', - 'wechatpayWeb', - 'amazonpay', - 'applepay', - 'wechatpayQR', - 'alipay', - 'alipay_hk' - ]; - return in_array($notificationPaymentMethod, $walletPaymentMethods); - } - public function isWalletPaymentMethod(MethodInterface $paymentMethodInstance): bool { return boolval($paymentMethodInstance->getConfigData('is_wallet')); @@ -627,12 +613,18 @@ public function isAutoCapture(Order $order, string $notificationPaymentMethod): public function compareOrderAndWebhookPaymentMethods(Order $order, Notification $notification): bool { - // For cards, it can be 'VI', 'MI',... For alternatives, it can be 'ideal', 'directEbanking',... - $orderPaymentMethod = $order->getPayment()->getCcType(); + $paymentMethodInstance = $order->getPayment()->getMethodInstance(); + + if ($this->isAlternativePaymentMethod($paymentMethodInstance)) { + $orderPaymentMethod = $this->getAlternativePaymentMethodTxVariant($paymentMethodInstance); + } else { + $orderPaymentMethod = $order->getPayment()->getCcType(); + } + $notificationPaymentMethod = $notification->getPaymentMethod(); // Returns if the payment method is wallet like wechatpayWeb, amazonpay, applepay, paywithgoogle - $isWalletPaymentMethod = $this->isWalletTxVariant($orderPaymentMethod); + $isWalletPaymentMethod = $this->isWalletPaymentMethod($paymentMethodInstance); $isCardPaymentMethod = $order->getPayment()->getMethod() === 'adyen_cc' || $order->getPayment()->getMethod() === 'adyen_oneclick'; // If it is a wallet method OR a card OR the methods match exactly, return true diff --git a/Helper/PaymentsDetails.php b/Helper/PaymentsDetails.php index 164d74124..117d77298 100644 --- a/Helper/PaymentsDetails.php +++ b/Helper/PaymentsDetails.php @@ -65,6 +65,7 @@ public function initiatePaymentDetails(OrderInterface $order, string $payload): $service = $this->adyenHelper->createAdyenCheckoutService($client); $requestOptions['idempotencyKey'] = $this->idempotencyHelper->generateIdempotencyKey($apiPayload); + $requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders(); $paymentDetails = $service->paymentsDetails($apiPayload, $requestOptions); } catch (AdyenException $e) { diff --git a/Model/Api/PaymentRequest.php b/Model/Api/PaymentRequest.php index 8abecb4fd..63c27f2df 100755 --- a/Model/Api/PaymentRequest.php +++ b/Model/Api/PaymentRequest.php @@ -12,7 +12,7 @@ namespace Adyen\Payment\Model\Api; use Adyen\AdyenException; -use Adyen\Config; +use Adyen\Payment\Helper\Config; use Adyen\Payment\Helper\Data; use Adyen\Payment\Logger\AdyenLogger; use Adyen\Payment\Model\RecurringType; @@ -101,8 +101,8 @@ public function authorise3d(Payment $payment): mixed ]; try { - $client = $this->configHelper->initializeAdyenClient($storeId); - $service = $this->configHelper->createAdyenCheckoutService($client); + $client = $this->adyenHelper->initializeAdyenClient($storeId); + $service = $this->adyenHelper->createAdyenCheckoutService($client); $result = $service->paymentsDetails($request); } catch (AdyenException $e) { throw new LocalizedException(__('3D secure failed')); @@ -173,13 +173,13 @@ public function listRecurringContractByType(string $shopperReference, int $store $contract = ['contract' => $recurringType]; $request = [ "merchantAccount" => $this->configHelper->getAdyenAbstractConfigData('merchant_account', $storeId), - "shopperReference" => $this->configHelper->padShopperReference($shopperReference), + "shopperReference" => $this->adyenHelper->padShopperReference($shopperReference), "recurring" => $contract, ]; // call lib - $client = $this->configHelper->initializeAdyenClient($storeId); - $service = $this->configHelper->createAdyenRecurringService($client); + $client = $this->adyenHelper->initializeAdyenClient($storeId); + $service = $this->adyenHelper->createAdyenRecurringService($client); $result = $service->listRecurringDetails($request); return $result; @@ -200,7 +200,7 @@ public function disableRecurringContract( int $storeId ): bool { $merchantAccount = $this->configHelper->getAdyenAbstractConfigData("merchant_account", $storeId); - $shopperReference = $this->configHelper->padShopperReference($shopperReference); + $shopperReference = $this->adyenHelper->padShopperReference($shopperReference); $request = [ "merchantAccount" => $merchantAccount, "shopperReference" => $shopperReference, @@ -208,8 +208,8 @@ public function disableRecurringContract( ]; // call lib - $client = $this->configHelper->initializeAdyenClient($storeId); - $service = $this->configHelper->createAdyenRecurringService($client); + $client = $this->adyenHelper->initializeAdyenClient($storeId); + $service = $this->adyenHelper->createAdyenRecurringService($client); try { $result = $service->disable($request); diff --git a/Model/Config/Backend/WebhookCredentials.php b/Model/Config/Backend/WebhookCredentials.php index 5f4f8853c..77f0d9e95 100644 --- a/Model/Config/Backend/WebhookCredentials.php +++ b/Model/Config/Backend/WebhookCredentials.php @@ -62,7 +62,7 @@ public function beforeSave() $username = $this->getValue(); $password = $this->getFieldsetDataValue('notification_password'); - $webhookUrl = $this->url->getBaseUrl() . 'adyen/process/json'; + $webhookUrl = $this->url->getBaseUrl() . 'adyen/webhook'; $isDemoMode = (int)$this->getFieldsetDataValue('demo_mode'); $environment = $isDemoMode ? 'test' : 'live'; diff --git a/Test/Unit/Block/Checkout/Multishipping/SuccessTest.php b/Test/Unit/Block/Checkout/Multishipping/SuccessTest.php new file mode 100644 index 000000000..5c7ac20dd --- /dev/null +++ b/Test/Unit/Block/Checkout/Multishipping/SuccessTest.php @@ -0,0 +1,73 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Block\Checkout\Multishipping; + +use Adyen\Payment\Block\Checkout\Multishipping\Success; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Framework\Session\SessionManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\Data\OrderSearchResultInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + +class SuccessTest extends AbstractAdyenTestCase +{ + private $successBlock; + private $contextMock; + private $sessionMock; + private $orderRepositoryMock; + private $searchCriteriaBuilderMock; + private $orderSearchResultMock; + + protected function setUp(): void + { + $this->sessionMock = $this->createGeneratedMock(SessionManager::class, [ + 'getOrderIds' + ]); + $this->sessionMock->method('getOrderIds')->willReturn(['1' => 1, '2' => 2]); + + $this->contextMock = $this->createMock(Context::class); + $this->contextMock->method('getSession')->willReturn($this->sessionMock); + + $this->orderSearchResultMock = $this->createMock(OrderSearchResultInterface::class); + $this->orderSearchResultMock->method('getItems')->willReturn([]); + + $this->orderRepositoryMock = $this->createMock(OrderRepositoryInterface::class); + $this->orderRepositoryMock->method('getList')->willReturn($this->orderSearchResultMock); + $this->searchCriteriaBuilderMock = $this->createMock(SearchCriteriaBuilder::class); + $this->searchCriteriaBuilderMock->method('addFilter')->willReturnSelf(); + $this->searchCriteriaBuilderMock->method('create')->willReturn( + $this->createMock(SearchCriteriaInterface::class) + ); + + $payments = [ + ['result_code' => 'Authorised'], + ['result_code' => 'IdentifyShopper', 'action' => 'DUMMY_ACTION_OBJECT'] + ]; + + $objectManager = new ObjectManager($this); + $this->successBlock = $objectManager->getObject(Success::class, [ + 'paymentResponseEntities' => $payments, + 'context' => $this->contextMock, + 'orderRepository' => $this->orderRepositoryMock, + 'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock + ]); + } + + public function testRenderAction() + { + $result = $this->successBlock->renderAction(); + $this->assertTrue($result); + } +} diff --git a/Test/Unit/Gateway/Http/Client/TransactionPaymentTest.php b/Test/Unit/Gateway/Http/Client/TransactionPaymentTest.php new file mode 100644 index 000000000..dd39158b3 --- /dev/null +++ b/Test/Unit/Gateway/Http/Client/TransactionPaymentTest.php @@ -0,0 +1,165 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Gateway\Http\Client; + +use Adyen\Payment\Api\Data\PaymentResponseInterface; +use Adyen\Payment\Model\PaymentResponse; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Adyen\Service\Checkout; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Adyen\Payment\Helper\Data; +use Adyen\Payment\Model\PaymentResponseFactory; +use Adyen\Payment\Model\ResourceModel\PaymentResponse as PaymentResponseResourceModel; +use Adyen\Payment\Helper\Idempotency; +use Adyen\Payment\Helper\OrdersApi; +use Adyen\Payment\Gateway\Http\Client\TransactionPayment; +use Magento\Store\Model\StoreManagerInterface; +use Adyen\Payment\Helper\GiftcardPayment; +use Magento\Payment\Gateway\Http\TransferInterface; + +class TransactionPaymentTest extends AbstractAdyenTestCase +{ + private $adyenHelperMock; + private $paymentResponseFactoryMock; + private $paymentResponseResourceModelMock; + private $idempotencyHelperMock; + private $orderApiHelperMock; + private $storeManagerMock; + private $giftcardPaymentHelperMock; + private $transactionPayment; + + protected function setUp(): void + { + $objectManager = new ObjectManager($this); + + $this->adyenHelperMock = $this->createMock(Data::class); + $this->paymentResponseResourceModelMock = $this->createMock(PaymentResponseResourceModel::class); + $this->idempotencyHelperMock = $this->createMock(Idempotency::class); + $this->orderApiHelperMock = $this->createMock(OrdersApi::class); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->giftcardPaymentHelperMock = $this->createMock(GiftcardPayment::class); + $paymentResponseInterfaceMock = $this->createMock(PaymentResponseInterface::class); + $paymentResponseMock = $this->createMock(PaymentResponse::class); + $paymentResponseMock->method('setResponse')->willReturn($paymentResponseInterfaceMock); + $paymentResponseMock->method('setResultCode')->willReturn($paymentResponseInterfaceMock); + $paymentResponseMock->method('setMerchantReference')->willReturn($paymentResponseInterfaceMock); + $this->paymentResponseFactoryMock = $this->createGeneratedMock(PaymentResponseFactory::class, ['create']); + $this->paymentResponseFactoryMock->method('create')->willReturn($paymentResponseMock); + + $this->transactionPayment = $objectManager->getObject( + TransactionPayment::class, + [ + 'adyenHelper' => $this->adyenHelperMock, + 'paymentResponseFactory' => $this->paymentResponseFactoryMock, + 'paymentResponseResourceModel' => $this->paymentResponseResourceModelMock, + 'idempotencyHelper' => $this->idempotencyHelperMock, + 'orderApiHelper' => $this->orderApiHelperMock, + 'storeManager' => $this->storeManagerMock, + 'giftcardPaymentHelper' => $this->giftcardPaymentHelperMock, + ] + ); + } + + public function testPlaceRequestWithResultCode() + { + $transferObjectMock = $this->createMock(TransferInterface::class); + $requestBody = ['resultCode' => 'Authorised', 'amount' => ['value' => 1000]]; + $transferObjectMock->method('getBody')->willReturn($requestBody); + + $result = $this->transactionPayment->placeRequest($transferObjectMock); + + $this->assertEquals($requestBody, $result); + } + + public function testPlaceRequestGeneratesIdempotencyKey() + { + $requestBody = ['reference' => 'ABC12345', 'amount' => ['value' => 100]]; + $transferObjectMock = $this->createConfiguredMock(TransferInterface::class, [ + 'getBody' => $requestBody, + 'getHeaders' => ['idempotencyExtraData' => ['someData']], + 'getClientConfig' => [] + ]); + + $expectedIdempotencyKey = 'generated_idempotency_key'; + $this->idempotencyHelperMock->expects($this->once()) + ->method('generateIdempotencyKey') + ->with( + $this->equalTo(['reference' => 'ABC12345', 'amount' => ['value' => 100]]), + $this->equalTo(['someData']) + ) + ->willReturn($expectedIdempotencyKey); + + $mockedPaymentResponse = [ + 'reference' => 'ABC12345', + 'amount' => ['value' => 100], + 'resultCode' => 'Authorised' + ]; + $serviceMock = $this->createMock(Checkout::class); + $serviceMock->expects($this->once()) + ->method('payments') + ->with( + $this->anything(), + $this->callback(function ($requestOptions) use ($expectedIdempotencyKey) { + return isset($requestOptions['idempotencyKey']) && + $requestOptions['idempotencyKey'] === $expectedIdempotencyKey; + }) + ) + ->willReturn($mockedPaymentResponse); + + $this->adyenHelperMock->method('createAdyenCheckoutService')->willReturn($serviceMock); + + $response = $this->transactionPayment->placeRequest($transferObjectMock); + + $this->assertArrayHasKey('resultCode', $response); + $this->assertEquals('Authorised', $response['resultCode']); + } + + public function testRequestHeadersAreAddedToPaymentsCall() + { + $requestBody = ['reference' => 'ABC12345', 'amount' => ['value' => 1000]]; + $expectedHeaders = ['header1' => 'value1', 'header2' => 'value2']; + + $transferObjectMock = $this->createConfiguredMock(TransferInterface::class, [ + 'getBody' => ['reference' => 'ABC12345', 'amount' => ['value' => 1000]], + 'getHeaders' => ['header1' => 'value1', 'header2' => 'value2'], + 'getClientConfig' => [] + ]); + + $this->adyenHelperMock->expects($this->once()) + ->method('buildRequestHeaders') + ->willReturn($expectedHeaders); + + $mockedPaymentResponse = [ + 'reference' => 'ABC12345', + 'amount' => ['value' => 100], + 'resultCode' => 'Authorised' + ]; + + $serviceMock = $this->createMock(Checkout::class); + $serviceMock->expects($this->once()) + ->method('payments') + ->with( + $this->equalTo($requestBody), + $this->callback(function ($requestOptions) use ($expectedHeaders) { + return isset($requestOptions['headers']) && $requestOptions['headers'] === $expectedHeaders; + }) + ) + ->willReturn($mockedPaymentResponse); + + $this->adyenHelperMock->method('createAdyenCheckoutService')->willReturn($serviceMock); + + $response = $this->transactionPayment->placeRequest($transferObjectMock); + + $this->assertArrayHasKey('resultCode', $response); + $this->assertEquals('Authorised', $response['resultCode']); + } +} diff --git a/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php b/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php new file mode 100644 index 000000000..d5499766c --- /dev/null +++ b/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php @@ -0,0 +1,98 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Gateway\Http\Client; + +use Adyen\Client; +use Adyen\Payment\Gateway\Http\Client\TransactionRefund; +use Adyen\Payment\Helper\Data; +use Adyen\Payment\Helper\Idempotency; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Adyen\Service\Checkout; +use Magento\Payment\Gateway\Http\TransferInterface; +use PHPUnit\Framework\MockObject\MockObject; + +class TransactionRefundTest extends AbstractAdyenTestCase +{ + /** + * @var Data|MockObject + */ + private $adyenHelperMock; + + /** + * @var Idempotency|MockObject + */ + private $idempotencyHelperMock; + + /** + * @var TransactionRefund + */ + private $transactionRefund; + + protected function setUp(): void + { + $this->adyenHelperMock = $this->createMock(Data::class); + $this->idempotencyHelperMock = $this->createMock(Idempotency::class); + + $this->transactionRefund = new TransactionRefund( + $this->adyenHelperMock, + $this->idempotencyHelperMock + ); + } + + public function testPlaceRequestIncludesHeadersInRequest() + { + $requestBody = [ + 'amount' => ['value' => 1000, 'currency' => 'EUR'], + 'paymentPspReference' => '123456789' + ]; + + $headers = ['idempotencyExtraData' => ['order_id' => '1001']]; + + $transferObjectMock = $this->createConfiguredMock(TransferInterface::class, [ + 'getBody' => [$requestBody], + 'getHeaders' => $headers, + 'getClientConfig' => [] + ]); + + $checkoutServiceMock = $this->createMock(Checkout::class); + $adyenClientMock = $this->createMock(Client::class); + + $this->adyenHelperMock->method('initializeAdyenClientWithClientConfig')->willReturn($adyenClientMock); + $this->adyenHelperMock->method('createAdyenCheckoutService')->willReturn($checkoutServiceMock); + $this->adyenHelperMock->method('buildRequestHeaders')->willReturn(['custom-header' => 'value']); + + $this->idempotencyHelperMock->expects($this->once()) + ->method('generateIdempotencyKey') + ->with($requestBody, $headers['idempotencyExtraData']) + ->willReturn('generated_idempotency_key'); + + $checkoutServiceMock->expects($this->once()) + ->method('refunds') + ->with( + $this->equalTo($requestBody), + $this->callback(function ($requestOptions) { + $this->assertArrayHasKey('idempotencyKey', $requestOptions); + $this->assertArrayHasKey('headers', $requestOptions); + $this->assertEquals('generated_idempotency_key', $requestOptions['idempotencyKey']); + $this->assertArrayHasKey('custom-header', $requestOptions['headers']); + return true; + }) + ) + ->willReturn(['pspReference' => 'refund_psp_reference']); + + $responses = $this->transactionRefund->placeRequest($transferObjectMock); + + $this->assertIsArray($responses); + $this->assertCount(1, $responses); + $this->assertArrayHasKey('pspReference', $responses[0]); + } +} diff --git a/Test/Unit/Gateway/Request/AddressDataBuilderTest.php b/Test/Unit/Gateway/Request/AddressDataBuilderTest.php new file mode 100644 index 000000000..2989208ed --- /dev/null +++ b/Test/Unit/Gateway/Request/AddressDataBuilderTest.php @@ -0,0 +1,134 @@ +adyenRequestsHelperMock = $this->createMock(Requests::class); + $this->paymentDataObjectMock = $this->createMock(PaymentDataObjectInterface::class); + $this->orderAdapterMock = $this->createMock(OrderAdapterInterface::class); + $this->addressAdapterMock = $this->createMock(AddressAdapterInterface::class); + + $this->addressDataBuilder = new AddressDataBuilder( + $this->adyenRequestsHelperMock + ); + } + + public function testBuildWithValidAddress() + { + $expectedResult = [ + 'body' => [ + 'billingAddress' => [ + 'street' => '123 Main Street', + 'postalCode' => '12345', + 'city' => 'Anytown', + 'country' => 'US', + 'stateOrProvince' => 'NY', + 'houseNumberOrName' => '123', + ] + ] + ]; + + $this->paymentDataObjectMock->expects($this->once()) + ->method('getOrder') + ->willReturn($this->orderAdapterMock); + + $this->orderAdapterMock->expects($this->once()) + ->method('getBillingAddress') + ->willReturn($this->addressAdapterMock); + + $this->orderAdapterMock->expects($this->once()) + ->method('getShippingAddress') + ->willReturn($this->addressAdapterMock); + + $this->orderAdapterMock->expects($this->once()) + ->method('getStoreId') + ->willReturn('store_id'); + + $this->adyenRequestsHelperMock->expects($this->once()) + ->method('buildAddressData') + ->with( + $this->addressAdapterMock, + $this->addressAdapterMock, + 'store_id', + [] + ) + ->willReturn($expectedResult['body']); + + $result = $this->addressDataBuilder->build(['payment' => $this->paymentDataObjectMock]); + + $this->assertEquals($expectedResult, $result); + } + + public function testBuildWithMissingAddress() + { + $expectedResult = [ + 'body' => [] + ]; + + $this->paymentDataObjectMock->expects($this->once()) + ->method('getOrder') + ->willReturn($this->orderAdapterMock); + + $this->orderAdapterMock->expects($this->once()) + ->method('getBillingAddress') + ->willReturn(null); + + $this->orderAdapterMock->expects($this->once()) + ->method('getShippingAddress') + ->willReturn(null); + + $this->orderAdapterMock->expects($this->once()) + ->method('getStoreId') + ->willReturn('store_id'); + + $this->adyenRequestsHelperMock->expects($this->once()) + ->method('buildAddressData') + ->with( + null, + null, + 'store_id', + [] + ) + ->willReturn($expectedResult['body']); + + $result = $this->addressDataBuilder->build(['payment' => $this->paymentDataObjectMock]); + + $this->assertEquals($expectedResult, $result); + } +} diff --git a/Test/Unit/Gateway/Request/GiftcardDataBuilderTest.php b/Test/Unit/Gateway/Request/GiftcardDataBuilderTest.php new file mode 100644 index 000000000..7daa7c7a7 --- /dev/null +++ b/Test/Unit/Gateway/Request/GiftcardDataBuilderTest.php @@ -0,0 +1,61 @@ + + */ + +namespace Adyen\Payment\Test\Gateway\Request; + +use Adyen\Payment\Gateway\Request\GiftcardDataBuilder; +use Adyen\Payment\Model\ResourceModel\StateData\Collection; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Payment\Gateway\Data\PaymentDataObject; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Payment; + +class GiftcardDataBuilderTest extends AbstractAdyenTestCase +{ + private $giftcardDataBuilder; + private $adyenStateDataCollectionMock; + + protected function setUp(): void + { + $this->adyenStateDataCollectionMock = $this->createMock(Collection::class); + + $objectManager = new ObjectManager($this); + $this->giftcardDataBuilder = $objectManager->getObject(GiftcardDataBuilder::class, [ + 'adyenStateData' => $this->adyenStateDataCollectionMock + ]); + } + + public function testBuild() + { + $orderMock = $this->createMock(Order::class); + $orderMock->method('getQuoteId')->willReturn(1); + + $paymentMock = $this->createMock(Payment::class); + $paymentMock->method('getOrder')->willReturn($orderMock); + + $buildSubject = [ + 'payment' => $this->createConfiguredMock(PaymentDataObject::class, [ + 'getPayment' => $paymentMock + ]) + ]; + + $stateDataMock = [ + ['entity_id' => 1, 'state_data' => '{"paymentMethod":{"type":"giftcard","brand":"genericgiftcard"}}'] + ]; + + $this->adyenStateDataCollectionMock->method('getStateDataRowsWithQuoteId')->willReturnSelf(); + $this->adyenStateDataCollectionMock->method('getData')->willReturn($stateDataMock); + + $request = $this->giftcardDataBuilder->build($buildSubject); + $this->assertArrayHasKey('giftcardRequestParameters', $request['body']); + } +} diff --git a/Test/Unit/Helper/DataTest.php b/Test/Unit/Helper/DataTest.php index 2f93c0f0d..91f49e193 100755 --- a/Test/Unit/Helper/DataTest.php +++ b/Test/Unit/Helper/DataTest.php @@ -64,7 +64,8 @@ public function setUp(): void $backendHelper = $this->createMock(BackendHelper::class); $productMetadata = $this->createConfiguredMock(ProductMetadata::class, [ 'getName' => 'magento', - 'getVersion' => '2.x.x' + 'getVersion' => '2.x.x', + 'getEdition' => 'Community' ]); $adyenLogger = $this->createMock(AdyenLogger::class); $storeManager = $this->createMock(StoreManager::class); @@ -190,4 +191,30 @@ public function testInitializeAdyenClientWithClientConfig($clientConfig) $this->dataHelper->initializeAdyenClientWithClientConfig($clientConfig) ); } + + public function testGetMagentoDetails() + { + $expectedDetails = [ + 'name' => 'magento', + 'version' => '2.x.x', + 'edition' => 'Community' + ]; + + $actualDetails = $this->dataHelper->getMagentoDetails(); + $this->assertEquals($expectedDetails, $actualDetails); + } + + public function testBuildRequestHeaders() + { + $expectedHeaders = [ + 'external-platform-name' => 'adyen-magento2', + 'external-platform-version' => '1.2.3', + 'merchant-application-name' => 'magento', + 'merchant-application-version' => '2.x.x', + 'merchant-application-edition' => 'Community' + ]; + + $headers = $this->dataHelper->buildRequestHeaders(); + $this->assertEquals($expectedHeaders, $headers); + } } diff --git a/Test/Unit/Helper/PaymentDetailsTest.php b/Test/Unit/Helper/PaymentDetailsTest.php new file mode 100644 index 000000000..eef777514 --- /dev/null +++ b/Test/Unit/Helper/PaymentDetailsTest.php @@ -0,0 +1,98 @@ + + */ +namespace Adyen\Payment\Test\Unit\Helper; + +use Adyen\Payment\Helper\PaymentsDetails; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Payment; +use Adyen\Payment\Helper\Data; +use Adyen\Payment\Logger\AdyenLogger; +use Adyen\Payment\Helper\PaymentResponseHandler; +use Adyen\Payment\Helper\Idempotency; +use Magento\Checkout\Model\Session; +use Adyen\Service\Checkout; +use Adyen\Client; + +class PaymentDetailsTest extends AbstractAdyenTestCase +{ + private $checkoutSessionMock; + private $adyenHelperMock; + private $adyenLoggerMock; + private $paymentResponseHandlerMock; + private $idempotencyHelperMock; + private $paymentDetails; + + protected function setUp(): void + { + $this->checkoutSessionMock = $this->createMock(Session::class); + $this->adyenHelperMock = $this->createMock(Data::class); + $this->adyenLoggerMock = $this->createMock(AdyenLogger::class); + $this->paymentResponseHandlerMock = $this->createMock(PaymentResponseHandler::class); + $this->idempotencyHelperMock = $this->createMock(Idempotency::class); + + $this->paymentDetails = new PaymentsDetails( + $this->checkoutSessionMock, + $this->adyenHelperMock, + $this->adyenLoggerMock, + $this->paymentResponseHandlerMock, + $this->idempotencyHelperMock + ); + } + + public function testRequestHeadersAreAddedToRequest() + { + $orderMock = $this->createMock(OrderInterface::class); + $paymentMock = $this->createMock(Payment::class); + $checkoutServiceMock = $this->createMock(Checkout::class); + $adyenClientMock = $this->createMock(Client::class); + $storeId = 1; + $payload = json_encode([ + 'details' => 'some_details', + 'paymentData' => 'some_payment_data', + 'threeDSAuthenticationOnly' => true + ]); + $requestOptions = [ + 'idempotencyKey' => 'some_idempotency_key', + 'headers' => ['headerKey' => 'headerValue'] + ]; + $paymentDetailsResult = ['resultCode' => 'Authorised', 'action' => null, 'additionalData' => null]; + + $orderMock->method('getPayment')->willReturn($paymentMock); + $orderMock->method('getStoreId')->willReturn($storeId); + $paymentMock->method('getOrder')->willReturn($orderMock); + + $this->adyenHelperMock->method('initializeAdyenClient')->willReturn($adyenClientMock); + $this->adyenHelperMock->method('createAdyenCheckoutService')->willReturn($checkoutServiceMock); + $this->adyenHelperMock->method('buildRequestHeaders')->willReturn($requestOptions['headers']); + $this->idempotencyHelperMock->method('generateIdempotencyKey')->willReturn($requestOptions['idempotencyKey']); + + $checkoutServiceMock->expects($this->once()) + ->method('paymentsDetails') + ->with( + $this->equalTo([ + 'details' => 'some_details', + 'paymentData' => 'some_payment_data', + 'threeDSAuthenticationOnly' => true + ]), + $this->equalTo($requestOptions) + ) + ->willReturn($paymentDetailsResult); + + $this->paymentResponseHandlerMock->method('handlePaymentResponse')->willReturn(true); + $this->paymentResponseHandlerMock->method('formatPaymentResponse')->willReturn($paymentDetailsResult); + + $result = $this->paymentDetails->initiatePaymentDetails($orderMock, $payload); + + $this->assertJson($result); + $this->assertEquals(json_encode($paymentDetailsResult), $result); + } +} diff --git a/Test/Unit/Helper/PaymentMethodsTest.php b/Test/Unit/Helper/PaymentMethodsTest.php new file mode 100644 index 000000000..18f05a5c3 --- /dev/null +++ b/Test/Unit/Helper/PaymentMethodsTest.php @@ -0,0 +1,86 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Helper; + +use Adyen\Payment\Helper\PaymentMethods; +use Adyen\Payment\Model\Notification; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Payment\Model\MethodInterface; +use Magento\Sales\Model\Order; + +class PaymentMethodsTest extends AbstractAdyenTestCase +{ + private object $paymentMethodsHelper; + + protected function setUp(): void + { + $objectManager = new ObjectManager($this); + $this->paymentMethodsHelper = $objectManager->getObject(PaymentMethods::class, []); + } + + /** + * @dataProvider comparePaymentMethodProvider + */ + public function testCompareOrderAndWebhookPaymentMethods( + $orderPaymentMethod, + $notificationPaymentMethod, + $assert, + $ccType = null + ) { + $methodMock = $this->createMock(MethodInterface::class); + $methodMock->method('getConfigData') + ->willReturnMap([ + ['group', null, PaymentMethods::ADYEN_GROUP_ALTERNATIVE_PAYMENT_METHODS], + ['is_wallet', null, '0'] + ]); + $methodMock->method('getCode')->willReturn($orderPaymentMethod); + + $paymentMock = $this->createMock(Order\Payment::class); + $paymentMock->method('getMethodInstance')->willReturn($methodMock); + $paymentMock->method('getMethod')->willReturn($orderPaymentMethod); + $paymentMock->method('getCcType')->willReturn($ccType); + + $orderMock = $this->createMock(Order::class); + $orderMock->method('getPayment')->willReturn($paymentMock); + + $notificationMock = $this->createMock(Notification::class); + $notificationMock->method('getPaymentMethod')->willReturn($notificationPaymentMethod); + + $this->assertEquals( + $assert, + $this->paymentMethodsHelper->compareOrderAndWebhookPaymentMethods($orderMock, $notificationMock) + ); + } + + public static function comparePaymentMethodProvider(): array + { + return [ + [ + 'orderPaymentMethod' => 'adyen_klarna', + 'notificationPaymentMethod' => 'klarna', + 'assert' => true + ], + [ + 'orderPaymentMethod' => 'adyen_cc', + 'notificationPaymentMethod' => 'visa', + 'assert' => true, + 'ccType' => 'visa' + ], + [ + 'orderPaymentMethod' => 'adyen_klarna', + 'notificationPaymentMethod' => 'boleto', + 'assert' => false + ] + ]; + } +} diff --git a/Test/Unit/Helper/PaymentRequestTest.php b/Test/Unit/Helper/PaymentRequestTest.php new file mode 100644 index 000000000..dbc965fdc --- /dev/null +++ b/Test/Unit/Helper/PaymentRequestTest.php @@ -0,0 +1,125 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Helper; + +use Adyen\Client; +use Adyen\Payment\Helper\Config; +use Adyen\Payment\Helper\Data; +use Adyen\Payment\Model\Api\PaymentRequest; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Adyen\Service\Checkout; +use Adyen\Service\Recurring; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Payment; + +class PaymentRequestTest extends AbstractAdyenTestCase +{ + private $paymentRequest; + private $configHelper; + private $adyenHelper; + + protected function setUp(): void + { + $this->configHelper = $this->createMock(Config::class); + $this->configHelper + ->method('getAdyenAbstractConfigData') + ->willReturn('MERCHANT_ACCOUNT_PLACEHOLDER'); + + $this->adyenHelper = $this->createMock(Data::class); + $this->adyenHelper->method('padShopperReference')->willReturn('001'); + + + $objectManager = new ObjectManager($this); + $this->paymentRequest = $objectManager->getObject(PaymentRequest::class, [ + 'configHelper' => $this->configHelper, + 'adyenHelper' => $this->adyenHelper + ]); + } + + public function testAuthorise3d() + { + $orderMock = $this->createMock(Order::class); + $orderMock->method('getStoreId')->willReturn(1); + + $paymentMock = $this->createMock(Payment::class); + $paymentMock->method('getOrder')->willReturn($orderMock); + + $checkoutServiceMock = $this->createMock(Checkout::class); + $checkoutServiceMock->method('paymentsDetails')->willReturn([]); + + $this->adyenHelper + ->method('initializeAdyenClient') + ->willReturn($this->createMock(Client::class)); + + $this->adyenHelper->method('createAdyenCheckoutService')->willReturn($checkoutServiceMock); + + $result = $this->paymentRequest->authorise3d($paymentMock); + $this->assertIsArray($result); + } + + public function testListRecurringContractByType() + { + $recurringServiceMock = $this->createMock(Recurring::class); + $recurringServiceMock->method('listRecurringDetails')->willReturn([]); + + $this->adyenHelper + ->method('initializeAdyenClient') + ->willReturn($this->createMock(Client::class)); + $this->adyenHelper->method('createAdyenRecurringService')->willReturn($recurringServiceMock); + + $this->assertIsArray($this->paymentRequest->listRecurringContractByType('001', 1, 'CardOnFile')); + } + + /** + * @dataProvider disableRecurringContractProvider + */ + public function testDisableRecurringContract($response, $assert) + { + if (!$assert) { + $this->expectException(LocalizedException::class); + } + + $result = [ + 'response' => $response + ]; + + $recurringServiceMock = $this->createMock(Recurring::class); + $recurringServiceMock->method('disable')->willReturn($result); + + $this->adyenHelper + ->method('initializeAdyenClient') + ->willReturn($this->createMock(Client::class)); + $this->adyenHelper->method('createAdyenRecurringService')->willReturn($recurringServiceMock); + + $apiResponse = $this->paymentRequest->disableRecurringContract('TOKEN_PLACEHOLDER', '001', 1); + + if ($assert) { + $this->assertTrue($apiResponse); + } + } + + public static function disableRecurringContractProvider(): array + { + return [ + [ + 'response' => '[detail-successfully-disabled]', + 'assert' => true + ], + [ + 'response' => '[failed]', + 'assert' => false + ] + ]; + } +} diff --git a/composer.json b/composer.json index b83d9e45f..92eb4a037 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "adyen/module-payment", "description": "Official Magento2 Plugin to connect to Payment Service Provider Adyen.", "type": "magento2-module", - "version": "9.0.6", + "version": "9.0.7", "license": "MIT", "repositories": [ { @@ -12,7 +12,7 @@ ], "require": { "php": ">=8.1", - "adyen/php-api-library": "^15.4.0", + "adyen/php-api-library": "^17.0.0", "adyen/php-webhook-module": "^0.8.0", "magento/framework": ">=103.0.4", "magento/module-vault": ">=101.2.4", @@ -41,12 +41,6 @@ "test": [ "Composer\\Config::disableProcessTimeout", "vendor/bin/phpunit -c Test/phpunit.xml" - ], - "post-install-cmd": [ - "([ $COMPOSER_DEV_MODE -eq 0 ] || vendor/bin/phpcs --config-set installed_paths ../../magento/magento-coding-standard/,../../phpcompatibility/php-compatibility)" - ], - "post-update-cmd": [ - "([ $COMPOSER_DEV_MODE -eq 0 ] || vendor/bin/phpcs --config-set installed_paths ../../magento/magento-coding-standard/,../../phpcompatibility/php-compatibility)" ] } } diff --git a/etc/module.xml b/etc/module.xml index a2f258fbc..2ab28f830 100755 --- a/etc/module.xml +++ b/etc/module.xml @@ -12,7 +12,7 @@ --> - + diff --git a/view/adminhtml/templates/config/applepay_button.phtml b/view/adminhtml/templates/config/applepay_button.phtml index 6ba754cd0..e57195c9f 100644 --- a/view/adminhtml/templates/config/applepay_button.phtml +++ b/view/adminhtml/templates/config/applepay_button.phtml @@ -1,3 +1,3 @@ - + diff --git a/view/adminhtml/templates/config/configuration_wizard.phtml b/view/adminhtml/templates/config/configuration_wizard.phtml index 813b1e4c7..999768131 100644 --- a/view/adminhtml/templates/config/configuration_wizard.phtml +++ b/view/adminhtml/templates/config/configuration_wizard.phtml @@ -1,8 +1,8 @@ getNextButtonHtml() ?>