diff --git a/processor/package.json b/processor/package.json index b3e675e..c170a4e 100644 --- a/processor/package.json +++ b/processor/package.json @@ -1,7 +1,7 @@ { "name": "shopmacher-mollie-processor", "description": "Integration between commercetools and mollie payment service provider", - "version": "0.0.16", + "version": "0.0.17", "main": "index.js", "private": true, "scripts": { diff --git a/processor/src/commercetools/customFields.commercetools.ts b/processor/src/commercetools/customFields.commercetools.ts index f295062..bb03b4b 100644 --- a/processor/src/commercetools/customFields.commercetools.ts +++ b/processor/src/commercetools/customFields.commercetools.ts @@ -17,83 +17,71 @@ export async function createCustomPaymentType(): Promise { }) .execute(); - if (types.length > 0) { - const type = types[0]; - + if (types.length <= 0) { await apiRoot .types() - .withKey({ key: PAYMENT_TYPE_KEY }) - .delete({ - queryArgs: { - version: type.version, + .post({ + body: { + key: PAYMENT_TYPE_KEY, + name: { + en: 'SCTM - Payment method custom fields', + de: 'SCTM - Benutzerdefinierte Felder der Zahlungsmethode', + }, + resourceTypeIds: ['payment'], + fieldDefinitions: [ + { + name: CustomFields.payment.profileId, + label: { + en: 'Profile ID', + de: 'Profil-ID', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: CustomFields.payment.request, + label: { + en: 'The request object for listing payment methods', + de: 'Das Anforderungsobjekt für die Auflistung der Zahlwege', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: CustomFields.payment.response, + label: { + en: 'List of available payment methods', + de: 'Liste der verfügbaren Zahlungsarten', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: CustomFields.createPayment.request, + label: { + en: 'The request object for create Mollie payment', + de: 'Das Anforderungsobjekt zum Erstellen der Mollie-Zahlung', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + ], }, }) .execute(); } - - await apiRoot - .types() - .post({ - body: { - key: PAYMENT_TYPE_KEY, - name: { - en: 'SCTM - Payment method custom fields', - de: 'SCTM - Benutzerdefinierte Felder der Zahlungsmethode', - }, - resourceTypeIds: ['payment'], - fieldDefinitions: [ - { - name: CustomFields.payment.profileId, - label: { - en: 'Profile ID', - de: 'Profil-ID', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - { - name: CustomFields.payment.request, - label: { - en: 'The request object for listing payment methods', - de: 'Das Anforderungsobjekt für die Auflistung der Zahlwege', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - { - name: CustomFields.payment.response, - label: { - en: 'List of available payment methods', - de: 'Liste der verfügbaren Zahlungsarten', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - { - name: CustomFields.createPayment.request, - label: { - en: 'The request object for create Mollie payment', - de: 'Das Anforderungsobjekt zum Erstellen der Mollie-Zahlung', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - ], - }, - }) - .execute(); } export async function createCustomPaymentInterfaceInteractionType(): Promise { @@ -110,95 +98,83 @@ export async function createCustomPaymentInterfaceInteractionType(): Promise 0) { - const type = types[0]; - + if (types.length <= 0) { await apiRoot .types() - .withKey({ key: CustomFields.createPayment.interfaceInteraction }) - .delete({ - queryArgs: { - version: type.version, + .post({ + body: { + key: CustomFields.createPayment.interfaceInteraction, + name: { + en: 'SCTM - Mollie Payment Interface', + de: 'SCTM - Benutzerdefinierte Felder im Warenkorb', + }, + resourceTypeIds: ['payment-interface-interaction'], + fieldDefinitions: [ + { + name: 'id', + label: { + en: 'Interface Interaction ID', + de: 'Schnittstelleninteraktions-ID', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: 'actionType', + label: { + en: 'Action Type', + de: 'Aktionstyp', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: 'createdAt', + label: { + en: 'Created At', + de: 'Hergestellt in', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: 'request', + label: { + en: 'Interface Interaction Request', + de: 'Schnittstelleninteraktionsanforderung', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: 'response', + label: { + en: 'Interface Interaction Response', + de: 'Schnittstelleninteraktionsantwort', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + ], }, }) .execute(); } - - await apiRoot - .types() - .post({ - body: { - key: CustomFields.createPayment.interfaceInteraction, - name: { - en: 'SCTM - Mollie Payment Interface', - de: 'SCTM - Benutzerdefinierte Felder im Warenkorb', - }, - resourceTypeIds: ['payment-interface-interaction'], - fieldDefinitions: [ - { - name: 'id', - label: { - en: 'Interface Interaction ID', - de: 'Schnittstelleninteraktions-ID', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - { - name: 'actionType', - label: { - en: 'Action Type', - de: 'Aktionstyp', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - { - name: 'createdAt', - label: { - en: 'Created At', - de: 'Hergestellt in', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - { - name: 'request', - label: { - en: 'Interface Interaction Request', - de: 'Schnittstelleninteraktionsanforderung', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - { - name: 'response', - label: { - en: 'Interface Interaction Response', - de: 'Schnittstelleninteraktionsantwort', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - ], - }, - }) - .execute(); } export async function createCustomPaymentTransactionCancelRefundType(): Promise { @@ -217,61 +193,49 @@ export async function createCustomPaymentTransactionCancelRefundType(): Promise< }) .execute(); - if (types.length > 0) { - const type = types[0]; - + if (types.length <= 0) { await apiRoot .types() - .withKey({ key: customFieldName }) - .delete({ - queryArgs: { - version: type.version, + .post({ + body: { + key: customFieldName, + name: { + en: 'SCTM - Payment Cancel Refund on Transaction custom fields', + de: 'SCTM - Zahlung stornieren Rückerstattung bei benutzerdefinierten Transaktionsfeldern', + }, + description: { + en: 'Showing the reason of cancelling and identifying if the cancel action came from CommerceTools or Mollie', + de: 'Anzeige des Kündigungsgrundes und Identifizierung, ob die Kündigung von CommerceTools oder Mollie erfolgte', + }, + resourceTypeIds: ['transaction'], + fieldDefinitions: [ + { + name: 'reasonText', + label: { + en: 'The reason of cancelling the refund, include the user name', + de: 'Der Grund für die Stornierung der Rückerstattung, den Benutzernamen einschließen', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + { + name: 'statusText', + label: { + en: 'To differentiate between the “failure” from CommerceTools and the real status', + de: 'Um zwischen dem „Fehler“ von CommerceTools und dem tatsächlichen Status zu unterscheiden', + }, + required: false, + type: { + name: 'String', + }, + inputHint: 'MultiLine', + }, + ], }, }) .execute(); } - - await apiRoot - .types() - .post({ - body: { - key: customFieldName, - name: { - en: 'SCTM - Payment Cancel Refund on Transaction custom fields', - de: 'SCTM - Zahlung stornieren Rückerstattung bei benutzerdefinierten Transaktionsfeldern', - }, - description: { - en: 'Showing the reason of cancelling and identifying if the cancel action came from CommerceTools or Mollie', - de: 'Anzeige des Kündigungsgrundes und Identifizierung, ob die Kündigung von CommerceTools oder Mollie erfolgte', - }, - resourceTypeIds: ['transaction'], - fieldDefinitions: [ - { - name: 'reasonText', - label: { - en: 'The reason of cancelling the refund, include the user name', - de: 'Der Grund für die Stornierung der Rückerstattung, den Benutzernamen einschließen', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - { - name: 'statusText', - label: { - en: 'To differentiate between the “failure” from CommerceTools and the real status', - de: 'Um zwischen dem „Fehler“ von CommerceTools und dem tatsächlichen Status zu unterscheiden', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - ], - }, - }) - .execute(); } diff --git a/processor/src/controllers/payment.controller.ts b/processor/src/controllers/payment.controller.ts index a825804..982cec2 100644 --- a/processor/src/controllers/payment.controller.ts +++ b/processor/src/controllers/payment.controller.ts @@ -10,6 +10,7 @@ import { PaymentReference, Payment } from '@commercetools/platform-sdk'; import { ConnectorActions } from '../utils/constant.utils'; import { validateCommerceToolsPaymentPayload } from '../validators/payment.validators'; import SkipError from '../errors/skip.error'; +import { logger } from '../utils/logger.utils'; /** * Handle the cart controller according to the action @@ -23,25 +24,37 @@ export const paymentController = async ( resource: PaymentReference, ): Promise => { const ctPayment: Payment = JSON.parse(JSON.stringify(resource)).obj; + logger.debug('SCTM - payment processing - paymentController - ctPayment', ctPayment); const paymentAction = determinePaymentAction(ctPayment); + logger.debug('SCTM - payment processing - paymentController - determined payment action', paymentAction); + if (paymentAction === ConnectorActions.NoAction) { throw new SkipError('No payment actions matched'); } validateCommerceToolsPaymentPayload(action, paymentAction, ctPayment); + logger.debug( + `SCTM - payment processing - paymentController - payload validated, starting to handle the action: ${paymentAction}`, + ); + switch (paymentAction) { case ConnectorActions.GetPaymentMethods: + logger.debug('SCTM - payment processing - paymentController - handleListPaymentMethodsByPayment'); return await handleListPaymentMethodsByPayment(ctPayment); case ConnectorActions.CreatePayment: + logger.debug('SCTM - payment processing - paymentController - handleCreatePayment'); return await handleCreatePayment(ctPayment); case ConnectorActions.CreateRefund: + logger.debug('SCTM - payment processing - paymentController - handleCreateRefund'); return await handleCreateRefund(ctPayment); case ConnectorActions.CancelRefund: + logger.debug('SCTM - payment processing - paymentController - handlePaymentCancelRefund'); return await handlePaymentCancelRefund(ctPayment); default: + logger.debug('SCTM - payment processing - paymentController - No payment actions matched'); throw new SkipError('No payment actions matched'); } }; diff --git a/processor/src/controllers/processor.controller.ts b/processor/src/controllers/processor.controller.ts index 7e6814c..6a4158f 100644 --- a/processor/src/controllers/processor.controller.ts +++ b/processor/src/controllers/processor.controller.ts @@ -45,9 +45,13 @@ export const post = async (request: Request, response: Response) => { return apiSuccess(200, response, []); } if (error instanceof CustomError) { + logger.debug('Error occurred when processing request', error); + return apiError(response, error.errors); } + logger.debug('Unexpected error occurred when processing request', error); + return apiError(response, formatErrorResponse(error).errors); } }; diff --git a/processor/src/mollie/payment.mollie.ts b/processor/src/mollie/payment.mollie.ts index d79e3c5..20cce35 100644 --- a/processor/src/mollie/payment.mollie.ts +++ b/processor/src/mollie/payment.mollie.ts @@ -24,13 +24,12 @@ export const createMolliePayment = async (paymentParams: PaymentCreateParams): P let errorMessage; if (error instanceof MollieApiError) { - errorMessage = `createMolliePayment - error: ${error.message}, field: ${error.field}`; + errorMessage = `SCTM - createMolliePayment - error: ${error.message}, field: ${error.field}`; } else { - errorMessage = `createMolliePayment - Failed to create payment with unknown errors`; + errorMessage = `SCTM - createMolliePayment - Failed to create payment with unknown errors`; } - logger.error({ - message: errorMessage, + logger.error(errorMessage, { error, }); @@ -60,13 +59,12 @@ export const listPaymentMethods = async (options: MethodsListParams): Promise
  • { const transactionCustomFieldName = CustomFields.paymentCancelRefund; - let transactionCustomFieldValue; - try { - transactionCustomFieldValue = !pendingRefundTransaction.custom?.fields[transactionCustomFieldName] - ? {} - : JSON.parse(pendingRefundTransaction.custom?.fields[transactionCustomFieldName]); - } catch (error: unknown) { - logger.error( - `SCTM - handleCancelRefund - Failed to parse the JSON string from the custom field ${transactionCustomFieldName}.`, - ); - throw new CustomError( - 400, - `SCTM - handleCancelRefund - Failed to parse the JSON string from the custom field ${transactionCustomFieldName}.`, - ); - } + const transactionCustomFieldValue = parseStringToJsonObject( + pendingRefundTransaction.custom?.fields[transactionCustomFieldName], + transactionCustomFieldName, + 'SCTM - handleCancelRefund', + pendingRefundTransaction.id, + ); const newTransactionCustomFieldValue = { reasonText: transactionCustomFieldValue.reasonText, diff --git a/processor/src/utils/app.utils.ts b/processor/src/utils/app.utils.ts index e886f21..3a79edd 100644 --- a/processor/src/utils/app.utils.ts +++ b/processor/src/utils/app.utils.ts @@ -1,3 +1,5 @@ +import CustomError from '../errors/custom.error'; +import { logger } from './logger.utils'; /** * Generates an ISO string date * @returns Returns the current date converted to ISO. @@ -5,3 +7,35 @@ export function createDateNowString(): string { return new Date().toISOString(); } + +/** + * Parses a string into a JSON object. + * Write log if the string cannot be parsed + * + * @param {string} targetedString - The string to be parsed. + * @param {string} fieldName - The name of the custom field. + * @param {string} commerceToolsId - CommerceTools Payment ID or Transaction ID. + * @returns {object} - The parsed JSON object. + * @throws {CustomError} - If the string cannot be parsed into a JSON object. + */ +export function parseStringToJsonObject( + targetedString: string, + fieldName?: string, + errorPrefix?: string, + commerceToolsId?: string, +) { + if (targetedString === undefined || targetedString.trim() === '') { + return {}; + } + + try { + return JSON.parse(targetedString); + } catch { + const errorMessage = `${errorPrefix ? errorPrefix : 'SCTM - PAYMENT PROCESSING'} - Failed to parse the JSON string from the custom field ${fieldName}.`; + logger.error(errorMessage, { + commerceToolsId: commerceToolsId, + }); + + throw new CustomError(400, errorMessage); + } +} diff --git a/processor/src/utils/map.utils.ts b/processor/src/utils/map.utils.ts index b84dd23..cb6587f 100644 --- a/processor/src/utils/map.utils.ts +++ b/processor/src/utils/map.utils.ts @@ -5,6 +5,7 @@ import { ParsedMethodsRequestType } from '../types/mollie.types'; import { Payment } from '@commercetools/platform-sdk'; import CustomError from '../errors/custom.error'; import { PaymentCreateParams, MethodsListParams, PaymentMethod } from '@mollie/api-client'; +import { parseStringToJsonObject } from './app.utils'; const extractMethodsRequest = (ctPayment: Payment): ParsedMethodsRequestType | undefined => { return ctPayment?.custom?.fields?.[CustomFields.payment.request]; @@ -40,7 +41,10 @@ export const mapCommercetoolsPaymentCustomFieldsToMollieListParams = async ( if (!parsedMethodsRequest) { logger.debug( 'SCTM - field {custom.fields.sctm_payment_methods_request} not found. Returning default Mollie object', - baseParams, + { + ...baseParams, + commerceToolsPaymentId: ctPayment.id, + }, ); return baseParams; } @@ -50,7 +54,13 @@ export const mapCommercetoolsPaymentCustomFieldsToMollieListParams = async ( ...buildMethodsListParams(parsedMethodsRequest), }; } catch (error: unknown) { - logger.error('SCTM - PARSING ERROR - field {custom.fields.sctm_payment_methods_request}'); + logger.error( + `SCTM - PARSING ERROR - field {custom.fields.sctm_payment_methods_request}, CommerceTools Payment ID: ${ctPayment.id}`, + { + commerceToolsPaymentId: ctPayment.id, + error, + }, + ); throw new CustomError(400, 'SCTM - PARSING ERROR - field {custom.fields.sctm_payment_methods_request}'); } }; @@ -88,7 +98,12 @@ export const createMollieCreatePaymentParams = (payment: Payment): PaymentCreate const [method, issuer] = paymentMethodInfo?.method?.split(',') ?? [null, null]; const requestCustomField = custom?.fields?.[CustomFields.createPayment.request]; - const paymentRequest = requestCustomField ? JSON.parse(requestCustomField) : {}; + const paymentRequest = parseStringToJsonObject( + payment.custom?.fields?.[CustomFields.createPayment.request], + CustomFields.createPayment.request, + 'SCTM - PAYMENT PROCESSING', + payment.id, + ); const defaultWebhookEndpoint = new URL(process.env.CONNECT_SERVICE_URL ?? '').origin + '/webhook'; diff --git a/processor/src/validators/payment.validators.ts b/processor/src/validators/payment.validators.ts index 0053fea..8c34f5f 100644 --- a/processor/src/validators/payment.validators.ts +++ b/processor/src/validators/payment.validators.ts @@ -6,6 +6,7 @@ import { logger } from '../utils/logger.utils'; import { ConnectorActions, CustomFields } from '../utils/constant.utils'; import { DeterminePaymentActionType } from '../types/controller.types'; import { CTTransactionState, CTTransactionType } from '../types/commercetools.types'; +import { parseStringToJsonObject } from '../utils/app.utils'; /** * Checks if the given action is either 'Create' or 'Update'. @@ -67,16 +68,29 @@ export const checkPaymentMethodInput = ( const [method] = CTPaymentMethod.split(','); if (!method) { - logger.error('SCTM - PAYMENT PROCESSING - Payment method must be set in order to create a Mollie payment.'); + logger.error( + `SCTM - PAYMENT PROCESSING - Payment method must be set in order to create a Mollie payment, CommerceTools Payment ID: ${ctPayment.id}.`, + { + commerceToolsPaymentId: ctPayment.id, + }, + ); throw new CustomError( 400, - `SCTM - PAYMENT PROCESSING - Payment method must be set in order to create a Mollie payment.`, + `SCTM - PAYMENT PROCESSING - Payment method must be set in order to create a Mollie payment, CommerceTools Payment ID: ${ctPayment.id}.`, ); } if (!hasValidPaymentMethod(ctPayment.paymentMethodInfo?.method)) { - logger.error(`SCTM - PAYMENT PROCESSING - Invalid paymentMethodInfo.method "${method}".`); - throw new CustomError(400, `SCTM - PAYMENT PROCESSING - Invalid paymentMethodInfo.method "${method}".`); + logger.error( + `SCTM - PAYMENT PROCESSING - Invalid paymentMethodInfo.method "${method}", CommerceTools Payment ID: ${ctPayment.id}.`, + { + commerceToolsPaymentId: ctPayment.id, + }, + ); + throw new CustomError( + 400, + `SCTM - PAYMENT PROCESSING - Invalid paymentMethodInfo.method "${method}", CommerceTools Payment ID: ${ctPayment.id}.`, + ); } if (method === PaymentMethod.creditcard) { @@ -98,8 +112,13 @@ export const checkValidRefundTransaction = (ctPayment: CTPayment): boolean => { ); if (!successChargeTransaction?.interactionId) { - logger.error('SCTM - handleCreateRefund - No successful charge transaction found'); - throw new CustomError(400, 'SCTM - handleCreateRefund - No successful charge transaction found'); + logger.error( + `SCTM - handleCreateRefund - No successful charge transaction found, CommerceTools Transaction ID: ${successChargeTransaction?.id}.`, + ); + throw new CustomError( + 400, + `SCTM - handleCreateRefund - No successful charge transaction found, CommerceTools Transaction ID: ${successChargeTransaction?.id}.`, + ); } const initialRefundTransaction = ctPayment.transactions.find( @@ -107,13 +126,18 @@ export const checkValidRefundTransaction = (ctPayment: CTPayment): boolean => { ); if (!initialRefundTransaction) { - logger.error('SCTM - handleCreateRefund - No initial refund transaction found'); + logger.error(`SCTM - handleCreateRefund - No initial refund transaction found.`); throw new CustomError(400, 'SCTM - handleCreateRefund - No initial refund transaction found'); } if (!initialRefundTransaction?.amount || !initialRefundTransaction?.amount.centAmount) { - logger.error('SCTM - handleCreateRefund - No amount found in initial refund transaction'); - throw new CustomError(400, 'SCTM - handleCreateRefund - No amount found in initial refund transaction'); + logger.error( + `SCTM - handleCreateRefund - No amount found in initial refund transaction, CommerceTools Transaction ID: ${initialRefundTransaction?.id}`, + ); + throw new CustomError( + 400, + `SCTM - handleCreateRefund - No amount found in initial refund transaction, CommerceTools Transaction ID: ${initialRefundTransaction?.id}`, + ); } return true; @@ -131,30 +155,33 @@ export const checkValidRefundTransaction = (ctPayment: CTPayment): boolean => { * The `errorMessage` property contains the error message if the input is invalid. */ export const checkPaymentMethodSpecificParameters = (ctPayment: CTPayment): void => { - let paymentCustomFields; + const paymentCustomFields = parseStringToJsonObject( + ctPayment.custom?.fields?.[CustomFields.createPayment.request], + CustomFields.createPayment.request, + 'SCTM - PAYMENT PROCESSING', + ctPayment.id, + ); - try { - paymentCustomFields = ctPayment.custom?.fields?.[CustomFields.createPayment.request] - ? JSON.parse(ctPayment.custom?.fields?.[CustomFields.createPayment.request]) - : {}; - } catch (error: unknown) { + if (!paymentCustomFields?.cardToken) { logger.error( - 'SCTM - PAYMENT PROCESSING - Failed to parse the JSON string from the custom field sctm_create_payment_request.', - ); - throw new CustomError( - 400, - `SCTM - PAYMENT PROCESSING - Failed to parse the JSON string from the custom field sctm_create_payment_request.`, + `SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard, CommerceTools Payment ID: ${ctPayment.id}`, + { + commerceToolsPaymentId: ctPayment.id, + cardToken: paymentCustomFields?.cardToken, + }, ); - } - - if (!paymentCustomFields?.cardToken || paymentCustomFields.cardToken == '') { - logger.error('SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard'); throw new CustomError(400, 'SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard'); } if (typeof paymentCustomFields?.cardToken !== 'string' || paymentCustomFields?.cardToken.trim() === '') { - logger.error('SCTM - PAYMENT PROCESSING - cardToken must be a string and not empty for payment method creditcard'); + logger.error( + `SCTM - PAYMENT PROCESSING - cardToken must be a string and not empty for payment method creditcard, CommerceTools Payment ID: ${ctPayment.id}`, + { + commerceToolsPaymentId: ctPayment.id, + cardToken: paymentCustomFields?.cardToken, + }, + ); throw new CustomError( 400, @@ -165,7 +192,12 @@ export const checkPaymentMethodSpecificParameters = (ctPayment: CTPayment): void export const checkAmountPlanned = (ctPayment: CTPayment): true | CustomError => { if (!ctPayment?.amountPlanned) { - logger.error('SCTM - PAYMENT PROCESSING - Payment {amountPlanned} not found.'); + logger.error( + `SCTM - PAYMENT PROCESSING - Payment {amountPlanned} not found, commerceToolsPaymentId: ${ctPayment.id}.`, + { + commerceToolsPaymentId: ctPayment.id, + }, + ); throw new CustomError(400, 'SCTM - PAYMENT PROCESSING - Payment {amountPlanned} not found.'); } diff --git a/processor/tests/mollie/payment.mollie.spec.ts b/processor/tests/mollie/payment.mollie.spec.ts index a830560..f699806 100644 --- a/processor/tests/mollie/payment.mollie.spec.ts +++ b/processor/tests/mollie/payment.mollie.spec.ts @@ -55,13 +55,12 @@ describe('createMolliePayment', () => { try { await createMolliePayment(paymentParams); } catch (error: any) { - expect(error.message).toBe(`createMolliePayment - error: Bad request, field: Test`); + expect(error.message).toBe(`SCTM - createMolliePayment - error: Bad request, field: Test`); expect(error.statusCode).toBe(400); expect(mockPaymentsCreate).toHaveBeenCalledTimes(1); expect(mockPaymentsCreate).toHaveBeenCalledWith(paymentParams); expect(logger.error).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledWith({ - message: `createMolliePayment - error: Bad request, field: Test`, + expect(logger.error).toHaveBeenCalledWith(`SCTM - createMolliePayment - error: Bad request, field: Test`, { error: new MollieApiError('Bad request', { statusCode: 400, field: 'Test' }), }); } @@ -83,14 +82,16 @@ describe('createMolliePayment', () => { try { await createMolliePayment(paymentParams); } catch (error: any) { - expect(error.message).toBe(`createMolliePayment - Failed to create payment with unknown errors`); + expect(error.message).toBe(`SCTM - createMolliePayment - Failed to create payment with unknown errors`); expect(mockPaymentsCreate).toHaveBeenCalledTimes(1); expect(mockPaymentsCreate).toHaveBeenCalledWith(paymentParams); expect(logger.error).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledWith({ - message: `createMolliePayment - Failed to create payment with unknown errors`, - error: new Error('Unknown error'), - }); + expect(logger.error).toHaveBeenCalledWith( + `SCTM - createMolliePayment - Failed to create payment with unknown errors`, + { + error: new Error('Unknown error'), + }, + ); } }); }); @@ -143,13 +144,12 @@ describe('listPaymentMethods', () => { try { await listPaymentMethods(mockOption); } catch (error: any) { - expect(error.message).toBe(`listPaymentMethods - error: Bad request, field: Test`); + expect(error.message).toBe(`SCTM - listPaymentMethods - error: Bad request, field: Test`); expect(error.statusCode).toBe(400); expect(mockPaymentsList).toHaveBeenCalledTimes(1); expect(mockPaymentsList).toHaveBeenCalledWith(mockOption); expect(logger.error).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledWith({ - message: `listPaymentMethods - error: Bad request, field: Test`, + expect(logger.error).toHaveBeenCalledWith(`SCTM - listPaymentMethods - error: Bad request, field: Test`, { error: new MollieApiError('Bad request', { statusCode: 400, field: 'Test' }), }); } @@ -171,15 +171,17 @@ describe('listPaymentMethods', () => { try { await listPaymentMethods(mockOption); } catch (error: any) { - expect(error.message).toBe(`listPaymentMethods - Failed to list payments with unknown errors`); + expect(error.message).toBe(`SCTM - listPaymentMethods - Failed to list payments with unknown errors`); expect(error.statusCode).toBe(400); expect(mockPaymentsList).toHaveBeenCalledTimes(1); expect(mockPaymentsList).toHaveBeenCalledWith(mockOption); expect(logger.error).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledWith({ - message: `listPaymentMethods - Failed to list payments with unknown errors`, - error: new Error('Unknown error'), - }); + expect(logger.error).toHaveBeenCalledWith( + `SCTM - listPaymentMethods - Failed to list payments with unknown errors`, + { + error: new Error('Unknown error'), + }, + ); } }); }); diff --git a/processor/tests/mollie/refund.mollie.spec.ts b/processor/tests/mollie/refund.mollie.spec.ts index b404cad..cc21c41 100644 --- a/processor/tests/mollie/refund.mollie.spec.ts +++ b/processor/tests/mollie/refund.mollie.spec.ts @@ -61,10 +61,13 @@ describe('createPaymentRefund', () => { } catch (error: unknown) { expect(error).toBeInstanceOf(CustomError); expect(logger.error).toBeCalledTimes(1); - expect(logger.error).toBeCalledWith({ - message: `createMolliePaymentRefund - error: ` + errorMessage, - error: mollieApiError, - }); + expect(logger.error).toBeCalledWith( + `SCTM - createMolliePaymentRefund - Calling Mollie API - error: ${errorMessage}`, + { + paymentId: paymentCreateRefund.paymentId, + error: mollieApiError, + }, + ); } }); @@ -86,10 +89,13 @@ describe('createPaymentRefund', () => { } catch (error: unknown) { expect(error).toBeInstanceOf(CustomError); expect(logger.error).toBeCalledTimes(1); - expect(logger.error).toBeCalledWith({ - message: `createMolliePaymentRefund - Calling Mollie API - Failed to create refund with unknown errors`, - error: new Error('Unknown error'), - }); + expect(logger.error).toBeCalledWith( + `SCTM - createMolliePaymentRefund - Calling Mollie API - Failed to create refund with unknown errors`, + { + paymentId: paymentCreateRefund.paymentId, + error: new Error('Unknown error'), + }, + ); } }); }); @@ -127,10 +133,14 @@ describe('cancelPaymentRefund', () => { } catch (error: unknown) { expect(error).toBeInstanceOf(CustomError); expect(logger.error).toBeCalledTimes(1); - expect(logger.error).toBeCalledWith({ - message: `cancelMolliePaymentRefund - error: ` + errorMessage, - error: mollieApiError, - }); + expect(logger.error).toBeCalledWith( + `SCTM - cancelMolliePaymentRefund - Calling Mollie API - error: ${errorMessage}`, + { + molliePaymentId: paymentCancelRefund.paymentId, + mollieRefundId: 'refund_id_1', + error: mollieApiError, + }, + ); } }); @@ -148,10 +158,14 @@ describe('cancelPaymentRefund', () => { } catch (error: unknown) { expect(error).toBeInstanceOf(CustomError); expect(logger.error).toBeCalledTimes(1); - expect(logger.error).toBeCalledWith({ - message: `cancelMollieRefund - Calling Mollie API - Failed to cancel the refund with unknown errors`, - error: new Error('Unknown error'), - }); + expect(logger.error).toBeCalledWith( + `SCTM - cancelMolliePaymentRefund - Calling Mollie API - Failed to cancel the refund with unknown errors`, + { + molliePaymentId: paymentCancelRefund.paymentId, + mollieRefundId: 'refund_id_1', + error: new Error('Unknown error'), + }, + ); } }); }); diff --git a/processor/tests/service/payment.service.spec.ts b/processor/tests/service/payment.service.spec.ts index b73db52..8e55300 100644 --- a/processor/tests/service/payment.service.spec.ts +++ b/processor/tests/service/payment.service.spec.ts @@ -594,6 +594,9 @@ describe('Test getPaymentCancelRefundActions', () => { expect(logger.error).toBeCalledTimes(1); expect(logger.error).toBeCalledWith( `SCTM - handleCancelRefund - Failed to parse the JSON string from the custom field ${transactionCustomFieldName}.`, + { + commerceToolsId: '5c8b0375-305a-4f19-ae8e-07806b101999', + }, ); } }); @@ -766,7 +769,7 @@ describe('Test handlePaymentCancelRefund', () => { expect(error).toBeInstanceOf(CustomError); expect(logger.error).toBeCalledTimes(1); expect(logger.error).toBeCalledWith( - `SCTM - handleCancelRefund - Mollie Payment status must be pending, payment ID: ${molliePayment.id}`, + `SCTM - handleCancelRefund - Mollie Payment status must be pending, Mollie Payment ID: ${molliePayment.id}`, ); } }); diff --git a/processor/tests/utils/app.utils.spec.ts b/processor/tests/utils/app.utils.spec.ts index 76bfc3d..178e3e8 100644 --- a/processor/tests/utils/app.utils.spec.ts +++ b/processor/tests/utils/app.utils.spec.ts @@ -1,10 +1,42 @@ -import { describe, test, expect, jest } from '@jest/globals'; -import { createDateNowString } from '../../src/utils/app.utils'; +import { describe, test, expect, it, jest } from '@jest/globals'; +import { createDateNowString, parseStringToJsonObject } from '../../src/utils/app.utils'; +import { logger } from '../../src/utils/logger.utils'; +import CustomError from '../../src/errors/custom.error'; -describe('Test app.utils.ts', () => { - test('should return createDateNowString correct time', async () => { +describe('Test createDateNowString', () => { + test('should return correct time', async () => { jest.useFakeTimers().setSystemTime(new Date('2024-07-24')); expect(createDateNowString()).toBe('2024-07-24T00:00:00.000Z'); }); }); + +describe('Test parseStringToJsonObject', () => { + it('should return an empty object if the targeted string is empty', () => { + expect(parseStringToJsonObject('')).toEqual({}); + }); + + it('should return the correct object', async () => { + expect(parseStringToJsonObject('{"key": "value"}')).toEqual({ key: 'value' }); + }); + + it('should throw an error with extra params configured correctly when parsing failed', () => { + const fieldName = 'test field name'; + const prefix = 'test prefix'; + const commerceToolsId = 'test-commercetools-id'; + + try { + parseStringToJsonObject('not a json string', fieldName, prefix, commerceToolsId); + } catch (error) { + const errorMessage = `${prefix} - Failed to parse the JSON string from the custom field ${fieldName}.`; + expect(logger.error).toBeCalledTimes(1); + expect(logger.error).toBeCalledWith(errorMessage, { + commerceToolsId: commerceToolsId, + }); + + expect(error).toBeInstanceOf(CustomError); + expect((error as CustomError).message).toBe(errorMessage); + expect((error as CustomError).statusCode).toBe(400); + } + }); +}); diff --git a/processor/tests/validators/payment.validators.spec.ts b/processor/tests/validators/payment.validators.spec.ts index 3051719..e792021 100644 --- a/processor/tests/validators/payment.validators.spec.ts +++ b/processor/tests/validators/payment.validators.spec.ts @@ -176,7 +176,7 @@ describe('checkPaymentMethodInput', () => { } catch (error: any) { expect(error).toBeInstanceOf(CustomError); expect(error.message).toBe( - 'SCTM - PAYMENT PROCESSING - Payment method must be set in order to create a Mollie payment.', + `SCTM - PAYMENT PROCESSING - Payment method must be set in order to create a Mollie payment, CommerceTools Payment ID: ${CTPayment.id}.`, ); } }); @@ -206,7 +206,7 @@ describe('checkPaymentMethodInput', () => { } catch (error: any) { expect(error).toBeInstanceOf(CustomError); expect(error.message).toBe( - `SCTM - PAYMENT PROCESSING - Invalid paymentMethodInfo.method "${CTPayment.paymentMethodInfo.method}".`, + `SCTM - PAYMENT PROCESSING - Invalid paymentMethodInfo.method "${CTPayment.paymentMethodInfo.method}", CommerceTools Payment ID: ${CTPayment.id}.`, ); } }); @@ -265,7 +265,11 @@ describe('checkPaymentMethodSpecificParameters', () => { ); expect(logger.error).toBeCalledTimes(1); expect(logger.error).toBeCalledWith( - 'SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard', + `SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard, CommerceTools Payment ID: ${CTPayment.id}`, + { + cardToken: undefined, + commerceToolsPaymentId: CTPayment.id, + }, ); } }); @@ -309,7 +313,11 @@ describe('checkPaymentMethodSpecificParameters', () => { ); expect(logger.error).toBeCalledTimes(1); expect(logger.error).toBeCalledWith( - 'SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard', + `SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard, CommerceTools Payment ID: ${CTPayment.id}`, + { + cardToken: '', + commerceToolsPaymentId: CTPayment.id, + }, ); } }); @@ -350,6 +358,9 @@ describe('checkPaymentMethodSpecificParameters', () => { expect(logger.error).toBeCalledTimes(1); expect(logger.error).toHaveBeenCalledWith( 'SCTM - PAYMENT PROCESSING - Failed to parse the JSON string from the custom field sctm_create_payment_request.', + { + commerceToolsId: CTPayment.id, + }, ); } });