Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Expand
titlefrontend/src/state/payment/hooks/getPaymentMethodsState/index.ts

Update the interface:

frontend/src/state/payment/hooks/types.ts:

Code Block
languagetypescript
export interface ISetupPaymentMethodState {
  enableNewMethod: boolean;
}

Adjust the getPaymentMethodsState:

Code Block
languagetypescript
export const getPaymentMethodsState = ({
  // ... rest of the code
  enableNewMethod,
}: ISetupPaymentMethodState) => {
  // ... rest of the code

  if (enableNewMethod) {
    availablePaymentMethodList.push(NEW_PAYMENT_METHOD_PLACEHOLDER);
  }
  // ... rest of the code
};

  • Adjust the payment hook

Expand
titlefrontend/src/state/payment/hooks/use-payment.tsx

Update cash account variable:

Code Block
languagetypescript
const cashAccount: IPaymentMethod = {
  // ... rest of the code
  newMethod: null,
};

Add the new feature flag here and then pass through the getPaymentMethodState:

Code Block
languagetypescript
const usePayment = ({
  // ... rest of the code
  const enableNewMethod = useFlag(LaunchDarklyFlag.ENABLE_CREDIT_CARD_AT_HOME_PAYCOMET); // new line
  
  
    const initPaymentMethods = useCallback(() => {
      const {
        availablePaymentMethodList,
        validDefaultPaymentMethodId,
        validDefaultReloadPaymentMethodId,
      } = getPaymentMethodsState({
         enableNewMethod, // new line here
      });
    },
    []
  );
});

Adjust the unit tests mocking the new flag where necessary: frontend/src/state/payment/tests/use-payment.test.tsx

...

Expand
titlefrontend/src/pages/cart/payment/order-payment/use-order-payment.ts

Add a new state for the new payment method:

const [isPaycometNewMethodSelected, setIsPaycometNewMethodSelected] = useState('');

Update the handlePaymentMethodSelected adding the new method:

Code Block
languagetypescript
  const handlePaymentMethodSelected = (newFdAccountId?: string) => {
    if (!newFdAccountId) {
      payment.setCheckoutPaymentMethodId('');
      return;
    }
    
    // ... rest of the code
    setIsPaycometNewMethodSelected(''); // New line
  };

Clear our new method in the useEffect logic that already exist:

Code Block
languagetypescript
  // when checkout payment method have been updated from payment context
  // the flags to show add new credit card or add gift card
  // have to be updated.
  useEffect(() => {
      // ... rest of the code
      setIsPaycometNewMethodSelected('');
    }
  }, [setCheckoutPaymentMethodId, checkoutPaymentMethodId]);

