From 119662d3b1310c0eda37b7b6eeefe95db74b8426 Mon Sep 17 00:00:00 2001 From: Win Date: Mon, 14 Oct 2024 13:40:00 +0700 Subject: [PATCH] MOL-439/MOL-532: integration for processor module --- application/.env.dev | 2 +- application/custom-application-config.ts | 2 +- application/setup.js | 2 +- .../method-details/method-details-form.tsx | 1 + .../method-details/method-details.tsx | 15 ++- .../src/components/welcome/welcome.tsx | 44 ++++---- .../use-mollie-connector.ts | 38 ++++--- application/src/types/app.ts | 13 +++ processor/src/service/payment.service.ts | 102 +++++++++++++++--- 9 files changed, 153 insertions(+), 66 deletions(-) diff --git a/application/.env.dev b/application/.env.dev index 62f04ed..db15d5c 100644 --- a/application/.env.dev +++ b/application/.env.dev @@ -2,7 +2,7 @@ ENABLE_NEW_JSX_TRANSFORM="true" FAST_REFRESH="true" ENTRY_POINT_URI_PATH="mollie" -PROJECT_KEY="shopm-adv-dev" +PROJECT_KEY="shopm-adv-windev" CLOUD_IDENTIFIER="gcp-eu" CUSTOM_APPLICATION_ID="app-id" APPLICATION_URL="http://localhost:3001" \ No newline at end of file diff --git a/application/custom-application-config.ts b/application/custom-application-config.ts index 37fc7b4..95fe782 100644 --- a/application/custom-application-config.ts +++ b/application/custom-application-config.ts @@ -15,7 +15,7 @@ const config = { cloudIdentifier: CLOUD_IDENTIFIER, env: { development: { - initialProjectKey: 'shopm-adv-dev', + initialProjectKey: 'shopm-adv-windev', }, production: { applicationId: CUSTOM_APPLICATION_ID, diff --git a/application/setup.js b/application/setup.js index 76e656b..b6e1549 100644 --- a/application/setup.js +++ b/application/setup.js @@ -5,7 +5,7 @@ jest.mock('./src/constants', () => { View: 'ViewMollie', Manage: 'TestMollie', }, - PROJECT_KEY: 'shopm-adv-dev', + PROJECT_KEY: 'shopm-adv-windev', CLOUD_IDENTIFIER: 'gcp-eu', CUSTOM_APPLICATION_ID: '', APPLICATION_URL: 'http://localhost:3001', diff --git a/application/src/components/method-details/method-details-form.tsx b/application/src/components/method-details/method-details-form.tsx index ffc4ebb..4185d2c 100644 --- a/application/src/components/method-details/method-details-form.tsx +++ b/application/src/components/method-details/method-details-form.tsx @@ -91,6 +91,7 @@ const MethodDetailsForm = (props: TCustomObjectDetailsFormProps) => { onBlur={formik.handleBlur} isReadOnly={props.isReadOnly} horizontalConstraint={13} + isRequired={false} renderError={(errorKey) => { if (errorKey === 'invalidLength') { return intl.formatMessage( diff --git a/application/src/components/method-details/method-details.tsx b/application/src/components/method-details/method-details.tsx index 51b559f..9f99c21 100644 --- a/application/src/components/method-details/method-details.tsx +++ b/application/src/components/method-details/method-details.tsx @@ -51,7 +51,6 @@ const MethodDetails = (props: TMethodDetailsProps) => { }); const handleSubmit = async (formikValues: TMethodObjectValueFormValues) => { - console.log('formikValues', formikValues); try { if (method?.container && method?.key && formikValues) { await customObjectUpdater.execute({ @@ -88,16 +87,11 @@ const MethodDetails = (props: TMethodDetailsProps) => { ) => { try { if (method?.container && method?.key && formikValues) { + let clonedValues = { ...formikValues, ...{ status: status } }; await customObjectUpdater.execute({ container: method?.container, key: method?.key, - value: JSON.stringify({ - id: formikValues.id, - description: formikValues.description, - status: status, - imageUrl: formikValues.imageUrl, - displayOrder: formikValues.displayOrder, - }), + value: JSON.stringify(clonedValues), }); showNotification({ kind: NOTIFICATION_KINDS_SIDE.success, @@ -219,7 +213,10 @@ const MethodDetails = (props: TMethodDetailsProps) => { /> formProps.submitForm()} + onClick={(event) => { + event.preventDefault(); + formProps.submitForm(); + }} isDisabled={ formProps.isSubmitting || !formProps.isDirty || !canManage } diff --git a/application/src/components/welcome/welcome.tsx b/application/src/components/welcome/welcome.tsx index e5eb105..d27cc16 100644 --- a/application/src/components/welcome/welcome.tsx +++ b/application/src/components/welcome/welcome.tsx @@ -65,7 +65,10 @@ const Welcome = () => { ), }, { key: 'image', label: intl.formatMessage(messages.iconHeader) }, - { key: 'order', label: intl.formatMessage(messages.displayOrderHeader) }, + { + key: 'order', + label: intl.formatMessage(messages.displayOrderHeader), + }, ]; const customObjectUpdater = useCustomObjectDetailsUpdater(); const { page, perPage } = usePaginationState(); @@ -85,7 +88,8 @@ const Welcome = () => { const [refresh, setRefresh] = useState(0); const { fetchedData, fetchedDataLoading } = usePaymentMethodsFetcher( - extension?.destination?.url + extension?.destination?.url, + projectLanguages ); const handleRefresh = useCallback(() => { @@ -175,23 +179,19 @@ const Welcome = () => { ); case 'name': - return ( - - {item.name - ? formatLocalizedString( - { - name: item.name, - }, - { - key: 'name', - locale: dataLocale, - fallbackOrder: projectLanguages, - fallback: NO_VALUE_FALLBACK, - } - ) - : item.description} - - ); + return item.name + ? formatLocalizedString( + { + name: item.name, + }, + { + key: 'name', + locale: dataLocale, + fallbackOrder: projectLanguages, + fallback: NO_VALUE_FALLBACK, + } + ) + : item.description; case 'image': return ( { > ); case 'order': - return ( - - {item.displayOrder ?? '-'} - - ); + return item.displayOrder ?? '-'; default: return null; } diff --git a/application/src/hooks/use-mollie-connector/use-mollie-connector.ts b/application/src/hooks/use-mollie-connector/use-mollie-connector.ts index 54f8738..4dd0555 100644 --- a/application/src/hooks/use-mollie-connector/use-mollie-connector.ts +++ b/application/src/hooks/use-mollie-connector/use-mollie-connector.ts @@ -14,6 +14,7 @@ import { MollieMethod, CustomMethodObject, MollieResult, + SupportedPaymentMethods, } from '../../types/app'; /** @@ -29,27 +30,32 @@ const config = { }; const convertMollieMethodToCustomMethod = ( - results: MollieResult + results: MollieResult, + projectLanguages: string[] ): CustomMethodObject[] => { const methods = results['_embedded']['methods']; const availableMethods = methods.filter( - (method: MollieMethod) => method.status === 'activated' + (method: MollieMethod) => + method.status === 'activated' && + SupportedPaymentMethods[method.id as SupportedPaymentMethods] ); return availableMethods.map((method: MollieMethod) => ({ id: method.id, - name: { - 'en-GB': method.description, - }, - description: { - 'en-GB': '', - }, + name: projectLanguages.reduce((acc, lang) => { + acc[lang] = method.description; + return acc; + }, {} as Record), + description: projectLanguages.reduce((acc, lang) => { + acc[lang] = ''; + return acc; + }, {} as Record), imageUrl: method.image.svg, status: 'Inactive', displayOrder: undefined, })); }; -const getMethods = async (targetUrl?: string) => { +const getMethods = async (projectLanguages: string[], targetUrl?: string) => { if (!targetUrl) { logger.error('usePaymentMethodsFetcher - No target URL provided'); return []; @@ -78,18 +84,24 @@ const getMethods = async (targetUrl?: string) => { } ) .then((res) => - convertMollieMethodToCustomMethod(res as unknown as MollieResult) + convertMollieMethodToCustomMethod( + res as unknown as MollieResult, + projectLanguages + ) ) .catch((error) => logger.error(error)); }; -export const usePaymentMethodsFetcher = (url: string | undefined) => { +export const usePaymentMethodsFetcher = ( + url: string | undefined, + projectLanguages: string[] +) => { const [fetchedData, setFetchedData] = useState([]); const [fetchedDataLoading, setFetchedDataLoading] = useState(true); useEffect(() => { const fetchData = async () => { - const data = (await getMethods(url)) ?? []; + const data = (await getMethods(projectLanguages, url)) ?? []; setFetchedData(data); setFetchedDataLoading(false); }; @@ -97,7 +109,7 @@ export const usePaymentMethodsFetcher = (url: string | undefined) => { if (url) { fetchData(); } - }, [url]); + }, [url, projectLanguages]); return { fetchedData, fetchedDataLoading }; }; diff --git a/application/src/types/app.ts b/application/src/types/app.ts index 317f906..baadfcc 100644 --- a/application/src/types/app.ts +++ b/application/src/types/app.ts @@ -42,3 +42,16 @@ export type MollieResult = { methods: MollieMethod[]; }; }; + +export enum SupportedPaymentMethods { + ideal = 'ideal', + creditcard = 'creditcard', + bancontact = 'bancontact', + banktransfer = 'banktransfer', + przelewy24 = 'przelewy24', + kbc = 'kbc', + blik = 'blik', + applepay = 'applepay', + paypal = 'paypal', + giftcard = 'giftcard', +} diff --git a/processor/src/service/payment.service.ts b/processor/src/service/payment.service.ts index 3866ef9..248e578 100644 --- a/processor/src/service/payment.service.ts +++ b/processor/src/service/payment.service.ts @@ -60,6 +60,80 @@ import { parseStringToJsonObject } from '../utils/app.utils'; import ApplePaySession from '@mollie/api-client/dist/types/src/data/applePaySession/ApplePaySession'; import { getMethodConfigObjects } from '../commercetools/customObjects.commercetools'; +type CustomMethod = { + id: string; + name: Record; + description: Record; + image: string; + order: number; +}; + +/** + * Validates and sorts the payment methods. + * + * @param {CustomMethod[]} methods - The list of payment methods. + * @param {CustomObject[]} configObjects - The configuration objects. + * @return {CustomMethod[]} - The validated and sorted payment methods. + */ +const validateAndSortMethods = (methods: CustomMethod[], configObjects: CustomObject[]): CustomMethod[] => { + if (!configObjects.length) { + return methods.filter( + (method: CustomMethod) => SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods], + ); + } + + return methods + .filter((method) => isValidMethod(method, configObjects)) + .map((method) => mapMethodToCustomMethod(method, configObjects)) + .sort((a, b) => b.order - a.order); // Descending order sort +}; + +/** + * Checks if a method is valid based on the configuration objects. + * + * @param {CustomMethod} method - The payment method. + * @param {CustomObject[]} configObjects - The configuration objects. + * @return {boolean} - True if the method is valid, false otherwise. + */ +const isValidMethod = (method: CustomMethod, configObjects: CustomObject[]): boolean => { + return ( + !!configObjects.find((config) => config.key === method.id && config.value.status === 'Active') && + !!SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods] + ); +}; + +/** + * Maps a payment method to a custom method. + * + * @param {CustomMethod} method - The payment method. + * @param {CustomObject[]} configObjects - The configuration objects. + * @return {CustomMethod} - The custom method. + */ +const mapMethodToCustomMethod = (method: CustomMethod, configObjects: CustomObject[]): CustomMethod => { + const config = configObjects.find((config) => config.key === method.id); + + return { + id: method.id, + name: config?.value?.name, + description: config?.value?.description, + image: config?.value?.imageUrl, + order: config?.value?.displayOrder || 0, + }; +}; + +/** + * Determines if the card component should be enabled. + * + * @param {CustomMethod[]} validatedMethods - The validated payment methods. + * @return {boolean} - True if the card component should be enabled, false otherwise. + */ +const shouldEnableCardComponent = (validatedMethods: CustomMethod[]): boolean => { + return ( + toBoolean(readConfiguration().mollie.cardComponent, true) && + validatedMethods.some((method) => method.id === PaymentMethod.creditcard) + ); +}; + /** * Handles listing payment methods by payment. * @@ -72,27 +146,21 @@ export const handleListPaymentMethodsByPayment = async (ctPayment: Payment): Pro const mollieOptions = await mapCommercetoolsPaymentCustomFieldsToMollieListParams(ctPayment); const methods: List = await listPaymentMethods(mollieOptions); const configObjects: CustomObject[] = await getMethodConfigObjects(); - let validatedMethods: Method[] = []; - if (configObjects.length) { - validatedMethods = methods.filter( - (method) => - !!configObjects.find((config) => config.key === method.id && config.value.status === 'Active') && - SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods], - ); - } else { - validatedMethods = methods.filter( - (method: Method) => SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods], - ); - } - - const enableCardComponent = - toBoolean(readConfiguration().mollie.cardComponent, true) && - validatedMethods.filter((method: Method) => method.id === PaymentMethod.creditcard).length > 0; + const customMethods = methods.map((method) => ({ + id: method.id, + name: { 'en-GB': method.description }, + description: { 'en-GB': '' }, + image: method.image.svg, + order: 0, + })); + const validatedMethods = validateAndSortMethods(customMethods, configObjects); + + const enableCardComponent = shouldEnableCardComponent(validatedMethods); const ctUpdateActions: UpdateAction[] = []; if (enableCardComponent) { validatedMethods.splice( - validatedMethods.findIndex((method: Method) => method.id === PaymentMethod.creditcard), + validatedMethods.findIndex((method) => method.id === PaymentMethod.creditcard), 1, ); }