Skip to content

Commit

Permalink
Create temporary basket for express pdp (#1183)
Browse files Browse the repository at this point in the history
* feat(SFI-876): applepay express pdp

* chore(SFI-876): unit tests

* chore(SFI-876): unit tests

* feat(SFI-876): create temporary basket for express pdp

* fix(SFI-876): handle temporary basket creation failure

* fix: adding csrf validation to applePayExpressCommon.js and linting

* chore: linting

---------

Co-authored-by: Zenit Shkreli <[email protected]>
  • Loading branch information
shanikantsingh and zenit2001 committed Oct 15, 2024
1 parent c49aafa commit 38adebf
Show file tree
Hide file tree
Showing 19 changed files with 316 additions and 104 deletions.
8 changes: 7 additions & 1 deletion jest/sfccCartridgeMocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ jest.mock(
{ virtual: true },
);

jest.mock(
'*/cartridge/adyen/scripts/expressPayments/createTemporaryBasket',
() => jest.fn(),
{ virtual: true },
);

jest.mock(
'*/cartridge/adyen/scripts/expressPayments/selectShippingMethods',
() => jest.fn(),
Expand Down Expand Up @@ -467,4 +473,4 @@ jest.mock(
getInstallmentValues: jest.fn(),
}),
{ virtual: true },
);
);
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,9 @@ function getGiftCardConfig() {
async: false,
success: (data) => {
giftcardBalance = data.balance;
document.querySelector('button[value="submit-payment"]').disabled =
false;
document.querySelector(
'button[value="submit-payment"]',
).disabled = false;
if (data.resultCode === constants.SUCCESS) {
const {
giftCardsInfoMessageContainer,
Expand All @@ -219,8 +220,9 @@ function getGiftCardConfig() {
initialPartialObject.totalDiscountedAmount;
});

document.querySelector('button[value="submit-payment"]').disabled =
true;
document.querySelector(
'button[value="submit-payment"]',
).disabled = true;
giftCardsInfoMessageContainer.innerHTML = '';
giftCardsInfoMessageContainer.classList.remove(
'gift-cards-info-message-container',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ function removeGiftCards() {
giftCardsInfoMessageContainer.classList.remove(
'gift-cards-info-message-container',
);
document.querySelector('button[value="submit-payment"]').disabled =
false;
document.querySelector(
'button[value="submit-payment"]',
).disabled = false;

if (res.resultCode === constants.RECEIVED) {
document
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ const {
checkIfExpressMethodsAreReady,
updateLoadedExpressMethods,
getPaymentMethods,
createTemporaryBasket,
} = require('./commons');
const { APPLE_PAY } = require('./constants');

let checkout;
let shippingMethodsData;
let paymentMethodsResponse;
let temporaryBasketId;

function formatCustomerObject(customerData, billingData) {
return {
Expand Down Expand Up @@ -95,6 +97,7 @@ function callPaymentFromComponent(data, resolveApplePay, rejectApplePay) {
data: {
data: JSON.stringify(data),
paymentMethod: APPLE_PAY,
csrf_token: $('#adyen-token').val(),
},
success(response) {
helpers.createShowConfirmationForm(window.showConfirmationAction);
Expand All @@ -108,11 +111,12 @@ function callPaymentFromComponent(data, resolveApplePay, rejectApplePay) {
});
}

function selectShippingMethod({ shipmentUUID, ID }) {
function selectShippingMethod({ shipmentUUID, ID }, basketId) {
const request = {
paymentMethodType: APPLE_PAY,
shipmentUUID,
methodID: ID,
basketId,
};
return fetch(window.selectShippingMethodUrl, {
method: 'POST',
Expand All @@ -123,9 +127,10 @@ function selectShippingMethod({ shipmentUUID, ID }) {
});
}

function getShippingMethod(shippingContact, isExpressPdp) {
function getShippingMethod(shippingContact, basketId) {
const request = {
paymentMethodType: APPLE_PAY,
basketId,
};
if (shippingContact) {
request.address = {
Expand All @@ -136,11 +141,7 @@ function getShippingMethod(shippingContact, isExpressPdp) {
postalCode: shippingContact.postalCode,
};
}
let url = window.shippingMethodsUrl;
if (isExpressPdp) {
url += '?expressPdp=true';
}
return fetch(url, {
return fetch(window.shippingMethodsUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
Expand All @@ -152,8 +153,6 @@ function getShippingMethod(shippingContact, isExpressPdp) {
async function initializeCheckout() {
const paymentMethods = await getPaymentMethods();
paymentMethodsResponse = await paymentMethods.json();
const shippingMethods = await getShippingMethod(null, window.isExpressPdp);
shippingMethodsData = await shippingMethods.json();
const applicationInfo = paymentMethodsResponse?.applicationInfo;
checkout = await AdyenCheckout({
environment: window.environment,
Expand All @@ -176,7 +175,6 @@ async function init() {
paymentMethodsResponse?.AdyenPaymentMethods?.paymentMethods.find(
(pm) => pm.type === APPLE_PAY,
);

if (!applePayPaymentMethod) {
updateLoadedExpressMethods(APPLE_PAY);
checkIfExpressMethodsAreReady();
Expand Down Expand Up @@ -226,7 +224,7 @@ async function init() {
};

await callPaymentFromComponent(
{ ...stateData, customer },
{ ...stateData, customer, basketId: temporaryBasketId },
resolveApplePay,
reject,
);
Expand All @@ -238,6 +236,31 @@ async function init() {
// This handler is empty to prevent sending a second payment request
// We already do the payment in paymentFromComponent
},
onClick: async (resolve, reject) => {
if (window.isExpressPdp) {
const tempBasket = await createTemporaryBasket();
if (tempBasket.ok) {
const tempBasketResponse = await tempBasket.json();
temporaryBasketId = tempBasketResponse.basketId;
applePayButtonConfig.amount = {
value: tempBasketResponse.amount.value,
currency: tempBasketResponse.amount.currency,
};
const applePayAmountUpdate = {
newTotal: {
type: 'final',
label: applePayConfig.merchantName,
amount: tempBasketResponse.amount.value,
},
};
resolve(applePayAmountUpdate);
} else {
reject();
}
} else {
resolve();
}
},
onShippingMethodSelected: async (resolve, reject, event) => {
const { shippingMethod } = event;
const matchingShippingMethod =
Expand All @@ -246,6 +269,7 @@ async function init() {
);
const calculationResponse = await selectShippingMethod(
matchingShippingMethod,
temporaryBasketId,
);
if (calculationResponse.ok) {
const newCalculation = await calculationResponse.json();
Expand All @@ -267,14 +291,18 @@ async function init() {
},
onShippingContactSelected: async (resolve, reject, event) => {
const { shippingContact } = event;
const shippingMethods = await getShippingMethod(shippingContact);
const shippingMethods = await getShippingMethod(
shippingContact,
temporaryBasketId,
);
if (shippingMethods.ok) {
shippingMethodsData = await shippingMethods.json();
if (shippingMethodsData.shippingMethods?.length) {
const selectedShippingMethod =
shippingMethodsData.shippingMethods[0];
const calculationResponse = await selectShippingMethod(
selectedShippingMethod,
temporaryBasketId,
);
if (calculationResponse.ok) {
const shippingMethodsStructured =
Expand Down Expand Up @@ -309,7 +337,6 @@ async function init() {
const cartContainer = document.getElementsByClassName(APPLE_PAY);
const applePayButton = await createApplePayButton(applePayButtonConfig);
const isApplePayButtonAvailable = await applePayButton.isAvailable();

if (isApplePayButtonAvailable) {
for (
let expressCheckoutNodesIndex = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,24 @@ module.exports.fetchGiftCards = async function fetchGiftCards() {
* Makes an ajax call to the controller function GetPaymentMethods
*/
module.exports.getPaymentMethods = async function getPaymentMethods() {
return fetch(window.getPaymentMethodsURL);
return fetch(window.getPaymentMethodsURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
});
};

/**
* Makes an ajax call to the controller function createTemporaryBasket
*/
module.exports.createTemporaryBasket = async function createTemporaryBasket() {
const productForm = document.getElementById('express-product-form');

return fetch(window.createTemporaryBasketUrl, {
method: 'POST',
body: new FormData(productForm),
});
};

module.exports.checkIfExpressMethodsAreReady =
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const applePayExpressModule = require('../applePayExpressCommon');
const { APPLE_PAY } = require('../constants');

function getProductForm(product) {
const $productInputEl = document.createElement('input');
$productInputEl.setAttribute('id', 'selected-express-product');
$productInputEl.setAttribute('name', 'selected-express-product');
$productInputEl.setAttribute('type', 'hidden');
$productInputEl.setAttribute('data-pid', `${product.id}`);
$productInputEl.setAttribute('data-basketId', '');
$productInputEl.value = JSON.stringify(product);
const $productForm = document.createElement('form');
$productForm.setAttribute('id', 'express-product-form');
$productForm.setAttribute('name', 'express-product-form');
$productForm.append($productInputEl);
return $productForm;
}

function getValueForCurrency(amount, currency) {
const value = Math.round(amount * 10 ** window.fractionDigits);
return { value, currency };
}

function getExpressPaymentButtons(product) {
const expressMethodsConfig = {
[APPLE_PAY]: window.isApplePayExpressOnPdpEnabled === 'true',
};
const enabledExpressPaymentButtons = [];
Object.keys(expressMethodsConfig).forEach((key) => {
if (expressMethodsConfig[key]) {
const $container = document.createElement('div');
$container.setAttribute('id', `${key}-pdp`);
$container.setAttribute('class', `expressComponent ${key}`);
$container.setAttribute('data-method', `${key}`);
$container.setAttribute('data-pid', `${product.id}`);
enabledExpressPaymentButtons.push($container);
}
});
return enabledExpressPaymentButtons;
}

function renderApplePayButton() {
applePayExpressModule.init();
}

function renderExpressPaymentButtons() {
$('body').on('product:renderExpressPaymentButtons', (e, response) => {
const { product = {} } = response;
const $expressPaymentButtonsContainer = document.getElementById(
'express-payment-buttons',
);
if (product.readyToOrder && product.available) {
const { price, selectedQuantity } = product;
const { value, currency } = price.sales;
const amount = getValueForCurrency(value * selectedQuantity, currency);
window.basketAmount = JSON.stringify(amount);
const expressPaymentButtons = getExpressPaymentButtons(product);
const $productForm = getProductForm(product);
$expressPaymentButtonsContainer.replaceChildren(
...expressPaymentButtons,
$productForm,
);
renderApplePayButton();
} else {
$expressPaymentButtonsContainer.replaceChildren();
}
});
}

function init() {
$('body').on('product:updateAddToCart', (e, response) => {
$('body').trigger('product:renderExpressPaymentButtons', {
product: response.product,
});
});
$(document).ready(async () => {
$.spinner().start();
const dataUrl = $('.quantity-select').find('option:selected').data('url');
const productVariation = await fetch(dataUrl);
if (productVariation.ok) {
const { product } = await productVariation.json();
$('body').trigger('product:renderExpressPaymentButtons', {
product,
});
}
$.spinner().stop();
});
}

module.exports = {
init,
renderExpressPaymentButtons,
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ const processInclude = require('base/util');

$(document).ready(() => {
processInclude(require('base/product/detail'));
processInclude(require('./product/detail'));
processInclude(require('./product/expressPayments'));
});
Loading

0 comments on commit 38adebf

Please sign in to comment.