Skip to content

Commit

Permalink
Merge pull request #38 from mollie/feature/PICT-235/MOL-235
Browse files Browse the repository at this point in the history
PICT-235: Configure due date period for bank transfer payments
  • Loading branch information
Tung-Huynh-Shopmacher authored Aug 12, 2024
2 parents d7640d5 + 8b8956d commit 03dda24
Show file tree
Hide file tree
Showing 16 changed files with 494 additions and 14 deletions.
3 changes: 3 additions & 0 deletions connect.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ deployAs:
description: Debug mode (0 or 1)
required: false
default: "0"
- key: MOLLIE_BANK_TRANSFER_DUE_DATE
description: Payment method Bank Transfer due date (1d -> 100d)
default: "14d"
securedConfiguration:
- key: MOLLIE_API_TEST_KEY
description: Mollie PSP test API key
Expand Down
1 change: 1 addition & 0 deletions processor/.env.jest
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ MOLLIE_CARD_COMPONENT=0
CONNECTOR_MODE=test ## Either test or live
MOLLIE_API_TEST_KEY=test_12345
MOLLIE_API_LIVE_KEY=live_12345
MOLLIE_BANK_TRANSFER_DUE_DATE=

CONNECT_SERVICE_URL=http://localhost:3000/processor
1 change: 1 addition & 0 deletions processor/src/types/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ export type ConnectorEnvVars = {
profileId: string;
debug: string;
cardComponent: string;
bankTransferDueDate: string;
};
};
1 change: 1 addition & 0 deletions processor/src/utils/config.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const readConfiguration = () => {
debug: process.env.DEBUG as string,
profileId: process.env.MOLLIE_PROFILE_ID as string,
cardComponent: process.env.MOLLIE_CARD_COMPONENT as string,
bankTransferDueDate: process.env.MOLLIE_BANK_TRANSFER_DUE_DATE as string,
},
};

