Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PICT-235: Configure due date period for bank transfer payments #38

Merged
merged 2 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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),
Tung-Huynh-Shopmacher marked this conversation as resolved.
Show resolved Hide resolved
};
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 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 @@
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 @@
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