We will work on Apr 26th (Saturday) and will be off from Apr 30th (Wednesday) until May 2nd (Friday) for public holiday 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