UNPKG

@noaignite/react-centra-checkout

Version:

React components and helpers for Centra checkout api

1 lines 8.16 kB
{"version":3,"sources":["../src/PaymentEmbed.tsx"],"sourcesContent":["import type * as CheckoutApi from '@noaignite/centra-types'\nimport { isPlainObject } from '@noaignite/utils'\nimport { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport isEqual from 'react-fast-compare'\nimport { useCentraHandlers, useCentraSelection } from './Context'\nimport { HtmlEmbed } from './internal/HtmlEmbed'\n\nexport interface PaymentEmbedProps {\n values: Record<string, unknown>\n onPaymentSuccess?: (paymentResult: CheckoutApi.SuccessResponse<CheckoutApi.Payment>) => void\n onPaymentError?: (error: CheckoutApi.Errors) => void\n}\n\n/** This component handles rendering of payment widgets such as Klarna Checkout and Adyen drop-in, if you submit payments yourself directly,\nyou should simply call the submitPayment method of the context instead */\nexport const PaymentEmbed = memo(function PaymentEmbed(\n props: PaymentEmbedProps,\n): React.ReactElement | null {\n const { values, onPaymentError, onPaymentSuccess } = props\n\n const [paymentResult, setPaymentResult] =\n useState<CheckoutApi.Response<CheckoutApi.Payment> | null>(null)\n const [paymentCallbackError, setPaymentCallbackError] = useState<CheckoutApi.Errors | null>(null)\n\n const previousPaymentMethod = useRef<string | null>(null)\n\n // Get selection\n const { selection, paymentMethods } = useCentraSelection()\n const { submitPayment } = useCentraHandlers()\n\n // Get payment method\n const paymentMethodId = selection?.paymentMethod\n const paymentMethod = useMemo(\n () => paymentMethods?.find((p) => p.paymentMethod === paymentMethodId),\n [paymentMethods, paymentMethodId],\n )\n\n // Submit payment\n const handlePaymentCallback = useCallback(\n (event: GlobalEventHandlersEventMap['centra_checkout_payment_callback']) => {\n const {\n addressIncluded,\n billingAddress: detailsAddress,\n shippingAddress: detailsShippingAddress,\n paymentMethod: detailsPaymentMethod,\n paymentMethodSpecificFields,\n } = event.detail\n\n const payload = {\n address: values.address,\n shippingAddress: values.shippingAddress,\n paymentMethod: detailsPaymentMethod,\n paymentMethodSpecificFields,\n termsAndConditions: values.termsAndConditions,\n } as Record<string, unknown>\n\n if (addressIncluded) {\n // if address is included in event, send that address instead\n payload.address = detailsAddress\n payload.shippingAddress = detailsShippingAddress\n }\n\n submitPayment?.(payload)\n .then((result) => {\n if ('errors' in result && Boolean(result.errors)) {\n onPaymentError?.(result.errors)\n // set paymentResult to null so that a new POST /payment request is made to refresh the widget\n setPaymentResult(null)\n setPaymentCallbackError(result.errors)\n } else {\n onPaymentSuccess?.(result)\n setPaymentCallbackError(null)\n }\n })\n .catch((err: unknown) => {\n console.error('@noaignite/react-centra-checkout: Could not submit payment')\n\n if (isPlainObject(err)) {\n setPaymentCallbackError(err)\n }\n\n console.error(err)\n })\n },\n [submitPayment, onPaymentSuccess, onPaymentError, values],\n )\n\n // Reset payment result when method changes\n useEffect(() => {\n if (selection?.paymentMethod !== previousPaymentMethod.current) {\n setPaymentResult(null)\n }\n }, [selection?.paymentMethod])\n\n // Retrieve formHtml\n useEffect(() => {\n const shouldRequestPayment =\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we actually want || here because we are checking for undefined, null and false\n paymentMethod?.supportsInitiateOnly || paymentMethod?.providesCustomerAddressAfterPayment // if either of these are true, this is a paymentMethod which provides an embed\n\n if (\n ((selection?.paymentMethod === previousPaymentMethod.current || !shouldRequestPayment) &&\n !paymentCallbackError) ||\n !selection?.paymentMethod\n ) {\n return\n }\n\n previousPaymentMethod.current = selection.paymentMethod\n\n // don't send termsAndConditions when fetching embed payments, as we don't want it to fail just yet.\n submitPayment?.({ ...values, termsAndConditions: undefined })\n .then((result) => {\n if ('errors' in result) {\n console.error(result.errors)\n onPaymentError?.(result.errors)\n throw new Error('@noaignite/react-centra-checkout: Error while fetching widget')\n }\n\n if (result.action === 'form' && !result.formHtml) {\n throw new Error('@noaignite/react-centra-checkout: No form to render')\n }\n\n setPaymentResult(result)\n setPaymentCallbackError(null)\n })\n .catch((err: unknown) => {\n console.error(err)\n setPaymentResult(null)\n })\n }, [\n onPaymentError,\n paymentCallbackError,\n paymentMethod,\n selection?.paymentMethod,\n selection?.totals?.grandTotalPriceAsNumber,\n submitPayment,\n values,\n ])\n\n useEffect(() => {\n // Fires when customer submits payment\n document.addEventListener('centra_checkout_payment_callback', handlePaymentCallback)\n\n return () => {\n document.removeEventListener('centra_checkout_payment_callback', handlePaymentCallback)\n }\n }, [handlePaymentCallback])\n\n const formHtml = paymentResult && 'formHtml' in paymentResult && paymentResult.formHtml\n\n return formHtml ? <HtmlEmbed html={formHtml} id=\"centra-payment-form\" /> : null\n}, isEqual)\n"],"mappings":"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","names":["PaymentEmbed","isEqual"]}