Browse Source

fix(payments-plugin): Mollie - allow overriding `immediateCapture` from the plugin level (#4142)

Martijn 1 day ago
parent
commit
dfda66d25e

+ 50 - 1
packages/payments-plugin/e2e/mollie-payment.e2e-spec.ts

@@ -23,7 +23,7 @@ import {
 import nock from 'nock';
 import nock from 'nock';
 import fetch from 'node-fetch';
 import fetch from 'node-fetch';
 import path from 'path';
 import path from 'path';
-import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
+import { afterAll, afterEach, beforeAll, describe, expect, it, onTestFinished, vi } from 'vitest';
 
 
 import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
 import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
@@ -582,6 +582,55 @@ describe('Mollie payments', () => {
             expect(mollieRequest.captureMode).toBe('manual');
             expect(mollieRequest.captureMode).toBe('manual');
         });
         });
 
 
+        it('Should not allow setting immediateCapture=false via client input, when it is already set on the plugin level to true', async () => {
+            const originalImmediateCapture = MolliePlugin.options.immediateCapture;
+            MolliePlugin.options.immediateCapture = true;
+            const logSpy = vi.spyOn(Logger, 'warn');
+            onTestFinished(() => {
+                // Revert back to plugin setting for next test
+                MolliePlugin.options.immediateCapture = originalImmediateCapture;
+                logSpy.mockClear();
+            });
+            let mollieRequest: any;
+            nock('https://api.mollie.com/')
+                .post('/v2/payments', body => {
+                    mollieRequest = body;
+                    return true;
+                })
+                .reply(200, mollieMockData.molliePaymentResponse);
+            await shopClient.query(CREATE_MOLLIE_PAYMENT_INTENT, {
+                input: {
+                    immediateCapture: false,
+                },
+            });
+            expect(logSpy.mock.calls?.[0]?.[0]).toContain(
+                `'immediateCapture' is overridden by the plugin options to 'true'`,
+            );
+            expect(mollieRequest.captureMode).toBe('automatic');
+        });
+
+        it('Should not allow setting immediateCapture=true via client input, when it is already set on the plugin level to false', async () => {
+            MolliePlugin.options.immediateCapture = false;
+            const logSpy = vi.spyOn(Logger, 'warn');
+            let mollieRequest: any;
+            nock('https://api.mollie.com/')
+                .post('/v2/payments', body => {
+                    mollieRequest = body;
+                    return true;
+                })
+                .reply(200, mollieMockData.molliePaymentResponse);
+            await shopClient.query(CREATE_MOLLIE_PAYMENT_INTENT, {
+                input: {
+                    immediateCapture: true,
+                },
+            });
+            MolliePlugin.options.immediateCapture = undefined; // Reset again for next test
+            expect(logSpy.mock.calls?.[0]?.[0]).toContain(
+                `'immediateCapture' is overridden by the plugin options to 'false'`,
+            );
+            expect(mollieRequest.captureMode).toBe('manual');
+        });
+
         it('Should authorize payment with immediateCapture = false', async () => {
         it('Should authorize payment with immediateCapture = false', async () => {
             nock('https://api.mollie.com/')
             nock('https://api.mollie.com/')
                 .get(`/v2/payments/${mollieMockData.molliePaymentResponse.id}`)
                 .get(`/v2/payments/${mollieMockData.molliePaymentResponse.id}`)

+ 1 - 0
packages/payments-plugin/src/mollie/api-extensions.ts

@@ -28,6 +28,7 @@ const commonSchemaExtensions = gql`
         Set this to false when you expect that order fulfillment takes longer than 24 hours.
         Set this to false when you expect that order fulfillment takes longer than 24 hours.
         If set to false, you will need to settle the "Authorized" payment in Vendure manually!
         If set to false, you will need to settle the "Authorized" payment in Vendure manually!
         If you fail to do so, the Authorized payment will expire after 28 days.
         If you fail to do so, the Authorized payment will expire after 28 days.
+        This setting can be overridden on the plugin level via the plugin options.
         """
         """
         immediateCapture: Boolean
         immediateCapture: Boolean
         """
         """

+ 8 - 0
packages/payments-plugin/src/mollie/mollie.plugin.ts

@@ -74,6 +74,14 @@ export interface MolliePluginOptions {
         ctx: RequestContext,
         ctx: RequestContext,
         order: Order | null,
         order: Order | null,
     ) => AdditionalEnabledPaymentMethodsParams | Promise<AdditionalEnabledPaymentMethodsParams>;
     ) => AdditionalEnabledPaymentMethodsParams | Promise<AdditionalEnabledPaymentMethodsParams>;
+    /**
+     * @description
+     * Immediate capture mode for pay-later methods like Klarna.
+     * Setting this option will make the plugin ignore the `immediateCapture` option in the `createMolliePaymentIntent` mutation.
+     *
+     * The default is true, unless set otherwise as input in the `createMolliePaymentIntent` mutation.
+     */
+    immediateCapture?: boolean;
 }
 }
 
 
 /**
 /**

+ 13 - 1
packages/payments-plugin/src/mollie/mollie.service.ts

@@ -189,6 +189,17 @@ export class MollieService {
                 url: redirectUrl,
                 url: redirectUrl,
             };
             };
         }
         }
+        // Define immediateCapture based on plugin options or client input
+        const immediateCapture = this.options.immediateCapture ?? input.immediateCapture;
+        if (input.immediateCapture !== undefined && immediateCapture !== input.immediateCapture) {
+            // Given input is different from what will be passed to Mollie, so we log a warning
+            Logger.warn(
+                `'immediateCapture' is overridden by the plugin options to '${String(
+                    this.options.immediateCapture,
+                )}'. Ignoring client input of 'immediateCapture=${String(input.immediateCapture)}'`,
+                loggerCtx,
+            );
+        }
         const paymentInput: CreatePaymentParameters = {
         const paymentInput: CreatePaymentParameters = {
             description: order.code,
             description: order.code,
             amount: toAmount(amountToPay, order.currencyCode),
             amount: toAmount(amountToPay, order.currencyCode),
@@ -199,8 +210,9 @@ export class MollieService {
             lines: toMolliePaymentLines(order, alreadyPaid),
             lines: toMolliePaymentLines(order, alreadyPaid),
             metadata: {
             metadata: {
                 languageCode: ctx.languageCode,
                 languageCode: ctx.languageCode,
+                immediateCapture,
             },
             },
-            captureMode: input.immediateCapture === false ? CaptureMethod.manual : CaptureMethod.automatic,
+            captureMode: immediateCapture === false ? CaptureMethod.manual : CaptureMethod.automatic, // default should be automatic
         };
         };
         if (molliePaymentMethodCode) {
         if (molliePaymentMethodCode) {
             paymentInput.method = molliePaymentMethodCode as MollieClientMethod;
             paymentInput.method = molliePaymentMethodCode as MollieClientMethod;