Update the placeOrder function to deal with the new method:

  • Option 1:

    • Adjust the if condition for paycomet processor to deal with the new method:

      Code Block
      languagetypescript
      } else if (payment.isPaycomet) {
              // ... rest of the code
              if (payCometValues.isPaypal) {
                // ... rest of the code
              } else {
                // add a new condition here for our new method, like the sodexo one
                let paymentMethodBrandValue = undefined;
                if (isPaycometSodexoMethodSelected || isPaycometChequeGourmetMethodSelected) {
                  paymentMethodBrandValue = isPaycometSodexoMethodSelected
                    ? PaymentMethodBrand.SODEXO
                    : PaymentMethodBrand.CHEQUE_GOURMET;
                }
                commitInput = {
                  // ... rest of the code
                };
            }
        // ... rest of the code

  • Option 2:

    • Separate this logic in another reusable block and then pass the new paymentMethodBrand (as a kind of builder pattern or something similar). We can discuss that on an A&D section.

Adjust the processOrderWithAccount function for the new method like this Sodexo example:

Code Block
languagetypescript
        // ... rest of the code
        const isPaymentWithVoucher =
          paymentAccount.paymentMethodBrand === PaymentMethodBrand.SODEXO_VOUCHER ||
          paymentAccount.paymentMethodBrand === PaymentMethodBrand.CHEQUE_GOURMET_VOUCHER;
        if (payment.checkoutPaymentMethodId === CASH_ACCOUNT_IDENTIFIER || isPaymentWithVoucher) {
          const paymentDetails: IPayment = {
            cashPayment: true,
            fullName,
            paymentMethodBrand: paymentAccount?.paymentMethodBrand ?? undefined,
          };

          if (payment.isVrPayment) {
            // The GraphQL endpoint for VR Payment expects this object even
            // for Cash orders, otherwise the order creation fails
            paymentDetails.vrPaymentInput = {
              merchantAccount: '',
              pspReference: '',
              storePaymentMethod: true,
            };
          }

          return commitOrder({
            ...commitInput,
            creditType: CASH_ACCOUNT_IDENTIFIER,
            payment: paymentDetails,
          });
        }

Adjust the handleOrderCommit to place the order with the new method:

Code Block
languagetypescript
      // Add new condition here as the isPaymentWithVoucher from the Sodexo example
      
      const isPaymentWithVoucher = isPaymentWithSodexoVoucher || isPaymentWithChequeGourmetVoucher;

      if (
        (showAddPaymentMethod ||
          isAdyenBlikMethodSelected ||
          reloadAddPaymentMethod ||
          isPayPalRegistration) &&
        !(selectedPaymentMethod?.cash || isPaymentWithVoucher || payment.isFreeOrderPayment)
      ) {
        await placeOrder();
      } else {
        await placeOrderWithAccount();
      }

...

Expand
titlefrontend/src/components/payment-method/utils/get-current-selected-method-type.ts

Add the new payment in the enum CurrentSelectedMethodType:

  • frontend/src/components/payment-method/types.ts:

Code Block
languagetypescript
export enum CurrentSelectedMethodType {
  // ... rest of the code
  ADD_NEW_PAYMENT_NEW_METHOD = 'addNewPaymentNewMethod',
}

Adjust the IPaymentMethodProps:

Code Block
languagetypescript
export interface IPaymentMethodProps {
  // ... rest of the code
  setIsPaycometNewMethodSelected?: Dispatch<SetStateAction<string>>;
  isPaycometNewMethodSelected?: string;
}

Add the new condition in getCurrentSelectedMethodType:

Code Block
type GetCurrentSelectedMethodTypeParams = {
  // ... rest of the code
  isPaycometNewMethodSelected?: string;
};

function getCurrentSelectedMethodType({
  // ... rest of the code
  isPaycometNewMethodSelected
}: GetCurrentSelectedMethodTypeParams): CurrentSelectedMethodType {
  let currentSelectedMethodType = CurrentSelectedMethodType.ADD_NEW_PAYMENT_METHOD;
  // ... rest of the code
  if (isPaycometNewMethodSelected === 'card') {
    currentSelectedMethodType = CurrentSelectedMethodType.ADD_NEW_PAYMENT_NEW_METHOD;
  }
  // ... rest of the code
}

  • Adjust order-payment comp:

Expand
titlefrontend/src/pages/cart/payment/order-payment/payment.tsx

Return the new create state from useOrderPayment:

Code Block
languagetypescript
  const {
   // ... rest of the code
   isPaycometNewMethodSelected, // new line here
  } = useOrderPayment({
    serverOrder,
    onPriceOrder,
    enableOrderTimedFire,
  });

Pass the returned value to the getCurrentSelectedMethodType:

Code Block
languagetypescript
  const currentSelectedMethodType = getCurrentSelectedMethodType({
    isPaycometNewMethodSelected,
  });

Pass the new method to the <PaymentMethod> comp:

Code Block
languagetypescript
<PaymentMethod
   // ...rest rest of the code
   isPaycometNewMethodSelected={isPaycometNewMethodSelected}
/>

...

Expand
titleworkspaces/frontend/src/components/payment-method/index.tsx

Add new props in PaymentMethod:

Code Block
languagetypescript
function PaymentMethod({
  // ... rest of the code
  isPaycometNewMethodSelected,
  setIsPaycometNewMethodSelected,
}: IPaymentMethodProps) {
  // ... rest of the code
  
  // pass the new prop in the getCurrentSelectedMethodType
  const currentSelectedMethodType = getCurrentSelectedMethodType({
    isPaycometNewMethodSelected
  });
});

