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 48b45ef5 authored by Rik ter Beek's avatar Rik ter Beek Committed by GitHub

Merge pull request #367 from Adyen/PW-790(incomplete)

Pw 790 Save and update billing agreements based on the response from the payments call
parents fa758982 37705217
...@@ -29,12 +29,12 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface ...@@ -29,12 +29,12 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface
/** /**
* @var \Adyen\Payment\Helper\Data * @var \Adyen\Payment\Helper\Data
*/ */
protected $_adyenHelper; protected $adyenHelper;
/** /**
* @var \Magento\AdminNotification\Model\InboxFactory * @var \Magento\AdminNotification\Model\InboxFactory
*/ */
protected $_inboxFactory; protected $inboxFactory;
/** /**
* @var \Magento\Store\Model\StoreManagerInterface * @var \Magento\Store\Model\StoreManagerInterface
...@@ -53,6 +53,8 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface ...@@ -53,6 +53,8 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface
* *
* @param \Adyen\Payment\Helper\Data $adyenHelper * @param \Adyen\Payment\Helper\Data $adyenHelper
* @param \Magento\AdminNotification\Model\InboxFactory $inboxFactory * @param \Magento\AdminNotification\Model\InboxFactory $inboxFactory
* @param \Magento\Store\Model\StoreManagerInterface $storeManagerInterface
* @param \Magento\Backend\Model\Auth\Session $authSession
*/ */
public function __construct( public function __construct(
\Adyen\Payment\Helper\Data $adyenHelper, \Adyen\Payment\Helper\Data $adyenHelper,
...@@ -60,8 +62,8 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface ...@@ -60,8 +62,8 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface
\Magento\Store\Model\StoreManagerInterface $storeManagerInterface, \Magento\Store\Model\StoreManagerInterface $storeManagerInterface,
\Magento\Backend\Model\Auth\Session $authSession \Magento\Backend\Model\Auth\Session $authSession
) { ) {
$this->_adyenHelper = $adyenHelper; $this->adyenHelper = $adyenHelper;
$this->_inboxFactory = $inboxFactory; $this->inboxFactory = $inboxFactory;
$this->storeManagerInterface = $storeManagerInterface; $this->storeManagerInterface = $storeManagerInterface;
$this->authSession = $authSession; $this->authSession = $authSession;
} }
...@@ -83,24 +85,29 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface ...@@ -83,24 +85,29 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface
*/ */
public function isDisplayed() public function isDisplayed()
{ {
// Only execute the query the first time you access the Admin page // Only execute the query the first time you access the Admin page
if ($this->authSession->isFirstPageAfterLogin() && empty($this->_adyenHelper->getAPIKey())) { if ($this->authSession->isFirstPageAfterLogin() &&
!empty($this->adyenHelper->getWsUsername()) &&
empty($this->adyenHelper->getAPIKey())
) {
try { try {
$title = "Adyen extension requires the API KEY!"; $title = 'Adyen extension requires the API KEY!';
$messageData[] = array( $messageData[] = [
'severity' => $this->getSeverity(), 'severity' => $this->getSeverity(),
'date_added' => date("Y-m-d"), 'date_added' => date('Y-m-d'),
'title' => $title, 'title' => $title,
'description' => $this->getText(), 'description' => $this->getText(),
'url' => "https://docs.adyen.com/developers/plug-ins-and-partners/magento-2/set-up-the-plugin-in-magento#step3configuretheplugininmagento", 'url' => 'https://docs.adyen.com/developers/plug-ins-and-partners/magento-2/' .
); 'set-up-the-plugin-in-magento#step3configuretheplugininmagento',
];
/* /*
* The parse function checks if the $versionData message exists in the inbox, * The parse function checks if the $versionData message exists in the inbox,
* otherwise it will create it and add it to the inbox. * otherwise it will create it and add it to the inbox.
*/ */
$this->_inboxFactory->create()->parse(array_reverse($messageData)); $this->inboxFactory->create()->parse($messageData);
return true; return true;
} catch (\Exception $e) { } catch (\Exception $e) {
...@@ -114,14 +121,16 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface ...@@ -114,14 +121,16 @@ class APIKeyMessage implements \Magento\Framework\Notification\MessageInterface
/** /**
* Retrieve system message text * Retrieve system message text
* *
* @return \Magento\Framework\Phrase * @return string
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/ */
public function getText() public function getText()
{ {
if (!empty($this->_adyenHelper->getWsUsername())) { $message = '';
$message = "Please provide API-KEY for the webservice user " . $this->_adyenHelper->getWsUsername() . " for default/store " . $this->storeManagerInterface->getStore()->getName(); if (!empty($this->adyenHelper->getWsUsername())) {
}else{ $message = 'Please provide API-KEY for the webservice user ' .
$message = "Please provide API-KEY for default/store " . $this->storeManagerInterface->getStore()->getName(); $this->adyenHelper->getWsUsername() . ' for default/store ' .
$this->storeManagerInterface->getStore()->getName();
} }
return $message; return $message;
......
...@@ -235,9 +235,7 @@ class Redirect extends \Magento\Payment\Block\Form ...@@ -235,9 +235,7 @@ class Redirect extends \Magento\Payment\Block\Form
); );
$formFields['shopperEmail'] = $shopperEmail; $formFields['shopperEmail'] = $shopperEmail;
// recurring // recurring
$recurringType = trim($this->_adyenHelper->getAdyenAbstractConfigData( $recurringType = $this->_adyenHelper->getRecurringTypeFromOneclickRecurringSetting();
'recurring_type'
));
$brandCode = $this->_order->getPayment()->getAdditionalInformation( $brandCode = $this->_order->getPayment()->getAdditionalInformation(
\Adyen\Payment\Observer\AdyenHppDataAssignObserver::BRAND_CODE \Adyen\Payment\Observer\AdyenHppDataAssignObserver::BRAND_CODE
); );
......
...@@ -183,7 +183,7 @@ class PayByMailCommand implements CommandInterface ...@@ -183,7 +183,7 @@ class PayByMailCommand implements CommandInterface
$formFields['shopperEmail'] = $shopperEmail; $formFields['shopperEmail'] = $shopperEmail;
// recurring // recurring
$recurringType = trim($this->_adyenHelper->getAdyenAbstractConfigData('recurring_type', $storeId)); $recurringType = $this->_adyenHelper->getRecurringTypeFromOneclickRecurringSetting($storeId);
$sessionValidity = $this->_adyenHelper->getAdyenPayByMailConfigData('session_validity', $storeId); $sessionValidity = $this->_adyenHelper->getAdyenPayByMailConfigData('session_validity', $storeId);
......
...@@ -65,7 +65,11 @@ class CcAuthorizationDataBuilder implements BuilderInterface ...@@ -65,7 +65,11 @@ class CcAuthorizationDataBuilder implements BuilderInterface
$storeId = $order->getStoreId(); $storeId = $order->getStoreId();
$request = []; $request = [];
// If ccType is set use this. For bcmc you need bcmc otherwise it will fail
$request['paymentMethod']['type'] = "scheme"; $request['paymentMethod']['type'] = "scheme";
if ($payment->getAdditionalInformation(AdyenCcDataAssignObserver::VARIANT)) {
$request['paymentMethod']['type'] = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::VARIANT);
}
if ($cardNumber = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::CREDIT_CARD_NUMBER)) { if ($cardNumber = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::CREDIT_CARD_NUMBER)) {
$request['paymentMethod']['encryptedCardNumber'] = $cardNumber; $request['paymentMethod']['encryptedCardNumber'] = $cardNumber;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
* *
* Author: Adyen <magento@adyen.com> * Author: Adyen <magento@adyen.com>
*/ */
namespace Adyen\Payment\Gateway\Request; namespace Adyen\Payment\Gateway\Request;
use Magento\Payment\Gateway\Request\BuilderInterface; use Magento\Payment\Gateway\Request\BuilderInterface;
...@@ -50,9 +51,11 @@ class RecurringDataBuilder implements BuilderInterface ...@@ -50,9 +51,11 @@ class RecurringDataBuilder implements BuilderInterface
$this->appState = $context->getAppState(); $this->appState = $context->getAppState();
} }
/** /**
* @param array $buildSubject * @param array $buildSubject
* @return array * @return array
* @throws \Magento\Framework\Exception\LocalizedException
*/ */
public function build(array $buildSubject) public function build(array $buildSubject)
{ {
...@@ -61,45 +64,25 @@ class RecurringDataBuilder implements BuilderInterface ...@@ -61,45 +64,25 @@ class RecurringDataBuilder implements BuilderInterface
/** @var \Magento\Payment\Gateway\Data\PaymentDataObject $paymentDataObject */ /** @var \Magento\Payment\Gateway\Data\PaymentDataObject $paymentDataObject */
$paymentDataObject = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($buildSubject); $paymentDataObject = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($buildSubject);
$payment = $paymentDataObject->getPayment(); $payment = $paymentDataObject->getPayment();
// Needs to change when oneclick,cc using facade impl.
$paymentMethodCode = $payment->getMethodInstance()->getCode();
$customerId = $payment->getOrder()->getCustomerId();
$storeId = null; $storeId = null;
if ($this->appState->getAreaCode() === \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) { if ($this->appState->getAreaCode() === \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) {
$storeId = $payment->getOrder()->getStoreId(); $storeId = $payment->getOrder()->getStoreId();
} }
$recurringType = $this->adyenHelper->getAdyenAbstractConfigData('recurring_type', $storeId);
// set the recurring type $enableOneclick = $this->adyenHelper->getAdyenAbstractConfigData('enable_oneclick', $storeId);
$recurringContractType = null; $enableRecurring = $this->adyenHelper->getAdyenAbstractConfigData('enable_recurring', $storeId);
if ($recurringType) {
if ($paymentMethodCode == \Adyen\Payment\Model\Ui\AdyenOneclickConfigProvider::CODE) { if ($enableOneclick) {
/* $result['enableOneclick'] = true;
* For ONECLICK look at the recurringPaymentType that the merchant
* has selected in Adyen ONECLICK settings
*/
if ($payment->getAdditionalInformation('customer_interaction')) {
$recurringContractType = \Adyen\Payment\Model\RecurringType::ONECLICK;
} else {
$recurringContractType = \Adyen\Payment\Model\RecurringType::RECURRING;
}
} elseif ($paymentMethodCode == \Adyen\Payment\Model\Ui\AdyenCcConfigProvider::CODE) {
if ($payment->getAdditionalInformation("store_cc") == "" &&
($recurringType == "ONECLICK,RECURRING" || $recurringType == "RECURRING")) {
$recurringContractType = \Adyen\Payment\Model\RecurringType::RECURRING;
} elseif ($payment->getAdditionalInformation("store_cc") == "1") {
$recurringContractType = $recurringType;
}
} else {
$recurringContractType = $recurringType;
} }
if ($enableRecurring) {
$result['enableRecurring'] = true;
} }
// only when recurringContractType is set and when a customer is loggedIn if ($payment->getAdditionalInformation('store_cc') === '1') {
if ($recurringContractType && $customerId > 0) { $result['paymentMethod']['storeDetails'] = true;
$recurring = ['contract' => $recurringContractType];
$result['recurring'] = $recurring;
} }
return $result; return $result;
......
...@@ -27,6 +27,18 @@ use Magento\Payment\Gateway\Response\HandlerInterface; ...@@ -27,6 +27,18 @@ use Magento\Payment\Gateway\Response\HandlerInterface;
class CheckoutPaymentsDetailsHandler implements HandlerInterface class CheckoutPaymentsDetailsHandler implements HandlerInterface
{ {
/**
* @var \Adyen\Payment\Helper\Data
*/
protected $adyenHelper;
public function __construct(
\Adyen\Payment\Helper\Data $adyenHelper
) {
$this->adyenHelper = $adyenHelper;
}
/** /**
* @param array $handlingSubject * @param array $handlingSubject
* @param array $response * @param array $response
...@@ -53,9 +65,16 @@ class CheckoutPaymentsDetailsHandler implements HandlerInterface ...@@ -53,9 +65,16 @@ class CheckoutPaymentsDetailsHandler implements HandlerInterface
$payment->setTransactionId($response['pspReference']); $payment->setTransactionId($response['pspReference']);
} }
if (!empty($response['additionalData']) &&
!empty($response['additionalData']['recurring.recurringDetailReference'])
) {
$order = $payment->getOrder();
$this->adyenHelper->createAdyenBillingAgreement($order, $response['additionalData']);
}
// do not close transaction so you can do a cancel() and void // do not close transaction so you can do a cancel() and void
$payment->setIsTransactionClosed(false); $payment->setIsTransactionClosed(false);
$payment->setShouldCloseParentTransaction(false); $payment->setShouldCloseParentTransaction(false);
} }
} }
...@@ -35,8 +35,8 @@ class Data extends AbstractHelper ...@@ -35,8 +35,8 @@ class Data extends AbstractHelper
const LIVE = 'live'; const LIVE = 'live';
const CHECKOUT_CONTEXT_URL_LIVE = 'https://checkoutshopper-live.adyen.com/checkoutshopper/'; const CHECKOUT_CONTEXT_URL_LIVE = 'https://checkoutshopper-live.adyen.com/checkoutshopper/';
const CHECKOUT_CONTEXT_URL_TEST = 'https://checkoutshopper-test.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.0.0-beta.4/adyen.js'; const CHECKOUT_COMPONENT_JS_LIVE = 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/2.0.0/adyen.js';
const CHECKOUT_COMPONENT_JS_TEST = 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/2.0.0-beta.4/adyen.js'; const CHECKOUT_COMPONENT_JS_TEST = 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/2.0.0/adyen.js';
/** /**
* @var \Magento\Framework\Encryption\EncryptorInterface * @var \Magento\Framework\Encryption\EncryptorInterface
...@@ -108,9 +108,13 @@ class Data extends AbstractHelper ...@@ -108,9 +108,13 @@ class Data extends AbstractHelper
*/ */
protected $cache; protected $cache;
/**
* @var \Adyen\Payment\Model\Billing\AgreementFactory
*/
protected $billingAgreementFactory;
/** /**
* Data constructor. * Data constructor.
*
* @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Framework\App\Helper\Context $context
* @param \Magento\Framework\Encryption\EncryptorInterface $encryptor * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
* @param \Magento\Framework\Config\DataInterface $dataStorage * @param \Magento\Framework\Config\DataInterface $dataStorage
...@@ -126,6 +130,7 @@ class Data extends AbstractHelper ...@@ -126,6 +130,7 @@ class Data extends AbstractHelper
* @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger * @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger
* @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Framework\App\CacheInterface $cache * @param \Magento\Framework\App\CacheInterface $cache
* @param \Adyen\Payment\Model\Billing\AgreementFactory $billingAgreementFactory
*/ */
public function __construct( public function __construct(
\Magento\Framework\App\Helper\Context $context, \Magento\Framework\App\Helper\Context $context,
...@@ -142,7 +147,8 @@ class Data extends AbstractHelper ...@@ -142,7 +147,8 @@ class Data extends AbstractHelper
\Magento\Framework\App\ProductMetadataInterface $productMetadata, \Magento\Framework\App\ProductMetadataInterface $productMetadata,
\Adyen\Payment\Logger\AdyenLogger $adyenLogger, \Adyen\Payment\Logger\AdyenLogger $adyenLogger,
\Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\App\CacheInterface $cache \Magento\Framework\App\CacheInterface $cache,
\Adyen\Payment\Model\Billing\AgreementFactory $billingAgreementFactory
) { ) {
parent::__construct($context); parent::__construct($context);
$this->_encryptor = $encryptor; $this->_encryptor = $encryptor;
...@@ -159,6 +165,7 @@ class Data extends AbstractHelper ...@@ -159,6 +165,7 @@ class Data extends AbstractHelper
$this->adyenLogger = $adyenLogger; $this->adyenLogger = $adyenLogger;
$this->storeManager = $storeManager; $this->storeManager = $storeManager;
$this->cache = $cache; $this->cache = $cache;
$this->billingAgreementFactory = $billingAgreementFactory;
} }
/** /**
...@@ -1345,14 +1352,13 @@ class Data extends AbstractHelper ...@@ -1345,14 +1352,13 @@ class Data extends AbstractHelper
{ {
return new \Adyen\Service\PosPayment($client); return new \Adyen\Service\PosPayment($client);
} }
return $client;
}
/** /**
* @return \Adyen\Client * @return \Adyen\Client
* @throws \Adyen\AdyenException * @throws \Adyen\AdyenException
*/ */
private function createAdyenClient() { private function createAdyenClient()
{
return new \Adyen\Client(); return new \Adyen\Client();
} }
...@@ -1412,7 +1418,8 @@ class Data extends AbstractHelper ...@@ -1412,7 +1418,8 @@ class Data extends AbstractHelper
* @param int|null $storeId * @param int|null $storeId
* @return string * @return string
*/ */
public function getCheckoutContextUrl($storeId = null) { public function getCheckoutContextUrl($storeId = null)
{
if ($this->isDemoMode($storeId)) { if ($this->isDemoMode($storeId)) {
return self::CHECKOUT_CONTEXT_URL_TEST; return self::CHECKOUT_CONTEXT_URL_TEST;
} }
...@@ -1434,11 +1441,84 @@ class Data extends AbstractHelper ...@@ -1434,11 +1441,84 @@ class Data extends AbstractHelper
* @param int|null $storeId * @param int|null $storeId
* @return string * @return string
*/ */
public function getCheckoutCardComponentJs($storeId = null) { public function getCheckoutCardComponentJs($storeId = null)
{
if ($this->isDemoMode($storeId)) { if ($this->isDemoMode($storeId)) {
return self::CHECKOUT_COMPONENT_JS_TEST; return self::CHECKOUT_COMPONENT_JS_TEST;
} }
return self::CHECKOUT_COMPONENT_JS_LIVE; return self::CHECKOUT_COMPONENT_JS_LIVE;
} }
public function createAdyenBillingAgreement($order, $additionalData)
{
if (!empty($additionalData['recurring.recurringDetailReference'])) {
$listRecurringContracts = null;
try {
// Get or create billing agreement
$billingAgreement = $this->billingAgreementFactory->create();
$billingAgreement->load($additionalData['recurring.recurringDetailReference'], 'reference_id');
// check if BA exists
if (!($billingAgreement && $billingAgreement->getAgreementId() > 0 && $billingAgreement->isValid())) {
// create new BA
$this->adyenLogger->addAdyenDebug("Creating new Billing Agreement");
$billingAgreement = $this->billingAgreementFactory->create();
$billingAgreement->setStoreId($order->getStoreId());
$billingAgreement->importOrderPayment($order->getPayment());
$message = __('Created billing agreement #%1.',
$additionalData['recurring.recurringDetailReference']);
} else {
$billingAgreement->setIsObjectChanged(true);
$message = __('Updated billing agreement #%1.',
$additionalData['recurring.recurringDetailReference']);
}
// Populate billing agreement data
$billingAgreement->setCcBillingAgreement($additionalData);
if ($billingAgreement->isValid()) {
// save into sales_billing_agreement_order
$billingAgreement->addOrderRelation($order);
// add to order to save agreement
$order->addRelatedObject($billingAgreement);
} else {
$message = __('Failed to create billing agreement for this order.');
throw new \Exception($message);
}
} catch (\Exception $exception) {
$message = $exception->getMessage();
$this->adyenLogger->error("exception: " . $message);
}
$comment = $order->addStatusHistoryComment($message);
$order->addRelatedObject($comment);
}
}
/**
* For backwards compatibility get the recurringType used for HPP + current billing agreements
*
* @param null $storeId
* @return null|string
*/
public function getRecurringTypeFromOneclickRecurringSetting($storeId = null)
{
$enableOneclick = $this->getAdyenAbstractConfigData('enable_oneclick', $storeId);
$enableRecurring = $this->getAdyenAbstractConfigData('enable_recurring', $storeId);
if ($enableOneclick && $enableRecurring) {
return \Adyen\Payment\Model\RecurringType::ONECLICK_RECURRING;
} elseif ($enableOneclick && !$enableRecurring) {
return \Adyen\Payment\Model\RecurringType::ONECLICK;
} elseif (!$enableOneclick && $enableRecurring) {
return \Adyen\Payment\Model\RecurringType::RECURRING;
} else {
return \Adyen\Payment\Model\RecurringType::NONE;
}
return null;
}
} }
...@@ -119,6 +119,8 @@ class PaymentRequest extends DataObject ...@@ -119,6 +119,8 @@ class PaymentRequest extends DataObject
throw new \Magento\Framework\Exception\LocalizedException(__('3D secure failed')); throw new \Magento\Framework\Exception\LocalizedException(__('3D secure failed'));
} }
$this->_adyenHelper->createAdyenBillingAgreement($order, $result['additionalData']);
return $result; return $result;
} }
......
...@@ -29,7 +29,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement ...@@ -29,7 +29,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement
/** /**
* @var \Adyen\Payment\Helper\Data * @var \Adyen\Payment\Helper\Data
*/ */
private $_adyenHelper; private $adyenHelper;
/** /**
* Agreement constructor. * Agreement constructor.
...@@ -66,7 +66,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement ...@@ -66,7 +66,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement
$data $data
); );
$this->_adyenHelper = $adyenHelper; $this->adyenHelper = $adyenHelper;
} }
/** /**
...@@ -105,7 +105,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement ...@@ -105,7 +105,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement
// Billing agreement is CC // Billing agreement is CC
if (isset($data['card']['number'])) { if (isset($data['card']['number'])) {
$ccType = $data['variant']; $ccType = $data['variant'];
$ccTypes = $this->_adyenHelper->getCcTypesAltData(); $ccTypes = $this->adyenHelper->getCcTypesAltData();
if (isset($ccTypes[$ccType])) { if (isset($ccTypes[$ccType])) {
$ccType = $ccTypes[$ccType]['name']; $ccType = $ccTypes[$ccType]['name'];
...@@ -123,7 +123,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement ...@@ -123,7 +123,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement
} }
if ($data['variant'] == 'paypal') { if ($data['variant'] == 'paypal') {
$email = ""; $email = '';
if (isset($data['tokenDetails']['tokenData']['EmailId'])) { if (isset($data['tokenDetails']['tokenData']['EmailId'])) {
$email = $data['tokenDetails']['tokenData']['EmailId']; $email = $data['tokenDetails']['tokenData']['EmailId'];
...@@ -150,9 +150,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement ...@@ -150,9 +150,7 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement
public function setAgreementData($data) public function setAgreementData($data)
{ {
if (is_array($data)) { if (is_array($data)) {
unset($data['creationDate']); unset($data['creationDate'], $data['recurringDetailReference'], $data['payment_method']);
unset($data['recurringDetailReference']);
unset($data['payment_method']);
} }
$this->setData('agreement_data', json_encode($data)); $this->setData('agreement_data', json_encode($data));
...@@ -166,4 +164,51 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement ...@@ -166,4 +164,51 @@ class Agreement extends \Magento\Paypal\Model\Billing\Agreement
{ {
return json_decode($this->getData('agreement_data'), true); return json_decode($this->getData('agreement_data'), true);
} }
public function setCcBillingAgreement($contractDetail)
{
$this
->setMethodCode('adyen_oneclick')
->setReferenceId($contractDetail['recurring.recurringDetailReference']);
// Billing agreement is CC
if (isset($contractDetail['cardBin']) &&
isset($contractDetail['cardHolderName']) &&
isset($contractDetail['cardSummary']) &&
isset($contractDetail['expiryDate']) &&
isset($contractDetail['paymentMethod'])) {
$ccType = $contractDetail['paymentMethod'];
$ccTypes = $this->adyenHelper->getCcTypesAltData();
if (isset($ccTypes[$ccType])) {
$ccType = $ccTypes[$ccType]['name'];
}
$label = __(
'%1, %2, **** %3',
$ccType,
$contractDetail['cardHolderName'],
$contractDetail['cardSummary']
);
$this->setAgreementLabel($label);
}
$expiryDate = explode('/', $contractDetail['expiryDate']);
$recurringType = $this->adyenHelper->getRecurringTypeFromOneclickRecurringSetting();
$agreementData = [
'card' => [
'holderName' => $contractDetail['cardHolderName'],
'number' => $contractDetail['cardSummary'],
'expiryMonth' => $expiryDate[0],
'expiryYear' => $expiryDate[1]
],
'variant' => $contractDetail['paymentMethod'],
'contractTypes' => explode(',', $recurringType)
];
$this->setAgreementData($agreementData);
return $this;
}
} }
...@@ -130,10 +130,10 @@ class AdyenCcConfigProvider implements ConfigProviderInterface ...@@ -130,10 +130,10 @@ class AdyenCcConfigProvider implements ConfigProviderInterface
] ]
]); ]);
$recurringType = $this->_adyenHelper->getAdyenAbstractConfigData('recurring_type'); $enableOneclick = $this->_adyenHelper->getAdyenAbstractConfigData('enable_oneclick');
$canCreateBillingAgreement = false; $canCreateBillingAgreement = false;
if ($recurringType == "ONECLICK" || $recurringType == "ONECLICK,RECURRING") { if ($enableOneclick) {
$canCreateBillingAgreement = true; $canCreateBillingAgreement = true;
} }
......
...@@ -138,9 +138,9 @@ class AdyenOneclickConfigProvider implements ConfigProviderInterface ...@@ -138,9 +138,9 @@ class AdyenOneclickConfigProvider implements ConfigProviderInterface
$config['payment']['adyenOneclick']['checkoutUrl'] = $this->_adyenHelper->getCheckoutContextUrl($this->_storeManager->getStore()->getId()); $config['payment']['adyenOneclick']['checkoutUrl'] = $this->_adyenHelper->getCheckoutContextUrl($this->_storeManager->getStore()->getId());
$config['payment']['adyenOneclick']['locale'] = $this->_adyenHelper->getStoreLocale($this->_storeManager->getStore()->getId()); $config['payment']['adyenOneclick']['locale'] = $this->_adyenHelper->getStoreLocale($this->_storeManager->getStore()->getId());
$recurringType = $this->_adyenHelper->getAdyenAbstractConfigData('recurring_type'); $enableOneclick = $this->_adyenHelper->getAdyenAbstractConfigData('enable_oneclick');
$canCreateBillingAgreement = false; $canCreateBillingAgreement = false;
if ($recurringType == "ONECLICK" || $recurringType == "ONECLICK,RECURRING") { if ($enableOneclick) {
$canCreateBillingAgreement = true; $canCreateBillingAgreement = true;
} }
......
...@@ -39,6 +39,7 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver ...@@ -39,6 +39,7 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver
const EXPIRY_MONTH = 'expiryMonth'; const EXPIRY_MONTH = 'expiryMonth';
const EXPIRY_YEAR = 'expiryYear'; const EXPIRY_YEAR = 'expiryYear';
const HOLDER_NAME = 'holderName'; const HOLDER_NAME = 'holderName';
const VARIANT = 'variant';
/** /**
* @var array * @var array
...@@ -51,7 +52,8 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver ...@@ -51,7 +52,8 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver
self::SECURITY_CODE, self::SECURITY_CODE,
self::EXPIRY_MONTH, self::EXPIRY_MONTH,
self::EXPIRY_YEAR, self::EXPIRY_YEAR,
self::HOLDER_NAME self::HOLDER_NAME,
self::VARIANT
]; ];
/** /**
......
...@@ -34,6 +34,7 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver ...@@ -34,6 +34,7 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
const RECURRING_DETAIL_REFERENCE = 'recurring_detail_reference'; const RECURRING_DETAIL_REFERENCE = 'recurring_detail_reference';
const SECURITY_CODE = 'cvc'; const SECURITY_CODE = 'cvc';
const NUMBER_OF_INSTALLMENTS = 'number_of_installments'; const NUMBER_OF_INSTALLMENTS = 'number_of_installments';
const VARIANT = 'variant';
/** /**
* @var array * @var array
...@@ -41,7 +42,8 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver ...@@ -41,7 +42,8 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
protected $additionalInformationList = [ protected $additionalInformationList = [
self::RECURRING_DETAIL_REFERENCE, self::RECURRING_DETAIL_REFERENCE,
self::SECURITY_CODE, self::SECURITY_CODE,
self::NUMBER_OF_INSTALLMENTS self::NUMBER_OF_INSTALLMENTS,
self::VARIANT
]; ];
/** /**
......
<?php
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2015 Adyen BV (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <magento@adyen.com>
*/
namespace Adyen\Payment\Setup;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\App\Config\Storage\WriterInterface;
/**
* Class UpgradeData
* @package Adyen\Payment\Setup
*/
class UpgradeData implements UpgradeDataInterface
{
/**
* @var WriterInterface
*/
private $configWriter;
/**
* @var ReinitableConfigInterface
*/
private $reinitableConfig;
public function __construct(
WriterInterface $configWriter,
ReinitableConfigInterface $reinitableConfig
) {
$this->configWriter = $configWriter;
$this->reinitableConfig = $reinitableConfig;
}
/**
* @param ModuleDataSetupInterface $setup
* @param ModuleContextInterface $context
*/
public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
if (version_compare($context->getVersion(), '2.4.4', '<')) {
$this->updateSchemaVersion244($setup);
}
$setup->endSetup();
}
/**
* Upgrade to 2.4.4
* We use new configuration options to define if you want to store the payment for oneclick or
* recurring or a combination of those in a more friendly way and make it easier to integrate with our checkout API
*
* @param ModuleDataSetupInterface $setup
*/
public function updateSchemaVersion244(ModuleDataSetupInterface $setup)
{
// convert billing agreement select box to oneclick recurring settings
$pathEnableOneclick = "payment/adyen_abstract/enable_oneclick";
$pathEnableRecurring = "payment/adyen_abstract/enable_recurring";
$configDataTable = $setup->getTable('core_config_data');
$connection = $setup->getConnection();
$select = $connection->select()
->from($configDataTable)
->where(
'path = ?',
'payment/adyen_abstract/recurring_type'
);
$configRecurringTypeValues = $connection->fetchAll($select);
foreach ($configRecurringTypeValues as $configRecurringTypeValue) {
$scope = $configRecurringTypeValue['scope'];
$scopeId = $configRecurringTypeValue['scope_id'];
switch ($configRecurringTypeValue['value']) {
case \Adyen\Payment\Model\RecurringType::ONECLICK:
$this->configWriter->save(
$pathEnableOneclick,
'1',
$scope,
$scopeId
);
$this->configWriter->save(
$pathEnableRecurring,
'0',
$scope,
$scopeId
);
break;
case \Adyen\Payment\Model\RecurringType::ONECLICK_RECURRING:
$this->configWriter->save(
$pathEnableOneclick,
'1',
$scope,
$scopeId
);
$this->configWriter->save(
$pathEnableRecurring,
'1',
$scope,
$scopeId
);
break;
case \Adyen\Payment\Model\RecurringType::RECURRING:
$this->configWriter->save(
$pathEnableOneclick,
'0',
$scope,
$scopeId
);
$this->configWriter->save(
$pathEnableRecurring,
'1',
$scope,
$scopeId
);
break;
case \Adyen\Payment\Model\RecurringType::NONE:
$this->configWriter->save(
$pathEnableOneclick,
'0',
$scope,
$scopeId
);
$this->configWriter->save(
$pathEnableRecurring,
'0',
$scope,
$scopeId
);
break;
}
}
// re-initialize otherwise it will cause errors
$this->reinitableConfig->reinit();
}
}
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"name": "adyen/module-payment", "name": "adyen/module-payment",
"description": "Official Magento2 Plugin to connect to Payment Service Provider Adyen.", "description": "Official Magento2 Plugin to connect to Payment Service Provider Adyen.",
"type": "magento2-module", "type": "magento2-module",
"version": "2.4.3", "version": "2.4.4",
"license": [ "license": [
"OSL-3.0", "OSL-3.0",
"AFL-3.0" "AFL-3.0"
......
...@@ -22,33 +22,30 @@ ...@@ -22,33 +22,30 @@
* Author: Adyen <magento@adyen.com> * Author: Adyen <magento@adyen.com>
*/ */
--> -->
<include xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_include.xsd"> <include xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<group id="adyen_billing_agreements" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_include.xsd">
<group id="adyen_billing_agreements" translate="label" type="text" sortOrder="50" showInDefault="1"
showInWebsite="1" showInStore="1">
<label><![CDATA[Advanced: Billing Agreements]]></label> <label><![CDATA[Advanced: Billing Agreements]]></label>
<frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model>
<comment> <field id="enable_oneclick" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"
<![CDATA[ showInStore="1">
<p> <label>Enable OneClick</label>
<strong>ONECLICK</strong>: The shopper opts in to storing their card details for future use. <tooltip>The shopper opts in to storing their card details for future use. The shopper is present for the
The shopper is present for the subsequent transaction, for cards the security code (CVC/CVV) is required. subsequent transaction, for cards the security code (CVC/CVV) is required.
</p> </tooltip>
<p> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<strong>RECURRING*</strong>: Payment details are stored for future use. For cards, the security <config_path>payment/adyen_abstract/enable_oneclick</config_path>
code (CVC/CVV) is not required for subsequent payments. </field>
</p>
<p>
<strong>ONECLICK, RECURRING*</strong>: Payment details are stored for future use. This allows the use of
the stored payment details regardless of whether the shopper is on your site or not.
</p>
<p>* keep in mind you need the recurring permission enabled. You can contact magento@adyen.com to enable this for your account.
]]>
</comment>
<field id="recurring_type" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <field id="enable_recurring" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"
<label>Agreement Type</label> showInStore="1">
<tooltip>When enabled, users can save their Credit Cards and their SEPA authorizations. ONECLICK will require the input of the CVC for subsequent payments, while RECURRING does not.</tooltip> <label>Enable Recurring</label>
<source_model>Adyen\Payment\Model\Config\Source\RecurringType</source_model> <tooltip>Payment details are stored for future use. For cards, the security
<config_path>payment/adyen_abstract/recurring_type</config_path> code (CVC/CVV) is not required for subsequent payments.
</tooltip>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<config_path>payment/adyen_abstract/enable_recurring</config_path>
</field> </field>
</group> </group>
</include> </include>
\ No newline at end of file
...@@ -28,20 +28,21 @@ ...@@ -28,20 +28,21 @@
<adyen_abstract> <adyen_abstract>
<active>0</active> <active>0</active>
<model>AdyenPaymentGenericFacade</model> <model>AdyenPaymentGenericFacade</model>
<recurring_type>ONECLICK</recurring_type>
<order_status>pending</order_status> <order_status>pending</order_status>
<demo_mode>0</demo_mode> <demo_mode>1</demo_mode>
<debug>1</debug> <debug>1</debug>
<title_renderer>title_image</title_renderer> <title_renderer>title_image</title_renderer>
<sepa_flow>sale</sepa_flow> <sepa_flow>sale</sepa_flow>
<split_payments_refund_strategy>1</split_payments_refund_strategy> <split_payments_refund_strategy>1</split_payments_refund_strategy>
<return_path>checkout/cart</return_path> <return_path>checkout/cart</return_path>
<enable_oneclick>1</enable_oneclick>
<enable_recurring>0</enable_recurring>
<group>adyen</group> <group>adyen</group>
</adyen_abstract> </adyen_abstract>
<adyen_cc> <adyen_cc>
<active>1</active> <active>1</active>
<model>AdyenPaymentCcFacade</model> <model>AdyenPaymentCcFacade</model>
<title>Adyen CreditCard</title> <title>Credit Card</title>
<allowspecific>0</allowspecific> <allowspecific>0</allowspecific>
<sort_order>2</sort_order> <sort_order>2</sort_order>
<cctypes>AE,VI,MC,DI</cctypes> <cctypes>AE,VI,MC,DI</cctypes>
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
--> -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <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="2.4.3"> <module name="Adyen_Payment" setup_version="2.4.4">
<sequence> <sequence>
<module name="Magento_Sales"/> <module name="Magento_Sales"/>
<module name="Magento_Quote"/> <module name="Magento_Quote"/>
......
...@@ -166,24 +166,860 @@ ...@@ -166,24 +166,860 @@
/* Checkout card components style */ /* Checkout card components style */
.adyen-checkout-card__form .adyen-checkout__input iframe{ .adyen-checkout__payment-method {
max-height: 40px; position: relative;
background: white;
border: 1px solid #edf0f3;
cursor: pointer;
margin-top: -1px;
width: 100%;
transition: opacity 0.3s ease-out;
/* transition: margin 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; */
} }
.adyen-checkout-card__form .adyen-checkout__input img{ .adyen-checkout__payment-method:focus {
display: none; outline: 0;
}
.adyen-checkout__payment-method:first-child,
.adyen-checkout__payment-method--selected + .adyen-checkout__payment-method {
margin-top: 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
.adyen-checkout__payment-method--next-selected {
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}
.adyen-checkout__payment-method--loading {
opacity: 0.2;
}
.adyen-checkout__payment-method--selected.adyen-checkout__payment-method--loading {
opacity: 0.9;
}
.adyen-checkout__payment-method--loading .adyen-checkout__spinner__wrapper {
position: absolute;
right: 0;
left: 0;
z-index: 1;
} }
#adyen-cc-form .fieldset > .field > .label, .fieldset > .fields > .field > .label { .adyen-checkout__payment-method__header {
display: flex;
align-items: center;
font-weight: 400; font-weight: 400;
font-size: 16px;
color: #00202e;
padding: 16px;
position: relative;
transition: background 0.1s ease-out;
width: 100%;
} }
#adyen-cc-form .fieldset > .holdername.field{ .adyen-checkout__payment-method__surcharge {
margin-bottom: 10px; color: #687282;
margin-left: 5px;
}
.adyen-checkout__payment-method--selected {
transition: margin 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
background: #f7f8f9;
border: 1px solid #d4d9db;
margin: 8px 0;
border-radius: 3px;
cursor: default;
}
.adyen-checkout__payment-method--selected .adyen-checkout__payment-method__header {
font-weight: 500;
}
.adyen-checkout__payment-method__details__content {
padding: 6px 16px 24px;
}
.adyen-checkout__payment-method__image__wrapper {
height: 26px;
position: relative;
}
.adyen-checkout__payment-method__image__wrapper:after {
content: '';
position: absolute;
top: 0;
width: 100%;
height: 100%;
left: 0;
border-radius: 3px;
border: 1px solid rgba(0, 27, 43, 0.17);
}
.adyen-checkout__payment-method__image {
border-radius: 3px;
}
.adyen-checkout__payment-method__disable_oneclick {
background-color: transparent;
color: #687282;
cursor: pointer;
border: none;
display: block;
font-size: 13px;
padding: 0;
position: absolute;
right: 70px;
text-decoration: underline;
}
/* Payment Method Radio Button */
.adyen-checkout__payment-method__radio {
position: absolute;
background-color: #fff;
border: 1px solid #b9c4c9;
border-radius: 50%;
height: 18px;
width: 18px;
right: 20px;
transition: border-color 0.3s ease-out;
box-shadow: inset 0 1px 3px rgba(0, 27, 43, 0.15);
}
.adyen-checkout__payment-method__radio:after {
content: '';
display: block;
position: absolute;
margin: 0 auto;
left: 0;
right: 0;
top: 50%;
height: 6px;
width: 6px;
background-color: #fff;
border-radius: 50%;
transform: translateY(-50%) scale(0);
transition: transform 0.3s ease-out;
box-shadow: 0 1px 1px rgba(0, 15, 45, 0.25);
}
.adyen-checkout__payment-method__radio:hover {
border-color: #00a3ff;
cursor: pointer;
}
.adyen-checkout__payment-method__radio--selected {
background-color: #00a3ff;
border: 0px solid transparent;
transition: all 0.3s ease-out;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.26);
}
.adyen-checkout__payment-method__radio--selected:after {
transform: translateY(-50%) scale(1);
}
/* /Payment Method Radio Button */
.adyen-checkout__spinner__wrapper {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.adyen-checkout__spinner__wrapper--inline {
height: auto;
display: inline-block;
margin-right: 8px;
}
.adyen-checkout__spinner {
border: 2px solid #00a3ff;
border-radius: 50%;
height: 43px;
width: 43px;
border-top-color: transparent;
animation: rotateSpinner 2s infinite linear;
}
.adyen-checkout__spinner--large {
height: 43px;
width: 43px;
}
.adyen-checkout__spinner--small {
height: 16px;
width: 16px;
}
.adyen-checkout__spinner--medium {
height: 28px;
width: 28px;
}
@keyframes rotateSpinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.adyen-checkout__pay-button {
width: 100%;
padding: 16px;
border: 0;
border-radius: 3px;
background: #001b2b;
color: #fff;
font-weight: 700;
font-size: 1em;
transition: background 0.3s ease-out;
cursor: pointer;
box-shadow: 0 3px 4px rgba(0, 15, 45, 0.2);
}
.adyen-checkout__pay-button:disabled {
background: #e6e9eb;
box-shadow: none;
}
.adyen-checkout__pay-button--loading {
background: #4c5f6b;
box-shadow: none;
outline: 0;
user-select: none;
pointer-events: none;
}
.adyen-checkout__pay-button .adyen-checkout__spinner {
border-color: #fff;
border-width: 3px;
border-top-color: transparent;
}
.adyen-checkout__field {
display: block;
margin-bottom: 16px;
}
.adyen-checkout__field--error input {
border-color: #d81b4a;
color: #d81b4a;
}
.adyen-checkout__field:last-child {
margin-bottom: 0;
}
.adyen-checkout__label__text,
.adyen-checkout__helper-text {
color: #001b2b;
display: block;
font-size: 13px;
font-weight: normal;
line-height: 13px;
padding-bottom: 8px;
}
.adyen-checkout__helper-text {
color: #687282;
}
.adyen-checkout__label__text {
transition: color 0.2s ease-out;
}
.adyen-checkout__label--focused .adyen-checkout__label__text {
color: #0077bb;
}
.adyen-checkout__error-text,
.adyen-checkout__label__text--error {
color: #d81b4a;
font-weight: normal;
margin-top: 10px;
}
.adyen-checkout__radio_group__input {
display: none;
}
.adyen-checkout__radio_group__label {
padding-left: 24px;
position: relative;
display: block;
margin-bottom: 8px;
font-size: 13px;
font-weight: normal;
line-height: 16px;
}
.adyen-checkout__radio_group__label:before {
content: '';
position: absolute;
background-color: #fff;
border: 1px solid #b9c4c9;
border-radius: 50%;
height: 16px;
width: 16px;
left: 0;
top: 0;
transition: border-color 0.3s ease-out;
}
.adyen-checkout__radio_group__label:after {
content: '';
display: block;
position: absolute;
margin: 0 auto;
left: 5px;
top: 5px;
height: 6px;
width: 6px;
background-color: #fff;
border-radius: 50%;
transform: scale(0);
transition: transform 0.3s ease-out;
box-shadow: 0 1px 1px rgba(0, 15, 45, 0.25);
}
.adyen-checkout__radio_group__label:hover {
border-color: #00a3ff;
cursor: pointer;
}
.adyen-checkout__radio_group__input:checked + .adyen-checkout__radio_group__label:before,
.adyen-checkout__radio_group__label--selected {
background-color: #00a3ff;
border: 0px solid transparent;
transition: all 0.3s ease-out;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.26);
}
.adyen-checkout__radio_group__input:checked + .adyen-checkout__radio_group__label:after {
transform: scale(1);
}
.adyen-checkout__checkbox {
display: block;
}
.adyen-checkout__checkbox > input[type='checkbox'] {
position: absolute;
opacity: 0;
pointer-events: none;
}
.adyen-checkout__checkbox__label {
position: relative;
padding-left: 24px;
cursor: pointer;
display: inline-block;
line-height: 16px;
font-size: 1rem;
color: #001b2b;
font-size: 13px;
font-weight: normal;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Check */
.adyen-checkout__checkbox__input + span:before {
content: '';
position: absolute;
top: 2px;
left: 1px;
width: 6px;
height: 11px;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-right: 2px solid #fff;
border-bottom: 2px solid #fff;
-webkit-transform: rotateZ(37deg);
transform: rotateZ(37deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
z-index: 1;
border-radius: 0px 2px 1px 2px;
opacity: 0;
transition: opacity 0.2s ease-out;
}
.adyen-checkout__checkbox__input:checked + span:before {
opacity: 1;
}
/* Box */
.adyen-checkout__checkbox__input + span:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 16px;
height: 16px;
border-radius: 3px;
background-color: #fff;
border: 1px solid #b9c4c9;
z-index: 0;
transition: background 0.15s ease-out, border 0.05s ease-out, box-shadow 0.1s ease-out;
}
.adyen-checkout__checkbox__input:checked + span:after {
border: 1px solid #00a3ff;
background-color: #00a3ff;
}
.adyen-checkout__checkbox__input:focus + span:after {
border: 1px solid #00a3ff;
box-shadow: 0 0 0 2px #91d7ff;
}
.adyen-checkout__dropdown {
width: 100%;
max-width: 200px;
font-size: 16px;
}
.adyen-checkout__dropdown--small {
max-width: 130px;
width: 100%;
}
.adyen-checkout__dropdown--large {
max-width: 300px;
width: 100%;
}
.adyen-checkout__dropdown__button {
padding: 9px 20px 9px 8px;
border: 1px solid #b9c4c9;
background: #fff;
border-radius: 3px;
outline: 0;
width: 100%;
font-size: 16px;
height: 40px;
line-height: 20px;
transition: border 0.2s ease-out, box-shadow 0.2s ease-out;
}
.adyen-checkout__dropdown__button__icon {
margin-right: 8px;
max-width: 32px;
max-height: 20px;
}
.adyen-checkout__dropdown__button--active,
.adyen-checkout__dropdown__button:active,
.adyen-checkout__dropdown__button:focus {
border-color: #00a3ff;
box-shadow: 0 0 0 2px #91d7ff;
}
.adyen-checkout__dropdown__list {
z-index: 2;
border-radius: 3px;
margin-top: 2px;
box-shadow: 0px 2px 7px rgba(0, 15, 45, 0.3);
} }
#adyen-cc-form .helper-text{ .adyen-checkout__dropdown__list.adyen-checkout__dropdown__list--active {
animation: expand 100ms ease-out;
}
.adyen-checkout__dropdown__element {
padding: 8px;
line-height: 20px;
border: 1px solid transparent;
border-bottom: 1px solid #e6e9eb;
word-break: break-word;
hyphens: auto;
cursor: pointer;
outline: 0;
transition: background 0.2s ease-out, border-color 0.2s ease-out;
}
.adyen-checkout__dropdown__element:last-child {
border-bottom: 0;
}
.adyen-checkout__dropdown__element:hover,
.adyen-checkout__dropdown__element:focus,
.adyen-checkout__dropdown__element:active {
background: #f0f2f4;
}
.adyen-checkout__dropdown__element:active,
.adyen-checkout__dropdown__element:focus {
border-top-color: #00a3ff;
border-bottom-color: #00a3ff;
}
.adyen-checkout__dropdown__element__icon {
margin-right: 8px;
max-width: 32px;
max-height: 20px;
}
@keyframes expand {
0% {
-webkit-transform: scale3d(1, 0, 1);
transform: scale3d(1, 0, 1);
}
100% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
.adyen-checkout__select-list {
margin: 0;
padding: 0;
}
.adyen-checkout__select-list__item {
display: inline-block;
padding: 9px;
border-top: 1px solid #b9c4c9;
background: #fff;
outline: 0;
width: 100%;
font-size: 16px;
cursor: pointer;
line-height: 20px;
}
.adyen-checkout__select-list__item:first-child {
border-top: 0;
}
.adyen-checkout__select-list__item:hover,
.adyen-checkout__select-list__item:focus,
.adyen-checkout__select-list__item:active {
background: rgba(145, 215, 255, 0.5);
}
.adyen-checkout__select-list__item--selected {
background: rgba(145, 215, 255, 0.5);
font-weight: bold;
}
.adyen-checkout__input {
color: #001b2b;
font-size: 16px;
font-family: inherit;
display: block;
height: 40px;
background: white;
border: 1px solid #b9c4c9;
border-radius: 3px;
padding: 5px 8px;
position: relative;
outline: none;
width: 200px;
transition: border 0.2s ease-out, box-shadow 0.2s ease-out;
}
.adyen-checkout__input.adyen-checkout__input--small {
width: 130px;
}
.adyen-checkout__input.adyen-checkout__input--large {
width: 300px;
}
.adyen-checkout__input--error {
border-color: #d0021b;
}
.adyen-checkout__input::placeholder {
color: #90a2bd;
font-weight: 200;
}
.adyen-checkout__input:active,
.adyen-checkout__input--active,
.adyen-checkout__input:focus {
border: 1px solid #00a3ff;
box-shadow: 0 0 0 2px #91d7ff;
}
.adyen-checkout__input[readonly] {
background-color: #e6e9eb;
color: #687282;
cursor: default;
border-color: transparent;
}
.open-invoice__field {
margin-bottom: 15px;
}
.open-invoice__field select {
background: #ffffff;
border: 1px solid #d8d8d8;
box-shadow: none;
font-size: 0.93333333333em;
height: 40px;
margin-top: 4px;
max-width: 420px;
width: 100%;
padding: 5px 8px;
display: block;
}
.open-invoice__field input[type='text'],
.open-invoice__field input[type='date'] {
display: block;
height: 35px;
width: 100%;
max-width: 420px;
}
.adyen-checkout__fieldset {
display: block;
padding-bottom: 30px;
color: #687282;
width: 100%;
}
.adyen-checkout__fieldset--readonly {
}
.adyen-checkout__fieldset--readonly p {
color: #001b2b;
font-size: 13px;
line-height: 19px;
margin: 0;
}
.adyen-checkout__fieldset--deliveryAddress {
padding-top: 30px;
}
.adyen-checkout__fieldset__title {
display: block;
font-size: 11px; font-size: 11px;
color: rgb(144, 162, 189); font-weight: bold;
font-weight: 100; letter-spacing: 1px;
text-transform: uppercase;
padding: 0 0 20px;
margin: 0;
color: #687282;
}
.adyen-checkout__link__klarna--more-information {
clear: both;
display: block;
padding: 10px 0 25px;
}
.adyen-checkout__applepay__button {
width: 240px;
height: 40px;
}
.adyen-checkout__card__exp-cvc {
display: flex;
} }
.adyen-checkout__card__cardNumber {
max-width: 400px;
}
.adyen-checkout__card__exp-date__input--oneclick {
line-height: 40px;
height: 40px;
font-weight: 400;
}
.adyen-checkout__card__exp-cvc .adyen-checkout__field {
margin-right: 24px;
margin-bottom: 0;
}
.adyen-checkout__card__form {
margin-bottom: 16px;
}
.adyen-checkout__giropay__results {
background: #fff;
border: 1px solid #b9c4c9;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
max-height: 140px;
min-height: 100px;
overflow-y: scroll;
width: 100%;
}
.adyen-checkout__giropay__no-results {
color: #687282;
display: block;
font-size: 13px;
padding: 0 0 0 2px;
}
.adyen-checkout__giropay__placeholder {
color: #90a2bd;
display: block;
padding: 0 0 0 2px;
font-weight: 200;
}
.adyen-checkout__giropay__loading {
display: block;
min-height: 100px;
}
.adyen-checkout__giropay__loading .adyen-checkout__spinner__wrapper {
display: inline-block;
vertical-align: middle;
}
.adyen-checkout__giropay__loading-text {
color: #687282;
font-size: 13px;
line-height: 16px;
vertical-align: middle;
}
.adyen-checkout__giropay__error {
color: #d0021b;
font-size: 13px;
}
.adyen-checkout__iban-input__number {
text-transform: uppercase;
}
.adyen-checkout__wechatpay {
background: #fff;
padding: 40px;
text-align: center;
border: 1px solid rgb(221, 225, 227);
border-radius: 3px;
min-height: 443px;
}
.adyen-checkout__wechatpay__brand-logo {
height: 20px;
width: 109px;
}
.adyen-checkout__wechatpay__subtitle {
margin-top: 32px;
}
.adyen-checkout__wechatpay__subtitle,
.adyen-checkout__wechatpay__payment_amount {
color: #001b2b;
font-size: 16px;
line-height: 19px;
}
.adyen-checkout__wechatpay__progress {
height: 4px;
background: #d4d9db;
border-radius: 25px;
margin: 32px auto 12px auto;
width: 152px;
padding-right: 3%;
}
.adyen-checkout__wechatpay__progress > span {
display: block;
height: 100%;
border-radius: 25px;
background: #00a3ff;
}
.adyen-checkout__wechatpay__countdown {
color: #687282;
font-size: 13px;
}
.adyen-checkout__wechatpay .adyen-checkout__spinner__wrapper {
margin: 60px 0;
}
.adyen-checkout__alert {
background-color: #00a3ff;
align-items: flex-start;
border-radius: 3px;
color: #fff;
display: flex;
justify-content: space-between;
line-height: 1;
margin: 0;
padding: 12px;
}
.adyen-checkout__alert--error {
background-color: #d81b4a;
}
.adyen-checkout__alert--success {
background-color: #0abf53;
}
.adyen-checkout__alert--info {
background-color: #00a3ff;
}
.adyen-checkout__sdk,
.adyen-checkout__sdk *,
.adyen-checkout__sdk *:after,
.adyen-checkout__sdk * :before {
box-sizing: border-box;
}
.adyen-checkout__payment-methods-list {
border-radius: 3px;
}
.adyen-checkout__payment-methods-list--loading {
user-select: none;
pointer-events: none;
}
/* Forms */
.adyen-checkout__link {
color: #687282;
font-size: 13px;
text-decoration: underline;
}
/*.adyen-checkout__card__form .adyen-checkout__input {*/
/*max-height: 40px;*/
/*}*/
/*.adyen-checkout__card__form .adyen-checkout__input img{*/
/*display: none;*/
/*}*/
/*#adyen-cc-form .fieldset > .field > .label, .fieldset > .fields > .field > .label {*/
/*font-weight: 400;*/
/*}*/
/*#adyen-cc-form .fieldset > .holdername.field{*/
/*margin-bottom: 10px;*/
/*}*/
/*#adyen-cc-form .helper-text{*/
/*font-size: 11px;*/
/*color: rgb(144, 162, 189);*/
/*font-weight: 100;*/
/*}*/
\ No newline at end of file
...@@ -54,7 +54,9 @@ define( ...@@ -54,7 +54,9 @@ define(
'expiryYear', 'expiryYear',
'setStoreCc', 'setStoreCc',
'installment', 'installment',
'creditCardDetailsValid' 'creditCardDetailsValid',
'variant',
'placeOrderAllowed'
]); ]);
return this; return this;
...@@ -68,10 +70,8 @@ define( ...@@ -68,10 +70,8 @@ define(
*/ */
renderSecureFields: function() { renderSecureFields: function() {
var self = this; var self = this;
self.placeOrderAllowed(false);
self.creditCardOwner.subscribe(function () {
self.updateButton();
});
installments.setInstallments(0); installments.setInstallments(0);
...@@ -85,8 +85,9 @@ define( ...@@ -85,8 +85,9 @@ define(
var card = checkout.create('card', { var card = checkout.create('card', {
originKey: self.getOriginKey(), originKey: self.getOriginKey(),
loadingContext: self.getLoadingContext(),
type: 'card', type: 'card',
hasHolderName: true,
holderNameRequired: true,
groupTypes: self.getAvailableCardTypeAltCodes(), groupTypes: self.getAvailableCardTypeAltCodes(),
onChange: function(state) { onChange: function(state) {
...@@ -134,26 +135,24 @@ define( ...@@ -134,26 +135,24 @@ define(
}else{ }else{
self.creditCardType("") self.creditCardType("")
} }
},
if (state.isValid) { onValid: function(state) {
// Here we enable the button if the component is now valid self.variant(state.brand);
self.creditCardNumber(state.data.encryptedCardNumber); self.creditCardNumber(state.data.encryptedCardNumber);
self.expiryMonth(state.data.encryptedExpiryMonth); self.expiryMonth(state.data.encryptedExpiryMonth);
self.expiryYear(state.data.encryptedExpiryYear); self.expiryYear(state.data.encryptedExpiryYear);
self.securityCode(state.data.encryptedSecurityCode); self.securityCode(state.data.encryptedSecurityCode);
self.creditCardOwner(state.data.holderName);
self.creditCardDetailsValid(true); self.creditCardDetailsValid(true);
}else{ self.placeOrderAllowed(true);
},
onError: function(state) {
self.creditCardDetailsValid(false); self.creditCardDetailsValid(false);
self.placeOrderAllowed(false);
} }
self.updateButton();
},
onError: function() {}
}); });
card.mount(cardNode); card.mount(cardNode);
self.isPlaceOrderActionAllowed(false);
}, },
/** /**
* Builds the payment details part of the payment information reqeust * Builds the payment details part of the payment information reqeust
...@@ -164,6 +163,7 @@ define( ...@@ -164,6 +163,7 @@ define(
return { return {
'method': this.item.method, 'method': this.item.method,
additional_data: { additional_data: {
'card_brand': this.variant(),
'cc_type': this.creditCardType(), 'cc_type': this.creditCardType(),
'number': this.creditCardNumber(), 'number': this.creditCardNumber(),
'cvc': this.securityCode(), 'cvc': this.securityCode(),
...@@ -180,19 +180,7 @@ define( ...@@ -180,19 +180,7 @@ define(
* @returns {boolean} * @returns {boolean}
*/ */
isButtonActive: function() { isButtonActive: function() {
return this.isActive() && this.getCode() == this.isChecked() && this.isPlaceOrderActionAllowed(); return this.isActive() && this.getCode() == this.isChecked() && this.isPlaceOrderActionAllowed() && this.placeOrderAllowed();
},
/**
* Checks if the pay button can be enabled and enables if can
*/
updateButton: function() {
var self = this;
if (self.isCardOwnerValid() && self.isCreditCardDetailsValid()) {
self.isPlaceOrderActionAllowed(true);
} else {
self.isPlaceOrderActionAllowed(false);
}
}, },
/** /**
* Custom place order function * Custom place order function
...@@ -246,9 +234,7 @@ define( ...@@ -246,9 +234,7 @@ define(
var validate = $(form).validation() && $(form).validation('isValid'); var validate = $(form).validation() && $(form).validation('isValid');
var owner = Boolean($(form + ' #creditCardHolderName').valid()); if (!validate) {
if (!validate || !owner) {
return false; return false;
} }
......
...@@ -45,6 +45,7 @@ define( ...@@ -45,6 +45,7 @@ define(
var variant = ko.observable(null); var variant = ko.observable(null);
var paymentMethod = ko.observable(null); var paymentMethod = ko.observable(null);
var numberOfInstallments = ko.observable(null); var numberOfInstallments = ko.observable(null);
var isValid = ko.observable(false);
return Component.extend({ return Component.extend({
defaults: { defaults: {
...@@ -141,8 +142,12 @@ define( ...@@ -141,8 +142,12 @@ define(
'creditCardExpMonth': ko.observable(creditCardExpMonth), 'creditCardExpMonth': ko.observable(creditCardExpMonth),
'creditCardExpYear': ko.observable(creditCardExpYear), 'creditCardExpYear': ko.observable(creditCardExpYear),
'getInstallments': ko.observableArray(installments), 'getInstallments': ko.observableArray(installments),
'placeOrderAllowed': ko.observable(false),
isButtonActive: function() {
return self.isActive() && this.getCode() == self.isChecked() && self.isBillingAgreementChecked() && this.placeOrderAllowed();
},
/** /**
* @override * @override
*/ */
...@@ -189,7 +194,7 @@ define( ...@@ -189,7 +194,7 @@ define(
* creates the card component, * creates the card component,
* sets up the callbacks for card components * sets up the callbacks for card components
*/ */
renderSecureCVC: function() { renderSecureCVC: function () {
var self = this; var self = this;
var oneClickCardNode = document.getElementById('cvcContainer-' + self.value); var oneClickCardNode = document.getElementById('cvcContainer-' + self.value);
...@@ -198,12 +203,22 @@ define( ...@@ -198,12 +203,22 @@ define(
locale: self.getLocale() locale: self.getLocale()
}); });
// this should be fixed in new version of checkout card component
var hideCVC = false;
if (self.agreement_data.variant == "bcmc") {
hideCVC = true;
self.placeOrderAllowed(true);
} else if(self.agreement_data.variant == "maestro") {
// for maestro cvc is optional
self.placeOrderAllowed(true);
}
var oneClickCard = checkout var oneClickCard = checkout
.create('card', { .create('card', {
originKey: self.getOriginKey(), originKey: self.getOriginKey(),
loadingContext: self.getLoadingContext(),
type: self.agreement_data.variant, type: self.agreement_data.variant,
oneClick: true, oneClick: true,
hideCVC: hideCVC,
// Specific for oneClick cards // Specific for oneClick cards
details: [ details: [
...@@ -221,15 +236,32 @@ define( ...@@ -221,15 +236,32 @@ define(
} }
}, },
onChange: function(state) { onChange: function (state) {
if (state.isValid) { if (state.isValid) {
self.encryptedCreditCardVerificationNumber = state.data.encryptedSecurityCode; self.encryptedCreditCardVerificationNumber = state.data.encryptedSecurityCode;
} else { } else {
self.encryptedCreditCardVerificationNumber = ''; self.encryptedCreditCardVerificationNumber = '';
} }
},
onValid: function (state) {
if (state.isValid) {
self.placeOrderAllowed(true);
isValid(true);
} else {
isValid(false);
}
return;
},
onError: function(data) {
self.placeOrderAllowed(false);
isValid(false);
return;
} }
}) })
.mount(oneClickCardNode); .mount(oneClickCardNode);
window.adyencheckout = oneClickCard;
}, },
/** /**
* Builds the payment details part of the payment information reqeust * Builds the payment details part of the payment information reqeust
...@@ -261,16 +293,8 @@ define( ...@@ -261,16 +293,8 @@ define(
var validate = $(form).validation() && $(form).validation('isValid'); var validate = $(form).validation() && $(form).validation('isValid');
// if oneclick or recurring is a card check CVC validity // bcmc does not have any cvc
var cid = true; if (!validate || (isValid() == false && variant() != "bcmc" && variant() != "maestro")) {
if (this.agreement_data.card) {
// if encrypted cvc is empty the request is not valid
if (this.hasVerification() && this.encryptedCreditCardVerificationNumber.length === 0) {
cid = false;
}
}
if (!validate || !cid) {
return false; return false;
} }
......
...@@ -87,7 +87,9 @@ ...@@ -87,7 +87,9 @@
</li> </li>
<!--/ko--> <!--/ko-->
</ul> </ul>
<p class="helper-text" data-bind="css: {hidden: creditCardType() !== ''} "><!-- ko text: $t('(Please provide a card with the type from the list above)')--><!-- /ko --></p> <p class="helper-text" data-bind="css: {hidden: creditCardType() !== ''} ">
<!-- ko text: $t('(Please provide a card with the type from the list above)')-->
<!-- /ko --></p>
<input type="hidden" <input type="hidden"
name="payment[cc_type]" name="payment[cc_type]"
class="input-text" class="input-text"
...@@ -98,36 +100,11 @@ ...@@ -98,36 +100,11 @@
</div> </div>
</div> </div>
<div class="field holdername type">
<label data-bind="attr: {for: 'creditCardHolderName'}" class="label">
<span><!-- ko text: $t('Credit Card Owner')--><!-- /ko --></span>
</label>
<div class="control">
<input type="text"
class="input-text required-entry"
data-encrypted-name="holderName"
value=""
data-bind="attr: {
id: 'creditCardHolderName',
title: $t('Credit Card Owner'),
placeholder: $t('Credit Card Owner'),
'data-container': getCode() + '-cc-owner'
},
enable: isActive($parents),
value: creditCardOwner,
valueUpdate: 'keyup' "
data-validate="{required:true}"
/>
</div>
</div>
<div class="field number cardContainerField"> <div class="field number cardContainerField">
<div afterRender="renderSecureFields()" data-bind="attr: { id: 'cardContainer'}" ></div> <div afterRender="renderSecureFields()" data-bind="attr: { id: 'cardContainer'}"></div>
</div> </div>
<!-- ko if: (hasInstallments())--> <!-- ko if: (hasInstallments())-->
<div class="field required" <div class="field required"
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
--> -->
<!-- ko foreach: getAdyenBillingAgreements() --> <!-- ko foreach: getAdyenBillingAgreements() -->
<div class="payment-method" data-bind="css: {'_active': (value == $parent.isBillingAgreementChecked())}"> <div class="payment-method" data-bind="css: {'_active': (value == $parent.isBillingAgreementChecked())}">
...@@ -63,7 +62,6 @@ ...@@ -63,7 +62,6 @@
</div> </div>
<form class="form" action="#" method="post" data-bind=" <form class="form" action="#" method="post" data-bind="
attr: {'id': 'adyen_oneclick_' + value, 'data-role': 'adyen_oneclick_' + value }, attr: {'id': 'adyen_oneclick_' + value, 'data-role': 'adyen_oneclick_' + value },
mageInit: { mageInit: {
...@@ -73,7 +71,8 @@ ...@@ -73,7 +71,8 @@
'orderSaveUrl':$parent.getPlaceOrderUrl(), 'orderSaveUrl':$parent.getPlaceOrderUrl(),
}, 'validation':[]}"> }, 'validation':[]}">
<fieldset data-bind="attr: {class: 'fieldset payment items ccard ' + getCode(), id: 'payment_form_' + $parent.getCode() + '_' + value}"> <fieldset
data-bind="attr: {class: 'fieldset payment items ccard ' + getCode(), id: 'payment_form_' + $parent.getCode() + '_' + value}">
<!-- ko if: agreement_data.card --> <!-- ko if: agreement_data.card -->
...@@ -87,7 +86,7 @@ ...@@ -87,7 +86,7 @@
</div> </div>
<!-- ko if: hasVerification()--> <!-- ko if: hasVerification()-->
<div afterRender="renderSecureCVC()" data-bind="attr: { id: 'cvcContainer-' + value}" ></div> <div afterRender="renderSecureCVC()" data-bind="attr: { id: 'cvcContainer-' + value}"></div>
<!-- /ko --> <!-- /ko -->
<!--/ko--> <!--/ko-->
...@@ -124,7 +123,8 @@ ...@@ -124,7 +123,8 @@
<!-- ko if: number_of_installments.length > 0 --> <!-- ko if: number_of_installments.length > 0 -->
<div class="field required" data-bind="attr: {id: getCode() + '_installments_div'}, visible: getInstallments().length > 0"> <div class="field required"
data-bind="attr: {id: getCode() + '_installments_div'}, visible: getInstallments().length > 0">
<label data-bind="attr: {for: getCode() + '_installments'}" class="label"> <label data-bind="attr: {for: getCode() + '_installments'}" class="label">
<span><!-- ko text: $t('Installments')--><!-- /ko --></span> <span><!-- ko text: $t('Installments')--><!-- /ko --></span>
</label> </label>
...@@ -160,8 +160,7 @@ ...@@ -160,8 +160,7 @@
data-bind=" data-bind="
click: placeOrder, click: placeOrder,
attr: {title: $t('Place Order')}, attr: {title: $t('Place Order')},
enable: (value == $parent.isBillingAgreementChecked()) enable: isButtonActive()"
"
disabled> disabled>
<span data-bind="text: $t('Place Order')"></span> <span data-bind="text: $t('Place Order')"></span>
</button> </button>
......
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