@noaignite/react-centra-checkout
Version:
React components and helpers for Centra checkout api
99 lines (98 loc) • 4.07 kB
JavaScript
import { useCentraHandlers, useCentraSelection } from "./Context.js";
import { HtmlEmbed } from "./internal/HtmlEmbed.js";
import { isPlainObject } from "@noaignite/utils";
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { jsx } from "react/jsx-runtime";
import isEqual from "react-fast-compare";
//#region src/PaymentEmbed.tsx
/** This component handles rendering of payment widgets such as Klarna Checkout and Adyen drop-in, if you submit payments yourself directly,
you should simply call the submitPayment method of the context instead */
const PaymentEmbed = memo(function PaymentEmbed(props) {
const { values, onPaymentError, onPaymentSuccess } = props;
const [paymentResult, setPaymentResult] = useState(null);
const [paymentCallbackError, setPaymentCallbackError] = useState(null);
const previousPaymentMethod = useRef(null);
const { selection, paymentMethods } = useCentraSelection();
const { submitPayment } = useCentraHandlers();
const paymentMethodId = selection?.paymentMethod;
const paymentMethod = useMemo(() => paymentMethods?.find((p) => p.paymentMethod === paymentMethodId), [paymentMethods, paymentMethodId]);
const handlePaymentCallback = useCallback((event) => {
const { addressIncluded, billingAddress: detailsAddress, shippingAddress: detailsShippingAddress, paymentMethod: detailsPaymentMethod, paymentMethodSpecificFields } = event.detail;
const payload = {
address: values.address,
shippingAddress: values.shippingAddress,
paymentMethod: detailsPaymentMethod,
paymentMethodSpecificFields,
termsAndConditions: values.termsAndConditions
};
if (addressIncluded) {
payload.address = detailsAddress;
payload.shippingAddress = detailsShippingAddress;
}
submitPayment?.(payload).then((result) => {
if ("errors" in result && Boolean(result.errors)) {
onPaymentError?.(result.errors);
setPaymentResult(null);
setPaymentCallbackError(result.errors);
} else {
onPaymentSuccess?.(result);
setPaymentCallbackError(null);
}
}).catch((err) => {
console.error("@noaignite/react-centra-checkout: Could not submit payment");
if (isPlainObject(err)) setPaymentCallbackError(err);
console.error(err);
});
}, [
submitPayment,
onPaymentSuccess,
onPaymentError,
values
]);
useEffect(() => {
if (selection?.paymentMethod !== previousPaymentMethod.current) setPaymentResult(null);
}, [selection?.paymentMethod]);
useEffect(() => {
const shouldRequestPayment = paymentMethod?.supportsInitiateOnly || paymentMethod?.providesCustomerAddressAfterPayment;
if ((selection?.paymentMethod === previousPaymentMethod.current || !shouldRequestPayment) && !paymentCallbackError || !selection?.paymentMethod) return;
previousPaymentMethod.current = selection.paymentMethod;
submitPayment?.({
...values,
termsAndConditions: void 0
}).then((result) => {
if ("errors" in result) {
console.error(result.errors);
onPaymentError?.(result.errors);
throw new Error("@noaignite/react-centra-checkout: Error while fetching widget");
}
if (result.action === "form" && !result.formHtml) throw new Error("@noaignite/react-centra-checkout: No form to render");
setPaymentResult(result);
setPaymentCallbackError(null);
}).catch((err) => {
console.error(err);
setPaymentResult(null);
});
}, [
onPaymentError,
paymentCallbackError,
paymentMethod,
selection?.paymentMethod,
selection?.totals?.grandTotalPriceAsNumber,
submitPayment,
values
]);
useEffect(() => {
document.addEventListener("centra_checkout_payment_callback", handlePaymentCallback);
return () => {
document.removeEventListener("centra_checkout_payment_callback", handlePaymentCallback);
};
}, [handlePaymentCallback]);
const formHtml = paymentResult && "formHtml" in paymentResult && paymentResult.formHtml;
return formHtml ? /* @__PURE__ */ jsx(HtmlEmbed, {
html: formHtml,
id: "centra-payment-form"
}) : null;
}, isEqual);
//#endregion
export { PaymentEmbed };
//# sourceMappingURL=PaymentEmbed.js.map