From e77d1c42307d4bfb188d89f30a62055d4d511bf4 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 11:18:20 +0100 Subject: [PATCH 01/11] Solve issue where the Component prop would we forwarded tot the DOM element of the PaymentMethodActionCard --- .changeset/big-feet-scream.md | 5 +++++ .../PaymentMethodActionCardListForm.tsx | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 .changeset/big-feet-scream.md diff --git a/.changeset/big-feet-scream.md b/.changeset/big-feet-scream.md new file mode 100644 index 0000000000..0d402bb7e3 --- /dev/null +++ b/.changeset/big-feet-scream.md @@ -0,0 +1,5 @@ +--- +'@graphcommerce/magento-cart-payment-method': patch +--- + +Solve issue where the Component prop would we forwarded tot the DOM element of the PaymentMethodActionCard diff --git a/packages/magento-cart-payment-method/PaymentMethodActionCardList/PaymentMethodActionCardListForm.tsx b/packages/magento-cart-payment-method/PaymentMethodActionCardList/PaymentMethodActionCardListForm.tsx index 61e3e44f32..3cf189f032 100644 --- a/packages/magento-cart-payment-method/PaymentMethodActionCardList/PaymentMethodActionCardListForm.tsx +++ b/packages/magento-cart-payment-method/PaymentMethodActionCardList/PaymentMethodActionCardListForm.tsx @@ -9,15 +9,15 @@ import { Trans } from '@lingui/react' import type { SxProps, Theme } from '@mui/material' import { useEffect } from 'react' import type { PaymentOptionsProps } from '../Api/PaymentMethod' -import { usePaymentMethodContext } from '../PaymentMethodContext/paymentMethodContextType' import { useCartLock } from '../hooks' +import { usePaymentMethodContext } from '../PaymentMethodContext/paymentMethodContextType' function PaymentMethodActionCard( props: ActionCardItemRenderProps & { sx?: SxProps }, ) { - const { onReset, code, step, child, Container, sx = [] } = props + const { onReset, code, step, child, Container, sx = [], ...rest } = props const { selectedMethod, selectedModule, modules } = usePaymentMethodContext() const selectedAndOptions = @@ -29,6 +29,8 @@ function PaymentMethodActionCard( return ( ) } - {...props} + {...rest} /> ) } From f27b021baff584fa10a8930cd9731867394720b7 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 11:34:55 +0100 Subject: [PATCH 02/11] Make sure we are correctly selecting the shipping method form when a user selects a previously selected method again. --- .changeset/soft-paws-do.md | 5 +++++ .../components/ShippingMethodForm/ShippingMethodForm.tsx | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changeset/soft-paws-do.md diff --git a/.changeset/soft-paws-do.md b/.changeset/soft-paws-do.md new file mode 100644 index 0000000000..94740fa3c1 --- /dev/null +++ b/.changeset/soft-paws-do.md @@ -0,0 +1,5 @@ +--- +'@graphcommerce/magento-cart-shipping-method': patch +--- + +Make sure we are correctly selecting the shipping method form when a user selects a previously selected method again. diff --git a/packages/magento-cart-shipping-method/components/ShippingMethodForm/ShippingMethodForm.tsx b/packages/magento-cart-shipping-method/components/ShippingMethodForm/ShippingMethodForm.tsx index 1a47140c83..2df7a90576 100644 --- a/packages/magento-cart-shipping-method/components/ShippingMethodForm/ShippingMethodForm.tsx +++ b/packages/magento-cart-shipping-method/components/ShippingMethodForm/ShippingMethodForm.tsx @@ -72,7 +72,6 @@ export function ShippingMethodForm(props: ShippingMethodFormProps) { ShippingMethodFormMutation, ShippingMethodFormMutationVariables & { carrierMethod?: string } >(ShippingMethodFormDocument, { - skipUnchanged: true, defaultValues: { carrierMethod }, onBeforeSubmit: (variables) => { const [carrier, method] = (variables.carrierMethod ?? '').split('-') From b11f29a9253cdcd54ed1e0a58abc2ef2f2bb3ebd Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 11:35:59 +0100 Subject: [PATCH 03/11] Make sure we are toggling the selected shipping method when a user selects a previously selected shipping method --- .changeset/big-glasses-mix.md | 5 +++++ .../components/CustomerAddressForm/CustomerAddressForm.tsx | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/big-glasses-mix.md diff --git a/.changeset/big-glasses-mix.md b/.changeset/big-glasses-mix.md new file mode 100644 index 0000000000..f7a0033b35 --- /dev/null +++ b/.changeset/big-glasses-mix.md @@ -0,0 +1,5 @@ +--- +'@graphcommerce/magento-cart-shipping-address': patch +--- + +Make sure we are toggling the selected shipping method when a user selects a previously selected shipping method diff --git a/packages/magento-cart-shipping-address/components/CustomerAddressForm/CustomerAddressForm.tsx b/packages/magento-cart-shipping-address/components/CustomerAddressForm/CustomerAddressForm.tsx index dce7ffe4f0..6f4add4559 100644 --- a/packages/magento-cart-shipping-address/components/CustomerAddressForm/CustomerAddressForm.tsx +++ b/packages/magento-cart-shipping-address/components/CustomerAddressForm/CustomerAddressForm.tsx @@ -13,7 +13,7 @@ import { useFormGqlMutationCart, } from '@graphcommerce/magento-cart' import { CustomerDocument } from '@graphcommerce/magento-customer' -import { FormRow, filterNonNullableKeys } from '@graphcommerce/next-ui' +import { filterNonNullableKeys, FormRow } from '@graphcommerce/next-ui' import { i18n } from '@lingui/core' import { Trans } from '@lingui/macro' import type { SxProps, Theme } from '@mui/material' @@ -86,7 +86,6 @@ export function CustomerAddressForm(props: CustomerAddressListProps) { } >(Mutation, { defaultValues: { customer_address_id: cartAddressId }, - skipUnchanged: true, onBeforeSubmit: (vars) => { const { customer_address_id } = vars if (customer_address_id === -1) return false From 881e146acf0d129d6d44ec207619a45ad04d673b Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 11:51:03 +0100 Subject: [PATCH 04/11] Solve an issue where the payment submission would remain in a spinning state when placing an order failed: `useFormGql` will now set `root` error on the form when there is an error response on the GraphQL operation, an error is thrown in onBeforeSubmit and in onSuccess. --- .changeset/shy-years-judge.md | 5 +++ packages/react-hook-form/src/useFormGql.tsx | 42 +++++++++++++-------- 2 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 .changeset/shy-years-judge.md diff --git a/.changeset/shy-years-judge.md b/.changeset/shy-years-judge.md new file mode 100644 index 0000000000..ef6740b3b7 --- /dev/null +++ b/.changeset/shy-years-judge.md @@ -0,0 +1,5 @@ +--- +'@graphcommerce/react-hook-form': patch +--- + +Solve an issue where the payment submission would remain in a spinning state when placing an order failed: `useFormGql` will now set `root` error on the form when there is an error response on the GraphQL operation, an error is thrown in onBeforeSubmit and in onSuccess. diff --git a/packages/react-hook-form/src/useFormGql.tsx b/packages/react-hook-form/src/useFormGql.tsx index 388c7ef073..7bb8a993d6 100644 --- a/packages/react-hook-form/src/useFormGql.tsx +++ b/packages/react-hook-form/src/useFormGql.tsx @@ -1,5 +1,4 @@ import type { - ApolloError, FetchResult, LazyQueryHookOptions, LazyQueryResultTuple, @@ -8,7 +7,7 @@ import type { MutationTuple, TypedDocumentNode, } from '@apollo/client' -import { isApolloError } from '@apollo/client' +import { ApolloError, isApolloError } from '@apollo/client' import { getOperationName } from '@apollo/client/utilities' import useEventCallback from '@mui/utils/useEventCallback' import { useEffect, useRef } from 'react' @@ -179,16 +178,23 @@ export function useFormGql( if (onBeforeSubmitError) { if (isApolloError(onBeforeSubmitError)) { returnedError.current = onBeforeSubmitError + form.setError('root', { message: onBeforeSubmitError.message }) } else { - console.info( - 'A non ApolloError was thrown during the onBeforeSubmit handler.', - onBeforeSubmitError, - ) + const message = + process.env.NODE_ENV === 'development' + ? `A non ApolloError was thrown during the onBeforeSubmit handler: ${onBeforeSubmitError.message}` + : 'An unexpected error occurred, please contact the store owner' + form.setError('root', { message }) + returnedError.current = new ApolloError({ graphQLErrors: [{ message }] }) } + return + } + if (onBeforeSubmitResult === false) { + form.setError('root', { message: 'Form submission cancelled' }) return } - if (onBeforeSubmitResult === false) return + variables = onBeforeSubmitResult submittedVariables.current = variables @@ -207,21 +213,25 @@ export function useFormGql( }, }) - const [, onCompleteError] = await complete(result, variables) - if (onCompleteError) { - returnedError.current = onCompleteError as ApolloError + // If there are submission errors, set the error and return + if (result.errors) { + form.setError('root', { message: result.errors.map((e) => e.message).join(', ') }) return } + + const [, onCompleteError] = await complete(result, variables) if (onCompleteError) { if (isApolloError(onCompleteError)) { returnedError.current = onCompleteError + form.setError('root', { message: onCompleteError.message }) } else { - console.info( - 'A non ApolloError was thrown during the onComplete handler.', - onCompleteError, - ) + const message = + process.env.NODE_ENV === 'development' + ? `A non ApolloError was thrown during the onComplete handler: ${onCompleteError.message}` + : 'An unexpected error occurred, please contact the store owner' + form.setError('root', { message }) + returnedError.current = new ApolloError({ graphQLErrors: [{ message }] }) } - return } @@ -236,7 +246,7 @@ export function useFormGql( ...gqlDocumentHandler, handleSubmit, data, - error: error ?? returnedError.current, + error: returnedError.current ?? error, submittedVariables: submittedVariables.current, } } From 22b9111b93bbfaa4d323c183b8c5a1e72b807725 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 11:52:53 +0100 Subject: [PATCH 05/11] Support Magento 2.4.7 placeOrder.errors field to handle possible errors while placing the order. An `assertOrderPlaced` method was created to assert a valid placed order. --- .changeset/fifty-timers-scream.md | 6 +++ .../PaymentMethodPlaceOrderNoop.graphql | 5 +++ .../PaymentMethodPlaceOrderNoop.tsx | 8 ++-- .../assertOrderPlaced.ts | 40 +++++++++++++++++++ packages/magento-cart-payment-method/index.ts | 4 +- .../schema-247/Mutation-placeOrder.graphqls | 5 +++ 6 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 .changeset/fifty-timers-scream.md create mode 100644 packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/assertOrderPlaced.ts diff --git a/.changeset/fifty-timers-scream.md b/.changeset/fifty-timers-scream.md new file mode 100644 index 0000000000..b4e2815691 --- /dev/null +++ b/.changeset/fifty-timers-scream.md @@ -0,0 +1,6 @@ +--- +'@graphcommerce/magento-cart-payment-method': patch +'@graphcommerce/magento-graphql': patch +--- + +Support Magento 2.4.7 placeOrder.errors field to handle possible errors while placing the order. An `assertOrderPlaced` method was created to assert a valid placed order. diff --git a/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.graphql b/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.graphql index aeb72ba0f8..e65a377e9b 100644 --- a/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.graphql +++ b/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.graphql @@ -1,5 +1,10 @@ mutation PaymentMethodPlaceOrderNoop($cartId: String!) { placeOrder(input: { cart_id: $cartId }) { + errors { + code + message + } + order { order_number } diff --git a/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.tsx b/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.tsx index 98e084b593..4e9dea7fda 100644 --- a/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.tsx +++ b/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.tsx @@ -3,6 +3,7 @@ import { useFormCompose } from '@graphcommerce/react-hook-form' import { t } from '@lingui/macro' import type { PaymentPlaceOrderProps } from '../Api/PaymentMethod' import { usePaymentMethodContext } from '../PaymentMethodContext/paymentMethodContextType' +import { assertOrderPlaced } from './assertOrderPlaced' import { PaymentMethodPlaceOrderNoopDocument } from './PaymentMethodPlaceOrderNoop.gql' export function PaymentMethodPlaceOrderNoop(props: PaymentPlaceOrderProps) { @@ -11,11 +12,8 @@ export function PaymentMethodPlaceOrderNoop(props: PaymentPlaceOrderProps) { const form = useFormGqlMutationCart(PaymentMethodPlaceOrderNoopDocument, { onComplete: async (result) => { - if (!result.data?.placeOrder?.order) - throw Error( - t`An error occurred while processing your payment. Please contact the store owner`, - ) - + assertOrderPlaced(result) + console.log('result', result) await onSuccess(result.data.placeOrder.order.order_number) }, submitWhileLocked: true, diff --git a/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/assertOrderPlaced.ts b/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/assertOrderPlaced.ts new file mode 100644 index 0000000000..775df42f82 --- /dev/null +++ b/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/assertOrderPlaced.ts @@ -0,0 +1,40 @@ +import type { FetchResult } from '@graphcommerce/graphql' +import { ApolloError } from '@graphcommerce/graphql' +import { t } from '@lingui/macro' +import type { PaymentMethodPlaceOrderNoopMutation } from './PaymentMethodPlaceOrderNoop.gql' + +export type PlacedOrder> = NonNullable< + NonNullable['placeOrder']>['order'] +> + +export type AssertedOrderPlaced> = T & { + data: { + placeOrder: { order: PlacedOrder } + } +} + +/** Assert that the order was place successfully. */ +export function assertOrderPlaced>( + result: T, +): asserts result is AssertedOrderPlaced { + if (result.errors && result.errors.length > 0) { + const graphQLErrors = result.errors.filter((e) => e !== null) + throw new ApolloError({ graphQLErrors }) + } + + if (result.data?.placeOrder?.errors && result.data.placeOrder.errors.length > 0) { + const graphQLErrors = result.data.placeOrder.errors.filter((e) => e !== null) + throw new ApolloError({ graphQLErrors }) + } + + if (!result.data?.placeOrder?.order?.order_number) { + console.info('Error while placing order', result) + throw new ApolloError({ + graphQLErrors: [ + { + message: t`An error occurred while processing your payment. Please contact the store owner`, + }, + ], + }) + } +} diff --git a/packages/magento-cart-payment-method/index.ts b/packages/magento-cart-payment-method/index.ts index 3a86d3aa67..a644140c79 100644 --- a/packages/magento-cart-payment-method/index.ts +++ b/packages/magento-cart-payment-method/index.ts @@ -1,5 +1,6 @@ export * from './Api/PaymentMethod' export * from './hooks' +export * from './PaymentMethodActionCardList/PaymentMethodActionCardListForm' export * from './PaymentMethodButton/PaymentMethodButton' export * from './PaymentMethodContext/PaymentMethodContext' export * from './PaymentMethodContext/paymentMethodContextType' @@ -7,6 +8,7 @@ export * from './PaymentMethodOptions/PaymentMethodOptions' export * from './PaymentMethodOptionsNoop/PaymentMethodOptionsNoop' export * from './PaymentMethodOptionsNoop/PaymentMethodOptionsNoop.gql' export * from './PaymentMethodPlaceOrder/PaymentMethodPlaceOrder' +export * from './PaymentMethodPlaceOrderNoop/assertOrderPlaced' export * from './PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop' +export * from './PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.gql' export * from './PaymentMethodToggles/PaymentMethodToggles' -export * from './PaymentMethodActionCardList/PaymentMethodActionCardListForm' diff --git a/packages/magento-graphql/schema-247/Mutation-placeOrder.graphqls b/packages/magento-graphql/schema-247/Mutation-placeOrder.graphqls index 7997d99e13..6a91189b95 100644 --- a/packages/magento-graphql/schema-247/Mutation-placeOrder.graphqls +++ b/packages/magento-graphql/schema-247/Mutation-placeOrder.graphqls @@ -3,6 +3,11 @@ type PlaceOrderOutput { The ID of the order. """ order: Order + """ + An array of place order errors. + """ + errors: [PlaceOrderError]! @deprecated(reason: "Magento >= 2.4.7") + """ Full order information. """ From 32fb7dab88273df94061ea28c8e5171f065e6d1d Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 11:56:19 +0100 Subject: [PATCH 06/11] Support Magento 2.4.7 placeOrder.errors field when placing a Mollie order --- .changeset/smooth-ghosts-fix.md | 5 +++ .../MolliePlaceOrder/MolliePlaceOrder.graphql | 5 +++ .../MolliePlaceOrder/MolliePlaceOrder.tsx | 24 +++++------- .../assertMollieOrderPlaced.ts | 37 +++++++++++++++++++ 4 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 .changeset/smooth-ghosts-fix.md create mode 100644 packages/mollie-magento-payment/components/MolliePlaceOrder/assertMollieOrderPlaced.ts diff --git a/.changeset/smooth-ghosts-fix.md b/.changeset/smooth-ghosts-fix.md new file mode 100644 index 0000000000..a3ffa8214f --- /dev/null +++ b/.changeset/smooth-ghosts-fix.md @@ -0,0 +1,5 @@ +--- +'@graphcommerce/mollie-magento-payment': patch +--- + +Support Magento 2.4.7 placeOrder.errors field when placing a Mollie order diff --git a/packages/mollie-magento-payment/components/MolliePlaceOrder/MolliePlaceOrder.graphql b/packages/mollie-magento-payment/components/MolliePlaceOrder/MolliePlaceOrder.graphql index 09f6a37034..421bcde8e7 100644 --- a/packages/mollie-magento-payment/components/MolliePlaceOrder/MolliePlaceOrder.graphql +++ b/packages/mollie-magento-payment/components/MolliePlaceOrder/MolliePlaceOrder.graphql @@ -1,5 +1,10 @@ mutation MolliePlaceOrder($cartId: String!, $returnUrl: String!) { placeOrder(input: { cart_id: $cartId, mollie_return_url: $returnUrl }) { + errors { + code + message + } + order { order_number mollie_redirect_url diff --git a/packages/mollie-magento-payment/components/MolliePlaceOrder/MolliePlaceOrder.tsx b/packages/mollie-magento-payment/components/MolliePlaceOrder/MolliePlaceOrder.tsx index dd8aa9ffec..7474b7cbd3 100644 --- a/packages/mollie-magento-payment/components/MolliePlaceOrder/MolliePlaceOrder.tsx +++ b/packages/mollie-magento-payment/components/MolliePlaceOrder/MolliePlaceOrder.tsx @@ -1,9 +1,9 @@ import { useFormGqlMutationCart } from '@graphcommerce/magento-cart' -import type { PaymentPlaceOrderProps } from '@graphcommerce/magento-cart-payment-method' +import { type PaymentPlaceOrderProps } from '@graphcommerce/magento-cart-payment-method' import { useFormCompose } from '@graphcommerce/react-hook-form' -import { t } from '@lingui/macro' import { useRouter } from 'next/router' import { useCartLockWithToken } from '../../hooks/useCartLockWithToken' +import { assertMollieOrderPlaced } from './assertMollieOrderPlaced' import { MolliePlaceOrderDocument } from './MolliePlaceOrder.gql' export function MolliePlaceOrder(props: PaymentPlaceOrderProps) { @@ -27,21 +27,15 @@ export function MolliePlaceOrder(props: PaymentPlaceOrderProps) { return { ...variables, returnUrl } }, - onComplete: async ({ data, errors }) => { - if (!data?.placeOrder?.order || errors) return + onComplete: async (result) => { + assertMollieOrderPlaced(result) - const { mollie_redirect_url, mollie_payment_token, order_number } = data.placeOrder.order + const { mollie_payment_token, order_number, mollie_redirect_url } = + result.data.placeOrder.order - // When redirecting to the payment gateway - if (mollie_redirect_url && mollie_payment_token) { - await lock({ mollie_payment_token, method: code, order_number }) - - await push(mollie_redirect_url) - } else { - throw Error( - t`An error occurred while processing your payment. Please contact the store owner`, - ) - } + // Redirect to the payment gateway + await lock({ mollie_payment_token, method: code, order_number }) + await push(mollie_redirect_url) }, }) diff --git a/packages/mollie-magento-payment/components/MolliePlaceOrder/assertMollieOrderPlaced.ts b/packages/mollie-magento-payment/components/MolliePlaceOrder/assertMollieOrderPlaced.ts new file mode 100644 index 0000000000..30a39b6104 --- /dev/null +++ b/packages/mollie-magento-payment/components/MolliePlaceOrder/assertMollieOrderPlaced.ts @@ -0,0 +1,37 @@ +import { ApolloError, type FetchResult } from '@graphcommerce/graphql' +import type { AssertedOrderPlaced, PlacedOrder } from '@graphcommerce/magento-cart-payment-method' +import { assertOrderPlaced } from '@graphcommerce/magento-cart-payment-method' +import { t } from '@lingui/macro' +import type { MolliePlaceOrderMutation } from './MolliePlaceOrder.gql' + +/** Assert that the order was place successfully. */ +export function assertMollieOrderPlaced>( + result: T, +): asserts result is AssertedOrderPlaced & { + data: { + placeOrder: { + order: PlacedOrder & { + mollie_redirect_url: NonNullable['mollie_redirect_url']> + mollie_payment_token: NonNullable['mollie_payment_token']> + } + } + } +} { + assertOrderPlaced(result) + + const { mollie_redirect_url, mollie_payment_token } = result.data.placeOrder.order + + if (!mollie_redirect_url || !mollie_payment_token) { + console.error( + 'Mollie: Order was placed, but no redirect url or payment token was returned, this is an issue on the Magento Mollie side.', + result, + ) + throw new ApolloError({ + graphQLErrors: [ + { + message: t`An error occurred while processing your payment. Please contact the store owner`, + }, + ], + }) + } +} From fd6e6cb2a7cfe48d6138ed73d4039a78f7b6db63 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 11:59:26 +0100 Subject: [PATCH 07/11] Support Magento 2.4.7 placeOrder.errors field when placing a PayPal order. --- .../PayPalPaymentHandler.graphql | 5 +++++ .../PayPalPaymentHandler.tsx | 17 ++++++++++------- .../PayPalPaymentPlaceOrder.tsx | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/magento-payment-paypal/components/PayPalPaymentHandler/PayPalPaymentHandler.graphql b/packages/magento-payment-paypal/components/PayPalPaymentHandler/PayPalPaymentHandler.graphql index cf9f704480..edd7391fc6 100644 --- a/packages/magento-payment-paypal/components/PayPalPaymentHandler/PayPalPaymentHandler.graphql +++ b/packages/magento-payment-paypal/components/PayPalPaymentHandler/PayPalPaymentHandler.graphql @@ -5,6 +5,11 @@ mutation PayPalPaymentHandler($cartId: String!, $paymentMethod: PaymentMethodInp } } placeOrder(input: { cart_id: $cartId }) { + errors { + code + message + } + order { order_number } diff --git a/packages/magento-payment-paypal/components/PayPalPaymentHandler/PayPalPaymentHandler.tsx b/packages/magento-payment-paypal/components/PayPalPaymentHandler/PayPalPaymentHandler.tsx index 943a3cd107..6cf6ae297d 100644 --- a/packages/magento-payment-paypal/components/PayPalPaymentHandler/PayPalPaymentHandler.tsx +++ b/packages/magento-payment-paypal/components/PayPalPaymentHandler/PayPalPaymentHandler.tsx @@ -2,7 +2,10 @@ import { ApolloErrorSnackbar } from '@graphcommerce/ecommerce-ui' import { useMutation } from '@graphcommerce/graphql' import { useCurrentCartId } from '@graphcommerce/magento-cart' import type { PaymentHandlerProps } from '@graphcommerce/magento-cart-payment-method' -import { usePaymentMethodContext } from '@graphcommerce/magento-cart-payment-method' +import { + assertOrderPlaced, + usePaymentMethodContext, +} from '@graphcommerce/magento-cart-payment-method' import { useRouter } from 'next/router' import { useEffect } from 'react' import { usePayPalCartLock } from '../../hooks/usePayPalCartLock' @@ -41,15 +44,15 @@ export function PayPalPaymentHandler(props: PaymentHandlerProps) { if (!cartId) return const fetchData = async () => { - const res = await placeOrder() + const result = await placeOrder() - if (res.errors || !res.data?.placeOrder?.order) { + try { + assertOrderPlaced(result) + // eslint-disable-next-line @typescript-eslint/no-floating-promises + onSuccess(result.data.placeOrder.order.order_number) + } catch (e) { await unlock({ token: null, PayerID: null }) - return } - - // eslint-disable-next-line @typescript-eslint/no-floating-promises - onSuccess(res.data?.placeOrder?.order.order_number) } // eslint-disable-next-line @typescript-eslint/no-floating-promises diff --git a/packages/magento-payment-paypal/components/PayPalPaymentPlaceOrder/PayPalPaymentPlaceOrder.tsx b/packages/magento-payment-paypal/components/PayPalPaymentPlaceOrder/PayPalPaymentPlaceOrder.tsx index 9a6d520d1f..06d2941c72 100644 --- a/packages/magento-payment-paypal/components/PayPalPaymentPlaceOrder/PayPalPaymentPlaceOrder.tsx +++ b/packages/magento-payment-paypal/components/PayPalPaymentPlaceOrder/PayPalPaymentPlaceOrder.tsx @@ -1,6 +1,11 @@ import { useFormCompose } from '@graphcommerce/ecommerce-ui' import { useFormGqlMutationCart } from '@graphcommerce/magento-cart' -import type { PaymentPlaceOrderProps } from '@graphcommerce/magento-cart-payment-method' +import { + assertOrderPlaced, + type PaymentPlaceOrderProps, +} from '@graphcommerce/magento-cart-payment-method' +import { ApolloError } from '@apollo/client' +import { GraphQLError } from 'graphql' import { useRouter } from 'next/router' import { usePayPalCartLock } from '../../hooks/usePayPalCartLock' import { PayPalPaymentPlaceOrderDocument } from './PayPalPaymentPlaceOrder.gql' @@ -17,15 +22,16 @@ export function PayPalPaymentPlaceOrder(props: PaymentPlaceOrderProps) { urls: { cancel_url: 'checkout/payment', return_url: 'checkout/payment' }, }), onComplete: async (result) => { - if (result.errors) return + if (result.errors && result.errors.length > 0) return const start = result.data?.createPaypalExpressToken?.paypal_urls?.start const token = result.data?.createPaypalExpressToken?.token - if (!start) - throw Error( - 'Error while starting the PayPal payment, please try again with a different payment method', - ) + if (!start) { + const message = + 'Error while starting the PayPal payment, please try again with a different payment method' + throw new ApolloError({ graphQLErrors: [{ message }] }) + } await lock({ token, method: code, PayerID: null }) // We are going to redirect, but we're not waiting, because we need to complete the submission to release the buttons From 083aca523af42bc5a7dcf8af4823bee1924f5a74 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 12:02:15 +0100 Subject: [PATCH 08/11] Support Magento 2.4.7 placeOrder.errors field when placing a MultiSafePay order --- .../MSPPaymentPlaceOrder.graphql | 5 +++++ .../MSPPaymentPlaceOrder.tsx | 17 +++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/magento-payment-multisafepay/components/MSPPaymentPlaceOrder/MSPPaymentPlaceOrder.graphql b/packages/magento-payment-multisafepay/components/MSPPaymentPlaceOrder/MSPPaymentPlaceOrder.graphql index 1e96a047fa..2951a67ac6 100644 --- a/packages/magento-payment-multisafepay/components/MSPPaymentPlaceOrder/MSPPaymentPlaceOrder.graphql +++ b/packages/magento-payment-multisafepay/components/MSPPaymentPlaceOrder/MSPPaymentPlaceOrder.graphql @@ -1,5 +1,10 @@ mutation MSPPaymentPlaceOrder($cartId: String!) { placeOrder(input: { cart_id: $cartId }) { + errors { + code + message + } + order { order_number multisafepay_payment_url { diff --git a/packages/magento-payment-multisafepay/components/MSPPaymentPlaceOrder/MSPPaymentPlaceOrder.tsx b/packages/magento-payment-multisafepay/components/MSPPaymentPlaceOrder/MSPPaymentPlaceOrder.tsx index bd74c9edb7..2fcecddbfd 100644 --- a/packages/magento-payment-multisafepay/components/MSPPaymentPlaceOrder/MSPPaymentPlaceOrder.tsx +++ b/packages/magento-payment-multisafepay/components/MSPPaymentPlaceOrder/MSPPaymentPlaceOrder.tsx @@ -3,7 +3,11 @@ import { useMutation } from '@graphcommerce/graphql' import { useCartQuery, useFormGqlMutationCart } from '@graphcommerce/magento-cart' import { BillingPageDocument } from '@graphcommerce/magento-cart-checkout' import type { PaymentPlaceOrderProps } from '@graphcommerce/magento-cart-payment-method' -import { usePaymentMethodContext } from '@graphcommerce/magento-cart-payment-method' +import { + assertOrderPlaced, + throwGenericPlaceOrderError, + usePaymentMethodContext, +} from '@graphcommerce/magento-cart-payment-method' import { ErrorSnackbar } from '@graphcommerce/next-ui' import { t } from '@lingui/macro' import { useRouter } from 'next/router' @@ -27,14 +31,11 @@ export function MSPPaymentPlaceOrder(props: PaymentPlaceOrderProps) { */ const form = useFormGqlMutationCart(MSPPaymentPlaceOrderDocument, { onComplete: async (result, variables) => { - const url = result.data?.placeOrder?.order.multisafepay_payment_url - - if (result.errors) return + assertOrderPlaced(result) + const url = result.data.placeOrder.order.multisafepay_payment_url if (!selectedMethod?.code) { - throw Error( - t`An error occurred while processing your payment. Please contact the store owner`, - ) + throwGenericPlaceOrderError() } if (url?.error || !url?.payment_url) { @@ -47,7 +48,7 @@ export function MSPPaymentPlaceOrder(props: PaymentPlaceOrderProps) { await lock({ method: selectedMethod.code, - order_number: result.data?.placeOrder?.order.order_number, + order_number: result.data.placeOrder.order.order_number, }) await new Promise((resolve) => setTimeout(resolve, 1000)) From 88de61dbaa68edeee4c587a32391626827d72442 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 12:22:31 +0100 Subject: [PATCH 09/11] Use throwGenericPlaceOrderError --- .../assertOrderPlaced.ts | 18 +++++++++++------- .../assertMollieOrderPlaced.ts | 16 ++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/assertOrderPlaced.ts b/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/assertOrderPlaced.ts index 775df42f82..c1eacc053c 100644 --- a/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/assertOrderPlaced.ts +++ b/packages/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/assertOrderPlaced.ts @@ -13,6 +13,16 @@ export type AssertedOrderPlaced>( result: T, @@ -29,12 +39,6 @@ export function assertOrderPlaced Date: Mon, 23 Dec 2024 12:22:49 +0100 Subject: [PATCH 10/11] Support Magento 2.4.7 placeOrder.errors field when placing an Adyen order --- .../AdyenPaymentOptionsAndPlaceOrder.graphql | 5 +++++ .../AdyenPaymentOptionsAndPlaceOrder.tsx | 13 ++++++++++--- .../hooks/useAdyenHandlePaymentResponse.ts | 5 +---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/magento-payment-adyen/components/AdyenPaymentOptionsAndPlaceOrder/AdyenPaymentOptionsAndPlaceOrder.graphql b/packages/magento-payment-adyen/components/AdyenPaymentOptionsAndPlaceOrder/AdyenPaymentOptionsAndPlaceOrder.graphql index a25439401e..037f081156 100644 --- a/packages/magento-payment-adyen/components/AdyenPaymentOptionsAndPlaceOrder/AdyenPaymentOptionsAndPlaceOrder.graphql +++ b/packages/magento-payment-adyen/components/AdyenPaymentOptionsAndPlaceOrder/AdyenPaymentOptionsAndPlaceOrder.graphql @@ -17,6 +17,11 @@ mutation AdyenPaymentOptionsAndPlaceOrder( } } placeOrder(input: { cart_id: $cartId }) { + errors { + code + message + } + order { order_number adyen_payment_status { diff --git a/packages/magento-payment-adyen/components/AdyenPaymentOptionsAndPlaceOrder/AdyenPaymentOptionsAndPlaceOrder.tsx b/packages/magento-payment-adyen/components/AdyenPaymentOptionsAndPlaceOrder/AdyenPaymentOptionsAndPlaceOrder.tsx index 075297bb5d..15f6c760ba 100644 --- a/packages/magento-payment-adyen/components/AdyenPaymentOptionsAndPlaceOrder/AdyenPaymentOptionsAndPlaceOrder.tsx +++ b/packages/magento-payment-adyen/components/AdyenPaymentOptionsAndPlaceOrder/AdyenPaymentOptionsAndPlaceOrder.tsx @@ -1,7 +1,11 @@ import { FormPersist, TextFieldElement, useFormCompose } from '@graphcommerce/ecommerce-ui' import { useFormGqlMutationCart } from '@graphcommerce/magento-cart' import type { PaymentOptionsProps } from '@graphcommerce/magento-cart-payment-method' -import { usePaymentMethodContext } from '@graphcommerce/magento-cart-payment-method' +import { + assertOrderPlaced, + throwGenericPlaceOrderError, + usePaymentMethodContext, +} from '@graphcommerce/magento-cart-payment-method' import { FormRow } from '@graphcommerce/next-ui' import { t } from '@lingui/macro' import { useRouter } from 'next/router' @@ -40,15 +44,18 @@ export function HppOptions(props: PaymentOptionsProps) { brandCode, }), onComplete: async (result) => { + assertOrderPlaced(result.data?.placeOrder) const merchantReference = result.data?.placeOrder?.order.order_number const action = result?.data?.placeOrder?.order.adyen_payment_status?.action if (result.errors) return if (!merchantReference || !selectedMethod?.code || !action) { - throw Error( - t`An error occurred while processing your payment. Please contact the store owner`, + console.error( + 'Adyen: Order was placed, but no merchant reference or action was returned, this is an issue on the Magento Adyen side.', + result, ) + throwGenericPlaceOrderError() } const url = JSON.parse(action).url as string diff --git a/packages/magento-payment-adyen/hooks/useAdyenHandlePaymentResponse.ts b/packages/magento-payment-adyen/hooks/useAdyenHandlePaymentResponse.ts index ca59618913..72764696ce 100644 --- a/packages/magento-payment-adyen/hooks/useAdyenHandlePaymentResponse.ts +++ b/packages/magento-payment-adyen/hooks/useAdyenHandlePaymentResponse.ts @@ -31,10 +31,7 @@ export type AdyenPaymentResponse = { additionalData?: Types.checkout.PaymentResponse['additionalData'] } -/** - * @deprecated Will be removed - * @public - */ +/** @public */ export function parsePaymentResponse( status?: AdyenPaymentResponseFragment | null, ): AdyenPaymentResponse { From 88995a0dc45d637c61b5a75450aea7dac6ccb193 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 23 Dec 2024 12:38:39 +0100 Subject: [PATCH 11/11] Make sure we correctly check for errors and do not consider an empty array an error --- packages/react-hook-form/src/useFormGql.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-hook-form/src/useFormGql.tsx b/packages/react-hook-form/src/useFormGql.tsx index 7bb8a993d6..585b987925 100644 --- a/packages/react-hook-form/src/useFormGql.tsx +++ b/packages/react-hook-form/src/useFormGql.tsx @@ -214,7 +214,7 @@ export function useFormGql( }) // If there are submission errors, set the error and return - if (result.errors) { + if (result.errors && result.errors.length > 0) { form.setError('root', { message: result.errors.map((e) => e.message).join(', ') }) return }