Skip to content

Commit

Permalink
Merge branch 'main' of github.com:mollie/commercetools-connector
Browse files Browse the repository at this point in the history
  • Loading branch information
Tung-Huynh-Shopmacher committed Aug 6, 2024
2 parents b5e580e + cade8d1 commit e667866
Show file tree
Hide file tree
Showing 11 changed files with 582 additions and 100 deletions.
9 changes: 6 additions & 3 deletions processor/src/mollie/payment.mollie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { logger } from '../utils/logger.utils';
import axios, { AxiosError } from 'axios';
import { readConfiguration } from '../utils/config.utils';
import { LIBRARY_NAME, LIBRARY_VERSION } from '../utils/constant.utils';
import { CustomPayment } from '../types/mollie.types';

/**
* Creates a Mollie payment using the provided payment parameters.
Expand Down Expand Up @@ -102,7 +103,7 @@ export const cancelPayment = async (paymentId: string): Promise<void> => {
}
};

export const createPaymentWithCustomMethod = async (paymentParams: PaymentCreateParams) => {
export const createPaymentWithCustomMethod = async (paymentParams: PaymentCreateParams): Promise<CustomPayment> => {
try {
const { mollie } = readConfiguration();

Expand All @@ -111,14 +112,16 @@ export const createPaymentWithCustomMethod = async (paymentParams: PaymentCreate
versionStrings: `${LIBRARY_NAME}/${LIBRARY_VERSION}`,
};

return await axios.post('https://api.mollie.com/v2/payments', paymentParams, { headers });
const response = await axios.post('https://api.mollie.com/v2/payments', paymentParams, { headers });

return response.data;
} catch (error: unknown) {
let errorMessage;

if (error instanceof AxiosError) {
errorMessage = `SCTM - createPaymentWithCustomMethod - error: ${error.response?.data?.detail}, field: ${error.response?.data?.field}`;
} else {
errorMessage = `SCTM - createPaymentWithCustomMethod - Failed to create a payment with unknown errors`;
errorMessage = 'SCTM - createPaymentWithCustomMethod - Failed to create a payment with unknown errors';
}

logger.error(errorMessage, {
Expand Down
8 changes: 2 additions & 6 deletions processor/src/service/payment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ 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';

/**
* Handles listing payment methods by payment.
Expand Down Expand Up @@ -257,7 +258,7 @@ export const handleCreatePayment = async (ctPayment: Payment): Promise<Controlle
* @return {Promise<UpdateAction[]>} A promise that resolves to an array of update actions.
* @throws {Error} If the original transaction is not found.
*/
export const getCreatePaymentUpdateAction = async (molliePayment: MPayment | any, CTPayment: Payment) => {
export const getCreatePaymentUpdateAction = async (molliePayment: MPayment | CustomPayment, CTPayment: Payment) => {
try {
// Find the original transaction which triggered create order
const originalTransaction = CTPayment.transactions?.find((transaction) => {
Expand Down Expand Up @@ -435,11 +436,6 @@ export const handleCancelPayment = async (ctPayment: Payment): Promise<Controlle
transaction.type === CTTransactionType.Authorization && transaction.state === CTTransactionState.Success,
);

const initialCancelAuthorizationTransaction = ctPayment.transactions.find(
(transaction) =>
transaction.type === CTTransactionType.CancelAuthorization && transaction.state === CTTransactionState.Initial,
);

const molliePayment = await getPaymentById(successAuthorizationTransaction?.interactionId as string);

if (molliePayment.isCancelable === false) {
Expand Down
12 changes: 12 additions & 0 deletions processor/src/types/mollie.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { PaymentData } from '@mollie/api-client/dist/types/src/data/payments/data';

export type ParsedMethodsRequestType = {
locale?: string;
billingCountry?: string;
Expand All @@ -7,3 +9,13 @@ export type ParsedMethodsRequestType = {
pricing?: string;
sequenceType?: string;
};

export enum CustomPaymentMethod {
blik = 'blik',
}

export type CustomPayment = Readonly<
Omit<PaymentData, 'method'> & {
method: CustomPaymentMethod;
}
>;
12 changes: 12 additions & 0 deletions processor/src/utils/app.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,15 @@ export function removeEmptyProperties(obj: object) {

return clonedObject;
}

/**
* Validates an email address using a regular expression.
*
* @param {string} email - The email address to validate.
* @return {boolean} Returns true if the email is valid, false otherwise.
*/
export function validateEmail(email: string): boolean {
const emailRegex: RegExp = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;

return emailRegex.test(email);
}
8 changes: 6 additions & 2 deletions processor/src/utils/map.utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CustomFields } from './constant.utils';
import { logger } from './logger.utils';
import { makeMollieAmount } from './mollie.utils';
import { ParsedMethodsRequestType } from '../types/mollie.types';
import { CustomPaymentMethod, 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';
Expand Down Expand Up @@ -65,7 +65,7 @@ export const mapCommercetoolsPaymentCustomFieldsToMollieListParams = async (
}
};

const getSpecificPaymentParams = (method: PaymentMethod, paymentRequest: any) => {
const getSpecificPaymentParams = (method: PaymentMethod | CustomPaymentMethod, paymentRequest: any) => {
switch (method) {
case PaymentMethod.applepay:
return { applePayPaymentToken: paymentRequest.applePayPaymentToken ?? '' };
Expand All @@ -88,6 +88,10 @@ const getSpecificPaymentParams = (method: PaymentMethod, paymentRequest: any) =>
};
case PaymentMethod.creditcard:
return { cardToken: paymentRequest.cardToken ?? '' };
case CustomPaymentMethod.blik:
return {
billingEmail: paymentRequest.billingEmail ?? '',
};
default:
return {};
}
Expand Down
105 changes: 73 additions & 32 deletions processor/src/validators/payment.validators.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Payment as CTPayment } from '@commercetools/platform-sdk';
import { PaymentMethod as MolliePaymentMethods, PaymentMethod } from '@mollie/api-client';
import { PaymentMethod as MolliePaymentMethods } from '@mollie/api-client';
import SkipError from '../errors/skip.error';
import CustomError from '../errors/custom.error';
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';
import { parseStringToJsonObject, validateEmail } from '../utils/app.utils';
import { readConfiguration } from '../utils/config.utils';
import { toBoolean } from 'validator';
import { CustomPaymentMethod } from '../types/mollie.types';

/**
* Checks if the given action is either 'Create' or 'Update'.
Expand Down Expand Up @@ -50,7 +51,7 @@ export const checkPaymentInterface = (ctPayment: CTPayment): true | SkipError =>
* @return {boolean} Returns true if the method is supported by Mollie
*/
export const hasValidPaymentMethod: (method: string | undefined) => boolean = (method: string | undefined): boolean => {
return !!MolliePaymentMethods[method as MolliePaymentMethods] || method?.toLowerCase() === 'blik';
return !!MolliePaymentMethods[method as MolliePaymentMethods] || !!CustomPaymentMethod[method as CustomPaymentMethod];
};

/**
Expand Down Expand Up @@ -95,8 +96,12 @@ export const checkPaymentMethodInput = (
);
}

if (method === PaymentMethod.creditcard) {
checkPaymentMethodSpecificParameters(ctPayment);
if (
[MolliePaymentMethods.creditcard, CustomPaymentMethod.blik].includes(
method as MolliePaymentMethods | CustomPaymentMethod,
)
) {
checkPaymentMethodSpecificParameters(ctPayment, method);
}

return true;
Expand Down Expand Up @@ -215,43 +220,79 @@ export const checkValidSuccessAuthorizationTransaction = (ctPayment: CTPayment):
* The `isInvalid` property indicates if the payment method input is invalid.
* The `errorMessage` property contains the error message if the input is invalid.
*/
export const checkPaymentMethodSpecificParameters = (ctPayment: CTPayment): void => {
export const checkPaymentMethodSpecificParameters = (ctPayment: CTPayment, method: string): void => {
const paymentCustomFields = parseStringToJsonObject(
ctPayment.custom?.fields?.[CustomFields.createPayment.request],
CustomFields.createPayment.request,
'SCTM - PAYMENT PROCESSING',
ctPayment.id,
);

const cardComponentEnabled = toBoolean(readConfiguration().mollie.cardComponent, true);
switch (method) {
case MolliePaymentMethods.creditcard: {
const cardComponentEnabled = toBoolean(readConfiguration().mollie.cardComponent, true);

if (cardComponentEnabled) {
if (!paymentCustomFields?.cardToken) {
logger.error(
`SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard, CommerceTools Payment ID: ${ctPayment.id}`,
{
commerceToolsPaymentId: ctPayment.id,
cardToken: paymentCustomFields?.cardToken,
},
);

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, CommerceTools Payment ID: ${ctPayment.id}`,
{
commerceToolsPaymentId: ctPayment.id,
cardToken: paymentCustomFields?.cardToken,
},
);

throw new CustomError(
400,
'SCTM - PAYMENT PROCESSING - cardToken must be a string and not empty for payment method creditcard',
);
}
}

if (cardComponentEnabled) {
if (!paymentCustomFields?.cardToken) {
logger.error(
`SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard, CommerceTools Payment ID: ${ctPayment.id}`,
{
commerceToolsPaymentId: ctPayment.id,
cardToken: paymentCustomFields?.cardToken,
},
);

throw new CustomError(400, 'SCTM - PAYMENT PROCESSING - cardToken is required for payment method creditcard');
break;
}

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, CommerceTools Payment ID: ${ctPayment.id}`,
{
commerceToolsPaymentId: ctPayment.id,
cardToken: paymentCustomFields?.cardToken,
},
);

throw new CustomError(
400,
'SCTM - PAYMENT PROCESSING - cardToken must be a string and not empty for payment method creditcard',
);
}
case CustomPaymentMethod.blik:
if (ctPayment.amountPlanned.currencyCode.toLowerCase() !== 'pln') {
logger.error(`SCTM - PAYMENT PROCESSING - Currency Code must be PLN for payment method BLIK`, {
commerceToolsPayment: ctPayment,
});

throw new CustomError(400, 'SCTM - PAYMENT PROCESSING - Currency Code must be PLN for payment method BLIK');
}

if (!paymentCustomFields?.billingEmail) {
logger.error(`SCTM - PAYMENT PROCESSING - billingEmail is required for payment method BLIK`, {
commerceToolsPayment: ctPayment,
});

throw new CustomError(400, 'SCTM - PAYMENT PROCESSING - billingEmail is required for payment method BLIK');
}

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

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

break;

default:
break;
}
};

Expand Down
Loading

0 comments on commit e667866

Please sign in to comment.