Create new item inside PaymentMethod comp:

Code Block
languagejsx
// ... rest of the file
const SelectedNewMethodOptionItem = (
  <PaymentMethodAddNewItemOption
    data-testid="payment-new-method-name"
    text={formatMessage({ id: 'addNewPaymentNewMethod' })}
    Icon={<StyledNewMethodCardIcon />}
    onClick={getSelectedMethodClickHandler()}
    selected
  />
);

Add the new method in the map structure:

Code Block
  const CurrentSelectedMethodMap = {
    // ... rest of the file
    [CurrentSelectedMethodType.ADD_NEW_PAYMENT_METHOD]: SelectedNewMethodOptionItem,
  };

Pass the new prop in the <PaymentMethodOptions> comp:

Code Block
languagetsx
 <PaymentMethodOptions
    setIsPaycometNewMethodSelected={setIsPaycometNewMethodSelected}
  />

...

Expand
titlefrontend/src/components/payment-method/payment-method-options/payment-method-options.tsx

Add the new prop in PaymentMethodOptions comp:

Code Block
languagetypescript
function PaymentMethodOptions({
  // .. rest of the file
  setIsPaycometNewMethodSelected
}: PaymentMethodOptionProps) {
  const enableNewMethod = useFlag(LaunchDarklyFlag.ENABLE_CREDIT_CARD_AT_HOME_PAYCOMET); // new line

});

Add new condition in handleUnselectedMethodClick, handleAddNewCardOptionClick, handleAddNewGiftCardOptionClick, handlePaypalOptionClick and handleAdyenIdealOptionClick to clear our new state:

Code Block
languagetypescript
  if (setIsPaycometSodexoOptionSelected) {
    setIsPaycometNewMethodSelected('');
  }

Add a new handle for our new method (using useCallback) where we’ll clear the other methods and set your:

Code Block
languagetypescript
  const handleNewMethodOptionClick = useCallback(
    (e: React.FormEvent<HTMLButtonElement>, newMethodType: string) => {
      e.preventDefault();
      setIsAddNewCardOptionSelected(false);
      setIsAddNewGiftCardOptionSelected(false);
      if (setIsPaycometPaypalOptionSelected) {
        setIsPaycometPaypalOptionSelected(false);
      }
      if (setIsAdyenIdealOptionSelected) {
        setIsAdyenIdealOptionSelected(false);
      }
      if (setIsAdyenBlikOptionSelected) {
        setIsAdyenBlikOptionSelected(false);
      }
      if (setIsPaycometSodexoOptionSelected) {
        setIsPaycometSodexoOptionSelected('');
      }
      if (setIsPaycometSodexoOptionSelected) {
        setIsPaycometSodexoOptionSelected('');
      }
      // our new method here
      if (setIsPaycometNewMethodSelected) {
        setIsPaycometNewMethodSelected(newMethodType);
        if (newMethodType === 'card') {
          setIsAddNewCardOptionSelected(true);
        }
      }

      handleSelect();
      setCollapsed(true);
    },
    [
     // callback dependencies here
    ]
  );

Add in the enableSortPaymentMethods && unSelectedMethods.map a new block for our new method:

  • We also need to condition this payment method to be shown only if delivery mode is selected

Code Block
languagetsx
const isDelivery = serverOrder?.delivery;

{method?.newMethod && method?.transient && isDelivery && (
  <StyledOption>
    <PaymentMethodOptionItemDivider />
    <PaymentMethodAddNewItemOption
      data-testid="add-new-method-payment"
      text={formatMessage({ id: newMethodLabel(isGuestOrder) })}
      Icon={<StyledNewMethodCardIcon />}
      onClick={e => handleNewMethodOptionClick(e, 'card')}
      isLoading={isLoadingPopup}
    />
  </StyledOption>
)}

Add a new block for for the case where we not have the enableSortPaymentMethods as true:

Code Block
languagetsx
{!enableSortPaymentMethods && enableNewMethod && (
  <StyledOption>
    <PaymentMethodOptionItemDivider />
    <PaymentMethodAddNewItemOption
      data-testid=add-new-method-payment"
      text={formatMessage({ id: newMethodLabel(isGuestOrder) })}
      Icon={<StyledNewMethodCardIcon />}
      onClick={e => handleNewMethodOptionClick(e, 'card')}
      isLoading={isLoadingPopup}
    />
  </StyledOption>
)}