Expand Down
4 changes: 4 additions & 0 deletions processor/src/utils/constant.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ export const ErrorMessages = {
export const PAY_LATER_ENUMS = [PaymentMethod.klarnapaylater, PaymentMethod.klarnasliceit];

export const CancelStatusText = 'Cancelled from shop side';

export const DUE_DATE_PATTERN = /^(\d+)d$/;

export const DEFAULT_DUE_DATE = 14;
6 changes: 3 additions & 3 deletions processor/src/utils/map.utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { CustomFields } from './constant.utils';
import { logger } from './logger.utils';
import { makeMollieAmount } from './mollie.utils';
import { calculateDueDate, makeMollieAmount } from './mollie.utils';
import { CustomPaymentMethod, ParsedMethodsRequestType } from '../types/mollie.types';
import { Payment } from '@commercetools/platform-sdk';
import CustomError from '../errors/custom.error';
import { MethodsListParams, PaymentCreateParams, PaymentMethod } from '@mollie/api-client';
import { parseStringToJsonObject, removeEmptyProperties } from './app.utils';
import { readConfiguration } from './config.utils';

const extractMethodsRequest = (ctPayment: Payment): ParsedMethodsRequestType | undefined => {
return ctPayment?.custom?.fields?.[CustomFields.payment.request];
Expand Down Expand Up @@ -71,8 +72,7 @@ const getSpecificPaymentParams = (method: PaymentMethod | CustomPaymentMethod, p
return { applePayPaymentToken: paymentRequest.applePayPaymentToken ?? '' };
case PaymentMethod.banktransfer:
return {
dueDate: paymentRequest.dueDate ?? '',
billingEmail: paymentRequest.billingEmail ?? '',
dueDate: calculateDueDate(readConfiguration().mollie.bankTransferDueDate),
};
case PaymentMethod.przelewy24:
return { billingEmail: paymentRequest.billingEmail ?? '' };
Expand Down
27 changes: 27 additions & 0 deletions processor/src/utils/mollie.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { CentPrecisionMoney } from '@commercetools/platform-sdk';
import { Amount } from '@mollie/api-client/dist/types/src/data/global';
import { CTMoney, CTTransactionState } from '../types/commercetools.types';
import { PaymentStatus, RefundStatus } from '@mollie/api-client';
import { DEFAULT_DUE_DATE, DUE_DATE_PATTERN } from './constant.utils';
import { logger } from './logger.utils';
import CustomError from '../errors/custom.error';

const convertCTToMollieAmountValue = (ctValue: number, fractionDigits = 2): string => {
const divider = Math.pow(10, fractionDigits);
Expand Down Expand Up @@ -94,3 +97,27 @@ export const shouldRefundStatusUpdate = (
}
return shouldUpdate;
};

export const calculateDueDate = (input?: string): string => {
if (!input) {
input = DEFAULT_DUE_DATE + 'd';
}

const match = input.match(DUE_DATE_PATTERN);

if (match) {
const days = parseInt(match[1]);
if (!isNaN(days)) {
const today = new Date();
const futureDate = new Date(today.getTime() + days * 24 * 60 * 60 * 1000);

return futureDate.toISOString().split('T')[0] as string;
}
}

const errorMessage = `SCTM - calculateDueDate - Failed to calculate the due date, input: ${input}`;

logger.error(errorMessage);

throw new CustomError(400, errorMessage);
};
8 changes: 7 additions & 1 deletion processor/src/validators/env.validators.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { optional, standardKey, standardString, region } from './helpers.validators';
import { optional, standardKey, standardString, region, standardDueDate } from './helpers.validators';

/**
* Create here your own validators
Expand Down Expand Up @@ -102,6 +102,12 @@ const envValidators = [
max: 4,
},
),
standardDueDate(['mollie', 'bankTransferDueDate'], {
code: 'InvalidBankTransferDueDate',
message:
'Bank transfer due date must be from 1d to 100d, the number must be an integer. If it was not set, the default will be 14d',
referencedBy: 'environmentVariables',
}),
];

export default envValidators;
25 changes: 25 additions & 0 deletions processor/src/validators/helpers.validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import validator from 'validator';
import { ValidatorCreator, Wrapper } from '../types/index.types';
import { DUE_DATE_PATTERN } from '../utils/constant.utils';

/**
* File used to create helpers to validate the fields
Expand All @@ -20,6 +21,30 @@ export const standardString: ValidatorCreator = (path, message, overrideConfig =

export const standardEmail: ValidatorCreator = (path, message) => [path, [[required(validator.isEmail), message]]];

export const standardDueDate = (path, message) => [
path,
[
[
required((value) => {
if (!value) {
return true;
}

const match = value.match(DUE_DATE_PATTERN);

if (match) {
const days = parseInt(match[1]);

return days >= 1 && days <= 100;
}

return false;
}),
message,
],
],
];

export const standardNaturalNumber = (path, message) => [
path,
[[required((value) => validator.isNumeric(String(value), { no_symbols: true })), message]],
Expand Down
26 changes: 26 additions & 0 deletions processor/src/validators/payment.validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,32 @@ export const checkPaymentMethodSpecificParameters = (ctPayment: CTPayment, metho
break;
}

case MolliePaymentMethods.banktransfer: {
if (!paymentCustomFields?.billingAddress || !paymentCustomFields?.billingAddress?.email) {
logger.error(
`SCTM - PAYMENT PROCESSING - email is required for payment method banktransfer. Please make sure you have sent it in billingAddress.email of the custom field`,
{
commerceToolsPayment: ctPayment,
},
);

throw new CustomError(
400,
'SCTM - PAYMENT PROCESSING - email is required for payment method banktransfer. Please make sure you have sent it in billingAddress.email of the custom field',
);
}

if (!validateEmail(paymentCustomFields.billingAddress.email)) {
logger.error(`SCTM - PAYMENT PROCESSING - email must be a valid email address`, {
commerceToolsPayment: ctPayment,
});

throw new CustomError(400, 'SCTM - PAYMENT PROCESSING - email must be a valid email address');
}

break;
}

case CustomPaymentMethod.blik:
if (ctPayment.amountPlanned.currencyCode.toLowerCase() !== 'pln') {
logger.error(`SCTM - PAYMENT PROCESSING - Currency Code must be PLN for payment method BLIK`, {
Expand Down
8 changes: 8 additions & 0 deletions processor/tests/utils/config.utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { readConfiguration } from '../../src/utils/config.utils';
import CustomError from '../../src/errors/custom.error';
import { describe, expect, test } from '@jest/globals';

const env = process.env;

Check warning on line 5 in processor/tests/utils/config.utils.spec.ts

View workflow job for this annotation

GitHub Actions / build-processor

'env' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 5 in processor/tests/utils/config.utils.spec.ts

View workflow job for this annotation

GitHub Actions / build-processor

'env' is assigned a value but never used

describe('Test src/utils/config.utils.ts', () => {
test('should return the correct configuration when all env vars are valid', () => {
const config = readConfiguration();
Expand All @@ -20,6 +22,7 @@ describe('Test src/utils/config.utils.ts', () => {
debug: process.env.DEBUG,
profileId: process.env.MOLLIE_PROFILE_ID,
cardComponent: process.env.MOLLIE_CARD_COMPONENT,
bankTransferDueDate: process.env.MOLLIE_BANK_TRANSFER_DUE_DATE,
},
});
});
Expand Down Expand Up @@ -73,4 +76,9 @@ describe('Test src/utils/config.utils.ts', () => {
delete process.env.CONNECTOR_MODE;
expect(() => readConfiguration()).toThrow(CustomError);
});

test('should throw an error when MOLLIE_BANK_TRANSFER_DUE_DATE is invalid', () => {
process.env.MOLLIE_BANK_TRANSFER_DUE_DATE = 'dummy';
expect(() => readConfiguration()).toThrow(CustomError);
});
});
11 changes: 11 additions & 0 deletions processor/tests/utils/constant.utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
LIBRARY_NAME,
LIBRARY_VERSION,
PAY_LATER_ENUMS,
DUE_DATE_PATTERN,
DEFAULT_DUE_DATE,
} from '../../src/utils/constant.utils';
import { version } from '../../package.json';

Expand Down Expand Up @@ -67,4 +69,13 @@ describe('Test constant.utils.ts', () => {
expect(PAY_LATER_ENUMS).toContain('klarnapaylater');
expect(PAY_LATER_ENUMS).toContain('klarnasliceit');
});

test('should return correct {DUE_DATE_PATTERN} pattern', () => {
expect(DUE_DATE_PATTERN).toBeDefined();
});

test('should return correct {DEFAULT_DUE_DATE} pattern', () => {
expect(DEFAULT_DUE_DATE).toBeDefined();
expect(DEFAULT_DUE_DATE).toBe(14);
});
});
24 changes: 18 additions & 6 deletions processor/tests/utils/map.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { describe, test, expect, it } from '@jest/globals';
import { describe, test, expect, it, jest } from '@jest/globals';
import {
createMollieCreatePaymentParams,
mapCommercetoolsPaymentCustomFieldsToMollieListParams,
} from '../../src/utils/map.utils';
import { Payment } from '@commercetools/platform-sdk';
import { MethodsListParams, PaymentCreateParams, PaymentMethod } from '@mollie/api-client';
import { makeMollieAmount } from '../../src/utils/mollie.utils';
import { calculateDueDate, makeMollieAmount } from '../../src/utils/mollie.utils';
import { CustomPaymentMethod } from '../../src/types/mollie.types';

jest.mock('../../src/utils/mollie.utils.ts', () => ({
// @ts-expect-error ignore type error
...jest.requireActual('../../src/utils/mollie.utils.ts'),
// __esModule: true,
calculateDueDate: jest.fn(),
}));

describe('Test map.utils.ts', () => {
let mockCtPayment: Payment;
let mockMolObject: MethodsListParams;
Expand Down Expand Up @@ -267,8 +274,9 @@ describe('createMollieCreatePaymentParams', () => {
locale: 'en_GB',
redirectUrl: 'https://example.com/success',
webhookUrl: 'https://example.com/webhook',
dueDate: '2024-01-01',
billingEmail: '[email protected]',
billingAddress: {
email: '[email protected]',
},
};

const CTPayment: Payment = {
Expand Down Expand Up @@ -300,7 +308,11 @@ describe('createMollieCreatePaymentParams', () => {
};
const extensionUrl = 'https://example.com/webhook';

const dueDate = '2024-01-01';
(calculateDueDate as jest.Mock).mockReturnValueOnce(dueDate);

const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl);

expect(mollieCreatePaymentParams).toEqual({
method: PaymentMethod.banktransfer,
amount: {
Expand All @@ -311,8 +323,8 @@ describe('createMollieCreatePaymentParams', () => {
redirectUrl: customFieldObject.redirectUrl,
webhookUrl: extensionUrl,
description: customFieldObject.description,
dueDate: customFieldObject.dueDate,
billingEmail: customFieldObject.billingEmail,
billingAddress: customFieldObject.billingAddress,
dueDate,
});
});

Expand Down
30 changes: 29 additions & 1 deletion processor/tests/utils/mollie.utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import {
isPayment,
shouldPaymentStatusUpdate,
shouldRefundStatusUpdate,
calculateDueDate,
} from '../../src/utils/mollie.utils';
import { Amount } from '@mollie/api-client/dist/types/src/data/global';
import { expect, describe, it } from '@jest/globals';
import { expect, describe, it, test, jest } from '@jest/globals';
import { logger } from '../../src/utils/logger.utils';
import CustomError from '../../src/errors/custom.error';

describe('Test mollie.utils.ts', () => {
describe('convertCTToMollieAmountValue', () => {
Expand Down Expand Up @@ -179,4 +182,29 @@ describe('Test mollie.utils.ts', () => {
expect(shouldRefundStatusUpdate('unknown' as RefundStatus, CTTransactionState.Pending)).toBe(false);
});
});

describe('Test calculateDueDate', () => {
test('return the date which is 14 days later in format YYYY-MM-DD when the input is not defined', () => {
jest.useFakeTimers().setSystemTime(new Date('2024-01-01'));

expect(calculateDueDate()).toEqual('2024-01-15');
});

test('return the date which is to day + input day in format YYYY-MM-DD when the input is defined', () => {
jest.useFakeTimers().setSystemTime(new Date('2024-01-01'));

expect(calculateDueDate('5d')).toEqual('2024-01-06');
});

test('should throw error if no matches', () => {
try {
calculateDueDate('5');
} catch (error: unknown) {
expect(logger.error).toBeCalledTimes(1);
expect(logger.error).toBeCalledWith('SCTM - calculateDueDate - Failed to calculate the due date, input: 5');

expect(error).toBeInstanceOf(CustomError);
}
});
});
});
Loading

0 comments on commit 03dda24

Please sign in to comment.