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
$paymentDataObject = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($buildSubject);
$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')) {
$shopperInteraction = "Ecommerce";
} else {
......@@ -76,9 +62,7 @@ class OneclickAuthorizationDataBuilder implements BuilderInterface
}
$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 ($payment->getCcType() == "sepadirectdebit") {
......
......@@ -1713,7 +1713,7 @@ class Data extends AbstractHelper
$billingAgreement->getAgreementId(),
$order->getId()
)) {
// save into sales_billing_agreement_order
// save into billing_agreement_order
$billingAgreement->addOrderRelation($order);
}
// add to order to save agreement
......@@ -1733,6 +1733,7 @@ class Data extends AbstractHelper
$comment = $order->addStatusHistoryComment($message);
$order->addRelatedObject($comment);
$order->save();
}
}
......
......@@ -27,6 +27,7 @@ use Adyen\Payment\Logger\AdyenLogger;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderPaymentInterface;
use Magento\Sales\Model\Order;
use Adyen\Payment\Helper\Vault;
class PaymentResponseHandler
{
......@@ -41,12 +42,36 @@ class PaymentResponseHandler
const ERROR = 'Error';
const CANCELLED = 'Cancelled';
/**
* @var 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(
AdyenLogger $adyenLogger
AdyenLogger $adyenLogger,
Data $adyenHelper,
Vault $vaultHelper
) {
$this->adyenLogger = $adyenLogger;
$this->adyenHelper = $adyenHelper;
$this->vaultHelper = $vaultHelper;
}
public function formatPaymentResponse($resultCode, $action = null, $additionalData = null)
......@@ -144,6 +169,24 @@ class PaymentResponseHandler
}
break;
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::CHALLENGE_SHOPPER:
break;
......
......@@ -27,7 +27,6 @@ use Adyen\AdyenException;
use Adyen\Payment\Api\AdyenPaymentDetailsInterface;
use Adyen\Payment\Helper\Data;
use Adyen\Payment\Helper\PaymentResponseHandler;
use Adyen\Payment\Helper\Vault;
use Adyen\Payment\Logger\AdyenLogger;
use Magento\Checkout\Model\Session;
use Magento\Framework\Exception\LocalizedException;
......@@ -50,11 +49,6 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface
*/
private $adyenLogger;
/**
* @var Vault
*/
private $vaultHelper;
/**
* @var OrderRepositoryInterface
*/
......@@ -71,7 +65,6 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface
* @param Session $checkoutSession
* @param Data $adyenHelper
* @param AdyenLogger $adyenLogger
* @param Vault $vaultHelper
* @param OrderRepositoryInterface $orderRepository
* @param PaymentResponseHandler $paymentResponseHandler
*/
......@@ -79,14 +72,12 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface
Session $checkoutSession,
Data $adyenHelper,
AdyenLogger $adyenLogger,
Vault $vaultHelper,
OrderRepositoryInterface $orderRepository,
PaymentResponseHandler $paymentResponseHandler
) {
$this->checkoutSession = $checkoutSession;
$this->adyenHelper = $adyenHelper;
$this->adyenLogger = $adyenLogger;
$this->vaultHelper = $vaultHelper;
$this->orderRepository = $orderRepository;
$this->paymentResponseHandler = $paymentResponseHandler;
}
......@@ -139,14 +130,7 @@ class AdyenPaymentDetails implements AdyenPaymentDetailsInterface
throw new LocalizedException(__('Payment details call failed'));
}
//TODO test this with payments that return additionalData
//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
// Handle response
if (!$this->paymentResponseHandler->handlePaymentResponse($paymentDetails, $payment, $order)) {
$this->checkoutSession->restoreQuote();
throw new LocalizedException(__('The payment is REFUSED.'));
......
......@@ -37,6 +37,7 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver
const GUEST_EMAIL = 'guestEmail';
const COMBO_CARD_TYPE = 'combo_card_type';
const STATE_DATA = 'stateData';
const STORE_PAYMENT_METHOD = 'storePaymentMethod';
/**
* Approved root level keys from additional data array
......@@ -113,5 +114,10 @@ class AdyenCcDataAssignObserver extends AbstractDataAssignObserver
if (!empty($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 @@
namespace Adyen\Payment\Observer;
use Adyen\Service\Validator\CheckoutStateDataValidator;
use Adyen\Service\Validator\DataArrayValidator;
use Magento\Framework\Event\Observer;
use Magento\Payment\Observer\AbstractDataAssignObserver;
use Magento\Quote\Api\Data\PaymentInterface;
class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
{
const RECURRING_DETAIL_REFERENCE = 'recurring_detail_reference';
const ENCRYPTED_SECURITY_CODE = 'cvc';
const CC_TYPE = 'cc_type';
const BRAND = 'brand';
const NUMBER_OF_INSTALLMENTS = 'number_of_installments';
const VARIANT = 'variant';
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 STATE_DATA = 'stateData';
/**
* Approved root level keys from additional data array
*
* @var array
*/
protected $additionalInformationList = [
self::RECURRING_DETAIL_REFERENCE,
self::ENCRYPTED_SECURITY_CODE,
private static $approvedAdditionalDataKeys = [
self::STATE_DATA,
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
*/
......@@ -72,9 +67,11 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
* @param \Adyen\Payment\Helper\Data $adyenHelper
*/
public function __construct(
CheckoutStateDataValidator $checkoutStateDataValidator,
\Adyen\Payment\Helper\Data $adyenHelper,
\Magento\Framework\Model\Context $context
) {
$this->checkoutStateDataValidator = $checkoutStateDataValidator;
$this->adyenHelper = $adyenHelper;
$this->appState = $context->getAppState();
}
......@@ -85,27 +82,47 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
*/
public function execute(Observer $observer)
{
// Get request fields
$data = $this->readDataArgument($observer);
// Get additional data array
$additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA);
if (!is_array($additionalData)) {
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);
foreach ($additionalData as $key => $data) {
$paymentInfo->setAdditionalInformation($key, $data);
}
// set ccType
$variant = $additionalData['variant'];
$ccType = $this->adyenHelper->getMagentoCreditCartType($variant);
$paymentInfo->setCcType($ccType);
foreach ($this->additionalInformationList as $additionalInformationKey) {
if (isset($additionalData[$additionalInformationKey])) {
$paymentInfo->setAdditionalInformation(
$additionalInformationKey,
$additionalData[$additionalInformationKey]
);
}
if (!empty($stateData[self::BRAND])) {
$ccType = $this->adyenHelper->getMagentoCreditCartType($stateData[self::BRAND]);
$paymentInfo->setCcType($ccType);
}
// set customerInteraction
......@@ -115,11 +132,6 @@ class AdyenOneclickDataAssignObserver extends AbstractDataAssignObserver
} else {
$paymentInfo->setAdditionalInformation('customer_interaction', false);
}
// set ccType
$variant = $additionalData['variant'];
$ccType = $this->adyenHelper->getMagentoCreditCartType($variant);
$paymentInfo->setAdditionalInformation('cc_type', $ccType);
}
/**
......
......@@ -21,548 +21,489 @@
*/
define(
[
'ko',
'underscore',
'jquery',
'Magento_Payment/js/view/payment/cc-form',
'Magento_Checkout/js/action/select-payment-method',
'Magento_Checkout/js/model/payment/additional-validators',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/checkout-data',
'Magento_Checkout/js/action/redirect-on-success',
'uiLayout',
'Magento_Ui/js/model/messages',
'mage/url',
'Magento_Checkout/js/model/full-screen-loader',
'Magento_Paypal/js/action/set-payment-method',
'Magento_Checkout/js/model/url-builder',
'mage/storage',
'Magento_Checkout/js/action/place-order',
'Magento_Checkout/js/model/error-processor',
'Adyen_Payment/js/model/adyen-payment-service',
'Adyen_Payment/js/bundle',
],
function(
ko,
_,
$,
Component,
selectPaymentMethodAction,
additionalValidators,
quote,
checkoutData,
redirectOnSuccessAction,
layout,
Messages,
url,
fullScreenLoader,
setPaymentMethodAction,
urlBuilder,
storage,
placeOrderAction,
errorProcessor,
adyenPaymentService,
AdyenComponent
) {
'use strict';
var messageComponents;
var recurringDetailReference = ko.observable(null);
var variant = ko.observable(null);
var paymentMethod = ko.observable(null);
var numberOfInstallments = ko.observable(null);
var isValid = ko.observable(false);
return Component.extend({
isPlaceOrderActionAllowed: ko.observable(quote.billingAddress() != null),
defaults: {
template: 'Adyen_Payment/payment/oneclick-form',
recurringDetailReference: '',
variant: '',
numberOfInstallments: '',
},
initObservable: function() {
this._super().observe([
'recurringDetailReference',
'creditCardType',
'encryptedCreditCardVerificationNumber',
'variant',
'numberOfInstallments',
]);
return this;
},
initialize: function() {
var self = this;
this._super();
// create component needs to be in initialize method
var messageComponents = {};
_.map(window.checkoutConfig.payment.adyenOneclick.billingAgreements,
function(value) {
var messageContainer = new Messages();
var name = 'messages-' + value.reference_id;
var messagesComponent = {
parent: self.name,
name: 'messages-' + value.reference_id,
// name: self.name + '.messages',
displayArea: 'messages-' + value.reference_id,
component: 'Magento_Ui/js/view/messages',
config: {
messageContainer: messageContainer,
},
};
layout([messagesComponent]);
messageComponents[name] = messageContainer;
});
this.messageComponents = messageComponents;
},
/**
* List all Adyen billing agreements
* Set up installments
*
* @returns {Array}
*/
getAdyenBillingAgreements: function() {
var self = this;
// shareable adyen checkout component
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) {
'ko',
'underscore',
'jquery',
'Magento_Payment/js/view/payment/cc-form',
'Magento_Checkout/js/action/select-payment-method',
'Magento_Checkout/js/model/payment/additional-validators',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/checkout-data',
'uiLayout',
'Magento_Ui/js/model/messages',
'mage/url',
'Magento_Checkout/js/model/full-screen-loader',
'Magento_Checkout/js/action/place-order',
'Magento_Checkout/js/model/error-processor',
'Adyen_Payment/js/model/adyen-payment-service',
'Adyen_Payment/js/bundle',
'Adyen_Payment/js/model/adyen-configuration',
],
function(
ko,
_,
$,
Component,
selectPaymentMethodAction,
additionalValidators,
quote,
checkoutData,
layout,
Messages,
url,
fullScreenLoader,
placeOrderAction,
errorProcessor,
adyenPaymentService,
AdyenComponent,
adyenConfiguration,
) {
'use strict';
var messageComponents;
var recurringDetailReference = ko.observable(null);
var variant = ko.observable(null);
var paymentMethod = ko.observable(null);
var numberOfInstallments = ko.observable(null);
var isValid = ko.observable(false);
return Component.extend({
isPlaceOrderActionAllowed: ko.observable(
quote.billingAddress() != null),
defaults: {
template: 'Adyen_Payment/payment/oneclick-form',
recurringDetailReference: '',
variant: '',
numberOfInstallments: '',
},
initObservable: function() {
this._super().observe([
'recurringDetailReference',
'variant',
'numberOfInstallments',
]);
return this;
},
initialize: function() {
let self = this;
this._super();
// create component needs to be in initialize method
let messageComponents = {};
_.map(
window.checkoutConfig.payment.adyenOneclick.billingAgreements,
function(billingAgreement) {
let messageContainer = new Messages();
let name = 'messages-' + billingAgreement.reference_id;
let messagesComponent = {
parent: self.name,
name: 'messages-' + billingAgreement.reference_id,
// name: self.name + '.messages',
displayArea: 'messages-' +
billingAgreement.reference_id,
component: 'Magento_Ui/js/view/messages',
config: {
messageContainer: messageContainer,
},
};
layout([messagesComponent]);
messageComponents[name] = messageContainer;
});
this.messageComponents = messageComponents;
let paymentMethodsObserver = adyenPaymentService.getPaymentMethods();
let paymentMethodsResponse = paymentMethodsObserver();
if (!!paymentMethodsResponse) {
this.checkoutComponent = new AdyenCheckout({
locale: adyenConfiguration.getLocale(),
originKey: adyenConfiguration.getOriginKey(),
environment: adyenConfiguration.getCheckoutEnvironment(),
paymentMethodsResponse: paymentMethodsResponse.paymentMethodsResponse,
onAdditionalDetails: this.handleOnAdditionalDetails.bind(this),
},
);
}
},
handleOnAdditionalDetails: function(result) {
var self = this;
var request = result.data;
request.orderId = self.orderId;
if (event) {
event.preventDefault();
}
// only use installments for cards
if (self.agreement_data.card) {
if (self.hasVerification()) {
var options = {enableValidations: false};
}
numberOfInstallments(self.installment);
}
fullScreenLoader.stopLoader();
if (this.validate() && additionalValidators.validate()) {
fullScreenLoader.startLoader();
self.isPlaceOrderActionAllowed(false);
// TODO outsource creating the modal
var popupModal = $('#oneclick_actionModal').modal({
// 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(
function() {
fullScreenLoader.stopLoader();
popupModal.modal('openModal');
adyenPaymentService.paymentDetails(request).
done(function(responseJSON) {
self.handleAdyenResult(responseJSON,
self.orderId);
}).
fail(function(response) {
errorProcessor.process(response,
self.messageContainer);
self.isPlaceOrderActionAllowed(true);
},
).done(
function(orderId) {
self.afterPlaceOrder();
adyenPaymentService.getOrderPaymentStatus(orderId).
done(function(responseJSON) {
self.validateThreeDS2OrPlaceOrder(responseJSON,
orderId);
});
},
);
}
return false;
},
/**
* Renders the secure CVC field,
* creates the card component,
* sets up the callbacks for card components
*/
renderSecureCVC: function() {
fullScreenLoader.stopLoader();
});
},
/**
* Based on the response we can start a 3DS2 validation or place the order
* @param responseJSON
*/
handleAdyenResult: function(responseJSON, orderId) {
var self = this;
var response = JSON.parse(responseJSON);
if (!self.getOriginKey()) {
return;
if (!!response.isFinal) {
// 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);
var hideCVC = false;
// hide cvc if contract has been stored as recurring
if (!this.hasVerification()) {
hideCVC = true;
},
handleAction: function(action, orderId) {
try {
this.checkoutComponent.createFromAction(
action).
mount('#oneclick_actionContainer');
} catch (e) {
console.log(e);
}
var oneClickCard = checkout.create('card', {
hideCVC: hideCVC,
brand: self.agreement_data.variant,
storedPaymentMethodId: this.value,
expiryMonth: self.agreement_data.card.expiryMonth,
expiryYear: self.agreement_data.card.expiryYear,
holderName: self.agreement_data.card.holderName,
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) {
},
/**
* List all Adyen billing agreements
* Set up installments
*
* @returns {Array}
*/
getAdyenBillingAgreements: function() {
var self = this;
var response = JSON.parse(responseJSON);
if (!!response.threeDS2) {
// render component
self.renderThreeDS2Component(response.type, response.token,
orderId);
} else {
window.location.replace(url.build(
window.checkoutConfig.payment[quote.paymentMethod().method].redirectUrl));
}
},
/**
* Rendering the 3DS2.0 components
* To do the device fingerprint at the response of IdentifyShopper render the threeDS2DeviceFingerprint
* component
* To render the challenge for the customer at the response of ChallengeShopper render the
* threeDS2Challenge component
* Both of them is going to be rendered in a Magento dialog popup
*
* @param type
* @param token
*/
renderThreeDS2Component: function(type, token, orderId) {
// convert to list so you can iterate
var paymentList = _.map(
window.checkoutConfig.payment.adyenOneclick.billingAgreements,
function(billingAgreement) {
var creditCardExpMonth, creditCardExpYear = false;
if (billingAgreement.agreement_data.card) {
creditCardExpMonth = billingAgreement.agreement_data.card.expiryMonth;
creditCardExpYear = billingAgreement.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 (billingAgreement.number_of_installments) {
for (i = 0; i <
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 threeDS2Node = document.getElementById(
'threeDS2ContainerOneClick');
if (type == 'IdentifyShopper') {
self.threeDS2Component = checkout.create(
'threeDS2DeviceFingerprint', {
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();
// set payment method data
var data = {
'method': self.method,
'po_number': null,
'additional_data': {
recurring_detail_reference: self.value,
},
};
var popupModal = $('#threeDS2ModalOneClick').modal({
// disable user to hide popup
clickableOverlay: false,
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));
},
});
}
// set the brandCode
recurringDetailReference(self.value);
variant(self.agreement_data.variant);
self.threeDS2Component.mount(threeDS2Node);
},
/**
* 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() {
// set payment method
paymentMethod(self.method);
var code = self.item.method;
var value = this.value;
var codeValue = code + '_' + value;
selectPaymentMethodAction(data);
checkoutData.setSelectedPaymentMethod(self.method);
var form = 'form[data-role=' + codeValue + ']';
return true;
},
var validate = $(form).validation() &&
$(form).validation('isValid');
isBillingAgreementChecked: ko.computed(function() {
// bcmc does not have any cvc
if (!validate ||
(isValid() == false && variant() != 'bcmc' && variant() !=
'maestro')) {
return false;
if (!quote.paymentMethod()) {
return null;
}
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;
},
getCode: function() {
return self.item.method;
},
hasVerification: function() {
return self.hasVerification();
},
getMessageName: function() {
return 'messages-' + value.reference_id;
},
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;
},
getControllerName: function() {
return window.checkoutConfig.payment.iframe.controllerName[this.getCode()];
},
context: function() {
return this;
},
isShowLegend: function() {
return true;
},
});
},
});
}
)
;
);
......@@ -71,6 +71,12 @@
<fieldset
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 -->
<div class="field number">
<label class="label">
......@@ -135,10 +141,6 @@
</div>
<!-- /ko -->
<div id="threeDS2ModalOneClick">
<div id="threeDS2ContainerOneClick"></div>
</div>
</fieldset>
<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