Browse Source

feat(payments-plugin): E2e test fixed

Martijn 1 year ago
parent
commit
1e65032d13

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

@@ -277,7 +277,7 @@ describe('Mollie payments with useDynamicRedirectUrl=false', () => {
                     },
                     },
                 },
                 },
             );
             );
-            expect(result.message).toContain('The following variants are out of stock');
+            expect(result.message).toContain('insufficient stock of Pinelab stickers');
             // Set stock back to not tracking
             // Set stock back to not tracking
             ({ updateProductVariants } = await adminClient.query(UPDATE_PRODUCT_VARIANTS, {
             ({ updateProductVariants } = await adminClient.query(UPDATE_PRODUCT_VARIANTS, {
                 input: {
                 input: {
@@ -335,6 +335,31 @@ describe('Mollie payments with useDynamicRedirectUrl=false', () => {
             });
             });
         });
         });
 
 
+        it('Should reuse payment url when amount is the same', async () => {
+            // Should only fetch the order from Mollie, not create a new one
+            nock('https://api.mollie.com/')
+                .get('/v2/orders/ord_mockId')
+                .reply(200, {
+                    ...mockData.mollieOrderResponse,
+                    amount: {
+                        value: '1009.90',
+                        currency: 'USD',
+                    },
+                    _links: {
+                        // Mock a new checkout url, to test that this one is actually reused
+                        checkout: {
+                            href: 'https://this-means-reuse-succeeded',
+                        },
+                    },
+                });
+            const { createMolliePaymentIntent } = await shopClient.query(CREATE_MOLLIE_PAYMENT_INTENT, {
+                input: {
+                    paymentMethodCode: mockData.methodCode,
+                },
+            });
+            expect(createMolliePaymentIntent).toEqual({ url: 'https://this-means-reuse-succeeded' });
+        });
+
         it('Should get payment url with deducted amount if a payment is already made', async () => {
         it('Should get payment url with deducted amount if a payment is already made', async () => {
             let mollieRequest: any | undefined;
             let mollieRequest: any | undefined;
             nock('https://api.mollie.com/')
             nock('https://api.mollie.com/')

+ 18 - 30
packages/payments-plugin/src/mollie/mollie.service.ts

@@ -113,30 +113,27 @@ export class MollieService {
                 'payments',
                 'payments',
             ],
             ],
         });
         });
-        if (!order.lines?.length) {
-            return new PaymentIntentError('Cannot create payment intent for empty order');
-        }
-        if (!order.customer) {
-            return new PaymentIntentError('Cannot create payment intent for order without customer');
+        if (order.state !== 'ArrangingPayment' && order.state !== 'ArrangingAdditionalPayment') {
+            // Pre-check if order is transitionable to ArrangingPayment, because that will happen after Mollie payment
+            try {
+                await this.canTransitionTo(ctx, order.id, 'ArrangingPayment');
+            } catch (e) {
+                if ((e as Error).message) {
+                    return new PaymentIntentError((e as Error).message);
+                }
+                throw e;
+            }
         }
         }
-        if (!order.customer.firstName.length) {
+        if (!order.customer?.firstName.length) {
             return new PaymentIntentError(
             return new PaymentIntentError(
                 'Cannot create payment intent for order with customer that has no firstName set',
                 'Cannot create payment intent for order with customer that has no firstName set',
             );
             );
         }
         }
-        if (!order.customer.lastName.length) {
+        if (!order.customer?.lastName.length) {
             return new PaymentIntentError(
             return new PaymentIntentError(
                 'Cannot create payment intent for order with customer that has no lastName set',
                 'Cannot create payment intent for order with customer that has no lastName set',
             );
             );
         }
         }
-        if (!order.customer.emailAddress.length) {
-            return new PaymentIntentError(
-                'Cannot create payment intent for order with customer that has no emailAddress set',
-            );
-        }
-        if (!order.shippingLines?.length) {
-            return new PaymentIntentError('Cannot create payment intent for order without shippingMethod');
-        }
         if (!paymentMethod) {
         if (!paymentMethod) {
             return new PaymentIntentError(`No paymentMethod found with code ${paymentMethodCode}`);
             return new PaymentIntentError(`No paymentMethod found with code ${paymentMethodCode}`);
         }
         }
@@ -156,19 +153,6 @@ export class MollieService {
             }
             }
             redirectUrl = paymentMethodRedirectUrl;
             redirectUrl = paymentMethodRedirectUrl;
         }
         }
-        // FIXME: The manual checks above can be removed, now that we do a canTransition check?
-        if (order.state !== 'ArrangingPayment' && order.state !== 'ArrangingAdditionalPayment') {
-            // Check if order is transitionable to ArrangingPayment, because that will happen after Mollie payment
-            await this.canTransitionTo(ctx, order.id, 'ArrangingPayment');
-        }
-        const variantsWithInsufficientSaleableStock = await this.getVariantsWithInsufficientStock(ctx, order);
-        if (variantsWithInsufficientSaleableStock.length) {
-            return new PaymentIntentError(
-                `The following variants are out of stock: ${variantsWithInsufficientSaleableStock
-                    .map(v => v.name)
-                    .join(', ')}`,
-            );
-        }
         const apiKey = paymentMethod.handler.args.find(arg => arg.name === 'apiKey')?.value;
         const apiKey = paymentMethod.handler.args.find(arg => arg.name === 'apiKey')?.value;
         if (!apiKey) {
         if (!apiKey) {
             Logger.warn(
             Logger.warn(
@@ -487,11 +471,15 @@ export class MollieService {
             amountToPay,
             amountToPay,
             existingMollieOrder.amount,
             existingMollieOrder.amount,
         );
         );
-        if (checkoutUrl && amountsMatch) {
-            return checkoutUrl;
+        if (amountsMatch) {
+            return checkoutUrl ?? undefined;
         }
         }
     }
     }
 
 
+    /**
+     * Dry run a transition to a given state.
+     * As long as we don't call 'finalize', the transition never completes.
+     */
     private async canTransitionTo(ctx: RequestContext, orderId: ID, state: OrderState) {
     private async canTransitionTo(ctx: RequestContext, orderId: ID, state: OrderState) {
         // Fetch new order object, because `transition()` mutates the order object
         // Fetch new order object, because `transition()` mutates the order object
         const orderCopy = await assertFound(this.orderService.findOne(ctx, orderId));
         const orderCopy = await assertFound(this.orderService.findOne(ctx, orderId));