Add the new payment method in

...

pagescartpaymentorder/paycomet-hosted-page-payment/
Expand
title

the sortPaymentMethods

  • frontend/src/

  • components/

  • payment-method/

  • utils/

  • sort-payment

  • -methods.ts

Code Block
languagetypescript
  const paymentMethodListTypeOrder = [
    // rest of the code
    'NEW_METHOD',
  ];
  
  // Add new case in switch for our new method
  function filterMethodByType(typeOrder: string, paymentMethods: IPaymentMethod[]): IPaymentMethod[] {
  switch (typeOrder) {
    // rest of the code
    case 'NEW_METHOD':
      return paymentMethods.filter(
        p => !p?.transient && p?.newMethod && p.accountIdentifier === typeOrder
      );
    // rest of the code
  }
}

Add a new logic to the filter !enableSortPaymentMethods && unSelectedMethods.filter(isNotLocalWallet)... to now show our payment method if delivery mode is not selected:

Code Block
languagetypescript
  const isDelivery = serverOrder?.delivery;

  const isNotLocalWallet = (method: IPaymentMethod) => {
    return !method.blik && !method.ideal;
  };

  const sortedMethodsFilterConditions = (method: IPaymentMethod) => {
    if (method.chequeGourmet && !isDelivery) {
      return false; // remove our item from the list if delivery is not selected
    }

    return isNotLocalWallet(method);
  };
      
      // A suggestion would be the creation of a new function for the filter conditions
      {!enableSortPaymentMethods &&
        unSelectedMethods.filter(sortedMethodsFilterConditions).map(method => {

  • Add the new method in paycomet-hosted-page-payment comp

Expand
titlefrontend/src/pages/cart/payment/order-payment/paycomet-hosted-page-payment/paycomet-hosted-page-payment.tsx

Return the new values from the useOrderPayment hook

Code Block
languagetypescript
const {
    // rest of code
    setIsPaycometNewMethodSelected,
    isPaycometNewMethodSelected,
  } = useOrderPayment({
    enableOrderTimedFire,
    onPriceOrder,
    serverOrder,
  });

Pass down the values to the <PaymentMethod> comp

Code Block
languagetsx
// rest of the code
<PaymentInfoBackground $hostedPayment>
  {showPaymentMethodSelection && (
    <PaymentMethod
      // rest of the code
      setIsPaycometNewMethodSelected={setIsPaycometNewMethodSelected} // new line
      isPaycometNewMethodSelected={isPaycometNewMethodSelected} // new line
      onResult={onPaymentOutcome}
    />
  )}

...

Expand
titlefrontend/src/state/payment/types.ts
Code Block
languagetypescript
export interface IPaymentMethod {
  // ... rest of the code
  newPaymentMethod?: boolean | null;
}

...

Expand
titlefrontend/src/components/payment-method-option/index.tsx

Adjust the RenderMethodType and add the new method created above

Code Block
languagetsx
  const RenderMethodType = () => {
    // ... rest of the code

    // New if here
    if (method.newPaymentMethod) {
      return <NewPaymentMethod />;
    }   
    
    return null;
  };
  
  // add the new payment method inside the <MethodTypeWrapper> in the return
  
   return 
    // rest of the code
    (
    // rest of the code
      <MethodTypeWrapper
        data-private
        data-dd-privacy="mask"
        onClick={onClickMethod}
        $isClickable={!!onClick}
        disableMethod={disableMethod}
        fromCheckout={fromCheckout}
        data-testid={`method-type-wrapper-${method.accountIdentifier ?? method.fdAccountId ?? ''}`}
        selected={selected}
      >
        // rest of the code
        <div>
          <MethodTypeDescription>
            // Add new method here inside the description
            {method.newPaymentMethod && <StyledNewPaymentMethodCardIcon />}
          </MethodTypeDescription>
          // rest of the code
        </div>
      </MethodTypeWrapper>
     // rest of the code
  );

...