Skip to content

Commit

Permalink
fix(payments-plugin): Fix Mollie not calling webhook on updated orders (
Browse files Browse the repository at this point in the history
#3014)

Fixes #2941
  • Loading branch information
martijnvdbrug authored Aug 19, 2024
1 parent bcfcf7d commit 694845f
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 278 deletions.
8 changes: 2 additions & 6 deletions packages/payments-plugin/e2e/mollie-dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
/**
* This should only be used to locally test the Mollie payment plugin
* Make sure you have `MOLLIE_APIKEY=test_xxxx` in your .env file
* Make sure you have `MOLLIE_APIKEY=test_xxxx` in your .env file
*/
/* eslint-disable @typescript-eslint/no-floating-promises */
async function runMollieDevServer() {
Expand Down Expand Up @@ -109,13 +108,10 @@ async function runMollieDevServer() {
// eslint-disable-next-line no-console
console.log('Payment intent result', result);

// Change order amount and create new intent
await createFixedDiscountCoupon(adminClient, 20000, 'DISCOUNT_ORDER');
await shopClient.query(APPLY_COUPON_CODE, { couponCode: 'DISCOUNT_ORDER' });
await new Promise(resolve => setTimeout(resolve, 3000));
// Create another Payment Intent to test duplicate paymnets
const result2 = await shopClient.query(CREATE_MOLLIE_PAYMENT_INTENT, { input: {} });
// eslint-disable-next-line no-console
console.log('Payment intent result', result2);
console.log('Second payment intent result', result2);
}

(async () => {
Expand Down
120 changes: 52 additions & 68 deletions packages/payments-plugin/e2e/mollie-payment.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ChannelService,
EventBus,
LanguageCode,
Logger,
mergeConfig,
Order,
OrderPlacedEvent,
Expand All @@ -23,7 +24,7 @@ import {
import nock from 'nock';
import fetch from 'node-fetch';
import path from 'path';
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest';

import { initialData } from '../../../e2e-common/e2e-initial-data';
import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
Expand Down Expand Up @@ -215,6 +216,34 @@ describe('Mollie payments', () => {
expect(customers).toHaveLength(2);
});

it('Should create a Mollie paymentMethod', async () => {
const { createPaymentMethod } = await adminClient.query<
CreatePaymentMethodMutation,
CreatePaymentMethodMutationVariables
>(CREATE_PAYMENT_METHOD, {
input: {
code: mockData.methodCode,
enabled: true,
handler: {
code: molliePaymentHandler.code,
arguments: [
{ name: 'redirectUrl', value: mockData.redirectUrl },
{ name: 'apiKey', value: mockData.apiKey },
{ name: 'autoCapture', value: 'false' },
],
},
translations: [
{
languageCode: LanguageCode.en,
name: 'Mollie payment test',
description: 'This is a Mollie test payment method',
},
],
},
});
expect(createPaymentMethod.code).toBe(mockData.methodCode);
});

describe('Payment intent creation', () => {
it('Should prepare an order', async () => {
await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
Expand All @@ -240,34 +269,6 @@ describe('Mollie payments', () => {
expect(order.code).toBeDefined();
});

it('Should add a Mollie paymentMethod', async () => {
const { createPaymentMethod } = await adminClient.query<
CreatePaymentMethodMutation,
CreatePaymentMethodMutationVariables
>(CREATE_PAYMENT_METHOD, {
input: {
code: mockData.methodCode,
enabled: true,
handler: {
code: molliePaymentHandler.code,
arguments: [
{ name: 'redirectUrl', value: mockData.redirectUrl },
{ name: 'apiKey', value: mockData.apiKey },
{ name: 'autoCapture', value: 'false' },
],
},
translations: [
{
languageCode: LanguageCode.en,
name: 'Mollie payment test',
description: 'This is a Mollie test payment method',
},
],
},
});
expect(createPaymentMethod.code).toBe(mockData.methodCode);
});

it('Should fail to create payment intent without shippingmethod', async () => {
await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
const { createMolliePaymentIntent: result } = await shopClient.query(
Expand Down Expand Up @@ -389,45 +390,6 @@ describe('Mollie payments', () => {
});
});

it('Should recreate all order lines in Mollie', async () => {
// Should fetch the existing order from Mollie
nock('https://api.mollie.com/')
.get('/v2/orders/ord_mockId')
.reply(200, mockData.mollieOrderResponse);
// Should patch existing order
nock('https://api.mollie.com/')
.patch(`/v2/orders/${mockData.mollieOrderResponse.id}`)
.reply(200, mockData.mollieOrderResponse);
// Should patch existing order lines
let molliePatchRequest: any | undefined;
nock('https://api.mollie.com/')
.patch(`/v2/orders/${mockData.mollieOrderResponse.id}/lines`, body => {
molliePatchRequest = body;
return true;
})
.reply(200, mockData.mollieOrderResponse);
const { createMolliePaymentIntent } = await shopClient.query(CREATE_MOLLIE_PAYMENT_INTENT, {
input: {
paymentMethodCode: mockData.methodCode,
},
});
expect(createMolliePaymentIntent.url).toBeDefined();
// Should have removed all 3 previous order lines
const cancelledLines = molliePatchRequest.operations.filter((o: any) => o.operation === 'cancel');
expect(cancelledLines.length).toBe(3);
// Should have added all 3 new order lines
const addedLines = molliePatchRequest.operations.filter((o: any) => o.operation === 'add');
expect(addedLines.length).toBe(3);
addedLines.forEach((line: any) => {
expect(line.data).toHaveProperty('name');
expect(line.data).toHaveProperty('quantity');
expect(line.data).toHaveProperty('unitPrice');
expect(line.data).toHaveProperty('totalAmount');
expect(line.data).toHaveProperty('vatRate');
expect(line.data).toHaveProperty('vatAmount');
});
});

it('Should get payment url with deducted amount if a payment is already made', async () => {
let mollieRequest: any | undefined;
nock('https://api.mollie.com/')
Expand Down Expand Up @@ -566,6 +528,28 @@ describe('Mollie payments', () => {
expect(order.state).toBe('PaymentSettled');
});

it('Should log error when order is paid again with a different mollie order', async () => {
const errorLogSpy = vi.spyOn(Logger, 'error');
nock('https://api.mollie.com/')
.get('/v2/orders/ord_newMockId')
.reply(200, {
...mockData.mollieOrderResponse,
id: 'ord_newMockId',
amount: { value: '100', currency: 'EUR' }, // Try to pay another 100
orderNumber: order.code,
status: OrderStatus.paid,
});
await fetch(`http://localhost:${serverPort}/payments/mollie/${E2E_DEFAULT_CHANNEL_TOKEN}/1`, {
method: 'post',
body: JSON.stringify({ id: 'ord_newMockId' }),
headers: { 'Content-Type': 'application/json' },
});
const logMessage = errorLogSpy.mock.calls?.[0]?.[0];
expect(logMessage).toBe(
`Order '${order.code}' is already paid. Mollie order 'ord_newMockId' should be refunded.`,
);
});

it('Should have preserved original languageCode ', () => {
// We've set the languageCode to 'nl' in the mock response's metadata
expect(orderPlacedEvent?.ctx.languageCode).toBe('nl');
Expand Down
16 changes: 0 additions & 16 deletions packages/payments-plugin/src/mollie/custom-fields.ts

This file was deleted.

76 changes: 0 additions & 76 deletions packages/payments-plugin/src/mollie/extended-mollie-client.ts

This file was deleted.

2 changes: 0 additions & 2 deletions packages/payments-plugin/src/mollie/mollie.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {

import { shopApiExtensions, adminApiExtensions } from './api-extensions';
import { PLUGIN_INIT_OPTIONS } from './constants';
import { orderCustomFields } from './custom-fields';
import { MollieCommonResolver } from './mollie.common-resolver';
import { MollieController } from './mollie.controller';
import { molliePaymentHandler } from './mollie.handler';
Expand Down Expand Up @@ -195,7 +194,6 @@ export interface MolliePluginOptions {
providers: [MollieService, { provide: PLUGIN_INIT_OPTIONS, useFactory: () => MolliePlugin.options }],
configuration: (config: RuntimeVendureConfig) => {
config.paymentOptions.paymentMethodHandlers.push(molliePaymentHandler);
config.customFields.Order.push(...orderCustomFields);
return config;
},
shopApiExtensions: {
Expand Down
Loading

0 comments on commit 694845f

Please sign in to comment.