Commit ed625243 authored by attilak's avatar attilak

Merge branch 'PW-2212' into feature/generic-component

# Conflicts:
#	Block/Form/Cc.php
#	Gateway/Request/CcAuthorizationDataBuilder.php
#	Gateway/Request/CheckoutDataBuilder.php
#	Gateway/Request/RecurringDataBuilder.php
#	Gateway/Validator/CheckoutResponseValidator.php
#	Helper/PaymentMethods.php
#	Helper/Requests.php
#	Model/AdyenThreeDS2Process.php
#	Model/Ui/AdyenGenericConfigProvider.php
#	Observer/AdyenCcDataAssignObserver.php
#	Observer/AdyenHppDataAssignObserver.php
#	view/frontend/web/js/model/adyen-method-list.js
#	view/frontend/web/js/model/adyen-payment-service.js
#	view/frontend/web/js/view/payment/adyen-methods.js
#	view/frontend/web/js/view/payment/method-renderer/adyen-cc-method.js
#	view/frontend/web/js/view/payment/method-renderer/adyen-hpp-method.js
#	view/frontend/web/js/view/payment/method-renderer/adyen-oneclick-method.js
parents 7ee6f945 c6bdeeec
...@@ -67,22 +67,14 @@ class Cc extends \Magento\Payment\Block\Form\Cc ...@@ -67,22 +67,14 @@ class Cc extends \Magento\Payment\Block\Form\Cc
$this->checkoutSession = $checkoutSession; $this->checkoutSession = $checkoutSession;
} }
/** /**
* @return string * @return string
*/ * @throws \Adyen\AdyenException
public function getCheckoutCardComponentJs() */
{ public function getCheckoutOriginKeys()
return $this->adyenHelper->getCheckoutCardComponentJs($this->checkoutSession->getQuote()->getStore()->getId()); {
} return $this->adyenHelper->getOriginKeyForBaseUrl();
}
/**
* @return string
* @throws \Adyen\AdyenException
*/
public function getCheckoutOriginKeys()
{
return $this->adyenHelper->getOriginKeyForBaseUrl();
}
/** /**
* @return string * @return string
......
<?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\Gateway\Request;
use Magento\Payment\Gateway\Request\BuilderInterface;
use Adyen\Payment\Observer\AdyenCcDataAssignObserver;
class CcAuthorizationDataBuilder implements BuilderInterface
{
/**
* @param array $buildSubject
* @return mixed
*/
public function build(array $buildSubject)
{
/** @var \Magento\Payment\Gateway\Data\PaymentDataObject $paymentDataObject */
$paymentDataObject = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($buildSubject);
$payment = $paymentDataObject->getPayment();
$requestBody = [];
// If ccType is set use this. For bcmc you need bcmc otherwise it will fail
$requestBody['paymentMethod']['type'] = "scheme";
if ($cardNumber = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::ENCRYPTED_CREDIT_CARD_NUMBER)) {
$requestBody['paymentMethod']['encryptedCardNumber'] = $cardNumber;
}
if ($expiryMonth = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::ENCRYPTED_EXPIRY_MONTH)) {
$requestBody['paymentMethod']['encryptedExpiryMonth'] = $expiryMonth;
}
if ($expiryYear = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::ENCRYPTED_EXPIRY_YEAR)) {
$requestBody['paymentMethod']['encryptedExpiryYear'] = $expiryYear;
}
if ($holderName = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::HOLDER_NAME)) {
$requestBody['paymentMethod']['holderName'] = $holderName;
}
if ($securityCode = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::ENCRYPTED_SECURITY_CODE)) {
$requestBody['paymentMethod']['encryptedSecurityCode'] = $securityCode;
}
// Remove from additional data
$payment->unsAdditionalInformation(AdyenCcDataAssignObserver::ENCRYPTED_CREDIT_CARD_NUMBER);
$payment->unsAdditionalInformation(AdyenCcDataAssignObserver::ENCRYPTED_EXPIRY_MONTH);
$payment->unsAdditionalInformation(AdyenCcDataAssignObserver::ENCRYPTED_EXPIRY_YEAR);
$payment->unsAdditionalInformation(AdyenCcDataAssignObserver::ENCRYPTED_SECURITY_CODE);
$payment->unsAdditionalInformation(AdyenCcDataAssignObserver::HOLDER_NAME);
// if installments is set and card type is credit card add it into the request
$numberOfInstallments = $payment->getAdditionalInformation(
AdyenCcDataAssignObserver::NUMBER_OF_INSTALLMENTS
) ?: 0;
$comboCardType = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::COMBO_CARD_TYPE) ?: 'credit';
if ($numberOfInstallments > 0) {
$requestBody['installments']['value'] = $numberOfInstallments;
}
// if card type is debit then change the issuer type and unset the installments field
if ($comboCardType == 'debit') {
if ($selectedDebitBrand = $this->getSelectedDebitBrand($payment->getAdditionalInformation('cc_type'))) {
$requestBody['additionalData']['overwriteBrand'] = true;
$requestBody['selectedBrand'] = $selectedDebitBrand;
$requestBody['paymentMethod']['type'] = $selectedDebitBrand;
}
unset($requestBody['installments']);
}
$request['body'] = $requestBody;
return $request;
}
/**
* @param string $brand
* @return string
*/
private function getSelectedDebitBrand($brand)
{
if ($brand == 'VI') {
return 'electron';
}
if ($brand == 'MC') {
return 'maestro';
}
return null;
}
}
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
namespace Adyen\Payment\Gateway\Request; namespace Adyen\Payment\Gateway\Request;
use Adyen\Payment\Observer\AdyenCcDataAssignObserver;
use Magento\Payment\Gateway\Request\BuilderInterface; use Magento\Payment\Gateway\Request\BuilderInterface;
use Adyen\Payment\Observer\AdyenHppDataAssignObserver; use Adyen\Payment\Observer\AdyenHppDataAssignObserver;
...@@ -82,14 +83,17 @@ class CheckoutDataBuilder implements BuilderInterface ...@@ -82,14 +83,17 @@ class CheckoutDataBuilder implements BuilderInterface
// do not send email // do not send email
$order->setCanSendNewEmailFlag(false); $order->setCanSendNewEmailFlag(false);
$requestBodyPaymentMethod['type'] = $payment->getAdditionalInformation( $componentStateData = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::STATE_DATA);
AdyenHppDataAssignObserver::BRAND_CODE $requestBody = array_merge($requestBody, $componentStateData);
);
// Additional data for payment methods with issuer list
if ($payment->getAdditionalInformation(AdyenHppDataAssignObserver::ISSUER_ID)) { if (empty($requestBody['paymentMethod']['type']) && !empty(
$requestBodyPaymentMethod['issuer'] = $payment->getAdditionalInformation( $payment->getAdditionalInformation(
AdyenHppDataAssignObserver::ISSUER_ID AdyenHppDataAssignObserver::BRAND_CODE
)
)) {
$requestBody['paymentMethod']['type'] = $payment->getAdditionalInformation(
AdyenHppDataAssignObserver::BRAND_CODE
); );
} }
...@@ -97,59 +101,24 @@ class CheckoutDataBuilder implements BuilderInterface ...@@ -97,59 +101,24 @@ class CheckoutDataBuilder implements BuilderInterface
\Magento\Framework\UrlInterface::URL_TYPE_LINK \Magento\Framework\UrlInterface::URL_TYPE_LINK
) . 'adyen/process/result'; ) . 'adyen/process/result';
// Additional data for ACH // TODO set order payment extra data -> mapping required
if ($payment->getAdditionalInformation("bankAccountNumber")) {
$requestBody['bankAccount']['bankAccountNumber'] = $payment->getAdditionalInformation("bankAccountNumber");
}
if ($payment->getAdditionalInformation("bankLocationId")) {
$requestBody['bankAccount']['bankLocationId'] = $payment->getAdditionalInformation("bankLocationId");
}
if ($payment->getAdditionalInformation("bankAccountOwnerName")) {
$requestBody['bankAccount']['ownerName'] = $payment->getAdditionalInformation("bankAccountOwnerName");
}
// Additional data for open invoice payment
if ($payment->getAdditionalInformation("gender")) { if ($payment->getAdditionalInformation("gender")) {
$order->setCustomerGender( $order->setCustomerGender(
$this->gender->getMagentoGenderFromAdyenGender( \Adyen\Payment\Model\Gender::getMagentoGenderFromAdyenGender(
$payment->getAdditionalInformation("gender") $payment->getAdditionalInformation("gender")
) )
); );
$requestBodyPaymentMethod['personalDetails']['gender'] = $payment->getAdditionalInformation("gender");
} }
if ($payment->getAdditionalInformation("dob")) { if ($payment->getAdditionalInformation("dob")) {
$order->setCustomerDob($payment->getAdditionalInformation("dob")); $order->setCustomerDob($payment->getAdditionalInformation("dob"));
$requestBodyPaymentMethod['personalDetails']['dateOfBirth'] = $this->adyenHelper->formatDate(
$payment->getAdditionalInformation("dob"),
'Y-m-d'
);
} }
if ($payment->getAdditionalInformation("telephone")) { if ($payment->getAdditionalInformation("telephone")) {
$order->getBillingAddress()->setTelephone($payment->getAdditionalInformation("telephone")); $order->getBillingAddress()->setTelephone($payment->getAdditionalInformation("telephone"));
$requestBodyPaymentMethod['personalDetails']['telephoneNumber'] = $payment->getAdditionalInformation(
"telephone"
);
}
if ($payment->getAdditionalInformation("ssn")) {
$requestBodyPaymentMethod['personalDetails']['socialSecurityNumber'] =
$payment->getAdditionalInformation("ssn");
}
// Additional data for sepa direct debit
if ($payment->getAdditionalInformation("ownerName")) {
$requestBodyPaymentMethod['sepa.ownerName'] = $payment->getAdditionalInformation("ownerName");
}
if ($payment->getAdditionalInformation("ibanNumber")) {
$requestBodyPaymentMethod['sepa.ibanNumber'] = $payment->getAdditionalInformation("ibanNumber");
} }
// TODO new function isOpenInvoiceLineItemNeeded
if ($this->adyenHelper->isPaymentMethodOpenInvoiceMethod( if ($this->adyenHelper->isPaymentMethodOpenInvoiceMethod(
$payment->getAdditionalInformation(AdyenHppDataAssignObserver::BRAND_CODE) $payment->getAdditionalInformation(AdyenHppDataAssignObserver::BRAND_CODE)
) || $this->adyenHelper->isPaymentMethodAfterpayTouchMethod( ) || $this->adyenHelper->isPaymentMethodAfterpayTouchMethod(
...@@ -163,6 +132,7 @@ class CheckoutDataBuilder implements BuilderInterface ...@@ -163,6 +132,7 @@ class CheckoutDataBuilder implements BuilderInterface
} }
// Ratepay specific Fingerprint // Ratepay specific Fingerprint
// TODO check if this is still necessary or if there is a component that can handle this
if ($payment->getAdditionalInformation("df_value") && $this->adyenHelper->isPaymentMethodRatepayMethod( if ($payment->getAdditionalInformation("df_value") && $this->adyenHelper->isPaymentMethodRatepayMethod(
$payment->getAdditionalInformation(AdyenHppDataAssignObserver::BRAND_CODE) $payment->getAdditionalInformation(AdyenHppDataAssignObserver::BRAND_CODE)
)) { )) {
...@@ -183,16 +153,17 @@ class CheckoutDataBuilder implements BuilderInterface ...@@ -183,16 +153,17 @@ class CheckoutDataBuilder implements BuilderInterface
} }
if ($payment->getMethod() == \Adyen\Payment\Model\Ui\AdyenBoletoConfigProvider::CODE) { if ($payment->getMethod() == \Adyen\Payment\Model\Ui\AdyenBoletoConfigProvider::CODE) {
$boletoTypes = $this->adyenHelper->getAdyenBoletoConfigData('boletotypes'); // TODO check if this is coming from the component now
/*$boletoTypes = $this->adyenHelper->getAdyenBoletoConfigData('boletotypes');
$boletoTypes = explode(',', $boletoTypes); $boletoTypes = explode(',', $boletoTypes);
if (count($boletoTypes) == 1) { if (count($boletoTypes) == 1) {
$requestBody['selectedBrand'] = $boletoTypes[0]; $requestBody['selectedBrand'] = $boletoTypes[0];
$requestBodyPaymentMethod['type'] = $boletoTypes[0]; $requestBody['paymentMethod']['type'] = $boletoTypes[0];
} else { } else {
$requestBody['selectedBrand'] = $payment->getAdditionalInformation("boleto_type"); $requestBody['selectedBrand'] = $payment->getAdditionalInformation("boleto_type");
$requestBodyPaymentMethod['type'] = $payment->getAdditionalInformation("boleto_type"); $requestBody['paymentMethod']['type'] = $payment->getAdditionalInformation("boleto_type");
} }*/
$deliveryDays = (int)$this->adyenHelper->getAdyenBoletoConfigData("delivery_days", $storeId); $deliveryDays = (int)$this->adyenHelper->getAdyenBoletoConfigData("delivery_days", $storeId);
$deliveryDays = (!empty($deliveryDays)) ? $deliveryDays : 5; $deliveryDays = (!empty($deliveryDays)) ? $deliveryDays : 5;
...@@ -213,7 +184,23 @@ class CheckoutDataBuilder implements BuilderInterface ...@@ -213,7 +184,23 @@ class CheckoutDataBuilder implements BuilderInterface
$order->setCanSendNewEmailFlag(true); $order->setCanSendNewEmailFlag(true);
} }
$requestBody['paymentMethod'] = $requestBodyPaymentMethod; // if installments is set and card type is credit card add it into the request
$numberOfInstallments = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::NUMBER_OF_INSTALLMENTS) ?: 0;
$comboCardType = $payment->getAdditionalInformation(AdyenCcDataAssignObserver::COMBO_CARD_TYPE) ?: 'credit';
if ($numberOfInstallments > 0) {
$requestBody['installments']['value'] = $numberOfInstallments;
}
// if card type is debit then change the issuer type and unset the installments field
if ($comboCardType == 'debit') {
if ($selectedDebitBrand = $this->getSelectedDebitBrand($payment->getAdditionalInformation('cc_type'))) {
$requestBody['additionalData']['overwriteBrand'] = true;
$requestBody['selectedBrand'] = $selectedDebitBrand;
$requestBody['paymentMethod']['type'] = $selectedDebitBrand;
}
unset($requestBody['installments']);
}
$request['body'] = $requestBody; $request['body'] = $requestBody;
return $request; return $request;
...@@ -312,4 +299,19 @@ class CheckoutDataBuilder implements BuilderInterface ...@@ -312,4 +299,19 @@ class CheckoutDataBuilder implements BuilderInterface
return $formFields; return $formFields;
} }
/**
* @param string $brand
* @return string
*/
private function getSelectedDebitBrand($brand)
{
if ($brand == 'VI') {
return 'electron';
}
if ($brand == 'MC') {
return 'maestro';
}
return null;
}
} }
...@@ -63,13 +63,7 @@ class RecurringDataBuilder implements BuilderInterface ...@@ -63,13 +63,7 @@ class RecurringDataBuilder implements BuilderInterface
$payment = $paymentDataObject->getPayment(); $payment = $paymentDataObject->getPayment();
$storeId = $payment->getOrder()->getStoreId(); $storeId = $payment->getOrder()->getStoreId();
$areaCode = $this->appState->getAreaCode(); $areaCode = $this->appState->getAreaCode();
$additionalInformation = $payment->getAdditionalInformation(); $request['body'] = $this->adyenRequestsHelper->buildRecurringData([], $areaCode, $storeId);
$request['body'] = $this->adyenRequestsHelper->buildRecurringData(
$areaCode,
$storeId,
$additionalInformation,
[]
);
return $request; return $request;
} }
} }
...@@ -69,21 +69,9 @@ class CheckoutResponseValidator extends AbstractValidator ...@@ -69,21 +69,9 @@ class CheckoutResponseValidator extends AbstractValidator
// validate result // validate result
if (!empty($response['resultCode'])) { if (!empty($response['resultCode'])) {
switch ($response['resultCode']) { switch ($response['resultCode']) {
case "IdentifyShopper":
$payment->setAdditionalInformation('threeDSType', $response['resultCode']);
$payment->setAdditionalInformation(
'threeDS2Token',
$response['authentication']['threeds2.fingerprintToken']
);
$payment->setAdditionalInformation('threeDS2PaymentData', $response['paymentData']);
break;
case "ChallengeShopper": case "ChallengeShopper":
$payment->setAdditionalInformation('threeDSType', $response['resultCode']); case "IdentifyShopper":
$payment->setAdditionalInformation( $payment->setAdditionalInformation('action', $response['action']);
'threeDS2Token',
$response['authentication']['threeds2.challengeToken']
);
$payment->setAdditionalInformation('threeDS2PaymentData', $response['paymentData']);
break; break;
case "Authorised": case "Authorised":
case "Received": case "Received":
......
...@@ -39,11 +39,6 @@ class InstallmentValidator extends AbstractValidator ...@@ -39,11 +39,6 @@ class InstallmentValidator extends AbstractValidator
*/ */
private $adyenHelper; private $adyenHelper;
/**
* @var \Magento\Framework\Serialize\SerializerInterface
*/
private $serializer;
/** /**
* @var \Magento\Quote\Model\QuoteRepository * @var \Magento\Quote\Model\QuoteRepository
*/ */
...@@ -55,19 +50,16 @@ class InstallmentValidator extends AbstractValidator ...@@ -55,19 +50,16 @@ class InstallmentValidator extends AbstractValidator
* @param \Magento\Payment\Gateway\Validator\ResultInterfaceFactory $resultFactory * @param \Magento\Payment\Gateway\Validator\ResultInterfaceFactory $resultFactory
* @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger * @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger
* @param \Adyen\Payment\Helper\Data $adyenHelper * @param \Adyen\Payment\Helper\Data $adyenHelper
* @param \Magento\Framework\Serialize\SerializerInterface $serializer
* @param \Magento\Quote\Model\QuoteRepository $quoteRepository * @param \Magento\Quote\Model\QuoteRepository $quoteRepository
*/ */
public function __construct( public function __construct(
\Magento\Payment\Gateway\Validator\ResultInterfaceFactory $resultFactory, \Magento\Payment\Gateway\Validator\ResultInterfaceFactory $resultFactory,
\Adyen\Payment\Logger\AdyenLogger $adyenLogger, \Adyen\Payment\Logger\AdyenLogger $adyenLogger,
\Adyen\Payment\Helper\Data $adyenHelper, \Adyen\Payment\Helper\Data $adyenHelper,
\Magento\Framework\Serialize\SerializerInterface $serializer,
\Magento\Quote\Model\QuoteRepository $quoteRepository \Magento\Quote\Model\QuoteRepository $quoteRepository
) { ) {
$this->adyenLogger = $adyenLogger; $this->adyenLogger = $adyenLogger;
$this->adyenHelper = $adyenHelper; $this->adyenHelper = $adyenHelper;
$this->serializer = $serializer;
$this->quoteRepository = $quoteRepository; $this->quoteRepository = $quoteRepository;
parent::__construct($resultFactory); parent::__construct($resultFactory);
} }
...@@ -87,27 +79,14 @@ class InstallmentValidator extends AbstractValidator ...@@ -87,27 +79,14 @@ class InstallmentValidator extends AbstractValidator
$installmentsEnabled = $this->adyenHelper->getAdyenCcConfigData('enable_installments'); $installmentsEnabled = $this->adyenHelper->getAdyenCcConfigData('enable_installments');
if ($quote && $installmentsEnabled) { if ($quote && $installmentsEnabled) {
$grandTotal = $quote->getGrandTotal(); $grandTotal = $quote->getGrandTotal();
$installmentsAvailable = $this->adyenHelper->getAdyenCcConfigData('installments'); $installments = $this->adyenHelper->getAdyenCcConfigData('installments');
$installmentSelected = $payment->getAdditionalInformation('number_of_installments'); $installmentSelected = $payment->getAdditionalInformation('number_of_installments');
$ccType = $payment->getAdditionalInformation('cc_type'); $ccType = $payment->getAdditionalInformation('cc_type');
if ($installmentsAvailable) {
$installments = $this->serializer->unserialize($installmentsAvailable);
}
if ($installmentSelected && $installmentsAvailable) { if ($installmentSelected && $installmentsAvailable) {
$isValid = false; $isValid = false;
$fails[] = __('Installments not valid.'); $fails[] = __('Installments not valid.');
if ($installments) { if ($installments) {
foreach ($installments as $ccTypeInstallment => $installment) { //TODO how implement custom validation of generated installments code against selected value?
if ($ccTypeInstallment == $ccType) {
foreach ($installment as $amount => $installmentsData) {
if ($installmentSelected == $installmentsData) {
if ($grandTotal >= $amount) {
$isValid = true;
}
}
}
}
}
} }
} }
} }
......
...@@ -34,8 +34,6 @@ class Data extends AbstractHelper ...@@ -34,8 +34,6 @@ class Data extends AbstractHelper
const MODULE_NAME = 'adyen-magento2'; const MODULE_NAME = 'adyen-magento2';
const TEST = 'test'; const TEST = 'test';
const LIVE = 'live'; const LIVE = 'live';
const CHECKOUT_COMPONENT_JS_LIVE = 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/3.2.0/adyen.js';
const CHECKOUT_COMPONENT_JS_TEST = 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/3.2.0/adyen.js';
/** /**
* @var \Magento\Framework\Encryption\EncryptorInterface * @var \Magento\Framework\Encryption\EncryptorInterface
...@@ -1626,19 +1624,6 @@ class Data extends AbstractHelper ...@@ -1626,19 +1624,6 @@ class Data extends AbstractHelper
return new \Adyen\Service\CheckoutUtility($client); return new \Adyen\Service\CheckoutUtility($client);
} }
/**
* @param int|null $storeId
* @return string
*/
public function getCheckoutCardComponentJs($storeId = null)
{
if ($this->isDemoMode($storeId)) {
return self::CHECKOUT_COMPONENT_JS_TEST;
}
return self::CHECKOUT_COMPONENT_JS_LIVE;
}
/** /**
* @param $order * @param $order
* @param $additionalData * @param $additionalData
...@@ -1827,27 +1812,6 @@ class Data extends AbstractHelper ...@@ -1827,27 +1812,6 @@ class Data extends AbstractHelper
return $timeStamp->format($format); return $timeStamp->format($format);
} }
/**
* @param string|null $type
* @param string|null $token
* @return string
*/
public function buildThreeDS2ProcessResponseJson($type = null, $token = null)
{
$response = ['threeDS2' => false];
if (!empty($type)) {
$response['type'] = $type;
}
if ($type && $token) {
$response['threeDS2'] = true;
$response['token'] = $token;
}
return json_encode($response);
}
/** /**
* @param int $storeId * @param int $storeId
* @return mixed|string * @return mixed|string
......
...@@ -90,6 +90,11 @@ class PaymentMethods extends AbstractHelper ...@@ -90,6 +90,11 @@ class PaymentMethods extends AbstractHelper
*/ */
protected $quote; protected $quote;
/**
* @var \Magento\Checkout\Model\PaymentDetailsFactory
*/
protected $paymentDetailsFactory;
/** /**
* PaymentMethods constructor. * PaymentMethods constructor.
* *
...@@ -104,6 +109,7 @@ class PaymentMethods extends AbstractHelper ...@@ -104,6 +109,7 @@ class PaymentMethods extends AbstractHelper
* @param \Magento\Framework\View\Asset\Source $assetSource * @param \Magento\Framework\View\Asset\Source $assetSource
* @param \Magento\Framework\View\DesignInterface $design * @param \Magento\Framework\View\DesignInterface $design
* @param \Magento\Framework\View\Design\Theme\ThemeProviderInterface $themeProvider * @param \Magento\Framework\View\Design\Theme\ThemeProviderInterface $themeProvider
* @param \Magento\Checkout\Model\PaymentDetailsFactory $paymentDetailsFactory
*/ */
public function __construct( public function __construct(
\Magento\Quote\Api\CartRepositoryInterface $quoteRepository, \Magento\Quote\Api\CartRepositoryInterface $quoteRepository,
...@@ -116,7 +122,8 @@ class PaymentMethods extends AbstractHelper ...@@ -116,7 +122,8 @@ class PaymentMethods extends AbstractHelper
\Magento\Framework\App\RequestInterface $request, \Magento\Framework\App\RequestInterface $request,
\Magento\Framework\View\Asset\Source $assetSource, \Magento\Framework\View\Asset\Source $assetSource,
\Magento\Framework\View\DesignInterface $design, \Magento\Framework\View\DesignInterface $design,
\Magento\Framework\View\Design\Theme\ThemeProviderInterface $themeProvider \Magento\Framework\View\Design\Theme\ThemeProviderInterface $themeProvider,
\Magento\Checkout\Model\PaymentDetailsFactory $paymentDetailsFactory
) { ) {
$this->quoteRepository = $quoteRepository; $this->quoteRepository = $quoteRepository;
$this->config = $config; $this->config = $config;
...@@ -129,6 +136,7 @@ class PaymentMethods extends AbstractHelper ...@@ -129,6 +136,7 @@ class PaymentMethods extends AbstractHelper
$this->assetSource = $assetSource; $this->assetSource = $assetSource;
$this->design = $design; $this->design = $design;
$this->themeProvider = $themeProvider; $this->themeProvider = $themeProvider;
$this->paymentDetailsFactory = $paymentDetailsFactory;
} }
/** /**
...@@ -146,7 +154,7 @@ class PaymentMethods extends AbstractHelper ...@@ -146,7 +154,7 @@ class PaymentMethods extends AbstractHelper
$this->setQuote($quote); $this->setQuote($quote);
$paymentMethods = $this->fetchAlternativeMethods($country); $paymentMethods = $this->fetchPaymentMethods($country);
return $paymentMethods; return $paymentMethods;
} }
...@@ -154,7 +162,7 @@ class PaymentMethods extends AbstractHelper ...@@ -154,7 +162,7 @@ class PaymentMethods extends AbstractHelper
* @param $country * @param $country
* @return array * @return array
*/ */
protected function fetchAlternativeMethods($country) protected function fetchPaymentMethods($country)
{ {
$quote = $this->getQuote(); $quote = $this->getQuote();
$store = $quote->getStore(); $store = $quote->getStore();
...@@ -192,64 +200,78 @@ class PaymentMethods extends AbstractHelper ...@@ -192,64 +200,78 @@ class PaymentMethods extends AbstractHelper
$responseData = $this->getPaymentMethodsResponse($adyFields, $store); $responseData = $this->getPaymentMethodsResponse($adyFields, $store);
$paymentMethods = []; if (empty($responseData['paymentMethods'])) {
if (isset($responseData['paymentMethods'])) { return [];
foreach ($responseData['paymentMethods'] as $paymentMethod) { }
$paymentMethods = $responseData['paymentMethods'];
$response['paymentMethodsResponse'] = $responseData;
// Add extra details per payment mmethod
$paymentMethodsExtraDetails = [];
if ($this->adyenHelper->showLogos()) {
// Explicitly setting theme
$themeCode = "Magento/blank";
$themeId = $this->design->getConfigurationDesignTheme(\Magento\Framework\App\Area::AREA_FRONTEND);
if (!empty($themeId)) {
$theme = $this->themeProvider->getThemeById($themeId);
if ($theme && !empty($theme->getCode())) {
$themeCode = $theme->getCode();
}
}
$params = [];
$params = array_merge(
[
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'_secure' => $this->request->isSecure(),
'theme' => $themeCode
],
$params
);
foreach ($paymentMethods as $paymentMethod) {
$paymentMethodCode = $paymentMethod['type']; $paymentMethodCode = $paymentMethod['type'];
$paymentMethod = $this->fieldMapPaymentMethod($paymentMethod);
$asset = $this->assetRepo->createAsset(
'Adyen_Payment::images/logos/' .
$paymentMethodCode . '.png',
$params
);
$placeholder = $this->assetSource->findSource($asset);
$icon = [];
if ($placeholder) {
list($width, $height) = getimagesize($asset->getSourceFile());
$icon = [
'url' => $asset->getUrl(),
'width' => $width,
'height' => $height
];
} else {
$icon = [
'url' => 'https://checkoutshopper-live.adyen.com/checkoutshopper/images/logos/medium/' .$paymentMethodCode . '.png',
'width' => 77,
'height' => 50
];
}
$paymentMethodsExtraDetails[$paymentMethodCode]['icon'] = $icon;
// check if payment method is an openinvoice method // check if payment method is an openinvoice method
$paymentMethod['isPaymentMethodOpenInvoiceMethod'] = $paymentMethodsExtraDetails[$paymentMethodCode]['isOpenInvoice'] =
$this->adyenHelper->isPaymentMethodOpenInvoiceMethod($paymentMethodCode); $this->adyenHelper->isPaymentMethodOpenInvoiceMethod($paymentMethodCode);
// add icon location in result
if ($this->adyenHelper->showLogos()) {
// Fix for MAGETWO-70402 https://github.com/magento/magento2/pull/7686
// Explicitly setting theme
$themeCode = "Magento/blank";
$themeId = $this->design->getConfigurationDesignTheme(\Magento\Framework\App\Area::AREA_FRONTEND);
if (!empty($themeId)) {
$theme = $this->themeProvider->getThemeById($themeId);
if ($theme && !empty($theme->getCode())) {
$themeCode = $theme->getCode();
}
}
$params = [];
$params = array_merge(
[
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'_secure' => $this->request->isSecure(),
'theme' => $themeCode
],
$params
);
$asset = $this->assetRepo->createAsset(
'Adyen_Payment::images/logos/' .
$paymentMethodCode . '.png',
$params
);
$placeholder = $this->assetSource->findSource($asset);
$icon = null;
if ($placeholder) {
list($width, $height) = getimagesize($asset->getSourceFile());
$icon = [
'url' => $asset->getUrl(),
'width' => $width,
'height' => $height
];
}
$paymentMethod['icon'] = $icon;
}
$paymentMethods[$paymentMethodCode] = $paymentMethod;
} }
} }
return $paymentMethods; $response['paymentMethodsExtraDetails'] = $paymentMethodsExtraDetails;
//TODO this should be the implemented with an interface
return json_encode($response);
} }
/** /**
...@@ -306,28 +328,6 @@ class PaymentMethods extends AbstractHelper ...@@ -306,28 +328,6 @@ class PaymentMethods extends AbstractHelper
return ""; return "";
} }
/**
* @var array
*/
protected $fieldMapPaymentMethod = [
'name' => 'title'
];
/**
* @param $paymentMethod
* @return mixed
*/
protected function fieldMapPaymentMethod($paymentMethod)
{
foreach ($this->fieldMapPaymentMethod as $field => $newField) {
if (isset($paymentMethod[$field])) {
$paymentMethod[$newField] = $paymentMethod[$field];
unset($paymentMethod[$field]);
}
}
return $paymentMethod;
}
/** /**
* @param $requestParams * @param $requestParams
* @param $store * @param $store
......
...@@ -315,17 +315,6 @@ class Requests extends AbstractHelper ...@@ -315,17 +315,6 @@ class Requests extends AbstractHelper
$request['additionalData']['allow3DS2'] = true; $request['additionalData']['allow3DS2'] = true;
$request['origin'] = $this->adyenHelper->getOrigin(); $request['origin'] = $this->adyenHelper->getOrigin();
$request['channel'] = 'web'; $request['channel'] = 'web';
$request['browserInfo']['screenWidth'] = $additionalData[AdyenCcDataAssignObserver::SCREEN_WIDTH];
$request['browserInfo']['screenHeight'] = $additionalData[AdyenCcDataAssignObserver::SCREEN_HEIGHT];
$request['browserInfo']['colorDepth'] = $additionalData[AdyenCcDataAssignObserver::SCREEN_COLOR_DEPTH];
$request['browserInfo']['timeZoneOffset'] = $additionalData[AdyenCcDataAssignObserver::TIMEZONE_OFFSET];
$request['browserInfo']['language'] = $additionalData[AdyenCcDataAssignObserver::LANGUAGE];
if ($javaEnabled = $additionalData[AdyenCcDataAssignObserver::JAVA_ENABLED]) {
$request['browserInfo']['javaEnabled'] = $javaEnabled;
} else {
$request['browserInfo']['javaEnabled'] = false;
}
} else { } else {
$request['additionalData']['allow3DS2'] = false; $request['additionalData']['allow3DS2'] = false;
$request['origin'] = $this->adyenHelper->getOrigin(); $request['origin'] = $this->adyenHelper->getOrigin();
...@@ -341,7 +330,7 @@ class Requests extends AbstractHelper ...@@ -341,7 +330,7 @@ class Requests extends AbstractHelper
* @param $storeId * @param $storeId
* @param $payment * @param $payment
*/ */
public function buildRecurringData($areaCode, int $storeId, $additionalData, $request = []) public function buildRecurringData($areaCode, int $storeId, $request = [])
{ {
// If the vault feature is on this logic is handled in the VaultDataBuilder // If the vault feature is on this logic is handled in the VaultDataBuilder
if (!$this->adyenHelper->isCreditCardVaultEnabled()) { if (!$this->adyenHelper->isCreditCardVaultEnabled()) {
...@@ -363,11 +352,6 @@ class Requests extends AbstractHelper ...@@ -363,11 +352,6 @@ class Requests extends AbstractHelper
} else { } else {
$request['enableRecurring'] = false; $request['enableRecurring'] = false;
} }
// value can be 0,1 or true
if (!empty($additionalData[AdyenCcDataAssignObserver::STORE_CC])) {
$request['paymentMethod']['storeDetails'] = true;
}
} }
return $request; return $request;
......
...@@ -75,18 +75,11 @@ class AdyenOrderPaymentStatus implements \Adyen\Payment\Api\AdyenOrderPaymentSta ...@@ -75,18 +75,11 @@ class AdyenOrderPaymentStatus implements \Adyen\Payment\Api\AdyenOrderPaymentSta
) { ) {
$additionalInformation = $payment->getAdditionalInformation(); $additionalInformation = $payment->getAdditionalInformation();
$type = null; if (!empty($additionalInformation['action'])) {
if (!empty($additionalInformation['threeDSType'])) { return json_encode($additionalInformation['action']);
$type = $additionalInformation['threeDSType'];
} }
$token = null;
if (!empty($additionalInformation['threeDS2Token'])) {
$token = $additionalInformation['threeDS2Token'];
}
return $this->adyenHelper->buildThreeDS2ProcessResponseJson($type, $token);
} }
return true; return true;
} }
} }
...@@ -75,6 +75,7 @@ class AdyenThreeDS2Process implements AdyenThreeDS2ProcessInterface ...@@ -75,6 +75,7 @@ class AdyenThreeDS2Process implements AdyenThreeDS2ProcessInterface
public function initiate($payload) public function initiate($payload)
{ {
// Decode payload from frontend // Decode payload from frontend
// TODO implement interface to handle the request the correct way
$payload = json_decode($payload, true); $payload = json_decode($payload, true);
// Validate JSON that has just been parsed if it was in a valid format // Validate JSON that has just been parsed if it was in a valid format
...@@ -84,6 +85,7 @@ class AdyenThreeDS2Process implements AdyenThreeDS2ProcessInterface ...@@ -84,6 +85,7 @@ class AdyenThreeDS2Process implements AdyenThreeDS2ProcessInterface
); );
} }
// Validate if order id is present
if (empty($payload['orderId'])) { if (empty($payload['orderId'])) {
$order = $this->getOrder(); $order = $this->getOrder();
// In the next major release remove support for retrieving order from session and throw exception instead // In the next major release remove support for retrieving order from session and throw exception instead
...@@ -96,30 +98,16 @@ class AdyenThreeDS2Process implements AdyenThreeDS2ProcessInterface ...@@ -96,30 +98,16 @@ class AdyenThreeDS2Process implements AdyenThreeDS2ProcessInterface
$payment = $order->getPayment(); $payment = $order->getPayment();
// Init payments/details request
$result = [];
if ($paymentData = $payment->getAdditionalInformation("threeDS2PaymentData")) {
// Add payment data into the request object
$request = [
"paymentData" => $payment->getAdditionalInformation("threeDS2PaymentData")
];
// unset payment data from additional information // Unset action from additional info since it is not needed anymore
$payment->unsAdditionalInformation("threeDS2PaymentData"); $payment->unsAdditionalInformation("action");
} else {
$this->adyenLogger->error("3D secure 2.0 failed, payment data not found");
throw new \Magento\Framework\Exception\LocalizedException(
__('3D secure 2.0 failed, payment data not found')
);
}
// Depends on the component's response we send a fingerprint or the challenge result // TODO validate and format the request root level keys
if (!empty($payload['details']['threeds2.fingerprint'])) { $request = $payload;
$request['details']['threeds2.fingerprint'] = $payload['details']['threeds2.fingerprint'];
} elseif (!empty($payload['details']['threeds2.challengeResult'])) { // Init payments/details request
$request['details']['threeds2.challengeResult'] = $payload['details']['threeds2.challengeResult']; $result = [];
}
// Send the request // Send the request
try { try {
...@@ -135,12 +123,9 @@ class AdyenThreeDS2Process implements AdyenThreeDS2ProcessInterface ...@@ -135,12 +123,9 @@ class AdyenThreeDS2Process implements AdyenThreeDS2ProcessInterface
// Check if result is challenge shopper, if yes return the token // Check if result is challenge shopper, if yes return the token
if (!empty($result['resultCode']) && if (!empty($result['resultCode']) &&
$result['resultCode'] === 'ChallengeShopper' && $result['resultCode'] === 'ChallengeShopper' &&
!empty($result['authentication']['threeds2.challengeToken']) !empty($result['action'])
) { ) {
return $this->adyenHelper->buildThreeDS2ProcessResponseJson( return json_encode($result['action']);
$result['resultCode'],
$result['authentication']['threeds2.challengeToken']
);
} }
// Save the payments response because we are going to need it during the place order flow // Save the payments response because we are going to need it during the place order flow
......
...@@ -59,6 +59,8 @@ class GuestAdyenPaymentMethodManagement implements \Adyen\Payment\Api\GuestAdyen ...@@ -59,6 +59,8 @@ class GuestAdyenPaymentMethodManagement implements \Adyen\Payment\Api\GuestAdyen
// if shippingAddress is provided use this country // if shippingAddress is provided use this country
$country = null; $country = null;
// TODO check why the frontend doesn't have the shipping address
// or get it from the cart
if ($shippingAddress) { if ($shippingAddress) {
$country = $shippingAddress->getCountryId(); $country = $shippingAddress->getCountryId();
} }
......
...@@ -67,11 +67,6 @@ class AdyenCcConfigProvider implements ConfigProviderInterface ...@@ -67,11 +67,6 @@ class AdyenCcConfigProvider implements ConfigProviderInterface
*/ */
private $storeManager; private $storeManager;
/**
* @var \Magento\Framework\Serialize\SerializerInterface
*/
private $serializer;
/** /**
* AdyenCcConfigProvider constructor. * AdyenCcConfigProvider constructor.
* *
...@@ -82,7 +77,6 @@ class AdyenCcConfigProvider implements ConfigProviderInterface ...@@ -82,7 +77,6 @@ class AdyenCcConfigProvider implements ConfigProviderInterface
* @param \Magento\Framework\View\Asset\Source $assetSource * @param \Magento\Framework\View\Asset\Source $assetSource
* @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Payment\Model\CcConfig $ccConfig * @param \Magento\Payment\Model\CcConfig $ccConfig
* @param \Magento\Framework\Serialize\SerializerInterface $serializer
*/ */
public function __construct( public function __construct(
\Magento\Payment\Helper\Data $paymentHelper, \Magento\Payment\Helper\Data $paymentHelper,
...@@ -91,8 +85,7 @@ class AdyenCcConfigProvider implements ConfigProviderInterface ...@@ -91,8 +85,7 @@ class AdyenCcConfigProvider implements ConfigProviderInterface
\Magento\Framework\UrlInterface $urlBuilder, \Magento\Framework\UrlInterface $urlBuilder,
\Magento\Framework\View\Asset\Source $assetSource, \Magento\Framework\View\Asset\Source $assetSource,
\Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Payment\Model\CcConfig $ccConfig, \Magento\Payment\Model\CcConfig $ccConfig
\Magento\Framework\Serialize\SerializerInterface $serializer
) { ) {
$this->_paymentHelper = $paymentHelper; $this->_paymentHelper = $paymentHelper;
$this->_adyenHelper = $adyenHelper; $this->_adyenHelper = $adyenHelper;
...@@ -101,7 +94,6 @@ class AdyenCcConfigProvider implements ConfigProviderInterface ...@@ -101,7 +94,6 @@ class AdyenCcConfigProvider implements ConfigProviderInterface
$this->_assetSource = $assetSource; $this->_assetSource = $assetSource;
$this->ccConfig = $ccConfig; $this->ccConfig = $ccConfig;
$this->storeManager = $storeManager; $this->storeManager = $storeManager;
$this->serializer = $serializer;
} }
/** /**
...@@ -170,7 +162,7 @@ class AdyenCcConfigProvider implements ConfigProviderInterface ...@@ -170,7 +162,7 @@ class AdyenCcConfigProvider implements ConfigProviderInterface
$installments = $this->_adyenHelper->getAdyenCcConfigData('installments'); $installments = $this->_adyenHelper->getAdyenCcConfigData('installments');
if ($installmentsEnabled && $installments) { if ($installmentsEnabled && $installments) {
$config['payment']['adyenCc']['installments'] = $this->serializer->unserialize($installments); $config['payment']['adyenCc']['installments'] = $installments;
$config['payment']['adyenCc']['hasInstallments'] = true; $config['payment']['adyenCc']['hasInstallments'] = true;
} else { } else {
$config['payment']['adyenCc']['installments'] = []; $config['payment']['adyenCc']['installments'] = [];
......
...@@ -69,10 +69,6 @@ class AdyenGenericConfigProvider implements ConfigProviderInterface ...@@ -69,10 +69,6 @@ class AdyenGenericConfigProvider implements ConfigProviderInterface
$config['payment']['adyen']['showLogo'] = false; $config['payment']['adyen']['showLogo'] = false;
} }
$config['payment']['checkoutCardComponentSource'] = $this->_adyenHelper->getCheckoutCardComponentJs(
$this->storeManager->getStore()->getId()
);
return $config; return $config;
} }
......
<?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\Observer;
use Magento\Framework\DataObject;
use Magento\Payment\Observer\AbstractDataAssignObserver;
use Magento\Quote\Api\Data\PaymentInterface;
abstract class AdyenAbstractDataAssignObserver extends AbstractDataAssignObserver
{
const BRAND_CODE = 'brand_code';
const STATE_DATA = 'state_data';
const BROWSER_INFO = 'browserInfo';
const PAYMENT_METHOD = 'paymentMethod';
const RISK_DATA = 'riskData';
const STORE_PAYMENT_METHOD = 'storePaymentMethod';
const CC_TYPE = 'cc_type';
const NUMBER_OF_INSTALLMENTS = 'number_of_installments';
const COMBO_CARD_TYPE = 'combo_card_type';
/**
* @param DataObject $data
* @return array
*/
protected function getValidatedAdditionalData(DataObject $data)
{
// Get additional data array
$additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA);
if (!is_array($additionalData)) {
return [];
}
// Get a validated additional data array
$additionalData = $this->getArrayOnlyWithApprovedKeys($additionalData, $this->approvedAdditionalDataKeys);
// json decode state data
$stateData = [];
if (!empty($additionalData[self::STATE_DATA])) {
$stateData = json_decode($additionalData[self::STATE_DATA], true);
}
// Get validated state data array
if (!empty($stateData)) {
$stateData = $this->getArrayOnlyWithApprovedKeys($stateData, $this->approvedStateDataKeys);
}
// Replace state data with the decoded and validated state data
$additionalData[self::STATE_DATA] = $stateData;
return $additionalData;
}
/**
* Returns an array with only the approved keys
*
* @param array $array
* @param array $approvedKeys
* @return array
*/
private function getArrayOnlyWithApprovedKeys($array, $approvedKeys)
{
$result = [];
foreach ($approvedKeys as $approvedKey) {
if (isset($array[$approvedKey])) {
$result[$approvedKey] = $array[$approvedKey];
}
}
return $result;
}
}
...@@ -24,50 +24,31 @@ ...@@ -24,50 +24,31 @@
namespace Adyen\Payment\Observer; namespace Adyen\Payment\Observer;
use Magento\Framework\Event\Observer; use Magento\Framework\Event\Observer;
use Magento\Payment\Observer\AbstractDataAssignObserver;
use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Api\Data\PaymentInterface;
class AdyenCcDataAssignObserver extends AbstractDataAssignObserver class AdyenCcDataAssignObserver extends AbstractDataAssignObserver
{ {
const CC_TYPE = 'cc_type'; /**
const NUMBER_OF_INSTALLMENTS = 'number_of_installments'; * Approved root level keys from additional data array
const STORE_CC = 'store_cc'; *
const ENCRYPTED_CREDIT_CARD_NUMBER = 'number'; * @var array
const ENCRYPTED_SECURITY_CODE = 'cvc'; */
const ENCRYPTED_EXPIRY_MONTH = 'expiryMonth'; protected $approvedAdditionalDataKeys = [
const ENCRYPTED_EXPIRY_YEAR = 'expiryYear'; self::STATE_DATA,
const HOLDER_NAME = 'holderName'; self::COMBO_CARD_TYPE,
const VARIANT = 'variant'; self::NUMBER_OF_INSTALLMENTS
const JAVA_ENABLED = 'java_enabled'; ];
const SCREEN_COLOR_DEPTH = 'screen_color_depth';
const SCREEN_WIDTH = 'screen_width';
const SCREEN_HEIGHT = 'screen_height';
const TIMEZONE_OFFSET = 'timezone_offset';
const LANGUAGE = 'language';
const GUEST_EMAIL = 'guestEmail';
const COMBO_CARD_TYPE = 'combo_card_type';
/** /**
* Approved root level keys from the checkout component's state data object
*
* @var array * @var array
*/ */
protected $additionalInformationList = [ protected $approvedStateDataKeys = [
self::CC_TYPE, self::BROWSER_INFO,
self::NUMBER_OF_INSTALLMENTS, self::PAYMENT_METHOD,
self::STORE_CC, self::RISK_DATA,
self::ENCRYPTED_CREDIT_CARD_NUMBER, self::STORE_PAYMENT_METHOD
self::ENCRYPTED_SECURITY_CODE,
self::ENCRYPTED_EXPIRY_MONTH,
self::ENCRYPTED_EXPIRY_YEAR,
self::HOLDER_NAME,
self::VARIANT,
self::JAVA_ENABLED,
self::SCREEN_COLOR_DEPTH,
self::SCREEN_WIDTH,
self::SCREEN_HEIGHT,
self::TIMEZONE_OFFSET,
self::LANGUAGE,
self::GUEST_EMAIL,
self::COMBO_CARD_TYPE
]; ];
/** /**
...@@ -76,27 +57,20 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver ...@@ -76,27 +57,20 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver
*/ */
public function execute(Observer $observer) public function execute(Observer $observer)
{ {
// Get request fields
$data = $this->readDataArgument($observer); $data = $this->readDataArgument($observer);
$additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA); $additionalData = $this->getValidatedAdditionalData($data);
if (!is_array($additionalData)) {
return;
}
// Set additional data in the payment
$paymentInfo = $this->readPaymentModelArgument($observer); $paymentInfo = $this->readPaymentModelArgument($observer);
foreach ($additionalData as $key => $data) {
// set ccType $paymentInfo->setAdditionalInformation($key, $data);
if (!empty($additionalData['cc_type'])) {
$paymentInfo->setCcType($additionalData['cc_type']);
} }
foreach ($this->additionalInformationList as $additionalInformationKey) { // set ccType
if (isset($additionalData[$additionalInformationKey])) { if (!empty($additionalData[self::CC_TYPE])) {
$paymentInfo->setAdditionalInformation( $paymentInfo->setCcType($additionalData[self::CC_TYPE]);
$additionalInformationKey,
$additionalData[$additionalInformationKey]
);
}
} }
} }
} }
...@@ -24,40 +24,29 @@ ...@@ -24,40 +24,29 @@
namespace Adyen\Payment\Observer; namespace Adyen\Payment\Observer;
use Magento\Framework\Event\Observer; use Magento\Framework\Event\Observer;
use Magento\Payment\Observer\AbstractDataAssignObserver;
use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Api\Data\PaymentInterface;
class AdyenHppDataAssignObserver extends AbstractDataAssignObserver class AdyenHppDataAssignObserver extends AbstractDataAssignObserver
{ {
const BRAND_CODE = 'brand_code'; /**
const ISSUER_ID = 'issuer_id'; * Approved root level keys from additional data array
const GENDER = 'gender'; *
const DOB = 'dob'; * @var array
const TELEPHONE = 'telephone'; */
const DF_VALUE = 'df_value'; protected $approvedAdditionalDataKeys = [
const SSN = 'ssn'; self::STATE_DATA,
const OWNER_NAME = 'ownerName'; self::BRAND_CODE
const BANK_ACCOUNT_OWNER_NAME = 'bankAccountOwnerName'; ];
const IBAN_NUMBER = 'ibanNumber';
const BANK_ACCOUNT_NUMBER = 'bankAccountNumber';
const BANK_LOCATIONID = 'bankLocationId';
/** /**
* Approved root level keys from the checkout component's state data object
*
* @var array * @var array
*/ */
protected $additionalInformationList = [ protected $approvedStateDataKeys = [
self::BRAND_CODE, self::BROWSER_INFO,
self::ISSUER_ID, self::PAYMENT_METHOD,
self::GENDER, self::RISK_DATA
self::DOB,
self::TELEPHONE,
self::DF_VALUE,
self::SSN,
self::OWNER_NAME,
self::BANK_ACCOUNT_OWNER_NAME,
self::IBAN_NUMBER,
self::BANK_ACCOUNT_NUMBER,
self::BANK_LOCATIONID
]; ];
/** /**
...@@ -66,26 +55,20 @@ class AdyenHppDataAssignObserver extends AbstractDataAssignObserver ...@@ -66,26 +55,20 @@ class AdyenHppDataAssignObserver extends AbstractDataAssignObserver
*/ */
public function execute(Observer $observer) public function execute(Observer $observer)
{ {
// Get request fields
$data = $this->readDataArgument($observer); $data = $this->readDataArgument($observer);
$additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA); $additionalData = $this->getValidatedAdditionalData($data);
if (!is_array($additionalData)) {
return;
}
// Set additional data in the payment
$paymentInfo = $this->readPaymentModelArgument($observer); $paymentInfo = $this->readPaymentModelArgument($observer);
foreach ($additionalData as $key => $data) {
$paymentInfo->setAdditionalInformation($key, $data);
}
// Set BrandCode into CCType
if (isset($additionalData[self::BRAND_CODE])) { if (isset($additionalData[self::BRAND_CODE])) {
$paymentInfo->setCcType($additionalData[self::BRAND_CODE]); $paymentInfo->setCcType($additionalData[self::BRAND_CODE]);
} }
foreach ($this->additionalInformationList as $additionalInformationKey) {
if (isset($additionalData[$additionalInformationKey])) {
$paymentInfo->setAdditionalInformation(
$additionalInformationKey,
$additionalData[$additionalInformationKey]
);
}
}
} }
} }
...@@ -77,20 +77,17 @@ ...@@ -77,20 +77,17 @@
<tooltip>Enable installments for each credit card type.</tooltip> <tooltip>Enable installments for each credit card type.</tooltip>
<config_path>payment/adyen_cc/enable_installments</config_path> <config_path>payment/adyen_cc/enable_installments</config_path>
</field> </field>
<field id="installments" translate="label" sortOrder="220" showInDefault="1" showInWebsite="1" <field id="installments" type="textarea" translate="label" sortOrder="220" showInDefault="1"
showInStore="1"> showInWebsite="1" showInStore="1">
<label>Installments</label> <label>Installments</label>
<depends> <depends>
<field id="enable_installments">1</field> <field id="enable_installments">1</field>
</depends> </depends>
<tooltip>Configure your installment for each credit card type: Insert the minimum amount required to <validate>required-entry</validate>
make the configured installment available in the amount range column. <comment><![CDATA[
Example: if the amount range is configured to 100 and the number of installments to 4x, the shopper Generate the desired installments configuration <a href="">here</a> and paste it on this field.
will see the 4x option only if the payment total is higher or equal than 100. ]]></comment>
</tooltip> <config_path>payment/adyen_cc/installments_code</config_path>
<frontend_model>Adyen\Payment\Block\Adminhtml\System\Config\Field\Installments</frontend_model>
<backend_model>Adyen\Payment\Model\Config\Backend\Installments</backend_model>
<config_path>payment/adyen_cc/installments</config_path>
</field> </field>
</group> </group>
......
...@@ -557,7 +557,7 @@ ...@@ -557,7 +557,7 @@
<item name="payment" xsi:type="string">Adyen\Payment\Gateway\Request\PaymentDataBuilder</item> <item name="payment" xsi:type="string">Adyen\Payment\Gateway\Request\PaymentDataBuilder</item>
<item name="browserinfo" xsi:type="string">Adyen\Payment\Gateway\Request\BrowserInfoDataBuilder</item> <item name="browserinfo" xsi:type="string">Adyen\Payment\Gateway\Request\BrowserInfoDataBuilder</item>
<item name="recurring" xsi:type="string">Adyen\Payment\Gateway\Request\RecurringDataBuilder</item> <item name="recurring" xsi:type="string">Adyen\Payment\Gateway\Request\RecurringDataBuilder</item>
<item name="transaction" xsi:type="string">Adyen\Payment\Gateway\Request\CcAuthorizationDataBuilder</item> <item name="transaction" xsi:type="string">Adyen\Payment\Gateway\Request\CheckoutDataBuilder</item>
<item name="vault" xsi:type="string">Adyen\Payment\Gateway\Request\VaultDataBuilder</item> <item name="vault" xsi:type="string">Adyen\Payment\Gateway\Request\VaultDataBuilder</item>
<item name="threeds2" xsi:type="string">Adyen\Payment\Gateway\Request\ThreeDS2DataBuilder</item> <item name="threeds2" xsi:type="string">Adyen\Payment\Gateway\Request\ThreeDS2DataBuilder</item>
</argument> </argument>
...@@ -990,11 +990,6 @@ ...@@ -990,11 +990,6 @@
<argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\Serialize</argument> <argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\Serialize</argument>
</arguments> </arguments>
</type> </type>
<type name="Adyen\Payment\Gateway\Validator\InstallmentValidator">
<arguments>
<argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\Serialize</argument>
</arguments>
</type>
<type name="Adyen\Payment\Model\Ui\AdyenPosCloudConfigProvider"> <type name="Adyen\Payment\Model\Ui\AdyenPosCloudConfigProvider">
<arguments> <arguments>
<argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\Serialize</argument> <argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\Serialize</argument>
......
This diff is collapsed.
This diff is collapsed.
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* 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>
*/
define(
[
],
function () {
'use strict';
return {
getOriginKey: function () {
return window.checkoutConfig.payment.adyenCc.originKey;
},
showLogo: function () {
return window.checkoutConfig.payment.adyen.showLogo;
},
getLocale: function () {
return window.checkoutConfig.payment.adyenCc.locale;
},
getCheckoutEnvironment: function () {
return window.checkoutConfig.payment.adyenCc.checkoutEnvironment;
},
};
}
);
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
define(
[
'ko'
],
function (ko) {
'use strict';
return ko.observableArray([]);
}
);
...@@ -4,37 +4,23 @@ ...@@ -4,37 +4,23 @@
*/ */
define( define(
[ [
'ko',
'underscore', 'underscore',
'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/quote',
'Adyen_Payment/js/model/adyen-method-list',
'Magento_Customer/js/model/customer', 'Magento_Customer/js/model/customer',
'Magento_Checkout/js/model/url-builder', 'Magento_Checkout/js/model/url-builder',
'mage/storage' 'mage/storage',
'Adyen_Payment/js/bundle',
], ],
function (_, quote, methodList, customer, urlBuilder, storage) { function (ko, _, quote, customer, urlBuilder, storage, adyenComponent) {
'use strict'; 'use strict';
var checkoutComponent = {};
var paymentMethods = ko.observable({})
return { return {
/**
* Populate the list of payment methods
* @param {Array} methods
*/
setPaymentMethods: function (methods) {
methodList(methods);
},
/**
* Get the list of available payment methods.
* @returns {Array}
*/
getAvailablePaymentMethods: function () {
return methodList();
},
/** /**
* Retrieve the list of available payment methods from the server * Retrieve the list of available payment methods from the server
*/ */
retrieveAvailablePaymentMethods: function (callback) { retrieveAvailablePaymentMethods: function () {
var self = this;
// retrieve payment methods // retrieve payment methods
var serviceUrl, var serviceUrl,
payload; payload;
...@@ -51,28 +37,60 @@ define( ...@@ -51,28 +37,60 @@ define(
shippingAddress: quote.shippingAddress() shippingAddress: quote.shippingAddress()
}; };
storage.post( return storage.post(
serviceUrl, serviceUrl,
JSON.stringify(payload) JSON.stringify(payload),
).done( true
function (response) {
self.setPaymentMethods(response);
if (callback) {
callback();
}
}
).fail(
function () {
self.setPaymentMethods([]);
}
) )
}, },
/**
* The results that the 3DS2 components returns in the onComplete callback needs to be sent to the
* backend to the /adyen/threeDS2Process endpoint and based on the response render a new threeDS2
* component or place the order (validateThreeDS2OrPlaceOrder)
* @param response
*/
processThreeDS2: function (data) {
var payload = {
"payload": JSON.stringify(data)
};
var serviceUrl = urlBuilder.createUrl('/adyen/threeDS2Process', {});
return storage.post(
serviceUrl,
JSON.stringify(payload),
true
);
},
getOrderPaymentStatus: function (orderId) { getOrderPaymentStatus: function (orderId) {
var serviceUrl = urlBuilder.createUrl('/adyen/orders/:orderId/payment-status', { var serviceUrl = urlBuilder.createUrl('/adyen/orders/:orderId/payment-status', {
orderId: orderId orderId: orderId
}); });
return storage.get(serviceUrl); return storage.get(serviceUrl);
},
initCheckoutComponent: function(paymentMethodsResponse, originKey, locale, environment, ) {
checkoutComponent = new AdyenCheckout({
locale: locale,
originKey: originKey,
environment: environment,
paymentMethodsResponse: paymentMethodsResponse,
consentCheckbox: false,
visibility: {
personalDetails: 'editable',
billingAddress: 'editable',
separateDeliveryAddress: 'hidden',
deliveryAddress: 'hidden'
}
});
paymentMethods(paymentMethodsResponse.paymentMethods);
},
getCheckoutComponent: function() {
return checkoutComponent;
},
getPaymentMethodsObservable: function() {
return paymentMethods;
} }
}; };
} }
......
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
define(
[
'ko',
'jquery',
],
function (ko, $) {
'use strict';
return {
/**
*
* @param installments
* @param grandTotal
* @param precision
* @param currencyCode
* @returns {Array}
*/
getInstallmentsWithPrices: function (installments, grandTotal, precision, currencyCode) {
let numberOfInstallments = [];
let dividedAmount = 0;
let dividedString = "";
$.each(installments, function (amount, installment) {
if (grandTotal >= amount) {
dividedAmount = (grandTotal / installment).toFixed(precision);
dividedString = installment + " x " + dividedAmount + " " + currencyCode;
numberOfInstallments.push({
key: [dividedString],
value: installment
});
}
});
return numberOfInstallments;
}
};
}
);
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
define(
[
'Magento_Checkout/js/model/url-builder',
'mage/storage'
],
function (urlBuilder, storage) {
'use strict';
return {
/**
* The results that the 3DS2 components returns in the onComplete callback needs to be sent to the
* backend to the /adyen/threeDS2Process endpoint and based on the response render a new threeDS2
* component or place the order (validateThreeDS2OrPlaceOrder)
* @param response
*/
processThreeDS2: function (data) {
var payload = {
"payload": JSON.stringify(data)
};
var serviceUrl = urlBuilder.createUrl('/adyen/threeDS2Process', {});
return storage.post(
serviceUrl,
JSON.stringify(payload),
true
);
}
};
}
);
...@@ -24,11 +24,19 @@ ...@@ -24,11 +24,19 @@
define( define(
[ [
'uiComponent', 'uiComponent',
'Magento_Checkout/js/model/payment/renderer-list' 'Magento_Checkout/js/model/payment/renderer-list',
'Adyen_Payment/js/model/adyen-payment-service',
'Adyen_Payment/js/model/adyen-configuration',
'Magento_Checkout/js/model/quote',
'Magento_Customer/js/model/customer'
], ],
function ( function (
Component, Component,
rendererList rendererList,
adyenPaymentService,
adyenConfiguration,
quote,
customer
) { ) {
'use strict'; 'use strict';
rendererList.push( rendererList.push(
...@@ -63,16 +71,13 @@ define( ...@@ -63,16 +71,13 @@ define(
); );
/** Add view logic here if needed */ /** Add view logic here if needed */
return Component.extend({ return Component.extend({
defaults: {
countryCode: ""
},
initialize: function () { initialize: function () {
var self = this; var self = this;
this._super();
// include checkout card component javascript this._super();
var checkoutCardComponentScriptTag = document.createElement('script');
checkoutCardComponentScriptTag.id = "AdyenCheckoutCardComponentScript";
checkoutCardComponentScriptTag.src = self.getCheckoutCardComponentSource();
checkoutCardComponentScriptTag.type = "text/javascript";
document.head.appendChild(checkoutCardComponentScriptTag);
if (this.isGooglePayEnabled()) { if (this.isGooglePayEnabled()) {
var googlepayscript = document.createElement('script'); var googlepayscript = document.createElement('script');
...@@ -80,13 +85,59 @@ define( ...@@ -80,13 +85,59 @@ define(
googlepayscript.type = "text/javascript"; googlepayscript.type = "text/javascript";
document.head.appendChild(googlepayscript); document.head.appendChild(googlepayscript);
} }
if (customer.isLoggedIn()) {
self.setAdyenPaymentMethods();
}
quote.shippingAddress.subscribe(function() {
if (!!quote.shippingAddress().countryId && self.countryCode !== quote.shippingAddress().countryId) {
self.countryCode = quote.shippingAddress().countryId;
self.setAdyenPaymentMethods();
}
})
}, },
getCheckoutCardComponentSource: function () { setAdyenPaymentMethods: function() {
return window.checkoutConfig.payment.checkoutCardComponentSource; adyenPaymentService.retrieveAvailablePaymentMethods().done(function (response) {
var responseJson = JSON.parse(response);
var paymentMethodsResponse = responseJson.paymentMethodsResponse;
// TODO check if this is still required or if can be outsourced for the generic component, or checkout can create a ratepay component
/*if (!!window.checkoutConfig.payment.adyenHpp) {
if (JSON.stringify(paymentMethods).indexOf("ratepay") > -1) {
var ratePayId = window.checkoutConfig.payment.adyenHpp.ratePayId;
var dfValueRatePay = self.getRatePayDeviceIdentToken();
window.di = {
t: dfValueRatePay.replace(':', ''),
v: ratePayId,
l: 'Checkout'
};
// Load Ratepay script
var ratepayScriptTag = document.createElement('script');
ratepayScriptTag.src = "//d.ratepay.com/" + ratePayId + "/di.js";
ratepayScriptTag.type = "text/javascript";
document.body.appendChild(ratepayScriptTag);
}
}*/
// Initialises adyen checkout main component with default configuration
adyenPaymentService.initCheckoutComponent(
paymentMethodsResponse,
adyenConfiguration.getOriginKey(),
adyenConfiguration.getLocale(),
adyenConfiguration.getCheckoutEnvironment()
);
})
}, },
isGooglePayEnabled: function () { isGooglePayEnabled: function () {
return window.checkoutConfig.payment.adyenGooglePay.active; return window.checkoutConfig.payment.adyenGooglePay.active;
},
getRatePayDeviceIdentToken: function () {
return window.checkoutConfig.payment.adyenHpp.deviceIdentToken;
} }
}); });
} }
); );
\ No newline at end of file
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
<!-- /ko --> <!-- /ko -->
<div class="field number cardContainerField"> <div class="field number cardContainerField">
<div class="checkout-component-dock" afterRender="renderSecureFields()" data-bind="attr: { id: 'cardContainer'}"></div> <div class="checkout-component-dock" afterRender="renderCardComponent()" data-bind="attr: { id: 'cardContainer'}"></div>
</div> </div>
<div id="threeDS2Wrapper"> <div id="threeDS2Wrapper">
...@@ -101,34 +101,6 @@ ...@@ -101,34 +101,6 @@
</div> </div>
</div> </div>
<!-- ko if: (hasInstallments())-->
<div class="field required"
data-bind="attr: {id: getCode() + '_installments_div'}, visible: installments().length > 0">
<label data-bind="attr: {for: getCode() + '_installments'}" class="label">
<span><!-- ko text: $t('Installments')--><!-- /ko --></span>
</label>
<div class="control">
<select class="select"
name="payment[number_of_installments]"
data-bind="attr: {id: getCode() + '_installments', 'data-container': getCode() + '-installments', 'data-validate': JSON.stringify({required:false})},
enable: isActive($parents),
options: installments,
optionsValue: 'value',
optionsText: 'key',
optionsCaption: $t('Do not use Installments'),
value: installment"
data-validate="{required:true}">
</select>
</div>
</div>
<!-- /ko -->
<!-- ko if: (isVaultEnabled())--> <!-- ko if: (isVaultEnabled())-->
<div class="field choice"> <div class="field choice">
<input type="checkbox" <input type="checkbox"
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* Author: Adyen <magento@adyen.com> * Author: Adyen <magento@adyen.com>
*/ */
--> -->
<!-- ko foreach: getAdyenBillingAgreements() --> <!-- ko foreach: getAdyenStoredPayments() -->
<div class="payment-method" data-bind="css: {'_active': (value == $parent.isBillingAgreementChecked())}"> <div class="payment-method" data-bind="css: {'_active': (value == $parent.isBillingAgreementChecked())}">
<div class="payment-method-title field choice"> <div class="payment-method-title field choice">
<input type="radio" <input type="radio"
...@@ -46,7 +46,8 @@ ...@@ -46,7 +46,8 @@
<div class="payment-method-content"> <div class="payment-method-content">
<!-- ko ifnot: (getOriginKey()) --> <!-- ko ifnot: (getOriginKey()) -->
<span class="message message-error error"><!-- ko text: $t('Please configure an API Key and a live endpoint prefix(if in Production Mode) in your Adyen Required Settings')--><!-- /ko --></span> <span class="message message-error error"><!-- ko text: $t('Please configure an API Key and a live endpoint prefix(if in Production Mode) in your Adyen Required Settings')-->
<!-- /ko --></span>
<!--/ko--> <!--/ko-->
<!-- ko foreach: $parent.getRegion(getMessageName()) --> <!-- ko foreach: $parent.getRegion(getMessageName()) -->
...@@ -69,72 +70,12 @@ ...@@ -69,72 +70,12 @@
}, 'validation':[]}"> }, 'validation':[]}">
<fieldset <fieldset
data-bind="attr: {class: 'fieldset payment items ccard ' + getCode(), id: 'payment_form_' + $parent.getCode() + '_' + value}"> data-bind="attr: {class: 'fieldset payment items ccard ' + getCode(), id: 'payment_form_' + $parent.getCode() + '_' + value}">
<!-- ko if: agreement_data.card --> <div class="checkout-component-dock" afterRender="renderStoredPaymentComponent()"
<div class="field number"> data-bind="attr: { id: 'storedPaymentContainer-' + value}"></div>
<label class="label">
<span><!-- ko text: $t('Credit Card Number')--><!-- /ko --></span>
</label>
<div class="control">
<span data-bind="text: '**** **** **** ' + agreement_data.card.number"></span>
</div>
</div>
<div class="checkout-component-dock" afterRender="renderSecureCVC()" data-bind="attr: { id: 'cvcContainer-' + value}"></div>
<!--/ko-->
<!-- ko if: agreement_data.bank -->
<div class="field number">
<label class="label">
<span><!-- ko text: $t('Bank account holder name')--><!-- /ko --></span>
</label>
<div class="control">
<span data-bind="text: agreement_data.bank.ownerName"></span>
</div>
</div>
<div class="field number">
<label class="label">
<span><!-- ko text: $t('Iban')--><!-- /ko --></span>
</label>
<div class="control">
<span data-bind="text: agreement_data.bank.iban"></span>
</div>
</div>
<div class="field number">
<label class="label">
<span><!-- ko text: $t('Country')--><!-- /ko --></span>
</label>
<div class="control">
<span data-bind="text: agreement_data.bank.countryCode"></span>
</div>
</div>
<!--/ko--> <!--/ko-->
<!-- ko if: number_of_installments.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">
<span><!-- ko text: $t('Installments')--><!-- /ko --></span>
</label>
<div class="control">
<select class="select"
name="payment[number_of_installments]"
data-bind="attr: {id: getCode() + '_installments', 'data-container': getCode() + '-installments', 'data-validate': JSON.stringify({required:false})},
enable: $parent.isActive($parents),
options: getInstallments,
optionsValue: 'value',
optionsText: 'key',
optionsCaption: $t('Do not use Installments'),
value: installment"
data-validate="{required:true}">
</select>
</div>
</div>
<!-- /ko -->
<div id="threeDS2ModalOneClick"> <div id="threeDS2ModalOneClick">
<div id="threeDS2ContainerOneClick"></div> <div id="threeDS2ContainerOneClick"></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