Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PW-8077 Resolve conflicts and merge the Apple Pay Toklenisation branch #832

Merged
merged 5 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jest/sfccCartridgeMocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ jest.mock('*/cartridge/scripts/util/adyenHelper', () => ({
getValueOrNull: jest.fn(() => 1000),
})),
isAdyenGivingAvailable: jest.fn(() => true),
isApplePay: jest.fn(() => true),
getAdyenGivingConfig: jest.fn(() => true),
isOpenInvoiceMethod: jest.fn(() => false),
getDonationAmounts: jest.fn(() => [10, 20, 30]),
Expand Down Expand Up @@ -246,6 +247,7 @@ jest.mock('*/cartridge/scripts/util/adyenConfigs', () => {
return {
getAdyenEnvironment: jest.fn(() => 'TEST'),
getCreditCardInstallments: jest.fn(() => true),
getAdyenApplePayTokenisationEnabled: jest.fn(() => true),
getAdyenClientKey: jest.fn(() => 'mocked_client_key'),
getGoogleMerchantID: jest.fn(() => 'mocked_google_merchant_id'),
getAdyenCardholderNameEnabled: jest.fn(() => true),
Expand Down
9 changes: 9 additions & 0 deletions metadata/site_import/meta/system-objecttype-extensions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,14 @@
<externally-managed-flag>false</externally-managed-flag>
<default-value>false</default-value>
</attribute-definition>
<attribute-definition attribute-id="AdyenApplePayTokenisationEnabled">
<display-name xml:lang="x-default">Tokenize Apple Pay payments</display-name>
<description xml:lang="x-default">Tokenize payment details for Apple Pay</description>
<type>boolean</type>
<mandatory-flag>false</mandatory-flag>
<externally-managed-flag>false</externally-managed-flag>
<default-value>false</default-value>
</attribute-definition>
<attribute-definition attribute-id="AdyenCreditCardInstallments">
<display-name xml:lang="x-default">Credit Card Installments</display-name>
<description xml:lang="x-default">If installments are required please create the configuration value using this link: https://adyen.github.io/adyen-salesforce-commerce-cloud</description>
Expand Down Expand Up @@ -631,6 +639,7 @@
<attribute attribute-id="Adyen_IntegratorName"/>
<attribute attribute-id="Adyen_StoreId"/>
<attribute attribute-id="AdyenOneClickEnabled"/>
<attribute attribute-id="AdyenApplePayTokenisationEnabled"/>
<attribute attribute-id="AdyenCreditCardInstallments"/>
<attribute attribute-id="AdyenBasketFieldsEnabled"/>
<attribute attribute-id="AdyenLevel23DataEnabled"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,16 @@ function getAmazonpayConfig() {
};
}

function getApplePayConfig() {
return {
showPayButton: true,
onSubmit: (state, component) => {
helpers.assignPaymentMethodValue();
helpers.paymentFromComponent(state.data, component);
},
};
}

function setCheckoutConfiguration() {
store.checkoutConfiguration.onChange = handleOnChange;
store.checkoutConfiguration.onAdditionalDetails = handleOnAdditionalDetails;
Expand All @@ -287,6 +297,7 @@ function setCheckoutConfiguration() {
paypal: getPaypalConfig(),
amazonpay: getAmazonpayConfig(),
giftcard: getGiftCardConfig(),
applepay: getApplePayConfig(),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ function paymentFromComponent(data, component = {}) {
},
success(response) {
setOrderFormData(response);

if (response.fullResponse?.action) {
component.handleAction(response.fullResponse.action);
}
if (response.paymentError || response.error) {
} else if (response.isApplePay) {
document.querySelector('#result').value = JSON.stringify(response);
document.querySelector('#showConfirmationForm').submit();
} else if (response.paymentError || response.error) {
component.handleError();
}
},
Expand Down Expand Up @@ -97,7 +98,9 @@ function displaySelectedMethod(type) {
resetPaymentMethod();

document.querySelector('button[value="submit-payment"]').disabled =
['paypal', 'paywithgoogle', 'googlepay', 'amazonpay'].indexOf(type) > -1;
['paypal', 'paywithgoogle', 'googlepay', 'amazonpay', 'applepay'].indexOf(
type,
) > -1;

document
.querySelector(`#component_${type}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ function paymentFromComponent(data, component) {
},
success(response) {
helpers.setOrderFormData(response);

handleAmazonResponse(response, component);
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exports[`Payment from Component should return json response 1`] = `
[
[
{
"isApplePay": true,
"orderNo": "mocked_orderNo",
"orderToken": "mocked_orderToken",
"resultCode": "Authorised",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ const AdyenConfigs = require('*/cartridge/scripts/util/adyenConfigs');
const adyenCheckout = require('*/cartridge/scripts/adyenCheckout');
const AdyenHelper = require('*/cartridge/scripts/util/adyenHelper');
const AdyenLogs = require('*/cartridge/scripts/adyenCustomLogs');
const constants = require('*/cartridge/adyenConstants/constants');

function responseContainsErrors(response) {
return (
response.error || response.resultCode !== constants.RESULTCODES.AUTHORISED
);
}

function makePartialPayment(req, res, next) {
try {
Expand All @@ -32,6 +39,14 @@ function makePartialPayment(req, res, next) {
partialPaymentRequest,
); // no order created yet and no PI needed (for giftcards it will be created on Order level)

if (responseContainsErrors) {
let errorMsg = 'partial payment request did not go through';
errorMsg += response.resultCode
? `.. resultCode: ${response.resultCode}`
: '';
throw new Error(errorMsg);
}

Transaction.wrap(() => {
session.privacy.giftCardResponse = JSON.stringify({
giftCardpspReference: response.pspReference,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,13 @@ function handleRefusedResultCode(result, reqDataObj, order) {
}

function isExpressPayment(reqDataObj) {
return reqDataObj.paymentType === 'express'; // applepay
return reqDataObj.paymentType === 'express';
}

function handleExpressPayment(reqDataObj, currentBasket) {
if (isExpressPayment(reqDataObj)) {
setBillingAndShippingAddress(reqDataObj, currentBasket);
}
}

/**
Expand Down Expand Up @@ -201,9 +207,7 @@ function paymentFromComponent(req, res, next) {
] = req.form.paymentMethod.toLowerCase();
});

if (isExpressPayment(reqDataObj)) {
setBillingAndShippingAddress(reqDataObj, currentBasket);
}
handleExpressPayment(reqDataObj, currentBasket);

const order = COHelpers.createOrder(currentBasket);

Expand All @@ -225,6 +229,9 @@ function paymentFromComponent(req, res, next) {
if (session.privacy.giftCardResponse) {
handleGiftCardPayment(currentBasket, order);
}
if (AdyenHelper.isApplePay(reqDataObj.paymentMethod?.type)) {
result.isApplePay = true;
}

result.orderNo = order.orderNo;
result.orderToken = order.orderToken;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const constants = require('*/cartridge/adyenConstants/constants');
const { clearForms } = require('*/cartridge/controllers/utils/index');
const AdyenLogs = require('*/cartridge/scripts/adyenCustomLogs');

function handlePaymentError(order, { res, next }) {
function handlePaymentError(order, adyenPaymentInstrument, { res, next }) {
clearForms.clearAdyenData(adyenPaymentInstrument);
Transaction.wrap(() => {
OrderMgr.failOrder(order, true);
});
Expand Down Expand Up @@ -54,7 +55,7 @@ function handleAuthorisedPayment(
// Places the order
const placeOrderResult = COHelpers.placeOrder(order, fraudDetectionStatus);
if (placeOrderResult.error) {
return handlePaymentError(order, { res, next });
return handlePaymentError(order, adyenPaymentInstrument, { res, next });
}

const currentLocale = Locale.getLocale(req.locale.id);
Expand All @@ -68,6 +69,7 @@ function handleAuthorisedPayment(
AdyenHelper.savePaymentDetails(adyenPaymentInstrument, order, result);
});

clearForms.clearAdyenData(adyenPaymentInstrument);
clearForms.clearForms();
// determines SFRA version for backwards compatibility
if (AdyenConfigs.getAdyenSFRA6Compatibility() === true) {
Expand All @@ -89,15 +91,42 @@ function handleAuthorisedPayment(
return next();
}

function handlePaymentResult(result, order, adyenPaymentInstrument, options) {
// Authorised: The payment authorisation was successfully completed.
if (
[
constants.RESULTCODES.AUTHORISED,
constants.RESULTCODES.PENDING,
constants.RESULTCODES.RECEIVED,
].indexOf(result.resultCode) > -1
) {
return handleAuthorisedPayment(
order,
result,
adyenPaymentInstrument,
options,
);
}
return handlePaymentError(order, adyenPaymentInstrument, options);
}

// eslint-disable-next-line complexity
function handlePayment(stateData, order, options) {
const paymentInstruments = order.getPaymentInstruments(
AdyenHelper.getOrderMainPaymentInstrumentType(order),
);
const result = options.req.form?.result;

const adyenPaymentInstrument = paymentInstruments[0];
const hasStateData = stateData?.paymentData && stateData?.details;

if (result?.error || order.status.value === Order.ORDER_STATUS_FAILED) {
AdyenLogs.error_log(
`Could not call payment/details for order ${order.orderNo}`,
);
return handlePaymentError(order, adyenPaymentInstrument, options);
}

let finalResult;
if (!hasStateData) {
if (
Expand All @@ -107,17 +136,10 @@ function handlePayment(stateData, order, options) {
) {
finalResult = JSON.parse(result);
} else {
return handlePaymentError(order, options);
return handlePaymentError(order, adyenPaymentInstrument, options);
}
}

if (order.status.value === Order.ORDER_STATUS_FAILED) {
AdyenLogs.error_log(
`Could not call payment/details for failed order ${order.orderNo}`,
);
return handlePaymentError(order, options);
}

const detailsCall = hasStateData
? handlePaymentsDetailsCall(stateData, adyenPaymentInstrument)
: null;
Expand All @@ -127,22 +149,12 @@ function handlePayment(stateData, order, options) {
});
finalResult = finalResult || detailsCall?.result;

// Authorised: The payment authorisation was successfully completed.
if (
[
constants.RESULTCODES.AUTHORISED,
constants.RESULTCODES.PENDING,
constants.RESULTCODES.RECEIVED,
].indexOf(finalResult.resultCode) > -1
) {
return handleAuthorisedPayment(
order,
finalResult,
adyenPaymentInstrument,
options,
);
}
return handlePaymentError(order, options);
return handlePaymentResult(
finalResult,
order,
adyenPaymentInstrument,
options,
);
}

module.exports = handlePayment;
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ module.exports = {
},
CHECKOUT_COMPONENT_IMAGE_URL_PATH: 'images/logos/medium/',

PAYMENTMETHODS: {
APPLEPAY: 'applepay',
AMAZONPAY: 'amazonpay',
},

SERVICE: {
PAYMENT: 'AdyenPayment',
PAYMENTDETAILS: 'AdyenPaymentDetails',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ function createPaymentRequest(args) {
}
}

//Set Apple Pay tokenisation
if (AdyenConfigs.getAdyenApplePayTokenisationEnabled() && AdyenHelper.isApplePay(paymentRequest.paymentMethod.type)) {
paymentRequest.storePaymentMethod = true;
}
setPaymentTransactionType(paymentInstrument, paymentRequest.paymentMethod);

// make API call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ const adyenConfigsObj = {
return getCustomPreference('Adyen_Frontend_Region').value;
},

getAdyenApplePayTokenisationEnabled: function () {
return getCustomPreference('AdyenApplePayTokenisationEnabled');
},

getAdyenSalePaymentMethods: function () {
return getCustomPreference('AdyenSalePaymentMethods') ? getCustomPreference('AdyenSalePaymentMethods').toString().split(',') : '';
},

getAdyenBasketFieldsEnabled() {
return getCustomPreference('AdyenBasketFieldsEnabled');
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,34 +604,42 @@ var adyenHelperObj = {
if (!empty(cardType)) {
switch (cardType) {
case 'visa':
case 'visa_applepay':
cardType = 'Visa';
break;
case 'mc':
case 'mc_applepay':
cardType = 'Mastercard';
break;
case 'amex':
case 'amex_applepay':
cardType = 'Amex';
break;
case 'discover':
case 'discover_applepay':
cardType = 'Discover';
break;
case 'maestro':
case 'maestrouk':
case 'maestro_applepay':
cardType = 'Maestro';
break;
case 'diners':
case 'diners_applepay':
cardType = 'Diners';
break;
case 'bcmc':
cardType = 'Bancontact';
break;
case 'jcb':
case 'jcb_applepay':
cardType = 'JCB';
break;
case 'cup':
cardType = 'CUP';
break;
case 'cartebancaire':
case 'cartebancaire_applepay':
cardType = 'Carte Bancaire';
break;
default:
Expand Down Expand Up @@ -752,6 +760,10 @@ var adyenHelperObj = {
return applicationInfo;
},

isApplePay(paymentMethod) {
return paymentMethod === constants.PAYMENTMETHODS.APPLEPAY;
},

// validates all fields in a state data object. Filters out all invalid fields
validateStateData(stateData) {
const validFields = [
Expand Down