From 59ec4ccd7e632a8c72e0d2c539202ea142db3bb2 Mon Sep 17 00:00:00 2001 From: Rok Popov Ledinski Date: Wed, 21 Aug 2024 11:38:57 +0200 Subject: [PATCH] Feature/paypal express (#101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [ECP-9072] Implement backend requirements of PayPal express (#84) * [ECP-9072] Add new configuration values * [ECP-9072] Refactor Button classes and create a new one for PayPal * [ECP-9072] Implement an abstract config getter for showOn fields * [ECP-9072] Define payment method variants * [ECP-9072] Implement abstract observer and simplify the observer logic * [ECP-9072] Add new event for PayPal observer * [ECP-9072] Add new resolver for PayPal * [ECP-9072] Rename the variable * [ECP-9072] Create forced required frontend template file * [ECP-8992] Remove setup_version constraint from the data patch * [ECP-8992] Update configuration paths to reflect real Adyen tx_variants * [ECP-8992] Implement abstract template for shortcut buttons * [ECP-8992] Update abstract observer * [ECP-8992] Create Magento frontend JS component for PayPal express * [ECP-8992] Update frontend template * [ECP-8992] Refactor code duplication * [ECP-8992] Create abstract config route patcher --------- Co-authored-by: Can Demiralp * [ECP-9114] Implement /payments endpoint for express use cases (#88) Co-authored-by: Can Demiralp * [ECP-9082] Implement Checkout API's /paypal/updateOrder endpoint (#86) * Implement Checkout API's /paypal/updateOrder endpoint * [ECP-9082] Obtain order related information from backend and implement guest endpoint --------- Co-authored-by: Can Demiralp Co-authored-by: Can Demiralp * [ECP-9072] Implement backend requirements of PayPal express (#84) * [ECP-9072] Add new configuration values * [ECP-9072] Refactor Button classes and create a new one for PayPal * [ECP-9072] Implement an abstract config getter for showOn fields * [ECP-9072] Define payment method variants * [ECP-9072] Implement abstract observer and simplify the observer logic * [ECP-9072] Add new event for PayPal observer * [ECP-9072] Add new resolver for PayPal * [ECP-9072] Rename the variable * [ECP-9072] Create forced required frontend template file * [ECP-8992] Remove setup_version constraint from the data patch * [ECP-8992] Update configuration paths to reflect real Adyen tx_variants * [ECP-8992] Implement abstract template for shortcut buttons * [ECP-8992] Update abstract observer * [ECP-8992] Create Magento frontend JS component for PayPal express * [ECP-8992] Update frontend template * [ECP-8992] Refactor code duplication * [ECP-8992] Create abstract config route patcher --------- Co-authored-by: Can Demiralp * [ECP-9114] Implement /payments endpoint for express use cases (#88) Co-authored-by: Can Demiralp * [ECP-9082] Implement Checkout API's /paypal/updateOrder endpoint (#86) * Implement Checkout API's /paypal/updateOrder endpoint * [ECP-9082] Obtain order related information from backend and implement guest endpoint --------- Co-authored-by: Can Demiralp Co-authored-by: Can Demiralp * [ECP-9143] Configure the place for the express components (PDP, minicart, summary), (#91) * [ECP-9143] Configure the place for the express components (PDP, minicart, summary), * [ECP-9143] Adding braces for if else block * Update Observer/AddGooglePayShortcuts.php Removing unwanted , Co-authored-by: Rok Popov Ledinski * [ECP-8400] Support virtual products on express payments (#78) * [ECP-8400] Implement virtualQuoteModel to obtain quote type * [ECP-8400] Fix pdp virtual quote condition * [ECP-8400] Fix placing order with virtual product * [ECP-8400] Handle payment status * [ECP-8400] Support virtual products on Apple Pay express * [ECP-8400] Update the related callbacks * [ECP-8400] Fix virtualQuote model * [ECP-8400] Fix virtualQuote model * [ECP-8400] Fix virtualQuote model --------- Co-authored-by: Can Demiralp * Add onAvailable event for Apple Pay (#82) Co-authored-by: Dimitri BOUTEILLE Co-authored-by: Can Demiralp * Version bump 2.1.0 * [ECP-9143] Configure the place for the express components (PDP, minicart, summary), # Conflicts: # Observer/AddApplePayShortcuts.php # Observer/AddGooglePayShortcuts.php * Solving sonarcloud pipeline --------- Co-authored-by: Rok Popov Ledinski Co-authored-by: Can Demiralp Co-authored-by: Can Demiralp Co-authored-by: Dimitri BOUTEILLE <34821762+dimitriBouteille@users.noreply.github.com> Co-authored-by: Dimitri BOUTEILLE Co-authored-by: RokPopov * [ECP-9079] Implementing frontend logic for Paypal Express (#95) * [ECP-9079] Adding frontend component to Paypal * [ECP-9079] Adding frontend component to Paypal * [ECP-9079] Adding frontend component to Paypal * [ECP-9079] Adding frontend component to Paypal * [ECP-9079] Adding frontend component to Paypal * [ECP-9079] Adding frontend component to Paypal - adding new Observer to change the payment method name back to paypal and set status as New. * Abstracting the code, removing the console.logs * Adding payment-details call block for express and not using module-payment one, bcoz it incorrectly checks the loggedIn User logic * Removing isMultishipping parameter, as this is not a valid use case for Express * Removing duplicates * Removing useless comments Co-authored-by: Ángel Campos * Resolving comments on PR by not passing amount from frontend rather getting the subtotal from the quote for the /payments call and also removing unwanted comments and getting the order status dynamically from the state * Updating naming conventions to prevent confusions for future. * Updating assignments within the condition check * Found the refence here https://docs.adyen.com/development-resources/client-side-authentication/migrate-from-origin-key-to-client-key/ * Update button.js Removing unwanted console.log * Update AdyenInitPayments.php Getting the currency also from the Quote instead of chargedQuoteCurrency --------- Co-authored-by: Ángel Campos * Enabling Paypal Express on PDP (#102) Solving some of the obvious sonarcloud suggestions * Providing telemetry's endpoint expressPage field to payment method component configurations (#103) * Refactoring the code and removing deprecated facades * Updating changes for PDP for logged in user * [ECP-9381] Fix PDP payment, discounted price and tax calculation (#104) * [ECP-8715] Refactor initPayments JS action * Revert "[ECP-9230] Prevent refreshing Quote ID during page refresh on product page for express payment (#93)" This reverts commit 4deb8c4382965d0d2b1801950a75d2ee8fea08da. * Improve error handling on shipping details changes * Revert ECP-9230 * Fix PDP payment issues and update endpoints * Fix tax calculation issue on PayPal express * Use billing address if the quote is virtual * Fix /paymentMethods request amount value --------- Co-authored-by: Can Demiralp * Adding missing semicolons * Removing unused versions * Beautifying the code --------- Co-authored-by: Can Demiralp Co-authored-by: Can Demiralp Co-authored-by: raoulritter <59527829+raoulritter@users.noreply.github.com> Co-authored-by: khushboos Co-authored-by: Dimitri BOUTEILLE <34821762+dimitriBouteille@users.noreply.github.com> Co-authored-by: Dimitri BOUTEILLE Co-authored-by: Ángel Campos --- Api/AdyenInitPaymentsInterface.php | 31 + Api/AdyenPaypalUpdateOrderInterface.php | 31 + Api/GuestAdyenInitPaymentsInterface.php | 29 + Api/GuestAdyenPaypalUpdateOrderInterface.php | 31 + Block/ApplePay/Shortcut/Button.php | 66 +- Block/Buttons/AbstractButton.php | 103 ++- Block/GooglePay/Shortcut/Button.php | 103 +-- Block/Paypal/Shortcut/Button.php | 22 + Helper/PaypalUpdateOrder.php | 118 ++++ Helper/Util/PaypalDeliveryMethodValidator.php | 31 + ...PaypalDeliveryMethodValidatorInterface.php | 39 ++ Model/AdyenInitPayments.php | 203 ++++++ Model/AdyenPaypalUpdateOrder.php | 217 ++++++ Model/Configuration.php | 56 +- Model/ConfigurationInterface.php | 38 +- Model/GetAdyenPaymentMethodsByProduct.php | 2 +- Model/GuestAdyenInitPayments.php | 75 ++ Model/GuestAdyenPaypalUpdateOrder.php | 63 ++ .../PaymentResponse/Collection.php | 30 + Observer/AbstractPaymentMethodShortcuts.php | 111 +++ Observer/AddApplePayShortcuts.php | 66 +- Observer/AddGooglePayShortcuts.php | 66 +- Observer/AddPaypalShortcuts.php | 26 + Observer/SubmitQuoteObserver.php | 42 ++ .../Gateway/Request/CheckoutDataBuilder.php | 4 +- .../AbstractConfigurationPathPatcher.php | 102 +++ .../Data/SeparateConfigurationFields.php | 36 + Setup/Patch/Data/UpdateExpressDBPaths.php | 109 +-- .../system/adyen_online_checkout.xml | 22 +- etc/config.xml | 43 ++ etc/di.xml | 33 + etc/events.xml | 3 + etc/frontend/di.xml | 18 +- etc/frontend/events.xml | 1 + etc/webapi.xml | 32 + .../templates/abstract/shortcut.phtml | 15 + .../templates/applepay/shortcut.phtml | 36 - view/frontend/templates/checkout-config.phtml | 3 +- .../templates/googlepay/shortcut.phtml | 34 - view/frontend/web/css/source/_module.less | 2 +- view/frontend/web/js/actions/createOrder.js | 18 + .../web/js/actions/getExpressMethods.js | 65 +- view/frontend/web/js/actions/initPayments.js | 36 + .../web/js/actions/updatePaypalOrder.js | 78 +++ view/frontend/web/js/applepay/button.js | 13 +- view/frontend/web/js/googlepay/button.js | 16 +- .../frontend/web/js/helpers/getCurrentPage.js | 19 + .../web/js/helpers/getPaypalStyles.js | 11 + .../js/helpers/manageQuoteIdOnPageRefresh.js | 12 - .../web/js/model/adyen-payment-service.js | 55 ++ view/frontend/web/js/paypal_express/button.js | 640 ++++++++++++++++++ 51 files changed, 2491 insertions(+), 564 deletions(-) create mode 100644 Api/AdyenInitPaymentsInterface.php create mode 100755 Api/AdyenPaypalUpdateOrderInterface.php create mode 100644 Api/GuestAdyenInitPaymentsInterface.php create mode 100644 Api/GuestAdyenPaypalUpdateOrderInterface.php create mode 100644 Block/Paypal/Shortcut/Button.php create mode 100644 Helper/PaypalUpdateOrder.php create mode 100644 Helper/Util/PaypalDeliveryMethodValidator.php create mode 100644 Helper/Util/PaypalDeliveryMethodValidatorInterface.php create mode 100644 Model/AdyenInitPayments.php create mode 100755 Model/AdyenPaypalUpdateOrder.php create mode 100644 Model/GuestAdyenInitPayments.php create mode 100644 Model/GuestAdyenPaypalUpdateOrder.php create mode 100644 Model/ResourceModel/PaymentResponse/Collection.php create mode 100644 Observer/AbstractPaymentMethodShortcuts.php create mode 100644 Observer/AddPaypalShortcuts.php create mode 100644 Observer/SubmitQuoteObserver.php create mode 100644 Setup/Patch/Abstract/AbstractConfigurationPathPatcher.php create mode 100644 Setup/Patch/Data/SeparateConfigurationFields.php create mode 100644 etc/config.xml create mode 100644 view/frontend/templates/abstract/shortcut.phtml delete mode 100644 view/frontend/templates/applepay/shortcut.phtml delete mode 100644 view/frontend/templates/googlepay/shortcut.phtml create mode 100644 view/frontend/web/js/actions/createOrder.js create mode 100644 view/frontend/web/js/actions/initPayments.js create mode 100755 view/frontend/web/js/actions/updatePaypalOrder.js create mode 100644 view/frontend/web/js/helpers/getCurrentPage.js create mode 100644 view/frontend/web/js/helpers/getPaypalStyles.js delete mode 100644 view/frontend/web/js/helpers/manageQuoteIdOnPageRefresh.js create mode 100644 view/frontend/web/js/model/adyen-payment-service.js create mode 100755 view/frontend/web/js/paypal_express/button.js diff --git a/Api/AdyenInitPaymentsInterface.php b/Api/AdyenInitPaymentsInterface.php new file mode 100644 index 00000000..a25137d4 --- /dev/null +++ b/Api/AdyenInitPaymentsInterface.php @@ -0,0 +1,31 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Api; + +interface AdyenInitPaymentsInterface +{ + const PAYMENT_CHANNEL_WEB = 'web'; + + /** + * @param string $stateData + * @param int|null $adyenCartId + * @param string|null $adyenMaskedQuoteId + * @return string + */ + public function execute( + string $stateData, + ?int $adyenCartId = null, + ?string $adyenMaskedQuoteId = null + ): string; +} diff --git a/Api/AdyenPaypalUpdateOrderInterface.php b/Api/AdyenPaypalUpdateOrderInterface.php new file mode 100755 index 00000000..c5a934a5 --- /dev/null +++ b/Api/AdyenPaypalUpdateOrderInterface.php @@ -0,0 +1,31 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Api; + +interface AdyenPaypalUpdateOrderInterface +{ + /** + * @param string $paymentData + * @param int|null $adyenCartId + * @param string|null $adyenMaskedQuoteId + * @param string $deliveryMethods + * @return mixed + */ + public function execute( + string $paymentData, + ?int $adyenCartId = null, + ?string $adyenMaskedQuoteId = null, + string $deliveryMethods = '' + ): string; +} diff --git a/Api/GuestAdyenInitPaymentsInterface.php b/Api/GuestAdyenInitPaymentsInterface.php new file mode 100644 index 00000000..b10d1b48 --- /dev/null +++ b/Api/GuestAdyenInitPaymentsInterface.php @@ -0,0 +1,29 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Api; + +interface GuestAdyenInitPaymentsInterface +{ + /** + * @param string $stateData + * @param string|null $guestMaskedId + * @param string|null $adyenMaskedQuoteId + * @return string + */ + public function execute( + string $stateData, + ?string $guestMaskedId = null, + ?string $adyenMaskedQuoteId = null + ): string; +} diff --git a/Api/GuestAdyenPaypalUpdateOrderInterface.php b/Api/GuestAdyenPaypalUpdateOrderInterface.php new file mode 100644 index 00000000..9e158aa7 --- /dev/null +++ b/Api/GuestAdyenPaypalUpdateOrderInterface.php @@ -0,0 +1,31 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Api; + +interface GuestAdyenPaypalUpdateOrderInterface +{ + /** + * @param string $paymentData + * @param string|null $guestMaskedId + * @param string|null $adyenMaskedQuoteId + * @param string $deliveryMethods + * @return mixed + */ + public function execute( + string $paymentData, + ?string $guestMaskedId = null, + ?string $adyenMaskedQuoteId = null, + string $deliveryMethods = '' + ): string; +} diff --git a/Block/ApplePay/Shortcut/Button.php b/Block/ApplePay/Shortcut/Button.php index ce779803..a00cada1 100644 --- a/Block/ApplePay/Shortcut/Button.php +++ b/Block/ApplePay/Shortcut/Button.php @@ -14,7 +14,6 @@ namespace Adyen\ExpressCheckout\Block\ApplePay\Shortcut; use Adyen\Payment\Helper\Data as AdyenHelper; -use Adyen\Payment\Helper\Config as AdyenConfigHelper; use Adyen\ExpressCheckout\Block\Buttons\AbstractButton; use Adyen\ExpressCheckout\Model\ConfigurationInterface; use Magento\Checkout\Model\Session; @@ -22,44 +21,19 @@ use Magento\Checkout\Model\DefaultConfigProvider; use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\Template\Context; use Magento\Payment\Model\MethodInterface; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Model\StoreManagerInterface; class Button extends AbstractButton implements ShortcutInterface { - /** - * @var DefaultConfigProvider $defaultConfigProvider - */ - private $defaultConfigProvider; - - /** - * @var UrlInterface $url - */ - private $url; - - /** - * @var CustomerSession $customerSession - */ - private $customerSession; - - /** - * @var StoreManagerInterface $storeManager - */ - private $storeManager; - - /** - * @var ScopeConfigInterface $scopeConfig - */ - private $scopeConfig; + const PAYMENT_METHOD_VARIANT = 'applepay'; /** * @var ConfigurationInterface $configuration */ - private $configuration; + private ConfigurationInterface $configuration; /** * Button Constructor @@ -73,7 +47,6 @@ class Button extends AbstractButton implements ShortcutInterface * @param DefaultConfigProvider $defaultConfigProvider * @param ScopeConfigInterface $scopeConfig * @param AdyenHelper $adyenHelper - * @param AdyenConfigHelper $adyenConfigHelper * @param ConfigurationInterface $configuration * @param array $data */ @@ -87,7 +60,6 @@ public function __construct( DefaultConfigProvider $defaultConfigProvider, ScopeConfigInterface $scopeConfig, AdyenHelper $adyenHelper, - AdyenConfigHelper $adyenConfigHelper, ConfigurationInterface $configuration, array $data = [] ) { @@ -100,40 +72,28 @@ public function __construct( $storeManagerInterface, $scopeConfig, $adyenHelper, - $adyenConfigHelper, + $defaultConfigProvider, $data ); - $this->defaultConfigProvider = $defaultConfigProvider; + $this->configuration = $configuration; } /** - * Current Quote ID for guests - * * @return string - * @throws LocalizedException - * @throws NoSuchEntityException */ - public function getQuoteId(): string + public function getButtonColor(): string { - try { - $config = $this->defaultConfigProvider->getConfig(); - if (!empty($config['quoteData']['entity_id'])) { - return $config['quoteData']['entity_id']; - } - } catch (NoSuchEntityException $e) { - if ($e->getMessage() !== 'No such entity with cartId = ') { - throw $e; - } - } - return ''; + return $this->configuration->getApplePayButtonColor(); } - /** - * @return string - */ - public function getButtonColor(): string + public function buildConfiguration(): array { - return $this->configuration->getApplePayButtonColor(); + $baseConfiguration = parent::buildConfiguration(); + $variant = $this->getPaymentMethodVariant(); + + $baseConfiguration["Adyen_ExpressCheckout/js/$variant/button"]['buttonColor'] = $this->getButtonColor(); + + return $baseConfiguration; } } diff --git a/Block/Buttons/AbstractButton.php b/Block/Buttons/AbstractButton.php index f8a12807..f720634d 100644 --- a/Block/Buttons/AbstractButton.php +++ b/Block/Buttons/AbstractButton.php @@ -13,18 +13,17 @@ namespace Adyen\ExpressCheckout\Block\Buttons; -use Adyen\Payment\Helper\Config; use Adyen\Payment\Helper\Data as AdyenHelper; +use Exception; +use Magento\Checkout\Model\DefaultConfigProvider; use Magento\Checkout\Model\Session; use Magento\Customer\Model\Session as CustomerSession; -use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\Template; use Magento\Framework\View\Element\Template\Context; use Magento\Payment\Model\MethodInterface; -use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\App\Config\ScopeConfigInterface; @@ -38,42 +37,42 @@ abstract class AbstractButton extends Template /** * @var Session */ - private $checkoutSession; + private Session $checkoutSession; /** * @var MethodInterface */ - private $payment; + private MethodInterface $payment; /** * @var UrlInterface $url */ - private $url; + private UrlInterface $url; /** * @var CustomerSession $customerSession */ - private $customerSession; + private CustomerSession $customerSession; /** * @var StoreManagerInterface $storeManager */ - private $storeManager; + private StoreManagerInterface $storeManager; /** * @var ScopeConfigInterface $scopeConfig */ - private $scopeConfig; + private ScopeConfigInterface $scopeConfig; /** * @var AdyenHelper */ - private $adyenHelper; + private AdyenHelper $adyenHelper; /** - * @var Config + * @var DefaultConfigProvider */ - private $adyenConfigHelper; + private DefaultConfigProvider $defaultConfigProvider; /** * Button constructor. @@ -85,7 +84,7 @@ abstract class AbstractButton extends Template * @param StoreManagerInterface $storeManagerInterface * @param ScopeConfigInterface $scopeConfig * @param AdyenHelper $adyenHelper - * @param Config $adyenConfigHelper + * @param DefaultConfigProvider $defaultConfigProvider * @param array $data */ public function __construct( @@ -97,7 +96,7 @@ public function __construct( StoreManagerInterface $storeManagerInterface, ScopeConfigInterface $scopeConfig, AdyenHelper $adyenHelper, - Config $adyenConfigHelper, + DefaultConfigProvider $defaultConfigProvider, array $data = [] ) { parent::__construct($context, $data); @@ -108,7 +107,7 @@ public function __construct( $this->storeManager = $storeManagerInterface; $this->scopeConfig = $scopeConfig; $this->adyenHelper = $adyenHelper; - $this->adyenConfigHelper = $adyenConfigHelper; + $this->defaultConfigProvider = $defaultConfigProvider; } /** @@ -255,4 +254,78 @@ public function getContainerId(): string { return $this->getData(self::BUTTON_ELEMENT_INDEX) ?: ''; } + + public function getRandomElementId(): string + { + try { + $id = sprintf('%s%s', $this->getContainerId(), random_int(PHP_INT_MIN, PHP_INT_MAX)); + } catch (Exception $e) { + /** + * Exception only thrown if an appropriate source of randomness cannot be found. + * https://www.php.net/manual/en/function.random-int.php + */ + $id = "0"; + } + + return $id; + } + + /** + * Current Quote ID for guests + * + * @return string + * @throws LocalizedException + * @throws NoSuchEntityException + */ + public function getQuoteId(): string + { + try { + $config = $this->defaultConfigProvider->getConfig(); + if (!empty($config['quoteData']['entity_id'])) { + return $config['quoteData']['entity_id']; + } + } catch (NoSuchEntityException $e) { + if ($e->getMessage() !== 'No such entity with cartId = ') { + throw $e; + } + } + return ''; + } + + /** + * Returns Adyen payment method variant + * + * @return string + */ + public function getPaymentMethodVariant(): string + { + return static::PAYMENT_METHOD_VARIANT; + } + + /** + * Returns the base configuration for express frontend + * + * @return array[] + * @throws LocalizedException + * @throws NoSuchEntityException + */ + public function buildConfiguration(): array + { + $variant = $this->getPaymentMethodVariant(); + + return [ + "Adyen_ExpressCheckout/js/$variant/button" => [ + 'actionSuccess' => $this->getActionSuccess(), + 'storeCode' => $this->getStorecode(), + 'countryCode' => $this->getDefaultCountryCode(), + 'currency' => $this->getCurrency(), + 'merchantAccount' => $this->getMerchantAccount(), + 'format' => $this->getFormat(), + 'locale' => $this->getLocale(), + 'originkey' => $this->getOriginKey(), + 'checkoutenv' => $this->getCheckoutEnvironment(), + 'isProductView' => (bool) $this->getIsProductView() + ] + ]; + } } diff --git a/Block/GooglePay/Shortcut/Button.php b/Block/GooglePay/Shortcut/Button.php index 0b0e4be5..e5900e27 100644 --- a/Block/GooglePay/Shortcut/Button.php +++ b/Block/GooglePay/Shortcut/Button.php @@ -13,111 +13,10 @@ namespace Adyen\ExpressCheckout\Block\GooglePay\Shortcut; -use Adyen\Payment\Helper\Data as AdyenHelper; -use Adyen\Payment\Helper\Config as AdyenConfigHelper; use Adyen\ExpressCheckout\Block\Buttons\AbstractButton; -use Magento\Checkout\Model\Session; use Magento\Catalog\Block\ShortcutInterface; -use Magento\Checkout\Model\DefaultConfigProvider; -use Magento\Customer\Model\Session as CustomerSession; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\UrlInterface; -use Magento\Framework\View\Element\Template\Context; -use Magento\Payment\Model\MethodInterface; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Store\Model\StoreManagerInterface; class Button extends AbstractButton implements ShortcutInterface { - /** - * @var DefaultConfigProvider $defaultConfigProvider - */ - private $defaultConfigProvider; - - /** - * @var UrlInterface $url - */ - private $url; - - /** - * @var CustomerSession $customerSession - */ - private $customerSession; - - /** - * @var StoreManagerInterface $storeManager - */ - private $storeManager; - - /** - * @var ScopeConfigInterface $scopeConfig - */ - private $scopeConfig; - - /** - * Button constructor - * - * @param Context $context - * @param Session $checkoutSession - * @param MethodInterface $payment - * @param UrlInterface $url - * @param CustomerSession $customerSession - * @param StoreManagerInterface $storeManagerInterface - * @param DefaultConfigProvider $defaultConfigProvider - * @param ScopeConfigInterface $scopeConfig - * @param AdyenHelper $adyenHelper - * @param AdyenConfigHelper $adyenConfigHelper - * @param array $data - */ - public function __construct( - Context $context, - Session $checkoutSession, - MethodInterface $payment, - UrlInterface $url, - CustomerSession $customerSession, - StoreManagerInterface $storeManagerInterface, - DefaultConfigProvider $defaultConfigProvider, - ScopeConfigInterface $scopeConfig, - AdyenHelper $adyenHelper, - AdyenConfigHelper $adyenConfigHelper, - array $data = [] - ) { - parent::__construct( - $context, - $checkoutSession, - $payment, - $url, - $customerSession, - $storeManagerInterface, - $scopeConfig, - $adyenHelper, - $adyenConfigHelper, - $data - ); - $this->defaultConfigProvider = $defaultConfigProvider; - } - - /** - * Current Quote ID for guests - * - * @return string - * @throws LocalizedException - * @throws NoSuchEntityException - */ - public function getQuoteId(): string - { - try { - $config = $this->defaultConfigProvider->getConfig(); - if (!empty($config['quoteData']['entity_id'])) { - return $config['quoteData']['entity_id']; - } - } catch (NoSuchEntityException $e) { - if ($e->getMessage() !== 'No such entity with cartId = ') { - throw $e; - } - } - return ''; - } + const PAYMENT_METHOD_VARIANT = 'googlepay'; } diff --git a/Block/Paypal/Shortcut/Button.php b/Block/Paypal/Shortcut/Button.php new file mode 100644 index 00000000..e1e0add4 --- /dev/null +++ b/Block/Paypal/Shortcut/Button.php @@ -0,0 +1,22 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Block\Paypal\Shortcut; + +use Adyen\ExpressCheckout\Block\Buttons\AbstractButton; +use Magento\Catalog\Block\ShortcutInterface; + +class Button extends AbstractButton implements ShortcutInterface +{ + const PAYMENT_METHOD_VARIANT = 'paypal_express'; +} diff --git a/Helper/PaypalUpdateOrder.php b/Helper/PaypalUpdateOrder.php new file mode 100644 index 00000000..8e6ab060 --- /dev/null +++ b/Helper/PaypalUpdateOrder.php @@ -0,0 +1,118 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Helper; + +use Adyen\Model\Checkout\Amount; +use Adyen\Model\Checkout\DeliveryMethod; +use Adyen\Model\Checkout\PaypalUpdateOrderRequest; +use Adyen\Model\Checkout\PaypalUpdateOrderResponse; +use Adyen\Model\Checkout\TaxTotal; +use Adyen\Payment\Helper\Data; +use Adyen\Service\Checkout\UtilityApi; +use Adyen\AdyenException; +use Magento\Framework\Exception\NoSuchEntityException; + +class PaypalUpdateOrder +{ + /** + * @var Data + */ + private Data $adyenHelper; + + /** + * @param Data $adyenHelper + */ + public function __construct( + Data $adyenHelper + ) { + $this->adyenHelper = $adyenHelper; + } + + /** + * Creates and returns service class for Adyen Utility API + * + * @param $storeId + * @return UtilityApi + * @throws AdyenException + * @throws NoSuchEntityException + */ + public function createAdyenUtilityApiService($storeId): UtilityApi + { + return new UtilityApi($this->adyenHelper->initializeAdyenClient($storeId)); + } + + /** + * Builds the request object for /paypal/updateOrder endpoint of Adyen Checkout API + * + * @param string $pspReference + * @param string $paymentData + * @param int $amountValue + * @param string $amountCurrency + * @param array $deliveryMethods + * @return PaypalUpdateOrderRequest + */ + public function buildPaypalUpdateOrderRequest( + string $pspReference, + string $paymentData, + int $amountValue, + int $taxAmount, + string $amountCurrency, + array $deliveryMethods = [] + ): PaypalUpdateOrderRequest { + $amount = new Amount(); + $amount->setValue($amountValue); + $amount->setCurrency($amountCurrency); + + $taxTotalAmount = new Amount(); + $taxTotalAmount->setValue($taxAmount); + $taxTotalAmount->setCurrency($amountCurrency); + + $taxTotal = new TaxTotal(); + $taxTotal->setAmount($taxTotalAmount); + + $paypalUpdateOrderRequest = new PaypalUpdateOrderRequest(); + + $paypalUpdateOrderRequest->setPspReference($pspReference); + $paypalUpdateOrderRequest->setPaymentData($paymentData); + $paypalUpdateOrderRequest->setAmount($amount); + $paypalUpdateOrderRequest->setTaxTotal($taxTotal); + + if (!empty($deliveryMethods)) { + $deliveryMethodsObjectArray = []; + + foreach ($deliveryMethods as $deliveryMethod) { + $deliveryMethodObject = new DeliveryMethod($deliveryMethod); + $deliveryMethodsObjectArray[] = $deliveryMethodObject; + } + + $paypalUpdateOrderRequest->setDeliveryMethods($deliveryMethodsObjectArray); + } + + return $paypalUpdateOrderRequest; + } + + /** + * Returns paypal/updateOrder response in a structured array. + * + * @param PaypalUpdateOrderResponse $paypalUpdateOrderResponse + * @return array + */ + public function handlePaypalUpdateOrderResponse(PaypalUpdateOrderResponse $paypalUpdateOrderResponse): array + { + return [ + 'status' => $paypalUpdateOrderResponse->getStatus(), + 'paymentData' => $paypalUpdateOrderResponse->getPaymentData() + ]; + } +} diff --git a/Helper/Util/PaypalDeliveryMethodValidator.php b/Helper/Util/PaypalDeliveryMethodValidator.php new file mode 100644 index 00000000..18a9c800 --- /dev/null +++ b/Helper/Util/PaypalDeliveryMethodValidator.php @@ -0,0 +1,31 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Helper\Util; + +use Adyen\Payment\Helper\Util\DataArrayValidator; + +class PaypalDeliveryMethodValidator implements PaypalDeliveryMethodValidatorInterface +{ + public function getValidatedDeliveryMethod(array $deliveryMethod): array + { + if (!empty($deliveryMethod)) { + $deliveryMethod = DataArrayValidator::getArrayOnlyWithApprovedKeys( + $deliveryMethod, + self::DELIVERY_METHOD_FIELDS + ); + } + + return $deliveryMethod; + } +} diff --git a/Helper/Util/PaypalDeliveryMethodValidatorInterface.php b/Helper/Util/PaypalDeliveryMethodValidatorInterface.php new file mode 100644 index 00000000..69720cd5 --- /dev/null +++ b/Helper/Util/PaypalDeliveryMethodValidatorInterface.php @@ -0,0 +1,39 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Helper\Util; + +interface PaypalDeliveryMethodValidatorInterface +{ + const DELIVERY_METHOD_FIELD_REFERENCE = 'reference'; + const DELIVERY_METHOD_FIELD_DESCRIPTION = 'description'; + const DELIVERY_METHOD_FIELD_TYPE = 'type'; + const DELIVERY_METHOD_FIELD_AMOUNT = 'amount'; + const DELIVERY_METHOD_FIELD_SELECTED = 'selected'; + + const DELIVERY_METHOD_FIELDS = [ + self::DELIVERY_METHOD_FIELD_REFERENCE, + self::DELIVERY_METHOD_FIELD_DESCRIPTION, + self::DELIVERY_METHOD_FIELD_TYPE, + self::DELIVERY_METHOD_FIELD_AMOUNT, + self::DELIVERY_METHOD_FIELD_SELECTED + ]; + + /** + * Validates and clean-up the invalid data from PayPal's delivery methods and returns a valid array. + * + * @param array $deliveryMethod + * @return array + */ + public function getValidatedDeliveryMethod(array $deliveryMethod): array; +} diff --git a/Model/AdyenInitPayments.php b/Model/AdyenInitPayments.php new file mode 100644 index 00000000..08c18c34 --- /dev/null +++ b/Model/AdyenInitPayments.php @@ -0,0 +1,203 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Model; + +use Adyen\ExpressCheckout\Api\AdyenInitPaymentsInterface; +use Adyen\Payment\Gateway\Http\Client\TransactionPayment; +use Adyen\Payment\Gateway\Http\TransferFactory; +use Adyen\Payment\Helper\Config; +use Adyen\Payment\Helper\Data; +use Adyen\Payment\Helper\PaymentResponseHandler; +use Adyen\Payment\Helper\ReturnUrlHelper; +use Adyen\Payment\Helper\Util\CheckoutStateDataValidator; +use Exception; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\ValidatorException; +use Magento\Payment\Gateway\Http\ClientException; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; + +class AdyenInitPayments implements AdyenInitPaymentsInterface +{ + /** + * @var CartRepositoryInterface + */ + private CartRepositoryInterface $cartRepository; + + /** + * @var Config + */ + private Config $configHelper; + + /** + * @var ReturnUrlHelper + */ + private ReturnUrlHelper $returnUrlHelper; + + /** + * @var CheckoutStateDataValidator + */ + private CheckoutStateDataValidator $checkoutStateDataValidator; + + /** + * @var TransferFactory + */ + private TransferFactory $transferFactory; + + /** + * @var TransactionPayment + */ + private TransactionPayment $transactionPaymentClient; + + /** + * @var Data + */ + private Data $adyenHelper; + + /** + * @var PaymentResponseHandler + */ + private PaymentResponseHandler $paymentResponseHandler; + + /** + * @var QuoteIdMaskFactory + */ + private QuoteIdMaskFactory $quoteIdMaskFactory; + + /** + * @param CartRepositoryInterface $cartRepository + * @param Config $configHelper + * @param ReturnUrlHelper $returnUrlHelper + * @param CheckoutStateDataValidator $checkoutStateDataValidator + * @param TransferFactory $transferFactory + * @param TransactionPayment $transactionPaymentClient + * @param Data $adyenHelper + * @param PaymentResponseHandler $paymentResponseHandler + * @param QuoteIdMaskFactory $quoteIdMaskFactory + */ + public function __construct( + CartRepositoryInterface $cartRepository, + Config $configHelper, + ReturnUrlHelper $returnUrlHelper, + CheckoutStateDataValidator $checkoutStateDataValidator, + TransferFactory $transferFactory, + TransactionPayment $transactionPaymentClient, + Data $adyenHelper, + PaymentResponseHandler $paymentResponseHandler, + QuoteIdMaskFactory $quoteIdMaskFactory + ) { + $this->cartRepository = $cartRepository; + $this->configHelper = $configHelper; + $this->returnUrlHelper = $returnUrlHelper; + $this->checkoutStateDataValidator = $checkoutStateDataValidator; + $this->transferFactory = $transferFactory; + $this->transactionPaymentClient = $transactionPaymentClient; + $this->adyenHelper = $adyenHelper; + $this->paymentResponseHandler = $paymentResponseHandler; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + } + + /** + * @param string $stateData + * @param int|null $adyenCartId + * @param string|null $adyenMaskedQuoteId + * @return string + * @throws ClientException + * @throws NoSuchEntityException + * @throws ValidatorException + * @throws LocalizedException + */ + public function execute( + string $stateData, + ?int $adyenCartId = null, + ?string $adyenMaskedQuoteId = null + ): string { + if (is_null($adyenCartId)) { + /** @var $quoteIdMask QuoteIdMask */ + $quoteIdMask = $this->quoteIdMaskFactory->create()->load( + $adyenMaskedQuoteId, + 'masked_id' + ); + $adyenCartId = (int) $quoteIdMask->getQuoteId(); + } + + $quote = $this->cartRepository->get($adyenCartId); + + // Reserve an order ID for the quote to obtain the reference and save the quote + if (is_null($quote->getReservedOrderId())) { + $quote->reserveOrderId(); + $this->cartRepository->save($quote); + } + + $stateData = json_decode($stateData, true); + // Validate JSON that has just been parsed if it was in a valid format + if (json_last_error() !== JSON_ERROR_NONE) { + throw new ValidatorException( + __('Payments call failed because stateData was not a valid JSON!') + ); + } + // Validate the keys in stateData and remove invalid keys + $stateData = $this->checkoutStateDataValidator->getValidatedAdditionalData($stateData); + $paymentsRequest = $this->buildPaymentsRequest($quote, $stateData); + + $transfer = $this->transferFactory->create([ + 'body' => $paymentsRequest, + 'clientConfig' => ['storeId' => $quote->getStoreId()] + ]); + + try { + $response = $this->transactionPaymentClient->placeRequest($transfer); + return json_encode( + $this->paymentResponseHandler->formatPaymentResponse($response['resultCode'], $response['action']) + ); + } catch (Exception $e) { + throw new ClientException( + __('Error with payment method, please select a different payment method!') + ); + } + } + + /** + * @param Quote $quote + * @param array $stateData + * @return array + */ + protected function buildPaymentsRequest(Quote $quote, array $stateData): array + { + $merchantReference = $quote->getReservedOrderId(); + $storeId = $quote->getStoreId(); + $returnUrl = sprintf( + "%s?merchantReference=%s", + $this->returnUrlHelper->getStoreReturnUrl($storeId), + $merchantReference + ); + $currency = $quote->getQuoteCurrencyCode(); + $amount = $quote->getSubtotalWithDiscount(); + $request = [ + 'amount' => [ + 'currency' => $currency, + 'value' => $this->adyenHelper->formatAmount($amount, $currency) + ], + 'reference' => $merchantReference, + 'returnUrl' => $returnUrl, + 'merchantAccount' => $this->configHelper->getMerchantAccount($storeId), + 'channel' => self::PAYMENT_CHANNEL_WEB + ]; + + return array_merge($request, $stateData); + } +} diff --git a/Model/AdyenPaypalUpdateOrder.php b/Model/AdyenPaypalUpdateOrder.php new file mode 100755 index 00000000..b176d38c --- /dev/null +++ b/Model/AdyenPaypalUpdateOrder.php @@ -0,0 +1,217 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Model; + +use Adyen\AdyenException; +use Adyen\Client; +use Adyen\ExpressCheckout\Api\AdyenPaypalUpdateOrderInterface; +use Adyen\ExpressCheckout\Helper\PaypalUpdateOrder; +use Adyen\ExpressCheckout\Helper\Util\PaypalDeliveryMethodValidator; +use Adyen\ExpressCheckout\Model\ResourceModel\PaymentResponse\Collection as AdyenPaymentResponseCollection; +use Adyen\Payment\Helper\ChargedCurrency; +use Adyen\Payment\Helper\Data; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\ValidatorException; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; + +class AdyenPaypalUpdateOrder implements AdyenPaypalUpdateOrderInterface +{ + /** + * @var PaypalUpdateOrder + */ + protected PaypalUpdateOrder $paypalUpdateOrderHelper; + + /** + * @var CartRepositoryInterface + */ + private CartRepositoryInterface $cartRepository; + + /** + * @var PaypalDeliveryMethodValidator + */ + private PaypalDeliveryMethodValidator $deliveryMethodValidator; + + /** + * @var ChargedCurrency + */ + private ChargedCurrency $chargedCurrency; + + /** + * @var Data + */ + private Data $adyenHelper; + + /** + * @var AdyenPaymentResponseCollection + */ + private AdyenPaymentResponseCollection $paymentResponseCollection; + + /** + * @var QuoteIdMaskFactory + */ + private QuoteIdMaskFactory $quoteIdMaskFactory; + + /** + * @param PaypalUpdateOrder $updatePaypalOrderHelper + * @param CartRepositoryInterface $cartRepository + * @param PaypalDeliveryMethodValidator $deliveryMethodValidator + * @param ChargedCurrency $chargedCurrency + * @param Data $adyenHelper + * @param AdyenPaymentResponseCollection $paymentResponseCollection + * @param QuoteIdMaskFactory $quoteIdMaskFactory + */ + public function __construct( + PaypalUpdateOrder $updatePaypalOrderHelper, + CartRepositoryInterface $cartRepository, + PaypalDeliveryMethodValidator $deliveryMethodValidator, + ChargedCurrency $chargedCurrency, + Data $adyenHelper, + AdyenPaymentResponseCollection $paymentResponseCollection, + QuoteIdMaskFactory $quoteIdMaskFactory + ) { + $this->paypalUpdateOrderHelper = $updatePaypalOrderHelper; + $this->cartRepository = $cartRepository; + $this->deliveryMethodValidator = $deliveryMethodValidator; + $this->chargedCurrency = $chargedCurrency; + $this->adyenHelper = $adyenHelper; + $this->paymentResponseCollection = $paymentResponseCollection; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + } + + /** + * @param string $paymentData + * @param int|null $adyenCartId + * @param string|null $adyenMaskedQuoteId + * @param string $deliveryMethods + * @return string + * @throws NoSuchEntityException + * @throws ValidatorException + */ + public function execute( + string $paymentData, + ?int $adyenCartId = null, + ?string $adyenMaskedQuoteId = null, + string $deliveryMethods = '' + ): string { + if (is_null($adyenCartId)) { + /** @var $quoteIdMask QuoteIdMask */ + $quoteIdMask = $this->quoteIdMaskFactory->create()->load( + $adyenMaskedQuoteId, + 'masked_id' + ); + $adyenCartId = (int) $quoteIdMask->getQuoteId(); + } + + /** @var Quote $quote */ + $quote = $this->cartRepository->get($adyenCartId); + $merchantReference = $quote->getReservedOrderId(); + $deliveryMethods = json_decode($deliveryMethods, true); + + foreach ($deliveryMethods as &$method) { + // Ensure the amount value is an integer + $method['amount']['value'] = (int) $method['amount']['value']; + + // Validate the current method + $validatedMethod = $this->deliveryMethodValidator->getValidatedDeliveryMethod([$method]); + + // Replace the original method with the validated one + if (!empty($validatedMethod)) { + $method = $validatedMethod[0]; + } + } + unset($method); + + // Handle the case where JSON decoding fails + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \InvalidArgumentException('Invalid JSON provided for delivery methods.'); + } + if (is_null($merchantReference)) { + throw new ValidatorException( + __('Order ID has not been reserved!') + ); + } + + $paymentResponse = $this->paymentResponseCollection->getPaymentResponseWithMerchantReference( + $merchantReference + ); + + if (is_null($paymentResponse)) { + throw new ValidatorException( + __('Payment response couldn\'t be found!') + ); + } + + $decodedPaymentResponse = json_decode($paymentResponse['response'], true); + if (!isset($decodedPaymentResponse['pspReference'])) { + throw new ValidatorException( + __('Payment pspreference does not exist in the payment response!') + ); + } else { + $pspReference = $decodedPaymentResponse['pspReference']; + } + + $storeId = $quote->getStoreId(); + $quoteAmountCurrency = $this->chargedCurrency->getQuoteAmountCurrency($quote); + $amountCurrency = $quoteAmountCurrency->getCurrencyCode(); + $amountValue = $this->adyenHelper->formatAmount($quote->getGrandTotal(), $amountCurrency); + + if ($quote->isVirtual()) { + $taxAmount = $quote->getBillingAddress()->getTaxAmount(); + } else { + $taxAmount = $quote->getShippingAddress()->getTaxAmount(); + } + + $formattedTaxAmount = $this->adyenHelper->formatAmount($taxAmount, $amountCurrency); + + try { + $paypalUpdateOrderService = $this->paypalUpdateOrderHelper->createAdyenUtilityApiService($storeId); + $paypalUpdateOrderRequest = $this->paypalUpdateOrderHelper->buildPaypalUpdateOrderRequest( + $pspReference, + $paymentData, + $amountValue, + $formattedTaxAmount, + $amountCurrency, + $deliveryMethods + ); + + $this->adyenHelper->logRequest( + $paypalUpdateOrderRequest->toArray(), + Client::API_CHECKOUT_VERSION, + '/paypal/updateOrder' + ); + + $paypalUpdateOrderResponse = $paypalUpdateOrderService->updatesOrderForPaypalExpressCheckout( + $paypalUpdateOrderRequest + ); + } catch (AdyenException $e) { + $errorResponse['error'] = $e->getMessage(); + $errorResponse['errorCode'] = $e->getAdyenErrorCode(); + + $this->adyenHelper->logResponse($errorResponse); + + throw new ValidatorException( + __('Error with payment method, please select a different payment method.') + ); + } + + $this->adyenHelper->logResponse($paypalUpdateOrderResponse->toArray()); + + return json_encode( + $this->paypalUpdateOrderHelper->handlePaypalUpdateOrderResponse($paypalUpdateOrderResponse) + ); + } +} diff --git a/Model/Configuration.php b/Model/Configuration.php index 5a4695f5..383f2ea6 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -36,26 +36,27 @@ public function __construct( } /** - * Returns configuration value for where to show apple pay - * + * @param string $paymentMethodVariant * @param string $scopeType - * @param null|int|string $scopeCode + * @param $scopeCode * @return array */ - public function getShowApplePayOn( + public function getShowPaymentMethodOn( + string $paymentMethodVariant, string $scopeType = ScopeInterface::SCOPE_STORE, $scopeCode = null ): array { - $value = $this->scopeConfig->getValue( - self::SHOW_APPLE_PAY_ON_CONFIG_PATH, - $scopeType, - $scopeCode + $configPath = sprintf( + "%s/%s_%s/%s", + self::CONFIG_PATH_PAYMENT, + self::CONFIG_PATH_ADYEN_PREFIX, + $paymentMethodVariant, + self::CONFIG_PATH_SHOW_EXPRESS_ON ); - return $value ? - explode( - ',', - $value - ) : []; + + $value = $this->scopeConfig->getValue($configPath, $scopeType, $scopeCode); + + return $value ? explode(',', $value) : []; } /** @@ -63,7 +64,7 @@ public function getShowApplePayOn( */ public function getApplePayButtonColor( string $scopeType = ScopeInterface::SCOPE_STORE, - $scopeCode = null + $scopeCode = null ): string { $value = $this->scopeConfig->getValue( self::APPLE_PAY_BUTTON_COLOR_CONFIG_PATH, @@ -75,6 +76,33 @@ public function getApplePayButtonColor( } /** + * @deprecated use getShowPaymentMethodOn() instead + * + * Returns configuration value for where to show apple pay + * + * @param string $scopeType + * @param null|int|string $scopeCode + * @return array + */ + public function getShowApplePayOn( + string $scopeType = ScopeInterface::SCOPE_STORE, + $scopeCode = null + ): array { + $value = $this->scopeConfig->getValue( + self::SHOW_APPLE_PAY_ON_CONFIG_PATH, + $scopeType, + $scopeCode + ); + return $value ? + explode( + ',', + $value + ) : []; + } + + /** + * @deprecated use getShowPaymentMethodOn() instead + * * Returns configuration value for where to show google pay * * @param string $scopeType diff --git a/Model/ConfigurationInterface.php b/Model/ConfigurationInterface.php index 6539ed97..c2a4cdb7 100644 --- a/Model/ConfigurationInterface.php +++ b/Model/ConfigurationInterface.php @@ -22,20 +22,28 @@ */ interface ConfigurationInterface { + public const CONFIG_PATH_ADYEN_PREFIX = 'adyen'; + public const CONFIG_PATH_PAYMENT = 'payment'; + public const CONFIG_PATH_SHOW_EXPRESS_ON = 'express_show_on'; + public const CONFIG_PATH_EXPRESS_BUTTON_COLOR = 'express_button_color'; + + /** @deprecated */ + public const APPLE_PAY_BUTTON_COLOR_CONFIG_PATH = 'payment/adyen_express/apple_pay_button_color'; + /** @deprecated */ public const SHOW_APPLE_PAY_ON_CONFIG_PATH = 'payment/adyen_express/show_apple_pay_on'; + /** @deprecated */ public const SHOW_GOOGLE_PAY_ON_CONFIG_PATH = 'payment/adyen_express/show_google_pay_on'; - public const APPLE_PAY_BUTTON_COLOR_CONFIG_PATH = 'payment/adyen_express/apple_pay_button_color'; /** - * Returns configuration value for where to show apple pay - * + * @param string $paymentMethodVariant * @param string $scopeType - * @param null|int|string $scopeCode + * @param null $scopeCode * @return array */ - public function getShowApplePayOn( + public function getShowPaymentMethodOn( + string $paymentMethodVariant, string $scopeType = ScopeInterface::SCOPE_STORE, - $scopeCode = null + $scopeCode = null ): array; /** @@ -47,10 +55,26 @@ public function getShowApplePayOn( */ public function getApplePayButtonColor( string $scopeType = ScopeInterface::SCOPE_STORE, - $scopeCode = null + $scopeCode = null ): string; /** + * @deprecated use getShowPaymentMethodOn() instead + * + * Returns configuration value for where to show apple pay + * + * @param string $scopeType + * @param null|int|string $scopeCode + * @return array + */ + public function getShowApplePayOn( + string $scopeType = ScopeInterface::SCOPE_STORE, + $scopeCode = null + ): array; + + /** + * @deprecated use getShowPaymentMethodOn() instead + * * Returns configuration value for where to show google pay * * @param string $scopeType diff --git a/Model/GetAdyenPaymentMethodsByProduct.php b/Model/GetAdyenPaymentMethodsByProduct.php index fe975bb1..307928b4 100644 --- a/Model/GetAdyenPaymentMethodsByProduct.php +++ b/Model/GetAdyenPaymentMethodsByProduct.php @@ -104,7 +104,7 @@ public function execute( "countryCode" => $this->getCurrentCountryCode($store), "shopperLocale" => $this->adyenHelper->getCurrentLocaleCode($store->getId()), "amount" => [ - "value" => (float) $adyenAmountCurrency->getAmount(), + "value" => $this->adyenHelper->formatAmount($adyenAmountCurrency->getAmount(), $currencyCode), "currency" => $currencyCode ] ]; diff --git a/Model/GuestAdyenInitPayments.php b/Model/GuestAdyenInitPayments.php new file mode 100644 index 00000000..d6717147 --- /dev/null +++ b/Model/GuestAdyenInitPayments.php @@ -0,0 +1,75 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Model; + +use Adyen\ExpressCheckout\Api\GuestAdyenInitPaymentsInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\ValidatorException; +use Magento\Payment\Gateway\Http\ClientException; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; + +class GuestAdyenInitPayments implements GuestAdyenInitPaymentsInterface +{ + /** + * @var QuoteIdMaskFactory + */ + private QuoteIdMaskFactory $quoteIdMaskFactory; + + /** + * @var AdyenInitPayments + */ + private AdyenInitPayments $adyenInitPayments; + + /** + * @param QuoteIdMaskFactory $quoteIdMaskFactory + * @param AdyenInitPayments $adyenInitPayments + */ + public function __construct( + QuoteIdMaskFactory $quoteIdMaskFactory, + AdyenInitPayments $adyenInitPayments + ) { + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + $this->adyenInitPayments = $adyenInitPayments; + } + + /** + * @param string $stateData + * @param string|null $guestMaskedId + * @param string|null $adyenMaskedQuoteId + * @return string + * @throws ClientException + * @throws NoSuchEntityException + * @throws ValidatorException + * @throws LocalizedException + */ + public function execute( + string $stateData, + ?string $guestMaskedId = null, + ?string $adyenMaskedQuoteId = null + ): string { + $quoteId = null; + if ($guestMaskedId !== null) { + /** @var $quoteIdMask QuoteIdMask */ + $quoteIdMask = $this->quoteIdMaskFactory->create()->load( + $guestMaskedId, + 'masked_id' + ); + $quoteId = (int) $quoteIdMask->getQuoteId(); + } + + return $this->adyenInitPayments->execute($stateData, $quoteId, $adyenMaskedQuoteId); + } +} diff --git a/Model/GuestAdyenPaypalUpdateOrder.php b/Model/GuestAdyenPaypalUpdateOrder.php new file mode 100644 index 00000000..ef0a4763 --- /dev/null +++ b/Model/GuestAdyenPaypalUpdateOrder.php @@ -0,0 +1,63 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Model; + +use Adyen\ExpressCheckout\Api\AdyenPaypalUpdateOrderInterface; +use Adyen\ExpressCheckout\Api\GuestAdyenPaypalUpdateOrderInterface; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; + +class GuestAdyenPaypalUpdateOrder implements GuestAdyenPaypalUpdateOrderInterface +{ + /** + * @var QuoteIdMaskFactory + */ + private QuoteIdMaskFactory $quoteIdMaskFactory; + + /** + * @var AdyenPaypalUpdateOrder + */ + private AdyenPaypalUpdateOrder $adyenUpdatePaypalOrder; + + /** + * @param QuoteIdMaskFactory $quoteIdMaskFactory + * @param AdyenPaypalUpdateOrderInterface $adyenUpdatePaypalOrder + */ + public function __construct( + QuoteIdMaskFactory $quoteIdMaskFactory, + AdyenPaypalUpdateOrderInterface $adyenUpdatePaypalOrder + ) { + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + $this->adyenUpdatePaypalOrder = $adyenUpdatePaypalOrder; + } + + public function execute( + string $paymentData, + ?string $guestMaskedId = null, + ?string $adyenMaskedQuoteId = null, + string $deliveryMethods = '' + ): string { + $quoteId = null; + if ($guestMaskedId !== null) { + /** @var $quoteIdMask QuoteIdMask */ + $quoteIdMask = $this->quoteIdMaskFactory->create()->load( + $guestMaskedId, + 'masked_id' + ); + $quoteId = (int) $quoteIdMask->getQuoteId(); + } + + return $this->adyenUpdatePaypalOrder->execute($paymentData, $quoteId, $adyenMaskedQuoteId, $deliveryMethods); + } +} diff --git a/Model/ResourceModel/PaymentResponse/Collection.php b/Model/ResourceModel/PaymentResponse/Collection.php new file mode 100644 index 00000000..289cc0a5 --- /dev/null +++ b/Model/ResourceModel/PaymentResponse/Collection.php @@ -0,0 +1,30 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Model\ResourceModel\PaymentResponse; + +use Adyen\Payment\Model\ResourceModel\PaymentResponse\Collection as AdyenPaymentResponseCollection; + +class Collection extends AdyenPaymentResponseCollection +{ + /** + * Fetch the payment response for the merchant reference supplied + * + * @param string $merchantReference + * @return array|null + */ + public function getPaymentResponseWithMerchantReference(string $merchantReference): ?array + { + return $this->addFieldToFilter('merchant_reference', $merchantReference)->getLastItem()->getData(); + } +} diff --git a/Observer/AbstractPaymentMethodShortcuts.php b/Observer/AbstractPaymentMethodShortcuts.php new file mode 100644 index 00000000..36b9b914 --- /dev/null +++ b/Observer/AbstractPaymentMethodShortcuts.php @@ -0,0 +1,111 @@ + + */ +namespace Adyen\ExpressCheckout\Observer; + +use Adyen\ExpressCheckout\Model\Config\Source\ShortcutAreas; +use Adyen\ExpressCheckout\Model\ConfigurationInterface; +use Magento\Catalog\Block\ShortcutButtons; +use Magento\Catalog\Block\ShortcutInterface; +use Magento\Checkout\Block\QuoteShortcutButtons; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\LocalizedException; + +abstract class AbstractPaymentMethodShortcuts implements ObserverInterface +{ + /** + * @var ConfigurationInterface + */ + private ConfigurationInterface $configuration; + + /** + * @var ShortcutInterface + */ + private ShortcutInterface $shortcutButton; + + /** + * @param ConfigurationInterface $configuration + * @param ShortcutInterface $shortcutButton + */ + public function __construct( + ConfigurationInterface $configuration, + ShortcutInterface $shortcutButton + ) { + $this->configuration = $configuration; + $this->shortcutButton = $shortcutButton; + } + + /** + * @param Observer $observer + * @return void + * @throws LocalizedException + */ + public function execute(Observer $observer): void + { + $currentPageIdentifier = $this->getCurrentPageIdentifier($observer); + $showPaymentMethodOn = $this->configuration->getShowPaymentMethodOn( + $this->shortcutButton->getPaymentMethodVariant() + ); + if (!in_array( + $currentPageIdentifier, + $showPaymentMethodOn + )) { + return; + } + /** @var ShortcutButtons $shortcutButtons */ + $shortcutButtons = $observer->getEvent()->getContainer(); + $shortcut = $shortcutButtons->getLayout()->createBlock($this->shortcutButton::class); + $isProductView = false; + $isCart = false; + $handles = $shortcutButtons->getLayout()->getUpdate()->getHandles(); + + // Check if any of the layout handles indicate a product page or cart page + if (in_array('catalog_product_view', $handles)) { + $isProductView = true; + } + elseif(in_array('checkout_cart_index', $handles)) { + $isCart = true; + } + $shortcut->setIsProductView($isProductView); + $shortcut->setIsCart($isCart); + $shortcutButtons->addShortcut($shortcut); + } + + /** + * Return current page identifier to compare with config values + * + * @param Observer $observer + * @return int + */ + private function getCurrentPageIdentifier( + Observer $observer + ): int { + $shortcutsBlock = $observer->getEvent()->getContainer(); + $handles = $shortcutsBlock->getLayout()->getUpdate()->getHandles(); + + //Check MiniCart + if ((bool)$shortcutsBlock->getData('is_minicart') === true) { + return ShortcutAreas::MINICART_VALUE; + } + + //Check Cart Page or PDP + if(in_array('catalog_product_view', $handles)) { + return ShortcutAreas::PRODUCT_VIEW_VALUE; + } + elseif(in_array('checkout_cart_index', $handles)) { + return ShortcutAreas::CART_PAGE_VALUE; + } + else { + return 0; + } + } +} diff --git a/Observer/AddApplePayShortcuts.php b/Observer/AddApplePayShortcuts.php index b39f55a8..921cad93 100644 --- a/Observer/AddApplePayShortcuts.php +++ b/Observer/AddApplePayShortcuts.php @@ -12,73 +12,15 @@ namespace Adyen\ExpressCheckout\Observer; use Adyen\ExpressCheckout\Block\ApplePay\Shortcut\Button; -use Adyen\ExpressCheckout\Model\Config\Source\ShortcutAreas; use Adyen\ExpressCheckout\Model\ConfigurationInterface; -use Magento\Catalog\Block\ShortcutButtons; -use Magento\Checkout\Block\QuoteShortcutButtons; -use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Exception\LocalizedException; -class AddApplePayShortcuts implements ObserverInterface +class AddApplePayShortcuts extends AbstractPaymentMethodShortcuts implements ObserverInterface { - /** - * @var ConfigurationInterface - */ - private $configuration; - - /** - * AddApplePayShortcuts constructor - * - * @param ConfigurationInterface $configuration - */ public function __construct( - ConfigurationInterface $configuration + ConfigurationInterface $configuration, + Button $applepayButton ) { - $this->configuration = $configuration; - } - - /** - * Add apple pay shortcut button - * - * @param Observer $observer - * @return void - * @throws LocalizedException - */ - public function execute(Observer $observer) - { - $currentPageIdentifier = $this->getCurrentPageIdentifier($observer); - if (!in_array( - $currentPageIdentifier, - $this->configuration->getShowApplePayOn() - )) { - return; - } - /** @var ShortcutButtons $shortcutButtons */ - $shortcutButtons = $observer->getEvent()->getContainer(); - $shortcut = $shortcutButtons->getLayout()->createBlock(Button::class); - $shortcut->setIsProductView((bool)$observer->getData('is_catalog_product')); - $shortcut->setIsCart(get_class($shortcutButtons) === QuoteShortcutButtons::class); - $shortcutButtons->addShortcut($shortcut); - } - - /** - * Return current page identifier to compare with config values - * - * @param Observer $observer - * @return int - */ - private function getCurrentPageIdentifier( - Observer $observer - ): int { - if ($observer->getData('is_catalog_product')) { - return ShortcutAreas::PRODUCT_VIEW_VALUE; - } - $shortcutsBlock = $observer->getEvent()->getContainer(); - $isMinicart = (bool) $shortcutsBlock->getData('is_minicart'); - if ($isMinicart === true) { - return ShortcutAreas::MINICART_VALUE; - } - return ShortcutAreas::CART_PAGE_VALUE; + parent::__construct($configuration, $applepayButton); } } diff --git a/Observer/AddGooglePayShortcuts.php b/Observer/AddGooglePayShortcuts.php index c7d72b7f..33571bca 100644 --- a/Observer/AddGooglePayShortcuts.php +++ b/Observer/AddGooglePayShortcuts.php @@ -12,73 +12,15 @@ namespace Adyen\ExpressCheckout\Observer; use Adyen\ExpressCheckout\Block\GooglePay\Shortcut\Button; -use Adyen\ExpressCheckout\Model\Config\Source\ShortcutAreas; use Adyen\ExpressCheckout\Model\ConfigurationInterface; -use Magento\Catalog\Block\ShortcutButtons; -use Magento\Checkout\Block\QuoteShortcutButtons; -use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Exception\LocalizedException; -class AddGooglePayShortcuts implements ObserverInterface +class AddGooglePayShortcuts extends AbstractPaymentMethodShortcuts implements ObserverInterface { - /** - * @var ConfigurationInterface - */ - private $configuration; - - /** - * AddGooglePayShortcuts constructor - * - * @param ConfigurationInterface $configuration - */ public function __construct( - ConfigurationInterface $configuration + ConfigurationInterface $configuration, + Button $googlepayButton ) { - $this->configuration = $configuration; - } - - /** - * Add google pay shortcut button - * - * @param Observer $observer - * @return void - * @throws LocalizedException - */ - public function execute(Observer $observer) - { - $currentPageIdentifier = $this->getCurrentPageIdentifier($observer); - if (!in_array( - $currentPageIdentifier, - $this->configuration->getShowGooglePayOn() - )) { - return; - } - /** @var ShortcutButtons $shortcutButtons */ - $shortcutButtons = $observer->getEvent()->getContainer(); - $shortcut = $shortcutButtons->getLayout()->createBlock(Button::class); - $shortcut->setIsProductView((bool)$observer->getData('is_catalog_product')); - $shortcut->setIsCart(get_class($shortcutButtons) === QuoteShortcutButtons::class); - $shortcutButtons->addShortcut($shortcut); - } - - /** - * Return current page identifier to compare with config values - * - * @param Observer $observer - * @return int - */ - private function getCurrentPageIdentifier( - Observer $observer - ): int { - if ($observer->getData('is_catalog_product')) { - return ShortcutAreas::PRODUCT_VIEW_VALUE; - } - $shortcutsBlock = $observer->getEvent()->getContainer(); - $isMinicart = (bool) $shortcutsBlock->getData('is_minicart'); - if ($isMinicart === true) { - return ShortcutAreas::MINICART_VALUE; - } - return ShortcutAreas::CART_PAGE_VALUE; + parent::__construct($configuration, $googlepayButton); } } diff --git a/Observer/AddPaypalShortcuts.php b/Observer/AddPaypalShortcuts.php new file mode 100644 index 00000000..84eb8cfe --- /dev/null +++ b/Observer/AddPaypalShortcuts.php @@ -0,0 +1,26 @@ + + */ +namespace Adyen\ExpressCheckout\Observer; + +use Adyen\ExpressCheckout\Block\Paypal\Shortcut\Button; +use Adyen\ExpressCheckout\Model\ConfigurationInterface; +use Magento\Framework\Event\ObserverInterface; + +class AddPaypalShortcuts extends AbstractPaymentMethodShortcuts implements ObserverInterface +{ + public function __construct( + ConfigurationInterface $configuration, + Button $paypalButton + ) { + parent::__construct($configuration, $paypalButton); + } +} diff --git a/Observer/SubmitQuoteObserver.php b/Observer/SubmitQuoteObserver.php new file mode 100644 index 00000000..d1880067 --- /dev/null +++ b/Observer/SubmitQuoteObserver.php @@ -0,0 +1,42 @@ + + */ + +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Observer; + +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Event\Observer; +use Magento\Sales\Model\Order; + +class SubmitQuoteObserver implements ObserverInterface +{ + /** + * Execute method for the observer. + * + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer) + { + $order = $observer->getEvent()->getOrder(); + $payment = $order->getPayment(); + + if ($payment->getMethod() == 'adyen_paypal_express') { + $payment->setMethod('adyen_paypal'); + } + + $order->setState(Order::STATE_NEW); + $order->setStatus($order->getConfig()->getStateDefaultStatus(Order:: STATE_NEW)); + $order->save(); + } +} diff --git a/Plugin/Gateway/Request/CheckoutDataBuilder.php b/Plugin/Gateway/Request/CheckoutDataBuilder.php index a5006b0a..1876d5f4 100644 --- a/Plugin/Gateway/Request/CheckoutDataBuilder.php +++ b/Plugin/Gateway/Request/CheckoutDataBuilder.php @@ -51,8 +51,8 @@ public function afterBuild( /** @var PaymentDataObject $paymentDataObject */ $paymentDataObject = SubjectReader::readPayment($buildSubject); $payment = $paymentDataObject->getPayment(); - $isAppleOrGooglePay = $this->isExpressMethodResolver->execute($payment); - if ($isAppleOrGooglePay === true) { + $isExpressMethod = $this->isExpressMethodResolver->execute($payment); + if ($isExpressMethod === true) { $paymentMethodStateData = $paymentAdditionalInfo['stateData']['paymentMethod'] ?? []; $result['body']['paymentMethod'] = $paymentMethodStateData; } diff --git a/Setup/Patch/Abstract/AbstractConfigurationPathPatcher.php b/Setup/Patch/Abstract/AbstractConfigurationPathPatcher.php new file mode 100644 index 00000000..e1b146a5 --- /dev/null +++ b/Setup/Patch/Abstract/AbstractConfigurationPathPatcher.php @@ -0,0 +1,102 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Setup\Patch\Abstract; + +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +abstract class AbstractConfigurationPathPatcher implements DataPatchInterface +{ + private ModuleDataSetupInterface $moduleDataSetup; + private WriterInterface $configWriter; + private ReinitableConfigInterface $reinitableConfig; + + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + WriterInterface $configWriter, + ReinitableConfigInterface $reinitableConfig + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->configWriter = $configWriter; + $this->reinitableConfig = $reinitableConfig; + } + + public function apply(): void + { + $this->moduleDataSetup->getConnection()->startSetup(); + + foreach (static::REPLACE_CONFIG_PATHS as $oldConfigPath => $newConfigPath) { + $this->updateConfigValue( + $this->moduleDataSetup, + $oldConfigPath, + $newConfigPath + ); + } + + $this->moduleDataSetup->getConnection()->endSetup(); + } + + private function updateConfigValue( + ModuleDataSetupInterface $setup, + string $oldPath, + string $newPath + ): void { + $config = $this->findConfig($setup, $oldPath); + + if ($config !== false) { + $this->configWriter->save( + $newPath, + $config['value'], + $config['scope'], + $config['scope_id'] + ); + } + + $this->reinitableConfig->reinit(); + } + + private function findConfig(ModuleDataSetupInterface $setup, string $path): mixed + { + $configDataTable = $setup->getTable('core_config_data'); + $connection = $setup->getConnection(); + + $select = $connection->select() + ->from($configDataTable) + ->where( + 'path = ?', + $path + ); + + $matchingConfigs = $connection->fetchAll($select); + return reset($matchingConfigs); + } + + /** + * @inheritdoc + */ + public function getAliases(): array + { + return []; + } + + /** + * @inheritdoc + */ + public static function getDependencies(): array + { + return []; + } +} diff --git a/Setup/Patch/Data/SeparateConfigurationFields.php b/Setup/Patch/Data/SeparateConfigurationFields.php new file mode 100644 index 00000000..1540f921 --- /dev/null +++ b/Setup/Patch/Data/SeparateConfigurationFields.php @@ -0,0 +1,36 @@ + + */ +declare(strict_types=1); + +namespace Adyen\ExpressCheckout\Setup\Patch\Data; + +use Adyen\ExpressCheckout\Setup\Patch\Abstract\AbstractConfigurationPathPatcher; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +class SeparateConfigurationFields extends AbstractConfigurationPathPatcher implements DataPatchInterface +{ + const REPLACE_CONFIG_PATHS = [ + 'payment/adyen_express/show_apple_pay_on' => 'payment/adyen_applepay/express_show_on', + 'payment/adyen_express/show_google_pay_on' => 'payment/adyen_googlepay/express_show_on', + 'payment/adyen_express/apple_pay_button_color' => 'payment/adyen_applepay/express_button_color' + ]; + + /** + * @inheritdoc + */ + public static function getDependencies(): array + { + return [ + UpdateExpressDBPaths::class + ]; + } +} diff --git a/Setup/Patch/Data/UpdateExpressDBPaths.php b/Setup/Patch/Data/UpdateExpressDBPaths.php index 294ef895..6053d94c 100644 --- a/Setup/Patch/Data/UpdateExpressDBPaths.php +++ b/Setup/Patch/Data/UpdateExpressDBPaths.php @@ -13,109 +13,14 @@ namespace Adyen\ExpressCheckout\Setup\Patch\Data; -use Magento\Framework\App\Config\ReinitableConfigInterface; -use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; +use Adyen\ExpressCheckout\Setup\Patch\Abstract\AbstractConfigurationPathPatcher; use Magento\Framework\Setup\Patch\DataPatchInterface; -use Magento\Framework\Setup\Patch\PatchVersionInterface; -class UpdateExpressDBPaths implements DataPatchInterface, PatchVersionInterface +class UpdateExpressDBPaths extends AbstractConfigurationPathPatcher implements DataPatchInterface { - private ModuleDataSetupInterface $moduleDataSetup; - private WriterInterface $configWriter; - private ReinitableConfigInterface $reinitableConfig; - - public function __construct( - ModuleDataSetupInterface $moduleDataSetup, - WriterInterface $configWriter, - ReinitableConfigInterface $reinitableConfig - ) { - $this->moduleDataSetup = $moduleDataSetup; - $this->configWriter = $configWriter; - $this->reinitableConfig = $reinitableConfig; - } - - public function apply(): void - { - $this->moduleDataSetup->getConnection()->startSetup(); - - // Update Apple Pay config path - $this->updateConfigValue( - $this->moduleDataSetup, - 'payment/adyen_hpp/show_apple_pay_on', - 'payment/adyen_express/show_apple_pay_on' - ); - - // Update Google Pay config path - $this->updateConfigValue( - $this->moduleDataSetup, - 'payment/adyen_hpp/show_google_pay_on', - 'payment/adyen_express/show_google_pay_on' - ); - - // Update Google Pay config path - $this->updateConfigValue( - $this->moduleDataSetup, - 'payment/adyen_hpp/apple_pay_button_color', - 'payment/adyen_express/apple_pay_button_color' - ); - - $this->moduleDataSetup->getConnection()->endSetup(); - } - - private function updateConfigValue( - ModuleDataSetupInterface $setup, - string $oldPath, - string $newPath - ): void { - $config = $this->findConfig($setup, $oldPath); - - if ($config !== false) { - $this->configWriter->save( - $newPath, - $config['value'], - $config['scope'], - $config['scope_id'] - ); - } - - $this->reinitableConfig->reinit(); - } - - private function findConfig(ModuleDataSetupInterface $setup, string $path): mixed - { - $configDataTable = $setup->getTable('core_config_data'); - $connection = $setup->getConnection(); - - $select = $connection->select() - ->from($configDataTable) - ->where( - 'path = ?', - $path - ); - - $matchingConfigs = $connection->fetchAll($select); - return reset($matchingConfigs); - } - - /** - * @inheritdoc - */ - public function getAliases(): array - { - return []; - } - - /** - * @inheritdoc - */ - public static function getDependencies(): array - { - return []; - } - - public static function getVersion(): string - { - return '2.0.0'; - } + const REPLACE_CONFIG_PATHS = [ + 'payment/adyen_hpp/show_apple_pay_on' => 'payment/adyen_express/show_apple_pay_on', + 'payment/adyen_hpp/show_google_pay_on' => 'payment/adyen_express/show_google_pay_on', + 'payment/adyen_hpp/apple_pay_button_color' => 'payment/adyen_express/apple_pay_button_color' + ]; } diff --git a/etc/adminhtml/system/adyen_online_checkout.xml b/etc/adminhtml/system/adyen_online_checkout.xml index af098a49..d9c373d1 100755 --- a/etc/adminhtml/system/adyen_online_checkout.xml +++ b/etc/adminhtml/system/adyen_online_checkout.xml @@ -16,31 +16,41 @@ Magento\Config\Block\System\Config\Form\Fieldset - + Magento\Config\Block\System\Config\Form\Fieldset - + Adyen\ExpressCheckout\Model\Config\Source\ShortcutAreas 1 - payment/adyen_express/show_google_pay_on + payment/adyen_googlepay/express_show_on Magento\Config\Block\System\Config\Form\Fieldset - + Adyen\ExpressCheckout\Model\Config\Source\ShortcutAreas 1 - payment/adyen_express/show_apple_pay_on + payment/adyen_applepay/express_show_on Adyen\ExpressCheckout\Model\Config\Source\ApplePay\ButtonColor - payment/adyen_express/apple_pay_button_color + payment/adyen_applepay/express_button_color Apple Pay documentation.]]> + + + Magento\Config\Block\System\Config\Form\Fieldset + + + Adyen\ExpressCheckout\Model\Config\Source\ShortcutAreas + 1 + payment/adyen_paypal_express/express_show_on + + diff --git a/etc/config.xml b/etc/config.xml new file mode 100644 index 00000000..011ab661 --- /dev/null +++ b/etc/config.xml @@ -0,0 +1,43 @@ + + + + + + + 0 + AdyenPaymentPaypalExpressFacade + pending + PayPal + 0 + 0 + order + 0 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + adyen-express-payment-method + + + + diff --git a/etc/di.xml b/etc/di.xml index 67990a77..243a907a 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -26,10 +26,15 @@ + + + + + @@ -58,6 +63,7 @@ applepay googlepay paywithgoogle + paypal @@ -68,4 +74,31 @@ + + + adyen_paypal_express + Magento\Payment\Block\Form + Adyen\Payment\Block\Info\PaymentMethodInfo + AdyenPaymentPaypalExpressValueHandlerPool + AdyenPaymentValidatorPool + AdyenPaymentCommandPool + + + + + + AdyenPaymentPaypalExpressConfigValueHandler + + + + + + AdyenPaymentPaypalExpressConfig + + + + + adyen_paypal_express + + diff --git a/etc/events.xml b/etc/events.xml index c52811a6..0093ab2d 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -17,4 +17,7 @@ + + + diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index 860b4d1a..93c4e0c5 100755 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -14,21 +14,31 @@ - Adyen_ExpressCheckout::applepay/shortcut.phtml + Adyen_ExpressCheckout::abstract/shortcut.phtml adyen.applepay.mini-cart adyen-applepay-mini-cart - AdyenPaymentHppFacade + AdyenPaymentApplePayFacade - Adyen_ExpressCheckout::googlepay/shortcut.phtml + Adyen_ExpressCheckout::abstract/shortcut.phtml adyen.googlepay.mini-cart adyen-googlepay-mini-cart - AdyenPaymentHppFacade + AdyenPaymentGooglepayFacade + + + + + + Adyen_ExpressCheckout::abstract/shortcut.phtml + adyen.paypal.mini-cart + adyen-paypal-mini-cart + + AdyenPaymentPaypalExpressFacade diff --git a/etc/frontend/events.xml b/etc/frontend/events.xml index 0c66000f..d6b2b0d3 100644 --- a/etc/frontend/events.xml +++ b/etc/frontend/events.xml @@ -14,5 +14,6 @@ + diff --git a/etc/webapi.xml b/etc/webapi.xml index caf27607..d9b6624c 100644 --- a/etc/webapi.xml +++ b/etc/webapi.xml @@ -59,4 +59,36 @@ + + + + + + + + %adyen_cart_id% + + + + + + + + + + + + + + + + %adyen_cart_id% + + + + + + + + diff --git a/view/frontend/templates/abstract/shortcut.phtml b/view/frontend/templates/abstract/shortcut.phtml new file mode 100644 index 00000000..c035a714 --- /dev/null +++ b/view/frontend/templates/abstract/shortcut.phtml @@ -0,0 +1,15 @@ + + +
+
diff --git a/view/frontend/templates/applepay/shortcut.phtml b/view/frontend/templates/applepay/shortcut.phtml deleted file mode 100644 index cb23ef2a..00000000 --- a/view/frontend/templates/applepay/shortcut.phtml +++ /dev/null @@ -1,36 +0,0 @@ -getContainerId(), random_int(PHP_INT_MIN, PHP_INT_MAX)); -} catch (Exception $e) { - /** - * Exception only thrown if an appropriate source of randomness cannot be found. - * https://www.php.net/manual/en/function.random-int.php - */ - $id = 0; -} - -$config = [ - 'Adyen_ExpressCheckout/js/applepay/button' => [ - 'actionSuccess' => $block->getActionSuccess(), - 'storeCode' => $block->getStorecode(), - 'countryCode' => $block->getDefaultCountryCode(), - 'currency' => $block->getCurrency(), - 'merchantAccount' => $block->getMerchantAccount(), - 'format' => $block->getFormat(), - 'locale' => $block->getLocale(), - 'originkey' => $block->getOriginKey(), - 'checkoutenv' => $block->getCheckoutEnvironment(), - 'isProductView' => (bool) $block->getIsProductView(), - 'buttonColor' => $block->getButtonColor(), - ] -]; -?> - diff --git a/view/frontend/templates/checkout-config.phtml b/view/frontend/templates/checkout-config.phtml index 7edf40e1..4edece6b 100644 --- a/view/frontend/templates/checkout-config.phtml +++ b/view/frontend/templates/checkout-config.phtml @@ -5,8 +5,7 @@ getSerializedCheckoutConfig(); $checkoutConfig = __('window.checkoutConfig = %1;', $serializedCheckoutConfig); -?> -getAdyenData(); +$adyenData = $viewModel->getAdyenData(); $serializedAdyenData = json_encode($adyenData, JSON_HEX_TAG); ?>