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/package-lock.json b/processor/package-lock.json index 6a01e96..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": { diff --git a/processor/src/client/mollie.client.ts b/processor/src/client/mollie.client.ts index aa0883e..d5dc369 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, readConfiguration } 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,15 @@ 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}`, + }); +}; + +export const initMollieClientForApplePaySession = (): MollieClient => { + return createMollieClient({ + apiKey: readConfiguration().mollie.liveApiKey, + versionStrings: `${VERSION_STRING}`, }); }; diff --git a/processor/src/commercetools/customFields.commercetools.ts b/processor/src/commercetools/customFields.commercetools.ts index da836c2..f8e92d6 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 7c41bbe..588f0a3 100644 --- a/processor/src/mollie/payment.mollie.ts +++ b/processor/src/mollie/payment.mollie.ts @@ -7,12 +7,13 @@ 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 { readConfiguration } from '../utils/config.utils'; -import { LIBRARY_NAME, LIBRARY_VERSION } from '../utils/constant.utils'; -import { CustomPayment } from '../types/mollie.types'; +import { ApplePaySessionRequest, CustomPayment } from '../types/mollie.types'; +import ApplePaySession from '@mollie/api-client/dist/types/src/data/applePaySession/ApplePaySession'; +import { getApiKey } from '../utils/config.utils'; +import { VERSION_STRING } from '../utils/constant.utils'; import fetch from 'node-fetch'; /** @@ -107,12 +108,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', { @@ -149,3 +148,21 @@ export const createPaymentWithCustomMethod = async (paymentParams: PaymentCreate throw new CustomError(400, errorMessage); } }; + +export const getApplePaySession = async (options: ApplePaySessionRequest): Promise => { + try { + return await initMollieClientForApplePaySession().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 get ApplePay session 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 618f928..0df1d45 100644 --- a/processor/src/service/payment.service.ts +++ b/processor/src/service/payment.service.ts @@ -12,6 +12,7 @@ import { cancelPayment, createMolliePayment, createPaymentWithCustomMethod, + getApplePaySession, getPaymentById, listPaymentMethods, } from '../mollie/payment.mollie'; @@ -54,7 +55,9 @@ import { 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 { CustomPayment } from '../types/mollie.types'; +import { ApplePaySessionRequest, CustomPayment } from '../types/mollie.types'; +import { parseStringToJsonObject } from '../utils/app.utils'; +import ApplePaySession from '@mollie/api-client/dist/types/src/data/applePaySession/ApplePaySession'; /** * Handles listing payment methods by payment. @@ -527,3 +530,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/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/types/mollie.types.ts b/processor/src/types/mollie.types.ts index 87e0c82..1e6c5d8 100644 --- a/processor/src/types/mollie.types.ts +++ b/processor/src/types/mollie.types.ts @@ -10,6 +10,10 @@ export type ParsedMethodsRequestType = { sequenceType?: string; }; +export type ApplePaySessionRequest = { + domain: string; + validationUrl: string; +}; export enum CustomPaymentMethod { blik = 'blik', } 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..f74dcae 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', @@ -17,6 +19,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 +34,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 a60a535..cf8cc3e 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); 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/controllers/payment.controller.spec.ts b/processor/tests/controllers/payment.controller.spec.ts index d783051..4227826 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, handleCancelPayment, } from '../../src/service/payment.service'; import { CancelStatusText, ConnectorActions, CustomFields as CustomFieldName } from '../../src/utils/constant.utils'; @@ -20,6 +21,7 @@ jest.mock('../../src/service/payment.service', () => ({ handleCreatePayment: jest.fn(), handlePaymentCancelRefund: jest.fn(), handleCreateRefund: jest.fn(), + handleGetApplePaySession: jest.fn(), handleCancelPayment: jest.fn(), })); @@ -406,6 +408,59 @@ describe('Test payment.controller.ts', () => { 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); + }); + test('able to call and retrieve the result from handleCancelPayment', async () => { mockAction = 'Create' as string; mockResource = { 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..990ff49 100644 --- a/processor/tests/mollie/payment.mollie.spec.ts +++ b/processor/tests/mollie/payment.mollie.spec.ts @@ -5,18 +5,20 @@ import { createPaymentWithCustomMethod, getPaymentById, listPaymentMethods, + getApplePaySession, } from '../../src/mollie/payment.mollie'; 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(); 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(() => ({ @@ -29,6 +31,11 @@ jest.mock('../../src/client/mollie.client', () => ({ list: mockPaymentsList, }, })), + initMollieClientForApplePaySession: jest.fn(() => ({ + applePay: { + requestPaymentSession: mockRequestPaymentSession, + }, + })), })); // @ts-expect-error: Mock fetch globally @@ -128,8 +135,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 +173,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 +238,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 +294,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'; @@ -471,3 +478,68 @@ describe('cancelPayment', () => { expect(logger.error).toBeCalledTimes(0); }); }); + +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 104eb2b..305b07d 100644 --- a/processor/tests/service/payment.service.spec.ts +++ b/processor/tests/service/payment.service.spec.ts @@ -5,6 +5,7 @@ import { getPaymentCancelActions, getRefundStatusUpdateActions, handleCancelPayment, + handleGetApplePaySession, handleCreatePayment, handleCreateRefund, handleListPaymentMethodsByPayment, @@ -23,6 +24,7 @@ import { } from '../../src/types/commercetools.types'; import { cancelPayment, + getApplePaySession, createMolliePayment, getPaymentById, listPaymentMethods, @@ -65,6 +67,7 @@ jest.mock('../../src/mollie/payment.mollie', () => ({ getPaymentById: jest.fn(), getPaymentRefund: jest.fn(), cancelPayment: jest.fn(), + getApplePaySession: jest.fn(), createPaymentWithCustomMethod: jest.fn(), })); @@ -1584,161 +1587,222 @@ describe('Test handleCancelPayment', () => { }); }); }); - -describe('Test getRefundStatusUpdateActions', () => { - const mockChangeTransactionState = changeTransactionState as jest.MockedFunction; - const mockShouldRefundStatusUpdate = shouldRefundStatusUpdate as jest.MockedFunction; - const mockMakeCTMoney = makeCTMoney as jest.MockedFunction; - +describe('Test handleGetApplePaySession', () => { beforeEach(() => { jest.clearAllMocks(); }); + afterEach(() => { + jest.clearAllMocks(); + }); - it('should update transaction state if matching transaction found and should update', () => { - const ctTransactions: CTTransaction[] = [ - { - id: '1', - type: CTTransactionType.Refund, - interactionId: 'refund1', - state: CTTransactionState.Initial, - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, + 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, }, - ]; - const mollieRefunds: Refund[] = [ - { id: 'refund1', status: RefundStatus.pending, amount: { currency: 'EUR', value: '10.00' } } as Refund, - ]; - - mockShouldRefundStatusUpdate.mockReturnValue(true); - mockChangeTransactionState.mockReturnValue({ action: 'changeTransactionState' } as ChangeTransactionState); - - const result = getRefundStatusUpdateActions(ctTransactions, mollieRefunds); + 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, + }; - expect(result).toEqual([{ action: 'changeTransactionState' }]); - expect(mockShouldRefundStatusUpdate).toHaveBeenCalledWith('pending', CTTransactionState.Initial); - expect(mockChangeTransactionState).toHaveBeenCalledWith('1', mollieRefundToCTStatusMap['pending']); - }); + (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); - it('should not update transaction state if matching transaction found but should not update', () => { - const ctTransactions: CTTransaction[] = [ + expect(response.statusCode).toBe(200); + expect(response.actions).toEqual([ { - id: '1', - type: CTTransactionType.Refund, - interactionId: 'refund1', - state: CTTransactionState.Initial, - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, + action: 'setCustomField', + name: 'sctm_apple_pay_session_response', + value: + '{"domain":"pay.mywebshop.com","validationUrl":"https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession"}', }, - ]; - const mollieRefunds: Refund[] = [ - { id: 'refund1', status: RefundStatus.pending, amount: { currency: 'EUR', value: '10.00' } } as Refund, - ]; + ]); + }); - mockShouldRefundStatusUpdate.mockReturnValue(false); + describe('Test getRefundStatusUpdateActions', () => { + const mockChangeTransactionState = changeTransactionState as jest.MockedFunction; + const mockShouldRefundStatusUpdate = shouldRefundStatusUpdate as jest.MockedFunction< + typeof shouldRefundStatusUpdate + >; + const mockMakeCTMoney = makeCTMoney as jest.MockedFunction; - const result = getRefundStatusUpdateActions(ctTransactions, mollieRefunds); + beforeEach(() => { + jest.clearAllMocks(); + }); - expect(result).toEqual([]); - expect(mockShouldRefundStatusUpdate).toHaveBeenCalledWith('pending', CTTransactionState.Initial); - expect(mockChangeTransactionState).not.toHaveBeenCalled(); - }); + it('should update transaction state if matching transaction found and should update', () => { + const ctTransactions: CTTransaction[] = [ + { + id: '1', + type: CTTransactionType.Refund, + interactionId: 'refund1', + state: CTTransactionState.Initial, + amount: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1000, + fractionDigits: 2, + }, + }, + ]; + const mollieRefunds: Refund[] = [ + { id: 'refund1', status: RefundStatus.pending, amount: { currency: 'EUR', value: '10.00' } } as Refund, + ]; - it('should add a new transaction if no matching transaction is found', () => { - const ctTransactions: CTTransaction[] = []; - const mollieRefunds: Refund[] = [ - { id: 'refund1', status: RefundStatus.pending, amount: { currency: 'EUR', value: '10.00' } } as Refund, - ]; + mockShouldRefundStatusUpdate.mockReturnValue(true); + mockChangeTransactionState.mockReturnValue({ action: 'changeTransactionState' } as ChangeTransactionState); - mockMakeCTMoney.mockReturnValue({ currencyCode: 'EUR', centAmount: 1000 }); + const result = getRefundStatusUpdateActions(ctTransactions, mollieRefunds); - const result = getRefundStatusUpdateActions(ctTransactions, mollieRefunds); + expect(result).toEqual([{ action: 'changeTransactionState' }]); + expect(mockShouldRefundStatusUpdate).toHaveBeenCalledWith('pending', CTTransactionState.Initial); + expect(mockChangeTransactionState).toHaveBeenCalledWith('1', mollieRefundToCTStatusMap['pending']); + }); - expect(result).toEqual([ - { - action: 'addTransaction', - transaction: { + it('should not update transaction state if matching transaction found but should not update', () => { + const ctTransactions: CTTransaction[] = [ + { + id: '1', type: CTTransactionType.Refund, - amount: { currencyCode: 'EUR', centAmount: 1000 }, interactionId: 'refund1', - state: mollieRefundToCTStatusMap['pending'], + state: CTTransactionState.Initial, + amount: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1000, + fractionDigits: 2, + }, }, - }, - ]); - expect(mockMakeCTMoney).toHaveBeenCalledWith({ currency: 'EUR', value: '10.00' }); - }); + ]; + const mollieRefunds: Refund[] = [ + { id: 'refund1', status: RefundStatus.pending, amount: { currency: 'EUR', value: '10.00' } } as Refund, + ]; - it('should handle multiple refunds and transactions correctly', () => { - const ctTransactions: CTTransaction[] = [ - { - id: '1', - type: CTTransactionType.Refund, - interactionId: 'refund1', - state: CTTransactionState.Initial, - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - }, - { - id: '2', - type: CTTransactionType.Refund, - interactionId: 'refund2', - state: CTTransactionState.Initial, - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 2000, - fractionDigits: 2, - }, - }, - ]; - const mollieRefunds: Refund[] = [ - { id: 'refund1', status: RefundStatus.pending, amount: { currency: 'EUR', value: '10.00' } } as Refund, - { id: 'refund3', status: RefundStatus.refunded, amount: { currency: 'EUR', value: '20.00' } } as Refund, - ]; + mockShouldRefundStatusUpdate.mockReturnValue(false); - mockShouldRefundStatusUpdate.mockImplementation( - (mollieStatus, ctState) => mollieStatus === 'pending' && ctState === CTTransactionState.Initial, - ); - mockChangeTransactionState.mockReturnValue({ - action: 'changeTransactionState', - state: mollieRefundToCTStatusMap['pending'], - transactionId: '1', - } as ChangeTransactionState); - mockMakeCTMoney.mockImplementation((amount) => ({ - currencyCode: amount.currency, - centAmount: Number(amount.value) * 100, - })); + const result = getRefundStatusUpdateActions(ctTransactions, mollieRefunds); + + expect(result).toEqual([]); + expect(mockShouldRefundStatusUpdate).toHaveBeenCalledWith('pending', CTTransactionState.Initial); + expect(mockChangeTransactionState).not.toHaveBeenCalled(); + }); - const result = getRefundStatusUpdateActions(ctTransactions, mollieRefunds); + it('should add a new transaction if no matching transaction is found', () => { + const ctTransactions: CTTransaction[] = []; + const mollieRefunds: Refund[] = [ + { id: 'refund1', status: RefundStatus.pending, amount: { currency: 'EUR', value: '10.00' } } as Refund, + ]; - expect(result).toEqual([ - { + mockMakeCTMoney.mockReturnValue({ currencyCode: 'EUR', centAmount: 1000 }); + + const result = getRefundStatusUpdateActions(ctTransactions, mollieRefunds); + + expect(result).toEqual([ + { + action: 'addTransaction', + transaction: { + type: CTTransactionType.Refund, + amount: { currencyCode: 'EUR', centAmount: 1000 }, + interactionId: 'refund1', + state: mollieRefundToCTStatusMap['pending'], + }, + }, + ]); + expect(mockMakeCTMoney).toHaveBeenCalledWith({ currency: 'EUR', value: '10.00' }); + }); + + it('should handle multiple refunds and transactions correctly', () => { + const ctTransactions: CTTransaction[] = [ + { + id: '1', + type: CTTransactionType.Refund, + interactionId: 'refund1', + state: CTTransactionState.Initial, + amount: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1000, + fractionDigits: 2, + }, + }, + { + id: '2', + type: CTTransactionType.Refund, + interactionId: 'refund2', + state: CTTransactionState.Initial, + amount: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 2000, + fractionDigits: 2, + }, + }, + ]; + const mollieRefunds: Refund[] = [ + { id: 'refund1', status: RefundStatus.pending, amount: { currency: 'EUR', value: '10.00' } } as Refund, + { id: 'refund3', status: RefundStatus.refunded, amount: { currency: 'EUR', value: '20.00' } } as Refund, + ]; + + mockShouldRefundStatusUpdate.mockImplementation( + (mollieStatus, ctState) => mollieStatus === 'pending' && ctState === CTTransactionState.Initial, + ); + mockChangeTransactionState.mockReturnValue({ action: 'changeTransactionState', state: mollieRefundToCTStatusMap['pending'], transactionId: '1', - }, - { - action: 'addTransaction', - transaction: { - type: CTTransactionType.Refund, - amount: { currencyCode: 'EUR', centAmount: 2000 }, - interactionId: 'refund3', - state: 'Success', + } as ChangeTransactionState); + mockMakeCTMoney.mockImplementation((amount) => ({ + currencyCode: amount.currency, + centAmount: Number(amount.value) * 100, + })); + + const result = getRefundStatusUpdateActions(ctTransactions, mollieRefunds); + + expect(result).toEqual([ + { + action: 'changeTransactionState', + state: mollieRefundToCTStatusMap['pending'], + transactionId: '1', }, - }, - ]); - expect(mockChangeTransactionState).toHaveBeenCalledWith('1', mollieRefundToCTStatusMap['pending']); - expect(mockMakeCTMoney).toHaveBeenCalledWith({ currency: 'EUR', value: '20.00' }); + { + action: 'addTransaction', + transaction: { + type: CTTransactionType.Refund, + amount: { currencyCode: 'EUR', centAmount: 2000 }, + interactionId: 'refund3', + state: 'Success', + }, + }, + ]); + expect(mockChangeTransactionState).toHaveBeenCalledWith('1', mollieRefundToCTStatusMap['pending']); + expect(mockMakeCTMoney).toHaveBeenCalledWith({ currency: 'EUR', value: '20.00' }); + }); }); }); 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/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/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 36a7d90..caa0c34 100644 --- a/processor/tests/utils/paymentAction.utils.spec.ts +++ b/processor/tests/utils/paymentAction.utils.spec.ts @@ -349,6 +349,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)( 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,