We will be off from 27/1 (Monday) to 31/1 (Friday) (GMT +7) for our Tet Holiday (Lunar New Year) in our country

Commit 4d1745b6 authored by cyattilakiss's avatar cyattilakiss Committed by GitHub

Merge pull request #478 from Adyen/develop

Release 4.3.0
parents 31da74f6 578230a5
......@@ -28,7 +28,8 @@ interface AdyenInitiateTerminalApiInterface
{
/**
* Trigger sync call on terminal
* @param string $payload
* @return mixed
*/
public function initiate();
public function initiate($payload);
}
......@@ -41,7 +41,19 @@ class Installment extends \Magento\Framework\View\Element\Html\Select
'9' => '9x',
'10' => '10x',
'11' => '11x',
'12' => '12x'
'12' => '12x',
'13' => '13x',
'14' => '14x',
'15' => '15x',
'16' => '16x',
'17' => '17x',
'18' => '18x',
'19' => '19x',
'20' => '20x',
'21' => '21x',
'22' => '22x',
'23' => '23x',
'24' => '24x'
];
/**
......
......@@ -87,9 +87,9 @@ class Cc extends \Magento\Payment\Block\Form\Cc
/**
* @return string
*/
public function getCheckoutContextUrl()
public function getCheckoutEnvironment()
{
return $this->adyenHelper->getCheckoutContextUrl($this->checkoutSession->getQuote()->getStore()->getId());
return $this->adyenHelper->getCheckoutEnvironment($this->checkoutSession->getQuote()->getStore()->getId());
}
/**
......
......@@ -217,11 +217,11 @@ class Redirect extends \Magento\Payment\Block\Form
if ($this->_adyenHelper->isSeparateHouseNumberRequired($billingAddress->getCountryId())) {
$street = $this->_adyenHelper->getStreet($billingAddress);
if (isset($street['name']) && $street['name'] != "") {
if (!empty($street['name'])) {
$formFields['billingAddress.street'] = $street['name'];
}
if (isset($street['house_number']) && $street['house_number'] != "") {
if (!empty($street['house_number'])) {
$formFields['billingAddress.houseNumberOrName'] = $street['house_number'];
} else {
$formFields['billingAddress.houseNumberOrName'] = "NA";
......
......@@ -75,6 +75,7 @@ class Json extends \Magento\Framework\App\Action\Action
$request = $this->getRequest();
if ($request instanceof Http && $request->isPost()) {
$request->setParam('isAjax', true);
$request->getHeaders()->addHeaderLine('X_REQUESTED_WITH', 'XMLHttpRequest');
}
}
}
......
......@@ -249,7 +249,7 @@ class Result extends \Magento\Framework\App\Action\Action
break;
case Notification::RECEIVED:
$result = true;
if (strpos($paymentMethod, "alipay_hk_web") !== false) {
if (strpos($paymentMethod, "alipay_hk") !== false) {
$result = false;
}
$this->_adyenLogger->addAdyenResult('Do nothing wait for the notification');
......@@ -382,7 +382,10 @@ class Result extends \Magento\Framework\App\Action\Action
$request = [];
if (!empty($this->_session->getLastRealOrder()->getPayment()->getAdditionalInformation("paymentData"))) {
if (!empty($this->_session->getLastRealOrder()) &&
!empty($this->_session->getLastRealOrder()->getPayment()) &&
!empty($this->_session->getLastRealOrder()->getPayment()->getAdditionalInformation("paymentData"))
) {
$request['paymentData'] = $this->_session->getLastRealOrder()->getPayment()->getAdditionalInformation("paymentData");
}
......
......@@ -92,7 +92,7 @@ class TransactionPosCloudSync implements ClientInterface
//always do status call and return the response of the status call
$service = $this->adyenHelper->createAdyenPosPaymentService($this->client);
$poiId = $this->adyenHelper->getPoiId($this->storeId);
$poiId = $request['terminalID'];
$newServiceID = date("dHis");
$statusDate = date("U");
......
......@@ -25,7 +25,6 @@ namespace Adyen\Payment\Gateway\Request;
use Magento\Payment\Gateway\Request\BuilderInterface;
use Adyen\Payment\Observer\AdyenHppDataAssignObserver;
use Adyen\Payment\Observer\AdyenBoletoDataAssignObserver;
class CheckoutDataBuilder implements BuilderInterface
{
......@@ -40,39 +39,23 @@ class CheckoutDataBuilder implements BuilderInterface
private $storeManager;
/**
* @var \Magento\Checkout\Model\Session
* @var \Magento\Quote\Api\CartRepositoryInterface
*/
private $checkoutSession;
private $cartRepository;
/**
* @var \Magento\Quote\Model\Quote
*/
private $quote;
/**
* @var \Magento\Tax\Model\Config
*/
protected $taxConfig;
/**
* CheckoutDataBuilder constructor.
* @param \Adyen\Payment\Helper\Data $adyenHelper
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Checkout\Model\Session $checkoutSession
* @param \Magento\Tax\Model\Config $taxConfig
* @param \Magento\Quote\Api\CartRepositoryInterface $cartRepository
*/
public function __construct(
\Adyen\Payment\Helper\Data $adyenHelper,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Tax\Model\Config $taxConfig
)
{
\Magento\Quote\Api\CartRepositoryInterface $cartRepository
) {
$this->adyenHelper = $adyenHelper;
$this->storeManager = $storeManager;
$this->checkoutSession = $checkoutSession;
$this->quote = $checkoutSession->getQuote();
$this->taxConfig = $taxConfig;
$this->cartRepository = $cartRepository;
}
/**
......@@ -204,21 +187,24 @@ class CheckoutDataBuilder implements BuilderInterface
}
/**
* @param $formFields
* @return mixed
* @param \Magento\Sales\Model\Order $order
*
* @throws \Magento\Framework\Exception\NoSuchEntityException
*
* @return array
*/
protected function getOpenInvoiceData($order)
protected function getOpenInvoiceData($order): array
{
$formFields = [
'lineItems' => []
];
$currency = $this->quote->getCurrency();
/** @var \Magento\Quote\Model\Quote $cart */
$cart = $this->cartRepository->get($order->getQuoteId());
$currency = $cart->getCurrency();
$discountAmount = 0;
foreach ($this->quote->getAllVisibleItems() as $item) {
foreach ($cart->getAllVisibleItems() as $item) {
$numberOfItems = (int)$item->getQty();
// Summarize the discount amount item by item
......@@ -263,20 +249,18 @@ class CheckoutDataBuilder implements BuilderInterface
}
// Shipping cost
if ($this->quote->getShippingAddress()->getShippingAmount() > 0 || $this->quote->getShippingAddress()->getShippingTaxAmount() > 0) {
if ($cart->getShippingAddress()->getShippingAmount() > 0 || $cart->getShippingAddress()->getShippingTaxAmount() > 0) {
$priceExcludingTax = $this->quote->getShippingAddress()->getShippingAmount() - $this->quote->getShippingAddress()->getShippingTaxAmount();
$priceExcludingTax = $cart->getShippingAddress()->getShippingAmount() - $cart->getShippingAddress()->getShippingTaxAmount();
$formattedTaxAmount = $this->adyenHelper->formatAmount($this->quote->getShippingAddress()->getShippingTaxAmount(), $currency);
$formattedTaxAmount = $this->adyenHelper->formatAmount($cart->getShippingAddress()->getShippingTaxAmount(), $currency);
$formattedPriceExcludingTax = $this->adyenHelper->formatAmount($priceExcludingTax, $currency);
$taxClassId = $this->taxConfig->getShippingTaxClass($this->storeManager->getStore()->getId());
$formattedTaxPercentage = 0;
if ($priceExcludingTax !== 0) {
$formattedTaxPercentage = $this->quote->getShippingAddress()->getShippingTaxAmount() / $priceExcludingTax * 100 * 100;
$formattedTaxPercentage = $cart->getShippingAddress()->getShippingTaxAmount() / $priceExcludingTax * 100 * 100;
}
$formFields['lineItems'][] = [
......
......@@ -41,7 +41,8 @@ class PosCloudBuilder implements BuilderInterface
return [
"response" => $payment->getAdditionalInformation("terminalResponse"),
"serviceID" => $payment->getAdditionalInformation("serviceID"),
"initiateDate" => $payment->getAdditionalInformation("initiateDate")
"initiateDate" => $payment->getAdditionalInformation("initiateDate"),
"terminalID" => $payment->getAdditionalInformation("terminal_id")
];
}
}
......@@ -32,6 +32,11 @@ class CheckoutResponseValidator extends AbstractValidator
*/
private $adyenLogger;
/**
* @var \Adyen\Payment\Helper\Data
*/
private $adyenHelper;
/**
* GeneralResponseValidator constructor.
*
......@@ -40,9 +45,11 @@ class CheckoutResponseValidator extends AbstractValidator
*/
public function __construct(
\Magento\Payment\Gateway\Validator\ResultInterfaceFactory $resultFactory,
\Adyen\Payment\Logger\AdyenLogger $adyenLogger
\Adyen\Payment\Logger\AdyenLogger $adyenLogger,
\Adyen\Payment\Helper\Data $adyenHelper
) {
$this->adyenLogger = $adyenLogger;
$this->adyenHelper = $adyenHelper;
parent::__construct($resultFactory);
}
......@@ -80,6 +87,13 @@ class CheckoutResponseValidator extends AbstractValidator
}
}
// Save cc_type if available in the response
if (!empty($response['additionalData']['paymentMethod'])) {
$ccType = $this->adyenHelper->getMagentoCreditCartType($response['additionalData']['paymentMethod']);
$payment->setAdditionalInformation('cc_type', $ccType);
$payment->setCcType($ccType);
}
$payment->setAdditionalInformation('pspReference', $response['pspReference']);
break;
case "Received":
......
......@@ -32,6 +32,11 @@ class GeneralResponseValidator extends AbstractValidator
*/
private $adyenLogger;
/**
* @var \Adyen\Payment\Helper\Data
*/
private $adyenHelper;
/**
* GeneralResponseValidator constructor.
*
......@@ -40,9 +45,11 @@ class GeneralResponseValidator extends AbstractValidator
*/
public function __construct(
\Magento\Payment\Gateway\Validator\ResultInterfaceFactory $resultFactory,
\Adyen\Payment\Logger\AdyenLogger $adyenLogger
\Adyen\Payment\Logger\AdyenLogger $adyenLogger,
\Adyen\Payment\Helper\Data $adyenHelper
) {
$this->adyenLogger = $adyenLogger;
$this->adyenHelper = $adyenHelper;
parent::__construct($resultFactory);
}
......@@ -65,6 +72,14 @@ class GeneralResponseValidator extends AbstractValidator
switch ($response['resultCode']) {
case "Authorised":
$payment->setAdditionalInformation('pspReference', $response['pspReference']);
// Save cc_type if available in the response
if (!empty($response['additionalData']['paymentMethod'])) {
$ccType = $this->adyenHelper->getMagentoCreditCartType($response['additionalData']['paymentMethod']);
$payment->setAdditionalInformation('cc_type', $ccType);
$payment->setCcType($ccType);
}
break;
case "Received":
$payment->setAdditionalInformation('pspReference', $response['pspReference']);
......
......@@ -33,10 +33,8 @@ class Data extends AbstractHelper
const MODULE_NAME = 'adyen-magento2';
const TEST = 'test';
const LIVE = 'live';
const CHECKOUT_CONTEXT_URL_LIVE = 'https://checkoutshopper-live.adyen.com/checkoutshopper/';
const CHECKOUT_CONTEXT_URL_TEST = 'https://checkoutshopper-test.adyen.com/checkoutshopper/';
const CHECKOUT_COMPONENT_JS_LIVE = 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/2.5.0/adyen.js';
const CHECKOUT_COMPONENT_JS_TEST = 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/2.5.0/adyen.js';
const CHECKOUT_COMPONENT_JS_LIVE = 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/3.0.0/adyen.js';
const CHECKOUT_COMPONENT_JS_TEST = 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/3.0.0/adyen.js';
/**
* @var \Magento\Framework\Encryption\EncryptorInterface
......@@ -128,6 +126,12 @@ class Data extends AbstractHelper
*/
private $config;
/**
* @var \Magento\Backend\Helper\Data $helperBackend
*/
private $helperBackend;
/**
* Data constructor.
* @param \Magento\Framework\App\Helper\Context $context
......@@ -147,6 +151,9 @@ class Data extends AbstractHelper
* @param \Magento\Framework\App\CacheInterface $cache
* @param \Adyen\Payment\Model\Billing\AgreementFactory $billingAgreementFactory
* @param \Adyen\Payment\Model\ResourceModel\Billing\Agreement $agreementResourceModel
* @param \Magento\Framework\Locale\ResolverInterface $localeResolver
* @param \Magento\Framework\App\Config\ScopeConfigInterface $config
* @param \Magento\Backend\Helper\Data $helperBackend
*/
public function __construct(
\Magento\Framework\App\Helper\Context $context,
......@@ -167,7 +174,8 @@ class Data extends AbstractHelper
\Adyen\Payment\Model\Billing\AgreementFactory $billingAgreementFactory,
\Adyen\Payment\Model\ResourceModel\Billing\Agreement $agreementResourceModel,
\Magento\Framework\Locale\ResolverInterface $localeResolver,
\Magento\Framework\App\Config\ScopeConfigInterface $config
\Magento\Framework\App\Config\ScopeConfigInterface $config,
\Magento\Backend\Helper\Data $helperBackend
) {
parent::__construct($context);
$this->_encryptor = $encryptor;
......@@ -188,6 +196,7 @@ class Data extends AbstractHelper
$this->agreementResourceModel = $agreementResourceModel;
$this->localeResolver = $localeResolver;
$this->config = $config;
$this->helperBackend = $helperBackend;
}
/**
......@@ -249,39 +258,34 @@ class Data extends AbstractHelper
public function formatAmount($amount, $currency)
{
switch ($currency) {
case "JPY":
case "IDR":
case "KRW":
case "BYR":
case "VND":
case "CVE":
case "DJF":
case "GNF":
case "IDR":
case "JPY":
case "KMF":
case "KRW":
case "PYG":
case "RWF":
case "UGX":
case "VND":
case "VUV":
case "XAF":
case "XOF":
case "XPF":
case "GHC":
case "KMF":
$format = 0;
break;
case "MRO":
$format = 1;
break;
case "BHD":
case "IQD":
case "JOD":
case "KWD":
case "OMR":
case "LYD":
case "OMR":
case "TND":
$format = 3;
break;
default:
$format = 2;
break;
}
return (int)number_format($amount, $format, '', '');
......@@ -389,6 +393,9 @@ class Data extends AbstractHelper
if (count($street) != 1) {
return $street;
}
$street['0'] = trim($street['0']);
preg_match('/((\s\d{0,10})|(\s\d{0,10}\w{1,3}))$/i', $street['0'], $houseNumber, PREG_OFFSET_CAPTURE);
if (!empty($houseNumber['0'])) {
$_houseNumber = trim($houseNumber['0']['0']);
......@@ -460,18 +467,6 @@ class Data extends AbstractHelper
return $this->getConfigData($field, 'adyen_cc_vault', $storeId, true);
}
/**
* Gives back adyen_cc_threeds2 configuration values as flag
*
* @param $field
* @param null $storeId
* @return mixed
*/
public function getAdyenCcThreeDS2ConfigDataFlag($field, $storeId = null)
{
return $this->getConfigData($field, 'adyen_cc_threeds2', $storeId, true);
}
/**
* Gives back adyen_hpp configuration values
*
......@@ -1353,14 +1348,14 @@ class Data extends AbstractHelper
}
/**
* Return the Terminal ID for the current store/mode
* Return the Store ID for the current store/mode
*
* @param int|null $storeId
* @return mixed
*/
public function getPoiId($storeId = null)
public function getPosStoreId($storeId = null)
{
return $this->getAdyenPosCloudConfigData('pos_terminal_id', $storeId);
return $this->getAdyenPosCloudConfigData('pos_store_id', $storeId);
}
/**
......@@ -1481,9 +1476,17 @@ class Data extends AbstractHelper
* @return string
*/
public function getOrigin() {
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$state = $objectManager->get('Magento\Framework\App\State');
$baseUrl = $this->storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_WEB);
if ('adminhtml' === $state->getAreaCode()) {
$baseUrl = $this->helperBackend->getHomePageUrl();
}
$parsed = parse_url($baseUrl);
$origin = $parsed['scheme'] . "://" . $parsed['host'];
if (!empty($parsed['port'])) {
$origin .= ":" . $parsed['port'];
}
return $origin;
}
......@@ -1546,13 +1549,13 @@ class Data extends AbstractHelper
* @param int|null $storeId
* @return string
*/
public function getCheckoutContextUrl($storeId = null)
public function getCheckoutEnvironment($storeId = null)
{
if ($this->isDemoMode($storeId)) {
return self::CHECKOUT_CONTEXT_URL_TEST;
return self::TEST;
}
return self::CHECKOUT_CONTEXT_URL_LIVE;
return self::LIVE;
}
/**
......@@ -1724,7 +1727,7 @@ class Data extends AbstractHelper
*/
public function isCreditCardThreeDS2Enabled($storeId = null)
{
return $this->getAdyenCcThreeDS2ConfigDataFlag('active', $storeId);
return $this->getAdyenCcConfigDataFlag('threeds2_enabled', $storeId);
}
/**
......
......@@ -390,4 +390,41 @@ class PaymentMethods extends AbstractHelper
{
return $this->getQuote()->getCustomerId();
}
/**
* @return array|mixed
* @throws \Adyen\AdyenException
*/
public function getConnectedTerminals()
{
$storeId = $this->getQuote()->getStoreId();
// initialize the adyen client
$client = $this->adyenHelper->initializeAdyenClient($storeId, $this->adyenHelper->getPosApiKey($storeId));
// initialize service
$service = $this->adyenHelper->createAdyenPosPaymentService($client);
$requestParams = [
"merchantAccount" => $this->adyenHelper->getAdyenMerchantAccount('adyen_pos_cloud', $storeId),
];
// In case the POS store id is set, provide in the request
if (!empty($this->adyenHelper->getPosStoreId($storeId))) {
$requestParams['store'] = $this->adyenHelper->getPosStoreId($storeId);
}
try {
$responseData = $service->getConnectedTerminals($requestParams);
} catch (\Adyen\AdyenException $e) {
$this->adyenLogger->error(
"The getConnectedTerminals response is empty check your Adyen configuration in Magento."
);
// return empty result
return [];
}
return $responseData;
}
}
\ No newline at end of file
......@@ -450,7 +450,7 @@ class Requests extends AbstractHelper
// Parse address into street and house number where possible
$address = $this->adyenHelper->getStreetFromString($address->getStreetFull());
} else {
$address = $this->adyenHelper->getStreetFromString($address->getStreetLine1());
$address = $this->adyenHelper->getStreetFromString(implode(' ', [$address->getStreetLine1(), $address->getStreetLine2()]));
}
return $address;
......
......@@ -93,8 +93,22 @@ class AdyenInitiateTerminalApi implements AdyenInitiateTerminalApiInterface
* @return mixed
* @throws \Exception
*/
public function initiate()
public function initiate($payload)
{
// Decode payload from frontend
$payload = json_decode($payload, true);
// Validate JSON that has just been parsed if it was in a valid format
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Magento\Framework\Exception\LocalizedException(__('Terminal API initiate request was not a valid JSON'));
}
if (empty($payload['terminal_id'])) {
throw new \Adyen\AdyenException("Terminal ID is empty in initiate request");
}
$poiId = $payload['terminal_id'];
$quote = $this->checkoutSession->getQuote();
$payment = $quote->getPayment();
$payment->setMethod(AdyenPosCloudConfigProvider::CODE);
......@@ -102,7 +116,7 @@ class AdyenInitiateTerminalApi implements AdyenInitiateTerminalApiInterface
$service = $this->adyenHelper->createAdyenPosPaymentService($this->client);
$transactionType = \Adyen\TransactionType::NORMAL;
$poiId = $this->adyenHelper->getPoiId($this->storeId);
$serviceID = date("dHis");
$initiateDate = date("U");
$timeStamper = date("Y-m-d") . "T" . date("H:i:s+00:00");
......
......@@ -56,7 +56,7 @@ class CcType extends \Magento\Payment\Model\Source\Cctype
*/
public function getAllowedTypes()
{
return ['VI', 'MC', 'AE', 'DI', 'JCB', 'UN', 'MI', 'DN', 'BCMC', 'HIPERCARD','ELO', 'TROY'];
return ['VI', 'MC', 'AE', 'DI', 'JCB', 'UN', 'MI', 'DN', 'BCMC', 'HIPERCARD','ELO', 'TROY', 'DANKORT'];
}
/**
......
......@@ -1495,6 +1495,9 @@ class Cron
case 'laser':
case 'paypal':
case 'sepadirectdebit':
case 'dankort':
case 'elo':
case 'hipercard':
$manualCaptureAllowed = true;
break;
default:
......
......@@ -146,7 +146,7 @@ class AdyenCcConfigProvider implements ConfigProviderInterface
$config['payment']['adyenCc']['icons'] = $this->getIcons();
$config['payment']['adyenCc']['originKey'] = $this->_adyenHelper->getOriginKeyForBaseUrl();
$config['payment']['adyenCc']['checkoutUrl'] = $this->_adyenHelper->getCheckoutContextUrl($this->storeManager->getStore()->getId());
$config['payment']['adyenCc']['checkoutEnvironment'] = $this->_adyenHelper->getCheckoutEnvironment($this->storeManager->getStore()->getId());
// has installments by default false
$config['payment']['adyenCc']['hasInstallments'] = false;
......
......@@ -143,7 +143,7 @@ class AdyenOneclickConfigProvider implements ConfigProviderInterface
$config['payment']['adyenOneclick']['methodCode'] = self::CODE;
$config['payment']['adyenOneclick']['originKey'] = $this->_adyenHelper->getOriginKeyForBaseUrl();
$config['payment']['adyenOneclick']['checkoutUrl'] = $this->_adyenHelper->getCheckoutContextUrl($this->_storeManager->getStore()->getId());
$config['payment']['adyenOneclick']['checkoutEnvironment'] = $this->_adyenHelper->getCheckoutEnvironment($this->_storeManager->getStore()->getId());
$config['payment']['adyenOneclick']['locale'] = $this->_adyenHelper->getStoreLocale($this->_storeManager->getStore()->getId());
$enableOneclick = $this->_adyenHelper->getAdyenAbstractConfigData('enable_oneclick');
......
......@@ -43,6 +43,11 @@ class AdyenPosCloudConfigProvider implements ConfigProviderInterface
*/
protected $urlBuilder;
/**
* @var \Adyen\Payment\Helper\PaymentMethods
*/
protected $paymentMethodsHelper;
/**
* AdyenHppConfigProvider constructor.
*
......@@ -51,10 +56,12 @@ class AdyenPosCloudConfigProvider implements ConfigProviderInterface
*/
public function __construct(
\Magento\Framework\App\RequestInterface $request,
\Magento\Framework\UrlInterface $urlBuilder
\Magento\Framework\UrlInterface $urlBuilder,
\Adyen\Payment\Helper\PaymentMethods $paymentMethodsHelper
) {
$this->request = $request;
$this->urlBuilder = $urlBuilder;
$this->paymentMethodsHelper = $paymentMethodsHelper;
}
/**
......@@ -77,6 +84,8 @@ class AdyenPosCloudConfigProvider implements ConfigProviderInterface
]
];
$config['payment']['adyenPos']['connectedTerminals'] = $this->getConnectedTerminals();
return $config;
}
......@@ -89,4 +98,19 @@ class AdyenPosCloudConfigProvider implements ConfigProviderInterface
{
return $this->request;
}
/**
* @return array|mixed
* @throws \Adyen\AdyenException
*/
protected function getConnectedTerminals()
{
$connectedTerminals = $this->paymentMethodsHelper->getConnectedTerminals();
if (!empty($connectedTerminals['uniqueTerminalIds'])) {
return $connectedTerminals['uniqueTerminalIds'];
}
return [];
}
}
<?php
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2019 Adyen BV (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <magento@adyen.com>
*/
namespace Adyen\Payment\Observer;
use Magento\Framework\Event\Observer;
use Magento\Payment\Observer\AbstractDataAssignObserver;
use Magento\Quote\Api\Data\PaymentInterface;
/**
* Class DataAssignObserver
*/
class AdyenPosCloudDataAssignObserver extends AbstractDataAssignObserver
{
const TERMINAL_ID = 'terminal_id';
/**
* @var array
*/
protected $additionalInformationList = [
self::TERMINAL_ID
];
/**
* @param Observer $observer
* @return void
*/
public function execute(Observer $observer)
{
$data = $this->readDataArgument($observer);
$additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA);
if (!is_array($additionalData)) {
return;
}
$paymentInfo = $this->readPaymentModelArgument($observer);
foreach ($this->additionalInformationList as $additionalInformationKey) {
if (!empty($additionalData[$additionalInformationKey])) {
$paymentInfo->setAdditionalInformation(
$additionalInformationKey,
$additionalData[$additionalInformationKey]
);
}
}
}
}
......@@ -37,7 +37,7 @@ If you need to setup your cronjob in Magento <a href="http://devdocs.magento.com
We have defined this:
```
<group id="index">
<group id="adyen_payment">
<job name="adyen_payment_process_notification" instance="Adyen\Payment\Model\Cron" method="processNotification">
<schedule>*/1 * * * *</schedule>
</job>
......
......@@ -2,7 +2,7 @@
"name": "adyen/module-payment",
"description": "Official Magento2 Plugin to connect to Payment Service Provider Adyen.",
"type": "magento2-module",
"version": "4.2.1",
"version": "4.3.0",
"license": [
"OSL-3.0",
"AFL-3.0"
......@@ -14,7 +14,7 @@
}
],
"require": {
"adyen/php-api-library": ">=2.0.0",
"adyen/php-api-library": "~2.1",
"magento/framework": ">=101.0.8 <102 || >=102.0.1",
"magento/module-vault": "101.*"
},
......
......@@ -63,7 +63,7 @@
showInStore="1">
<label>3DS2.0 Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<config_path>payment/adyen_cc_threeds2/active</config_path>
<config_path>payment/adyen_cc/threeds2_enabled</config_path>
</field>
<group id="adyen_cc_advanced_settings" translate="label" showInDefault="1" showInWebsite="1" showInStore="1"
......
......@@ -41,24 +41,25 @@
<frontend_class>validate-number</frontend_class>
<config_path>payment/adyen_pos_cloud/sort_order</config_path>
</field>
<field id="pos_terminal_id" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Unique Terminal ID for Cloud API</label>
<tooltip>Copy this from the Adyen Customer Area => Point of sale => Terminal Fleet Manager => Unique Terminal Id </tooltip>
<config_path>payment/adyen_pos_cloud/pos_terminal_id</config_path>
</field>
<field id="pos_merchant_account" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Merchant Account for Cloud API</label>
<can_be_empty>1</can_be_empty>
<tooltip>Please insert your Merchant Account name used by the Cloud API. Please leave it blank if the Merchant Account is the same as the one configured in the Required Settings</tooltip>
<config_path>payment/adyen_pos_cloud/pos_merchant_account</config_path>
</field>
<field id="api_key_test" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0">
<field id="pos_store_id" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Store ID for Cloud API</label>
<can_be_empty>1</can_be_empty>
<tooltip>Please insert your store ID used by the Cloud API. Please leave it blank if you want to retrieve all the terminals connected to your merchant account</tooltip>
<config_path>payment/adyen_pos_cloud/pos_store_id</config_path>
</field>
<field id="api_key_test" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0">
<label>API key for Cloud API TEST</label>
<tooltip>Copy this from the Test Adyen Customer Area => Settings => Users => System => [web service user]=> Checkout API Key.</tooltip>
<backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
<config_path>payment/adyen_pos_cloud/api_key_test</config_path>
</field>
<field id="api_key_live" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0">
<field id="api_key_live" translate="label" type="obscure" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0">
<label>API key for Cloud API LIVE</label>
<tooltip>Copy this from the Live Adyen Customer Area => Settings => Users => System => [web service user]=> Checkout API Key.</tooltip>
<backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
......
......@@ -73,9 +73,13 @@
<label>Elo</label>
<code_alt>elo</code_alt>
</type>
<type id="TROY" order="130">
<type id="TROY" order="120">
<label>Troy</label>
<code_alt>troy</code_alt>
</type>
<type id="DANKORT" order="130">
<label>Dankort</label>
<code_alt>dankort</code_alt>
</type>
</adyen_credit_cards>
</payment>
......@@ -62,6 +62,7 @@
<can_cancel>1</can_cancel>
<can_authorize_vault>1</can_authorize_vault>
<can_capture_vault>1</can_capture_vault>
<threeds2_enabled>1</threeds2_enabled>
<group>adyen</group>
</adyen_cc>
<adyen_cc_vault>
......
......@@ -32,6 +32,9 @@
<event name="payment_method_assign_data_adyen_hpp">
<observer name="adyen_hpp_gateway_data_assign" instance="Adyen\Payment\Observer\AdyenHppDataAssignObserver" />
</event>
<event name="payment_method_assign_data_adyen_pos_cloud">
<observer name="adyen_pos_cloud_gateway_data_assign" instance="Adyen\Payment\Observer\AdyenPosCloudDataAssignObserver" />
</event>
<event name="payment_method_assign_data_adyen_boleto">
<observer name="adyen_boleto_gateway_data_assign" instance="Adyen\Payment\Observer\AdyenBoletoDataAssignObserver" />
</event>
......
......@@ -24,7 +24,7 @@
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Adyen_Payment" setup_version="4.2.1">
<module name="Adyen_Payment" setup_version="4.3.0">
<sequence>
<module name="Magento_Sales"/>
<module name="Magento_Quote"/>
......
......@@ -113,7 +113,7 @@ echo $code; ?>" style="display:none">
var card = checkout.create('card', {
originKey: "<?php echo $block->getCheckoutOriginKeys(); ?>",
loadingContext: "<?php echo $block->getCheckoutContextUrl(); ?>",
environment: "<?php echo $block->getCheckoutEnvironment(); ?>",
type: 'card',
groupTypes: ccTypes,
hideCVC: hideCVC,
......@@ -122,10 +122,14 @@ echo $code; ?>" style="display:none">
// When the state is valid update the input fields
if (state.isValid) {
// Here we enable the button if the component is now valid
jQuery("#<?php /* @noEscape */ echo $code; ?>-number").val(state.data.encryptedCardNumber);
jQuery("#<?php /* @noEscape */ echo $code; ?>-expiryMonth").val(state.data.encryptedExpiryMonth);
jQuery("#<?php /* @noEscape */ echo $code; ?>-expiryYear").val(state.data.encryptedExpiryYear);
jQuery("#<?php /* @noEscape */ echo $code; ?>-cvc").val(state.data.encryptedSecurityCode);
jQuery("#<?php /* @noEscape */ echo $code; ?>-number").val(state.data.paymentMethod.encryptedCardNumber);
jQuery("#<?php /* @noEscape */ echo $code; ?>-expiryMonth").val(state.data.paymentMethod.encryptedExpiryMonth);
jQuery("#<?php /* @noEscape */ echo $code; ?>-expiryYear").val(state.data.paymentMethod.encryptedExpiryYear);
jQuery("#<?php /* @noEscape */ echo $code; ?>-cvc").val(state.data.paymentMethod.encryptedSecurityCode);
}
},
onBrand: function (state) {
if (state.isValid) {
jQuery("#<?php /* @noEscape */ echo $code; ?>-cc_type").val(getCcCodeByAltCode(state.brand));
}
},
......
This diff is collapsed.
......@@ -7,8 +7,11 @@ define(
'underscore',
'Magento_Checkout/js/model/quote',
'Adyen_Payment/js/model/adyen-method-list',
'Magento_Customer/js/model/customer',
'Magento_Checkout/js/model/url-builder',
'mage/storage'
],
function (_, quote, methodList) {
function (_, quote, methodList, customer, urlBuilder, storage) {
'use strict';
return {
......@@ -25,6 +28,43 @@ define(
*/
getAvailablePaymentMethods: function () {
return methodList();
},
/**
* Retrieve the list of available payment methods from the server
*/
retrieveAvailablePaymentMethods: function (callback = null) {
var self = this;
// retrieve payment methods
var serviceUrl,
payload;
if (customer.isLoggedIn()) {
serviceUrl = urlBuilder.createUrl('/carts/mine/retrieve-adyen-payment-methods', {});
} else {
serviceUrl = urlBuilder.createUrl('/guest-carts/:cartId/retrieve-adyen-payment-methods', {
cartId: quote.getQuoteId()
});
}
payload = {
cartId: quote.getQuoteId(),
shippingAddress: quote.shippingAddress()
};
storage.post(
serviceUrl, JSON.stringify(payload)
).done(
function (response) {
self.setPaymentMethods(response);
if (callback !== null) {
callback();
}
}
).fail(
function (response) {
self.setPaymentMethods([]);
}
)
}
};
}
......
......@@ -84,7 +84,6 @@ define(
'expiryYear',
'installment',
'creditCardDetailsValid',
'variant',
'placeOrderAllowed'
]);
......@@ -119,7 +118,7 @@ define(
self.cardComponent = self.checkout.create('card', {
originKey: self.getOriginKey(),
loadingContext: self.getLoadingContext(),
environment: self.getCheckoutEnvironment(),
type: 'card',
hasHolderName: true,
holderNameRequired: true,
......@@ -128,13 +127,12 @@ define(
onChange: function (state, component) {
if (!!state.isValid && !component.state.errors.encryptedSecurityCode) {
self.storeCc = !!state.data.storeDetails;
self.variant(state.brand);
self.creditCardNumber(state.data.encryptedCardNumber);
self.expiryMonth(state.data.encryptedExpiryMonth);
self.expiryYear(state.data.encryptedExpiryYear);
self.securityCode(state.data.encryptedSecurityCode);
self.creditCardOwner(state.data.holderName);
self.storeCc = !!state.data.storePaymentMethod;
self.creditCardNumber(state.data.paymentMethod.encryptedCardNumber);
self.expiryMonth(state.data.paymentMethod.encryptedExpiryMonth);
self.expiryYear(state.data.paymentMethod.encryptedExpiryYear);
self.securityCode(state.data.paymentMethod.encryptedSecurityCode);
self.creditCardOwner(state.data.paymentMethod.holderName);
self.creditCardDetailsValid(true);
self.placeOrderAllowed(true);
} else {
......@@ -248,28 +246,44 @@ define(
self.threeDS2ChallengeComponent = self.checkout
.create('threeDS2Challenge', {
challengeToken: token,
size: '05',
onComplete: function (result) {
popupModal.modal("closeModal");
self.closeModal(popupModal);
fullScreenLoader.startLoader();
threeds2.processThreeDS2(result.data).done(function (responseJSON) {
self.validateThreeDS2OrPlaceOrder(responseJSON);
}).error(function () {
popupModal.modal("closeModal");
self.isPlaceOrderActionAllowed(true);
fullScreenLoader.stopLoader();
});
},
onError: function (error) {
self.closeModal(popupModal);
console.log(JSON.stringify(error));
}
});
self.threeDS2ChallengeComponent.mount(threeDS2Node);
}
},
/**
* This method is a workaround to close the modal in the right way and reconstruct the threeDS2Modal.
* This will solve issues when you cancel the 3DS2 challenge and retry the payment
*/
closeModal: function (popupModal) {
popupModal.modal("closeModal");
$('.threeDS2Modal').remove();
$('.modals-overlay').remove();
// reconstruct the threeDS2Modal container again otherwise component can not find the threeDS2Modal
$('#threeDS2Wrapper').append("<div id=\"threeDS2Modal\">" +
"<div id=\"threeDS2Container\"></div>" +
"</div>");
},
/**
* Builds the payment details part of the payment information reqeust
*
* @returns {{method: *, additional_data: {card_brand: *, cc_type: *, number: *, cvc: *, expiryMonth: *, expiryYear: *, holderName: *, store_cc: (boolean|*), number_of_installments: *, java_enabled: boolean, screen_color_depth: number, screen_width, screen_height, timezone_offset: *}}}
* @returns {{method: *, additional_data: {cc_type: *, number: *, cvc: *, expiryMonth: *, expiryYear: *, holderName: *, store_cc: (boolean|*), number_of_installments: *, java_enabled: () => boolean, screen_color_depth: number, screen_width, screen_height, timezone_offset: *, language: *}}}
*/
getCcData: function () {
const browserInfo = threeDS2Utils.getBrowserInfo();
......@@ -277,7 +291,6 @@ define(
var data = {
'method': this.item.method,
additional_data: {
'card_brand': this.variant(),
'cc_type': this.creditCardType(),
'number': this.creditCardNumber(),
'cvc': this.securityCode(),
......@@ -306,7 +319,6 @@ define(
return {
'method': this.item.method,
additional_data: {
'card_brand': this.variant(),
'cc_type': this.creditCardType(),
'store_cc': this.storeCc,
'number_of_installments': this.installment()
......@@ -373,7 +385,9 @@ define(
if (self.redirectAfterPlaceOrder) {
// use custom redirect Link for supporting 3D secure
window.location.replace(url.build(window.checkoutConfig.payment[quote.paymentMethod().method].redirectUrl));
window.location.replace(url.build(
window.checkoutConfig.payment[quote.paymentMethod().method].redirectUrl)
);
}
}
);
......@@ -454,8 +468,8 @@ define(
getOriginKey: function () {
return window.checkoutConfig.payment.adyenCc.originKey;
},
getLoadingContext: function () {
return window.checkoutConfig.payment.adyenCc.checkoutUrl;
getCheckoutEnvironment: function () {
return window.checkoutConfig.payment.adyenCc.checkoutEnvironment;
},
getLocale: function () {
return window.checkoutConfig.payment.adyenCc.locale;
......
......@@ -42,6 +42,7 @@ define(
var brandCode = ko.observable(null);
var paymentMethod = ko.observable(null);
var messageComponents;
var shippingAddressCountryCode = quote.shippingAddress().countryId;
/**
* Shareble adyen checkout component
* @type {AdyenCheckout}
......@@ -90,28 +91,9 @@ define(
// reset variable:
adyenPaymentService.setPaymentMethods();
// retrieve payment methods
var serviceUrl,
payload;
if (customer.isLoggedIn()) {
serviceUrl = urlBuilder.createUrl('/carts/mine/retrieve-adyen-payment-methods', {});
} else {
serviceUrl = urlBuilder.createUrl('/guest-carts/:cartId/retrieve-adyen-payment-methods', {
cartId: quote.getQuoteId()
});
}
payload = {
cartId: quote.getQuoteId(),
shippingAddress: quote.shippingAddress()
};
storage.post(
serviceUrl, JSON.stringify(payload)
).done(
function (response) {
adyenPaymentService.setPaymentMethods(response);
if (JSON.stringify(response).indexOf("ratepay") > -1) {
adyenPaymentService.retrieveAvailablePaymentMethods(function() {
let paymentMethods = adyenPaymentService.getAvailablePaymentMethods();
if (JSON.stringify(paymentMethods).indexOf("ratepay") > -1) {
var ratePayId = window.checkoutConfig.payment.adyenHpp.ratePayId;
var dfValueRatePay = self.getRatePayDeviceIdentToken();
......@@ -130,7 +112,7 @@ define(
// create component needs to be in initialize method
var messageComponents = {};
_.map(response, function (value) {
_.map(paymentMethods, function (value) {
var messageContainer = new Messages();
var name = 'messages-' + self.getBrandCodeFromPaymentMethod(value);
......@@ -149,14 +131,21 @@ define(
});
self.messageComponents = messageComponents;
fullScreenLoader.stopLoader();
}
).fail(function (error) {
fullScreenLoader.stopLoader();
});
},
getAdyenHppPaymentMethods: function () {
var self = this;
let currentShippingAddressCountryCode = quote.shippingAddress().countryId;
// retrieve new payment methods if country code changed
if (shippingAddressCountryCode != currentShippingAddressCountryCode) {
fullScreenLoader.startLoader();
adyenPaymentService.retrieveAvailablePaymentMethods();
shippingAddressCountryCode = currentShippingAddressCountryCode;
fullScreenLoader.stopLoader();
}
var paymentMethods = adyenPaymentService.getAvailablePaymentMethods();
var paymentList = _.map(paymentMethods, function (value) {
......@@ -289,16 +278,28 @@ define(
result.getBankAccountNumberMaxLength = function () {
return 17;
};
/**
* Finds the issuer property in the payment method's response and if available returns it's index
* @returns
*/
result.findIssuersProperty = function () {
var issuerKey = false;
if (typeof value.details !== 'undefined') {
$.each(value.details, function(key, detail) {
if (typeof detail.items !== 'undefined' && detail.key == 'issuer') {
issuerKey = key;
}
});
}
return issuerKey;
}
/**
* Checks if the payment method has issuers property available
* @returns {boolean}
*/
result.hasIssuersProperty = function () {
if (
typeof value.details !== 'undefined' &&
typeof value.details[0].items !== 'undefined' &&
value.details[0].key == 'issuer'
) {
if (result.findIssuersProperty() !== false) {
return true;
}
......@@ -309,7 +310,7 @@ define(
* @returns {boolean}
*/
result.hasIssuersAvailable = function () {
if (result.hasIssuersProperty() && value.details[0].items.length > 0) {
if (result.hasIssuersProperty() && value.details[result.findIssuersProperty()].items.length > 0) {
return true;
}
......@@ -321,7 +322,7 @@ define(
*/
result.getIssuers = function() {
if (result.hasIssuersAvailable()) {
return value.details[0].items;
return value.details[result.findIssuersProperty()].items;
}
return [];
......@@ -372,7 +373,7 @@ define(
items: result.getIssuers(),
onChange: function (state) {
if (!!state.isValid) {
result.issuer(state.data.issuer);
result.issuer(state.data.paymentMethod.issuer);
result.isPlaceOrderAllowed(true);
} else {
......@@ -397,8 +398,8 @@ define(
countryCode: self.getLocale(),
onChange: function (state) {
if (!!state.isValid) {
result.ownerName(state.data["sepa.ownerName"]);
result.ibanNumber(state.data["sepa.ibanNumber"]);
result.ownerName(state.data.paymentMethod["sepa.ownerName"]);
result.ibanNumber(state.data.paymentMethod["sepa.ibanNumber"]);
result.isPlaceOrderAllowed(true);
} else {
result.isPlaceOrderAllowed(false);
......@@ -430,9 +431,9 @@ define(
},
onChange: function (state) {
if (!!state.isValid) {
result.dob(state.data.personalDetails.dateOfBirth);
result.telephone(state.data.personalDetails.telephoneNumber);
result.gender(state.data.personalDetails.gender);
result.dob(state.data.paymentMethod.personalDetails.dateOfBirth);
result.telephone(state.data.paymentMethod.personalDetails.telephoneNumber);
result.gender(state.data.paymentMethod.personalDetails.gender);
result.isPlaceOrderAllowed(true);
} else {
result.isPlaceOrderAllowed(false);
......@@ -455,9 +456,9 @@ define(
},
onChange: function (state) {
if (!!state.isValid) {
result.dob(state.data.personalDetails.dateOfBirth);
result.telephone(state.data.personalDetails.telephoneNumber);
result.gender(state.data.personalDetails.gender);
result.dob(state.data.paymentMethod.personalDetails.dateOfBirth);
result.telephone(state.data.paymentMethod.personalDetails.telephoneNumber);
result.gender(state.data.paymentMethod.personalDetails.gender);
result.isPlaceOrderAllowed(true);
} else {
result.isPlaceOrderAllowed(false);
......
......@@ -110,7 +110,7 @@ define(
var checkout = new AdyenCheckout({
locale: self.getLocale(),
originKey: self.getOriginKey(),
loadingContext: self.getLoadingContext(),
environment: self.getCheckoutEnvironment(),
risk: {
enabled: false
}
......@@ -256,9 +256,10 @@ define(
isValid(true);
if (typeof state.data !== 'undefined' &&
typeof state.data.encryptedSecurityCode !== 'undefined'
typeof state.data.paymentMethod !== 'undefined' &&
typeof state.data.paymentMethod.encryptedSecurityCode !== 'undefined'
) {
self.encryptedCreditCardVerificationNumber = state.data.encryptedSecurityCode;
self.encryptedCreditCardVerificationNumber = state.data.paymentMethod.encryptedSecurityCode;
}
} else {
self.encryptedCreditCardVerificationNumber = '';
......@@ -268,14 +269,6 @@ define(
isValid(false);
}
}
// When we move to the component v2.2 it should be removed
if (self.agreement_data.variant == "maestro" &&
component.state.errors.encryptedSecurityCode
) {
self.placeOrderAllowed(false);
isValid(false);
}
}
})
.mount(oneClickCardNode);
......@@ -566,8 +559,8 @@ define(
getOriginKey: function () {
return window.checkoutConfig.payment.adyenOneclick.originKey;
},
getLoadingContext: function () {
return window.checkoutConfig.payment.adyenOneclick.checkoutUrl;
getCheckoutEnvironment: function () {
return window.checkoutConfig.payment.adyenOneclick.checkoutEnvironment;
}
});
}
......
......@@ -57,12 +57,26 @@ define(
agreementsAssigner(paymentData);
serviceUrl = urlBuilder.createUrl('/adyen/initiate', {});
fullScreenLoader.startLoader();
let payload = {
"payload": JSON.stringify({terminal_id: self.terminalId()})
}
return storage.post(
serviceUrl
serviceUrl,
JSON.stringify(payload)
).always(function(){
self.placeOrderPos()});
return false;
},
initObservable: function () {
this._super()
.observe([
'terminalId'
]);
return this;
},
posComplete: function () {
this.afterPlaceOrder();
if (this.redirectAfterPlaceOrder) {
......@@ -91,6 +105,33 @@ define(
}
)
},
getConnectedTerminals: function() {
let connectedTerminals = [];
const connectedTerminalsList = window.checkoutConfig.payment.adyenPos.connectedTerminals;
for (let i = 0; i < connectedTerminalsList.length; i++) {
connectedTerminals.push(
{
key: connectedTerminalsList[i],
value: connectedTerminalsList[i]
}
);
}
return connectedTerminals;
},
/**
* Get data for place order
* @returns {{method: *}}
*/
getData: function () {
return {
'method': this.item.method,
additional_data: {
'terminal_id': this.terminalId()
}
};
},
showLogo: function () {
return window.checkoutConfig.payment.adyen.showLogo;
},
......
......@@ -77,9 +77,12 @@
<div afterRender="renderSecureFields()" data-bind="attr: { id: 'cardContainer'}"></div>
</div>
<div id="threeDS2Wrapper">
<div id="threeDS2Modal">
<div id="threeDS2Container"></div>
</div>
</div>
<!-- ko if: (hasInstallments())-->
......
......@@ -50,6 +50,25 @@
<!--/ko-->
</div>
<div class="field required"
data-bind="attr: {id: getCode() + '_connected_terminals_div'}, visible: getConnectedTerminals().length > 0">
<label data-bind="attr: {for: getCode() + '_connected_terminals'}" class="label">
<span><!-- ko text: $t('Connected terminals')--><!-- /ko --></span>
</label>
<div class="control">
<select class="select"
name="paymentMethod[connected_terminals]"
data-bind="attr: {id: getCode() + '_connected_terminals', 'data-container': getCode() + '-connected-terminals', 'data-validate': JSON.stringify({required:true})},
options: getConnectedTerminals(),
optionsValue: 'value',
optionsText: 'key',
value: terminalId"
>
</select>
</div>
</div>
<div class="checkout-agreements-block">
<!-- ko foreach: $parent.getRegion('before-place-order') -->
<!-- ko template: getTemplate() --><!-- /ko -->
......@@ -62,8 +81,7 @@
data-bind="
click: initiate,
attr: {title: $t('Place Order')},
enable: (getCode() == isChecked()),
css: {disabled: !isPlaceOrderActionAllowed()}
enable: getConnectedTerminals().length > 0
"
disabled>
<span data-bind="text: $t('Place Order')"></span>
......@@ -72,4 +90,3 @@
</div>
</div>
</div>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment