diff --git a/processor/src/service/payment.service.ts b/processor/src/service/payment.service.ts index 7b942de..2f7cf13 100644 --- a/processor/src/service/payment.service.ts +++ b/processor/src/service/payment.service.ts @@ -6,7 +6,7 @@ import { createMollieCreatePaymentParams, mapCommercetoolsPaymentCustomFieldsToMollieListParams, } from '../utils/map.utils'; -import { CentPrecisionMoney, Payment, UpdateAction } from '@commercetools/platform-sdk'; +import { CentPrecisionMoney, Extension, Payment, UpdateAction } from '@commercetools/platform-sdk'; import CustomError from '../errors/custom.error'; import { createMolliePayment, getPaymentById, listPaymentMethods } from '../mollie/payment.mollie'; import { cancelPaymentRefund, createPaymentRefund } from '../mollie/refund.mollie'; @@ -40,6 +40,8 @@ import { CreateParameters, } from '@mollie/api-client/dist/types/src/binders/payments/refunds/parameters'; import { parseStringToJsonObject } from '../utils/app.utils'; +import { getPaymentExtension } from '../commercetools/extensions.commercetools'; +import { HttpDestination } from '@commercetools/platform-sdk/dist/declarations/src/generated/models/extension'; /** * Handles listing payment methods by payment. @@ -151,7 +153,9 @@ const getPaymentStatusUpdateAction = ( * @param ctPayment */ export const handleCreatePayment = async (ctPayment: Payment): Promise => { - const paymentParams = createMollieCreatePaymentParams(ctPayment); + const extensionUrl = (((await getPaymentExtension()) as Extension)?.destination as HttpDestination).url; + + const paymentParams = createMollieCreatePaymentParams(ctPayment, extensionUrl); const molliePayment = await createMolliePayment(paymentParams); diff --git a/processor/src/utils/map.utils.ts b/processor/src/utils/map.utils.ts index 34a6bd0..26d014f 100644 --- a/processor/src/utils/map.utils.ts +++ b/processor/src/utils/map.utils.ts @@ -93,7 +93,7 @@ const getSpecificPaymentParams = (method: PaymentMethod, paymentRequest: any) => } }; -export const createMollieCreatePaymentParams = (payment: Payment): PaymentCreateParams => { +export const createMollieCreatePaymentParams = (payment: Payment, extensionUrl: string): PaymentCreateParams => { const { amountPlanned, paymentMethodInfo } = payment; const [method, issuer] = paymentMethodInfo?.method?.split(',') ?? [null, null]; @@ -104,7 +104,7 @@ export const createMollieCreatePaymentParams = (payment: Payment): PaymentCreate payment.id, ); - const defaultWebhookEndpoint = new URL(process.env.CONNECT_SERVICE_URL ?? '').origin + '/webhook'; + const defaultWebhookEndpoint = new URL(extensionUrl).origin + '/webhook'; return { amount: makeMollieAmount(amountPlanned), diff --git a/processor/tests/service/payment.service.spec.ts b/processor/tests/service/payment.service.spec.ts index 8e55300..0849ddb 100644 --- a/processor/tests/service/payment.service.spec.ts +++ b/processor/tests/service/payment.service.spec.ts @@ -23,11 +23,17 @@ import CustomError from '../../src/errors/custom.error'; import { logger } from '../../src/utils/logger.utils'; import { getPaymentByMolliePaymentId, updatePayment } from '../../src/commercetools/payment.commercetools'; import { CreateParameters } from '@mollie/api-client/dist/types/src/binders/payments/refunds/parameters'; +import { getPaymentExtension } from '../../src/commercetools/extensions.commercetools'; + const uuid = '5c8b0375-305a-4f19-ae8e-07806b101999'; jest.mock('uuid', () => ({ v4: () => uuid, })); +jest.mock('../../src/commercetools/extensions.commercetools', () => ({ + getPaymentExtension: jest.fn(), +})); + jest.mock('../../src/commercetools/payment.commercetools', () => ({ getPaymentByMolliePaymentId: jest.fn(), updatePayment: jest.fn(), @@ -407,6 +413,11 @@ describe('Test createPayment', () => { } as molliePayment; (createMolliePayment as jest.Mock).mockReturnValueOnce(molliePayment); + (getPaymentExtension as jest.Mock).mockReturnValueOnce({ + destination: { + url: 'https://example.com', + }, + }); const actual = await handleCreatePayment(CTPayment); diff --git a/processor/tests/utils/map.utils.spec.ts b/processor/tests/utils/map.utils.spec.ts index e9b415c..1a90725 100644 --- a/processor/tests/utils/map.utils.spec.ts +++ b/processor/tests/utils/map.utils.spec.ts @@ -7,8 +7,6 @@ import { Payment } from '@commercetools/platform-sdk'; import { MethodsListParams, PaymentCreateParams, PaymentMethod } from '@mollie/api-client'; import { makeMollieAmount } from '../../src/utils/mollie.utils'; -const defaultWebhookEndpoint = new URL(process.env.CONNECT_SERVICE_URL ?? '').origin + '/webhook'; - describe('Test map.utils.ts', () => { let mockCtPayment: Payment; let mockMolObject: MethodsListParams; @@ -56,7 +54,7 @@ describe('Test map.utils.ts', () => { }); describe('createMollieCreatePaymentParams', () => { - it('should able to create a mollie payment params from CommerceTools payment object for with method as creditcard', () => { + it('should able to create a mollie payment params from CommerceTools payment object for with method as creditcard', async () => { const CTPayment: Payment = { id: '5c8b0375-305a-4f19-ae8e-07806b101999', version: 1, @@ -75,9 +73,9 @@ describe('createMollieCreatePaymentParams', () => { method: 'creditcard', }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment); - const defaultWebhookEndpoint = new URL(process.env.CONNECT_SERVICE_URL ?? '').origin + '/webhook'; + const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); const mollieAmount = makeMollieAmount(CTPayment.amountPlanned); expect(mollieCreatePaymentParams).toEqual({ @@ -88,7 +86,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: null, redirectUrl: null, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: '', applicationFee: {}, billingAddress: {}, @@ -101,7 +99,7 @@ describe('createMollieCreatePaymentParams', () => { }); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as creditcard which has custom field', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as creditcard which has custom field', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -137,8 +135,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: 'creditcard', @@ -148,7 +147,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, // Always use our default webhook endpoint + webhookUrl: extensionUrl, // Always use our default webhook endpoint description: customFieldObject.description, applicationFee: {}, billingAddress: {}, @@ -161,7 +160,7 @@ describe('createMollieCreatePaymentParams', () => { }); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as ideal', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as ideal', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -202,8 +201,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: PaymentMethod.ideal, amount: { @@ -212,7 +212,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: customFieldObject.description, issuer: 'ideal_TEST', include: customFieldObject.include, @@ -224,7 +224,7 @@ describe('createMollieCreatePaymentParams', () => { }); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as bancontact', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as bancontact', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -266,8 +266,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: PaymentMethod.bancontact, amount: { @@ -276,7 +277,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: customFieldObject.description, include: customFieldObject.include, issuer: '', @@ -288,7 +289,7 @@ describe('createMollieCreatePaymentParams', () => { }); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as banktransfer', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as banktransfer', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -325,8 +326,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: PaymentMethod.banktransfer, amount: { @@ -335,7 +337,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: customFieldObject.description, dueDate: customFieldObject.dueDate, billingEmail: customFieldObject.billingEmail, @@ -349,7 +351,7 @@ describe('createMollieCreatePaymentParams', () => { }); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as przelewy24', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as przelewy24', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -385,8 +387,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: PaymentMethod.przelewy24, amount: { @@ -395,7 +398,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: customFieldObject.description, billingEmail: customFieldObject.billingEmail, include: '', @@ -408,7 +411,7 @@ describe('createMollieCreatePaymentParams', () => { }); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as kbc', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as kbc', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -443,8 +446,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: PaymentMethod.kbc, amount: { @@ -453,7 +457,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: customFieldObject.description, include: '', issuer: '', @@ -515,7 +519,7 @@ describe('createMollieCreatePaymentParams', () => { // }); // }); - it('should able to create a mollie payment params from CommerceTools payment object with method as applepay', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as applepay', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -551,8 +555,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: PaymentMethod.applepay, amount: { @@ -561,7 +566,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: customFieldObject.description, applePayPaymentToken: customFieldObject.applePayPaymentToken, include: '', @@ -574,7 +579,7 @@ describe('createMollieCreatePaymentParams', () => { }); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as paypal', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as paypal', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -611,8 +616,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: PaymentMethod.paypal, amount: { @@ -621,7 +627,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: customFieldObject.description, sessionId: customFieldObject.sessionId, digitalGoods: customFieldObject.digitalGoods, @@ -635,7 +641,7 @@ describe('createMollieCreatePaymentParams', () => { }); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as giftcard', () => { + it('should able to create a mollie payment params from CommerceTools payment object with method as giftcard', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', @@ -672,8 +678,9 @@ describe('createMollieCreatePaymentParams', () => { }, }, }; + const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); expect(mollieCreatePaymentParams).toEqual({ method: PaymentMethod.giftcard, amount: { @@ -682,7 +689,7 @@ describe('createMollieCreatePaymentParams', () => { }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: defaultWebhookEndpoint, + webhookUrl: extensionUrl, description: customFieldObject.description, voucherNumber: customFieldObject.voucherNumber, voucherPin: customFieldObject.voucherPin, diff --git a/processor/tests/utils/mollie.utils.spec.ts b/processor/tests/utils/mollie.utils.spec.ts index 77949f0..b5cac58 100644 --- a/processor/tests/utils/mollie.utils.spec.ts +++ b/processor/tests/utils/mollie.utils.spec.ts @@ -1,38 +1,139 @@ -import { describe, test, expect, it } from '@jest/globals'; -import { isPayment, makeMollieAmount } from '../../src/utils/mollie.utils'; import { CentPrecisionMoney } from '@commercetools/platform-sdk'; +import { PaymentStatus } from '@mollie/api-client'; +import { CTMoney, CTTransactionState } from '../../src/types/commercetools.types'; +import { makeMollieAmount, makeCTMoney, isPayment, shouldPaymentStatusUpdate } from '../../src/utils/mollie.utils'; +import { Amount } from '@mollie/api-client/dist/types/src/data/global'; describe('Test mollie.utils.ts', () => { - test('call makeMollieAmount() with object reference', async () => { - const mockMoney = { - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - type: 'centPrecision', - } as unknown as CentPrecisionMoney; - - const response = makeMollieAmount(mockMoney); - - expect(response).toBeDefined(); - expect(response.value).toBe('10.00'); - expect(response.currency).toBe('EUR'); + describe('convertCTToMollieAmountValue', () => { + it('should convert cent amount to mollie amount with default fraction digits', () => { + const ctValue = 1234; + const expected = '12.34'; + expect((ctValue / 100).toFixed(2)).toBe(expected); + }); + + it('should convert cent amount to mollie amount with specified fraction digits', () => { + const ctValue = 12345; + const fractionDigits = 3; + const expected = '12.345'; + expect((ctValue / Math.pow(10, fractionDigits)).toFixed(fractionDigits)).toBe(expected); + }); }); - test('call makeMollieAmount() with no object reference', async () => { - const response = makeMollieAmount({} as CentPrecisionMoney); - expect(response).toBeDefined(); - expect(response.value).not.toBeNaN(); - expect(response.currency).toBeUndefined(); + describe('makeMollieAmount', () => { + it('should create a Mollie Amount from CentPrecisionMoney', () => { + const centPrecisionMoney: CentPrecisionMoney = { + centAmount: 1234, + fractionDigits: 2, + currencyCode: 'EUR', + } as CentPrecisionMoney; + + const expected: Amount = { + value: '12.34', + currency: 'EUR', + }; + + expect(makeMollieAmount(centPrecisionMoney)).toEqual(expected); + }); }); - it('call isPayment() should return true if the id starts with tr', () => { - expect(isPayment('tr_123')).toBeTruthy(); - expect(isPayment('tr_xxx')).toBeTruthy(); + describe('makeCTMoney', () => { + it('should create a CTMoney from a Mollie Amount', () => { + const mollieAmount: Amount = { + value: '12.34', + currency: 'EUR', + }; + + const expected: CTMoney = { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1234, + fractionDigits: 2, + }; + + expect(makeCTMoney(mollieAmount)).toEqual(expected); + }); + + it('should create a CTMoney with 0 fraction digits if no decimal part', () => { + const mollieAmount: Amount = { + value: '12', + currency: 'EUR', + }; + + const expected: CTMoney = { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 12 * Math.pow(10, 0), + fractionDigits: 0, + }; + + expect(makeCTMoney(mollieAmount)).toEqual(expected); + }); + + it('should round up the cent amount for positive values', () => { + const mollieAmount: Amount = { + value: '12.345', + currency: 'EUR', + }; + + const expected: CTMoney = { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: Math.ceil(12.345 * 1000), + fractionDigits: 3, + }; + + expect(makeCTMoney(mollieAmount)).toEqual(expected); + }); + + it('should round down the cent amount for negative values', () => { + const mollieAmount: Amount = { + value: '-12.345', + currency: 'EUR', + }; + + const expected: CTMoney = { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: Math.floor(-12.345 * 1000), + fractionDigits: 3, + }; + + expect(makeCTMoney(mollieAmount)).toEqual(expected); + }); + }); + + describe('isPayment', () => { + it('should return true for a valid payment resourceId', () => { + const resourceId = 'tr_123456'; + expect(isPayment(resourceId)).toBe(true); + }); + + it('should return false for an invalid payment resourceId', () => { + const resourceId = 'ord_123456'; + expect(isPayment(resourceId)).toBe(false); + }); }); - it('call isPayment() should return false if the id does not start with tr', () => { - expect(isPayment('order_123')).toBeFalsy(); - expect(isPayment('xxx_tr_123')).toBeFalsy(); - expect(isPayment('')).toBeFalsy(); + describe('shouldPaymentStatusUpdate', () => { + it('should return true if molliePaymentStatus is paid and ctTransactionState is not Success', () => { + expect(shouldPaymentStatusUpdate(PaymentStatus.paid, CTTransactionState.Initial)).toBe(true); + }); + + it('should return false if molliePaymentStatus is paid and ctTransactionState is Success', () => { + expect(shouldPaymentStatusUpdate(PaymentStatus.paid, CTTransactionState.Success)).toBe(false); + }); + + it('should return true if molliePaymentStatus is canceled and ctTransactionState is not Failure', () => { + expect(shouldPaymentStatusUpdate(PaymentStatus.canceled, CTTransactionState.Initial)).toBe(true); + }); + + it('should return false if molliePaymentStatus is canceled and ctTransactionState is Failure', () => { + expect(shouldPaymentStatusUpdate(PaymentStatus.canceled, CTTransactionState.Failure)).toBe(false); + }); + + it('should return false for unsupported molliePaymentStatus', () => { + expect(shouldPaymentStatusUpdate('unknown' as PaymentStatus, CTTransactionState.Initial)).toBe(false); + }); }); });