From fb403c142248b4cf0514b9e679b9d0aa6d68f219 Mon Sep 17 00:00:00 2001 From: NghiaDTr Date: Wed, 11 Dec 2024 15:13:17 +0700 Subject: [PATCH] Revert "Bugfix/MOL-587: Resolve develop" --- CHANGELOG.md | 195 ---- application/custom-application-config.ts | 2 +- .../cypress/fixtures/fetch-project.json | 2 +- .../src/components/welcome/welcome.tsx | 32 +- .../use-custom-objects-connector.ts | 6 +- .../use-mollie-connector.ts | 3 - application/src/types/app.ts | 96 +- docs/CancelPaymentRefund.md | 6 - docs/CreateRefund.md | 10 +- processor/package-lock.json | 2 +- processor/package.json | 2 +- .../customFields.commercetools.ts | 82 +- processor/src/service/connector.service.ts | 4 - processor/src/service/payment.service.ts | 112 +- processor/src/types/mollie.types.ts | 1 - processor/src/utils/app.utils.ts | 21 +- processor/src/utils/constant.utils.ts | 1 - processor/src/utils/paymentAction.utils.ts | 6 +- processor/tests/mollie/payment.mollie.spec.ts | 63 +- processor/tests/mollie/profile.mollie.spec.ts | 26 - .../tests/routes/processor.route.spec.ts | 6 - .../tests/service/payment.service.spec.ts | 977 +----------------- processor/tests/types/mollie.types.spec.ts | 25 +- processor/tests/utils/app.utils.spec.ts | 63 +- processor/tests/utils/config.utils.spec.ts | 14 +- 25 files changed, 84 insertions(+), 1673 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 246f994..b077070 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,201 +4,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## v1.2.1 - -Added - -- New custom field for transaction: `sctm_transaction_refund_for_mollie_payment` which would store the Mollie Payment ID that need to be refunded - -Fixes - -[Create Refund](./docs/CreateRefund.md) -- Handling the Refund Creation for the case that the Payment has more than one Success Charge transaction - - Changing the way to determine the Create Refund action: - - Before - ```Typescript - // processor/src/utils/paymentAction.utils.ts - - if (groups.successCharge.length === 1 && groups.initialRefund.length) { - return ConnectorActions.CreateRefund; - } - ``` - - - After - ```Typescript - // processor/src/utils/paymentAction.utils.ts - - if (groups.successCharge.length >= 1 && groups.initialRefund.length) { - return ConnectorActions.CreateRefund; - } - ``` - - - We are supporting to create the refund for the payment which has more than one Success Charge transactions - - By default, we will create the Refund for the latest Success Charge transaction. For example: - ```Typescript - // CommerceTools Payment - { - id: 'payment-id', - transactions: [ - { - type: 'Charge', - state: 'Success', - interactionId: 'tr_123456' // Mollie Payment ID - }, - { - type: 'Charge', - state: 'Success', - interactionId: 'tr_999999' // Mollie Payment ID - }, - { - type: 'Refund', - state: 'Initial', // Creating a Refund for the Mollie Payment tr_999999 - }, - ] - } - ``` - - - However, you can also specify the Mollie Payment ID (which stored in the `interactionId` of the Success Charge transaction) that you want to create a refund for by adding the Mollie Payment ID to the custom field `sctm_transaction_refund_for_mollie_payment` of the Initial Refund transaction. For example: - - ```Typescript - // CommerceTools Payment - { - id: 'payment-id', - transactions: [ - { - type: 'Charge', - state: 'Success', - interactionId: 'tr_123456' // Mollie Payment ID - }, - { - type: 'Charge', - state: 'Success', - interactionId: 'tr_999999' // Mollie Payment ID - }, - { - type: 'Refund', - state: 'Initial', - custom: { - type: { - ... - }, - fields: { - sctm_transaction_refund_for_mollie_payment: 'tr_123456' // Creating a Refund for the Mollie Payment tr_123456 - } - } - }, - ] - } - ``` - -[Cancel Refund](./docs/CancelPaymentRefund.md) -- Following the changes for creating refund, we also updated the handler for Refund Cancellation to match with the above changes - - Changing the way to determine the Cancel Refund action: - - Before - ```Typescript - // processor/src/utils/paymentAction.utils.ts - - if ( - groups.successCharge.length === 1 && - groups.pendingRefund.length === 1 && - groups.initialCancelAuthorization.length === 1 - ) { - return ConnectorActions.CancelRefund; - } - ``` - - - After - ```Typescript - // processor/src/utils/paymentAction.utils.ts - - if ( - groups.successCharge.length >= 1 && - groups.pendingRefund.length >= 1 && - groups.initialCancelAuthorization.length === 1 - ) { - return ConnectorActions.CancelRefund; - } - ``` - - - To support the old versions, we will create the cancellation for the latest Pending Refund transaction (which is a pending refund for the latest Success Charge transaction in that payment). For example: - ```Typescript - // CommerceTools Payment - { - id: 'payment-id', - transactions: [ - { - type: 'Charge', - state: 'Success', - interactionId: 'tr_123456' // Mollie Payment ID - }, - { - type: 'Charge', - state: 'Success', - interactionId: 'tr_999999' // Mollie Payment ID - }, - { - id: 'refund-transaction-1', - type: 'Refund', - state: 'Pending', - interactionId: 're_123456', // Mollie Refund ID - }, - { - id: 'refund-transaction-2', - type: 'Refund', - state: 'Pending', - interactionId: 're_999999', // Mollie Refund ID - }, - { - type: 'CancelAuthorization', - state: 'Initial' - // interactionId is not set - } - ] - } - - // In this case, this will be considered as a Cancellation request for the Pending Refund with id: refund-transaction-2 - ``` - __*Note:* The above solution is just for supporting the old versions and will be remove in the near future (in next versions). From this version, please follow the below solution.__ - - - However, to do it in a correct way, from this version, you should specify the Mollie Refund ID (which stored in the `interactionId` of the Pending Refund transaction) that you want to cancel by putting it in the `interactionId` of the Initial CancelAuthorization. For example: - ```Typescript - // CommerceTools Payment - { - id: 'payment-id', - transactions: [ - { - type: 'Charge', - state: 'Success', - interactionId: 'tr_123456' // Mollie Payment ID - }, - { - type: 'Charge', - state: 'Success', - interactionId: 'tr_999999' // Mollie Payment ID - }, - { - id: 'refund-transaction-1', - type: 'Refund', - state: 'Pending', - interactionId: 're_123456', // Mollie Refund ID - }, - { - id: 'refund-transaction-2', - type: 'Refund', - state: 'Pending', - interactionId: 're_999999', // Mollie Refund ID - }, - { - type: 'CancelAuthorization', - state: 'Initial', - interactionId: 're_123456' // Mollie Refund ID that you want to cancel - } - ] - } - - // In this case, this will be considered as a Cancellation request for the Pending Refund with id: refund-transaction-1 - ``` - ## v1.2.0 Added diff --git a/application/custom-application-config.ts b/application/custom-application-config.ts index 5a8d35f..95fe782 100644 --- a/application/custom-application-config.ts +++ b/application/custom-application-config.ts @@ -15,7 +15,7 @@ const config = { cloudIdentifier: CLOUD_IDENTIFIER, env: { development: { - initialProjectKey: 'your_project_key', + initialProjectKey: 'shopm-adv-windev', }, production: { applicationId: CUSTOM_APPLICATION_ID, diff --git a/application/cypress/fixtures/fetch-project.json b/application/cypress/fixtures/fetch-project.json index 9807ab7..bf40513 100644 --- a/application/cypress/fixtures/fetch-project.json +++ b/application/cypress/fixtures/fetch-project.json @@ -420,4 +420,4 @@ "__typename": "Project" } } -} +} \ No newline at end of file diff --git a/application/src/components/welcome/welcome.tsx b/application/src/components/welcome/welcome.tsx index ffca574..bcd4e80 100644 --- a/application/src/components/welcome/welcome.tsx +++ b/application/src/components/welcome/welcome.tsx @@ -17,7 +17,7 @@ import DataTable from '@commercetools-uikit/data-table'; import IconButton from '@commercetools-uikit/icon-button'; import { usePaymentMethodsFetcher } from '../../hooks/use-mollie-connector'; import { ContentNotification } from '@commercetools-uikit/notifications'; -import { CustomMethodObject, CustomObjectUpdaterError } from '../../types/app'; +import { CustomMethodObject } from '../../types/app'; import LoadingSpinner from '@commercetools-uikit/loading-spinner'; import Tootltip from '@commercetools-uikit/tooltip'; import { @@ -74,7 +74,7 @@ const Welcome = () => { key: 'key', order: 'asc', }); - const { customObjectsPaginatedResult, error, loading, refetch } = + const { customObjectsPaginatedResult, error, loading } = useCustomObjectsFetcher({ page, perPage, @@ -110,13 +110,7 @@ const Welcome = () => { value: JSON.stringify(method), }) .catch((error) => { - Object.values(error).forEach((e) => { - console.error( - `SCTM custom application: ${ - (e as CustomObjectUpdaterError).message - }` - ); - }); + console.error(`Error creating custom object: ${error}`); }); return method; } else { @@ -126,15 +120,9 @@ const Welcome = () => { } }) ); - refetch(); setMethods(updatedMethods); } - }, [ - customObjectUpdater, - customObjectsPaginatedResult?.results, - fetchedData, - refetch, - ]); + }, [customObjectUpdater, customObjectsPaginatedResult?.results, fetchedData]); useEffect(() => { if ( @@ -224,13 +212,13 @@ const Welcome = () => { sortDirection={tableSorting.value.order} onSortChange={tableSorting.onChange} onRowClick={(row) => { - const target = customObjectsPaginatedResult?.results.filter( - (obj) => obj.key === row.id + push( + `${match.url}/${ + customObjectsPaginatedResult?.results.filter( + (obj) => obj.key === row.id + )?.[0]?.id + }/general` ); - - if (target) { - push(`${match.url}/${target[0].id}/general`); - } }} /> diff --git a/application/src/hooks/use-custom-objects-connector/use-custom-objects-connector.ts b/application/src/hooks/use-custom-objects-connector/use-custom-objects-connector.ts index 92be795..4de4fca 100644 --- a/application/src/hooks/use-custom-objects-connector/use-custom-objects-connector.ts +++ b/application/src/hooks/use-custom-objects-connector/use-custom-objects-connector.ts @@ -21,7 +21,7 @@ import FetchCustomObjectsQuery from './fetch-custom-objects.ctp.graphql'; import FetchCustomObjectDetailsQuery from './fetch-custom-object-details.ctp.graphql'; import UpdateCustomObjectDetailsMutation from './update-custom-object-details.ctp.graphql'; import RemoveCustomObjectDetailsMutation from './remove-custom-object-details.ctp.graphql'; -import { ApolloError, ApolloQueryResult } from '@apollo/client'; +import { ApolloError } from '@apollo/client'; import { extractErrorFromGraphQlResponse } from '../../helpers'; type PaginationAndSortingProps = { @@ -37,7 +37,6 @@ type TUseCustomObjectsFetcher = ( customObjectsPaginatedResult?: TFetchCustomObjectsQuery['customObjects']; error?: ApolloError; loading: boolean; - refetch: () => Promise>; }; export const useCustomObjectsFetcher: TUseCustomObjectsFetcher = ({ @@ -46,7 +45,7 @@ export const useCustomObjectsFetcher: TUseCustomObjectsFetcher = ({ tableSorting, container, }) => { - const { data, error, loading, refetch } = useMcQuery< + const { data, error, loading } = useMcQuery< TFetchCustomObjectsQuery, TFetchCustomObjectsQueryVariables >(FetchCustomObjectsQuery, { @@ -65,7 +64,6 @@ export const useCustomObjectsFetcher: TUseCustomObjectsFetcher = ({ customObjectsPaginatedResult: data?.customObjects, error, loading, - refetch, }; }; type TUseCustomObjectDetailsFetcher = (id: string) => { diff --git a/application/src/hooks/use-mollie-connector/use-mollie-connector.ts b/application/src/hooks/use-mollie-connector/use-mollie-connector.ts index 0bd2a0e..ba85ba4 100644 --- a/application/src/hooks/use-mollie-connector/use-mollie-connector.ts +++ b/application/src/hooks/use-mollie-connector/use-mollie-connector.ts @@ -15,7 +15,6 @@ import { CustomMethodObject, MollieResult, SupportedPaymentMethods, - GooglePay, } from '../../types/app'; /** @@ -46,8 +45,6 @@ const convertMollieMethodToCustomMethod = ( method.status === 'activated' && SupportedPaymentMethods[method.id as SupportedPaymentMethods] ); - - availableMethods.push(GooglePay); return availableMethods.map((method: MollieMethod) => ({ id: method.id, technicalName: method.description, diff --git a/application/src/types/app.ts b/application/src/types/app.ts index 7ddc820..83d7cf1 100644 --- a/application/src/types/app.ts +++ b/application/src/types/app.ts @@ -16,13 +16,8 @@ export type MollieMethod = { currency: string; }; pricing: { - description: string; - fixed: { - value: string; - currency: string; - }; - variable: string; - feeRegion?: string; + value: string; + currency: string; }[]; status: string; _links: { @@ -71,90 +66,3 @@ export enum SupportedPaymentMethods { paypal = 'paypal', giftcard = 'giftcard', } - -export const GooglePay = { - resource: 'method', - id: 'googlepay', - description: 'Google Pay', - minimumAmount: { - value: '0.01', - currency: 'EUR', - }, - maximumAmount: { - value: '10000.00', - currency: 'EUR', - }, - image: { - size1x: - 'https://www.mollie.com/external/icons/payment-methods/googlepay.png', - size2x: - 'https://www.mollie.com/external/icons/payment-methods/googlepay%402x.png', - svg: 'https://www.mollie.com/external/icons/payment-methods/googlepay.svg', - }, - status: 'activated', - pricing: [ - { - description: 'American Express (intra-EEA)', - fixed: { - value: '0.25', - currency: 'EUR', - }, - variable: '2.9', - feeRegion: 'amex-intra-eea', - }, - { - description: 'Commercial & non-European cards', - fixed: { - value: '0.25', - currency: 'EUR', - }, - variable: '3.25', - feeRegion: 'other', - }, - { - description: 'Domestic consumer cards', - fixed: { - value: '0.25', - currency: 'EUR', - }, - variable: '1.8', - feeRegion: 'domestic', - }, - { - description: 'European commercial cards', - fixed: { - value: '0.25', - currency: 'EUR', - }, - variable: '2.9', - feeRegion: 'intra-eu-corporate', - }, - { - description: 'European consumer cards', - fixed: { - value: '0.25', - currency: 'EUR', - }, - variable: '1.8', - feeRegion: 'eu-cards', - }, - { - description: 'Pre-authorization fees', - fixed: { - value: '0.00', - currency: 'EUR', - }, - variable: '0.12', - }, - ], - _links: { - self: { - href: 'https://api.mollie.com/v2/methods/googlepay', - type: 'application/hal+json', - }, - }, -}; - -export interface CustomObjectUpdaterError { - message: string; -} diff --git a/docs/CancelPaymentRefund.md b/docs/CancelPaymentRefund.md index 13a6738..e5d9f42 100644 --- a/docs/CancelPaymentRefund.md +++ b/docs/CancelPaymentRefund.md @@ -3,7 +3,6 @@ * [Parameters map](#parameters-map) * [Representation: CT Payment](#representation-ct-payment) * [Creating CommerceTools actions from Mollie's response](#creating-commercetools-actions-from-mollies-response) - * [Update per version](#update-per-version) ## Overview This functionality is used to cancel the pending refund which means it is created but not complete yet. @@ -156,8 +155,3 @@ When order is successfully cancelled on Mollie, we update commercetools payment | `changeTransactionState` | `transactionId: , state: 'Failure'` | | `changeTransactionState` | `transactionId: , state: 'Success'` | | `setTransactionCustomType` | `transactionId: , type.key:sctm_payment_cancel_reason, fields: {reasonText: "cancellation reason", statusText: "cancelled from shop side"}` | - -## Update per version - -The function was updated at: -- [v1.2.1](../CHANGELOG.md#v121) \ No newline at end of file diff --git a/docs/CreateRefund.md b/docs/CreateRefund.md index 6f7fbc8..58067ed 100644 --- a/docs/CreateRefund.md +++ b/docs/CreateRefund.md @@ -4,7 +4,6 @@ * [Parameters map](#parameters-map) * [Representation: CommerceTools Payment](#representation-ct-payment) * [Creating commercetools actions from Mollie's response](#creating-commercetools-actions-from-mollies-response) - * [Update per version](#update-per-version) ## Overview @@ -25,6 +24,8 @@ A transaction with type "Refund" and state "Initial" triggers a refund. In commercetools, we have a Payment which has one Transaction. This maps to an order in mollie. The commercetools Payment's key is the mollie orderId, and the commercetools Transaction maps to the payment in mollie. +In commercetools, we have a Payment which has one Transaction. This maps to an order in mollie. The commercetools Payment's key is the mollie orderId, and the commercetools Transaction maps to the payment in mollie. + ``` { id: "c0887a2d-bfbf-4f77-8f3d-fc33fb4c0920", @@ -95,9 +96,4 @@ transactions: [ ] ``` -When the refund is completed, this transaction's state will be updated by the notifications module to "Success" or "Failure". - -## Update per version - -The function was updated at: -- [v1.2.1](../CHANGELOG.md#v121) \ No newline at end of file +When the refund is completed, this transaction's state will be updated by the notifications module to "Success" or "Failure". \ No newline at end of file diff --git a/processor/package-lock.json b/processor/package-lock.json index fdb27c8..a4b3130 100644 --- a/processor/package-lock.json +++ b/processor/package-lock.json @@ -9458,4 +9458,4 @@ } } } -} +} \ No newline at end of file diff --git a/processor/package.json b/processor/package.json index b4bd3a6..c0c0c2d 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": "1.2.1", + "version": "1.2.0", "main": "index.js", "private": true, "scripts": { diff --git a/processor/src/commercetools/customFields.commercetools.ts b/processor/src/commercetools/customFields.commercetools.ts index 0d1c507..7bd9e4e 100644 --- a/processor/src/commercetools/customFields.commercetools.ts +++ b/processor/src/commercetools/customFields.commercetools.ts @@ -333,7 +333,7 @@ export async function createTransactionSurchargeCustomType(): Promise { .types() .post({ body: { - key: CustomFields.transactionSurchargeCost, + key: CustomFields.createPayment.interfaceInteraction.key, name: { en: 'SCTM - Transaction surcharge amount', de: 'SCTM - Betrag des Transaktionszuschlags', @@ -379,83 +379,3 @@ export async function createTransactionSurchargeCustomType(): Promise { return; } } - -export async function createTransactionRefundForMolliePaymentCustomType(): Promise { - const apiRoot = createApiRoot(); - const customFields: FieldDefinition[] = [ - { - name: CustomFields.transactionRefundForMolliePayment, - label: { - en: 'Identify the Mollie payment which is being refunded', - de: 'Identifizieren Sie die Mollie-Zahlung, die zurückerstattet wird', - }, - required: false, - type: { - name: 'String', - }, - inputHint: 'MultiLine', - }, - ]; - - const { - body: { results: types }, - } = await apiRoot - .types() - .get({ - queryArgs: { - where: `key = "${CustomFields.transactionRefundForMolliePayment}"`, - }, - }) - .execute(); - - if (types.length <= 0) { - await apiRoot - .types() - .post({ - body: { - key: CustomFields.transactionRefundForMolliePayment, - name: { - en: 'Identify the Mollie payment which is being refunded', - de: 'Identifizieren Sie die Mollie-Zahlung, die zurückerstattet wird', - }, - resourceTypeIds: ['transaction'], - fieldDefinitions: customFields, - }, - }) - .execute(); - - return; - } - - const type = types[0]; - const definitions = type.fieldDefinitions; - - if (definitions.length > 0) { - const actions: TypeUpdateAction[] = []; - definitions.forEach((definition) => { - actions.push({ - action: 'removeFieldDefinition', - fieldName: definition.name, - }); - }); - customFields.forEach((field) => { - actions.push({ - action: 'addFieldDefinition', - fieldDefinition: field, - }); - }); - - await apiRoot - .types() - .withKey({ key: CustomFields.transactionRefundForMolliePayment }) - .post({ - body: { - version: type.version, - actions, - }, - }) - .execute(); - - return; - } -} diff --git a/processor/src/service/connector.service.ts b/processor/src/service/connector.service.ts index eeb0115..ad68651 100644 --- a/processor/src/service/connector.service.ts +++ b/processor/src/service/connector.service.ts @@ -3,8 +3,6 @@ import { createCustomPaymentType, createCustomPaymentInterfaceInteractionType, createCustomPaymentTransactionCancelReasonType, - createTransactionSurchargeCustomType, - createTransactionRefundForMolliePaymentCustomType, } from '../commercetools/customFields.commercetools'; import { getAccessToken } from '../commercetools/auth.commercetools'; @@ -14,8 +12,6 @@ export const createExtensionAndCustomFields = async (extensionUrl: string): Prom await createCustomPaymentType(); await createCustomPaymentInterfaceInteractionType(); await createCustomPaymentTransactionCancelReasonType(); - await createTransactionSurchargeCustomType(); - await createTransactionRefundForMolliePaymentCustomType(); }; export const removeExtension = async (): Promise => { diff --git a/processor/src/service/payment.service.ts b/processor/src/service/payment.service.ts index 39f7951..da91c83 100644 --- a/processor/src/service/payment.service.ts +++ b/processor/src/service/payment.service.ts @@ -71,7 +71,6 @@ import { convertCentToEUR, parseStringToJsonObject, roundSurchargeAmountToCent, - sortTransactionsByLatestCreationTime, } from '../utils/app.utils'; import ApplePaySession from '@mollie/api-client/dist/types/src/data/applePaySession/ApplePaySession'; import { getMethodConfigObjects, getSingleMethodConfigObject } from '../commercetools/customObjects.commercetools'; @@ -86,14 +85,6 @@ import { removeCartMollieCustomLineItem } from './cart.service'; * @return {CustomMethod[]} - The validated and sorted payment methods. */ const validateAndSortMethods = (methods: CustomMethod[], configObjects: CustomObject[]): CustomMethod[] => { - methods.push({ - id: 'googlepay', - name: { 'en-GB': 'Google Pay' }, - description: { 'en-GB': '' }, - image: '', - order: 0, - }); - if (!configObjects.length) { return methods.filter( (method: CustomMethod) => SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods], @@ -443,8 +434,6 @@ export const handleCreatePayment = async (ctPayment: Payment): Promise => { - let successChargeTransaction; - const updateActions = [] as UpdateAction[]; + const successChargeTransaction = ctPayment.transactions.find( + (transaction) => transaction.type === CTTransactionType.Charge && transaction.state === CTTransactionState.Success, + ); const initialRefundTransaction = ctPayment.transactions.find( (transaction) => transaction.type === CTTransactionType.Refund && transaction.state === CTTransactionState.Initial, ); - if (initialRefundTransaction?.custom?.fields[CustomFields.transactionRefundForMolliePayment]) { - logger.debug('SCTM - handleCreateRefund - creating a refund with specific payment id'); - - successChargeTransaction = ctPayment.transactions.find( - (transaction) => - transaction.type === CTTransactionType.Charge && - transaction.state === CTTransactionState.Success && - transaction.interactionId === - initialRefundTransaction?.custom?.fields[CustomFields.transactionRefundForMolliePayment], - ); - } else { - logger.debug('SCTM - handleCreateRefund - creating a refund for the latest success charge transaction'); - - const latestTransactions = sortTransactionsByLatestCreationTime(ctPayment.transactions); - - successChargeTransaction = latestTransactions.find( - (transaction) => - transaction.type === CTTransactionType.Charge && transaction.state === CTTransactionState.Success, - ); - - updateActions.push( - setTransactionCustomType(initialRefundTransaction?.id as string, CustomFields.transactionRefundForMolliePayment, { - [CustomFields.transactionRefundForMolliePayment]: successChargeTransaction?.interactionId, - }), - ); - } - - if (!successChargeTransaction) { - throw new CustomError(400, 'SCTM - handleCreateRefund - Cannot find valid success charge transaction'); - } - const paymentCreateRefundParams: CreateParameters = { paymentId: successChargeTransaction?.interactionId as string, amount: makeMollieAmount(initialRefundTransaction?.amount as CentPrecisionMoney), @@ -572,14 +531,12 @@ export const handleCreateRefund = async (ctPayment: Payment): Promise => { - let pendingRefundTransaction: any; - let successChargeTransaction: any; + const successChargeTransaction = ctPayment.transactions.find( + (transaction) => transaction.type === CTTransactionType.Charge && transaction.state === CTTransactionState.Success, + ); + + const pendingRefundTransaction = ctPayment.transactions.find( + (transaction) => transaction.type === CTTransactionType.Refund && transaction.state === CTTransactionState.Pending, + ); const initialCancelAuthorization = ctPayment.transactions.find( (transaction) => transaction.type === CTTransactionType.CancelAuthorization && transaction.state === CTTransactionState.Initial, ); - if (initialCancelAuthorization?.interactionId) { - pendingRefundTransaction = ctPayment.transactions.find( - (transaction) => - transaction.type === CTTransactionType.Refund && - transaction.state === CTTransactionState.Pending && - transaction?.interactionId === initialCancelAuthorization.interactionId, - ) as Transaction; - - if (pendingRefundTransaction) { - successChargeTransaction = ctPayment.transactions.find( - (transaction) => - transaction.type === CTTransactionType.Charge && - transaction.state === CTTransactionState.Success && - transaction.interactionId === - pendingRefundTransaction?.custom?.fields[CustomFields.transactionRefundForMolliePayment], - ) as Transaction; - } - - if (!successChargeTransaction) { - throw new CustomError( - 400, - 'SCTM - handlePaymentCancelRefund - Cannot find the valid Success Charge transaction.', - ); - } - } - - /** - * @deprecated v1.2 - Will be remove in the next version - */ - if (!pendingRefundTransaction || !successChargeTransaction) { - const latestTransactions = sortTransactionsByLatestCreationTime(ctPayment.transactions); - - pendingRefundTransaction = latestTransactions.find( - (transaction) => - transaction.type === CTTransactionType.Refund && transaction.state === CTTransactionState.Pending, - ); - - successChargeTransaction = latestTransactions.find( - (transaction) => - transaction.type === CTTransactionType.Charge && transaction.state === CTTransactionState.Success, - ); - } - /** - * end deprecated - */ - const paymentGetRefundParams: CancelParameters = { paymentId: successChargeTransaction?.interactionId as string, }; diff --git a/processor/src/types/mollie.types.ts b/processor/src/types/mollie.types.ts index eccebb9..f91d4b4 100644 --- a/processor/src/types/mollie.types.ts +++ b/processor/src/types/mollie.types.ts @@ -35,5 +35,4 @@ export enum SupportedPaymentMethods { applepay = 'applepay', paypal = 'paypal', giftcard = 'giftcard', - googlepay = 'googlepay', } diff --git a/processor/src/utils/app.utils.ts b/processor/src/utils/app.utils.ts index fbfe82e..5c30a59 100644 --- a/processor/src/utils/app.utils.ts +++ b/processor/src/utils/app.utils.ts @@ -1,5 +1,5 @@ import { SurchargeCost } from './../types/commercetools.types'; -import { Payment, Transaction } from '@commercetools/platform-sdk'; +import { Payment } from '@commercetools/platform-sdk'; import CustomError from '../errors/custom.error'; import { logger } from './logger.utils'; /** @@ -101,22 +101,3 @@ export const calculateTotalSurchargeAmount = (ctPayment: Payment, surcharges?: S export const roundSurchargeAmountToCent = (surchargeAmountInEur: number, fractionDigits: number): number => { return Math.round(surchargeAmountInEur * Math.pow(10, fractionDigits)); }; - -export const sortTransactionsByLatestCreationTime = (transactions: Transaction[]): Transaction[] => { - const clonedTransactions = Object.assign([], transactions); - - return clonedTransactions.sort((a: Transaction, b: Transaction) => { - const timeA = a.timestamp as string; - const timeB = b.timestamp as string; - - if (timeA < timeB) { - return 1; - } - - if (timeA > timeB) { - return -1; - } - - return 0; - }); -}; diff --git a/processor/src/utils/constant.utils.ts b/processor/src/utils/constant.utils.ts index d060fdf..3e39cee 100644 --- a/processor/src/utils/constant.utils.ts +++ b/processor/src/utils/constant.utils.ts @@ -39,7 +39,6 @@ export const CustomFields = { }, }, transactionSurchargeCost: 'sctm_transaction_surcharge_cost', - transactionRefundForMolliePayment: 'sctm_transaction_refund_for_mollie_payment', }; export enum ConnectorActions { diff --git a/processor/src/utils/paymentAction.utils.ts b/processor/src/utils/paymentAction.utils.ts index 03bbadc..cf8cc3e 100644 --- a/processor/src/utils/paymentAction.utils.ts +++ b/processor/src/utils/paymentAction.utils.ts @@ -68,13 +68,13 @@ const determineAction = (groups: ReturnType): Deter return ConnectorActions.CancelPayment; } - if (groups.successCharge.length >= 1 && groups.initialRefund.length) { + if (groups.successCharge.length === 1 && groups.initialRefund.length) { return ConnectorActions.CreateRefund; } if ( - groups.successCharge.length >= 1 && - groups.pendingRefund.length >= 1 && + groups.successCharge.length === 1 && + groups.pendingRefund.length === 1 && groups.initialCancelAuthorization.length === 1 ) { return ConnectorActions.CancelRefund; diff --git a/processor/tests/mollie/payment.mollie.spec.ts b/processor/tests/mollie/payment.mollie.spec.ts index e5f1f26..d7a5033 100644 --- a/processor/tests/mollie/payment.mollie.spec.ts +++ b/processor/tests/mollie/payment.mollie.spec.ts @@ -6,9 +6,8 @@ import { getPaymentById, listPaymentMethods, getApplePaySession, - getAllPaymentMethods, } from '../../src/mollie/payment.mollie'; -import { Locale, MethodInclude, MethodsListParams, MollieApiError, PaymentCreateParams } from '@mollie/api-client'; +import { MollieApiError, PaymentCreateParams } from '@mollie/api-client'; import { logger } from '../../src/utils/logger.utils'; import CustomError from '../../src/errors/custom.error'; import { MOLLIE_VERSION_STRINGS } from '../../src/utils/constant.utils'; @@ -544,63 +543,3 @@ describe('getApplePaySession', () => { } }); }); - -describe('getAllPaymentMethods', () => { - it('should call getAllPaymentMethods with the correct parameters', async () => { - const options: MethodsListParams = { - locale: Locale.de_DE, - include: MethodInclude.pricing, - }; - - const queryParams = new URLSearchParams(options as any).toString(); - - const getAllPaymentMethodsEndpoint = `https://api.mollie.com/v2/methods/all?${queryParams as any}`; - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${getApiKey()}`, - versionStrings: MOLLIE_VERSION_STRINGS, - }; - - (fetch as unknown as jest.Mock).mockImplementation(async () => - Promise.resolve({ - json: () => Promise.resolve({ data: [] }), - headers: new Headers(), - ok: true, - redirected: false, - status: 201, - statusText: 'OK', - url: '', - }), - ); - - await getAllPaymentMethods(options); - - expect(fetch).toHaveBeenCalledTimes(1); - expect(fetch).toHaveBeenCalledWith(getAllPaymentMethodsEndpoint, { - method: 'GET', - headers, - }); - }); - - it('should be able to return a proper error message when error which is an instance of MollieApiError occurred', async () => { - const errorMessage = 'Something wrong happened'; - const mollieApiError = new MollieApiError(errorMessage, { field: 'validationUrl' }); - - (fetch as unknown as jest.Mock).mockImplementation(async () => { - throw mollieApiError; - }); - - try { - await getAllPaymentMethods({}); - } catch (error: unknown) { - expect(error).toBeInstanceOf(CustomError); - expect(logger.error).toBeCalledTimes(1); - expect(logger.error).toBeCalledWith( - `SCTM - getAllPaymentMethods - Failed to get all payment methods with unknown errors`, - { - error: mollieApiError, - }, - ); - } - }); -}); diff --git a/processor/tests/mollie/profile.mollie.spec.ts b/processor/tests/mollie/profile.mollie.spec.ts index 71955e9..d4f7cda 100644 --- a/processor/tests/mollie/profile.mollie.spec.ts +++ b/processor/tests/mollie/profile.mollie.spec.ts @@ -56,30 +56,4 @@ describe('Test profile.mollie.ts', () => { await expect(getProfile()).resolves.toThrow(errorMessage); }); - - it('should return unknown errors when fetching the profile', async () => { - const errorMessage = 'SCTM - getProfile - Failed to get Mollie profile with unknown errors'; - (initMollieClient as jest.Mock).mockReturnValue({ - profiles: { - getCurrent: jest.fn().mockReturnValue(new Error(errorMessage)), - }, - }); - - await expect(getProfile()).resolves.toThrow(errorMessage); - }); - - it('should return MollieApi errors when fetching the profile', async () => { - const errorMessage = `SCTM - getProfile - error: error, field: validationUrl`; - (initMollieClient as jest.Mock).mockReturnValue({ - profiles: { - getCurrent: jest - .fn() - .mockReturnValue( - new MollieApiError('SCTM - getProfile - error: error, field: validationUrl', { field: 'validationUrl' }), - ), - }, - }); - - await expect(getProfile()).resolves.toThrow(errorMessage); - }); }); diff --git a/processor/tests/routes/processor.route.spec.ts b/processor/tests/routes/processor.route.spec.ts index 2e69811..768eb0d 100644 --- a/processor/tests/routes/processor.route.spec.ts +++ b/processor/tests/routes/processor.route.spec.ts @@ -7,8 +7,6 @@ import { createCustomPaymentType, createCustomPaymentInterfaceInteractionType, createCustomPaymentTransactionCancelReasonType, - createTransactionSurchargeCustomType, - createTransactionRefundForMolliePaymentCustomType, } from '../../src/commercetools/customFields.commercetools'; jest.mock('../../src/commercetools/extensions.commercetools', () => ({ @@ -20,8 +18,6 @@ jest.mock('../../src/commercetools/customFields.commercetools', () => ({ createCustomPaymentType: jest.fn(), createCustomPaymentInterfaceInteractionType: jest.fn(), createCustomPaymentTransactionCancelReasonType: jest.fn(), - createTransactionSurchargeCustomType: jest.fn(), - createTransactionRefundForMolliePaymentCustomType: jest.fn(), })); describe('Test src/route/processor.route.ts', () => { @@ -113,8 +109,6 @@ describe('Test src/route/processor.route.ts', () => { (createCustomPaymentType as jest.Mock).mockReturnValueOnce(Promise.resolve()); (createCustomPaymentInterfaceInteractionType as jest.Mock).mockReturnValueOnce(Promise.resolve()); (createCustomPaymentTransactionCancelReasonType as jest.Mock).mockReturnValueOnce(Promise.resolve()); - (createTransactionSurchargeCustomType as jest.Mock).mockReturnValueOnce(Promise.resolve()); - (createTransactionRefundForMolliePaymentCustomType as jest.Mock).mockReturnValueOnce(Promise.resolve()); req = { hostname: 'test.com', diff --git a/processor/tests/service/payment.service.spec.ts b/processor/tests/service/payment.service.spec.ts index 5bdfa86..e51aeb9 100644 --- a/processor/tests/service/payment.service.spec.ts +++ b/processor/tests/service/payment.service.spec.ts @@ -922,179 +922,6 @@ describe('Test listPaymentMethodsByPayment', () => { }); expect(JSON.stringify(response)).toContain('creditcard'); }); - - test('call listPaymentMethodsByPayment w/o custom objects', async () => { - (listPaymentMethods as jest.Mock).mockReturnValueOnce([ - { - resource: 'method', - id: 'paypal', - description: 'PayPal', - minimumAmount: { value: '0.01', currency: 'EUR' }, - maximumAmount: null, - image: { - size1x: 'https://www.mollie.com/external/icons/payment-methods/paypal.png', - size2x: 'https://www.mollie.com/external/icons/payment-methods/paypal%402x.png', - svg: 'https://www.mollie.com/external/icons/payment-methods/paypal.svg', - }, - status: 'activated', - _links: { - self: { - href: 'https://api.mollie.com/v2/methods/paypal', - type: 'application/hal+json', - }, - }, - }, - { - resource: 'method', - id: 'giftcard', - description: 'Geschenkkarten', - minimumAmount: { value: '0.01', currency: 'EUR' }, - maximumAmount: null, - image: { - size1x: 'https://www.mollie.com/external/icons/payment-methods/giftcard.png', - size2x: 'https://www.mollie.com/external/icons/payment-methods/giftcard%402x.png', - svg: 'https://www.mollie.com/external/icons/payment-methods/giftcard.svg', - }, - status: 'activated', - _links: { - self: { - href: 'https://api.mollie.com/v2/methods/giftcard', - type: 'application/hal+json', - }, - }, - }, - { - resource: 'method', - id: 'bancontact', - description: 'Bancontact', - minimumAmount: { value: '0.01', currency: 'EUR' }, - maximumAmount: null, - image: { - size1x: 'https://www.mollie.com/external/icons/payment-methods/bancontact.png', - size2x: 'https://www.mollie.com/external/icons/payment-methods/bancontact%402x.png', - svg: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', - }, - status: 'activated', - _links: { - self: { - href: 'https://api.mollie.com/v2/methods/bancontact', - type: 'application/hal+json', - }, - }, - }, - { - resource: 'method', - id: 'banktransfer', - description: 'Bank transfer', - minimumAmount: { value: '0.01', currency: 'EUR' }, - maximumAmount: null, - image: { - size1x: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.png', - size2x: 'https://www.mollie.com/external/icons/payment-methods/banktransfer%402x.png', - svg: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', - }, - status: 'activated', - _links: { - self: { - href: 'https://api.mollie.com/v2/methods/banktransfer', - type: 'application/hal+json', - }, - }, - }, - ]); - - (getMethodConfigObjects as jest.Mock).mockReturnValueOnce([]); - - mockResource = { - id: 'RANDOMID_12345', - paymentMethodInfo: { - paymentInterface: 'mollie', - method: 'card', - }, - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 500000, - fractionDigits: 2, - }, - custom: { - fields: { - sctm_payment_methods_request: JSON.stringify({ - locale: 'de_DE', - billingCountry: 'DE', - }), - }, - } as unknown as CustomFields, - } as unknown as Payment; - - const response = await handleListPaymentMethodsByPayment(mockResource); - expect(response).toBeDefined(); - expect(response.statusCode).toBe(200); - expect(response?.actions?.length).toBeGreaterThan(0); - expect(response?.actions?.[0]?.action).toBe('setCustomField'); - expect((response?.actions?.[1] as any)?.value).toBe( - JSON.stringify({ - count: 5, - methods: [ - { - id: 'paypal', - name: { - 'en-GB': 'PayPal', - }, - description: { - 'en-GB': '', - }, - image: 'https://www.mollie.com/external/icons/payment-methods/paypal.svg', - order: 0, - }, - { - id: 'giftcard', - name: { - 'en-GB': 'Geschenkkarten', - }, - description: { - 'en-GB': '', - }, - image: 'https://www.mollie.com/external/icons/payment-methods/giftcard.svg', - order: 0, - }, - { - id: 'bancontact', - name: { - 'en-GB': 'Bancontact', - }, - description: { - 'en-GB': '', - }, - image: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', - order: 0, - }, - { - id: 'banktransfer', - name: { - 'en-GB': 'Bank transfer', - }, - description: { - 'en-GB': '', - }, - image: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', - order: 0, - }, - { - id: 'googlepay', - name: { - 'en-GB': 'Google Pay', - }, - description: { - 'en-GB': '', - }, - image: '', - order: 0, - }, - ], - }), - ); - }); }); describe('Test getCreatePaymentUpdateAction', () => { @@ -1785,7 +1612,9 @@ describe('Test handleCreatePayment', () => { actions: ctActions, }); }); +}); +describe('Test handleCreateRefund', () => { it('should return status code and array of actions', async () => { const CTPayment: Payment = { id: '5c8b0375-305a-4f19-ae8e-07806b101999', @@ -1801,8 +1630,20 @@ describe('Test handleCreatePayment', () => { paymentStatus: {}, transactions: [ { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - type: 'Authorization', + id: uuid, + type: 'Charge', + interactionId: 'tr_123123', + amount: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1000, + fractionDigits: 2, + }, + state: 'Success', + }, + { + id: 'test_refund', + type: 'Refund', amount: { type: 'centPrecision', currencyCode: 'EUR', @@ -1814,486 +1655,38 @@ describe('Test handleCreatePayment', () => { ], interfaceInteractions: [], paymentMethodInfo: { - method: 'ideal', - }, - custom: { - type: { - typeId: 'type', - id: 'test', - }, - fields: { - sctm_payment_methods_request: JSON.stringify({ - billingCountry: 'DE', - }), - }, + method: 'creditcard', }, }; - const molliePayment: molliePayment = { - resource: 'payment', - id: 'tr_7UhSN1zuXS', + (changeTransactionState as jest.Mock).mockReturnValueOnce({ + action: 'changeTransactionState', + state: 'Pending', + transactionId: 'test_refund', + }); + + (createPaymentRefund as jest.Mock).mockReturnValue({ + id: 'fake_refund_id', + }); + + const paymentCreateRefundParams: CreateParameters = { + paymentId: 'tr_123123', amount: { value: '10.00', currency: 'EUR', }, - description: 'Order #12345', - redirectUrl: 'https://webshop.example.org/order/12345/', - webhookUrl: 'https://webshop.example.org/payments/webhook/', - metadata: '{"order_id":12345}', - profileId: 'pfl_QkEhN94Ba', - status: PaymentStatus.open, - isCancelable: false, - createdAt: '2024-03-20T09:13:37+00:00', - expiresAt: '2024-03-20T09:28:37+00:00', - _links: { - self: { - href: '...', - type: 'application/hal+json', - }, - checkout: { - href: 'https://www.mollie.com/checkout/select-method/7UhSN1zuXS', - type: 'text/html', - }, - documentation: { - href: '...', - type: 'text/html', - }, - }, - } as molliePayment; - - const customLineItem = { - id: 'custom-line', - key: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, }; - const mockedCart = { - id: 'mocked-cart', - customLineItems: [customLineItem], - } as Cart; + const result = await handleCreateRefund(CTPayment); - const methodConfig = { - value: { - pricingConstraints: [ - { - currencyCode: CTPayment.amountPlanned.currencyCode, - countryCode: JSON.parse(CTPayment.custom?.fields?.sctm_payment_methods_request).billingCountry, - surchargeCost: { - percentageAmount: 2, - fixedAmount: 10, - }, - }, - ], - }, - }; - - const appUtils = require('../../src/utils/app.utils'); - - jest.spyOn(appUtils, 'calculateTotalSurchargeAmount'); - - const mapUtils = require('../../src/utils/map.utils'); - - jest.spyOn(mapUtils, 'createCartUpdateActions'); - - (getCartFromPayment as jest.Mock).mockReturnValue(mockedCart); - (getSingleMethodConfigObject as jest.Mock).mockReturnValueOnce(methodConfig); - (createMolliePayment as jest.Mock).mockReturnValueOnce(molliePayment); - (getPaymentExtension as jest.Mock).mockReturnValueOnce({ - destination: { - url: 'https://example.com', - }, - }); - - (createMollieCreatePaymentParams as jest.Mock).mockReturnValueOnce({ - method: 'ideal', - }); - - (changeTransactionState as jest.Mock).mockReturnValueOnce({ - action: 'changeTransactionState', - state: 'Pending', - transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', - }); - - (updateCart as jest.Mock).mockReturnValue(mockedCart); - - const totalSurchargeAmount = 1020; - - const actual = await handleCreatePayment(CTPayment); - - const expectedCartUpdateActions = [ - { - action: 'removeCustomLineItem', - customLineItemId: customLineItem.id, - }, - { - action: 'addCustomLineItem', - name: { - de: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, - en: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, - }, - quantity: 1, - money: { - centAmount: totalSurchargeAmount, - currencyCode: CTPayment.amountPlanned.currencyCode, - }, - slug: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, - }, - ]; - - expect(getSingleMethodConfigObject).toHaveBeenCalledWith(CTPayment.paymentMethodInfo.method); - expect(calculateTotalSurchargeAmount).toHaveBeenCalledTimes(1); - expect(calculateTotalSurchargeAmount).toHaveBeenCalledWith( - CTPayment, - methodConfig.value.pricingConstraints[0].surchargeCost, - ); - expect(calculateTotalSurchargeAmount).toHaveReturnedWith( - totalSurchargeAmount / Math.pow(10, CTPayment.amountPlanned.fractionDigits), - ); - - expect(createCartUpdateActions).toHaveBeenCalledTimes(1); - expect(createCartUpdateActions).toHaveBeenCalledWith(mockedCart, CTPayment, totalSurchargeAmount); - expect(createCartUpdateActions).toHaveReturnedWith(expectedCartUpdateActions); - - const ctActions = [ - { - action: 'addInterfaceInteraction', - type: { key: 'sctm_interface_interaction_type' }, - fields: { - sctm_id: '5c8b0375-305a-4f19-ae8e-07806b101999', - sctm_action_type: 'createPayment', - sctm_created_at: '2024-03-20T09:13:37+00:00', - sctm_request: '{"transactionId":"5c8b0375-305a-4f19-ae8e-07806b101999","paymentMethod":"ideal"}', - sctm_response: - '{"molliePaymentId":"tr_7UhSN1zuXS","checkoutUrl":"https://www.mollie.com/checkout/select-method/7UhSN1zuXS","transactionId":"5c8b0375-305a-4f19-ae8e-07806b101999"}', - }, - }, - { - action: 'changeTransactionInteractionId', - transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', - interactionId: 'tr_7UhSN1zuXS', - }, - { - action: 'changeTransactionTimestamp', - transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', - timestamp: '2024-03-20T09:13:37+00:00', - }, - { - action: 'changeTransactionState', - transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', - state: 'Pending', - }, - { - action: 'setTransactionCustomType', - type: { - key: 'sctm_transaction_surcharge_cost', - }, - fields: { - surchargeAmountInCent: 1020, - }, - transactionId: CTPayment.transactions[0].id, - }, - ]; - - expect(actual).toEqual({ - statusCode: 201, - actions: ctActions, - }); - }); -}); - -describe('Test handleCreateRefund', () => { - it('should return status code and array of actions (1 success charge transaction)', async () => { - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [ - { - id: uuid, - type: 'Charge', - interactionId: 'tr_123123', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: 'test_refund', - type: 'Refund', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Initial', - }, - ], - interfaceInteractions: [], - paymentMethodInfo: { - method: 'creditcard', - }, - }; - - (changeTransactionState as jest.Mock).mockReturnValueOnce({ - action: 'changeTransactionState', - state: 'Pending', - transactionId: 'test_refund', - }); - - (createPaymentRefund as jest.Mock).mockReturnValue({ - id: 'fake_refund_id', - }); - - const paymentCreateRefundParams: CreateParameters = { - paymentId: 'tr_123123', - amount: { - value: '10.00', - currency: 'EUR', - }, - }; - - const result = await handleCreateRefund(CTPayment); - - expect(createPaymentRefund).toBeCalledTimes(1); - expect(createPaymentRefund).toBeCalledWith(paymentCreateRefundParams); - expect(result.statusCode).toBe(201); - expect(result.actions).toStrictEqual([ - { - action: 'setTransactionCustomType', - type: { - key: CustomFieldName.transactionRefundForMolliePayment, - }, - transactionId: 'test_refund', - fields: { - [CustomFieldName.transactionRefundForMolliePayment]: 'tr_123123', - }, - }, - { - action: 'changeTransactionInteractionId', - transactionId: 'test_refund', - interactionId: 'fake_refund_id', - }, - { - action: 'changeTransactionState', - transactionId: 'test_refund', - state: 'Pending', - }, - ]); - }); - - it('should return status code and array of actions (more than 1 success charge transaction, with Mollie payment that need to be refunded is not specified)', async () => { - const targetedMolliePaymentId = 'tr_123456'; - - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [ - { - id: uuid, - timestamp: '2024-06-24T08:28:43.474Z', - type: 'Charge', - interactionId: 'tr_123123', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: 'test-123', - timestamp: '2024-06-24T08:30:43.474Z', - type: 'Charge', - interactionId: targetedMolliePaymentId, - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: 'test_refund', - type: 'Refund', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Initial', - }, - ], - interfaceInteractions: [], - paymentMethodInfo: { - method: 'creditcard', - }, - }; - - (changeTransactionState as jest.Mock).mockReturnValueOnce({ - action: 'changeTransactionState', - state: 'Pending', - transactionId: 'test_refund', - }); - - (createPaymentRefund as jest.Mock).mockReturnValue({ - id: 'fake_refund_id', - }); - - const paymentCreateRefundParams: CreateParameters = { - paymentId: targetedMolliePaymentId, - amount: { - value: '10.00', - currency: 'EUR', - }, - }; - - const result = await handleCreateRefund(CTPayment); - - expect(createPaymentRefund).toBeCalledTimes(1); - expect(createPaymentRefund).toBeCalledWith(paymentCreateRefundParams); - expect(result.statusCode).toBe(201); - expect(result.actions).toStrictEqual([ - { - action: 'setTransactionCustomType', - type: { - key: CustomFieldName.transactionRefundForMolliePayment, - }, - transactionId: 'test_refund', - fields: { - [CustomFieldName.transactionRefundForMolliePayment]: targetedMolliePaymentId, - }, - }, - { - action: 'changeTransactionInteractionId', - transactionId: 'test_refund', - interactionId: 'fake_refund_id', - }, - { - action: 'changeTransactionState', - transactionId: 'test_refund', - state: 'Pending', - }, - ]); - }); - - it('should return status code and array of actions (more than 1 success charge transaction, with Mollie payment that need to be refunded is specified)', async () => { - const targetedMolliePaymentId = 'tr_123123'; - - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [ - { - id: uuid, - type: 'Charge', - interactionId: targetedMolliePaymentId, - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: 'test-123', - type: 'Charge', - interactionId: 'tr_123456', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: 'test_refund', - type: 'Refund', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Initial', - custom: { - type: { - typeId: 'type', - id: 'custom-type-id', - }, - fields: { - [CustomFieldName.transactionRefundForMolliePayment]: targetedMolliePaymentId, - }, - }, - }, - ], - interfaceInteractions: [], - paymentMethodInfo: { - method: 'creditcard', - }, - }; - - (changeTransactionState as jest.Mock).mockReturnValueOnce({ - action: 'changeTransactionState', - state: 'Pending', - transactionId: 'test_refund', - }); - - (createPaymentRefund as jest.Mock).mockReturnValue({ - id: 'fake_refund_id', - }); - - const paymentCreateRefundParams: CreateParameters = { - paymentId: targetedMolliePaymentId, - amount: { - value: '10.00', - currency: 'EUR', - }, - }; - - const result = await handleCreateRefund(CTPayment); - - expect(createPaymentRefund).toBeCalledTimes(1); - expect(createPaymentRefund).toBeCalledWith(paymentCreateRefundParams); - expect(result.statusCode).toBe(201); - expect(result.actions).toStrictEqual([ - { - action: 'changeTransactionInteractionId', - transactionId: 'test_refund', - interactionId: 'fake_refund_id', + expect(createPaymentRefund).toBeCalledTimes(1); + expect(createPaymentRefund).toBeCalledWith(paymentCreateRefundParams); + expect(result.statusCode).toBe(201); + expect(result.actions).toStrictEqual([ + { + action: 'changeTransactionInteractionId', + transactionId: 'test_refund', + interactionId: 'fake_refund_id', }, { action: 'changeTransactionState', @@ -2458,6 +1851,7 @@ describe('Test handlePaymentCancelRefund', () => { { id: '5c8b0375-305a-4f19-ae8e-07806b102000', type: 'CancelAuthorization', + interactionId: 're_4qqhO89gsT', amount: { type: 'centPrecision', currencyCode: 'EUR', @@ -2532,7 +1926,7 @@ describe('Test handlePaymentCancelRefund', () => { } }); - it('should return status code and array of actions (interactionId is not defined in the Initial CancelAuthorization transaction)', async () => { + it('should return status code and array of actions', async () => { const mollieRefund: Refund = { resource: 'refund', id: CTPayment.transactions[1].interactionId, @@ -2578,297 +1972,6 @@ describe('Test handlePaymentCancelRefund', () => { paymentId: CTPayment.transactions[0].interactionId, }); }); - - it('should return status code and array of actions (interactionId is defined in the Initial CancelAuthorization transaction)', async () => { - const CTPaymentMocked: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [ - { - id: '5c8b0375-305a-4f19-ae8e-07806b101992', - type: 'Charge', - interactionId: 'tr_test', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - type: 'Charge', - interactionId: 'tr_123123', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: '5c8b0375-305a-4f19-ae8e-07806b102011', - type: 'Refund', - interactionId: 're_TEST', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Pending', - }, - { - id: '5c8b0375-305a-4f19-ae8e-07806b102000', - type: 'Refund', - interactionId: 're_4qqhO89gsT', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Pending', - custom: { - type: { - typeId: 'type', - id: 'custom-type', - }, - fields: { - [CustomFieldName.transactionRefundForMolliePayment]: 'tr_123123', - }, - }, - }, - { - id: '5c8b0375-305a-4f19-ae8e-07806b102000', - type: 'CancelAuthorization', - interactionId: 're_4qqhO89gsT', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Initial', - custom: { - type: { - typeId: 'type', - id: 'sctm_payment_cancel_reason', - }, - fields: { - reasonText: 'dummy reason', - }, - }, - }, - ], - interfaceInteractions: [], - paymentMethodInfo: { - method: 'creditcard', - }, - }; - - const mollieRefund: Refund = { - resource: 'refund', - id: CTPaymentMocked.transactions[3].interactionId, - description: 'Order', - amount: { - currency: 'EUR', - value: '5.95', - }, - status: 'pending', - metadata: '{"bookkeeping_id":12345}', - paymentId: 'tr_7UhSN1zuXS', - createdAt: '2023-03-14T17:09:02.0Z', - _links: { - self: { - href: '...', - type: 'application/hal+json', - }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', - type: 'application/hal+json', - }, - documentation: { - href: '...', - type: 'text/html', - }, - }, - } as Refund; - - (getPaymentRefund as jest.Mock).mockReturnValueOnce(mollieRefund); - - (cancelPaymentRefund as jest.Mock).mockReturnValueOnce(true); - - (getPaymentCancelActions as jest.Mock).mockReturnValueOnce([]); - - await handlePaymentCancelRefund(CTPaymentMocked); - - expect(getPaymentRefund).toBeCalledTimes(1); - expect(getPaymentRefund).toBeCalledWith(CTPaymentMocked.transactions[3].interactionId, { - paymentId: CTPaymentMocked.transactions[1].interactionId, - }); - expect(cancelPaymentRefund).toBeCalledTimes(1); - expect(cancelPaymentRefund).toBeCalledWith(CTPaymentMocked.transactions[3].interactionId, { - paymentId: CTPaymentMocked.transactions[1].interactionId, - }); - }); - - it('should throw error if valid Success Charge transaction was not found (interactionId is defined in the Initial CancelAuthorization transaction)', async () => { - const CTPaymentMocked: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [ - { - id: '5c8b0375-305a-4f19-ae8e-07806b101992', - type: 'Charge', - interactionId: 'tr_test123123', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - type: 'Charge', - interactionId: 'tr_dummy', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Success', - }, - { - id: '5c8b0375-305a-4f19-ae8e-07806b102011', - type: 'Refund', - interactionId: 're_TEST', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Pending', - }, - { - id: '5c8b0375-305a-4f19-ae8e-07806b102000', - type: 'Refund', - interactionId: 're_4qqhO89gsT', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Pending', - custom: { - type: { - typeId: 'type', - id: 'custom-type', - }, - fields: { - [CustomFieldName.transactionRefundForMolliePayment]: 'tr_123123', - }, - }, - }, - { - id: '5c8b0375-305a-4f19-ae8e-07806b102000', - type: 'CancelAuthorization', - interactionId: 're_4qqhO89gsT', - amount: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - state: 'Initial', - custom: { - type: { - typeId: 'type', - id: 'sctm_payment_cancel_reason', - }, - fields: { - reasonText: 'dummy reason', - }, - }, - }, - ], - interfaceInteractions: [], - paymentMethodInfo: { - method: 'creditcard', - }, - }; - - const mollieRefund: Refund = { - resource: 'refund', - id: CTPaymentMocked.transactions[3].interactionId, - description: 'Order', - amount: { - currency: 'EUR', - value: '5.95', - }, - status: 'pending', - metadata: '{"bookkeeping_id":12345}', - paymentId: 'tr_7UhSN1zuXS', - createdAt: '2023-03-14T17:09:02.0Z', - _links: { - self: { - href: '...', - type: 'application/hal+json', - }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', - type: 'application/hal+json', - }, - documentation: { - href: '...', - type: 'text/html', - }, - }, - } as Refund; - - (getPaymentRefund as jest.Mock).mockReturnValueOnce(mollieRefund); - - (cancelPaymentRefund as jest.Mock).mockReturnValueOnce(true); - - (getPaymentCancelActions as jest.Mock).mockReturnValueOnce([]); - - try { - await handlePaymentCancelRefund(CTPaymentMocked); - } catch (error: any) { - expect(getPaymentRefund).toBeCalledTimes(0); - expect(cancelPaymentRefund).toBeCalledTimes(0); - - expect(error).toBeInstanceOf(CustomError); - expect((error as CustomError).message).toBe( - 'SCTM - handlePaymentCancelRefund - Cannot find the valid Success Charge transaction.', - ); - } - }); }); describe('Test handlePaymentWebhook', () => { diff --git a/processor/tests/types/mollie.types.spec.ts b/processor/tests/types/mollie.types.spec.ts index efb997e..0f9ef0c 100644 --- a/processor/tests/types/mollie.types.spec.ts +++ b/processor/tests/types/mollie.types.spec.ts @@ -1,10 +1,5 @@ import { describe, test, expect, jest } from '@jest/globals'; -import { - ApplePaySessionRequest, - CustomPaymentMethod, - ParsedMethodsRequestType, - SupportedPaymentMethods, -} from '../../src/types/mollie.types'; +import { ApplePaySessionRequest, ParsedMethodsRequestType } from '../../src/types/mollie.types'; const functions = { isParsedMethodsRequestType: jest.fn((obj: ParsedMethodsRequestType): obj is ParsedMethodsRequestType => { @@ -43,22 +38,4 @@ describe('Test mollie.types.ts', () => { } as ApplePaySessionRequest; expect(functions.isApplePaySessionRequest(mockType)).toBeFalsy(); }); - - test('should return the correct SupportedPaymentMethods', () => { - expect(SupportedPaymentMethods.ideal).toBe('ideal'); - expect(SupportedPaymentMethods.creditcard).toBe('creditcard'); - expect(SupportedPaymentMethods.bancontact).toBe('bancontact'); - expect(SupportedPaymentMethods.banktransfer).toBe('banktransfer'); - expect(SupportedPaymentMethods.przelewy24).toBe('przelewy24'); - expect(SupportedPaymentMethods.kbc).toBe('kbc'); - expect(SupportedPaymentMethods.blik).toBe('blik'); - expect(SupportedPaymentMethods.applepay).toBe('applepay'); - expect(SupportedPaymentMethods.paypal).toBe('paypal'); - expect(SupportedPaymentMethods.giftcard).toBe('giftcard'); - expect(SupportedPaymentMethods.googlepay).toBe('googlepay'); - }); - - test('should return correct custom method', () => { - expect(CustomPaymentMethod.blik).toBe('blik'); - }); }); diff --git a/processor/tests/utils/app.utils.spec.ts b/processor/tests/utils/app.utils.spec.ts index f76db6c..60eda64 100644 --- a/processor/tests/utils/app.utils.spec.ts +++ b/processor/tests/utils/app.utils.spec.ts @@ -6,12 +6,11 @@ import { parseStringToJsonObject, removeEmptyProperties, roundSurchargeAmountToCent, - sortTransactionsByLatestCreationTime, validateEmail, } from '../../src/utils/app.utils'; import { logger } from '../../src/utils/logger.utils'; import CustomError from '../../src/errors/custom.error'; -import { Payment, Transaction } from '@commercetools/platform-sdk'; +import { Payment } from '@commercetools/platform-sdk'; import { SurchargeCost } from '../../src/types/commercetools.types'; describe('Test createDateNowString', () => { @@ -146,63 +145,3 @@ describe('Test roundSurchargeAmountToCent', () => { expect(roundSurchargeAmountToCent(surchargeAmountInEur, fractionDigits)).toBe(30100); }); }); - -describe('Test sortTransactionsByLatestCreationTime', () => { - it('should return the correct order', () => { - const data = [ - { - id: '39c1eae1-e9b4-45f0-ac18-7d83ec429cc8', - timestamp: '2024-06-24T08:28:43.474Z', - type: 'Authorization', - amount: { - type: 'centPrecision', - currencyCode: 'GBP', - centAmount: 61879, - fractionDigits: 2, - }, - interactionId: '12789fae-d6d6-4b66-9739-3a420dbda2a8', - state: 'Failure', - }, - { - id: '39c1eae1-e9b4-45f0-ac18-7d83ec429cde', - timestamp: '2024-06-24T08:29:43.474Z', - type: 'Authorization', - amount: { - type: 'centPrecision', - currencyCode: 'GBP', - centAmount: 61879, - fractionDigits: 2, - }, - interactionId: '12789fae-d6d6-4b66-9739-3a420dbda2a8', - state: 'Failure', - }, - { - id: '39c1eae1-e9b4-45f0-ac18-7d83ec429cd9', - timestamp: '2024-06-24T08:30:43.474Z', - type: 'Authorization', - amount: { - type: 'centPrecision', - currencyCode: 'GBP', - centAmount: 61879, - fractionDigits: 2, - }, - interactionId: '12789fae-d6d6-4b66-9739-3a420dbda2a8', - state: 'Failure', - }, - { - id: '39c1eae1-e9b4-45f0-ac18-7d83ec429111', - type: 'Authorization', - amount: { - type: 'centPrecision', - currencyCode: 'GBP', - centAmount: 61879, - fractionDigits: 2, - }, - interactionId: '12789fae-d6d6-4b66-9739-3a420dbda2a8', - state: 'Failure', - }, - ] as Transaction[]; - - expect(sortTransactionsByLatestCreationTime(data)).toStrictEqual([data[2], data[1], data[0], data[3]]); - }); -}); diff --git a/processor/tests/utils/config.utils.spec.ts b/processor/tests/utils/config.utils.spec.ts index 36918cb..e5a5426 100644 --- a/processor/tests/utils/config.utils.spec.ts +++ b/processor/tests/utils/config.utils.spec.ts @@ -1,4 +1,4 @@ -import { readConfiguration, getApiKey } from '../../src/utils/config.utils'; +import { readConfiguration } from '../../src/utils/config.utils'; import CustomError from '../../src/errors/custom.error'; import { describe, expect, test } from '@jest/globals'; @@ -88,16 +88,4 @@ describe('Test src/utils/config.utils.ts', () => { process.env.AUTHENTICATION_MODE = 'dummy'; expect(() => readConfiguration()).toThrow(CustomError); }); - - test('getApiKey should return test key', () => { - process.env.CONNECTOR_MODE = 'test'; - const key = getApiKey(); - expect(key).toBe(process.env.MOLLIE_API_TEST_KEY); - }); - - test('getApiKey should return live key', () => { - process.env.CONNECTOR_MODE = 'live'; - const key = getApiKey(); - expect(key).toBe(process.env.MOLLIE_API_LIVE_KEY); - }); });