diff --git a/Api/InstantPurchase/PaymentMethodIntegration/AdyenAvailabilityCheckerInterface.php b/Api/InstantPurchase/PaymentMethodIntegration/AdyenAvailabilityCheckerInterface.php new file mode 100644 index 0000000000..ecaefc56d0 --- /dev/null +++ b/Api/InstantPurchase/PaymentMethodIntegration/AdyenAvailabilityCheckerInterface.php @@ -0,0 +1,31 @@ + + */ + +namespace Adyen\Payment\Api\InstantPurchase\PaymentMethodIntegration; + +use Magento\InstantPurchase\PaymentMethodIntegration\AvailabilityCheckerInterface; + +interface AdyenAvailabilityCheckerInterface extends AvailabilityCheckerInterface +{ + /** + * Checks if Adyen alternative payment method may be used for instant purchase. + * + * This interface extends the default `AvailabilityCheckerInterface` and implements + * a new method with payment method argument. This interface is used in `InstantPurchaseIntegrations` + * plugin to override the `AvailabilityCheckerInterface` which doesn't have payment method argument. + * + * @param string $paymentMethodCode + * + * @return bool + */ + public function isAvailableAdyenMethod(string $paymentMethodCode): bool; +} diff --git a/Gateway/Request/RecurringVaultDataBuilder.php b/Gateway/Request/RecurringVaultDataBuilder.php index 4cac64a81b..29ce938967 100644 --- a/Gateway/Request/RecurringVaultDataBuilder.php +++ b/Gateway/Request/RecurringVaultDataBuilder.php @@ -3,7 +3,7 @@ * * Adyen Payment module (https://www.adyen.com/) * - * Copyright (c) 2023 Adyen N.V. (https://www.adyen.com/) + * Copyright (c) 2024 Adyen N.V. (https://www.adyen.com/) * See LICENSE.txt for license details. * * Author: Adyen @@ -15,75 +15,87 @@ use Adyen\Payment\Helper\StateData; use Adyen\Payment\Helper\Vault; use Adyen\Payment\Model\Config\Source\ThreeDSFlow; -use Adyen\Payment\Model\Ui\AdyenCcConfigProvider; +use Magento\Framework\Exception\LocalizedException; use Magento\Payment\Gateway\Data\PaymentDataObject; use Magento\Payment\Gateway\Helper\SubjectReader; use Magento\Payment\Gateway\Request\BuilderInterface; +use Magento\Vault\Api\Data\PaymentTokenFactoryInterface; class RecurringVaultDataBuilder implements BuilderInterface { - private StateData $stateData; - private Vault $vaultHelper; - private Config $configHelper; - + /** + * @param StateData $stateData + * @param Vault $vaultHelper + * @param Config $configHelper + */ public function __construct( - StateData $stateData, - Vault $vaultHelper, - Config $configHelper - ) { - $this->stateData = $stateData; - $this->vaultHelper = $vaultHelper; - $this->configHelper = $configHelper; - } + private readonly StateData $stateData, + private readonly Vault $vaultHelper, + private readonly Config $configHelper + ) { } + /** + * @throws LocalizedException + */ public function build(array $buildSubject): array { /** @var PaymentDataObject $paymentDataObject */ $paymentDataObject = SubjectReader::readPayment($buildSubject); + $payment = $paymentDataObject->getPayment(); $paymentMethod = $payment->getMethodInstance(); + $order = $payment->getOrder(); $extensionAttributes = $payment->getExtensionAttributes(); + $paymentToken = $extensionAttributes->getVaultPaymentToken(); $details = json_decode((string) ($paymentToken->getTokenDetails() ?: '{}'), true); - // Initialize the request body with the current state data - $requestBody = $this->stateData->getStateData($order->getQuoteId()); + if ($paymentToken->getType() === PaymentTokenFactoryInterface::TOKEN_TYPE_CREDIT_CARD) { + // Build base request for card token payments (including card wallets) - // For now this will only be used by tokens created trough adyen_hpp payment methods - if (array_key_exists(Vault::TOKEN_TYPE, $details)) { - $requestBody['recurringProcessingModel'] = $details[Vault::TOKEN_TYPE]; - } else { - // If recurringProcessingModel doesn't exist in the token details, use the default value from config. - $requestBody['recurringProcessingModel'] = $this->vaultHelper->getPaymentMethodRecurringProcessingModel( - $paymentMethod->getProviderCode(), - $order->getStoreId() - ); - } + $isInstantPurchase = (bool) $payment->getAdditionalInformation('instant-purchase'); + + if ($isInstantPurchase) { + // `Instant Purchase` doesn't have the component and state data. Build the `paymentMethod` object. + $requestBody['paymentMethod']['type'] = 'scheme'; + $requestBody['paymentMethod']['storedPaymentMethodId'] = $paymentToken->getGatewayToken(); + } else { + // Initialize the request body with the current state data if it's not `Instant Purchase`. + $requestBody = $this->stateData->getStateData($order->getQuoteId()); + } - /* - * allow3DS flag is required to trigger the native 3DS challenge. - * Otherwise, shopper will be redirected to the issuer for challenge. - * Due to new VISA compliance requirements, holderName is added to the payments call - */ - if ($paymentMethod->getCode() === AdyenCcConfigProvider::CC_VAULT_CODE) { + /* + * `allow3DS: true` flag is required to trigger the native 3DS challenge. + * Otherwise, shopper will be redirected to the issuer for challenge. + */ $requestBody['additionalData']['allow3DS2'] = $this->configHelper->getThreeDSFlow($order->getStoreId()) === ThreeDSFlow::THREEDS_NATIVE; + + // Due to new VISA compliance requirements, holderName is added to the payments call $requestBody['paymentMethod']['holderName'] = $details['cardHolderName'] ?? null; - } + } else { + // Build base request for alternative payment methods for regular checkout and Instant Purchase - /** - * Build paymentMethod object for alternative payment methods - */ - if ($paymentMethod->getCode() !== AdyenCcConfigProvider::CC_VAULT_CODE) { $requestBody['paymentMethod'] = [ 'type' => $details['type'], 'storedPaymentMethodId' => $paymentToken->getGatewayToken() ]; } - $request['body'] = $requestBody; + // Check the `stateData` if `recurringProcessingModel` is added through a headless request. + if (array_key_exists(Vault::TOKEN_TYPE, $details)) { + $requestBody['recurringProcessingModel'] = $details[Vault::TOKEN_TYPE]; + } else { + // If recurringProcessingModel doesn't exist in the token details, use the default value from config. + $requestBody['recurringProcessingModel'] = $this->vaultHelper->getPaymentMethodRecurringProcessingModel( + $paymentMethod->getProviderCode(), + $order->getStoreId() + ); + } - return $request; + return [ + 'body' => $requestBody + ]; } } diff --git a/Helper/Config.php b/Helper/Config.php index 8657bfe26a..9bbebe10af 100644 --- a/Helper/Config.php +++ b/Helper/Config.php @@ -592,6 +592,16 @@ public function getThreeDSFlow(int $storeId = null): string ); } + public function getIsCvcRequiredForRecurringCardPayments(int $storeId = null): bool + { + return (bool) $this->getConfigData( + 'require_cvc', + Config::XML_ADYEN_CC_VAULT, + $storeId, + true + ); + } + public function getConfigData(string $field, string $xmlPrefix, ?int $storeId, bool $flag = false): mixed { $path = implode("/", [self::XML_PAYMENT_PREFIX, $xmlPrefix, $field]); diff --git a/Model/InstantPurchase/Card/AvailabilityChecker.php b/Model/InstantPurchase/Card/AvailabilityChecker.php new file mode 100644 index 0000000000..eedc0b0a8d --- /dev/null +++ b/Model/InstantPurchase/Card/AvailabilityChecker.php @@ -0,0 +1,59 @@ + + */ + +namespace Adyen\Payment\Model\InstantPurchase\Card; + +use Adyen\Payment\Helper\Config; +use Adyen\Payment\Helper\Vault; +use Adyen\Payment\Model\Ui\AdyenCcConfigProvider; +use Magento\InstantPurchase\PaymentMethodIntegration\AvailabilityCheckerInterface; +use Magento\Store\Model\StoreManagerInterface; + +class AvailabilityChecker implements AvailabilityCheckerInterface +{ + /** + * @param Config $configHelper + * @param Vault $vaultHelper + * @param StoreManagerInterface $storeManager + */ + public function __construct( + private readonly Config $configHelper, + private readonly Vault $vaultHelper, + private readonly StoreManagerInterface $storeManager + ) { } + + /** + * Instant Purchase is available if card recurring is enabled, recurring processing model is set to `CardOnFile` + * and CVC is not required to complete the payment. + */ + public function isAvailable(): bool + { + $storeId = $this->storeManager->getStore()->getId(); + + $isCardRecurringEnabled = $this->vaultHelper->getPaymentMethodRecurringActive( + AdyenCcConfigProvider::CODE, + $storeId + ); + + $recurringProcessingModel = $this->vaultHelper->getPaymentMethodRecurringProcessingModel( + AdyenCcConfigProvider::CODE, + $storeId + ); + + $isCvcRequiredForCardRecurringPayments = + $this->configHelper->getIsCvcRequiredForRecurringCardPayments($storeId); + + return $isCardRecurringEnabled && + !$isCvcRequiredForCardRecurringPayments && + $recurringProcessingModel === Vault::CARD_ON_FILE; + } +} diff --git a/Model/InstantPurchase/Card/TokenFormatter.php b/Model/InstantPurchase/Card/TokenFormatter.php new file mode 100644 index 0000000000..d90ddd1bfb --- /dev/null +++ b/Model/InstantPurchase/Card/TokenFormatter.php @@ -0,0 +1,56 @@ + + */ + +namespace Adyen\Payment\Model\InstantPurchase\Card; + +use Adyen\Payment\Helper\Data; +use InvalidArgumentException; +use Magento\InstantPurchase\PaymentMethodIntegration\PaymentTokenFormatterInterface; +use Magento\Vault\Api\Data\PaymentTokenInterface; + +/** + * Adyen stored card formatter. + */ +class TokenFormatter implements PaymentTokenFormatterInterface +{ + public function __construct( + protected readonly Data $adyenHelper + ) { } + + public function formatPaymentToken(PaymentTokenInterface $paymentToken): string + { + $details = json_decode($paymentToken->getTokenDetails() ?: '{}', true); + + if (!isset($details['type'], $details['maskedCC'], $details['expirationDate'])) { + throw new InvalidArgumentException('Invalid Adyen card token details.'); + } + + $ccTypes = $this->adyenHelper->getAdyenCcTypes(); + $typeArrayIndex = array_search($details['type'], array_column($ccTypes, 'code_alt')); + + if (is_int($typeArrayIndex)) { + $ccType = $ccTypes[array_keys($ccTypes)[$typeArrayIndex]]['name']; + } else { + $ccType = $details['type']; + } + + return sprintf( + '%s: %s, %s: %s (%s: %s)', + __('Card'), + $ccType, + __('ending'), + $details['maskedCC'], + __('expires'), + $details['expirationDate'] + ); + } +} diff --git a/Model/InstantPurchase/PaymentMethods/AvailabilityChecker.php b/Model/InstantPurchase/PaymentMethods/AvailabilityChecker.php new file mode 100644 index 0000000000..c192159181 --- /dev/null +++ b/Model/InstantPurchase/PaymentMethods/AvailabilityChecker.php @@ -0,0 +1,62 @@ + + */ + +namespace Adyen\Payment\Model\InstantPurchase\PaymentMethods; + +use Adyen\Payment\Api\InstantPurchase\PaymentMethodIntegration\AdyenAvailabilityCheckerInterface; +use Adyen\Payment\Helper\Vault; +use Magento\Framework\Exception\NotFoundException; +use Magento\Store\Model\StoreManagerInterface; + +class AvailabilityChecker implements AdyenAvailabilityCheckerInterface +{ + /** + * @param Vault $vaultHelper + * @param StoreManagerInterface $storeManager + */ + public function __construct( + private readonly Vault $vaultHelper, + private readonly StoreManagerInterface $storeManager + ) { } + + /** + * Instant Purchase is available if payment method recurring is enabled and + * recurring processing model is set to `CardOnFile`. + */ + public function isAvailableAdyenMethod(string $paymentMethodCode): bool + { + $storeId = $this->storeManager->getStore()->getId(); + + $isMethodRecurringEnabled = $this->vaultHelper->getPaymentMethodRecurringActive( + $paymentMethodCode, + $storeId + ); + $recurringProcessingModel = $this->vaultHelper->getPaymentMethodRecurringProcessingModel( + $paymentMethodCode, + $storeId + ); + + return $isMethodRecurringEnabled && $recurringProcessingModel === Vault::CARD_ON_FILE; + } + + /** + * @throws NotFoundException + */ + public function isAvailable(): bool + { + /* + * This is the pseudo implementation of the interface. Actual logic has been written + * in `isAvailableAdyenMethod() and implemented via plugin `InstantPurchaseIntegrationTest`. + */ + throw new NotFoundException(__('This method has not been implemented!')); + } +} diff --git a/Model/InstantPurchase/PaymentMethods/TokenFormatter.php b/Model/InstantPurchase/PaymentMethods/TokenFormatter.php new file mode 100644 index 0000000000..e5f27501f3 --- /dev/null +++ b/Model/InstantPurchase/PaymentMethods/TokenFormatter.php @@ -0,0 +1,28 @@ + + */ + +namespace Adyen\Payment\Model\InstantPurchase\PaymentMethods; + +use Magento\InstantPurchase\PaymentMethodIntegration\PaymentTokenFormatterInterface; +use Magento\Vault\Api\Data\PaymentTokenInterface; + +/** + * Adyen stored payment method formatter. + */ +class TokenFormatter implements PaymentTokenFormatterInterface +{ + public function formatPaymentToken(PaymentTokenInterface $paymentToken): string + { + $details = json_decode($paymentToken->getTokenDetails(), true); + return $details['tokenLabel']; + } +} diff --git a/Model/Ui/AdyenCcConfigProvider.php b/Model/Ui/AdyenCcConfigProvider.php index ece2d81eeb..f8722f0e9a 100755 --- a/Model/Ui/AdyenCcConfigProvider.php +++ b/Model/Ui/AdyenCcConfigProvider.php @@ -127,12 +127,8 @@ public function getConfig(): array } // check if cvc is required - $config['payment']['adyenCc']['requireCvc'] = $this->configHelper->getConfigData( - 'require_cvc', - Config::XML_ADYEN_CC_VAULT, - $storeId, - true - ); + $config['payment']['adyenCc']['requireCvc'] = + $this->configHelper->getIsCvcRequiredForRecurringCardPayments($storeId); return $config; } diff --git a/Plugin/InstantPurchaseIntegration.php b/Plugin/InstantPurchaseIntegration.php new file mode 100644 index 0000000000..62ef42985e --- /dev/null +++ b/Plugin/InstantPurchaseIntegration.php @@ -0,0 +1,65 @@ + + */ + +namespace Adyen\Payment\Plugin; + +use Adyen\Payment\Helper\PaymentMethods; +use Adyen\Payment\Api\InstantPurchase\PaymentMethodIntegration\AdyenAvailabilityCheckerInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\InstantPurchase\PaymentMethodIntegration\Integration; +use Magento\Payment\Helper\Data; + +class InstantPurchaseIntegration +{ + /** + * @param AdyenAvailabilityCheckerInterface $adyenAvailabilityChecker + * @param PaymentMethods $paymentMethodsHelper + * @param Data $paymentDataHelper + */ + public function __construct( + protected readonly AdyenAvailabilityCheckerInterface $adyenAvailabilityChecker, + protected readonly PaymentMethods $paymentMethodsHelper, + protected readonly Data $paymentDataHelper + ) { } + + /** + * @param Integration $subject + * @param callable $proceed + * @return bool + * @throws LocalizedException + */ + public function aroundIsAvailable(Integration $subject, callable $proceed): bool + { + $vaultPaymentMethodInstance = $subject->getPaymentMethod(); + $providerMethodCode = $vaultPaymentMethodInstance->getProviderCode(); + + $providerMethodInstance = $this->paymentDataHelper->getMethodInstance( + $providerMethodCode + ); + $isAdyenAlternativePaymentMethod = $this->paymentMethodsHelper->isAlternativePaymentMethod( + $providerMethodInstance + ); + $isAdyenWalletPaymentMethod = $this->paymentMethodsHelper->isWalletPaymentMethod( + $providerMethodInstance + ); + + if ($isAdyenAlternativePaymentMethod && !$isAdyenWalletPaymentMethod) { + /* + * As the same `AvailabilityChecker` is used for all alternative payment methods, + * we need to identify the payment method. This plugin overrides the `AvailabilityCheckerInterface` + * and implements a custom method to check the availability based on the payment method code. + */ + return $this->adyenAvailabilityChecker->isAvailableAdyenMethod($providerMethodCode); + } else { + return $proceed(); + } + } +} diff --git a/Test/Unit/Gateway/Request/RecurringVaultDataBuilderTest.php b/Test/Unit/Gateway/Request/RecurringVaultDataBuilderTest.php index 55960af346..35a83eb3e5 100644 --- a/Test/Unit/Gateway/Request/RecurringVaultDataBuilderTest.php +++ b/Test/Unit/Gateway/Request/RecurringVaultDataBuilderTest.php @@ -12,47 +12,72 @@ namespace Adyen\Payment\Test\Gateway\Request; use Adyen\Payment\Gateway\Request\RecurringVaultDataBuilder; +use Adyen\Payment\Helper\Config; +use Adyen\Payment\Helper\StateData; +use Adyen\Payment\Helper\Vault as AdyenVaultHelper; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; use Magento\Framework\Api\ExtensionAttributesInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Exception\LocalizedException; use Magento\Payment\Gateway\Data\PaymentDataObject; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; +use Magento\Vault\Api\Data\PaymentTokenFactoryInterface; use Magento\Vault\Api\Data\PaymentTokenInterface; use Magento\Vault\Model\Method\Vault; +use PHPUnit\Framework\MockObject\MockObject; class RecurringVaultDataBuilderTest extends AbstractAdyenTestCase { - private object $recurringVaultDataBuilder; - private $vaultHelperMock; + protected ?RecurringVaultDataBuilder $recurringVaultDataBuilder; + protected AdyenVaultHelper|MockObject $vaultHelperMock; + protected StateData|MockObject $stateDataHelperMock; + protected Config|MockObject $configHelperMock; /** * @return void */ protected function setUp(): void { - $this->vaultHelperMock = $this->createMock(\Adyen\Payment\Helper\Vault::class); + $this->vaultHelperMock = $this->createMock(AdyenVaultHelper::class); + $this->stateDataHelperMock = $this->createMock(StateData::class); + $this->configHelperMock = $this->createMock(Config::class); + + $this->recurringVaultDataBuilder = new RecurringVaultDataBuilder( + $this->stateDataHelperMock, + $this->vaultHelperMock, + $this->configHelperMock + ); + } - $objectManager = new ObjectManager($this); - $this->recurringVaultDataBuilder = $objectManager->getObject(RecurringVaultDataBuilder::class, [ - 'vaultHelper' => $this->vaultHelperMock - ]); + /** + * @return void + */ + protected function tearDown(): void + { + $this->recurringVaultDataBuilder = null; } /** * @param $paymentMethodCode * @param $tokenDetails - * @param $expect3dsFlag - * @dataProvider dataProvider + * @param $tokenType + * @param $isInstantPurchase + * * @return void + * @throws LocalizedException + * + * @dataProvider dataProvider */ - public function testBuild($paymentMethodCode, $tokenDetails, $expect3dsFlag) + public function testBuild($paymentMethodCode, $tokenDetails, $tokenType, $isInstantPurchase) { + $quoteId = 1; + $storeId = 1; + $paymentMethodProviderCode = str_replace('_vault', '', $paymentMethodCode); $orderMock = $this->createMock(Order::class); - $orderMock->method('getQuoteId')->willReturn(1); - $orderMock->method('getStoreId')->willReturn(1); + $orderMock->method('getQuoteId')->willReturn($quoteId); + $orderMock->method('getStoreId')->willReturn($storeId); $paymentMethodInstanceMock = $this->createMock(Vault::class); $paymentMethodInstanceMock->method('getProviderCode')->willReturn($paymentMethodProviderCode); @@ -61,6 +86,7 @@ public function testBuild($paymentMethodCode, $tokenDetails, $expect3dsFlag) $paymentTokenMock = $this->createMock(PaymentTokenInterface::class); $paymentTokenMock->method('getTokenDetails')->willReturn($tokenDetails); $paymentTokenMock->method('getGatewayToken')->willReturn("ABC1234567"); + $paymentTokenMock->method('getType')->willReturn($tokenType); $extensionAttributesMock = $this->createGeneratedMock(ExtensionAttributesInterface::class, [ 'getVaultPaymentToken' @@ -71,6 +97,9 @@ public function testBuild($paymentMethodCode, $tokenDetails, $expect3dsFlag) $paymentMock->method('getOrder')->willReturn($orderMock); $paymentMock->method('getMethodInstance')->willReturn($paymentMethodInstanceMock); $paymentMock->method('getExtensionAttributes')->willReturn($extensionAttributesMock); + $paymentMock->method('getAdditionalInformation') + ->with('instant-purchase') + ->willReturn($isInstantPurchase); $buildSubject = [ 'payment' => $this->createConfiguredMock(PaymentDataObject::class, [ @@ -81,12 +110,22 @@ public function testBuild($paymentMethodCode, $tokenDetails, $expect3dsFlag) $this->vaultHelperMock->method('getPaymentMethodRecurringProcessingModel') ->willReturn('CardOnFile'); + if ($tokenType === PaymentTokenFactoryInterface::TOKEN_TYPE_CREDIT_CARD && !$isInstantPurchase) { + $this->stateDataHelperMock->expects($this->once()) + ->method('getStateData') + ->with($quoteId); + } + $request = $this->recurringVaultDataBuilder->build($buildSubject); $this->assertIsArray($request); $this->assertArrayHasKey('recurringProcessingModel', $request['body']); - if ($expect3dsFlag) { + + if ($tokenType === PaymentTokenFactoryInterface::TOKEN_TYPE_CREDIT_CARD) { $this->assertArrayHasKey('allow3DS2', $request['body']['additionalData']); + $this->assertArrayHasKey('holderName', $request['body']['paymentMethod']); + } else { + $this->assertArrayNotHasKey('additionalData', $request['body']); } } @@ -96,23 +135,39 @@ public static function dataProvider(): array [ 'paymentMethodCode' => 'adyen_cc_vault', 'tokenDetails' => '{"type":"visa","maskedCC":"1111","expirationDate":"3\/2030", "tokenType": "CardOnFile"}', - 'expect3dsFlag' => true + 'tokenType' => 'card', + 'isInstantPurchase' => false ], [ 'paymentMethodCode' => 'adyen_cc_vault', 'tokenDetails' => '{"type":"visa","maskedCC":"1111","expirationDate":"3\/2030"}', - 'expect3dsFlag' => true + 'tokenType' => 'card', + 'isInstantPurchase' => false + ], + [ + 'paymentMethodCode' => 'adyen_cc_vault', + 'tokenDetails' => '{"type":"visa","maskedCC":"1111","expirationDate":"3\/2030"}', + 'tokenType' => 'card', + 'isInstantPurchase' => true ], [ 'paymentMethodCode' => 'adyen_klarna_vault', 'tokenDetails' => '{"type":"klarna", "tokenType": "CardOnFile"}', - 'expect3dsFlag' => false + 'tokenType' => 'account', + 'isInstantPurchase' => false ], [ 'paymentMethodCode' => 'adyen_klarna_vault', 'tokenDetails' => '{"type":"klarna"}', - 'expect3dsFlag' => false + 'tokenType' => 'account', + 'isInstantPurchase' => false ], + [ + 'paymentMethodCode' => 'adyen_klarna_vault', + 'tokenDetails' => '{"type":"klarna"}', + 'tokenType' => 'account', + 'isInstantPurchase' => true + ] ]; } } diff --git a/Test/Unit/Helper/ConfigTest.php b/Test/Unit/Helper/ConfigTest.php index bf70dd4f49..c67a06cbce 100644 --- a/Test/Unit/Helper/ConfigTest.php +++ b/Test/Unit/Helper/ConfigTest.php @@ -148,4 +148,25 @@ public function testGetThreeDSModes() $result = $this->configHelper->getThreeDSFlow($storeId); $this->assertEquals($expectedResult, $result); } + + public function testGetIsCvcRequiredForRecurringCardPayments() + { + $storeId = PHP_INT_MAX; + $expectedResult = true; + + $path = sprintf( + "%s/%s/%s", + Config::XML_PAYMENT_PREFIX, + Config::XML_ADYEN_CC_VAULT, + 'require_cvc' + ); + + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with($this->equalTo($path), $this->equalTo(ScopeInterface::SCOPE_STORE), $this->equalTo($storeId)) + ->willReturn($expectedResult); + + $result = $this->configHelper->getIsCvcRequiredForRecurringCardPayments($storeId); + $this->assertEquals($expectedResult, $result); + } } diff --git a/Test/Unit/Model/InstantPurchase/Card/AvailabilityCheckerTest.php b/Test/Unit/Model/InstantPurchase/Card/AvailabilityCheckerTest.php new file mode 100644 index 0000000000..b0d1cb4f6e --- /dev/null +++ b/Test/Unit/Model/InstantPurchase/Card/AvailabilityCheckerTest.php @@ -0,0 +1,126 @@ + + */ + +namespace Adyen\Payment\Test\Helper\Unit\Model\InstantPurchase\Card; + +use Adyen\Payment\Helper\Config; +use Adyen\Payment\Helper\Vault; +use Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker; +use Adyen\Payment\Model\Ui\AdyenCcConfigProvider; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; + +class AvailabilityCheckerTest extends AbstractAdyenTestCase +{ + const STORE_ID = PHP_INT_MAX; + + protected ?AvailabilityChecker $availabilityChecker; + protected Config|MockObject $configHelperMock; + protected Vault|MockObject $vaultHelperMock; + protected StoreManagerInterface|MockObject $storeManagerMock; + + /** + * @return void + */ + public function setUp(): void + { + $this->configHelperMock = $this->createMock(Config::class); + $this->vaultHelperMock = $this->createMock(Vault::class); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + + $store = $this->createMock(StoreInterface::class); + $store->method('getId')->willReturn(self::STORE_ID); + $this->storeManagerMock->method('getStore')->willReturn($store); + + $this->availabilityChecker = new AvailabilityChecker( + $this->configHelperMock, + $this->vaultHelperMock, + $this->storeManagerMock + ); + } + + /** + * @return void + */ + public function tearDown(): void + { + $this->availabilityChecker = null; + } + + /** + * @param $isCardRecurringEnabled + * @param $recurringProcessingModel + * @param $isCvcRequiredForCardRecurringPayments + * @param $expectedResult + * + * @dataProvider availabilityTestDataProvider + * + * @return void + */ + public function testIsAvailable( + $isCardRecurringEnabled, + $recurringProcessingModel, + $isCvcRequiredForCardRecurringPayments, + $expectedResult + ) { + $this->vaultHelperMock->expects($this->once()) + ->method('getPaymentMethodRecurringActive') + ->with(AdyenCcConfigProvider::CODE, self::STORE_ID) + ->willReturn($isCardRecurringEnabled); + + $this->vaultHelperMock->expects($this->once()) + ->method('getPaymentMethodRecurringProcessingModel') + ->with(AdyenCcConfigProvider::CODE, self::STORE_ID) + ->willReturn($recurringProcessingModel); + + $this->configHelperMock->expects($this->once()) + ->method('getIsCvcRequiredForRecurringCardPayments') + ->with(self::STORE_ID) + ->willReturn($isCvcRequiredForCardRecurringPayments); + + $this->assertEquals($expectedResult, $this->availabilityChecker->isAvailable()); + } + + /** + * @return array[] + */ + protected static function availabilityTestDataProvider(): array + { + return [ + [ + 'isCardRecurringEnabled' => true, + 'recurringProcessingModel' => Vault::CARD_ON_FILE, + 'isCvcRequiredForCardRecurringPayments' => false, + 'expectedResult' => true + ], + [ + 'isCardRecurringEnabled' => true, + 'recurringProcessingModel' => Vault::SUBSCRIPTION, + 'isCvcRequiredForCardRecurringPayments' => false, + 'expectedResult' => false + ], + [ + 'isCardRecurringEnabled' => false, + 'recurringProcessingModel' => Vault::CARD_ON_FILE, + 'isCvcRequiredForCardRecurringPayments' => false, + 'expectedResult' => false + ], + [ + 'isCardRecurringEnabled' => true, + 'recurringProcessingModel' => Vault::CARD_ON_FILE, + 'isCvcRequiredForCardRecurringPayments' => true, + 'expectedResult' => false + ] + ]; + } +} diff --git a/Test/Unit/Model/InstantPurchase/Card/TokenFormatterTest.php b/Test/Unit/Model/InstantPurchase/Card/TokenFormatterTest.php new file mode 100644 index 0000000000..0874bee0f4 --- /dev/null +++ b/Test/Unit/Model/InstantPurchase/Card/TokenFormatterTest.php @@ -0,0 +1,99 @@ + + */ + +namespace Adyen\Payment\Test\Helper\Unit\Model\InstantPurchase\Card; + +use Adyen\Payment\Helper\Data; +use Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use InvalidArgumentException; +use Magento\Vault\Api\Data\PaymentTokenInterface; +use PHPUnit\Framework\MockObject\MockObject; + +class TokenFormatterTest extends AbstractAdyenTestCase +{ + const VALID_TOKEN_DETAILS_VISA = '{"type":"visa","maskedCC":"1111","expirationDate":"3/2030","cardHolderName":"Veronica Costello","tokenType":"CardOnFile"}'; + const VALID_TOKEN_DETAILS_EXOTIC = '{"type":"exoticScheme","maskedCC":"4444","expirationDate":"3/2030","cardHolderName":"Veronica Costello","tokenType":"CardOnFile"}'; + + const INVALID_TOKEN_DETAILS = '{"type":"visa","maskedCC":"1111"}'; + const CC_TYPES_MOCK = [ + 'VI' => [ + 'code_alt' => 'visa', + 'name' => 'Visa' + ], + 'MC' => [ + 'code_alt' => 'mc', + 'name' => 'MasterCard' + ] + ]; + + protected TokenFormatter $tokenFormatter; + protected Data|MockObject $adyenHelperMock; + protected PaymentTokenInterface|MockObject $paymentTokenMock; + + public function setUp(): void + { + $this->paymentTokenMock = $this->createMock(PaymentTokenInterface::class); + $this->adyenHelperMock = $this->createMock(Data::class); + + $this->adyenHelperMock->method('getAdyenCcTypes')->willReturn(self::CC_TYPES_MOCK); + + $this->tokenFormatter = new TokenFormatter($this->adyenHelperMock); + } + + /** + * @param $tokenDetails + * @param $expected + * + * @dataProvider validTokenFormatterTestDataProvider + * + * @return void + */ + public function testFormatPaymentTokenValid($tokenDetails, $expected) + { + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn($tokenDetails); + + $this->assertEquals( + $expected, + $this->tokenFormatter->formatPaymentToken($this->paymentTokenMock) + ); + } + + protected static function validTokenFormatterTestDataProvider(): array + { + return [ + [ + 'tokenDetails' => self::VALID_TOKEN_DETAILS_VISA, + 'expected' => 'Card: Visa, ending: 1111 (expires: 3/2030)' + ], + [ + 'tokenDetails' => self::VALID_TOKEN_DETAILS_EXOTIC, + 'expected' => 'Card: exoticScheme, ending: 4444 (expires: 3/2030)' + ], + ]; + } + + /** + * @return void + */ + public function testFormatPaymentTokenInvalid() + { + $this->expectException(InvalidArgumentException::class); + + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(self::INVALID_TOKEN_DETAILS); + + $this->tokenFormatter->formatPaymentToken($this->paymentTokenMock); + } +} diff --git a/Test/Unit/Model/InstantPurchase/PaymentMethods/AvailabilityCheckerTest.php b/Test/Unit/Model/InstantPurchase/PaymentMethods/AvailabilityCheckerTest.php new file mode 100644 index 0000000000..0ac040e133 --- /dev/null +++ b/Test/Unit/Model/InstantPurchase/PaymentMethods/AvailabilityCheckerTest.php @@ -0,0 +1,121 @@ + + */ + +namespace Adyen\Payment\Test\Helper\Unit\Model\InstantPurchase\PaymentMethods; + +use Adyen\Payment\Helper\Vault; +use Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\Exception\NotFoundException; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; + +class AvailabilityCheckerTest extends AbstractAdyenTestCase +{ + const STORE_ID = PHP_INT_MAX; + + protected ?AvailabilityChecker $availabilityChecker; + protected Vault|MockObject $vaultHelperMock; + protected StoreManagerInterface|MockObject $storeManagerMock; + + /** + * @return void + */ + public function setUp(): void + { + $this->vaultHelperMock = $this->createMock(Vault::class); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + + $store = $this->createMock(StoreInterface::class); + $store->method('getId')->willReturn(self::STORE_ID); + $this->storeManagerMock->method('getStore')->willReturn($store); + + $this->availabilityChecker = new AvailabilityChecker( + $this->vaultHelperMock, + $this->storeManagerMock + ); + } + + /** + * @return void + */ + public function tearDown(): void + { + $this->availabilityChecker = null; + } + + /** + * @param $paymentMethod + * @param $isMethodRecurringEnabled + * @param $recurringProcessingModel + * @param $expectedResult + * + * @return void + * @dataProvider availabilityTestDataProvider + */ + public function testIsAvailableAdyenMethod( + $paymentMethod, + $isMethodRecurringEnabled, + $recurringProcessingModel, + $expectedResult + ) { + $this->vaultHelperMock->expects($this->once()) + ->method('getPaymentMethodRecurringActive') + ->with($paymentMethod, self::STORE_ID) + ->willReturn($isMethodRecurringEnabled); + + $this->vaultHelperMock->expects($this->once()) + ->method('getPaymentMethodRecurringProcessingModel') + ->with($paymentMethod, self::STORE_ID) + ->willReturn($recurringProcessingModel); + + $this->assertEquals($expectedResult, $this->availabilityChecker->isAvailableAdyenMethod($paymentMethod)); + } + + /** + * @return array[] + */ + protected static function availabilityTestDataProvider(): array + { + return [ + [ + 'paymentMethod' => 'sepa_direct_debit', + 'isMethodRecurringEnabled' => true, + 'recurringProcessingModel' => Vault::CARD_ON_FILE, + 'expectedResult' => true + ], + [ + 'paymentMethod' => 'sepa_direct_debit', + 'isMethodRecurringEnabled' => false, + 'recurringProcessingModel' => Vault::CARD_ON_FILE, + 'expectedResult' => false + ], + [ + 'paymentMethod' => 'sepa_direct_debit', + 'isMethodRecurringEnabled' => true, + 'recurringProcessingModel' => Vault::SUBSCRIPTION, + 'expectedResult' => false + ] + ]; + } + + /** + * Ensure `isAvailable` is not implemented. + * + * @return void + */ + public function testIsAvailable() + { + $this->expectException(NotFoundException::class); + $this->availabilityChecker->isAvailable(); + } +} diff --git a/Test/Unit/Model/InstantPurchase/PaymentMethods/TokenFormatterTest.php b/Test/Unit/Model/InstantPurchase/PaymentMethods/TokenFormatterTest.php new file mode 100644 index 0000000000..f6fce59ff2 --- /dev/null +++ b/Test/Unit/Model/InstantPurchase/PaymentMethods/TokenFormatterTest.php @@ -0,0 +1,36 @@ + + */ + +namespace Adyen\Payment\Test\Helper\Unit\Model\InstantPurchase\PaymentMethods; + +use Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Vault\Api\Data\PaymentTokenInterface; + +class TokenFormatterTest extends AbstractAdyenTestCase +{ + public function testFormatPaymentToken() + { + $tokenFormatter = new TokenFormatter(); + $paymentTokenMock = $this->createMock(PaymentTokenInterface::class); + + $tokenDetails = '{"type":"sepadirectdebit","tokenLabel":"SEPA Direct Debit token created on 2024-12-11"}'; + + $paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn($tokenDetails); + + $this->assertEquals( + 'SEPA Direct Debit token created on 2024-12-11', + $tokenFormatter->formatPaymentToken($paymentTokenMock) + ); + } +} diff --git a/Test/Unit/Model/Ui/AdyenCcConfigProviderTest.php b/Test/Unit/Model/Ui/AdyenCcConfigProviderTest.php new file mode 100644 index 0000000000..96c9bfb9e6 --- /dev/null +++ b/Test/Unit/Model/Ui/AdyenCcConfigProviderTest.php @@ -0,0 +1,160 @@ + + */ + +namespace Adyen\Payment\Model\Ui; + +use Adyen\Payment\Helper\Config; +use Adyen\Payment\Helper\Data; +use Adyen\Payment\Helper\PaymentMethods; +use Adyen\Payment\Helper\Vault; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Asset\File; +use Magento\Framework\View\Asset\Source; +use Magento\Payment\Model\CcConfig; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; + +class AdyenCcConfigProviderTest extends AbstractAdyenTestCase +{ + protected ?AdyenCcConfigProvider $adyenCcConfigProvider; + protected Data|MockObject $adyenHelperMock; + protected RequestInterface|MockObject $requestMock; + protected UrlInterface|MockObject $urlBuilderMock; + protected Source|MockObject $assetSourceMock; + protected StoreManagerInterface|MockObject $storeManagerMock; + protected CcConfig|MockObject $ccConfigMock; + protected SerializerInterface|MockObject $serializerMock; + protected Config|MockObject $configHelperMock; + protected PaymentMethods|MockObject $paymentMethodsHelperMock; + protected Vault|MockObject $vaultHelperMock; + + /** + * @return void + */ + protected function setUp(): void + { + $this->adyenHelperMock = $this->createMock(Data::class); + $this->requestMock = $this->createMock(RequestInterface::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->assetSourceMock = $this->createMock(Source::class); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->ccConfigMock = $this->createMock(CcConfig::class); + $this->serializerMock = $this->createMock(SerializerInterface::class); + $this->configHelperMock = $this->createMock(Config::class); + $this->paymentMethodsHelperMock = $this->createMock(PaymentMethods::class); + $this->vaultHelperMock = $this->createMock(Vault::class); + + $this->adyenCcConfigProvider = new AdyenCcConfigProvider( + $this->adyenHelperMock, + $this->requestMock, + $this->urlBuilderMock, + $this->assetSourceMock, + $this->storeManagerMock, + $this->ccConfigMock, + $this->serializerMock, + $this->configHelperMock, + $this->paymentMethodsHelperMock, + $this->vaultHelperMock + ); + } + + /** + * @return void + */ + protected function tearDown(): void + { + $this->adyenCcConfigProvider = null; + } + + /** + * @param $enableInstallments + * @return void + * + * @dataProvider getConfigTestDataProvider + */ + public function testGetConfig($enableInstallments) + { + $storeId = PHP_INT_MAX; + $store = $this->createMock(StoreInterface::class); + $store->method('getId')->willReturn($storeId); + $this->storeManagerMock->method('getStore')->willReturn($store); + + $this->configHelperMock->method('getAdyenCcConfigData') + ->willReturnMap([ + ['enable_installments', null, $enableInstallments], + ['installments', null, 'mock_serialized_installments'], + ['useccv', null, true] + ]); + + $this->adyenHelperMock->method('getAdyenCcTypes') + ->willReturn(['MC' => ['name' => 'MasterCard', 'code_alt' => 'mc']]); + + $assetMockMasterCard = $this->createMock(File::class); + $assetMockMasterCard->method('getSourceFile')->willReturn( + __DIR__ . '/../../../../view/base/web/images/adyen/adyen-hq.svg' + ); + + $this->ccConfigMock->method('createAsset')->willReturn($assetMockMasterCard); + + $this->assetSourceMock->method('findSource') + ->with($assetMockMasterCard) + ->willReturn('mock_relative_icon_path'); + + $this->ccConfigMock->expects($this->once()) + ->method('getCcMonths'); + + $this->ccConfigMock->expects($this->once()) + ->method('getCcYears'); + + $this->ccConfigMock->expects($this->once()) + ->method('getCvvImageUrl'); + + $configObject = $this->adyenCcConfigProvider->getConfig(); + + $this->assertArrayHasKey('installments', $configObject['payment']['adyenCc']); + $this->assertArrayHasKey('isClickToPayEnabled', $configObject['payment']['adyenCc']); + $this->assertArrayHasKey('icons', $configObject['payment']['adyenCc']); + $this->assertArrayHasKey('isCardRecurringEnabled', $configObject['payment']['adyenCc']); + $this->assertArrayHasKey('locale', $configObject['payment']['adyenCc']); + $this->assertArrayHasKey('title', $configObject['payment']['adyenCc']); + $this->assertArrayHasKey('methodCode', $configObject['payment']['adyenCc']); + $this->assertArrayHasKey('availableTypes', $configObject['payment']['ccform']); + $this->assertArrayHasKey('availableTypesByAlt', $configObject['payment']['ccform']); + $this->assertArrayHasKey('months', $configObject['payment']['ccform']); + $this->assertArrayHasKey('years', $configObject['payment']['ccform']); + $this->assertArrayHasKey('hasVerification', $configObject['payment']['ccform']); + $this->assertArrayHasKey('cvvImageUrl', $configObject['payment']['ccform']); + } + + /** + * @return array + */ + protected function getConfigTestDataProvider(): array + { + return [ + ['enableInstallments' => true], + ['enableInstallments' => false] + ]; + } + + /** + * @return void + */ + public function testConstants() + { + $this->assertEquals('adyen_cc', AdyenCcConfigProvider::CODE); + $this->assertEquals('adyen_cc_vault', AdyenCcConfigProvider::CC_VAULT_CODE); + } +} diff --git a/Test/Unit/Plugin/InstantPurchaseIntegrationTest.php b/Test/Unit/Plugin/InstantPurchaseIntegrationTest.php new file mode 100644 index 0000000000..f8fc8e3aea --- /dev/null +++ b/Test/Unit/Plugin/InstantPurchaseIntegrationTest.php @@ -0,0 +1,153 @@ + + */ + +namespace Adyen\Payment\Test\Plugin; + +use Adyen\Payment\Api\InstantPurchase\PaymentMethodIntegration\AdyenAvailabilityCheckerInterface; +use Adyen\Payment\Helper\PaymentMethods; +use Adyen\Payment\Model\Method\PaymentMethodVault; +use Adyen\Payment\Plugin\InstantPurchaseIntegration; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\Exception\LocalizedException; +use Magento\InstantPurchase\PaymentMethodIntegration\Integration; +use Magento\Payment\Helper\Data; +use Magento\Payment\Model\MethodInterface; +use PHPUnit\Framework\MockObject\MockObject; + +class InstantPurchaseIntegrationTest extends AbstractAdyenTestCase +{ + protected ?InstantPurchaseIntegration $instantPurchaseIntegrationPlugin; + protected Integration|MockObject $subjectMock; + protected AdyenAvailabilityCheckerInterface|MockObject $adyenAvailabilityCheckerMock; + protected PaymentMethods|MockObject $paymentMethodsMock; + protected Data $magentoDataHelperMock; + + public function setUp(): void + { + $this->adyenAvailabilityCheckerMock = $this->createMock( + AdyenAvailabilityCheckerInterface::class + ); + $this->paymentMethodsMock = $this->createMock(PaymentMethods::class); + $this->magentoDataHelperMock = $this->createMock(Data::class); + $this->subjectMock = $this->createMock(Integration::class); + + $this->instantPurchaseIntegrationPlugin = new InstantPurchaseIntegration( + $this->adyenAvailabilityCheckerMock, + $this->paymentMethodsMock, + $this->magentoDataHelperMock + ); + } + + public function tearDown(): void + { + $this->instantPurchaseIntegrationPlugin = null; + } + + /** + * @param $providerCode + * @param $isAdyenAlternativePaymentMethod + * @param $isWallet + * @param $shouldIntercept + * + * @return void + * @throws LocalizedException + * + * @dataProvider dataProviderForNotApplicableCases + * + */ + public function testAroundIsAvailable( + $providerCode, + $isAdyenAlternativePaymentMethod, + $isWallet, + $shouldIntercept + ) { + $vaultMethodInstanceMock = $this->createMock(PaymentMethodVault::class); + $vaultMethodInstanceMock->method('getProviderCode') + ->willReturn($providerCode); + + $this->subjectMock->expects($this->once()) + ->method('getPaymentMethod') + ->willReturn($vaultMethodInstanceMock); + + $providerMethodInstanceMock = $this->createMock(MethodInterface::class); + + $this->magentoDataHelperMock->expects($this->once()) + ->method('getMethodInstance') + ->with($providerCode) + ->willReturn($providerMethodInstanceMock); + + $this->paymentMethodsMock->expects($this->once()) + ->method('isAlternativePaymentMethod') + ->with($providerMethodInstanceMock) + ->willReturn($isAdyenAlternativePaymentMethod); + + $this->paymentMethodsMock->expects($this->once()) + ->method('isWalletPaymentMethod') + ->with($providerMethodInstanceMock) + ->willReturn($isWallet); + + $this->subjectMock->method('isAvailable')->willReturn(true); + + if ($shouldIntercept) { + $this->adyenAvailabilityCheckerMock + ->expects($this->once()) + ->method('isAvailableAdyenMethod') + ->with($providerCode) + ->willReturn(true); + } else { + $this->adyenAvailabilityCheckerMock->expects($this->never())->method('isAvailableAdyenMethod'); + } + + $result = $this->instantPurchaseIntegrationPlugin->aroundIsAvailable( + $this->subjectMock, + [$this, 'callableMockFunction'] + ); + + $this->assertTrue($result); + } + + /** + * Mock the callable argument of `aroundIsAvailable` method. + * + * @return bool + */ + public function callableMockFunction(): bool + { + return true; + } + + /** + * @return array[] + */ + public static function dataProviderForNotApplicableCases(): array + { + return [ + [ + 'providerCode' => 'adyen_cc', + 'isAdyenAlternativePaymentMethod' => false, + 'isWallet' => false, + 'shouldIntercept' => false, + ], + [ + 'providerCode' => 'adyen_googlepay', + 'isAdyenAlternativePaymentMethod' => true, + 'isWallet' => true, + 'shouldIntercept' => false, + ], + [ + 'providerCode' => 'adyen_sepa_direct_debit', + 'isAdyenAlternativePaymentMethod' => true, + 'isWallet' => false, + 'shouldIntercept' => true, + ] + ]; + } +} diff --git a/composer.json b/composer.json index 6c775c9348..d9be60eaa9 100755 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ "magento/module-vault": ">=101.2.4", "magento/module-multishipping": ">=100.4.4", "magento/module-graph-ql": ">=100.4.4", + "magento/module-instant-purchase": ">=100.4.4", "ext-json": "*" }, "require-dev": { diff --git a/etc/config.xml b/etc/config.xml index b432bd83ff..f47e7dfb23 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -68,6 +68,10 @@ Stored Cards (Adyen) AdyenPaymentCardVaultFacade 1 + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 @@ -171,6 +175,10 @@ Stored Klarna AdyenPaymentKlarnaVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -200,6 +208,10 @@ Stored PayPal AdyenPaymentPaypalVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -276,6 +288,10 @@ Stored Amazon Pay AdyenPaymentAmazonpayVaultFacade + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 @@ -305,6 +321,10 @@ Stored GooglePay AdyenPaymentGooglepayVaultFacade + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 @@ -383,6 +403,10 @@ Stored SEPA Direct Debit AdyenPaymentSepadirectdebitVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -498,6 +522,10 @@ Stored Apple Pay AdyenPaymentApplePayVaultFacade + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 @@ -896,6 +924,10 @@ Stored Klarna Debit Risk AdyenPaymentDirectEbankingVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -1054,6 +1086,10 @@ Stored EPS AdyenPaymentEpsVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -1168,6 +1204,10 @@ Stored Giropay AdyenPaymentGiropayVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -1286,6 +1326,10 @@ Stored Klarna Pay over Time AdyenPaymentKlarnaAccountVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -1312,6 +1356,10 @@ Stored Klarna Pay Now AdyenPaymentKlarnaPaynowVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -1514,6 +1562,10 @@ Stored Open banking AdyenPaymentPaybybankVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -1651,6 +1703,10 @@ Stored TWINT AdyenPaymentTwintVaultFacade + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 @@ -1743,6 +1799,10 @@ Stored Zip AdyenPaymentZipVaultFacade + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 @@ -1769,6 +1829,10 @@ Stored GCash AdyenPaymentGcashVaultFacade + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 @@ -1839,6 +1903,10 @@ Stored Carnet AdyenPaymentCarnetVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -1909,6 +1977,10 @@ Stored MoMo Wallet AdyenPaymentMomoWalletVaultFacade + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 @@ -1957,6 +2029,10 @@ Stored Bancontact AdyenPaymentBcmcVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -1983,6 +2059,10 @@ Stored Cash App Pay AdyenPaymentCashappVaultFacade + + Adyen\Payment\Model\InstantPurchase\PaymentMethods\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\PaymentMethods\TokenFormatter + 0 @@ -2207,6 +2287,10 @@ Stored DANA AdyenPaymentDanaVaultFacade + + Adyen\Payment\Model\InstantPurchase\Card\AvailabilityChecker + Adyen\Payment\Model\InstantPurchase\Card\TokenFormatter + 0 diff --git a/etc/di.xml b/etc/di.xml index 880ffe546c..e7fc019f4f 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -1465,6 +1465,8 @@ + Magento\Framework\Filesystem\Driver\File @@ -1721,6 +1723,9 @@ + + +