diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..2edeafb09 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/src/checkout/hooks/useCheckoutComplete.ts b/src/checkout/hooks/useCheckoutComplete.ts index 3eb4d651e..d72c0c653 100644 --- a/src/checkout/hooks/useCheckoutComplete.ts +++ b/src/checkout/hooks/useCheckoutComplete.ts @@ -1,4 +1,5 @@ import { useMemo } from "react"; +import { useAlerts } from "./useAlerts"; import { useCheckoutCompleteMutation } from "@/checkout/graphql"; import { useCheckout } from "@/checkout/hooks/useCheckout"; import { useSubmit } from "@/checkout/hooks/useSubmit"; @@ -9,6 +10,7 @@ export const useCheckoutComplete = () => { checkout: { id: checkoutId }, } = useCheckout(); const [{ fetching }, checkoutComplete] = useCheckoutCompleteMutation(); + const { showCustomErrors } = useAlerts(); const onCheckoutComplete = useSubmit<{}, typeof checkoutComplete>( useMemo( @@ -30,8 +32,12 @@ export const useCheckoutComplete = () => { window.location.href = newUrl; } }, + onError: ({ errors }) => { + const error = errors.join(", "); + showCustomErrors([{ message: error }]); + }, }), - [checkoutComplete, checkoutId], + [checkoutComplete, checkoutId, showCustomErrors], ), ); return { completingCheckout: fetching, onCheckoutComplete }; diff --git a/src/checkout/sections/PaymentSection/DummyPayment/DummyDropIn.tsx b/src/checkout/sections/PaymentSection/DummyPayment/DummyDropIn.tsx new file mode 100644 index 000000000..8477ea9b8 --- /dev/null +++ b/src/checkout/sections/PaymentSection/DummyPayment/DummyDropIn.tsx @@ -0,0 +1,77 @@ +import React from "react"; +import { type ParsedDummyGateway } from "../types"; +import { apiErrorMessages } from "../errorMessages"; +import { + useTransactionInitializeMutation, + type TransactionInitializeMutationVariables, +} from "@/checkout/graphql"; +import { useCheckout } from "@/checkout/hooks/useCheckout"; +import { useSubmit } from "@/checkout/hooks/useSubmit"; +import { Button } from "@/checkout/components"; +import { useCheckoutComplete } from "@/checkout/hooks/useCheckoutComplete"; +import { useAlerts } from "@/checkout/hooks/useAlerts"; +import { useErrorMessages } from "@/checkout/hooks/useErrorMessages"; + +type DummyDropinProps = { + config: ParsedDummyGateway; +}; + +const useDummyDropIn = (props: DummyDropinProps) => { + const { showCustomErrors } = useAlerts(); + const { errorMessages: commonErrorMessages } = useErrorMessages(apiErrorMessages); + + const { checkout } = useCheckout(); + const { onCheckoutComplete, completingCheckout: isCompleteCheckoutLoading } = useCheckoutComplete(); + + const [{ fetching: isTransactionInitializeLoading }, transactionInitialize] = + useTransactionInitializeMutation(); + + const isLoading = isCompleteCheckoutLoading || isTransactionInitializeLoading; + const isValid = !!checkout.shippingAddress && !!checkout.email && !!checkout.billingAddress; + + const isDisabled = isLoading || !isValid; + + const onTransactionInitialize = useSubmit< + TransactionInitializeMutationVariables, + typeof transactionInitialize + >( + React.useMemo( + () => ({ + onSubmit: transactionInitialize, + onError: (error) => { + console.error(error); + showCustomErrors([{ message: commonErrorMessages.somethingWentWrong }]); + }, + onSuccess: async () => { + void onCheckoutComplete(); + }, + }), + [commonErrorMessages.somethingWentWrong, onCheckoutComplete, showCustomErrors, transactionInitialize], + ), + ); + + const onSubmit = async () => { + await onTransactionInitialize({ + checkoutId: checkout.id, + paymentGateway: { + id: props.config.id, + }, + }); + }; + + return { onSubmit, isLoading, isDisabled }; +}; + +export const DummyDropIn = (props: DummyDropinProps) => { + const { onSubmit, isLoading, isDisabled } = useDummyDropIn(props); + + return ( +
+

Dummy Payment Gateway

+ {/*

+ Stored payment methods +

*/} +
+ ); +}; diff --git a/src/checkout/sections/PaymentSection/DummyPayment/types.ts b/src/checkout/sections/PaymentSection/DummyPayment/types.ts new file mode 100644 index 000000000..99bb6eb22 --- /dev/null +++ b/src/checkout/sections/PaymentSection/DummyPayment/types.ts @@ -0,0 +1,2 @@ +export const dummyGatewayId = "dummy.payment.saleor.app"; +export type DummyGatewayId = typeof dummyGatewayId; diff --git a/src/checkout/sections/PaymentSection/PaymentMethods.tsx b/src/checkout/sections/PaymentSection/PaymentMethods.tsx index 61c71d7ef..a6b1622ed 100644 --- a/src/checkout/sections/PaymentSection/PaymentMethods.tsx +++ b/src/checkout/sections/PaymentSection/PaymentMethods.tsx @@ -19,6 +19,12 @@ export const PaymentMethods = () => {
{availablePaymentGateways.map((gateway) => { const Component = paymentMethodToComponent[gateway.id]; + + if (!Component) { + console.warn(`No component found for gateway ${gateway.id}`); + return null; + } + return ( ; export type ParsedStripeGateway = ParsedPaymentGateway; +export type ParsedDummyGateway = ParsedPaymentGateway; -export type ParsedPaymentGateways = ReadonlyArray; +export type ParsedPaymentGateways = ReadonlyArray< + ParsedAdyenGateway | ParsedStripeGateway | ParsedDummyGateway +>; export interface ParsedPaymentGateway> extends Omit { diff --git a/src/checkout/sections/PaymentSection/usePaymentGatewaysInitialize.ts b/src/checkout/sections/PaymentSection/usePaymentGatewaysInitialize.ts index 8842f92bf..b85dc5ef0 100644 --- a/src/checkout/sections/PaymentSection/usePaymentGatewaysInitialize.ts +++ b/src/checkout/sections/PaymentSection/usePaymentGatewaysInitialize.ts @@ -8,10 +8,7 @@ import { getFilteredPaymentGateways } from "@/checkout/sections/PaymentSection/u export const usePaymentGatewaysInitialize = () => { const { - checkout: { billingAddress }, - } = useCheckout(); - const { - checkout: { id: checkoutId, availablePaymentGateways }, + checkout: { id: checkoutId, availablePaymentGateways, billingAddress }, } = useCheckout(); const billingCountry = billingAddress?.country.code as MightNotExist;