diff --git a/application/custom-application-config.ts b/application/custom-application-config.ts index 37fc7b4..5a8d35f 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: 'your_project_key', }, production: { applicationId: CUSTOM_APPLICATION_ID, diff --git a/application/cypress/fixtures/fetch-project.json b/application/cypress/fixtures/fetch-project.json index bf40513..c0401be 100644 --- a/application/cypress/fixtures/fetch-project.json +++ b/application/cypress/fixtures/fetch-project.json @@ -1,423 +1,404 @@ { "data": { "project": { - "key": "shopm-adv-dev", + "key": "shopm-adv-windev", "version": 16, - "name": "shopm-adv-dev", - "countries": [ - "GB", - "DE", - "US", - "IT", - "AT", - "PL" - ], - "currencies": [ - "EUR", - "GBP", - "USD", - "PLN" - ], - "languages": [ - "en-GB", - "de-DE", - "en-US", - "de-AT", - "it-IT", - "pl-PL" - ], + "name": "shopm-adv-windev", + "countries": ["GB", "DE", "US", "IT", "AT", "PL"], + "currencies": ["EUR", "GBP", "USD", "PLN"], + "languages": ["en-GB", "de-DE", "en-US", "de-AT", "it-IT", "pl-PL"], "initialized": true, "expiry": { - "isActive": false, - "daysLeft": 29, - "__typename": "ProjectExpiry" + "isActive": false, + "daysLeft": 29, + "__typename": "ProjectExpiry" }, "suspension": { - "isActive": false, - "reason": null, - "__typename": "ProjectSuspension" + "isActive": false, + "reason": null, + "__typename": "ProjectSuspension" }, "isProductionProject": false, "allAppliedPermissions": [ + { + "name": "canViewMollie", + "value": true, + "__typename": "AppliedPermission" + }, + { + "name": "canManageMollie", + "value": true, + "__typename": "AppliedPermission" + } + ], + "allAppliedActionRights": [ + { + "group": "products", + "name": "canAddPrices", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "products", + "name": "canAddProducts", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "products", + "name": "canDeletePrices", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "products", + "name": "canDeleteProducts", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "products", + "name": "canEditPrices", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "products", + "name": "canPublishProducts", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "products", + "name": "canUnpublishProducts", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "products", + "name": "canEditAttributes:all", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "orders", + "name": "canAddOrders", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "orders", + "name": "canAddDiscountCodes", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "orders", + "name": "canCreateReturns", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "quotes", + "name": "canSendQuote", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "quotes", + "name": "canCreateDraftQuote", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "standalonePrices", + "name": "canAddPrices", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "standalonePrices", + "name": "canDeletePrices", + "value": false, + "__typename": "AppliedActionRight" + }, + { + "group": "standalonePrices", + "name": "canEditPrices", + "value": false, + "__typename": "AppliedActionRight" + } + ], + "allAppliedDataFences": [], + "allPermissionsForAllApplications": { + "allAppliedPermissions": [ { - "name": "canViewMollie", - "value": true, - "__typename": "AppliedPermission" + "name": "canViewMollie", + "value": true, + "__typename": "AppliedPermission" }, { - "name": "canManageMollie", - "value": true, - "__typename": "AppliedPermission" + "name": "canManageMollie", + "value": true, + "__typename": "AppliedPermission" } - ], - "allAppliedActionRights": [ + ], + "allAppliedActionRights": [ { - "group": "products", - "name": "canAddPrices", - "value": false, - "__typename": "AppliedActionRight" + "group": "products", + "name": "canAddPrices", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "products", - "name": "canAddProducts", - "value": false, - "__typename": "AppliedActionRight" + "group": "products", + "name": "canAddProducts", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "products", - "name": "canDeletePrices", - "value": false, - "__typename": "AppliedActionRight" + "group": "products", + "name": "canDeletePrices", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "products", - "name": "canDeleteProducts", - "value": false, - "__typename": "AppliedActionRight" + "group": "products", + "name": "canDeleteProducts", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "products", - "name": "canEditPrices", - "value": false, - "__typename": "AppliedActionRight" + "group": "products", + "name": "canEditPrices", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "products", - "name": "canPublishProducts", - "value": false, - "__typename": "AppliedActionRight" + "group": "products", + "name": "canPublishProducts", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "products", - "name": "canUnpublishProducts", - "value": false, - "__typename": "AppliedActionRight" + "group": "products", + "name": "canUnpublishProducts", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "products", - "name": "canEditAttributes:all", - "value": false, - "__typename": "AppliedActionRight" + "group": "products", + "name": "canEditAttributes:all", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "orders", - "name": "canAddOrders", - "value": false, - "__typename": "AppliedActionRight" + "group": "orders", + "name": "canAddOrders", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "orders", - "name": "canAddDiscountCodes", - "value": false, - "__typename": "AppliedActionRight" + "group": "orders", + "name": "canAddDiscountCodes", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "orders", - "name": "canCreateReturns", - "value": false, - "__typename": "AppliedActionRight" + "group": "orders", + "name": "canCreateReturns", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "quotes", - "name": "canSendQuote", - "value": false, - "__typename": "AppliedActionRight" + "group": "quotes", + "name": "canSendQuote", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "quotes", - "name": "canCreateDraftQuote", - "value": false, - "__typename": "AppliedActionRight" + "group": "quotes", + "name": "canCreateDraftQuote", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "standalonePrices", - "name": "canAddPrices", - "value": false, - "__typename": "AppliedActionRight" + "group": "standalonePrices", + "name": "canAddPrices", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "standalonePrices", - "name": "canDeletePrices", - "value": false, - "__typename": "AppliedActionRight" + "group": "standalonePrices", + "name": "canDeletePrices", + "value": false, + "__typename": "AppliedActionRight" }, { - "group": "standalonePrices", - "name": "canEditPrices", - "value": false, - "__typename": "AppliedActionRight" + "group": "standalonePrices", + "name": "canEditPrices", + "value": false, + "__typename": "AppliedActionRight" } - ], - "allAppliedDataFences": [], - "allPermissionsForAllApplications": { - "allAppliedPermissions": [ - { - "name": "canViewMollie", - "value": true, - "__typename": "AppliedPermission" - }, - { - "name": "canManageMollie", - "value": true, - "__typename": "AppliedPermission" - } - ], - "allAppliedActionRights": [ - { - "group": "products", - "name": "canAddPrices", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "products", - "name": "canAddProducts", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "products", - "name": "canDeletePrices", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "products", - "name": "canDeleteProducts", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "products", - "name": "canEditPrices", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "products", - "name": "canPublishProducts", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "products", - "name": "canUnpublishProducts", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "products", - "name": "canEditAttributes:all", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "orders", - "name": "canAddOrders", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "orders", - "name": "canAddDiscountCodes", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "orders", - "name": "canCreateReturns", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "quotes", - "name": "canSendQuote", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "quotes", - "name": "canCreateDraftQuote", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "standalonePrices", - "name": "canAddPrices", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "standalonePrices", - "name": "canDeletePrices", - "value": false, - "__typename": "AppliedActionRight" - }, - { - "group": "standalonePrices", - "name": "canEditPrices", - "value": false, - "__typename": "AppliedActionRight" - } - ], - "allAppliedMenuVisibilities": [ - { - "name": "hideDashboard", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideProductsList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideModifiedProducts", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddProduct", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideProductSelectionsList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddProductSelection", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideDirectAccess", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideCategoriesList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideCategoriesSearch", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddCategory", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideCustomersList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddCustomer", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideCustomerGroupsList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddCustomerGroup", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideBusinessUnitsList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddBusinessUnit", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideOrdersList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddOrder", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideQuotes", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideProductDiscountsList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideCartDiscountsList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideDiscountCodesList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddDiscounts", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideGenerateDiscountCodes", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideProjectSettings", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideProductTypes", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideDeveloperSettings", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideStandalonePriceList", - "value": false, - "__typename": "AppliedMenuVisibilities" - }, - { - "name": "hideAddStandalonePrice", - "value": false, - "__typename": "AppliedMenuVisibilities" - } - ], - "allAppliedDataFences": [], - "__typename": "AllPermissionsForAllApplications" + ], + "allAppliedMenuVisibilities": [ + { + "name": "hideDashboard", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideProductsList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideModifiedProducts", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddProduct", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideProductSelectionsList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddProductSelection", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideDirectAccess", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideCategoriesList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideCategoriesSearch", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddCategory", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideCustomersList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddCustomer", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideCustomerGroupsList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddCustomerGroup", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideBusinessUnitsList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddBusinessUnit", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideOrdersList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddOrder", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideQuotes", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideProductDiscountsList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideCartDiscountsList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideDiscountCodesList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddDiscounts", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideGenerateDiscountCodes", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideProjectSettings", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideProductTypes", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideDeveloperSettings", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideStandalonePriceList", + "value": false, + "__typename": "AppliedMenuVisibilities" + }, + { + "name": "hideAddStandalonePrice", + "value": false, + "__typename": "AppliedMenuVisibilities" + } + ], + "allAppliedDataFences": [], + "__typename": "AllPermissionsForAllApplications" }, "owner": { - "id": "9adf9042-7177-4ea0-a928-a8e194c63009", - "name": "Shopmacher", - "__typename": "Organization" + "id": "9adf9042-7177-4ea0-a928-a8e194c63009", + "name": "Shopmacher", + "__typename": "Organization" }, "sampleDataImportDataset": "B2CLIFESTYLE", "isUserAdminOfCurrentProject": true, "__typename": "Project" } } -} \ No newline at end of file +} diff --git a/application/src/components/welcome/welcome.tsx b/application/src/components/welcome/welcome.tsx index bcd4e80..ffca574 100644 --- a/application/src/components/welcome/welcome.tsx +++ b/application/src/components/welcome/welcome.tsx @@ -17,7 +17,7 @@ import DataTable from '@commercetools-uikit/data-table'; import IconButton from '@commercetools-uikit/icon-button'; import { usePaymentMethodsFetcher } from '../../hooks/use-mollie-connector'; import { ContentNotification } from '@commercetools-uikit/notifications'; -import { CustomMethodObject } from '../../types/app'; +import { CustomMethodObject, CustomObjectUpdaterError } from '../../types/app'; import LoadingSpinner from '@commercetools-uikit/loading-spinner'; import Tootltip from '@commercetools-uikit/tooltip'; import { @@ -74,7 +74,7 @@ const Welcome = () => { key: 'key', order: 'asc', }); - const { customObjectsPaginatedResult, error, loading } = + const { customObjectsPaginatedResult, error, loading, refetch } = useCustomObjectsFetcher({ page, perPage, @@ -110,7 +110,13 @@ const Welcome = () => { value: JSON.stringify(method), }) .catch((error) => { - console.error(`Error creating custom object: ${error}`); + Object.values(error).forEach((e) => { + console.error( + `SCTM custom application: ${ + (e as CustomObjectUpdaterError).message + }` + ); + }); }); return method; } else { @@ -120,9 +126,15 @@ const Welcome = () => { } }) ); + refetch(); setMethods(updatedMethods); } - }, [customObjectUpdater, customObjectsPaginatedResult?.results, fetchedData]); + }, [ + customObjectUpdater, + customObjectsPaginatedResult?.results, + fetchedData, + refetch, + ]); useEffect(() => { if ( @@ -212,13 +224,13 @@ const Welcome = () => { sortDirection={tableSorting.value.order} onSortChange={tableSorting.onChange} onRowClick={(row) => { - push( - `${match.url}/${ - customObjectsPaginatedResult?.results.filter( - (obj) => obj.key === row.id - )?.[0]?.id - }/general` + const target = customObjectsPaginatedResult?.results.filter( + (obj) => obj.key === row.id ); + + if (target) { + push(`${match.url}/${target[0].id}/general`); + } }} /> diff --git a/application/src/hooks/use-custom-objects-connector/use-custom-objects-connector.ts b/application/src/hooks/use-custom-objects-connector/use-custom-objects-connector.ts index 4de4fca..92be795 100644 --- a/application/src/hooks/use-custom-objects-connector/use-custom-objects-connector.ts +++ b/application/src/hooks/use-custom-objects-connector/use-custom-objects-connector.ts @@ -21,7 +21,7 @@ import FetchCustomObjectsQuery from './fetch-custom-objects.ctp.graphql'; import FetchCustomObjectDetailsQuery from './fetch-custom-object-details.ctp.graphql'; import UpdateCustomObjectDetailsMutation from './update-custom-object-details.ctp.graphql'; import RemoveCustomObjectDetailsMutation from './remove-custom-object-details.ctp.graphql'; -import { ApolloError } from '@apollo/client'; +import { ApolloError, ApolloQueryResult } from '@apollo/client'; import { extractErrorFromGraphQlResponse } from '../../helpers'; type PaginationAndSortingProps = { @@ -37,6 +37,7 @@ type TUseCustomObjectsFetcher = ( customObjectsPaginatedResult?: TFetchCustomObjectsQuery['customObjects']; error?: ApolloError; loading: boolean; + refetch: () => Promise>; }; export const useCustomObjectsFetcher: TUseCustomObjectsFetcher = ({ @@ -45,7 +46,7 @@ export const useCustomObjectsFetcher: TUseCustomObjectsFetcher = ({ tableSorting, container, }) => { - const { data, error, loading } = useMcQuery< + const { data, error, loading, refetch } = useMcQuery< TFetchCustomObjectsQuery, TFetchCustomObjectsQueryVariables >(FetchCustomObjectsQuery, { @@ -64,6 +65,7 @@ export const useCustomObjectsFetcher: TUseCustomObjectsFetcher = ({ customObjectsPaginatedResult: data?.customObjects, error, loading, + refetch, }; }; type TUseCustomObjectDetailsFetcher = (id: string) => { 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 ba85ba4..0bd2a0e 100644 --- a/application/src/hooks/use-mollie-connector/use-mollie-connector.ts +++ b/application/src/hooks/use-mollie-connector/use-mollie-connector.ts @@ -15,6 +15,7 @@ import { CustomMethodObject, MollieResult, SupportedPaymentMethods, + GooglePay, } from '../../types/app'; /** @@ -45,6 +46,8 @@ const convertMollieMethodToCustomMethod = ( method.status === 'activated' && SupportedPaymentMethods[method.id as SupportedPaymentMethods] ); + + availableMethods.push(GooglePay); return availableMethods.map((method: MollieMethod) => ({ id: method.id, technicalName: method.description, diff --git a/application/src/types/app.ts b/application/src/types/app.ts index 83d7cf1..7ddc820 100644 --- a/application/src/types/app.ts +++ b/application/src/types/app.ts @@ -16,8 +16,13 @@ export type MollieMethod = { currency: string; }; pricing: { - value: string; - currency: string; + description: string; + fixed: { + value: string; + currency: string; + }; + variable: string; + feeRegion?: string; }[]; status: string; _links: { @@ -66,3 +71,90 @@ export enum SupportedPaymentMethods { paypal = 'paypal', giftcard = 'giftcard', } + +export const GooglePay = { + resource: 'method', + id: 'googlepay', + description: 'Google Pay', + minimumAmount: { + value: '0.01', + currency: 'EUR', + }, + maximumAmount: { + value: '10000.00', + currency: 'EUR', + }, + image: { + size1x: + 'https://www.mollie.com/external/icons/payment-methods/googlepay.png', + size2x: + 'https://www.mollie.com/external/icons/payment-methods/googlepay%402x.png', + svg: 'https://www.mollie.com/external/icons/payment-methods/googlepay.svg', + }, + status: 'activated', + pricing: [ + { + description: 'American Express (intra-EEA)', + fixed: { + value: '0.25', + currency: 'EUR', + }, + variable: '2.9', + feeRegion: 'amex-intra-eea', + }, + { + description: 'Commercial & non-European cards', + fixed: { + value: '0.25', + currency: 'EUR', + }, + variable: '3.25', + feeRegion: 'other', + }, + { + description: 'Domestic consumer cards', + fixed: { + value: '0.25', + currency: 'EUR', + }, + variable: '1.8', + feeRegion: 'domestic', + }, + { + description: 'European commercial cards', + fixed: { + value: '0.25', + currency: 'EUR', + }, + variable: '2.9', + feeRegion: 'intra-eu-corporate', + }, + { + description: 'European consumer cards', + fixed: { + value: '0.25', + currency: 'EUR', + }, + variable: '1.8', + feeRegion: 'eu-cards', + }, + { + description: 'Pre-authorization fees', + fixed: { + value: '0.00', + currency: 'EUR', + }, + variable: '0.12', + }, + ], + _links: { + self: { + href: 'https://api.mollie.com/v2/methods/googlepay', + type: 'application/hal+json', + }, + }, +}; + +export interface CustomObjectUpdaterError { + message: string; +} diff --git a/processor/package-lock.json b/processor/package-lock.json index fbc9d7e..3cc0f03 100644 --- a/processor/package-lock.json +++ b/processor/package-lock.json @@ -1,12 +1,12 @@ { "name": "shopmacher-mollie-processor", - "version": "1.2.0-alpha22.11.24.1410", + "version": "1.2.0-alpha25.11.24.1150", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "shopmacher-mollie-processor", - "version": "1.2.0-alpha22.11.24.1410", + "version": "1.2.0-alpha25.11.24.1150", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/processor/package.json b/processor/package.json index a164a1b..11b3899 100644 --- a/processor/package.json +++ b/processor/package.json @@ -1,7 +1,7 @@ { "name": "shopmacher-mollie-processor", "description": "Integration between commercetools and mollie payment service provider", - "version": "1.2.0-alpha22.11.24.1410", + "version": "1.2.0-alpha25.11.24.1150", "main": "index.js", "private": true, "scripts": { diff --git a/processor/src/service/payment.service.ts b/processor/src/service/payment.service.ts index 248e046..61c4700 100644 --- a/processor/src/service/payment.service.ts +++ b/processor/src/service/payment.service.ts @@ -85,6 +85,14 @@ import { removeCartMollieCustomLineItem } from './cart.service'; * @return {CustomMethod[]} - The validated and sorted payment methods. */ const validateAndSortMethods = (methods: CustomMethod[], configObjects: CustomObject[]): CustomMethod[] => { + methods.push({ + id: 'googlepay', + name: { 'en-GB': 'Google Pay' }, + description: { 'en-GB': '' }, + image: '', + order: 0, + }); + if (!configObjects.length) { return methods.filter( (method: CustomMethod) => SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods], @@ -414,6 +422,10 @@ export const handleCreatePayment = async (ctPayment: Payment): Promise { slug, }); }); + + test('should return a action for adding transaction custom fields', () => { + const name = 'customFieldName'; + const value = 'customFieldValue'; + const transactionId = 'transactionId'; + + expect(setTransactionCustomField(name, value, transactionId)).toStrictEqual({ + action: 'setTransactionCustomField', + name, + value, + transactionId, + }); + }); }); diff --git a/processor/tests/mollie/payment.mollie.spec.ts b/processor/tests/mollie/payment.mollie.spec.ts index d7a5033..e5f1f26 100644 --- a/processor/tests/mollie/payment.mollie.spec.ts +++ b/processor/tests/mollie/payment.mollie.spec.ts @@ -6,8 +6,9 @@ import { getPaymentById, listPaymentMethods, getApplePaySession, + getAllPaymentMethods, } from '../../src/mollie/payment.mollie'; -import { MollieApiError, PaymentCreateParams } from '@mollie/api-client'; +import { Locale, MethodInclude, MethodsListParams, MollieApiError, PaymentCreateParams } from '@mollie/api-client'; import { logger } from '../../src/utils/logger.utils'; import CustomError from '../../src/errors/custom.error'; import { MOLLIE_VERSION_STRINGS } from '../../src/utils/constant.utils'; @@ -543,3 +544,63 @@ describe('getApplePaySession', () => { } }); }); + +describe('getAllPaymentMethods', () => { + it('should call getAllPaymentMethods with the correct parameters', async () => { + const options: MethodsListParams = { + locale: Locale.de_DE, + include: MethodInclude.pricing, + }; + + const queryParams = new URLSearchParams(options as any).toString(); + + const getAllPaymentMethodsEndpoint = `https://api.mollie.com/v2/methods/all?${queryParams as any}`; + const headers = { + 'Content-Type': 'application/json', + Authorization: `Bearer ${getApiKey()}`, + versionStrings: MOLLIE_VERSION_STRINGS, + }; + + (fetch as unknown as jest.Mock).mockImplementation(async () => + Promise.resolve({ + json: () => Promise.resolve({ data: [] }), + headers: new Headers(), + ok: true, + redirected: false, + status: 201, + statusText: 'OK', + url: '', + }), + ); + + await getAllPaymentMethods(options); + + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenCalledWith(getAllPaymentMethodsEndpoint, { + method: 'GET', + headers, + }); + }); + + it('should be able to return a proper error message when error which is an instance of MollieApiError occurred', async () => { + const errorMessage = 'Something wrong happened'; + const mollieApiError = new MollieApiError(errorMessage, { field: 'validationUrl' }); + + (fetch as unknown as jest.Mock).mockImplementation(async () => { + throw mollieApiError; + }); + + try { + await getAllPaymentMethods({}); + } catch (error: unknown) { + expect(error).toBeInstanceOf(CustomError); + expect(logger.error).toBeCalledTimes(1); + expect(logger.error).toBeCalledWith( + `SCTM - getAllPaymentMethods - Failed to get all payment methods with unknown errors`, + { + error: mollieApiError, + }, + ); + } + }); +}); diff --git a/processor/tests/mollie/profile.mollie.spec.ts b/processor/tests/mollie/profile.mollie.spec.ts index d4f7cda..71955e9 100644 --- a/processor/tests/mollie/profile.mollie.spec.ts +++ b/processor/tests/mollie/profile.mollie.spec.ts @@ -56,4 +56,30 @@ describe('Test profile.mollie.ts', () => { await expect(getProfile()).resolves.toThrow(errorMessage); }); + + it('should return unknown errors when fetching the profile', async () => { + const errorMessage = 'SCTM - getProfile - Failed to get Mollie profile with unknown errors'; + (initMollieClient as jest.Mock).mockReturnValue({ + profiles: { + getCurrent: jest.fn().mockReturnValue(new Error(errorMessage)), + }, + }); + + await expect(getProfile()).resolves.toThrow(errorMessage); + }); + + it('should return MollieApi errors when fetching the profile', async () => { + const errorMessage = `SCTM - getProfile - error: error, field: validationUrl`; + (initMollieClient as jest.Mock).mockReturnValue({ + profiles: { + getCurrent: jest + .fn() + .mockReturnValue( + new MollieApiError('SCTM - getProfile - error: error, field: validationUrl', { field: 'validationUrl' }), + ), + }, + }); + + await expect(getProfile()).resolves.toThrow(errorMessage); + }); }); diff --git a/processor/tests/service/payment.service.spec.ts b/processor/tests/service/payment.service.spec.ts index e51aeb9..11d56f6 100644 --- a/processor/tests/service/payment.service.spec.ts +++ b/processor/tests/service/payment.service.spec.ts @@ -922,6 +922,179 @@ describe('Test listPaymentMethodsByPayment', () => { }); expect(JSON.stringify(response)).toContain('creditcard'); }); + + test('call listPaymentMethodsByPayment w/o custom objects', async () => { + (listPaymentMethods as jest.Mock).mockReturnValueOnce([ + { + resource: 'method', + id: 'paypal', + description: 'PayPal', + minimumAmount: { value: '0.01', currency: 'EUR' }, + maximumAmount: null, + image: { + size1x: 'https://www.mollie.com/external/icons/payment-methods/paypal.png', + size2x: 'https://www.mollie.com/external/icons/payment-methods/paypal%402x.png', + svg: 'https://www.mollie.com/external/icons/payment-methods/paypal.svg', + }, + status: 'activated', + _links: { + self: { + href: 'https://api.mollie.com/v2/methods/paypal', + type: 'application/hal+json', + }, + }, + }, + { + resource: 'method', + id: 'giftcard', + description: 'Geschenkkarten', + minimumAmount: { value: '0.01', currency: 'EUR' }, + maximumAmount: null, + image: { + size1x: 'https://www.mollie.com/external/icons/payment-methods/giftcard.png', + size2x: 'https://www.mollie.com/external/icons/payment-methods/giftcard%402x.png', + svg: 'https://www.mollie.com/external/icons/payment-methods/giftcard.svg', + }, + status: 'activated', + _links: { + self: { + href: 'https://api.mollie.com/v2/methods/giftcard', + type: 'application/hal+json', + }, + }, + }, + { + resource: 'method', + id: 'bancontact', + description: 'Bancontact', + minimumAmount: { value: '0.01', currency: 'EUR' }, + maximumAmount: null, + image: { + size1x: 'https://www.mollie.com/external/icons/payment-methods/bancontact.png', + size2x: 'https://www.mollie.com/external/icons/payment-methods/bancontact%402x.png', + svg: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', + }, + status: 'activated', + _links: { + self: { + href: 'https://api.mollie.com/v2/methods/bancontact', + type: 'application/hal+json', + }, + }, + }, + { + resource: 'method', + id: 'banktransfer', + description: 'Bank transfer', + minimumAmount: { value: '0.01', currency: 'EUR' }, + maximumAmount: null, + image: { + size1x: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.png', + size2x: 'https://www.mollie.com/external/icons/payment-methods/banktransfer%402x.png', + svg: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', + }, + status: 'activated', + _links: { + self: { + href: 'https://api.mollie.com/v2/methods/banktransfer', + type: 'application/hal+json', + }, + }, + }, + ]); + + (getMethodConfigObjects as jest.Mock).mockReturnValueOnce([]); + + mockResource = { + id: 'RANDOMID_12345', + paymentMethodInfo: { + paymentInterface: 'mollie', + method: 'card', + }, + amountPlanned: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 500000, + fractionDigits: 2, + }, + custom: { + fields: { + sctm_payment_methods_request: JSON.stringify({ + locale: 'de_DE', + billingCountry: 'DE', + }), + }, + } as unknown as CustomFields, + } as unknown as Payment; + + const response = await handleListPaymentMethodsByPayment(mockResource); + expect(response).toBeDefined(); + expect(response.statusCode).toBe(200); + expect(response?.actions?.length).toBeGreaterThan(0); + expect(response?.actions?.[0]?.action).toBe('setCustomField'); + expect((response?.actions?.[1] as any)?.value).toBe( + JSON.stringify({ + count: 5, + methods: [ + { + id: 'paypal', + name: { + 'en-GB': 'PayPal', + }, + description: { + 'en-GB': '', + }, + image: 'https://www.mollie.com/external/icons/payment-methods/paypal.svg', + order: 0, + }, + { + id: 'giftcard', + name: { + 'en-GB': 'Geschenkkarten', + }, + description: { + 'en-GB': '', + }, + image: 'https://www.mollie.com/external/icons/payment-methods/giftcard.svg', + order: 0, + }, + { + id: 'bancontact', + name: { + 'en-GB': 'Bancontact', + }, + description: { + 'en-GB': '', + }, + image: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', + order: 0, + }, + { + id: 'banktransfer', + name: { + 'en-GB': 'Bank transfer', + }, + description: { + 'en-GB': '', + }, + image: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', + order: 0, + }, + { + id: 'googlepay', + name: { + 'en-GB': 'Google Pay', + }, + description: { + 'en-GB': '', + }, + image: '', + order: 0, + }, + ], + }), + ); + }); }); describe('Test getCreatePaymentUpdateAction', () => { @@ -1612,6 +1785,219 @@ describe('Test handleCreatePayment', () => { actions: ctActions, }); }); + + it('should return status code and array of actions', async () => { + const CTPayment: Payment = { + id: '5c8b0375-305a-4f19-ae8e-07806b101999', + version: 1, + createdAt: '2024-07-04T14:07:35.625Z', + lastModifiedAt: '2024-07-04T14:07:35.625Z', + amountPlanned: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1000, + fractionDigits: 2, + }, + paymentStatus: {}, + transactions: [ + { + id: '5c8b0375-305a-4f19-ae8e-07806b101999', + type: 'Authorization', + amount: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1000, + fractionDigits: 2, + }, + state: 'Initial', + }, + ], + interfaceInteractions: [], + paymentMethodInfo: { + method: 'googlepay', + }, + custom: { + type: { + typeId: 'type', + id: 'test', + }, + fields: { + sctm_payment_methods_request: JSON.stringify({ + billingCountry: 'DE', + }), + }, + }, + }; + + const molliePayment: molliePayment = { + resource: 'payment', + id: 'tr_7UhSN1zuXS', + amount: { + value: '10.00', + currency: 'EUR', + }, + description: 'Order #12345', + redirectUrl: 'https://webshop.example.org/order/12345/', + webhookUrl: 'https://webshop.example.org/payments/webhook/', + metadata: '{"order_id":12345}', + profileId: 'pfl_QkEhN94Ba', + status: PaymentStatus.open, + isCancelable: false, + createdAt: '2024-03-20T09:13:37+00:00', + expiresAt: '2024-03-20T09:28:37+00:00', + _links: { + self: { + href: '...', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/checkout/select-method/7UhSN1zuXS', + type: 'text/html', + }, + documentation: { + href: '...', + type: 'text/html', + }, + }, + } as molliePayment; + + const customLineItem = { + id: 'custom-line', + key: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }; + + const mockedCart = { + id: 'mocked-cart', + customLineItems: [customLineItem], + } as Cart; + + const methodConfig = { + value: { + pricingConstraints: [ + { + currencyCode: CTPayment.amountPlanned.currencyCode, + countryCode: JSON.parse(CTPayment.custom?.fields?.sctm_payment_methods_request).billingCountry, + surchargeCost: { + percentageAmount: 2, + fixedAmount: 10, + }, + }, + ], + }, + }; + + const appUtils = require('../../src/utils/app.utils'); + + jest.spyOn(appUtils, 'calculateTotalSurchargeAmount'); + + const mapUtils = require('../../src/utils/map.utils'); + + jest.spyOn(mapUtils, 'createCartUpdateActions'); + + (getCartFromPayment as jest.Mock).mockReturnValue(mockedCart); + (getSingleMethodConfigObject as jest.Mock).mockReturnValueOnce(methodConfig); + (createMolliePayment as jest.Mock).mockReturnValueOnce(molliePayment); + (getPaymentExtension as jest.Mock).mockReturnValueOnce({ + destination: { + url: 'https://example.com', + }, + }); + + (createMollieCreatePaymentParams as jest.Mock).mockReturnValueOnce({ + method: 'googlepay', + }); + + (changeTransactionState as jest.Mock).mockReturnValueOnce({ + action: 'changeTransactionState', + state: 'Pending', + transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', + }); + + (updateCart as jest.Mock).mockReturnValue(mockedCart); + + const totalSurchargeAmount = 1020; + + const actual = await handleCreatePayment(CTPayment); + + const expectedCartUpdateActions = [ + { + action: 'removeCustomLineItem', + customLineItemId: customLineItem.id, + }, + { + action: 'addCustomLineItem', + name: { + de: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + en: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }, + quantity: 1, + money: { + centAmount: totalSurchargeAmount, + currencyCode: CTPayment.amountPlanned.currencyCode, + }, + slug: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }, + ]; + + expect(getSingleMethodConfigObject).toHaveBeenCalledWith(CTPayment.paymentMethodInfo.method); + expect(calculateTotalSurchargeAmount).toHaveBeenCalledTimes(1); + expect(calculateTotalSurchargeAmount).toHaveBeenCalledWith( + CTPayment, + methodConfig.value.pricingConstraints[0].surchargeCost, + ); + expect(calculateTotalSurchargeAmount).toHaveReturnedWith( + totalSurchargeAmount / Math.pow(10, CTPayment.amountPlanned.fractionDigits), + ); + + expect(createCartUpdateActions).toHaveBeenCalledTimes(1); + expect(createCartUpdateActions).toHaveBeenCalledWith(mockedCart, CTPayment, totalSurchargeAmount); + expect(createCartUpdateActions).toHaveReturnedWith(expectedCartUpdateActions); + + const ctActions = [ + { + action: 'addInterfaceInteraction', + type: { key: 'sctm_interface_interaction_type' }, + fields: { + sctm_id: '5c8b0375-305a-4f19-ae8e-07806b101999', + sctm_action_type: 'createPayment', + sctm_created_at: '2024-03-20T09:13:37+00:00', + sctm_request: '{"transactionId":"5c8b0375-305a-4f19-ae8e-07806b101999","paymentMethod":"googlepay"}', + sctm_response: + '{"molliePaymentId":"tr_7UhSN1zuXS","checkoutUrl":"https://www.mollie.com/checkout/select-method/7UhSN1zuXS","transactionId":"5c8b0375-305a-4f19-ae8e-07806b101999"}', + }, + }, + { + action: 'changeTransactionInteractionId', + transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', + interactionId: 'tr_7UhSN1zuXS', + }, + { + action: 'changeTransactionTimestamp', + transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', + timestamp: '2024-03-20T09:13:37+00:00', + }, + { + action: 'changeTransactionState', + transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', + state: 'Pending', + }, + { + action: 'setTransactionCustomType', + type: { + key: 'sctm_transaction_surcharge_cost', + }, + fields: { + surchargeAmountInCent: 1020, + }, + transactionId: CTPayment.transactions[0].id, + }, + ]; + + expect(actual).toEqual({ + statusCode: 201, + actions: ctActions, + }); + }); }); describe('Test handleCreateRefund', () => { diff --git a/processor/tests/types/mollie.types.spec.ts b/processor/tests/types/mollie.types.spec.ts index 0f9ef0c..efb997e 100644 --- a/processor/tests/types/mollie.types.spec.ts +++ b/processor/tests/types/mollie.types.spec.ts @@ -1,5 +1,10 @@ import { describe, test, expect, jest } from '@jest/globals'; -import { ApplePaySessionRequest, ParsedMethodsRequestType } from '../../src/types/mollie.types'; +import { + ApplePaySessionRequest, + CustomPaymentMethod, + ParsedMethodsRequestType, + SupportedPaymentMethods, +} from '../../src/types/mollie.types'; const functions = { isParsedMethodsRequestType: jest.fn((obj: ParsedMethodsRequestType): obj is ParsedMethodsRequestType => { @@ -38,4 +43,22 @@ describe('Test mollie.types.ts', () => { } as ApplePaySessionRequest; expect(functions.isApplePaySessionRequest(mockType)).toBeFalsy(); }); + + test('should return the correct SupportedPaymentMethods', () => { + expect(SupportedPaymentMethods.ideal).toBe('ideal'); + expect(SupportedPaymentMethods.creditcard).toBe('creditcard'); + expect(SupportedPaymentMethods.bancontact).toBe('bancontact'); + expect(SupportedPaymentMethods.banktransfer).toBe('banktransfer'); + expect(SupportedPaymentMethods.przelewy24).toBe('przelewy24'); + expect(SupportedPaymentMethods.kbc).toBe('kbc'); + expect(SupportedPaymentMethods.blik).toBe('blik'); + expect(SupportedPaymentMethods.applepay).toBe('applepay'); + expect(SupportedPaymentMethods.paypal).toBe('paypal'); + expect(SupportedPaymentMethods.giftcard).toBe('giftcard'); + expect(SupportedPaymentMethods.googlepay).toBe('googlepay'); + }); + + test('should return correct custom method', () => { + expect(CustomPaymentMethod.blik).toBe('blik'); + }); }); diff --git a/processor/tests/utils/config.utils.spec.ts b/processor/tests/utils/config.utils.spec.ts index e36231a..b867d89 100644 --- a/processor/tests/utils/config.utils.spec.ts +++ b/processor/tests/utils/config.utils.spec.ts @@ -1,4 +1,4 @@ -import { readConfiguration } from '../../src/utils/config.utils'; +import { readConfiguration, getApiKey } from '../../src/utils/config.utils'; import CustomError from '../../src/errors/custom.error'; import { describe, expect, test } from '@jest/globals'; @@ -87,4 +87,16 @@ describe('Test src/utils/config.utils.ts', () => { process.env.AUTHENTICATION_MODE = 'dummy'; expect(() => readConfiguration()).toThrow(CustomError); }); + + test('getApiKey should return test key', () => { + process.env.CONNECTOR_MODE = 'test'; + const key = getApiKey(); + expect(key).toBe(process.env.MOLLIE_API_TEST_KEY); + }); + + test('getApiKey should return live key', () => { + process.env.CONNECTOR_MODE = 'live'; + const key = getApiKey(); + expect(key).toBe(process.env.MOLLIE_API_LIVE_KEY); + }); });