diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml
new file mode 100644
index 00000000..f4949b24
--- /dev/null
+++ b/.github/workflows/create-release.yml
@@ -0,0 +1,44 @@
+name: Release
+
+on:
+# Manual run from Github UI (e.g. for when a merged PR labels have changed)
+ workflow_dispatch:
+ inputs:
+ pre-release:
+ required: false
+ type: boolean
+ default: false
+ description: "This release will be labeled as non-production ready"
+ # Publish the current version now, useful if the automated run failed
+ github-release:
+ required: false
+ type: boolean
+ default: false
+ description: "Publish Github release for the current version"
+ # Monitor pull request events
+
+ pull_request:
+ types:
+ - closed
+ branches:
+ - main
+
+jobs:
+ release:
+ permissions:
+ contents: write
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Prepare the next main release
+ uses: Adyen/release-automation-action@v1.3.1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ develop-branch: main
+ version-files: composer.json
+ release-title: Adyen Magento 2 Express Checkout Module
+ pre-release: ${{ inputs.pre-release || false }}
+# For a manual Github release
+ github-release: ${{ inputs.github-release || false }}
+ separator: .pre.beta
\ No newline at end of file
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/VERSION b/VERSION
new file mode 100644
index 00000000..50aea0e7
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+2.1.0
\ No newline at end of file
diff --git a/ViewModel/CheckoutConfig.php b/ViewModel/CheckoutConfig.php
index 3c32c349..cfa85d70 100644
--- a/ViewModel/CheckoutConfig.php
+++ b/ViewModel/CheckoutConfig.php
@@ -14,6 +14,7 @@
namespace Adyen\ExpressCheckout\ViewModel;
use Adyen\Payment\Model\Ui\AdyenGenericConfigProvider;
+use Adyen\Payment\Helper\Data;
use Magento\Framework\DataObject;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Exception\NoSuchEntityException;
@@ -28,6 +29,11 @@ class CheckoutConfig implements ArgumentInterface
*/
private $adyenGenericConfigProvider;
+ /**
+ * @var Data
+ */
+ private $adyenDataHelper;
+
/**
* @var SerializerInterface
*/
@@ -45,17 +51,20 @@ class CheckoutConfig implements ArgumentInterface
/**
* @param AdyenGenericConfigProvider $adyenGenericConfigProvider
+ * @param Data $adyenDataHelper
* @param SerializerInterface $serializer
* @param StoreManagerInterface $storeManager
* @param ManagerInterface $eventManager
*/
public function __construct(
AdyenGenericConfigProvider $adyenGenericConfigProvider,
+ Data $adyenDataHelper,
SerializerInterface $serializer,
StoreManagerInterface $storeManager,
ManagerInterface $eventManager
) {
$this->adyenGenericConfigProvider = $adyenGenericConfigProvider;
+ $this->adyenDataHelper = $adyenDataHelper;
$this->serializer = $serializer;
$this->storeManager = $storeManager;
$this->eventManager = $eventManager;
@@ -95,4 +104,14 @@ private function getStoreCode(): string
{
return $this->storeManager->getStore()->getCode();
}
+
+ /**
+ * Return application info values
+ *
+ * @return array
+ */
+ public function getAdyenData(): array
+ {
+ return $this->adyenDataHelper->buildRequestHeaders();
+ }
}
diff --git a/composer.json b/composer.json
index 77a85e85..2e47c90c 100755
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,7 @@
"name": "adyen/adyen-magento2-expresscheckout",
"description": "Official Adyen Magento2 plugin to add express payment method shortcuts.",
"type": "magento2-module",
- "version": "2.1.0",
+ "version": "2.2.0",
"license": "MIT",
"repositories": [
{
@@ -11,7 +11,7 @@
}
],
"require": {
- "adyen/module-payment": "^9"
+ "adyen/module-payment": "^9.7.1"
},
"require-dev": {
"phpunit/phpunit": "*",
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/module.xml b/etc/module.xml
index b5ae5670..c11fe601 100755
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -11,7 +11,7 @@
*/
-->
-
+
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 075d11e2..4edece6b 100644
--- a/view/frontend/templates/checkout-config.phtml
+++ b/view/frontend/templates/checkout-config.phtml
@@ -5,9 +5,12 @@
getSerializedCheckoutConfig();
$checkoutConfig = __('window.checkoutConfig = %1;', $serializedCheckoutConfig);
+$adyenData = $viewModel->getAdyenData();
+$serializedAdyenData = json_encode($adyenData, JSON_HEX_TAG);
?>
diff --git a/view/frontend/templates/googlepay/shortcut.phtml b/view/frontend/templates/googlepay/shortcut.phtml
deleted file mode 100644
index 06ddd3c2..00000000
--- a/view/frontend/templates/googlepay/shortcut.phtml
+++ /dev/null
@@ -1,34 +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/googlepay/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()
- ]
-];
-?>
-
-
-
diff --git a/view/frontend/web/css/source/_module.less b/view/frontend/web/css/source/_module.less
index 62990517..0b9712ff 100644
--- a/view/frontend/web/css/source/_module.less
+++ b/view/frontend/web/css/source/_module.less
@@ -1,5 +1,5 @@
& when (@media-common = true) {
- .google-pay-button-card .gpay-card-info-container {
+ .googlepay-button-card .gpay-card-info-container {
min-width: auto;
width: 100%;
}
diff --git a/view/frontend/web/js/actions/createOrder.js b/view/frontend/web/js/actions/createOrder.js
new file mode 100644
index 00000000..f4e72668
--- /dev/null
+++ b/view/frontend/web/js/actions/createOrder.js
@@ -0,0 +1,18 @@
+define([
+ 'mage/storage',
+ 'Adyen_ExpressCheckout/js/helpers/getApiUrl'
+], function (storage, getApiUrl) {
+ 'use strict';
+ return function (payload, isProductView) {
+ return storage.put(
+ getApiUrl('order', isProductView),
+ payload,
+ false
+ ).then(function(response) {
+ // Assuming response contains orderId
+ return response;
+ }).catch(function(response) {
+ throw new Error('Failed to place order');
+ });
+ };
+});
diff --git a/view/frontend/web/js/actions/initPayments.js b/view/frontend/web/js/actions/initPayments.js
new file mode 100644
index 00000000..b81dd5f1
--- /dev/null
+++ b/view/frontend/web/js/actions/initPayments.js
@@ -0,0 +1,36 @@
+define([
+ 'mage/storage',
+ 'Adyen_ExpressCheckout/js/helpers/getIsLoggedIn',
+ 'Adyen_ExpressCheckout/js/helpers/getMaskedIdFromCart',
+ 'Adyen_ExpressCheckout/js/model/maskedId'
+], function (
+ storage,
+ getIsLoggedIn,
+ getMaskedIdFromCart,
+ maskedIdModel
+) {
+ 'use strict';
+
+ return function (paymentData, isProductView) {
+ let payload= {
+ stateData: JSON.stringify(paymentData)
+ };
+
+ const url = getIsLoggedIn()
+ ? 'rest/V1/adyen/express/init-payments/mine'
+ : 'rest/V1/adyen/express/init-payments/guest';
+
+ if (isProductView) {
+ payload.adyenMaskedQuoteId = maskedIdModel().getMaskedId();
+ } else {
+ payload.guestMaskedId = getMaskedIdFromCart();
+ }
+
+ return new Promise(function (resolve, reject) {
+ storage.post(
+ url,
+ JSON.stringify(payload)
+ ).done(resolve).fail(reject);
+ });
+ };
+});
diff --git a/view/frontend/web/js/actions/updatePaypalOrder.js b/view/frontend/web/js/actions/updatePaypalOrder.js
new file mode 100755
index 00000000..6d59ac84
--- /dev/null
+++ b/view/frontend/web/js/actions/updatePaypalOrder.js
@@ -0,0 +1,78 @@
+define([
+ 'mage/storage',
+ 'Magento_Checkout/js/model/url-builder',
+ 'Adyen_ExpressCheckout/js/helpers/getIsLoggedIn',
+ 'Adyen_ExpressCheckout/js/model/maskedId',
+ 'Adyen_ExpressCheckout/js/helpers/getMaskedIdFromCart',
+], function (
+ storage,
+ urlBuilder,
+ getIsLoggedIn,
+ maskedIdModel,
+ getMaskedIdFromCart
+) {
+ 'use strict';
+
+ function updateOrder(isProductView, paymentData, shippingMethods, currency, selectedShippingMethod = null) {
+ let updateOrderUrl = getIsLoggedIn()
+ ? urlBuilder.createUrl('/adyen/express/paypal-update-order/mine', {})
+ : urlBuilder.createUrl('/adyen/express/paypal-update-order/guest', {});
+
+ const updateOrderPayload = {
+ paymentData: paymentData
+ };
+
+ if (isProductView) {
+ updateOrderPayload.adyenMaskedQuoteId = maskedIdModel().getMaskedId();
+ } else {
+ updateOrderPayload.guestMaskedId = getMaskedIdFromCart();
+ }
+
+ let deliveryMethods = [];
+ if (selectedShippingMethod) {
+ let method = {
+ reference: selectedShippingMethod.id,
+ description: selectedShippingMethod.label,
+ type: 'Shipping',
+ amount: {
+ currency: currency,
+ value: Math.round(selectedShippingMethod.amount.value * 100)
+ },
+ selected: true
+ };
+ deliveryMethods.push(method);
+ updateOrderPayload.deliveryMethods = JSON.stringify(deliveryMethods);
+ }
+ else {
+ for (let i = 0; i < shippingMethods.length; i++) {
+ let method = {
+ reference: (i + 1).toString(),
+ description: shippingMethods[i].detail,
+ type: 'Shipping',
+ amount: {
+ currency: currency,
+ value: Math.round(shippingMethods[i].amount * 100)
+ },
+ selected: i === 0
+ };
+ // Add method object to array.
+ deliveryMethods.push(method);
+ }
+ updateOrderPayload.deliveryMethods = JSON.stringify(deliveryMethods);
+ }
+
+ return storage.post(
+ updateOrderUrl,
+ JSON.stringify(updateOrderPayload)
+ ).then(function (response) {
+ return response;
+ }).catch(function (error) {
+ console.error('Failed to update PayPal order:', error);
+ throw error;
+ });
+ }
+
+ return {
+ updateOrder: updateOrder
+ };
+});
diff --git a/view/frontend/web/js/applepay/button.js b/view/frontend/web/js/applepay/button.js
index 08a77e8c..325b9fb9 100644
--- a/view/frontend/web/js/applepay/button.js
+++ b/view/frontend/web/js/applepay/button.js
@@ -29,6 +29,7 @@ define([
'Adyen_ExpressCheckout/js/model/countries',
'Adyen_ExpressCheckout/js/model/totals',
'Adyen_ExpressCheckout/js/model/currency',
+ 'Adyen_ExpressCheckout/js/helpers/getCurrentPage',
'Adyen_ExpressCheckout/js/model/virtualQuote'
],
function (
@@ -62,6 +63,7 @@ define([
countriesModel,
totalsModel,
currencyModel,
+ getCurrentPage,
virtualQuoteModel
) {
'use strict';
@@ -95,7 +97,7 @@ define([
setExpressMethods(response);
totalsModel().setTotal(response.totals.grand_total);
- currencyModel().setCurrency(response.totals.quote_currency_code)
+ currencyModel().setCurrency(response.totals.quote_currency_code);
const $priceBox = getPdpPriceBox();
const pdpForm = getPdpForm(element);
@@ -142,13 +144,31 @@ define([
initialiseApplePayComponent: async function (applePaymentMethod, element) {
const config = configModel().getConfig();
+ const adyenData = window.adyenData;
+ let currentPage = getCurrentPage(this.isProductView, element);
+
const adyenCheckoutComponent = await new AdyenCheckout({
locale: config.locale,
- originKey: config.originkey,
environment: config.checkoutenv,
+ analytics: {
+ analyticsData: {
+ applicationInfo: {
+ merchantApplication: {
+ name: adyenData['merchant-application-name'],
+ version: adyenData['merchant-application-version']
+ },
+ externalPlatform: {
+ name: adyenData['external-platform-name'],
+ version: adyenData['external-platform-version']
+ }
+ }
+ }
+ },
risk: {
enabled: false
},
+ isExpress: true,
+ expressPage: currentPage,
clientKey: AdyenConfiguration.getClientKey()
});
const applePayConfiguration = this.getApplePayConfiguration(applePaymentMethod, element);
@@ -249,6 +269,7 @@ define([
: formatAmount(getCartSubtotal() * 100),
currency: currency
},
+ isExpress: true,
supportedNetworks: getSupportedNetworks(),
merchantCapabilities: ['supports3DS'],
requiredShippingContactFields: ['postalAddress', 'name', 'email', 'phone'],
diff --git a/view/frontend/web/js/googlepay/button.js b/view/frontend/web/js/googlepay/button.js
index 9aea73ef..a6eeb6ea 100644
--- a/view/frontend/web/js/googlepay/button.js
+++ b/view/frontend/web/js/googlepay/button.js
@@ -37,7 +37,8 @@ define([
'Adyen_ExpressCheckout/js/model/countries',
'Adyen_ExpressCheckout/js/model/totals',
'Adyen_ExpressCheckout/js/model/currency',
- 'Adyen_ExpressCheckout/js/model/virtualQuote'
+ 'Adyen_ExpressCheckout/js/model/virtualQuote',
+ 'Adyen_ExpressCheckout/js/helpers/getCurrentPage'
],
function (
$,
@@ -78,7 +79,8 @@ define([
countriesModel,
totalsModel,
currencyModel,
- virtualQuoteModel
+ virtualQuoteModel,
+ getCurrentPage
) {
'use strict';
@@ -142,7 +144,7 @@ define([
setExpressMethods(response);
totalsModel().setTotal(response.totals.grand_total);
- currencyModel().setCurrency(response.totals.quote_currency_code)
+ currencyModel().setCurrency(response.totals.quote_currency_code);
const $priceBox = getPdpPriceBox();
const pdpForm = getPdpForm(element);
@@ -171,12 +173,31 @@ define([
initialiseGooglePayComponent: async function (googlePaymentMethod, element) {
const config = configModel().getConfig();
+ const adyenData = window.adyenData;
+ let currentPage = getCurrentPage(this.isProductView, element);
+
this.checkoutComponent = await new AdyenCheckout({
locale: config.locale,
clientKey: config.originkey,
environment: config.checkoutenv,
+ analytics: {
+ analyticsData: {
+ applicationInfo: {
+ merchantApplication: {
+ name: adyenData['merchant-application-name'],
+ version: adyenData['merchant-application-version']
+ },
+ externalPlatform: {
+ name: adyenData['external-platform-name'],
+ version: adyenData['external-platform-version']
+ }
+ }
+ }
+ },
paymentMethodsResponse: getPaymentMethod('googlepay', this.isProductView),
onAdditionalDetails: this.handleOnAdditionalDetails.bind(this),
+ isExpress: true,
+ expressPage: currentPage,
risk: {
enabled: false
}
@@ -256,6 +277,7 @@ define([
format: 'FULL',
phoneNumberRequired: true
},
+ isExpress: true,
callbackIntents: !isVirtual ? ['SHIPPING_ADDRESS', 'SHIPPING_OPTION'] : ['OFFER'],
transactionInfo: {
totalPriceStatus: 'ESTIMATED',
diff --git a/view/frontend/web/js/helpers/getCurrentPage.js b/view/frontend/web/js/helpers/getCurrentPage.js
new file mode 100644
index 00000000..e86a7289
--- /dev/null
+++ b/view/frontend/web/js/helpers/getCurrentPage.js
@@ -0,0 +1,19 @@
+define(['jquery',], function ($) {
+ 'use strict';
+
+ return function (isProductView, element) {
+ let currentPage = '';
+
+ if (isProductView) {
+ currentPage = 'pdp';
+ } else if ($(element).closest('.minicart-wrapper').length > 0) {
+ currentPage = 'minicart';
+ } else if ($('.cart-container').length > 0) {
+ currentPage = 'cart';
+ } else if ($('body').hasClass('checkout-index-index')) {
+ currentPage = 'checkout';
+ }
+
+ return currentPage;
+ };
+});
diff --git a/view/frontend/web/js/helpers/getPaypalStyles.js b/view/frontend/web/js/helpers/getPaypalStyles.js
new file mode 100644
index 00000000..2c59d563
--- /dev/null
+++ b/view/frontend/web/js/helpers/getPaypalStyles.js
@@ -0,0 +1,11 @@
+define(function () {
+ 'use strict';
+
+ return function () {
+ // Default styles that can be overridden by themes.
+ return {
+ buttonColor: 'black',
+ buttonType: 'long'
+ };
+ };
+});
diff --git a/view/frontend/web/js/model/adyen-payment-service.js b/view/frontend/web/js/model/adyen-payment-service.js
new file mode 100644
index 00000000..e23d8ee2
--- /dev/null
+++ b/view/frontend/web/js/model/adyen-payment-service.js
@@ -0,0 +1,55 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+define(
+ [
+ 'jquery',
+ 'underscore',
+ 'Magento_Checkout/js/model/quote',
+ 'Magento_Checkout/js/model/url-builder',
+ 'Adyen_ExpressCheckout/js/helpers/getIsLoggedIn',
+ 'mage/storage'
+ ],
+ function(
+ $,
+ _,
+ quote,
+ urlBuilder,
+ getIsLoggedIn,
+ storage
+ ){
+ 'use strict';
+
+ function paymentDetails(data, orderId, quoteId = null) {
+ let serviceUrl;
+ let payload = {
+ 'payload': JSON.stringify(data),
+ 'orderId': orderId,
+ 'quoteId': quoteId
+ };
+ const isLoggedIn = getIsLoggedIn();
+ if (isLoggedIn) {
+ serviceUrl = urlBuilder.createUrl(
+ '/adyen/carts/mine/payments-details',
+ {}
+ );
+ } else {
+ serviceUrl = urlBuilder.createUrl(
+ '/adyen/guest-carts/:cartId/payments-details', {
+ cartId: quoteId ?? quote.getQuoteId()
+ }
+ );
+ }
+
+ return storage.post(
+ serviceUrl,
+ JSON.stringify(payload),
+ true
+ );
+ }
+ return {
+ paymentDetails: paymentDetails
+ };
+ }
+);
diff --git a/view/frontend/web/js/paypal_express/button.js b/view/frontend/web/js/paypal_express/button.js
new file mode 100755
index 00000000..cbff53cb
--- /dev/null
+++ b/view/frontend/web/js/paypal_express/button.js
@@ -0,0 +1,640 @@
+define([
+ 'uiComponent',
+ 'mage/translate',
+ 'Magento_Customer/js/customer-data',
+ 'Adyen_Payment/js/model/adyen-configuration',
+ 'Adyen_Payment/js/adyen',
+ 'Adyen_ExpressCheckout/js/actions/activateCart',
+ 'Adyen_ExpressCheckout/js/actions/createOrder',
+ 'Adyen_ExpressCheckout/js/actions/getShippingMethods',
+ 'Adyen_ExpressCheckout/js/actions/getExpressMethods',
+ 'Adyen_ExpressCheckout/js/actions/setShippingInformation',
+ 'Adyen_ExpressCheckout/js/actions/setTotalsInfo',
+ 'Adyen_ExpressCheckout/js/helpers/formatAmount',
+ 'Adyen_ExpressCheckout/js/helpers/getPaypalStyles',
+ 'Adyen_ExpressCheckout/js/helpers/getCartSubtotal',
+ 'Adyen_ExpressCheckout/js/helpers/getPaymentMethod',
+ 'Adyen_ExpressCheckout/js/helpers/getPdpForm',
+ 'Adyen_ExpressCheckout/js/helpers/getPdpPriceBox',
+ 'Adyen_ExpressCheckout/js/helpers/isConfigSet',
+ 'Adyen_ExpressCheckout/js/helpers/getRegionId',
+ 'Adyen_ExpressCheckout/js/helpers/redirectToSuccess',
+ 'Adyen_ExpressCheckout/js/helpers/setExpressMethods',
+ 'Adyen_ExpressCheckout/js/helpers/validatePdpForm',
+ 'Adyen_ExpressCheckout/js/model/config',
+ 'Adyen_ExpressCheckout/js/model/countries',
+ 'Adyen_ExpressCheckout/js/model/totals',
+ 'Adyen_ExpressCheckout/js/model/currency',
+ 'knockout',
+ 'Magento_Customer/js/model/customer',
+ 'Magento_Checkout/js/model/quote',
+ 'Adyen_ExpressCheckout/js/actions/initPayments',
+ 'Adyen_ExpressCheckout/js/actions/updatePaypalOrder',
+ 'Adyen_ExpressCheckout/js/actions/setBillingAddress',
+ 'Magento_Checkout/js/model/full-screen-loader',
+ 'Adyen_ExpressCheckout/js/model/adyen-payment-service',
+ 'jquery',
+ 'Adyen_ExpressCheckout/js/model/virtualQuote',
+ 'Adyen_ExpressCheckout/js/model/maskedId',
+ 'Adyen_ExpressCheckout/js/helpers/getCurrentPage',
+ 'Adyen_ExpressCheckout/js/helpers/getMaskedIdFromCart',
+], function (
+ Component,
+ $t,
+ customerData,
+ AdyenConfiguration,
+ AdyenCheckout,
+ activateCart,
+ createOrder,
+ getShippingMethods,
+ getExpressMethods,
+ setShippingInformation,
+ setTotalsInfo,
+ formatAmount,
+ getPaypalStyles,
+ getCartSubtotal,
+ getPaymentMethod,
+ getPdpForm,
+ getPdpPriceBox,
+ isConfigSet,
+ getRegionId,
+ redirectToSuccess,
+ setExpressMethods,
+ validatePdpForm,
+ configModel,
+ countriesModel,
+ totalsModel,
+ currencyModel,
+ ko,
+ customer,
+ quote,
+ initPayments,
+ updatePaypalOrder,
+ setBillingAddress,
+ fullScreenLoader,
+ adyenPaymentService,
+ $,
+ virtualQuoteModel,
+ maskedIdModel,
+ getCurrentPage,
+ getMaskedIdFromCart
+) {
+ 'use strict';
+
+ return Component.extend({
+ isPlaceOrderActionAllowed: ko.observable(
+ quote.billingAddress() != null),
+
+ defaults: {
+ shippingMethods: {},
+ isProductView: false,
+ maskedId: null,
+ paypalComponent: null,
+ shippingAddress: {},
+ shippingMethod: null,
+ shopperEmail: null,
+ billingAddress : {},
+ orderId: null,
+ quoteId: null
+ },
+
+ initialize: async function (config, element) {
+ this._super();
+
+ // Set the config and countries model
+ configModel().setConfig(config);
+ countriesModel();
+
+ // Determine if this is a product view page
+ this.isProductView = config.isProductView;
+
+ if (!this.isProductView) {
+ // Retrieve the PayPal payment method
+ let paypalPaymentMethod = await getPaymentMethod('paypal', this.isProductView);
+ virtualQuoteModel().setIsVirtual(false);
+
+ if (!paypalPaymentMethod) {
+ // Subscribe to cart updates if PayPal method is not immediately available
+ const cart = customerData.get('cart');
+ cart.subscribe(function () {
+ this.reloadPaypalButton(element);
+ }.bind(this));
+ } else {
+ // Initialize the PayPal component if config is set
+ if (!isConfigSet(paypalPaymentMethod, ['merchantId'])) {
+ return;
+ }
+ this.initialisePaypalComponent(paypalPaymentMethod, element);
+ }
+ } else {
+ // Initialize PayPal component on product view page
+ this.initialiseonPDP(config, element);
+ }
+ },
+
+ initialiseonPDP: async function (config, element) {
+ // Configuration setup
+ try {
+ const response = await getExpressMethods().getRequest(element);
+ const cart = customerData.get('cart');
+
+ virtualQuoteModel().setIsVirtual(true, response);
+
+ cart.subscribe(function () {
+ this.reloadPaypalButton(element);
+ }.bind(this));
+
+ setExpressMethods(response);
+ totalsModel().setTotal(response.totals.grand_total);
+ currencyModel().setCurrency(response.totals.quote_currency_code);
+
+ const $priceBox = getPdpPriceBox();
+ const pdpForm = getPdpForm(element);
+
+ $priceBox.on('priceUpdated', async function () {
+ const isValid = new Promise((resolve, reject) => {
+ return validatePdpForm(resolve, reject, pdpForm, true);
+ });
+
+ isValid
+ .then(async function () {
+ this.reloadPaypalButton(element);
+ }.bind(this))
+ .catch(function (error) {
+ console.log(error);
+ });
+ }.bind(this));
+
+ let paypalPaymentMethod = await getPaymentMethod('paypal', this.isProductView);
+
+ if (!paypalPaymentMethod) {
+ console.error('PayPal payment method not found');
+ return;
+ }
+
+ await this.initialisePaypalComponent(paypalPaymentMethod, element);
+ } catch (error) {
+ console.error('Error in initialiseonPDP:', error);
+ }
+ },
+
+
+ initialisePaypalComponent: async function (paypalPaymentMethod, element) {
+ // Configuration setup
+ const config = configModel().getConfig();
+ const adyenData = window.adyenData;
+ let currentPage = getCurrentPage(this.isProductView, element);
+
+ const adyenCheckoutComponent = await new AdyenCheckout({
+ locale: config.locale,
+ originKey: config.originkey,
+ environment: config.checkoutenv,
+ analytics: {
+ analyticsData: {
+ applicationInfo: {
+ merchantApplication: {
+ name: adyenData['merchant-application-name'],
+ version: adyenData['merchant-application-version']
+ },
+ externalPlatform: {
+ name: adyenData['external-platform-name'],
+ version: adyenData['external-platform-version']
+ }
+ }
+ }
+ },
+ risk: {
+ enabled: false
+ },
+ isExpress: true,
+ expressPage: currentPage,
+ clientKey: AdyenConfiguration.getClientKey()
+ });
+
+ const paypalConfiguration = this.getPaypalConfiguration(paypalPaymentMethod, element);
+
+ if (this.isProductView) {
+ paypalConfiguration.currencyCode = currencyModel().getCurrency();
+ paypalConfiguration.amount.currency = currencyModel().getCurrency();
+ }
+
+ try {
+ this.paypalComponent = adyenCheckoutComponent.create('paypal', paypalConfiguration);
+
+ if (typeof this.paypalComponent.isAvailable === 'function') {
+ this.paypalComponent
+ .isAvailable()
+ .then(() => {
+ this.onAvailable(element);
+ })
+ .catch((e) => {
+ this.onNotAvailable(e);
+ });
+ } else {
+ this.onAvailable(element);
+ }
+ } catch (error) {
+ console.error('Error creating PayPal component', error);
+ }
+ },
+
+ onNotAvailable: function (error) {
+ console.log('PayPal is unavailable.', error);
+ },
+
+ onAvailable: function (element) {
+ element.style.display = 'block';
+ this.paypalComponent.mount(element);
+ },
+
+ unmountPaypal: function () {
+ if (this.paypalComponent) {
+ this.paypalComponent.unmount();
+ }
+ },
+
+ reloadPaypalButton: async function (element) {
+ const paypalPaymentMethod = await getPaymentMethod('paypal', this.isProductView);
+
+ if (this.isProductView) {
+ const pdpResponse = await getExpressMethods().getRequest(element);
+
+ virtualQuoteModel().setIsVirtual(true, pdpResponse);
+ setExpressMethods(pdpResponse);
+ totalsModel().setTotal(pdpResponse.totals.grand_total);
+ } else {
+ virtualQuoteModel().setIsVirtual(false);
+ }
+
+ this.unmountPaypal();
+
+ if (!isConfigSet(paypalPaymentMethod, ['merchantId'])) {
+ return;
+ }
+
+ this.initialisePaypalComponent(paypalPaymentMethod, element);
+ },
+
+ getPaypalConfiguration: function (paypalPaymentMethod, element) {
+ const paypalStyles = getPaypalStyles();
+ const config = configModel().getConfig();
+ const countryCode = config.countryCode;
+
+ let currency;
+ let paypalBaseConfiguration;
+
+ if (this.isProductView) {
+ currency = currencyModel().getCurrency();
+ } else {
+ const cartData = customerData.get('cart');
+ const adyenMethods = cartData()['adyen_payment_methods'];
+ const paymentMethodExtraDetails = adyenMethods.paymentMethodsExtraDetails[paypalPaymentMethod.type];
+ currency = paymentMethodExtraDetails.configuration.amount.currency;
+ }
+
+ paypalBaseConfiguration = {
+ countryCode: countryCode,
+ environment: config.checkoutenv.toUpperCase(),
+ isExpress: true,
+ configuration: paypalPaymentMethod.configuration,
+ amount: {
+ currency: currency,
+ value: this.isProductView
+ ? formatAmount(totalsModel().getTotal() * 100)
+ : formatAmount(getCartSubtotal() * 100)
+ },
+ onSubmit: (state, component) => {
+ const paymentData = state.data;
+
+ paymentData.merchantAccount = config.merchantAccount;
+ initPayments(paymentData, this.isProductView).then((responseJSON) => {
+ let response = JSON.parse(responseJSON);
+ if (response.action) {
+ component.handleAction(response.action);
+ } else {
+ console.log('Init Payments call failed', response);
+ }
+ }).catch((error) => {
+ console.error('Payment initiation failed', error);
+ });
+ },
+ onShippingAddressChange: async (data, actions, component) => {
+ try {
+ this.shippingAddress = data.shippingAddress;
+ if(this.isProductView) {
+ await activateCart(this.isProductView);
+ }
+
+ const shippingMethods = await this.getShippingMethods(data.shippingAddress);
+ let shippingMethod = shippingMethods.find(method => method.identifier === this.shippingMethod);
+ await this.setShippingAndTotals(shippingMethod, data.shippingAddress);
+
+ const currentPaymentData = component.paymentData;
+
+ await updatePaypalOrder.updateOrder(
+ this.isProductView,
+ currentPaymentData,
+ shippingMethods,
+ currency
+ ).then(function (response) {
+ let parsedResponse = JSON.parse(response);
+ component.updatePaymentData(parsedResponse.paymentData);
+ }).catch(function () {
+ component.updatePaymentData(currentPaymentData);
+ return actions.reject();
+ });
+ } catch (error) {
+ return actions.reject();
+ }
+ },
+ onShippingOptionsChange: async (data, actions, component) => {
+ let shippingMethod = [];
+ const currentPaymentData = component.paymentData;
+ for (const method of Object.values(this.shippingMethods)) {
+ if (method.carrier_title === data.selectedShippingOption.label) {
+ this.shippingMethod = method.method_code;
+ shippingMethod = {
+ identifier: method.method_code,
+ label: method.method_title,
+ detail: method.carrier_title ? method.carrier_title : '',
+ amount: parseFloat(method.amount).toFixed(2),
+ carrierCode: method.carrier_code,
+ };
+ break;
+ }
+ }
+ await this.setShippingAndTotals(shippingMethod, this.shippingAddress);
+
+ await updatePaypalOrder.updateOrder(
+ this.isProductView,
+ currentPaymentData,
+ this.shippingMethods,
+ currency,
+ data.selectedShippingOption
+ ).then(function (response) {
+ let parsedResponse = JSON.parse(response);
+ component.updatePaymentData(parsedResponse.paymentData);
+ }).catch(function () {
+ component.updatePaymentData(currentPaymentData);
+ return actions.reject();
+ });
+ },
+ onShopperDetails: async (shopperDetails, rawData, actions) => {
+ try {
+ const isVirtual = virtualQuoteModel().getIsVirtual();
+
+ const { billingAddress, shippingAddress } = await this.setupAddresses(shopperDetails);
+
+ let billingAddressPayload = {
+ address: billingAddress,
+ 'useForShipping': false
+ };
+
+ let shippingInformationPayload = {
+ addressInformation: {
+ shipping_address: shippingAddress,
+ billing_address: billingAddress,
+ shipping_method_code: this.shippingMethod,
+ shipping_carrier_code: this.shippingMethods[this.shippingMethod].carrier_code
+ }
+ };
+ activateCart(this.isProductView)
+ .then(() => {
+ return setBillingAddress(billingAddressPayload, this.isProductView);
+ })
+ .then(() => {
+ if (!isVirtual) {
+ return setShippingInformation(shippingInformationPayload, this.isProductView)
+ .then(() => {
+ return this.createOrder();
+ })
+ .then(() => {
+ actions.resolve();
+ });
+ } else {
+ return this.createOrder().then(() => {
+ actions.resolve();
+ });
+ }
+ })
+ .catch((error) => {
+ console.error('An error occurred:', error);
+ });
+ } catch (error) {
+ console.error('Failed to complete order:', error);
+ actions.reject();
+ }
+ },
+
+ onAdditionalDetails: async (state, component) => {
+ let request = state.data;
+ let self = this;
+ fullScreenLoader.startLoader();
+ request.orderId = this.orderId;
+
+ let quoteId = this.isProductView ? maskedIdModel().getMaskedId() : getMaskedIdFromCart();
+
+ adyenPaymentService.paymentDetails(request, this.orderId, quoteId).
+ done(function(responseJSON) {
+ fullScreenLoader.stopLoader();
+ self.handleAdyenResult(responseJSON, self.orderId);
+ }).
+ fail(function(response) {
+ self.isPlaceOrderActionAllowed(true); //Complete this function
+ fullScreenLoader.stopLoader();
+ });
+
+ },
+ style: paypalStyles
+ };
+
+ return paypalBaseConfiguration;
+ },
+
+ setupAddresses: async function (shopperDetails) {
+ let billingAddress = {
+ 'email': shopperDetails.shopperEmail,
+ 'telephone': shopperDetails.telephoneNumber,
+ 'firstname': shopperDetails.shopperName.firstName,
+ 'lastname': shopperDetails.shopperName.lastName,
+ 'street': [
+ shopperDetails.billingAddress.street
+ ],
+ 'city': shopperDetails.billingAddress.city,
+ 'region': shopperDetails.billingAddress.stateOrProvince,
+ 'region_id': getRegionId(shopperDetails.billingAddress.country, shopperDetails.billingAddress.stateOrProvince),
+ 'region_code': null,
+ 'country_id': shopperDetails.billingAddress.country.toUpperCase(),
+ 'postcode': shopperDetails.billingAddress.postalCode,
+ 'same_as_billing': 0,
+ 'customer_address_id': 0,
+ 'save_in_address_book': 0
+ };
+
+ let shippingAddress = {
+ 'email': shopperDetails.shopperEmail,
+ 'telephone': shopperDetails.telephoneNumber,
+ 'firstname': shopperDetails.shopperName.firstName,
+ 'lastname': shopperDetails.shopperName.lastName,
+ 'street': [
+ shopperDetails.shippingAddress.street
+ ],
+ 'city': shopperDetails.shippingAddress.city,
+ 'region': shopperDetails.shippingAddress.stateOrProvince,
+ 'region_id': getRegionId(shopperDetails.shippingAddress.country, shopperDetails.shippingAddress.stateOrProvince),
+ 'region_code': null,
+ 'country_id': shopperDetails.shippingAddress.country.toUpperCase(),
+ 'postcode': shopperDetails.shippingAddress.postalCode,
+ 'same_as_billing': 0,
+ 'customer_address_id': 0,
+ 'save_in_address_book': 0
+ };
+
+ return {
+ billingAddress: billingAddress,
+ shippingAddress: shippingAddress
+ };
+ },
+
+ // Extracted method to get shipping methods
+ getShippingMethods: function (shippingAddress) {
+ const payload = {
+ address: {
+ country_id: shippingAddress.countryCode,
+ postcode: shippingAddress.postalCode,
+ street: ['']
+ }
+ };
+
+ return new Promise((resolve, reject) => {
+ getShippingMethods(payload, this.isProductView).then(result => {
+ if (result.length === 0) {
+ reject(new Error($t('There are no shipping methods available for you right now. Please try again or use an alternative payment method.')));
+ }
+
+ let shippingMethods = [];
+
+ for (let method of result) {
+ if (typeof method.method_code !== 'string') {
+ continue;
+ }
+ let shippingMethod = {
+ identifier: method.method_code,
+ label: method.method_title,
+ detail: method.carrier_title ? method.carrier_title : '',
+ amount: parseFloat(method.amount).toFixed(2),
+ carrierCode: method.carrier_code,
+ };
+ shippingMethods.push(shippingMethod);
+ this.shippingMethods[method.method_code] = method;
+ if (!this.shippingMethod) {
+ this.shippingMethod = method.method_code;
+ }
+ }
+ resolve(shippingMethods);
+ }).catch(error => {
+ console.error('Failed to retrieve shipping methods:', error);
+ reject(new Error($t('Failed to retrieve shipping methods. Please try again later.')));
+ });
+ });
+ },
+ createOrder: function(email) {
+ const payload = {
+ paymentMethod: {
+ method: 'adyen_paypal_express',
+ additional_data: [
+ 'brand_code:paypal'
+ ]
+ }
+ };
+
+ if (window.checkout && window.checkout.agreementIds) {
+ payload.paymentMethod.extension_attributes = {
+ agreement_ids: window.checkout.agreementIds
+ };
+ }
+
+ return new Promise((resolve, reject) => {
+ createOrder(JSON.stringify(payload), this.isProductView)
+ .then(function(orderId) {
+ if (orderId) {
+ this.orderId = orderId;
+ resolve(orderId);
+ } else {
+ reject(new Error('Order ID not returned'));
+ }
+ }.bind(this))
+ .catch(function(e) {
+ console.error('Adyen Paypal Unable to take payment', e);
+ reject(e);
+ });
+ });
+ },
+
+ handleAdyenResult: function (responseJSON, orderId) {
+ let self = this;
+ let response = JSON.parse(responseJSON);
+
+ if (response.isFinal) {
+ // Status is final redirect to the success page
+ redirectToSuccess();
+ } else {
+ // Handle action
+ self.handleAction(response.action, orderId); // Complete this
+ }
+ },
+
+ handleAction: function(action, orderId) {
+ let self = this;
+ let popupModal;
+
+ fullScreenLoader.stopLoader();
+
+ if (action.type === 'threeDS2' || action.type === 'await') {
+ popupModal = self.showModal();
+ }
+
+ try {
+ self.adyenCheckoutComponent.createFromAction(action).mount('#' + this.modalLabel);
+ } catch (e) {
+ console.log(e);
+ self.closeModal(popupModal);
+ }
+ },
+
+ // Extracted method to set shipping information and totals
+ setShippingAndTotals: function (shippingMethod, shippingAddress) {
+ let address = {
+ 'countryId': shippingAddress.countryCode,
+ 'region': shippingAddress.state,
+ 'regionId': getRegionId(shippingAddress.country_id, shippingAddress.state),
+ 'postcode': shippingAddress.postalCode
+ };
+
+ let shippingInformationPayload = {
+ addressInformation: {
+ shipping_address: address,
+ billing_address: address,
+ shipping_method_code: this.shippingMethod,
+ shipping_carrier_code: shippingMethod ? shippingMethod.carrierCode : ''
+ }
+ };
+
+ let totalsPayload = {
+ 'addressInformation': {
+ 'address': address,
+ 'shipping_method_code': this.shippingMethod,
+ 'shipping_carrier_code': shippingMethod ? shippingMethod.carrierCode : ''
+ }
+ };
+
+ return Promise.all([
+ setShippingInformation(shippingInformationPayload, this.isProductView),
+ setTotalsInfo(totalsPayload, this.isProductView)
+ ]).catch(error => {
+ console.error('Failed to set shipping and totals information:', error);
+ throw new Error($t('Failed to set shipping and totals information. Please try again later.'));
+ });
+ }
+ });
+});