From 727a6aec43b7becb442ed06fd40225d3baa948d5 Mon Sep 17 00:00:00 2001 From: Win Date: Fri, 2 Aug 2024 18:58:06 +0700 Subject: [PATCH 1/6] MOL-186/PICT-225: add apple pay session request and update response to custom field --- processor/package-lock.json | 76 +------------------ .../customFields.commercetools.ts | 24 ++++++ .../src/controllers/payment.controller.ts | 4 + processor/src/mollie/payment.mollie.ts | 20 +++++ processor/src/service/payment.service.ts | 28 ++++++- processor/src/types/controller.types.ts | 3 +- processor/src/types/mollie.types.ts | 5 ++ processor/src/utils/constant.utils.ts | 7 ++ processor/src/utils/paymentAction.utils.ts | 8 ++ 9 files changed, 99 insertions(+), 76 deletions(-) diff --git a/processor/package-lock.json b/processor/package-lock.json index 84c10dc..5713711 100644 --- a/processor/package-lock.json +++ b/processor/package-lock.json @@ -1,12 +1,12 @@ { "name": "shopmacher-mollie-processor", - "version": "0.0.19", + "version": "0.0.21", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "shopmacher-mollie-processor", - "version": "0.0.19", + "version": "0.0.21", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2037,11 +2037,6 @@ "node": ">=0.10.0" } }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, "node_modules/jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", @@ -2098,18 +2093,6 @@ "integrity": "sha512-xiNMgCuoy4mCL4JTywk9XFs5xpRUcKxtWEcMR6FNMtsgewYTIgIR+nvlP4A4iRCAzRsHMnPhvTRrzp4AGcRTEA==", "dev": true }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -2235,15 +2218,6 @@ "node": ">=10" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, "node_modules/jest-cli": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", @@ -3205,14 +3179,6 @@ "semver": "bin/semver.js" } }, - "node_modules/winston-transport/node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4594,11 +4560,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -4902,18 +4863,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6579,21 +6528,6 @@ "node": ">= 0.6" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ngrok": { "version": "5.0.0-beta.2", "resolved": "https://registry.npmjs.org/ngrok/-/ngrok-5.0.0-beta.2.tgz", @@ -7754,12 +7688,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", diff --git a/processor/src/commercetools/customFields.commercetools.ts b/processor/src/commercetools/customFields.commercetools.ts index 8a08fff..fc39edd 100644 --- a/processor/src/commercetools/customFields.commercetools.ts +++ b/processor/src/commercetools/customFields.commercetools.ts @@ -77,6 +77,30 @@ export async function createCustomPaymentType(): Promise { }, inputHint: 'MultiLine', }, + { + name: CustomFields.applePay.session.request, + label: { + en: 'The request object for inquiring the Apple Pay payment session', + de: 'Das Anfrageobjekt für die Abfrage der Apple Pay-Zahlungssitzung', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: CustomFields.applePay.session.response, + label: { + en: 'The response object holding the Apple Pay payment session', + de: 'Das Antwortobjekt zur Speicherung der Apple Pay-Zahlungssitzung', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, ], }, }) diff --git a/processor/src/controllers/payment.controller.ts b/processor/src/controllers/payment.controller.ts index f6cf203..189d9aa 100644 --- a/processor/src/controllers/payment.controller.ts +++ b/processor/src/controllers/payment.controller.ts @@ -4,6 +4,7 @@ import { handleCancelPayment, handleCreatePayment, handleCreateRefund, + handleGetApplePaySession, handleListPaymentMethodsByPayment, handlePaymentCancelRefund, } from '../service/payment.service'; @@ -56,6 +57,9 @@ export const paymentController = async ( case ConnectorActions.CancelRefund: logger.debug('SCTM - payment processing - paymentController - handlePaymentCancelRefund'); return await handlePaymentCancelRefund(ctPayment); + case ConnectorActions.GetApplePaySession: + logger.debug('SCTM - payment processing - paymentController - getApplePaySession'); + return await handleGetApplePaySession(ctPayment); default: logger.debug('SCTM - payment processing - paymentController - No payment actions matched'); throw new SkipError('No payment actions matched'); diff --git a/processor/src/mollie/payment.mollie.ts b/processor/src/mollie/payment.mollie.ts index 8f4be29..ec64873 100644 --- a/processor/src/mollie/payment.mollie.ts +++ b/processor/src/mollie/payment.mollie.ts @@ -10,6 +10,8 @@ import { import { initMollieClient } from '../client/mollie.client'; import CustomError from '../errors/custom.error'; import { logger } from '../utils/logger.utils'; +import { ApplePaySessionRequest } from '../types/mollie.types'; +import ApplePaySession from '@mollie/api-client/dist/types/src/data/applePaySession/ApplePaySession'; /** * Creates a Mollie payment using the provided payment parameters. @@ -89,3 +91,21 @@ export const cancelPayment = async (paymentId: string): Promise => { throw new CustomError(400, errorMessage); } }; + +export const getApplePaySession = async (options: ApplePaySessionRequest): Promise => { + try { + return await initMollieClient().applePay.requestPaymentSession(options); + } catch (error: unknown) { + let errorMessage; + if (error instanceof MollieApiError) { + errorMessage = `SCTM - getApplePaySession - error: ${error.message}, field: ${error.field}`; + } else { + errorMessage = `SCTM - getApplePaySession - Failed to cancel payments with unknown errors`; + } + + logger.error(errorMessage, { + error, + }); + throw new CustomError(400, errorMessage); + } +}; diff --git a/processor/src/service/payment.service.ts b/processor/src/service/payment.service.ts index f9b185f..63e691b 100644 --- a/processor/src/service/payment.service.ts +++ b/processor/src/service/payment.service.ts @@ -8,7 +8,13 @@ import { } from '../utils/map.utils'; import { CentPrecisionMoney, Extension, Payment, UpdateAction } from '@commercetools/platform-sdk'; import CustomError from '../errors/custom.error'; -import { cancelPayment, createMolliePayment, getPaymentById, listPaymentMethods } from '../mollie/payment.mollie'; +import { + cancelPayment, + createMolliePayment, + getApplePaySession, + getPaymentById, + listPaymentMethods, +} from '../mollie/payment.mollie'; import { AddTransaction, ChangeTransactionState, @@ -43,6 +49,8 @@ import { parseStringToJsonObject } from '../utils/app.utils'; import { getPaymentExtension } from '../commercetools/extensions.commercetools'; import { HttpDestination } from '@commercetools/platform-sdk/dist/declarations/src/generated/models/extension'; import { cancelPaymentRefund, createPaymentRefund, getPaymentRefund } from '../mollie/refund.mollie'; +import { ApplePaySessionRequest } from '../types/mollie.types'; +import ApplePaySession from '@mollie/api-client/dist/types/src/data/applePaySession/ApplePaySession'; /** * Handles listing payment methods by payment. @@ -385,3 +393,21 @@ export const handleCancelPayment = async (ctPayment: Payment): Promise => { + const requestOptions: ApplePaySessionRequest = parseStringToJsonObject( + ctPayment.custom?.fields[CustomFields.applePay.session.request], + CustomFields.applePay.session.request, + 'SCTM - handleGetApplePaySession', + ctPayment.id, + ); + + const session: ApplePaySession = await getApplePaySession(requestOptions); + + const ctActions: UpdateAction[] = [setCustomFields(CustomFields.applePay.session.response, JSON.stringify(session))]; + + return { + statusCode: 200, + actions: ctActions, + }; +}; diff --git a/processor/src/types/controller.types.ts b/processor/src/types/controller.types.ts index 89f815c..e830708 100644 --- a/processor/src/types/controller.types.ts +++ b/processor/src/types/controller.types.ts @@ -11,4 +11,5 @@ export type DeterminePaymentActionType = | 'cancelPayment' | 'createRefund' | 'cancelRefund' - | 'noAction'; + | 'noAction' + | 'getApplePaySession'; diff --git a/processor/src/types/mollie.types.ts b/processor/src/types/mollie.types.ts index 986c681..8e18229 100644 --- a/processor/src/types/mollie.types.ts +++ b/processor/src/types/mollie.types.ts @@ -7,3 +7,8 @@ export type ParsedMethodsRequestType = { pricing?: string; sequenceType?: string; }; + +export type ApplePaySessionRequest = { + domain: string; + validationUrl: string; +}; diff --git a/processor/src/utils/constant.utils.ts b/processor/src/utils/constant.utils.ts index 36fa35e..70327d5 100644 --- a/processor/src/utils/constant.utils.ts +++ b/processor/src/utils/constant.utils.ts @@ -17,6 +17,12 @@ export const CustomFields = { interfaceInteraction: 'sctm_interface_interaction_type', }, paymentCancelReason: 'sctm_payment_cancel_reason', + applePay: { + session: { + request: 'sctm_apple_pay_session_request', + response: 'sctm_apple_pay_session_response', + }, + }, }; export enum ConnectorActions { @@ -26,6 +32,7 @@ export enum ConnectorActions { CreateRefund = 'createRefund', CancelRefund = 'cancelRefund', NoAction = 'noAction', + GetApplePaySession = 'getApplePaySession', } export const ErrorMessages = { diff --git a/processor/src/utils/paymentAction.utils.ts b/processor/src/utils/paymentAction.utils.ts index 0d5b831..c523721 100644 --- a/processor/src/utils/paymentAction.utils.ts +++ b/processor/src/utils/paymentAction.utils.ts @@ -99,6 +99,14 @@ export const determinePaymentAction = (ctPayment?: Payment): DeterminePaymentAct return ConnectorActions.GetPaymentMethods; } + const shouldGetAppleSession = + ctPayment.custom?.fields?.[CustomFields.applePay.session.request] && + ctPayment.custom?.fields?.[CustomFields.applePay.session.request].length > 0; + + if (shouldGetAppleSession) { + return ConnectorActions.GetApplePaySession; + } + const { transactions } = ctPayment; const groups = getTransactionGroups(transactions); From 57da040dd1afbab7131860efc2eca850d926f7f8 Mon Sep 17 00:00:00 2001 From: Win Date: Mon, 5 Aug 2024 16:35:29 +0700 Subject: [PATCH 2/6] MOL-186/PICT-225: add test cases --- processor/src/mollie/payment.mollie.ts | 2 +- .../controllers/payment.controller.spec.ts | 55 +++++++++++++++ processor/tests/mollie/payment.mollie.spec.ts | 70 +++++++++++++++++++ .../tests/service/payment.service.spec.ts | 64 +++++++++++++++++ .../tests/types/controller.types.spec.ts | 6 ++ processor/tests/types/mollie.types.spec.ts | 21 +++++- processor/tests/utils/constant.utils.spec.ts | 37 +++++++++- .../tests/utils/paymentAction.utils.spec.ts | 33 +++++++++ 8 files changed, 285 insertions(+), 3 deletions(-) diff --git a/processor/src/mollie/payment.mollie.ts b/processor/src/mollie/payment.mollie.ts index ec64873..569de0c 100644 --- a/processor/src/mollie/payment.mollie.ts +++ b/processor/src/mollie/payment.mollie.ts @@ -100,7 +100,7 @@ export const getApplePaySession = async (options: ApplePaySessionRequest): Promi if (error instanceof MollieApiError) { errorMessage = `SCTM - getApplePaySession - error: ${error.message}, field: ${error.field}`; } else { - errorMessage = `SCTM - getApplePaySession - Failed to cancel payments with unknown errors`; + errorMessage = `SCTM - getApplePaySession - Failed to get ApplePay session with unknown errors`; } logger.error(errorMessage, { diff --git a/processor/tests/controllers/payment.controller.spec.ts b/processor/tests/controllers/payment.controller.spec.ts index 531b711..8274cdc 100644 --- a/processor/tests/controllers/payment.controller.spec.ts +++ b/processor/tests/controllers/payment.controller.spec.ts @@ -8,6 +8,7 @@ import { handleListPaymentMethodsByPayment, handlePaymentCancelRefund, handleCreateRefund, + handleGetApplePaySession, } from '../../src/service/payment.service'; import { CancelStatusText, ConnectorActions, CustomFields as CustomFieldName } from '../../src/utils/constant.utils'; import { validateCommerceToolsPaymentPayload } from '../../src/validators/payment.validators'; @@ -17,6 +18,7 @@ jest.mock('../../src/service/payment.service', () => ({ handleCreatePayment: jest.fn(), handlePaymentCancelRefund: jest.fn(), handleCreateRefund: jest.fn(), + handleGetApplePaySession: jest.fn(), })); jest.mock('../../src/validators/payment.validators.ts', () => ({ @@ -320,4 +322,57 @@ describe('Test payment.controller.ts', () => { expect(handlePaymentCancelRefund).toBeCalledTimes(1); expect(handlePaymentCancelRefund).toReturnWith(handlePaymentCancelRefundResponse); }); + + test('call handleGetApplePaySession with valid object reference', async () => { + mockAction = 'Create' as string; + mockResource = { + typeId: 'payment', + obj: { + amountPlanned: { + centAmount: 11000, + currencyCode: 'EUR', + fractionDigits: 2, + type: 'centPrecision', + }, + paymentMethodInfo: { + method: 'paypal', + paymentInterface: 'mollie', + }, + custom: { + fields: { + sctm_apple_pay_session_request: + '{"domain":"pay.mywebshop.com","validationUrl":"https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession"}', + }, + } as unknown as CustomFields, + } as unknown as Payment, + } as unknown as PaymentReference; + + const handleGetApplePaySessionResponse = { + statusCode: 200, + actions: [ + { + action: 'setCustomField', + name: 'sctm_apple_pay_session_response', + value: + '{"epochTimestamp":1555507053169,"expiresAt":1555510653169,"merchantSessionIdentifier":"SSH2EAF8AFAEAA94DEEA898162A5DAFD36E_916523AAED1343F5BC5815E12BEE9250AFFDC1A17C46B0DE5A9","nonce":"0206b8db","merchantIdentifier":"BD62FEB196874511C22DB28A9E14A89E3534C93194F73EA417EC566368D391EB","domainName":"pay.example.org","displayName":"Chuck Norris\'s Store","signature":"308006092a864886f7...8cc030ad3000000000000"}', + }, + ], + }; + + (validateCommerceToolsPaymentPayload as jest.Mock).mockImplementationOnce(() => { + return; + }); + + (handleGetApplePaySession as jest.Mock).mockReturnValue(handleGetApplePaySessionResponse); + (determinePaymentAction as jest.Mock).mockReturnValue(ConnectorActions.GetApplePaySession); + + const response = await paymentController(mockAction, mockResource); + expect(response).toBeDefined(); + expect(response.statusCode).toBe(200); + expect(response?.actions?.length).toBeGreaterThan(0); + expect(response?.actions?.[0].action).toBe('setCustomField'); + expect(handleGetApplePaySession).toBeCalledTimes(1); + expect(handleGetApplePaySession).toReturnWith(handleGetApplePaySessionResponse); + expect(handleCreatePayment).toBeCalledTimes(0); + }); }); diff --git a/processor/tests/mollie/payment.mollie.spec.ts b/processor/tests/mollie/payment.mollie.spec.ts index 1ddf03f..b1e1837 100644 --- a/processor/tests/mollie/payment.mollie.spec.ts +++ b/processor/tests/mollie/payment.mollie.spec.ts @@ -4,6 +4,7 @@ import { createMolliePayment, getPaymentById, listPaymentMethods, + getApplePaySession, } from '../../src/mollie/payment.mollie'; import { MollieApiError, PaymentCreateParams } from '@mollie/api-client'; import { logger } from '../../src/utils/logger.utils'; @@ -13,6 +14,7 @@ const mockPaymentsCreate = jest.fn(); const mockPaymentsGet = jest.fn(); const mockPaymentsList = jest.fn(); const mockPaymentCancel = jest.fn(); +const mockRequestPaymentSession = jest.fn(); jest.mock('../../src/client/mollie.client', () => ({ initMollieClient: jest.fn(() => ({ @@ -24,6 +26,9 @@ jest.mock('../../src/client/mollie.client', () => ({ methods: { list: mockPaymentsList, }, + applePay: { + requestPaymentSession: mockRequestPaymentSession, + }, })), })); @@ -243,3 +248,68 @@ describe('cancelPayment', () => { } }); }); + +describe('getApplePaySession', () => { + afterEach(() => { + jest.clearAllMocks(); // Clear all mocks after each test + }); + + it('should call getApplePaySession with the correct parameters', async () => { + await getApplePaySession({ + domain: 'pay.mywebshop.com', + validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', + }); + + expect(mockRequestPaymentSession).toHaveBeenCalledTimes(1); + expect(mockRequestPaymentSession).toHaveBeenCalledWith({ + domain: 'pay.mywebshop.com', + validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', + }); + }); + + 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' }); + + (mockRequestPaymentSession as jest.Mock).mockImplementation(() => { + throw mollieApiError; + }); + + try { + await getApplePaySession({ + domain: 'pay.mywebshop.com', + validationUrl: '', + }); + } catch (error: unknown) { + expect(error).toBeInstanceOf(CustomError); + expect(logger.error).toBeCalledTimes(1); + expect(logger.error).toBeCalledWith(`SCTM - getApplePaySession - error: ${errorMessage}, field: validationUrl`, { + error: mollieApiError, + }); + } + }); + + it('should be able to return a proper error message when error which is not an instance of MollieApiError occurred', async () => { + const unexpectedError = new CustomError(400, 'dummy message'); + + (mockRequestPaymentSession as jest.Mock).mockImplementation(() => { + throw unexpectedError; + }); + + try { + await getApplePaySession({ + domain: '', + validationUrl: '', + }); + } catch (error: unknown) { + expect(error).toBeInstanceOf(CustomError); + expect(logger.error).toBeCalledTimes(1); + expect(logger.error).toBeCalledWith( + 'SCTM - getApplePaySession - Failed to get ApplePay session with unknown errors', + { + error: unexpectedError, + }, + ); + } + }); +}); diff --git a/processor/tests/service/payment.service.spec.ts b/processor/tests/service/payment.service.spec.ts index 7b6290b..f7ba571 100644 --- a/processor/tests/service/payment.service.spec.ts +++ b/processor/tests/service/payment.service.spec.ts @@ -9,6 +9,7 @@ import { handleCreateRefund, handlePaymentCancelRefund, handleCancelPayment, + handleGetApplePaySession, } from '../../src/service/payment.service'; import { ControllerResponseType } from '../../src/types/controller.types'; import { CancelStatusText, ConnectorActions, CustomFields as CustomFieldName } from '../../src/utils/constant.utils'; @@ -19,6 +20,7 @@ import { getPaymentById, createMolliePayment, cancelPayment, + getApplePaySession, } from '../../src/mollie/payment.mollie'; import { cancelPaymentRefund, createPaymentRefund, getPaymentRefund } from '../../src/mollie/refund.mollie'; import CustomError from '../../src/errors/custom.error'; @@ -53,6 +55,7 @@ jest.mock('../../src/mollie/payment.mollie', () => ({ getPaymentById: jest.fn(), getPaymentRefund: jest.fn(), cancelPayment: jest.fn(), + getApplePaySession: jest.fn(), })); jest.mock('../../src/mollie/refund.mollie', () => ({ @@ -1265,3 +1268,64 @@ describe('Test handleCancelPayment', () => { }); }); }); + +describe('Test handleGetApplePaySession', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should handle with update action', 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: {}, + interfaceInteractions: [], + paymentMethodInfo: { + method: 'applepay', + }, + transactions: [], + custom: { + type: { + typeId: 'type', + key: 'sctm-payment-custom-type', + }, + fields: { + sctm_apple_pay_session_request: JSON.stringify({ + domain: 'pay.mywebshop.com', + validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', + }), + }, + } as unknown as CustomFields, + }; + + (getApplePaySession as jest.Mock).mockImplementationOnce(() => { + return { + domain: 'pay.mywebshop.com', + validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', + }; + }); + const response = await handleGetApplePaySession(CTPayment); + + expect(response.statusCode).toBe(200); + expect(response.actions).toEqual([ + { + action: 'setCustomField', + name: 'sctm_apple_pay_session_response', + value: + '{"domain":"pay.mywebshop.com","validationUrl":"https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession"}', + }, + ]); + }); +}); diff --git a/processor/tests/types/controller.types.spec.ts b/processor/tests/types/controller.types.spec.ts index b0ebcc9..b82aaa2 100644 --- a/processor/tests/types/controller.types.spec.ts +++ b/processor/tests/types/controller.types.spec.ts @@ -32,5 +32,11 @@ describe('Test controller.types.ts', () => { test('should return the correct {DeterminePaymentActionType} type declaration', () => { expect(functions.isDeterminePaymentActionType('getPaymentMethods')).toBeTruthy(); + expect(functions.isDeterminePaymentActionType('createPayment')).toBeTruthy(); + expect(functions.isDeterminePaymentActionType('cancelPayment')).toBeTruthy(); + expect(functions.isDeterminePaymentActionType('createRefund')).toBeTruthy(); + expect(functions.isDeterminePaymentActionType('cancelRefund')).toBeTruthy(); + expect(functions.isDeterminePaymentActionType('noAction')).toBeTruthy(); + expect(functions.isDeterminePaymentActionType('getApplePaySession')).toBeTruthy(); }); }); diff --git a/processor/tests/types/mollie.types.spec.ts b/processor/tests/types/mollie.types.spec.ts index d78cab0..0f9ef0c 100644 --- a/processor/tests/types/mollie.types.spec.ts +++ b/processor/tests/types/mollie.types.spec.ts @@ -1,10 +1,14 @@ import { describe, test, expect, jest } from '@jest/globals'; -import { ParsedMethodsRequestType } from '../../src/types/mollie.types'; +import { ApplePaySessionRequest, ParsedMethodsRequestType } from '../../src/types/mollie.types'; const functions = { isParsedMethodsRequestType: jest.fn((obj: ParsedMethodsRequestType): obj is ParsedMethodsRequestType => { return typeof obj?.locale === 'string'; }), + + isApplePaySessionRequest: jest.fn((obj: ApplePaySessionRequest): obj is ApplePaySessionRequest => { + return typeof obj?.domain === 'string' && typeof obj?.validationUrl === 'string'; + }), }; describe('Test mollie.types.ts', () => { @@ -19,4 +23,19 @@ describe('Test mollie.types.ts', () => { const mockType = 'Not a correct type' as ParsedMethodsRequestType; expect(functions.isParsedMethodsRequestType(mockType)).toBeFalsy(); }); + + test('should return the correct {ApplePaySessionRequest} type declaration', () => { + const mockType = { + domain: 'pay.mywebshop.com', + validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', + } as ApplePaySessionRequest; + expect(functions.isApplePaySessionRequest(mockType)).toBeTruthy(); + }); + + test('should return the incorrect {ApplePaySessionRequest} type declaration', () => { + const mockType = { + domain: 'pay.mywebshop.com', + } as ApplePaySessionRequest; + expect(functions.isApplePaySessionRequest(mockType)).toBeFalsy(); + }); }); diff --git a/processor/tests/utils/constant.utils.spec.ts b/processor/tests/utils/constant.utils.spec.ts index d8623d2..77654d0 100644 --- a/processor/tests/utils/constant.utils.spec.ts +++ b/processor/tests/utils/constant.utils.spec.ts @@ -1,13 +1,41 @@ import { describe, test, expect } from '@jest/globals'; -import { CustomFields, ConnectorActions, ErrorMessages, CancelStatusText } from '../../src/utils/constant.utils'; +import { + CustomFields, + ConnectorActions, + ErrorMessages, + CancelStatusText, + LIBRARY_NAME, + LIBRARY_VERSION, + PAY_LATER_ENUMS, +} from '../../src/utils/constant.utils'; +import { version } from '../../package.json'; describe('Test constant.utils.ts', () => { + test('should return the correct {LIBRARY_NAME} constant', () => { + expect(LIBRARY_NAME).toBeDefined(); + expect(LIBRARY_NAME).toBe('ShopmacherCommercetoolsMollieConnector'); + }); + + test('should return the correct {LIBRARY_VERSION} constant', () => { + expect(LIBRARY_VERSION).toBeDefined(); + expect(LIBRARY_VERSION).toBe(version); + }); + test('should return the correct {CustomFields} constant', () => { expect(CustomFields).toBeDefined(); expect(CustomFields?.payment).toBeDefined(); expect(CustomFields?.payment?.request).toBeDefined(); expect(CustomFields?.payment?.response).toBeDefined(); expect(CustomFields?.payment?.error).toBeDefined(); + expect(CustomFields?.payment?.profileId).toBeDefined(); + + expect(CustomFields?.createPayment?.request).toBeDefined(); + expect(CustomFields?.createPayment?.interfaceInteraction).toBeDefined(); + + expect(CustomFields?.paymentCancelReason).toBeDefined(); + + expect(CustomFields?.applePay?.session?.request).toBeDefined(); + expect(CustomFields?.applePay?.session?.response).toBeDefined(); }); test('should return the correct {ErrorMessages} constant', () => { @@ -26,10 +54,17 @@ describe('Test constant.utils.ts', () => { expect(ConnectorActions?.CreatePayment).toBe('createPayment'); expect(ConnectorActions?.CancelRefund).toBe('cancelRefund'); expect(ConnectorActions?.NoAction).toBe('noAction'); + expect(ConnectorActions?.GetApplePaySession).toBe('getApplePaySession'); }); test('should return the correct {CancelStatusText} constant', () => { expect(CancelStatusText).toBeDefined(); expect(CancelStatusText).toBe('Cancelled from shop side'); }); + + test('should return the correct {PAY_LATER_ENUMS} constant', () => { + expect(PAY_LATER_ENUMS).toBeDefined(); + expect(PAY_LATER_ENUMS).toContain('klarnapaylater'); + expect(PAY_LATER_ENUMS).toContain('klarnasliceit'); + }); }); diff --git a/processor/tests/utils/paymentAction.utils.spec.ts b/processor/tests/utils/paymentAction.utils.spec.ts index 1080e5d..5ca78bb 100644 --- a/processor/tests/utils/paymentAction.utils.spec.ts +++ b/processor/tests/utils/paymentAction.utils.spec.ts @@ -337,6 +337,39 @@ describe('determinePaymentAction', () => { expectedConnectorAction: ConnectorActions.CancelRefund, expectedErrorMessage: '', }, + { + CTPayment: { + id: '5c8b0375-305a-4f19-ae8e-07806b101999', + key: 'get-apple-pay-session-case', + 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: [], + interfaceInteractions: [], + paymentMethodInfo: {}, + custom: { + type: { + typeId: 'type', + id: 'sctm_apple_pay_session_request', + }, + fields: { + sctm_apple_pay_session_request: JSON.stringify({ + domain: 'pay.mywebshop.com', + validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', + }), + }, + } as unknown as CustomFields, + } as Payment, + expectedConnectorAction: ConnectorActions.GetApplePaySession, + expectedErrorMessage: '', + }, ]; it.each(dataSet)( From ffa5aaca7ab32f3adfac038dc01b0dc5d13b7745 Mon Sep 17 00:00:00 2001 From: Win Date: Thu, 8 Aug 2024 14:59:22 +0700 Subject: [PATCH 3/6] MOL-263/PICT-236: update add live key --- connect.yaml | 11 ++++-- processor/.env.example | 4 ++- processor/.env.jest | 4 ++- processor/src/client/mollie.client.ts | 9 +++-- processor/src/mollie/payment.mollie.ts | 10 +++--- processor/src/types/index.types.ts | 4 ++- processor/src/utils/config.utils.ts | 11 +++++- processor/src/utils/constant.utils.ts | 2 ++ processor/src/validators/env.validators.ts | 25 ++++++++++++-- processor/tests/client/mollie.client.spec.ts | 22 +++--------- .../middleware/userAgent.middleware.spec.ts | 4 +-- processor/tests/mollie/payment.mollie.spec.ts | 20 +++++------ processor/tests/utils/config.utils.spec.ts | 18 ++++++++-- .../tests/validators/env.validators.spec.ts | 34 ++++++++++++++++--- .../validators/helpers.validators.spec.ts | 8 +++-- 15 files changed, 127 insertions(+), 59 deletions(-) diff --git a/connect.yaml b/connect.yaml index 5031db7..42bed82 100644 --- a/connect.yaml +++ b/connect.yaml @@ -15,13 +15,20 @@ deployAs: description: Enable Mollie cart component (0 or 1) required: false default: "0" + - key: CONNECTOR_MODE + description: Either test or live + required: true + default: "test" - key: DEBUG description: Debug mode (0 or 1) required: false default: "0" securedConfiguration: - - key: MOLLIE_API_KEY - description: Mollie PSP API key + - key: MOLLIE_API_TEST_KEY + description: Mollie PSP test API key + required: true + - key: MOLLIE_API_LIVE_KEY + description: Mollie PSP live API key required: true - key: MOLLIE_PROFILE_ID description: Mollie PSP profile ID diff --git a/processor/.env.example b/processor/.env.example index f91a487..2f84405 100644 --- a/processor/.env.example +++ b/processor/.env.example @@ -11,7 +11,9 @@ CTP_API_URL=https://api..commercetools.com ## Mollie PSP credentials DEBUG= ## Either 1 for enable or 0 for disable -MOLLIE_API_KEY= +CONNECTOR_MODE= ## Either test or live +MOLLIE_API_TEST_KEY= +MOLLIE_API_LIVE_KEY= MOLLIE_PROFILE_ID= MOLLIE_CARD_COMPONENT=0 ## Either 1 for enable or 0 for disable diff --git a/processor/.env.jest b/processor/.env.jest index e590678..610d232 100644 --- a/processor/.env.jest +++ b/processor/.env.jest @@ -6,9 +6,11 @@ CTP_PROJECT_KEY=TEST CTP_SCOPE=TEST CTP_REGION=europe-west1.gcp ## MOLLIE vars -MOLLIE_API_KEY=12345678901234567890123456789012 MOLLIE_PROFILE_ID=pfl_12345 DEBUG=0 MOLLIE_CARD_COMPONENT=0 +CONNECTOR_MODE=test ## Either test or live +MOLLIE_API_TEST_KEY=test_12345 +MOLLIE_API_LIVE_KEY=live_12345 CONNECT_SERVICE_URL=http://localhost:3000/processor diff --git a/processor/src/client/mollie.client.ts b/processor/src/client/mollie.client.ts index aa0883e..75aaec0 100644 --- a/processor/src/client/mollie.client.ts +++ b/processor/src/client/mollie.client.ts @@ -1,6 +1,6 @@ import createMollieClient, { MollieClient } from '@mollie/api-client'; -import { readConfiguration } from '../utils/config.utils'; -import { LIBRARY_NAME, LIBRARY_VERSION } from '../utils/constant.utils'; +import { getApiKey } from '../utils/config.utils'; +import { VERSION_STRING } from '../utils/constant.utils'; /** * Initializes the Mollie client using the API key from the configuration. @@ -8,9 +8,8 @@ import { LIBRARY_NAME, LIBRARY_VERSION } from '../utils/constant.utils'; * @return {MollieClient} Returns the initialized Mollie client. */ export const initMollieClient = (): MollieClient => { - const { mollie } = readConfiguration(); return createMollieClient({ - apiKey: mollie.apiKey, - versionStrings: `${LIBRARY_NAME}/${LIBRARY_VERSION}`, + apiKey: getApiKey(), + versionStrings: `${VERSION_STRING}`, }); }; diff --git a/processor/src/mollie/payment.mollie.ts b/processor/src/mollie/payment.mollie.ts index 7c41bbe..6c3c00e 100644 --- a/processor/src/mollie/payment.mollie.ts +++ b/processor/src/mollie/payment.mollie.ts @@ -10,8 +10,8 @@ import { import { initMollieClient } from '../client/mollie.client'; import CustomError from '../errors/custom.error'; import { logger } from '../utils/logger.utils'; -import { readConfiguration } from '../utils/config.utils'; -import { LIBRARY_NAME, LIBRARY_VERSION } from '../utils/constant.utils'; +import { getApiKey } from '../utils/config.utils'; +import { VERSION_STRING } from '../utils/constant.utils'; import { CustomPayment } from '../types/mollie.types'; import fetch from 'node-fetch'; @@ -107,12 +107,10 @@ export const createPaymentWithCustomMethod = async (paymentParams: PaymentCreate let errorMessage; try { - const { mollie } = readConfiguration(); - const headers = { 'Content-Type': 'application/json', - Authorization: `Bearer ${mollie.apiKey}`, - versionStrings: `${LIBRARY_NAME}/${LIBRARY_VERSION}`, + Authorization: `Bearer ${getApiKey()}`, + versionStrings: `${VERSION_STRING}`, }; const response = await fetch('https://api.mollie.com/v2/payments', { diff --git a/processor/src/types/index.types.ts b/processor/src/types/index.types.ts index a0a8320..0ef36da 100644 --- a/processor/src/types/index.types.ts +++ b/processor/src/types/index.types.ts @@ -23,7 +23,9 @@ export type ConnectorEnvVars = { region: string; }; mollie: { - apiKey: string; + testApiKey: string; + liveApiKey: string; + mode: string; profileId: string; debug: string; cardComponent: string; diff --git a/processor/src/utils/config.utils.ts b/processor/src/utils/config.utils.ts index 08cd5f7..871029e 100644 --- a/processor/src/utils/config.utils.ts +++ b/processor/src/utils/config.utils.ts @@ -18,7 +18,9 @@ export const readConfiguration = () => { region: process.env.CTP_REGION as string, }, mollie: { - apiKey: process.env.MOLLIE_API_KEY as string, + testApiKey: process.env.MOLLIE_API_TEST_KEY as string, + liveApiKey: process.env.MOLLIE_API_LIVE_KEY as string, + mode: process.env.CONNECTOR_MODE as string, debug: process.env.DEBUG as string, profileId: process.env.MOLLIE_PROFILE_ID as string, cardComponent: process.env.MOLLIE_CARD_COMPONENT as string, @@ -36,3 +38,10 @@ export const readConfiguration = () => { return envVars; }; + +export const getApiKey = (): string => { + if (process.env.CONNECTOR_MODE === 'test') { + return process.env.MOLLIE_API_TEST_KEY as string; + } + return process.env.MOLLIE_API_LIVE_KEY as string; +}; diff --git a/processor/src/utils/constant.utils.ts b/processor/src/utils/constant.utils.ts index 36fa35e..37a9fc9 100644 --- a/processor/src/utils/constant.utils.ts +++ b/processor/src/utils/constant.utils.ts @@ -5,6 +5,8 @@ export const LIBRARY_NAME = 'ShopmacherCommercetoolsMollieConnector'; export const LIBRARY_VERSION = PACKAGE_VERSION; +export const VERSION_STRING = `${LIBRARY_NAME}/${LIBRARY_VERSION}`; + export const CustomFields = { payment: { error: 'sctm_payment_methods_error', diff --git a/processor/src/validators/env.validators.ts b/processor/src/validators/env.validators.ts index 6e6c124..09967ae 100644 --- a/processor/src/validators/env.validators.ts +++ b/processor/src/validators/env.validators.ts @@ -46,9 +46,15 @@ const envValidators = [ referencedBy: 'environmentVariables', }), - standardKey(['mollie', 'apiKey'], { - code: 'InvalidMollieApiKey', - message: 'Mollie API key should be a valid string.', + standardKey(['mollie', 'testApiKey'], { + code: 'InvalidMollieTestApiKey', + message: 'Mollie test API key should be a valid string.', + referencedBy: 'environmentVariables', + }), + + standardKey(['mollie', 'liveApiKey'], { + code: 'InvalidMollieLiveApiKey', + message: 'Mollie live API key should be a valid string.', referencedBy: 'environmentVariables', }), @@ -83,6 +89,19 @@ const envValidators = [ max: 1, }, ), + + standardString( + ['mollie', 'mode'], + { + code: 'InvalidMode', + message: 'Mode should be a valid string of either "test" or "live".', + referencedBy: 'environmentVariables', + }, + { + min: 1, + max: 4, + }, + ), ]; export default envValidators; diff --git a/processor/tests/client/mollie.client.spec.ts b/processor/tests/client/mollie.client.spec.ts index b03fdcf..1d01417 100644 --- a/processor/tests/client/mollie.client.spec.ts +++ b/processor/tests/client/mollie.client.spec.ts @@ -1,4 +1,4 @@ -import { readConfiguration } from '../../src/utils/config.utils'; +import { getApiKey } from '../../src/utils/config.utils'; import { initMollieClient } from '../../src/client/mollie.client'; import createMollieClient, { MollieClient } from '@mollie/api-client'; import { describe, jest, expect, it } from '@jest/globals'; @@ -13,33 +13,21 @@ jest.mock('@mollie/api-client', () => ({ })); jest.mock('../../src/utils/config.utils', () => ({ - readConfiguration: jest.fn(() => { - return { - mollie: { - apiKey: 'test-api-key', - environment: 'test', - }, - }; - }), + getApiKey: jest.fn(), })); describe('Test mollie.client.ts', () => { - const mockReadConfiguration = readConfiguration as jest.Mock; const mockCreateMollieClient = createMollieClient as jest.Mock; + const mockGetApiKey = getApiKey as jest.Mock; let mockMollieClient: MollieClient; it('should initialize and return a MollieClient instance with the correct API key', () => { mockCreateMollieClient.mockReturnValue(mockMollieClient); - mockReadConfiguration.mockReturnValue({ - mollie: { - apiKey: 'test-api-key', - environment: 'test', - }, - }); + mockGetApiKey.mockReturnValue('test-api-key'); const client = initMollieClient(); - expect(mockReadConfiguration).toHaveBeenCalled(); + expect(mockGetApiKey).toHaveBeenCalled(); expect(mockCreateMollieClient).toHaveBeenCalledWith({ apiKey: 'test-api-key', versionStrings: `${LIBRARY_NAME}/${LIBRARY_VERSION}`, diff --git a/processor/tests/middleware/userAgent.middleware.spec.ts b/processor/tests/middleware/userAgent.middleware.spec.ts index 8d9a165..6dd62c7 100644 --- a/processor/tests/middleware/userAgent.middleware.spec.ts +++ b/processor/tests/middleware/userAgent.middleware.spec.ts @@ -1,11 +1,11 @@ import { describe, it, expect } from '@jest/globals'; import { userAgentMiddlewareOptions } from '../../src/middleware/userAgent.middleware'; -import { LIBRARY_NAME, LIBRARY_VERSION } from '../../src/utils/constant.utils'; +import { VERSION_STRING } from '../../src/utils/constant.utils'; describe('Test userAgent.middleware.ts', () => { it('should return correct version string', () => { expect(`${userAgentMiddlewareOptions.libraryName}/${userAgentMiddlewareOptions.libraryVersion}`).toEqual( - `${LIBRARY_NAME}/${LIBRARY_VERSION}`, + `${VERSION_STRING}`, ); }); }); diff --git a/processor/tests/mollie/payment.mollie.spec.ts b/processor/tests/mollie/payment.mollie.spec.ts index 2f26a25..d8949ea 100644 --- a/processor/tests/mollie/payment.mollie.spec.ts +++ b/processor/tests/mollie/payment.mollie.spec.ts @@ -9,8 +9,8 @@ import { import { MollieApiError, PaymentCreateParams } from '@mollie/api-client'; import { logger } from '../../src/utils/logger.utils'; import CustomError from '../../src/errors/custom.error'; -import { LIBRARY_NAME, LIBRARY_VERSION } from '../../src/utils/constant.utils'; -import { readConfiguration } from '../../src/utils/config.utils'; +import { VERSION_STRING } from '../../src/utils/constant.utils'; +import { getApiKey } from '../../src/utils/config.utils'; import fetch from 'node-fetch'; const mockPaymentsCreate = jest.fn(); @@ -128,8 +128,8 @@ describe('createPaymentWithCustomMethod', () => { const createPaymentEndpoint = 'https://api.mollie.com/v2/payments'; const headers = { 'Content-Type': 'application/json', - Authorization: `Bearer ${readConfiguration().mollie.apiKey}`, - versionStrings: `${LIBRARY_NAME}/${LIBRARY_VERSION}`, + Authorization: `Bearer ${getApiKey()}`, + versionStrings: `${VERSION_STRING}`, }; (fetch as unknown as jest.Mock).mockImplementation(async () => @@ -166,8 +166,8 @@ describe('createPaymentWithCustomMethod', () => { const createPaymentEndpoint = 'https://api.mollie.com/v2/payments'; const headers = { 'Content-Type': 'application/json', - Authorization: `Bearer ${readConfiguration().mollie.apiKey}`, - versionStrings: `${LIBRARY_NAME}/${LIBRARY_VERSION}`, + Authorization: `Bearer ${getApiKey()}`, + versionStrings: `${VERSION_STRING}`, }; const response = { @@ -231,8 +231,8 @@ describe('createPaymentWithCustomMethod', () => { const createPaymentEndpoint = 'https://api.mollie.com/v2/payments'; const headers = { 'Content-Type': 'application/json', - Authorization: `Bearer ${readConfiguration().mollie.apiKey}`, - versionStrings: `${LIBRARY_NAME}/${LIBRARY_VERSION}`, + Authorization: `Bearer ${getApiKey()}`, + versionStrings: `${VERSION_STRING}`, }; const response = { @@ -287,8 +287,8 @@ describe('createPaymentWithCustomMethod', () => { const createPaymentEndpoint = 'https://api.mollie.com/v2/payments'; const headers = { 'Content-Type': 'application/json', - Authorization: `Bearer ${readConfiguration().mollie.apiKey}`, - versionStrings: `${LIBRARY_NAME}/${LIBRARY_VERSION}`, + Authorization: `Bearer ${getApiKey()}`, + versionStrings: `${VERSION_STRING}`, }; const errorMessage = 'SCTM - createPaymentWithCustomMethod - Failed to create a payment with unknown errors'; diff --git a/processor/tests/utils/config.utils.spec.ts b/processor/tests/utils/config.utils.spec.ts index 06f26b3..9a71f63 100644 --- a/processor/tests/utils/config.utils.spec.ts +++ b/processor/tests/utils/config.utils.spec.ts @@ -14,7 +14,9 @@ describe('Test src/utils/config.utils.ts', () => { region: process.env.CTP_REGION, }, mollie: { - apiKey: process.env.MOLLIE_API_KEY, + liveApiKey: process.env.MOLLIE_API_LIVE_KEY, + testApiKey: process.env.MOLLIE_API_TEST_KEY, + mode: process.env.CONNECTOR_MODE, debug: process.env.DEBUG, profileId: process.env.MOLLIE_PROFILE_ID, cardComponent: process.env.MOLLIE_CARD_COMPONENT, @@ -47,8 +49,13 @@ describe('Test src/utils/config.utils.ts', () => { expect(() => readConfiguration()).toThrow(CustomError); }); - test('should throw an error when MOLLIE_API_KEY is not defined', () => { - delete process.env.MOLLIE_API_KEY; + test('should throw an error when MOLLIE_API_LIVE_KEY is not defined', () => { + delete process.env.MOLLIE_API_LIVE_KEY; + expect(() => readConfiguration()).toThrow(CustomError); + }); + + test('should throw an error when MOLLIE_API_TEST_KEY is not defined', () => { + delete process.env.MOLLIE_API_TEST_KEY; expect(() => readConfiguration()).toThrow(CustomError); }); @@ -61,4 +68,9 @@ describe('Test src/utils/config.utils.ts', () => { delete process.env.MOLLIE_CARD_COMPONENT; expect(() => readConfiguration()).toThrow(CustomError); }); + + test('should throw an error when CONNECTOR_MODE is not defined', () => { + delete process.env.CONNECTOR_MODE; + expect(() => readConfiguration()).toThrow(CustomError); + }); }); diff --git a/processor/tests/validators/env.validators.spec.ts b/processor/tests/validators/env.validators.spec.ts index 444b752..4dcdfa7 100644 --- a/processor/tests/validators/env.validators.spec.ts +++ b/processor/tests/validators/env.validators.spec.ts @@ -67,10 +67,10 @@ describe('Test env.validators.ts', () => { index1: 5, index2: 0, field1: 'mollie', - field2: 'apiKey', + field2: 'testApiKey', error: { - code: 'InvalidMollieApiKey', - message: 'Mollie API key should be a valid string.', + code: 'InvalidMollieTestApiKey', + message: 'Mollie test API key should be a valid string.', referencedBy: 'environmentVariables', }, condition: undefined, @@ -79,6 +79,18 @@ describe('Test env.validators.ts', () => { index1: 6, index2: 0, field1: 'mollie', + field2: 'liveApiKey', + error: { + code: 'InvalidMollieLiveApiKey', + message: 'Mollie live API key should be a valid string.', + referencedBy: 'environmentVariables', + }, + condition: undefined, + }, + { + index1: 7, + index2: 0, + field1: 'mollie', field2: 'profileId', error: { code: 'InvalidMollieProfileId', @@ -88,7 +100,7 @@ describe('Test env.validators.ts', () => { condition: undefined, }, { - index1: 7, + index1: 8, index2: 0, field1: 'mollie', field2: 'debug', @@ -100,7 +112,7 @@ describe('Test env.validators.ts', () => { condition: { min: 1, max: 1 }, }, { - index1: 8, + index1: 9, index2: 0, field1: 'mollie', field2: 'cardComponent', @@ -111,6 +123,18 @@ describe('Test env.validators.ts', () => { }, condition: { min: 1, max: 1 }, }, + { + index1: 10, + index2: 0, + field1: 'mollie', + field2: 'mode', + error: { + code: 'InvalidMode', + message: 'Mode should be a valid string of either "test" or "live".', + referencedBy: 'environmentVariables', + }, + condition: { min: 1, max: 4 }, + }, ])( 'should return the correct validation array contains [%s, %s]', async ({ index1, index2, field1, field2, error, condition }) => { diff --git a/processor/tests/validators/helpers.validators.spec.ts b/processor/tests/validators/helpers.validators.spec.ts index b33c541..aa624c2 100644 --- a/processor/tests/validators/helpers.validators.spec.ts +++ b/processor/tests/validators/helpers.validators.spec.ts @@ -233,7 +233,9 @@ describe('Test helpers.validators.ts', () => { region: process.env.CTP_REGION as string, }, mollie: { - apiKey: process.env.MOLLIE_API_KEY as string, + liveApiKey: process.env.MOLLIE_API_LIVE_KEY as string, + testApiKey: process.env.MOLLIE_API_TEST_KEY as string, + mode: process.env.CONNECTOR_MODE as string, debug: (process.env.DEBUG ?? '0') as string, profileId: process.env.MOLLIE_PROFILE_ID as string, cardComponent: (process.env.MOLLIE_CARD_COMPONENT ?? '0') as string, @@ -254,7 +256,9 @@ describe('Test helpers.validators.ts', () => { region: process.env.CTP_REGION as string, }, mollie: { - apiKey: process.env.MOLLIE_API_KEY as string, + liveApiKey: process.env.MOLLIE_API_LIVE_KEY as string, + testApiKey: process.env.MOLLIE_API_TEST_KEY as string, + mode: process.env.CONNECTOR_MODE as string, debug: (process.env.DEBUG ?? '0') as string, profileId: process.env.MOLLIE_PROFILE_ID as string, cardComponent: (process.env.MOLLIE_CARD_COMPONENT ?? '0') as string, From ea2c6de78bda0f553085f53f55370a90c9650285 Mon Sep 17 00:00:00 2001 From: Win Date: Thu, 8 Aug 2024 16:49:20 +0700 Subject: [PATCH 4/6] Merge branch 'feature/MOL-263/PICT-236' into feature/MOL-186/PICT-225 + MOL-263/PICT-236: use live key to get apple session --- processor/src/client/mollie.client.ts | 9 ++++++++- processor/src/mollie/payment.mollie.ts | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/processor/src/client/mollie.client.ts b/processor/src/client/mollie.client.ts index 75aaec0..d5dc369 100644 --- a/processor/src/client/mollie.client.ts +++ b/processor/src/client/mollie.client.ts @@ -1,5 +1,5 @@ import createMollieClient, { MollieClient } from '@mollie/api-client'; -import { getApiKey } from '../utils/config.utils'; +import { getApiKey, readConfiguration } from '../utils/config.utils'; import { VERSION_STRING } from '../utils/constant.utils'; /** @@ -13,3 +13,10 @@ export const initMollieClient = (): MollieClient => { versionStrings: `${VERSION_STRING}`, }); }; + +export const initMollieClientForApplePaySession = (): MollieClient => { + return createMollieClient({ + apiKey: readConfiguration().mollie.liveApiKey, + versionStrings: `${VERSION_STRING}`, + }); +}; diff --git a/processor/src/mollie/payment.mollie.ts b/processor/src/mollie/payment.mollie.ts index 767e41b..588f0a3 100644 --- a/processor/src/mollie/payment.mollie.ts +++ b/processor/src/mollie/payment.mollie.ts @@ -7,7 +7,7 @@ import { PaymentCreateParams, PaymentEmbed, } from '@mollie/api-client'; -import { initMollieClient } from '../client/mollie.client'; +import { initMollieClient, initMollieClientForApplePaySession } from '../client/mollie.client'; import CustomError from '../errors/custom.error'; import { logger } from '../utils/logger.utils'; import { ApplePaySessionRequest, CustomPayment } from '../types/mollie.types'; @@ -151,7 +151,7 @@ export const createPaymentWithCustomMethod = async (paymentParams: PaymentCreate export const getApplePaySession = async (options: ApplePaySessionRequest): Promise => { try { - return await initMollieClient().applePay.requestPaymentSession(options); + return await initMollieClientForApplePaySession().applePay.requestPaymentSession(options); } catch (error: unknown) { let errorMessage; if (error instanceof MollieApiError) { From 2d85d69f7a2e5a6aa007a93e45f3b3ea5490720c Mon Sep 17 00:00:00 2001 From: Win Date: Thu, 8 Aug 2024 16:54:01 +0700 Subject: [PATCH 5/6] MOL-263/PICT-236: update test cases --- processor/tests/mollie/payment.mollie.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/processor/tests/mollie/payment.mollie.spec.ts b/processor/tests/mollie/payment.mollie.spec.ts index 5b4a8ef..990ff49 100644 --- a/processor/tests/mollie/payment.mollie.spec.ts +++ b/processor/tests/mollie/payment.mollie.spec.ts @@ -30,6 +30,8 @@ jest.mock('../../src/client/mollie.client', () => ({ methods: { list: mockPaymentsList, }, + })), + initMollieClientForApplePaySession: jest.fn(() => ({ applePay: { requestPaymentSession: mockRequestPaymentSession, }, From 682745991c513a3e01ba8021a0f8fa38a29369f3 Mon Sep 17 00:00:00 2001 From: Win Date: Thu, 8 Aug 2024 17:16:03 +0700 Subject: [PATCH 6/6] MOL-263/PICT-236: update test cases --- processor/package-lock.json | 78 +++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/processor/package-lock.json b/processor/package-lock.json index 05560f9..b8b11a0 100644 --- a/processor/package-lock.json +++ b/processor/package-lock.json @@ -1,12 +1,12 @@ { "name": "shopmacher-mollie-processor", - "version": "0.0.24", + "version": "0.0.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "shopmacher-mollie-processor", - "version": "0.0.24", + "version": "0.0.25", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2039,6 +2039,11 @@ "node": ">=0.10.0" } }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, "node_modules/jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", @@ -2095,6 +2100,18 @@ "integrity": "sha512-xiNMgCuoy4mCL4JTywk9XFs5xpRUcKxtWEcMR6FNMtsgewYTIgIR+nvlP4A4iRCAzRsHMnPhvTRrzp4AGcRTEA==", "dev": true }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -2220,6 +2237,15 @@ "node": ">=10" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/jest-cli": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", @@ -3181,6 +3207,14 @@ "semver": "bin/semver.js" } }, + "node_modules/winston-transport/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4562,6 +4596,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -4865,6 +4904,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6540,6 +6591,21 @@ "node": ">= 0.6" } }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ngrok": { "version": "5.0.0-beta.2", "resolved": "https://registry.npmjs.org/ngrok/-/ngrok-5.0.0-beta.2.tgz", @@ -7700,6 +7766,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -8852,4 +8924,4 @@ } } } -} +} \ No newline at end of file