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 8da089ce authored by Attila Kiss's avatar Attila Kiss Committed by GitHub

[PW-3130] Use the generic component for stored payment methods (#919)

* Tokenize payment methods in payment response handler

Do not save recurring details "manually" for /payments/details response

* Standardise Oneclick Observer

* Use generic component for recurring payments

* sonarcloud suggestions

* Remove unused div
parent 3f7ebc9e
...@@ -55,20 +55,6 @@ class OneclickAuthorizationDataBuilder implements BuilderInterface ...@@ -55,20 +55,6 @@ class OneclickAuthorizationDataBuilder implements BuilderInterface
$paymentDataObject = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($buildSubject); $paymentDataObject = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($buildSubject);
$payment = $paymentDataObject->getPayment(); $payment = $paymentDataObject->getPayment();
// If ccType is set use this. For bcmc you need bcmc otherwise it will fail
$requestBody['paymentMethod']['type'] = "scheme";
if ($variant = $payment->getAdditionalInformation(AdyenOneclickDataAssignObserver::VARIANT)) {
$requestBody['paymentMethod']['type'] = $variant;
}
if ($securityCode = $payment->getAdditionalInformation(
AdyenOneclickDataAssignObserver::ENCRYPTED_SECURITY_CODE
)) {
$requestBody['paymentMethod']['encryptedSecurityCode'] = $securityCode;
}
$payment->unsAdditionalInformation(AdyenOneclickDataAssignObserver::ENCRYPTED_SECURITY_CODE);
if ($payment->getAdditionalInformation('customer_interaction')) { if ($payment->getAdditionalInformation('customer_interaction')) {
$shopperInteraction = "Ecommerce"; $shopperInteraction = "Ecommerce";
} else { } else {
...@@ -76,9 +62,7 @@ class OneclickAuthorizationDataBuilder implements BuilderInterface ...@@ -76,9 +62,7 @@ class OneclickAuthorizationDataBuilder implements BuilderInterface
} }
$requestBody['shopperInteraction'] = $shopperInteraction; $requestBody['shopperInteraction'] = $shopperInteraction;
$requestBody['paymentMethod']['recurringDetailReference'] = $payment->getAdditionalInformation(
AdyenOneclickDataAssignObserver::RECURRING_DETAIL_REFERENCE
);
// if it is a sepadirectdebit set selectedBrand to sepadirectdebit in the case of oneclick // if it is a sepadirectdebit set selectedBrand to sepadirectdebit in the case of oneclick
if ($payment->getCcType() == "sepadirectdebit") { if ($payment->getCcType() == "sepadirectdebit") {
......
...@@ -1713,7 +1713,7 @@ class Data extends AbstractHelper ...@@ -1713,7 +1713,7 @@ class Data extends AbstractHelper
$billingAgreement->getAgreementId(), $billingAgreement->getAgreementId(),
$order->getId() $order->getId()
)) { )) {
// save into sales_billing_agreement_order // save into billing_agreement_order
$billingAgreement->addOrderRelation($order); $billingAgreement->addOrderRelation($order);
} }
// add to order to save agreement // add to order to save agreement
...@@ -1733,6 +1733,7 @@ class Data extends AbstractHelper ...@@ -1733,6 +1733,7 @@ class Data extends AbstractHelper
$comment = $order->addStatusHistoryComment($message); $comment = $order->addStatusHistoryComment($message);
$order->addRelatedObject($comment); $order->addRelatedObject($comment);
$order->save();
} }
} }
......
...@@ -27,6 +27,7 @@ use Adyen\Payment\Logger\AdyenLogger; ...@@ -27,6 +27,7 @@ use Adyen\Payment\Logger\AdyenLogger;
use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Api\Data\OrderPaymentInterface;
use Magento\Sales\Model\Order; use Magento\Sales\Model\Order;
use Adyen\Payment\Helper\Vault;
class PaymentResponseHandler class PaymentResponseHandler
{ {
...@@ -41,12 +42,36 @@ class PaymentResponseHandler ...@@ -41,12 +42,36 @@ class PaymentResponseHandler
const ERROR = 'Error'; const ERROR = 'Error';
const CANCELLED = 'Cancelled'; const CANCELLED = 'Cancelled';
/**
* @var AdyenLogger
*/
private $adyenLogger; private $adyenLogger;
/**
* @var Data
*/
private $adyenHelper;
/**
* @var Vault
*/
private $vaultHelper;
/**
* PaymentResponseHandler constructor.
*
* @param AdyenLogger $adyenLogger
* @param Data $adyenHelper
* @param \Adyen\Payment\Helper\Vault $vaultHelper
*/
public function __construct( public function __construct(
AdyenLogger $adyenLogger AdyenLogger $adyenLogger,
Data $adyenHelper,
Vault $vaultHelper
) { ) {
$this->adyenLogger = $adyenLogger; $this->adyenLogger = $adyenLogger;
$this->adyenHelper = $adyenHelper;
$this->vaultHelper = $vaultHelper;
} }
public function formatPaymentResponse($resultCode, $action = null, $additionalData = null) public function formatPaymentResponse($resultCode, $action = null, $additionalData = null)
...@@ -144,6 +169,24 @@ class PaymentResponseHandler ...@@ -144,6 +169,24 @@ class PaymentResponseHandler
} }
break; break;
case self::AUTHORISED: case self::AUTHORISED:
if (!empty($paymentsResponse['pspReference'])) {
// set pspReference as transactionId
$payment->setCcTransId($paymentsResponse['pspReference']);
$payment->setLastTransId($paymentsResponse['pspReference']);
// set transaction
$payment->setTransactionId($paymentsResponse['pspReference']);
}
if (!empty($paymentsResponse['additionalData']['recurring.recurringDetailReference']) &&
$payment->getMethodInstance()->getCode() !== \Adyen\Payment\Model\Ui\AdyenOneclickConfigProvider::CODE) {
if ($this->adyenHelper->isCreditCardVaultEnabled()) {
$this->vaultHelper->saveRecurringDetails($payment, $paymentsResponse['additionalData']);
} else {
$order = $payment->getOrder();
$this->adyenHelper->createAdyenBillingAgreement($order, $paymentsResponse['additionalData']);
}
}
case self::IDENTIFY_SHOPPER: case self::IDENTIFY_SHOPPER:
case self::CHALLENGE_SHOPPER: case self::CHALLENGE_SHOPPER:
break; break;
......
...@@ -27,7 +27,6 @@ use Adyen\AdyenException; ...@@ -27,7 +27,6 @@ use Adyen\AdyenException;
use Adyen\Payment\Api\AdyenPaymentDetailsInterface; use Adyen\Payment\Api\AdyenPaymentDetailsInterface;
use Adyen\Payment\Helper\Data; use Adyen\Payment\Helper\Data;
use Adyen\Payment\Helper\PaymentResponseHandler; use Adyen\Payment\Helper\PaymentResponseHandler;
use Adyen\Payment\Helper\Vault;
use Adyen\Payment\Logger\AdyenLogger; use Adyen\Payment\Logger\AdyenLogger;
use Magento\Checkout\Model\Session; use Magento\Checkout\Model\Session;
use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\LocalizedException;
...@@ -50,11 +49,6 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface ...@@ -50,11 +49,6 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface
*/ */
private $adyenLogger; private $adyenLogger;
/**
* @var Vault
*/
private $vaultHelper;
/** /**
* @var OrderRepositoryInterface * @var OrderRepositoryInterface
*/ */
...@@ -71,7 +65,6 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface ...@@ -71,7 +65,6 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface
* @param Session $checkoutSession * @param Session $checkoutSession
* @param Data $adyenHelper * @param Data $adyenHelper
* @param AdyenLogger $adyenLogger * @param AdyenLogger $adyenLogger
* @param Vault $vaultHelper
* @param OrderRepositoryInterface $orderRepository * @param OrderRepositoryInterface $orderRepository
* @param PaymentResponseHandler $paymentResponseHandler * @param PaymentResponseHandler $paymentResponseHandler
*/ */
...@@ -79,14 +72,12 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface ...@@ -79,14 +72,12 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface
Session $checkoutSession, Session $checkoutSession,
Data $adyenHelper, Data $adyenHelper,
AdyenLogger $adyenLogger, AdyenLogger $adyenLogger,
Vault $vaultHelper,
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
PaymentResponseHandler $paymentResponseHandler PaymentResponseHandler $paymentResponseHandler
) { ) {
$this->checkoutSession = $checkoutSession; $this->checkoutSession = $checkoutSession;
$this->adyenHelper = $adyenHelper; $this->adyenHelper = $adyenHelper;
$this->adyenLogger = $adyenLogger; $this->adyenLogger = $adyenLogger;
$this->vaultHelper = $vaultHelper;
$this->orderRepository = $orderRepository; $this->orderRepository = $orderRepository;
$this->paymentResponseHandler = $paymentResponseHandler; $this->paymentResponseHandler = $paymentResponseHandler;
} }
...@@ -139,14 +130,7 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface ...@@ -139,14 +130,7 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface
throw new LocalizedException(__('Payment details call failed')); throw new LocalizedException(__('Payment details call failed'));
} }
//TODO test this with payments that return additionalData // Handle response
//TODO check for Authorized result code and move to the handler
if (!empty($paymentDetails['additionalData'])) {
$this->vaultHelper->saveRecurringDetails($payment, $paymentDetails['additionalData']);
}
//TODO check if order save is necessary to save additionalData
if (!$this->paymentResponseHandler->handlePaymentResponse($paymentDetails, $payment, $order)) { if (!$this->paymentResponseHandler->handlePaymentResponse($paymentDetails, $payment, $order)) {
$this->checkoutSession->restoreQuote(); $this->checkoutSession->restoreQuote();
throw new LocalizedException(__('The payment is REFUSED.')); throw new LocalizedException(__('The payment is REFUSED.'));
......
...@@ -37,6 +37,7 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver ...@@ -37,6 +37,7 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver
const GUEST_EMAIL = 'guestEmail'; const GUEST_EMAIL = 'guestEmail';
const COMBO_CARD_TYPE = 'combo_card_type'; const COMBO_CARD_TYPE = 'combo_card_type';
const STATE_DATA = 'stateData'; const STATE_DATA = 'stateData';
const STORE_PAYMENT_METHOD = 'storePaymentMethod';
/** /**
* Approved root level keys from additional data array * Approved root level keys from additional data array
...@@ -113,5 +114,10 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver ...@@ -113,5 +114,10 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver
if (!empty($additionalData[self::CC_TYPE])) { if (!empty($additionalData[self::CC_TYPE])) {
$paymentInfo->setCcType($additionalData[self::CC_TYPE]); $paymentInfo->setCcType($additionalData[self::CC_TYPE]);
} }
// set storeCc
if (!empty($stateData[self::STORE_PAYMENT_METHOD])) {
$paymentInfo->setAdditionalInformation(self::STORE_CC, $stateData[self::STORE_PAYMENT_METHOD]);
}
} }
} }
...@@ -23,39 +23,34 @@ ...@@ -23,39 +23,34 @@
namespace Adyen\Payment\Observer; namespace Adyen\Payment\Observer;
use Adyen\Service\Validator\CheckoutStateDataValidator;
use Adyen\Service\Validator\DataArrayValidator;
use Magento\Framework\Event\Observer; use Magento\Framework\Event\Observer;
use Magento\Payment\Observer\AbstractDataAssignObserver; use Magento\Payment\Observer\AbstractDataAssignObserver;
use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Api\Data\PaymentInterface;
class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
{ {
const RECURRING_DETAIL_REFERENCE = 'recurring_detail_reference'; const CC_TYPE = 'cc_type';
const ENCRYPTED_SECURITY_CODE = 'cvc'; const BRAND = 'brand';
const NUMBER_OF_INSTALLMENTS = 'number_of_installments'; const NUMBER_OF_INSTALLMENTS = 'number_of_installments';
const VARIANT = 'variant'; const STATE_DATA = 'stateData';
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';
/** /**
* Approved root level keys from additional data array
*
* @var array * @var array
*/ */
protected $additionalInformationList = [ private static $approvedAdditionalDataKeys = [
self::RECURRING_DETAIL_REFERENCE, self::STATE_DATA,
self::ENCRYPTED_SECURITY_CODE,
self::NUMBER_OF_INSTALLMENTS, self::NUMBER_OF_INSTALLMENTS,
self::VARIANT,
self::JAVA_ENABLED,
self::SCREEN_COLOR_DEPTH,
self::SCREEN_WIDTH,
self::SCREEN_HEIGHT,
self::TIMEZONE_OFFSET,
self::LANGUAGE
]; ];
/**
* @var CheckoutStateDataValidator
*/
private $checkoutStateDataValidator;
/** /**
* @var \Adyen\Payment\Helper\Data * @var \Adyen\Payment\Helper\Data
*/ */
...@@ -72,9 +67,11 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver ...@@ -72,9 +67,11 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
* @param \Adyen\Payment\Helper\Data $adyenHelper * @param \Adyen\Payment\Helper\Data $adyenHelper
*/ */
public function __construct( public function __construct(
CheckoutStateDataValidator $checkoutStateDataValidator,
\Adyen\Payment\Helper\Data $adyenHelper, \Adyen\Payment\Helper\Data $adyenHelper,
\Magento\Framework\Model\Context $context \Magento\Framework\Model\Context $context
) { ) {
$this->checkoutStateDataValidator = $checkoutStateDataValidator;
$this->adyenHelper = $adyenHelper; $this->adyenHelper = $adyenHelper;
$this->appState = $context->getAppState(); $this->appState = $context->getAppState();
} }
...@@ -85,27 +82,47 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver ...@@ -85,27 +82,47 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
*/ */
public function execute(Observer $observer) public function execute(Observer $observer)
{ {
// Get request fields
$data = $this->readDataArgument($observer); $data = $this->readDataArgument($observer);
// Get additional data array
$additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA); $additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA);
if (!is_array($additionalData)) { if (!is_array($additionalData)) {
return; return;
} }
// Get a validated additional data array
$additionalData = DataArrayValidator::getArrayOnlyWithApprovedKeys(
$additionalData,
self::$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->checkoutStateDataValidator->getValidatedAdditionalData(
$stateData
);
}
// Replace state data with the decoded and validated state data
$additionalData[self::STATE_DATA] = $stateData;
// Set additional data in the payment
$paymentInfo = $this->readPaymentModelArgument($observer); $paymentInfo = $this->readPaymentModelArgument($observer);
foreach ($additionalData as $key => $data) {
$paymentInfo->setAdditionalInformation($key, $data);
}
// set ccType // set ccType
$variant = $additionalData['variant']; if (!empty($stateData[self::BRAND])) {
$ccType = $this->adyenHelper->getMagentoCreditCartType($variant); $ccType = $this->adyenHelper->getMagentoCreditCartType($stateData[self::BRAND]);
$paymentInfo->setCcType($ccType); $paymentInfo->setCcType($ccType);
foreach ($this->additionalInformationList as $additionalInformationKey) {
if (isset($additionalData[$additionalInformationKey])) {
$paymentInfo->setAdditionalInformation(
$additionalInformationKey,
$additionalData[$additionalInformationKey]
);
}
} }
// set customerInteraction // set customerInteraction
...@@ -115,11 +132,6 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver ...@@ -115,11 +132,6 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
} else { } else {
$paymentInfo->setAdditionalInformation('customer_interaction', false); $paymentInfo->setAdditionalInformation('customer_interaction', false);
} }
// set ccType
$variant = $additionalData['variant'];
$ccType = $this->adyenHelper->getMagentoCreditCartType($variant);
$paymentInfo->setAdditionalInformation('cc_type', $ccType);
} }
/** /**
......
...@@ -21,548 +21,489 @@ ...@@ -21,548 +21,489 @@
*/ */
define( define(
[ [
'ko', 'ko',
'underscore', 'underscore',
'jquery', 'jquery',
'Magento_Payment/js/view/payment/cc-form', 'Magento_Payment/js/view/payment/cc-form',
'Magento_Checkout/js/action/select-payment-method', 'Magento_Checkout/js/action/select-payment-method',
'Magento_Checkout/js/model/payment/additional-validators', 'Magento_Checkout/js/model/payment/additional-validators',
'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/checkout-data', 'Magento_Checkout/js/checkout-data',
'Magento_Checkout/js/action/redirect-on-success', 'uiLayout',
'uiLayout', 'Magento_Ui/js/model/messages',
'Magento_Ui/js/model/messages', 'mage/url',
'mage/url', 'Magento_Checkout/js/model/full-screen-loader',
'Magento_Checkout/js/model/full-screen-loader', 'Magento_Checkout/js/action/place-order',
'Magento_Paypal/js/action/set-payment-method', 'Magento_Checkout/js/model/error-processor',
'Magento_Checkout/js/model/url-builder', 'Adyen_Payment/js/model/adyen-payment-service',
'mage/storage', 'Adyen_Payment/js/bundle',
'Magento_Checkout/js/action/place-order', 'Adyen_Payment/js/model/adyen-configuration',
'Magento_Checkout/js/model/error-processor', ],
'Adyen_Payment/js/model/adyen-payment-service', function(
'Adyen_Payment/js/bundle', ko,
], _,
$,
function( Component,
ko, selectPaymentMethodAction,
_, additionalValidators,
$, quote,
Component, checkoutData,
selectPaymentMethodAction, layout,
additionalValidators, Messages,
quote, url,
checkoutData, fullScreenLoader,
redirectOnSuccessAction, placeOrderAction,
layout, errorProcessor,
Messages, adyenPaymentService,
url, AdyenComponent,
fullScreenLoader, adyenConfiguration,
setPaymentMethodAction, ) {
urlBuilder,
storage, 'use strict';
placeOrderAction,
errorProcessor, var messageComponents;
adyenPaymentService,
AdyenComponent var recurringDetailReference = ko.observable(null);
) { var variant = ko.observable(null);
var paymentMethod = ko.observable(null);
'use strict'; var numberOfInstallments = ko.observable(null);
var isValid = ko.observable(false);
var messageComponents;
return Component.extend({
var recurringDetailReference = ko.observable(null); isPlaceOrderActionAllowed: ko.observable(
var variant = ko.observable(null); quote.billingAddress() != null),
var paymentMethod = ko.observable(null); defaults: {
var numberOfInstallments = ko.observable(null); template: 'Adyen_Payment/payment/oneclick-form',
var isValid = ko.observable(false); recurringDetailReference: '',
variant: '',
return Component.extend({ numberOfInstallments: '',
isPlaceOrderActionAllowed: ko.observable(quote.billingAddress() != null), },
defaults: { initObservable: function() {
template: 'Adyen_Payment/payment/oneclick-form', this._super().observe([
recurringDetailReference: '', 'recurringDetailReference',
variant: '', 'variant',
numberOfInstallments: '', 'numberOfInstallments',
}, ]);
initObservable: function() { return this;
this._super().observe([ },
'recurringDetailReference', initialize: function() {
'creditCardType', let self = this;
'encryptedCreditCardVerificationNumber', this._super();
'variant',
'numberOfInstallments', // create component needs to be in initialize method
]); let messageComponents = {};
return this; _.map(
}, window.checkoutConfig.payment.adyenOneclick.billingAgreements,
initialize: function() { function(billingAgreement) {
var self = this;
this._super(); let messageContainer = new Messages();
let name = 'messages-' + billingAgreement.reference_id;
// create component needs to be in initialize method let messagesComponent = {
var messageComponents = {}; parent: self.name,
_.map(window.checkoutConfig.payment.adyenOneclick.billingAgreements, name: 'messages-' + billingAgreement.reference_id,
function(value) { // name: self.name + '.messages',
displayArea: 'messages-' +
var messageContainer = new Messages(); billingAgreement.reference_id,
var name = 'messages-' + value.reference_id; component: 'Magento_Ui/js/view/messages',
var messagesComponent = { config: {
parent: self.name, messageContainer: messageContainer,
name: 'messages-' + value.reference_id, },
// name: self.name + '.messages', };
displayArea: 'messages-' + value.reference_id, layout([messagesComponent]);
component: 'Magento_Ui/js/view/messages',
config: { messageComponents[name] = messageContainer;
messageContainer: messageContainer, });
}, this.messageComponents = messageComponents;
};
layout([messagesComponent]); let paymentMethodsObserver = adyenPaymentService.getPaymentMethods();
let paymentMethodsResponse = paymentMethodsObserver();
messageComponents[name] = messageContainer;
}); if (!!paymentMethodsResponse) {
this.messageComponents = messageComponents;
}, this.checkoutComponent = new AdyenCheckout({
/** locale: adyenConfiguration.getLocale(),
* List all Adyen billing agreements originKey: adyenConfiguration.getOriginKey(),
* Set up installments environment: adyenConfiguration.getCheckoutEnvironment(),
* paymentMethodsResponse: paymentMethodsResponse.paymentMethodsResponse,
* @returns {Array} onAdditionalDetails: this.handleOnAdditionalDetails.bind(this),
*/ },
getAdyenBillingAgreements: function() { );
var self = this; }
},
// shareable adyen checkout component handleOnAdditionalDetails: function(result) {
var checkout = new AdyenCheckout({
locale: self.getLocale(),
originKey: self.getOriginKey(),
environment: self.getCheckoutEnvironment(),
risk: {
enabled: false,
},
});
// convert to list so you can iterate
var paymentList = _.map(
window.checkoutConfig.payment.adyenOneclick.billingAgreements,
function(value) {
var creditCardExpMonth, creditCardExpYear = false;
if (value.agreement_data.card) {
creditCardExpMonth = value.agreement_data.card.expiryMonth;
creditCardExpYear = value.agreement_data.card.expiryYear;
}
// pre-define installments if they are set
var i, installments = [];
var grandTotal = quote.totals().grand_total;
var dividedString = '';
var dividedAmount = 0;
if (value.number_of_installments) {
for (i = 0; i < value.number_of_installments.length; i++) {
dividedAmount = (grandTotal /
value.number_of_installments[i]).toFixed(
quote.getPriceFormat().precision);
dividedString = value.number_of_installments[i] + ' x ' +
dividedAmount + ' ' + quote.totals().quote_currency_code;
installments.push({
key: [dividedString],
value: value.number_of_installments[i],
});
}
}
var messageContainer = self.messageComponents['messages-' +
value.reference_id];
// for recurring enable the placeOrder button at all times
var placeOrderAllowed = true;
if (self.hasVerification()) {
placeOrderAllowed = false;
} else {
// for recurring cards there is no validation needed
isValid(true);
}
return {
'label': value.agreement_label,
'value': value.reference_id,
'agreement_data': value.agreement_data,
'logo': value.logo,
'installment': '',
'number_of_installments': value.number_of_installments,
'method': self.item.method,
'encryptedCreditCardVerificationNumber': '',
'creditCardExpMonth': ko.observable(creditCardExpMonth),
'creditCardExpYear': ko.observable(creditCardExpYear),
'getInstallments': ko.observableArray(installments),
'placeOrderAllowed': ko.observable(placeOrderAllowed),
isButtonActive: function() {
return self.isActive() && this.getCode() == self.isChecked() &&
self.isBillingAgreementChecked() &&
this.placeOrderAllowed() &&
self.isPlaceOrderActionAllowed();
},
/**
* Custom place order function
*
* @override
*
* @param data
* @param event
* @returns {boolean}
*/
placeOrder: function(data, event) {
var self = this; var self = this;
var request = result.data;
request.orderId = self.orderId;
if (event) { fullScreenLoader.stopLoader();
event.preventDefault();
}
// only use installments for cards
if (self.agreement_data.card) {
if (self.hasVerification()) {
var options = {enableValidations: false};
}
numberOfInstallments(self.installment);
}
if (this.validate() && additionalValidators.validate()) { // TODO outsource creating the modal
fullScreenLoader.startLoader(); var popupModal = $('#oneclick_actionModal').modal({
self.isPlaceOrderActionAllowed(false); // disable user to hide popup
clickableOverlay: false,
responsive: true,
innerScroll: false,
// empty buttons, we don't need that
buttons: [],
modalClass: 'oneclick_actionModal',
});
self.getPlaceOrderDeferredObject().fail( popupModal.modal('openModal');
function() {
fullScreenLoader.stopLoader(); adyenPaymentService.paymentDetails(request).
done(function(responseJSON) {
self.handleAdyenResult(responseJSON,
self.orderId);
}).
fail(function(response) {
errorProcessor.process(response,
self.messageContainer);
self.isPlaceOrderActionAllowed(true); self.isPlaceOrderActionAllowed(true);
}, fullScreenLoader.stopLoader();
).done( });
function(orderId) { },
self.afterPlaceOrder(); /**
adyenPaymentService.getOrderPaymentStatus(orderId). * Based on the response we can start a 3DS2 validation or place the order
done(function(responseJSON) { * @param responseJSON
self.validateThreeDS2OrPlaceOrder(responseJSON, */
orderId); handleAdyenResult: function(responseJSON, orderId) {
});
},
);
}
return false;
},
/**
* Renders the secure CVC field,
* creates the card component,
* sets up the callbacks for card components
*/
renderSecureCVC: function() {
var self = this; var self = this;
var response = JSON.parse(responseJSON);
if (!self.getOriginKey()) { if (!!response.isFinal) {
return; // Status is final redirect to the redirectUrl
window.location.replace(url.build(
window.checkoutConfig.payment[quote.paymentMethod().method].redirectUrl,
));
} else {
// Handle action
self.handleAction(response.action, orderId);
} }
var oneClickCardNode = document.getElementById( },
'cvcContainer-' + self.value); handleAction: function(action, orderId) {
try {
var hideCVC = false; this.checkoutComponent.createFromAction(
// hide cvc if contract has been stored as recurring action).
if (!this.hasVerification()) { mount('#oneclick_actionContainer');
hideCVC = true; } catch (e) {
console.log(e);
} }
},
var oneClickCard = checkout.create('card', { /**
hideCVC: hideCVC, * List all Adyen billing agreements
brand: self.agreement_data.variant, * Set up installments
storedPaymentMethodId: this.value, *
expiryMonth: self.agreement_data.card.expiryMonth, * @returns {Array}
expiryYear: self.agreement_data.card.expiryYear, */
holderName: self.agreement_data.card.holderName, getAdyenBillingAgreements: function() {
onChange: function(state, component) {
if (state.isValid) {
self.placeOrderAllowed(true);
isValid(true);
if (typeof state.data !== 'undefined' &&
typeof state.data.paymentMethod !== 'undefined' &&
typeof state.data.paymentMethod.encryptedSecurityCode !==
'undefined'
) {
self.encryptedCreditCardVerificationNumber = state.data.paymentMethod.encryptedSecurityCode;
}
} else {
self.encryptedCreditCardVerificationNumber = '';
if (self.agreement_data.variant != 'maestro') {
self.placeOrderAllowed(false);
isValid(false);
}
}
},
}).mount(oneClickCardNode);
window.adyencheckout = oneClickCard;
},
/**
* Based on the response we can start a 3DS2 validation or place the order
* @param responseJSON
*/
validateThreeDS2OrPlaceOrder: function(responseJSON, orderId) {
var self = this; var self = this;
var response = JSON.parse(responseJSON);
if (!!response.threeDS2) { // convert to list so you can iterate
// render component var paymentList = _.map(
self.renderThreeDS2Component(response.type, response.token, window.checkoutConfig.payment.adyenOneclick.billingAgreements,
orderId); function(billingAgreement) {
} else {
window.location.replace(url.build( var creditCardExpMonth, creditCardExpYear = false;
window.checkoutConfig.payment[quote.paymentMethod().method].redirectUrl));
} if (billingAgreement.agreement_data.card) {
}, creditCardExpMonth = billingAgreement.agreement_data.card.expiryMonth;
/** creditCardExpYear = billingAgreement.agreement_data.card.expiryYear;
* Rendering the 3DS2.0 components }
* To do the device fingerprint at the response of IdentifyShopper render the threeDS2DeviceFingerprint
* component // pre-define installments if they are set
* To render the challenge for the customer at the response of ChallengeShopper render the var i, installments = [];
* threeDS2Challenge component var grandTotal = quote.totals().grand_total;
* Both of them is going to be rendered in a Magento dialog popup var dividedString = '';
* var dividedAmount = 0;
* @param type
* @param token if (billingAgreement.number_of_installments) {
*/ for (i = 0; i <
renderThreeDS2Component: function(type, token, orderId) { billingAgreement.number_of_installments.length; i++) {
dividedAmount = (grandTotal /
billingAgreement.number_of_installments[i]).toFixed(
quote.getPriceFormat().precision);
dividedString = billingAgreement.number_of_installments[i] +
' x ' +
dividedAmount + ' ' +
quote.totals().quote_currency_code;
installments.push({
key: [dividedString],
value: billingAgreement.number_of_installments[i],
});
}
}
var messageContainer = self.messageComponents['messages-' +
billingAgreement.reference_id];
// for recurring enable the placeOrder button at all times
var placeOrderAllowed = true;
if (self.hasVerification()) {
placeOrderAllowed = false;
} else {
// for recurring cards there is no validation needed
isValid(true);
}
return {
'label': billingAgreement.agreement_label,
'value': billingAgreement.reference_id,
'agreement_data': billingAgreement.agreement_data,
'logo': billingAgreement.logo,
'installment': '',
'number_of_installments': billingAgreement.number_of_installments,
'method': self.item.method,
'creditCardExpMonth': ko.observable(
creditCardExpMonth),
'creditCardExpYear': ko.observable(
creditCardExpYear),
'getInstallments': ko.observableArray(installments),
'placeOrderAllowed': ko.observable(
placeOrderAllowed),
isButtonActive: function() {
return self.isActive() && this.getCode() ==
self.isChecked() &&
self.isBillingAgreementChecked() &&
this.placeOrderAllowed() &&
self.isPlaceOrderActionAllowed();
},
/**
* Custom place order function
*
* @override
*
* @param data
* @param event
* @returns {boolean}
*/
placeOrder: function(data, event) {
var innerSelf = this;
if (event) {
event.preventDefault();
}
// only use installments for cards
if (this.agreement_data.card) {
if (this.hasVerification()) {
var options = {enableValidations: false};
}
numberOfInstallments(this.installment);
}
if (this.validate() &&
additionalValidators.validate()) {
fullScreenLoader.startLoader();
this.isPlaceOrderActionAllowed(false);
this.getPlaceOrderDeferredObject().fail(
function() {
fullScreenLoader.stopLoader();
innerSelf.isPlaceOrderActionAllowed(
true);
},
).done(
function(orderId) {
innerSelf.afterPlaceOrder();
self.orderId = orderId;
adyenPaymentService.getOrderPaymentStatus(
orderId).
done(function(responseJSON) {
self.handleAdyenResult(
responseJSON,
orderId);
});
},
);
}
return false;
},
/**
* Renders the secure CVC field,
* creates the card component,
* sets up the callbacks for card components
*/
renderSecureCVC: function() {
if (!this.getOriginKey()) {
return;
}
var hideCVC = false;
// hide cvc if contract has been stored as recurring
if (!this.hasVerification()) {
hideCVC = true;
}
try {
this.component = self.checkoutComponent.create(
'card', {
hideCVC: hideCVC,
brand: this.agreement_data.variant,
storedPaymentMethodId: this.value,
expiryMonth: this.agreement_data.card.expiryMonth,
expiryYear: this.agreement_data.card.expiryYear,
holderName: this.agreement_data.card.holderName,
onChange: this.handleOnChange.bind(this)
}).mount('#cvcContainer-' + this.value);
} catch (err) {
console.log(err);
// The component does not exist yet
}
},
handleOnChange: function(state, component) {
this.placeOrderAllowed(
!!state.isValid);
isValid(!!state.isValid);
},
/**
* Builds the payment details part of the payment information reqeust
*
* @returns {{method: *, additional_data: {variant: *, recurring_detail_reference: *, number_of_installments: *, cvc: (string|*), expiryMonth: *, expiryYear: *}}}
*/
getData: function() {
var self = this;
let stateData;
if ('component' in self) {
stateData = self.component.data;
}
return {
'method': self.method,
additional_data: {
number_of_installments: numberOfInstallments(),
stateData: JSON.stringify(stateData),
},
};
},
validate: function() {
var code = self.item.method;
var value = this.value;
var codeValue = code + '_' + value;
var form = 'form[data-role=' + codeValue + ']';
var validate = $(form).validation() &&
$(form).validation('isValid');
// bcmc does not have any cvc
if (!validate ||
(isValid() == false && variant() !=
'bcmc' && variant() !=
'maestro')) {
return false;
}
return true;
},
getCode: function() {
return self.item.method;
},
hasVerification: function() {
return self.hasVerification();
},
getMessageName: function() {
return 'messages-' +
billingAgreement.reference_id;
},
getMessageContainer: function() {
return messageContainer;
},
getOriginKey: function() {
return adyenConfiguration.getOriginKey();
},
isPlaceOrderActionAllowed: function() {
return self.isPlaceOrderActionAllowed(); // needed for placeOrder method
},
afterPlaceOrder: function() {
return self.afterPlaceOrder(); // needed for placeOrder method
},
getPlaceOrderDeferredObject: function() {
return $.when(
placeOrderAction(this.getData(),
this.getMessageContainer()),
);
},
};
});
return paymentList;
},
/**
* Select a billing agreement (stored one click payment method) from the list
*
* @returns {boolean}
*/
selectBillingAgreement: function() {
var self = this; var self = this;
var threeDS2Node = document.getElementById( // set payment method data
'threeDS2ContainerOneClick'); var data = {
'method': self.method,
if (type == 'IdentifyShopper') { 'po_number': null,
self.threeDS2Component = checkout.create( 'additional_data': {
'threeDS2DeviceFingerprint', { recurring_detail_reference: self.value,
fingerprintToken: token, },
onComplete: function(result) { };
var request = result.data;
request.orderId = orderId;
adyenPaymentService.paymentDetails(request).
done(function(responseJSON) {
self.validateThreeDS2OrPlaceOrder(responseJSON,
orderId);
}).
fail(function(result) {
errorProcessor.process(result,
self.getMessageContainer());
self.isPlaceOrderActionAllowed(true);
fullScreenLoader.stopLoader();
});
},
onError: function(error) {
console.log(JSON.stringify(error));
},
});
} else if (type == 'ChallengeShopper') {
fullScreenLoader.stopLoader();
var popupModal = $('#threeDS2ModalOneClick').modal({ // set the brandCode
// disable user to hide popup recurringDetailReference(self.value);
clickableOverlay: false, variant(self.agreement_data.variant);
responsive: true,
innerScroll: false,
// empty buttons, we don't need that
buttons: [],
modalClass: 'threeDS2Modal',
});
popupModal.modal('openModal');
self.threeDS2Component = checkout.create('threeDS2Challenge',
{
challengeToken: token,
onComplete: function(result) {
popupModal.modal('closeModal');
fullScreenLoader.startLoader();
var request = result.data;
request.orderId = orderId;
adyenPaymentService.paymentDetails(request).
done(function(responseJSON) {
self.validateThreeDS2OrPlaceOrder(responseJSON,
orderId);
}).
fail(function(result) {
errorProcessor.process(result,
self.getMessageContainer());
self.isPlaceOrderActionAllowed(true);
fullScreenLoader.stopLoader();
});
},
onError: function(error) {
console.log(JSON.stringify(error));
},
});
}
self.threeDS2Component.mount(threeDS2Node); // set payment method
}, paymentMethod(self.method);
/**
* Builds the payment details part of the payment information reqeust
*
* @returns {{method: *, additional_data: {variant: *, recurring_detail_reference: *, number_of_installments: *, cvc: (string|*), expiryMonth: *, expiryYear: *}}}
*/
getData: function() {
var self = this;
// todo use state.data
var browserInfo = [];
return {
'method': self.method,
additional_data: {
variant: variant(),
recurring_detail_reference: recurringDetailReference(),
store_cc: true,
number_of_installments: numberOfInstallments(),
cvc: self.encryptedCreditCardVerificationNumber,
java_enabled: browserInfo.javaEnabled,
screen_color_depth: browserInfo.colorDepth,
screen_width: browserInfo.screenWidth,
screen_height: browserInfo.screenHeight,
timezone_offset: browserInfo.timeZoneOffset,
language: browserInfo.language,
},
};
},
validate: function() {
var code = self.item.method; selectPaymentMethodAction(data);
var value = this.value; checkoutData.setSelectedPaymentMethod(self.method);
var codeValue = code + '_' + value;
var form = 'form[data-role=' + codeValue + ']'; return true;
},
var validate = $(form).validation() && isBillingAgreementChecked: ko.computed(function() {
$(form).validation('isValid');
// bcmc does not have any cvc if (!quote.paymentMethod()) {
if (!validate || return null;
(isValid() == false && variant() != 'bcmc' && variant() !=
'maestro')) {
return false;
} }
if (quote.paymentMethod().method == paymentMethod()) {
return recurringDetailReference();
}
return null;
}),
getPlaceOrderUrl: function() {
return window.checkoutConfig.payment.iframe.placeOrderUrl[this.getCode()];
},
hasVerification: function() {
return window.checkoutConfig.payment.adyenOneclick.hasCustomerInteraction;
},
setPlaceOrderHandler: function(handler) {
this.placeOrderHandler = handler;
},
setValidateHandler: function(handler) {
this.validateHandler = handler;
},
getCode: function() {
return window.checkoutConfig.payment.adyenOneclick.methodCode;
},
isActive: function() {
return true; return true;
}, },
getCode: function() { getControllerName: function() {
return self.item.method; return window.checkoutConfig.payment.iframe.controllerName[this.getCode()];
}, },
hasVerification: function() { context: function() {
return self.hasVerification(); return this;
}, },
getMessageName: function() { isShowLegend: function() {
return 'messages-' + value.reference_id; return true;
}, },
getMessageContainer: function() { });
return messageContainer;
},
getOriginKey: function() {
return self.getOriginKey();
},
isPlaceOrderActionAllowed: function() {
return self.isPlaceOrderActionAllowed(); // needed for placeOrder method
},
afterPlaceOrder: function() {
return self.afterPlaceOrder(); // needed for placeOrder method
},
getPlaceOrderDeferredObject: function() {
return $.when(
placeOrderAction(this.getData(), this.getMessageContainer()),
);
},
};
});
return paymentList;
},
/**
* Select a billing agreement (stored one click payment method) from the list
*
* @returns {boolean}
*/
selectBillingAgreement: function() {
var self = this;
// set payment method data
var data = {
'method': self.method,
'po_number': null,
'additional_data': {
recurring_detail_reference: self.value,
},
};
// set the brandCode
recurringDetailReference(self.value);
variant(self.agreement_data.variant);
// set payment method
paymentMethod(self.method);
selectPaymentMethodAction(data);
checkoutData.setSelectedPaymentMethod(self.method);
return true;
},
isBillingAgreementChecked: ko.computed(function() {
if (!quote.paymentMethod()) {
return null;
}
if (quote.paymentMethod().method == paymentMethod()) {
return recurringDetailReference();
}
return null;
}),
placeOrderHandler: null,
validateHandler: null,
setPlaceOrderHandler: function(handler) {
this.placeOrderHandler = handler;
},
setValidateHandler: function(handler) {
this.validateHandler = handler;
},
getPlaceOrderUrl: function() {
return window.checkoutConfig.payment.iframe.placeOrderUrl[this.getCode()];
},
getCode: function() {
return window.checkoutConfig.payment.adyenOneclick.methodCode;
},
isActive: function() {
return true;
},
getControllerName: function() {
return window.checkoutConfig.payment.iframe.controllerName[this.getCode()];
},
context: function() {
return this;
},
canCreateBillingAgreement: function() {
return window.checkoutConfig.payment.adyenCc.canCreateBillingAgreement;
},
isShowLegend: function() {
return true;
},
hasVerification: function() {
return window.checkoutConfig.payment.adyenOneclick.hasCustomerInteraction;
},
getLocale: function() {
return window.checkoutConfig.payment.adyenOneclick.locale;
},
getOriginKey: function() {
return window.checkoutConfig.payment.adyen.originKey;
},
getCheckoutEnvironment: function() {
return window.checkoutConfig.payment.adyen.checkoutEnvironment;
}, },
}); );
}
)
;
...@@ -71,6 +71,12 @@ ...@@ -71,6 +71,12 @@
<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}">
<div id="oneclick_actionModalWrapper">
<div id="oneclick_actionModal">
<div id="oneclick_actionContainer"></div>
</div>
</div>
<!-- ko if: agreement_data.card --> <!-- ko if: agreement_data.card -->
<div class="field number"> <div class="field number">
<label class="label"> <label class="label">
...@@ -135,10 +141,6 @@ ...@@ -135,10 +141,6 @@
</div> </div>
<!-- /ko --> <!-- /ko -->
<div id="threeDS2ModalOneClick">
<div id="threeDS2ContainerOneClick"></div>
</div>
</fieldset> </fieldset>
<div class="checkout-agreements-block"> <div class="checkout-agreements-block">
......
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